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

本文实例为大家分享了C语言实现贪吃蛇AI的具体代码,供大家参考,具体内容如下

1. 目标

这一部分的目标是把之前写的贪吃蛇加入AI功能,即自动的去寻找食物并吃掉。

2. 控制策略

为了保证蛇不会走入“死地”,所以蛇每前进一步都需要检查,移动到新的位置后,能否找到走到蛇尾的路径,如果可以,才可以走到新的位置;否则在当前的位置寻找走到蛇尾的路径,并按照路径向前走一步,开始循环之前的操作,如下图所示。这个策略可以工作,但是并不高效,也可以尝试其他的控制策略,比如易水寒的贪吃蛇AI

运行效果如下:

3. 源代码

需要注意的是,由于mapnode的数据量比较大,这里需要把栈的大小设置大一点,如下图所示,否则会出现栈溢出的情况。

整个项目由以下三个文件组成:

a. snake AI.h

#ifndef SNAKE_H_
#define SNAKE_H_
#include<stdio.h>
#include<Windows.h> //SetConsoleCursorPosition, sleep函数的头函数
#include<time.h> //time()的头函数
#include<malloc.h>  //malloc()的头函数
#define N 32 //地图大小
#define snake_mark '#'//表示蛇身
#define food_mark '$'//表示食物
#define sleeptime 50//间隔时间 

#define W 10//权重 

typedef struct STARNODE{
  int x;//节点的x,y坐标
  int y;
  int G;//该节点的G, H值
  int H;
  int is_snakebody;//是否为蛇身,是为1,否则为0;
  int in_open_table;//是否在open_table中,是为1,否则为0;
  int in_close_table;//是否在close_table中,是为1,否则为0;
  struct STARNODE* ParentNode;//该节点的父节点
} starnode, *pstarnode; 

extern starnode (*mapnode)[N + 4];
extern pstarnode opentable[N*N / 2];
extern pstarnode closetable[N*N / 2]; 

extern int opennode_count;
extern int closenode_count; 

/*表示蛇身坐标的结构体*/
typedef struct SNAKE{
  int x; //行坐标
  int y; //列坐标
  struct SNAKE* next;
}snake_body, *psnake;
extern psnake snake;
extern psnake food;
extern psnake snaketail;
extern psnake nextnode; 

void set_cursor_position(int x, int y);
void initial_map();
void initial_mapnode();
void update_mapnode();
void printe_map();
void initial_snake();
void create_food();
int is_food();
void heapadjust(pstarnode a[], int m, int n);
void swap(pstarnode a[], int m, int n);
void crtheap(pstarnode a[], int n);
void heapsort(pstarnode a[], int n);
void insert_opentable(int x1, int y1, pstarnode pcurtnode, psnake endnode);
void find_neighbor(pstarnode pcurtnode, psnake endnode);
int search_short_road(psnake snakehead, psnake endnode);
int search_snaketail(psnake snakehead);
void update_snaketail(psnake snakehead);
void snake_move();
psnake create_tsnake();
void snake_control();
#endif

b. source.cpp

#include"Snake AI.h" 

/*控制光标的坐标*/
void set_cursor_position(int x, int y)
{
  COORD coord = { x, y };//x表示列,y表示行。
  SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
} 

/*初始化后的地图为 N列 N/2行*/
/*游戏的空间为2至N+1列,1至N/2行*/
void initial_map()
{
  int i = 0; 

  //打印上下边框(每个■占用一行两列)
  for (i = 0; i<N / 2 + 2; i++)
  {
    set_cursor_position(22 * i, 0);
    printf("■");
    set_cursor_position(22 * i, N / 2 + 1);
    printf("■");
  }
  for (i = 0; i<N / 2 + 2; i++)  //打印左右边框
  {
    set_cursor_position(0, i);
    printf("■");
    set_cursor_position(N + 2, i);
    printf("■");
  }
} 

//初始化mapnode
void initial_mapnode()
{
  int i = 0, j = 0;
  for (i = 0; i < N / 2 + 2; i++)
    for (j = 0; j < N + 4; j++)
    {
      mapnode[i][j].G = 0;
      mapnode[i][j].H = 0;
      mapnode[i][j].in_close_table = 0;
      mapnode[i][j].in_open_table = 0;
      mapnode[i][j].is_snakebody = 0;
      mapnode[i][j].ParentNode = NULL;
      mapnode[i][j].x = i;
      mapnode[i][j].y = j;
    }
} 

