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

2025-05-27 0 54

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

1. 目标

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

2. 控制策略

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

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

运行效果如下:

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

3. 源代码

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

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

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

a. snake AI.h

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63
#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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434
#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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33
#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;

}

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

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 C语言手把手教你实现贪吃蛇AI(下) https://www.kuaiidc.com/73015.html

相关文章

发表评论
暂无评论