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