【C/C++】10分钟教你用C++写一个贪吃蛇附带AI功能(附源代码详解和下载)(下)

简介: 【C/C++】10分钟教你用C++写一个贪吃蛇附带AI功能(附源代码详解和下载)

04 贪吃蛇类

定义贪吃蛇的移动,打印,吃食物等等。这节课我们暂时不讨论AI功能,先把手动操作的贪吃蛇做了跑起来,下节课再做AI功能的介绍。该类大体如下:

微信图片_20220421150411.jpg

4.1 成员变量

成员变量m_direction记录每次移动的方向。m_is_alive记录贪吃蛇是否还活着。m_coordinate则是贪吃蛇身体坐标的记录。贪吃蛇是一节一节的,整条蛇必然是由许多节组成的。因此用了一个vector来存储蛇身,每节类型是COORDINATE结构体的。

4.2 默认构造函数

默认构造函数Snake()里面主要是做了初始贪吃蛇的生成,以及移动方向的定义等。初始的蛇为3节。在中间位置,向上移动。代码如下:

1        //移动方向向上
 2    m_direction = 1;
 3    m_is_alive = true;
 4    COORDINATE snake_head;
 5    //蛇头生成位置
 6    snake_head.x = GameSetting::window_width / 2 - 15;
 7    snake_head.y = GameSetting::window_height / 2;
 8
 9    this->m_coordinate.push_back(snake_head);
10    snake_head.y++;
11    this->m_coordinate.push_back(snake_head);
12    snake_head.y++;
13    this->m_coordinate.push_back(snake_head); //初始蛇身长度三节

4.3 监听键盘

监听键盘用了C里面的一个库函数。_kbhit()非阻塞函数,可以不断监听键盘的情况从而不产生阻塞。有键盘按下的时候,就获取按下的键盘是哪个。然后做出相应的变化,其实是方向的调整。需要注意的是,当我们的蛇往上走的时候,按下方向的键,我们是不做处理的。其它方向一样。还有一个调整游戏速度的,speed是休眠时间,speed越小,速度越快。反之速度越慢。

1    //监听键盘
 2void listen_key_borad()
 3{
 4    char ch;
 5
 6    if (_kbhit())                    //kbhit 非阻塞函数 
 7    {
 8        ch = _getch();    //使用 getch 函数获取键盘输入 
 9        switch (ch)
10        {
11        case 'w':
12        case 'W':
13            if (this->m_direction == DOWN)
14                break;
15            this->m_direction = UP;
16            break;
17        case 's':
18        case 'S':
19            if (this->m_direction == UP)
20                break;
21            this->m_direction = DOWN;
22            break;
23        case 'a':
24        case 'A':
25            if (this->m_direction == RIGHT)
26                break;
27            this->m_direction = LEFT;
28            break;
29        case 'd':
30        case 'D':
31            if (this->m_direction == LEFT)
32                break;
33            this->m_direction = RIGHT;
34            break;
35        case '+':
36            if (speed >= 25)
37            {
38                speed -= 25;
39            }
40            break;
41        case '-':
42            if (speed < 250)
43            {
44                speed += 25;
45            }
46            break;
47        }
48    }
49}

4.4 移动贪吃蛇

移动贪吃蛇,我们用了一个方向变量,在监听键盘的时候获取移动的方向,然后在根据方向移动贪吃蛇的蛇头。这里的移动我们是这样处理的,首先,贪吃蛇每移动一次,需要改变的只有蛇头和蛇尾两节。我们只需要把新的蛇头插进去,最后再画出来就可以了。至于蛇尾,如果我们不删除蛇尾的话,蛇会不断变长的。因此我们的做法是:吃到食物的时候插入蛇头而不删除蛇尾,没有吃到食物的时候插入蛇头同时删除蛇尾。这样就完美搞定了。

1    //移动贪吃蛇
 2void move_snake()
 3{
 4    //监听键盘
 5    listen_key_borad();
 6    //蛇头
 7    COORDINATE head = m_coordinate[0];
 8    //direction方向:1 上  2 下  3 左  4 右
 9    switch (this->m_direction)
10    {
11    case UP:
12        head.y--;
13        break;
14    case DOWN:
15        head.y++;
16        break;
17    case LEFT:
18        head.x--;
19        break;
20    case RIGHT:
21        head.x++;
22        break;
23    }
24    //插入移动后新的蛇头。是否删除蛇尾,在后续吃到食物判断那里做
25    m_coordinate.insert(m_coordinate.begin(), head);
26}

4.5 是否吃到食物

判断是否吃到食物,就是看看蛇头的坐标等不等于食物的坐标。如果等于,就重新生成食物,不删除蛇尾,蛇变长一节。不等于,就删除蛇尾,蛇长不变。

