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