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