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
36 static struct termios savemodes
;
37 static int havemodes
= 0;
39 #define TL -B_COLS-1 /* top left */
40 #define TC -B_COLS /* top center */
41 #define TR -B_COLS+1 /* top right */
42 #define ML -1 /* middle left */
43 #define MR 1 /* middle right */
44 #define BL B_COLS-1 /* bottom left */
45 #define BC B_COLS /* bottom center */
46 #define BR B_COLS+1 /* bottom right */
48 /* These can be overridden by the user. */
49 #define DEFAULT_KEYS "jkl pq"
57 #define HIGH_SCORE_FILE "/var/games/tetris.scores"
58 #define TEMP_SCORE_FILE "/tmp/tetris-tmp.scores"
60 char *keys
= DEFAULT_KEYS
;
63 int lines_cleared
= 0;
64 int board
[B_SIZE
], shadow
[B_SIZE
];
66 int *peek_shape
; /* peek preview of next shape */
76 18, ML
, MR
, 2, /* sticks out */
88 6, TC
, BC
, 2 * B_COLS
, /* sticks out */
97 int preview
[B_COLS
* 10];
98 int shadow_preview
[B_COLS
* 10];
100 /* Display piece preview. */
101 memset(preview
, 0, sizeof(preview
));
102 preview
[2 * B_COLS
+ 1] = 7;
103 preview
[2 * B_COLS
+ 1 + peek_shape
[1]] = 7;
104 preview
[2 * B_COLS
+ 1 + peek_shape
[2]] = 7;
105 preview
[2 * B_COLS
+ 1 + peek_shape
[3]] = 7;
107 for (y
= 0; y
< 4; y
++) {
108 for (x
= 0; x
< B_COLS
; x
++) {
109 if (preview
[y
* B_COLS
+ x
] - shadow_preview
[y
* B_COLS
+ x
]) {
110 shadow_preview
[y
* B_COLS
+ x
] = preview
[y
* B_COLS
+ x
];
111 gotoxy(x
* 2 + 26 + 28, start
+ y
);
112 printf("\e[%dm ", preview
[y
* B_COLS
+ x
]);
119 for (y
= 1; y
< B_ROWS
- 1; y
++) {
120 for (x
= 0; x
< B_COLS
; x
++) {
121 if (board
[y
* B_COLS
+ x
] - shadow
[y
* B_COLS
+ x
]) {
122 shadow
[y
* B_COLS
+ x
] = board
[y
* B_COLS
+ x
];
123 gotoxy(x
* 2 + 28, y
);
124 printf("\e[%dm ", board
[y
* B_COLS
+ x
]);
129 /* Update points and level */
130 while (lines_cleared
>= 10) {
136 /* Display current level and points */
139 printf("Level : %d", level
);
141 printf("Points : %d", points
);
143 #ifdef ENABLE_PREVIEW
154 int fits_in(int *shape
, int pos
)
156 if (board
[pos
] || board
[pos
+ shape
[1]] || board
[pos
+ shape
[2]] || board
[pos
+ shape
[3]])
162 void place(int *shape
, int pos
, int b
)
165 board
[pos
+ shape
[1]] = b
;
166 board
[pos
+ shape
[2]] = b
;
167 board
[pos
+ shape
[3]] = b
;
170 int *next_shape(void)
172 int *next
= peek_shape
;
174 peek_shape
= &shapes
[rand() % 7 * 4];
181 void show_high_score(void)
183 #ifdef ENABLE_HIGH_SCORE
186 if ((tmpscore
= fopen(HIGH_SCORE_FILE
, "a"))) {
187 char *name
= getenv("LOGNAME");
192 fprintf(tmpscore
, "%7d\t %5d\t %3d\t%s\n", points
* level
, points
, level
, name
);
195 system("cat " HIGH_SCORE_FILE
"| sort -rn | head -10 >" TEMP_SCORE_FILE
196 "&& cp " TEMP_SCORE_FILE
" " HIGH_SCORE_FILE
);
197 remove(TEMP_SCORE_FILE
);
200 // puts("\nHit RETURN to see high scores, ^C to skip.");
201 fprintf(stderr
, " Score\tPoints\tLevel\tName\n");
202 system("cat " HIGH_SCORE_FILE
);
203 #endif /* ENABLE_HIGH_SCORE */
206 void show_online_help(void)
208 const int start
= 11;
211 gotoxy(26 + 28, start
);
213 gotoxy(26 + 28, start
+ 1);
215 gotoxy(26 + 28, start
+ 2);
217 gotoxy(26 + 28, start
+ 3);
218 puts("space - drop");
219 gotoxy(26 + 28, start
+ 4);
221 gotoxy(26 + 28, start
+ 5);
225 /* Code stolen from http://c-faq.com/osdep/cbreak.html */
228 struct termios modmodes
;
230 if (tcgetattr(fileno(stdin
), &savemodes
) < 0)
236 /* "stty cbreak -echo" */
237 modmodes
= savemodes
;
238 modmodes
.c_lflag
&= ~ICANON
;
239 modmodes
.c_lflag
&= ~ECHO
;
240 modmodes
.c_cc
[VMIN
] = 1;
241 modmodes
.c_cc
[VTIME
] = 0;
243 return tcsetattr(fileno(stdin
), TCSANOW
, &modmodes
);
254 return tcsetattr(fileno(stdin
), TCSANOW
, &savemodes
);
257 void freeze(int enable
)
262 sigaddset(&set
, SIGALRM
);
264 sigprocmask(enable
? SIG_BLOCK
: SIG_UNBLOCK
, &set
, NULL
);
267 void alarm_handler(int signo
)
271 /* On init from main() */
275 h
[3] -= h
[3] / (3000 - 10 * level
);
276 setitimer(0, (struct itimerval
*)h
, 0);
279 void exit_handler(int signo
)
290 SIGNAL(SIGINT
, exit_handler
);
291 SIGNAL(SIGTERM
, exit_handler
);
292 SIGNAL(SIGALRM
, alarm_handler
);
294 /* Start update timer. */
298 int main(int argc
, char *argv
[])
300 int c
= 0, i
, j
, *ptr
;
304 /* Initialize board */
306 for (i
= B_SIZE
; i
; i
--)
307 *ptr
++ = i
< 25 || i
% B_COLS
< 2 ? 7 : 0;
309 srand((unsigned int)time(NULL
));
310 if (tty_init() == -1)
319 shape
= next_shape();
322 if (fits_in(shape
, pos
+ B_COLS
)) {
325 place(shape
, pos
, 7);
327 for (j
= 0; j
< 252; j
= B_COLS
* (j
/ B_COLS
+ 1)) {
328 for (; board
[++j
];) {
329 if (j
% B_COLS
== 10) {
332 for (; j
% B_COLS
; board
[j
--] = 0)
336 for (; --j
; board
[j
+ B_COLS
] = board
[j
])
342 shape
= next_shape();
343 if (!fits_in(shape
, pos
= 17))
348 if (c
== keys
[KEY_LEFT
]) {
349 if (!fits_in(shape
, --pos
))
353 if (c
== keys
[KEY_ROTATE
]) {
355 shape
= &shapes
[4 * *shape
]; /* Rotate */
356 /* Check if it fits, if not restore shape from backup */
357 if (!fits_in(shape
, pos
))
361 if (c
== keys
[KEY_RIGHT
]) {
362 if (!fits_in(shape
, ++pos
))
366 if (c
== keys
[KEY_DROP
]) {
367 for (; fits_in(shape
, pos
+ B_COLS
); ++points
)
371 if (c
== keys
[KEY_PAUSE
] || c
== keys
[KEY_QUIT
]) {
374 if (c
== keys
[KEY_QUIT
]) {
379 printf("Your score: %d points x level %d = %d\n\n", points
, level
, points
* level
);
384 for (j
= B_SIZE
; j
--; shadow
[j
] = 0)
387 while (getchar() - keys
[KEY_PAUSE
])
390 // puts("\e[H\e[J\e[7m");
394 place(shape
, pos
, 7);
396 place(shape
, pos
, 0);
399 if (tty_exit() == -1)
407 * indent-tabs-mode: t
408 * c-file-style: "linux"