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