xref: /openbsd-src/games/tetris/tetris.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: tetris.c,v 1.9 2001/02/04 14:23:28 pjanzen Exp $	*/
2 /*	$NetBSD: tetris.c,v 1.2 1995/04/22 07:42:47 cgd Exp $	*/
3 
4 /*-
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Chris Torek and Darren F. Provine.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *	@(#)tetris.c	8.1 (Berkeley) 5/31/93
40  */
41 
42 #ifndef lint
43 static char copyright[] =
44 "@(#) Copyright (c) 1992, 1993\n\
45 	The Regents of the University of California.  All rights reserved.\n";
46 #endif /* not lint */
47 
48 /*
49  * Tetris (or however it is spelled).
50  */
51 
52 #include <sys/param.h>
53 #include <sys/time.h>
54 #include <sys/types.h>
55 
56 #include <err.h>
57 #include <signal.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 #include "input.h"
64 #include "scores.h"
65 #include "screen.h"
66 #include "tetris.h"
67 
68 cell	board[B_SIZE];
69 int	Rows, Cols;
70 struct shape *curshape;
71 struct shape *nextshape;
72 long	fallrate;
73 int	score;
74 gid_t	gid, egid;
75 char	key_msg[100];
76 int	showpreview;
77 
78 static void	elide __P((void));
79 static void	setup_board __P((void));
80 struct shape	*randshape __P((void));
81 void	onintr __P((int));
82 void	usage __P((void));
83 
84 /*
85  * Set up the initial board.  The bottom display row is completely set,
86  * along with another (hidden) row underneath that.  Also, the left and
87  * right edges are set.
88  */
89 static void
90 setup_board()
91 {
92 	register int i;
93 	register cell *p;
94 
95 	p = board;
96 	for (i = B_SIZE; i; i--)
97 		*p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2;
98 }
99 
100 /*
101  * Elide any full active rows.
102  */
103 static void
104 elide()
105 {
106 	register int i, j, base;
107 	register cell *p;
108 
109 	for (i = A_FIRST; i < A_LAST; i++) {
110 		base = i * B_COLS + 1;
111 		p = &board[base];
112 		for (j = B_COLS - 2; *p++ != 0;) {
113 			if (--j <= 0) {
114 				/* this row is to be elided */
115 				memset(&board[base], 0, B_COLS - 2);
116 				scr_update();
117 				tsleep();
118 				while (--base != 0)
119 					board[base + B_COLS] = board[base];
120 				scr_update();
121 				tsleep();
122 				break;
123 			}
124 		}
125 	}
126 }
127 
128 struct shape *
129 randshape()
130 {
131 	struct shape *tmp;
132 	int i, j;
133 
134 	tmp = &shapes[random() % 7];
135 	j = random() % 4;
136 	for (i = 0; i < j; i++)
137 		tmp = &shapes[tmp->rot];
138 	return (tmp);
139 }
140 
141 
142 int
143 main(argc, argv)
144 	int argc;
145 	char *argv[];
146 {
147 	register int pos, c;
148 	register char *keys;
149 	register int level = 2;
150 	char key_write[6][10];
151 	int ch, i, j;
152 
153 	keys = "jkl pq";
154 
155 	gid = getgid();
156 	egid = getegid();
157 	setegid(gid);
158 
159 	showpreview = 0;
160 	while ((ch = getopt(argc, argv, "hk:l:ps")) != -1)
161 		switch(ch) {
162 		case 'k':
163 			if (strlen(keys = optarg) != 6)
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);
171 			break;
172 		case 'p':
173 			showpreview = 1;
174 			break;
175 		case 's':
176 			showscores(0);
177 			exit(0);
178 		case '?':
179 		case 'h':
180 		default:
181 			usage();
182 		}
183 
184 	argc -= optind;
185 	argv += optind;
186 
187 	if (argc)
188 		usage();
189 
190 	fallrate = 1000000 / level;
191 
192 	for (i = 0; i <= 5; i++) {
193 		for (j = i+1; j <= 5; j++) {
194 			if (keys[i] == keys[j])
195 				errx(1, "duplicate command keys specified.");
196 		}
197 		if (keys[i] == ' ')
198 			strcpy(key_write[i], "<space>");
199 		else {
200 			key_write[i][0] = keys[i];
201 			key_write[i][1] = '\0';
202 		}
203 	}
204 
205 	sprintf(key_msg,
206 "%s - left   %s - rotate   %s - right   %s - drop   %s - pause   %s - quit",
207 		key_write[0], key_write[1], key_write[2], key_write[3],
208 		key_write[4], key_write[5]);
209 
210 	(void)signal(SIGINT, onintr);
211 	scr_init();
212 	setup_board();
213 
214 	srandom(getpid());
215 	scr_set();
216 
217 	pos = A_FIRST*B_COLS + (B_COLS/2)-1;
218 	nextshape = randshape();
219 	curshape = randshape();
220 
221 	scr_msg(key_msg, 1);
222 
223 	for (;;) {
224 		place(curshape, pos, 1);
225 		scr_update();
226 		place(curshape, pos, 0);
227 		c = tgetchar();
228 		if (c < 0) {
229 			/*
230 			 * Timeout.  Move down if possible.
231 			 */
232 			if (fits_in(curshape, pos + B_COLS)) {
233 				pos += B_COLS;
234 				continue;
235 			}
236 
237 			/*
238 			 * Put up the current shape `permanently',
239 			 * bump score, and elide any full rows.
240 			 */
241 			place(curshape, pos, 1);
242 			score++;
243 			elide();
244 
245 			/*
246 			 * Choose a new shape.  If it does not fit,
247 			 * the game is over.
248 			 */
249 			curshape = nextshape;
250 			nextshape = randshape();
251 			pos = A_FIRST*B_COLS + (B_COLS/2)-1;
252 			if (!fits_in(curshape, pos))
253 				break;
254 			continue;
255 		}
256 
257 		/*
258 		 * Handle command keys.
259 		 */
260 		if (c == keys[5]) {
261 			/* quit */
262 			break;
263 		}
264 		if (c == keys[4]) {
265 			static char msg[] =
266 			    "paused - press RETURN to continue";
267 
268 			place(curshape, pos, 1);
269 			do {
270 				scr_update();
271 				scr_msg(key_msg, 0);
272 				scr_msg(msg, 1);
273 				(void) fflush(stdout);
274 			} while (rwait((struct timeval *)NULL) == -1);
275 			scr_msg(msg, 0);
276 			scr_msg(key_msg, 1);
277 			place(curshape, pos, 0);
278 			continue;
279 		}
280 		if (c == keys[0]) {
281 			/* move left */
282 			if (fits_in(curshape, pos - 1))
283 				pos--;
284 			continue;
285 		}
286 		if (c == keys[1]) {
287 			/* turn */
288 			struct shape *new = &shapes[curshape->rot];
289 
290 			if (fits_in(new, pos))
291 				curshape = new;
292 			continue;
293 		}
294 		if (c == keys[2]) {
295 			/* move right */
296 			if (fits_in(curshape, pos + 1))
297 				pos++;
298 			continue;
299 		}
300 		if (c == keys[3]) {
301 			/* move to bottom */
302 			while (fits_in(curshape, pos + B_COLS)) {
303 				pos += B_COLS;
304 				score++;
305 			}
306 			continue;
307 		}
308 		if (c == '\f') {
309 			scr_clear();
310 			scr_msg(key_msg, 1);
311 		}
312 	}
313 
314 	scr_clear();
315 	scr_end();
316 
317 	if (showpreview == 0)
318 		(void)printf("Your score:  %d point%s  x  level %d  =  %d\n",
319 		    score, score == 1 ? "" : "s", level, score * level);
320 	else {
321 		(void)printf("Your score:  %d point%s x level %d x preview penalty %0.3f = %d\n",
322 		    score, score == 1 ? "" : "s", level, (double)PRE_PENALTY,
323 		    (int)(score * level * PRE_PENALTY));
324 		score = score * PRE_PENALTY;
325 	}
326 	savescore(level);
327 
328 	printf("\nHit RETURN to see high scores, ^C to skip.\n");
329 
330 	while ((i = getchar()) != '\n')
331 		if (i == EOF)
332 			break;
333 
334 	showscores(level);
335 
336 	exit(0);
337 }
338 
339 void
340 onintr(signo)
341 	int signo;
342 {
343 	scr_clear();
344 	scr_end();
345 	exit(0);
346 }
347 
348 void
349 usage()
350 {
351 	(void)fprintf(stderr, "usage: tetris [-ps] [-k keys] [-l level]\n");
352 	exit(1);
353 }
354