]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - fish/fish.c
1a9be58a5a603bbd5cd5f0383f253eb97b2a18b6
[bsdgames-darwin.git] / fish / fish.c
1 /* $NetBSD: fish.c,v 1.25 2021/05/02 12:24:59 rillig 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.25 2021/05/02 12:24:59 rillig 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 (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;
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 = getchar();
443 while (getchar() != '\n');
444 if (input != 'y')
445 return;
446
447 switch (pid = fork()) {
448 case 0: /* child */
449 if (!isatty(1))
450 pager = "cat";
451 else {
452 if (!(pager = getenv("PAGER")) || (*pager == 0))
453 pager = _PATH_MORE;
454 }
455 if ((fd = open(_PATH_INSTR, O_RDONLY)) == -1)
456 err(1, "open %s", _PATH_INSTR);
457 if (dup2(fd, 0) == -1)
458 err(1, "dup2");
459 (void)execl("/bin/sh", "sh", "-c", pager, (char *) NULL);
460 err(1, "exec sh -c %s", pager);
461 /*NOTREACHED*/
462 case -1:
463 err(1, "fork");
464 /*NOTREACHED*/
465 default:
466 (void)waitpid(pid, &status, 0);
467 break;
468 }
469 (void)printf("Hit return to continue...\n");
470 while ((input = getchar()) != EOF && input != '\n');
471 }
472
473 static void
474 usage(void)
475 {
476 (void)fprintf(stderr, "usage: fish [-p]\n");
477 exit(1);
478 }