curses版テトリス
以前書いた「curses版テトリス」がまあ形になったので公開する。コンソール環境上でC言語環境とcursesライブラリがインストールされていれば動かせる。
操作方法は「左右」でブロック移動。「上」でブロック回転(時計回り固定)。「下」でブロック落下。「Q」キーで終了。
#include <curses.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #define FIELD_W (12) #define FIELD_H (20) #define BLOCK_N (4) #define NONE (0) #define VWALL (1) #define HWALL (2) #define BLOCK (3) #define V (VWALL) #define H (HWALL) static int field[FIELD_H][FIELD_W] = { { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { V,0,0,0,0,0,0,0,0,0,0,V }, { H,H,H,H,H,H,H,H,H,H,H,H } }; static int block_base[][BLOCK_N][2] = { { { +0, +0 }, { -1, +0 }, { +0, +1 }, { +0, +2 } }, { { +0, +0 }, { +1, +0 }, { +0, +1 }, { +0, +2 } }, { { +0, +0 }, { +1, +0 }, { +0, +1 }, { -1, +1 } }, { { +0, +0 }, { +1, +0 }, { +0, -1 }, { -1, -1 } }, { { +0, +0 }, { +0, +1 }, { +0, -1 }, { +0, -2 } }, { { +0, +0 }, { +0, +1 }, { -1, +0 }, { -1, +1 } }, { { +0, +0 }, { +0, +1 }, { +0, -1 }, { -1, +0 } }, }; static int block_tbl[BLOCK_N][2]; static int key; static int lines; static int score; static int px; static int py; static int fall_time; static int fall_wait; static int block_num; static void initApp(void) { srand(time(NULL)); initscr(); noecho(); cbreak(); timeout(16); keypad(stdscr, TRUE); curs_set(0); lines = 0; score = 0; block_num = -1; } static void exitApp(void) { curs_set(1); endwin(); } static void initField(void) { int i, j; for(i = 0; i < (FIELD_H - 1); i++){ for(j = 1; j < (FIELD_W - 1); j++){ field[i][j] = NONE; } } } static void drawField(void) { int i, j; for(i = 0; i < FIELD_H; i++){ for(j = 0; j < FIELD_W; j++){ if(field[i][j] == VWALL){ mvaddch(i, j, '|'); } if(field[i][j] == HWALL){ mvaddch(i, j, '-'); } } } } static void initBlock(void) { int i, n; do{ n = rand() % 7; if(block_num == n){ continue; } block_num = n; break; }while(TRUE); for(i = 0; i < BLOCK_N; i++){ block_tbl[i][0] = block_base[block_num][i][0]; block_tbl[i][1] = block_base[block_num][i][1]; } px = 5; py = 1; fall_time = 30; fall_wait = 0; } static void eraseLine(int y) { int i, j; for(i = y; i >= 0; i--){ for(j = 1; j < (FIELD_W - 1); j++){ if(i > 0){ field[i][j] = field[i - 1][j]; if(field[i][j] == NONE){ mvaddch(i, j, ' '); }else{ mvaddch(i, j, '#'); } }else{ field[i][j] = NONE; mvaddch(i, j, ' '); } } } } static void eraseCheck(int y) { int i, j, add; bool erase; add = 0; for(i = y; i >= 0; i--){ erase = TRUE; for(j = 1; j < (FIELD_W - 1); j++){ if(field[i][j] != BLOCK){ erase = FALSE; } } if(erase == TRUE){ lines++; add += add + 10; eraseLine(i); i++; } } score += add; } static void fixBlock(int y, int x) { int i, cx, cy; for(i = 0; i < BLOCK_N; i++){ cy = y + block_tbl[i][0]; cx = x + block_tbl[i][1]; field[cy][cx] = BLOCK; mvaddch(cy, cx, '#'); } eraseCheck(FIELD_H - 2); } static bool collideBlock(int y, int x) { bool ret = FALSE; int i, cx, cy; for(i = 0; i < BLOCK_N; i++){ cy = y + block_tbl[i][0]; cx = x + block_tbl[i][1]; if(field[cy][cx] != 0){ ret = TRUE; break; } } return ret; } static void fallBlock(void) { int fy = 0; bool down = FALSE; fall_wait++; if(fall_wait >= fall_time){ fy = +1; fall_wait = 0; }else if(key == KEY_DOWN){ fy = +1; down = TRUE; }else{ return; } do{ if(collideBlock(py + fy, px) == TRUE){ fixBlock(py, px); initBlock(); down = FALSE; break; } py += fy; if(down == TRUE){ score++; } }while(down == TRUE); } static void rotateBlock(void) { int i, x, y; int tbl[BLOCK_N][2]; for(i = 0; i < BLOCK_N; i++){ tbl[i][0] = block_tbl[i][0]; tbl[i][1] = block_tbl[i][1]; } for(i = 0; i < BLOCK_N; i++){ y = +1 * block_tbl[i][1]; x = -1 * block_tbl[i][0]; block_tbl[i][0] = y; block_tbl[i][1] = x; } if(collideBlock(py, px) == TRUE){ for(i = 0; i < BLOCK_N; i++){ block_tbl[i][0] = tbl[i][0]; block_tbl[i][1] = tbl[i][1]; } } } static void moveBlock(void) { int vx = 0, vy = 0; if(key == KEY_UP) rotateBlock(); if(key == KEY_LEFT) vx = -1; if(key == KEY_RIGHT) vx = +1; if(collideBlock(py + vy, px + vx) == FALSE){ px += vx; py += vy; } fallBlock(); } static void drawBlock(void) { int i, x, y; for(i = 0; i < BLOCK_N; i++){ y = block_tbl[i][0]; x = block_tbl[i][1]; mvaddch(py + y, px + x, '#'); } } static void clearBlock(void) { int i, x, y; for(i = 0; i < BLOCK_N; i++){ y = block_tbl[i][0]; x = block_tbl[i][1]; mvaddch(py + y, px + x, ' '); } } static void drawApp(void) { drawBlock(); mvprintw(1, FIELD_W + 1, "LINE %d", lines); mvprintw(2, FIELD_W + 1, "SCORE %d", score); mvaddch(FIELD_H, px - 1, ' '); } static bool isExecApp(void) { if(key == 'q'){ return FALSE; } return TRUE; } static bool execApp(void) { bool exec = TRUE; drawField(); drawApp(); while(exec){ key = getch(); exec = isExecApp(); clearBlock(); moveBlock(); drawApp(); } return exec; } int main(int argc, char *argv[]) { initApp(); atexit(exitApp); while(TRUE){ initField(); initBlock(); if(execApp() == FALSE){ break; } } return 0; }