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[100]; 62 int showpreview, classic; 63 64 static void elide(void); 65 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 int 147 main(int argc, char *argv[]) 148 { 149 int pos, c; 150 const char *keys; 151 int level = 2; 152 char key_write[6][10]; 153 const char *errstr; 154 int ch, i, j; 155 156 keys = "jkl pq"; 157 158 classic = showpreview = 0; 159 while ((ch = getopt(argc, argv, "ck:l:ps")) != -1) 160 switch(ch) { 161 case 'c': 162 /* 163 * this means: 164 * - rotate the other way; 165 * - no reverse video. 166 */ 167 classic = 1; 168 break; 169 case 'k': 170 if (strlen(keys = optarg) != 6) 171 usage(); 172 break; 173 case 'l': 174 level = (int)strtonum(optarg, MINLEVEL, MAXLEVEL, 175 &errstr); 176 if (errstr) 177 errx(1, "level must be from %d to %d", 178 MINLEVEL, MAXLEVEL); 179 break; 180 case 'p': 181 showpreview = 1; 182 break; 183 case 's': 184 showscores(0); 185 return 0; 186 default: 187 usage(); 188 } 189 190 argc -= optind; 191 argv += optind; 192 193 if (argc) 194 usage(); 195 196 fallrate = 1000000000L / level; 197 198 for (i = 0; i <= 5; i++) { 199 for (j = i+1; j <= 5; j++) { 200 if (keys[i] == keys[j]) 201 errx(1, "duplicate command keys specified."); 202 } 203 if (keys[i] == ' ') 204 strlcpy(key_write[i], "<space>", sizeof key_write[i]); 205 else { 206 key_write[i][0] = keys[i]; 207 key_write[i][1] = '\0'; 208 } 209 } 210 211 snprintf(key_msg, sizeof key_msg, 212 "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit", 213 key_write[0], key_write[1], key_write[2], key_write[3], 214 key_write[4], key_write[5]); 215 216 signal(SIGINT, onintr); 217 scr_init(); 218 setup_board(); 219 220 scr_set(); 221 222 pos = A_FIRST*B_COLS + (B_COLS/2)-1; 223 nextshape = randshape(); 224 curshape = randshape(); 225 226 scr_msg(key_msg, 1); 227 228 for (;;) { 229 place(curshape, pos, 1); 230 scr_update(); 231 place(curshape, pos, 0); 232 c = tgetchar(); 233 if (c < 0) { 234 /* 235 * Timeout. Move down if possible. 236 */ 237 if (fits_in(curshape, pos + B_COLS)) { 238 pos += B_COLS; 239 continue; 240 } 241 242 /* 243 * Put up the current shape `permanently', 244 * bump score, and elide any full rows. 245 */ 246 place(curshape, pos, 1); 247 score++; 248 elide(); 249 250 /* 251 * Choose a new shape. If it does not fit, 252 * the game is over. 253 */ 254 curshape = nextshape; 255 nextshape = randshape(); 256 pos = A_FIRST*B_COLS + (B_COLS/2)-1; 257 if (!fits_in(curshape, pos)) 258 break; 259 continue; 260 } 261 262 /* 263 * Handle command keys. 264 */ 265 if (c == keys[5]) { 266 /* quit */ 267 break; 268 } 269 if (c == keys[4]) { 270 static char msg[] = 271 "paused - press RETURN to continue"; 272 273 place(curshape, pos, 1); 274 do { 275 scr_update(); 276 scr_msg(key_msg, 0); 277 scr_msg(msg, 1); 278 fflush(stdout); 279 } while (rwait(NULL) == -1); 280 scr_msg(msg, 0); 281 scr_msg(key_msg, 1); 282 place(curshape, pos, 0); 283 continue; 284 } 285 if (c == keys[0]) { 286 /* move left */ 287 if (fits_in(curshape, pos - 1)) 288 pos--; 289 continue; 290 } 291 if (c == keys[1]) { 292 /* turn */ 293 const struct shape *new = &shapes[ 294 classic? curshape->rotc : curshape->rot]; 295 296 if (fits_in(new, pos)) 297 curshape = new; 298 continue; 299 } 300 if (c == keys[2]) { 301 /* move right */ 302 if (fits_in(curshape, pos + 1)) 303 pos++; 304 continue; 305 } 306 if (c == keys[3]) { 307 /* move to bottom */ 308 while (fits_in(curshape, pos + B_COLS)) { 309 pos += B_COLS; 310 score++; 311 } 312 continue; 313 } 314 if (c == '\f') { 315 scr_clear(); 316 scr_msg(key_msg, 1); 317 } 318 } 319 320 scr_clear(); 321 scr_end(); 322 323 if (showpreview == 0) 324 printf("Your score: %d point%s x level %d = %d\n", 325 score, score == 1 ? "" : "s", level, score * level); 326 else { 327 printf("Your score: %d point%s x level %d x preview penalty %0.3f = %d\n", 328 score, score == 1 ? "" : "s", level, (double)PRE_PENALTY, 329 (int)(score * level * PRE_PENALTY)); 330 score = score * PRE_PENALTY; 331 } 332 savescore(level); 333 334 printf("\nHit RETURN to see high scores, ^C to skip.\n"); 335 336 while ((i = getchar()) != '\n') 337 if (i == EOF) 338 break; 339 340 showscores(level); 341 342 return 0; 343 } 344 345 void 346 onintr(int signo __unused) 347 { 348 scr_clear(); /* XXX signal race */ 349 scr_end(); /* XXX signal race */ 350 _exit(0); 351 } 352 353 void 354 usage(void) 355 { 356 fprintf(stderr, "usage: %s [-cps] [-k keys] " 357 "[-l level]\n", getprogname()); 358 exit(1); 359 } 360