第一周作业

1.来源:github
https://github.com/silence1772/GreedySnake、
2.(1):运行环境:
WINDOWS 11
VS 2022
(2):运行结果:
image
image
image
(3):伸缩源代码:
controller.cpp

点击查看代码
#include <iostream>
#include <time.h>
#include <conio.h>
#include <windows.h>
#include "controller.h"
#include "tools.h"
#include "startinterface.h"
#include "map.h"
#include "snake.h"
#include "food.h"

void Controller::Start()//开始界面
{
    SetWindowSize(41, 32);//设置窗口大小
    SetColor(2);//设置开始动画颜色
    StartInterface *start = new StartInterface();//动态分配一个StartInterface类start
    start->Action();//开始动画
    delete start;//释放内存空间

    /*设置关标位置,并输出提示语,等待任意键输入结束*/
    SetCursorPosition(13, 26);
    std::cout << "Press any key to start... " ;
    SetCursorPosition(13, 27);
    system("pause");
}

void Controller::Select()//选择界面
{
    /*初始化界面选项*/
    SetColor(3);
    SetCursorPosition(13, 26);
    std::cout << "                          " ;
    SetCursorPosition(13, 27);
    std::cout << "                          " ;
    SetCursorPosition(6, 21);
    std::cout << "请选择游戏难度:" ;
    SetCursorPosition(6, 22);
    std::cout << "(上下键选择,回车确认)" ;
    SetCursorPosition(27, 22);
    SetBackColor();//第一个选项设置背景色以表示当前选中
    std::cout << "简单模式" ;
    SetCursorPosition(27, 24);
    SetColor(3);
    std::cout << "普通模式" ;
    SetCursorPosition(27, 26);
    std::cout << "困难模式" ;
    SetCursorPosition(27, 28);
    std::cout << "炼狱模式" ;
    SetCursorPosition(0, 31);
    score = 0;

    /*上下方向键选择模块*/
    int ch;//记录键入值
    key = 1;//记录选中项,初始选择第一个
    bool flag = false;//记录是否键入Enter键标记,初始置为否
    while ((ch = getch()))
    {
        switch (ch)//检测输入键
        {
        case 72://UP上方向键
            if (key > 1)//当此时选中项为第一项时,UP上方向键无效
            {
                switch (key)
                {
                case 2:
                    SetCursorPosition(27, 22);//给待选中项设置背景色
                    SetBackColor();
                    std::cout << "简单模式" ;

                    SetCursorPosition(27, 24);//将已选中项取消我背景色
                    SetColor(3);
                    std::cout << "普通模式" ;

                    --key;
                    break;
                case 3:
                    SetCursorPosition(27, 24);
                    SetBackColor();
                    std::cout << "普通模式" ;

                    SetCursorPosition(27, 26);
                    SetColor(3);
                    std::cout << "困难模式" ;

                    --key;
                    break;
                case 4:
                    SetCursorPosition(27, 26);
                    SetBackColor();
                    std::cout << "困难模式" ;

                    SetCursorPosition(27, 28);
                    SetColor(3);
                    std::cout << "炼狱模式" ;

                    --key;
                    break;
                }
            }
            break;

        case 80://DOWN下方向键
            if (key < 4)
            {
                switch (key)
                {
                case 1:
                    SetCursorPosition(27, 24);
                    SetBackColor();
                    std::cout << "普通模式" ;
                    SetCursorPosition(27, 22);
                    SetColor(3);
                    std::cout << "简单模式" ;

                    ++key;
                    break;
                case 2:
                    SetCursorPosition(27, 26);
                    SetBackColor();
                    std::cout << "困难模式" ;
                    SetCursorPosition(27, 24);
                    SetColor(3);
                    std::cout << "普通模式" ;

                    ++key;
                    break;
                case 3:
                    SetCursorPosition(27, 28);
                    SetBackColor();
                    std::cout << "炼狱模式" ;
                    SetCursorPosition(27, 26);
                    SetColor(3);
                    std::cout << "困难模式" ;

                    ++key;
                    break;
                }
            }
            break;

        case 13://Enter回车键
            flag = true;
            break;
        default://无效按键
            break;
        }
        if (flag) break;//输入Enter回车键确认,退出检查输入循环

        SetCursorPosition(0, 31);//将光标置于左下角,避免关标闪烁影响游戏体验
    }

    switch (key)//根据所选选项设置蛇的移动速度,speed值越小,速度越快
    {
    case 1:
        speed = 135;
        break;
    case 2:
        speed = 100;
        break;
    case 3:
        speed = 60;
        break;
    case 4:
        speed = 30;
        break;
    default:
        break;
    }
}

void Controller::DrawGame()//绘制游戏界面
{
    system("cls");//清屏

    /*绘制地图*/
    SetColor(3);
    Map *init_map = new Map();
    init_map->PrintInitmap();
    delete init_map;

    /*绘制侧边栏*/
    SetColor(3);
    SetCursorPosition(33, 1);
    std::cout << "Greedy Snake" ;
    SetCursorPosition(34, 2);
    std::cout << "贪吃蛇" ;
    SetCursorPosition(31, 4);
    std::cout << "难度:" ;
    SetCursorPosition(36, 5);
    switch (key)
    {
    case 1:
        std::cout << "简单模式" ;
        break;
    case 2:
        std::cout << "普通模式" ;
        break;
    case 3:
        std::cout << "困难模式" ;
        break;
    case 4:
        std::cout << "炼狱模式" ;
        break;
    default:
        break;
    }
    SetCursorPosition(31, 7);
    std::cout << "得分:" ;
    SetCursorPosition(37, 8);
    std::cout << "     0" ;
    SetCursorPosition(33, 13);
    std::cout << " 方向键移动" ;
    SetCursorPosition(33, 15);
    std::cout << " ESC键暂停" ;
}

int Controller::PlayGame()//游戏二级循环
{
    /*初始化蛇和食物*/
    Snake *csnake = new Snake();
    Food *cfood = new Food();
    SetColor(6);
    csnake->InitSnake();
    srand((unsigned)time(NULL));//设置随机数种子,如果没有 食物的出现位置将会固定
    cfood->DrawFood(*csnake);

    /*游戏循环*/
    while (csnake->OverEdge() && csnake->HitItself()) //判断是否撞墙或撞到自身,即是否还有生命
    {
        /*调出选择菜单*/
        if (!csnake->ChangeDirection()) //按Esc键时
        {
            int tmp = Menu();//绘制菜单,并得到返回值
            switch (tmp)
            {
            case 1://继续游戏
                break;

            case 2://重新开始
                delete csnake;
                delete cfood;
                return 1;//将1作为PlayGame函数的返回值返回到Game函数中,表示重新开始

            case 3://退出游戏
                delete csnake;
                delete cfood;
                return 2;//将2作为PlayGame函数的返回值返回到Game函数中,表示退出游戏

            default:
                break;
            }
        }

        if (csnake->GetFood(*cfood)) //吃到食物
        {
            csnake->Move();//蛇增长
            UpdateScore(1);//更新分数,1为分数权重
            RewriteScore();//重新绘制分数
            cfood->DrawFood(*csnake);//绘制新食物
        }
        else
        {
            csnake->NormalMove();//蛇正常移动
        }

        if (csnake->GetBigFood(*cfood)) //吃到限时食物
        {
            csnake->Move();
            UpdateScore(cfood->GetProgressBar()/5);//分数根据限时食物进度条确定
            RewriteScore();
        }

        if (cfood->GetBigFlag()) //如果此时有限时食物,闪烁它
        {
            cfood->FlashBigFood();
        }

        Sleep(speed);//制造蛇的移动效果
    }

    /*蛇死亡*/
    delete csnake;//释放分配的内存空间
    delete cfood;
    int tmp = GameOver();//绘制游戏结束界面,并返回所选项
    switch (tmp)
    {
    case 1:
        return 1;//重新开始
    case 2:
        return 2;//退出游戏
    default:
        return 2;
    }
}

