1*0a6a1f1dSLionel Sambuc /* $NetBSD: tetris.c,v 1.30 2015/06/13 04:53:13 dholland Exp $ */
22f98b65aSThomas Cort
32f98b65aSThomas Cort /*-
42f98b65aSThomas Cort * Copyright (c) 1992, 1993
52f98b65aSThomas Cort * The Regents of the University of California. All rights reserved.
62f98b65aSThomas Cort *
72f98b65aSThomas Cort * This code is derived from software contributed to Berkeley by
82f98b65aSThomas Cort * Chris Torek and Darren F. Provine.
92f98b65aSThomas Cort *
102f98b65aSThomas Cort * Redistribution and use in source and binary forms, with or without
112f98b65aSThomas Cort * modification, are permitted provided that the following conditions
122f98b65aSThomas Cort * are met:
132f98b65aSThomas Cort * 1. Redistributions of source code must retain the above copyright
142f98b65aSThomas Cort * notice, this list of conditions and the following disclaimer.
152f98b65aSThomas Cort * 2. Redistributions in binary form must reproduce the above copyright
162f98b65aSThomas Cort * notice, this list of conditions and the following disclaimer in the
172f98b65aSThomas Cort * documentation and/or other materials provided with the distribution.
182f98b65aSThomas Cort * 3. Neither the name of the University nor the names of its contributors
192f98b65aSThomas Cort * may be used to endorse or promote products derived from this software
202f98b65aSThomas Cort * without specific prior written permission.
212f98b65aSThomas Cort *
222f98b65aSThomas Cort * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
232f98b65aSThomas Cort * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
242f98b65aSThomas Cort * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
252f98b65aSThomas Cort * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
262f98b65aSThomas Cort * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
272f98b65aSThomas Cort * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
282f98b65aSThomas Cort * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
292f98b65aSThomas Cort * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
302f98b65aSThomas Cort * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
312f98b65aSThomas Cort * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322f98b65aSThomas Cort * SUCH DAMAGE.
332f98b65aSThomas Cort *
342f98b65aSThomas Cort * @(#)tetris.c 8.1 (Berkeley) 5/31/93
352f98b65aSThomas Cort */
362f98b65aSThomas Cort
372f98b65aSThomas Cort #include <sys/cdefs.h>
382f98b65aSThomas Cort #ifndef lint
392f98b65aSThomas Cort __COPYRIGHT("@(#) Copyright (c) 1992, 1993\
402f98b65aSThomas Cort The Regents of the University of California. All rights reserved.");
412f98b65aSThomas Cort #endif /* not lint */
422f98b65aSThomas Cort
432f98b65aSThomas Cort /*
442f98b65aSThomas Cort * Tetris (or however it is spelled).
452f98b65aSThomas Cort */
462f98b65aSThomas Cort
472f98b65aSThomas Cort #include <sys/time.h>
482f98b65aSThomas Cort
492f98b65aSThomas Cort #include <err.h>
502f98b65aSThomas Cort #include <fcntl.h>
512f98b65aSThomas Cort #include <signal.h>
522f98b65aSThomas Cort #include <stdio.h>
532f98b65aSThomas Cort #include <stdlib.h>
542f98b65aSThomas Cort #include <string.h>
552f98b65aSThomas Cort #include <unistd.h>
562f98b65aSThomas Cort
572f98b65aSThomas Cort #include "input.h"
582f98b65aSThomas Cort #include "scores.h"
592f98b65aSThomas Cort #include "screen.h"
602f98b65aSThomas Cort #include "tetris.h"
612f98b65aSThomas Cort
622f98b65aSThomas Cort cell board[B_SIZE]; /* 1 => occupied, 0 => empty */
632f98b65aSThomas Cort
642f98b65aSThomas Cort int Rows, Cols; /* current screen size */
652f98b65aSThomas Cort
662f98b65aSThomas Cort static const struct shape *curshape;
672f98b65aSThomas Cort const struct shape *nextshape;
682f98b65aSThomas Cort
692f98b65aSThomas Cort long fallrate; /* less than 1 million; smaller => faster */
702f98b65aSThomas Cort
712f98b65aSThomas Cort int score; /* the obvious thing */
722f98b65aSThomas Cort gid_t gid, egid;
732f98b65aSThomas Cort
742f98b65aSThomas Cort char key_msg[100];
752f98b65aSThomas Cort int showpreview;
76*0a6a1f1dSLionel Sambuc int nocolor;
772f98b65aSThomas Cort
782f98b65aSThomas Cort static void elide(void);
792f98b65aSThomas Cort static void setup_board(void);
802f98b65aSThomas Cort static void onintr(int) __dead;
812f98b65aSThomas Cort static void usage(void) __dead;
822f98b65aSThomas Cort
832f98b65aSThomas Cort /*
842f98b65aSThomas Cort * Set up the initial board. The bottom display row is completely set,
852f98b65aSThomas Cort * along with another (hidden) row underneath that. Also, the left and
862f98b65aSThomas Cort * right edges are set.
872f98b65aSThomas Cort */
882f98b65aSThomas Cort static void
setup_board(void)892f98b65aSThomas Cort setup_board(void)
902f98b65aSThomas Cort {
912f98b65aSThomas Cort int i;
922f98b65aSThomas Cort cell *p;
932f98b65aSThomas Cort
942f98b65aSThomas Cort p = board;
952f98b65aSThomas Cort for (i = B_SIZE; i; i--)
96*0a6a1f1dSLionel Sambuc *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 7 : 0;
972f98b65aSThomas Cort }
982f98b65aSThomas Cort
992f98b65aSThomas Cort /*
1002f98b65aSThomas Cort * Elide any full active rows.
1012f98b65aSThomas Cort */
1022f98b65aSThomas Cort static void
elide(void)1032f98b65aSThomas Cort elide(void)
1042f98b65aSThomas Cort {
1052f98b65aSThomas Cort int i, j, base;
1062f98b65aSThomas Cort cell *p;
1072f98b65aSThomas Cort
1082f98b65aSThomas Cort for (i = A_FIRST; i < A_LAST; i++) {
1092f98b65aSThomas Cort base = i * B_COLS + 1;
1102f98b65aSThomas Cort p = &board[base];
1112f98b65aSThomas Cort for (j = B_COLS - 2; *p++ != 0;) {
1122f98b65aSThomas Cort if (--j <= 0) {
1132f98b65aSThomas Cort /* this row is to be elided */
1142f98b65aSThomas Cort memset(&board[base], 0, B_COLS - 2);
1152f98b65aSThomas Cort scr_update();
1162f98b65aSThomas Cort tsleep();
1172f98b65aSThomas Cort while (--base != 0)
1182f98b65aSThomas Cort board[base + B_COLS] = board[base];
1192f98b65aSThomas Cort scr_update();
1202f98b65aSThomas Cort tsleep();
1212f98b65aSThomas Cort break;
1222f98b65aSThomas Cort }
1232f98b65aSThomas Cort }
1242f98b65aSThomas Cort }
1252f98b65aSThomas Cort }
1262f98b65aSThomas Cort
1272f98b65aSThomas Cort int
main(int argc,char * argv[])1282f98b65aSThomas Cort main(int argc, char *argv[])
1292f98b65aSThomas Cort {
1302f98b65aSThomas Cort int pos, c;
1312f98b65aSThomas Cort const char *keys;
1322f98b65aSThomas Cort int level = 2;
133*0a6a1f1dSLionel Sambuc #define NUMKEYS 7
134*0a6a1f1dSLionel Sambuc char key_write[NUMKEYS][10];
1352f98b65aSThomas Cort int ch, i, j;
1362f98b65aSThomas Cort int fd;
1372f98b65aSThomas Cort
1382f98b65aSThomas Cort gid = getgid();
1392f98b65aSThomas Cort egid = getegid();
1402f98b65aSThomas Cort setegid(gid);
1412f98b65aSThomas Cort
1422f98b65aSThomas Cort fd = open("/dev/null", O_RDONLY);
1432f98b65aSThomas Cort if (fd < 3)
1442f98b65aSThomas Cort exit(1);
1452f98b65aSThomas Cort close(fd);
1462f98b65aSThomas Cort
147*0a6a1f1dSLionel Sambuc keys = "jkl pqn";
1482f98b65aSThomas Cort
149*0a6a1f1dSLionel Sambuc while ((ch = getopt(argc, argv, "bk:l:ps")) != -1)
1502f98b65aSThomas Cort switch(ch) {
151*0a6a1f1dSLionel Sambuc case 'b':
152*0a6a1f1dSLionel Sambuc nocolor = 1;
153*0a6a1f1dSLionel Sambuc break;
1542f98b65aSThomas Cort case 'k':
155*0a6a1f1dSLionel Sambuc if (strlen(keys = optarg) != NUMKEYS)
1562f98b65aSThomas Cort usage();
1572f98b65aSThomas Cort break;
1582f98b65aSThomas Cort case 'l':
1592f98b65aSThomas Cort level = atoi(optarg);
1602f98b65aSThomas Cort if (level < MINLEVEL || level > MAXLEVEL) {
1612f98b65aSThomas Cort errx(1, "level must be from %d to %d",
1622f98b65aSThomas Cort MINLEVEL, MAXLEVEL);
1632f98b65aSThomas Cort }
1642f98b65aSThomas Cort break;
1652f98b65aSThomas Cort case 'p':
1662f98b65aSThomas Cort showpreview = 1;
1672f98b65aSThomas Cort break;
1682f98b65aSThomas Cort case 's':
1692f98b65aSThomas Cort showscores(0);
1702f98b65aSThomas Cort exit(0);
1712f98b65aSThomas Cort case '?':
1722f98b65aSThomas Cort default:
1732f98b65aSThomas Cort usage();
1742f98b65aSThomas Cort }
1752f98b65aSThomas Cort
1762f98b65aSThomas Cort argc -= optind;
1772f98b65aSThomas Cort argv += optind;
1782f98b65aSThomas Cort
1792f98b65aSThomas Cort if (argc)
1802f98b65aSThomas Cort usage();
1812f98b65aSThomas Cort
1822f98b65aSThomas Cort fallrate = 1000000 / level;
1832f98b65aSThomas Cort
184*0a6a1f1dSLionel Sambuc for (i = 0; i <= (NUMKEYS-1); i++) {
185*0a6a1f1dSLionel Sambuc for (j = i+1; j <= (NUMKEYS-1); j++) {
1862f98b65aSThomas Cort if (keys[i] == keys[j]) {
1872f98b65aSThomas Cort errx(1, "duplicate command keys specified.");
1882f98b65aSThomas Cort }
1892f98b65aSThomas Cort }
1902f98b65aSThomas Cort if (keys[i] == ' ')
1912f98b65aSThomas Cort strcpy(key_write[i], "<space>");
1922f98b65aSThomas Cort else {
1932f98b65aSThomas Cort key_write[i][0] = keys[i];
1942f98b65aSThomas Cort key_write[i][1] = '\0';
1952f98b65aSThomas Cort }
1962f98b65aSThomas Cort }
1972f98b65aSThomas Cort
1982f98b65aSThomas Cort snprintf(key_msg, sizeof(key_msg),
199*0a6a1f1dSLionel Sambuc "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit %s - down",
2002f98b65aSThomas Cort key_write[0], key_write[1], key_write[2], key_write[3],
201*0a6a1f1dSLionel Sambuc key_write[4], key_write[5], key_write[6]);
2022f98b65aSThomas Cort
2032f98b65aSThomas Cort (void)signal(SIGINT, onintr);
2042f98b65aSThomas Cort scr_init();
2052f98b65aSThomas Cort setup_board();
2062f98b65aSThomas Cort
2072f98b65aSThomas Cort srandom(getpid());
2082f98b65aSThomas Cort scr_set();
2092f98b65aSThomas Cort
2102f98b65aSThomas Cort pos = A_FIRST*B_COLS + (B_COLS/2)-1;
2112f98b65aSThomas Cort nextshape = randshape();
2122f98b65aSThomas Cort curshape = randshape();
2132f98b65aSThomas Cort
2142f98b65aSThomas Cort scr_msg(key_msg, 1);
2152f98b65aSThomas Cort
2162f98b65aSThomas Cort for (;;) {
2172f98b65aSThomas Cort place(curshape, pos, 1);
2182f98b65aSThomas Cort scr_update();
2192f98b65aSThomas Cort place(curshape, pos, 0);
2202f98b65aSThomas Cort c = tgetchar();
2212f98b65aSThomas Cort if (c < 0) {
2222f98b65aSThomas Cort /*
2232f98b65aSThomas Cort * Timeout. Move down if possible.
2242f98b65aSThomas Cort */
2252f98b65aSThomas Cort if (fits_in(curshape, pos + B_COLS)) {
2262f98b65aSThomas Cort pos += B_COLS;
2272f98b65aSThomas Cort continue;
2282f98b65aSThomas Cort }
2292f98b65aSThomas Cort
2302f98b65aSThomas Cort /*
2312f98b65aSThomas Cort * Put up the current shape `permanently',
2322f98b65aSThomas Cort * bump score, and elide any full rows.
2332f98b65aSThomas Cort */
2342f98b65aSThomas Cort place(curshape, pos, 1);
2352f98b65aSThomas Cort score++;
2362f98b65aSThomas Cort elide();
2372f98b65aSThomas Cort
2382f98b65aSThomas Cort /*
2392f98b65aSThomas Cort * Choose a new shape. If it does not fit,
2402f98b65aSThomas Cort * the game is over.
2412f98b65aSThomas Cort */
2422f98b65aSThomas Cort curshape = nextshape;
2432f98b65aSThomas Cort nextshape = randshape();
2442f98b65aSThomas Cort pos = A_FIRST*B_COLS + (B_COLS/2)-1;
2452f98b65aSThomas Cort if (!fits_in(curshape, pos))
2462f98b65aSThomas Cort break;
2472f98b65aSThomas Cort continue;
2482f98b65aSThomas Cort }
2492f98b65aSThomas Cort
2502f98b65aSThomas Cort /*
2512f98b65aSThomas Cort * Handle command keys.
2522f98b65aSThomas Cort */
2532f98b65aSThomas Cort if (c == keys[5]) {
2542f98b65aSThomas Cort /* quit */
2552f98b65aSThomas Cort break;
2562f98b65aSThomas Cort }
2572f98b65aSThomas Cort if (c == keys[4]) {
2582f98b65aSThomas Cort static char msg[] =
2592f98b65aSThomas Cort "paused - press RETURN to continue";
2602f98b65aSThomas Cort
2612f98b65aSThomas Cort place(curshape, pos, 1);
2622f98b65aSThomas Cort do {
2632f98b65aSThomas Cort scr_update();
2642f98b65aSThomas Cort scr_msg(key_msg, 0);
2652f98b65aSThomas Cort scr_msg(msg, 1);
2662f98b65aSThomas Cort (void) fflush(stdout);
2672f98b65aSThomas Cort } while (rwait(NULL) == -1);
2682f98b65aSThomas Cort scr_msg(msg, 0);
2692f98b65aSThomas Cort scr_msg(key_msg, 1);
2702f98b65aSThomas Cort place(curshape, pos, 0);
2712f98b65aSThomas Cort continue;
2722f98b65aSThomas Cort }
2732f98b65aSThomas Cort if (c == keys[0]) {
2742f98b65aSThomas Cort /* move left */
2752f98b65aSThomas Cort if (fits_in(curshape, pos - 1))
2762f98b65aSThomas Cort pos--;
2772f98b65aSThomas Cort continue;
2782f98b65aSThomas Cort }
2792f98b65aSThomas Cort if (c == keys[1]) {
2802f98b65aSThomas Cort /* turn */
2812f98b65aSThomas Cort const struct shape *new = &shapes[curshape->rot];
2822f98b65aSThomas Cort
2832f98b65aSThomas Cort if (fits_in(new, pos))
2842f98b65aSThomas Cort curshape = new;
2852f98b65aSThomas Cort continue;
2862f98b65aSThomas Cort }
2872f98b65aSThomas Cort if (c == keys[2]) {
2882f98b65aSThomas Cort /* move right */
2892f98b65aSThomas Cort if (fits_in(curshape, pos + 1))
2902f98b65aSThomas Cort pos++;
2912f98b65aSThomas Cort continue;
2922f98b65aSThomas Cort }
2932f98b65aSThomas Cort if (c == keys[3]) {
2942f98b65aSThomas Cort /* move to bottom */
2952f98b65aSThomas Cort while (fits_in(curshape, pos + B_COLS)) {
2962f98b65aSThomas Cort pos += B_COLS;
2972f98b65aSThomas Cort score++;
2982f98b65aSThomas Cort }
2992f98b65aSThomas Cort continue;
3002f98b65aSThomas Cort }
301*0a6a1f1dSLionel Sambuc if (c == keys[6]) {
302*0a6a1f1dSLionel Sambuc /* move down */
303*0a6a1f1dSLionel Sambuc if (fits_in(curshape, pos + B_COLS)) {
304*0a6a1f1dSLionel Sambuc pos += B_COLS;
305*0a6a1f1dSLionel Sambuc score++;
306*0a6a1f1dSLionel Sambuc }
307*0a6a1f1dSLionel Sambuc continue;
308*0a6a1f1dSLionel Sambuc }
3092f98b65aSThomas Cort if (c == '\f') {
3102f98b65aSThomas Cort scr_clear();
3112f98b65aSThomas Cort scr_msg(key_msg, 1);
3122f98b65aSThomas Cort }
3132f98b65aSThomas Cort }
3142f98b65aSThomas Cort
3152f98b65aSThomas Cort scr_clear();
3162f98b65aSThomas Cort scr_end();
3172f98b65aSThomas Cort
3182f98b65aSThomas Cort (void)printf("Your score: %d point%s x level %d = %d\n",
3192f98b65aSThomas Cort score, score == 1 ? "" : "s", level, score * level);
3202f98b65aSThomas Cort savescore(level);
3212f98b65aSThomas Cort
3222f98b65aSThomas Cort printf("\nHit RETURN to see high scores, ^C to skip.\n");
3232f98b65aSThomas Cort
3242f98b65aSThomas Cort while ((i = getchar()) != '\n')
3252f98b65aSThomas Cort if (i == EOF)
3262f98b65aSThomas Cort break;
3272f98b65aSThomas Cort
3282f98b65aSThomas Cort showscores(level);
3292f98b65aSThomas Cort
3302f98b65aSThomas Cort exit(0);
3312f98b65aSThomas Cort }
3322f98b65aSThomas Cort
3332f98b65aSThomas Cort static void
onintr(int signo __unused)3342f98b65aSThomas Cort onintr(int signo __unused)
3352f98b65aSThomas Cort {
3362f98b65aSThomas Cort scr_clear();
3372f98b65aSThomas Cort scr_end();
3382f98b65aSThomas Cort exit(0);
3392f98b65aSThomas Cort }
3402f98b65aSThomas Cort
3412f98b65aSThomas Cort static void
usage(void)3422f98b65aSThomas Cort usage(void)
3432f98b65aSThomas Cort {
344*0a6a1f1dSLionel Sambuc (void)fprintf(stderr, "usage: %s [-bps] [-k keys] [-l level]\n",
3452f98b65aSThomas Cort getprogname());
3462f98b65aSThomas Cort exit(1);
3472f98b65aSThomas Cort }
348