c++实现2048小游戏(windows控制台)

编程环境:windows10、c++17、clion但是设置为在外部控制台(其实就是windows的cmd)中输出。

说是c++,其实基本上全是c语言的语法。

不多说,直接上代码。

源代码:

#include <iostream>
#include "windows.h"
#include "conio.h"

//======================================================================================================================
// 定义颜色代码
enum ConsoleColor {
    BLACK = 0,
    BLUE = 1,
    GREEN = 2,
    CYAN = 3,
    RED = 4,
    MAGENTA = 5,
    BROWN = 6,
    LIGHT_GRAY = 7,
    DARK_GRAY = 8,
    LIGHT_BLUE = 9,
    LIGHT_GREEN = 10,
    LIGHT_CYAN = 11,
    LIGHT_RED = 12,
    LIGHT_MAGENTA = 13,
    YELLOW = 14,
    WHITE = 15
};

int board[4][4];             //存放十六个格子中每一个元素,零代表空格

int score;              //计算当前得分

struct cursor_position{
    //这个结构体用来记录光标的位置
    short x;
    short y;
};
cursor_position cp;
//======================================================================================================================
void print_board();        //打印棋盘
void print_number();       //打印数字
void set_cursor_position();             //设置当前光标的位置
void set_console_color(ConsoleColor color);// 改变控制台文本颜色的函数
void initial_game();        //初始化游戏
void running_game();        //游戏运行的主体函数
bool is_success();            //判断是否赢得游戏
void game_over();          //游戏结束
void set_cursor_invisible();          //将控制台的光标设置为不可见
void print_score();           //在棋盘右下角打印得分
//======================================================================================================================
int main() {
    initial_game();
    running_game();

    system("pause");
    return 0;
}
void print_board(){
    printf("+-------+-------+-------+-------+\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("+-------+-------+-------+-------+\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("+-------+-------+-------+-------+\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("+-------+-------+-------+-------+\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("|\t|\t|\t|\t|\n");
    printf("+-------+-------+-------+-------+\n");
}
void print_number(){
    for(int i = 0; i < 4; ++i){
        for(int j = 0; j < 4; ++j){
            if(board[i][j] == 0){
                //如果该位置数字为零,则输出\t,表示空着
                cp.x = i * 4 + 2;
                cp.y = j * 8 + 2;
                set_cursor_position();
                printf("\t");
                continue;
            }
            // x 是行,y 是列
            cp.x = i * 4 + 2;
            cp.y = j * 8 + 2;
            set_cursor_position();
            printf("\t");
            set_cursor_position();
            set_console_color(GREEN);
            printf("%d", board[i][j]);
            set_console_color(WHITE);
        }
    }

}
void set_cursor_position(){
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut == INVALID_HANDLE_VALUE) {
        std::cerr << "Error getting console output handle!\n";
        exit(-1);
    }

    // 设置光标位置为(x, y),注意这里的坐标是从0开始的
    COORD coord = {cp.y , cp.x}; // 列(y)和行(x)
    SetConsoleCursorPosition(hOut, coord);
}
void set_console_color(ConsoleColor color) {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, color);
}
void initial_game(){
    score = 0;              //将得分置为零

    set_cursor_invisible();                        //将光标设置为不可见

    memset(board, 0, sizeof(board));//将所有数字置为零

    //将随机两个格子设置为2
    srand(unsigned(time(nullptr)));
    int r = rand() % 4, c = rand() % 4;
    board[r][c] = 2;
    r = rand() % 4, c = rand() % 4;
    board[r][c] = 2;


    print_board();//打印棋盘
    print_number();//打印数字
}
void running_game(){
    while(true){
        if(is_success()){
            //判断玩家是否已经取胜
            cp.x = 20;
            cp.y = 22;
            set_cursor_position();
            set_console_color(RED);
            printf("游戏胜利!\n\t\t\t");
            system("pause");
        }

        bool is_full = true;            //判断格子是否满了

        //===========================接下来判断格子是否满了================================
        //如果所有数字都不为零,那么说明格子满了
        for(int i = 0; i < 4; ++i){
            for(int j = 0; j < 4; ++j){
                if(board[i][j] == 0){
                    is_full = false;
                }
            }
        }

        bool can_move_left = false;       //判断是否可以向左移动
        bool can_move_right = false;       //判断是否可以向右移动
        bool can_move_up = false;       //判断是否可以向上移动
        bool can_move_down = false;       //判断是否可以向下移动


        //===========================接下来判断是否可以向下移动================================
        /* 例子:
         * 0 4  0 2
         * 0 0  4 4
         * 8 4  2 4
         * 4 16 8 16
         * 怎么判断是否可以向下移动?
         * 第1列(j == 1),board[0][1] == 4,而board[1][1] == 0,board[0][1]下面是零,所以第1列可以向下移动
         * 第3列(j == 3),board[1][3] == 4,而board[2][3] == 4,board[1][3]下面的数字和他相同,所以第3列可以向下移动
         * 所有得出判断依据:只要某个数下面的数为零,或者它下面的数和它相等,那么就可以移动
         */
        for(int j = 0; j < 4; ++j){
            int row = -1;//临时存放某个数字所在行数,row = -1表示未存放
            for(int i = 0; i < 4; ++i){
                if(board[i][j] == 0){
                    if(row == -1){
                        //如果board[i][j] == 0 且 row == -1,那么continue即可
                        continue;
                    }else{
                        //如果board[i][j] == 0 且 row 不为 -1,那么对应了“某个数字下面是零”的情况,说明可以移动
                        can_move_down = true;
                        break;
                    }
                }else{
                    if(row == -1){
                        //如果board[i][j] != 0 且 row == -1,那么将 row 为 i 即可
                        row = i;
                    }else{
                        //如果board[i][j] != 0 且 row != -1,那么需要判断board[i][j] 和 board[row][j]是否相等
                        if(board[i][j] == board[row][j]){
                            //如果它们相等,那么对应了“某个数字下面的数和它相等”的情况,说明可以移动
                            can_move_down = true;
                            break;
                        }else{
                            //如果它们不等,那么不能移动,将 row 为 i 即可
                            row = i;
                        }
                    }
                }
            }
        }
        //===========================接下来判断是否可以向上移动================================
        for(int j = 0; j < 4; ++j){
            int row = -1;
            for(int i = 3; i >= 0; --i){
                if(board[i][j] == 0){
                    if(row == -1){
                        continue;
                    }else{
                        can_move_up = true;
                        break;
                    }
                }else{
                    if(row == -1){
                        row = i;
                    }else{
                        if(board[i][j] == board[row][j]){
                            can_move_up = true;
                            break;
                        }else{
                            row = i;
                        }
                    }
                }
            }
        }
        //===========================接下来判断是否可以向左移动================================
        for(int i = 0; i < 4; ++i){
            int col = -1;
            for(int j = 3; j >= 0; --j){
                if(board[i][j] == 0){
                    if(col == -1){
                        continue;
                    }else{
                        can_move_left = true;
                        break;
                    }
                }else{
                    if(col == -1){
                        col = j;
                    }else{
                        if(board[i][j] == board[i][col]){
                            can_move_left = true;
                            break;
                        }else{
                            col = j;
                        }
                    }
                }
            }
        }
        //===========================接下来判断是否可以向右移动================================
        for(int i = 0; i < 4; ++i){
            int col = -1;
            for(int j = 0; j < 4; ++j){
                if(board[i][j] == 0){
                    if(col == -1){
                        continue;
                    }else{
                        can_move_right = true;
                        break;
                    }
                }else{
                    if(col == -1){
                        col = j;
                    }else{
                        if(board[i][j] == board[i][col]){
                            can_move_right = true;
                            break;
                        }else{
                            col = j;
                        }
                    }
                }
            }
        }
        //===========================接下来判断是否游戏结束================================
        int operator_char = getch();      //操作符:w代表向上,a代表左,s代表下,d代表右

        if(is_full && (!can_move_up && !can_move_down && !can_move_left && !can_move_right)){
            //如果格子满了,而且上下左右都不能移动,那说明游戏结束
            game_over();
        }else if(!is_full && !can_move_up && !can_move_down && !can_move_left && !can_move_right) {
            //如果格子未满,而且上下左右都不能移动,那用户无论输入哪个方向键都无所谓,直接随机一个空格子将数字置为2即可
            //我知道用goto语句不好,这里为了偷懒将就用了

            if(operator_char == 'w' || operator_char == 'W' || operator_char == 'a' || operator_char == 'A' || operator_char == 's' || operator_char == 'S' || operator_char == 'd' || operator_char == 'D'){
                goto loop;
            }
            continue;
        }
        //只要上下左右有一个方向可以移动就无所谓,继续执行后面语句即可



        if(operator_char == 's' || operator_char == 'S'){
            if(can_move_down){
                //如果可以向下移动,那直接移动即可
                for(int j = 0; j < 4; ++j){
                    int row = -1;//临时存放某个数字所在行数,row = -1表示未存放
                    for(int i = 0; i < 4; ++i){
                        if(board[i][j] == 0){
                            if(row == -1){
                                //如果board[i][j] == 0 且 row == -1,那么continue即可
                                continue;
                            }else{
                                //如果board[i][j] == 0 且 row 不为 -1,那么对应了“某个数字下面是零”的情况,说明可以移动,将board[row][j]赋值给board[i][j]并归零
                                board[i][j] = board[row][j];
                                board[row][j] = 0;
                                row = i; //为什么把 i 赋值给 row ?
                                //比如某一列从下到上依次为 2 0 2 4,如果2上面是零就变成 0 2 2 4,下一次循环就可以把 2 和 2 合并了
                            }
                        }else{
                            if(row == -1){
                                //如果board[i][j] != 0 且 row == -1,那么将 row 为 i 即可
                                row = i;
                            }else{
                                //如果board[i][j] != 0 且 row != -1,那么需要判断board[i][j] 和 board[row][j]是否相等
                                if(board[i][j] == board[row][j]){
                                    //如果它们相等,那么对应了“某个数字下面的数和它相等”的情况,说明可以移动,将两数相加并归零board[row][j]
                                    board[i][j] *= 2;
                                    board[row][j] = 0;
                                    score += board[i][j];             //合并成功了,就把合并后的数字算作得分加上去

                                    row = -1; //为什么把row置为-1?
                                    //比如某一列从下到上依次为 2 0 2 4,
                                    //先把 2 和 0 交换变成 0 2 2 4
                                    //将 2 和 2 合并后得到 0 0 4 4
                                    //但是前面那个 4 就是合并之后得到的,不能拿去和最后一个 4 合并了,将row = -1避免这种情况
                                }else{
                                    //如果它们不等,那么不能移动,将 row 为 i 即可
                                    row = i;
                                }
                            }
                        }
                    }
                }
                /* 以上代码是我第一遍写的,其实是错的
                 * 例如:
                 * 2 4 4 8
                 * 2 0 2 4
                 * 4 0 2 2
                 * 4 4 0 2
                 * 使用这个算法向下移动会变成:
                 * 0 0 4 8
                 * 4 0 0 4
                 * 0 0 0 0
                 * 8 8 4 4
                 * 可是实际应该是:
                 * 0 0 0 0
                 * 0 0 0 8
                 * 4 0 4 4
                 * 8 8 4 4
                 * 可以发现这个算法虽然把该合并的东西合并了,但是某些该移动的东西没有动
                 * 所以得把这个算法后面加一个补丁:想办法让下面有零的数字“往下落”
                 * 即:依次按列-行遍历,如果遇到零,就将它和它上面的数字不停地交换位置,直到它上面为零或者它位于第零行为止(类似于冒泡排序)
                 *
                 */
                for(int j = 0; j < 4; ++j){
                    for(int i = 0; i < 4; ++i){
                        if(board[i][j] == 0){
                            int k = i;
                            while(k > 0){
                                if(board[k - 1][j] == 0){
                                    //如果它上面的数为零,那么不需要交换了,跳出本次循环
                                    break;
                                }else{
                                    //否则将它和它上面的数字不停地交换位置
                                    board[k][j] = board[k - 1][j];
                                    board[k - 1][j] = 0;
                                }
                                k--;
                            }
                        }
                    }
                }
            }else{
                //如果不能向下移动,那按s键没用,直接跳出本次循环
                continue;
            }
        }else if(operator_char == 'a' || operator_char == 'A'){
            if(can_move_left){
                for(int i = 0; i < 4; ++i){
                    int col = -1;
                    for(int j = 3; j >= 0; --j){
                        if(board[i][j] == 0){
                            if(col == -1){
                                continue;
                            }else{
                                board[i][j] = board[i][col];
                                board[i][col] = 0;
                                col = j;
                            }
                        }else{
                            if(col == -1){
                                col = j;
                            }else{
                                if(board[i][j] == board[i][col]){
                                    board[i][j] *= 2;
                                    board[i][col] = 0;
                                    score += board[i][j];             //合并成功了,就把合并后的数字算作得分加上去

                                    col = -1;
                                }else{
                                    col = j;
                                }
                            }
                        }
                    }
                }
                for(int i = 0; i < 4; ++i){
                    for(int j = 3; j >= 0; --j){
                        if(board[i][j] == 0){
                            int k = j;
                            while(k < 3){
                                if(board[i][k + 1] == 0){
                                    break;
                                }else{
                                    board[i][k] = board[i][k + 1];
                                    board[i][k + 1] = 0;
                                }
                                k++;
                            }
                        }
                    }
                }
            }else{
                continue;
            }
        }else if(operator_char == 'w' || operator_char == 'W'){
            if(can_move_up){
                for(int j = 0; j < 4; ++j){
                    int row = -1;
                    for(int i = 3; i >= 0; --i){
                        if(board[i][j] == 0){
                            if(row == -1){
                                continue;
                            }else{
                                board[i][j] = board[row][j];
                                board[row][j] = 0;
                                row = i;
                            }
                        }else{
                            if(row == -1){
                                row = i;
                            }else{
                                if(board[i][j] == board[row][j]){
                                    board[i][j] *= 2;
                                    board[row][j] = 0;
                                    score += board[i][j];             //合并成功了,就把合并后的数字算作得分加上去

                                    row = -1;
                                }else{
                                    row = i;
                                }
                            }
                        }
                    }
                }
                for(int j = 0; j < 4; ++j){
                    for(int i = 3; i >= 0; --i){
                        if(board[i][j] == 0){
                            int k = i;
                            while(k < 3){
                                if(board[k + 1][j] == 0){
                                    //如果它下面的数为零,那么不需要交换了,跳出本次循环
                                    break;
                                }else{
                                    //否则将它和它下面的数字不停地交换位置
                                    board[k][j] = board[k + 1][j];
                                    board[k + 1][j] = 0;
                                }
                                k++;
                            }
                        }
                    }
                }
            }else{
                continue;
            }
        }else if(operator_char == 'd' || operator_char == 'D'){
            if(can_move_right){
                for(int i = 0; i < 4; ++i){
                    int col = -1;
                    for(int j = 0; j < 4; ++j){
                        if(board[i][j] == 0){
                            if(col == -1){
                                continue;
                            }else{
                                board[i][j] = board[i][col];
                                board[i][col] = 0;
                                col = j;
                            }
                        }else{
                            if(col == -1){
                                col = j;
                            }else{
                                if(board[i][j] == board[i][col]){
                                    board[i][j] *= 2;
                                    board[i][col] = 0;
                                    score += board[i][j];             //合并成功了,就把合并后的数字算作得分加上去

                                    col = -1;
                                }else{
                                    col = j;
                                }
                            }
                        }
                    }
                }
                for(int i = 0; i < 4; ++i){
                    for(int j = 0; j < 4; ++j){
                        if(board[i][j] == 0){
                            int k = j;
                            while(k > 0){
                                if(board[i][k - 1] == 0){
                                    break;
                                }else{
                                    board[i][k] = board[i][k - 1];
                                    board[i][k - 1] = 0;
                                }
                                k--;
                            }
                        }
                    }
                }
            }else{
                continue;
            }
        }else{
            //如果按的不是上下左右,那么跳出本次循环
            continue;
        }
        loop:
        //每次移动棋盘之后,一定有空位。因为能够移动就说明合并了某些格子,说明一定有空的格子

//        //随机一个空格子将数字置为2
//        while(true){
//            int r = rand() % 4, c = rand() % 4;
//            if(board[r][c] == 0){
//                board[r][c] = 2;
//                break;
//            }
//        }
        //一开始我是随机把一个空格子置为2,后来发现这样难度有点高,那就改一下,空格子2/3概率出现2, 1/3概率出现4
        int value = (rand()%2 == 0)?4:2;
        while(true){
            int r = rand() % 4, c = rand() % 4;
            if(board[r][c] == 0){
                board[r][c] = value;
                break;
            }
        }
        print_number();         //打印数字
        print_score();            //打印得分
    }

}

bool is_success() {
    //如果存在一个数字的值为2048,那么说明游戏获胜
    for(int i = 0; i < 4; ++i){
        for(int j = 0; j < 4; ++j){
            if(board[i][j] == 2048){
                return true;
            }
        }
    }
    return false;
}
void game_over(){
    cp.x = 20;
    cp.y = 14;
    set_cursor_position();
    set_console_color(RED);
    printf("无法移动,游戏结束!得分:%d\n\n\t\t", score);
    system("pause");
}
void set_cursor_invisible(){
    CONSOLE_CURSOR_INFO cursor;
    cursor.bVisible = 0;
    cursor.dwSize = 1;
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorInfo(hOut, &cursor);
}
void print_score(){
    cp.x = 18;
    cp.y = 20;
    set_cursor_position();
    set_console_color( BLUE);
    printf("当前得分:%d\n\t\t\t", score);
}

代码逻辑就不讲了,我认为注释已经讲的比较清楚了。

运行结果:

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值