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