--- /dev/null
+.\" $NetBSD: fish.6,v 1.8 2003/08/07 09:37:13 agc Exp $
+.\"
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fish.6 8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt FISH 6
+.Os
+.Sh NAME
+.Nm fish
+.Nd play
+.Dq Go Fish
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Sh DESCRIPTION
+.Nm
+is the game
+.Dq Go Fish ,
+a traditional children's card game.
+.Pp
+The computer deals the player and itself seven cards, and places
+the rest of the deck face-down (figuratively).
+The object of the game is to collect
+.Dq books ,
+or all of the members of a single rank.
+For example, collecting four 2's would give the player a
+.Dq book of 2's .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl p
+Professional mode.
+.El
+.Pp
+The computer makes a random decision as to who gets to start the
+game, and then the computer and player take turns asking each other
+for cards of a specified rank.
+If the asked player has any cards of the requested rank, they give
+them up to the asking player.
+A player must have at least one of the cards of the rank they request
+in their hand.
+When a player asks for a rank of which the other player has no
+cards, the asker is told to
+.Dq Go Fish! .
+Then, the asker draws a card from the non-dealt cards.
+If they draw the card they asked for, they continue their turn, asking
+for more ranks from the other player.
+Otherwise, the other player gets a turn.
+.Pp
+When a player completes a book, either by getting cards from the
+other player or drawing from the deck, they set those cards aside and
+the rank is no longer in play.
+.Pp
+The game ends when either player no longer has any cards in their hand.
+The player with the most books wins.
+.Pp
+.Nm
+provides instructions as to what input it accepts.
+.Sh BUGS
+The computer cheats only rarely.
--- /dev/null
+/* $NetBSD: fish.c,v 1.22 2011/09/01 07:18:50 plunky Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Muffy Barkocy.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1990, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)fish.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: fish.c,v 1.22 2011/09/01 07:18:50 plunky Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <err.h>
+#include "pathnames.h"
+
+#define RANKS 13
+#define HANDSIZE 7
+#define CARDS 4
+#define TOTCARDS RANKS * CARDS
+
+#define USER 1
+#define COMPUTER 0
+#define OTHER(a) (1 - (a))
+
+static const char *const cards[] = {
+ "A", "2", "3", "4", "5", "6", "7",
+ "8", "9", "10", "J", "Q", "K", NULL,
+};
+#define PRC(card) (void)printf(" %s", cards[card])
+
+static int promode;
+static int asked[RANKS], comphand[RANKS], deck[TOTCARDS];
+static int userasked[RANKS], userhand[RANKS];
+static int curcard = TOTCARDS;
+
+static void chkwinner(int, const int *);
+static int compmove(void);
+static int countbooks(const int *);
+static int countcards(const int *);
+static int drawcard(int, int *);
+static int gofish(int, int, int *);
+static void goodmove(int, int, int *, int *);
+static void init(void);
+static void instructions(void);
+static int nrandom(int);
+static void printhand(const int *);
+static void printplayer(int);
+static int promove(void);
+static void usage(void) __dead;
+static int usermove(void);
+
+int
+main(int argc, char **argv)
+{
+ int ch, move;
+
+ /* Revoke setgid privileges */
+ setgid(getgid());
+
+ while ((ch = getopt(argc, argv, "p")) != -1)
+ switch(ch) {
+ case 'p':
+ promode = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ srandom(time(NULL));
+ instructions();
+ init();
+
+ if (nrandom(2) == 1) {
+ printplayer(COMPUTER);
+ (void)printf("get to start.\n");
+ goto istart;
+ }
+ printplayer(USER);
+ (void)printf("get to start.\n");
+
+ for (;;) {
+ move = usermove();
+ if (!comphand[move]) {
+ if (gofish(move, USER, userhand))
+ continue;
+ } else {
+ goodmove(USER, move, userhand, comphand);
+ continue;
+ }
+
+istart: for (;;) {
+ move = compmove();
+ if (!userhand[move]) {
+ if (!gofish(move, COMPUTER, comphand))
+ break;
+ } else
+ goodmove(COMPUTER, move, comphand, userhand);
+ }
+ }
+ /* NOTREACHED */
+}
+
+static int
+usermove(void)
+{
+ int n;
+ const char *const *p;
+ char buf[256];
+
+ (void)printf("\nYour hand is:");
+ printhand(userhand);
+
+ for (;;) {
+ (void)printf("You ask me for: ");
+ (void)fflush(stdout);
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ exit(0);
+ if (buf[0] == '\0')
+ continue;
+ if (buf[0] == '\n') {
+ (void)printf("%d cards in my hand, %d in the pool.\n",
+ countcards(comphand), curcard);
+ (void)printf("My books:");
+ (void)countbooks(comphand);
+ continue;
+ }
+ buf[strlen(buf) - 1] = '\0';
+ if (!strcasecmp(buf, "p") && !promode) {
+ promode = 1;
+ (void)printf("Entering pro mode.\n");
+ continue;
+ }
+ if (!strcasecmp(buf, "quit"))
+ exit(0);
+ for (p = cards; *p; ++p)
+ if (!strcasecmp(*p, buf))
+ break;
+ if (!*p) {
+ (void)printf("I don't understand!\n");
+ continue;
+ }
+ n = p - cards;
+ if (userhand[n]) {
+ userasked[n] = 1;
+ return(n);
+ }
+ if (nrandom(3) == 1)
+ (void)printf("You don't have any of those!\n");
+ else
+ (void)printf("You don't have any %s's!\n", cards[n]);
+ if (nrandom(4) == 1)
+ (void)printf("No cheating!\n");
+ (void)printf("Guess again.\n");
+ }
+ /* NOTREACHED */
+}
+
+static int
+compmove(void)
+{
+ static int lmove;
+
+ if (promode)
+ lmove = promove();
+ else {
+ do {
+ lmove = (lmove + 1) % RANKS;
+ } while (!comphand[lmove] || comphand[lmove] == CARDS);
+ }
+ asked[lmove] = 1;
+
+ (void)printf("I ask you for: %s.\n", cards[lmove]);
+ return(lmove);
+}
+
+static int
+promove(void)
+{
+ int i, max;
+
+ for (i = 0; i < RANKS; ++i)
+ if (userasked[i] &&
+ comphand[i] > 0 && comphand[i] < CARDS) {
+ userasked[i] = 0;
+ return(i);
+ }
+ if (nrandom(3) == 1) {
+ for (i = 0;; ++i)
+ if (comphand[i] && comphand[i] != CARDS) {
+ max = i;
+ break;
+ }
+ while (++i < RANKS)
+ if (comphand[i] != CARDS &&
+ comphand[i] > comphand[max])
+ max = i;
+ return(max);
+ }
+ if (nrandom(1024) == 0723) {
+ for (i = 0; i < RANKS; ++i)
+ if (userhand[i] && comphand[i])
+ return(i);
+ }
+ for (;;) {
+ for (i = 0; i < RANKS; ++i)
+ if (comphand[i] && comphand[i] != CARDS &&
+ !asked[i])
+ return(i);
+ for (i = 0; i < RANKS; ++i)
+ asked[i] = 0;
+ }
+ /* NOTREACHED */
+}
+
+static int
+drawcard(int player, int *hand)
+{
+ int card;
+
+ ++hand[card = deck[--curcard]];
+ if (player == USER || hand[card] == CARDS) {
+ printplayer(player);
+ (void)printf("drew %s", cards[card]);
+ if (hand[card] == CARDS) {
+ (void)printf(" and made a book of %s's!\n",
+ cards[card]);
+ chkwinner(player, hand);
+ } else
+ (void)printf(".\n");
+ }
+ return(card);
+}
+
+static int
+gofish(int askedfor, int player, int *hand)
+{
+ printplayer(OTHER(player));
+ (void)printf("say \"GO FISH!\"\n");
+ if (askedfor == drawcard(player, hand)) {
+ printplayer(player);
+ (void)printf("drew the guess!\n");
+ printplayer(player);
+ (void)printf("get to ask again!\n");
+ return(1);
+ }
+ return(0);
+}
+
+static void
+goodmove(int player, int move, int *hand, int *opphand)
+{
+ printplayer(OTHER(player));
+ (void)printf("have %d %s%s.\n",
+ opphand[move], cards[move], opphand[move] == 1 ? "": "'s");
+
+ hand[move] += opphand[move];
+ opphand[move] = 0;
+
+ if (hand[move] == CARDS) {
+ printplayer(player);
+ (void)printf("made a book of %s's!\n", cards[move]);
+ chkwinner(player, hand);
+ }
+
+ chkwinner(OTHER(player), opphand);
+
+ printplayer(player);
+ (void)printf("get another guess!\n");
+}
+
+static void
+chkwinner(int player, const int *hand)
+{
+ int cb, i, ub;
+
+ for (i = 0; i < RANKS; ++i)
+ if (hand[i] > 0 && hand[i] < CARDS)
+ return;
+ printplayer(player);
+ (void)printf("don't have any more cards!\n");
+ (void)printf("My books:");
+ cb = countbooks(comphand);
+ (void)printf("Your books:");
+ ub = countbooks(userhand);
+ (void)printf("\nI have %d, you have %d.\n", cb, ub);
+ if (ub > cb) {
+ (void)printf("\nYou win!!!\n");
+ if (nrandom(1024) == 0723)
+ (void)printf("Cheater, cheater, pumpkin eater!\n");
+ } else if (cb > ub) {
+ (void)printf("\nI win!!!\n");
+ if (nrandom(1024) == 0723)
+ (void)printf("Hah! Stupid peasant!\n");
+ } else
+ (void)printf("\nTie!\n");
+ exit(0);
+}
+
+static void
+printplayer(int player)
+{
+ switch (player) {
+ case COMPUTER:
+ (void)printf("I ");
+ break;
+ case USER:
+ (void)printf("You ");
+ break;
+ }
+}
+
+static void
+printhand(const int *hand)
+{
+ int book, i, j;
+
+ for (book = i = 0; i < RANKS; i++)
+ if (hand[i] < CARDS)
+ for (j = hand[i]; --j >= 0;)
+ PRC(i);
+ else
+ ++book;
+ if (book) {
+ (void)printf(" + Book%s of", book > 1 ? "s" : "");
+ for (i = 0; i < RANKS; i++)
+ if (hand[i] == CARDS)
+ PRC(i);
+ }
+ (void)putchar('\n');
+}
+
+static int
+countcards(const int *hand)
+{
+ int i, count;
+
+ for (count = i = 0; i < RANKS; i++)
+ count += *hand++;
+ return(count);
+}
+
+static int
+countbooks(const int *hand)
+{
+ int i, count;
+
+ for (count = i = 0; i < RANKS; i++)
+ if (hand[i] == CARDS) {
+ ++count;
+ PRC(i);
+ }
+ if (!count)
+ (void)printf(" none");
+ (void)putchar('\n');
+ return(count);
+}
+
+static void
+init(void)
+{
+ int i, j, temp;
+
+ for (i = 0; i < TOTCARDS; ++i)
+ deck[i] = i % RANKS;
+ for (i = 0; i < TOTCARDS - 1; ++i) {
+ j = nrandom(TOTCARDS-i);
+ if (j == 0)
+ continue;
+ temp = deck[i];
+ deck[i] = deck[i+j];
+ deck[i+j] = temp;
+ }
+ for (i = 0; i < HANDSIZE; ++i) {
+ ++userhand[deck[--curcard]];
+ ++comphand[deck[--curcard]];
+ }
+}
+
+static int
+nrandom(int n)
+{
+
+ return((int)random() % n);
+}
+
+static void
+instructions(void)
+{
+ int input;
+ pid_t pid;
+ int fd;
+ const char *pager;
+ int status;
+
+ (void)printf("Would you like instructions (y or n)? ");
+ input = getchar();
+ while (getchar() != '\n');
+ if (input != 'y')
+ return;
+
+ switch (pid = fork()) {
+ case 0: /* child */
+ if (!isatty(1))
+ pager = "cat";
+ else {
+ if (!(pager = getenv("PAGER")) || (*pager == 0))
+ pager = _PATH_MORE;
+ }
+ if ((fd = open(_PATH_INSTR, O_RDONLY)) == -1)
+ err(1, "open %s", _PATH_INSTR);
+ if (dup2(fd, 0) == -1)
+ err(1, "dup2");
+ (void)execl("/bin/sh", "sh", "-c", pager, (char *) NULL);
+ err(1, "exec sh -c %s", pager);
+ /*NOTREACHED*/
+ case -1:
+ err(1, "fork");
+ /*NOTREACHED*/
+ default:
+ (void)waitpid(pid, &status, 0);
+ break;
+ }
+ (void)printf("Hit return to continue...\n");
+ while ((input = getchar()) != EOF && input != '\n');
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: fish [-p]\n");
+ exit(1);
+}