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