/* * Copyright (c) 2011 Martin Decky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** Attributations * * tetris.c 8.1 (Berkeley) 5/31/93 * NetBSD: tetris.c,v 1.2 1995/04/22 07:42:47 cgd * OpenBSD: tetris.c,v 1.21 2006/04/20 03:24:12 ray * * Based upon BSD Tetris * * Copyright (c) 1992, 1993 * The Regents of the University of California. * Distributed under BSD license. * * This code is derived from software contributed to Berkeley by * Chris Torek and Darren F. Provine. * */ /** @addtogroup tetris Tetris * @brief Tetris ported from OpenBSD * @{ */ /** @file */ static const char copyright[] = "@(#) Copyright (c) 1992, 1993\n" "\tThe Regents of the University of California. All rights reserved.\n"; #include #include #include #include #include #include #include #include #include #include "scores.h" #include "screen.h" #include "tetris.h" cell board[B_SIZE]; int Rows; int Cols; const struct shape *curshape; const struct shape *nextshape; long fallrate; int score; char key_msg[100]; int showpreview; int classic; static void elide(void); static void setup_board(void); static const struct shape *randshape(void); static void usage(void); static int firstgame = 1; /* * Set up the initial board. The bottom display row is completely set, * along with another (hidden) row underneath that. Also, the left and * right edges are set. */ static void setup_board(void) { int i; cell *p = board; for (i = B_SIZE; i; i--) *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 0x0000ff : 0x000000; } /* * Elide any full active rows. */ static void elide(void) { int rows = 0; int i; int j; int base; cell *p; for (i = A_FIRST; i < A_LAST; i++) { base = i * B_COLS + 1; p = &board[base]; for (j = B_COLS - 2; *p++ != 0;) { if (--j <= 0) { /* This row is to be elided */ rows++; memset(&board[base], 0, sizeof(cell) * (B_COLS - 2)); scr_update(); tsleep(); while (--base != 0) board[base + B_COLS] = board[base]; scr_update(); tsleep(); break; } } } switch (rows) { case 1: score += 10; break; case 2: score += 30; break; case 3: score += 70; break; case 4: score += 150; break; default: break; } } const struct shape *randshape(void) { const struct shape *tmp = &shapes[random() % 7]; int i; int j = random() % 4; for (i = 0; i < j; i++) tmp = &shapes[classic ? tmp->rotc : tmp->rot]; return (tmp); } static void srandomdev(void) { struct timeval tv; gettimeofday(&tv, NULL); srandom(tv.tv_sec + tv.tv_usec / 100000); } static void tetris_menu_draw(int level) { clear_screen(); moveto(5, 10); puts("Tetris\n\n"); moveto(8, 10); printf("Level = %d (press keys 1 - 9 to change)", level); moveto(9, 10); printf("Preview is %s (press 'p' to change)", (showpreview ? "on ": "off")); moveto(12, 10); printf("Press 'h' to show hiscore table."); moveto(13, 10); printf("Press 's' to start game."); moveto(14, 10); printf("Press 'q' to quit game."); moveto(20, 10); printf("In game controls:"); moveto(21, 0); puts(key_msg); } static int tetris_menu(int *level) { tetris_menu_draw(*level); while (1) { int i = getchar(); switch(i) { case 'p': showpreview = !showpreview; moveto(9, 21); if (showpreview) printf("on "); else printf("off"); break; case 'h': loadscores(); showscores(firstgame); tetris_menu_draw(*level); break; case 's': firstgame = 0; return 1; case 'q': return 0; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *level = i - '0'; moveto(8, 18); printf("%d", *level); break; } } } int main(int argc, char *argv[]) { int pos; int c; const char *keys; int level = 2; char key_write[6][10]; int i; int j; int ch; console = console_init(stdin, stdout); keys = "jkl pq"; classic = 0; showpreview = 1; while ((ch = getopt(argc, argv, "ck:ps")) != -1) switch(ch) { case 'c': /* * this means: * - rotate the other way * - no reverse video */ classic = 1; break; case 'k': if (str_size(keys = optarg) != 6) usage(); break; case 'p': showpreview = 1; break; case 's': showscores(0); exit(0); default: usage(); } argc -= optind; argv += optind; if (argc) usage(); for (i = 0; i <= 5; i++) { for (j = i + 1; j <= 5; j++) { if (keys[i] == keys[j]) errx(1, "%s", "duplicate command keys specified."); } if (keys[i] == ' ') str_cpy(key_write[i], sizeof(key_write[i]), ""); else { key_write[i][0] = keys[i]; key_write[i][1] = '\0'; } } snprintf(key_msg, sizeof(key_msg), "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit", key_write[0], key_write[1], key_write[2], key_write[3], key_write[4], key_write[5]); scr_init(); if (loadscores() != EOK) initscores(); while (tetris_menu(&level)) { fallrate = 1000000 / level; scr_clear(); setup_board(); srandomdev(); scr_set(); pos = A_FIRST * B_COLS + (B_COLS / 2) - 1; nextshape = randshape(); curshape = randshape(); scr_msg(key_msg, 1); while (1) { place(curshape, pos, 1); scr_update(); place(curshape, pos, 0); c = tgetchar(); if (c < 0) { /* * Timeout. Move down if possible. */ if (fits_in(curshape, pos + B_COLS)) { pos += B_COLS; continue; } /* * Put up the current shape `permanently', * bump score, and elide any full rows. */ place(curshape, pos, 1); score++; elide(); /* * Choose a new shape. If it does not fit, * the game is over. */ curshape = nextshape; nextshape = randshape(); pos = A_FIRST * B_COLS + (B_COLS / 2) - 1; if (!fits_in(curshape, pos)) break; continue; } /* * Handle command keys. */ if (c == keys[5]) { /* quit */ break; } if (c == keys[4]) { static char msg[] = "paused - press RETURN to continue"; place(curshape, pos, 1); do { scr_update(); scr_msg(key_msg, 0); scr_msg(msg, 1); console_flush(console); } while (!twait()); scr_msg(msg, 0); scr_msg(key_msg, 1); place(curshape, pos, 0); continue; } if (c == keys[0]) { /* move left */ if (fits_in(curshape, pos - 1)) pos--; continue; } if (c == keys[1]) { /* turn */ const struct shape *new = &shapes[classic ? curshape->rotc : curshape->rot]; if (fits_in(new, pos)) curshape = new; continue; } if (c == keys[2]) { /* move right */ if (fits_in(curshape, pos + 1)) pos++; continue; } if (c == keys[3]) { /* move to bottom */ while (fits_in(curshape, pos + B_COLS)) { pos += B_COLS; score++; } continue; } if (c == '\f') { scr_clear(); scr_msg(key_msg, 1); } } scr_clear(); loadscores(); insertscore(score, level); savescores(); score = 0; } scr_clear(); printf("\nGame over.\n"); scr_end(); return 0; } void usage(void) { fprintf(stderr, "usage: tetris [-ps] [-k keys]\n"); exit(1); } /** @} */