1 /* $NetBSD: tetris.c,v 1.19 2008/07/20 01:03:22 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Chris Torek and Darren F. Provine. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)tetris.c 8.1 (Berkeley) 5/31/93 35 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 __COPYRIGHT("@(#) Copyright (c) 1992, 1993\ 40 The Regents of the University of California. All rights reserved."); 41 #endif /* not lint */ 42 43 /* 44 * Tetris (or however it is spelled). 45 */ 46 47 #include <sys/time.h> 48 49 #include <err.h> 50 #include <fcntl.h> 51 #include <signal.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 #include "input.h" 58 #include "scores.h" 59 #include "screen.h" 60 #include "tetris.h" 61 62 cell board[B_SIZE]; /* 1 => occupied, 0 => empty */ 63 64 int Rows, Cols; /* current screen size */ 65 66 const struct shape *curshape; 67 const struct shape *nextshape; 68 69 long fallrate; /* less than 1 million; smaller => faster */ 70 71 int score; /* the obvious thing */ 72 gid_t gid, egid; 73 74 char key_msg[100]; 75 int showpreview; 76 77 static void elide(void); 78 static void setup_board(void); 79 int main(int, char **); 80 void onintr(int) __dead; 81 void usage(void) __dead; 82 83 /* 84 * Set up the initial board. The bottom display row is completely set, 85 * along with another (hidden) row underneath that. Also, the left and 86 * right edges are set. 87 */ 88 static void 89 setup_board() 90 { 91 int i; 92 cell *p; 93 94 p = board; 95 for (i = B_SIZE; i; i--) 96 *p++ = i <= (2 * B_COLS) || (i % B_COLS) < 2; 97 } 98 99 /* 100 * Elide any full active rows. 101 */ 102 static void 103 elide() 104 { 105 int i, j, base; 106 cell *p; 107 108 for (i = A_FIRST; i < A_LAST; i++) { 109 base = i * B_COLS + 1; 110 p = &board[base]; 111 for (j = B_COLS - 2; *p++ != 0;) { 112 if (--j <= 0) { 113 /* this row is to be elided */ 114 memset(&board[base], 0, B_COLS - 2); 115 scr_update(); 116 tsleep(); 117 while (--base != 0) 118 board[base + B_COLS] = board[base]; 119 scr_update(); 120 tsleep(); 121 break; 122 } 123 } 124 } 125 } 126 127 int 128 main(argc, argv) 129 int argc; 130 char *argv[]; 131 { 132 int pos, c; 133 const char *keys; 134 int level = 2; 135 char key_write[6][10]; 136 int ch, i, j; 137 int fd; 138 139 gid = getgid(); 140 egid = getegid(); 141 setegid(gid); 142 143 fd = open("/dev/null", O_RDONLY); 144 if (fd < 3) 145 exit(1); 146 close(fd); 147 148 keys = "jkl pq"; 149 150 while ((ch = getopt(argc, argv, "k:l:ps")) != -1) 151 switch(ch) { 152 case 'k': 153 if (strlen(keys = optarg) != 6) 154 usage(); 155 break; 156 case 'l': 157 level = atoi(optarg); 158 if (level < MINLEVEL || level > MAXLEVEL) { 159 errx(1, "level must be from %d to %d", 160 MINLEVEL, MAXLEVEL); 161 } 162 break; 163 case 'p': 164 showpreview = 1; 165 break; 166 case 's': 167 showscores(0); 168 exit(0); 169 case '?': 170 default: 171 usage(); 172 } 173 174 argc -= optind; 175 argv += optind; 176 177 if (argc) 178 usage(); 179 180 fallrate = 1000000 / level; 181 182 for (i = 0; i <= 5; i++) { 183 for (j = i+1; j <= 5; j++) { 184 if (keys[i] == keys[j]) { 185 errx(1, "duplicate command keys specified."); 186 } 187 } 188 if (keys[i] == ' ') 189 strcpy(key_write[i], "<space>"); 190 else { 191 key_write[i][0] = keys[i]; 192 key_write[i][1] = '\0'; 193 } 194 } 195 196 sprintf(key_msg, 197 "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit", 198 key_write[0], key_write[1], key_write[2], key_write[3], 199 key_write[4], key_write[5]); 200 201 (void)signal(SIGINT, onintr); 202 scr_init(); 203 setup_board(); 204 205 srandom(getpid()); 206 scr_set(); 207 208 pos = A_FIRST*B_COLS + (B_COLS/2)-1; 209 nextshape = randshape(); 210 curshape = randshape(); 211 212 scr_msg(key_msg, 1); 213 214 for (;;) { 215 place(curshape, pos, 1); 216 scr_update(); 217 place(curshape, pos, 0); 218 c = tgetchar(); 219 if (c < 0) { 220 /* 221 * Timeout. Move down if possible. 222 */ 223 if (fits_in(curshape, pos + B_COLS)) { 224 pos += B_COLS; 225 continue; 226 } 227 228 /* 229 * Put up the current shape `permanently', 230 * bump score, and elide any full rows. 231 */ 232 place(curshape, pos, 1); 233 score++; 234 elide(); 235 236 /* 237 * Choose a new shape. If it does not fit, 238 * the game is over. 239 */ 240 curshape = nextshape; 241 nextshape = randshape(); 242 pos = A_FIRST*B_COLS + (B_COLS/2)-1; 243 if (!fits_in(curshape, pos)) 244 break; 245 continue; 246 } 247 248 /* 249 * Handle command keys. 250 */ 251 if (c == keys[5]) { 252 /* quit */ 253 break; 254 } 255 if (c == keys[4]) { 256 static char msg[] = 257 "paused - press RETURN to continue"; 258 259 place(curshape, pos, 1); 260 do { 261 scr_update(); 262 scr_msg(key_msg, 0); 263 scr_msg(msg, 1); 264 (void) fflush(stdout); 265 } while (rwait((struct timeval *)NULL) == -1); 266 scr_msg(msg, 0); 267 scr_msg(key_msg, 1); 268 place(curshape, pos, 0); 269 continue; 270 } 271 if (c == keys[0]) { 272 /* move left */ 273 if (fits_in(curshape, pos - 1)) 274 pos--; 275 continue; 276 } 277 if (c == keys[1]) { 278 /* turn */ 279 const struct shape *new = &shapes[curshape->rot]; 280 281 if (fits_in(new, pos)) 282 curshape = new; 283 continue; 284 } 285 if (c == keys[2]) { 286 /* move right */ 287 if (fits_in(curshape, pos + 1)) 288 pos++; 289 continue; 290 } 291 if (c == keys[3]) { 292 /* move to bottom */ 293 while (fits_in(curshape, pos + B_COLS)) { 294 pos += B_COLS; 295 score++; 296 } 297 continue; 298 } 299 if (c == '\f') { 300 scr_clear(); 301 scr_msg(key_msg, 1); 302 } 303 } 304 305 scr_clear(); 306 scr_end(); 307 308 (void)printf("Your score: %d point%s x level %d = %d\n", 309 score, score == 1 ? "" : "s", level, score * level); 310 savescore(level); 311 312 printf("\nHit RETURN to see high scores, ^C to skip.\n"); 313 314 while ((i = getchar()) != '\n') 315 if (i == EOF) 316 break; 317 318 showscores(level); 319 320 exit(0); 321 } 322 323 void 324 onintr(signo) 325 int signo __unused; 326 { 327 scr_clear(); 328 scr_end(); 329 exit(0); 330 } 331 332 void 333 usage() 334 { 335 (void)fprintf(stderr, "usage: tetris [-ps] [-k keys] [-l level]\n"); 336 exit(1); 337 } 338