void Controller::UpdateScore(const int& tmp)//更新分数
{
    score += key * 10 * tmp;//所得分数根据游戏难度及传人的参数tmp确定
}

void Controller::RewriteScore()//重绘分数
{
    /*为保持分数尾部对齐,将最大分数设置为6位,计算当前分数位数,将剩余位数用空格补全,再输出分数*/
    SetCursorPosition(37, 8);
    SetColor(11);
    int bit = 0;
    int tmp = score;
    while (tmp != 0)
    {
        ++bit;
        tmp /= 10;
    }
    for (int i = 0; i < (6 - bit); ++i)
    {
        std::cout << " " ;
    }
    std::cout << score ;
}

int Controller::Menu()//选择菜单
{
    /*绘制菜单*/
    SetColor(11);
    SetCursorPosition(32, 19);
    std::cout << "菜单:" ;
    Sleep(100);
    SetCursorPosition(34, 21);
    SetBackColor();
    std::cout << "继续游戏" ;
    Sleep(100);
    SetCursorPosition(34, 23);
    SetColor(11);
    std::cout << "重新开始" ;
    Sleep(100);
    SetCursorPosition(34, 25);
    std::cout << "退出游戏" ;
    SetCursorPosition(0, 31);

    /*选择部分*/
    int ch;
    int tmp_key = 1;
    bool flag = false;
    while ((ch = getch()))
    {
        switch (ch)
        {
        case 72://UP
            if (tmp_key > 1)
            {
                switch (tmp_key)
                {
                case 2:
                    SetCursorPosition(34, 21);
                    SetBackColor();
                    std::cout << "继续游戏" ;
                    SetCursorPosition(34, 23);
                    SetColor(11);
                    std::cout << "重新开始" ;

                    --tmp_key;
                    break;
                case 3:
                    SetCursorPosition(34, 23);
                    SetBackColor();
                    std::cout << "重新开始" ;
                    SetCursorPosition(34, 25);
                    SetColor(11);
                    std::cout << "退出游戏" ;

                    --tmp_key;
                    break;
                }
            }
            break;

        case 80://DOWN
            if (tmp_key < 3)
            {
                switch (tmp_key)
                {
                case 1:
                    SetCursorPosition(34, 23);
                    SetBackColor();
                    std::cout << "重新开始" ;
                    SetCursorPosition(34, 21);
                    SetColor(11);
                    std::cout << "继续游戏" ;

                    ++tmp_key;
                    break;
                case 2:
                    SetCursorPosition(34, 25);
                    SetBackColor();
                    std::cout << "退出游戏" ;
                    SetCursorPosition(34, 23);
                    SetColor(11);
                    std::cout << "重新开始" ;

                    ++tmp_key;
                    break;
                }
            }
            break;

        case 13://Enter
            flag = true;
            break;

        default:
            break;
        }

        if (flag)
        {
            break;
        }
        SetCursorPosition(0, 31);
    }

    if (tmp_key == 1) //选择继续游戏,则将菜单擦除
    {
        SetCursorPosition(32, 19);
        std::cout << "      " ;
        SetCursorPosition(34, 21);
        std::cout << "        ";
        SetCursorPosition(34, 23);
        std::cout << "        ";
        SetCursorPosition(34, 25);
        std::cout << "        ";
    }
    return tmp_key;
}

void Controller::Game()//游戏一级循环
{
    Start();//开始界面
    while (true)//游戏可视为一个死循环,直到退出游戏时循环结束
    {
        Select();//选择界面
        DrawGame();//绘制游戏界面
        int tmp = PlayGame();//开启游戏循环,当重新开始或退出游戏时,结束循环并返回值给tmp
        if (tmp == 1) //返回值为1时重新开始游戏
        {
            system("cls");
            continue;
        }
        else if (tmp == 2) //返回值为2时退出游戏
        {
            break;
        }
        else
        {
            break;
        }
    }
}

int Controller::GameOver()//游戏结束界面
{
    /*绘制游戏结束界面*/
    Sleep(500);
    SetColor(11);
    SetCursorPosition(10, 8);
    std::cout << "━━━━━━━━━━━━━━━━━━━━━━" ;
    Sleep(30);
    SetCursorPosition(9, 9);
    std::cout << " ┃               Game Over !!!              ┃" ;
    Sleep(30);
    SetCursorPosition(9, 10);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 11);
    std::cout << " ┃              很遗憾!你挂了              ┃" ;
    Sleep(30);
    SetCursorPosition(9, 12);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 13);
    std::cout << " ┃             你的分数为:                 ┃" ;
    SetCursorPosition(24, 13);
    std::cout << score ;
    Sleep(30);
    SetCursorPosition(9, 14);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 15);
    std::cout << " ┃   是否再来一局?                         ┃" ;
    Sleep(30);
    SetCursorPosition(9, 16);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 17);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 18);
    std::cout << " ┃    嗯,好的        不了,还是学习有意思  ┃" ;
    Sleep(30);
    SetCursorPosition(9, 19);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 20);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(10, 21);
    std::cout << "━━━━━━━━━━━━━━━━━━━━━━" ;

    Sleep(100);
    SetCursorPosition(12, 18);
    SetBackColor();
    std::cout << "嗯,好的" ;
    SetCursorPosition(0, 31);

    /*选择部分*/
    int ch;
    int tmp_key = 1;
    bool flag = false;
    while ((ch = getch()))
    {
        switch (ch)
        {
        case 75://LEFT
            if (tmp_key > 1)
            {
                SetCursorPosition(12, 18);
                SetBackColor();
                std::cout << "嗯,好的" ;
                SetCursorPosition(20, 18);
                SetColor(11);
                std::cout << "不了,还是学习有意思" ;
                --tmp_key;
            }
            break;

        case 77://RIGHT
            if (tmp_key < 2)
            {
                SetCursorPosition(20, 18);
                SetBackColor();
                std::cout << "不了,还是学习有意思" ;
                SetCursorPosition(12, 18);
                SetColor(11);
                std::cout << "嗯,好的" ;
                ++tmp_key;
            }
            break;

        case 13://Enter
            flag = true;
            break;

        default:
            break;
        }

        SetCursorPosition(0, 31);
        if (flag) {
            break;
        }
    }

    SetColor(11);
    switch (tmp_key)
    {
    case 1:
        return 1;//重新开始
    case 2:
        return 2;//退出游戏
    default:
        return 1;
    }
}
controller.h
点击查看代码
#ifndef CONTROLLER_H
#define CONTROLLER_H


