1*bc5a8259Sbeck /* $OpenBSD: tetris.c,v 1.35 2021/07/12 15:09:18 beck Exp $ */
2df930be7Sderaadt /* $NetBSD: tetris.c,v 1.2 1995/04/22 07:42:47 cgd Exp $ */
3df930be7Sderaadt
4df930be7Sderaadt /*-
5df930be7Sderaadt * Copyright (c) 1992, 1993
6df930be7Sderaadt * The Regents of the University of California. All rights reserved.
7df930be7Sderaadt *
8df930be7Sderaadt * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt * Chris Torek and Darren F. Provine.
10df930be7Sderaadt *
11df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt * modification, are permitted provided that the following conditions
13df930be7Sderaadt * are met:
14df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt * documentation and/or other materials provided with the distribution.
197a09557bSmillert * 3. Neither the name of the University nor the names of its contributors
20df930be7Sderaadt * may be used to endorse or promote products derived from this software
21df930be7Sderaadt * without specific prior written permission.
22df930be7Sderaadt *
23df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt * SUCH DAMAGE.
34df930be7Sderaadt *
35df930be7Sderaadt * @(#)tetris.c 8.1 (Berkeley) 5/31/93
36df930be7Sderaadt */
37df930be7Sderaadt
38df930be7Sderaadt /*
39df930be7Sderaadt * Tetris (or however it is spelled).
40df930be7Sderaadt */
41df930be7Sderaadt
42dcf934a0Spjanzen #include <err.h>
4341b00738Srob #include <errno.h>
4434278d36Sguenther #include <limits.h>
45df930be7Sderaadt #include <signal.h>
46df930be7Sderaadt #include <stdio.h>
47df930be7Sderaadt #include <stdlib.h>
48df930be7Sderaadt #include <string.h>
49df930be7Sderaadt #include <unistd.h>
50df930be7Sderaadt
51df930be7Sderaadt #include "input.h"
52df930be7Sderaadt #include "scores.h"
53df930be7Sderaadt #include "screen.h"
54df930be7Sderaadt #include "tetris.h"
55df930be7Sderaadt
56f26e040bStb #define NUMKEYS 6
57f26e040bStb
58b124352eSpjanzen cell board[B_SIZE];
59b124352eSpjanzen int Rows, Cols;
608e58c664Smickey const struct shape *curshape;
618e58c664Smickey const struct shape *nextshape;
62b124352eSpjanzen long fallrate;
63b124352eSpjanzen int score;
64b124352eSpjanzen char key_msg[100];
6541b00738Srob char scorepath[PATH_MAX];
6649cea301Smickey int showpreview, classic;
67b124352eSpjanzen
68c72b5b24Smillert static void elide(void);
69c72b5b24Smillert void onintr(int);
702010f3c8Smestre const struct shape *randshape(void);
712010f3c8Smestre static void setup_board(void);
72f0628b46Smestre __dead void usage(void);
73df930be7Sderaadt
74df930be7Sderaadt /*
75df930be7Sderaadt * Set up the initial board. The bottom display row is completely set,
76df930be7Sderaadt * along with another (hidden) row underneath that. Also, the left and
77df930be7Sderaadt * right edges are set.
78df930be7Sderaadt */
79df930be7Sderaadt static void
setup_board(void)80ff8320a7Sderaadt setup_board(void)
81df930be7Sderaadt {
8297419aa0Spjanzen int i;
8397419aa0Spjanzen cell *p;
84df930be7Sderaadt
85df930be7Sderaadt p = board;
86df930be7Sderaadt for (i = B_SIZE; i; i--)
87df930be7Sderaadt *p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2;
88df930be7Sderaadt }
89df930be7Sderaadt
90df930be7Sderaadt /*
91df930be7Sderaadt * Elide any full active rows.
92df930be7Sderaadt */
93df930be7Sderaadt static void
elide(void)94ff8320a7Sderaadt elide(void)
95df930be7Sderaadt {
96a7200f5aStedu int rows = 0;
9797419aa0Spjanzen int i, j, base;
9897419aa0Spjanzen cell *p;
99df930be7Sderaadt
100df930be7Sderaadt for (i = A_FIRST; i < A_LAST; i++) {
101df930be7Sderaadt base = i * B_COLS + 1;
102df930be7Sderaadt p = &board[base];
103df930be7Sderaadt for (j = B_COLS - 2; *p++ != 0;) {
104df930be7Sderaadt if (--j <= 0) {
105df930be7Sderaadt /* this row is to be elided */
106a7200f5aStedu rows++;
107dcf934a0Spjanzen memset(&board[base], 0, B_COLS - 2);
108df930be7Sderaadt scr_update();
109df930be7Sderaadt tsleep();
110df930be7Sderaadt while (--base != 0)
111df930be7Sderaadt board[base + B_COLS] = board[base];
112ca027646Stb memset(&board[1], 0, B_COLS - 2);
113df930be7Sderaadt scr_update();
114df930be7Sderaadt tsleep();
115df930be7Sderaadt break;
116df930be7Sderaadt }
117df930be7Sderaadt }
118df930be7Sderaadt }
119a7200f5aStedu switch (rows) {
120a7200f5aStedu case 1:
121a7200f5aStedu score += 10;
122a7200f5aStedu break;
123a7200f5aStedu case 2:
124a7200f5aStedu score += 30;
125a7200f5aStedu break;
126a7200f5aStedu case 3:
127a7200f5aStedu score += 70;
128a7200f5aStedu break;
129a7200f5aStedu case 4:
130a7200f5aStedu score += 150;
131a7200f5aStedu break;
132a7200f5aStedu default:
133a7200f5aStedu break;
134a7200f5aStedu }
135df930be7Sderaadt }
136df930be7Sderaadt
1378e58c664Smickey const struct shape *
randshape(void)138ff8320a7Sderaadt randshape(void)
139f627625dSpjanzen {
1408e58c664Smickey const struct shape *tmp;
141f627625dSpjanzen int i, j;
142f627625dSpjanzen
14366e49541Snaddy tmp = &shapes[arc4random_uniform(7)];
14466e49541Snaddy j = arc4random_uniform(4);
145f627625dSpjanzen for (i = 0; i < j; i++)
14649cea301Smickey tmp = &shapes[classic? tmp->rotc : tmp->rot];
147f627625dSpjanzen return (tmp);
148f627625dSpjanzen }
149f627625dSpjanzen
150df930be7Sderaadt int
main(int argc,char * argv[])151ff8320a7Sderaadt main(int argc, char *argv[])
152df930be7Sderaadt {
15397419aa0Spjanzen int pos, c;
15497419aa0Spjanzen char *keys;
15541b00738Srob int level = 2, ret;
156f26e040bStb char key_write[NUMKEYS][10];
15741b00738Srob char *home;
1580b8d5d5cSray const char *errstr;
159df930be7Sderaadt int ch, i, j;
160df930be7Sderaadt
16141b00738Srob home = getenv("HOME");
16241b00738Srob if (home == NULL || *home == '\0')
16341b00738Srob err(1, "getenv");
16441b00738Srob
16541b00738Srob ret = snprintf(scorepath, sizeof(scorepath), "%s/%s", home,
16641b00738Srob ".tetris.scores");
16741b00738Srob if (ret < 0 || ret >= PATH_MAX)
16841b00738Srob errc(1, ENAMETOOLONG, "%s/%s", home, ".tetris.scores");
16941b00738Srob
17041b00738Srob if (pledge("stdio rpath wpath cpath tty unveil", NULL) == -1)
1718f55f2bbStb err(1, "pledge");
172df930be7Sderaadt
1738f55f2bbStb keys = "jkl pq";
17475b7a267Stholo
17549cea301Smickey classic = showpreview = 0;
1760b8d5d5cSray while ((ch = getopt(argc, argv, "ck:l:ps")) != -1)
177df930be7Sderaadt switch(ch) {
17849cea301Smickey case 'c':
17949cea301Smickey /*
18049cea301Smickey * this means:
18149cea301Smickey * - rotate the other way;
18249cea301Smickey * - no reverse video.
18349cea301Smickey */
18449cea301Smickey classic = 1;
18549cea301Smickey break;
186df930be7Sderaadt case 'k':
187f26e040bStb if (strlen(keys = optarg) != NUMKEYS)
188df930be7Sderaadt usage();
189df930be7Sderaadt break;
190df930be7Sderaadt case 'l':
1910b8d5d5cSray level = (int)strtonum(optarg, MINLEVEL, MAXLEVEL,
1920b8d5d5cSray &errstr);
1930b8d5d5cSray if (errstr)
194dcf934a0Spjanzen errx(1, "level must be from %d to %d",
195df930be7Sderaadt MINLEVEL, MAXLEVEL);
196df930be7Sderaadt break;
197f627625dSpjanzen case 'p':
198f627625dSpjanzen showpreview = 1;
199f627625dSpjanzen break;
200df930be7Sderaadt case 's':
201df930be7Sderaadt showscores(0);
20217641e31Stb return 0;
203df930be7Sderaadt default:
204df930be7Sderaadt usage();
205df930be7Sderaadt }
206df930be7Sderaadt
207df930be7Sderaadt argc -= optind;
208df930be7Sderaadt argv += optind;
209df930be7Sderaadt
210df930be7Sderaadt if (argc)
211df930be7Sderaadt usage();
212df930be7Sderaadt
213176f6ce7Stedu fallrate = 1000000000L / level;
214df930be7Sderaadt
215f26e040bStb for (i = 0; i < NUMKEYS; i++) {
216f26e040bStb for (j = i+1; j < NUMKEYS; j++) {
217dcf934a0Spjanzen if (keys[i] == keys[j])
2182cbe7629Spjanzen errx(1, "duplicate command keys specified.");
219df930be7Sderaadt }
220df930be7Sderaadt if (keys[i] == ' ')
221fec224e0Sderaadt strlcpy(key_write[i], "<space>", sizeof key_write[i]);
222df930be7Sderaadt else {
223df930be7Sderaadt key_write[i][0] = keys[i];
224df930be7Sderaadt key_write[i][1] = '\0';
225df930be7Sderaadt }
226df930be7Sderaadt }
227df930be7Sderaadt
22842ceebb3Sderaadt snprintf(key_msg, sizeof key_msg,
229df930be7Sderaadt "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit",
230df930be7Sderaadt key_write[0], key_write[1], key_write[2], key_write[3],
231df930be7Sderaadt key_write[4], key_write[5]);
232df930be7Sderaadt
233df930be7Sderaadt (void)signal(SIGINT, onintr);
234df930be7Sderaadt scr_init();
23541b00738Srob
23641b00738Srob if (unveil(scorepath, "rwc") == -1)
237*bc5a8259Sbeck err(1, "unveil %s", scorepath);
23841b00738Srob
23941b00738Srob if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
24041b00738Srob err(1, "pledge");
24141b00738Srob
242df930be7Sderaadt setup_board();
243df930be7Sderaadt
244df930be7Sderaadt scr_set();
245df930be7Sderaadt
246df930be7Sderaadt pos = A_FIRST*B_COLS + (B_COLS/2)-1;
247f627625dSpjanzen nextshape = randshape();
248df930be7Sderaadt curshape = randshape();
249df930be7Sderaadt
250df930be7Sderaadt scr_msg(key_msg, 1);
251df930be7Sderaadt
252df930be7Sderaadt for (;;) {
253df930be7Sderaadt place(curshape, pos, 1);
254df930be7Sderaadt scr_update();
255df930be7Sderaadt place(curshape, pos, 0);
256df930be7Sderaadt c = tgetchar();
257df930be7Sderaadt if (c < 0) {
258df930be7Sderaadt /*
259df930be7Sderaadt * Timeout. Move down if possible.
260df930be7Sderaadt */
261df930be7Sderaadt if (fits_in(curshape, pos + B_COLS)) {
262df930be7Sderaadt pos += B_COLS;
263df930be7Sderaadt continue;
264df930be7Sderaadt }
265df930be7Sderaadt
266df930be7Sderaadt /*
267df930be7Sderaadt * Put up the current shape `permanently',
268df930be7Sderaadt * bump score, and elide any full rows.
269df930be7Sderaadt */
270df930be7Sderaadt place(curshape, pos, 1);
271df930be7Sderaadt score++;
272df930be7Sderaadt elide();
273df930be7Sderaadt
274df930be7Sderaadt /*
275df930be7Sderaadt * Choose a new shape. If it does not fit,
276df930be7Sderaadt * the game is over.
277df930be7Sderaadt */
278f627625dSpjanzen curshape = nextshape;
279f627625dSpjanzen nextshape = randshape();
280df930be7Sderaadt pos = A_FIRST*B_COLS + (B_COLS/2)-1;
281df930be7Sderaadt if (!fits_in(curshape, pos))
282df930be7Sderaadt break;
283df930be7Sderaadt continue;
284df930be7Sderaadt }
285df930be7Sderaadt
286df930be7Sderaadt /*
287df930be7Sderaadt * Handle command keys.
288df930be7Sderaadt */
289df930be7Sderaadt if (c == keys[5]) {
290df930be7Sderaadt /* quit */
291df930be7Sderaadt break;
292df930be7Sderaadt }
293df930be7Sderaadt if (c == keys[4]) {
294df930be7Sderaadt static char msg[] =
295df930be7Sderaadt "paused - press RETURN to continue";
296df930be7Sderaadt
297df930be7Sderaadt place(curshape, pos, 1);
298df930be7Sderaadt do {
299df930be7Sderaadt scr_update();
300df930be7Sderaadt scr_msg(key_msg, 0);
301df930be7Sderaadt scr_msg(msg, 1);
302df930be7Sderaadt (void) fflush(stdout);
303176f6ce7Stedu } while (rwait(NULL) == -1);
304df930be7Sderaadt scr_msg(msg, 0);
305df930be7Sderaadt scr_msg(key_msg, 1);
306df930be7Sderaadt place(curshape, pos, 0);
307df930be7Sderaadt continue;
308df930be7Sderaadt }
309df930be7Sderaadt if (c == keys[0]) {
310df930be7Sderaadt /* move left */
311df930be7Sderaadt if (fits_in(curshape, pos - 1))
312df930be7Sderaadt pos--;
313df930be7Sderaadt continue;
314df930be7Sderaadt }
315df930be7Sderaadt if (c == keys[1]) {
316df930be7Sderaadt /* turn */
3178e58c664Smickey const struct shape *new = &shapes[
31849cea301Smickey classic? curshape->rotc : curshape->rot];
319df930be7Sderaadt
320df930be7Sderaadt if (fits_in(new, pos))
321df930be7Sderaadt curshape = new;
322df930be7Sderaadt continue;
323df930be7Sderaadt }
324df930be7Sderaadt if (c == keys[2]) {
325df930be7Sderaadt /* move right */
326df930be7Sderaadt if (fits_in(curshape, pos + 1))
327df930be7Sderaadt pos++;
328df930be7Sderaadt continue;
329df930be7Sderaadt }
330df930be7Sderaadt if (c == keys[3]) {
331df930be7Sderaadt /* move to bottom */
332df930be7Sderaadt while (fits_in(curshape, pos + B_COLS)) {
333df930be7Sderaadt pos += B_COLS;
334df930be7Sderaadt score++;
335df930be7Sderaadt }
336df930be7Sderaadt continue;
337df930be7Sderaadt }
338f627625dSpjanzen if (c == '\f') {
339df930be7Sderaadt scr_clear();
340f627625dSpjanzen scr_msg(key_msg, 1);
341f627625dSpjanzen }
342df930be7Sderaadt }
343df930be7Sderaadt
344df930be7Sderaadt scr_clear();
345df930be7Sderaadt scr_end();
346df930be7Sderaadt
347f627625dSpjanzen if (showpreview == 0)
348df930be7Sderaadt (void)printf("Your score: %d point%s x level %d = %d\n",
349df930be7Sderaadt score, score == 1 ? "" : "s", level, score * level);
350f627625dSpjanzen else {
351f627625dSpjanzen (void)printf("Your score: %d point%s x level %d x preview penalty %0.3f = %d\n",
352f627625dSpjanzen score, score == 1 ? "" : "s", level, (double)PRE_PENALTY,
353f627625dSpjanzen (int)(score * level * PRE_PENALTY));
354f627625dSpjanzen score = score * PRE_PENALTY;
355f627625dSpjanzen }
356df930be7Sderaadt savescore(level);
357df930be7Sderaadt
358df930be7Sderaadt printf("\nHit RETURN to see high scores, ^C to skip.\n");
359df930be7Sderaadt
360df930be7Sderaadt while ((i = getchar()) != '\n')
361df930be7Sderaadt if (i == EOF)
362df930be7Sderaadt break;
363df930be7Sderaadt
364df930be7Sderaadt showscores(level);
365df930be7Sderaadt
36617641e31Stb return 0;
367df930be7Sderaadt }
368df930be7Sderaadt
369df930be7Sderaadt void
onintr(int signo)370ff8320a7Sderaadt onintr(int signo)
371df930be7Sderaadt {
3727af11270Sderaadt scr_clear(); /* XXX signal race */
3737af11270Sderaadt scr_end(); /* XXX signal race */
3747af11270Sderaadt _exit(0);
375df930be7Sderaadt }
376df930be7Sderaadt
377df930be7Sderaadt void
usage(void)378ff8320a7Sderaadt usage(void)
379df930be7Sderaadt {
3806fa5e1daSmestre (void)fprintf(stderr, "usage: %s [-cps] [-k keys] "
3816fa5e1daSmestre "[-l level]\n", getprogname());
382df930be7Sderaadt exit(1);
383df930be7Sderaadt }
384