//初始化mapnode
void update_mapnode()
{
  psnake temp = snake;
  int x, y; 

  initial_mapnode();//初始化mapnode 

  while (temp)
  {
    x = temp->x;
    y = temp->y;
    mapnode[x][y].is_snakebody = 1;
    temp = temp->next;
  }
} 

void printe_map()
{
  psnake temp = snake;
  while (temp)
  {
    set_cursor_position(temp->y, temp->x);
    printf("%c", snake_mark);
    temp = temp->next;
  }
  if (food)
    set_cursor_position(food->y, food->x);
  printf("%c", food_mark);
  set_cursor_position(0, N / 2 + 2);
} 

/*初始化蛇身*/
/*蛇身初始化坐标为(8,5),(8,4), (8,3) */
void initial_snake()
{
  int i = 5;//列
  int j = N / 4;//行
  psnake tsnake = NULL, temp = NULL; 

  snake = (psnake)malloc(sizeof(snake_body));
  (snake)->x = j;
  (snake)->y = i;
  (snake)->next = NULL;
  tsnake = snake; 

  for (i = 4; i >2; i--)
  {
    temp = (psnake)malloc(sizeof(snake_body));
    (temp)->x = j;
    (temp)->y = i;
    (temp)->next = NULL;
    (tsnake)->next = (temp);
    (tsnake) = (tsnake)->next;
  } 

  snaketail = tsnake;
} 

//生成食物
void create_food()
{
  srand((unsigned)time(NULL));
  food->y = rand() % N + 2;//列
  food->x = rand() % (N / 2) + 1;//行 

  //检查食物是否和蛇身重回
  update_mapnode();
  if (mapnode[food->x][food->y].is_snakebody)
  {
    create_food();
  }
} 

//判断是否吃到食物,吃到食物返回 1,否则返回 0;
int is_food()
{
  if (snake->x == food->x && snake->y == food->y)
    return 1;
  return 0;
} 

//根据指针所指向的节点的F值,按大顶堆进行调整
void heapadjust(pstarnode a[], int m, int n)
{
  int i;
  pstarnode temp = a[m];
  for (i = 22 * m; i <= n; i *= 2)
  {
    if (i + 1 <= n && (a[i + 1]->G + a[i + 1]->H)>(a[i]->G + a[i]->H))
    {
      i++;
    }
    if ((temp->G + temp->H)>(a[i]->G + a[i]->H))
    {
      break;
    }
    a[m] = a[i];
    m = i;
  }
  a[m] = temp;
} 

void swap(pstarnode a[], int m, int n)
{
  pstarnode temp;
  temp = a[m];
  a[m] = a[n];
  a[n] = temp;
} 

void crtheap(pstarnode a[], int n)
{
  int i;
  for (i = n / 2; i>0; i--)
  {
    heapadjust(a, i, n);
  }
} 

void heapsort(pstarnode a[], int n)
{
  int i;
  crtheap(a, n);
  for (i = n; i>1; i--)
  {
    swap(a, 1, i);
    heapadjust(a, 1, i - 1);
  }
} 

//x1, y1是邻域点坐标
//curtnode是当前点坐标
//endnode是目标点坐标
void insert_opentable(int x1, int y1, pstarnode pcurtnode, psnake endnode)
{
  int i = 1;
  if (!mapnode[x1][y1].is_snakebody && !mapnode[x1][y1].in_close_table)//如果不是蛇身也不在closetable中
  {
    if (mapnode[x1][y1].in_open_table)//如果已经在opentable中
    {
      if (mapnode[x1][y1].G > pcurtnode->G + W)//但是不是最优路径
      {
        mapnode[x1][y1].G = pcurtnode->G + W;//把G值更新(变小)
        mapnode[x1][y1].ParentNode = pcurtnode;//把该邻点的双亲节点更新
        //由于改变了opentable中一个点的F值,需要对opentable中的点的顺序进行调整,以满足有序
        for (i = 1; i <= opennode_count; i++)
        {
          if (opentable[i]->x == x1 && opentable[i]->y == y1)
          {
            break;
          }
        }
        heapsort(opentable, i);
      }
    }
    else//如果不在opentable中,把该点加入opentable中
    {
      opentable[++opennode_count] = &mapnode[x1][y1]; 

      mapnode[x1][y1].G = pcurtnode->G + W;
      mapnode[x1][y1].H = (abs(endnode->x - x1) + abs(endnode->y - y1))*W;
      mapnode[x1][y1].in_open_table = 1;
      mapnode[x1][y1].ParentNode = pcurtnode;
      heapsort(opentable, opennode_count);
    }
  }
} 

