C语言实现贪吃蛇游戏设计

C语言实现贪吃蛇,供大家参考,具体内容如下

实验平台:DEV C++

 /********************************************************************************
*File name:SnakeGame3.0.c
*Description:贪吃蛇游戏源代码(C语言),采用
*宽度优先算法,计算蛇到食物的最短路径(时间复杂度n^3空间复杂度n^2),这个算法遇 *
*到自身围困情况将失效,无法计算出最短路径                    *
*********************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<time.h>
#include<math.h>
#include<conio.h>

#define SIZE 25 //定义地图大小
#define MAX_LENGTH 19 * 19 //定义蛇的最大长度

typedef struct point //地图上的点的节点
{
 int r;
 int c;
} point;
typedef struct queue //迭代搜索最短路径用到的队列
{
 point * body[5 * SIZE]; //保存蛇的身体的数组(栈的深度最大为5 * SIZE)
 int num; //记录队列中节点数
 int first_in_pos; //第一个进入队列的元素的索引值
} queue;
HANDLE stdOutput; //声明windows标准输出句柄

void init(int * length, point * foodAt, int * dir, point body[], char map[][SIZE]); //初始化
int getDir(int dir); //获取蛇的行进方向
int getAIDir(int dir, int length, point body[], point foodAt); //获取AI判断得出的行进方向
int moveable(point moveTo, int length, point body[]);
int move(point foodAt, int dir, int length, point body[]); //蛇的运动
void draw(int length, point foodAt, point body[], char map[][SIZE]); //画图
void food(point * foodAt, point body[], int length, char map[][SIZE]); //生成食物

//栈相关的操作
point * pop(queue *queue); //从列队取出最先进入的点,返回取出点的指针,取出失败返回NULL
void push(point *point, queue *queue); //推入列队中

int main()
{
 char map[SIZE][SIZE]; //定义地图
 point body[MAX_LENGTH], foodAt; //整个蛇身体和食物的所在点(body数组的第一个值为蛇头)
 int length; //蛇的实际长度
 int dir; //行进方向
 int rate = 1; //行进速率
 int result; //保存蛇运动的结果:【死亡】、【得分】、【无】
 init(&length, &foodAt, &dir, body, map); //初始化

 while (1)
 {
 Sleep(100 / rate);
 //dir = getDir(dir); //获取蛇的行进方向
 dir = getAIDir(dir, length, body, foodAt); //获取AI判断得出的行进方向
 result = move(foodAt, dir, length, body); //蛇的运动
 if (result == 1) //如果吃到食物
 {
  length++;
  rate = length / 3;
  if (length == MAX_LENGTH)
  {
  printf("您已通关!");
  break;
  }
  food(&foodAt, body, length, map); //生成食物
 }
 draw(length, foodAt, body, map); //画图
 if (result == -1) //如果死亡
 {
  break;
 }

 }
 Sleep(500);
 printf(" 失败,此次得分为%d             ", (length - 3) * 100);
 system("pause");
}
void init(int * length, point * foodAt, int * dir, point body[], char map[][SIZE]) //初始化
{
 memset(map, '*', SIZE * SIZE); //初始化地图
 body[0].r = 3, body[0].c = 2; //初始化蛇的身体
 body[1].r = 2, body[1].c = 2;
 body[2].r = 1, body[2].c = 2;
 *length = 3; //初始长度为3
 *dir = 2; //初始方向向下
 food(foodAt, body, *length, map); //生成食物
 draw(*length, *foodAt, body, map); //画图
 printf(" 按下任意键开始,用ASDW控制方向,ESC暂停\n");
 _getch();
 srand((unsigned)time(NULL)); //生成随机数种子,备用
 stdOutput = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出句柄
 CONSOLE_CURSOR_INFO cci;
 cci.bVisible = 0;
 cci.dwSize = 1;
 SetConsoleCursorInfo(stdOutput, &cci);
 COORD coord = { 0, SIZE * 2 + 3 };
}
int getDir(int dir) //获取蛇的行进方向,规定返回值0代表向上,1代表向右,2代表向下,3代表向左
{
 char key;
 int newDir = dir;
 if (_kbhit())
 {
 key = _getch();
 switch (key)
 {
 case 'A': case 'a': newDir = 3; break;
 case 'S': case 's': newDir = 2; break;
 case 'D': case 'd': newDir = 1; break;
 case 'W': case 'w': newDir = 0; break;
 case 27: _getch(); break;
 }
 }
 if (newDir - dir == 2 || newDir - dir == -2) //蛇不能反向
 {
 newDir = dir;
 }
 return newDir;
}
int getAIDir(int dir, int length, point body[], point foodAt) //获取AI判断得出的行进方向
{
 static int *shortestPathDir, count = 0; //保存最短路径的方向(方向的先后顺序为倒序,即排在后面的方向先走)

 if (count == 0) //如果最短路径还没生成,那么重新生成
 {
 int map_of_steps[SIZE][SIZE]; //保存到达地图上某一点的最小步数
 queue queue = { 0,0 };
 point *last_body = (point *)malloc(length * sizeof(point)); //保存计算过程中上次蛇的身体位置
 point *next_body; //保存下一次蛇的身体
 point next_point;
 int i, step = 0;
 point moveTo;
 memcpy(last_body, body, length * sizeof(point));
 memset(map_of_steps, 0, SIZE * SIZE * sizeof(int));

 //向队列中放入初始body
 push(last_body, &queue);
 push(NULL, &queue); //插入NULL来标识宽度优先搜索的某一层的结束
 step++; //用step来表示步数,也代表层数

 while (queue.num != 0)
 {
  last_body = pop(&queue);
  if (last_body == NULL) //如果某一层结束
  {
  if (queue.num != 0) //如果还有下一层的元素
  {
   step++;
   push(NULL, &queue); //插入下一层的结束标志
   continue;
  }
  else
  {
   break;
  }

  }
  for (i = 0; i < 4; i++) //分别检测四个方向能否移动
  {
  switch (i)
  {
  case 0: moveTo.r = last_body[0].r - 1, moveTo.c = last_body[0].c; break;
  case 1: moveTo.r = last_body[0].r, moveTo.c = last_body[0].c + 1; break;
  case 2: moveTo.r = last_body[0].r + 1, moveTo.c = last_body[0].c; break;
  case 3: moveTo.r = last_body[0].r, moveTo.c = last_body[0].c - 1; break;
  }
  if (moveable(moveTo, length, last_body) && map_of_steps[moveTo.r][moveTo.c] == 0) //如果移向的点之前没有移到过
             //(即当前路径是到该点的最短路径),而且该点是moveable的
  {
   map_of_steps[moveTo.r][moveTo.c] = step;
   if (moveTo.r == foodAt.r && moveTo.c == foodAt.c) //如果下一步就可以到达食物所在点
   {
   //先free一些没用的动态内存
   free(last_body);
   while (queue.num != 0)
   {
    free(pop(&queue));
   }
   goto outer; //跳出循环
   }
   //生成next_body并将其推入队列
   next_body = (point *)malloc(length * sizeof(point));
   for (i = length - 1; i > 0; i--) //移动蛇的位置
   {
   next_body[i] = body[i - 1];
   }
   next_body[0] = moveTo; //换一个头

   push(next_body, &queue);//推入队列

  }
  }
  //free一些没用的动态内存
  free(last_body);
 }
 outer:;
 if (map_of_steps[foodAt.r][foodAt.c] == 0) //如果无法到达食物所在点,那么按原路走,直到死亡
 {
  return dir;
 }
 //生成shortestPath
 shortestPathDir = (int *)malloc(step * sizeof(int));
 count = step;
 next_point = foodAt;
 for (i = 0; i < step - 1; i++) //利用map_of_steps和下一个点推知到上一个点到下一个点的方向dir
 {
  if (next_point.r + 1 < SIZE && map_of_steps[next_point.r][next_point.c] ==
  map_of_steps[next_point.r + 1][next_point.c] + 1)
  {
  shortestPathDir[i] = 0;
  next_point.r += 1;
  }
  else if (next_point.c - 1 >= 0 && map_of_steps[next_point.r][next_point.c] ==
  map_of_steps[next_point.r][next_point.c - 1] + 1)
  {
  shortestPathDir[i] = 1;
  next_point.c -= 1;
  }
  else if (next_point.r - 1 >= 0 && map_of_steps[next_point.r][next_point.c] ==
  map_of_steps[next_point.r - 1][next_point.c] + 1)
  {
  shortestPathDir[i] = 2;
  next_point.r -= 1;
  }
  else
  {
  shortestPathDir[i] = 3;
  next_point.c += 1;
  }
 }
 //第一步要单独判断(因为map_of_steps的值为0的点可能是蛇头,也可能是蛇身,这样会对蛇第一步方向判断产生干扰)
 if (body[0].r > next_point.r)
 {
  shortestPathDir[step - 1] = 0;
 }
 else if (body[0].r < next_point.r)
 {
  shortestPathDir[step - 1] = 2;
 }
 else if (body[0].c > next_point.c)
 {
  shortestPathDir[step - 1] = 3;
 }
 else
 {
  shortestPathDir[step - 1] = 1;
 }
 /*printf("\n\n\n");
 int j;
 for (i = 0; i < SIZE; i++)
 {
  for (j = 0; j < SIZE; j++)
  {
  printf("%3d", map_of_steps[i][j]);
  }
  printf("\n");
 }
  printf("\n");*/
 }
 //沿着最短路径走
 return shortestPathDir[--count];
}
int moveable(point moveTo, int length, point body[]) //判断是否可以移动到moveTo点,能1,不能0
{
 int i;
 for (i = 0; i < length - 1; i++)
 {
 if (moveTo.r == body[i].r && moveTo.c == body[i].c)
 {
  return 0;
 }
 }
 if (moveTo.r < 0 || moveTo.r >= SIZE || moveTo.c < 0 || moveTo.c >= SIZE) //如果超出边界
 {
 return 0;
 }
 return 1;
}
int move(point foodAt, int dir, int length, point body[]) //蛇的运动,规定返回值-1代表死亡,0代表没有吃到食物,1代表吃到食物
{
 int i, flag = 0;
 point head = body[0];
 switch (dir)
 {
 case 0: head.r -= 1; break;
 case 1: head.c += 1; break;
 case 2: head.r += 1; break;
 case 3: head.c -= 1; break;
 }
 if (head.r < 0 || head.r >= SIZE || head.c < 0 || head.c >= SIZE) //出界了死亡
 {
 return -1;
 }
 for (i = 0; i < length - 1; i++)
 {
 if (head.r == body[i].r && head.c == body[i].c) //咬到了自己死亡
 {
  return -1;
 }
 }
 if (head.r == foodAt.r && head.c == foodAt.c) //吃到了食物
 {
 length++;
 flag = 1; //标记一下,便与等下返回值为1
 }
 for (i = length - 1; i > 0; i--) //移动蛇的位置
 {
 body[i] = body[i - 1];
 }
 body[0] = head; //换一个头
 if (flag == 1)
 {
 return 1;
 }
 return 0;
}
void draw(int length, point foodAt, point body[], char map[][SIZE]) //画图
{

 static char bitmap[SIZE + 2][SIZE + 2]; //定义一个数组,用于把地图背景、边界、蛇、食物都画上去
 int i, j;
 for (i = 0; i < SIZE; i++) //背景
 {
 for (j = 0; j < SIZE; j++)
 {
  bitmap[i + 1][j + 1] = map[i][j];
 }
 }
 //边框
 bitmap[0][0] = '0', bitmap[0][SIZE + 1] = '1';
 bitmap[SIZE + 1][0] = '2', bitmap[SIZE + 1][SIZE + 1] = '3';
 for (i = 0; i < SIZE; i++)
 {
 bitmap[0][i + 1] = '4', bitmap[SIZE + 1][i + 1] = '4';
 bitmap[i + 1][0] = '5', bitmap[i + 1][SIZE + 1] = '5';
 }
 bitmap[foodAt.r + 1][foodAt.c + 1] = 'f'; //食物
 bitmap[body[0].r + 1][body[0].c + 1] = 'h'; //蛇头
 for (i = 1; i < length; i++) //蛇身
 {
 bitmap[body[i].r + 1][body[i].c + 1] = 'b';
 }

 COORD coord = { 0, 0 }; //座标0,0
 SetConsoleCursorPosition(stdOutput, coord); //把光标设置到0,0位置
 for (i = 0; i < SIZE + 2; i++)
 {
 for (j = 0; j < SIZE + 2; j++)
 {
  switch (bitmap[i][j])
  {
  case 'f': printf("★"); break;
  case 'b': printf("●"); break;
  case 'h': printf("○"); break;
  case '0': printf("┏"); break;
  case '1': printf("━┓"); break;
  case '2': printf("┗"); break;
  case '3': printf("━┛"); break;
  case '4': printf(" ━"); break;
  case '5': printf("┃ "); break;
  default: printf(" ");
  }
 }
 printf("\n");
 }
}
void food(point * foodAt, point body[], int length, char map[][SIZE]) //生成食物
{
 int i;
 while (1)
 {
 foodAt->r = rand() % SIZE, foodAt->c = rand() % SIZE; //随机生成食物位置
 for (i = 0; i < length; i++)
 {
  if (foodAt->r == body[i].r && foodAt->c == body[i].c) //如果该位置在蛇的身体上
  {
  goto retry;
  }
 }
 break;
 retry:;
 }
}

