Path: uunet!tektronix!tekgen!tekred!games-request
From: games-request@tekred.TEK.COM
Newsgroups: comp.sources.games
Subject: v02i065: cg - game of chess guess
Message-ID: <1751@tekred.TEK.COM>
Date: 27 Oct 87 19:55:45 GMT
Sender: billr@tekred.TEK.COM
Lines: 753
Approved: billr@tekred.TEK.COM
Submitted by: unicom!physh (Jon Foreman)
Comp.sources.games: Volume 2, Issue 65
Archive-name: cg
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh README <<'END_OF_README'
XCg plays the game of chess guess, which was described in one of Martin
XGardener's famous articles on recreational mathematics. The game starts
Xout with a standard 8 X 8 chess board with 5 pieces on it,
Xa King, a Queen, a Bishop, a Knight, and a Rook. The pieces
Xare all disguised. But it isn't really a guessing game. You may query
Xsquares on the board for how may pieces attack that square. From this
Xinformation you can deduce which pieces are which. The object of the
Xgame is to minimize the number of squares queried in order to find out
Xthe identity of all the pieces.
X
XThis should be a complete package. You should just be
Xable to make it and the correct things should happen. Mostly,
Xthe instructions are in the manual file. There are no obvious
Xcompile time options, except to invoke the optimizer.
END_OF_README
if test 815 -ne `wc -c makefile <<'END_OF_makefile'
Xall: cg.c
X cc -O -o cg cg.c -lcurses -ltermlib
END_OF_makefile
if test 47 -ne `wc -c cg.6 <<'END_OF_cg.6'
X.. To view this file in a sensible manner, use "nroff -man cg.6 | more"
X..
X.TH CG 6L "Sep 24, 1987"
X.UC 4
X.SH NAME
Xcg \- play the chess guess game
X.SH SYNOPSIS
X.B /usr/games/cg
X[-n]
X.SH DESCRIPTION
X.I Cg
Xplays the game of chess guess, which was described in one of Martin
XGardener's famous articles on recreational mathematics. The game starts
Xout with a standard 8 X 8 chess board with 5 pieces on it,
Xa King, a Queen, a Bishop, a Knight, and a Rook. The pieces
Xare all disguised. But it isn't really a guessing game. You may query
Xsquares on the board for how may pieces attack that square. From this
Xinformation you can deduce which pieces are which. The object of the
Xgame is to minimize the number of squares queried in order to find out
Xthe identity of all the pieces.
X.PP
XWhen the game starts up there is a help menu and a reminder on the right
Xhand side of the screen, and the game board is to the left. To query
Xa blank square for attacks, you move the to the square with the standard
Xemacs(1?) or vi(1) cursor movement keys, and hit the space bar. The number
Xof pieces that can attack that square will be displayed. To make a guess
Xat what piece is at a certain square, you move to the square with the
Xpiece on it, and type G (for guess) and they letter that represents
Xthe piece you think it is: K for king, Q for queen, B for Bishop, N
Xfor Knight, and R for Rook. If you are correct the piece will be displayed.
XAn individual game will end when you have guessed all the pieces, or have
Xquit. If you don't quit, and you finish the game, a list of statistics
Xwill be displayed at the top of the screen. It is rumored that any
Xlayout of the board can be successfully guessed with only two attack queries or
Xone attack query and one missed guess. It is certainly possible to guess all
Xthe pieces without making any attack queries, but it's probable that you
Xwon't succeed.
X.SH OPTIONS
XThere is only one option:
X.IP \-n
XDisplay a black on black (or white on white, depending on how you view it)
Xboard instead of the standard black/white arrangement. This can also
Xbe selected from inside the program.
X.SH AUTHOR
XJon Foreman
XCollege of Marin, in Kentfield, California.
XPlausible net addresses are: ...!ucbvax!dual!unicom!physh,
Xor ptsfa!unicom!physh or hplabs!well!unicom!physh or
Xkiller!rrm!ssbn!physh
X.SH "SEE ALSO"
Xcurses(3X),
X.I "Screen Updating and Cursor Movement Optimization:"
X.IR "A Library Package" ,
X.SH BUGS
XThere are
X.I no
Xbugs in
X.I my
Xcode, but there may be any number of undesirable features.
X.SH PORTABILITY
XOther than using curses(3X) for screen optimization, this code should
Xbe easily portable to any machine running UNIX(r). There are no particularly
Xmachine dependent things with the exception of "isupper()" and "islower()"
Xwhich rely on [A-Z] and [a-z] being contiguous.
X.SH PROBLEMS
XThere are other ways of implementing this program, and I leave it to
Xthe general body of students and others who wish to explore new and
Xdifferent ideas to implement them. Here are some suggestions. Instead
Xof displaying disguised pieces, don't display the pieces at all. This
Xmakes the game harder. Another alternative would be to put all the
Xnon pawn types of pieces on the board (1 King, 1 Queen, 2 Bishops (one on a
Xwhile diagonal, and one on a black), 2 Knights,
Xand 2 Rooks.) Still another would be to just display all the attacks, and
Xto divine the location and types of all the pieces from just this information.
XThe squares under the pieces should show the number of attacks on that piece.
X.SH NOTES
XThe code for this game has certain landmarks which people seem to
Xpick out every time. For instance, the 'for' loops in the nitemove()
Xfunction. In addition, I think that the program, while bigger, is
Xa better sampler for the use of curses(3X) than either twinkle or
Xlife, which are listed in the curses documents. At least the
Xhelp() function does expand on the use of windows, which is not covered
Xat all well in the curses documents that I have. In any case, I leave
Xthis program in the custody of the public domain, so that all may
Xbenefit. If you really enjoy it, send a thank you letter so that I
Xmay know that some of my effort has been of use.
END_OF_cg.6
if test 4200 -ne `wc -c cg.c <<'END_OF_cg.c'
X#define VERSION 13 /* displays as X.X or so */
X/*************************************************************************\
X* The all new super dooper good nifty chess guess. *
X* *
X* This game is dedicated to Martin Gardener, who inspired it with *
X* his wonderful articles on recreational mathematics. I hope someone *
X* someday takes up where he left off. *
X* *
X* Notes: I can't concieve of a UNIX system where this won't compile *
X* and run right out of the box, I'm really doing nothing particulary *
X* bizzare. Well, thats not strictly true, you do need to have termcap *
X* and curses. So for those that have, enjoy, those that don't, sorry. *
X* *
X* Modification History: *
X* started: 9/4/84 *
X* updated for the net: 4/7/86 *
X* updated again for the net, 9/19/87 *
X* updated 9/22/87 for bug fixes, and add version number *
X* *
X* The all important, super vicious copywrong message: *
X* Copyright 1984,86 and 87 by Jon Foreman, all rights reserved *
X* Permission is granted to distribute this code, or modify it, or use *
X* portions in other programs, but you must give me credit in either the *
X* program or documentation, and you must not remove the copyright from *
X* the source text of this program. You may not sell this program or *
X* source code for profit. *
X* (This is really an leagally backed ego booster for me, really). *
X* *
X* It would be really enjoyable to have this become part of the Berkeley *
X* or Bell unix distributions, hint, hint. *
X* *
X\*************************************************************************/
X
X#include
X#include
X
X#ifndef LINT
Xchar *copyleft = "Copyright 1987 by Jon Foreman, all rights reserved.";
X#endif
X
X#define T 1 /* TRUE */
X#define F 0 /* FALSE */
X
Xstruct bd {
X int b_attcnt; /* number of attacks on this cell */
X char b_pcetype; /* type piece type is on this cell */
X char b_shown; /* have we shown this cell yet? */
X} board[8][8];
X
Xint kingmove(), queenmove(), rookmove(), bishmove(), nitemove();
X
Xstruct pce {
X char *p_name;
X char p_type;
X int (*p_attfun)();
X int p_x, p_y;
X char p_guessed;
X} piece[5] = {
X {"King", 'K', kingmove, 0, 0, 0},
X {"Queen", 'Q', queenmove, 0, 0, 0}, /* queenmove is both bish & rook */
X {"Rook", 'R', rookmove, 0, 0, 0},
X {"Bishop", 'B', bishmove, 0, 0, 0},
X {"Knight", 'N', nitemove, 0, 0, 0}
X};
X
Xint bguess = 0, /* number of guessed pieces */
X tests = 0, /* number of attack tests */
X bw = 1; /* display black and white squares? */
X
X/*
X** If you haven't figured out what main is for yet, then you really
X** will have a great deal of trouble figuring out what the rest of this
X** code does. One note, though, single letter varibles are almost always
X** loop indexes or other short term temporaries.
X*/
Xmain (ac, av)
Xchar **av;
X{
X int endp();
X long time();
X int did = 0; /* have we seen the board yet? */
X
X srandom (getpid () ^ (int) time (0L)); /* I bet this isn't portable */
X
X/*
X** Just have to find a way to get command line options in!
X*/
X if (ac > 1 && av[1][0] == '-' && av[1][1] == 'n')
X bw = 0;
X
X initscr ();
X
X (void) signal (SIGINT, endp);
X (void) signal (SIGHUP, endp);
X
X nonl ();
X noecho ();
X crmode ();
X leaveok (stdscr, FALSE);
X scrollok (stdscr, FALSE);
X
X move (0, 0);
X printw ("Chess guess, version %2d.%1d", VERSION/10, VERSION%10);
X
X for ( ; ; did = 1) {
X initboard ();
X dispboard (did, bw);
X showpiece ();
X userinterface ();
X }
X /* NOT REACHED */
X}
X
X/*
X** Display the pieces on the gameboard
X*/
Xshowpiece ()
X{
X int i;
X
X for (i = 0; i < 5; i++) {
X move (piece[i].p_y*2+2, piece[i].p_x*4+3+2);
X addch (piece[i].p_guessed ? piece[i].p_type : '*');
X refresh ();
X }
X return;
X}
X
X/*
X** Initialize the play board. First clear all the cells, then place the
X** pieces, and then count attacks. Nothing to it.
X*/
Xinitboard ()
X{
X int i, j;
X
X bguess = tests = 0;
X
X for (i = 0; i < 8; i++)
X for (j = 0; j < 8; j++)
X
X board[i][j].b_pcetype = board[i][j].b_attcnt = board[i][j].b_shown = 0;
X
X
X/*
X** I suppose it is possible to get an infinite loop here, but there are
X** after all 64 squares on the board, and I'm only using 5. I suspect
X** that rnd() will eventually return 5 different pairs of x,y's.
X*/
X for (i = 0; i < 5; i++)
X for ( ;; )
X if (board[piece[i].p_x = rnd (8)][piece[i].p_y = rnd (8)].b_pcetype == 0) {
X board[piece[i].p_x][piece[i].p_y].b_pcetype = piece[i].p_type;
X break;
X }
X
X for (i = 0; i < 5; i++) {
X piece[i].p_guessed = 0;
X piece[i].p_attfun (&piece[i]);
X }
X return;
X}
X
X/*
X** Come here whenever we want to stop playing, and unsetup all the stuff
X** that got set up.
X*/
Xendp ()
X{
X (void) signal (SIGINT, SIG_IGN);
X (void) signal (SIGHUP, SIG_IGN);
X mvcur (0, COLS-1, LINES-1, 0); /* cleveland */
X endwin ();
X exit (0);
X}
X
X/*
X** Display the playfield, and stuff like that.
X*/
Xdispboard (dispfix, bw)
Xint dispfix; /* just fixing the display (=1) */
Xint bw; /* want black & white squares (-1) */
X{
X int i, j, phase;
X
X if (!dispfix)
X mvaddstr (1, 40, "There are one each of these:");
X
X for (phase = i = 0; i <= 8; i++) {
X mvaddstr (i*2+1, 3, "+---+---+---+---+---+---+---+---+");
X if (i != 8) {
X move (i*2+2, 0);
X printw ("%2d", 8 - i);
X move (i*2+2, 3);
X
X for (j = 0; j <= 8; j++, phase = !phase) {
X addch ('|');
X if (j != 8) {
X addch ((bw && phase) ? '.' : ' ');
X if (board[j][i].b_shown)
X addch (board[j][i].b_pcetype ? board[j][i].b_pcetype : (board[j][i].b_attcnt + '0'));
X else if (board[j][i].b_pcetype != 0)
X addch ('*');
X else
X addch ((bw && phase) ? '.' : ' ');
X addch ((bw && phase) ? '.' : ' ');
X }
X }
X refresh ();
X }
X }
X
X if (dispfix) /* only fixing display board */
X return;
X
X mvaddstr (i*2, 5, "a b c d e f g h");
X refresh ();
X for (i = 0; i < 5; i++) {
X move (i+2, 40);
X printw ("%c = %s\n", piece[i].p_type, piece[i].p_name);
X refresh ();
X }
X mvaddstr ( 8, 40, "Commands:");
X mvaddstr ( 9, 40, "'Q' or is Quit");
X mvaddstr (10, 40, "'G' is Guess");
X mvaddstr (11, 40, "' ' is ask for attacks");
X mvaddstr (12, 40, "'h' or '^B' moves back");
X mvaddstr (13, 40, "'j' or '^N' moves down");
X mvaddstr (14, 40, "'k' or '^P' moves up");
X mvaddstr (15, 40, "'l' or '^F' moves forward");
X mvaddstr (16, 40, "'r' or '^L' redraws screen");
X mvaddstr (17, 40, "'t' to toggle black & white squares");
X mvaddstr (18, 40, "'?' to get help");
X refresh ();
X}
X
X/*
X** Kings can move one square in any direction.
X*/
Xkingmove (p)
Xstruct pce *p;
X{
X int i, j;
X
X for (i = -1; i <= 1; i++)
X for (j = -1; j <= 1; j++) {
X if (!onboard (p->p_x + i, p->p_y + j) || (!i && !j))
X continue;
X board[p->p_x + i][p->p_y + j].b_attcnt ++ ;
X }
X return;
X}
X
X/*
X** Knights can move over things, so just check to see if there is
X** anything at where we end up. Problem: figure out exactly what
X** the for(;;) loops do.
X*/
Xnitemove (p)
Xstruct pce *p;
X{
X int i, j;
X
X for (i = -2; i <= 2; i += (!++i) ? 1 : 0)
X for (j = 2; j >= -2; j += (!--j) ? -1 : 0) {
X if (abs (j) == abs (i))
X continue;
X if (!onboard (p->p_x + i, p->p_y + j))
X continue;
X board[p->p_x + i][p->p_y + j].b_attcnt ++ ;
X }
X return;
X}
X
X/*
X** Rooks can only move horizontally or vertically, and cannot move through
X** other pieces to get there
X*/
Xrookmove (p)
Xstruct pce *p;
X{
X /*
X ** radiate outward in the correct directions until we hit
X ** something. Like the end of the board, or another piece.
X */
X register int q;
X
X for (q = p->p_x - 1; onboard (q, p->p_y); --q)
X board[q][p->p_y].b_attcnt++;
X for (q = p->p_x + 1; onboard (q, p->p_y); ++q)
X board[q][p->p_y].b_attcnt++;
X for (q = p->p_y - 1; onboard (p->p_x, q); --q)
X board[p->p_x][q].b_attcnt++;
X for (q = p->p_y + 1; onboard (p->p_x, q); ++q)
X board[p->p_x][q].b_attcnt++;
X return;
X}
X
X/*
X** Bishops move diagonally
X*/
Xbishmove (p)
Xstruct pce *p;
X{
X /*
X ** radiate in the proper directions until you hit something.
X */
X int i;
X int done, a, b, c, d;
X
X for (a = b = c = d = T, done = 0, i = 1; !done; done = !(a|b|c|d), i++) {
X if (a)
X if (a = onboard (p->p_x + i, p->p_y + i))
X board[p->p_x + i][p->p_y + i].b_attcnt ++ ;
X if (b)
X if (b = onboard (p->p_x + i, p->p_y - i))
X board[p->p_x + i][p->p_y - i].b_attcnt ++ ;
X if (c)
X if (c = onboard (p->p_x - i, p->p_y + i))
X board[p->p_x - i][p->p_y + i].b_attcnt ++ ;
X if (d)
X if (d = onboard (p->p_x - i, p->p_y - i))
X board[p->p_x - i][p->p_y - i].b_attcnt ++ ;
X }
X return;
X}
X/*
X** Queens are just rooks | bishops
X*/
Xqueenmove (p)
Xstruct pce *p;
X{
X bishmove (p);
X rookmove (p);
X return;
X}
X
X/*
X** Make sure that we're still on the board someplace.
X*/
Xonboard (x, y)
Xregister int x, y;
X{
X if (x >= 8 || y >= 8 || x < 0 || y < 0 || board[x][y].b_pcetype)
X return (F);
X return (T);
X}
X
X/*
X** Simplified integer random number generator, taylor for this application
X*/
Xrnd (n)
Xint n;
X{
X long random();
X
X return ((unsigned) random() % (unsigned) n);
X}
X
X/*
X** MacIntosh inspired keyboard interface..., no wait, that a lie. Actually,
X** this one if fairly intuitional (what with the help on the screen and all)
X** and I suppose if I had a Sun I could even implement mouse functions.
X** But I don't, so I didn't. Just remember, all good programs have a
X** well designed userinterface().
X*/
Xuserinterface ()
X{
X char c;
X int x, y, ox, oy, gsd;
X
X x = y = gsd = 0;
X move (y*2+2, x*4+3+2);
X refresh ();
X
X for (;;) {
X c = tolower (getch ());
X getyx (stdscr, oy, ox);
X move (22, 0);
X clrtoeol ();
X move (oy, ox);
X refresh ();
X switch (c) {
X case 'h': case '\002':
X if (x > 0)
X --x;
X break;
X
X case 'j': case '\016':
X if (y++ > 6)
X --y;
X break;
X
X case 'k': case '\020':
X if (y > 0)
X --y;
X break;
X
X case 'l': case '\006':
X if (x++ > 6)
X --x;
X break;
X
X case ' ':
X if (board[x][y].b_shown)
X break;
X
X if (board[x][y].b_pcetype == 0) {
X printw ("%1d", board[x][y].b_attcnt);
X board[x][y].b_shown = 1;
X tests++;
X }
X break;
X
X case 'g': case 'G':
X if (board[x][y].b_shown)
X break;
X
X if (board[x][y].b_pcetype == 0)
X break;
X getyx (stdscr, oy, ox);
X mvaddstr (22, 3, "Piece? ");
X clrtoeol (stdscr);
X refresh ();
X echo ();
X c = getch ();
X refresh ();
X move (oy, ox);
X noecho ();
X if (toupper (c) == board[x][y].b_pcetype) {
X int q;
X
X addch (board[x][y].b_pcetype);
X board[x][y].b_shown = 1;
X refresh ();
X for (q = gsd = 0; q < 5; q++) {
X if (board[x][y].b_pcetype == piece[q].p_type) {
X gsd++;
X piece[q].p_guessed = 1;
X } else if (piece[q].p_guessed) {
X gsd++;
X }
X }
X if (gsd == 5) {
X win ();
X return;
X }
X } else {
X putchar ('\007');
X bguess++;
X }
X break;
X
X case 'q':
X endp ();
X
X case 'r': case '\014':
X wrefresh (curscr);
X break;
X
X case 't':
X dispboard (F, bw = !bw);
X refresh ();
X break;
X
X case '?':
X help();
X break;
X
X default: /* reminder */
X break;
X }
X move (y*2+2, x*4+3+2);
X refresh ();
X }
X}
X
X/*
X** I have troubles finding toupper on every machine I use, so I wrote my
X** own. I know, I know, "ctype.h" right. Wro. This is slower, but
X** at least I know everyone will have it.
X*/
Xtoupper (c)
Xchar c;
X{
X return ((c >= 'a' && c <= 'z') ? c -= '\040' : c);
X}
X
X/*
X** See comment for toupper (above). These routines also make more sense
X** in that we don't have to find out if islower() or isupper is true
X** or not first.
X*/
Xtolower (c)
Xchar c;
X{
X return ((c >= 'A' && c <= 'Z') ? c += '\040' : c);
X}
X
Xwin ()
X/*
X** Colors, someone won one! Hurray!.
X*/
X{
X char *buf[200];
X
X sprintf (buf, "YOU WIN! statistics: %d tests, %d bad guesses\n", tests, bguess);
X mvaddstr (0, 0, buf);
X mvaddstr (22, 3, "Go again? ");
X refresh ();
X if (getch () == 'n')
X endp ();
X move (0, 0);
X clrtoeol ();
X move (22, 0);
X clrtoeol ();
X return;
X}
X
X/*
X** Generate a help screen, and if nothing else, this is a good
X** example of how to create overlapping windows (like in rogue
X** and so forth
X*/
X#define HWXMAX 50
X#define HWYMAX 18
X
Xhelp ()
X{
X WINDOW *hwin;
X int curx, cury;
X
X getyx (stdscr, cury, curx);
X hwin = newwin (HWYMAX, HWXMAX, 0, 0);
X mybox (hwin, HWYMAX, HWXMAX);
X
X mvwaddstr (hwin,2,2," Chess guess is a game that was described in");
X mvwaddstr (hwin,3,2,"one of Martin Gardeners famous recreational");
X mvwaddstr (hwin,4,2,"mathematics articles for \"Scientific American\"");
X mvwaddstr (hwin,5,2,"magazine.");
X mvwaddstr (hwin,7,2," You are given a chess board with 5 pieces");
X mvwaddstr (hwin,8,2,"displayed. The pieces are disguised, and your");
X mvwaddstr (hwin,9,2,"mission (should you decide to accept it) is to");
X mvwaddstr (hwin,10,2,"guess which pieces are which. You can ask for");
X mvwaddstr (hwin,11,2,"help in the form of asking how many pieces can");
X mvwaddstr (hwin,12,2,"attack various squares on the board. The");
X mvwaddstr (hwin,13,2,"challenge is to pick squares that maximize");
X mvwaddstr (hwin,14,2,"your chance of guessing the correct pieces.");
X mvwaddstr (hwin,16,2," (when done reading, press space) ");
X wrefresh (hwin);
X getch ();
X
X delwin (hwin);
X move (cury, curx);
X touchwin (stdscr);
X refresh ();
X return;
X}
X
X/*
X** This is my box routine, here because the standard box() didn't really
X** do what I wanted. Note, it clear the window as it goes along.
X*/
Xmybox (hwin, ymax, xmax)
XWINDOW *hwin;
X{
X int y, x;
X
X for (y = 0; y < ymax; y++) {
X for (x = 0; x < xmax; x++)
X if (x == 0 && y == 0)
X mvwaddch (hwin, y, x, '/');
X else if (x == 0 && y == ymax-1)
X mvwaddch (hwin, y, x, '\\');
X else if (x == xmax-1 && y == 0)
X mvwaddch (hwin, y, x, '\\');
X else if (x == xmax-1 && y == ymax-1)
X mvwaddch (hwin, y, x, '/');
X else if (x > xmax-1 || y > ymax-1)
X mvwaddch (hwin, y, x, ' ');
X else if (y == 0)
X mvwaddch (hwin, y, x, '=');
X else if (y == ymax-1)
X mvwaddch (hwin, y, x, '-');
X else if (x == 0 || x == xmax-1)
X mvwaddch (hwin, y, x, '|');
X }
X wmove (hwin, 0, 2);
X waddstr (hwin, " * Help * ");
X return;
X}
END_OF_cg.c
if test 14136 -ne `wc -c