Bring in OpenBSD's tetris(6).
authorzrj <rimvydas.jasinskas@gmail.com>
Thu, 16 Nov 2017 12:31:47 +0000 (14:31 +0200)
committerzrj <zrj@dragonflybsd.org>
Thu, 16 Nov 2017 14:59:02 +0000 (16:59 +0200)
Will be handy to test upcoming ncurses update.

Taken-from: OpenBSD

12 files changed:
games/Makefile
games/tetris/Makefile [new file with mode: 0644]
games/tetris/input.c [new file with mode: 0644]
games/tetris/input.h [new file with mode: 0644]
games/tetris/scores.c [new file with mode: 0644]
games/tetris/scores.h [new file with mode: 0644]
games/tetris/screen.c [new file with mode: 0644]
games/tetris/screen.h [new file with mode: 0644]
games/tetris/shapes.c [new file with mode: 0644]
games/tetris/tetris.6 [new file with mode: 0644]
games/tetris/tetris.c [new file with mode: 0644]
games/tetris/tetris.h [new file with mode: 0644]

index aff7947..a317555 100644 (file)
@@ -38,6 +38,7 @@ SUBDIR= adventure \
        rogue \
        sail \
        snake \
+       tetris \
        trek \
        wargames \
        worm \
diff --git a/games/tetris/Makefile b/games/tetris/Makefile
new file mode 100644 (file)
index 0000000..6a81221
--- /dev/null
@@ -0,0 +1,12 @@
+#      $OpenBSD: Makefile,v 1.8 2015/11/17 15:27:24 tedu Exp $
+
+PROG=  tetris
+SRCS=  input.c screen.c shapes.c scores.c tetris.c
+MAN=   tetris.6
+DPADD= ${LIBNCURSES}
+LDADD= -lprivate_ncurses
+
+CFLAGS+=       -I${_SHLIBDIRPREFIX}/usr/include/priv/ncurses
+LDFLAGS+=      ${PRIVATELIB_LDFLAGS}
+
+.include <bsd.prog.mk>
diff --git a/games/tetris/input.c b/games/tetris/input.c
new file mode 100644 (file)
index 0000000..fdd3cff
--- /dev/null
@@ -0,0 +1,143 @@
+/*     $OpenBSD: input.c,v 1.19 2017/08/13 02:12:16 tedu Exp $ */
+/*    $NetBSD: input.c,v 1.3 1996/02/06 22:47:33 jtc Exp $    */
+
+/*-
+ * 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. 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/time.h>
+
+#include <errno.h>
+#include <poll.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "input.h"
+#include "tetris.h"
+
+/* return true iff the given timespec is positive */
+#define        TS_POS(ts) \
+       ((ts)->tv_sec > 0 || ((ts)->tv_sec == 0 && (ts)->tv_nsec > 0))
+
+/*
+ * Do a `read wait': poll for reading from stdin, with timeout *limit.
+ * On return, subtract the time spent waiting from *limit.
+ * It will be positive only if input appeared before the time ran out;
+ * otherwise it will be zero or perhaps negative.
+ *
+ * If limit is NULL, wait forever, but return if poll is interrupted.
+ *
+ * Return 0 => no input, 1 => can read() from stdin, -1 => interrupted
+ */
+int
+rwait(struct timespec *limit)
+{
+       struct timespec start, end, elapsed;
+       struct pollfd pfd[1];
+
+       pfd[0].fd = STDIN_FILENO;
+       pfd[0].events = POLLIN;
+
+       if (limit != NULL)
+               clock_gettime(CLOCK_MONOTONIC, &start);
+again:
+       switch (ppoll(pfd, 1, limit, NULL)) {
+       case -1:
+               if (limit == NULL)
+                       return (-1);
+               if (errno == EINTR)
+                       goto again;
+               stop("poll failed, help");
+       case 0: /* timed out */
+               timespecclear(limit);
+               return (0);
+       }
+       if (limit != NULL) {
+               /* we have input, so subtract the elapsed time from *limit */
+               clock_gettime(CLOCK_MONOTONIC, &end);
+               timespecsub(&end, &start, &elapsed);
+               timespecsub(limit, &elapsed, limit);
+       }
+       return (1);
+}
+
+/*
+ * `sleep' for the current turn time and eat any
+ * input that becomes available.
+ */
+void
+tsleep(void)
+{
+       struct timespec ts;
+       char c;
+
+       ts.tv_sec = 0;
+       ts.tv_nsec = fallrate;
+       while (TS_POS(&ts))
+               if (rwait(&ts) && read(STDIN_FILENO, &c, 1) != 1)
+                       break;
+}
+
+/*
+ * getchar with timeout.
+ */
+int
+tgetchar(void)
+{
+       static struct timespec 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 (!TS_POS(&timeleft)) {
+               faster();       /* go faster */
+               timeleft.tv_sec = 0;
+               timeleft.tv_nsec = fallrate;
+       }
+       if (!rwait(&timeleft))
+               return (-1);
+       if (read(STDIN_FILENO, &c, 1) != 1)
+               stop("end of file, help");
+       return ((int)(unsigned char)c);
+}
diff --git a/games/tetris/input.h b/games/tetris/input.h
new file mode 100644 (file)
index 0000000..351fa6b
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $OpenBSD: input.h,v 1.6 2017/08/13 02:12:16 tedu Exp $  */
+/*     $NetBSD: input.h,v 1.2 1995/04/22 07:42:36 cgd Exp $    */
+
+/*-
+ * 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. 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
+ */
+
+/* Operations on timespecs. */
+#define        timespecclear(tsp)      (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
+#define        timespecsub(tsp, usp, vsp)                                      \
+       do {                                                            \
+               (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;          \
+               (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;       \
+               if ((vsp)->tv_nsec < 0) {                               \
+                       (vsp)->tv_sec--;                                \
+                       (vsp)->tv_nsec += 1000000000L;                  \
+               }                                                       \
+       } while (/* CONSTCOND */ 0)
+
+int rwait(struct timespec *);
+int tgetchar(void);
+void tsleep(void);
diff --git a/games/tetris/scores.c b/games/tetris/scores.c
new file mode 100644 (file)
index 0000000..23a6998
--- /dev/null
@@ -0,0 +1,436 @@
+/*     $OpenBSD: scores.c,v 1.22 2016/08/27 02:00:10 guenther Exp $    */
+/*     $NetBSD: scores.c,v 1.2 1995/04/22 07:42:38 cgd Exp $   */
+
+/*-
+ * 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. 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 <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "scores.h"
+#include "screen.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 unsigned int nscores;
+static int gotscores;
+static struct highscore scores[NUMSPOTS];
+
+static int checkscores(struct highscore *, unsigned int);
+static int cmpscores(const void *, const void *);
+static void getscores(FILE **);
+static void printem(int, int, struct highscore *, int, const char *);
+static char *thisuser(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(FILE **fpp)
+{
+       int sd, mint, ret;
+       unsigned int i;
+       const char *mstr, *human, *home;
+       char scorepath[PATH_MAX];
+       FILE *sf;
+
+       if (fpp != NULL) {
+               mint = O_RDWR | O_CREAT;
+               mstr = "r+";
+               human = "read/write";
+               *fpp = NULL;
+       } else {
+               mint = O_RDONLY;
+               mstr = "r";
+               human = "reading";
+       }
+
+       home = getenv("HOME");
+       if (home == NULL || *home == '\0')
+               err(1, "getenv");
+
+       ret = snprintf(scorepath, sizeof(scorepath), "%s/%s", home, ".tetris.scores");
+       if (ret < 0 || ret >= PATH_MAX)
+               errc(1, ENAMETOOLONG, "%s/%s", home, ".tetris.scores");
+
+       sd = open(scorepath, mint, 0666);
+       if (sd < 0) {
+               if (fpp == NULL) {
+                       nscores = 0;
+                       return;
+               }
+               err(1, "cannot open %s for %s", scorepath, human);
+       }
+       if ((sf = fdopen(sd, mstr)) == NULL)
+               err(1, "cannot fdopen %s for %s", scorepath, human);
+
+       nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf);
+       if (ferror(sf))
+               err(1, "error reading %s", scorepath);
+       for (i = 0; i < nscores; i++)
+               if (scores[i].hs_level < MINLEVEL ||
+                   scores[i].hs_level > MAXLEVEL)
+                       errx(1, "scorefile %s corrupt", scorepath);
+
+       if (fpp)
+               *fpp = sf;
+       else
+               fclose(sf);
+}
+
+void
+savescore(int level)
+{
+       struct highscore *sp;
+       unsigned int i;
+       int change;
+       FILE *sf;
+       const char *me;
+
+       getscores(&sf);
+       gotscores = 1;
+       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) {
+                       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) {
+                       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) {
+               strlcpy(sp->hs_name, me, sizeof sp->hs_name);
+               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);
+               if (fseek(sf, 0L, SEEK_SET) == -1)
+                       err(1, "fseek");
+               if (fwrite(scores, sizeof(*sp), nscores, sf) != nscores ||
+                   fflush(sf) == EOF)
+                       warnx("error writing scorefile: %s\n\t-- %s",
+                           strerror(errno),
+                           "high scores may be damaged");
+       }
+       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(void)
+{
+       const char *p;
+       static char u[sizeof(scores[0].hs_name)];
+
+       if (u[0])
+               return (u);
+       p = getenv("LOGNAME");
+       if (p == NULL || *p == '\0')
+               p = getenv("USER");
+       if (p == NULL || *p == '\0')
+               p = getlogin();
+       if (p == NULL || *p == '\0')
+               p = "  ???";
+       strlcpy(u, p, sizeof(u));
+       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(const void *x, const void *y)
+{
+       const struct highscore *a, *b;
+       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(struct highscore *hs, unsigned int num)
+{
+       struct highscore *sp;
+       unsigned int i, j, k, nrnames;
+       int levelfound[NLEVELS];
+       struct peruser {
+               char *name;
+               int times;
+       } count[NUMSPOTS];
+       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;
+       nrnames = 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 < nrnames; j++, pu++)
+                       if (strcmp(sp->hs_name, pu->name) == 0)
+                               break;
+               if (j == nrnames) {
+                       /*
+                        * Add new user, set per-user count to 1.
+                        */
+                       pu->name = sp->hs_name;
+                       pu->times = 1;
+                       nrnames++;
+               } 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 &&
+                            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(int level)
+{
+       struct highscore *sp;
+       unsigned int i, n;
+       int c;
+       const char *me;
+       int levelfound[NLEVELS];
+
+       if (!gotscores)
+               getscores((FILE **)NULL);
+       printf("\n\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 = 20;
+               if (i + n > nscores)
+                       n = nscores - i;
+               printem(level, i + 1, sp, n, me);
+               if ((i += n) < nscores) {
+                       printf("\nHit RETURN to continue.");
+                       fflush(stdout);
+                       while ((c = getchar()) != '\n')
+                               if (c == EOF)
+                                       break;
+                       printf("\n");
+               }
+       }
+
+       if (nscores == 0)
+               printf("\t\t\t      - none to date.\n");
+}
+
+static void
+printem(int level, int offset, struct highscore *hs, int n, const char *me)
+{
+       struct highscore *sp;
+       int row, highlight;
+       unsigned int i;
+       char buf[100];
+#define        TITLE "Rank  Score   Name                          (points/level)"
+#define        TITL2 "=========================================================="
+
+       printf("%s\n%s\n", TITLE, TITL2);
+
+       highlight = 0;
+
+       for (row = 0; row < n; row++) {
+               sp = &hs[row];
+               snprintf(buf, sizeof(buf),
+                   "%3d%c %6d  %-31s (%6d on %d)\n",
+                   row + offset, sp->hs_time ? '*' : ' ',
+                   sp->hs_score * sp->hs_level,
+                   sp->hs_name, sp->hs_score, sp->hs_level);
+               /* Print leaders every three lines */
+               if ((row + 1) % 3 == 0) {
+                       for (i = 0; i < sizeof(buf); i++)
+                               if (buf[i] == ' ')
+                                       buf[i] = '_';
+               }
+               /*
+                * 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;
+               }
+               printf("%s", buf);
+               if (highlight) {
+                       putpad(SEstr);
+                       highlight = 0;
+               }
+       }
+}
diff --git a/games/tetris/scores.h b/games/tetris/scores.h
new file mode 100644 (file)
index 0000000..da6770e
--- /dev/null
@@ -0,0 +1,55 @@
+/*     $OpenBSD: scores.h,v 1.6 2014/11/16 04:49:49 guenther Exp $     */
+/*     $NetBSD: scores.h,v 1.2 1995/04/22 07:42:40 cgd Exp $   */
+
+/*-
+ * 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. 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
+ */
+
+#include <sys/param.h>
+
+/*
+ * Tetris scores.
+ */
+struct highscore {
+       char    hs_name[MAXLOGNAME];    /* 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(int);
+void showscores(int);
diff --git a/games/tetris/screen.c b/games/tetris/screen.c
new file mode 100644 (file)
index 0000000..bbcc9f1
--- /dev/null
@@ -0,0 +1,488 @@
+/*     $OpenBSD: screen.c,v 1.18 2017/04/16 18:04:02 tb Exp $  */
+/*     $NetBSD: screen.c,v 1.4 1995/04/29 01:11:36 mycroft Exp $       */
+
+/*-
+ * 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. 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 <sys/ioctl.h>
+
+#include <err.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+#include <unistd.h>
+
+#include "screen.h"
+#include "tetris.h"
+
+static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */
+static int curscore;
+static int isset;              /* true => terminal is in game mode */
+static struct termios oldtt;
+static void (*tstp)(int);
+
+static void scr_stop(int);
+static void stopset(int);
+
+/*
+ * Capabilities from TERMCAP.
+ */
+extern char PC, *BC, *UP;      /* tgoto requires globals: ugh! */
+
+static char *bcstr;            /* backspace char */
+static char *CEstr;            /* clear to end of line */
+static char *CLstr;            /* clear screen */
+static char *CMstr;            /* cursor motion string */
+#ifdef unneeded
+static char *CRstr;            /* "\r" equivalent */
+#endif
+static char *HOstr;            /* cursor home */
+static char *LLstr;            /* last line, first column */
+static char *pcstr;            /* pad character */
+static char *TEstr;            /* end cursor motion mode */
+static char *TIstr;            /* begin cursor motion mode */
+static char *VIstr;            /* make cursor invisible */
+static char *VEstr;            /* make cursor appear normal */
+char *SEstr;                   /* end standout mode */
+char *SOstr;                   /* begin standout mode */
+static int COnum;              /* co# value */
+static int LInum;              /* li# value */
+static int 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},
+       {"vi", &VIstr},
+       {"ve", &VEstr},
+       {"up", &UP},            /* cursor up */
+       { {0}, NULL}
+};
+
+/* This is where we will actually stuff the information */
+
+static char combuf[1024], tbuf[1024];
+
+
+/*
+ * Routine used by tputs().
+ */
+int
+put(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)       fputs(s, stdout)
+#define        moveto(r, c)    putpad(tgoto(CMstr, c, r))
+
+/*
+ * Set up from termcap.
+ */
+void
+scr_init(void)
+{
+       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}, NULL}
+       }, tcnums[] = {
+               {"co", &COnum},
+               {"li", &LInum},
+               {"sg", &sgnum},
+               { {0}, NULL}
+       };
+
+       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;
+       {
+               struct tcsinfo *p;
+
+               for (p = tcstrings; p->tcaddr; p++)
+                       *p->tcaddr = tgetstr(p->tcname, &fill);
+       }
+       if (classic)
+               SOstr = SEstr = NULL;
+       {
+               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 = __DECONST(char *, "\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;
+
+static void
+stopset(int sig)
+{
+       sigset_t sigset;
+
+       signal(sig, SIG_DFL);
+       kill(getpid(), sig);
+       sigemptyset(&sigset);
+       sigaddset(&sigset, sig);
+       sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
+       longjmp(scr_onstop, 1);
+}
+
+static void
+scr_stop(int sig)
+{
+       sigset_t sigset;
+
+       scr_end();
+       kill(getpid(), sig);
+       sigemptyset(&sigset);
+       sigaddset(&sigset, sig);
+       sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
+       scr_set();
+       scr_msg(key_msg, 1);
+}
+
+/*
+ * Set up screen mode.
+ */
+void
+scr_set(void)
+{
+       struct winsize ws;
+       struct termios newtt;
+       sigset_t sigset, osigset;
+       void (*ttou)(int);
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGTSTP);
+       sigaddset(&sigset, SIGTTOU);
+       sigprocmask(SIG_BLOCK, &sigset, &osigset);
+       if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
+               signal(SIGTSTP, SIG_IGN);
+       if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
+               signal(SIGTTOU, 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.
+        */
+       setjmp(scr_onstop);
+       sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+       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) {
+               char smallscr[55];
+
+               snprintf(smallscr, sizeof(smallscr),
+                   "the screen is too small (must be at least %dx%d)",
+                   MINROWS, MINCOLS);
+               stop(smallscr);
+       }
+       if (tcgetattr(0, &oldtt) < 0)
+               stop("tcgetattr() fails");
+       newtt = oldtt;
+       newtt.c_lflag &= ~(ICANON|ECHO);
+       newtt.c_oflag &= ~OXTABS;
+       if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
+               stop("tcsetattr() fails");
+       sigprocmask(SIG_BLOCK, &sigset, &osigset);
+
+       /*
+        * 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 (VIstr)
+               putstr(VIstr);  /* termcap(5) says this is not padded */
+       if (tstp != SIG_IGN)
+               signal(SIGTSTP, scr_stop);
+       if (ttou != SIG_IGN)
+               signal(SIGTTOU, ttou);
+
+       isset = 1;
+       sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+       scr_clear();
+}
+
+/*
+ * End screen mode.
+ */
+void
+scr_end(void)
+{
+       sigset_t sigset, osigset;
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGTSTP);
+       sigaddset(&sigset, SIGTTOU);
+       sigprocmask(SIG_BLOCK, &sigset, &osigset);
+       /* 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 */
+       if (VEstr)
+               putstr(VEstr);  /* termcap(5) says this is not padded */
+       fflush(stdout);
+       tcsetattr(0, TCSADRAIN, &oldtt);
+       isset = 0;
+       /* restore signals */
+       signal(SIGTSTP, tstp);
+       sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+}
+
+void
+stop(const char *why)
+{
+       if (isset)
+               scr_end();
+       errx(1, "aborting: %s", why);
+}
+
+/*
+ * Clear the screen, forgetting the current contents in the process.
+ */
+void
+scr_clear(void)
+{
+       putpad(CLstr);
+       curscore = -1;
+       memset((char *)curscreen, 0, sizeof(curscreen));
+}
+
+typedef cell regcell;
+
+/*
+ * Update the screen.
+ */
+void
+scr_update(void)
+{
+       cell *bp, *sp;
+       regcell so, cur_so = 0;
+       int i, ccol, j;
+       sigset_t sigset, osigset;
+       static const struct shape *lastshape;
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGTSTP);
+       sigprocmask(SIG_BLOCK, &sigset, &osigset);
+
+       /* 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);
+               printf("Score: %d", score);
+               curscore = score;
+       }
+
+       /* draw preview of next pattern */
+       if (showpreview && (nextshape != lastshape)) {
+               static int r=5, c=2;
+               int tr, tc, t;
+
+               lastshape = nextshape;
+
+               /* clean */
+               putpad(SEstr);
+               moveto(r-1, c-1); putstr("          ");
+               moveto(r,   c-1); putstr("          ");
+               moveto(r+1, c-1); putstr("          ");
+               moveto(r+2, c-1); putstr("          ");
+
+               moveto(r-3, c-2);
+               putstr("Next shape:");
+
+               /* draw */
+               if (SOstr)
+                       putpad(SOstr);
+               moveto(r, 2 * c);
+               putstr(SOstr ? "  " : "[]");
+               for (i = 0; i < 3; i++) {
+                       t = c + r * B_COLS;
+                       t += nextshape->off[i];
+
+                       tr = t / B_COLS;
+                       tc = t % B_COLS;
+
+                       moveto(tr, 2*tc);
+                       putstr(SOstr ? "  " : "[]");
+               }
+               putpad(SEstr);
+       }
+
+       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 ? "[]" : "  ");
+                       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);
+       fflush(stdout);
+       sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+}
+
+/*
+ * 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(char *s, int set)
+{
+       if (set || CEstr == NULL) {
+               int l = strlen(s);
+
+               moveto(Rows - 2, ((Cols - l) >> 1) - 1);
+               if (set)
+                       putstr(s);
+               else
+                       while (--l >= 0)
+                               putchar(' ');
+       } else {
+               moveto(Rows - 2, 0);
+               putpad(CEstr);
+       }
+}
diff --git a/games/tetris/screen.h b/games/tetris/screen.h
new file mode 100644 (file)
index 0000000..711ef07
--- /dev/null
@@ -0,0 +1,55 @@
+/*     $OpenBSD: screen.h,v 1.5 2003/06/03 03:01:41 millert Exp $      */
+/*     $NetBSD: screen.h,v 1.2 1995/04/22 07:42:42 cgd Exp $   */
+
+/*-
+ * 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. 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).
+ */
+extern char *SEstr;            /* end standout mode */
+extern char *SOstr;            /* begin standout mode */
+
+/*
+ * putpad() is for padded strings with count=1.
+ */
+#define        putpad(s)       tputs(s, 1, put)
+
+int put(int);                  /* just calls putchar; for tputs */
+void scr_clear(void);
+void scr_end(void);
+void scr_init(void);
+void scr_msg(char *, int);
+void scr_set(void);
+void scr_update(void);
diff --git a/games/tetris/shapes.c b/games/tetris/shapes.c
new file mode 100644 (file)
index 0000000..da2924f
--- /dev/null
@@ -0,0 +1,107 @@
+/*     $OpenBSD: shapes.c,v 1.10 2016/01/04 17:33:24 mestre Exp $      */
+/*     $NetBSD: shapes.c,v 1.2 1995/04/22 07:42:44 cgd Exp $   */
+
+/*-
+ * 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. 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 <unistd.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 */
+
+const struct shape shapes[] = {
+       /* 0*/  { 7,    7,      { TL, TC, MR } },
+       /* 1*/  { 8,    8,      { TC, TR, ML } },
+       /* 2*/  { 9,    11,     { ML, MR, BC } },
+       /* 3*/  { 3,    3,      { TL, TC, ML } },
+       /* 4*/  { 12,   14,     { ML, BL, MR } },
+       /* 5*/  { 15,   17,     { ML, BR, MR } },
+       /* 6*/  { 18,   18,     { ML, MR, 2  } }, /* sticks out */
+       /* 7*/  { 0,    0,      { TC, ML, BL } },
+       /* 8*/  { 1,    1,      { TC, MR, BR } },
+       /* 9*/  { 10,   2,      { TC, MR, BC } },
+       /*10*/  { 11,   9,      { TC, ML, MR } },
+       /*11*/  { 2,    10,     { TC, ML, BC } },
+       /*12*/  { 13,   4,      { TC, BC, BR } },
+       /*13*/  { 14,   12,     { TR, ML, MR } },
+       /*14*/  { 4,    13,     { TL, TC, BC } },
+       /*15*/  { 16,   5,      { TR, TC, BC } },
+       /*16*/  { 17,   15,     { TL, MR, ML } },
+       /*17*/  { 5,    16,     { TC, BC, BL } },
+       /*18*/  { 6,    6,      { TC, BC, 2*B_COLS } }/* sticks out */
+};
+
+/*
+ * Return true iff the given shape fits in the given position,
+ * taking the current board into account.
+ */
+int
+fits_in(const struct shape *shape, int pos)
+{
+       const 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(const struct shape *shape, int pos, int onoff)
+{
+       const int *o = shape->off;
+
+       board[pos] = onoff;
+       board[pos + *o++] = onoff;
+       board[pos + *o++] = onoff;
+       board[pos + *o] = onoff;
+}
diff --git a/games/tetris/tetris.6 b/games/tetris/tetris.6
new file mode 100644 (file)
index 0000000..9766ca9
--- /dev/null
@@ -0,0 +1,172 @@
+.\"    $OpenBSD: tetris.6,v 1.21 2017/06/19 06:04:16 bentley Exp $
+.\"
+.\" 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. 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 November 16, 2017
+.Dt TETRIS 6
+.Os
+.Sh NAME
+.Nm tetris
+.Nd the game of tetris
+.Sh SYNOPSIS
+.Nm
+.Op Fl cps
+.Op Fl k Ar keys
+.Op Fl l Ar level
+.Sh DESCRIPTION
+The
+.Nm
+command runs a display-based game.
+The object is to fit shapes together to form 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 c
+Classic tetris mode, in which shapes rotate clockwise and are drawn with
+.Dq [] .
+.It Fl k Ar keys
+The default control keys can be changed using the
+.Fl k
+option.
+The
+.Ar keys
+argument must have the six keys in order; 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 game, i.e. level 2 with the default
+control keys.
+The current key settings are displayed at the bottom of the screen
+during play.
+.It Fl l Ar level
+Select a level of play.
+.It Fl p
+Switch on previewing of the shape that will appear next.
+This penalizes your score.
+.It Fl s
+Display the top scores.
+.El
+.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.)
+Completing a row rewards you with a bonus corresponding to the number
+of simultaneous rows completed.
+Your total score is the product of the level of play
+and your accumulated
+points \(em 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 ENVIRONMENT
+.Bl -tag -width $HOME/.tetris.scores
+.It Ev LOGNAME
+Name displayed in high score file.
+.El
+.Sh FILES
+.Bl -tag -width $HOME/.tetris.scores
+.It Pa $HOME/.tetris.scores
+High score file.
+.El
+.Sh AUTHORS
+.An -nosplit
+Adapted from a 1989 International Obfuscated C Code Contest winner by
+.An Chris Torek
+and
+.An Darren F. Provine .
+.Pp
+Manual adapted from the original entry written by
+.An Nancy L. Tinkham
+and
+.An Darren F. Provine .
+.Pp
+Shape previewing code adapted from code by
+.An Hubert Feyrer .
+.Sh BUGS
+The higher levels are unplayable without a fast terminal connection.
diff --git a/games/tetris/tetris.c b/games/tetris/tetris.c
new file mode 100644 (file)
index 0000000..5d5079f
--- /dev/null
@@ -0,0 +1,359 @@
+/*     $OpenBSD: tetris.c,v 1.32 2017/08/13 02:12:16 tedu Exp $        */
+/*     $NetBSD: tetris.c,v 1.2 1995/04/22 07:42:47 cgd Exp $   */
+
+/*-
+ * 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. 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
+ */
+
+/*
+ * Tetris (or however it is spelled).
+ */
+
+#include <err.h>
+#include <limits.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"
+
+cell   board[B_SIZE];
+int    Rows, Cols;
+const struct shape *curshape;
+const struct shape *nextshape;
+long   fallrate;
+int    score;
+char   key_msg[100];
+int    showpreview, classic;
+
+static void elide(void);
+void onintr(int __unused);
+const struct shape *randshape(void);
+static void setup_board(void);
+__dead2 void usage(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(void)
+{
+       int i;
+       cell *p;
+
+       p = board;
+       for (i = B_SIZE; i; i--)
+               *p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2;
+}
+
+/*
+ * Elide any full active rows.
+ */
+static void
+elide(void)
+{
+       int rows = 0;
+       int i, j, base;
+       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 */
+                               rows++;
+                               memset(&board[base], 0, B_COLS - 2);
+                               scr_update();
+                               tsleep();
+                               while (--base != 0)
+                                       board[base + B_COLS] = board[base];
+                               memset(&board[1], 0, B_COLS - 2);
+                               scr_update();
+                               tsleep();
+                               break;
+                       }
+               }
+       }
+       switch (rows) {
+       case 1:
+               score += 10;
+               break;
+       case 2:
+               score += 30;
+               break;
+       case 3:
+               score += 70;
+               break;
+       case 4:
+               score += 150;
+               break;
+       default:
+               break;
+       }
+}
+
+const struct shape *
+randshape(void)
+{
+       const struct shape *tmp;
+       int i, j;
+
+       tmp = &shapes[arc4random_uniform(7)];
+       j = arc4random_uniform(4);
+       for (i = 0; i < j; i++)
+               tmp = &shapes[classic? tmp->rotc : tmp->rot];
+       return (tmp);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int pos, c;
+       const char *keys;
+       int level = 2;
+       char key_write[6][10];
+       const char *errstr;
+       int ch, i, j;
+
+       keys = "jkl pq";
+
+       classic = showpreview = 0;
+       while ((ch = getopt(argc, argv, "ck:l:ps")) != -1)
+               switch(ch) {
+               case 'c':
+                       /*
+                        * this means:
+                        *      - rotate the other way;
+                        *      - no reverse video.
+                        */
+                       classic = 1;
+                       break;
+               case 'k':
+                       if (strlen(keys = optarg) != 6)
+                               usage();
+                       break;
+               case 'l':
+                       level = (int)strtonum(optarg, MINLEVEL, MAXLEVEL,
+                           &errstr);
+                       if (errstr)
+                               errx(1, "level must be from %d to %d",
+                                   MINLEVEL, MAXLEVEL);
+                       break;
+               case 'p':
+                       showpreview = 1;
+                       break;
+               case 's':
+                       showscores(0);
+                       return 0;
+               default:
+                       usage();
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc)
+               usage();
+
+       fallrate = 1000000000L / level;
+
+       for (i = 0; i <= 5; i++) {
+               for (j = i+1; j <= 5; j++) {
+                       if (keys[i] == keys[j])
+                               errx(1, "duplicate command keys specified.");
+               }
+               if (keys[i] == ' ')
+                       strlcpy(key_write[i], "<space>", sizeof key_write[i]);
+               else {
+                       key_write[i][0] = keys[i];
+                       key_write[i][1] = '\0';
+               }
+       }
+
+       snprintf(key_msg, sizeof 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]);
+
+       signal(SIGINT, onintr);
+       scr_init();
+       setup_board();
+
+       scr_set();
+
+       pos = A_FIRST*B_COLS + (B_COLS/2)-1;
+       nextshape = randshape();
+       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 = nextshape;
+                       nextshape = 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);
+                               fflush(stdout);
+                       } while (rwait(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 */
+                       const struct shape *new = &shapes[
+                           classic? curshape->rotc : 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_msg(key_msg, 1);
+               }
+       }
+
+       scr_clear();
+       scr_end();
+
+       if (showpreview == 0)
+               printf("Your score:  %d point%s  x  level %d  =  %d\n",
+                   score, score == 1 ? "" : "s", level, score * level);
+       else {
+               printf("Your score:  %d point%s x level %d x preview penalty %0.3f = %d\n",
+                   score, score == 1 ? "" : "s", level, (double)PRE_PENALTY,
+                   (int)(score * level * PRE_PENALTY));
+               score = score * PRE_PENALTY;
+       }
+       savescore(level);
+
+       printf("\nHit RETURN to see high scores, ^C to skip.\n");
+
+       while ((i = getchar()) != '\n')
+               if (i == EOF)
+                       break;
+
+       showscores(level);
+
+       return 0;
+}
+
+void
+onintr(int signo __unused)
+{
+       scr_clear();            /* XXX signal race */
+       scr_end();              /* XXX signal race */
+       _exit(0);
+}
+
+void
+usage(void)
+{
+       fprintf(stderr, "usage: %s [-cps] [-k keys] "
+           "[-l level]\n", getprogname());
+       exit(1);
+}
diff --git a/games/tetris/tetris.h b/games/tetris/tetris.h
new file mode 100644 (file)
index 0000000..e3d2507
--- /dev/null
@@ -0,0 +1,177 @@
+/*     $OpenBSD: tetris.h,v 1.12 2017/08/13 02:12:16 tedu Exp $        */
+/*     $NetBSD: tetris.h,v 1.2 1995/04/22 07:42:48 cgd Exp $   */
+
+/*-
+ * 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. 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;
+extern 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
+
+extern int Rows, Cols; /* current screen size */
+
+/*
+ * Translations from board coordinates to display coordinates.
+ * As with board coordinates, display coordinates 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     rotc;   /* -- " -- in classic version  */
+       int     off[3]; /* offsets to other blots if center is at (0,0) */
+};
+
+extern const struct shape shapes[];
+
+extern const struct shape *curshape;
+extern const struct shape *nextshape;
+
+/*
+ * Shapes fall at a rate faster than once per second.
+ *
+ * The initial rate is determined by dividing 1 billion nanoseconds
+ * by the game `level'.  (This is at most 1 billion, or one second.)
+ * Each time the fallrate 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.
+ */
+extern long fallrate;  /* less than 1 billion; smaller => faster */
+#define        faster() (fallrate -= fallrate / 3000000)
+
+/*
+ * 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).
+ *
+ * If previewing has been turned on, the score is multiplied by PRE_PENALTY.
+ */
+#define PRE_PENALTY 0.75
+
+extern int     score;          /* the obvious thing */
+
+extern char    key_msg[100];
+extern int     showpreview;
+extern int     classic;
+
+int fits_in(const struct shape *, int);
+void place(const struct shape *, int, int);
+void stop(const char *);