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