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