Importing games/tetris
No Minix specific changes needed. Change-Id: Ia6a2d9d2192ae0cee29e395aa42271005c6179c2
This commit is contained in:
parent
18f97ad415
commit
2f98b65abf
17 changed files with 2537 additions and 2 deletions
|
@ -569,6 +569,7 @@
|
||||||
./usr/games/primes minix-sys
|
./usr/games/primes minix-sys
|
||||||
./usr/games/random minix-sys
|
./usr/games/random minix-sys
|
||||||
./usr/games/strfile minix-sys
|
./usr/games/strfile minix-sys
|
||||||
|
./usr/games/tetris minix-sys
|
||||||
./usr/games/unstr minix-sys
|
./usr/games/unstr minix-sys
|
||||||
./usr/games/wargames minix-sys
|
./usr/games/wargames minix-sys
|
||||||
./usr/include minix-sys
|
./usr/include minix-sys
|
||||||
|
@ -4770,6 +4771,7 @@
|
||||||
./usr/man/man6/ppt.6 minix-sys
|
./usr/man/man6/ppt.6 minix-sys
|
||||||
./usr/man/man6/primes.6 minix-sys
|
./usr/man/man6/primes.6 minix-sys
|
||||||
./usr/man/man6/random.6 minix-sys
|
./usr/man/man6/random.6 minix-sys
|
||||||
|
./usr/man/man6/tetris.6
|
||||||
./usr/man/man6/wargames.6 minix-sys
|
./usr/man/man6/wargames.6 minix-sys
|
||||||
./usr/man/man7 minix-sys
|
./usr/man/man7 minix-sys
|
||||||
./usr/man/man7/ascii.7 minix-sys
|
./usr/man/man7/ascii.7 minix-sys
|
||||||
|
@ -5946,6 +5948,8 @@
|
||||||
./var/db/obsolete minix-sys
|
./var/db/obsolete minix-sys
|
||||||
./var/db/obsolete/minix minix-sys
|
./var/db/obsolete/minix minix-sys
|
||||||
./var/db/obsolete/tests minix-sys kyua,atf
|
./var/db/obsolete/tests minix-sys kyua,atf
|
||||||
|
./var/games minix-sys
|
||||||
|
./var/games/tetris.scores minix-sys
|
||||||
./var/log minix-sys
|
./var/log minix-sys
|
||||||
./var/mail minix-sys
|
./var/mail minix-sys
|
||||||
./var/run minix-sys
|
./var/run minix-sys
|
||||||
|
|
14
etc/Makefile
14
etc/Makefile
|
@ -350,6 +350,19 @@ install-etc-files-safe: .PHONY .MAKE check_DESTDIR MAKEDEV
|
||||||
${_MKMSG_INSTALL} ${DESTDIR}/dev/MAKEDEV
|
${_MKMSG_INSTALL} ${DESTDIR}/dev/MAKEDEV
|
||||||
${ETC_INSTALL_OBJ_FILE} -o ${BINOWN} -g ${BINGRP} -m 555 \
|
${ETC_INSTALL_OBJ_FILE} -o ${BINOWN} -g ${BINGRP} -m 555 \
|
||||||
MAKEDEV ${DESTDIR}/dev
|
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 \
|
.for owner group mode file in \
|
||||||
${BINOWN} operator 664 /etc/dumpdates \
|
${BINOWN} operator 664 /etc/dumpdates \
|
||||||
${BINOWN} operator 600 /etc/skeykeys \
|
${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/saillog \
|
||||||
games games 664 /var/games/snakerawscores \
|
games games 664 /var/games/snakerawscores \
|
||||||
games games 664 /var/games/snake.log \
|
games games 664 /var/games/snake.log \
|
||||||
games games 664 /var/games/tetris.scores
|
|
||||||
${_MKMSG_INSTALL} ${DESTDIR}${file}
|
${_MKMSG_INSTALL} ${DESTDIR}${file}
|
||||||
if [ ! -e ${DESTDIR}${file} -o -s ${DESTDIR}${file} ]; then \
|
if [ ! -e ${DESTDIR}${file} -o -s ${DESTDIR}${file} ]; then \
|
||||||
${ETC_INSTALL_FILE} -o ${owner} -g ${group} -m ${mode} \
|
${ETC_INSTALL_FILE} -o ${owner} -g ${group} -m ${mode} \
|
||||||
|
|
|
@ -157,6 +157,7 @@
|
||||||
./var
|
./var
|
||||||
./var/db
|
./var/db
|
||||||
./var/db/obsolete
|
./var/db/obsolete
|
||||||
|
./var/games uname=games gname=games mode=0775
|
||||||
./var/mail
|
./var/mail
|
||||||
./var/run
|
./var/run
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ SUBDIR= adventure arithmetic \
|
||||||
factor fortune \
|
factor fortune \
|
||||||
monop morse number \
|
monop morse number \
|
||||||
pig ppt primes \
|
pig ppt primes \
|
||||||
random \
|
random tetris \
|
||||||
wargames
|
wargames
|
||||||
|
|
||||||
.if !defined(__MINIX)
|
.if !defined(__MINIX)
|
||||||
|
|
12
games/tetris/Makefile
Normal file
12
games/tetris/Makefile
Normal 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
162
games/tetris/input.c
Normal 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
39
games/tetris/input.h
Normal 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
37
games/tetris/pathnames.h
Normal 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
956
games/tetris/scores.c
Normal 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
87
games/tetris/scores.h
Normal 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
402
games/tetris/screen.c
Normal 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
54
games/tetris/screen.h
Normal 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
105
games/tetris/shapes.c
Normal 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
156
games/tetris/tetris.6
Normal 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
334
games/tetris/tetris.c
Normal 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
173
games/tetris/tetris.h
Normal 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;
|
|
@ -82,6 +82,7 @@
|
||||||
2013/12/1 12:00:00,games/ppt
|
2013/12/1 12:00:00,games/ppt
|
||||||
2013/12/1 12:00:00,games/primes
|
2013/12/1 12:00:00,games/primes
|
||||||
2013/12/1 12:00:00,games/random
|
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,games/wargames
|
||||||
2013/12/1 12:00:00,gnu/dist/texinfo
|
2013/12/1 12:00:00,gnu/dist/texinfo
|
||||||
2013/12/1 12:00:00,gnu/usr.bin/texinfo
|
2013/12/1 12:00:00,gnu/usr.bin/texinfo
|
||||||
|
|
Loading…
Reference in a new issue