xref: /netbsd/games/fish/fish.c (revision bf9ec67e)
1 /*	$NetBSD: fish.c,v 1.13 2000/05/08 07:56:03 mycroft Exp $	*/
2 
3 /*-
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Muffy Barkocy.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n");
43 #endif /* not lint */
44 
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)fish.c	8.1 (Berkeley) 5/31/93";
48 #else
49 __RCSID("$NetBSD: fish.c,v 1.13 2000/05/08 07:56:03 mycroft Exp $");
50 #endif
51 #endif /* not lint */
52 
53 #include <sys/types.h>
54 #include <sys/wait.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <time.h>
62 #include <err.h>
63 #include "pathnames.h"
64 
65 #define	RANKS		13
66 #define	HANDSIZE	7
67 #define	CARDS		4
68 #define	TOTCARDS	RANKS * CARDS
69 
70 #define	USER		1
71 #define	COMPUTER	0
72 #define	OTHER(a)	(1 - (a))
73 
74 const char *const cards[] = {
75 	"A", "2", "3", "4", "5", "6", "7",
76 	"8", "9", "10", "J", "Q", "K", NULL,
77 };
78 #define	PRC(card)	(void)printf(" %s", cards[card])
79 
80 int promode;
81 int asked[RANKS], comphand[RANKS], deck[TOTCARDS];
82 int userasked[RANKS], userhand[RANKS];
83 int curcard = TOTCARDS;
84 
85 void	chkwinner __P((int, const int *));
86 int	compmove __P((void));
87 int	countbooks __P((const int *));
88 int	countcards __P((const int *));
89 int	drawcard __P((int, int *));
90 int	gofish __P((int, int, int *));
91 void	goodmove __P((int, int, int *, int *));
92 void	init __P((void));
93 void	instructions __P((void));
94 int	main __P((int, char *[]));
95 int	nrandom __P((int));
96 void	printhand __P((const int *));
97 void	printplayer __P((int));
98 int	promove __P((void));
99 void	usage __P((void)) __attribute__((__noreturn__));
100 int	usermove __P((void));
101 
102 int
103 main(argc, argv)
104 	int argc;
105 	char **argv;
106 {
107 	int ch, move;
108 
109 	/* Revoke setgid privileges */
110 	setgid(getgid());
111 
112 	while ((ch = getopt(argc, argv, "p")) != -1)
113 		switch(ch) {
114 		case 'p':
115 			promode = 1;
116 			break;
117 		case '?':
118 		default:
119 			usage();
120 		}
121 
122 	srandom(time((time_t *)NULL));
123 	instructions();
124 	init();
125 
126 	if (nrandom(2) == 1) {
127 		printplayer(COMPUTER);
128 		(void)printf("get to start.\n");
129 		goto istart;
130 	}
131 	printplayer(USER);
132 	(void)printf("get to start.\n");
133 
134 	for (;;) {
135 		move = usermove();
136 		if (!comphand[move]) {
137 			if (gofish(move, USER, userhand))
138 				continue;
139 		} else {
140 			goodmove(USER, move, userhand, comphand);
141 			continue;
142 		}
143 
144 istart:		for (;;) {
145 			move = compmove();
146 			if (!userhand[move]) {
147 				if (!gofish(move, COMPUTER, comphand))
148 					break;
149 			} else
150 				goodmove(COMPUTER, move, comphand, userhand);
151 		}
152 	}
153 	/* NOTREACHED */
154 }
155 
156 int
157 usermove()
158 {
159 	int n;
160 	const char *const *p;
161 	char buf[256];
162 
163 	(void)printf("\nYour hand is:");
164 	printhand(userhand);
165 
166 	for (;;) {
167 		(void)printf("You ask me for: ");
168 		(void)fflush(stdout);
169 		if (fgets(buf, sizeof(buf), stdin) == NULL)
170 			exit(0);
171 		if (buf[0] == '\0')
172 			continue;
173 		if (buf[0] == '\n') {
174 			(void)printf("%d cards in my hand, %d in the pool.\n",
175 			    countcards(comphand), curcard);
176 			(void)printf("My books:");
177 			(void)countbooks(comphand);
178 			continue;
179 		}
180 		buf[strlen(buf) - 1] = '\0';
181 		if (!strcasecmp(buf, "p") && !promode) {
182 			promode = 1;
183 			(void)printf("Entering pro mode.\n");
184 			continue;
185 		}
186 		if (!strcasecmp(buf, "quit"))
187 			exit(0);
188 		for (p = cards; *p; ++p)
189 			if (!strcasecmp(*p, buf))
190 				break;
191 		if (!*p) {
192 			(void)printf("I don't understand!\n");
193 			continue;
194 		}
195 		n = p - cards;
196 		if (userhand[n]) {
197 			userasked[n] = 1;
198 			return(n);
199 		}
200 		if (nrandom(3) == 1)
201 			(void)printf("You don't have any of those!\n");
202 		else
203 			(void)printf("You don't have any %s's!\n", cards[n]);
204 		if (nrandom(4) == 1)
205 			(void)printf("No cheating!\n");
206 		(void)printf("Guess again.\n");
207 	}
208 	/* NOTREACHED */
209 }
210 
211 int
212 compmove()
213 {
214 	static int lmove;
215 
216 	if (promode)
217 		lmove = promove();
218 	else {
219 		do {
220 			lmove = (lmove + 1) % RANKS;
221 		} while (!comphand[lmove] || comphand[lmove] == CARDS);
222 	}
223 	asked[lmove] = 1;
224 
225 	(void)printf("I ask you for: %s.\n", cards[lmove]);
226 	return(lmove);
227 }
228 
229 int
230 promove()
231 {
232 	int i, max;
233 
234 	for (i = 0; i < RANKS; ++i)
235 		if (userasked[i] &&
236 		    comphand[i] > 0 && comphand[i] < CARDS) {
237 			userasked[i] = 0;
238 			return(i);
239 		}
240 	if (nrandom(3) == 1) {
241 		for (i = 0;; ++i)
242 			if (comphand[i] && comphand[i] != CARDS) {
243 				max = i;
244 				break;
245 			}
246 		while (++i < RANKS)
247 			if (comphand[i] != CARDS &&
248 			    comphand[i] > comphand[max])
249 				max = i;
250 		return(max);
251 	}
252 	if (nrandom(1024) == 0723) {
253 		for (i = 0; i < RANKS; ++i)
254 			if (userhand[i] && comphand[i])
255 				return(i);
256 	}
257 	for (;;) {
258 		for (i = 0; i < RANKS; ++i)
259 			if (comphand[i] && comphand[i] != CARDS &&
260 			    !asked[i])
261 				return(i);
262 		for (i = 0; i < RANKS; ++i)
263 			asked[i] = 0;
264 	}
265 	/* NOTREACHED */
266 }
267 
268 int
269 drawcard(player, hand)
270 	int player;
271 	int *hand;
272 {
273 	int card;
274 
275 	++hand[card = deck[--curcard]];
276 	if (player == USER || hand[card] == CARDS) {
277 		printplayer(player);
278 		(void)printf("drew %s", cards[card]);
279 		if (hand[card] == CARDS) {
280 			(void)printf(" and made a book of %s's!\n",
281 			     cards[card]);
282 			chkwinner(player, hand);
283 		} else
284 			(void)printf(".\n");
285 	}
286 	return(card);
287 }
288 
289 int
290 gofish(askedfor, player, hand)
291 	int askedfor, player;
292 	int *hand;
293 {
294 	printplayer(OTHER(player));
295 	(void)printf("say \"GO FISH!\"\n");
296 	if (askedfor == drawcard(player, hand)) {
297 		printplayer(player);
298 		(void)printf("drew the guess!\n");
299 		printplayer(player);
300 		(void)printf("get to ask again!\n");
301 		return(1);
302 	}
303 	return(0);
304 }
305 
306 void
307 goodmove(player, move, hand, opphand)
308 	int player, move;
309 	int *hand, *opphand;
310 {
311 	printplayer(OTHER(player));
312 	(void)printf("have %d %s%s.\n",
313 	    opphand[move], cards[move], opphand[move] == 1 ? "": "'s");
314 
315 	hand[move] += opphand[move];
316 	opphand[move] = 0;
317 
318 	if (hand[move] == CARDS) {
319 		printplayer(player);
320 		(void)printf("made a book of %s's!\n", cards[move]);
321 		chkwinner(player, hand);
322 	}
323 
324 	chkwinner(OTHER(player), opphand);
325 
326 	printplayer(player);
327 	(void)printf("get another guess!\n");
328 }
329 
330 void
331 chkwinner(player, hand)
332 	int player;
333 	const int *hand;
334 {
335 	int cb, i, ub;
336 
337 	for (i = 0; i < RANKS; ++i)
338 		if (hand[i] > 0 && hand[i] < CARDS)
339 			return;
340 	printplayer(player);
341 	(void)printf("don't have any more cards!\n");
342 	(void)printf("My books:");
343 	cb = countbooks(comphand);
344 	(void)printf("Your books:");
345 	ub = countbooks(userhand);
346 	(void)printf("\nI have %d, you have %d.\n", cb, ub);
347 	if (ub > cb) {
348 		(void)printf("\nYou win!!!\n");
349 		if (nrandom(1024) == 0723)
350 			(void)printf("Cheater, cheater, pumpkin eater!\n");
351 	} else if (cb > ub) {
352 		(void)printf("\nI win!!!\n");
353 		if (nrandom(1024) == 0723)
354 			(void)printf("Hah!  Stupid peasant!\n");
355 	} else
356 		(void)printf("\nTie!\n");
357 	exit(0);
358 }
359 
360 void
361 printplayer(player)
362 	int player;
363 {
364 	switch (player) {
365 	case COMPUTER:
366 		(void)printf("I ");
367 		break;
368 	case USER:
369 		(void)printf("You ");
370 		break;
371 	}
372 }
373 
374 void
375 printhand(hand)
376 	const int *hand;
377 {
378 	int book, i, j;
379 
380 	for (book = i = 0; i < RANKS; i++)
381 		if (hand[i] < CARDS)
382 			for (j = hand[i]; --j >= 0;)
383 				PRC(i);
384 		else
385 			++book;
386 	if (book) {
387 		(void)printf(" + Book%s of", book > 1 ? "s" : "");
388 		for (i = 0; i < RANKS; i++)
389 			if (hand[i] == CARDS)
390 				PRC(i);
391 	}
392 	(void)putchar('\n');
393 }
394 
395 int
396 countcards(hand)
397 	const int *hand;
398 {
399 	int i, count;
400 
401 	for (count = i = 0; i < RANKS; i++)
402 		count += *hand++;
403 	return(count);
404 }
405 
406 int
407 countbooks(hand)
408 	const int *hand;
409 {
410 	int i, count;
411 
412 	for (count = i = 0; i < RANKS; i++)
413 		if (hand[i] == CARDS) {
414 			++count;
415 			PRC(i);
416 		}
417 	if (!count)
418 		(void)printf(" none");
419 	(void)putchar('\n');
420 	return(count);
421 }
422 
423 void
424 init()
425 {
426 	int i, j, temp;
427 
428 	for (i = 0; i < TOTCARDS; ++i)
429 		deck[i] = i % RANKS;
430 	for (i = 0; i < TOTCARDS - 1; ++i) {
431 		j = nrandom(TOTCARDS-i);
432 		if (j == 0)
433 			continue;
434 		temp = deck[i];
435 		deck[i] = deck[i+j];
436 		deck[i+j] = temp;
437 	}
438 	for (i = 0; i < HANDSIZE; ++i) {
439 		++userhand[deck[--curcard]];
440 		++comphand[deck[--curcard]];
441 	}
442 }
443 
444 int
445 nrandom(n)
446 	int n;
447 {
448 
449 	return((int)random() % n);
450 }
451 
452 void
453 instructions()
454 {
455 	int input;
456 	pid_t pid;
457 	int fd;
458 	const char *pager;
459 	int status;
460 
461 	(void)printf("Would you like instructions (y or n)? ");
462 	input = getchar();
463 	while (getchar() != '\n');
464 	if (input != 'y')
465 		return;
466 
467 	switch (pid = fork()) {
468 	case 0: /* child */
469 		if (!isatty(1))
470 			pager = "cat";
471 		else {
472 			if (!(pager = getenv("PAGER")) || (*pager == 0))
473 				pager = _PATH_MORE;
474 		}
475 		if ((fd = open(_PATH_INSTR, O_RDONLY)) == -1)
476 			err(1, "open %s", _PATH_INSTR);
477 		if (dup2(fd, 0) == -1)
478 			err(1, "dup2");
479 		(void)execl("/bin/sh", "sh", "-c", pager, NULL);
480 		err(1, "exec sh -c %s", pager);
481 		/*NOTREACHED*/
482 	case -1:
483 		err(1, "fork");
484 		/*NOTREACHED*/
485 	default:
486 		(void)waitpid(pid, &status, 0);
487 		break;
488 	}
489 	(void)printf("Hit return to continue...\n");
490 	while ((input = getchar()) != EOF && input != '\n');
491 }
492 
493 void
494 usage()
495 {
496 	(void)fprintf(stderr, "usage: fish [-p]\n");
497 	exit(1);
498 }
499