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