27 enum { ERASE_PIECE, DRAW_PIECE };
29 enum { BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, YELLOW, WHITE };
31 /* dimensions of the whole screen */
35 /* dimensions of the playfield */
38 /* offset of the playfield from the left side of the screen */
42 #define CHAR(c, fg, bg) \
43 ((uint16_t)(c) | ((uint16_t)(fg) << 12) | ((uint16_t)(bg) << 8))
45 int scr[SCR_COLS * SCR_ROWS];
47 static int collision(int piece, const int *pos);
48 static void stick(int piece, const int *pos);
49 static void erase_completed(void);
50 static void draw_piece(int piece, const int *pos, int rot, int mode);
51 static void drawbg(void);
52 static void drawpf(void);
53 static void draw_line(int row, int blink);
54 static void wrtile(int tileid);
57 static int pos[2], next_pos[2];
58 static int cur_piece = -1;
59 static int cur_rot, prev_rot;
60 static int complines[4] = {-1, -1, -1, -1};
61 static int num_complines;
75 #define FIRST_PIECE_TILE TILE_IPIECE
77 static uint16_t tiles[][2] = {
78 { CHAR(' ', BLACK, BLACK), CHAR(' ', BLACK, BLACK) }, /* black tile */
79 { CHAR(' ', WHITE, WHITE), CHAR(' ', WHITE, WHITE) }, /* playfield background */
80 { CHAR(G_CHECKER, WHITE, BLACK), CHAR(G_CHECKER, WHITE, BLACK) }, /* well separator */
81 { CHAR(' ', CYAN, CYAN), CHAR(' ', CYAN, CYAN) }, /* straight */
82 { CHAR(' ', BLUE, BLUE), CHAR(' ', BLUE, BLUE) }, /* box */
83 { CHAR(' ', GREEN, GREEN), CHAR(' ', GREEN, GREEN) }, /* J */
84 { CHAR(' ', YELLOW, YELLOW), CHAR(' ', YELLOW, YELLOW) }, /* L */
85 { CHAR(' ', MAGENTA, MAGENTA), CHAR(' ', MAGENTA, MAGENTA) }, /* S */
86 { CHAR(' ', RED, BLACK), CHAR(' ', RED, BLACK) }, /* T */
87 { CHAR(' ', RED, RED), CHAR(' ', RED, RED) }, /* Z */
103 /* fill the screen buffer, and draw */
104 for(i=0; i<SCR_ROWS; i++) {
105 for(j=0; j<SCR_COLS; j++) {
106 if(i > PF_ROWS || j < PF_XOFFS - 1 || j > PF_XOFFS + PF_COLS) {
108 } else if((i == PF_ROWS && j >= PF_XOFFS && j < PF_XOFFS + PF_COLS) ||
109 j == PF_XOFFS - 1 || j == PF_XOFFS + PF_COLS) {
124 void cleanup_game(void)
129 #define BLINK_UPD_RATE 100
131 long update(long msec)
133 static long prev_tick;
136 dt = msec - prev_tick;
139 /* lines where completed, we're in blinking mode */
140 int i, blink = dt >> 8;
148 for(i=0; i<num_complines; i++) {
149 draw_line(complines[i], blink & 1);
152 return BLINK_UPD_RATE;
157 while(dt >= tick_interval) {
159 next_pos[0] = pos[0] + 1;
160 if(collision(cur_piece, next_pos)) {
161 next_pos[0] = pos[0];
162 stick(cur_piece, next_pos);
168 cur_piece = rand() % NUM_PIECES;
169 prev_rot = cur_rot = 0;
170 pos[0] = piece_spawnpos[cur_piece][0];
171 next_pos[0] = pos[0] + 1;
172 pos[1] = next_pos[1] = PF_COLS / 2 + piece_spawnpos[cur_piece][1];
179 if(cur_piece >= 0 && (memcmp(pos, next_pos, sizeof pos) != 0 || cur_rot != prev_rot)) {
180 draw_piece(cur_piece, pos, prev_rot, ERASE_PIECE);
181 draw_piece(cur_piece, next_pos, cur_rot, DRAW_PIECE);
182 memcpy(pos, next_pos, sizeof pos);
186 return tick_interval - dt;
193 static void runesc(int csi, char *buf)
195 if(csi != C0) return;
200 game_input('w'); /* up */
203 game_input('s'); /* down */
206 game_input('d'); /* right */
209 game_input('a'); /* left */
217 void game_input(int c)
221 static char escbuf[64];
243 if(c < 0x20 || c >= 0x80) {
248 escbuf[esctop++] = c;
255 runesc(prevcsi, escbuf);
275 next_pos[1] = pos[1] - 1;
276 if(collision(cur_piece, next_pos)) {
277 next_pos[1] = pos[1];
282 next_pos[1] = pos[1] + 1;
283 if(collision(cur_piece, next_pos)) {
284 next_pos[1] = pos[1];
291 cur_rot = (cur_rot + 1) & 3;
292 if(collision(cur_piece, next_pos)) {
298 next_pos[0] = pos[0] + 1;
299 if(collision(cur_piece, next_pos)) {
300 next_pos[0] = pos[0];
305 fprintf(stderr, "unhandled input: %x\n", c);
310 static int collision(int piece, const int *pos)
313 unsigned char *p = pieces[piece][cur_rot];
316 int x = PF_XOFFS + pos[1] + BLKX(*p);
317 int y = PF_YOFFS + pos[0] + BLKY(*p);
322 if(scr[y * SCR_COLS + x] != TILE_PF) return 1;
328 static void stick(int piece, const int *pos)
332 unsigned char *p = pieces[piece][cur_rot];
335 int x = pos[1] + BLKX(*p);
336 int y = pos[0] + BLKY(*p);
339 pfline = scr + (y + PF_YOFFS) * SCR_COLS + PF_XOFFS;
340 pfline[x] = piece + FIRST_PIECE_TILE;
343 for(j=0; j<PF_COLS; j++) {
344 if(pfline[j] == TILE_PF) {
350 complines[num_complines++] = y;
360 static void erase_completed(void)
362 int i, j, srow, drow;
363 int *pfstart = scr + PF_YOFFS * SCR_COLS + PF_XOFFS;
365 /* sort completed lines from highest to lowest row number */
366 for(i=0; i<num_complines-1; i++) {
367 for(j=i+1; j<num_complines; j++) {
368 if(complines[j] > complines[i]) {
369 int tmp = complines[j];
370 complines[j] = complines[i];
376 srow = drow = PF_ROWS - 1;
378 for(i=0; i<PF_ROWS; i++) {
379 for(j=0; j<num_complines; j++) {
380 if(complines[j] == srow) {
388 int *sptr = pfstart + srow * SCR_COLS;
389 int *dptr = pfstart + drow * SCR_COLS;
390 memcpy(dptr, sptr, PF_COLS * sizeof *dptr);
401 static void draw_piece(int piece, const int *pos, int rot, int mode)
404 int tile = mode == ERASE_PIECE ? TILE_PF : FIRST_PIECE_TILE + piece;
405 unsigned char *p = pieces[piece][rot];
408 int x = PF_XOFFS + pos[1] + BLKX(*p);
409 int y = PF_YOFFS + pos[0] + BLKY(*p);
414 ansi_setcursor(y, x * 2);
420 static void drawbg(void)
425 for(i=0; i<SCR_ROWS; i++) {
426 ansi_setcursor(i, 0);
427 for(j=0; j<SCR_COLS; j++) {
433 static void drawpf(void)
436 int *sptr = scr + PF_YOFFS * SCR_COLS + PF_XOFFS;
438 for(i=0; i<PF_ROWS; i++) {
439 ansi_setcursor(i, PF_XOFFS * 2);
440 for(j=0; j<PF_COLS; j++) {
447 static void draw_line(int row, int blink)
451 ansi_setcursor(row, PF_XOFFS * 2);
454 int *sptr = scr + (row + PF_YOFFS) * SCR_COLS + PF_XOFFS;
456 for(i=0; i<PF_COLS; i++) {
460 for(i=0; i<PF_COLS; i++) {
466 static void wrtile(int tileid)
471 uint16_t c = tiles[tileid][i];
472 unsigned char cc = c & 0xff;
473 unsigned char ca = c >> 8;
475 ansi_ibmchar(cc, ca);