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__)
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 #include "input.h"
58 #include "scores.h"
59 #include "screen.h"
60 #include "tetris.h"
62 cell board[B_SIZE]; /* 1 => occupied, 0 => empty */
64 int Rows, Cols; /* current screen size */
65 int Offset; /* used to center board & shapes */
67 static const struct shape *curshape;
68 const struct shape *nextshape;
70 long fallrate; /* less than 1 million; smaller => faster */
72 int score; /* the obvious thing */
73 gid_t gid, egid;
75 char key_msg[100];
76 int showpreview;
77 int nocolor;
78 int dofaster;
80 static void elide(void);
81 static void setup_board(void);
82 static void onintr(int) __dead;
83 static void usage(void) __dead;
85 /*
86 * Set up the initial board. The bottom display row is completely set,
87 * along with another (hidden) row underneath that. Also, the left and
88 * right edges are set.
89 */
90 static void
91 setup_board(void)
92 {
93 int i;
94 cell *p;
96 p = board;
97 for (i = B_SIZE; i; i--)
98 *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 7 : 0;
99 }
101 /*
102 * Elide any full active rows.
103 */
104 static void
105 elide(void)
107 int i, j, base;
108 cell *p;
110 for (i = A_FIRST; i < A_LAST; i++) {
111 base = i * B_COLS + 1;
112 p = &board[base];
113 for (j = B_COLS - 2; *p++ != 0;) {
114 if (--j <= 0) {
115 /* this row is to be elided */
116 memset(&board[base], 0, B_COLS - 2);
117 scr_update();
118 tsleep();
119 while (--base != 0)
120 board[base + B_COLS] = board[base];
121 /* don't forget to clear 0th row */
122 memset(&board[1], 0, B_COLS - 2);
123 scr_update();
124 tsleep();
125 break;
131 int
132 main(int argc, char *argv[])
134 int pos, c;
135 const char *keys;
136 int level = 2;
137 #define NUMKEYS 7
138 char key_write[NUMKEYS][10];
139 char *nocolor_env;
140 int ch, i, j;
141 int fd;
143 gid = getgid();
144 egid = getegid();
145 setegid(gid);
147 fd = open("/dev/null", O_RDONLY);
148 if (fd < 3)
149 exit(1);
150 close(fd);
152 keys = "jkl pqn";
154 while ((ch = getopt(argc, argv, "bfk:l:ps")) != -1)
155 switch(ch) {
156 case 'b':
157 nocolor = 1;
158 break;
159 case 'f':
160 dofaster = 1;
161 break;
162 case 'k':
163 if (strlen(keys = optarg) != NUMKEYS)
164 usage();
165 break;
166 case 'l':
167 level = atoi(optarg);
168 if (level < MINLEVEL || level > MAXLEVEL) {
169 errx(1, "level must be from %d to %d",
170 MINLEVEL, MAXLEVEL);
172 break;
173 case 'p':
174 showpreview = 1;
175 break;
176 case 's':
177 showscores(0);
178 exit(0);
179 case '?':
180 default:
181 usage();
184 argc -= optind;
185 argv += optind;
187 if (argc)
188 usage();
190 nocolor_env = getenv("NO_COLOR");
192 if (nocolor_env != NULL && nocolor_env[0] != '\0')
193 nocolor = 1;
195 fallrate = 1000000 / level;
197 for (i = 0; i <= (NUMKEYS-1); i++) {
198 for (j = i+1; j <= (NUMKEYS-1); j++) {
199 if (keys[i] == keys[j]) {
200 errx(1, "duplicate command keys specified.");
203 if (keys[i] == ' ')
204 strcpy(key_write[i], "<space>");
205 else {
206 key_write[i][0] = keys[i];
207 key_write[i][1] = '\0';
211 snprintf(key_msg, sizeof(key_msg),
212 "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit %s - down",
213 key_write[0], key_write[1], key_write[2], key_write[3],
214 key_write[4], key_write[5], key_write[6]);
216 (void)signal(SIGINT, onintr);
217 scr_init();
218 setup_board();
220 scr_set();
222 pos = A_FIRST*B_COLS + (B_COLS/2)-1;
223 nextshape = randshape();
224 curshape = randshape();
226 scr_msg(key_msg, 1);
228 for (;;) {
229 place(curshape, pos, 1);
230 scr_update();
231 place(curshape, pos, 0);
232 c = tgetchar();
233 if (c < 0) {
234 /*
235 * Timeout. Move down if possible.
236 */
237 if (fits_in(curshape, pos + B_COLS)) {
238 pos += B_COLS;
239 continue;
242 /*
243 * Put up the current shape `permanently',
244 * bump score, and elide any full rows.
245 */
246 place(curshape, pos, 1);
247 score++;
248 elide();
250 /*
251 * Choose a new shape. If it does not fit,
252 * the game is over.
253 */
254 curshape = nextshape;
255 nextshape = randshape();
256 pos = A_FIRST*B_COLS + (B_COLS/2)-1;
257 if (!fits_in(curshape, pos))
258 break;
259 continue;
262 /*
263 * Handle command keys.
264 */
265 if (c == keys[5]) {
266 /* quit */
267 break;
269 if (c == keys[4]) {
270 static char msg[] =
271 "paused - press RETURN to continue";
273 place(curshape, pos, 1);
274 do {
275 scr_update();
276 scr_msg(key_msg, 0);
277 scr_msg(msg, 1);
278 (void) fflush(stdout);
279 } while (rwait(NULL) == -1);
280 scr_msg(msg, 0);
281 scr_msg(key_msg, 1);
282 place(curshape, pos, 0);
283 continue;
285 if (c == keys[0]) {
286 /* move left */
287 if (fits_in(curshape, pos - 1))
288 pos--;
289 continue;
291 if (c == keys[1]) {
292 /* turn */
293 const struct shape *new = &shapes[curshape->rot];
295 if (fits_in(new, pos))
296 curshape = new;
297 continue;
299 if (c == keys[2]) {
300 /* move right */
301 if (fits_in(curshape, pos + 1))
302 pos++;
303 continue;
305 if (c == keys[3]) {
306 /* move to bottom */
307 while (fits_in(curshape, pos + B_COLS)) {
308 pos += B_COLS;
309 score++;
311 continue;
313 if (c == keys[6]) {
314 /* move down */
315 if (fits_in(curshape, pos + B_COLS)) {
316 pos += B_COLS;
317 score++;
319 continue;
321 if (c == '\f') {
322 scr_clear();
323 scr_msg(key_msg, 1);
327 scr_clear();
328 scr_end();
330 (void)printf("Your score: %d point%s x level %d = %d\n",
331 score, score == 1 ? "" : "s", level, score * level);
332 savescore(level);
334 printf("\nHit RETURN to see high scores, ^C to skip.\n");
336 while ((i = getchar()) != '\n')
337 if (i == EOF)
338 break;
340 showscores(level);
342 exit(0);
345 static void
346 onintr(int signo __unused)
348 scr_clear();
349 scr_end();
350 exit(0);
353 static void
354 usage(void)
356 (void)fprintf(stderr, "usage: %s [-bps] [-k keys] [-l level]\n",
357 getprogname());
358 exit(1);