ascii tetris!

This commit is contained in:
cgd 1994-05-06 06:50:50 +00:00
parent dd267b8c9d
commit fece8b5011
12 changed files with 2053 additions and 0 deletions

10
games/tetris/Makefile Normal file
View File

@ -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 <bsd.prog.mk>

180
games/tetris/input.c Normal file
View File

@ -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 <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include <unistd.h>
#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);
}

42
games/tetris/input.h Normal file
View File

@ -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));

39
games/tetris/pathnames.h Normal file
View File

@ -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"

472
games/tetris/scores.c Normal file
View File

@ -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 <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/*
* XXX - need a <termcap.h>
*/
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');
}
}
}

54
games/tetris/scores.h Normal file
View File

@ -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));

454
games/tetris/screen.c Normal file
View File

@ -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 <sgtty.h>
#include <sys/ioctl.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef sigmask
#define sigmask(s) (1 << ((s) - 1))
#endif
#include "screen.h"
#include "tetris.h"
/*
* XXX - need a <termcap.h>
*/
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);
}
}

56
games/tetris/screen.h Normal file
View File

@ -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));

111
games/tetris/shapes.c Normal file
View File

@ -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 <sys/cdefs.h>
#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;
}

154
games/tetris/tetris.6 Normal file
View File

@ -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 "<space>" -compact -offset indent
.It j
move left
.It k
rotate 1/4 turn counterclockwise
.It l
move right
.It <space>
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.

312
games/tetris/tetris.c Normal file
View File

@ -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 <sys/time.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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], "<space>");
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);
}

169
games/tetris/tetris.h Normal file
View File

@ -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 *));