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 } 362