1bool is_eat_food(Food & f)
 2{
 3    //获取食物坐标
 4    COORDINATE food_coordinate = f.GetFoodCoordinate();
 5    //吃到食物,食物重新生成,不删除蛇尾
 6    if (m_coordinate[HEAD].x == food_coordinate.x && m_coordinate[HEAD].y == food_coordinate.y)
 7    {
 8        f.RandomXY(m_coordinate);
 9        return true;
10    }
11    else
12    {
13        //没有吃到食物,删除蛇尾
14        m_coordinate.erase(m_coordinate.end() - 1);
15        return false;
16    }
17}

4.6判断蛇是否还存活

判断蛇是否GG,主要是看是否超出边界,是否碰到自己身体其他部分。

1//判断贪吃蛇死了没
 2bool snake_is_alive()
 3{
 4    if (m_coordinate[HEAD].x <= 0 ||
 5        m_coordinate[HEAD].x >= GameSetting::window_width - 29 ||
 6        m_coordinate[HEAD].y <= 0 ||
 7        m_coordinate[HEAD].y >= GameSetting::window_height - 1)
 8    {
 9        //超出边界
10        m_is_alive = false;
11        return m_is_alive;
12    }
13    //和自己碰到一起
14    for (unsigned int i = 1; i < m_coordinate.size(); i++)
15    {
16        if (m_coordinate[i].x == m_coordinate[HEAD].x && m_coordinate[i].y == m_coordinate[HEAD].y)
17        {
18            m_is_alive = false;
19            return m_is_alive;
20        }
21    }
22    m_is_alive = true;
23
24    return m_is_alive;
25}

4.7 画出贪吃蛇

画出贪吃蛇比较简单,gotoxy到身体的每一节,然后cout就行。在此之前设置了颜色为浅绿色。

1//画出贪吃蛇
 2void draw_snake()
 3{
 4    //设置颜色为浅绿色
 5    setColor(10, 0);
 6    for (unsigned int i = 0; i < this->m_coordinate.size(); i++)
 7    {
 8        gotoxy(m_coordinate[i].x, m_coordinate[i].y);
 9        cout << "*";
10    }
11    //恢复原来的颜色
12    setColor(7, 0);
13}

4.8 清除屏幕上的贪吃蛇

我们是死循环不断刷新打印贪吃蛇的,因此每移动一次,必然会在屏幕上留下上一次贪吃蛇的痕迹。因此我们每次在画蛇之前,不是添足,而是清理一下上次遗留的蛇身。我们知道,蛇每次移动,变的只有蛇头和蛇尾,因此该函数我们只需要清理蛇尾就行。gotoxy到蛇尾的坐标,cout<<" ";就行。

1gotoxy(m_coordinate[this->m_coordinate.size()-1].x, m_coordinate[this->m_coordinate.size() - 1].y);
2cout << " ";

05 主函数,组装我们的游戏

我们的游戏在主函数里面进行组装。然后开始运行。
首先我们做游戏相关的初始化和模式选择。

1GameSetting setting;
 2PrintInfo print_info;
 3Snake  snake;
 4//初始化游戏
 5setting.GameInit();
 6//游戏模式选择
 7print_info.DrawChoiceInfo();
 8
 9char ch = _getch();
10switch (ch)
11{
12case '1':
13    snake.set_model(true);
14    speed = 50;
15    break;
16case '2':
17    snake.set_model(false);
18    break;
19default:
20    gotoxy(GameSetting::window_width / 2 - 10, GameSetting::window_height / 2 + 3);
21    cout << "输入错误,Bye!" << endl;
22    cin.get();
23    cin.get();
24    return 0;
25}
26gotoxy(GameSetting::window_width / 2 - 10, GameSetting::window_height / 2 + 3);
27system("pause");

然后就是画地图边框,打印游戏相关信息和说明。生成食物了。

1//画地图
2print_info.DrawMap();
3print_info.DrawGameInfo(snake.GetModel());
4//生成食物
5Food food(snake.m_coordinate);

最后就是游戏死循环,在死循环里面,我们需要不断移动蛇,画蛇,判断蛇的状态,判断食物的状态,是否吃到食物等等。具体代码:

1//游戏死循环
 2while (true)
 3{
 4    //打印成绩
 5    print_info.DrawScore(snake.GetSnakeSize());
 6    //画出食物
 7    food.DrawFood();
 8    //清理蛇尾,每次画蛇前必做
 9    snake.ClearSnake();
10    //判断是否吃到食物
11    snake.is_eat_food(food);
12    //根据用户模式选择不同的调度方式
13    if (snake.GetModel() == true)
14    {
15        snake.move_snake();
16    }
17    else
18    {
19        snake.AI_find_path(food);
20        snake.AI_move_snake();
21    }
22    //画蛇
23    snake.draw_snake();
24    //判断蛇是否还活着
25    if (!snake.snake_is_alive())
26    {
27        print_info.GameOver(snake.GetSnakeSize());
28        break;
29    }
30    //控制游戏速度
31    Sleep(speed);
32}

