Merge branch 'vendor/DHCPCD'
[dragonfly.git] / games / tetris / tetris.c
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
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
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 *
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
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
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
356 usage(void)
357 {
358         fprintf(stderr, "usage: %s [-cps] [-k keys] "
359             "[-l level]\n", getprogname());
360         exit(1);
361 }