class Controller
{
public:
    Controller() : speed(200), key(1), score(0) {}
    void Start();
    void Select();
    void DrawGame();
    int PlayGame();
    void UpdateScore(const int&);
    void RewriteScore();
    int Menu();
    void Game();
    int GameOver();
private:
    int speed;
    int key;
    int score;
};
#endif // CONTROLLER_H
food.cpp
点击查看代码
#include "food.h"
#include "tools.h"
#include <cstdlib>
#include <iostream>


void Food::DrawFood(Snake& csnake)//绘制食物
{
    /*利用rand函数获得坐标,并将其范围限制在2-29内,即在地图内,如果获得的坐标与蛇身重叠,则重新获取。
      同时每5颗食物就出现一颗限时食物*/
    while (true)
    {
        int tmp_x = rand() % 30;
        int tmp_y = rand() % 30;
        if(tmp_x < 2) tmp_x += 2;
        if(tmp_y < 2) tmp_y += 2;
        bool flag = false;
        for (auto& point : csnake.snake)
        {
            if ((point.GetX() == tmp_x && point.GetY() == tmp_y) || (tmp_x == big_x && tmp_y == big_y)) {
                flag = true;
                break;
            }
        }
        if (flag)
            continue;
        x = tmp_x;
        y = tmp_y;
        SetCursorPosition(x, y);
        SetColor(13);
        std::cout << "★" ;
        ++cnt;
        cnt %= 5;
        if (cnt == 0)
        {
            DrawBigFood(csnake);
        }
        break;
    }
}

void Food::DrawBigFood(Snake& csnake)//绘制限时食物
{
    SetCursorPosition(5, 0);
    SetColor(11);
    std::cout << "------------------------------------------" ;//进度条
    progress_bar = 42;
    while (true)
    {
        int tmp_x = rand() % 30;
        int tmp_y = rand() % 30;
        if(tmp_x < 2) tmp_x += 2;
        if(tmp_y < 2) tmp_y += 2;
        bool flag = false;
        for (auto& point : csnake.snake)
        {
            if ((point.GetX() == tmp_x && point.GetY() == tmp_y) || (tmp_x == x && tmp_y == y))
            {
                flag = true;
                break;
            }
        }
        if (flag)
            continue;

        big_x = tmp_x;
        big_y = tmp_y;
        SetCursorPosition(big_x, big_y);
        SetColor(18);
        std::cout << "■" ;
        big_flag = true;
        flash_flag = true;
        break;
    }
}

int Food::GetCnt()
{
    return cnt;
}

void Food::FlashBigFood()//闪烁限时食物
{
    SetCursorPosition(big_x, big_y);
    SetColor(18);
    if (flash_flag)
    {
        std::cout << "  " ;
        flash_flag = false;
    }
    else
    {
        std::cout << "■" ;
        flash_flag = true;
    }

    SetCursorPosition(26, 0);
    SetColor(11);
    for (int i = 42; i >= progress_bar; --i)//进度条缩短
        std::cout << "\b \b" ;
    --progress_bar;
    if (progress_bar == 0) {
        SetCursorPosition(big_x, big_y);
        std::cout << "  " ;
        big_flag = false;
        big_x = 0;
        big_y = 0;
    }
}

bool Food::GetBigFlag()
{
    return big_flag;
}

int Food::GetProgressBar()
{
    return progress_bar;
}

food.h
点击查看代码
#ifndef FOOD_H
#define FOOD_H

#include "snake.h"
class Snake;
class Food
{
public:
    Food() : cnt(0), flash_flag(false), big_flag(false), x(0), y(0), big_x(0), big_y(0), progress_bar(0) {}
    void DrawFood(Snake&);
    void DrawBigFood(Snake&);
    int GetCnt();
    void FlashBigFood();
    bool GetBigFlag();
    int GetProgressBar();
private:
    int cnt;
    bool flash_flag;//闪烁标记
    bool big_flag;//是否有限时食物标记
    int x, y;
    int big_x, big_y;
    int progress_bar;//限时食物进度条
    friend class Snake;
};
#endif // FOOD_H
main.cpp
点击查看代码
#include "controller.h"

int main()//程序入口
{
    Controller c;//声明一个Controller类
    c.Game();//整个游戏循环
    return 0;
}

GreedySnake.exe

map.cpp

点击查看代码
#include "controller.h"

#include "map.h"
#include <windows.h>

void Map::PrintInitmap()//绘制初始地图
{
    for (auto& point : initmap)
    {
        point.Print();
        Sleep(10);//调用Sleep函数可营造动画效果
    }
}
map.h
点击查看代码
#ifndef MAP_H
#define MAP_H

#include <vector>
#include "point.h"

