xref: /minix/games/tetris/tetris.c (revision 0a6a1f1d)
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