Remove __attribute__ ((unused)) clutter
[tetris.git] / tetris.c
blob63e13de674b4158a30b7a54639bd43ea7d988f61
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
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/time.h>
28 #include <sys/wait.h>
29 #include <termios.h>
30 #include <time.h>
31 #include <unistd.h>
33 #include "conio.h"
34 #include "tetris.h"
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"
50 #define KEY_LEFT 0
51 #define KEY_RIGHT 2
52 #define KEY_ROTATE 1
53 #define KEY_DROP 3
54 #define KEY_PAUSE 4
55 #define KEY_QUIT 5
57 #define HIGH_SCORE_FILE "/var/games/tetris.scores"
58 #define TEMP_SCORE_FILE "/tmp/tetris-tmp.scores"
60 char *keys = DEFAULT_KEYS;
61 int level = 1;
62 int points = 0;
63 int lines_cleared = 0;
64 int board[B_SIZE], shadow[B_SIZE];
66 int *peek_shape; /* peek preview of next shape */
67 int *shape;
69 int shapes[] = {
70 7, TL, TC, MR,
71 8, TR, TC, ML,
72 9, ML, MR, BC,
73 3, TL, TC, ML,
74 12, ML, BL, MR,
75 15, ML, BR, MR,
76 18, ML, MR, 2, /* sticks out */
77 0, TC, ML, BL,
78 1, TC, MR, BR,
79 10, TC, MR, BC,
80 11, TC, ML, MR,
81 2, TC, ML, BC,
82 13, TC, BC, BR,
83 14, TR, ML, MR,
84 4, TL, TC, BC,
85 16, TR, TC, BC,
86 17, TL, MR, ML,
87 5, TC, BC, BL,
88 6, TC, BC, 2 * B_COLS, /* sticks out */
91 int update(void)
93 int x, y;
95 #ifdef ENABLE_PREVIEW
96 const int start = 5;
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]);
116 #endif
118 /* Display board. */
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) {
131 lines_cleared -= 10;
132 level++;
135 #ifdef ENABLE_SCORE
136 /* Display current level and points */
137 textattr(RESETATTR);
138 gotoxy(26 + 28, 2);
139 printf("Level : %d", level);
140 gotoxy(26 + 28, 3);
141 printf("Points : %d", points);
142 #endif
143 #ifdef ENABLE_PREVIEW
144 gotoxy(26 + 28, 5);
145 printf("Preview:");
146 #endif
147 gotoxy(26 + 28, 10);
148 printf("Keys:");
150 return getchar();
153 int fits_in(int *shape, int pos)
155 if (board[pos] || board[pos + shape[1]] || board[pos + shape[2]] || board[pos + shape[3]])
156 return 0;
158 return 1;
161 void place(int *shape, int pos, int b)
163 board[pos] = 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];
174 if (!next)
175 return next_shape();
177 return next;
180 void show_high_score(void)
182 #ifdef ENABLE_HIGH_SCORE
183 FILE *tmpscore;
185 if ((tmpscore = fopen(HIGH_SCORE_FILE, "a"))) {
186 char *name = getenv("LOGNAME");
188 if (!name)
189 name = "anonymous";
191 fprintf(tmpscore, "%7d\t %5d\t %3d\t%s\n", points * level, points, level, name);
192 fclose(tmpscore);
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;
209 textattr(RESETATTR);
210 gotoxy(26 + 28, start);
211 puts("j - left");
212 gotoxy(26 + 28, start + 1);
213 puts("k - rotate");
214 gotoxy(26 + 28, start + 2);
215 puts("l - right");
216 gotoxy(26 + 28, start + 3);
217 puts("space - drop");
218 gotoxy(26 + 28, start + 4);
219 puts("p - pause");
220 gotoxy(26 + 28, start + 5);
221 puts("q - quit");
224 /* Code stolen from http://c-faq.com/osdep/cbreak.html */
225 int tty_break(void)
227 struct termios modmodes;
229 if (tcgetattr(fileno(stdin), &savemodes) < 0)
230 return -1;
232 havemodes = 1;
233 hidecursor();
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);
245 int tty_fix(void)
247 if (!havemodes)
248 return 0;
250 showcursor();
252 /* "stty sane" */
253 return tcsetattr(fileno(stdin), TCSANOW, &savemodes);
256 void alarm_handler(int signo)
258 static long h[4];
260 /* On init from main() */
261 if (!signo)
262 h[3] = 500000;
264 h[3] -= h[3] / (3000 - 10 * level);
265 setitimer(0, (struct itimerval *)h, 0);
268 void exit_handler(int signo)
270 clrscr();
271 tty_fix();
272 exit(0);
275 int sig_init(void)
277 struct sigaction sa;
279 SIGNAL(SIGINT, exit_handler);
280 SIGNAL(SIGTERM, exit_handler);
281 SIGNAL(SIGALRM, alarm_handler);
283 /* Start update timer. */
284 alarm_handler(0);
287 int main(int argc, char *argv[])
289 int c = 0, i, j, *ptr;
290 int pos = 17;
291 int *backup;
292 sigset_t set;
294 /* Initialize board */
295 ptr = 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)
301 return 1;
303 /* Set up signal set with just SIGALRM. */
304 sigemptyset(&set);
305 sigaddset(&set, SIGALRM);
307 /* Set up signals */
308 sig_init();
310 clrscr();
311 show_online_help();
313 shape = next_shape();
314 while (1) {
315 if (c < 0) {
316 if (fits_in(shape, pos + B_COLS)) {
317 pos += B_COLS;
318 } else {
319 place(shape, pos, 7);
320 ++points;
321 for (j = 0; j < 252; j = B_COLS * (j / B_COLS + 1)) {
322 for (; board[++j];) {
323 if (j % B_COLS == 10) {
324 lines_cleared++;
326 for (; j % B_COLS; board[j--] = 0)
328 c = update();
330 for (; --j; board[j + B_COLS] = board[j])
332 c = update();
336 shape = next_shape();
337 if (!fits_in(shape, pos = 17))
338 c = keys[KEY_QUIT];
342 if (c == keys[KEY_LEFT]) {
343 if (!fits_in(shape, --pos))
344 ++pos;
347 if (c == keys[KEY_ROTATE]) {
348 backup = shape;
349 shape = &shapes[4 * *shape]; /* Rotate */
350 /* Check if it fits, if not restore shape from backup */
351 if (!fits_in(shape, pos))
352 shape = backup;
355 if (c == keys[KEY_RIGHT]) {
356 if (!fits_in(shape, ++pos))
357 --pos;
360 if (c == keys[KEY_DROP]) {
361 for (; fits_in(shape, pos + B_COLS); ++points)
362 pos += B_COLS;
365 if (c == keys[KEY_PAUSE] || c == keys[KEY_QUIT]) {
366 sigprocmask(SIG_BLOCK, &set, NULL);
368 if (c == keys[KEY_QUIT]) {
369 clrscr();
370 gotoxy(0, 0);
371 textattr(RESETATTR);
373 printf("Your score: %d points x level %d = %d\n\n", points, level, points * level);
374 show_high_score();
375 break;
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);
389 c = update();
390 place(shape, pos, 0);
393 if (tty_fix() == -1)
394 return 1;
396 return 0;
400 * Local Variables:
401 * indent-tabs-mode: t
402 * c-file-style: "linux"
403 * End: