Tetris

Check-in [6bde378d89]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Initial revision
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | descendants | trunk
Files: files | file ages | folders
SHA3-256:6bde378d891c3fd623d0c4fedf0f4559aa7e8f6c1bf4f39a576d5e784d5ed352
User & Date: ajv-899-334-8894@vsta.org 2008-10-25 01:16:44
Context
2008-10-25
02:55
Add some comments check-in: 2a240b982d user: ajv-899-334-8894@vsta.org tags: trunk
01:16
Initial revision check-in: 6bde378d89 user: ajv-899-334-8894@vsta.org tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace

Added Makefile.















>
>
>
>
>
>
>
1
2
3
4
5
6
7
CFLAGS=-g

OBJS=input.o scores.o screen.o shapes.o tetris.o
LIBS=-lncurses

tetris: $(OBJS)
	$(CC) $(CFLAGS) -o tetris $(OBJS) $(LIBS)

Added input.c.













































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*	$NetBSD: input.c,v 1.9 2003/08/07 09:37:47 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.
 *
 *	@(#)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"

/* BSD compat */
#ifndef INFTIM
#define INFTIM (-1)
#endif

/* 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(tvp)
	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 */
		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()
{
	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()
{
	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);
}

Added input.h.















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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);

Added pathnames.h.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/*-
 * 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.
 */

#define _PATH_SCOREFILE	"/var/games/tetris"

Added scores.c.











































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
/*	$NetBSD: scores.c,v 1.13 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.
 *
 *	@(#)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/file.h>
#include <sys/stat.h>
#include <time.h>
#include <termcap.h>
#include <unistd.h>

#include "pathnames.h"
#include "screen.h"
#include "scores.h"
#include "tetris.h"

/*
 * Within this code, we can hang onto one extra "high score", leaving
 * room for our current score (whether or not it is high).
 *
 * We also sometimes keep tabs on the "highest" score on each level.
 * As long as the scores are kept sorted, this is simply the first one at
 * that level.
 */
#define NUMSPOTS (MAXHISCORES + 1)
#define	NLEVELS (MAXLEVEL + 1)

static time_t now;
static int nscores;
static int gotscores;
static struct highscore scores[NUMSPOTS];

static int checkscores(struct highscore *, int);
static int cmpscores(const void *, const void *);
static void getscores(FILE **);
static void printem(int, int, struct highscore *, int, const char *);
static char *thisuser(void);

/*
 * Read the score file.  Can be called from savescore (before showscores)
 * or showscores (if savescore will not be called).  If the given pointer
 * is not NULL, sets *fpp to an open file pointer that corresponds to a
 * read/write score file that is locked with LOCK_EX.  Otherwise, the
 * file is locked with LOCK_SH for the read and closed before return.
 *
 * Note, we assume closing the stdio file releases the lock.
 */
static void
getscores(fpp)
	FILE **fpp;
{
	int sd, mint, lck;
	mode_t mask;
	const char *mstr, *human;
	FILE *sf;

	if (fpp != NULL) {
		mint = O_RDWR | O_CREAT;
		mstr = "r+";
		human = "read/write";
		lck = LOCK_EX;
	} else {
		mint = O_RDONLY;
		mstr = "r";
		human = "reading";
		lck = LOCK_SH;
	}
	setegid(egid);
	mask = umask(S_IWOTH);
	sd = open(_PATH_SCOREFILE, mint, 0666);
	(void)umask(mask);
	if (sd < 0) {
		if (fpp == NULL) {
			nscores = 0;
			setegid(gid);
			return;
		}
		err(1, "cannot open %s for %s", _PATH_SCOREFILE, human);
	}
	if ((sf = fdopen(sd, mstr)) == NULL) {
		err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human);
	}
	setegid(gid);

	/*
	 * Grab a lock.
	 */
	if (flock(sd, lck))
		warn("warning: score file %s cannot be locked",
		    _PATH_SCOREFILE);

	nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf);
	if (ferror(sf)) {
		err(1, "error reading %s", _PATH_SCOREFILE);
	}

	if (fpp)
		*fpp = sf;
	else
		(void)fclose(sf);
}

void
savescore(level)
	int level;
{
	struct highscore *sp;
	int i;
	int change;
	FILE *sf;
	const char *me;

	getscores(&sf);
	gotscores = 1;
	(void)time(&now);

	/*
	 * Allow at most one score per person per level -- see if we
	 * can replace an existing score, or (easiest) do nothing.
	 * Otherwise add new score at end (there is always room).
	 */
	change = 0;
	me = thisuser();
	for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) {
		if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0)
			continue;
		if (score > sp->hs_score) {
			(void)printf("%s bettered %s %d score of %d!\n",
			    "\nYou", "your old level", level,
			    sp->hs_score * sp->hs_level);
			sp->hs_score = score;	/* new score */
			sp->hs_time = now;	/* and time */
			change = 1;
		} else if (score == sp->hs_score) {
			(void)printf("%s tied %s %d high score.\n",
			    "\nYou", "your old level", level);
			sp->hs_time = now;	/* renew it */
			change = 1;		/* gotta rewrite, sigh */
		} /* else new score < old score: do nothing */
		break;
	}
	if (i >= nscores) {
		strcpy(sp->hs_name, me);
		sp->hs_level = level;
		sp->hs_score = score;
		sp->hs_time = now;
		nscores++;
		change = 1;
	}

	if (change) {
		/*
		 * Sort & clean the scores, then rewrite.
		 */
		nscores = checkscores(scores, nscores);
		rewind(sf);
		if (fwrite(scores, sizeof(*sp), nscores, sf) != (size_t)nscores ||
		    fflush(sf) == EOF)
			warnx("error writing %s: %s -- %s",
			    _PATH_SCOREFILE, strerror(errno),
			    "high scores may be damaged");
	}
	(void)fclose(sf);	/* releases lock */
}

/*
 * Get login name, or if that fails, get something suitable.
 * The result is always trimmed to fit in a score.
 */
static char *
thisuser()
{
	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(x, y)
	const void *x, *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(hs, num)
	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(level)
	int level;
{
	struct highscore *sp;
	int i, n, c;
	const char *me;
	int levelfound[NLEVELS];

	if (!gotscores)
		getscores((FILE **)NULL);
	(void)printf("\n\t\t\t    Tetris High Scores\n");

	/*
	 * If level == 0, the person has not played a game but just asked for
	 * the high scores; we do not need to check for printing in highlight
	 * mode.  If SOstr is null, we can't do highlighting anyway.
	 */
	me = level && SOstr ? thisuser() : NULL;

	/*
	 * Set times to 0 except for high score on each level.
	 */
	for (i = MINLEVEL; i < NLEVELS; i++)
		levelfound[i] = 0;
	for (i = 0, sp = scores; i < nscores; i++, sp++) {
        if (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(level, offset, hs, n, me)
	int level, 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(SOstr);
				highlight = 1;
			}
			(void)printf("%s", buf);
			if (highlight) {
				putpad(SEstr);
				highlight = 0;
			}

			/* fill in spaces so column 1 lines up */
			if (col == 0)
				for (i = 40 - strlen(buf); --i >= 0;)
					(void)putchar(' ');
			else /* col == 1 */
				(void)putchar('\n');
		}
	}
}

Added scores.h.









































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*	$NetBSD: scores.h,v 1.4 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.
 *
 *	@(#)scores.h	8.1 (Berkeley) 5/31/93
 */

/*
 * Tetris scores.
 */
struct highscore {
	char	hs_name[20];	/* login name */
	int	hs_score;	/* raw score */
	int	hs_level;	/* play level */
	time_t	hs_time;	/* time at game end */
};

#define MAXHISCORES	80
#define MAXSCORES	9	/* maximum high score entries per person */
#define	EXPIRATION	(5L * 365 * 24 * 60 * 60)

void	savescore(int);
void	showscores(int);

Added screen.c.































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/*	$NetBSD: screen.c,v screen.c,v 1.19 2004/01/27 20:30:30 jsm Exp $	*/
/* For Linux: still using old termcap interface from version 1.13.  */

/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek and Darren F. Provine.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)screen.c	8.1 (Berkeley) 5/31/93
 */

/*
 * Tetris screen control.
 */

#include <sys/ioctl.h>

#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termcap.h>
#include <termios.h>
#include <unistd.h>

#ifndef sigmask
#define sigmask(s) (1 << ((s) - 1))
#endif

/* BSD compat */
#ifndef OXTABS
#define OXTABS XTABS
#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) __attribute__((__noreturn__));


/*
 * Capabilities from TERMCAP.
 */
extern char	PC, *BC, *UP;	/* tgoto requires globals: ugh! */
static char BCdefault[] = "\b";
#ifndef NCURSES_VERSION
short	ospeed;
#endif

static char
	*bcstr,			/* backspace char */
	*CEstr,			/* clear to end of line */
	*CLstr,			/* clear screen */
	*CMstr,			/* cursor motion string */
