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, 2010, 2017 Joachim Nilsson <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
153 int fits_in(int *shape
, int pos
)
155 if (board
[pos
] || board
[pos
+ shape
[1]] || board
[pos
+ shape
[2]] || board
[pos
+ shape
[3]])
161 void place(int *shape
, int pos
, int b
)
164 board
[pos
+ shape
[1]] = b
;
165 board
[pos
+ shape
[2]] = b
;
166 board
[pos
+ shape
[3]] = b
;
169 int *next_shape(void)
171 int *next
= peek_shape
;
173 peek_shape
= &shapes
[rand() % 7 * 4];
180 void show_high_score(void)
182 #ifdef ENABLE_HIGH_SCORE
185 if ((tmpscore
= fopen(HIGH_SCORE_FILE
, "a"))) {
186 char *name
= getenv("LOGNAME");
191 fprintf(tmpscore
, "%7d\t %5d\t %3d\t%s\n", points
* level
, points
, level
, name
);
194 system("cat " HIGH_SCORE_FILE
"| sort -rn | head -10 >" TEMP_SCORE_FILE
195 "&& cp " TEMP_SCORE_FILE
" " HIGH_SCORE_FILE
);
196 remove(TEMP_SCORE_FILE
);
199 // puts("\nHit RETURN to see high scores, ^C to skip.");
200 fprintf(stderr
, " Score\tPoints\tLevel\tName\n");
201 system("cat " HIGH_SCORE_FILE
);
202 #endif /* ENABLE_HIGH_SCORE */
205 void show_online_help(void)
207 const int start
= 11;
210 gotoxy(26 + 28, start
);
212 gotoxy(26 + 28, start
+ 1);
214 gotoxy(26 + 28, start
+ 2);
216 gotoxy(26 + 28, start
+ 3);
217 puts("space - drop");
218 gotoxy(26 + 28, start
+ 4);
220 gotoxy(26 + 28, start
+ 5);
224 /* Code stolen from http://c-faq.com/osdep/cbreak.html */
227 struct termios modmodes
;
229 if (tcgetattr(fileno(stdin
), &savemodes
) < 0)
235 /* "stty cbreak -echo" */
236 modmodes
= savemodes
;
237 modmodes
.c_lflag
&= ~ICANON
;
238 modmodes
.c_lflag
&= ~ECHO
;
239 modmodes
.c_cc
[VMIN
] = 1;
240 modmodes
.c_cc
[VTIME
] = 0;
242 return tcsetattr(fileno(stdin
), TCSANOW
, &modmodes
);
253 return tcsetattr(fileno(stdin
), TCSANOW
, &savemodes
);
256 void alarm_handler(int signo
)
260 /* On init from main() */
264 h
[3] -= h
[3] / (3000 - 10 * level
);
265 setitimer(0, (struct itimerval
*)h
, 0);
268 void exit_handler(int signo
)
279 SIGNAL(SIGINT
, exit_handler
);
280 SIGNAL(SIGTERM
, exit_handler
);
281 SIGNAL(SIGALRM
, alarm_handler
);
283 /* Start update timer. */
287 int main(int argc
, char *argv
[])
289 int c
= 0, i
, j
, *ptr
;
294 /* Initialize board */
296 for (i
= B_SIZE
; i
; i
--)
297 *ptr
++ = i
< 25 || i
% B_COLS
< 2 ? 7 : 0;
299 srand((unsigned int)time(NULL
));
300 if (tty_break() == -1)
303 /* Set up signal set with just SIGALRM. */
305 sigaddset(&set
, SIGALRM
);
313 shape
= next_shape();
316 if (fits_in(shape
, pos
+ B_COLS
)) {
319 place(shape
, pos
, 7);
321 for (j
= 0; j
< 252; j
= B_COLS
* (j
/ B_COLS
+ 1)) {
322 for (; board
[++j
];) {
323 if (j
% B_COLS
== 10) {
326 for (; j
% B_COLS
; board
[j
--] = 0)
330 for (; --j
; board
[j
+ B_COLS
] = board
[j
])
336 shape
= next_shape();
337 if (!fits_in(shape
, pos
= 17))
342 if (c
== keys
[KEY_LEFT
]) {
343 if (!fits_in(shape
, --pos
))
347 if (c
== keys
[KEY_ROTATE
]) {
349 shape
= &shapes
[4 * *shape
]; /* Rotate */
350 /* Check if it fits, if not restore shape from backup */
351 if (!fits_in(shape
, pos
))
355 if (c
== keys
[KEY_RIGHT
]) {
356 if (!fits_in(shape
, ++pos
))
360 if (c
== keys
[KEY_DROP
]) {
361 for (; fits_in(shape
, pos
+ B_COLS
); ++points
)
365 if (c
== keys
[KEY_PAUSE
] || c
== keys
[KEY_QUIT
]) {
366 sigprocmask(SIG_BLOCK
, &set
, NULL
);
368 if (c
== keys
[KEY_QUIT
]) {
373 printf("Your score: %d points x level %d = %d\n\n", points
, level
, points
* level
);
378 for (j
= B_SIZE
; j
--; shadow
[j
] = 0)
381 while (getchar() - keys
[KEY_PAUSE
])
384 // puts("\e[H\e[J\e[7m");
385 sigprocmask(SIG_UNBLOCK
, &set
, NULL
);
388 place(shape
, pos
, 7);
390 place(shape
, pos
, 0);
401 * indent-tabs-mode: t
402 * c-file-style: "linux"