xref: /openbsd/games/tetris/tetris.c (revision 91f110e0)
1 /*	$OpenBSD: tetris.c,v 1.24 2013/08/29 20:22:20 naddy 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[arc4random_uniform(7)];
143 	j = arc4random_uniform(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 	scr_set();
229 
230 	pos = A_FIRST*B_COLS + (B_COLS/2)-1;
231 	nextshape = randshape();
232 	curshape = randshape();
233 
234 	scr_msg(key_msg, 1);
235 
236 	for (;;) {
237 		place(curshape, pos, 1);
238 		scr_update();
239 		place(curshape, pos, 0);
240 		c = tgetchar();
241 		if (c < 0) {
242 			/*
243 			 * Timeout.  Move down if possible.
244 			 */
245 			if (fits_in(curshape, pos + B_COLS)) {
246 				pos += B_COLS;
247 				continue;
248 			}
249 
250 			/*
251 			 * Put up the current shape `permanently',
252 			 * bump score, and elide any full rows.
253 			 */
254 			place(curshape, pos, 1);
255 			score++;
256 			elide();
257 
258 			/*
259 			 * Choose a new shape.  If it does not fit,
260 			 * the game is over.
261 			 */
262 			curshape = nextshape;
263 			nextshape = randshape();
264 			pos = A_FIRST*B_COLS + (B_COLS/2)-1;
265 			if (!fits_in(curshape, pos))
266 				break;
267 			continue;
268 		}
269 
270 		/*
271 		 * Handle command keys.
272 		 */
273 		if (c == keys[5]) {
274 			/* quit */
275 			break;
276 		}
277 		if (c == keys[4]) {
278 			static char msg[] =
279 			    "paused - press RETURN to continue";
280 
281 			place(curshape, pos, 1);
282 			do {
283 				scr_update();
284 				scr_msg(key_msg, 0);
285 				scr_msg(msg, 1);
286 				(void) fflush(stdout);
287 			} while (rwait((struct timeval *)NULL) == -1);
288 			scr_msg(msg, 0);
289 			scr_msg(key_msg, 1);
290 			place(curshape, pos, 0);
291 			continue;
292 		}
293 		if (c == keys[0]) {
294 			/* move left */
295 			if (fits_in(curshape, pos - 1))
296 				pos--;
297 			continue;
298 		}
299 		if (c == keys[1]) {
300 			/* turn */
301 			const struct shape *new = &shapes[
302 			    classic? curshape->rotc : curshape->rot];
303 
304 			if (fits_in(new, pos))
305 				curshape = new;
306 			continue;
307 		}
308 		if (c == keys[2]) {
309 			/* move right */
310 			if (fits_in(curshape, pos + 1))
311 				pos++;
312 			continue;
313 		}
314 		if (c == keys[3]) {
315 			/* move to bottom */
316 			while (fits_in(curshape, pos + B_COLS)) {
317 				pos += B_COLS;
318 				score++;
319 			}
320 			continue;
321 		}
322 		if (c == '\f') {
323 			scr_clear();
324 			scr_msg(key_msg, 1);
325 		}
326 	}
327 
328 	scr_clear();
329 	scr_end();
330 
331 	if (showpreview == 0)
332 		(void)printf("Your score:  %d point%s  x  level %d  =  %d\n",
333 		    score, score == 1 ? "" : "s", level, score * level);
334 	else {
335 		(void)printf("Your score:  %d point%s x level %d x preview penalty %0.3f = %d\n",
336 		    score, score == 1 ? "" : "s", level, (double)PRE_PENALTY,
337 		    (int)(score * level * PRE_PENALTY));
338 		score = score * PRE_PENALTY;
339 	}
340 	savescore(level);
341 
342 	printf("\nHit RETURN to see high scores, ^C to skip.\n");
343 
344 	while ((i = getchar()) != '\n')
345 		if (i == EOF)
346 			break;
347 
348 	showscores(level);
349 
350 	exit(0);
351 }
352 
353 void
354 onintr(int signo)
355 {
356 	scr_clear();		/* XXX signal race */
357 	scr_end();		/* XXX signal race */
358 	_exit(0);
359 }
360 
361 void
362 usage(void)
363 {
364 	(void)fprintf(stderr, "usage: tetris [-cps] [-k keys] [-l level]\n");
365 	exit(1);
366 }
367