前提:本文實現AI貪吃蛇自行對戰,加上人機對戰,讀者可再次基礎上自行添加電腦VS電腦和玩家VS玩家(其實把人機對戰寫完,這2個都沒什麼了,思路都一樣)
實現效果:
具體功能:
1.智能模式:電腦自己玩(自己吃食物)
2.人機對戰:電腦和人操作(在上步的基礎上加一個鍵盤控制的貪吃蛇即可)
實現環境:
Pycharm + Python3.6 + Curses + Win10
具體過程:
一:配置環境:
Curses: 參考鏈接 (Cp後面代表本地Python環境,別下錯了) ( Stackoverflow 真的是個非常好的地方)
二:
1.靈感來源+參考鏈接:
http://www.hawstein.com/posts/snake-ai.html (Chrome有時候打不開,Firefox可以打開)
2.算法思路:
AI算法:https://www.cnblogs.com/21207-iHome/p/6048969.html (本人之前接觸過,當時講課老師說是自動尋路算法,我感覺和BFS+DFS一樣,結果沒想到居然是AI算法)
BFS+DFS(略)
第一步是能制作一個 基本的貪吃蛇 ,熟悉Curses的相關環境(最好別對蛇和食物使用特殊字符,在windows環境下會導致像素延遲,非常丑)
#curses官方手冊:
https://docs.python.org/3.5/library/curses.html#module-curses
#curses參考手冊:
https://blog.csdn.net/chenxiaohua/article/details/2099304
具體思路:
熟悉Curses中相關指令後基本就沒什麼了, 保證按的下一個鍵不導致蛇死亡,保證蛇吃食物後食物不在蛇身上,保證蛇碰到自己和邊框就死亡,如果按其他鍵,會導致頭被插入2次,從而讓蛇死亡。(具體見代碼分析)
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # @Time : 2018/11/5 17:08
4 # @Author : Empirefree
5 # @File : 貪吃蛇-01.py
6 # @Software: PyCharm Community Edition
7
8 #curses官方手冊:https://docs.python.org/3.5/library/curses.html#module-curses
9 #curses參考手冊:https://blog.csdn.net/chenxiaohua/article/details/2099304
10
11 # 基本思路:while循環,讓蛇一直右走(直到按鍵,如果按了其他鍵就會導致蛇頭被重復插入1次到snake中,
12 # 繼而第二次循環就會退出),蛇是每次自動增長,但是每次沒吃到食物就會pop尾部(snake放在dict中,類似鏈表),按鍵檢查就是只能按方向鍵
13 # 按方向鍵也存在判別是否出錯(按了up後又按down),然後對於死亡情況就是碰到周圍和自己
14
15 # 1.蛇的移動和吃食物後的變化
16 # 2.按鍵:按其他鍵和方向鍵
17 # 3.死亡判斷
18
19 import curses
20 import random
21
22 # 開啟curses
23 def Init_Curse():
24 global s
25 s = curses.initscr()
26 curses.curs_set(0) #能見度光標,寫錯了哇
27 curses.noecho()
28 curses.cbreak() #立即得到響應
29 s.keypad(True) #特殊處理鍵位,返回KEY_LEFT
30
31 #關閉並回到終端
32 def Exit_Curse():
33 curses.echo()
34 curses.nocbreak()
35 s.keypad(False)
36 curses.endwin()
37
38 def Start_Game():
39 # 窗口化操作
40 y, x = s.getmaxyx() # curses中是y,x
41 w = curses.newwin(y, x, 0, 0)
42 w.keypad(1)
43 w.timeout(100)
44
45 # 初始化蛇的位置,並用dict存儲
46 snake_x = int(x / 4)
47 snake_y = int(y / 2)
48 snake = [[snake_y, snake_x], [snake_y, snake_x - 1], [snake_y, snake_x - 2]]
49
50 # 初始化食物
51 food_pos = [int(y / 2), int(x / 2)]
52 w.addch(food_pos[0], food_pos[1], '@') # 用@顯示食物字元
53
54 key = curses.KEY_RIGHT # 得到右方向鍵
55
56 # 開始,為什麼我感覺True比1看的爽一些
57 while True:
58 next_key = w.getch() # 等待輸入,傳回整數
59 print(next_key, 'QAQ')
60 # 防止Error
61 if next_key != -1:
62 if key == curses.KEY_RIGHT and next_key != curses.KEY_LEFT
63 or key == curses.KEY_LEFT and next_key != curses.KEY_RIGHT
64 or key == curses.KEY_DOWN and next_key != curses.KEY_UP
65 or key == curses.KEY_UP and next_key != curses.KEY_DOWN:
66 key = next_key
67
68 # 蛇死亡, 當蛇頭碰到蛇身或牆壁
69 if snake[0][0] in [0, y] or snake[0][1] in [0, x] or snake[0] in snake[1:]:
70 # print(snake[0], snake[1]) 按下其他鍵就會導致,new_head被插入2次,從而退出
71 curses.endwin()
72 print('!!!游戲結束!!!')
73 quit()
74
75 #按鍵移動
76 tempy = snake[0][0]
77 tempx = snake[0][1]
78 new_head = [tempy, tempx]
79 if key == curses.KEY_RIGHT:
80 new_head[1] += 1
81 elif key == curses.KEY_LEFT:
82 new_head[1] -= 1
83 elif key == curses.KEY_UP:
84 new_head[0] -= 1
85 elif key == curses.KEY_DOWN:
86 new_head[0] += 1
87 snake.insert(0, new_head) #保留蛇頭,根據按鍵更新蛇頭
88
89 #食物位置
90 if snake[0] == food_pos:
91 food_pos = None
92 while food_pos is None:
93 new_food = [random.randint(1, y - 1), random.randint(1, x - 1)]
94 if new_food not in snake:
95 food_pos = new_food
96 w.addch(food_pos[0], food_pos[1], '@') #再次添加食物,保證食物不在蛇上
97 else:
98 tail = snake.pop() #dict直接pop尾部
99 w.addch(tail[0], tail[1], ' ')
100
101 w.addch(snake[0][0], snake[0][1], 'Q')
102
103 if __name__ == '__main__':
104 Init_Curse()
105 Start_Game()
106
107 print('QAQ')
108 Exit_Curse()
基本貪吃蛇
3.代碼剖析:
[紅色為代碼所需函數]
(蛇每走一步,就更新snake距離food的board距離,涉及 board_rest (更新每個非snake元素距離food的距離)和 board_refresh (本文這裡采用BFS算法)),尋找到best_move,然後讓蛇移動即可
如果吃的到食物( find_safe_way ):----> 放出虛擬蛇( virtual_shortest_move )(防止蛇吃完食物就被自己繞死)
如果虛擬蛇吃完食物還可以找到 蛇尾(出的去)( is_tail_inside )
直接吃食物( choose_shortest_safe_move )
反之,出不去:
就跟著尾巴走( follow_tail )就好比一直上下繞,就絕對不會死,但是蛇就完全沒有靈性
如果吃不到食物
跟著尾巴(走最遠的路( choose_longest_safe_move )),四個方向走(如果是A*算法需要將8個方向改成4個方向)
如果上訴方法都不行,就涉及到a ny_possible_move ,挑選距離最小的走(這裡就會涉及到將自己吃死,有待改進)
(通過以上方法,就可以制造一個基本AI貪吃蛇了,當然,還有很多細節方面東西需要考慮)
報錯:
win = curses.newwin(HEIGHT, WIDTH, 0, 0)
_curses.error: curses function returned NULL
原因:Pycharm下面(或者cmd、exe太小,需要拉大點)
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # @Time : 2018/11/16 14:26
4 # @Author : Empirefree
5 # @File : 貪吃蛇-03.py
6 # @Software: PyCharm Community Edition
7
8 import curses
9 from curses import KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN
10 from random import randint
11
12 # 必須要弄成全局哇,不然需要用到的數據太多了
13 # 1.初始化界面
14 # 2.更新地圖,判斷是否可以吃到食物
15 # 3.如果可以吃到,放出虛擬蛇(這裡又設計到地圖更新(board_reset),記錄距離(board_refresh)操作)
16 # 3.1虛擬蛇若吃食物距離蛇尾有路徑(直接吃),否則,追蛇尾
17 # 3.2若吃不到,則追蛇尾
18 # 4.更新best_move,改變距離
19 ###########################################################################################
20 #作者:
21 print('**************************************************************************')
22 print('*****************!!!歡迎使用AI貪吃蛇 !!!*************************')
23 print('*****************作者:胡宇喬 *********************')
24 print('*****************工具: Pycharm *********************')
25 print('*****************
成功解決TypeError: only size-1 arr
About Python This is the month