//#include "tanchishe.h"
// #define _tanchishe_H_
#include <ctime>
#include<stdio.h>
#include<string.h>
#include<windows.h>
#include<time.h>
#include<conio.h> //getch()函数
#include<iostream>
using namespace std;
/*宏定义 */
#define UP 87
#define DOWN 83
#define LEFT 65
#define RIGHT 68
#define PAUSE 32
#define SPEED_UP 74
#define SPEED_DOWN 75
#define ESC 27
unsigned int KeyValue;// 记录键盘输入
/*蛇身位置 snake链表数据*/
typedef struct Snakes{
unsigned int x;
unsigned int y;
struct Snakes* next ;
}snake;
snake* head = (snake*)malloc(sizeof(snake)); //创建蛇头节点
/*食物位置 food 结构体数据*/
struct FOOD {
unsigned int x;
unsigned int y;
}food;
struct Player{ //玩家属性——得分,按键值 等
unsigned int score;
unsigned int key_click;
unsigned int high_score;
unsigned int speed;
}player;
void gotoxy(unsigned int x, unsigned int y){ /*设置光标位置 *///几列几行
HANDLE hOut; //创建 句柄
COORD pos;
pos.X=x;
pos.Y=y;
hOut=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut,pos); //传入 句柄 输出位置结构体变量
/*隐藏光标操作 */
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = FALSE;
cursor.dwSize = sizeof(cursor);
SetConsoleCursorInfo(hOut, &cursor);
}
void gotodelete(unsigned int x, unsigned int y){ //删除时 填充空格
gotoxy(x, y);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x06|0x09);
printf(" ");
}
void gotoprintwall(unsigned int x, unsigned int y){ //围墙
gotoxy(x, y);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x09|0x09);
printf("■");
}
void gotofood(unsigned int x, unsigned int y) { //食物打印 ★
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03|0x02); // 设置由hOut光标 输出的文本颜色 ,可调用
gotoxy(x, y);
printf("★");
}
void gotoprinthead(unsigned int x, unsigned int y) { //蛇头打印 ⊙
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03|0x02); // 设置由hOut光标 输出的文本颜色 ,可调用
gotoxy(x, y);
printf("⊙");
}
void gotoprintsnake(unsigned int x, unsigned int y) { //其他3打印 ◎
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03|0x02); // 设置由hOut光标 输出的文本颜色 ,可调用
gotoxy(x, y);
printf("◎");
}
void goto_other1(unsigned int x, unsigned int y) { //其他1打印 ☆
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03|0x02); // 设置由hOut光标 输出的文本颜色 ,可调用
gotoxy(x, y);
printf("☆");
}
void goto_other2(unsigned int x, unsigned int y) { //其他2打印 ¤
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03|0x02); // 设置由hOut光标 输出的文本颜色 ,可调用
gotoxy(x, y);
printf("¤");
}
void goto_other4(unsigned int x, unsigned int y) { //其他4打印 □ 围墙 ★☆□⊙
gotoxy(x, y);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x06|0x06);//橙色
printf("□");
}
void gotoprintf(unsigned int x, unsigned int y,const char* ha ){
gotoxy(x, y);
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03|0x02); // 设置由hOut光标 输出的文本颜色 ,可调用
//0x09|0x06白 0x06|0x06 橙色 0x04|0x04红色 ......
while(*ha)
{
cout<<*ha;
++ha;
}
}
void Get_Time2 ()//时间显示
{
time_t rawtime;
struct tm *info;
char buffer[50];//最后拷贝存放容器
time( &rawtime );
info = localtime( &rawtime );
strftime(buffer, 50, " %Y-%m-%d %H:%M:%S %A", info);
//size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
//根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0b|0x0b );//芥末色
gotoprintf(60,13, buffer);
}
/*开始界面*/
void Welcome(){
unsigned int x;
system("cls"); // 清屏
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0c|0x0a);//金色
gotoprintf(22,4,"=》请开启英文输入法操作《=") ;
gotoxy(22,6);
printf("哈哈哈,简单的贪吃蛇---");
gotoxy(22,8);
printf("请按空格键 开始 / 暂停");
gotoxy(22,10);
printf("操作:上下左右 wsad WSAD");
gotoxy(22,12);
printf(" 加速:J 减速:K");
gotoxy(22,14);
printf("按N重新开始 , 按 Esc 键 退出");
while(1){
x=getch();
if(x==' ' )break;
else if(x==ESC)
{
system("cls");
gotoprintf(15,13,"=======已退出游戏,按任意键关闭窗口=======") ;
exit(0);
}
};
/*清屏*/
system("cls");
}
/*结束界面*/
void Finish(){//游戏结束
/*地图右侧显示元素*/
unsigned char i;
for (i = 5; i <=19; i++)
{
goto_other4(56, i);
goto_other4(94, i);
}
for (i = 56; i <= 94; i=i+2)
{
goto_other4(i, 5);
goto_other4(i, 19);
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0c|0x0a);//白色
gotoxy(60, 11);
printf("得分 :%d = ̄ω ̄= ", player.score/10);
gotoprintf(34, 28,"游戏作者 : JH ");
gotoxy(60, 8);
printf("游戏结束 o(* ̄▽ ̄*)o");
gotoxy(60, 14);
printf("还不错哦 ,继续努力O( ∩_ ∩)O");
gotoxy(60, 16);
printf("=》按空格键继续,=》按ESC键退出");
// 清除蛇头以外的蛇身数据
snake* p = head->next, * q;
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
head->next=NULL;
while(1){
unsigned int x=getch();
if(x==' ' ) break;
else if(x==ESC)
{
system("cls");
gotoprintf(15,13,"=======已退出游戏,按任意键关闭窗口=======") ;
exit(0);
}
};
KeyValue=0;
player.key_click=0;
system("cls");
}
void creat_food(){// 随机产生一个食物
bool flag = false;
srand((int)time(NULL));
while (!flag)
{
flag = true;
food.x = rand() % (51-4) + 4;
food.y = rand() % (25-2) + 2;
//打印 一格中文字符 宽度为2 保证偶数位置
if (food.x & 0x01 != 0)
{
food.x = food.x + 1;
}
snake* judge = head;
while (1) //遍历排除蛇身重复
{
if (judge->next == NULL) break;
if (food.x == judge->x && food.y == judge->y)
{
flag = false;
}
judge = judge->next;
}
}//找到合适食物的位置 跳出循环
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x06|0x06 ); //橙色 食物
gotofood(food.x,food.y);
}
void Creat_snake(){ //创建初始蛇的位子
snake* p = (snake*)malloc(sizeof(snake));
snake* q = (snake*)malloc(sizeof(snake));
if(NULL!=p && NULL!=q)
{// 判断 p, q申请到内存空间了没
srand(time(NULL));//用于 随机方向 随机位置
player.key_click=rand()%(5-1)+1; //初始方向
head->x = rand()%(36-18)+18;//初始蛇身位置 x列 y行 2-52 1-26 | 18-36 9-18
head->y = rand()%(19-9)+9; //rand/(大-小)+小 所得区间 [小,大)
//打印 一格中文字符 宽度为2 保证偶数位置
if(head->x & 0x01)
{
head->x +=1;
}
switch (player.key_click){
case 1: player.key_click=UP;
p->x = head->x;
p->y = head->y+1;
q->x = p->x;
q->y = p->y+1;
break;
case 2: player.key_click=DOWN;
p->x = head->x;
p->y = head->y-1;
q->x = p->x;
q->y = p->y-1;
break;
case 3: player.key_click=LEFT;
p->x = head->x +2;
p->y = head->y;
q->x = p->x +2;
q->y = p->y;
break;
case 4: player.key_click=RIGHT;
p->x = head->x-2;
p->y = head->y;
q->x = p->x-2;
q->y = p->y;
break;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x09|0x09); //蓝色
/*打印初始的蛇 长度三*/
gotoprintsnake(head->x,head->y);
gotoprintsnake(p->x,p->y);
gotoprintsnake(q->x,q->y);
head->next = p;//最初蛇身 连接起来
p->next = q;
q->next = NULL;
q=NULL;
p=NULL;
free(p);
free(q);
}
else
{
gotoprintf(2,5, "创建失败");
q=NULL;
p=NULL;
free(p);
free(q);
exit(0);
}
}
void Creat_map(){//创建地图
unsigned int i;
//50*26
for (i = 2; i <= 52; i += 2){/*注意这里横坐标是每次+2 因为控制台字符宽高比为 1:2 */
gotoprintwall(i, 1);
gotoprintwall(i, 26);
}
for (i = 1; i <= 26; i++){
gotoprintwall(2, i);
gotoprintwall(52, i);
}
/*地图右侧显示元素*/
for (i = 5; i <=19; i++){
goto_other4(56, i);
goto_other4(94, i);
}
for (i = 56; i <= 94; i=i+2){
goto_other4(i, 5);
goto_other4(i, 19);
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0a|0x0a);
gotoprintf( 60, 8, "==== 游戏进行中 ====");
gotoxy(60, 11);
printf("得分 :%d = ̄ω ̄= ", player.score);
gotoxy(34, 28);
printf("游戏作者 : JH ");
Creat_snake();
creat_food();
player.speed=180;
player.score=0;
}
unsigned char Judge(){ //判断
if (head->x == 2 || head->x == 52 || head->y == 1 || head->y == 26) {return 0; }//失败
snake* p = head->next;
while (1){
if (p == NULL) break;
if (head->x == p->x && head->y == p->y)
{
return 0;//吃到自己 失败
}
p = p->next;
}
return 1; // 没有失败 继续移动
}
void AddBody(){//得分 添加长度 尾部
if (head->x == food.x && head->y == food.y)
{
creat_food();
snake* _new = (snake*)malloc(sizeof(snake));
snake* p;
p = head;
while (1)//遍历到 蛇身链表 尾部
{
if (p->next == NULL) break;
p = p->next;
}
p->next = _new;//在尾部 添加新的节点
_new->next = NULL;
player.score += 1;
gotoxy(60, 11);
printf("得分 :%d = ̄ω ̄= ", player.score);
}
}
void UpdateBody(unsigned int x, unsigned int y){//刷新蛇身坐标
/*删除尾部节点*/
snake* p = head;
while (p->next->next != NULL) {
p = p->next;
}
/*通过先清空尾部 后打印开头 每个位置坐标点固定部不动 只进行 添删头尾 操作 */
gotodelete(p->next->x, p->next->y); // 消除尾结点轨迹
free(p->next);//释放尾部节点
p->next = NULL;
/*添加移动后的坐标*/
p = (snake*)malloc(sizeof(snake));
p->x = x;
p->y = y;
/*添加 新的蛇头坐标 链表节点*/
p->next = head;
head = p;
/*最后释放p 这个临时节点 */
p=NULL;
free(p);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03|0x02);//淡蓝色 蛇身
gotoprintsnake(head->next->x ,head->next->y);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x09|0x06);//白色蛇头
gotoprinthead(x,y); /*打印新蛇头轨迹*/
}
void RunBody(){//完整行走动作
unsigned int x = head->x;
unsigned int y = head->y;
switch (player.key_click)
{
case UP:
y -= 1;
break;
case DOWN:
y += 1;
break;
case LEFT:
x -= 2;
break;
case RIGHT:
x += 2;
break;
}
if( !(x == 2 || x == 52 || y == 1 || y == 26))
{
if (x != head->x || y != head->y)
{
UpdateBody(x, y);/* 删除尾结点 添加新的头结点 */
}
}
/*把下面的else删了,撞墙不死*/
else
{//如果已经撞墙了
head->x=2;
}
// 蛇速度控制
Get_Time2 ();
Sleep(player.speed);
}
unsigned char ClickControl(){//按键控制
while (1)
{
if (Judge() == 0)
{
Finish();
return 0; //游戏失败 返回 0
}
if (_kbhit()) // _kbhit() 检测按键函数 请百度
{
KeyValue=getch();
if(KeyValue==ESC)
{//退出
KeyValue=0;
return 1; //退出返回 1
}
if(KeyValue=='N'||KeyValue=='n')
{//新游戏
KeyValue=0;
return 2;
}
switch (KeyValue)
{
case PAUSE : while(1)
{
if(_kbhit()) { KeyValue=getch(); break; }
} break;
case 106:
case SPEED_UP : if(player.speed>=60 ) { player.speed-=10; } KeyValue=0; break;
case 107:
case SPEED_DOWN : if(player.speed<=270) { player.speed+=10; } KeyValue=0; break;
case 119:
case UP : if(player.key_click!=DOWN) player.key_click=UP; break;
case 115:
case DOWN : if(player.key_click!=UP) player.key_click=DOWN; break;
case 97:
case LEFT : if(player.key_click!=RIGHT) player.key_click=LEFT; break;
case 100:
case RIGHT: if(player.key_click!=LEFT) player.key_click=RIGHT; break;
default : break;
}
}
RunBody();
AddBody();
}
return 1;
}
void game(){ //贪吃蛇游戏 封装一下
while(1)
{
Welcome();
Creat_map();
if(1==ClickControl())
{
break;
}
}
system("cls");
gotoprintf(20,20,"已经退出游戏,按任意键关闭窗口");
}
/*主函数 精简一些*/
int main()
{
game();
return 0;
}
纯C语言实现贪吃蛇
于 2022-03-18 19:42:46 首次发布