//寻找当前点的四邻域点,把符合条件的点加入opentable中
void find_neighbor(pstarnode pcurtnode, psnake endnode)
{
  int x;
  int y;
  x = pcurtnode->x;
  y = pcurtnode->y; 

  if (x + 1 <= N / 2)
  {
    insert_opentable(x + 1, y, pcurtnode, endnode);
  }
  if (x - 1 >= 1)
  {
    insert_opentable(x - 1, y, pcurtnode, endnode);
  }
  if (y + 1 <= N + 1)
  {
    insert_opentable(x, y + 1, pcurtnode, endnode);
  }
  if (y - 1 >= 2)
  {
    insert_opentable(x, y - 1, pcurtnode, endnode);
  }
} 

int search_short_road(psnake snakehead, psnake endnode)
{
  int is_search_short_road = 0;
  opennode_count = 0;
  closenode_count = 0;
  pstarnode pcurtnode;
  pstarnode temp;
  pstarnode startnode = &mapnode[snakehead->x][snakehead->y];//startnode指向蛇头所对应的结点 

  opentable[++opennode_count] = startnode;//起始点加入opentable中
  startnode->in_open_table = 1;
  startnode->ParentNode = NULL;
  startnode->G = 0;
  startnode->H = (abs(endnode->x - startnode->x) + abs(endnode->y - startnode->y))*W; 

  while (1)
  {
    //取出opentable中第1个节点加入closetable中
    if (!opennode_count)//如果opentable已经为空,即没有找到路径
    {
      //printf("No way");
      return is_search_short_road;
    }
    pcurtnode = opentable[1];
    opentable[1] = opentable[opennode_count--]; 

    closetable[++closenode_count] = pcurtnode;
    pcurtnode->in_open_table = 0;
    pcurtnode->in_close_table = 1; 

    if (pcurtnode->x == endnode->x && pcurtnode->y == endnode->y)
    {
      is_search_short_road = 1;
      break;
    } 

    find_neighbor(pcurtnode, endnode); 

  }
  if (is_search_short_road)//如果找到,则用nextnode记录蛇头下一步应该移动的位置
  { 

    temp = closetable[closenode_count];
    while (temp->ParentNode->ParentNode)
    {
      temp = temp->ParentNode;
    }
    nextnode->x = temp->x;
    nextnode->y = temp->y;
    nextnode->next = NULL;
  } 

  return is_search_short_road;
} 

int search_snaketail(psnake snakehead)
{
  int t = 0;
  update_mapnode();
  mapnode[snaketail->x][snaketail->y].is_snakebody = 0;
  t = search_short_road(snakehead, snaketail);
  mapnode[snaketail->x][snaketail->y].is_snakebody = 1;
  return t;
} 

//蛇尾向前移动一格,并把原来的蛇尾注销
void update_snaketail(psnake snakehead)
{
  psnake temp;
  temp = snakehead;
  while (temp->next->next)
  {
    temp = temp->next;
  }
  snaketail = temp;
  temp = temp->next;
  mapnode[temp->x][temp->y].is_snakebody = 0;//将蛇尾注销掉
} 

//将蛇身移动到指定的位置(nextnode),并打印出来
void snake_move()
{
  psnake snake_head = (psnake)malloc(sizeof(snake_body)); 

  snake_head->x = nextnode->x;
  snake_head->y = nextnode->y;
  snake_head->next = snake;
  snake = snake_head; 

  if (is_food())//如果是食物
  {
    create_food();
    printe_map();
  } 

  else//不是食物
  {
    psnake temp = snake_head;
    while (temp->next->next)//寻找蛇尾
    {
      temp = temp->next;
    }
    snaketail = temp;//更新snaketail的位置 

    set_cursor_position(temp->next->y, temp->next->x);
    printf(" ");//把蛇尾用空格消掉
    free(temp->next);//释放蛇尾的内存空间
    temp->next = NULL;//将temp的next置成NULL
    printe_map();
  }
  snake=snake_head;
} 

psnake create_tsnake()
{
  psnake tsnake = (psnake)malloc(sizeof(snake_body));
  tsnake->x = nextnode->x;
  tsnake->y = nextnode->y;
  tsnake->next = NULL;
  psnake temp1 = snake;
  psnake temp2 = tsnake; 

  while (temp1!=snaketail)
  {
    temp2->next = (psnake)malloc(sizeof(snake_body));
    temp2->next->x = temp1->x;
    temp2->next->y = temp1->y;
    temp2->next->next = NULL;
    temp1 = temp1->next;
    temp2 = temp2->next;
  }
  return tsnake;
} 

