DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T g

⟦1b57c3678⟧ TextFile

    Length: 14027 (0x36cb)
    Types: TextFile
    Names: »greed.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Greed/greed.c« 

TextFile

/* 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();
}