xref: /original-bsd/games/tetris/tetris.c (revision 426ca381)
1 /*-
2  * Copyright (c) 1992 The Regents of the University of California.
3  * 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	5.3 (Berkeley) 12/23/92
11  */
12 
13 #ifndef lint
14 char copyright[] =
15 "@(#) Copyright (c) 1992 The Regents of the University of California.\n\
16  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
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
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
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
273 onintr(signo)
274 	int signo;
275 {
276 	scr_clear();
277 	scr_end();
278 	exit(0);
279 }
280 
281 void
282 usage()
283 {
284 	(void)fprintf(stderr, "usage: tetris [-s] [-l level] [-keys]\n");
285 	exit(1);
286 }
287