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 * %sccs.include.redist.c%
9 *
10 * @(#)tetris.c 8.1 (Berkeley) 05/31/93
11 */
12
13 #ifndef lint
14 static char copyright[] =
15 "@(#) Copyright (c) 1992, 1993\n\
16 The Regents of the University of California. All rights reserved.\n";
17 #endif /* not lint */
18
19 /*
20 * Tetris (or however it is spelled).
21 */
22
23 #include <sys/time.h>
24
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "input.h"
32 #include "scores.h"
33 #include "screen.h"
34 #include "tetris.h"
35
36 void onintr __P((int));
37 void usage __P((void));
38
39 /*
40 * Set up the initial board. The bottom display row is completely set,
41 * along with another (hidden) row underneath that. Also, the left and
42 * right edges are set.
43 */
44 static void
setup_board()45 setup_board()
46 {
47 register int i;
48 register cell *p;
49
50 p = board;
51 for (i = B_SIZE; i; i--)
52 #ifndef mips
53 *p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2;
54 #else /* work around compiler bug */
55 *p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2 ? 1 : 0;
56 #endif
57 }
58
59 /*
60 * Elide any full active rows.
61 */
62 static void
elide()63 elide()
64 {
65 register int i, j, base;
66 register cell *p;
67
68 for (i = A_FIRST; i < A_LAST; i++) {
69 base = i * B_COLS + 1;
70 p = &board[base];
71 for (j = B_COLS - 2; *p++ != 0;) {
72 if (--j <= 0) {
73 /* this row is to be elided */
74 bzero(&board[base], B_COLS - 2);
75 scr_update();
76 tsleep();
77 while (--base != 0)
78 board[base + B_COLS] = board[base];
79 scr_update();
80 tsleep();
81 break;
82 }
83 }
84 }
85 }
86
87 int
main(argc,argv)88 main(argc, argv)
89 int argc;
90 char *argv[];
91 {
92 register int pos, c;
93 register struct shape *curshape;
94 register char *keys;
95 register int level = 2;
96 char key_write[6][10];
97 int ch, i, j;
98
99 keys = "jkl pq";
100
101 while ((ch = getopt(argc, argv, "k:l:s")) != EOF)
102 switch(ch) {
103 case 'k':
104 if (strlen(keys = optarg) != 6)
105 usage();
106 break;
107 case 'l':
108 level = atoi(optarg);
109 if (level < MINLEVEL || level > MAXLEVEL) {
110 (void)fprintf(stderr,
111 "tetris: level must be from %d to %d",
112 MINLEVEL, MAXLEVEL);
113 exit(1);
114 }
115 break;
116 case 's':
117 showscores(0);
118 exit(0);
119 case '?':
120 default:
121 usage();
122 }
123
124 argc -= optind;
125 argv += optind;
126
127 if (argc)
128 usage();
129
130 fallrate = 1000000 / level;
131
132 for (i = 0; i <= 5; i++) {
133 for (j = i+1; j <= 5; j++) {
134 if (keys[i] == keys[j]) {
135 (void)fprintf(stderr,
136 "%s: Duplicate command keys specified.\n",
137 argv[0]);
138 exit (1);
139 }
140 }
141 if (keys[i] == ' ')
142 strcpy(key_write[i], "<space>");
143 else {
144 key_write[i][0] = keys[i];
145 key_write[i][1] = '\0';
146 }
147 }
148
149 sprintf(key_msg,
150 "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit",
151 key_write[0], key_write[1], key_write[2], key_write[3],
152 key_write[4], key_write[5]);
153
154 (void)signal(SIGINT, onintr);
155 scr_init();
156 setup_board();
157
158 srandom(getpid());
159 scr_set();
160
161 pos = A_FIRST*B_COLS + (B_COLS/2)-1;
162 curshape = randshape();
163
164 scr_msg(key_msg, 1);
165
166 for (;;) {
167 place(curshape, pos, 1);
168 scr_update();
169 place(curshape, pos, 0);
170 c = tgetchar();
171 if (c < 0) {
172 /*
173 * Timeout. Move down if possible.
174 */
175 if (fits_in(curshape, pos + B_COLS)) {
176 pos += B_COLS;
177 continue;
178 }
179
180 /*
181 * Put up the current shape `permanently',
182 * bump score, and elide any full rows.
183 */
184 place(curshape, pos, 1);
185 score++;
186 elide();
187
188 /*
189 * Choose a new shape. If it does not fit,
190 * the game is over.
191 */
192 curshape = randshape();
193 pos = A_FIRST*B_COLS + (B_COLS/2)-1;
194 if (!fits_in(curshape, pos))
195 break;
196 continue;
197 }
198
199 /*
200 * Handle command keys.
201 */
202 if (c == keys[5]) {
203 /* quit */
204 break;
205 }
206 if (c == keys[4]) {
207 static char msg[] =
208 "paused - press RETURN to continue";
209
210 place(curshape, pos, 1);
211 do {
212 scr_update();
213 scr_msg(key_msg, 0);
214 scr_msg(msg, 1);
215 (void) fflush(stdout);
216 } while (rwait((struct timeval *)NULL) == -1);
217 scr_msg(msg, 0);
218 scr_msg(key_msg, 1);
219 place(curshape, pos, 0);
220 continue;
221 }
222 if (c == keys[0]) {
223 /* move left */
224 if (fits_in(curshape, pos - 1))
225 pos--;
226 continue;
227 }
228 if (c == keys[1]) {
229 /* turn */
230 struct shape *new = &shapes[curshape->rot];
231
232 if (fits_in(new, pos))
233 curshape = new;
234 continue;
235 }
236 if (c == keys[2]) {
237 /* move right */
238 if (fits_in(curshape, pos + 1))
239 pos++;
240 continue;
241 }
242 if (c == keys[3]) {
243 /* move to bottom */
244 while (fits_in(curshape, pos + B_COLS)) {
245 pos += B_COLS;
246 score++;
247 }
248 continue;
249 }
250 if (c == '\f')
251 scr_clear();
252 }
253
254 scr_clear();
255 scr_end();
256
257 (void)printf("Your score: %d point%s x level %d = %d\n",
258 score, score == 1 ? "" : "s", level, score * level);
259 savescore(level);
260
261 printf("\nHit RETURN to see high scores, ^C to skip.\n");
262
263 while ((i = getchar()) != '\n')
264 if (i == EOF)
265 break;
266
267 showscores(level);
268
269 exit(0);
270 }
271
272 void
onintr(signo)273 onintr(signo)
274 int signo;
275 {
276 scr_clear();
277 scr_end();
278 exit(0);
279 }
280
281 void
usage()282 usage()
283 {
284 (void)fprintf(stderr, "usage: tetris [-s] [-l level] [-keys]\n");
285 exit(1);
286 }
287