.github: set relaseId from prior build step
[tetris.git] / tetris.c
blob375eb9c39fcd5b8e582368dc577123cbf9759652
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
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:");
149 fflush(stdout);
151 return getchar();
154 int fits_in(int *shape, int pos)
156 if (board[pos] || board[pos + shape[1]] || board[pos + shape[2]] || board[pos + shape[3]])
157 return 0;
159 return 1;
162 void place(int *shape, int pos, int b)
164 board[pos] = 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];
175 if (!next)
176 return next_shape();
178 return next;
181 void show_high_score(void)
183 #ifdef ENABLE_HIGH_SCORE
184 FILE *tmpscore;
186 if ((tmpscore = fopen(HIGH_SCORE_FILE, "a"))) {
187 char *name = getenv("LOGNAME");
189 if (!name)
190 name = "anonymous";
192 fprintf(tmpscore, "%7d\t %5d\t %3d\t%s\n", points * level, points, level, name);
193 fclose(tmpscore);
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;
210 textattr(RESETATTR);
211 gotoxy(26 + 28, start);
212 puts("j - left");
213 gotoxy(26 + 28, start + 1);
214 puts("k - rotate");
215 gotoxy(26 + 28, start + 2);
216 puts("l - right");
217 gotoxy(26 + 28, start + 3);
218 puts("space - drop");
219 gotoxy(26 + 28, start + 4);
220 puts("p - pause");
221 gotoxy(26 + 28, start + 5);
222 puts("q - quit");
225 /* Code stolen from http://c-faq.com/osdep/cbreak.html */
226 int tty_init(void)
228 struct termios modmodes;
230 if (tcgetattr(fileno(stdin), &savemodes) < 0)
231 return -1;
233 havemodes = 1;
234 hidecursor();
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);
246 int tty_exit(void)
248 if (!havemodes)
249 return 0;
251 showcursor();
253 /* "stty sane" */
254 return tcsetattr(fileno(stdin), TCSANOW, &savemodes);
257 void freeze(int enable)
259 sigset_t set;
261 sigemptyset(&set);
262 sigaddset(&set, SIGALRM);
264 sigprocmask(enable ? SIG_BLOCK : SIG_UNBLOCK, &set, NULL);
267 void alarm_handler(int signo)
269 static long h[4];
271 /* On init from main() */
272 if (!signo)
273 h[3] = 500000;
275 h[3] -= h[3] / (3000 - 10 * level);
276 setitimer(0, (struct itimerval *)h, 0);
279 void exit_handler(int signo)
281 clrscr();
282 tty_exit();
283 exit(0);
286 int sig_init(void)
288 struct sigaction sa;
290 SIGNAL(SIGINT, exit_handler);
291 SIGNAL(SIGTERM, exit_handler);
292 SIGNAL(SIGALRM, alarm_handler);
294 /* Start update timer. */
295 alarm_handler(0);
298 int main(int argc, char *argv[])
300 int c = 0, i, j, *ptr;
301 int pos = 17;
302 int *backup;
304 /* Initialize board */
305 ptr = 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)
311 return 1;
313 /* Set up signals */
314 sig_init();
316 clrscr();
317 show_online_help();
319 shape = next_shape();
320 while (1) {
321 if (c < 0) {
322 if (fits_in(shape, pos + B_COLS)) {
323 pos += B_COLS;
324 } else {
325 place(shape, pos, 7);
326 ++points;
327 for (j = 0; j < 252; j = B_COLS * (j / B_COLS + 1)) {
328 for (; board[++j];) {
329 if (j % B_COLS == 10) {
330 lines_cleared++;
332 for (; j % B_COLS; board[j--] = 0)
334 c = update();
336 for (; --j; board[j + B_COLS] = board[j])
338 c = update();
342 shape = next_shape();
343 if (!fits_in(shape, pos = 17))
344 c = keys[KEY_QUIT];
348 if (c == keys[KEY_LEFT]) {
349 if (!fits_in(shape, --pos))
350 ++pos;
353 if (c == keys[KEY_ROTATE]) {
354 backup = shape;
355 shape = &shapes[4 * *shape]; /* Rotate */
356 /* Check if it fits, if not restore shape from backup */
357 if (!fits_in(shape, pos))
358 shape = backup;
361 if (c == keys[KEY_RIGHT]) {
362 if (!fits_in(shape, ++pos))
363 --pos;
366 if (c == keys[KEY_DROP]) {
367 for (; fits_in(shape, pos + B_COLS); ++points)
368 pos += B_COLS;
371 if (c == keys[KEY_PAUSE] || c == keys[KEY_QUIT]) {
372 freeze(1);
374 if (c == keys[KEY_QUIT]) {
375 clrscr();
376 gotoxy(0, 0);
377 textattr(RESETATTR);
379 printf("Your score: %d points x level %d = %d\n\n", points, level, points * level);
380 show_high_score();
381 break;
384 for (j = B_SIZE; j--; shadow[j] = 0)
387 while (getchar() - keys[KEY_PAUSE])
390 // puts("\e[H\e[J\e[7m");
391 freeze(0);
394 place(shape, pos, 7);
395 c = update();
396 place(shape, pos, 0);
399 if (tty_exit() == -1)
400 return 1;
402 return 0;
406 * Local Variables:
407 * indent-tabs-mode: t
408 * c-file-style: "linux"
409 * End: