C語(yǔ)言手把手教你實(shí)現(xiàn)貪吃蛇AI(下)
本文實(shí)例為大家分享了C語(yǔ)言實(shí)現(xiàn)貪吃蛇AI的具體代碼,供大家參考,具體內(nèi)容如下
1. 目標(biāo)
這一部分的目標(biāo)是把之前寫(xiě)的貪吃蛇加入AI功能,即自動(dòng)的去尋找食物并吃掉。
2. 控制策略
為了保證蛇不會(huì)走入“死地”,所以蛇每前進(jìn)一步都需要檢查,移動(dòng)到新的位置后,能否找到走到蛇尾的路徑,如果可以,才可以走到新的位置;否則在當(dāng)前的位置尋找走到蛇尾的路徑,并按照路徑向前走一步,開(kāi)始循環(huán)之前的操作,如下圖所示。這個(gè)策略可以工作,但是并不高效,也可以嘗試其他的控制策略,比如易水寒的貪吃蛇AI

運(yùn)行效果如下:

3. 源代碼
需要注意的是,由于mapnode的數(shù)據(jù)量比較大,這里需要把棧的大小設(shè)置大一點(diǎn),如下圖所示,否則會(huì)出現(xiàn)棧溢出的情況。

整個(gè)項(xiàng)目由以下三個(gè)文件組成:
a. snake AI.h
#ifndef SNAKE_H_
#define SNAKE_H_
#include<stdio.h>
#include<Windows.h> //SetConsoleCursorPosition, sleep函數(shù)的頭函數(shù)
#include<time.h> //time()的頭函數(shù)
#include<malloc.h> //malloc()的頭函數(shù)
#define N 32 //地圖大小
#define snake_mark '#'//表示蛇身
#define food_mark '$'//表示食物
#define sleeptime 50//間隔時(shí)間
#define W 10//權(quán)重
typedef struct STARNODE{
int x;//節(jié)點(diǎn)的x,y坐標(biāo)
int y;
int G;//該節(jié)點(diǎn)的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;//該節(jié)點(diǎn)的父節(jié)點(diǎn)
} 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;
/*表示蛇身坐標(biāo)的結(jié)構(gòu)體*/
typedef struct SNAKE{
int x; //行坐標(biāo)
int y; //列坐標(biāo)
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"
/*控制光標(biāo)的坐標(biāo)*/
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;
//打印上下邊框(每個(gè)■占用一行兩列)
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);
}
/*初始化蛇身*/
/*蛇身初始化坐標(biāo)為(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;
}
//根據(jù)指針?biāo)赶虻墓?jié)點(diǎn)的F值,按大頂堆進(jìn)行調(diào)整
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是鄰域點(diǎn)坐標(biāo)
//curtnode是當(dāng)前點(diǎn)坐標(biāo)
//endnode是目標(biāo)點(diǎn)坐標(biāo)
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)//如果已經(jīng)在opentable中
{
if (mapnode[x1][y1].G > pcurtnode->G + W)//但是不是最優(yōu)路徑
{
mapnode[x1][y1].G = pcurtnode->G + W;//把G值更新(變?。?
mapnode[x1][y1].ParentNode = pcurtnode;//把該鄰點(diǎn)的雙親節(jié)點(diǎn)更新
//由于改變了opentable中一個(gè)點(diǎn)的F值,需要對(duì)opentable中的點(diǎn)的順序進(jìn)行調(diào)整,以滿足有序
for (i = 1; i <= opennode_count; i++)
{
if (opentable[i]->x == x1 && opentable[i]->y == y1)
{
break;
}
}
heapsort(opentable, i);
}
}
else//如果不在opentable中,把該點(diǎn)加入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);
}
}
}
//尋找當(dāng)前點(diǎn)的四鄰域點(diǎn),把符合條件的點(diǎn)加入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指向蛇頭所對(duì)應(yīng)的結(jié)點(diǎn)
opentable[++opennode_count] = startnode;//起始點(diǎn)加入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個(gè)節(jié)點(diǎn)加入closetable中
if (!opennode_count)//如果opentable已經(jīng)為空,即沒(méi)有找到路徑
{
//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記錄蛇頭下一步應(yīng)該移動(dòng)的位置
{
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;
}
//蛇尾向前移動(dòng)一格,并把原來(lái)的蛇尾注銷(xiāo)
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;//將蛇尾注銷(xiāo)掉
}
//將蛇身移動(dòng)到指定的位置(nextnode),并打印出來(lái)
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);//釋放蛇尾的內(nèi)存空間
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)//如果能找到到達(dá)食物的路徑
{
x = nextnode->x;
y = nextnode->y;
tsnake=create_tsnake();
mapnode[x][y].is_snakebody = 1;
t = search_snaketail(tsnake);//走到下一個(gè)節(jié)點(diǎn)后,能否找到更新后的蛇尾
if (t==1)//如果按照路徑走到下一個(gè)位置,可以找到蛇尾,就把蛇頭移動(dòng)到下一個(gè)位置
{
nextnode->x = x;
nextnode->y = y;
Sleep(sleeptime);
snake_move();
}
else//否則,從該點(diǎn)出發(fā)去找蛇尾
{
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;//蛇頭下一步該走的結(jié)點(diǎn)
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;
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 基于C語(yǔ)言實(shí)現(xiàn)的貪吃蛇游戲完整實(shí)例代碼
- 貪吃蛇C語(yǔ)言代碼實(shí)現(xiàn)(難度可選)
- C語(yǔ)言貪吃蛇經(jīng)典小游戲
- C語(yǔ)言實(shí)現(xiàn)貪吃蛇游戲
- C語(yǔ)言鏈表實(shí)現(xiàn)貪吃蛇游戲
- C語(yǔ)言手把手教你實(shí)現(xiàn)貪吃蛇AI(上)
- C語(yǔ)言實(shí)現(xiàn)貪吃蛇小游戲
- 70行C語(yǔ)言代碼實(shí)現(xiàn)貪吃蛇
- C語(yǔ)言結(jié)構(gòu)數(shù)組實(shí)現(xiàn)貪吃蛇小游戲
- C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的貪吃蛇小游戲
相關(guān)文章
淺談在函數(shù)中返回動(dòng)態(tài)的內(nèi)存
下面小編就為大家?guī)?lái)一篇淺談在函數(shù)中返回動(dòng)態(tài)的內(nèi)存。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12
C++ 動(dòng)態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)
這篇文章主要介紹了C++ 動(dòng)態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)的相關(guān)資料,需要的朋友可以參考下2015-06-06
詳解C++的反調(diào)試技術(shù)與繞過(guò)手法
反調(diào)試技術(shù),惡意代碼會(huì)用它識(shí)別自身是否被調(diào)試,或者讓調(diào)試器失效,給反病毒工程師們制造麻煩,拉長(zhǎng)提取特征碼的時(shí)間線,本章將具體總結(jié)常見(jiàn)的反調(diào)試基礎(chǔ)的實(shí)現(xiàn)原理以及如何過(guò)掉這些反調(diào)試手段,從而讓我們能夠繼續(xù)分析惡意代碼2021-06-06
一起來(lái)學(xué)習(xí)C語(yǔ)言的輸入和輸出
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言的輸入和輸出,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03
C語(yǔ)言入門(mén)學(xué)習(xí)筆記之typedef簡(jiǎn)介
typedef為C語(yǔ)言的關(guān)鍵字,作用是為一種數(shù)據(jù)類(lèi)型定義一個(gè)新名字,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言入門(mén)學(xué)習(xí)筆記之typedef簡(jiǎn)介的相關(guān)資料,需要的朋友可以參考下2021-11-11
C++結(jié)構(gòu)體與類(lèi)的區(qū)別詳情
這篇文章主要介紹了C++結(jié)構(gòu)體與類(lèi)的區(qū)別,C++中的struct對(duì)C中的struct進(jìn)行了擴(kuò)充,它已經(jīng)不再只是一個(gè)包含不同數(shù)據(jù)類(lèi)型的數(shù)據(jù)結(jié)構(gòu)了,它已經(jīng)獲取了太多的功能。下面我們一起進(jìn)入文章倆姐具體內(nèi)容,需要的朋友也可以參考一下2021-11-11
記逆向小白的第一次vbsedit 9爆破及內(nèi)存補(bǔ)丁制作過(guò)程
這篇文章主要介紹了記逆向小白的第一次vbsedit 9爆破及內(nèi)存補(bǔ)丁制作過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04

