Linux下的字符界面扫雷游戏

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

这段时间事情比较忙,好不容易抽出些时间再找点游戏练练手,这里献丑了。 

我在Ubuntu 10.04下测试过,可以正常运行。

代码用到了NCURSES库。编译的时候链一下ncurses库就可以了,如:
cc -Wall -O2 -o mine mine.c -lncurses

 

首先放出界面欣赏一下,眼晕的看看外面再继续 ==!:

 Linux下的字符界面扫雷游戏  Linux下的字符界面扫雷游戏

 

 代码:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <sys/select.h>

#include <time.h>

#include <signal.h>

#include <unistd.h>

#include <ncurses.h>



/* 雷区的范围 */

#define MINEAREA_WIDTH      9

#define MINEAREA_LENGTH     9

/* 雷的个数 */

#define MINE_NUMBER         10



/*

 * 将每个方块的状态分类:

 * 1.初始状态

 * 2.判定为雷(也就是通常的插旗)

 * 3.排除(若周边有n个雷则显示为n,0则显示为空,用-1来表示雷)

 */

#define SQUARE_INIT         0

#define SQUARE_FLAG         1

#define SQUARE_CLEAN        2

#define SQUARE_ZERO         0

#define SQUARE_MINE         -1



/* 显示图形 */

#define GRAPH_INIT          '.'

#define GRAPH_MINE          '@'

#define GRAPH_NULL          ' '

#define GRAPH_FLAG          'F'



#define NEWLINE             addch('\n')

#define _W(y)               (y * 2 + 3)

#define _L(x)               (x * 3 + 1)

/* 设置光标 */

#define SET_CURSOR(y, x)    mvchgat(_W(y), _L(x), 2, A_REVERSE, 0, NULL)

#define CLEAN_CURSOR(y, x)  mvchgat(_W(y), _L(x), 2, A_NORMAL,  0, NULL)



#define WPRINT_NUMBER(y, x, v)   \

    mvprintw(y, x, "%d", v)

#define WPRINT_CHAR(y, x, c)     \

    mvaddch(y, x, c)



/* 光标的位置 */

int g_cur_y = 0;

int g_cur_x = 0;



struct square_t {

    int type;

    int mine;

};



/* timer process function */

int timer_p();

void sig_refresh_time(int signum);



int init_mine(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH]);

int check_yx(int y, int x);

int game_loop(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH]);

int clean_zero_squares(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int cur_y, int cur_x);



/* window functions */

int win_init(int width, int length, int mine_num);

int win_refresh(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int width, int length, int mines);

int win_refresh_remine_mines(int mines);

int win_refresh_secs(int secs);

int win_mv_cursor(int delta_y, int delta_x, int width, int length);

int win_destroy();

int win_bang();

int win_win();

int win_game_over();



int main()

{

    int pid_timer;

    int pid_main;



    switch (pid_timer = fork()) {

    case 0:

        /* timer进程,用作计时器 */

        timer_p();

        _exit(0);

    case -1:

        perror("fork() error!");

        return -1;

    default:

        /* main process */

        break;

    }



    pid_main = getpid();



    /* SIGUSR1信号用来刷新显示时间 */

    if (signal(SIGUSR1, sig_refresh_time) == SIG_ERR)

        return -1;



    struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH];



    if (init_mine(square) == -1) return -1;



    win_init(MINEAREA_WIDTH, MINEAREA_LENGTH, MINE_NUMBER);



    /* 主循环 */

    game_loop(square);



    win_game_over();



    /* 主进程结束前需要结束timer子进程 */

    kill(pid_timer, SIGKILL);



    int key = -1;

    do {

        key = getch();

    }

    while (key != 'y' && key != 'Y');



    wait(NULL);

    win_destroy();



    return 0;

}



/* 初始化雷区信息 */

int init_mine(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH])

{

    if (square == NULL)

        return -1;



    printf("waiting...\n");



    int n,m;

    for (n = 0; n < 9; ++n) {

        for (m = 0; m < 9; ++m) {

            square[n][m].type = 0;

            square[n][m].mine = 0;

        }

    }



    int i;

    int y, x;



    srandom((int)time(NULL));



    for (i = 0; i < MINE_NUMBER; ++i) {

        y = random() % MINEAREA_WIDTH;

        x = random() % MINEAREA_LENGTH;



        if (square[y][x].mine == SQUARE_MINE) {

            --i;

        }

        else {

            square[y][x].mine = SQUARE_MINE;



            if (check_yx(y-1, x  ) == 0 && square[y-1][x  ].mine != SQUARE_MINE)

                ++square[y-1][x  ].mine;



            if (check_yx(y+1, x  ) == 0 && square[y+1][x  ].mine != SQUARE_MINE)

                ++square[y+1][x  ].mine;



            if (check_yx(y  , x-1) == 0 && square[y  ][x-1].mine != SQUARE_MINE)

                ++square[y  ][x-1].mine;



            if (check_yx(y  , x+1) == 0 && square[y  ][x+1].mine != SQUARE_MINE)

                ++square[y  ][x+1].mine;



            if (check_yx(y-1, x-1) == 0 && square[y-1][x-1].mine != SQUARE_MINE)

                ++square[y-1][x-1].mine;



            if (check_yx(y+1, x-1) == 0 && square[y+1][x-1].mine != SQUARE_MINE)

                ++square[y+1][x-1].mine;



            if (check_yx(y-1, x+1) == 0 && square[y-1][x+1].mine != SQUARE_MINE)

                ++square[y-1][x+1].mine;



            if (check_yx(y+1, x+1) == 0 && square[y+1][x+1].mine != SQUARE_MINE)

                ++square[y+1][x+1].mine;

        }

    }



    return 0;

}



int check_yx(int y, int x)

{

    if (y >= 0

        && y < MINEAREA_WIDTH

        && x >= 0

        && x < MINEAREA_LENGTH)

    {

        return 0;

    }



    return -1;

}



/* 主循环 */

int game_loop(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH])

{

    fd_set rfd;

    FD_ZERO(&rfd);

    FD_SET(0, &rfd);



    static int sweeped_mines = 0; /* 判定正确的雷数 */



    int ret;

    int input;

    int remain_mines = MINE_NUMBER;



    while (1) {

        if ((ret = select(1, &rfd, NULL, NULL, NULL)) <= 0) {

            //return -1;    //当程序被信号中断时select可能会返回-1

            continue;

        }



        switch (input = getch()) {

        /* w,s,a,d方向键 */

        case 'w':

        case 'W':

            win_mv_cursor(-1, 0, MINEAREA_WIDTH, MINEAREA_LENGTH);

            break;

        case 's':

        case 'S':

            win_mv_cursor(+1, 0, MINEAREA_WIDTH, MINEAREA_LENGTH);

            break;

        case 'a':

        case 'A':

            win_mv_cursor(0, -1, MINEAREA_WIDTH, MINEAREA_LENGTH);

            break;

        case 'd':

        case 'D':

            win_mv_cursor(0, +1, MINEAREA_WIDTH, MINEAREA_LENGTH);

            break;



        /* 插旗 */

        case 'j':

        case 'J':

            if (square[g_cur_y][g_cur_x].type == SQUARE_INIT) {

                square[g_cur_y][g_cur_x].type = SQUARE_FLAG;

                --remain_mines;



                if (square[g_cur_y][g_cur_x].mine == SQUARE_MINE)

                    ++sweeped_mines;

            }

            else if (square[g_cur_y][g_cur_x].type == SQUARE_FLAG) {

                square[g_cur_y][g_cur_x].type = SQUARE_INIT;

                ++remain_mines;



                if (square[g_cur_y][g_cur_x].mine == SQUARE_MINE)

                    --sweeped_mines;

            }

            else

                break;



            if (sweeped_mines == MINE_NUMBER) {

                win_win();

                goto GAME_OVER;

            }



            win_refresh(square, MINEAREA_WIDTH, MINEAREA_LENGTH, remain_mines);

            break;



        /* 打开方块 */

        case 'k':

        case 'K':

            if (square[g_cur_y][g_cur_x].type == SQUARE_CLEAN)

                break;

            else if (square[g_cur_y][g_cur_x].mine == SQUARE_MINE) {

                win_bang();



                int n, m;

                for (n = 0; n < MINEAREA_WIDTH; ++n) {

                    for (m = 0; m < MINEAREA_LENGTH; ++m) {

                        square[n][m].type = SQUARE_CLEAN;

                    }

                }



                win_refresh(square, MINEAREA_WIDTH, MINEAREA_LENGTH, remain_mines);

                goto GAME_OVER;

            }



            square[g_cur_y][g_cur_x].type = SQUARE_CLEAN;



            if (square[g_cur_y][g_cur_x].mine == SQUARE_ZERO)

                clean_zero_squares(square, g_cur_y, g_cur_x);



            win_refresh(square, MINEAREA_WIDTH, MINEAREA_LENGTH, remain_mines);

            break;



        /* 退出 */

        case 'q':

        case 'Q':

            goto GAME_OVER;



        default:

            break;

        }

    }



GAME_OVER:

    return 0;

}



/* 如果打开的方块下面是0,则自动打开所有周围为0的方块 */

int clean_zero_squares(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int cur_y, int cur_x)

