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