Home | History | Annotate | Line # | Download | only in fish
fish.c revision 1.10
      1 /*	$NetBSD: fish.c,v 1.10 1999/09/12 09:02:21 jsm 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.10 1999/09/12 09:02:21 jsm 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 
     69 #define	USER		1
     70 #define	COMPUTER	0
     71 #define	OTHER(a)	(1 - (a))
     72 
     73 const char *const cards[] = {
     74 	"A", "2", "3", "4", "5", "6", "7",
     75 	"8", "9", "10", "J", "Q", "K", NULL,
     76 };
     77 #define	PRC(card)	(void)printf(" %s", cards[card])
     78 
     79 int promode;
     80 int asked[RANKS], comphand[RANKS], deck[RANKS];
     81 int userasked[RANKS], userhand[RANKS];
     82 
     83 void	chkwinner __P((int, const int *));
     84 int	compmove __P((void));
     85 int	countbooks __P((const int *));
     86 int	countcards __P((const int *));
     87 int	drawcard __P((int, int *));
     88 int	gofish __P((int, int, int *));
     89 void	goodmove __P((int, int, int *, int *));
     90 void	init __P((void));
     91 void	instructions __P((void));
     92 int	main __P((int, char *[]));
     93 int	nrandom __P((int));
     94 void	printhand __P((const int *));
     95 void	printplayer __P((int));
     96 int	promove __P((void));
     97 void	usage __P((void)) __attribute__((__noreturn__));
     98 int	usermove __P((void));
     99 
    100 int
    101 main(argc, argv)
    102 	int argc;
    103 	char **argv;
    104 {
    105 	int ch, move;
    106 
    107 	/* Revoke setgid privileges */
    108 	setregid(getgid(), getgid());
    109 
    110 	while ((ch = getopt(argc, argv, "p")) != -1)
    111 		switch(ch) {
    112 		case 'p':
    113 			promode = 1;
    114 			break;
    115 		case '?':
    116 		default:
    117 			(void)fprintf(stderr, "usage: fish [-p]\n");
    118 			exit(1);
    119 		}
    120 
    121 	srandom(time((time_t *)NULL));
    122 	instructions();
    123 	init();
    124 
    125 	if (nrandom(2) == 1) {
    126 		printplayer(COMPUTER);
    127 		(void)printf("get to start.\n");
    128 		goto istart;
    129 	}
    130 	printplayer(USER);
    131 	(void)printf("get to start.\n");
    132 
    133 	for (;;) {
    134 		move = usermove();
    135 		if (!comphand[move]) {
    136 			if (gofish(move, USER, userhand))
    137 				continue;
    138 		} else {
    139 			goodmove(USER, move, userhand, comphand);
    140 			continue;
    141 		}
    142 
    143 istart:		for (;;) {
    144 			move = compmove();
    145 			if (!userhand[move]) {
    146 				if (!gofish(move, COMPUTER, comphand))
    147 					break;
    148 			} else
    149 				goodmove(COMPUTER, move, comphand, userhand);
    150 		}
    151 	}
    152 	/* NOTREACHED */
    153 }
    154 
    155 int
    156 usermove()
    157 {
    158 	int n;
    159 	const char *const *p;
    160 	char buf[256];
    161 
    162 	(void)printf("\nYour hand is:");
    163 	printhand(userhand);
    164 
    165 	for (;;) {
    166 		(void)printf("You ask me for: ");
    167 		(void)fflush(stdout);
    168 		if (fgets(buf, sizeof(buf), stdin) == NULL)
    169 			exit(0);
    170 		if (buf[0] == '\0')
    171 			continue;
    172 		if (buf[0] == '\n') {
    173 			(void)printf("%d cards in my hand, %d in the pool.\n",
    174 			    countcards(comphand), countcards(deck));
    175 			(void)printf("My books:");
    176 			(void)countbooks(comphand);
    177 			continue;
    178 		}
    179 		buf[strlen(buf) - 1] = '\0';
    180 		if (!strcasecmp(buf, "p") && !promode) {
    181 			promode = 1;
    182 			(void)printf("Entering pro mode.\n");
    183 			continue;
    184 		}
    185 		if (!strcasecmp(buf, "quit"))
    186 			exit(0);
    187 		for (p = cards; *p; ++p)
    188 			if (!strcasecmp(*p, buf))
    189 				break;
    190 		if (!*p) {
    191 			(void)printf("I don't understand!\n");
    192 			continue;
    193 		}
    194 		n = p - cards;
    195 		if (userhand[n]) {
    196 			userasked[n] = 1;
    197 			return(n);
    198 		}
    199 		if (nrandom(3) == 1)
    200 			(void)printf("You don't have any of those!\n");
    201 		else
    202 			(void)printf("You don't have any %s's!\n", cards[n]);
    203 		if (nrandom(4) == 1)
    204 			(void)printf("No cheating!\n");
    205 		(void)printf("Guess again.\n");
    206 	}
    207 	/* NOTREACHED */
    208 }
    209 
    210 int
    211 compmove()
    212 {
    213 	static int lmove;
    214 
    215 	if (promode)
    216 		lmove = promove();
    217 	else {
    218 		do {
    219 			lmove = (lmove + 1) % RANKS;
    220 		} while (!comphand[lmove] || comphand[lmove] == CARDS);
    221 	}
    222 	asked[lmove] = 1;
    223 
    224 	(void)printf("I ask you for: %s.\n", cards[lmove]);
    225 	return(lmove);
    226 }
    227 
    228 int
    229 promove()
    230 {
    231 	int i, max;
    232 
    233 	for (i = 0; i < RANKS; ++i)
    234 		if (userasked[i] &&
    235 		    comphand[i] > 0 && comphand[i] < CARDS) {
    236 			userasked[i] = 0;
    237 			return(i);
    238 		}
    239 	if (nrandom(3) == 1) {
    240 		for (i = 0;; ++i)
    241 			if (comphand[i] && comphand[i] != CARDS) {
    242 				max = i;
    243 				break;
    244 			}
    245 		while (++i < RANKS)
    246 			if (comphand[i] != CARDS &&
    247 			    comphand[i] > comphand[max])
    248 				max = i;
    249 		return(max);
    250 	}
    251 	if (nrandom(1024) == 0723) {
    252 		for (i = 0; i < RANKS; ++i)
    253 			if (userhand[i] && comphand[i])
    254 				return(i);
    255 	}
    256 	for (;;) {
    257 		for (i = 0; i < RANKS; ++i)
    258 			if (comphand[i] && comphand[i] != CARDS &&
    259 			    !asked[i])
    260 				return(i);
    261 		for (i = 0; i < RANKS; ++i)
    262 			asked[i] = 0;
    263 	}
    264 	/* NOTREACHED */
    265 }
    266 
    267 int
    268 drawcard(player, hand)
    269 	int player;
    270 	int *hand;
    271 {
    272 	int card;
    273 
    274 	while (deck[card = nrandom(RANKS)] == 0);
    275 	++hand[card];
    276 	--deck[card];
    277 	if (player == USER || hand[card] == CARDS) {
    278 		printplayer(player);
    279 		(void)printf("drew %s", cards[card]);
    280 		if (hand[card] == CARDS) {
    281 			(void)printf(" and made a book of %s's!\n",
    282 			     cards[card]);
    283 			chkwinner(player, hand);
    284 		} else
    285 			(void)printf(".\n");
    286 	}
    287 	return(card);
    288 }
    289 
    290 int
    291 gofish(askedfor, player, hand)
    292 	int askedfor, player;
    293 	int *hand;
    294 {
    295 	printplayer(OTHER(player));
    296 	(void)printf("say \"GO FISH!\"\n");
    297 	if (askedfor == drawcard(player, hand)) {
    298 		printplayer(player);
    299 		(void)printf("drew the guess!\n");
    300 		printplayer(player);
    301 		(void)printf("get to ask again!\n");
    302 		return(1);
    303 	}
    304 	return(0);
    305 }
    306 
    307 void
    308 goodmove(player, move, hand, opphand)
    309 	int player, move;
    310 	int *hand, *opphand;
    311 {
    312 	printplayer(OTHER(player));
    313 	(void)printf("have %d %s%s.\n",
    314 	    opphand[move], cards[move], opphand[move] == 1 ? "": "'s");
    315 
    316 	hand[move] += opphand[move];
    317 	opphand[move] = 0;
    318 
    319 	if (hand[move] == CARDS) {
    320 		printplayer(player);
    321 		(void)printf("made a book of %s's!\n", cards[move]);
    322 		chkwinner(player, hand);
    323 	}
    324 
    325 	chkwinner(OTHER(player), opphand);
    326 
    327 	printplayer(player);
    328 	(void)printf("get another guess!\n");
    329 }
    330 
    331 void
    332 chkwinner(player, hand)
    333 	int player;
    334 	const int *hand;
    335 {
    336 	int cb, i, ub;
    337 
    338 	for (i = 0; i < RANKS; ++i)
    339 		if (hand[i] > 0 && hand[i] < CARDS)
    340 			return;
    341 	printplayer(player);
    342 	(void)printf("don't have any more cards!\n");
    343 	(void)printf("My books:");
    344 	cb = countbooks(comphand);
    345 	(void)printf("Your books:");
    346 	ub = countbooks(userhand);
    347 	(void)printf("\nI have %d, you have %d.\n", cb, ub);
    348 	if (ub > cb) {
    349 		(void)printf("\nYou win!!!\n");
    350 		if (nrandom(1024) == 0723)
    351 			(void)printf("Cheater, cheater, pumpkin eater!\n");
    352 	} else if (cb > ub) {
    353 		(void)printf("\nI win!!!\n");
    354 		if (nrandom(1024) == 0723)
    355 			(void)printf("Hah!  Stupid peasant!\n");
    356 	} else
    357 		(void)printf("\nTie!\n");
    358 	exit(0);
    359 }
    360 
    361 void
    362 printplayer(player)
    363 	int player;
    364 {
    365 	switch (player) {
    366 	case COMPUTER:
    367 		(void)printf("I ");
    368 		break;
    369 	case USER:
    370 		(void)printf("You ");
    371 		break;
    372 	}
    373 }
    374 
    375 void
    376 printhand(hand)
    377 	const int *hand;
    378 {
    379 	int book, i, j;
    380 
    381 	for (book = i = 0; i < RANKS; i++)
    382 		if (hand[i] < CARDS)
    383 			for (j = hand[i]; --j >= 0;)
    384 				PRC(i);
    385 		else
    386 			++book;
    387 	if (book) {
    388 		(void)printf(" + Book%s of", book > 1 ? "s" : "");
    389 		for (i = 0; i < RANKS; i++)
    390 			if (hand[i] == CARDS)
    391 				PRC(i);
    392 	}
    393 	(void)putchar('\n');
    394 }
    395 
    396 int
    397 countcards(hand)
    398 	const int *hand;
    399 {
    400 	int i, count;
    401 
    402 	for (count = i = 0; i < RANKS; i++)
    403 		count += *hand++;
    404 	return(count);
    405 }
    406 
    407 int
    408 countbooks(hand)
    409 	const int *hand;
    410 {
    411 	int i, count;
    412 
    413 	for (count = i = 0; i < RANKS; i++)
    414 		if (hand[i] == CARDS) {
    415 			++count;
    416 			PRC(i);
    417 		}
    418 	if (!count)
    419 		(void)printf(" none");
    420 	(void)putchar('\n');
    421 	return(count);
    422 }
    423 
    424 void
    425 init()
    426 {
    427 	int i, rank;
    428 
    429 	for (i = 0; i < RANKS; ++i)
    430 		deck[i] = CARDS;
    431 	for (i = 0; i < HANDSIZE; ++i) {
    432 		while (!deck[rank = nrandom(RANKS)]);
    433 		++userhand[rank];
    434 		--deck[rank];
    435 	}
    436 	for (i = 0; i < HANDSIZE; ++i) {
    437 		while (!deck[rank = nrandom(RANKS)]);
    438 		++comphand[rank];
    439 		--deck[rank];
    440 	}
    441 }
    442 
    443 int
    444 nrandom(n)
    445 	int n;
    446 {
    447 
    448 	return((int)random() % n);
    449 }
    450 
    451 void
    452 instructions()
    453 {
    454 	int input;
    455 	pid_t pid;
    456 	int fd;
    457 	const char *pager;
    458 	int status;
    459 
    460 	(void)printf("Would you like instructions (y or n)? ");
    461 	input = getchar();
    462 	while (getchar() != '\n');
    463 	if (input != 'y')
    464 		return;
    465 
    466 	switch (pid = fork()) {
    467 	case 0: /* child */
    468 		if (!isatty(1))
    469 			pager = "cat";
    470 		else {
    471 			if (!(pager = getenv("PAGER")) || (*pager == 0))
    472 				pager = _PATH_MORE;
    473 		}
    474 		if ((fd = open(_PATH_INSTR, O_RDONLY)) == -1)
    475 			err(1, "open %s", _PATH_INSTR);
    476 		if (dup2(fd, 0) == -1)
    477 			err(1, "dup2");
    478 		(void)execl("/bin/sh", "sh", "-c", pager, NULL);
    479 		err(1, "exec sh -c %s", pager);
    480 		/*NOTREACHED*/
    481 	case -1:
    482 		err(1, "fork");
    483 		/*NOTREACHED*/
    484 	default:
    485 		(void)waitpid(pid, &status, 0);
    486 		break;
    487 	}
    488 	(void)printf("Hit return to continue...\n");
    489 	while ((input = getchar()) != EOF && input != '\n');
    490 }
    491 
    492 void
    493 usage()
    494 {
    495 	(void)fprintf(stderr, "usage: fish [-p]\n");
    496 	exit(1);
    497 }
    498