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