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