Blob


1 /* $NetBSD: tetris.c,v 1.34 2023/07/01 10:51:35 nia Exp $ */
3 /*-
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek and Darren F. Provine.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * @(#)tetris.c 8.1 (Berkeley) 5/31/93
35 */
37 #include <sys/cdefs.h>
38 #if !defined(lint) && !defined(__OpenBSD__) && !defined(__linux__)
39 __COPYRIGHT("@(#) Copyright (c) 1992, 1993\
40 The Regents of the University of California. All rights reserved.");
41 #endif /* not lint */
43 /*
44 * Tetris (or however it is spelled).
45 */
47 #include <sys/time.h>
49 #include <err.h>
50 #include <fcntl.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
57 #ifdef __linux__
58 # include <bsd/stdlib.h>
59 #endif
61 #include "input.h"
62 #include "scores.h"
63 #include "screen.h"
64 #include "tetris.h"
66 cell board[B_SIZE]; /* 1 => occupied, 0 => empty */
68 int Rows, Cols; /* current screen size */
69 int Offset; /* used to center board & shapes */
71 static const struct shape *curshape;
72 const struct shape *nextshape;
74 long fallrate; /* less than 1 million; smaller => faster */
76 int score; /* the obvious thing */
77 gid_t gid, egid;
79 char key_msg[100];
80 int showpreview;
81 int nocolor;
82 int dofaster;
84 static void elide(void);
85 static void setup_board(void);
86 static void onintr(int) __dead;
87 static void usage(void) __dead;
89 /*
90 * Set up the initial board. The bottom display row is completely set,
91 * along with another (hidden) row underneath that. Also, the left and
92 * right edges are set.
93 */
94 static void
95 setup_board(void)
96 {
97 int i;
98 cell *p;
100 p = board;
101 for (i = B_SIZE; i; i--)
102 *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 7 : 0;
105 /*
106 * Elide any full active rows.
107 */
108 static void
109 elide(void)
111 int i, j, base;
112 cell *p;
114 for (i = A_FIRST; i < A_LAST; i++) {
115 base = i * B_COLS + 1;
116 p = &board[base];
117 for (j = B_COLS - 2; *p++ != 0;) {
118 if (--j <= 0) {
119 /* this row is to be elided */
120 memset(&board[base], 0, B_COLS - 2);
121 scr_update();
122 tsleep();
123 while (--base != 0)
124 board[base + B_COLS] = board[base];
125 /* don't forget to clear 0th row */
126 memset(&board[1], 0, B_COLS - 2);
127 scr_update();
128 tsleep();
129 break;
135 int
136 main(int argc, char *argv[])
138 int pos, c;
139 const char *keys;
140 int level = 2;
141 #define NUMKEYS 7
142 char key_write[NUMKEYS][10];
143 char *nocolor_env;
144 int ch, i, j;
145 int fd;
147 gid = getgid();
148 egid = getegid();
149 setegid(gid);
151 fd = open("/dev/null", O_RDONLY);
152 if (fd < 3)
153 exit(1);
154 close(fd);
156 keys = "jkl pqn";
158 while ((ch = getopt(argc, argv, "bfk:l:ps")) != -1)
159 switch(ch) {
160 case 'b':
161 nocolor = 1;
162 break;
163 case 'f':
164 dofaster = 1;
165 break;
166 case 'k':
167 if (strlen(keys = optarg) != NUMKEYS)
168 usage();
169 break;
170 case 'l':
171 level = atoi(optarg);
172 if (level < MINLEVEL || level > MAXLEVEL) {
173 errx(1, "level must be from %d to %d",
174 MINLEVEL, MAXLEVEL);
176 break;
177 case 'p':
178 showpreview = 1;
179 break;
180 case 's':
181 showscores(0);
182 exit(0);
183 case '?':
184 default:
185 usage();
188 argc -= optind;
189 argv += optind;
191 if (argc)
192 usage();
194 nocolor_env = getenv("NO_COLOR");
196 if (nocolor_env != NULL && nocolor_env[0] != '\0')
197 nocolor = 1;
199 fallrate = 1000000 / level;
201 for (i = 0; i <= (NUMKEYS-1); i++) {
202 for (j = i+1; j <= (NUMKEYS-1); j++) {
203 if (keys[i] == keys[j]) {
204 errx(1, "duplicate command keys specified.");
207 if (keys[i] == ' ')
208 strcpy(key_write[i], "<space>");
209 else {
210 key_write[i][0] = keys[i];
211 key_write[i][1] = '\0';
215 snprintf(key_msg, sizeof(key_msg),
216 "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit %s - down",
217 key_write[0], key_write[1], key_write[2], key_write[3],
218 key_write[4], key_write[5], key_write[6]);
220 (void)signal(SIGINT, onintr);
221 scr_init();
222 setup_board();
224 scr_set();
226 pos = A_FIRST*B_COLS + (B_COLS/2)-1;
227 nextshape = randshape();
228 curshape = randshape();
230 scr_msg(key_msg, 1);
232 for (;;) {
233 place(curshape, pos, 1);
234 scr_update();
235 place(curshape, pos, 0);
236 c = tgetchar();
237 if (c < 0) {
238 /*
239 * Timeout. Move down if possible.
240 */
241 if (fits_in(curshape, pos + B_COLS)) {
242 pos += B_COLS;
243 continue;
246 /*
247 * Put up the current shape `permanently',
248 * bump score, and elide any full rows.
249 */
250 place(curshape, pos, 1);
251 score++;
252 elide();
254 /*
255 * Choose a new shape. If it does not fit,
256 * the game is over.
257 */
258 curshape = nextshape;
259 nextshape = randshape();
260 pos = A_FIRST*B_COLS + (B_COLS/2)-1;
261 if (!fits_in(curshape, pos))
262 break;
263 continue;
266 /*
267 * Handle command keys.
268 */
269 if (c == keys[5]) {
270 /* quit */
271 break;
273 if (c == keys[4]) {
274 static char msg[] =
275 "paused - press RETURN to continue";
277 place(curshape, pos, 1);
278 do {
279 scr_update();
280 scr_msg(key_msg, 0);
281 scr_msg(msg, 1);
282 (void) fflush(stdout);
283 } while (rwait(NULL) == -1);
284 scr_msg(msg, 0);
285 scr_msg(key_msg, 1);
286 place(curshape, pos, 0);
287 continue;
289 if (c == keys[0]) {
290 /* move left */
291 if (fits_in(curshape, pos - 1))
292 pos--;
293 continue;
295 if (c == keys[1]) {
296 /* turn */
297 const struct shape *new = &shapes[curshape->rot];
299 if (fits_in(new, pos))
300 curshape = new;
301 continue;
303 if (c == keys[2]) {
304 /* move right */
305 if (fits_in(curshape, pos + 1))
306 pos++;
307 continue;
309 if (c == keys[3]) {
310 /* move to bottom */
311 while (fits_in(curshape, pos + B_COLS)) {
312 pos += B_COLS;
313 score++;
315 continue;
317 if (c == keys[6]) {
318 /* move down */
319 if (fits_in(curshape, pos + B_COLS)) {
320 pos += B_COLS;
321 score++;
323 continue;
325 if (c == '\f') {
326 scr_clear();
327 scr_msg(key_msg, 1);
331 scr_clear();
332 scr_end();
334 (void)printf("Your score: %d point%s x level %d = %d\n",
335 score, score == 1 ? "" : "s", level, score * level);
336 savescore(level);
338 printf("\nHit RETURN to see high scores, ^C to skip.\n");
340 while ((i = getchar()) != '\n')
341 if (i == EOF)
342 break;
344 showscores(level);
346 exit(0);
349 static void
350 onintr(int signo)
352 (void)signo;
353 scr_clear();
354 scr_end();
355 exit(0);
358 static void
359 usage(void)
361 (void)fprintf(stderr, "usage: tetris [-bps] [-k keys] [-l level]\n");
362 exit(1);