Pyhton实践项目之(一)五子棋人机对战,


  1 """五子棋之人机对战"""
  2 
  3 import random
  4 import sys
  5 
  6 import pygame
  7 import pygame.gfxdraw
  8 from pygame.locals import *
  9 
 10 from checkerboard import Checkerboard, BLACK_CHESSMAN, WHITE_CHESSMAN, offset, Point
 11 
 12 SIZE = 30  # 棋盘每个点时间的间隔
 13 Line_Points = 19  # 棋盘每行/每列点数
 14 Outer_Width = 20  # 棋盘外宽度
 15 Border_Width = 4  # 边框宽度
 16 Inside_Width = 4  # 边框跟实际的棋盘之间的间隔
 17 Border_Length = SIZE * (Line_Points - 1) + Inside_Width * 2 + Border_Width  # 边框线的长度
 18 Start_X = Start_Y = Outer_Width + int(Border_Width / 2) + Inside_Width  # 网格线起点(左上角)坐标
 19 SCREEN_HEIGHT = SIZE * (Line_Points - 1) + Outer_Width * 2 + Border_Width + Inside_Width * 2  # 游戏屏幕的高
 20 SCREEN_WIDTH = SCREEN_HEIGHT + 200  # 游戏屏幕的宽
 21 
 22 Stone_Radius = SIZE // 2 - 3  # 棋子半径
 23 Stone_Radius2 = SIZE // 2 + 3
 24 Checkerboard_Color = (0xE3, 0x92, 0x65)  # 棋盘颜色
 25 BLACK_COLOR = (0, 0, 0)
 26 WHITE_COLOR = (255, 255, 255)
 27 RED_COLOR = (200, 30, 30)
 28 BLUE_COLOR = (30, 30, 200)
 29 
 30 RIGHT_INFO_POS_X = SCREEN_HEIGHT + Stone_Radius2 * 2 + 10
 31 
 32 
 33 def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):
 34     imgText = font.render(text, True, fcolor)
 35     screen.blit(imgText, (x, y))
 36 
 37 
 38 def main():
 39     pygame.init()
 40     screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
 41     pygame.display.set_caption('五子棋')
 42 
 43     font1 = pygame.font.SysFont('SimHei', 32)
 44     font2 = pygame.font.SysFont('SimHei', 72)
 45     fwidth, fheight = font2.size('黑方获胜')
 46 
 47     checkerboard = Checkerboard(Line_Points)
 48     cur_runner = BLACK_CHESSMAN
 49     winner = None
 50     computer = AI(Line_Points, WHITE_CHESSMAN)
 51 
 52     black_win_count = 0
 53     white_win_count = 0
 54 
 55     while True:
 56         for event in pygame.event.get():
 57             if event.type == QUIT:
 58                 sys.exit()
 59             elif event.type == KEYDOWN:
 60                 if event.key == K_RETURN:
 61                     if winner is not None:
 62                         winner = None
 63                         cur_runner = BLACK_CHESSMAN
 64                         checkerboard = Checkerboard(Line_Points)
 65                         computer = AI(Line_Points, WHITE_CHESSMAN)
 66             elif event.type == MOUSEBUTTONDOWN:
 67                 if winner is None:
 68                     pressed_array = pygame.mouse.get_pressed()
 69                     if pressed_array[0]:
 70                         mouse_pos = pygame.mouse.get_pos()
 71                         click_point = _get_clickpoint(mouse_pos)
 72                         if click_point is not None:
 73                             if checkerboard.can_drop(click_point):
 74                                 winner = checkerboard.drop(cur_runner, click_point)
 75                                 if winner is None:
 76                                     cur_runner = _get_next(cur_runner)
 77                                     computer.get_opponent_drop(click_point)
 78                                     AI_point = computer.AI_drop()
 79                                     winner = checkerboard.drop(cur_runner, AI_point)
 80                                     if winner is not None:
 81                                         white_win_count += 1
 82                                     cur_runner = _get_next(cur_runner)
 83                                 else:
 84                                     black_win_count += 1
 85                         else:
 86                             print('超出棋盘区域')
 87 
 88         # 画棋盘
 89         _draw_checkerboard(screen)
 90 
 91         # 画棋盘上已有的棋子
 92         for i, row in enumerate(checkerboard.checkerboard):
 93             for j, cell in enumerate(row):
 94                 if cell == BLACK_CHESSMAN.Value:
 95                     _draw_chessman(screen, Point(j, i), BLACK_CHESSMAN.Color)
 96                 elif cell == WHITE_CHESSMAN.Value:
 97                     _draw_chessman(screen, Point(j, i), WHITE_CHESSMAN.Color)
 98 
 99         _draw_left_info(screen, font1, cur_runner, black_win_count, white_win_count)
