xref: /csrg-svn/games/quiz/quiz.c (revision 51609)
147862Sbostic /*-
2*51609Sbostic  * Copyright (c) 1991 The Regents of the University of California.
3*51609Sbostic  * All rights reserved.
4*51609Sbostic  *
5*51609Sbostic  * This code is derived from software contributed to Berkeley by
6*51609Sbostic  * Jim R. Oldroyd at The Instruction Set.
7*51609Sbostic  *
8*51609Sbostic  * %sccs.include.redist.c%
947862Sbostic  */
108867Smckusick 
1147862Sbostic #ifndef lint
12*51609Sbostic char copyright[] =
13*51609Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
14*51609Sbostic  All rights reserved.\n";
1547862Sbostic #endif /* not lint */
168867Smckusick 
17*51609Sbostic #ifndef lint
18*51609Sbostic static char sccsid[] = "@(#)quiz.c	5.1 (Berkeley) 11/10/91";
19*51609Sbostic #endif /* not lint */
20*51609Sbostic 
21*51609Sbostic #include <sys/types.h>
22*51609Sbostic #include <errno.h>
23*51609Sbostic #include <time.h>
248867Smckusick #include <stdio.h>
25*51609Sbostic #include <stdlib.h>
26*51609Sbostic #include <string.h>
27*51609Sbostic #include <ctype.h>
28*51609Sbostic #include "quiz.h"
2941774Sbostic #include "pathnames.h"
3041774Sbostic 
31*51609Sbostic static QE qlist;
32*51609Sbostic static int catone, cattwo, tflag;
33*51609Sbostic static u_int qsize;
348867Smckusick 
35*51609Sbostic char	*appdstr __P((char *, char *));
36*51609Sbostic void	 downcase __P((char *));
37*51609Sbostic void	 err __P((const char *, ...));
38*51609Sbostic void	 get_cats __P((char *, char *));
39*51609Sbostic void	 get_file __P((char *));
40*51609Sbostic char	*next_cat __P((char *));
41*51609Sbostic void	 quiz __P((void));
42*51609Sbostic void	 score __P((u_int, u_int, u_int));
43*51609Sbostic void	 show_index __P((void));
44*51609Sbostic void	 usage __P((void));
458867Smckusick 
46*51609Sbostic int
47*51609Sbostic main(argc, argv)
48*51609Sbostic 	int argc;
49*51609Sbostic 	char *argv[];
508867Smckusick {
5133196Sbostic 	register int ch;
52*51609Sbostic 	char *indexfile;
538867Smckusick 
54*51609Sbostic 	indexfile = _PATH_QUIZIDX;
55*51609Sbostic 	while ((ch = getopt(argc, argv, "i:t")) != EOF)
56*51609Sbostic 		switch(ch) {
57*51609Sbostic 		case 'i':
58*51609Sbostic 			indexfile = optarg;
59*51609Sbostic 			break;
60*51609Sbostic 		case 't':
61*51609Sbostic 			tflag = 1;
62*51609Sbostic 			break;
63*51609Sbostic 		case '?':
648867Smckusick 		default:
65*51609Sbostic 			usage();
668867Smckusick 		}
67*51609Sbostic 	argc -= optind;
68*51609Sbostic 	argv += optind;
698867Smckusick 
70*51609Sbostic 	switch(argc) {
71*51609Sbostic 	case 0:
72*51609Sbostic 		get_file(indexfile);
73*51609Sbostic 		show_index();
74*51609Sbostic 		break;
75*51609Sbostic 	case 2:
76*51609Sbostic 		get_file(indexfile);
77*51609Sbostic 		get_cats(argv[0], argv[1]);
78*51609Sbostic 		quiz();
79*51609Sbostic 		break;
80*51609Sbostic 	default:
81*51609Sbostic 		usage();
828867Smckusick 	}
83*51609Sbostic 	exit(0);
848867Smckusick }
858867Smckusick 
86*51609Sbostic void
87*51609Sbostic get_file(file)
88*51609Sbostic 	char *file;
898867Smckusick {
90*51609Sbostic 	register FILE *fp;
91*51609Sbostic 	register QE *qp;
92*51609Sbostic 	size_t len;
93*51609Sbostic 	char *lp;
948867Smckusick 
95*51609Sbostic 	if ((fp = fopen(file, "r")) == NULL)
96*51609Sbostic 		err("%s: %s", file, strerror(errno));
978867Smckusick 
98*51609Sbostic 	/*
99*51609Sbostic 	 * XXX
100*51609Sbostic 	 * Should really free up space from any earlier read list
101*51609Sbostic 	 * but there are no reverse pointers to do so with.
102*51609Sbostic 	 */
103*51609Sbostic 	qp = &qlist;
104*51609Sbostic 	qsize = 0;
105*51609Sbostic 	while ((lp = fgetline(fp, &len)) != NULL) {
106*51609Sbostic 		if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\')
107*51609Sbostic 			qp->q_text = appdstr(qp->q_text, lp);
108*51609Sbostic 		else {
109*51609Sbostic 			if ((qp->q_next = malloc(sizeof(QE))) == NULL)
110*51609Sbostic 				err("%s", strerror(errno));
111*51609Sbostic 			qp = qp->q_next;
112*51609Sbostic 			if ((qp->q_text = strdup(lp)) == NULL)
113*51609Sbostic 				err("%s", strerror(errno));
114*51609Sbostic 			qp->q_asked = qp->q_answered = FALSE;
115*51609Sbostic 			qp->q_next = NULL;
116*51609Sbostic 			++qsize;
1178867Smckusick 		}
1188867Smckusick 	}
119*51609Sbostic 	(void)fclose(fp);
1208867Smckusick }
1218867Smckusick 
122*51609Sbostic void
123*51609Sbostic show_index()
1248867Smckusick {
125*51609Sbostic 	register QE *qp;
126*51609Sbostic 	register char *p, *s;
127*51609Sbostic 	FILE *pf;
1288867Smckusick 
129*51609Sbostic 	if ((pf = popen(_PATH_PAGER, "w")) == NULL)
130*51609Sbostic 		err("%s: %s", _PATH_PAGER, strerror(errno));
131*51609Sbostic 	(void)fprintf(pf, "Subjects:\n\n");
132*51609Sbostic 	for (qp = qlist.q_next; qp; qp = qp->q_next) {
133*51609Sbostic 		for (s = next_cat(qp->q_text); s; s = next_cat(s)) {
134*51609Sbostic 			if (!rxp_compile(s))
135*51609Sbostic 				err("%s", rxperr);
136*51609Sbostic 			if (p = rxp_expand())
137*51609Sbostic 				(void)fprintf(pf, "%s ", p);
1388867Smckusick 		}
139*51609Sbostic 		(void)fprintf(pf, "\n");
1408867Smckusick 	}
141*51609Sbostic 	(void)fprintf(pf, "\n%s\n%s\n%s\n",
142*51609Sbostic "For example, \"quiz victim killer\" prints a victim's name and you reply",
143*51609Sbostic "with the killer, and \"quiz killer victim\" works the other way around.",
144*51609Sbostic "Type an empty line to get the correct answer.");
145*51609Sbostic 	(void)pclose(pf);
1468867Smckusick }
1478867Smckusick 
148*51609Sbostic void
149*51609Sbostic get_cats(cat1, cat2)
150*51609Sbostic 	char *cat1, *cat2;
1518867Smckusick {
152*51609Sbostic 	register QE *qp;
153*51609Sbostic 	int i;
154*51609Sbostic 	char *s;
155*51609Sbostic 
156*51609Sbostic 	downcase(cat1);
157*51609Sbostic 	downcase(cat2);
158*51609Sbostic 	for (qp = qlist.q_next; qp; qp = qp->q_next) {
159*51609Sbostic 		s = next_cat(qp->q_text);
160*51609Sbostic 		catone = cattwo = i = 0;
161*51609Sbostic 		while (s) {
162*51609Sbostic 			if (!rxp_compile(s))
163*51609Sbostic 				err("%s", rxperr);
164*51609Sbostic 			i++;
165*51609Sbostic 			if (rxp_match(cat1))
166*51609Sbostic 				catone = i;
167*51609Sbostic 			if (rxp_match(cat2))
168*51609Sbostic 				cattwo = i;
169*51609Sbostic 			s = next_cat(s);
1708867Smckusick 		}
171*51609Sbostic 		if (catone && cattwo && catone != cattwo) {
172*51609Sbostic 			if (!rxp_compile(qp->q_text))
173*51609Sbostic 				err("%s", rxperr);
174*51609Sbostic 			get_file(rxp_expand());
175*51609Sbostic 			return;
176*51609Sbostic 		}
1778867Smckusick 	}
178*51609Sbostic 	err("invalid categories");
1798867Smckusick }
1808867Smckusick 
181*51609Sbostic void
182*51609Sbostic quiz()
1838867Smckusick {
184*51609Sbostic 	register QE *qp;
185*51609Sbostic 	register int i;
186*51609Sbostic 	u_int guesses, rights, wrongs;
187*51609Sbostic 	int next;
188*51609Sbostic 	char *s, *t, question[LINE_SZ];
189*51609Sbostic 	char *answer;
1908867Smckusick 
191*51609Sbostic 	srandom(time(NULL));
192*51609Sbostic 	guesses = rights = wrongs = 0;
193*51609Sbostic 	for (;;) {
194*51609Sbostic 		if (qsize == 0)
1958867Smckusick 			break;
196*51609Sbostic 		next = random() % qsize;
197*51609Sbostic 		qp = qlist.q_next;
198*51609Sbostic 		for (i = 0; i < next; i++)
199*51609Sbostic 			qp = qp->q_next;
200*51609Sbostic 		while (qp && qp->q_answered)
201*51609Sbostic 			qp = qp->q_next;
202*51609Sbostic 		if (!qp) {
203*51609Sbostic 			qsize = next;
204*51609Sbostic 			continue;
2058867Smckusick 		}
206*51609Sbostic 		if (tflag && random() % 100 > 20) {
207*51609Sbostic 			/* repeat questions in tutorial mode */
208*51609Sbostic 			while (qp && (!qp->q_asked || qp->q_answered))
209*51609Sbostic 				qp = qp->q_next;
210*51609Sbostic 			if (!qp)
211*51609Sbostic 				continue;
2128867Smckusick 		}
213*51609Sbostic 		s = qp->q_text;
214*51609Sbostic 		for (i = 0; i < catone - 1; i++)
215*51609Sbostic 			s = next_cat(s);
216*51609Sbostic 		if (!rxp_compile(s))
217*51609Sbostic 			err("%s", rxperr);
218*51609Sbostic 		t = rxp_expand();
219*51609Sbostic 		if (!t || *t == '\0') {
220*51609Sbostic 			qp->q_answered = TRUE;
2218867Smckusick 			continue;
2228867Smckusick 		}
223*51609Sbostic 		(void)strcpy(question, t);
224*51609Sbostic 		s = qp->q_text;
225*51609Sbostic 		for (i = 0; i < cattwo - 1; i++)
226*51609Sbostic 			s = next_cat(s);
227*51609Sbostic 		if (!rxp_compile(s))
228*51609Sbostic 			err("%s", rxperr);
229*51609Sbostic 		t = rxp_expand();
230*51609Sbostic 		if (!t || *t == '\0') {
231*51609Sbostic 			qp->q_answered = TRUE;
232*51609Sbostic 			continue;
233*51609Sbostic 		}
234*51609Sbostic 		qp->q_asked = TRUE;
235*51609Sbostic 		(void)printf("%s?\n", question);
236*51609Sbostic 		for (;; ++guesses) {
237*51609Sbostic 			if ((answer = fgetline(stdin, NULL)) == NULL) {
238*51609Sbostic 				score(rights, wrongs, guesses);
239*51609Sbostic 				exit(0);
240*51609Sbostic 			}
241*51609Sbostic 			downcase(answer);
242*51609Sbostic 			if (rxp_match(answer)) {
243*51609Sbostic 				(void)printf("Right!\n");
244*51609Sbostic 				++rights;
245*51609Sbostic 				qp->q_answered = TRUE;
2468867Smckusick 				break;
2478867Smckusick 			}
248*51609Sbostic 			if (*answer == '\0') {
249*51609Sbostic 				(void)printf("%s\n", t);
250*51609Sbostic 				++wrongs;
251*51609Sbostic 				if (!tflag)
252*51609Sbostic 					qp->q_answered = TRUE;
2538867Smckusick 				break;
2548867Smckusick 			}
255*51609Sbostic 			(void)printf("What?\n");
2568867Smckusick 		}
2578867Smckusick 	}
258*51609Sbostic 	score(rights, wrongs, guesses);
2598867Smckusick }
2608867Smckusick 
261*51609Sbostic char *
262*51609Sbostic next_cat(s)
263*51609Sbostic 	register char *	s;
2648867Smckusick {
265*51609Sbostic 	for (;;)
266*51609Sbostic 		switch (*s++) {
267*51609Sbostic 		case '\0':
268*51609Sbostic 			return (NULL);
269*51609Sbostic 		case '\\':
2708867Smckusick 			break;
271*51609Sbostic 		case ':':
272*51609Sbostic 			return (s);
2738867Smckusick 		}
274*51609Sbostic 	/* NOTREACHED */
2758867Smckusick }
2768867Smckusick 
277*51609Sbostic char *
278*51609Sbostic appdstr(s, tp)
279*51609Sbostic 	char *s;
280*51609Sbostic 	register char *tp;
2818867Smckusick {
282*51609Sbostic 	register char *mp, *sp;
283*51609Sbostic 	register int ch;
284*51609Sbostic 	char *m;
285*51609Sbostic 
286*51609Sbostic 	if ((m = malloc(strlen(sp) + strlen(tp) + 1)) == NULL)
287*51609Sbostic 		err("%s", strerror(errno));
288*51609Sbostic 	for (mp = m, sp = s; *mp++ = *sp++;);
289*51609Sbostic 
290*51609Sbostic 	if (*(mp - 1) == '\\')
291*51609Sbostic 		--mp;
292*51609Sbostic 	while ((ch = *mp++ = *tp++) && ch != '\n');
293*51609Sbostic 	*mp = '\0';
294*51609Sbostic 
295*51609Sbostic 	free(s);
296*51609Sbostic 	return (m);
2978867Smckusick }
2988867Smckusick 
29946755Sbostic void
300*51609Sbostic score(r, w, g)
301*51609Sbostic 	u_int r, w, g;
3028867Smckusick {
303*51609Sbostic 	(void)printf("Rights %d, wrongs %d,", r, w);
304*51609Sbostic 	if (g)
305*51609Sbostic 		(void)printf(" extra guesses %d,", g);
306*51609Sbostic 	(void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0);
3078867Smckusick }
308*51609Sbostic 
309*51609Sbostic void
310*51609Sbostic downcase(p)
311*51609Sbostic 	register char *p;
3128867Smckusick {
313*51609Sbostic 	register int ch;
314*51609Sbostic 
315*51609Sbostic 	for (; ch = *p; ++p)
316*51609Sbostic 		if (isascii(ch) && isupper(ch))
317*51609Sbostic 			*p = tolower(ch);
3188867Smckusick }
3198867Smckusick 
320*51609Sbostic void
321*51609Sbostic usage()
322*51609Sbostic {
323*51609Sbostic 	(void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n");
324*51609Sbostic 	exit(1);
3258867Smckusick }
3268867Smckusick 
327*51609Sbostic #if __STDC__
328*51609Sbostic #include <stdarg.h>
329*51609Sbostic #else
330*51609Sbostic #include <varargs.h>
331*51609Sbostic #endif
332*51609Sbostic 
333*51609Sbostic void
334*51609Sbostic #if __STDC__
335*51609Sbostic err(const char *fmt, ...)
336*51609Sbostic #else
337*51609Sbostic err(fmt, va_alist)
338*51609Sbostic 	char *fmt;
339*51609Sbostic         va_dcl
340*51609Sbostic #endif
3418867Smckusick {
342*51609Sbostic 	va_list ap;
343*51609Sbostic #if __STDC__
344*51609Sbostic 	va_start(ap, fmt);
345*51609Sbostic #else
346*51609Sbostic 	va_start(ap);
347*51609Sbostic #endif
348*51609Sbostic 	(void)fprintf(stderr, "quiz: ");
349*51609Sbostic 	(void)vfprintf(stderr, fmt, ap);
350*51609Sbostic 	va_end(ap);
351*51609Sbostic 	(void)fprintf(stderr, "\n");
352*51609Sbostic 	exit(1);
3538867Smckusick }
354