C++实现贪吃蛇

mattuy 2018年10月20日 238次浏览

注意,编译前源文件字符编码必须为GB2312/GBK。否则填充字符会出现异常。

编译时需要指定按C++11标准编译,为了支持结构体字面量的语法。
g++ -std=c++11 -o gluttonous-snake.exe ./source.cpp

代码

#include <cstdlib>
#include <conio.h>
#include <deque>
#include <iostream>
#include <time.h>
#include <windows.h>
//定义一次步进
typedef struct {
    //是否纵向移动
    bool virticle = false;
    //移动步长
    int offset = 0;
}Step;
//定义'成长礼包'类型
enum GiftType {
    //礼物,增加长度
    GIFT_GIFT = FOREGROUND_GREEN,
    //陷阱,减少长度
    GIFT_TRAP = FOREGROUND_GREEN | FOREGROUND_BLUE,
    //利剑,直接死亡
    GIFT_SWORD = FOREGROUND_RED
};
//定义移动区域。窗口宽度应为width的两倍,因为ansi字符宽度仅高度的1/2。
#define FACTORY_WIDTH 64
#define FACTORY_HEIGHT 36

//定义蛇身颜色
#define SNAKE_BODY_COLOR (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
using namespace std;
//控制台输出句柄
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//蛇身数据容器。
deque<COORD> snakeBody;
//死亡标记
bool dead = false;
//'成长礼包'
COORD gift, trap, sword;


//判定点是否在蛇身上
bool inSnake(COORD point) {
    if (snakeBody.size() == 0)
        return false;
    deque<COORD>::iterator iter = snakeBody.begin();
    while (iter != snakeBody.end()) {
        if (point.X == (*iter).X && point.Y == (*iter).Y)
            return true;
        ++iter;
    }
    return false;
}
//生成'成长礼包'
COORD createGift(GiftType color) {
    //统计递归调用次数
    static int count = 0;
    ++count;
    COORD point;
    srand(rand());
    point.X = rand() % FACTORY_WIDTH * 2;
    srand(rand());
    point.Y = rand() % FACTORY_HEIGHT;
    if (inSnake(point)) {
        return createGift(color);
    }
    else {
        SetConsoleCursorPosition(hOutput, point);
        SetConsoleTextAttribute(hOutput, color);
        cout << "█";
        SetConsoleTextAttribute(hOutput, SNAKE_BODY_COLOR);
        dead = count > 12 ? true : false;
        count = 0;
        return point;
    }//连礼包都没地方放了,死了算了
}
//初始化
void init() {
    system("cls");
    snakeBody.clear();
    srand(time(NULL));
    snakeBody.push_front({ 0, 0 });
    snakeBody.push_front({ 2, 0 });
    snakeBody.push_front({ 4, 0 });
    deque<COORD>::iterator iter = snakeBody.begin();
    while (iter != snakeBody.end()) {
        SetConsoleCursorPosition(hOutput, *iter);
        cout << "█";
        ++iter;
    }
    sword = createGift(GIFT_SWORD);
    trap = createGift(GIFT_TRAP);
    gift = createGift(GIFT_GIFT);
}
//步进
void stepOnece(Step step) {
    COORD head = snakeBody.front();
    (step.virticle ? head.Y : head.X) += step.offset;
    //死亡
    if (inSnake(head)
        || snakeBody.size() == 0
        || (head.X == sword.X && head.Y == sword.Y)
        || head.X < 0 || head.X >= FACTORY_WIDTH * 2
        || head.Y < 0 || head.Y >= FACTORY_HEIGHT) {
        dead = true;
        return;
    }
    snakeBody.push_front(head);
    SetConsoleCursorPosition(hOutput, head);
    cout << "█";

    int count;
    if (head.X == trap.X && head.Y == trap.Y) {
        count = 2;
        trap = createGift(GIFT_TRAP);
    }//陷阱,变短
    else if (head.X == gift.X && head.Y == gift.Y) {
        count = 0;
        gift = createGift(GIFT_GIFT);
    }//礼物,增长
    else
        count = 1;
    for (int i = 0; i < count; i++) {
        COORD back = snakeBody.back();
        SetConsoleCursorPosition(hOutput, back);
        printf("  ");
        snakeBody.pop_back();
        if (snakeBody.size() == 0) {
            dead = true;
            break;
        }
    }
}
//控制循环
void gameLoop() {
    Step curStep = { false, 2 };
    Step step = curStep;
    while (!dead) {
        for (int i = 0; i < 200; i++) {
            Sleep(1);
            if (!_kbhit())
                continue;
            char cmd;
            bool breakdown = true;
            cmd = _getch();
            switch (cmd) {
            case 'w':
                step.virticle = true;
                step.offset = -1;
                break;
            case 's':
                step.virticle = true;
                step.offset = 1;
                break;
            case 'a':
                step.virticle = false;
                step.offset = -2;
                break;
            case 'd':
                step.virticle = false;
                step.offset = 2;
                break;
            default:
                breakdown = false;
                break;
            }
            //禁止当前方向反方向步进
            if (step.virticle == curStep.virticle && step.offset * curStep.offset < 0)
                continue;
            else
                curStep = step;
            if (breakdown)
                break;
        }
        stepOnece(curStep);
    }
}
int main() {
    //设置缓冲区大小
    SMALL_RECT rect = { 0, 0, 10, 10 };
    SetConsoleWindowInfo(hOutput, true, &rect);
    SetConsoleScreenBufferSize(hOutput, { FACTORY_WIDTH * 2, FACTORY_HEIGHT });
    //设置窗口大小
    rect = { 0, 0, FACTORY_WIDTH * 2 - 1, FACTORY_HEIGHT - 1 };
    SetConsoleWindowInfo(hOutput, true, &rect);
    //设置窗口样式
    SetConsoleTextAttribute(hOutput, SNAKE_BODY_COLOR);
    CONSOLE_CURSOR_INFO cursorInfo = { 1, false };
    SetConsoleCursorInfo(hOutput, &cursorInfo);
    //规则介绍
    cout << "使用W、S、A、D控制方向,吃到绿色礼包长度增加,吃到黄色礼包时长度减短,"\
        "吃到红色礼包直接死亡。您也可以按CTRL + C退出游戏。按任意键开始游戏。" << endl;
    system("pause");
    //开始游戏
    while (true) {
        init();
        gameLoop();
        system("cls");
        SetConsoleCursorPosition(hOutput, { 0, 0 });
        printf("You has dead! Retry? (y/n)");
        char cmd;
        cin >> cmd;
        if (cmd != 'y')
            break;
        else
            dead = false;
    }
}