#ifdef unneeded
	*CRstr,			/* "\r" equivalent */
#endif
	*HOstr,			/* cursor home */
	*LLstr,			/* last line, first column */
	*pcstr,			/* pad character */
	*TEstr,			/* end cursor motion mode */
	*TIstr;			/* begin cursor motion mode */
char
	*SEstr,			/* end standout mode */
	*SOstr;			/* begin standout mode */
static int
	COnum,			/* co# value */
	LInum,			/* li# value */
	MSflag;			/* can move in standout mode */


struct tcsinfo {	/* termcap string info; some abbrevs above */
	char tcname[3];
	char **tcaddr;
} tcstrings[] = {
	{"bc", &bcstr},
	{"ce", &CEstr},
	{"cl", &CLstr},
	{"cm", &CMstr},
#ifdef unneeded
	{"cr", &CRstr},
#endif
	{"le", &BC},		/* move cursor left one space */
	{"pc", &pcstr},
	{"se", &SEstr},
	{"so", &SOstr},
	{"te", &TEstr},
	{"ti", &TIstr},
	{"up", &UP},		/* cursor up */
	{ {0}, NULL}
};

/* This is where we will actually stuff the information */

static char combuf[1024], tbuf[1024];


/*
 * Routine used by tputs().
 */
int
put(c)
	int c;
{

	return (putchar(c));
}

/*
 * putstr() is for unpadded strings (either as in termcap(5) or
 * simply literal strings); putpad() is for padded strings with
 * count=1.  (See screen.h for putpad().)
 */
#define	putstr(s)	(void)fputs(s, stdout)
#define	moveto(r, c)	putpad(tgoto(CMstr, c, r))

/*
 * Set up from termcap.
 */
void
scr_init()
{
	static int bsflag, xsflag, sgnum;
#ifdef unneeded
	static int ncflag;
#endif
	char *term, *fill;
	static struct tcninfo {	/* termcap numeric and flag info */
		char tcname[3];
		int *tcaddr;
	} tcflags[] = {
		{"bs", &bsflag},
		{"ms", &MSflag},
#ifdef unneeded
		{"nc", &ncflag},
#endif
		{"xs", &xsflag},
		{ {0}, NULL}
	}, tcnums[] = {
		{"co", &COnum},
		{"li", &LInum},
		{"sg", &sgnum},
		{ {0}, NULL}
	};
	
	if ((term = getenv("TERM")) == NULL)
		stop("you must set the TERM environment variable");
	if (tgetent(tbuf, term) <= 0)
		stop("cannot find your termcap");
	fill = combuf;
	{
		struct tcsinfo *p;

		for (p = tcstrings; p->tcaddr; p++)
			*p->tcaddr = tgetstr(p->tcname, &fill);
	}
	{
		struct tcninfo *p;

		for (p = tcflags; p->tcaddr; p++)
			*p->tcaddr = tgetflag(p->tcname);
		for (p = tcnums; p->tcaddr; p++)
			*p->tcaddr = tgetnum(p->tcname);
	}
	if (bsflag)
		BC = BCdefault;
	else if (BC == NULL && bcstr != NULL)
		BC = bcstr;
	if (CLstr == NULL)
		stop("cannot clear screen");
	if (CMstr == NULL || UP == NULL || BC == NULL)
		stop("cannot do random cursor positioning via tgoto()");
	PC = pcstr ? *pcstr : 0;
	if (sgnum >= 0 || xsflag)
		SOstr = SEstr = NULL;
#ifdef unneeded
	if (ncflag)
		CRstr = NULL;
	else if (CRstr == NULL)
		CRstr = "\r";
#endif
}

/* this foolery is needed to modify tty state `atomically' */
static jmp_buf scr_onstop;

static void
stopset(sig)
	int sig;
{
	sigset_t sigset;

	(void) signal(sig, SIG_DFL);
	(void) kill(getpid(), sig);
	sigemptyset(&sigset);
	sigaddset(&sigset, sig);
	(void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
	longjmp(scr_onstop, 1);
}

static void
scr_stop(sig)
	int sig;
{
	sigset_t sigset;

	scr_end();
	(void) kill(getpid(), sig);
	sigemptyset(&sigset);
	sigaddset(&sigset, sig);
	(void) sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *)0);
	scr_set();
	scr_msg(key_msg, 1);
}

/*
 * Set up screen mode.
 */