class Map
{
public:
    Map()//默认构造函数,将正方形各点压入initmap
    {
        initmap.emplace_back(Point(1, 1));
        initmap.emplace_back(Point(2, 1));
        initmap.emplace_back(Point(3, 1));
        initmap.emplace_back(Point(4, 1));
        initmap.emplace_back(Point(5, 1));
        initmap.emplace_back(Point(6, 1));
        initmap.emplace_back(Point(7, 1));
        initmap.emplace_back(Point(8, 1));
        initmap.emplace_back(Point(9, 1));
        initmap.emplace_back(Point(10, 1));
        initmap.emplace_back(Point(11, 1));
        initmap.emplace_back(Point(12, 1));
        initmap.emplace_back(Point(13, 1));
        initmap.emplace_back(Point(14, 1));
        initmap.emplace_back(Point(15, 1));
        initmap.emplace_back(Point(16, 1));
        initmap.emplace_back(Point(17, 1));
        initmap.emplace_back(Point(18, 1));
        initmap.emplace_back(Point(19, 1));
        initmap.emplace_back(Point(20, 1));
        initmap.emplace_back(Point(21, 1));
        initmap.emplace_back(Point(22, 1));
        initmap.emplace_back(Point(23, 1));
        initmap.emplace_back(Point(24, 1));
        initmap.emplace_back(Point(25, 1));
        initmap.emplace_back(Point(26, 1));
        initmap.emplace_back(Point(27, 1));
        initmap.emplace_back(Point(28, 1));
        initmap.emplace_back(Point(29, 1));
        initmap.emplace_back(Point(30, 1));
        initmap.emplace_back(Point(1, 2));
        initmap.emplace_back(Point(30, 2));
        initmap.emplace_back(Point(1, 3));
        initmap.emplace_back(Point(30, 3));
        initmap.emplace_back(Point(1, 4));
        initmap.emplace_back(Point(30, 4));
        initmap.emplace_back(Point(1, 5));
        initmap.emplace_back(Point(30, 5));
        initmap.emplace_back(Point(1, 6));
        initmap.emplace_back(Point(30, 6));
        initmap.emplace_back(Point(1, 7));
        initmap.emplace_back(Point(30, 7));
        initmap.emplace_back(Point(1, 8));
        initmap.emplace_back(Point(30, 8));
        initmap.emplace_back(Point(1, 9));
        initmap.emplace_back(Point(30, 9));
        initmap.emplace_back(Point(1, 10));
        initmap.emplace_back(Point(30, 10));
        initmap.emplace_back(Point(1, 11));
        initmap.emplace_back(Point(30, 11));
        initmap.emplace_back(Point(1, 12));
        initmap.emplace_back(Point(30, 12));
        initmap.emplace_back(Point(1, 13));
        initmap.emplace_back(Point(30, 13));
        initmap.emplace_back(Point(1, 14));
        initmap.emplace_back(Point(30, 14));
        initmap.emplace_back(Point(1, 15));
        initmap.emplace_back(Point(30, 15));
        initmap.emplace_back(Point(1, 16));
        initmap.emplace_back(Point(30, 16));
        initmap.emplace_back(Point(1, 17));
        initmap.emplace_back(Point(30, 17));
        initmap.emplace_back(Point(1, 18));
        initmap.emplace_back(Point(30, 18));
        initmap.emplace_back(Point(1, 19));
        initmap.emplace_back(Point(30, 19));
        initmap.emplace_back(Point(1, 20));
        initmap.emplace_back(Point(30, 20));
        initmap.emplace_back(Point(1, 21));
        initmap.emplace_back(Point(30, 21));
        initmap.emplace_back(Point(1, 22));
        initmap.emplace_back(Point(30, 22));
        initmap.emplace_back(Point(1, 23));
        initmap.emplace_back(Point(30, 23));
        initmap.emplace_back(Point(1, 24));
        initmap.emplace_back(Point(30, 24));
        initmap.emplace_back(Point(1, 25));
        initmap.emplace_back(Point(30, 25));
        initmap.emplace_back(Point(1, 26));
        initmap.emplace_back(Point(30, 26));
        initmap.emplace_back(Point(1, 27));
        initmap.emplace_back(Point(30, 27));
        initmap.emplace_back(Point(1, 28));
        initmap.emplace_back(Point(30, 28));
        initmap.emplace_back(Point(1, 29));
        initmap.emplace_back(Point(30, 29));
        initmap.emplace_back(Point(1, 30));
        initmap.emplace_back(Point(2, 30));
        initmap.emplace_back(Point(3, 30));
        initmap.emplace_back(Point(4, 30));
        initmap.emplace_back(Point(5, 30));
        initmap.emplace_back(Point(6, 30));
        initmap.emplace_back(Point(7, 30));
        initmap.emplace_back(Point(8, 30));
        initmap.emplace_back(Point(9, 30));
        initmap.emplace_back(Point(10, 30));
        initmap.emplace_back(Point(11, 30));
        initmap.emplace_back(Point(12, 30));
        initmap.emplace_back(Point(13, 30));
        initmap.emplace_back(Point(14, 30));
        initmap.emplace_back(Point(15, 30));
        initmap.emplace_back(Point(16, 30));
        initmap.emplace_back(Point(17, 30));
        initmap.emplace_back(Point(18, 30));
        initmap.emplace_back(Point(19, 30));
        initmap.emplace_back(Point(20, 30));
        initmap.emplace_back(Point(21, 30));
        initmap.emplace_back(Point(22, 30));
        initmap.emplace_back(Point(23, 30));
        initmap.emplace_back(Point(24, 30));
        initmap.emplace_back(Point(25, 30));
        initmap.emplace_back(Point(26, 30));
        initmap.emplace_back(Point(27, 30));
        initmap.emplace_back(Point(28, 30));
        initmap.emplace_back(Point(29, 30));
        initmap.emplace_back(Point(30, 30));
    }
    void PrintInitmap();//绘制初始地图
private:
    std::vector<Point> initmap;//保存初始地图
    /*Map类可自定义多种地图,只需将表示地图的各个点保存在相应的map中,并在Snake类中增加相应判断撞墙函数即可
    std::vector<Point> map1;
    std::vector<Point> map2;
    */
};
#endif // MAP_H
point.cpp
点击查看代码
#include "point.h"
#include "tools.h"
#include <iostream>

void Point::Print()//输出方块
{
    SetCursorPosition(x, y);
    std::cout << "■" ;
}

void Point::PrintCircular()//输出圆形
{
    SetCursorPosition(x, y);
    std::cout << "●" ;
}

void Point::Clear()//清除输出
{
    SetCursorPosition(x, y);
    std::cout << "  " ;
}

void Point::ChangePosition(const int x, const int y)//改变坐标
{
    this->x = x;
    this->y = y;
}

point.h
点击查看代码
#ifndef POINT_H
#define POINT_H

class Point
{
public:
    Point(){}
    Point(const int x, const int y) : x(x), y(y) {}
    void Print();
    void PrintCircular();
    void Clear();
    void ChangePosition(const int x, const int y);
    bool operator== (const Point& point) { return (point.x == this->x) && (point.y == this->y); }
    int GetX(){ return this->x; }
    int GetY(){ return this->y; }
private:
    int x, y;
};
#endif // POINT_H
README.md
点击查看代码
# GreedySnake
c++经典项目贪吃蛇游戏控制台版,详细注释。
(点个star再走不急)
教程地址:http://blog.csdn.net/silence1772/article/details/55005008

