xref: /netbsd-src/games/tetris/tetris.c (revision ae9172d6cd9432a6a1a56760d86b32c57a66c39c)
1 /*-
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Chris Torek and Darren F. Provine.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	@(#)tetris.c	8.1 (Berkeley) 5/31/93
37  */
38 
39 #ifndef lint
40 static char copyright[] =
41 "@(#) Copyright (c) 1992, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n";
43 #endif /* not lint */
44 
45 /*
46  * Tetris (or however it is spelled).
47  */
48 
49 #include <sys/time.h>
50 
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 #include "input.h"
58 #include "scores.h"
59 #include "screen.h"
60 #include "tetris.h"
61 
62 void onintr __P((int));
63 void usage __P((void));
64 
65 /*
66  * Set up the initial board.  The bottom display row is completely set,
67  * along with another (hidden) row underneath that.  Also, the left and
68  * right edges are set.
69  */
70 static void
71 setup_board()
72 {
73 	register int i;
74 	register cell *p;
75 
76 	p = board;
77 	for (i = B_SIZE; i; i--)
78 #ifndef mips
79 		*p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2;
80 #else /* work around compiler bug */
81 		*p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2 ? 1 : 0;
82 #endif
83 }
84 
85 /*
86  * Elide any full active rows.
87  */
88 static void
89 elide()
90 {
91 	register int i, j, base;
92 	register cell *p;
93 
94 	for (i = A_FIRST; i < A_LAST; i++) {
95 		base = i * B_COLS + 1;
96 		p = &board[base];
97 		for (j = B_COLS - 2; *p++ != 0;) {
98 			if (--j <= 0) {
99 				/* this row is to be elided */
100 				bzero(&board[base], B_COLS - 2);
101 				scr_update();
102 				tsleep();
103 				while (--base != 0)
104 					board[base + B_COLS] = board[base];
105 				scr_update();
106 				tsleep();
107 				break;
108 			}
109 		}
110 	}
111 }
112 
113 int
114 main(argc, argv)
115 	int argc;
116 	char *argv[];
117 {
118 	register int pos, c;
119 	register struct shape *curshape;
120 	register char *keys;
121 	register int level = 2;
122 	char key_write[6][10];
123 	int ch, i, j;
124 
125 	keys = "jkl pq";
126 
127 	while ((ch = getopt(argc, argv, "k:l:s")) != EOF)
128 		switch(ch) {
129 		case 'k':
130 			if (strlen(keys = optarg) != 6)
131 				usage();
132 			break;
133 		case 'l':
134 			level = atoi(optarg);
135 			if (level < MINLEVEL || level > MAXLEVEL) {
136 				(void)fprintf(stderr,
137 				    "tetris: level must be from %d to %d",
138 				    MINLEVEL, MAXLEVEL);
139 				exit(1);
140 			}
141 			break;
142 		case 's':
143 			showscores(0);
144 			exit(0);
145 		case '?':
146 		default:
147 			usage();
148 		}
149 
150 	argc -= optind;
151 	argv += optind;
152 
153 	if (argc)
154 		usage();
155 
156 	fallrate = 1000000 / level;
157 
158 	for (i = 0; i <= 5; i++) {
159 		for (j = i+1; j <= 5; j++) {
160 			if (keys[i] == keys[j]) {
161 				(void)fprintf(stderr,
162 				    "%s: Duplicate command keys specified.\n",
163 				    argv[0]);
164 				exit (1);
165 			}
166 		}
167 		if (keys[i] == ' ')
168 			strcpy(key_write[i], "<space>");
169 		else {
170 			key_write[i][0] = keys[i];
171 			key_write[i][1] = '\0';
172 		}
173 	}
174 
175 	sprintf(key_msg,
176 "%s - left   %s - rotate   %s - right   %s - drop   %s - pause   %s - quit",
177 		key_write[0], key_write[1], key_write[2], key_write[3],
178 		key_write[4], key_write[5]);
179 
180 	(void)signal(SIGINT, onintr);
181 	scr_init();
182 	setup_board();
183 
184 	srandom(getpid());
185 	scr_set();
186 
187 	pos = A_FIRST*B_COLS + (B_COLS/2)-1;
188 	curshape = randshape();
189 
190 	scr_msg(key_msg, 1);
191 
192 	for (;;) {
193 		place(curshape, pos, 1);
194 		scr_update();
195 		place(curshape, pos, 0);
196 		c = tgetchar();
197 		if (c < 0) {
198 			/*
199 			 * Timeout.  Move down if possible.
200 			 */
201 			if (fits_in(curshape, pos + B_COLS)) {
202 				pos += B_COLS;
203 				continue;
204 			}
205 
206 			/*
207 			 * Put up the current shape `permanently',
208 			 * bump score, and elide any full rows.
209 			 */
210 			place(curshape, pos, 1);
211 			score++;
212 			elide();
213 
214 			/*
215 			 * Choose a new shape.  If it does not fit,
216 			 * the game is over.
217 			 */
218 			curshape = randshape();
219 			pos = A_FIRST*B_COLS + (B_COLS/2)-1;
220 			if (!fits_in(curshape, pos))
221 				break;
222 			continue;
223 		}
224 
225 		/*
226 		 * Handle command keys.
227 		 */
228 		if (c == keys[5]) {
229 			/* quit */
230 			break;
231 		}
232 		if (c == keys[4]) {
233 			static char msg[] =
234 			    "paused - press RETURN to continue";
235 
236 			place(curshape, pos, 1);
237 			do {
238 				scr_update();
239 				scr_msg(key_msg, 0);
240 				scr_msg(msg, 1);
241 				(void) fflush(stdout);
242 			} while (rwait((struct timeval *)NULL) == -1);
243 			scr_msg(msg, 0);
244 			scr_msg(key_msg, 1);
245 			place(curshape, pos, 0);
246 			continue;
247 		}
248 		if (c == keys[0]) {
249 			/* move left */
250 			if (fits_in(curshape, pos - 1))
251 				pos--;
252 			continue;
253 		}
254 		if (c == keys[1]) {
255 			/* turn */
256 			struct shape *new = &shapes[curshape->rot];
257 
258 			if (fits_in(new, pos))
259 				curshape = new;
260 			continue;
261 		}
262 		if (c == keys[2]) {
263 			/* move right */
264 			if (fits_in(curshape, pos + 1))
265 				pos++;
266 			continue;
267 		}
268 		if (c == keys[3]) {
269 			/* move to bottom */
270 			while (fits_in(curshape, pos + B_COLS)) {
271 				pos += B_COLS;
272 				score++;
273 			}
274 			continue;
275 		}
276 		if (c == '\f')
277 			scr_clear();
278 	}
279 
280 	scr_clear();
281 	scr_end();
282 
283 	(void)printf("Your score:  %d point%s  x  level %d  =  %d\n",
284 	    score, score == 1 ? "" : "s", level, score * level);
285 	savescore(level);
286 
287 	printf("\nHit RETURN to see high scores, ^C to skip.\n");
288 
289 	while ((i = getchar()) != '\n')
290 		if (i == EOF)
291 			break;
292 
293 	showscores(level);
294 
295 	exit(0);
296 }
297 
298 void
299 onintr(signo)
300 	int signo;
301 {
302 	scr_clear();
303 	scr_end();
304 	exit(0);
305 }
306 
307 void
308 usage()
309 {
310 	(void)fprintf(stderr, "usage: tetris [-s] [-l level] [-keys]\n");
311 	exit(1);
312 }
313