void
scr_set()
{
	struct winsize ws;
	struct termios newtt;
	sigset_t sigset, osigset;
	void (*ttou)(int);

	sigemptyset(&sigset);
	sigaddset(&sigset, SIGTSTP);
	sigaddset(&sigset, SIGTTOU);
	(void) sigprocmask(SIG_BLOCK, &sigset, &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 = LInum;
	if (Cols == 0)
	Cols = COnum;
	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;
	newtt.c_cc[VMIN] = 1;
	newtt.c_cc[VTIME] = 0;
	if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
		stop("tcsetattr() fails");
	ospeed = cfgetospeed(&newtt);
	(void) sigprocmask(SIG_BLOCK, &sigset, &osigset);

	/*
	 * We made it.  We are now in screen mode, modulo TIstr
	 * (which we will fix immediately).
	 */
	if (TIstr)
		putstr(TIstr);	/* termcap(5) says this is not padded */
	if (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()
{
	sigset_t sigset, osigset;

	sigemptyset(&sigset);
	sigaddset(&sigset, SIGTSTP);
	sigaddset(&sigset, SIGTTOU);
	(void) sigprocmask(SIG_BLOCK, &sigset, &osigset);
	/* move cursor to last line */
	if (LLstr)
		putstr(LLstr);	/* termcap(5) says this is not padded */
	else
		moveto(Rows - 1, 0);
	/* exit screen mode */
	if (TEstr)
		putstr(TEstr);	/* termcap(5) says this is not padded */
	(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(why)
	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()
{

	putpad(CLstr);
	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()
{
	cell *bp, *sp;
	regcell so, cur_so = 0;
	int i, ccol, j;
	sigset_t sigset, osigset;
	static const struct shape *lastshape;

	sigemptyset(&sigset);
	sigaddset(&sigset, SIGTSTP);
	(void) sigprocmask(SIG_BLOCK, &sigset, &osigset);

	/* always leave cursor after last displayed point */
	curscreen[D_LAST * B_COLS - 1] = -1;

	if (score != curscore) {
		if (HOstr)
			putpad(HOstr);
		else
			moveto(0, 0);
		(void) printf("Score: %d", score);
		curscore = score;
	}

	/* draw preview of nextpattern */
	if (showpreview && (nextshape != lastshape)) {
		int i;
		static int r=5, c=2;
		int tr, tc, t; 

		lastshape = nextshape;
		
		/* clean */
		putpad(SEstr);
		moveto(r-1, c-1); putstr("          ");
		moveto(r,   c-1); putstr("          ");
		moveto(r+1, c-1); putstr("          ");
		moveto(r+2, c-1); putstr("          ");

		moveto(r-3, c-2);
		putstr("Next shape:");
						
		/* draw */
		putpad(SOstr);
		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(SEstr);
	}
	
	bp = &board[D_FIRST * B_COLS];
	sp = &curscreen[D_FIRST * B_COLS];
	for (j = D_FIRST; j < D_LAST; j++) {
		ccol = -1;
		for (i = 0; i < B_COLS; bp++, sp++, i++) {
			if (*sp == (so = *bp))
				continue;
			*sp = so;
			if (i != ccol) {
				if (cur_so && MSflag) {
					putpad(SEstr);
					cur_so = 0;
				}
				moveto(RTOD(j), CTOD(i));
			}
			if (SOstr) {
				if (so != cur_so) {
					putpad(so ? SOstr : SEstr);
					cur_so = so;
				}
				putstr("  ");
			} else
				putstr(so ? "XX" : "  ");
			ccol = i + 1;
			/*
			 * Look ahead a bit, to avoid extra motion if
			 * we will be redrawing the cell after the next.
			 * Motion probably takes four or more characters,
			 * so we save even if we rewrite two cells
			 * `unnecessarily'.  Skip it all, though, if
			 * the next cell is a different color.
			 */
#define	STOP (B_COLS - 3)
			if (i > STOP || sp[1] != bp[1] || so != bp[1])
				continue;
			if (sp[2] != bp[2])
				sp[1] = -1;
			else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
				sp[2] = -1;
				sp[1] = -1;
			}
		}
	}
	if (cur_so)
		putpad(SEstr);
	(void) fflush(stdout);
	(void) 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(s, set)
	char *s;
	int set;
{
	
	if (set || CEstr == NULL) {
		int l = strlen(s);

		moveto(Rows - 2, ((Cols - l) >> 1) - 1);
		if (set)
			putstr(s);
		else
			while (--l >= 0)
				(void) putchar(' ');
	} else {
		moveto(Rows - 2, 0);
		putpad(CEstr);
	}
}

Added screen.h.













































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*	$NetBSD: screen.h,v 1.8 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.
 *
 *	@(#)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);

Added shapes.c.



























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*	$NetBSD: shapes.c,v 1.6 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.
 *
 *	@(#)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(shape, pos)
	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(shape, pos, onoff)
	const struct shape *shape;
	int pos, onoff;
{
	const int *o = shape->off;

	board[pos] = onoff;
	board[pos + *o++] = onoff;
	board[pos + *o++] = onoff;
	board[pos + *o] = onoff;
}

Added tetris.6.



























































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
.\"	$NetBSD: tetris.6,v 1.10 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
.\" 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
.Pp
.Sh PLAY
At the start of the game, a shape will appear at the top of the screen,
falling one square at a time.
The speed at which it falls is determined directly by the level:
if you select level 2, the blocks will fall twice per second;
at level 9, they fall 9 times per second.
(As the game goes on, things speed up,
no matter what your initial selection.)
When this shape
.Dq touches down
on the bottom of the field, another will appear at the top.
.Pp
You can move shapes to the left or right, rotate them counterclockwise,
or drop them to the bottom by pressing the appropriate keys.
As you fit them together, completed horizontal rows vanish,
and any blocks above fall down to fill in.
When the blocks stack up to the top of the screen, the game is over.
.Sh SCORING
You get one point for every block you fit into the stack,
and one point for every space a block falls when you hit the drop key.
(Dropping the blocks is therefore a good way to increase your score.)
Your total score is the product of the level of play
and your accumulated
.ie t points\(em200
.el points -- 200
points on level 3 gives you a score of 600.
Each player gets at most one entry on any level,
for a total of nine scores in the high scores file.
Players who no longer have accounts are limited to one score.
Also, scores over 5 years old are expired.
The exception to these conditions is that the highest score on a given
level is
.Em always
kept,
so that following generations can pay homage to those who have
wasted serious amounts of time.
.Pp
The score list is produced at the end of the game.
The printout includes each player's overall ranking,
name, score, and how many points were scored on what level.
Scores which are the highest on a given level
are marked with asterisks
.Dq * .
.Sh FILES
.Bl -tag -width @tetris_scorefile@xx
.It @tetris_scorefile@
high score file
.El
.Sh BUGS
The higher levels are unplayable without a fast terminal connection.
.Sh AUTHORS
Adapted from a 1989 International Obfuscated C Code Contest winner by
Chris Torek and Darren F. Provine.
.Pp
Manual adapted from the original entry written by Nancy L. Tinkham and
Darren F. Provine.
.Pp
Code for previewing next shape added by Hubert Feyrer in 1999.

Added tetris.c.



























































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*	$NetBSD: tetris.c,v 1.17 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.
 *
 *	@(#)tetris.c	8.1 (Berkeley) 5/31/93
 */

#include <sys/cdefs.h>

/*
 * 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 */

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);
	int	main(int, char **);
	void	onintr(int) __attribute__((__noreturn__));
	void	usage(void) __attribute__((__noreturn__));

/*
 * 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()
{
	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()
{
	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(argc, argv)
	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';
		}
	}

	sprintf(key_msg,
"%s - left   %s - rotate   %s - right   %s - drop   %s - pause   %s - quit",
		key_write[0], key_write[1], key_write[2], key_write[3],
		key_write[4], key_write[5]);

	(void)signal(SIGINT, onintr);
	scr_init();
	setup_board();

	srandom(getpid());
	scr_set();

	pos = A_FIRST*B_COLS + (B_COLS/2)-1;
	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((struct timeval *)NULL) == -1);
			scr_msg(msg, 0);
			scr_msg(key_msg, 1);
			place(curshape, pos, 0);
			continue;
		}
		if (c == keys[0]) {
			/* move left */
			if (fits_in(curshape, pos - 1))
				pos--;
			continue;
		}
		if (c == keys[1]) {
			/* turn */
			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);
}

void
onintr(signo)
	int signo __attribute__((__unused__));
{
	scr_clear();
	scr_end();
	exit(0);
}

void
usage()
{
	(void)fprintf(stderr, "usage: tetris-bsd [-ps] [-k keys] [-l level]\n");
	exit(1);
}

Added tetris.h.





























































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*	$NetBSD: tetris.h,v 1.10 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.
 *
 *	@(#)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 *curshape;
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 *) __attribute__((__noreturn__));