|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T c
Length: 14136 (0x3738)
Types: TextFile
Names: »cg.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Chessguess/cg.c«
#define VERSION 13 /* displays as X.X or so */
/*************************************************************************\
* The all new super dooper good nifty chess guess. *
* *
* This game is dedicated to Martin Gardener, who inspired it with *
* his wonderful articles on recreational mathematics. I hope someone *
* someday takes up where he left off. *
* *
* Notes: I can't concieve of a UNIX system where this won't compile *
* and run right out of the box, I'm really doing nothing particulary *
* bizzare. Well, thats not strictly true, you do need to have termcap *
* and curses. So for those that have, enjoy, those that don't, sorry. *
* *
* Modification History: *
* started: 9/4/84 *
* updated for the net: 4/7/86 *
* updated again for the net, 9/19/87 *
* updated 9/22/87 for bug fixes, and add version number *
* *
* The all important, super vicious copywrong message: *
* Copyright 1984,86 and 87 by Jon Foreman, all rights reserved *
* Permission is granted to distribute this code, or modify it, or use *
* portions in other programs, but you must give me credit in either the *
* program or documentation, and you must not remove the copyright from *
* the source text of this program. You may not sell this program or *
* source code for profit. *
* (This is really an leagally backed ego booster for me, really). *
* *
* It would be really enjoyable to have this become part of the Berkeley *
* or Bell unix distributions, hint, hint. *
* *
\*************************************************************************/
#include <curses.h>
#include <signal.h>
#ifndef LINT
char *copyleft = "Copyright 1987 by Jon Foreman, all rights reserved.";
#endif
#define T 1 /* TRUE */
#define F 0 /* FALSE */
struct bd {
int b_attcnt; /* number of attacks on this cell */
char b_pcetype; /* type piece type is on this cell */
char b_shown; /* have we shown this cell yet? */
} board[8][8];
int kingmove(), queenmove(), rookmove(), bishmove(), nitemove();
struct pce {
char *p_name;
char p_type;
int (*p_attfun)();
int p_x, p_y;
char p_guessed;
} piece[5] = {
{"King", 'K', kingmove, 0, 0, 0},
{"Queen", 'Q', queenmove, 0, 0, 0}, /* queenmove is both bish & rook */
{"Rook", 'R', rookmove, 0, 0, 0},
{"Bishop", 'B', bishmove, 0, 0, 0},
{"Knight", 'N', nitemove, 0, 0, 0}
};
int bguess = 0, /* number of guessed pieces */
tests = 0, /* number of attack tests */
bw = 1; /* display black and white squares? */
/*
** If you haven't figured out what main is for yet, then you really
** will have a great deal of trouble figuring out what the rest of this
** code does. One note, though, single letter varibles are almost always
** loop indexes or other short term temporaries.
*/
main (ac, av)
char **av;
{
int endp();
long time();
int did = 0; /* have we seen the board yet? */
srandom (getpid () ^ (int) time (0L)); /* I bet this isn't portable */
/*
** Just have to find a way to get command line options in!
*/
if (ac > 1 && av[1][0] == '-' && av[1][1] == 'n')
bw = 0;
initscr ();
(void) signal (SIGINT, endp);
(void) signal (SIGHUP, endp);
nonl ();
noecho ();
crmode ();
leaveok (stdscr, FALSE);
scrollok (stdscr, FALSE);
move (0, 0);
printw ("Chess guess, version %2d.%1d", VERSION/10, VERSION%10);
for ( ; ; did = 1) {
initboard ();
dispboard (did, bw);
showpiece ();
userinterface ();
}
/* NOT REACHED */
}
/*
** Display the pieces on the gameboard
*/
showpiece ()
{
int i;
for (i = 0; i < 5; i++) {
move (piece[i].p_y*2+2, piece[i].p_x*4+3+2);
addch (piece[i].p_guessed ? piece[i].p_type : '*');
refresh ();
}
return;
}
/*
** Initialize the play board. First clear all the cells, then place the
** pieces, and then count attacks. Nothing to it.
*/
initboard ()
{
int i, j;
bguess = tests = 0;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
board[i][j].b_pcetype = board[i][j].b_attcnt = board[i][j].b_shown = 0;
/*
** I suppose it is possible to get an infinite loop here, but there are
** after all 64 squares on the board, and I'm only using 5. I suspect
** that rnd() will eventually return 5 different pairs of x,y's.
*/
for (i = 0; i < 5; i++)
for ( ;; )
if (board[piece[i].p_x = rnd (8)][piece[i].p_y = rnd (8)].b_pcetype == 0) {
board[piece[i].p_x][piece[i].p_y].b_pcetype = piece[i].p_type;
break;
}
for (i = 0; i < 5; i++) {
piece[i].p_guessed = 0;
piece[i].p_attfun (&piece[i]);
}
return;
}
/*
** Come here whenever we want to stop playing, and unsetup all the stuff
** that got set up.
*/
endp ()
{
(void) signal (SIGINT, SIG_IGN);
(void) signal (SIGHUP, SIG_IGN);
mvcur (0, COLS-1, LINES-1, 0); /* cleveland */
endwin ();
exit (0);
}
/*
** Display the playfield, and stuff like that.
*/
dispboard (dispfix, bw)
int dispfix; /* just fixing the display (=1) */
int bw; /* want black & white squares (-1) */
{
int i, j, phase;
if (!dispfix)
mvaddstr (1, 40, "There are one each of these:");
for (phase = i = 0; i <= 8; i++) {
mvaddstr (i*2+1, 3, "+---+---+---+---+---+---+---+---+");
if (i != 8) {
move (i*2+2, 0);
printw ("%2d", 8 - i);
move (i*2+2, 3);
for (j = 0; j <= 8; j++, phase = !phase) {
addch ('|');
if (j != 8) {
addch ((bw && phase) ? '.' : ' ');
if (board[j][i].b_shown)
addch (board[j][i].b_pcetype ? board[j][i].b_pcetype : (board[j][i].b_attcnt + '0'));
else if (board[j][i].b_pcetype != 0)
addch ('*');
else
addch ((bw && phase) ? '.' : ' ');
addch ((bw && phase) ? '.' : ' ');
}
}
refresh ();
}
}
if (dispfix) /* only fixing display board */
return;
mvaddstr (i*2, 5, "a b c d e f g h");
refresh ();
for (i = 0; i < 5; i++) {
move (i+2, 40);
printw ("%c = %s\n", piece[i].p_type, piece[i].p_name);
refresh ();
}
mvaddstr ( 8, 40, "Commands:");
mvaddstr ( 9, 40, "'Q' or <BREAK> is Quit");
mvaddstr (10, 40, "'G' is Guess");
mvaddstr (11, 40, "' ' is ask for attacks");
mvaddstr (12, 40, "'h' or '^B' moves back");
mvaddstr (13, 40, "'j' or '^N' moves down");
mvaddstr (14, 40, "'k' or '^P' moves up");
mvaddstr (15, 40, "'l' or '^F' moves forward");
mvaddstr (16, 40, "'r' or '^L' redraws screen");
mvaddstr (17, 40, "'t' to toggle black & white squares");
mvaddstr (18, 40, "'?' to get help");
refresh ();
}
/*
** Kings can move one square in any direction.
*/
kingmove (p)
struct pce *p;
{
int i, j;
for (i = -1; i <= 1; i++)
for (j = -1; j <= 1; j++) {
if (!onboard (p->p_x + i, p->p_y + j) || (!i && !j))
continue;
board[p->p_x + i][p->p_y + j].b_attcnt ++ ;
}
return;
}
/*
** Knights can move over things, so just check to see if there is
** anything at where we end up. Problem: figure out exactly what
** the for(;;) loops do.
*/
nitemove (p)
struct pce *p;
{
int i, j;
for (i = -2; i <= 2; i += (!++i) ? 1 : 0)
for (j = 2; j >= -2; j += (!--j) ? -1 : 0) {
if (abs (j) == abs (i))
continue;
if (!onboard (p->p_x + i, p->p_y + j))
continue;
board[p->p_x + i][p->p_y + j].b_attcnt ++ ;
}
return;
}
/*
** Rooks can only move horizontally or vertically, and cannot move through
** other pieces to get there
*/
rookmove (p)
struct pce *p;
{
/*
** radiate outward in the correct directions until we hit
** something. Like the end of the board, or another piece.
*/
register int q;
for (q = p->p_x - 1; onboard (q, p->p_y); --q)
board[q][p->p_y].b_attcnt++;
for (q = p->p_x + 1; onboard (q, p->p_y); ++q)
board[q][p->p_y].b_attcnt++;
for (q = p->p_y - 1; onboard (p->p_x, q); --q)
board[p->p_x][q].b_attcnt++;
for (q = p->p_y + 1; onboard (p->p_x, q); ++q)
board[p->p_x][q].b_attcnt++;
return;
}
/*
** Bishops move diagonally
*/
bishmove (p)
struct pce *p;
{
/*
** radiate in the proper directions until you hit something.
*/
int i;
int done, a, b, c, d;
for (a = b = c = d = T, done = 0, i = 1; !done; done = !(a|b|c|d), i++) {
if (a)
if (a = onboard (p->p_x + i, p->p_y + i))
board[p->p_x + i][p->p_y + i].b_attcnt ++ ;
if (b)
if (b = onboard (p->p_x + i, p->p_y - i))
board[p->p_x + i][p->p_y - i].b_attcnt ++ ;
if (c)
if (c = onboard (p->p_x - i, p->p_y + i))
board[p->p_x - i][p->p_y + i].b_attcnt ++ ;
if (d)
if (d = onboard (p->p_x - i, p->p_y - i))
board[p->p_x - i][p->p_y - i].b_attcnt ++ ;
}
return;
}
/*
** Queens are just rooks | bishops
*/
queenmove (p)
struct pce *p;
{
bishmove (p);
rookmove (p);
return;
}
/*
** Make sure that we're still on the board someplace.
*/
onboard (x, y)
register int x, y;
{
if (x >= 8 || y >= 8 || x < 0 || y < 0 || board[x][y].b_pcetype)
return (F);
return (T);
}
/*
** Simplified integer random number generator, taylor for this application
*/
rnd (n)
int n;
{
long random();
return ((unsigned) random() % (unsigned) n);
}
/*
** MacIntosh inspired keyboard interface..., no wait, that a lie. Actually,
** this one if fairly intuitional (what with the help on the screen and all)
** and I suppose if I had a Sun I could even implement mouse functions.
** But I don't, so I didn't. Just remember, all good programs have a
** well designed userinterface().
*/
userinterface ()
{
char c;
int x, y, ox, oy, gsd;
x = y = gsd = 0;
move (y*2+2, x*4+3+2);
refresh ();
for (;;) {
c = tolower (getch ());
getyx (stdscr, oy, ox);
move (22, 0);
clrtoeol ();
move (oy, ox);
refresh ();
switch (c) {
case 'h': case '\002':
if (x > 0)
--x;
break;
case 'j': case '\016':
if (y++ > 6)
--y;
break;
case 'k': case '\020':
if (y > 0)
--y;
break;
case 'l': case '\006':
if (x++ > 6)
--x;
break;
case ' ':
if (board[x][y].b_shown)
break;
if (board[x][y].b_pcetype == 0) {
printw ("%1d", board[x][y].b_attcnt);
board[x][y].b_shown = 1;
tests++;
}
break;
case 'g': case 'G':
if (board[x][y].b_shown)
break;
if (board[x][y].b_pcetype == 0)
break;
getyx (stdscr, oy, ox);
mvaddstr (22, 3, "Piece? ");
clrtoeol (stdscr);
refresh ();
echo ();
c = getch ();
refresh ();
move (oy, ox);
noecho ();
if (toupper (c) == board[x][y].b_pcetype) {
int q;
addch (board[x][y].b_pcetype);
board[x][y].b_shown = 1;
refresh ();
for (q = gsd = 0; q < 5; q++) {
if (board[x][y].b_pcetype == piece[q].p_type) {
gsd++;
piece[q].p_guessed = 1;
} else if (piece[q].p_guessed) {
gsd++;
}
}
if (gsd == 5) {
win ();
return;
}
} else {
putchar ('\007');
bguess++;
}
break;
case 'q':
endp ();
case 'r': case '\014':
wrefresh (curscr);
break;
case 't':
dispboard (F, bw = !bw);
refresh ();
break;
case '?':
help();
break;
default: /* reminder */
break;
}
move (y*2+2, x*4+3+2);
refresh ();
}
}
/*
** I have troubles finding toupper on every machine I use, so I wrote my
** own. I know, I know, "ctype.h" right. Wro. This is slower, but
** at least I know everyone will have it.
*/
toupper (c)
char c;
{
return ((c >= 'a' && c <= 'z') ? c -= '\040' : c);
}
/*
** See comment for toupper (above). These routines also make more sense
** in that we don't have to find out if islower() or isupper is true
** or not first.
*/
tolower (c)
char c;
{
return ((c >= 'A' && c <= 'Z') ? c += '\040' : c);
}
win ()
/*
** Colors, someone won one! Hurray!.
*/
{
char *buf[200];
sprintf (buf, "YOU WIN! statistics: %d tests, %d bad guesses\n", tests, bguess);
mvaddstr (0, 0, buf);
mvaddstr (22, 3, "Go again? ");
refresh ();
if (getch () == 'n')
endp ();
move (0, 0);
clrtoeol ();
move (22, 0);
clrtoeol ();
return;
}
/*
** Generate a help screen, and if nothing else, this is a good
** example of how to create overlapping windows (like in rogue
** and so forth
*/
#define HWXMAX 50
#define HWYMAX 18
help ()
{
WINDOW *hwin;
int curx, cury;
getyx (stdscr, cury, curx);
hwin = newwin (HWYMAX, HWXMAX, 0, 0);
mybox (hwin, HWYMAX, HWXMAX);
mvwaddstr (hwin,2,2," Chess guess is a game that was described in");
mvwaddstr (hwin,3,2,"one of Martin Gardeners famous recreational");
mvwaddstr (hwin,4,2,"mathematics articles for \"Scientific American\"");
mvwaddstr (hwin,5,2,"magazine.");
mvwaddstr (hwin,7,2," You are given a chess board with 5 pieces");
mvwaddstr (hwin,8,2,"displayed. The pieces are disguised, and your");
mvwaddstr (hwin,9,2,"mission (should you decide to accept it) is to");
mvwaddstr (hwin,10,2,"guess which pieces are which. You can ask for");
mvwaddstr (hwin,11,2,"help in the form of asking how many pieces can");
mvwaddstr (hwin,12,2,"attack various squares on the board. The");
mvwaddstr (hwin,13,2,"challenge is to pick squares that maximize");
mvwaddstr (hwin,14,2,"your chance of guessing the correct pieces.");
mvwaddstr (hwin,16,2," (when done reading, press space) ");
wrefresh (hwin);
getch ();
delwin (hwin);
move (cury, curx);
touchwin (stdscr);
refresh ();
return;
}
/*
** This is my box routine, here because the standard box() didn't really
** do what I wanted. Note, it clear the window as it goes along.
*/
mybox (hwin, ymax, xmax)
WINDOW *hwin;
{
int y, x;
for (y = 0; y < ymax; y++) {
for (x = 0; x < xmax; x++)
if (x == 0 && y == 0)
mvwaddch (hwin, y, x, '/');
else if (x == 0 && y == ymax-1)
mvwaddch (hwin, y, x, '\\');
else if (x == xmax-1 && y == 0)
mvwaddch (hwin, y, x, '\\');
else if (x == xmax-1 && y == ymax-1)
mvwaddch (hwin, y, x, '/');
else if (x > xmax-1 || y > ymax-1)
mvwaddch (hwin, y, x, ' ');
else if (y == 0)
mvwaddch (hwin, y, x, '=');
else if (y == ymax-1)
mvwaddch (hwin, y, x, '-');
else if (x == 0 || x == xmax-1)
mvwaddch (hwin, y, x, '|');
}
wmove (hwin, 0, 2);
waddstr (hwin, " * Help * ");
return;
}