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_CHESSMAN111 else:112 return BLACK_CHESSMAN113 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 = 5137 else:138 radius = 3139 # 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_X177 pos_y = click_pos[1] - Start_Y178 if pos_x < -Inside_Width or pos_y < -Inside_Width:179 return None180 x = pos_x // SIZE181 y = pos_y // SIZE182 if pos_x % SIZE > Stone_Radius:183 x += 1184 if pos_y % SIZE > Stone_Radius:185 y += 1186 if x >= Line_Points or y >= Line_Points:187 return None188 189 return Point(x, y)190 191 192 class AI:193 def __init__(self, line_points, chessman):194 self._line_points = line_points195 self._my = chessman196 self._opponent = BLACK_CHESSMAN if chessman == WHITE_CHESSMAN else WHITE_CHESSMAN197 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.Value201 202 def AI_drop(self):203 point = None204 score = 0205 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 = _score211 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.Value217 return point218 219 def _get_point_score(self, point):220 score = 0221 for os in offset:222 score += self._get_direction_score(point, os[0], os[1])223 return score224 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_offset238 y = point.Y + step * y_offset239 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 += 1243 if space is False:244 space = True245 elif self._checkerboard[y][x] == self._opponent.Value:246 _both += 1247 break248 else:249 if space is None:250 space = False251 else:252 break # 遇到第二个空格退出253 elif flag == 2:254 if self._checkerboard[y][x] == self._my.Value:255 _both += 1256 break257 elif self._checkerboard[y][x] == self._opponent.Value:258 _count += 1259 if _space is False:260 _space = True261 else:262 if _space is None:263 _space = False264 else:265 break266 else:267 # 遇到边也就是阻挡268 if flag == 1:269 both += 1270 elif flag == 2:271 _both += 1272 273 if space is False:274 space = None275 if _space is False:276 _space = None277 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_offset282 y = point.Y - step * y_offset283 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 += 1287 if space is False:288 space = True289 elif self._checkerboard[y][x] == self._opponent.Value:290 _both += 1291 break292 else:293 if space is None:294 space = False295 else:296 break # 遇到第二个空格退出297 elif _flag == 2:298 if self._checkerboard[y][x] == self._my.Value:299 _both += 1300 break301 elif self._checkerboard[y][x] == self._opponent.Value:302 _count += 1303 if _space is False:304 _space = True305 else:306 if _space is None:307 _space = False308 else:309 break310 else:311 # 遇到边也就是阻挡312 if _flag == 1:313 both += 1314 elif _flag == 2:315 _both += 1316 317 score = 0318 if count == 4:319 score = 10000320 elif _count == 4:321 score = 9000322 elif count == 3:323 if both == 0:324 score = 1000325 elif both == 1:326 score = 100327 else:328 score = 0329 elif _count == 3:330 if _both == 0:331 score = 900332 elif _both == 1:333 score = 90334 else:335 score = 0336 elif count == 2:337 if both == 0:338 score = 100339 elif both == 1:340 score = 10341 else:342 score = 0343 elif _count == 2:344 if _both == 0:345 score = 90346 elif _both == 1:347 score = 9348 else:349 score = 0350 elif count == 1:351 score = 10352 elif _count == 1:353 score = 9354 else:355 score = 0356 357 if space or _space:358 score /= 2359 360 return score361 362 # 判断指定位置处在指定方向上是我方子、对方子、空363 def _get_stone_color(self, point, x_offset, y_offset, next):364 x = point.X + x_offset365 y = point.Y + y_offset366 if 0 <= x < self._line_points and 0 <= y < self._line_points:367 if self._checkerboard[y][x] == self._my.Value:368 return 1369 elif self._checkerboard[y][x] == self._opponent.Value:370 return 2371 else:372 if next:373 return self._get_stone_color(Point(x, y), x_offset, y_offset, False)374 else:375 return 0376 else:377 return 0378 379 380 if __name__ == '__main__':381 main()