大佬教程收集整理的这篇文章主要介绍了C语言手把手教你实现贪吃蛇AI(下),大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
本文实例为大家分享了C语言实现贪吃蛇AI的具体代码,供大家参考,具体内容如下
1. 目标@H_801_4@
这一部分的目标是把之前写的贪吃蛇加入AI功能,即自动的去寻找食物并吃掉。
2. 控制策略
@H_801_4@
为了保证蛇不会走入“死地”,所以蛇每前进一步都需要检查,移动到新的位置后,能否找到走到蛇尾的路径,如果可以,才可以走到新的位置;否则在当前的位置寻找走到蛇尾的路径,并按照路径向前走一步,开始循环之前的操作,如下图所示。这个策略可以工作,但是并不高效,也可以尝试其他的控制策略,比如易水寒的贪吃蛇AI
运行效果如下:
3. 源代码
@H_801_4@
需要注意的是,由于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 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),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 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 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,i - 1); } } //x1,y1是邻域点坐标 //curtnode是当前点坐标 //endnode是目标点坐标 voID insert_opentable(int x1,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,endnodE); } if (y + 1 <= N + 1) { insert_opentable(x,y + 1,endnodE); } if (y - 1 >= 2) { insert_opentable(x,y - 1,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语言手把手教你实现贪吃蛇AI(下)所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。