100 
101         if winner:
102             print_text(screen, font2, (SCREEN_WIDTH - fwidth) // 2, (SCREEN_HEIGHT - fheight) // 2, winner.Name + '获胜',
103                        RED_COLOR)
104 
105         pygame.display.flip()
106 
107 
108 def _get_next(cur_runner):
109     if cur_runner == BLACK_CHESSMAN:
110         return WHITE_CHESSMAN
111     else:
112         return BLACK_CHESSMAN
113 
114 
115 # 画棋盘
116 def _draw_checkerboard(screen):
117     # 填充棋盘背景色
118     screen.fill(Checkerboard_Color)
119     # 画棋盘网格线外的边框
120     pygame.draw.rect(screen, BLACK_COLOR, (Outer_Width, Outer_Width, Border_Length, Border_Length), Border_Width)
121     # 画网格线
122     for i in range(Line_Points):
123         pygame.draw.line(screen, BLACK_COLOR,
124                          (Start_Y, Start_Y + SIZE * i),
125                          (Start_Y + SIZE * (Line_Points - 1), Start_Y + SIZE * i),
126                          1)
127     for j in range(Line_Points):
128         pygame.draw.line(screen, BLACK_COLOR,
129                          (Start_X + SIZE * j, Start_X),
130                          (Start_X + SIZE * j, Start_X + SIZE * (Line_Points - 1)),
131                          1)
132     # 画星位和天元
133     for i in (3, 9, 15):
134         for j in (3, 9, 15):
135             if i == j == 9:
136                 radius = 5
137             else:
138                 radius = 3
139             # pygame.draw.circle(screen, BLACK, (Start_X + SIZE * i, Start_Y + SIZE * j), radius)
140             pygame.gfxdraw.aacircle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)
141             pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)
142 
143 
144 # 画棋子
145 def _draw_chessman(screen, point, stone_color):
146     # pygame.draw.circle(screen, stone_color, (Start_X + SIZE * point.X, Start_Y + SIZE * point.Y), Stone_Radius)
147     pygame.gfxdraw.aacircle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)
148     pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)
149 
150 
151 # 画左侧信息显示
152 def _draw_left_info(screen, font, cur_runner, black_win_count, white_win_count):
153     _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2), BLACK_CHESSMAN.Color)
154     _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2 * 4), WHITE_CHESSMAN.Color)
155 
156     print_text(screen, font, RIGHT_INFO_POS_X, Start_X + 3, '玩家', BLUE_COLOR)
157     print_text(screen, font, RIGHT_INFO_POS_X, Start_X + Stone_Radius2 * 3 + 3, '电脑', BLUE_COLOR)
158 
159     print_text(screen, font, SCREEN_HEIGHT, SCREEN_HEIGHT - Stone_Radius2 * 8, '战况:', BLUE_COLOR)
160     _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - int(Stone_Radius2 * 4.5)),
161                        BLACK_CHESSMAN.Color)
162     _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - Stone_Radius2 * 2), WHITE_CHESSMAN.Color)
163     print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - int(Stone_Radius2 * 5.5) + 3, f'{black_win_count} 胜',
164                BLUE_COLOR)
165     print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - Stone_Radius2 * 3 + 3, f'{white_win_count} 胜',
166                BLUE_COLOR)
167 
168 
169 def _draw_chessman_pos(screen, pos, stone_color):
170     pygame.gfxdraw.aacircle(screen, pos[0], pos[1], Stone_Radius2, stone_color)
171     pygame.gfxdraw.filled_circle(screen, pos[0], pos[1], Stone_Radius2, stone_color)
172 
173 
174 # 根据鼠标点击位置,返回游戏区坐标
175 def _get_clickpoint(click_pos):
176     pos_x = click_pos[0] - Start_X
177     pos_y = click_pos[1] - Start_Y
178     if pos_x < -Inside_Width or pos_y < -Inside_Width:
179         return None
180     x = pos_x // SIZE
181     y = pos_y // SIZE
182     if pos_x % SIZE > Stone_Radius:
183         x += 1
184     if pos_y % SIZE > Stone_Radius:
185         y += 1
186     if x >= Line_Points or y >= Line_Points:
187         return None
188 
189     return Point(x, y)
190 
191 
192 class AI:
193     def __init__(self, line_points, chessman):
194         self._line_points = line_points
195         self._my = chessman
196         self._opponent = BLACK_CHESSMAN if chessman == WHITE_CHESSMAN else WHITE_CHESSMAN
197         self._checkerboard = [[0] * line_points for _ in range(line_points)]
198 
199     def get_opponent_drop(self, point):
200         self._checkerboard[point.Y][point.X] = self._opponent.Value
201 
202     def AI_drop(self):
203         point = None
204         score = 0
205         for i in range(self._line_points):
206             for j in range(self._line_points):
207                 if self._checkerboard[j][i] == 0:
208                     _score = self._get_point_score(Point(i, j))
209                     if _score > score:
210                         score = _score
211                         point = Point(i, j)
212                     elif _score == score and _score > 0:
213                         r = random.randint(0, 100)
214                         if r % 2 == 0:
215                             point = Point(i, j)
216         self._checkerboard[point.Y][point.X] = self._my.Value
217         return point
218 
219     def _get_point_score(self, point):
220         score = 0
221         for os in offset:
222             score += self._get_direction_score(point, os[0], os[1])
223         return score
224 
225     def _get_direction_score(self, point, x_offset, y_offset):
226         count = 0  # 落子处我方连续子数
227         _count = 0  # 落子处对方连续子数
228         space = None  # 我方连续子中有无空格
229         _space = None  # 对方连续子中有无空格
230         both = 0  # 我方连续子两端有无阻挡
231         _both = 0  # 对方连续子两端有无阻挡
232 
233         # 如果是 1 表示是边上是我方子,2 表示敌方子
234         flag = self._get_stone_color(point, x_offset, y_offset, True)
235         if flag != 0:
236             for step in range(1, 6):
237                 x = point.X + step * x_offset
238                 y = point.Y + step * y_offset
239                 if 0 <= x < self._line_points and 0 <= y < self._line_points:
240                     if flag == 1:
241                         if self._checkerboard[y][x] == self._my.Value:
242                             count += 1
243                             if space is False:
244                                 space = True
245                         elif self._checkerboard[y][x] == self._opponent.Value:
246                             _both += 1
247                             break
248                         else:
249                             if space is None:
250                                 space = False
251                             else:
252                                 break  # 遇到第二个空格退出
253                     elif flag == 2:
254                         if self._checkerboard[y][x] == self._my.Value:
255                             _both += 1
256                             break
257                         elif self._checkerboard[y][x] == self._opponent.Value:
258                             _count += 1
259                             if _space is False:
260                                 _space = True
261                         else:
262                             if _space is None:
263                                 _space = False
264                             else:
265                                 break
266                 else:
267                     # 遇到边也就是阻挡
268                     if flag == 1:
269                         both += 1
270                     elif flag == 2:
271                         _both += 1
272 
273         if space is False:
274             space = None
275         if _space is False:
276             _space = None
277 
278         _flag = self._get_stone_color(point, -x_offset, -y_offset, True)
279         if _flag != 0:
280             for step in range(1, 6):
281                 x = point.X - step * x_offset
282                 y = point.Y - step * y_offset
283                 if 0 <= x < self._line_points and 0 <= y < self._line_points:
284                     if _flag == 1:
285                         if self._checkerboard[y][x] == self._my.Value:
286                             count += 1
287                             if space is False:
288                                 space = True
289                         elif self._checkerboard[y][x] == self._opponent.Value:
290                             _both += 1
291                             break
292                         else:
293                             if space is None:
294                                 space = False
295                             else:
296                                 break  # 遇到第二个空格退出
297                     elif _flag == 2:
298                         if self._checkerboard[y][x] == self._my.Value:
299                             _both += 1
300                             break
301                         elif self._checkerboard[y][x] == self._opponent.Value:
302                             _count += 1
303                             if _space is False:
304                                 _space = True
305                         else:
306                             if _space is None:
307                                 _space = False
308                             else:
309                                 break
310                 else:
311                     # 遇到边也就是阻挡
312                     if _flag == 1:
313                         both += 1
314                     elif _flag == 2:
315                         _both += 1
316 
317         score = 0
318         if count == 4:
319             score = 10000
320         elif _count == 4:
321             score = 9000
322         elif count == 3:
323             if both == 0:
324                 score = 1000
325             elif both == 1:
326                 score = 100
327             else:
328                 score = 0
329         elif _count == 3:
330             if _both == 0:
331                 score = 900
332             elif _both == 1:
333                 score = 90
334             else:
335                 score = 0
336         elif count == 2:
337             if both == 0:
338                 score = 100
339             elif both == 1:
340                 score = 10
341             else:
342                 score = 0
343         elif _count == 2:
344             if _both == 0:
345                 score = 90
346             elif _both == 1:
347                 score = 9
348             else:
349                 score = 0
350         elif count == 1:
351             score = 10
352         elif _count == 1:
353             score = 9
354         else:
355             score = 0
356 
357         if space or _space:
358             score /= 2
359 
360         return score
361 
362     # 判断指定位置处在指定方向上是我方子、对方子、空
363     def _get_stone_color(self, point, x_offset, y_offset, next):
364         x = point.X + x_offset
365         y = point.Y + y_offset
366         if 0 <= x < self._line_points and 0 <= y < self._line_points:
367             if self._checkerboard[y][x] == self._my.Value:
368                 return 1
369             elif self._checkerboard[y][x] == self._opponent.Value:
370                 return 2
371             else:
372                 if next:
373                     return self._get_stone_color(Point(x, y), x_offset, y_offset, False)
374                 else:
375                     return 0
376         else:
377             return 0
378 
379 
380 if __name__ == '__main__':
381     main()

 

评论关闭