From 1a4c6defc63314df63eb514291290a64ff2c73b0 Mon Sep 17 00:00:00 2001 From: cgd Date: Fri, 6 May 1994 06:50:50 +0000 Subject: ascii tetris! --- tetris/Makefile | 10 ++ tetris/input.c | 180 ++++++++++++++++++++ tetris/input.h | 42 +++++ tetris/pathnames.h | 39 +++++ tetris/scores.c | 472 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tetris/scores.h | 54 ++++++ tetris/screen.c | 454 +++++++++++++++++++++++++++++++++++++++++++++++++++ tetris/screen.h | 56 +++++++ tetris/shapes.c | 111 +++++++++++++ tetris/tetris.6 | 154 +++++++++++++++++ tetris/tetris.c | 312 +++++++++++++++++++++++++++++++++++ tetris/tetris.h | 169 +++++++++++++++++++ 12 files changed, 2053 insertions(+) create mode 100644 tetris/Makefile create mode 100644 tetris/input.c create mode 100644 tetris/input.h create mode 100644 tetris/pathnames.h create mode 100644 tetris/scores.c create mode 100644 tetris/scores.h create mode 100644 tetris/screen.c create mode 100644 tetris/screen.h create mode 100644 tetris/shapes.c create mode 100644 tetris/tetris.6 create mode 100644 tetris/tetris.c create mode 100644 tetris/tetris.h (limited to 'tetris') diff --git a/tetris/Makefile b/tetris/Makefile new file mode 100644 index 00000000..88a59c38 --- /dev/null +++ b/tetris/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= tetris +SRCS= input.c screen.c shapes.c scores.c tetris.c +MAN6= tetris.0 +DPADD= ${LIBTERM} +LDADD= -ltermcap +HIDEGAME=hidegame + +.include diff --git a/tetris/input.c b/tetris/input.c new file mode 100644 index 00000000..48da0884 --- /dev/null +++ b/tetris/input.c @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)input.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris input. + */ + +#include +#include + +#include +#include + +#include "input.h" +#include "tetris.h" + +/* return true iff the given timeval is positive */ +#define TV_POS(tv) \ + ((tv)->tv_sec > 0 || ((tv)->tv_sec == 0 && (tv)->tv_usec > 0)) + +/* subtract timeval `sub' from `res' */ +#define TV_SUB(res, sub) \ + (res)->tv_sec -= (sub)->tv_sec; \ + (res)->tv_usec -= (sub)->tv_usec; \ + if ((res)->tv_usec < 0) { \ + (res)->tv_usec += 1000000; \ + (res)->tv_sec--; \ + } + +/* + * Do a `read wait': select for reading from stdin, with timeout *tvp. + * On return, modify *tvp to reflect the amount of time spent waiting. + * It will be positive only if input appeared before the time ran out; + * otherwise it will be zero or perhaps negative. + * + * If tvp is nil, wait forever, but return if select is interrupted. + * + * Return 0 => no input, 1 => can read() from stdin + */ +int +rwait(tvp) + register struct timeval *tvp; +{ + int i; + struct timeval starttv, endtv, *s; + extern int errno; +#define NILTZ ((struct timezone *)0) + + /* + * Someday, select() will do this for us. + * Just in case that day is now, and no one has + * changed this, we use a temporary. + */ + if (tvp) { + (void) gettimeofday(&starttv, NILTZ); + endtv = *tvp; + s = &endtv; + } else + s = 0; +again: + i = 1; + switch (select(1, (fd_set *)&i, (fd_set *)0, (fd_set *)0, s)) { + + case -1: + if (tvp == 0) + return (-1); + if (errno == EINTR) + goto again; + stop("select failed, help"); + /* NOTREACHED */ + + case 0: /* timed out */ + tvp->tv_sec = 0; + tvp->tv_usec = 0; + return (0); + } + if (tvp) { + /* since there is input, we may not have timed out */ + (void) gettimeofday(&endtv, NILTZ); + TV_SUB(&endtv, &starttv); + TV_SUB(tvp, &endtv); /* adjust *tvp by elapsed time */ + } + return (1); +} + +/* + * `sleep' for the current turn time (using select). + * Eat any input that might be available. + */ +void +tsleep() +{ + struct timeval tv; + char c; + + tv.tv_sec = 0; + tv.tv_usec = fallrate; + while (TV_POS(&tv)) + if (rwait(&tv) && read(0, &c, 1) != 1) + break; +} + +/* + * Eat up any input (used at end of game). + */ +void +eat_input() +{ + struct timeval tv; + char c; + + do { + tv.tv_sec = tv.tv_usec = 0; + } while (rwait(&tv) && read(0, &c, 1) == 1); +} + +/* + * getchar with timeout. + */ +int +tgetchar() +{ + static struct timeval timeleft; + char c; + + /* + * Reset timeleft to fallrate whenever it is not positive. + * In any case, wait to see if there is any input. If so, + * take it, and update timeleft so that the next call to + * tgetchar() will not wait as long. If there is no input, + * make timeleft zero or negative, and return -1. + * + * Most of the hard work is done by rwait(). + */ + if (!TV_POS(&timeleft)) { + faster(); /* go faster */ + timeleft.tv_sec = 0; + timeleft.tv_usec = fallrate; + } + if (!rwait(&timeleft)) + return (-1); + if (read(0, &c, 1) != 1) + stop("end of file, help"); + return ((int)(unsigned char)c); +} diff --git a/tetris/input.h b/tetris/input.h new file mode 100644 index 00000000..733182d7 --- /dev/null +++ b/tetris/input.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)input.h 8.1 (Berkeley) 5/31/93 + */ + +void eat_input __P((void)); +int rwait __P((struct timeval *)); +int tgetchar __P((void)); +void tsleep __P((void)); diff --git a/tetris/pathnames.h b/tetris/pathnames.h new file mode 100644 index 00000000..ba08dd11 --- /dev/null +++ b/tetris/pathnames.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_SCOREFILE "/var/games/tetris.scores" diff --git a/tetris/scores.c b/tetris/scores.c new file mode 100644 index 00000000..e7e6bb59 --- /dev/null +++ b/tetris/scores.c @@ -0,0 +1,472 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)scores.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu) + * modified 22 January 1992, to limit the number of entries any one + * person has. + * + * Major whacks since then. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * XXX - need a + */ +int tputs __P((const char *, int, int (*)(int))); + +#include "pathnames.h" +#include "screen.h" +#include "scores.h" +#include "tetris.h" + +/* + * Within this code, we can hang onto one extra "high score", leaving + * room for our current score (whether or not it is high). + * + * We also sometimes keep tabs on the "highest" score on each level. + * As long as the scores are kept sorted, this is simply the first one at + * that level. + */ +#define NUMSPOTS (MAXHISCORES + 1) +#define NLEVELS (MAXLEVEL + 1) + +static time_t now; +static int nscores; +static int gotscores; +static struct highscore scores[NUMSPOTS]; + +static int checkscores __P((struct highscore *, int)); +static int cmpscores __P((const void *, const void *)); +static void getscores __P((FILE **)); +static void printem __P((int, int, struct highscore *, int, const char *)); +static char *thisuser __P((void)); + +/* + * Read the score file. Can be called from savescore (before showscores) + * or showscores (if savescore will not be called). If the given pointer + * is not NULL, sets *fpp to an open file pointer that corresponds to a + * read/write score file that is locked with LOCK_EX. Otherwise, the + * file is locked with LOCK_SH for the read and closed before return. + * + * Note, we assume closing the stdio file releases the lock. + */ +static void +getscores(fpp) + FILE **fpp; +{ + int sd, mint, lck; + char *mstr, *human; + FILE *sf; + + if (fpp != NULL) { + mint = O_RDWR | O_CREAT; + mstr = "r+"; + human = "read/write"; + lck = LOCK_EX; + } else { + mint = O_RDONLY; + mstr = "r"; + human = "reading"; + lck = LOCK_SH; + } + sd = open(_PATH_SCOREFILE, mint, 0666); + if (sd < 0) { + if (fpp == NULL) { + nscores = 0; + return; + } + (void)fprintf(stderr, "tetris: cannot open %s for %s: %s\n", + _PATH_SCOREFILE, human, strerror(errno)); + exit(1); + } + if ((sf = fdopen(sd, mstr)) == NULL) { + (void)fprintf(stderr, "tetris: cannot fdopen %s for %s: %s\n", + _PATH_SCOREFILE, human, strerror(errno)); + exit(1); + } + + /* + * Grab a lock. + */ + if (flock(sd, lck)) + (void)fprintf(stderr, + "tetris: warning: score file %s cannot be locked: %s\n", + _PATH_SCOREFILE, strerror(errno)); + + nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf); + if (ferror(sf)) { + (void)fprintf(stderr, "tetris: error reading %s: %s\n", + _PATH_SCOREFILE, strerror(errno)); + exit(1); + } + + if (fpp) + *fpp = sf; + else + (void)fclose(sf); +} + +void +savescore(level) + int level; +{ + register struct highscore *sp; + register int i; + int change; + FILE *sf; + const char *me; + + getscores(&sf); + gotscores = 1; + (void)time(&now); + + /* + * Allow at most one score per person per level -- see if we + * can replace an existing score, or (easiest) do nothing. + * Otherwise add new score at end (there is always room). + */ + change = 0; + me = thisuser(); + for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) { + if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0) + continue; + if (score > sp->hs_score) { + (void)printf("%s bettered %s %d score of %d!\n", + "\nYou", "your old level", level, + sp->hs_score * sp->hs_level); + sp->hs_score = score; /* new score */ + sp->hs_time = now; /* and time */ + change = 1; + } else if (score == sp->hs_score) { + (void)printf("%s tied %s %d high score.\n", + "\nYou", "your old level", level); + sp->hs_time = now; /* renew it */ + change = 1; /* gotta rewrite, sigh */ + } /* else new score < old score: do nothing */ + break; + } + if (i >= nscores) { + strcpy(sp->hs_name, me); + sp->hs_level = level; + sp->hs_score = score; + sp->hs_time = now; + nscores++; + change = 1; + } + + if (change) { + /* + * Sort & clean the scores, then rewrite. + */ + nscores = checkscores(scores, nscores); + rewind(sf); + if (fwrite(scores, sizeof(*sp), nscores, sf) != nscores || + fflush(sf) == EOF) + (void)fprintf(stderr, + "tetris: error writing %s: %s -- %s\n", + _PATH_SCOREFILE, strerror(errno), + "high scores may be damaged"); + } + (void)fclose(sf); /* releases lock */ +} + +/* + * Get login name, or if that fails, get something suitable. + * The result is always trimmed to fit in a score. + */ +static char * +thisuser() +{ + register const char *p; + register struct passwd *pw; + register size_t l; + static char u[sizeof(scores[0].hs_name)]; + + if (u[0]) + return (u); + p = getlogin(); + if (p == NULL || *p == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + p = pw->pw_name; + else + p = " ???"; + } + l = strlen(p); + if (l >= sizeof(u)) + l = sizeof(u) - 1; + bcopy(p, u, l); + u[l] = '\0'; + return (u); +} + +/* + * Score comparison function for qsort. + * + * If two scores are equal, the person who had the score first is + * listed first in the highscore file. + */ +static int +cmpscores(x, y) + const void *x, *y; +{ + register const struct highscore *a, *b; + register long l; + + a = x; + b = y; + l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score; + if (l < 0) + return (-1); + if (l > 0) + return (1); + if (a->hs_time < b->hs_time) + return (-1); + if (a->hs_time > b->hs_time) + return (1); + return (0); +} + +/* + * If we've added a score to the file, we need to check the file and ensure + * that this player has only a few entries. The number of entries is + * controlled by MAXSCORES, and is to ensure that the highscore file is not + * monopolised by just a few people. People who no longer have accounts are + * only allowed the highest score. Scores older than EXPIRATION seconds are + * removed, unless they are someone's personal best. + * Caveat: the highest score on each level is always kept. + */ +static int +checkscores(hs, num) + register struct highscore *hs; + int num; +{ + register struct highscore *sp; + register int i, j, k, numnames; + int levelfound[NLEVELS]; + struct peruser { + char *name; + int times; + } count[NUMSPOTS]; + register struct peruser *pu; + + /* + * Sort so that highest totals come first. + * + * levelfound[i] becomes set when the first high score for that + * level is encountered. By definition this is the highest score. + */ + qsort((void *)hs, nscores, sizeof(*hs), cmpscores); + for (i = MINLEVEL; i < NLEVELS; i++) + levelfound[i] = 0; + numnames = 0; + for (i = 0, sp = hs; i < num;) { + /* + * This is O(n^2), but do you think we care? + */ + for (j = 0, pu = count; j < numnames; j++, pu++) + if (strcmp(sp->hs_name, pu->name) == 0) + break; + if (j == numnames) { + /* + * Add new user, set per-user count to 1. + */ + pu->name = sp->hs_name; + pu->times = 1; + numnames++; + } else { + /* + * Two ways to keep this score: + * - Not too many (per user), still has acct, & + * score not dated; or + * - High score on this level. + */ + if ((pu->times < MAXSCORES && + getpwnam(sp->hs_name) != NULL && + sp->hs_time + EXPIRATION >= now) || + levelfound[sp->hs_level] == 0) + pu->times++; + else { + /* + * Delete this score, do not count it, + * do not pass go, do not collect $200. + */ + num--; + for (k = i; k < num; k++) + hs[k] = hs[k + 1]; + continue; + } + } + levelfound[sp->hs_level] = 1; + i++, sp++; + } + return (num > MAXHISCORES ? MAXHISCORES : num); +} + +/* + * Show current scores. This must be called after savescore, if + * savescore is called at all, for two reasons: + * - Showscores munches the time field. + * - Even if that were not the case, a new score must be recorded + * before it can be shown anyway. + */ +void +showscores(level) + int level; +{ + register struct highscore *sp; + register int i, n, c; + const char *me; + int levelfound[NLEVELS]; + + if (!gotscores) + getscores((FILE **)NULL); + (void)printf("\n\t\t\t Tetris High Scores\n"); + + /* + * If level == 0, the person has not played a game but just asked for + * the high scores; we do not need to check for printing in highlight + * mode. If SOstr is null, we can't do highlighting anyway. + */ + me = level && SOstr ? thisuser() : NULL; + + /* + * Set times to 0 except for high score on each level. + */ + for (i = MINLEVEL; i < NLEVELS; i++) + levelfound[i] = 0; + for (i = 0, sp = scores; i < nscores; i++, sp++) { + if (levelfound[sp->hs_level]) + sp->hs_time = 0; + else { + sp->hs_time = 1; + levelfound[sp->hs_level] = 1; + } + } + + /* + * Page each screenful of scores. + */ + for (i = 0, sp = scores; i < nscores; sp += n) { + n = 40; + if (i + n > nscores) + n = nscores - i; + printem(level, i + 1, sp, n, me); + if ((i += n) < nscores) { + (void)printf("\nHit RETURN to continue."); + (void)fflush(stdout); + while ((c = getchar()) != '\n') + if (c == EOF) + break; + (void)printf("\n"); + } + } +} + +static void +printem(level, offset, hs, n, me) + int level, offset; + register struct highscore *hs; + register int n; + const char *me; +{ + register struct highscore *sp; + int nrows, row, col, item, i, highlight; + char buf[100]; +#define TITLE "Rank Score Name (points/level)" + + /* + * This makes a nice two-column sort with headers, but it's a bit + * convoluted... + */ + printf("%s %s\n", TITLE, n > 1 ? TITLE : ""); + + highlight = 0; + nrows = (n + 1) / 2; + + for (row = 0; row < nrows; row++) { + for (col = 0; col < 2; col++) { + item = col * nrows + row; + if (item >= n) { + /* + * Can only occur on trailing columns. + */ + (void)putchar('\n'); + continue; + } + (void)printf(item + offset < 10 ? " " : " "); + sp = &hs[item]; + (void)sprintf(buf, + "%d%c %6d %-11s (%d on %d)", + item + offset, sp->hs_time ? '*' : ' ', + sp->hs_score * sp->hs_level, + sp->hs_name, sp->hs_score, sp->hs_level); + /* + * Highlight if appropriate. This works because + * we only get one score per level. + */ + if (me != NULL && + sp->hs_level == level && + sp->hs_score == score && + strcmp(sp->hs_name, me) == 0) { + putpad(SOstr); + highlight = 1; + } + (void)printf("%s", buf); + if (highlight) { + putpad(SEstr); + highlight = 0; + } + + /* fill in spaces so column 1 lines up */ + if (col == 0) + for (i = 40 - strlen(buf); --i >= 0;) + (void)putchar(' '); + else /* col == 1 */ + (void)putchar('\n'); + } + } +} diff --git a/tetris/scores.h b/tetris/scores.h new file mode 100644 index 00000000..7a4865ad --- /dev/null +++ b/tetris/scores.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)scores.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris scores. + */ +struct highscore { + char hs_name[20]; /* login name */ + int hs_score; /* raw score */ + int hs_level; /* play level */ + time_t hs_time; /* time at game end */ +}; + +#define MAXHISCORES 80 +#define MAXSCORES 9 /* maximum high score entries per person */ +#define EXPIRATION (5L * 365 * 24 * 60 * 60) + +void savescore __P((int)); +void showscores __P((int)); diff --git a/tetris/screen.c b/tetris/screen.c new file mode 100644 index 00000000..41e3f4a8 --- /dev/null +++ b/tetris/screen.c @@ -0,0 +1,454 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)screen.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris screen control. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef sigmask +#define sigmask(s) (1 << ((s) - 1)) +#endif + +#include "screen.h" +#include "tetris.h" + +/* + * XXX - need a + */ +int tgetent __P((char *, const char *)); +int tgetflag __P((const char *)); +int tgetnum __P((const char *)); +int tputs __P((const char *, int, int (*)(int))); + +static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */ +static int curscore; +static int isset; /* true => terminal is in game mode */ +static struct sgttyb oldtt; +static void (*tstp)(); + +char *tgetstr(), *tgoto(); + + +/* + * Capabilities from TERMCAP. + */ +char PC, *BC, *UP; /* tgoto requires globals: ugh! */ +short ospeed; + +static char + *bcstr, /* backspace char */ + *CEstr, /* clear to end of line */ + *CLstr, /* clear screen */ + *CMstr, /* cursor motion string */ +#ifdef unneeded + *CRstr, /* "\r" equivalent */ +#endif + *HOstr, /* cursor home */ + *LLstr, /* last line, first column */ + *pcstr, /* pad character */ + *TEstr, /* end cursor motion mode */ + *TIstr; /* begin cursor motion mode */ +char + *SEstr, /* end standout mode */ + *SOstr; /* begin standout mode */ +static int + COnum, /* co# value */ + LInum, /* li# value */ + MSflag; /* can move in standout mode */ + + +struct tcsinfo { /* termcap string info; some abbrevs above */ + char tcname[3]; + char **tcaddr; +} tcstrings[] = { + "bc", &bcstr, + "ce", &CEstr, + "cl", &CLstr, + "cm", &CMstr, +#ifdef unneeded + "cr", &CRstr, +#endif + "le", &BC, /* move cursor left one space */ + "pc", &pcstr, + "se", &SEstr, + "so", &SOstr, + "te", &TEstr, + "ti", &TIstr, + "up", &UP, /* cursor up */ + 0 +}; + +/* This is where we will actually stuff the information */ + +static char combuf[1024], tbuf[1024]; + + +/* + * Routine used by tputs(). + */ +int +put(c) + int c; +{ + + return (putchar(c)); +} + +/* + * putstr() is for unpadded strings (either as in termcap(5) or + * simply literal strings); putpad() is for padded strings with + * count=1. (See screen.h for putpad().) + */ +#define putstr(s) (void)fputs(s, stdout) +#define moveto(r, c) putpad(tgoto(CMstr, c, r)) + +/* + * Set up from termcap. + */ +void +scr_init() +{ + static int bsflag, xsflag, sgnum; +#ifdef unneeded + static int ncflag; +#endif + char *term, *fill; + static struct tcninfo { /* termcap numeric and flag info */ + char tcname[3]; + int *tcaddr; + } tcflags[] = { + "bs", &bsflag, + "ms", &MSflag, +#ifdef unneeded + "nc", &ncflag, +#endif + "xs", &xsflag, + 0 + }, tcnums[] = { + "co", &COnum, + "li", &LInum, + "sg", &sgnum, + 0 + }; + + if ((term = getenv("TERM")) == NULL) + stop("you must set the TERM environment variable"); + if (tgetent(tbuf, term) <= 0) + stop("cannot find your termcap"); + fill = combuf; + { + register struct tcsinfo *p; + + for (p = tcstrings; p->tcaddr; p++) + *p->tcaddr = tgetstr(p->tcname, &fill); + } + { + register struct tcninfo *p; + + for (p = tcflags; p->tcaddr; p++) + *p->tcaddr = tgetflag(p->tcname); + for (p = tcnums; p->tcaddr; p++) + *p->tcaddr = tgetnum(p->tcname); + } + if (bsflag) + BC = "\b"; + else if (BC == NULL && bcstr != NULL) + BC = bcstr; + if (CLstr == NULL) + stop("cannot clear screen"); + if (CMstr == NULL || UP == NULL || BC == NULL) + stop("cannot do random cursor positioning via tgoto()"); + PC = pcstr ? *pcstr : 0; + if (sgnum >= 0 || xsflag) + SOstr = SEstr = NULL; +#ifdef unneeded + if (ncflag) + CRstr = NULL; + else if (CRstr == NULL) + CRstr = "\r"; +#endif +} + +/* this foolery is needed to modify tty state `atomically' */ +static jmp_buf scr_onstop; + +#define sigunblock(mask) sigsetmask(sigblock(0) & ~(mask)) + +static void +stopset(sig) + int sig; +{ + (void) signal(sig, SIG_DFL); + (void) kill(getpid(), sig); + (void) sigunblock(sigmask(sig)); + longjmp(scr_onstop, 1); +} + +static void +scr_stop() +{ + scr_end(); + (void) kill(getpid(), SIGTSTP); + (void) sigunblock(sigmask(SIGTSTP)); + scr_set(); + scr_msg(key_msg, 1); +} + +/* + * Set up screen mode. + */ +void +scr_set() +{ + struct winsize ws; + struct sgttyb newtt; + volatile int omask; + void (*ttou)(); + + omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); + if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN) + (void) signal(SIGTSTP, SIG_IGN); + if ((ttou = signal(SIGTSTP, stopset)) == SIG_IGN) + (void) signal(SIGTSTP, SIG_IGN); + /* + * At last, we are ready to modify the tty state. If + * we stop while at it, stopset() above will longjmp back + * to the setjmp here and we will start over. + */ + (void) setjmp(scr_onstop); + (void) sigsetmask(omask); + Rows = 0, Cols = 0; + if (ioctl(0, TIOCGWINSZ, &ws) == 0) { + Rows = ws.ws_row; + Cols = ws.ws_col; + } + if (Rows == 0) + Rows = LInum; + if (Cols == 0) + Cols = COnum; + if (Rows < MINROWS || Cols < MINCOLS) { + (void) fprintf(stderr, + "the screen is too small: must be at least %d x %d", + MINROWS, MINCOLS); + stop(""); /* stop() supplies \n */ + } + if (ioctl(0, TIOCGETP, &oldtt)) + stop("ioctl(TIOCGETP) fails"); + newtt = oldtt; + newtt.sg_flags = (newtt.sg_flags | CBREAK) & ~(CRMOD | ECHO); + if ((newtt.sg_flags & TBDELAY) == XTABS) + newtt.sg_flags &= ~TBDELAY; + if (ioctl(0, TIOCSETN, &newtt)) + stop("ioctl(TIOCSETN) fails"); + ospeed = newtt.sg_ospeed; + omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); + + /* + * We made it. We are now in screen mode, modulo TIstr + * (which we will fix immediately). + */ + if (TIstr) + putstr(TIstr); /* termcap(5) says this is not padded */ + if (tstp != SIG_IGN) + (void) signal(SIGTSTP, scr_stop); + (void) signal(SIGTTOU, ttou); + + isset = 1; + (void) sigsetmask(omask); + scr_clear(); +} + +/* + * End screen mode. + */ +void +scr_end() +{ + int omask = sigblock(sigmask(SIGTSTP) | sigmask(SIGTTOU)); + + /* move cursor to last line */ + if (LLstr) + putstr(LLstr); /* termcap(5) says this is not padded */ + else + moveto(Rows - 1, 0); + /* exit screen mode */ + if (TEstr) + putstr(TEstr); /* termcap(5) says this is not padded */ + (void) fflush(stdout); + (void) ioctl(0, TIOCSETN, &oldtt); + isset = 0; + /* restore signals */ + (void) signal(SIGTSTP, tstp); + (void) sigsetmask(omask); +} + +void +stop(why) + char *why; +{ + + if (isset) + scr_end(); + (void) fprintf(stderr, "aborting: %s\n", why); + exit(1); +} + +/* + * Clear the screen, forgetting the current contents in the process. + */ +void +scr_clear() +{ + + putpad(CLstr); + curscore = -1; + bzero((char *)curscreen, sizeof(curscreen)); +} + +#if vax && !__GNUC__ +typedef int regcell; /* pcc is bad at `register char', etc */ +#else +typedef cell regcell; +#endif + +/* + * Update the screen. + */ +void +scr_update() +{ + register cell *bp, *sp; + register regcell so, cur_so = 0; + register int i, ccol, j; + int omask = sigblock(sigmask(SIGTSTP)); + + /* always leave cursor after last displayed point */ + curscreen[D_LAST * B_COLS - 1] = -1; + + if (score != curscore) { + if (HOstr) + putpad(HOstr); + else + moveto(0, 0); + (void) printf("%d", score); + curscore = score; + } + + bp = &board[D_FIRST * B_COLS]; + sp = &curscreen[D_FIRST * B_COLS]; + for (j = D_FIRST; j < D_LAST; j++) { + ccol = -1; + for (i = 0; i < B_COLS; bp++, sp++, i++) { + if (*sp == (so = *bp)) + continue; + *sp = so; + if (i != ccol) { + if (cur_so && MSflag) { + putpad(SEstr); + cur_so = 0; + } + moveto(RTOD(j), CTOD(i)); + } + if (SOstr) { + if (so != cur_so) { + putpad(so ? SOstr : SEstr); + cur_so = so; + } + putstr(" "); + } else + putstr(so ? "XX" : " "); + ccol = i + 1; + /* + * Look ahead a bit, to avoid extra motion if + * we will be redrawing the cell after the next. + * Motion probably takes four or more characters, + * so we save even if we rewrite two cells + * `unnecessarily'. Skip it all, though, if + * the next cell is a different color. + */ +#define STOP (B_COLS - 3) + if (i > STOP || sp[1] != bp[1] || so != bp[1]) + continue; + if (sp[2] != bp[2]) + sp[1] = -1; + else if (i < STOP && so == bp[2] && sp[3] != bp[3]) { + sp[2] = -1; + sp[1] = -1; + } + } + } + if (cur_so) + putpad(SEstr); + (void) fflush(stdout); + (void) sigsetmask(omask); +} + +/* + * Write a message (set!=0), or clear the same message (set==0). + * (We need its length in case we have to overwrite with blanks.) + */ +void +scr_msg(s, set) + register char *s; + int set; +{ + + if (set || CEstr == NULL) { + register int l = strlen(s); + + moveto(Rows - 2, ((Cols - l) >> 1) - 1); + if (set) + putstr(s); + else + while (--l >= 0) + (void) putchar(' '); + } else { + moveto(Rows - 2, 0); + putpad(CEstr); + } +} diff --git a/tetris/screen.h b/tetris/screen.h new file mode 100644 index 00000000..fd6793d1 --- /dev/null +++ b/tetris/screen.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)screen.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Capabilities from TERMCAP (used in the score code). + */ +char *SEstr; /* end standout mode */ +char *SOstr; /* begin standout mode */ + +/* + * putpad() is for padded strings with count=1. + */ +#define putpad(s) tputs(s, 1, put) + +int put __P((int)); /* just calls putchar; for tputs */ +void scr_clear __P((void)); +void scr_end __P((void)); +void scr_init __P((void)); +void scr_msg __P((char *, int)); +void scr_set __P((void)); +void scr_update __P((void)); diff --git a/tetris/shapes.c b/tetris/shapes.c new file mode 100644 index 00000000..39ba3788 --- /dev/null +++ b/tetris/shapes.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)shapes.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris shapes and related routines. + * + * Note that the first 7 are `well known'. + */ + +#include +#include "tetris.h" + +#define TL -B_COLS-1 /* top left */ +#define TC -B_COLS /* top center */ +#define TR -B_COLS+1 /* top right */ +#define ML -1 /* middle left */ +#define MR 1 /* middle right */ +#define BL B_COLS-1 /* bottom left */ +#define BC B_COLS /* bottom center */ +#define BR B_COLS+1 /* bottom right */ + +struct shape shapes[] = { + /* 0*/ 7, TL, TC, MR, + /* 1*/ 8, TC, TR, ML, + /* 2*/ 9, ML, MR, BC, + /* 3*/ 3, TL, TC, ML, + /* 4*/ 12, ML, BL, MR, + /* 5*/ 15, ML, BR, MR, + /* 6*/ 18, ML, MR, /* sticks out */ 2, + /* 7*/ 0, TC, ML, BL, + /* 8*/ 1, TC, MR, BR, + /* 9*/ 10, TC, MR, BC, + /*10*/ 11, TC, ML, MR, + /*11*/ 2, TC, ML, BC, + /*12*/ 13, TC, BC, BR, + /*13*/ 14, TR, ML, MR, + /*14*/ 4, TL, TC, BC, + /*15*/ 16, TR, TC, BC, + /*16*/ 17, TL, MR, ML, + /*17*/ 5, TC, BC, BL, + /*18*/ 6, TC, BC, /* sticks out */ 2*B_COLS, +}; + +/* + * Return true iff the given shape fits in the given position, + * taking the current board into account. + */ +int +fits_in(shape, pos) + struct shape *shape; + register int pos; +{ + register int *o = shape->off; + + if (board[pos] || board[pos + *o++] || board[pos + *o++] || + board[pos + *o]) + return 0; + return 1; +} + +/* + * Write the given shape into the current board, turning it on + * if `onoff' is 1, and off if `onoff' is 0. + */ +void +place(shape, pos, onoff) + struct shape *shape; + register int pos, onoff; +{ + register int *o = shape->off; + + board[pos] = onoff; + board[pos + *o++] = onoff; + board[pos + *o++] = onoff; + board[pos + *o] = onoff; +} diff --git a/tetris/tetris.6 b/tetris/tetris.6 new file mode 100644 index 00000000..9513ca05 --- /dev/null +++ b/tetris/tetris.6 @@ -0,0 +1,154 @@ +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Nancy L. Tinkham and Darren F. Provine. +.\" +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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. +.\" +.\" @(#)tetris.6 8.1 (Berkeley) 5/31/93 +.\" +.Dd "May 31, 1993" +.Dt TETRIS 6 +.Os +.Sh NAME +.Nm tetris +.Nd the game of tetris +.Sh SYNOPSIS +.Nm +.Op Fl s +.Op Fl k Ar keys +.Op Fl l Ar level +.Sh DESCRIPTION +The +.Nm +command runs display-based game which must be played on a CRT terminal. +The object is to fit the shapes together forming complete rows, +which then vanish. +When the shapes fill up to the top, the game ends. +You can optionally select a level of play, or custom-select control keys. +.Pp +The default level of play is 2. +.Pp +The default control keys are as follows: +.Pp +.Bl -tag -width "" -compact -offset indent +.It j +move left +.It k +rotate 1/4 turn counterclockwise +.It l +move right +.It +drop +.It p +pause +.It q +quit +.El +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl k +The default control keys can be changed using the +.Fl k option. +The +.Ar keys +argument must have the six keys in order, and, remember to quote any +space or tab characters from the shell. +For example: +.sp +.Dl "tetris -l 2 -k 'jkl pq'" +.sp +will play the default games, i.e. level 2 and with the default +control keys. +The current key settings are displayed at the bottom of the screen +during play. +.It Fl l +Select a level of play. +.It Fl s +Display the top scores. +.El +.Pp +.Sh PLAY +At the start of the game, a shape will appear at the top of the screen, +falling one square at a time. +The speed at which it falls is determined directly by the level: +if you select level 2, the blocks will fall twice per second; +at level 9, they fall 9 times per second. +(As the game goes on, things speed up, +no matter what your initial selection.) +When this shape +.Dq "touches down" +on the bottom of the field, another will appear at the top. +.Pp +You can move shapes to the left or right, rotate them counterclockwise, +or drop them to the bottom by pressing the appropriate keys. +As you fit them together, completed horizontal rows vanish, +and any blocks above fall down to fill in. +When the blocks stack up to the top of the screen, the game is over. +.Sh SCORING +You get one point for every block you fit into the stack, +and one point for every space a block falls when you hit the drop key. +(Dropping the blocks is therefore a good way to increase your score.) +Your total score is the product of the level of play +and your accumulated +.ie t points\(em200 +.el points -- 200 +points on level 3 gives you a score of 600. +Each player gets at most one entry on any level, +for a total of nine scores in the high scores file. +Players who no longer have accounts are limited to one score. +Also, scores over 5 years old are expired. +The exception to these conditions is that the highest score on a given +level is +.Em always +kept, +so that following generations can pay homage to those who have +wasted serious amounts of time. +.Pp +The score list is produced at the end of the game. +The printout includes each player's overall ranking, +name, score, and how many points were scored on what level. +Scores which are the highest on a given level +are marked with asterisks +.Dq * . +.Sh FILES +.Bl -tag -width /var/games/tetris.scoresxx +.It /var/games/tetris.scores +high score file +.El +.Sh BUGS +The higher levels are unplayable without a fast terminal connection. +.Sh AUTHORS +Adapted from a 1989 International Obfuscated C Code Contest winner by +Chris Torek and Darren F. Provine. +.Pp +Manual adapted from the original entry written by Nancy L. Tinkham and +Darren F. Provine. diff --git a/tetris/tetris.c b/tetris/tetris.c new file mode 100644 index 00000000..63265bc6 --- /dev/null +++ b/tetris/tetris.c @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)tetris.c 8.1 (Berkeley) 5/31/93 + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +/* + * Tetris (or however it is spelled). + */ + +#include + +#include +#include +#include +#include +#include + +#include "input.h" +#include "scores.h" +#include "screen.h" +#include "tetris.h" + +void onintr __P((int)); +void usage __P((void)); + +/* + * Set up the initial board. The bottom display row is completely set, + * along with another (hidden) row underneath that. Also, the left and + * right edges are set. + */ +static void +setup_board() +{ + register int i; + register cell *p; + + p = board; + for (i = B_SIZE; i; i--) +#ifndef mips + *p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2; +#else /* work around compiler bug */ + *p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2 ? 1 : 0; +#endif +} + +/* + * Elide any full active rows. + */ +static void +elide() +{ + register int i, j, base; + register cell *p; + + for (i = A_FIRST; i < A_LAST; i++) { + base = i * B_COLS + 1; + p = &board[base]; + for (j = B_COLS - 2; *p++ != 0;) { + if (--j <= 0) { + /* this row is to be elided */ + bzero(&board[base], B_COLS - 2); + scr_update(); + tsleep(); + while (--base != 0) + board[base + B_COLS] = board[base]; + scr_update(); + tsleep(); + break; + } + } + } +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int pos, c; + register struct shape *curshape; + register char *keys; + register int level = 2; + char key_write[6][10]; + int ch, i, j; + + keys = "jkl pq"; + + while ((ch = getopt(argc, argv, "k:l:s")) != EOF) + switch(ch) { + case 'k': + if (strlen(keys = optarg) != 6) + usage(); + break; + case 'l': + level = atoi(optarg); + if (level < MINLEVEL || level > MAXLEVEL) { + (void)fprintf(stderr, + "tetris: level must be from %d to %d", + MINLEVEL, MAXLEVEL); + exit(1); + } + break; + case 's': + showscores(0); + exit(0); + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc) + usage(); + + fallrate = 1000000 / level; + + for (i = 0; i <= 5; i++) { + for (j = i+1; j <= 5; j++) { + if (keys[i] == keys[j]) { + (void)fprintf(stderr, + "%s: Duplicate command keys specified.\n", + argv[0]); + exit (1); + } + } + if (keys[i] == ' ') + strcpy(key_write[i], ""); + else { + key_write[i][0] = keys[i]; + key_write[i][1] = '\0'; + } + } + + sprintf(key_msg, +"%s - left %s - rotate %s - right %s - drop %s - pause %s - quit", + key_write[0], key_write[1], key_write[2], key_write[3], + key_write[4], key_write[5]); + + (void)signal(SIGINT, onintr); + scr_init(); + setup_board(); + + srandom(getpid()); + scr_set(); + + pos = A_FIRST*B_COLS + (B_COLS/2)-1; + curshape = randshape(); + + scr_msg(key_msg, 1); + + for (;;) { + place(curshape, pos, 1); + scr_update(); + place(curshape, pos, 0); + c = tgetchar(); + if (c < 0) { + /* + * Timeout. Move down if possible. + */ + if (fits_in(curshape, pos + B_COLS)) { + pos += B_COLS; + continue; + } + + /* + * Put up the current shape `permanently', + * bump score, and elide any full rows. + */ + place(curshape, pos, 1); + score++; + elide(); + + /* + * Choose a new shape. If it does not fit, + * the game is over. + */ + curshape = randshape(); + pos = A_FIRST*B_COLS + (B_COLS/2)-1; + if (!fits_in(curshape, pos)) + break; + continue; + } + + /* + * Handle command keys. + */ + if (c == keys[5]) { + /* quit */ + break; + } + if (c == keys[4]) { + static char msg[] = + "paused - press RETURN to continue"; + + place(curshape, pos, 1); + do { + scr_update(); + scr_msg(key_msg, 0); + scr_msg(msg, 1); + (void) fflush(stdout); + } while (rwait((struct timeval *)NULL) == -1); + scr_msg(msg, 0); + scr_msg(key_msg, 1); + place(curshape, pos, 0); + continue; + } + if (c == keys[0]) { + /* move left */ + if (fits_in(curshape, pos - 1)) + pos--; + continue; + } + if (c == keys[1]) { + /* turn */ + struct shape *new = &shapes[curshape->rot]; + + if (fits_in(new, pos)) + curshape = new; + continue; + } + if (c == keys[2]) { + /* move right */ + if (fits_in(curshape, pos + 1)) + pos++; + continue; + } + if (c == keys[3]) { + /* move to bottom */ + while (fits_in(curshape, pos + B_COLS)) { + pos += B_COLS; + score++; + } + continue; + } + if (c == '\f') + scr_clear(); + } + + scr_clear(); + scr_end(); + + (void)printf("Your score: %d point%s x level %d = %d\n", + score, score == 1 ? "" : "s", level, score * level); + savescore(level); + + printf("\nHit RETURN to see high scores, ^C to skip.\n"); + + while ((i = getchar()) != '\n') + if (i == EOF) + break; + + showscores(level); + + exit(0); +} + +void +onintr(signo) + int signo; +{ + scr_clear(); + scr_end(); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: tetris [-s] [-l level] [-keys]\n"); + exit(1); +} diff --git a/tetris/tetris.h b/tetris/tetris.h new file mode 100644 index 00000000..b95627bb --- /dev/null +++ b/tetris/tetris.h @@ -0,0 +1,169 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)tetris.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Definitions for Tetris. + */ + +/* + * The display (`board') is composed of 23 rows of 12 columns of characters + * (numbered 0..22 and 0..11), stored in a single array for convenience. + * Columns 1 to 10 of rows 1 to 20 are the actual playing area, where + * shapes appear. Columns 0 and 11 are always occupied, as are all + * columns of rows 21 and 22. Rows 0 and 22 exist as boundary areas + * so that regions `outside' the visible area can be examined without + * worrying about addressing problems. + */ + + /* the board */ +#define B_COLS 12 +#define B_ROWS 23 +#define B_SIZE (B_ROWS * B_COLS) + +typedef unsigned char cell; +cell board[B_SIZE]; /* 1 => occupied, 0 => empty */ + + /* the displayed area (rows) */ +#define D_FIRST 1 +#define D_LAST 22 + + /* the active area (rows) */ +#define A_FIRST 1 +#define A_LAST 21 + +/* + * Minimum display size. + */ +#define MINROWS 23 +#define MINCOLS 40 + +int Rows, Cols; /* current screen size */ + +/* + * Translations from board coordinates to display coordinates. + * As with board coordinates, display coordiates are zero origin. + */ +#define RTOD(x) ((x) - 1) +#define CTOD(x) ((x) * 2 + (((Cols - 2 * B_COLS) >> 1) - 1)) + +/* + * A `shape' is the fundamental thing that makes up the game. There + * are 7 basic shapes, each consisting of four `blots': + * + * X.X X.X X.X + * X.X X.X X.X.X X.X X.X.X X.X.X X.X.X.X + * X X X + * + * 0 1 2 3 4 5 6 + * + * Except for 3 and 6, the center of each shape is one of the blots. + * This blot is designated (0,0). The other three blots can then be + * described as offsets from the center. Shape 3 is the same under + * rotation, so its center is effectively irrelevant; it has been chosen + * so that it `sticks out' upward and leftward. Except for shape 6, + * all the blots are contained in a box going from (-1,-1) to (+1,+1); + * shape 6's center `wobbles' as it rotates, so that while it `sticks out' + * rightward, its rotation---a vertical line---`sticks out' downward. + * The containment box has to include the offset (2,0), making the overall + * containment box range from offset (-1,-1) to (+2,+1). (This is why + * there is only one row above, but two rows below, the display area.) + * + * The game works by choosing one of these shapes at random and putting + * its center at the middle of the first display row (row 1, column 5). + * The shape is moved steadily downward until it collides with something: + * either another shape, or the bottom of the board. When the shape can + * no longer be moved downwards, it is merged into the current board. + * At this time, any completely filled rows are elided, and blots above + * these rows move down to make more room. A new random shape is again + * introduced at the top of the board, and the whole process repeats. + * The game ends when the new shape will not fit at (1,5). + * + * While the shapes are falling, the user can rotate them counterclockwise + * 90 degrees (in addition to moving them left or right), provided that the + * rotation puts the blots in empty spaces. The table of shapes is set up + * so that each shape contains the index of the new shape obtained by + * rotating the current shape. Due to symmetry, each shape has exactly + * 1, 2, or 4 rotations total; the first 7 entries in the table represent + * the primary shapes, and the remaining 12 represent their various + * rotated forms. + */ +struct shape { + int rot; /* index of rotated version of this shape */ + int off[3]; /* offsets to other blots if center is at (0,0) */ +}; + +extern struct shape shapes[]; +#define randshape() (&shapes[random() % 7]) + +/* + * Shapes fall at a rate faster than once per second. + * + * The initial rate is determined by dividing 1 million microseconds + * by the game `level'. (This is at most 1 million, or one second.) + * Each time the fall-rate is used, it is decreased a little bit, + * depending on its current value, via the `faster' macro below. + * The value eventually reaches a limit, and things stop going faster, + * but by then the game is utterly impossible. + */ +long fallrate; /* less than 1 million; smaller => faster */ +#define faster() (fallrate -= fallrate / 3000) + +/* + * Game level must be between 1 and 9. This controls the initial fall rate + * and affects scoring. + */ +#define MINLEVEL 1 +#define MAXLEVEL 9 + +/* + * Scoring is as follows: + * + * When the shape comes to rest, and is integrated into the board, + * we score one point. If the shape is high up (at a low-numbered row), + * and the user hits the space bar, the shape plummets all the way down, + * and we score a point for each row it falls (plus one more as soon as + * we find that it is at rest and integrate it---until then, it can + * still be moved or rotated). + */ +int score; /* the obvious thing */ + +char key_msg[100]; + +int fits_in __P((struct shape *, int)); +void place __P((struct shape *, int, int)); +void stop __P((char *)); -- cgit v1.2.3-56-ge451