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