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