1 /* $NetBSD: tetris.c,v 1.21 2009/05/25 04:33:53 dholland 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(void) 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(void) 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(int argc, char *argv[]) 129 { 130 int pos, c; 131 const char *keys; 132 int level = 2; 133 char key_write[6][10]; 134 int ch, i, j; 135 int fd; 136 137 gid = getgid(); 138 egid = getegid(); 139 setegid(gid); 140 141 fd = open("/dev/null", O_RDONLY); 142 if (fd < 3) 143 exit(1); 144 close(fd); 145 146 keys = "jkl pq"; 147 148 while ((ch = getopt(argc, argv, "k:l:ps")) != -1) 149 switch(ch) { 150 case 'k': 151 if (strlen(keys = optarg) != 6) 152 usage(); 153 break; 154 case 'l': 155 level = atoi(optarg); 156 if (level < MINLEVEL || level > MAXLEVEL) { 157 errx(1, "level must be from %d to %d", 158 MINLEVEL, MAXLEVEL); 159 } 160 break; 161 case 'p': 162 showpreview = 1; 163 break; 164 case 's': 165 showscores(0); 166 exit(0); 167 case '?': 168 default: 169 usage(); 170 } 171 172 argc -= optind; 173 argv += optind; 174 175 if (argc) 176 usage(); 177 178 fallrate = 1000000 / level; 179 180 for (i = 0; i <= 5; i++) { 181 for (j = i+1; j <= 5; j++) { 182 if (keys[i] == keys[j]) { 183 errx(1, "duplicate command keys specified."); 184 } 185 } 186 if (keys[i] == ' ') 187 strcpy(key_write[i], "<space>"); 188 else { 189 key_write[i][0] = keys[i]; 190 key_write[i][1] = '\0'; 191 } 192 } 193 194 snprintf(key_msg, sizeof(key_msg), 195 "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit", 196 key_write[0], key_write[1], key_write[2], key_write[3], 197 key_write[4], key_write[5]); 198 199 (void)signal(SIGINT, onintr); 200 scr_init(); 201 setup_board(); 202 203 srandom(getpid()); 204 scr_set(); 205 206 pos = A_FIRST*B_COLS + (B_COLS/2)-1; 207 nextshape = randshape(); 208 curshape = randshape(); 209 210 scr_msg(key_msg, 1); 211 212 for (;;) { 213 place(curshape, pos, 1); 214 scr_update(); 215 place(curshape, pos, 0); 216 c = tgetchar(); 217 if (c < 0) { 218 /* 219 * Timeout. Move down if possible. 220 */ 221 if (fits_in(curshape, pos + B_COLS)) { 222 pos += B_COLS; 223 continue; 224 } 225 226 /* 227 * Put up the current shape `permanently', 228 * bump score, and elide any full rows. 229 */ 230 place(curshape, pos, 1); 231 score++; 232 elide(); 233 234 /* 235 * Choose a new shape. If it does not fit, 236 * the game is over. 237 */ 238 curshape = nextshape; 239 nextshape = randshape(); 240 pos = A_FIRST*B_COLS + (B_COLS/2)-1; 241 if (!fits_in(curshape, pos)) 242 break; 243 continue; 244 } 245 246 /* 247 * Handle command keys. 248 */ 249 if (c == keys[5]) { 250 /* quit */ 251 break; 252 } 253 if (c == keys[4]) { 254 static char msg[] = 255 "paused - press RETURN to continue"; 256 257 place(curshape, pos, 1); 258 do { 259 scr_update(); 260 scr_msg(key_msg, 0); 261 scr_msg(msg, 1); 262 (void) fflush(stdout); 263 } while (rwait((struct timeval *)NULL) == -1); 264 scr_msg(msg, 0); 265 scr_msg(key_msg, 1); 266 place(curshape, pos, 0); 267 continue; 268 } 269 if (c == keys[0]) { 270 /* move left */ 271 if (fits_in(curshape, pos - 1)) 272 pos--; 273 continue; 274 } 275 if (c == keys[1]) { 276 /* turn */ 277 const struct shape *new = &shapes[curshape->rot]; 278 279 if (fits_in(new, pos)) 280 curshape = new; 281 continue; 282 } 283 if (c == keys[2]) { 284 /* move right */ 285 if (fits_in(curshape, pos + 1)) 286 pos++; 287 continue; 288 } 289 if (c == keys[3]) { 290 /* move to bottom */ 291 while (fits_in(curshape, pos + B_COLS)) { 292 pos += B_COLS; 293 score++; 294 } 295 continue; 296 } 297 if (c == '\f') { 298 scr_clear(); 299 scr_msg(key_msg, 1); 300 } 301 } 302 303 scr_clear(); 304 scr_end(); 305 306 (void)printf("Your score: %d point%s x level %d = %d\n", 307 score, score == 1 ? "" : "s", level, score * level); 308 savescore(level); 309 310 printf("\nHit RETURN to see high scores, ^C to skip.\n"); 311 312 while ((i = getchar()) != '\n') 313 if (i == EOF) 314 break; 315 316 showscores(level); 317 318 exit(0); 319 } 320 321 void 322 onintr(int signo __unused) 323 { 324 scr_clear(); 325 scr_end(); 326 exit(0); 327 } 328 329 void 330 usage(void) 331 { 332 (void)fprintf(stderr, "usage: tetris [-ps] [-k keys] [-l level]\n"); 333 exit(1); 334 } 335