Importing games/tetris

No Minix specific changes needed.

Change-Id: Ia6a2d9d2192ae0cee29e395aa42271005c6179c2
This commit is contained in:
Thomas Cort 2014-03-22 06:14:14 -04:00 committed by Lionel Sambuc
parent 18f97ad415
commit 2f98b65abf
17 changed files with 2537 additions and 2 deletions

View file

@ -569,6 +569,7 @@
./usr/games/primes minix-sys
./usr/games/random minix-sys
./usr/games/strfile minix-sys
./usr/games/tetris minix-sys
./usr/games/unstr minix-sys
./usr/games/wargames minix-sys
./usr/include minix-sys
@ -4770,6 +4771,7 @@
./usr/man/man6/ppt.6 minix-sys
./usr/man/man6/primes.6 minix-sys
./usr/man/man6/random.6 minix-sys
./usr/man/man6/tetris.6
./usr/man/man6/wargames.6 minix-sys
./usr/man/man7 minix-sys
./usr/man/man7/ascii.7 minix-sys
@ -5946,6 +5948,8 @@
./var/db/obsolete minix-sys
./var/db/obsolete/minix minix-sys
./var/db/obsolete/tests minix-sys kyua,atf
./var/games minix-sys
./var/games/tetris.scores minix-sys
./var/log minix-sys
./var/mail minix-sys
./var/run minix-sys

View file

@ -350,6 +350,19 @@ install-etc-files-safe: .PHONY .MAKE check_DESTDIR MAKEDEV
${_MKMSG_INSTALL} ${DESTDIR}/dev/MAKEDEV
${ETC_INSTALL_OBJ_FILE} -o ${BINOWN} -g ${BINGRP} -m 555 \
MAKEDEV ${DESTDIR}/dev
.else
.for owner group mode file in \
games games 664 /var/games/tetris.scores
${_MKMSG_INSTALL} ${DESTDIR}${file}
if [ ! -e ${DESTDIR}${file} -o -s ${DESTDIR}${file} ]; then \
${ETC_INSTALL_FILE} -o ${owner} -g ${group} -m ${mode} \
/dev/null ${DESTDIR}${file}; \
else true; fi
.endfor
.endif # !defined(__MINIX)
# TAC as software is imported, move the files from the .for block below
# TAC to the .for block above.
.if !defined(__MINIX)
.for owner group mode file in \
${BINOWN} operator 664 /etc/dumpdates \
${BINOWN} operator 600 /etc/skeykeys \
@ -383,7 +396,6 @@ install-etc-files-safe: .PHONY .MAKE check_DESTDIR MAKEDEV
games games 664 /var/games/saillog \
games games 664 /var/games/snakerawscores \
games games 664 /var/games/snake.log \
games games 664 /var/games/tetris.scores
${_MKMSG_INSTALL} ${DESTDIR}${file}
if [ ! -e ${DESTDIR}${file} -o -s ${DESTDIR}${file} ]; then \
${ETC_INSTALL_FILE} -o ${owner} -g ${group} -m ${mode} \

View file

@ -157,6 +157,7 @@
./var
./var/db
./var/db/obsolete
./var/games uname=games gname=games mode=0775
./var/mail
./var/run

View file

@ -13,7 +13,7 @@ SUBDIR= adventure arithmetic \
factor fortune \
monop morse number \
pig ppt primes \
random \
random tetris \
wargames
.if !defined(__MINIX)

12
games/tetris/Makefile Normal file
View file

@ -0,0 +1,12 @@
# $NetBSD: Makefile,v 1.7 2010/02/03 15:34:39 roy Exp $
# @(#)Makefile 8.1 (Berkeley) 5/31/93
PROG= tetris
SRCS= input.c screen.c shapes.c scores.c tetris.c
MAN= tetris.6
DPADD= ${LIBTERMINFO}
LDADD= -lterminfo
HIDEGAME=hidegame
SETGIDGAME=yes
.include <bsd.prog.mk>

162
games/tetris/input.c Normal file
View file

