1 /* $OpenBSD: fish.c,v 1.15 2009/10/27 23:59:24 deraadt 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/types.h> 37 #include <sys/wait.h> 38 #include <err.h> 39 #include <fcntl.h> 40 #include <paths.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <string.h> 45 #include <time.h> 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 void usage(void); 83 int usermove(void); 84 85 int 86 main(int argc, char *argv[]) 87 { 88 int ch, move; 89 90 while ((ch = getopt(argc, argv, "ph")) != -1) 91 switch(ch) { 92 case 'p': 93 promode = 1; 94 break; 95 case '?': 96 case 'h': 97 default: 98 usage(); 99 } 100 101 srandomdev(); 102 instructions(); 103 init(); 104 105 if (nrandom(2) == 1) { 106 printplayer(COMPUTER); 107 (void)printf("get to start.\n"); 108 goto istart; 109 } 110 printplayer(USER); 111 (void)printf("get to start.\n"); 112 113 for (;;) { 114 move = usermove(); 115 if (!comphand[move]) { 116 if (gofish(move, USER, userhand)) 117 continue; 118 } else { 119 goodmove(USER, move, userhand, comphand); 120 continue; 121 } 122 123 istart: for (;;) { 124 move = compmove(); 125 if (!userhand[move]) { 126 if (!gofish(move, COMPUTER, comphand)) 127 break; 128 } else 129 goodmove(COMPUTER, move, comphand, userhand); 130 } 131 } 132 /* NOTREACHED */ 133 } 134 135 int 136 usermove(void) 137 { 138 int n; 139 const char *const *p; 140 char buf[256]; 141 142 (void)printf("\nYour hand is:"); 143 printhand(userhand); 144 145 for (;;) { 146 (void)printf("You ask me for: "); 147 (void)fflush(stdout); 148 if (fgets(buf, sizeof(buf), stdin) == NULL) 149 exit(0); 150 if (buf[0] == '\0') 151 continue; 152 if (buf[0] == '\n') { 153 (void)printf("%d cards in my hand, %d in the pool.\n", 154 countcards(comphand), curcard); 155 (void)printf("My books:"); 156 (void)countbooks(comphand); 157 continue; 158 } 159 buf[strlen(buf) - 1] = '\0'; 160 if (!strcasecmp(buf, "p") && !promode) { 161 promode = 1; 162 (void)printf("Entering pro mode.\n"); 163 continue; 164 } 165 if (!strcasecmp(buf, "quit")) 166 exit(0); 167 for (p = cards; *p; ++p) 168 if (!strcasecmp(*p, buf)) 169 break; 170 if (!*p) { 171 (void)printf("I don't understand!\n"); 172 continue; 173 } 174 n = p - cards; 175 if (userhand[n]) { 176 userasked[n] = 1; 177 return(n); 178 } 179 if (nrandom(3) == 1) 180 (void)printf("You don't have any of those!\n"); 181 else 182 (void)printf("You don't have any %s's!\n", cards[n]); 183 if (nrandom(4) == 1) 184 (void)printf("No cheating!\n"); 185 (void)printf("Guess again.\n"); 186 } 187 /* NOTREACHED */ 188 } 189 190 int 191 compmove(void) 192 { 193 static int lmove; 194 195 if (promode) 196 lmove = promove(); 197 else { 198 do { 199 lmove = (lmove + 1) % RANKS; 200 } while (!comphand[lmove] || comphand[lmove] == CARDS); 201 } 202 asked[lmove] = 1; 203 204 (void)printf("I ask you for: %s.\n", cards[lmove]); 205 return(lmove); 206 } 207 208 int 209 promove(void) 210 { 211 int i, max; 212 213 for (i = 0; i < RANKS; ++i) 214 if (userasked[i] && 215 comphand[i] > 0 && comphand[i] < CARDS) { 216 userasked[i] = 0; 217 return(i); 218 } 219 if (nrandom(3) == 1) { 220 for (i = 0;; ++i) 221 if (comphand[i] && comphand[i] != CARDS) { 222 max = i; 223 break; 224 } 225 while (++i < RANKS) 226 if (comphand[i] != CARDS && 227 comphand[i] > comphand[max]) 228 max = i; 229 return(max); 230 } 231 if (nrandom(1024) == 0723) { 232 for (i = 0; i < RANKS; ++i) 233 if (userhand[i] && comphand[i]) 234 return(i); 235 } 236 for (;;) { 237 for (i = 0; i < RANKS; ++i) 238 if (comphand[i] && comphand[i] != CARDS && 239 !asked[i]) 240 return(i); 241 for (i = 0; i < RANKS; ++i) 242 asked[i] = 0; 243 } 244 /* NOTREACHED */ 245 } 246 247 int 248 drawcard(int player, int *hand) 249 { 250 int card; 251 252 ++hand[card = deck[--curcard]]; 253 if (player == USER || hand[card] == CARDS) { 254 printplayer(player); 255 (void)printf("drew %s", cards[card]); 256 if (hand[card] == CARDS) { 257 (void)printf(" and made a book of %s's!\n", 258 cards[card]); 259 chkwinner(player, hand); 260 } else 261 (void)printf(".\n"); 262 } 263 return(card); 264 } 265 266 int 267 gofish(int askedfor, int player, int *hand) 268 { 269 printplayer(OTHER(player)); 270 (void)printf("say \"GO FISH!\"\n"); 271 if (askedfor == drawcard(player, hand)) { 272 printplayer(player); 273 (void)printf("drew the guess!\n"); 274 printplayer(player); 275 (void)printf("get to ask again!\n"); 276 return(1); 277 } 278 return(0); 279 } 280 281 void 282 goodmove(int player, int move, int *hand, int *opphand) 283 { 284 printplayer(OTHER(player)); 285 (void)printf("have %d %s%s.\n", 286 opphand[move], cards[move], opphand[move] == 1 ? "": "'s"); 287 288 hand[move] += opphand[move]; 289 opphand[move] = 0; 290 291 if (hand[move] == CARDS) { 292 printplayer(player); 293 (void)printf("made a book of %s's!\n", cards[move]); 294 chkwinner(player, hand); 295 } 296 297 chkwinner(OTHER(player), opphand); 298 299 printplayer(player); 300 (void)printf("get another guess!\n"); 301 } 302 303 void 304 chkwinner(int player, const int *hand) 305 { 306 int cb, i, ub; 307 308 for (i = 0; i < RANKS; ++i) 309 if (hand[i] > 0 && hand[i] < CARDS) 310 return; 311 printplayer(player); 312 (void)printf("don't have any more cards!\n"); 313 (void)printf("My books:"); 314 cb = countbooks(comphand); 315 (void)printf("Your books:"); 316 ub = countbooks(userhand); 317 (void)printf("\nI have %d, you have %d.\n", cb, ub); 318 if (ub > cb) { 319 (void)printf("\nYou win!!!\n"); 320 if (nrandom(1024) == 0723) 321 (void)printf("Cheater, cheater, pumpkin eater!\n"); 322 } else if (cb > ub) { 323 (void)printf("\nI win!!!\n"); 324 if (nrandom(1024) == 0723) 325 (void)printf("Hah! Stupid peasant!\n"); 326 } else 327 (void)printf("\nTie!\n"); 328 exit(0); 329 } 330 331 void 332 printplayer(int player) 333 { 334 switch (player) { 335 case COMPUTER: 336 (void)printf("I "); 337 break; 338 case USER: 339 (void)printf("You "); 340 break; 341 } 342 } 343 344 void 345 printhand(const int *hand) 346 { 347 int book, i, j; 348 349 for (book = i = 0; i < RANKS; i++) 350 if (hand[i] < CARDS) 351 for (j = hand[i]; --j >= 0;) 352 PRC(i); 353 else 354 ++book; 355 if (book) { 356 (void)printf(" + Book%s of", book > 1 ? "s" : ""); 357 for (i = 0; i < RANKS; i++) 358 if (hand[i] == CARDS) 359 PRC(i); 360 } 361 (void)putchar('\n'); 362 } 363 364 int 365 countcards(const int *hand) 366 { 367 int i, count; 368 369 for (count = i = 0; i < RANKS; i++) 370 count += *hand++; 371 return(count); 372 } 373 374 int 375 countbooks(const int *hand) 376 { 377 int i, count; 378 379 for (count = i = 0; i < RANKS; i++) 380 if (hand[i] == CARDS) { 381 ++count; 382 PRC(i); 383 } 384 if (!count) 385 (void)printf(" none"); 386 (void)putchar('\n'); 387 return(count); 388 } 389 390 void 391 init(void) 392 { 393 int i, j, temp; 394 395 curcard = TOTCARDS; 396 for (i = 0; i < TOTCARDS; ++i) 397 deck[i] = i % RANKS; 398 for (i = 0; i < TOTCARDS - 1; ++i) { 399 j = nrandom(TOTCARDS-i); 400 if (j == 0) 401 continue; 402 temp = deck[i]; 403 deck[i] = deck[i+j]; 404 deck[i+j] = temp; 405 } 406 for (i = 0; i < HANDSIZE; ++i) { 407 ++userhand[deck[--curcard]]; 408 ++comphand[deck[--curcard]]; 409 } 410 } 411 412 int 413 nrandom(int n) 414 { 415 return((int)random() % n); 416 } 417 418 int 419 getans(const char *prompt) 420 { 421 char buf[20]; 422 423 /* 424 * simple routine to ask the yes/no question specified until the user 425 * answers yes or no, then return 1 if they said 'yes' and 0 if they 426 * answered 'no'. 427 */ 428 for (;;) { 429 (void)printf("%s", prompt); 430 (void)fflush(stdout); 431 if (!fgets(buf, sizeof(buf), stdin)) { 432 (void)printf("\n"); 433 exit(0); 434 } 435 if (*buf == 'N' || *buf == 'n') 436 return(0); 437 if (*buf == 'Y' || *buf == 'y') 438 return(1); 439 (void)printf( 440 "I don't understand your answer; please enter 'y' or 'n'!\n"); 441 } 442 /* NOTREACHED */ 443 } 444 445 void 446 instructions(void) 447 { 448 const char *pager; 449 pid_t pid; 450 int status; 451 int input; 452 int fd; 453 454 if (getans("Would you like instructions (y or n)? ") == 0) 455 return; 456 457 if ((fd = open(_PATH_INSTR, O_RDONLY)) == -1) 458 (void)printf("No instruction file found!\n"); 459 else { 460 switch (pid = fork()) { 461 case 0: /* child */ 462 if (!isatty(1)) 463 pager = "/bin/cat"; 464 else { 465 if (!(pager = getenv("PAGER")) || (*pager == 0)) 466 pager = _PATH_MORE; 467 } 468 if (dup2(fd, 0) == -1) 469 err(1, "dup2"); 470 (void)execl(_PATH_BSHELL, "sh", "-c", pager, (char *)NULL); 471 err(1, "exec sh -c %s", pager); 472 /* NOT REACHED */ 473 case -1: 474 err(1, "fork"); 475 /* NOT REACHED */ 476 default: 477 (void)waitpid(pid, &status, 0); 478 close(fd); 479 break; 480 } 481 } 482 483 (void)printf("Hit return to continue...\n"); 484 while ((input = getchar()) != EOF && input != '\n'); 485 } 486 487 void 488 usage(void) 489 { 490 (void)fprintf(stderr, "usage: fish [-p]\n"); 491 exit(1); 492 } 493