forked from colingogogo/gobang_AI
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgobang_AI.py
306 lines (244 loc) · 10.3 KB
/
gobang_AI.py
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
from graphics import *
from math import *
import numpy as np
GRID_WIDTH = 40
COLUMN = 15
ROW = 15
list1 = [] # AI
list2 = [] # human
list3 = [] # all
list_all = [] # 整个棋盘的点
next_point = [0, 0] # AI下一步最应该下的位置
ratio = 1 # 进攻的系数 大于1 进攻型, 小于1 防守型
DEPTH = 3 # 搜索深度 只能是单数。 如果是负数, 评估函数评估的的是自己多少步之后的自己得分的最大值,并不意味着是最好的棋, 评估函数的问题
# 棋型的评估分数
shape_score = [(50, (0, 1, 1, 0, 0)),
(50, (0, 0, 1, 1, 0)),
(200, (1, 1, 0, 1, 0)),
(500, (0, 0, 1, 1, 1)),
(500, (1, 1, 1, 0, 0)),
(5000, (0, 1, 1, 1, 0)),
(5000, (0, 1, 0, 1, 1, 0)),
(5000, (0, 1, 1, 0, 1, 0)),
(5000, (1, 1, 1, 0, 1)),
(5000, (1, 1, 0, 1, 1)),
(5000, (1, 0, 1, 1, 1)),
(5000, (1, 1, 1, 1, 0)),
(5000, (0, 1, 1, 1, 1)),
(50000, (0, 1, 1, 1, 1, 0)),
(99999999, (1, 1, 1, 1, 1))]
def ai():
global cut_count # 统计剪枝次数
cut_count = 0
global search_count # 统计搜索次数
search_count = 0
negamax(True, DEPTH, -99999999, 99999999)
print("本次共剪枝次数:" + str(cut_count))
print("本次共搜索次数:" + str(search_count))
return next_point[0], next_point[1]
# 负值极大算法搜索 alpha + beta剪枝
def negamax(is_ai, depth, alpha, beta):
# 游戏是否结束 | | 探索的递归深度是否到边界
if game_win(list1) or game_win(list2) or depth == 0:
return evaluation(is_ai)
blank_list = list(set(list_all).difference(set(list3)))
order(blank_list) # 搜索顺序排序 提高剪枝效率
# 遍历每一个候选步
for next_step in blank_list:
global search_count
search_count += 1
# 如果要评估的位置没有相邻的子, 则不去评估 减少计算
if not has_neightnor(next_step):
continue
if is_ai:
list1.append(next_step)
else:
list2.append(next_step)
list3.append(next_step)
value = -negamax(not is_ai, depth - 1, -beta, -alpha)
if is_ai:
list1.remove(next_step)
else:
list2.remove(next_step)
list3.remove(next_step)
if value > alpha:
print(str(value) + "alpha:" + str(alpha) + "beta:" + str(beta))
print(list3)
if depth == DEPTH:
next_point[0] = next_step[0]
next_point[1] = next_step[1]
# alpha + beta剪枝点
if value >= beta:
global cut_count
cut_count += 1
return beta
alpha = value
return alpha
# 离最后落子的邻居位置最有可能是最优点
def order(blank_list):
last_pt = list3[-1]
for item in blank_list:
for i in range(-1, 2):
for j in range(-1, 2):
if i == 0 and j == 0:
continue
if (last_pt[0] + i, last_pt[1] + j) in blank_list:
blank_list.remove((last_pt[0] + i, last_pt[1] + j))
blank_list.insert(0, (last_pt[0] + i, last_pt[1] + j))
def has_neightnor(pt):
for i in range(-1, 2):
for j in range(-1, 2):
if i == 0 and j == 0:
continue
if (pt[0] + i, pt[1]+j) in list3:
return True
return False
# 评估函数
def evaluation(is_ai):
total_score = 0
if is_ai:
my_list = list1
enemy_list = list2
else:
my_list = list2
enemy_list = list1
# 算自己的得分
score_all_arr = [] # 得分形状的位置 用于计算如果有相交 得分翻倍
my_score = 0
for pt in my_list:
m = pt[0]
n = pt[1]
my_score += cal_score(m, n, 0, 1, enemy_list, my_list, score_all_arr)
my_score += cal_score(m, n, 1, 0, enemy_list, my_list, score_all_arr)
my_score += cal_score(m, n, 1, 1, enemy_list, my_list, score_all_arr)
my_score += cal_score(m, n, -1, 1, enemy_list, my_list, score_all_arr)
# 算敌人的得分, 并减去
score_all_arr_enemy = []
enemy_score = 0
for pt in enemy_list:
m = pt[0]
n = pt[1]
enemy_score += cal_score(m, n, 0, 1, my_list, enemy_list, score_all_arr_enemy)
enemy_score += cal_score(m, n, 1, 0, my_list, enemy_list, score_all_arr_enemy)
enemy_score += cal_score(m, n, 1, 1, my_list, enemy_list, score_all_arr_enemy)
enemy_score += cal_score(m, n, -1, 1, my_list, enemy_list, score_all_arr_enemy)
total_score = my_score - enemy_score*ratio*0.1
return total_score
# 每个方向上的分值计算
def cal_score(m, n, x_decrict, y_derice, enemy_list, my_list, score_all_arr):
add_score = 0 # 加分项
# 在一个方向上, 只取最大的得分项
max_score_shape = (0, None)
# 如果此方向上,该点已经有得分形状,不重复计算
for item in score_all_arr:
for pt in item[1]:
if m == pt[0] and n == pt[1] and x_decrict == item[2][0] and y_derice == item[2][1]:
return 0
# 在落子点 左右方向上循环查找得分形状
for offset in range(-5, 1):
# offset = -2
pos = []
for i in range(0, 6):
if (m + (i + offset) * x_decrict, n + (i + offset) * y_derice) in enemy_list:
pos.append(2)
elif (m + (i + offset) * x_decrict, n + (i + offset) * y_derice) in my_list:
pos.append(1)
else:
pos.append(0)
tmp_shap5 = (pos[0], pos[1], pos[2], pos[3], pos[4])
tmp_shap6 = (pos[0], pos[1], pos[2], pos[3], pos[4], pos[5])
for (score, shape) in shape_score:
if tmp_shap5 == shape or tmp_shap6 == shape:
if tmp_shap5 == (1,1,1,1,1):
print('wwwwwwwwwwwwwwwwwwwwwwwwwww')
if score > max_score_shape[0]:
max_score_shape = (score, ((m + (0+offset) * x_decrict, n + (0+offset) * y_derice),
(m + (1+offset) * x_decrict, n + (1+offset) * y_derice),
(m + (2+offset) * x_decrict, n + (2+offset) * y_derice),
(m + (3+offset) * x_decrict, n + (3+offset) * y_derice),
(m + (4+offset) * x_decrict, n + (4+offset) * y_derice)), (x_decrict, y_derice))
# 计算两个形状相交, 如两个3活 相交, 得分增加 一个子的除外
if max_score_shape[1] is not None:
for item in score_all_arr:
for pt1 in item[1]:
for pt2 in max_score_shape[1]:
if pt1 == pt2 and max_score_shape[0] > 10 and item[0] > 10:
add_score += item[0] + max_score_shape[0]
score_all_arr.append(max_score_shape)
return add_score + max_score_shape[0]
def game_win(list):
for m in range(COLUMN):
for n in range(ROW):
if n < ROW - 4 and (m, n) in list and (m, n + 1) in list and (m, n + 2) in list and (
m, n + 3) in list and (m, n + 4) in list:
return True
elif m < ROW - 4 and (m, n) in list and (m + 1, n) in list and (m + 2, n) in list and (
m + 3, n) in list and (m + 4, n) in list:
return True
elif m < ROW - 4 and n < ROW - 4 and (m, n) in list and (m + 1, n + 1) in list and (
m + 2, n + 2) in list and (m + 3, n + 3) in list and (m + 4, n + 4) in list:
return True
elif m < ROW - 4 and n > 3 and (m, n) in list and (m + 1, n - 1) in list and (
m + 2, n - 2) in list and (m + 3, n - 3) in list and (m + 4, n - 4) in list:
return True
return False
def gobangwin():
win = GraphWin("this is a gobang game", GRID_WIDTH * COLUMN, GRID_WIDTH * ROW)
win.setBackground("yellow")
i1 = 0
while i1 <= GRID_WIDTH * COLUMN:
l = Line(Point(i1, 0), Point(i1, GRID_WIDTH * COLUMN))
l.draw(win)
i1 = i1 + GRID_WIDTH
i2 = 0
while i2 <= GRID_WIDTH * ROW:
l = Line(Point(0, i2), Point(GRID_WIDTH * ROW, i2))
l.draw(win)
i2 = i2 + GRID_WIDTH
return win
def main():
win = gobangwin()
for i in range(COLUMN+1):
for j in range(ROW+1):
list_all.append((i, j))
change = 0
g = 0
m = 0
n = 0
while g == 0:
if change % 2 == 1:
pos = ai()
if pos in list3:
message = Text(Point(200, 200), "不可用的位置" + str(pos[0]) + "," + str(pos[1]))
message.draw(win)
g = 1
list1.append(pos)
list3.append(pos)
piece = Circle(Point(GRID_WIDTH * pos[0], GRID_WIDTH * pos[1]), 16)
piece.setFill('white')
piece.draw(win)
if game_win(list1):
message = Text(Point(100, 100), "white win.")
message.draw(win)
g = 1
change = change + 1
else:
p2 = win.getMouse()
if not ((round((p2.getX()) / GRID_WIDTH), round((p2.getY()) / GRID_WIDTH)) in list3):
a2 = round((p2.getX()) / GRID_WIDTH)
b2 = round((p2.getY()) / GRID_WIDTH)
list2.append((a2, b2))
list3.append((a2, b2))
piece = Circle(Point(GRID_WIDTH * a2, GRID_WIDTH * b2), 16)
piece.setFill('black')
piece.draw(win)
if game_win(list2):
message = Text(Point(100, 100), "black win.")
message.draw(win)
g = 1
change = change + 1
message = Text(Point(100, 120), "Click anywhere to quit.")
message.draw(win)
win.getMouse()
win.close()
main()