## 游戏截图:
![](https://github.com/silence1772/GreedySnake/raw/master/shot01.jpg)
![](https://github.com/silence1772/GreedySnake/raw/master/shot02.jpg)
![](https://github.com/silence1772/GreedySnake/raw/master/shot03.gif)
![](https://github.com/silence1772/GreedySnake/raw/master/shot04.gif)

snake.cpp
点击查看代码
#include "snake.h"
#include <conio.h>
#include "tools.h"
#include <iostream>

void Snake::InitSnake()//初始化蛇
{
    for (auto& point : snake)
    {
        point.PrintCircular();
    }
}

void Snake::Move()//蛇增长
{
    switch (direction)
    {
    case Direction::UP:
        snake.emplace_back(Point(snake.back().GetX(), snake.back().GetY() - 1 ));
        break;
    case Direction::DOWN:
        snake.emplace_back(Point(snake.back().GetX(), snake.back().GetY() + 1 ));
        break;
    case Direction::LEFT:
        snake.emplace_back(Point(snake.back().GetX() - 1, snake.back().GetY() ));
        break;
    case Direction::RIGHT:
        snake.emplace_back(Point(snake.back().GetX() + 1, snake.back().GetY() ));
        break;
    default:
        break;
    }
    SetColor(14);
    snake.back().PrintCircular();
}

void Snake::NormalMove()//蛇正常移动,头增长,尾缩短
{
    Move();
    snake.front().Clear();
    snake.pop_front();
}

bool Snake::OverEdge()//超出边界
{
    return snake.back().GetX() < 30 &&
           snake.back().GetY() < 30 &&
           snake.back().GetX() > 1  &&
           snake.back().GetY() > 1;
}

bool Snake::HitItself()//撞到自身
{
    std::deque<Point>::size_type cnt = 1;
    Point *head = new Point(snake.back().GetX(), snake.back().GetY());//获得头部坐标
    for (auto& point : snake) //如果整条蛇中与蛇头不相同的坐标不等于蛇长,则意味着蛇头碰撞到自身
    {
        if ( !(point == *head) )
            ++cnt;
        else
            break;
    }
    delete head;
    if(cnt == snake.size())
        return true;
    else
        return false;
}

bool Snake::ChangeDirection()//改变方向
{
    char ch;
    if (kbhit())//kbhit函数返回值为两个,需注意
    {
        ch = getch();
        switch (ch)
        {
        case -32:
            ch = getch();
            switch (ch)
            {
            case 72:
                if (direction != Direction::DOWN)//如果方向与当前运动方向相反,无效
                    direction = Direction::UP;
                break;
            case 80:
                if (direction != Direction::UP)
                    direction = Direction::DOWN;
                break;
            case 75:
                if (direction != Direction::RIGHT)
                    direction = Direction::LEFT;
                break;
            case 77:
                if (direction != Direction::LEFT)
                    direction = Direction::RIGHT;
                break;
            default:
                break;
            }
            return true;

        case 27://ESC
            return false;

        default:
            return true;

        }
    }
    return true;
}

bool Snake::GetFood(const Food& cfood)
{
    if (snake.back().GetX() == cfood.x && snake.back().GetY() == cfood.y)
        return true;
    else
        return false;
}

bool Snake::GetBigFood(Food& cfood)
{
    if (snake.back().GetX() == cfood.big_x && snake.back().GetY() == cfood.big_y)
    {
        cfood.big_flag = false;
        cfood.big_x = 0;
        cfood.big_y = 0;
        SetCursorPosition(1, 0);
        std::cout << "                                                            " ;
        return true;
    }
    else
        return false;
}

snack.h
点击查看代码
#ifndef SNAKE_H
#define SNAKE_H

#include <deque>
#include "point.h"
#include "food.h"

class Food;
class Snake
{
public:
    enum Direction {UP, DOWN, LEFT, RIGHT};

    Snake() {
        snake.emplace_back(14, 8);
        snake.emplace_back(15, 8);
        snake.emplace_back(16, 8);
        direction = Direction::DOWN;
    }
    void InitSnake();
    void Move();
    void NormalMove();
    bool OverEdge();
    bool HitItself();
    bool ChangeDirection();
    bool GetFood(const Food&);
    bool GetBigFood(Food&);
private:
    std::deque<Point> snake;
    Direction direction;
    friend class Food;//将Food类置为友元,以便访问其私有元素
};
#endif // SNAKE_H

startinterface.cpp
点击查看代码
#include "startinterface.h"
#include <windows.h>


void StartInterface::PrintFirst()//蛇从左边出现到完全出现的过程
{
    for (auto& point : startsnake)
    {
        point.Print();
        Sleep(speed);
    }
}

void StartInterface::PrintSecond()//蛇从左向右移动的过程
{
    for (int i = 10; i != 40; ++i) //蛇头需要从10移动到40
    {
        /*计算蛇头的下一个位置,并将其压入startsnake中,绘制出来,将蛇尾去掉*/
        int j = ( ((i-2)%8) < 4 )?( 15 + (i-2)%8 ) : ( 21 - (i-2)%8 );
        startsnake.emplace_back( Point(i, j) );
        startsnake.back().Print();
        startsnake.front().Clear();
        startsnake.pop_front();
        Sleep(speed);
    }
}

void StartInterface::PrintThird()//蛇从接触右边到消失的过程
{
    while ( !startsnake.empty() || textsnake.back().GetX() < 33 ) //当蛇还没消失或文字没移动到指定位置
    {
        if ( !startsnake.empty() ) //如果蛇还没消失,继续移动
        {
            startsnake.front().Clear();
            startsnake.pop_front();
        }
        ClearText();//清除已有文字
        PrintText();//绘制更新位置后的文字
        Sleep(speed);
    }
}

void StartInterface::PrintText()
{
    for (auto& point : textsnake)
    {
        if (point.GetX() >= 0)
            point.Print();
    }
}

void StartInterface::ClearText()
{
    for (auto& point : textsnake) //清除的同时将文字整体向右移动一格
    {
        if (point.GetX() >= 0)
            point.Clear();
        point.ChangePosition(point.GetX() + 1, point.GetY());
    }
}

void StartInterface::Action()
{
    PrintFirst();
    PrintSecond();
    PrintThird();
}

startinterface.h
点击查看代码
#ifndef STRATINTERFACE_H
#define STARTINTERFACE_H

#include <deque>
#include <vector>
#include "point.h"

class StartInterface
{
public:
    StartInterface() : speed(35) {
        startsnake.emplace_back(Point(0,14));//Éß
        startsnake.emplace_back(Point(1,14));
        startsnake.emplace_back(Point(2,15));
        startsnake.emplace_back(Point(3,16));
        startsnake.emplace_back(Point(4,17));
        startsnake.emplace_back(Point(5,18));
        startsnake.emplace_back(Point(6,17));
        startsnake.emplace_back(Point(7,16));
        startsnake.emplace_back(Point(8,15));
        startsnake.emplace_back(Point(9,14));

        textsnake.emplace_back(Point(-26, 14));//S
        textsnake.emplace_back(Point(-25, 14));
        textsnake.emplace_back(Point(-27, 15));
        textsnake.emplace_back(Point(-26, 16));
        textsnake.emplace_back(Point(-25, 17));
        textsnake.emplace_back(Point(-27, 18));
        textsnake.emplace_back(Point(-26, 18));

        textsnake.emplace_back(Point(-23, 14));//N
        textsnake.emplace_back(Point(-23, 15));
        textsnake.emplace_back(Point(-23, 16));
        textsnake.emplace_back(Point(-23, 17));
        textsnake.emplace_back(Point(-23, 18));
        textsnake.emplace_back(Point(-22, 15));
        textsnake.emplace_back(Point(-21, 16));
        textsnake.emplace_back(Point(-20, 17));
        textsnake.emplace_back(Point(-19, 14));
        textsnake.emplace_back(Point(-19, 15));
        textsnake.emplace_back(Point(-19, 16));
        textsnake.emplace_back(Point(-19, 17));
        textsnake.emplace_back(Point(-19, 18));

        textsnake.emplace_back(Point(-17, 18));//A
        textsnake.emplace_back(Point(-16, 17));
        textsnake.emplace_back(Point(-15, 16));
        textsnake.emplace_back(Point(-14, 15));
        textsnake.emplace_back(Point(-14, 16));
        textsnake.emplace_back(Point(-13, 14));
        textsnake.emplace_back(Point(-13, 16));
        textsnake.emplace_back(Point(-12, 15));
        textsnake.emplace_back(Point(-12, 16));
        textsnake.emplace_back(Point(-11, 16));
        textsnake.emplace_back(Point(-10, 17));
        textsnake.emplace_back(Point(-9, 18));

        textsnake.emplace_back(Point(-7, 14));//K
        textsnake.emplace_back(Point(-7, 15));
        textsnake.emplace_back(Point(-7, 16));
        textsnake.emplace_back(Point(-7, 17));
        textsnake.emplace_back(Point(-7, 18));
        textsnake.emplace_back(Point(-6, 16));
        textsnake.emplace_back(Point(-5, 15));
        textsnake.emplace_back(Point(-5, 17));
        textsnake.emplace_back(Point(-4, 14));
        textsnake.emplace_back(Point(-4, 18));

        textsnake.emplace_back(Point(-2, 14));//E
        textsnake.emplace_back(Point(-2, 15));
        textsnake.emplace_back(Point(-2, 16));
        textsnake.emplace_back(Point(-2, 17));
        textsnake.emplace_back(Point(-2, 18));
        textsnake.emplace_back(Point(-1, 14));
        textsnake.emplace_back(Point(-1, 16));
        textsnake.emplace_back(Point(-1, 18));
        textsnake.emplace_back(Point(0, 14));
        textsnake.emplace_back(Point(0, 16));
        textsnake.emplace_back(Point(0, 18));
    }
    void PrintFirst();
    void PrintSecond();
    void PrintThird();
    void PrintText();
    void ClearText();
    void Action();
private:
    std::deque<Point> startsnake;//开始动画中的蛇
    std::vector<Point> textsnake;//开始动画中的文字
    int speed;//动画的速度
};
#endif // STRATINTERFACE_H

tools.cpp
点击查看代码
#include "tools.h"
#include <windows.h>
#include <stdio.h>

void SetWindowSize(int cols, int lines)//设置窗口大小
{
    system("title 贪吃蛇");//设置窗口标题
    char cmd[30];
    sprintf(cmd, "mode con cols=%d lines=%d", cols * 2, lines);//一个图形■占两个字符,故宽度乘以2
    system(cmd);//system(mode con cols=88 lines=88)设置窗口宽度和高度
}

void SetCursorPosition(const int x, const int y)//设置光标位置
{
    COORD position;
    position.X = x * 2;
    position.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
}

void SetColor(int colorID)//设置文本颜色
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colorID);
}

void SetBackColor()//设置文本背景色
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
                            FOREGROUND_BLUE |
                            BACKGROUND_BLUE |
                            BACKGROUND_GREEN |
                            BACKGROUND_RED );
}
tools.h
点击查看代码
#ifndef TOOLS_H
#define TOOLS_H


