贪吃蛇游戏的实现

一.技术要点:

贪吃蛇需要掌握:

        c语言函数,枚举,结构体,动态内存管理,预处理指令,链表,Win32 API等

二.Win32 API

1.Win32 API简介

windows可以帮应用程序卡其视窗,描绘图案,使用周边设备,,Win32 API就是windows32位平台上的应用程序编程接口

2.控制台程序

(1).使用cmd命令设置控制台窗口的长宽

输入mode con cols=100 lines=30

(2.)可以通过命令设置控制台的窗口的名字

title xxx

(3.)C语言的system函数实现控制台的命令

#include<stdio.h>
#include<stdlib.h>
int main()
{system("mode con cols=50 lines=20");system("title 贪吃蛇");system("pause");return 0;
}

(4.)控制台屏幕上的坐标

COORD是windows API中定义的结构体,表示一个字符在控制台屏幕缓冲区上的坐标,坐标(0,0)位于缓冲区左上角

CORRD类型声明:

typedef struct _CORRD
{SHORT X;SHORT Y;
}CORRD, *PCORRD;

给结构体定坐标:

#include <windows.h>
CORRD pos = { 10,15 };

(5.)GetStdHandle获取设备

它也是一个windows API函数,用于从一个特定的标准设备(标准输入,标准输出或标准错误)中取得一个句柄(用来标识不同设备的数值),使用这个句柄可以操作设备

函数的声明:

HANDLE GetStdHandle(DWORD nStdHandle);

eg.

HANDLE hOutput = NULL;//获取标准输出的句柄
hOutput = GetStHandle(STD_OUTPUT_HANDLE);

(6.)GetConsoleCursorInfo获取光标信息

检索有关指定控制台屏幕缓冲区的光标大小和可见性信息(鼠标信息)

函数的声明:

BOOL WINAPI GetConsoleCursorInfo(HANDLE               hConsoleOutput PCONSOLE_CURSOR_INFO lpConsoleCursorInfo 
);

 PCONSOLE_CURSOR_INFO是指向CONSOLE_CURSOR_INFO结构的指针,该结构接受有感主机游标(光标)的信息

eg.

#include <windows.h>
int main()
{CONSOLE_CURSOR_INFO cursor_info = { 0 };HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);GetConsoleCursorInfo(handle,&cursor_info);return 0;
}

CONSOLE_CURSOR_INFO是结构体,其中有两个成员,:bVisible和dwSize分别控制光标的可可见性和大小

(7.)SetConsoleCursorPosition设置光标位置

void SetPos(int x,int y)
{//获得设备句柄HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//根据句柄设置光标位置CORRD pos = { x,y };SetConsoleCursorPosition(handle,pos);
}

(8.)GetAsyncKeyState获取按键情况

想要判断一个键是否被按,可以检测GetAsyncKeyState返回值的最低为是否为1

#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 0x1) ? 1 : 0)

eg.

#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 0x1) ? 1 : 0)
int main()
{while(1){    if(KEY_PRESS(0x30))printf("0\n");else if(KEY_PRESS(0x31))printf("1\n");else if(KEY_PRESS(0x32))printf("2\n");else if(KEY_PRESS(0x33))printf("3\n");else if(KEY_PRESS(0x34))printf("4\n");......}
}

这样可以在按下数字键是在屏幕上输出数字

三.贪吃蛇游戏的设计与分析

一.地图

1.<locale.h>本地化

<locale.h>提供的函数用于控制C标准库中对于不同地区会产生不一样的行为的部分

在标准中,依赖地区的部分有以下几项:

1.数字量的格式

2.货币量的格式

3.字符集

4.日期和时间的表示形式

2.类项

1.LC_COLLATE:影响字符串比较函数strcoll()和strxfrm()

2.LC_CTYPE:影响字符处理函数的行为

3.LC_MONCTARY:影响货币格式

4.LC_NUMERIC:影响printf()的数字格式

5.LC_TIME:影响时间格式strftime()和wcsfting()

6.LC_ALL:针对所有类项修改,将以上的多有类别设置为给定的语言环境

3.setlocale函数

该函数用于修改当前的地区,可以针对一个类项,也可以针对所有类项

函数声明:

char* setlocale(int category,const char* locale);

第一个参数是修改的类项,第二个为地区

