|
|
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 g
Length: 14027 (0x36cb)
Types: TextFile
Names: »greed.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Greed/greed.c«
/* greed.c v2.0c - Written by Matthew T. Day (mday@ohs.uucp), 07/09/89 *
* Document and send all source code changes to the above address, I'll make *
* sure the approved patches are posted. Please don't redistribute this. */
/* vi:set ts=8: set tabspace alignment to 8, usually I use 6 */
static char *version = "Greed v2.0c";
#include <curses.h>
#include <signal.h>
#include <pwd.h>
#ifdef NOTBSD
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
#ifdef NOTBSD
#define crmode cbreak
#define random lrand48 /* use high quality random routines */
#define srandom srand48
#endif
#define MAXSCORE 10 /* max number of high score entries */
#define FILESIZE (MAXSCORE * sizeof(struct score)) /* total byte size of *
* high score file */
#define rnd(x) (int) ((random() % (x))+1) /* rnd() returns random num *
* between 1 and x */
#define ME '@' /* marker of current screen location */
#define LOCKPATH "/tmp/Greed.lock" /* lock path for high score file */
struct score { /* changing stuff in this struct */
char user[9]; /* makes old score files incompatible */
int score;
};
int allmoves = 0, score = 1, grid[22][79], y, x, havebotmsg = 0;
char *cmdname;
extern long random();
void topscores();
/* botmsg() writes "msg" (in normal video) at the middle of the bottom *
* line of the screen. Boolean "backcur" specifies whether to put cursor *
* back on the grid or leave it on the bottom line (e.g. for questions). */
void botmsg(msg, backcur)
register char *msg;
register backcur;
{
standend();
mvaddstr(23, 40, msg);
clrtoeol();
standout();
if (backcur) move(y, x);
refresh();
havebotmsg = 1;
}
/* quit() is ran when the user hits ^C or ^\, it queries the user if he *
* really wanted to quit, and if so, checks the high score stuff (with the *
* current score) and quits; otherwise, simply returns to the game. */
quit() {
int (*osig)() = signal(SIGINT, SIG_IGN); /* save old signal */
(void) signal(SIGQUIT, SIG_IGN);
if (stdscr) {
botmsg("Really quit? ", 0);
if (getch() != 'y') {
move(y, x);
(void) signal(SIGINT, osig); /* reset old signal */
(void) signal(SIGQUIT, osig);
refresh();
return(1);
}
move(23, 0);
refresh();
endwin();
puts("\n");
topscores(score);
}
exit(0);
}
/* out() is run when the signal SIGTERM is sent, it corrects the terminal *
* state (if necessary) and exits. */
void out() {
if (stdscr) endwin();
exit(0);
}
/* usage() prints out the proper command line usage for Greed and exits. */
void usage() {
fprintf(stderr, "Usage: %s [-p] [-s]\n", cmdname);
exit(1);
}
/* showscore() prints the score and the percentage of the screen eaten at the *
* beginning of the bottom line of the screen, moves the cursor back on the *
* grid, and refreshes the screen. */
void showscore() {
standend();
mvprintw(23, 7, "%d %.2f%%", score, (float) score / 17.32);
move(y, x);
standout();
refresh();
}
main(argc, argv)
int argc;
char *argv[];
{
register val = 1;
extern long time();
void showmoves();
cmdname = argv[0]; /* save the command name */
if (argc == 2) { /* process the command line */
if (strlen(argv[1]) != 2 || argv[1][0] != '-') usage();
switch(argv[1][1]) {
case 'p':
allmoves = 1;
break;
case 's':
topscores(0);
exit(0);
default:
usage();
}
} else if (argc > 2) usage(); /* can't have > 2 arguments */
(void) signal(SIGINT, quit); /* catch off the signals */
(void) signal(SIGQUIT, quit);
(void) signal(SIGTERM, out);
initscr(); /* set up the terminal modes */
crmode();
noecho();
refresh(); /* clears the screen */
srandom(time(0) ^ getpid() << 16); /* initialize the random seed *
* with a unique number */
for (y=0; y < 22; y++) /* fill the grid array and */
for (x=0; x < 79; x++) /* print numbers out */
mvaddch(y, x, (grid[y][x] = rnd(9)) + '0');
mvaddstr(23, 0, "Score: "); /* initialize bottom line */
mvprintw(23, 40, "%s - Hit '?' for help.", version);
standout();
y = rnd(22)-1; x = rnd(79)-1; /* random initial location */
mvaddch(y, x, ME);
grid[y][x] = 0; /* eat initial square */
if (allmoves) showmoves(1);
showscore();
while ((val = tunnel(getch())) > 0); /* main loop, gives tunnel() *
* user command */
if (!val) { /* if didn't quit by 'q' cmd */
botmsg("Hit <Enter>", 0); /* then let user examine */
while (getch() != '\n'); /* final screen */
}
move(23, 0);
refresh();
endwin();
puts("\n"); /* writes two newlines */
topscores(score);
exit(0);
}
/* tunnel() does the main game work. Returns 1 if everything's okay, 0 if *
* user "died", and -1 if user specified and confirmed 'q' (fast quit). */
tunnel(cmd)
register cmd;
{
register dy, dx, distance;
void help();
switch (cmd) { /* process user command */
case 'h': case '4':
dy = 0; dx = -1;
break;
case 'j': case '2':
dy = 1; dx = 0;
break;
case 'k': case '8':
dy = -1; dx = 0;
break;
case 'l': case '6':
dy = 0; dx = 1;
break;
case 'b': case '1':
dy = 1; dx = -1;
break;
case 'n': case '3':
dy = dx = 1;
break;
case 'y': case '7':
dy = dx = -1;
break;
case 'u': case '9':
dy = -1; dx = 1;
break;
case 'q':
return(quit());
case '?':
help();
return(1);
case '\14': /* Control-L (redraw) */
wrefresh(curscr); /* falls through to return */
default:
return(1);
}
distance = grid[y+dy][x+dx]; /* get attempted distance */
{
register j = y, i = x, d = distance;
do { /* process move for validity */
j += dy;
i += dx;
if (j >= 0 && i >= 0 && j < 22 && i < 79 && grid[j][i])
continue; /* if off the screen */
else if (!othermove(dy, dx)) { /* no other good move */
j -= dy;
i -= dx;
mvaddch(y, x, ' ');
while (y != j || x != i) {
y += dy; /* so we manually */
x += dx; /* print chosen path */
score++;
mvaddch(y, x, ' ');
}
mvaddch(y, x, '*'); /* with a '*' */
showscore(); /* print final score */
return(0);
} else { /* otherwise prevent bad move */
botmsg("Bad move.", 1);
return(1);
}
} while (--d);
}
if (allmoves) showmoves(0); /* remove possible moves */
if (havebotmsg) { /* if old bottom msg exists */
standend(); /* put standard message back */
mvprintw(23, 40, "%s - Hit '?' for help.", version);
standout();
havebotmsg = 0;
}
mvaddch(y, x, ' '); /* erase old ME */
do { /* print good path */
y += dy;
x += dx;
score++;
grid[y][x] = 0;
mvaddch(y, x, ' ');
} while (--distance);
mvaddch(y, x, ME); /* put new ME */
if (allmoves) showmoves(1); /* put new possible moves */
showscore(); /* does refresh() finally */
return(1);
}
/* othermove() checks area for an existing possible move. bady and badx are *
* direction variables that tell othermove() they are already no good, and to *
* not process them. I don't know if this is efficient, but it works! */
othermove(bady, badx)
register bady, badx;
{
register dy = -1, dx;
for (; dy <= 1; dy++)
for (dx = -1; dx <= 1; dx++)
if ((!dy && !dx) || (dy == bady && dx == badx))
/* don't do 0,0 or bad coordinates */
continue;
else {
register j=y, i=x, d=grid[y+dy][x+dx];
if (!d) continue;
do { /* "walk" the path, checking */
j += dy;
i += dx;
if (j < 0 || i < 0 || j >= 22 ||
i >= 79 || !grid[j][i]) break;
} while (--d);
if (!d) return(1); /* if "d" got to 0, *
* move was okay. */
}
return(0); /* no good moves were found */
}
/* showmoves() is nearly identical to othermove(), but it highlights possible */
/* moves instead. "on" tells showmoves() whether to add or remove moves. */
void showmoves(on)
register on;
{
register dy = -1, dx;
for (; dy <= 1; dy++)
for (dx = -1; dx <= 1; dx++) {
register j=y, i=x, d=grid[y+dy][x+dx];
if (!d) continue;
do {
j += dy;
i += dx;
if (j < 0 || i < 0 || j >= 22
|| i >= 79 || !grid[j][i]) break;
} while (--d);
if (!d) {
register j=y, i=x, d=grid[y+dy][x+dx];
/* The next section chooses inverse-video *
* or not, and then "walks" chosen valid *
* move, reprinting characters with new mode */
if (on) standout();
else standend();
do {
j += dy;
i += dx;
mvaddch(j, i, grid[j][i]);
} while (--d);
if (!on) standout();
}
}
}
/* doputc() simply prints out a character to stdout, used by tputs() */
void doputc(c)
register char c;
{
(void) fputc(c, stdout);
}
/* topscores() processes it's argument with the high score file, makes any *
* updates to the file, and outputs the list to the screen. If "newscore" *
* is 0, the score file is printed to the screen (i.e. "greed -s") */
void topscores(newscore)
register int newscore;
{
register fd, count = 1;
static char termbuf[BUFSIZ];
char *tptr = (char *) malloc(16), *boldon, *boldoff;
struct score *toplist = (struct score *) malloc(FILESIZE);
register struct score *ptrtmp, *eof = &toplist[MAXSCORE], *new = NULL;
extern char *getenv(), *tgetstr();
extern struct passwd *getpwuid();
void lockit();
(void) signal(SIGINT, SIG_IGN); /* Catch all signals, so high */
(void) signal(SIGQUIT, SIG_IGN); /* score file doesn't get */
(void) signal(SIGTERM, SIG_IGN); /* messed up with a kill. */
(void) signal(SIGHUP, SIG_IGN);
/* following open() creates the file if it doesn't exist */
/* already, using secure mode */
if ((fd = open(SCOREFILE, O_RDWR | O_CREAT, 0600)) == -1) {
fprintf(stderr, "%s: %s: Cannot open.\n", cmdname, SCOREFILE);
exit(1);
}
lockit(1); /* lock score file */
for (ptrtmp=toplist; ptrtmp < eof; ptrtmp++) ptrtmp->score = 0;
/* initialize scores to 0 */
read(fd, toplist, FILESIZE); /* read whole score file in at once */
if (newscore) { /* if possible high score */
for (ptrtmp=toplist; ptrtmp < eof; ptrtmp++)
/* find new location for score */
if (newscore > ptrtmp->score) break;
if (ptrtmp < eof) { /* if it's a new high score */
new = ptrtmp; /* put "new" at new location */
ptrtmp = eof-1; /* start at end of list */
while (ptrtmp > new) { /* shift list one down */
*ptrtmp = *(ptrtmp-1);
ptrtmp--;
}
new->score = newscore; /* fill "new" with the info */
strncpy(new->user, getpwuid(getuid())->pw_name, 8);
(void) lseek(fd, 0, 0); /* seek back to top of file */
write(fd, toplist, FILESIZE); /* write it all out */
}
}
close(fd);
lockit(0); /* unlock score file */
if (toplist->score) puts("Rank Score Name Percentage");
else puts("No high scores."); /* perhaps "greed -s" was run before *
* any greed had been played? */
if (new && tgetent(termbuf, getenv("TERM")) > 0) {
boldon = tgetstr("so", &tptr); /* grab off inverse */
boldoff = tgetstr("se", &tptr); /* video codes */
if (!boldon || !boldoff) boldon = boldoff = 0;
/* if only got one of the *
* codes, use neither */
}
/* print out list to screen, highlighting new score, if any */
for (ptrtmp=toplist; ptrtmp < eof && ptrtmp->score; ptrtmp++, count++) {
if (ptrtmp == new && boldon) tputs(boldon, 1, doputc);
printf("%-5d %-6d %-8s %.2f%%\n", count, ptrtmp->score,
ptrtmp->user, (float) ptrtmp->score / 17.32);
if (ptrtmp == new && boldoff) tputs(boldoff, 1, doputc);
}
}
/* lockit() creates a file with mode 0 to serve as a lock file. The creat() *
* call will fail if the file exists already, since it was made with mode 0. *
* lockit() will wait approx. 15 seconds for the lock file, and then *
* override it (shouldn't happen, but might). "on" says whether to turn *
* locking on or not. */
void lockit(on)
register on;
{
register fd, x = 1;
if (on) {
while ((fd = creat(LOCKPATH, 0)) == -1) {
printf("Waiting for scorefile access... %d/15\n", x);
if (x++ >= 15) {
puts("Overriding stale lock...");
if (unlink(LOCKPATH) == -1) {
fprintf(stderr,
"%s: %s: Can't unlink lock.\n",
cmdname, LOCKPATH);
exit(1);
}
}
sleep(1);
}
close(fd);
} else unlink(LOCKPATH);
}
#define msg(row, msg) mvwaddstr(helpwin, row, 2, msg);
/* help() simply creates a new window over stdscr, and writes the help info *
* inside it. Uses macro msg() to save space. */
void help() {
WINDOW *helpwin = newwin(18, 65, 1, 7);
(void) wclear(helpwin);
box(helpwin, '|', '-'); /* print box around info */
/* put '+' at corners, looks better */
(void) waddch(helpwin, '+'); mvwaddch(helpwin, 0, 64, '+');
mvwaddch(helpwin, 17, 0, '+'); mvwaddch(helpwin, 17, 64, '+');
mvwprintw(helpwin, 1, 2,
"Welcome to %s, by Matthew T. Day (mday@ohs.uucp).", version);
msg(3, "The object of Greed is to erase as much of the screen as");
msg(4, "possible by moving around in a grid of numbers. To move");
msg(5, "your cursor, simply use the 'hjklyubn' keys or your number");
mvwprintw(helpwin, 6, 2,
"keypad. Your location is signified by the '%c' symbol.", ME);
msg(7, "When you move in a direction, you erase N number of grid");
msg(8, "squares in that direction, N being the first number in that");
msg(9, "direction. Your score reflects the total number of squares");
msg(10, "eaten. Greed will not let you make a move that would have");
msg(11, "placed you off the grid or over a previously eaten square");
msg(12, "unless no valid moves exist, in which case your game ends.");
msg(13, "Other Greed commands are 'Ctrl-L' to redraw the screen and");
msg(14, "'q' to quit. Command line options to Greed are '-s' to output");
msg(15, "the high score file and '-p' to highlight the possible moves");
msg(16, "when playing.");
(void) wmove(helpwin, 17, 64);
wrefresh(helpwin);
(void) wgetch(helpwin);
delwin(helpwin);
touchwin(stdscr);
refresh();
}