void SetWindowSize(int cols, int lines);
void SetCursorPosition(const int x, const int y);
void SetColor(int colorID);
void SetBackColor();

#endif // TOOLS_H

3.重要问题列表
(1)食物生成概率不均等:原代码使用 rand() % 30,再通过if判断将边界外的坐标强行拉回边界内(如if(tmp_x < 2) tmp_x += 2),导致靠近边界的格子生成食物的概率远高于中间区域。
(2)返回主菜单时动画丢失:在游戏结束选择“返回主菜单”后,原本开头的"SNAKE"字符画动画会消失,只留下选项文字,界面显得空洞。
(3)光标闪烁:游戏运行过程中,控制台的光标一直在闪烁,破坏了游戏画面的连贯性。
(4)难度梯度不合理:原版有4个难度,但最高速度和最低速度差异过大,且中途无法变化,后期缺乏挑战性或前期过于无聊。
改善:
(1)修改食物的随机生成算法,将随机范围直接限定在有效的游戏地图区域内(例如地图大小为 30x30,边界占2格,则有效区域为 2~28)。
(2)在Controller类的游戏循环中,检测到返回主菜单的信号时,重新调用绘制开场动画的函数。
(3)调用Windows API函数 SetConsoleCursorInfo 来隐藏控制台的光标。
(4)取消固定的多个难度档位,改为“加速模式”。蛇的移动速度随着吃到的食物数量(即得分)增加而逐渐加快,直到达到一个速度上限。
4.改进后代码:
main.cpp

点击查看代码
#include "controller.h"

int main()
{
    HideCursor();  // 新增:游戏开始前隐藏光标
    Controller c;
    c.Game();
    ShowCursor();  // 游戏结束后恢复光标
    return 0;
}
controller.h
点击查看代码
#ifndef CONTROLLER_H
#define CONTROLLER_H

class Controller
{
public:
    Controller() : speed(250), key(1), score(0), maxScore{0} {}
    void Start();
    void Select();
    void DrawGame();
    int PlayGame();
    void UpdateScore(const int&);
    void RewriteScore();
    int Menu();
    void Game();
    int GameOver();
    void ShowCursor(bool showFlag);  // 新增:光标控制函数声明
private:
    int speed;
    int key;
    int score;
    int maxScore[5];  // 新增:记录四个难度下的最高分
    const int INIT_SPEED = 250;  // 新增:初始速度
    const int MIN_SPEED = 50;    // 新增:最快速度
};
#endif // CONTROLLER_H
controller.cpp
点击查看代码
#include <iostream>
#include <time.h>
#include <conio.h>
#include <windows.h>
#include <algorithm>
#include "controller.h"
#include "tools.h"
#include "startinterface.h"
#include "map.h"
#include "snake.h"
#include "food.h"

using namespace std;

// 新增:光标控制函数
void ShowConsoleCursor(bool showFlag)
{
    HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(out, &cursorInfo);
    cursorInfo.bVisible = showFlag;
    SetConsoleCursorInfo(out, &cursorInfo);
}

void Controller::Start()
{
    SetWindowSize(41, 32);
    SetColor(2);
    StartInterface *start = new StartInterface();
    start->Action();
    delete start;

    SetCursorPosition(13, 26);
    cout << "Press any key to start... ";
    SetCursorPosition(13, 27);
    system("pause");
}