void snake_control()
{
  int r, t, x, y;
  psnake tsnake = NULL;; 

  while (1)
  { 

    r = 0;
    t = 0;
    x = 0;
    y = 0; 

    update_mapnode();
    r = search_short_road(snake, food);
    if (r == 1)//如果能找到到达食物的路径
    { 

      x = nextnode->x;
      y = nextnode->y; 

      tsnake=create_tsnake(); 

      mapnode[x][y].is_snakebody = 1; 

      t = search_snaketail(tsnake);//走到下一个节点后,能否找到更新后的蛇尾 

      if (t==1)//如果按照路径走到下一个位置,可以找到蛇尾,就把蛇头移动到下一个位置
      {
        nextnode->x = x;
        nextnode->y = y;
        Sleep(sleeptime);
        snake_move();
      }
      else//否则,从该点出发去找蛇尾
      {
        mapnode[x][y].is_snakebody = 0;
        search_snaketail(snake);
        Sleep(sleeptime);
        snake_move();
      }
      free(tsnake);
    }
    else//如果找不到食物
    {
      search_snaketail(snake);
      Sleep(sleeptime);
      snake_move();
    }
  }
}

c. main.cpp

#include"Snake AI.h" 

psnake snake = NULL;
psnake food = NULL;
psnake snaketail = NULL;
psnake nextnode = NULL;//蛇头下一步该走的结点 

starnode (*mapnode)[N+4]=(starnode(*)[N+4])malloc(sizeof(starnode)*(N/2+2)*(N+4));
pstarnode opentable[N*N / 2];
pstarnode closetable[N*N / 2]; 

int opennode_count = 0;
int closenode_count = 0; 

int main(void)
{
  initial_map();
  initial_snake();
  food = (psnake)malloc(sizeof(snake_body));
  nextnode = (psnake)malloc(sizeof(snake_body));
  food->next = NULL;
  create_food();
  food->x = 1;
  food->y = 3; 

  printe_map();
  snake_control(); 

  free(food);
  free(snake);
  free(mapnode);
  return 0;
}

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

您可能感兴趣的文章:

  • C语言手把手教你实现贪吃蛇AI(中)
  • 贪吃蛇C语言代码实现(难度可选)
  • C语言手把手教你实现贪吃蛇AI(上)
  • C语言贪吃蛇经典小游戏
  • C语言链表实现贪吃蛇游戏
  • C语言结构数组实现贪吃蛇小游戏
  • 基于C语言实现的贪吃蛇游戏完整实例代码
(0)

相关推荐

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

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

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

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

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

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

  • 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语言贪吃蛇经典小游戏

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

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

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

  • 基于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

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

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

  • C语言基于图形库实现双人贪吃蛇

    本文实例为大家分享了C语言基于图形库实现双人贪吃蛇的具体代码,供大家参考,具体内容如下 /* 蛇蛇大作战 作者:施瑞文 */   #include <conio.h> #include <graphics.h> #include <time.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <mmsystem.h> #pragma

  • Android手把手教大家制作APP首页(下拉刷新、自动加载)

    一.概述 作为一名三年Android开发经验的程序员,今天和大家一起实战一款APP的首页功能,这个首页在我们平时接触中还是很常见的,虽然页面简单,但是里面涉及的功能点还是挺多的.代码如有不足的还望各路同仁指点一二. 页面中使用的开发库: 整个首页架构使用的是LRecyclerView,包含下拉刷新和自动加载功能 compile 'com.github.jdsjlzx:LRecyclerView:1.3.3' 无限循环轮播图使用的是convenientbanner,效果还是很顺畅的,还可以根据自己

  • C语言实现贪吃蛇游戏

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

  • 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语言单链表贪吃蛇小游戏

    C语言实现单链表控制台贪吃蛇小游戏 编译环境:vs2019 需求: 统计游戏开始后的时间,控制贪吃蛇:吃到食物蛇身加长,得分加一:碰墙或蛇头碰到身体减一条生命:生命消耗完则结束游戏. 思路: 使用wasd键控制蛇的移动方向,蛇头碰到食物得分加一,并在地图上随机产生一个食物,累加得分,碰墙或碰自己减一条生命,并初始化整条蛇,生命值为0时结束游戏. 做法: 使用单链表控制贪吃蛇移动的核心思想就是:链表存储贪吃蛇所有坐标,每次循环贪吃蛇不断向一个方向插入一个新的结点作为新的蛇头,按下按键控制新蛇头产生

随机推荐