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