void Controller::Select()
{
    /* 初始化界面选项 */
    SetColor(3);
    SetCursorPosition(13, 26);
    cout << "                          ";
    SetCursorPosition(13, 27);
    cout << "                          ";
    SetCursorPosition(6, 21);
    cout << "请选择游戏难度:";
    SetCursorPosition(6, 22);
    cout << "(上下键选择,回车确认)";
    SetCursorPosition(27, 22);
    SetBackColor();
    cout << "简单模式";
    SetCursorPosition(27, 24);
    SetColor(3);
    cout << "普通模式";
    SetCursorPosition(27, 26);
    cout << "困难模式";
    SetCursorPosition(27, 28);
    cout << "炼狱模式";
    SetCursorPosition(0, 31);
    
    // 显示各难度历史最高分
    SetCursorPosition(40, 22);
    cout << "最高分:" << maxScore[1];
    SetCursorPosition(40, 24);
    cout << "最高分:" << maxScore[2];
    SetCursorPosition(40, 26);
    cout << "最高分:" << maxScore[3];
    SetCursorPosition(40, 28);
    cout << "最高分:" << maxScore[4];
    
    score = 0;

    /* 上下方向键选择模块 */
    int ch;
    key = 1;
    bool flag = false;
    while ((ch = getch()))
    {
        switch (ch)
        {
        case 72:
            if (key > 1)
            {
                switch (key)
                {
                case 2:
                    SetCursorPosition(27, 22);
                    SetBackColor();
                    cout << "简单模式";

                    SetCursorPosition(27, 24);
                    SetColor(3);
                    cout << "普通模式";
                    --key;
                    break;
                case 3:
                    SetCursorPosition(27, 24);
                    SetBackColor();
                    cout << "普通模式";

                    SetCursorPosition(27, 26);
                    SetColor(3);
                    cout << "困难模式";
                    --key;
                    break;
                case 4:
                    SetCursorPosition(27, 26);
                    SetBackColor();
                    cout << "困难模式";

                    SetCursorPosition(27, 28);
                    SetColor(3);
                    cout << "炼狱模式";
                    --key;
                    break;
                }
            }
            break;

        case 80:
            if (key < 4)
            {
                switch (key)
                {
                case 1:
                    SetCursorPosition(27, 24);
                    SetBackColor();
                    cout << "普通模式";
                    SetCursorPosition(27, 22);
                    SetColor(3);
                    cout << "简单模式";
                    ++key;
                    break;
                case 2:
                    SetCursorPosition(27, 26);
                    SetBackColor();
                    cout << "困难模式";
                    SetCursorPosition(27, 24);
                    SetColor(3);
                    cout << "普通模式";
                    ++key;
                    break;
                case 3:
                    SetCursorPosition(27, 28);
                    SetBackColor();
                    cout << "炼狱模式";
                    SetCursorPosition(27, 26);
                    SetColor(3);
                    cout << "困难模式";
                    ++key;
                    break;
                }
            }
            break;

        case 13:
            flag = true;
            break;
        default:
            break;
        }

        // 更新难度对应的速度
        switch (key)
        {
        case 1:
            speed = 250;  // 简单模式
            break;
        case 2:
            speed = 200;  // 普通模式
            break;
        case 3:
            speed = 150;  // 困难模式
            break;
        case 4:
            speed = 100;  // 炼狱模式
            break;
        }

        if (flag)
            break;
    }
}

void Controller::DrawGame()
{
    system("cls");
    SetCursorPosition(1, 1);
    SetColor(11);
    cout << "得分:";
    SetCursorPosition(7, 1);
    SetColor(14);
    cout << score;
    
    // 显示当前难度最高分
    SetCursorPosition(15, 1);
    SetColor(11);
    cout << "最高分:";
    SetCursorPosition(23, 1);
    SetColor(14);
    cout << maxScore[key];
    
    SetCursorPosition(30, 1);
    SetColor(12);
    cout << "速度:" << 300 - speed;
}

int Controller::PlayGame()
{
    srand((unsigned)time(NULL));
    int flag = 0;
    int time = 200;

    Snake *csnake = new Snake();  // 动态分配Snake类csnake
    Food *cfood = new Food();      // 动态分配Food类cfood
    csnake->InitSnake();           // 初始化蛇
    cfood->DrawFood(*csnake);      // 产生食物

    while (csnake->GetAlive())      // 如果蛇是活的,进入循环
    {
        if (_kbhit())               // 如果有键盘输入
        {
            int ch = _getch();
            if (ch == 224)          // 如果是方向键
            {
                ch = _getch();
                csnake->ChangeDirection(ch);
            }
            else if (ch == 27)      // 如果是ESC键
            {
                flag = Menu();
                if (flag == 1)       // 返回主菜单
                {
                    delete csnake;
                    delete cfood;
                    return 1;
                }
                else if (flag == 2)  // 重新开始
                {
                    delete csnake;
                    delete cfood;
                    return 0;
                }
                else if (flag == 3)  // 继续游戏
                {
                    DrawGame();
                }
            }
        }

        if (GetTickCount() - time > (unsigned int)speed)
        {
            time = GetTickCount();
            csnake->Move();

            // 判断是否吃到食物
            if (csnake->GetFood(*cfood))
            {
                csnake->Move();  // 蛇增长
                UpdateScore(1);  // 更新分数
                RewriteScore();  // 重新绘制分数
                
                // 动态加速机制:分数每增加20分,速度加快5ms
                if (key == 4 && speed > MIN_SPEED)
                {
                    speed -= 5;
                    if (speed < MIN_SPEED)
                        speed = MIN_SPEED;
                }
                
                cfood->DrawFood(*csnake);  // 绘制新食物
            }
            csnake->DrawSnake();  // 绘制蛇
        }
    }

    // 更新最高分
    maxScore[key] = max(maxScore[key], score);

    delete csnake;
    delete cfood;
    int tmp = GameOver();
    if (tmp == 1)
        return 1;
    else if (tmp == 2)
        return 0;
    else
        return 2;
}

void Controller::UpdateScore(const int &weight)
{
    score += 10 * weight;
}

void Controller::RewriteScore()
{
    SetCursorPosition(7, 1);
    SetColor(14);
    cout << "    ";  // 清空原分数
    SetCursorPosition(7, 1);
    cout << score;
    
    // 更新最高分显示
    if (score > maxScore[key])
    {
        maxScore[key] = score;
        SetCursorPosition(23, 1);
        SetColor(14);
        cout << maxScore[key];
    }
}

int Controller::Menu()
{
    SetColor(3);
    SetCursorPosition(6, 5);
    cout << "******************************";
    SetCursorPosition(6, 6);
    cout << "*        游戏暂停            *";
    SetCursorPosition(6, 7);
    cout << "*                            *";
    SetCursorPosition(6, 8);
    cout << "*    1.返回主菜单             *";
    SetCursorPosition(6, 9);
    cout << "*    2.重新开始               *";
    SetCursorPosition(6, 10);
    cout << "*    3.继续游戏               *";
    SetCursorPosition(6, 11);
    cout << "*                            *";
    SetCursorPosition(6, 12);
    cout << "******************************";

    char ch = _getch();
    switch (ch)
    {
    case '1':
        return 1;
    case '2':
        return 2;
    case '3':
        return 3;
    default:
        return 3;
    }
}

void Controller::Game()
{
    Start();  // 开场动画
    while (true)
    {
        Select();      // 难度选择
        DrawGame();    // 绘制游戏界面
        int tmp = PlayGame();  // 开始游戏
        if (tmp == 1)
        {
            system("cls");
            // 修复:返回主菜单时重新绘制开场动画
            StartInterface* start_t = new StartInterface();
            start_t->clear();  // 清空蛇的动画部分
            SetColor(2);
            start_t->PrintThird();  // 只绘制SNAKE文字动画
            delete start_t;
            continue;
        }
        else if (tmp == 2)
        {
            break;
        }
        else
        {
            break;
        }
    }
}

