1*d92552c3Snia /* $NetBSD: tetris.c,v 1.34 2023/07/01 10:51:35 nia Exp $ */
2aaeacec8Scgd
3fece8b50Scgd /*-
4fece8b50Scgd * Copyright (c) 1992, 1993
5fece8b50Scgd * The Regents of the University of California. All rights reserved.
6fece8b50Scgd *
7fece8b50Scgd * This code is derived from software contributed to Berkeley by
8fece8b50Scgd * Chris Torek and Darren F. Provine.
9fece8b50Scgd *
10fece8b50Scgd * Redistribution and use in source and binary forms, with or without
11fece8b50Scgd * modification, are permitted provided that the following conditions
12fece8b50Scgd * are met:
13fece8b50Scgd * 1. Redistributions of source code must retain the above copyright
14fece8b50Scgd * notice, this list of conditions and the following disclaimer.
15fece8b50Scgd * 2. Redistributions in binary form must reproduce the above copyright
16fece8b50Scgd * notice, this list of conditions and the following disclaimer in the
17fece8b50Scgd * documentation and/or other materials provided with the distribution.
18e5aeb4eaSagc * 3. Neither the name of the University nor the names of its contributors
19fece8b50Scgd * may be used to endorse or promote products derived from this software
20fece8b50Scgd * without specific prior written permission.
21fece8b50Scgd *
22fece8b50Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23fece8b50Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24fece8b50Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25fece8b50Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26fece8b50Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27fece8b50Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28fece8b50Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29fece8b50Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30fece8b50Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31fece8b50Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32fece8b50Scgd * SUCH DAMAGE.
33fece8b50Scgd *
34fece8b50Scgd * @(#)tetris.c 8.1 (Berkeley) 5/31/93
35fece8b50Scgd */
36fece8b50Scgd
37161dd449Slukem #include <sys/cdefs.h>
38fece8b50Scgd #ifndef lint
392fe2731dSlukem __COPYRIGHT("@(#) Copyright (c) 1992, 1993\
402fe2731dSlukem The Regents of the University of California. All rights reserved.");
41fece8b50Scgd #endif /* not lint */
42fece8b50Scgd
43fece8b50Scgd /*
44fece8b50Scgd * Tetris (or however it is spelled).
45fece8b50Scgd */
46fece8b50Scgd
47fece8b50Scgd #include <sys/time.h>
48fece8b50Scgd
49233f519dSjsm #include <err.h>
505367f340Sjsm #include <fcntl.h>
51fece8b50Scgd #include <signal.h>
52fece8b50Scgd #include <stdio.h>
53fece8b50Scgd #include <stdlib.h>
54fece8b50Scgd #include <string.h>
55fece8b50Scgd #include <unistd.h>
56fece8b50Scgd
57fece8b50Scgd #include "input.h"
58fece8b50Scgd #include "scores.h"
59fece8b50Scgd #include "screen.h"
60fece8b50Scgd #include "tetris.h"
61fece8b50Scgd
62e2dbfa95Sjsm cell board[B_SIZE]; /* 1 => occupied, 0 => empty */
63e2dbfa95Sjsm
64e2dbfa95Sjsm int Rows, Cols; /* current screen size */
65ff18370bSnat int Offset; /* used to center board & shapes */
66e2dbfa95Sjsm
671e99780eSdholland static const struct shape *curshape;
68e2dbfa95Sjsm const struct shape *nextshape;
69e2dbfa95Sjsm
70e2dbfa95Sjsm long fallrate; /* less than 1 million; smaller => faster */
71e2dbfa95Sjsm
72e2dbfa95Sjsm int score; /* the obvious thing */
735367f340Sjsm gid_t gid, egid;
745367f340Sjsm
75e2dbfa95Sjsm char key_msg[100];
76e2dbfa95Sjsm int showpreview;
7768006fcaSpgoyette int nocolor;
78e2dbfa95Sjsm
79cb5fd834Sjsm static void elide(void);
80cb5fd834Sjsm static void setup_board(void);
811e99780eSdholland static void onintr(int) __dead;
821e99780eSdholland static void usage(void) __dead;
83fece8b50Scgd
84fece8b50Scgd /*
85fece8b50Scgd * Set up the initial board. The bottom display row is completely set,
86fece8b50Scgd * along with another (hidden) row underneath that. Also, the left and
87fece8b50Scgd * right edges are set.
88fece8b50Scgd */
89fece8b50Scgd static void
setup_board(void)90bbc67e0fSdholland setup_board(void)
91fece8b50Scgd {
9210b0b6bcSwiz int i;
9310b0b6bcSwiz cell *p;
94fece8b50Scgd
95fece8b50Scgd p = board;
96fece8b50Scgd for (i = B_SIZE; i; i--)
97a5d27926Schristos *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 7 : 0;
98fece8b50Scgd }
99fece8b50Scgd
100fece8b50Scgd /*
101fece8b50Scgd * Elide any full active rows.
102fece8b50Scgd */
103fece8b50Scgd static void
elide(void)104bbc67e0fSdholland elide(void)
105fece8b50Scgd {
10610b0b6bcSwiz int i, j, base;
10710b0b6bcSwiz cell *p;
108fece8b50Scgd
109fece8b50Scgd for (i = A_FIRST; i < A_LAST; i++) {
110fece8b50Scgd base = i * B_COLS + 1;
111fece8b50Scgd p = &board[base];
112fece8b50Scgd for (j = B_COLS - 2; *p++ != 0;) {
113fece8b50Scgd if (--j <= 0) {
114fece8b50Scgd /* this row is to be elided */
115e31adf91Sperry memset(&board[base], 0, B_COLS - 2);
116fece8b50Scgd scr_update();
117fece8b50Scgd tsleep();
118fece8b50Scgd while (--base != 0)
119fece8b50Scgd board[base + B_COLS] = board[base];
120099f0359Schristos /* don't forget to clear 0th row */
121099f0359Schristos memset(&board[1], 0, B_COLS - 2);
122fece8b50Scgd scr_update();
123fece8b50Scgd tsleep();
124fece8b50Scgd break;
125fece8b50Scgd }
126fece8b50Scgd }
127fece8b50Scgd }
128fece8b50Scgd }
129fece8b50Scgd
130fece8b50Scgd int
main(int argc,char * argv[])131bbc67e0fSdholland main(int argc, char *argv[])
132fece8b50Scgd {
13310b0b6bcSwiz int pos, c;
13410b0b6bcSwiz const char *keys;
13510b0b6bcSwiz int level = 2;
1362343aabbSmrg #define NUMKEYS 7
1372343aabbSmrg char key_write[NUMKEYS][10];
138*d92552c3Snia char *nocolor_env;
139fece8b50Scgd int ch, i, j;
1405367f340Sjsm int fd;
1415367f340Sjsm
1425367f340Sjsm gid = getgid();
1435367f340Sjsm egid = getegid();
1445367f340Sjsm setegid(gid);
1455367f340Sjsm
1465367f340Sjsm fd = open("/dev/null", O_RDONLY);
1475367f340Sjsm if (fd < 3)
1485367f340Sjsm exit(1);
1495367f340Sjsm close(fd);
150fece8b50Scgd
1512343aabbSmrg keys = "jkl pqn";
152fece8b50Scgd
153990812d6Spgoyette while ((ch = getopt(argc, argv, "bk:l:ps")) != -1)
154fece8b50Scgd switch(ch) {
155990812d6Spgoyette case 'b':
15668006fcaSpgoyette nocolor = 1;
15768006fcaSpgoyette break;
158fece8b50Scgd case 'k':
1592343aabbSmrg if (strlen(keys = optarg) != NUMKEYS)
160fece8b50Scgd usage();
161fece8b50Scgd break;
162fece8b50Scgd case 'l':
163fece8b50Scgd level = atoi(optarg);
164fece8b50Scgd if (level < MINLEVEL || level > MAXLEVEL) {
165233f519dSjsm errx(1, "level must be from %d to %d",
166fece8b50Scgd MINLEVEL, MAXLEVEL);
167fece8b50Scgd }
168fece8b50Scgd break;
1696cfbee59Shubertf case 'p':
1706cfbee59Shubertf showpreview = 1;
1716cfbee59Shubertf break;
172fece8b50Scgd case 's':
173fece8b50Scgd showscores(0);
174fece8b50Scgd exit(0);
175fece8b50Scgd case '?':
176fece8b50Scgd default:
177fece8b50Scgd usage();
178fece8b50Scgd }
179fece8b50Scgd
180fece8b50Scgd argc -= optind;
181fece8b50Scgd argv += optind;
182fece8b50Scgd
183fece8b50Scgd if (argc)
184fece8b50Scgd usage();
185fece8b50Scgd
186*d92552c3Snia nocolor_env = getenv("NO_COLOR");
187*d92552c3Snia
188*d92552c3Snia if (nocolor_env != NULL && nocolor_env[0] != '\0')
189*d92552c3Snia nocolor = 1;
190*d92552c3Snia
191fece8b50Scgd fallrate = 1000000 / level;
192fece8b50Scgd
1932343aabbSmrg for (i = 0; i <= (NUMKEYS-1); i++) {
1942343aabbSmrg for (j = i+1; j <= (NUMKEYS-1); j++) {
195fece8b50Scgd if (keys[i] == keys[j]) {
196233f519dSjsm errx(1, "duplicate command keys specified.");
197fece8b50Scgd }
198fece8b50Scgd }
199fece8b50Scgd if (keys[i] == ' ')
200fece8b50Scgd strcpy(key_write[i], "<space>");
201fece8b50Scgd else {
202fece8b50Scgd key_write[i][0] = keys[i];
203fece8b50Scgd key_write[i][1] = '\0';
204fece8b50Scgd }
205fece8b50Scgd }
206fece8b50Scgd
207b3988369Sdholland snprintf(key_msg, sizeof(key_msg),
2082343aabbSmrg "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit %s - down",
209fece8b50Scgd key_write[0], key_write[1], key_write[2], key_write[3],
2102343aabbSmrg key_write[4], key_write[5], key_write[6]);
211fece8b50Scgd
212fece8b50Scgd (void)signal(SIGINT, onintr);
213fece8b50Scgd scr_init();
214fece8b50Scgd setup_board();
215fece8b50Scgd
216fece8b50Scgd scr_set();
217fece8b50Scgd
218fece8b50Scgd pos = A_FIRST*B_COLS + (B_COLS/2)-1;
219f42c52beShubertf nextshape = randshape();
220fece8b50Scgd curshape = randshape();
221fece8b50Scgd
222fece8b50Scgd scr_msg(key_msg, 1);
223fece8b50Scgd
224fece8b50Scgd for (;;) {
225fece8b50Scgd place(curshape, pos, 1);
226fece8b50Scgd scr_update();
227fece8b50Scgd place(curshape, pos, 0);
228fece8b50Scgd c = tgetchar();
229fece8b50Scgd if (c < 0) {
230fece8b50Scgd /*
231fece8b50Scgd * Timeout. Move down if possible.
232fece8b50Scgd */
233fece8b50Scgd if (fits_in(curshape, pos + B_COLS)) {
234fece8b50Scgd pos += B_COLS;
235fece8b50Scgd continue;
236fece8b50Scgd }
237fece8b50Scgd
238fece8b50Scgd /*
239fece8b50Scgd * Put up the current shape `permanently',
240fece8b50Scgd * bump score, and elide any full rows.
241fece8b50Scgd */
242fece8b50Scgd place(curshape, pos, 1);
243fece8b50Scgd score++;
244fece8b50Scgd elide();
245fece8b50Scgd
246fece8b50Scgd /*
247fece8b50Scgd * Choose a new shape. If it does not fit,
248fece8b50Scgd * the game is over.
249fece8b50Scgd */
250f42c52beShubertf curshape = nextshape;
251f42c52beShubertf nextshape = randshape();
252fece8b50Scgd pos = A_FIRST*B_COLS + (B_COLS/2)-1;
253fece8b50Scgd if (!fits_in(curshape, pos))
254fece8b50Scgd break;
255fece8b50Scgd continue;
256fece8b50Scgd }
257fece8b50Scgd
258fece8b50Scgd /*
259fece8b50Scgd * Handle command keys.
260fece8b50Scgd */
261fece8b50Scgd if (c == keys[5]) {
262fece8b50Scgd /* quit */
263fece8b50Scgd break;
264fece8b50Scgd }
265fece8b50Scgd if (c == keys[4]) {
266fece8b50Scgd static char msg[] =
267fece8b50Scgd "paused - press RETURN to continue";
268fece8b50Scgd
269fece8b50Scgd place(curshape, pos, 1);
270fece8b50Scgd do {
271fece8b50Scgd scr_update();
272fece8b50Scgd scr_msg(key_msg, 0);
273fece8b50Scgd scr_msg(msg, 1);
274fece8b50Scgd (void) fflush(stdout);
2759f61b804Splunky } while (rwait(NULL) == -1);
276fece8b50Scgd scr_msg(msg, 0);
277fece8b50Scgd scr_msg(key_msg, 1);
278fece8b50Scgd place(curshape, pos, 0);
279fece8b50Scgd continue;
280fece8b50Scgd }
281fece8b50Scgd if (c == keys[0]) {
282fece8b50Scgd /* move left */
283fece8b50Scgd if (fits_in(curshape, pos - 1))
284fece8b50Scgd pos--;
285fece8b50Scgd continue;
286fece8b50Scgd }
287fece8b50Scgd if (c == keys[1]) {
288fece8b50Scgd /* turn */
289092d3130Sjsm const struct shape *new = &shapes[curshape->rot];
290fece8b50Scgd
291fece8b50Scgd if (fits_in(new, pos))
292fece8b50Scgd curshape = new;
293fece8b50Scgd continue;
294fece8b50Scgd }
295fece8b50Scgd if (c == keys[2]) {
296fece8b50Scgd /* move right */
297fece8b50Scgd if (fits_in(curshape, pos + 1))
298fece8b50Scgd pos++;
299fece8b50Scgd continue;
300fece8b50Scgd }
301fece8b50Scgd if (c == keys[3]) {
302fece8b50Scgd /* move to bottom */
303fece8b50Scgd while (fits_in(curshape, pos + B_COLS)) {
304fece8b50Scgd pos += B_COLS;
305fece8b50Scgd score++;
306fece8b50Scgd }
307fece8b50Scgd continue;
308fece8b50Scgd }
3092343aabbSmrg if (c == keys[6]) {
3102343aabbSmrg /* move down */
3112343aabbSmrg if (fits_in(curshape, pos + B_COLS)) {
3122343aabbSmrg pos += B_COLS;
3132343aabbSmrg score++;
3142343aabbSmrg }
3152343aabbSmrg continue;
3162343aabbSmrg }
317f42c52beShubertf if (c == '\f') {
318fece8b50Scgd scr_clear();
319f42c52beShubertf scr_msg(key_msg, 1);
320f42c52beShubertf }
321fece8b50Scgd }
322fece8b50Scgd
323fece8b50Scgd scr_clear();
324fece8b50Scgd scr_end();
325fece8b50Scgd
326fece8b50Scgd (void)printf("Your score: %d point%s x level %d = %d\n",
327fece8b50Scgd score, score == 1 ? "" : "s", level, score * level);
328fece8b50Scgd savescore(level);
329fece8b50Scgd
330fece8b50Scgd printf("\nHit RETURN to see high scores, ^C to skip.\n");
331fece8b50Scgd
332fece8b50Scgd while ((i = getchar()) != '\n')
333fece8b50Scgd if (i == EOF)
334fece8b50Scgd break;
335fece8b50Scgd
336fece8b50Scgd showscores(level);
337fece8b50Scgd
338fece8b50Scgd exit(0);
339fece8b50Scgd }
340fece8b50Scgd
3411e99780eSdholland static void
onintr(int signo __unused)342bbc67e0fSdholland onintr(int signo __unused)
343fece8b50Scgd {
344fece8b50Scgd scr_clear();
345fece8b50Scgd scr_end();
346fece8b50Scgd exit(0);
347fece8b50Scgd }
348fece8b50Scgd
3491e99780eSdholland static void
usage(void)350bbc67e0fSdholland usage(void)
351fece8b50Scgd {
352cd1ecdeaSdholland (void)fprintf(stderr, "usage: %s [-bps] [-k keys] [-l level]\n",
35366880467Spgoyette getprogname());
354fece8b50Scgd exit(1);
355fece8b50Scgd }
356