1 /* Micro Tetris, based on an obfuscated tetris, 1989 IOCCC Best Game
3 * Copyright (c) 1989 John Tromp <john.tromp@gmail.com>
4 * Copyright (c) 2009-2021 Joachim Wiberg <troglobit@gmail.com>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * See the following URLs for more information, first John Tromp's page about
19 * the game http://homepages.cwi.nl/~tromp/tetris.html then there's the entry
20 * page at IOCCC http://www.ioccc.org/1989/tromp.hint
33 #define clrscr() puts ("\033[2J\033[1;1H")
34 #define gotoxy(x,y) printf("\033[%d;%dH", y, x)
35 #define hidecursor() puts ("\033[?25l")
36 #define showcursor() puts ("\033[?25h")
37 #define bgcolor(c,s) printf("\033[%dm" s, c ? c + 40 : 0)
39 #define SIGNAL(signo, cb) \
40 sigemptyset(&sa.sa_mask); \
41 sigaddset(&sa.sa_mask, signo); \
44 sigaction(signo, &sa, NULL)
49 #define B_SIZE (B_ROWS * B_COLS)
51 #define TL -B_COLS-1 /* top left */
52 #define TC -B_COLS /* top center */
53 #define TR -B_COLS+1 /* top right */
54 #define ML -1 /* middle left */
55 #define MR 1 /* middle right */
56 #define BL B_COLS-1 /* bottom left */
57 #define BC B_COLS /* bottom center */
58 #define BR B_COLS+1 /* bottom right */
60 /* These can be overridden by the user. */
61 #define DEFAULT_KEYS "jkl pq"
69 #define HIGH_SCORE_FILE "/var/games/tetris.scores"
70 #define TEMP_SCORE_FILE "/tmp/tetris-tmp.scores"
72 static volatile sig_atomic_t running
= 1;
74 static struct termios savemodes
;
75 static int havemodes
= 0;
77 static char *keys
= DEFAULT_KEYS
;
79 static int points
= 0;
80 static int lines_cleared
= 0;
81 static int board
[B_SIZE
], shadow
[B_SIZE
];
83 static int *peek_shape
; /* peek preview of next shape */
88 static int shapes
[] = {
89 7, TL
, TC
, MR
, 2, /* ""__ */
90 8, TR
, TC
, ML
, 3, /* __"" */
91 9, ML
, MR
, BC
, 1, /* "|" */
92 3, TL
, TC
, ML
, 4, /* square */
93 12, ML
, BL
, MR
, 5, /* |""" */
94 15, ML
, BR
, MR
, 6, /* """| */
95 18, ML
, MR
, 2, 7, /* ---- sticks out */
96 0, TC
, ML
, BL
, 2, /* / */
97 1, TC
, MR
, BR
, 3, /* \ */
98 10, TC
, MR
, BC
, 1, /* |- */
99 11, TC
, ML
, MR
, 1, /* _|_ */
100 2, TC
, ML
, BC
, 1, /* -| */
101 13, TC
, BC
, BR
, 5, /* |_ */
102 14, TR
, ML
, MR
, 5, /* ___| */
103 4, TL
, TC
, BC
, 5, /* "| */
104 16, TR
, TC
, BC
, 6, /* |" */
105 17, TL
, MR
, ML
, 6, /* |___ */
106 5, TC
, BC
, BL
, 6, /* _| */
107 6, TC
, BC
, 2 * B_COLS
, 7, /* | sticks out */
110 static void draw(int x
, int y
, int c
)
116 static int update(void)
120 #ifdef ENABLE_PREVIEW
121 static int shadow_preview
[B_COLS
* 10] = { 0 };
122 int preview
[B_COLS
* 10] = { 0 };
125 preview
[2 * B_COLS
+ 1] = pcolor
;
126 preview
[2 * B_COLS
+ 1 + peek_shape
[1]] = pcolor
;
127 preview
[2 * B_COLS
+ 1 + peek_shape
[2]] = pcolor
;
128 preview
[2 * B_COLS
+ 1 + peek_shape
[3]] = pcolor
;
130 for (y
= 0; y
< 4; y
++) {
131 for (x
= 0; x
< B_COLS
; x
++) {
132 if (preview
[y
* B_COLS
+ x
] - shadow_preview
[y
* B_COLS
+ x
]) {
133 int c
= preview
[y
* B_COLS
+ x
]; /* color */
135 shadow_preview
[y
* B_COLS
+ x
] = c
;
136 draw(x
* 2 + 26 + 28, start
+ y
, c
);
143 for (y
= 1; y
< B_ROWS
- 1; y
++) {
144 for (x
= 0; x
< B_COLS
; x
++) {
145 if (board
[y
* B_COLS
+ x
] - shadow
[y
* B_COLS
+ x
]) {
146 int c
= board
[y
* B_COLS
+ x
]; /* color */
148 shadow
[y
* B_COLS
+ x
] = c
;
149 draw(x
* 2 + 28, y
, c
);
154 /* Update points and level */
155 while (lines_cleared
>= 10) {
161 /* Display current level and points */
163 printf("\033[0mLevel : %d", level
);
165 printf("Points : %d", points
);
167 #ifdef ENABLE_PREVIEW
178 /* Check if shape fits in the current position */
179 static int fits_in(int *s
, int pos
)
181 if (board
[pos
] || board
[pos
+ s
[1]] || board
[pos
+ s
[2]] || board
[pos
+ s
[3]])
187 /* place shape at pos with color */
188 static void place(int *s
, int pos
, int c
)
191 board
[pos
+ s
[1]] = c
;
192 board
[pos
+ s
[2]] = c
;
193 board
[pos
+ s
[3]] = c
;
196 static int *next_shape(void)
198 int pos
= rand() % 7 * 5;
199 int *next
= peek_shape
;
201 peek_shape
= &shapes
[pos
];
202 pcolor
= peek_shape
[4];
210 static void show_high_score(void)
212 #ifdef ENABLE_HIGH_SCORE
215 if ((tmpscore
= fopen(HIGH_SCORE_FILE
, "a"))) {
216 char *name
= getenv("LOGNAME");
221 fprintf(tmpscore
, "%7d\t %5d\t %3d\t%s\n", points
* level
, points
, level
, name
);
224 system("cat " HIGH_SCORE_FILE
"| sort -rn | head -10 >" TEMP_SCORE_FILE
225 "&& cp " TEMP_SCORE_FILE
" " HIGH_SCORE_FILE
);
226 remove(TEMP_SCORE_FILE
);
229 if (!access(HIGH_SCORE_FILE
, R_OK
)) {
230 // puts("\nHit RETURN to see high scores, ^C to skip.");
231 fprintf(stderr
, " Score\tPoints\tLevel\tName\n");
232 system("cat " HIGH_SCORE_FILE
);
234 #endif /* ENABLE_HIGH_SCORE */
237 static void show_online_help(void)
239 const int start
= 11;
241 gotoxy(26 + 28, start
);
242 puts("\033[0mj - left");
243 gotoxy(26 + 28, start
+ 1);
245 gotoxy(26 + 28, start
+ 2);
247 gotoxy(26 + 28, start
+ 3);
248 puts("space - drop");
249 gotoxy(26 + 28, start
+ 4);
251 gotoxy(26 + 28, start
+ 5);
255 /* Code stolen from http://c-faq.com/osdep/cbreak.html */
256 static int tty_init(void)
258 struct termios modmodes
;
260 if (tcgetattr(fileno(stdin
), &savemodes
) < 0)
266 /* "stty cbreak -echo" */
267 modmodes
= savemodes
;
268 modmodes
.c_lflag
&= ~ICANON
;
269 modmodes
.c_lflag
&= ~ECHO
;
270 modmodes
.c_cc
[VMIN
] = 1;
271 modmodes
.c_cc
[VTIME
] = 0;
273 return tcsetattr(fileno(stdin
), TCSANOW
, &modmodes
);
276 static int tty_exit(void)
284 return tcsetattr(fileno(stdin
), TCSANOW
, &savemodes
);
287 static void freeze(int enable
)
292 sigaddset(&set
, SIGALRM
);
294 sigprocmask(enable
? SIG_BLOCK
: SIG_UNBLOCK
, &set
, NULL
);
297 static void alarm_handler(int signo
)
303 /* On init from main() */
307 h
[3] -= h
[3] / (3000 - 10 * level
);
308 setitimer(0, (struct itimerval
*)h
, 0);
311 static void exit_handler(int signo
)
317 static void sig_init(void)
321 SIGNAL(SIGINT
, exit_handler
);
322 SIGNAL(SIGTERM
, exit_handler
);
323 SIGNAL(SIGALRM
, alarm_handler
);
325 /* Start update timer. */
331 int c
= 0, i
, j
, *ptr
;
335 /* Initialize board, grey border, used to be white(7) */
337 for (i
= B_SIZE
; i
; i
--)
338 *ptr
++ = i
< 25 || i
% B_COLS
< 2 ? 60 : 0;
340 srand((unsigned int)time(NULL
));
341 if (tty_init() == -1)
350 shape
= next_shape();
353 if (fits_in(shape
, pos
+ B_COLS
)) {
356 place(shape
, pos
, color
);
358 for (j
= 0; j
< 252; j
= B_COLS
* (j
/ B_COLS
+ 1)) {
359 for (; board
[++j
];) {
360 if (j
% B_COLS
== 10) {
363 for (; j
% B_COLS
; board
[j
--] = 0)
367 for (; --j
; board
[j
+ B_COLS
] = board
[j
])
373 shape
= next_shape();
374 if (!fits_in(shape
, pos
= 17))
379 if (c
== keys
[KEY_LEFT
]) {
380 if (!fits_in(shape
, --pos
))
384 if (c
== keys
[KEY_ROTATE
]) {
386 shape
= &shapes
[5 * *shape
]; /* Rotate */
387 /* Check if it fits, if not restore shape from backup */
388 if (!fits_in(shape
, pos
))
392 if (c
== keys
[KEY_RIGHT
]) {
393 if (!fits_in(shape
, ++pos
))
397 if (c
== keys
[KEY_DROP
]) {
398 for (; fits_in(shape
, pos
+ B_COLS
); ++points
)
402 if (c
== keys
[KEY_PAUSE
] || c
== keys
[KEY_QUIT
]) {
405 if (c
== keys
[KEY_QUIT
]) {
409 printf("\033[0mYour score: %d points x level %d = %d\n\n", points
, level
, points
* level
);
414 for (j
= B_SIZE
; j
--; shadow
[j
] = 0)
417 while (getchar() - keys
[KEY_PAUSE
])
420 // puts("\033[H\033[J\033[7m");
424 place(shape
, pos
, color
);
426 place(shape
, pos
, 0);
430 if (tty_exit() == -1)
438 * indent-tabs-mode: t
439 * c-file-style: "linux"