#DATE 2010/07/28
#2012/08/27由cnblogs迁入
我在Ubuntu 10.04下测试过,可以正常运行。不过界面让人蛋疼。
代码用到了NCURSES库。编译的时候链一下ncurses库就可以了,如:
cc -Wall -O2 -o c01 file.c -lncurses
界面:
/*************************************** * * TETRIS * * author: dave * date : 2010/07/28 * ***************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <ncurses.h> #define TETRADS_LEN 4 #define GAMEWIN_YLEN 20 #define GAMEWIN_XLEN 10 #define GAMEWIN_Y 1 #define GAMEWIN_X 2 #define INFOWIN_YLEN 10 #define INFOWIN_XLEN 10 #define INFOWIN_Y GAMEWIN_Y #define INFOWIN_X (GAMEWIN_X + GAMEWIN_XLEN*2 + 5) #define PIC_BLOCK '#' #define PIC_NULL ' ' #define _X(x) ((x)*2) #define BASEWIN \ WINDOW *win; \ void (*init)(); \ void (*show)(); #define EXCHANGE_XY(_pos) \ do { \ (_pos).x = (_pos).y + (_pos).x; \ (_pos).y = (_pos).x - (_pos).y; \ (_pos).x -= (_pos).y; \ } while (0) #define EXCHANGE_2Y(_pos) \ (_pos).y = 2 - (_pos).y #define COPY_TETRADS(_dest, _src) \ memcpy(&(_dest), &(_src), sizeof(Tetrads)) #define TETRISNEW(_p, _t) \ do { \ (_p) = (_t*)malloc(sizeof(_t)); \ (_p)->init = init##_t; \ (_p)->show = show##_t; \ } while (0) #define TETRISDEL(_p) \ do { \ delwin(_p->win); \ free(_p); \ } while (0) /* 俄罗斯方块的7种方块(Tetromino) */ typedef enum { TETRADS_S = 0, TETRADS_Z, TETRADS_L, TETRADS_J, TETRADS_O, TETRADS_T, TETRADS_I, TETRADS_MAX } ETetrads; typedef enum { DIR_UP = 0, DIR_DOWN, DIR_LEFT, DIR_RIGHT, DIR_MAX } EDirction; typedef enum { TETRIS_STATE_NEW, TETRIS_STATE_MOVE, TETRIS_STATE_STOP, TETRIS_STATE_MAX, } ETetrisState; typedef struct { int y; int x; } Point; typedef struct { ETetrads type; Point blocks[TETRADS_LEN]; } Tetrads; /*** BEGIN : 界面显示 ***/ /* 将界面显示与数据处理分离 */ typedef struct _BaseWin { /** * WINDOW *win; * void (*init)(); * void (*show)(); */ BASEWIN } BaseWin; typedef struct _GameWin { /** * WINDOW *win; * void (*init)(); * void (*show)(); */ BASEWIN char background[GAMEWIN_YLEN][GAMEWIN_XLEN]; char matrix[GAMEWIN_YLEN][GAMEWIN_XLEN]; int level; Point pos; Tetrads curTetrads; } GameWin; typedef struct _InfoWin { /** * WINDOW *win; * void (*init)(); * void (*show)(); */ BASEWIN int score; int level; Tetrads nextTetrads; } InfoWin; void tetrisPrint(WINDOW *win, Point pos, Point block, char chr); void initGameWin(GameWin* _self) { _self->win = newwin(GAMEWIN_YLEN + 2, _X(GAMEWIN_XLEN) + 1, GAMEWIN_Y, GAMEWIN_X); box(_self->win, 0, 0); mvwprintw(_self->win, 0, (_X(GAMEWIN_XLEN) + 2)/2 - 3, "TETRIS"); wrefresh(_self->win); memset(_self->background, PIC_NULL, GAMEWIN_YLEN * GAMEWIN_XLEN); memset(_self->matrix, PIC_NULL, GAMEWIN_YLEN * GAMEWIN_XLEN); _self->pos = (Point){1, GAMEWIN_XLEN/2}; _self->level = 0; } void initInfoWin(InfoWin* _self) { _self->win = newwin(INFOWIN_YLEN, _X(GAMEWIN_XLEN), INFOWIN_Y, INFOWIN_X); box(_self->win, 0, 0); mvwprintw(_self->win, 0, _X(GAMEWIN_XLEN)/2 - 2, "INFO"); mvwprintw(_self->win, 7, 2, "SCORE: 0"); mvwprintw(_self->win, 8, 2, "LEVEL: 0"); wrefresh(_self->win); _self->score = 0; _self->level = 0; } void showGameWin(GameWin* _self) { int y = 0; int x = 0; for (y = 0; y < GAMEWIN_YLEN; ++y) for (x = 0; x < GAMEWIN_XLEN; ++x) tetrisPrint(_self->win, (Point){0, 0}, (Point){y, x}, (_self->background)[y][x]); for (y = 0; y < GAMEWIN_YLEN; ++y) for (x = 0; x < GAMEWIN_XLEN; ++x) tetrisPrint(_self->win, (Point){0, 0}, (Point){y, x}, (_self->matrix)[y][x]); for (x = 0; x < TETRADS_LEN; ++x) tetrisPrint(_self->win, (Point){(_self->pos).y, (_self->pos).x}, (_self->curTetrads).blocks[x], PIC_BLOCK); wrefresh(_self->win); } void showInfoWin(InfoWin* _self) { int i = 0; mvwprintw(_self->win, 2, _X(INFOWIN_XLEN/2 - 2), " "); mvwprintw(_self->win, 3, _X(INFOWIN_XLEN/2 - 2), " "); mvwprintw(_self->win, 4, _X(INFOWIN_XLEN/2 - 2), " "); mvwprintw(_self->win, 5, _X(INFOWIN_XLEN/2 - 2), " "); mvwprintw(_self->win, 6, _X(INFOWIN_XLEN/2 - 2), " "); for (i = 0; i < TETRADS_LEN; ++i) tetrisPrint(_self->win, (Point){2, INFOWIN_XLEN/2 - 2}, _self->nextTetrads.blocks[i], PIC_BLOCK); mvwprintw(_self->win, INFOWIN_YLEN - 3, 2, "SCORE: %d", _self->score); mvwprintw(_self->win, INFOWIN_YLEN - 2, 2, "LEVEL: %d", _self->level); wrefresh(_self->win); } /*** END : 界面显示 ***/ /*** 函数声明 ***/ void newTetrads(Tetrads *tetrads); void initTetrads(Tetrads *tetrads); void spinTetrads(Tetrads *tetrads); int runTetris(GameWin *gwin); int checkBorder(GameWin *gwin); int checkStop(GameWin *gwin); int checkOver(GameWin *gwin); int checkClean(GameWin *gwin); void refreshMatrix(GameWin *gwin); int genRandom(int max); int main() { /* init ncurses screen */ initscr(); raw(); noecho(); keypad(stdscr, TRUE); curs_set(0); refresh(); /* 初始化界面 */ GameWin *gwin; InfoWin *iwin; TETRISNEW(gwin, GameWin); TETRISNEW(iwin, InfoWin); gwin->init(gwin); iwin->init(iwin); /* Tetris的处理使用简单的状态机实现 */ int f_end = 0; int state = TETRIS_STATE_NEW; newTetrads(&(iwin->nextTetrads)); while (!f_end) { switch (state) { case TETRIS_STATE_NEW: COPY_TETRADS(gwin->curTetrads, iwin->nextTetrads); gwin->pos = (Point){1, 4}; newTetrads(&(iwin->nextTetrads)); iwin->show(iwin); state = TETRIS_STATE_MOVE; break; case TETRIS_STATE_MOVE: gwin->show(gwin); switch (runTetris(gwin)) { case -1: goto END; break; case 0: break; case 1: state = TETRIS_STATE_STOP; break; default: break; } break; case TETRIS_STATE_STOP: refreshMatrix(gwin); iwin->score = checkClean(gwin); state = TETRIS_STATE_NEW; break; default : f_end = 1; break; } } END: mvwprintw(gwin->win, GAMEWIN_YLEN/2 - 2, 5, "GAME OVER!!!"); mvwprintw(gwin->win, GAMEWIN_YLEN/2, 4, "Press any key"); mvwprintw(gwin->win, GAMEWIN_YLEN/2 + 1, 6, "to quit..."); wrefresh(gwin->win); getch(); TETRISDEL(iwin); TETRISDEL(gwin); endwin(); return 0; } void tetrisPrint(WINDOW *win, Point pos, Point block, char chr) { mvwaddch(win, pos.y + block.y + 1, (pos.x + block.x) * 2 + 1, chr); } void newTetrads(Tetrads *tetrads) { tetrads->type = genRandom(TETRADS_MAX); initTetrads(tetrads); int spin = genRandom(DIR_MAX); int i = 0; for (; i <= spin; ++i) { spinTetrads(tetrads); } } void initTetrads(Tetrads *tetrads) { switch (tetrads->type) { case TETRADS_S: tetrads->blocks[0] = (Point){2, 0}; tetrads->blocks[1] = (Point){2, 1}; tetrads->blocks[2] = (Point){1, 1}; tetrads->blocks[3] = (Point){1, 2}; break; case TETRADS_Z: tetrads->blocks[0] = (Point){1, 0}; tetrads->blocks[1] = (Point){1, 1}; tetrads->blocks[2] = (Point){2, 1}; tetrads->blocks[3] = (Point){2, 2}; break; case TETRADS_L: tetrads->blocks[0] = (Point){2, 0}; tetrads->blocks[1] = (Point){2, 1}; tetrads->blocks[2] = (Point){1, 1}; tetrads->blocks[3] = (Point){0, 1}; break; case TETRADS_J: tetrads->blocks[0] = (Point){0, 0}; tetrads->blocks[1] = (Point){1, 0}; tetrads->blocks[2] = (Point){1, 1}; tetrads->blocks[3] = (Point){1, 2}; break; case TETRADS_O: tetrads->blocks[0] = (Point){0, 0}; tetrads->blocks[1] = (Point){0, 1}; tetrads->blocks[2] = (Point){1, 0}; tetrads->blocks[3] = (Point){1, 1}; break; case TETRADS_T: tetrads->blocks[0] = (Point){0, 1}; tetrads->blocks[1] = (Point){1, 0}; tetrads->blocks[2] = (Point){1, 1}; tetrads->blocks[3] = (Point){1, 2}; break; case TETRADS_I: tetrads->blocks[0] = (Point){0, 1}; tetrads->blocks[1] = (Point){1, 1}; tetrads->blocks[2] = (Point){2, 1}; tetrads->blocks[3] = (Point){3, 1}; break; default: break; } } /** * 旋转Tetrads */ void spinTetrads(Tetrads *tetrads) { int i = 0; switch (tetrads->type) { case TETRADS_O: break; case TETRADS_I: /* x,y互换 */ for (i = 0; i < TETRADS_LEN; ++i) EXCHANGE_XY(tetrads->blocks[i]); break; default: for (i = 0; i < TETRADS_LEN; ++i) EXCHANGE_XY(tetrads->blocks[i]); for (i = 0; i < TETRADS_LEN; ++i) EXCHANGE_2Y(tetrads->blocks[i]); break; } } /** * Return: * -1 game over * 0 continue * 1 stop */ int runTetris(GameWin *gwin) { int ret = 0; fd_set fset; FD_ZERO(&fset); FD_SET(0, &fset); struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 500000 - 10000 * gwin->level; int fd = -1; if ((fd = select(1, &fset, NULL, NULL, &timeout)) <= 0) { ++((gwin->pos).y); while (checkStop(gwin) != 0) { --((gwin->pos).y); ret = 1; } if (ret == 1) { if (checkOver(gwin)) return -1; return 1; } return 0; } Tetrads tmptetrads; char ch; int n = 0; switch (ch = getch()) { case 'w': COPY_TETRADS(tmptetrads, gwin->curTetrads); spinTetrads(&gwin->curTetrads); while ((n = checkBorder(gwin)) != 0) (gwin->pos).x += n; while (checkStop(gwin) != 0) { --((gwin->pos).y); ret = 1; } return ret; case 's': ++((gwin->pos).y); while (checkStop(gwin) != 0) { --((gwin->pos).y); ret = 1; } if (ret == 1) { if (checkOver(gwin)) return -1; return 1; } return 0; case 'a': --((gwin->pos).x); while ((n = checkBorder(gwin)) != 0) (gwin->pos).x += n; while (checkStop(gwin) != 0) { ++((gwin->pos).x); ret = 1; } break; case 'd': ++((gwin->pos).x); while ((n = checkBorder(gwin)) != 0) (gwin->pos).x += n; while (checkStop(gwin) != 0) { --((gwin->pos).x); ret = 1; } break; default: break; } return 0; } /** * 检查是否达到边线 */ int checkBorder(GameWin *gwin) { int i = 0; int n = 0; for (i = 0; i < TETRADS_LEN; ++i) { if ((n = ((gwin->pos).x + (gwin->curTetrads).blocks[i].x)) < 0) return -n; if ((n = ((gwin->pos).x + (gwin->curTetrads).blocks[i].x)) > GAMEWIN_XLEN - 1) return GAMEWIN_XLEN - 1 - n; } return 0; } /** * 检查是否停止 */ int checkStop(GameWin *gwin) { int i = 0; for (i = 0; i < TETRADS_LEN; ++i) if (gwin->matrix[(gwin->pos).y + (gwin->curTetrads).blocks[i].y][(gwin->pos).x + (gwin->curTetrads).blocks[i].x] == PIC_BLOCK || ((gwin->pos).y + (gwin->curTetrads).blocks[i].y) >= GAMEWIN_YLEN) return 1; return 0; } /** * 检查是否游戏结束 */ int checkOver(GameWin *gwin) { int i = 0; for (i = 0; i < TETRADS_LEN; ++i) if ((gwin->pos).y <= 0) return 1; return 0; } /** * 检查是否需要清楚一行 */ int checkClean(GameWin *gwin) { char bline[GAMEWIN_XLEN]; memset(bline, PIC_BLOCK, GAMEWIN_XLEN); int i = 0; int num = 0; int score = 0; for (i = 0; i < TETRADS_LEN; ++i) { num = (gwin->pos).y + (gwin->curTetrads).blocks[i].y; if (strncmp(gwin->matrix[num], bline, GAMEWIN_XLEN) == 0) { score += 10; for (; num > 0; --num) memcpy(gwin->matrix[num], gwin->matrix[num-1], GAMEWIN_XLEN); } } return score; } void refreshMatrix(GameWin *gwin) { int i = 0; for (i = 0; i < TETRADS_LEN; ++i) gwin->matrix[(gwin->pos).y + (gwin->curTetrads).blocks[i].y][(gwin->pos).x + (gwin->curTetrads).blocks[i].x] = PIC_BLOCK; } /** * 返回0~max-1的一个随机数 */ int genRandom(int max) { srandom((int)time(NULL)); return (random() % max); }
*** END ***