分享一个蛋疼的俄罗斯方块小游戏

转载请注明出处。http://www.cnblogs.com/dave_cn/ 

我在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*** 

 

 

posted @ 2010-07-28 22:59  mr. dave  阅读(2021)  评论(9编辑  收藏  举报