@ -0,0 +1,162 @@
/* $NetBSD: input.c,v 1.11 2009/05/25 04:33:53 dholland 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/types.h>
#include <sys/time.h>
#include <sys/poll.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': poll 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 poll is interrupted.
*
* Return 0 => no input, 1 => can read() from stdin
*/
int
rwait(struct timeval *tvp)
{
struct pollfd set[1];
struct timeval starttv, endtv;
int timeout;
#define NILTZ ((struct timezone *)0)
if (tvp) {
(void) gettimeofday(&starttv, NILTZ);
endtv = *tvp;
timeout = tvp->tv_sec * 1000 + tvp->tv_usec / 1000;
} else
timeout = INFTIM;
again:
set[0].fd = STDIN_FILENO;
set[0].events = POLLIN;
switch (poll(set, 1, timeout)) {
case -1:
if (tvp == 0)
return (-1);
if (errno == EINTR)
goto again;
stop("poll failed, help");
/* NOTREACHED */
case 0: /* timed out */
if (tvp) {
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.
* Eat any input that might be available.
*/
void
tsleep(void)
{
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;
}
/*
* getchar with timeout.
*/
int
tgetchar(void)
{
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);
}

39
games/tetris/input.h Normal file
View file

@ -0,0 +1,39 @@
/* $NetBSD: input.h,v 1.5 2004/01/27 20:30:30 jsm 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
*/
int rwait(struct timeval *);
int tgetchar(void);
void tsleep(void);

37
games/tetris/pathnames.h Normal file
View file

@ -0,0 +1,37 @@
/* $NetBSD: pathnames.h,v 1.3 2003/08/07 09:37:48 agc 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.
*
* @(#)pathnames.h 8.1 (Berkeley) 5/31/93
*/
#define _PATH_SCOREFILE "/var/games/tetris.scores"

956
games/tetris/scores.c Normal file
View file

@ -0,0 +1,956 @@
/* $NetBSD: scores.c,v 1.21 2013/10/19 17:23:08 christos 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 <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <term.h>
#include <unistd.h>
#include "pathnames.h"
#include "screen.h"
#include "scores.h"
#include "tetris.h"
/*
* Allow updating the high scores unless we're built as part of /rescue.
*/
#ifndef RESCUEDIR
#define ALLOW_SCORE_UPDATES
#endif
/*
* 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(struct highscore *, int);
static int cmpscores(const void *, const void *);
static void getscores(int *);
static void printem(int, int, struct highscore *, int, const char *);
static char *thisuser(void);
/* contents chosen to be a highly illegal username */
static const char hsh_magic_val[HSH_MAGIC_SIZE] = "//:\0\0://";
#define HSH_ENDIAN_NATIVE 0x12345678
#define HSH_ENDIAN_OPP 0x78563412
/* current file format version */
#define HSH_VERSION 1
/* codes for scorefile_probe return */
#define SCOREFILE_ERROR (-1)
#define SCOREFILE_CURRENT 0 /* 40-byte */
#define SCOREFILE_CURRENT_OPP 1 /* 40-byte, opposite-endian */
#define SCOREFILE_599 2 /* 36-byte */
#define SCOREFILE_599_OPP 3 /* 36-byte, opposite-endian */
#define SCOREFILE_50 4 /* 32-byte */
#define SCOREFILE_50_OPP 5 /* 32-byte, opposite-endian */
/*
* Check (or guess) what kind of score file contents we have.
*/
static int
scorefile_probe(int sd)
{
struct stat st;
int t1, t2, t3, tx;
ssize_t result;
uint32_t numbers[3], offset56, offset60, offset64;
if (fstat(sd, &st) < 0) {
warn("Score file %s: fstat", _PATH_SCOREFILE);
return -1;
}
t1 = st.st_size % sizeof(struct highscore_ondisk) == 0;
t2 = st.st_size % sizeof(struct highscore_ondisk_599) == 0;
t3 = st.st_size % sizeof(struct highscore_ondisk_50) == 0;
tx = t1 + t2 + t3;
if (tx == 1) {
/* Size matches exact number of one kind of records */
if (t1) {
return SCOREFILE_CURRENT;
} else if (t2) {
return SCOREFILE_599;
} else {
return SCOREFILE_50;
}
} else if (tx == 0) {
/* Size matches nothing, pick most likely as default */
goto wildguess;
}
/*
* File size is multiple of more than one structure size.
* (For example, 288 bytes could be 9*hso50 or 8*hso599.)
* Read the file and see if we can figure out what's going
* on. This is the layout of the first two records:
*
* offset hso / current hso_599 hso_50
* (40-byte) (36-byte) (32-byte)
*
* 0 name #0 name #0 name #0
* 4 : : :
* 8 : : :
* 12 : : :
* 16 : : :
* 20 score #0 score #0 score #0
* 24 level #0 level #0 level #0
* 28 (pad) time #0 time #0
* 32 time #0 name #1
* 36 name #1 :
* 40 name #1 : :
* 44 : : :
* 48 : : :
* 52 : : score #1
* 56 : score #1 level #1
* 60 score #1 level #1 time #1
* 64 level #1 time #1 name #2
* 68 (pad) : :
* 72 time #1 name #2 :
* 76 : : :
* 80 --- end ---
*
* There are a number of things we could check here, but the
* most effective test is based on the following restrictions:
*
* - The level must be between 1 and 9 (inclusive)
* - All times must be after 1985 and are before 2038,
* so the high word must be 0 and the low word may not be
* a small value.
* - Integer values of 0 or 1-9 cannot be the beginning of
* a login name string.
* - Values of 1-9 are probably not a score.
*
* So we read the three words at offsets 56, 60, and 64, and
* poke at the values to try to figure things...
*/
if (lseek(sd, 56, SEEK_SET) < 0) {
warn("Score file %s: lseek", _PATH_SCOREFILE);
return -1;
}
result = read(sd, &numbers, sizeof(numbers));
if (result < 0) {
warn("Score file %s: read", _PATH_SCOREFILE);
return -1;
}
if ((size_t)result != sizeof(numbers)) {
/*
* The smallest file whose size divides by more than
* one of the sizes is substantially larger than 64,
* so this should *never* happen.
*/
warnx("Score file %s: Unexpected EOF", _PATH_SCOREFILE);
return -1;
}
offset56 = numbers[0];
offset60 = numbers[1];
offset64 = numbers[2];
if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) {
/* 40-byte structure */
return SCOREFILE_CURRENT;
} else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) {
/* 36-byte structure */
return SCOREFILE_599;
} else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) {
/* 32-byte structure */
return SCOREFILE_50;
}
/* None was a valid level; try opposite endian */
offset64 = bswap32(offset64);
offset60 = bswap32(offset60);
offset56 = bswap32(offset56);
if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) {
/* 40-byte structure */
return SCOREFILE_CURRENT_OPP;
} else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) {
/* 36-byte structure */
return SCOREFILE_599_OPP;
} else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) {
/* 32-byte structure */
return SCOREFILE_50_OPP;
}
/* That didn't work either, dunno what's going on */
wildguess:
warnx("Score file %s is likely corrupt", _PATH_SCOREFILE);
if (sizeof(void *) == 8 && sizeof(time_t) == 8) {
return SCOREFILE_CURRENT;
} else if (sizeof(time_t) == 8) {
return SCOREFILE_599;
} else {
return SCOREFILE_50;
}
}
/*
* Copy a string safely, making sure it's null-terminated.
*/
static void
readname(char *to, size_t maxto, const char *from, size_t maxfrom)
{
size_t amt;
amt = maxto < maxfrom ? maxto : maxfrom;
memcpy(to, from, amt);
to[maxto-1] = '\0';
}
/*
* Copy integers, byte-swapping if desired.
*/
static int32_t
read32(int32_t val, int doflip)
{
if (doflip) {
val = bswap32(val);
}
return val;
}
static int64_t
read64(int64_t val, int doflip)
{
if (doflip) {
val = bswap64(val);
}
return val;
}
/*
* Read up to MAXHISCORES scorefile_ondisk entries.
*/
static int
readscores(int sd, int doflip)
{
struct highscore_ondisk buf[MAXHISCORES];
ssize_t result;
int i;
result = read(sd, buf, sizeof(buf));
if (result < 0) {
warn("Score file %s: read", _PATH_SCOREFILE);
return -1;
}
nscores = result / sizeof(buf[0]);
for (i=0; i<nscores; i++) {
readname(scores[i].hs_name, sizeof(scores[i].hs_name),
buf[i].hso_name, sizeof(buf[i].hso_name));
scores[i].hs_score = read32(buf[i].hso_score, doflip);
scores[i].hs_level = read32(buf[i].hso_level, doflip);
scores[i].hs_time = read64(buf[i].hso_time, doflip);
}
return 0;
}
/*
* Read up to MAXHISCORES scorefile_ondisk_599 entries.
*/
static int
readscores599(int sd, int doflip)
{
struct highscore_ondisk_599 buf[MAXHISCORES];
ssize_t result;
int i;
result = read(sd, buf, sizeof(buf));
if (result < 0) {
warn("Score file %s: read", _PATH_SCOREFILE);
return -1;
}
nscores = result / sizeof(buf[0]);
for (i=0; i<nscores; i++) {
readname(scores[i].hs_name, sizeof(scores[i].hs_name),
buf[i].hso599_name, sizeof(buf[i].hso599_name));
scores[i].hs_score = read32(buf[i].hso599_score, doflip);
scores[i].hs_level = read32(buf[i].hso599_level, doflip);
/*
* Don't bother pasting the time together into a
* 64-bit value; just take whichever half is nonzero.
*/
scores[i].hs_time =
read32(buf[i].hso599_time[buf[i].hso599_time[0] == 0],
doflip);
}
return 0;
}
/*
* Read up to MAXHISCORES scorefile_ondisk_50 entries.
*/
static int
readscores50(int sd, int doflip)
{
struct highscore_ondisk_50 buf[MAXHISCORES];
ssize_t result;
int i;
result = read(sd, buf, sizeof(buf));
if (result < 0) {
warn("Score file %s: read", _PATH_SCOREFILE);
return -1;
}
nscores = result / sizeof(buf[0]);
for (i=0; i<nscores; i++) {
readname(scores[i].hs_name, sizeof(scores[i].hs_name),
buf[i].hso50_name, sizeof(buf[i].hso50_name));
scores[i].hs_score = read32(buf[i].hso50_score, doflip);
scores[i].hs_level = read32(buf[i].hso50_level, doflip);
scores[i].hs_time = read32(buf[i].hso50_time, doflip);
}
return 0;
}
/*
* 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 *fdp to an open file handle 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.
*/
static void
getscores(int *fdp)
{
struct highscore_header header;
int sd, mint, lck;
mode_t mask;
const char *human;
int doflip;
ssize_t result;
#ifdef ALLOW_SCORE_UPDATES
if (fdp != NULL) {
mint = O_RDWR | O_CREAT;
human = "read/write";
lck = LOCK_EX;
} else
#endif
{
mint = O_RDONLY;
human = "reading";
lck = LOCK_SH;
}
setegid(egid);
mask = umask(S_IWOTH);
sd = open(_PATH_SCOREFILE, mint, 0666);
(void)umask(mask);
setegid(gid);
if (sd < 0) {
/*
* If the file simply isn't there because nobody's
* played yet, and we aren't going to be trying to
* update it, don't warn. Even if we are going to be
* trying to write it, don't fail -- we can still show
* the player the score they got.
*/
if (fdp != NULL || errno != ENOENT) {
warn("Cannot open %s for %s", _PATH_SCOREFILE, human);
}
goto fail;
}
/*
* Grab a lock.
* XXX: failure here should probably be more fatal than this.
*/
if (flock(sd, lck))
warn("warning: score file %s cannot be locked",
_PATH_SCOREFILE);
/*
* The current format (since -current of 20090525) is
*
* struct highscore_header
* up to MAXHIGHSCORES x struct highscore_ondisk
*
* Before this, there is no header, and the contents
* might be any of three formats:
*
* highscore_ondisk (64-bit machines with 64-bit time_t)
* highscore_ondisk_599 (32-bit machines with 64-bit time_t)
* highscore_ondisk_50 (32-bit machines with 32-bit time_t)
*
* The first two appear in 5.99 between the time_t change and
* 20090525, depending on whether the compiler inserts
* structure padding before an unaligned 64-bit time_t. The
* last appears in 5.0 and earlier.
*
* Any or all of these might also appear on other OSes where
* this code has been ported.
*
* Since the old file has no header, we will have to guess
* which of these formats it has.
*/
/*
* First, look for a header.
*/
result = read(sd, &header, sizeof(header));
if (result < 0) {
warn("Score file %s: read", _PATH_SCOREFILE);
goto sdfail;
}
if (result != 0 && (size_t)result != sizeof(header)) {
warnx("Score file %s: read: unexpected EOF", _PATH_SCOREFILE);
/*
* File is hopelessly corrupt, might as well truncate it
* and start over with empty scores.
*/
if (lseek(sd, 0, SEEK_SET) < 0) {
/* ? */
warn("Score file %s: lseek", _PATH_SCOREFILE);
goto sdfail;
}
if (ftruncate(sd, 0) == 0) {
result = 0;
} else {
goto sdfail;
}
}
if (result == 0) {
/* Empty file; that just means there are no scores. */
nscores = 0;
} else {
/*
* Is what we read a header, or the first 16 bytes of
* a score entry? hsh_magic_val is chosen to be
* something that is extremely unlikely to appear in
* hs_name[].
*/
if (!memcmp(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE)) {
/* Yes, we have a header. */
if (header.hsh_endiantag == HSH_ENDIAN_NATIVE) {
/* native endian */
doflip = 0;
} else if (header.hsh_endiantag == HSH_ENDIAN_OPP) {
doflip = 1;
} else {
warnx("Score file %s: Unknown endian tag %u",
_PATH_SCOREFILE, header.hsh_endiantag);
goto sdfail;
}
if (header.hsh_version != HSH_VERSION) {
warnx("Score file %s: Unknown version code %u",
_PATH_SCOREFILE, header.hsh_version);
goto sdfail;
}
if (readscores(sd, doflip) < 0) {
goto sdfail;
}
} else {
/*
* Ok, it wasn't a header. Try to figure out what
* size records we have.
*/
result = scorefile_probe(sd);
if (lseek(sd, 0, SEEK_SET) < 0) {
warn("Score file %s: lseek", _PATH_SCOREFILE);
goto sdfail;
}
switch (result) {
case SCOREFILE_CURRENT:
result = readscores(sd, 0 /* don't flip */);
break;
case SCOREFILE_CURRENT_OPP:
result = readscores(sd, 1 /* do flip */);
break;
case SCOREFILE_599:
result = readscores599(sd, 0 /* don't flip */);
break;
case SCOREFILE_599_OPP:
result = readscores599(sd, 1 /* do flip */);
break;
case SCOREFILE_50:
result = readscores50(sd, 0 /* don't flip */);
break;
case SCOREFILE_50_OPP:
result = readscores50(sd, 1 /* do flip */);
break;
default:
goto sdfail;
}
if (result < 0) {
goto sdfail;
}
}
}
if (fdp)
*fdp = sd;
else
close(sd);
return;
sdfail:
close(sd);
fail:
if (fdp != NULL) {
*fdp = -1;
}
nscores = 0;
}
#ifdef ALLOW_SCORE_UPDATES
/*
* Paranoid write wrapper; unlike fwrite() it preserves errno.
*/
static int
dowrite(int sd, const void *vbuf, size_t len)
{
const char *buf = vbuf;
ssize_t result;
size_t done = 0;
while (done < len) {
result = write(sd, buf+done, len-done);
if (result < 0) {
if (errno == EINTR) {
continue;
}
return -1;
}
done += result;
}
return 0;
}
#endif /* ALLOW_SCORE_UPDATES */
/*
* Write the score file out.
*/
static void
putscores(int sd)
{
#ifdef ALLOW_SCORE_UPDATES
struct highscore_header header;
struct highscore_ondisk buf[MAXHISCORES];
int i;
if (sd == -1) {
return;
}
memcpy(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE);
header.hsh_endiantag = HSH_ENDIAN_NATIVE;
header.hsh_version = HSH_VERSION;
for (i=0; i<nscores; i++) {
strncpy(buf[i].hso_name, scores[i].hs_name,
sizeof(buf[i].hso_name));
buf[i].hso_score = scores[i].hs_score;
buf[i].hso_level = scores[i].hs_level;
buf[i].hso_pad = 0xbaadf00d;
buf[i].hso_time = scores[i].hs_time;
}
if (lseek(sd, 0, SEEK_SET) < 0) {
warn("Score file %s: lseek", _PATH_SCOREFILE);
goto fail;
}
if (dowrite(sd, &header, sizeof(header)) < 0 ||
dowrite(sd, buf, sizeof(buf[0]) * nscores) < 0) {
warn("Score file %s: write", _PATH_SCOREFILE);
goto fail;
}
return;
fail:
warnx("high scores may be damaged");
#else
(void)sd;
#endif /* ALLOW_SCORE_UPDATES */
}
/*
* Close the score file.
*/
static void
closescores(int sd)
{
flock(sd, LOCK_UN);
close(sd);
}
/*
* Read and update the scores file with the current reults.
*/
void
savescore(int level)
{
struct highscore *sp;
int i;
int change;
int sd;
const char *me;
getscores(&sd);
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);
putscores(sd);
}
closescores(sd);
}
/*
* 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;
struct passwd *pw;
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;
memcpy(u, p, 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(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, int num)
{
struct highscore *sp;
int i, j, k, numnames;
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;
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;
}
}
if (sp->hs_level < NLEVELS && sp->hs_level >= 0)
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;
int i, n, c;
const char *me;
int levelfound[NLEVELS];
if (!gotscores)
getscores(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 && enter_standout_mode ? 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 (sp->hs_level < NLEVELS && sp->hs_level >= 0) {
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(int level, int offset, struct highscore *hs, int n, const char *me)
{
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;
}
sp = &hs[item];
(void)snprintf(buf, sizeof(buf),
"%3d%c %6d %-11s (%6d 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(enter_standout_mode);
highlight = 1;
}
(void)printf("%s", buf);
if (highlight) {
putpad(exit_standout_mode);
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');
}
}
}

87
games/tetris/scores.h Normal file
View file

@ -0,0 +1,87 @@
/* $NetBSD: scores.h,v 1.5 2009/05/25 08:33:57 dholland 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
*/
/*
* Tetris scores.
*/
/* Header for high score file. */
#define HSH_MAGIC_SIZE 8
struct highscore_header {
char hsh_magic[HSH_MAGIC_SIZE];
uint32_t hsh_endiantag;
uint32_t hsh_version;
};
/* Current on-disk high score record. */
struct highscore_ondisk {
char hso_name[20];
int32_t hso_score;
int32_t hso_level;
int32_t hso_pad;
int64_t hso_time;
};
/* 5.99.x after time_t change, on 32-bit machines */
struct highscore_ondisk_599 {
char hso599_name[20];
int32_t hso599_score;
int32_t hso599_level;
int32_t hso599_time[2];
};
/* 5.0 and earlier on-disk high score record. */
struct highscore_ondisk_50 {
char hso50_name[20];
int32_t hso50_score;
int32_t hso50_level;
int32_t hso50_time;
};
/* In-memory high score record. */
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(int);
void showscores(int);

