/* compile with: * gcc -o tetris tetris.c */ #include #include #include #define ARRAY_SIZE(x) (sizeof(x) / sizeof *(x)) static const struct { short f; short b; } colortab[] = { { COLOR_WHITE, COLOR_BLACK }, /* not used */ { COLOR_RED, COLOR_BLACK }, { COLOR_GREEN, COLOR_BLACK }, { COLOR_YELLOW, COLOR_BLACK }, { COLOR_BLUE, COLOR_BLACK }, { COLOR_MAGENTA, COLOR_BLACK }, { COLOR_CYAN, COLOR_BLACK }, }; #define C_STANDARD COLOR_PAIR(0) #define C_FLOOR COLOR_PAIR(1) #define C_MONEY COLOR_PAIR(3) #define C_PLAYER COLOR_PAIR(2) const chtype blocktype[] = { C_STANDARD | '.', C_FLOOR | '#', C_STANDARD | '#', }; #define PIECE_WIDTH 4 #define PIECE_HEIGHT 4 typedef int piece_bitmap[PIECE_HEIGHT][PIECE_WIDTH]; const piece_bitmap piece_table[] = { { {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0} }, { {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0} }, { {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0} }, }; #define MAX_COLORS ARRAY_SIZE(colortab) #define MAX_BLOCKS ARRAY_SIZE(piece) #define GRAPH_WIDTH 20 #define GRAPH_HEIGHT 16 static int graph[GRAPH_HEIGHT][GRAPH_WIDTH]; static void init_graph() { int x, y; for(y = 0; y < GRAPH_HEIGHT; y++) { for(x = 0; x < GRAPH_WIDTH; x++) { graph[y][x] = 0; } } } static int bounds_check(int px, int py, piece_bitmap piece) { int wx, wy; for(wy = 0; wy < PIECE_HEIGHT; wy++) { for(wx = 0; wx < PIECE_WIDTH; wx++) { if(piece[wy][wx]) { if(px + wx < 0 || py + wy < 0 || px + wx >= GRAPH_WIDTH || py + wy >= GRAPH_HEIGHT) { return 0; /* out of bounds */ } if(graph[py+wy][px+wx]) { return -1; /* actual intersect */ } } } } return 1; /* okay */ } static void draw_cell(int x, int y, chtype ch) { mvwaddch(stdscr, y, x, ch); /* double width */ /* mvwaddch(stdscr,y,x*2,ch); mvwaddch(stdscr,y,x*2+1,ch); */ } static void clear_piece(int x, int y, piece_bitmap piece) { int wy, wx; /* redraw a section of the original graph */ for(wy = 0; wy < PIECE_HEIGHT; wy++) { for(wx = 0; wx < PIECE_WIDTH; wx++) { if(piece[wy][wx]) { draw_cell(x + wx, y + wy, blocktype[graph[y+wy][x+wx]]); } } } } static void draw_piece(int x, int y, piece_bitmap piece) { int wy, wx; /* draw the new piece */ for(wy = 0; wy < PIECE_HEIGHT; wy++) { for(wx = 0; wx < PIECE_WIDTH; wx++) { int m; m = piece[wy][wx]; if(m) { draw_cell(x + wx, y + wy, blocktype[m]); } } } } static void do_piece(int *x, int *y, int newx, int newy, piece_bitmap piece) { if(bounds_check(newx, newy, piece) != 1) return; if(*x != newx || *y != newy) { clear_piece(*x, *y, piece); } draw_piece(newx, newy, piece); *x = newx; *y = newy; wrefresh(stdscr); } /* centers or truncates a string to width */ static void center(int y, int x, unsigned width, const char *str) { size_t len = strlen(str); int ofs; if (len > width) ofs = 0; else ofs = (width - len) / 2; mvprintw(y, x + ofs, "%.*s", width, str); } static void go_draw() { int wx, wy; for(wy = 0; wy < GRAPH_HEIGHT; wy++) { wmove(stdscr, wy, 0); for(wx = 0; wx < GRAPH_WIDTH; wx++) { waddch(stdscr, blocktype[graph[wy][wx]]); } } center(wy, 0, GRAPH_WIDTH, "** X to Exit **"); wrefresh(stdscr); } static void piece_load(piece_bitmap piece, int ptype, int orient) { int sx, sy; for(sy = 0; sy < PIECE_HEIGHT; sy++) { for(sx = 0; sx < PIECE_WIDTH; sx++) { switch(orient) { default: case 0: piece[sy][sx] = piece_table[ptype][sy][sx]; break; case 1: piece[sx][PIECE_WIDTH - 1 - sy] = piece_table[ptype][sy][sx]; break; case 2: piece[PIECE_HEIGHT - 1 - sy][PIECE_WIDTH - 1 - sx] = piece_table[ptype][sy][sx]; break; case 3: piece[PIECE_HEIGHT - 1 - sx][sy] = piece_table[ptype][sy][sx]; break; } } } } int rotate_piece(int px, int py, piece_bitmap piece, int ptype, int *orient, int direction) { int neworient; piece_bitmap newpiece; if(direction) { neworient = (*orient + 1) % 4; } else { neworient = ((unsigned) * orient - 1) % 4; } piece_load(newpiece, ptype, neworient); if(bounds_check(px, py, newpiece) != 1) { return 0; /* failed to rotate */ } clear_piece(px, py, piece); /* clear the original */ draw_piece(px, py, newpiece); /* draw the new */ *orient = neworient; memcpy(piece, newpiece, sizeof * piece * PIECE_HEIGHT); wrefresh(stdscr); return 1; /* success */ } static void go() { int ch; int px, py; int ptype; int orient; piece_bitmap piece; init_graph(); go_draw(); /* full refresh */ px = 1; py = 1; ptype = 1; orient = 0; piece_load(piece, ptype, orient); do_piece(&px, &py, px, py, piece); /* */ do { ch = getch(); switch(ch) { case ',': rotate_piece(px, py, piece, ptype, &orient, 0); break; case '.': rotate_piece(px, py, piece, ptype, &orient, 1); break; case 'i': case KEY_UP: do_piece(&px, &py, px, py - 1, piece); break; case 'k': case KEY_DOWN: do_piece(&px, &py, px, py + 1, piece); break; case 'j': case KEY_LEFT: do_piece(&px, &py, px - 1, py, piece); break; case 'l': case KEY_RIGHT: do_piece(&px, &py, px + 1, py, piece); break; } if(ch == 'x') break; } while(1); } static void go_colors() { unsigned i; start_color(); /* skip over the first cellry */ for(i = 1; i < ARRAY_SIZE(colortab); i++) { init_pair(i, colortab[i].f, colortab[i].b); } } static void go_shutdown() { endwin(); } static void go_screen() { if(!initscr()) { exit(EXIT_FAILURE); } atexit(go_shutdown); cbreak(); noecho(); keypad(stdscr, TRUE); /* nodelay(stdscr,TRUE); */ halfdelay(1); leaveok(stdscr, TRUE); /* */ curs_set(0); /* try to make the cursor invisible */ go_colors(); attrset(COLOR_PAIR(0)); } int main() { go_screen(); go(); return EXIT_SUCCESS; }