{

    if (check_yx(cur_y - 1, cur_x) == 0

        && square[cur_y - 1][cur_x].mine == SQUARE_ZERO

        && square[cur_y - 1][cur_x].type != SQUARE_CLEAN)

    {

        square[cur_y - 1][cur_x].type = SQUARE_CLEAN;

        clean_zero_squares(square, cur_y - 1, cur_x);

    }



    if (check_yx(cur_y + 1, cur_x) == 0

        && square[cur_y + 1][cur_x].mine == SQUARE_ZERO

        && square[cur_y + 1][cur_x].type != SQUARE_CLEAN)

    {

        square[cur_y + 1][cur_x].type = SQUARE_CLEAN;

        clean_zero_squares(square, cur_y + 1, cur_x);

    }



    if (check_yx(cur_y, cur_x - 1) == 0

        && square[cur_y][cur_x - 1].mine == SQUARE_ZERO

        && square[cur_y][cur_x - 1].type != SQUARE_CLEAN)

    {

        square[cur_y][cur_x - 1].type = SQUARE_CLEAN;

        clean_zero_squares(square, cur_y, cur_x - 1);

    }



    if (check_yx(cur_y, cur_x + 1) == 0

        && square[cur_y][cur_x + 1].mine == SQUARE_ZERO

        && square[cur_y][cur_x + 1].type != SQUARE_CLEAN)

    {

        square[cur_y][cur_x + 1].type = SQUARE_CLEAN;

        clean_zero_squares(square, cur_y, cur_x + 1);

    }



    return 0;

}



/*****************************************************************************/

/* 初始化显示界面 */

int win_init(int width, int length, int mine_num)

{

    initscr();

    raw();

    noecho();

    keypad(stdscr, TRUE);

    curs_set(0);

    refresh();



    win_refresh_remine_mines(MINE_NUMBER);

    win_refresh_secs(0);



    int frame_width  = width  * 2 + 1;

    int frame_length = length * 3 + 1;

    char *line = NULL;

    line = (char*)malloc((frame_length + 1) * sizeof(char));

    memset(line, '-', frame_length);

    *(line + frame_length) = '\0';

    mvprintw(2, 0, line);NEWLINE;



    int i, j;

    for (j = 0; j < frame_width - 2; ++j) {

        addch('|');

        for (i = 0; i < length * 2 + 1 - 2; ++i) {

            if (j % 2 == 0) {

                if (i % 2 == 0) {

                    addch(GRAPH_INIT);addch(' ');

                }

                else {

                    addch('|');

                }

            }

            else {

                if (i % 2 == 0) {

                    addch('-');addch('-');

                }

                else {

                    addch('+');

                }

            }

        }

        addch('|');NEWLINE;

    }



    printw(line);NEWLINE;



    /* set cursor position */

    SET_CURSOR(g_cur_y, g_cur_x);



    refresh();

    return 0;

}



/* 刷新显示界面 */

int win_refresh(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int width, int length, int mines)

{

    if (square == NULL)

        return -1;



    win_refresh_remine_mines(mines);



    int j, i;

    for (j = 0; j < width; ++j) {

        for (i = 0; i < length; ++i) {

            switch (square[j][i].type) {

            case SQUARE_INIT:

                WPRINT_CHAR(_W(j), _L(i), GRAPH_INIT);

                break;

            case SQUARE_FLAG:

                WPRINT_CHAR(_W(j), _L(i), GRAPH_FLAG);

                break;

            case SQUARE_CLEAN:

                switch (square[j][i].mine) {

                case SQUARE_MINE:

                    WPRINT_CHAR(_W(j), _L(i), GRAPH_MINE);

                    break;

                case SQUARE_ZERO:

                    WPRINT_CHAR(_W(j), _L(i), GRAPH_NULL);

                    break;

                default:

                    WPRINT_NUMBER(_W(j), _L(i), square[j][i].mine);

                    break;

                }

                break;

            default:

                break;

            }

        }

    }



    refresh();



    return 0;

}



int win_refresh_remine_mines(int mines)

{

    mvprintw(0, 0, "Mines: %d", mines);

    mvprintw(1, 0, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    refresh();

    return 0;

}



int win_refresh_secs(int secs)

{

    mvprintw(0, 15, "Seconds: %d", secs);

    refresh();

    return 0;

}





int win_mv_cursor(int delta_y, int delta_x, int width, int length)

{

    CLEAN_CURSOR(g_cur_y, g_cur_x);



    if (g_cur_y + delta_y < width && g_cur_y + delta_y >= 0)

        g_cur_y += delta_y;



    if (g_cur_x + delta_x < length && g_cur_x + delta_x >= 0)

        g_cur_x += delta_x;



    SET_CURSOR(g_cur_y, g_cur_x);



    refresh();



    return 0;

}



int win_destroy()

{

    endwin();



    return 0;

}



int win_bang()

{

    mvprintw(0, 0, "BANG!!!!");

    refresh();

    return 0;

}



int win_win()

{

    mvprintw(0, 0, "WIN!!!!");

    refresh();

    return 0;

}



int win_game_over()

{

    mvprintw(1, 0, "Game Over!");

    mvprintw(1, 0, "Press 'y' or 'Y' to end.");

    refresh();

    return 0;

}



/*****************************************************************************/

int timer_p()

{

    /* 每秒钟给主进程发一次信号 */

    do {

        sleep(1);

        kill(getppid(), SIGUSR1);

    }

    while (1);



    return 0;

}



void sig_refresh_time(int signum)

{

    static int secs = 0;

    win_refresh_secs(++secs);

}



 

这里给出我自己编的一个可执行文件(ubuntu 10.4):

 /Files/dave_cn/mine.zip

 

-- END -- 

你可能感兴趣的:(linux)