402
games/tetris/screen.c Normal file
View file

@ -0,0 +1,402 @@
/* $NetBSD: screen.c,v 1.27 2011/10/03 12:32:28 roy 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/cdefs.h>
#include <sys/ioctl.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <term.h>
#include <termios.h>
#include <unistd.h>
#ifndef sigmask
#define sigmask(s) (1 << ((s) - 1))
#endif
#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) __dead;
/*
* 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) (void)fputs(s, stdout)
static void
moveto(int r, int c)
{
char *buf;
buf = tiparm(cursor_address, r, c);
if (buf != NULL)
putpad(buf);
}
/*
* Set up from termcap.
*/
void
scr_init(void)
{
setupterm(NULL, 0, NULL);
if (clear_screen == NULL)
stop("cannot clear screen");
if (cursor_address == NULL || cursor_up == NULL)
stop("cannot do random cursor positioning");
}
/* this foolery is needed to modify tty state `atomically' */
static jmp_buf scr_onstop;
static void
stopset(int sig)
{
sigset_t set;
(void) signal(sig, SIG_DFL);
(void) kill(getpid(), sig);
sigemptyset(&set);
sigaddset(&set, sig);
(void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0);
longjmp(scr_onstop, 1);
}
static void
scr_stop(int sig)
{
sigset_t set;
scr_end();
(void) kill(getpid(), sig);
sigemptyset(&set);
sigaddset(&set, sig);
(void) sigprocmask(SIG_UNBLOCK, &set, (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 nsigset, osigset;
void (*ttou)(int);
sigemptyset(&nsigset);
sigaddset(&nsigset, SIGTSTP);
sigaddset(&nsigset, SIGTTOU);
(void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
(void) signal(SIGTSTP, SIG_IGN);
if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
(void) 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.
*/
(void) setjmp(scr_onstop);
(void) 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 = lines;
if (Cols == 0)
Cols = columns;
if (Rows < MINROWS || Cols < MINCOLS) {
(void) fprintf(stderr,
"the screen is too small: must be at least %dx%d, ",
MINCOLS, MINROWS);
stop(""); /* stop() supplies \n */
}
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");
ospeed = cfgetospeed(&newtt);
(void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
/*
* We made it. We are now in screen mode, modulo TIstr
* (which we will fix immediately).
*/
if (enter_ca_mode)
putstr(enter_ca_mode);
if (cursor_invisible)
putstr(cursor_invisible);
if (tstp != SIG_IGN)
(void) signal(SIGTSTP, scr_stop);
if (ttou != SIG_IGN)
(void) signal(SIGTTOU, ttou);
isset = 1;
(void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
scr_clear();
}
/*
* End screen mode.
*/
void
scr_end(void)
{
sigset_t nsigset, osigset;
sigemptyset(&nsigset);
sigaddset(&nsigset, SIGTSTP);
sigaddset(&nsigset, SIGTTOU);
(void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
/* move cursor to last line */
if (cursor_to_ll)
putstr(cursor_to_ll);
else
moveto(Rows - 1, 0);
/* exit screen mode */
if (exit_ca_mode)
putstr(exit_ca_mode);
if (cursor_normal)
putstr(cursor_normal);
(void) fflush(stdout);
(void) tcsetattr(0, TCSADRAIN, &oldtt);
isset = 0;
/* restore signals */
(void) signal(SIGTSTP, tstp);
(void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
}
void
stop(const 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(void)
{
putpad(clear_screen);
curscore = -1;
memset((char *)curscreen, 0, 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(void)
{
cell *bp, *sp;
regcell so, cur_so = 0;
int i, ccol, j;
sigset_t nsigset, osigset;
static const struct shape *lastshape;
sigemptyset(&nsigset);
sigaddset(&nsigset, SIGTSTP);
(void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
/* always leave cursor after last displayed point */
curscreen[D_LAST * B_COLS - 1] = -1;
if (score != curscore) {
if (cursor_home)
putpad(cursor_home);
else
moveto(0, 0);
(void) printf("Score: %d", score);
curscore = score;
}
/* draw preview of nextpattern */
if (showpreview && (nextshape != lastshape)) {
static int r=5, c=2;
int tr, tc, t;
lastshape = nextshape;
/* clean */
putpad(exit_standout_mode);
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 */
putpad(enter_standout_mode);
moveto(r, 2*c);
putstr(" ");
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(" ");
}
putpad(exit_standout_mode);
}
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 && move_standout_mode) {
putpad(exit_standout_mode);
cur_so = 0;
}
moveto(RTOD(j), CTOD(i));
}
if (enter_standout_mode) {
if (so != cur_so) {
putpad(so ?
enter_standout_mode :
exit_standout_mode);
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(exit_standout_mode);
(void) fflush(stdout);
(void) 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 || clr_eol == NULL) {
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(clr_eol);
}
}

54
games/tetris/screen.h Normal file
View file

@ -0,0 +1,54 @@
/* $NetBSD: screen.h,v 1.9 2009/08/12 08:51:21 dholland 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);

105
games/tetris/shapes.c Normal file
View file

@ -0,0 +1,105 @@
/* $NetBSD: shapes.c,v 1.8 2009/05/25 04:33:53 dholland 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 <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 */
const 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, 2 } }, /* sticks out */
/* 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, 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;
}

156
games/tetris/tetris.6 Normal file
View file

@ -0,0 +1,156 @@
.\" $NetBSD: tetris.6,v 1.11 2005/09/15 02:09:42 wiz 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 May 31, 1993
.Dt TETRIS 6
.Os
.Sh NAME
.Nm tetris
.Nd the game of tetris
.Sh SYNOPSIS
.Nm
.Op Fl ps
.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 "xxspacexx" -compact -offset indent
.It j
move left
.It k
rotate 1/4 turn counterclockwise
.It l
move right
.It Aq 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.
.It Fl p
Switch on previewing of the shape that will appear next.
.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.)
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 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.
.Pp
Code for previewing next shape added by Hubert Feyrer in 1999.
.Sh BUGS
The higher levels are unplayable without a fast terminal connection.

334
games/tetris/tetris.c Normal file
View file

@ -0,0 +1,334 @@
/* $NetBSD: tetris.c,v 1.24 2011/08/31 16:24:56 plunky 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
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1992, 1993\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
/*
* Tetris (or however it is spelled).
*/
#include <sys/time.h>
#include <err.h>
#include <fcntl.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]; /* 1 => occupied, 0 => empty */
int Rows, Cols; /* current screen size */
static const struct shape *curshape;
const struct shape *nextshape;
long fallrate; /* less than 1 million; smaller => faster */
int score; /* the obvious thing */
gid_t gid, egid;
char key_msg[100];
int showpreview;
static void elide(void);
static void setup_board(void);
static void onintr(int) __dead;
static void usage(void) __dead;
/*
* 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 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 */
memset(&board[base], 0, B_COLS - 2);
scr_update();
tsleep();
while (--base != 0)
board[base + B_COLS] = board[base];
scr_update();
tsleep();
break;
}
}
}
}
int
main(int argc, char *argv[])
{
int pos, c;
const char *keys;
int level = 2;
char key_write[6][10];
int ch, i, j;
int fd;
gid = getgid();
egid = getegid();
setegid(gid);
fd = open("/dev/null", O_RDONLY);
if (fd < 3)
exit(1);
close(fd);
keys = "jkl pq";
while ((ch = getopt(argc, argv, "k:l:ps")) != -1)
switch(ch) {
case 'k':
if (strlen(keys = optarg) != 6)
usage();
break;
case 'l':
level = atoi(optarg);
if (level < MINLEVEL || level > MAXLEVEL) {
errx(1, "level must be from %d to %d",
MINLEVEL, MAXLEVEL);
}
break;
case 'p':
showpreview = 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]) {
errx(1, "duplicate command keys specified.");
}
}
if (keys[i] == ' ')
strcpy(key_write[i], "<space>");
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]);
(void)signal(SIGINT, onintr);
scr_init();
setup_board();
srandom(getpid());
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);
(void) 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[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();
(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);
}
static void
onintr(int signo __unused)
{
scr_clear();
scr_end();
exit(0);
}
static void
usage(void)
{
(void)fprintf(stderr, "usage: %s [-ps] [-k keys] [-l level]\n",
getprogname());
exit(1);
}

173
games/tetris/tetris.h Normal file
View file

@ -0,0 +1,173 @@
/* $NetBSD: tetris.h,v 1.12 2009/08/12 08:51:21 dholland 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
*/
#include <sys/types.h>
/*
* 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 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 const struct shape shapes[];
#define randshape() (&shapes[random() % 7])
extern const struct shape *nextshape;
/*
* 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.
*/
extern 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).
*/
extern int score; /* the obvious thing */
extern gid_t gid, egid;
extern char key_msg[100];
extern int showpreview;
int fits_in(const struct shape *, int);
void place(const struct shape *, int, int);
void stop(const char *) __dead;

View file

@ -82,6 +82,7 @@
2013/12/1 12:00:00,games/ppt
2013/12/1 12:00:00,games/primes
2013/12/1 12:00:00,games/random
2013/12/1 12:00:00,games/tetris
2013/12/1 12:00:00,games/wargames
2013/12/1 12:00:00,gnu/dist/texinfo
2013/12/1 12:00:00,gnu/usr.bin/texinfo