C的标准给了第二个参数2种可能取值:1."C"(正常模式)  2.""(本地模式)

在任意程序执行开始时,会隐藏执行调用setlocale(LC_ALL,"C");

第二个参数也可以传入NULL,通过传入NULL可以查询默认的本地信息

4.宽字符的打印

一个宽字符占领个字符的位置

int main()
{setlocale(LC_ALL,"");wchar_t ch = L'中国';wprintf(L"%lc",ch);return 0;
}

5.地图坐标

可以假设设计一个27行,58列的地图并围绕地图画出墙壁,如下

二.蛇身和食物

蛇身

可以初始化蛇身长度为5,在随机坐标出现蛇,连续五个节点代表蛇身

注意:为了防止社的一个节点的一半出现在墙体里,另一半在墙外,最好让蛇身的每一个节点的X坐标为2的倍数

食物

在墙体内随机生成一个坐标(x的坐标必须为2的倍数),同时坐标不能与蛇身重合

如图:

三.数据结构设计

一.蛇身节点

蛇身可以使用链表设计,需要存储的信息为当前节点的蛇身的坐标(x,y)和下一个节点

typedef struct snakenode
{int x;int y;//节点坐标struct snake* next;
}snakenode, * psnakenode;

二.贪吃蛇状态

//游戏状态
enum GAME_STATUS
{OK = 1,//正常进行ESC,//退出KILL_BY_WALL,//撞墙KILL_BY_SELF//撞到自己
};//运动方向
enum DIRECTION
{UP = 1,DOWN,LEFT,RIGHT
};typedef struct snake
{psnakenode psnake;//维护整条蛇的指针psnakenode pfood; //指向食物的指针int score;        //当前累计的分数int foodweight;   //一个食物的分数int sleeptime;    //蛇休眠的时间.休眠时间越短,蛇移动的速度越快//游戏当前的状态enum GAME_STATUS status;//蛇的运动方向enum DIRECTION dir;}snake,psnake;

三.开始游戏

#define _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"//设置光标位置
void SetPos(int x, int y)
{//获得设备句柄HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//根据句柄设置光标位置COORD pos = { x,y };SetConsoleCursorPosition(handle, pos);
}void welcome()
{//欢迎信息SetPos(35,10);printf("欢迎来到贪吃蛇");SetPos(37,20);system("pause");//请按任意键继续system("cls");//清理屏幕上的信息//功能介绍SetPos(15, 10);printf("用↑,↓,←,→来控制蛇的移动,F3加速,F4减速");SetPos(15, 11);printf("加速可获得更高的分数");SetPos(37, 20);system("pause");system("cls");
}void creativemap()
{int i = 0;//上SetPos(0, 0);for (i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);}//下SetPos(0, 26);for (i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);}//左for (i = 0; i <= 25; i++){SetPos(0, i);wprintf(L"%lc", WALL);}//右for (i = 0; i <= 25; i++){SetPos(56, i);wprintf(L"%lc", WALL);}system("pause");
}void gamestart(psnake ps)
{//设置控制台信息,窗口大小,窗口名system("mode con cols=100 lines=30");system("title 贪吃蛇");//隐藏光标HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(handle, &CursorInfo);//获取光标信息CursorInfo.bVisible = false;SetConsoleCursorInfo(handle, &CursorInfo);//打印欢迎信息welcome();//绘制地图creativemap();
}

绘制地图:

void creativemap()
{int i = 0;//上SetPos(0, 0);for (i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);}//下SetPos(0, 26);for (i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);}//左for (i = 0; i <= 25; i++){SetPos(0, i);wprintf(L"%lc", WALL);}//右for (i = 0; i <= 25; i++){SetPos(56, i);wprintf(L"%lc", WALL);}}

蛇的初始化

void InitSnake(psnake ps)
{//创建5个蛇身节点psnakenode pcur = NULL;int i = 0;for (i = 0; i < 5; i++){pcur = (psnakenode)malloc(sizeof(snakenode));if (pcur == NULL){perror("InitSnake():malloc()");return;}pcur->x = POS_X + 2 * i;pcur->y = POS_Y;pcur->next = NULL;//进行头插if (ps->psnake == NULL){ps->psnake = pcur;}else{pcur->next = ps->psnake;ps->psnake = pcur;}}//打印蛇身pcur = ps->psnake;while (pcur){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}//贪吃蛇的其他信息ps->dir = RIGHT;ps->foodweight = 1;ps->pfood = NULL;ps->score = 0;ps->sleeptime = 200;//0.2秒ps->status = OK;getchar();
}

食物的初始化:

1.食物在地图中应该是随机出现的,因此食物的坐标需要随机

2.食物的坐标必须出现在墙内(2<=x<=54,1<=y<=15)

3.食物不能出现在蛇身上

void creatfood(psnake ps)
{int x;int y;again:do{x = rand() % 52 + 2;//x:(0 - 52) + 2y = rand() % 24 + 1;//y:(0 - 24) + 1//当x为奇数时会出现问题,因为蛇身始终处在偶数位} while (x % 2 != 0);//当x为偶数时生成成功,若为奇数,重新生成//坐标和蛇身坐标比较//如果与蛇身重合,则重新生成psnakenode pcur = ps->psnake;while (pcur){if (x == pcur->x && y == pcur->y){goto again;}pcur = pcur->next;}//创建食物psnakenode pfood = (psnakenode)malloc(sizeof(snakenode));if (pfood == NULL){perror("CreateFood()::malloc()");return;}pfood->x = x;pfood->y = y;ps->pfood = pfood;SetPos(x, y);wprintf(L"%lc", FOOD);getchar();
}

游戏的运行:

void pause()
{while (1){Sleep(100);if (KEY_PRESS(VK_SPACE)){break;}}
}int NextIsFood(psnake ps, psnakenode pnext)
{if (ps->pfood->x == pnext->x && ps->pfood->y == pnext->y){return 1;}elsereturn 0;
}void EatFood(psnake ps, psnakenode pnext)
{//ps->psnake是头节点pnext->next = ps->psnake;ps->psnake = pnext;//打印蛇身psnakenode pcur = ps->psnake;while (pcur){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}ps->score += ps->foodweight;//删除旧的食物free(ps->pfood);//新建食物creatfood(ps);
}void NotEatFood(psnake ps, psnakenode pnext)
{//先头插pnext->next = ps->psnake;ps->psnake = pnext;//删除尾节点//同时打印蛇身psnakenode pcur = ps->psnake;while (pcur->next->next!= NULL){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}//将尾节点变为空白字符SetPos(pcur->next->x, pcur->next->y);printf("  ");free(pcur->next);pcur->next = NULL;}void KillByWall(psnake ps)
{if (ps->psnake->x == 0 || ps->psnake->x == 56 || ps->psnake->y == 0 || ps->psnake->y == 26){ps->status = KILL_BY_WALL;}
}void KillBySelf(psnake ps)
{psnakenode pcur = ps->psnake->next;while (pcur){if (pcur->x == ps->psnake->x && pcur->y == ps->psnake->y){ps->status = KILL_BY_SELF;return;}pcur = pcur->next;}
}void snakemove(psnake ps)
{psnakenode pnext = (psnakenode)malloc(sizeof(snakenode));if (pnext == NULL){perror("snakemove()::malloc()");return;}switch (ps->dir){case UP:pnext->x = ps->psnake->x;pnext->y = ps->psnake->y - 1;break;case DOWN:pnext->x = ps->psnake->x;pnext->y = ps->psnake->y + 1;break;case LEFT:pnext->x = ps->psnake->x - 2;pnext->y = ps->psnake->y;break;case RIGHT:pnext->x = ps->psnake->x + 2;pnext->y = ps->psnake->y;break;}//下一个坐标是否是食物if (NextIsFood(ps, pnext)){//是食物EatFood(ps, pnext);}else{//不是食物,正常向下走NotEatFood(ps, pnext);}KillByWall(ps);KillBySelf(ps);
}void gamerun(psnake ps)
{//打印帮助信息PrintHelpInfo();//对于游戏的循环结构,一般使用do whiledo{//当前分数SetPos(62, 10);printf("总分:%6d\n", ps->score);SetPos(62, 11);printf("食物分值:%02d\n", ps->foodweight);//检测按键if (KEY_PRESS(VK_UP) && ps->dir != DOWN){ps->dir = UP;}else if (KEY_PRESS(VK_DOWN) && ps->dir != UP){ps->dir = DOWN;}else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT){ps->dir = LEFT;}else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT){ps->dir = RIGHT;}else if (KEY_PRESS(VK_ESCAPE)){ps->status = ESC;}else if (KEY_PRESS(VK_ESCAPE)){ps->status = ESC;break;}else if (KEY_PRESS(VK_SPACE)){//暂停与恢复pause();}else if (KEY_PRESS(VK_F3)){if(ps->sleeptime >= 50){ ps->sleeptime -= 50; ps->score += 2;}}else if (KEY_PRESS(VK_F4)){if (ps->sleeptime <= 400){ps->sleeptime += 50;ps->foodweight -= 2;}}//睡眠一会Sleep(ps->sleeptime);//走一步snakemove(ps);} while (ps->status == OK);}

四.游戏结束

void GameEnd(psnake ps)
{SetPos(16, 12);switch (ps->status){case ESC:printf("退出游戏");break;case KILL_BY_WALL:printf("撞墙了");break;case KILL_BY_SELF:printf("吃到自己了");break;}psnakenode pcur = ps->psnake;psnakenode del = NULL;while (pcur){del = pcur;pcur = pcur->next;free(del);}free(ps->pfood);ps = NULL;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/248325.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu20-github不通问题

github不通 一直在github下载失败 Git报错fatal unable to connect to github.com: github.com[0: 20.205.243.166] >>> alsa-ucm-conf v1.2.6.3 Downloading(卡在这里,很烦啊) 然后搜了很多文档,然后以下操作: 1.GitHub.com - GitHub: Lets build from here Git…

GPT-4 Vision根据应用程序截图生成博客和Readme 升级Streamlit八

GPT-4 Vision 系列: 翻译: GPT-4 with Vision 升级 Streamlit 应用程序的 7 种方式一翻译: GPT-4 with Vision 升级 Streamlit 应用程序的 7 种方式二翻译: GPT-4 Vision静态图表转换为动态数据可视化 升级Streamlit 三翻译: GPT-4 Vision从图像转换为完全可编辑的表格 升级St…

stm32中的SPI

SPI的简介 文章目录 SPI的简介物理层协议层基本通讯过程起始和终止信号数据有效性CPOL/CPHA及通讯模式 STM3的SPI特性及架构通讯引脚时钟控制逻辑数据控制逻辑整体控制逻辑通讯过程 代码配置实现指令集结构体的定义SPI时钟信号的定义SPI端口定义SPI命令 flash驱动代码初始化代码…

纯html+js+css个人博客

首页 <!DOCTYPE HTML> <html> <head> <title>博客</title> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <meta name"viewport" content"widthdevice-width, initial-sca…

LLM之RAG实战(二十一)| 使用LlamaIndex的Text2SQL和RAG的功能分析产品评论

亚马逊和沃尔玛等电子商务平台上每天都有大量的产品评论&#xff0c;这些评论是反映消费者对产品情绪的关键接触点。但是&#xff0c;企业如何从庞大的数据库获得有意义的见解&#xff1f; 我们可以使用LlamaIndex将SQL与RAG&#xff08;Retrieval Augmented Generation&#x…

基于链表实现贪吃蛇游戏

本文中&#xff0c;我们将使用链表和一些Win32 API的知识来实现贪吃蛇小游戏 一、功能 &#xff08;1&#xff09;游戏载入界面 &#xff08;2&#xff09;地图的绘制 &#xff08;3&#xff09;蛇身的移动和变长 &#xff08;4&#xff09;食物的生成 &#xff08;5&…

2024-01-25 力扣高频SQL50题目1193每月交易

1.1193每月交易 1 count可以这样用。。 COUNT(IF(state approved, 1, NULL)) AS approved_count 如果 COUNT(if(state approved,1,0))&#xff0c;这里变成0&#xff0c;就不对了。因为count计数时候&#xff0c;只要里面不是null&#xff0c;就会算进去。 sum(if(state …

CMake 完整入门教程(一)

1 前言 每一次学习新东西都是很有乐趣的&#xff0c;虽然刚开始会花费时间用来学习&#xff0c;但是实践证明&#xff0c;虽然学习新东西可能会花费一些时间&#xff0c;但是它们带来的好处会远远超过这些花费的时间。学习新东西是值得的&#xff0c;也是很有乐趣的。 网络上…

【数据库】聊聊explain如何优化sql以及索引最佳实践

在实际的开发中&#xff0c;我们难免会遇到一些SQL优化的场景&#xff0c;虽然之前也看过周阳的课程&#xff0c;但是一直没有进行细心的整理&#xff0c;所以本篇会进行详细列举explain的相关使用&#xff0c;以及常见的索引最佳实践&#xff0c;并通过案例进行讲解。 数据准…

数学公式OCR识别php 对接mathpix api 使用公式编译器

数学公式OCR识别php 对接mathpix api 一、注册账号官网网址&#xff1a;https://mathpix.com 二、该产品支持多端使用注意说明&#xff08;每月10次&#xff09; 三、api 对接第一步创建create keyphp对接api这里先封装两个请求函数&#xff0c;get 和post &#xff0c;通过官方…

短视频与小程序:如何实现完美结合?

在短视频日益成为人们娱乐、社交和信息获取的重要渠道的今天&#xff0c;如何在短视频平台进行小程序推广成为了许多企业和品牌关注的焦点。本文将介绍如何利用短视频平台进行小程序推广&#xff0c;提升品牌曝光和用户互动。 首先&#xff0c;打开乔拓云-门店系统的后台&#…

ArcGIS Pro如何新建字段

无论是地图制作还是数据分析&#xff0c;字段的操作是必不可少的&#xff0c;在某些时候现有的字段不能满足需求还需要新建字段&#xff0c;这里为大家讲解一下在ArcGIS Pro中怎么新建字段&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的水…

Dragons

题目链接&#xff1a; Problem - 230A - Codeforces 解题思路&#xff1a; 用结构体排序就好&#xff0c;从最小的开始比较&#xff0c;大于就加上奖励&#xff0c;小于输出NO 下面是c代码&#xff1a; #include<iostream> #include<algorithm> using namespac…

JDBC学习笔记

一.什么是JDBC 我们操作数据库是用sql语句&#xff0c;那么怎么编写程序来操作数据库呢&#xff1f;这就要学习JDBC。 JDBC就是使用Java中操作关系型数据库的一套API。全称&#xff1a;( Java DataBase Connectivity ) Java 数据库连接。 JDBC更准确的来说是一套接口/API&…

05 Redis之Benchmark+简单动态字符串SDS+集合的底层实现

3.8 Benchmark Redis安装完毕后会自动安装一个redis-benchmark测试工具&#xff0c;其是一个压力测试工具&#xff0c;用于测试 Redis 的性能。 src目录下可找到该工具 通过 redis-benchmark –help 命令可以查看到其用法 3.8.1 测试1 3.9 简单动态字符串SDS 无论是 Redis …

【vue】vue.config.js里面获取本机ip:

文章目录 一、效果&#xff1a;二、实现&#xff1a; 一、效果&#xff1a; 二、实现&#xff1a; const os require(os);function getLocalIpAddress() {const interfaces os.networkInterfaces();for (let key in interfaces) {const iface interfaces[key];for (let i …

MySQL安全(一)权限系统

一、授权 1、创建用户 在MySQL中&#xff0c;管理员可以通过以下命令创建用户&#xff1a; namelocalhost IDENTIFIED BY password; name是要创建的用户名&#xff0c;localhost表示该用户只能从本地连接到MySQL&#xff0c;password是该用户的密码。如果要允许该用户从任何…

142. 环形链表 II(力扣LeetCode)

文章目录 142. 环形链表 II题目描述解题思路判断链表是否有环如果有环&#xff0c;如何找到这个环的入口 c代码 142. 环形链表 II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个…

【学网攻】 第(9)节 -- 路由器使用以及原理

系列文章目录 目录 系列文章目录 文章目录 前言 一、路由器是什么&#xff1f; 二、实验 1.引入 总结 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学网攻】 第(4)节 -- 交换机划分Vlan…

MySQL行格式原理深度解析

MySQL中的行格式&#xff08;Row Format&#xff09;是指存储在数据库表中的数据的物理格式。它决定了数据是如何在磁盘上存储的&#xff0c;以及如何在查询时被读取和解析的。MySQL支持多种行格式&#xff0c;每种格式都有其特定的优点和适用场景。 提升编程效率的利器: 解析…