//队列相关的函数
point * pop(queue *queue) //从队列中取出,返回取出点的指针
{
 queue->num--;
 if (queue->first_in_pos == 5 * SIZE - 1) //返回第一进入队列的点,并将变量fist_in_pos改变
 {
 queue->first_in_pos = 0;
 return queue->body[5 * SIZE - 1];
 }
 else
 {
 return queue->body[queue->first_in_pos++];
 }
}
void push(point * body, queue *queue)
{
 if (queue->num == 0) //如果队列已空
 {
 queue->num++;
 queue->first_in_pos = 0; //将第一进入队列的位置设为0
 queue->body[queue->first_in_pos] = body;
 }
 else //否则插入队列
 {
 if (queue->first_in_pos + queue->num > 5 * SIZE - 1)
 {
  queue->body[queue->first_in_pos + queue->num++ - 5 * SIZE] = body;
 }
 else
 {
  queue->body[queue->first_in_pos + queue->num++] = body;
 }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C语言链表实现贪吃蛇游戏

    阅读学习了源代码,并做了简单的注释和修改,里面只用了链表数据结构,非常适合C语言入门者学习阅读. 程序可在VS2013下编译运行. #include<stdio.h> #include<time.h> #include<windows.h> #include<stdlib.h> #define U 1 #define D 2 #define L 3 #define R 4 //蛇的状态,U:上 :D:下:L:左 R:右 typedef struct SNAK

  • 贪吃蛇C语言代码实现(难度可选)

    本文实例为大家分享了C语言实现贪吃蛇的具体代码,供大家参考,具体内容如下 /********************************************************* ********************贪吃蛇(难度可选)******************** **************制作者:Xu Lizi 日期:2012/12/31******** ********************部分函数有借鉴************************ ****

  • C语言贪吃蛇经典小游戏

    一.贪吃蛇小游戏简介: 用上下左右控制蛇的方向,寻找吃的东西,每吃一口就能得到一定的积分,而且蛇的身子会越吃越长,身子越长玩的难度就越大,不能碰墙,也不能咬到自己的身体,等到了一定的分数,就能过关. 二.函数框架 三.数据结构 typedef struct Snake { size_t x; //行 size_t y; //列 struct Snake* next; }Snake, *pSnake; 定义蛇的结构体,利用单链表来表示蛇,每个结点为蛇身体的一部分. 四.代码实现(vs2010  c

  • C语言手把手教你实现贪吃蛇AI(上)

    本文实例为大家分享了手把手教你实现贪吃蛇AI的具体步骤,供大家参考,具体内容如下 1. 目标 编写一个贪吃蛇AI,也就是自动绕过障碍,去寻找最优路径吃食物. 2. 问题分析 为了达到这一目的,其实很容易,总共只需要两步,第一步抓一条蛇,第二步给蛇装一个脑子.具体来说就是,首先我们需要有一条普通的贪吃蛇,也就是我们常玩儿的,手动控制去吃食物的贪吃蛇:然后给这条蛇加入AI,也就是通过算法控制,告诉蛇怎么最方便的绕开障碍去吃食物.为了讲清楚这个问题,文章将分为三部分:上,写一个贪吃蛇程序:中,算法基础

  • C语言实现贪吃蛇游戏

    最近整理下电脑,看到了自己在上个学期打的贪吃蛇游戏的c代码,觉得真的是略微有点冗长,但是实现起来应该也算是比较好理解,于是把自己的代码搬上来,网络上写贪吃蛇的c语言的文章很多,我这篇也仅是给大家作为一个参考而已. 我的代码是在Windows下运行的,因为需要用到windows.h这个库. 然后也做了一个简单的ai模式,这在没有障碍物的情况下前期还是蛮不错的,但是到了后期蛇变长了之后就会有bug了. 好了,直接上代码吧: 1)头文件和宏定义 #include<stdio.h> #include&

  • C语言手把手教你实现贪吃蛇AI(中)

    手把手教你实现贪吃蛇AI,具体内容如下 1. 目标 这一部分主要是讲解编写贪吃蛇AI所需要用到的算法基础. 2. 问题分析 贪吃蛇AI说白了就是寻找一条从蛇头到食物的一条最短路径,同时这条路径需要避开障碍物,这里仅有的障碍就是蛇身.而A star 算法就是专门针对这一个问题的.在A star 算法中需要用到排序算法,这里采用堆排序(当然其他排序也可以),如果对堆排序不熟悉的朋友,请移步到这里--堆排序,先看看堆排序的内容. 3. A*算法 A star(也称A*)搜寻算法俗称A星算法.这是一种在

  • 70行C语言代码实现贪吃蛇

    本文实例为大家分享了C语言实现贪吃蛇的具体代码,供大家参考,具体内容如下 #include <stdio.h> #include <Windows.h> #include <conio.h> #include <time.h> #define MAX_WIDE 50 #define MAX_HIGH 16 short dx = 1, dy = 0, randxy, score = 0; COORD coord; struct Snake{ short len

  • C语言手把手教你实现贪吃蛇AI(下)

    本文实例为大家分享了C语言实现贪吃蛇AI的具体代码,供大家参考,具体内容如下 1. 目标 这一部分的目标是把之前写的贪吃蛇加入AI功能,即自动的去寻找食物并吃掉. 2. 控制策略 为了保证蛇不会走入"死地",所以蛇每前进一步都需要检查,移动到新的位置后,能否找到走到蛇尾的路径,如果可以,才可以走到新的位置:否则在当前的位置寻找走到蛇尾的路径,并按照路径向前走一步,开始循环之前的操作,如下图所示.这个策略可以工作,但是并不高效,也可以尝试其他的控制策略,比如易水寒的贪吃蛇AI 运行效果如

  • C语言结构数组实现贪吃蛇小游戏

    一.设计思路 蛇身本质上就是个结构数组,数组里存储了坐标x.y的值,再通过一个循环把它打印出来,蛇的移动则是不断地刷新重新打印.所以撞墙.咬到自己只是数组x.y值的简单比较. 二.用上的知识点 结构数组Windows API函数 三.具体实现 先来实现静态页面,把地图.初始蛇身.食物搞定. 这里需要用到Windows API的知识,也就是对控制台上坐标的修改 //这段代码来自参考1 void Pos(int x, int y) { COORD pos; HANDLE hOutput; pos.X

  • 基于C语言实现的贪吃蛇游戏完整实例代码

    本文以实例的形式讲述了基于C语言实现的贪吃蛇游戏代码,这是一个比较常见的游戏,代码备有比较详细的注释,对于读者理解有一定的帮助. 贪吃蛇完整实现代码如下: #include <graphics.h> #include <conio.h> #include <stdlib.h> #include <dos.h> #define NULL 0 #define UP 18432 #define DOWN 20480 #define LEFT 19200 #defi

随机推荐