最终,我们的代码就可以Run起来了。具体效果放在开头了。界面算不上好看,但是整个程序向大家展示了最基本最核心的功能和代码,大家可以在这个基础上开发自己喜欢的各种美丽的界面哦。

06 AI部分和完善

代码是画了几天间间断断写出来的,水平不算很高,代码也写得乱七八糟的。不过代码会在后期不断优化,尽量做到精简优美。至于AI功能,等下一篇博文写吧。

相关文章
|
3月前
|
人工智能 自然语言处理 前端开发
深度解析Playwright MCP:功能、优势与挑战,AI如何提升测试效率与覆盖率
Playwright MCP通过AI与浏览器交互,实现自然语言驱动的自动化测试。它降低门槛、提升效率,助力测试工程师聚焦高价值工作,是探索性测试与快速验证的新利器。
|
3月前
|
SQL 人工智能 搜索推荐
Dataphin功能Tips系列(71)X-数据管家:数据资产运营的「AI外挂」
在企业数据治理中,数据资产规模庞大、字段繁多,手动录入效率低且易出错。Dataphin推出「X-数据管家」,利用大模型智能生成标签、描述及字段类型等信息,支持一键批量上架,大幅提升资产运营效率。
145 0
人工智能 移动开发 JavaScript
113 0
|
3月前
|
人工智能 测试技术 项目管理
测试不再碎片化:AI智能体平台「项目资料套件」功能上线!
在实际项目中,需求文档分散、整理费时、测试遗漏等问题常困扰测试工作。霍格沃兹推出AI智能体测试平台全新功能——项目资料套件,可将多个关联文档打包管理,并一键生成测试用例,提升测试完整性与效率。支持套件创建、文档关联、编辑删除及用例生成,适用于复杂项目、版本迭代等场景,助力实现智能化测试协作,让测试更高效、更专业。
|
3月前
|
存储 人工智能 测试技术
用AI提升测试效率:智能体平台的「需求文档管理」功能上线啦!
霍格沃兹测试开发学社推出AI智能体测试平台,全新「需求文档管理」功能助力高效测试准备。集中管理需求文档,支持多种上传方式,智能生成测试用例,提升测试效率与准确性,助力迈向智能化测试新时代。
|
5月前
|
存储 人工智能 前端开发
从零构建智能对话助手:LangGraph + ReAct 实现具备记忆功能的 AI 智能体
本文系统介绍了基于 LangGraph 框架构建具备记忆能力的 ReAct(Reasoning + Action)智能体的技术实现方法。ReAct 智能体结合语言模型的推理能力与外部工具的执行能力,通过“思考-行动-观察”循环机制,实现复杂任务的自主处理。文章详细讲解了 LangGraph 的图结构设计、状态管理、工具集成与记忆系统等关键技术,并通过代码示例演示了从基础工作流到高级智能体系统的构建过程。最终实现的智能体具备多轮对话、工具调用、结果反馈与上下文记忆能力,为开发下一代智能应用提供了技术基础。
948 1
|
5月前
|
C++ Windows
.NET Framework安装不成功,下载`NET Framework 3.5`文件,Microsoft Visual C++
.NET Framework常见问题及解决方案汇总,涵盖缺失组件、安装失败、错误代码等,提供多种修复方法,包括全能王DLL修复工具、微软官方运行库及命令行安装等,适用于Windows系统,解决应用程序无法运行问题。
470 3
|
5月前
|
人工智能 JSON 小程序
【AI编程】AI+高德MCP不到10分钟搞定上海三日游
本文介绍了小白如何通过AI编程工具(如Trae)快速开发应用并实现技术变现。内容涵盖AI编程用途、工具准备、高德地图开发者权限获取、AI工具配置及实战生成旅游攻略与打印页面,帮助零基础用户轻松入门AI编程。
251 0
|
5月前
|
机器学习/深度学习 人工智能 文字识别
浏览器AI模型插件下载,支持chatgpt、claude、grok、gemini、DeepSeek等顶尖AI模型!
极客侧边栏是一款浏览器插件,集成ChatGPT、Claude、Grok、Gemini等全球顶尖AI模型,支持网页提问、文档分析、图片生成、智能截图、内容总结等功能。无需切换页面,办公写作效率倍增。内置书签云同步与智能整理功能,管理更高效。跨平台使用,安全便捷,是AI时代必备工具!
400 8