int Controller::GameOver()
{
    SetCursorPosition(6, 5);
    SetColor(4);
    cout << "******************************";
    SetCursorPosition(6, 6);
    cout << "*        游戏结束            *";
    SetCursorPosition(6, 7);
    cout << "*                            *";
    SetCursorPosition(6, 8);
    cout << "*    得分:" << score << "                 *";
    SetCursorPosition(6, 9);
    cout << "*                            *";
    SetCursorPosition(6, 10);
    cout << "*    1.返回主菜单             *";
    SetCursorPosition(6, 11);
    cout << "*    2.重新开始               *";
    SetCursorPosition(6, 12);
    cout << "*    3.退出游戏               *";
    SetCursorPosition(6, 13);
    cout << "******************************";

    char ch = _getch();
    switch (ch)
    {
    case '1':
        return 1;
    case '2':
        return 2;
    default:
        return 3;
    }
}
food.cpp
点击查看代码
#include "food.h"
#include "tools.h"
#include <stdlib.h>
#include <time.h>

Food::Food()
{
    big_flag = false;
    big_x = 0;
    big_y = 0;
    progress_bar = 40;
    flash_flag = true;
}

void Food::DrawFood(Snake &snake)
{
    int tmp_x, tmp_y;
    bool flag;

    srand((unsigned)time(NULL));

    do
    {
        flag = true;
        // 修复:食物生成概率不均问题
        // 原代码:int tmp_x = rand() % 30; if(tmp_x < 2) tmp_x += 2;
        // 新代码:直接限定在有效区域内(2-27)
        tmp_x = rand() % 26 + 2;  // 2到27之间
        tmp_y = rand() % 26 + 2;  // 每个格子概率完全相等

        Node *p = snake.GetNode();
        while (p->next != NULL)
        {
            if (p->x == tmp_x && p->y == tmp_y)
                flag = false;
            p = p->next;
        }
    } while (flag == false);

    x = tmp_x;
    y = tmp_y;

    SetCursorPosition(x, y);
    // 修复:颜色值控制在0-15范围内
    SetColor(12);  // 使用有效的红色(12)
    cout << "●";
    SetColor(7);   // 恢复默认颜色
}

void Food::DrawBigFood(Snake &snake)
{
    // 限时食物生成逻辑
    if (!big_flag)
    {
        int tmp_x, tmp_y;
        bool flag;

        do
        {
            flag = true;
            tmp_x = rand() % 26 + 2;
            tmp_y = rand() % 26 + 2;

            Node *p = snake.GetNode();
            while (p->next != NULL)
            {
                if (p->x == tmp_x && p->y == tmp_y)
                    flag = false;
                p = p->next;
            }
        } while (flag == false);

        big_x = tmp_x;
        big_y = tmp_y;
        big_flag = true;
        progress_bar = 40;
    }
}

void Food::FlashBigFood()
{
    if (big_flag)
    {
        SetCursorPosition(big_x, big_y);
        // 修复:颜色值从18改为11(青色)
        SetColor(11);
        if (flash_flag)
        {
            cout << "  ";
            flash_flag = false;
        }
        else
        {
            cout << "●";
            flash_flag = true;
        }

        // 进度条显示
        SetCursorPosition(26, 0);
        SetColor(11);
        for (int i = 42; i >= progress_bar; --i)
            cout << "\b \b";
        --progress_bar;
        
        if (progress_bar == 0)
        {
            SetCursorPosition(big_x, big_y);
            cout << "  ";
            big_flag = false;
            big_x = 0;
            big_y = 0;
        }
    }
}
startinterface.cpp
点击查看代码
#include "startinterface.h"
#include "tools.h"
#include <iostream>
#include <windows.h>
using namespace std;

StartInterface::StartInterface()
{
    // 初始化蛇的位置
    for (int i = 0; i < 11; ++i)
    {
        points.push_back(Point(11 - i, 5));
    }

    for (int i = 0; i < 9; ++i)
    {
        char ch = "SNAKE GAME"[i];
        for (int j = 0; j < 5; ++j)
        {
            word.push_back(Point(3 * j + 15 + i * 6, 15 + j));
        }
    }
}

void StartInterface::Action()
{
    // 第一阶段:蛇从左边出现
    for (int i = 0; i < 12; ++i)
    {
        SetCursorPosition(points.back().GetX() + i, points.back().GetY());
        cout << "■";
        Sleep(50);
    }

    // 第二阶段:蛇整体移动
    for (int i = 0; i < 18; ++i)
    {
        // 移动蛇头
        SetCursorPosition(points.front().GetX() + 1, points.front().GetY());
        cout << "■";
        // 删除蛇尾
        SetCursorPosition(points.back().GetX(), points.back().GetY());
        cout << "  ";

        // 更新蛇的位置
        points.pop_back();
        points.push_front(Point(points.front().GetX() + 1, points.front().GetY()));

        Sleep(100);
    }

    // 第三阶段:文字动画
    PrintThird();
}

void StartInterface::PrintThird()
{
    // 打印SNAKE文字
    for (int i = 0; i < 5; ++i)
    {
        SetCursorPosition(15, 14 + i);
        cout << "S    N    A    K    E    G    A    M    E";
        Sleep(100);
    }
}

// 新增:清空蛇的动画,只保留文字动画
void StartInterface::clear()
{
    this->points.clear();
}
tools.h
点击查看代码
#ifndef TOOLS_H
#define TOOLS_H

#include <windows.h>
#include <iostream>
using namespace std;

// 设置光标位置
inline void SetCursorPosition(int x, int y)
{
    COORD coord = { (SHORT)x, (SHORT)y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

// 设置文字颜色
inline void SetColor(int color)
{
    // 修复:颜色值校验,确保在0-15范围内
    if (color < 0 || color > 15)
        color = 7;  // 默认白色
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}

// 设置背景色
inline void SetBackColor()
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
        BACKGROUND_INTENSITY |
        BACKGROUND_BLUE |
        BACKGROUND_GREEN |
        BACKGROUND_RED);
}

// 设置窗口大小
inline void SetWindowSize(int width, int height)
{
    char cmd[64];
    sprintf_s(cmd, sizeof(cmd), "mode con cols=%d lines=%d", width, height);
    system(cmd);
}

// 新增:隐藏光标
inline void HideCursor()
{
    HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(out, &cursorInfo);
    cursorInfo.bVisible = false;
    SetConsoleCursorInfo(out, &cursorInfo);
}

// 新增:显示光标
inline void ShowCursor()
{
    HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(out, &cursorInfo);
    cursorInfo.bVisible = true;
    SetConsoleCursorInfo(out, &cursorInfo);
}

#endif // TOOLS_H
5.截图:

image

6.总结:
难点:从GitHub拉下来的老项目往往因为编译器版本(VS、GCC)、编码格式(UTF-8 vs GBK)、以及Windows/Linux API差异(如sprintf_s的安全检查)导致编译失败。
耗时点:逆向工程不仅仅是改bug,必须要看懂作者的思路。比如“食物概率”那个Bug时,如果只看代码表面,很容易忽略if语句导致的概率偏差。
思考:能跑的代码不一定很好,作者完成了基本功能,但是有很多BUG在其中,需要我们进行优化,改进。

posted @ 2026-03-10 20:22  lk1111  阅读(6)  评论(0)    收藏  举报