xref: /dragonfly/games/tetris/tetris.c (revision 3d33658b)
1 /*	$OpenBSD: tetris.c,v 1.32 2017/08/13 02:12:16 tedu 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[120];
62 int	showpreview, classic;
63 
64 static void elide(void);
65 __dead2 void onintr(int __unused);
66 const struct shape *randshape(void);
67 static void setup_board(void);
68 __dead2 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 #define NUMKEYS 6
147 
148 int
149 main(int argc, char *argv[])
150 {
151 	int pos, c;
152 	const char *keys;
153 	int level = 2;
154 	char key_write[NUMKEYS][10];
155 	const char *errstr;
156 	int ch, i, j;
157 
158 	keys = "jkl pq";
159 
160 	classic = showpreview = 0;
161 	while ((ch = getopt(argc, argv, "ck:l:ps")) != -1)
162 		switch(ch) {
163 		case 'c':
164 			/*
165 			 * this means:
166 			 *	- rotate the other way;
167 			 *	- no reverse video.
168 			 */
169 			classic = 1;
170 			break;
171 		case 'k':
172 			if (strlen(keys = optarg) != NUMKEYS)
173 				usage();
174 			break;
175 		case 'l':
176 			level = (int)strtonum(optarg, MINLEVEL, MAXLEVEL,
177 			    &errstr);
178 			if (errstr)
179 				errx(1, "level must be from %d to %d",
180 				    MINLEVEL, MAXLEVEL);
181 			break;
182 		case 'p':
183 			showpreview = 1;
184 			break;
185 		case 's':
186 			showscores(0);
187 			return 0;
188 		default:
189 			usage();
190 		}
191 
192 	argc -= optind;
193 	argv += optind;
194 
195 	if (argc)
196 		usage();
197 
198 	fallrate = 1000000000L / level;
199 
200 	for (i = 0; i <= (NUMKEYS-1); i++) {
201 		for (j = i+1; j <= (NUMKEYS-1); j++) {
202 			if (keys[i] == keys[j])
203 				errx(1, "duplicate command keys specified.");
204 		}
205 		if (keys[i] == ' ')
206 			strlcpy(key_write[i], "<space>", sizeof key_write[i]);
207 		else {
208 			key_write[i][0] = keys[i];
209 			key_write[i][1] = '\0';
210 		}
211 	}
212 
213 	snprintf(key_msg, sizeof(key_msg),
214 "%s - left   %s - rotate   %s - right   %s - drop   %s - pause   %s - quit",
215 		key_write[0], key_write[1], key_write[2], key_write[3],
216 		key_write[4], key_write[5]);
217 
218 	signal(SIGINT, onintr);
219 	scr_init();
220 	setup_board();
221 
222 	scr_set();
223 
224 	pos = A_FIRST*B_COLS + (B_COLS/2)-1;
225 	nextshape = randshape();
226 	curshape = randshape();
227 
228 	scr_msg(key_msg, 1);
229 
230 	for (;;) {
231 		place(curshape, pos, 1);
232 		scr_update();
233 		place(curshape, pos, 0);
234 		c = tgetchar();
235 		if (c < 0) {
236 			/*
237 			 * Timeout.  Move down if possible.
238 			 */
239 			if (fits_in(curshape, pos + B_COLS)) {
240 				pos += B_COLS;
241 				continue;
242 			}
243 
244 			/*
245 			 * Put up the current shape `permanently',
246 			 * bump score, and elide any full rows.
247 			 */
248 			place(curshape, pos, 1);
249 			score++;
250 			elide();
251 
252 			/*
253 			 * Choose a new shape.  If it does not fit,
254 			 * the game is over.
255 			 */
256 			curshape = nextshape;
257 			nextshape = randshape();
258 			pos = A_FIRST*B_COLS + (B_COLS/2)-1;
259 			if (!fits_in(curshape, pos))
260 				break;
261 			continue;
262 		}
263 
264 		/*
265 		 * Handle command keys.
266 		 */
267 		if (c == keys[5]) {
268 			/* quit */
269 			break;
270 		}
271 		if (c == keys[4]) {
272 			static char msg[] =
273 			    "paused - press RETURN to continue";
274 
275 			place(curshape, pos, 1);
276 			do {
277 				scr_update();
278 				scr_msg(key_msg, 0);
279 				scr_msg(msg, 1);
280 				fflush(stdout);
281 			} while (rwait(NULL) == -1);
282 			scr_msg(msg, 0);
283 			scr_msg(key_msg, 1);
284 			place(curshape, pos, 0);
285 			continue;
286 		}
287 		if (c == keys[0]) {
288 			/* move left */
289 			if (fits_in(curshape, pos - 1))
290 				pos--;
291 			continue;
292 		}
293 		if (c == keys[1]) {
294 			/* turn */
295 			const struct shape *new = &shapes[
296 			    classic? curshape->rotc : curshape->rot];
297 
298 			if (fits_in(new, pos))
299 				curshape = new;
300 			continue;
301 		}
302 		if (c == keys[2]) {
303 			/* move right */
304 			if (fits_in(curshape, pos + 1))
305 				pos++;
306 			continue;
307 		}
308 		if (c == keys[3]) {
309 			/* move to bottom */
310 			while (fits_in(curshape, pos + B_COLS)) {
311 				pos += B_COLS;
312 				score++;
313 			}
314 			continue;
315 		}
316 		if (c == '\f') {
317 			scr_clear();
318 			scr_msg(key_msg, 1);
319 		}
320 	}
321 
322 	scr_clear();
323 	scr_end();
324 
325 	if (showpreview == 0)
326 		printf("Your score:  %d point%s  x  level %d  =  %d\n",
327 		    score, score == 1 ? "" : "s", level, score * level);
328 	else {
329 		printf("Your score:  %d point%s x level %d x preview penalty %0.3f = %d\n",
330 		    score, score == 1 ? "" : "s", level, (double)PRE_PENALTY,
331 		    (int)(score * level * PRE_PENALTY));
332 		score = score * PRE_PENALTY;
333 	}
334 	savescore(level);
335 
336 	printf("\nHit RETURN to see high scores, ^C to skip.\n");
337 
338 	while ((i = getchar()) != '\n')
339 		if (i == EOF)
340 			break;
341 
342 	showscores(level);
343 
344 	return 0;
345 }
346 
347 void
348 onintr(int signo __unused)
349 {
350 	scr_clear();		/* XXX signal race */
351 	scr_end();		/* XXX signal race */
352 	_exit(0);
353 }
354 
355 void
356 usage(void)
357 {
358 	fprintf(stderr, "usage: %s [-cps] [-k keys] "
359 	    "[-l level]\n", getprogname());
360 	exit(1);
361 }
362