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 struct termios savemodes
;
73 static int havemodes
= 0;
75 static char *keys
= DEFAULT_KEYS
;
77 static int points
= 0;
78 static int lines_cleared
= 0;
79 static int board
[B_SIZE
], shadow
[B_SIZE
];
81 static int *peek_shape
; /* peek preview of next shape */
86 static int shapes
[] = {
87 7, TL
, TC
, MR
, 2, /* ""__ */
88 8, TR
, TC
, ML
, 3, /* __"" */
89 9, ML
, MR
, BC
, 1, /* "|" */
90 3, TL
, TC
, ML
, 4, /* square */
91 12, ML
, BL
, MR
, 5, /* |""" */
92 15, ML
, BR
, MR
, 6, /* """| */
93 18, ML
, MR
, 2, 7, /* ---- sticks out */
94 0, TC
, ML
, BL
, 2, /* / */
95 1, TC
, MR
, BR
, 3, /* \ */
96 10, TC
, MR
, BC
, 1, /* |- */
97 11, TC
, ML
, MR
, 1, /* _|_ */
98 2, TC
, ML
, BC
, 1, /* -| */
99 13, TC
, BC
, BR
, 5, /* |_ */
100 14, TR
, ML
, MR
, 5, /* ___| */
101 4, TL
, TC
, BC
, 5, /* "| */
102 16, TR
, TC
, BC
, 6, /* |" */
103 17, TL
, MR
, ML
, 6, /* |___ */
104 5, TC
, BC
, BL
, 6, /* _| */
105 6, TC
, BC
, 2 * B_COLS
, 7, /* | sticks out */
108 static void draw(int x
, int y
, int color
)
114 static int update(void)
118 #ifdef ENABLE_PREVIEW
119 static int shadow_preview
[B_COLS
* 10] = { 0 };
120 int preview
[B_COLS
* 10] = { 0 };
123 preview
[2 * B_COLS
+ 1] = pcolor
;
124 preview
[2 * B_COLS
+ 1 + peek_shape
[1]] = pcolor
;
125 preview
[2 * B_COLS
+ 1 + peek_shape
[2]] = pcolor
;
126 preview
[2 * B_COLS
+ 1 + peek_shape
[3]] = pcolor
;
128 for (y
= 0; y
< 4; y
++) {
129 for (x
= 0; x
< B_COLS
; x
++) {
130 if (preview
[y
* B_COLS
+ x
] - shadow_preview
[y
* B_COLS
+ x
]) {
131 int color
= preview
[y
* B_COLS
+ x
];
133 shadow_preview
[y
* B_COLS
+ x
] = color
;
134 draw(x
* 2 + 26 + 28, start
+ y
, color
);
141 for (y
= 1; y
< B_ROWS
- 1; y
++) {
142 for (x
= 0; x
< B_COLS
; x
++) {
143 if (board
[y
* B_COLS
+ x
] - shadow
[y
* B_COLS
+ x
]) {
144 int color
= board
[y
* B_COLS
+ x
];
146 shadow
[y
* B_COLS
+ x
] = color
;
147 draw(x
* 2 + 28, y
, color
);
152 /* Update points and level */
153 while (lines_cleared
>= 10) {
159 /* Display current level and points */
161 printf("\033[0mLevel : %d", level
);
163 printf("Points : %d", points
);
165 #ifdef ENABLE_PREVIEW
176 int fits_in(int *shape
, int pos
)
178 if (board
[pos
] || board
[pos
+ shape
[1]] || board
[pos
+ shape
[2]] || board
[pos
+ shape
[3]])
184 static void place(int *shape
, int pos
, int color
)
187 board
[pos
+ shape
[1]] = color
;
188 board
[pos
+ shape
[2]] = color
;
189 board
[pos
+ shape
[3]] = color
;
192 static int *next_shape(void)
194 int pos
= rand() % 7 * 5;
195 int *next
= peek_shape
;
197 peek_shape
= &shapes
[pos
];
198 pcolor
= peek_shape
[4];
206 static void show_high_score(void)
208 #ifdef ENABLE_HIGH_SCORE
211 if ((tmpscore
= fopen(HIGH_SCORE_FILE
, "a"))) {
212 char *name
= getenv("LOGNAME");
217 fprintf(tmpscore
, "%7d\t %5d\t %3d\t%s\n", points
* level
, points
, level
, name
);
220 system("cat " HIGH_SCORE_FILE
"| sort -rn | head -10 >" TEMP_SCORE_FILE
221 "&& cp " TEMP_SCORE_FILE
" " HIGH_SCORE_FILE
);
222 remove(TEMP_SCORE_FILE
);
225 if (!access(HIGH_SCORE_FILE
, R_OK
)) {
226 // puts("\nHit RETURN to see high scores, ^C to skip.");
227 fprintf(stderr
, " Score\tPoints\tLevel\tName\n");
228 system("cat " HIGH_SCORE_FILE
);
230 #endif /* ENABLE_HIGH_SCORE */
233 static void show_online_help(void)
235 const int start
= 11;
237 gotoxy(26 + 28, start
);
238 puts("\033[0mj - left");
239 gotoxy(26 + 28, start
+ 1);
241 gotoxy(26 + 28, start
+ 2);
243 gotoxy(26 + 28, start
+ 3);
244 puts("space - drop");
245 gotoxy(26 + 28, start
+ 4);
247 gotoxy(26 + 28, start
+ 5);
251 /* Code stolen from http://c-faq.com/osdep/cbreak.html */
252 static int tty_init(void)
254 struct termios modmodes
;
256 if (tcgetattr(fileno(stdin
), &savemodes
) < 0)
262 /* "stty cbreak -echo" */
263 modmodes
= savemodes
;
264 modmodes
.c_lflag
&= ~ICANON
;
265 modmodes
.c_lflag
&= ~ECHO
;
266 modmodes
.c_cc
[VMIN
] = 1;
267 modmodes
.c_cc
[VTIME
] = 0;
269 return tcsetattr(fileno(stdin
), TCSANOW
, &modmodes
);
272 static int tty_exit(void)
280 return tcsetattr(fileno(stdin
), TCSANOW
, &savemodes
);
283 static void freeze(int enable
)
288 sigaddset(&set
, SIGALRM
);
290 sigprocmask(enable
? SIG_BLOCK
: SIG_UNBLOCK
, &set
, NULL
);
293 static void alarm_handler(int signo
)
299 /* On init from main() */
303 h
[3] -= h
[3] / (3000 - 10 * level
);
304 setitimer(0, (struct itimerval
*)h
, 0);
307 static void exit_handler(int signo
)
316 static int sig_init(void)
320 SIGNAL(SIGINT
, exit_handler
);
321 SIGNAL(SIGTERM
, exit_handler
);
322 SIGNAL(SIGALRM
, alarm_handler
);
324 /* Start update timer. */
330 int c
= 0, i
, j
, *ptr
;
334 /* Initialize board, grey border, used to be white(7) */
336 for (i
= B_SIZE
; i
; i
--)
337 *ptr
++ = i
< 25 || i
% B_COLS
< 2 ? 60 : 0;
339 srand((unsigned int)time(NULL
));
340 if (tty_init() == -1)
349 shape
= next_shape();
352 if (fits_in(shape
, pos
+ B_COLS
)) {
355 place(shape
, pos
, color
);
357 for (j
= 0; j
< 252; j
= B_COLS
* (j
/ B_COLS
+ 1)) {
358 for (; board
[++j
];) {
359 if (j
% B_COLS
== 10) {
362 for (; j
% B_COLS
; board
[j
--] = 0)
366 for (; --j
; board
[j
+ B_COLS
] = board
[j
])
372 shape
= next_shape();
373 if (!fits_in(shape
, pos
= 17))
378 if (c
== keys
[KEY_LEFT
]) {
379 if (!fits_in(shape
, --pos
))
383 if (c
== keys
[KEY_ROTATE
]) {
385 shape
= &shapes
[5 * *shape
]; /* Rotate */
386 /* Check if it fits, if not restore shape from backup */
387 if (!fits_in(shape
, pos
))
391 if (c
== keys
[KEY_RIGHT
]) {
392 if (!fits_in(shape
, ++pos
))
396 if (c
== keys
[KEY_DROP
]) {
397 for (; fits_in(shape
, pos
+ B_COLS
); ++points
)
401 if (c
== keys
[KEY_PAUSE
] || c
== keys
[KEY_QUIT
]) {
404 if (c
== keys
[KEY_QUIT
]) {
408 printf("\033[0mYour score: %d points x level %d = %d\n\n", points
, level
, points
* level
);
413 for (j
= B_SIZE
; j
--; shadow
[j
] = 0)
416 while (getchar() - keys
[KEY_PAUSE
])
419 // puts("\033[H\033[J\033[7m");
423 place(shape
, pos
, color
);
425 place(shape
, pos
, 0);
428 if (tty_exit() == -1)
436 * indent-tabs-mode: t
437 * c-file-style: "linux"