五子棋人机对战算法_Python:游戏:五子棋之人机对战!电脑就是电脑!但是人脑更厉害...

五子棋比起我之前写的几款游戏来说,难度提高了不少。如果是人与人对战,那么,电脑只需要判断是否赢了就可以。如果是人机对战,那你还得让电脑知道怎么下。

我们先从简单的问题来看。

开端

画棋盘

首先肯定是要画出棋盘来,用 pygame 画出一个 19 × 19 或 15 × 15 的棋盘并不是什么难事,这在之前的文章中已经多次用到,就不赘述了。

画棋子

需要说一下的是画棋子,因为没找到什么合适的棋子图片,所以只要自己来画棋子。

我们用 pygame.draw.circle 画出来的圆形是这样的:

d1c28dd6f7786cce807ca340dc482639.png

锯齿状十分明显, pygame.draw 中有画抗锯齿直线的函数 aaline ,但是并没有 aacircle这样的函数来画一个抗锯齿的圆。

这里就需要用到 pygame.gfxdraw 啦。 pygame.gfxdraw 目前还仅是实验版本,这意味着这个 API 可能会在以后的 pygame 版本中发生变化或消失。

要绘制抗锯齿和填充形状,请首先使用函数的aa *版本,然后使用填充版本。例如:

col = (255, 0, 0)surf.fill((255, 255, 255))pygame.gfxdraw.aacircle(surf, x, y, 30, col)pygame.gfxdraw.filled_circle(surf, x, y, 30, col)

我们用这个方法在棋盘上画一个棋子试试看。

62c7da245caaaa1ff4615e6585c535b9.png

加群:960410445 即可获取源码!

可以看到效果已明显改善。

落子

落子需要判断鼠标事件,当鼠标左键点击,获取鼠标点击的位置,然后根据棋盘的位置,计算出棋子落在棋盘的位置。

 while True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() elif event.type == MOUSEBUTTONDOWN: pressed_array = pygame.mouse.get_pressed() if pressed_array[0]: # 鼠标左键点击 mouse_pos = pygame.mouse.get_pos() click_point = _get_clickpoint(mouse_pos)

胜利判定

当一子落下,如何判定是否胜利?

可以肯定的是,当某一子落下的时候,如果出现了 5 连,那么落下的这颗子必定在这条 5 连线上。那么这个问题就可以简化了,我们无需全盘扫描,只需要在落子位置上横竖撇捺扫描一下,判断是否出现 5 连即可。

我们定义一个棋盘类,类中实例化一个 19 × 19 的二维数组,初始值皆为 0,表示空,用 1 表示黑子,2 表示白子。这个类对外提供一个落子方法 drop ,接收参数落子方和落子坐标,如果落子后胜利,则返回胜利者,否则返回 None。

Chessman = namedtuple('Chessman', 'Name Value Color')Point = namedtuple('Point', 'X Y')BLACK_CHESSMAN = Chessman('黑子', 1, (45, 45, 45))WHITE_CHESSMAN = Chessman('白子', 2, (219, 219, 219))offset = [(1, 0), (0, 1), (1, 1), (1, -1)]class Checkerboard: def __init__(self, line_points): self._line_points = line_points self._checkerboard = [[0] * line_points for _ in range(line_points)] def _get_checkerboard(self): return self._checkerboard checkerboard = property(_get_checkerboard) # 判断是否可落子 def can_drop(self, point): return self._checkerboard[point.Y][point.X] == 0 def drop(self, chessman, point): """ 落子 :param chessman: 黑子/白子 :param point:落子位置 :return:若该子落下之后即可获胜,则返回获胜方,否则返回 None """ print(f'{chessman.Name} ({point.X}, {point.Y})') self._checkerboard[point.Y][point.X] = chessman.Value if self._win(point): print(f'{chessman.Name}获胜') return chessman # 判断是否赢了 def _win(self, point): cur_value = self._checkerboard[point.Y][point.X] for os in offset: if self._get_count_on_direction(point, cur_value, os[0], os[1]): return True def _get_count_on_direction(self, point, value, x_offset, y_offset): count = 1 for step in range(1, 5): x = point.X + step * x_offset y = point.Y + step * y_offset if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value: count += 1 else: break for step in range(1, 5): x = point.X - step * x_offset y = point.Y - step * y_offset if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value: count += 1 else: break return count >= 5

这里我定义了一个偏移量,我们一共要计算横竖撇捺 4 条线,任意一条线出现 5 连就算获胜。计算方法实际上是一样的,只是方向不同,所以定义一个偏移量数组,不同的偏移量表示不同的方向,这样就可以利用循环来实现了,节省了很多代码。

电脑落子

这就是全篇的重头戏了,要怎么教电脑下五子棋。

首先声明,我用的是相对传统的方式,不是深度学习。

五子棋就是要实现 5 连,所以,一开始,我的想法是:将所有连线保存在一个数组中,落子的时候选择最长的连线落子。但这样有个问题解决不掉,如何让电脑识别“三三”呢?

后来网上看到篇文章,使用的方法是:遍历棋盘上的空位,计算每一个位置其横竖撇捺 8 个方向上是否有己方的子,有一个就加 10 分,最后选得分最高的位置落子。

这样不太严谨,写出来的电脑估计水平很菜,但是这个思路却是对的,落子就是要找到最值得的地方,那么我们干脆对每一个可落子的地方来做一个评估,选出最优解。

这里我们需要了解一下五子棋的几种基本棋形:连五,活四,冲四,活三,眠三,活二,眠二。

连五

顾名思义,五颗同色棋子连在一起,赢了。

d9cd11c54667ef3f9d3b14d9d8be3eb2.png

活四

四颗同色棋子连在一起,并且左右两边都没有对方棋子阻挡,有两个连五点。

7d69ba017bc96876ba40b8390f3c3620.png

冲四

四颗同色棋子连在一起,并且一边有对方棋子阻挡,或者四颗棋子不是连的,当中有个空挡,这时只有一个连五点。

984a225db976840c15718c96f2914db0.png

活三、跳活三

活三:三颗同色棋子连在一起。

a239d4242cf36977396b8c440393d8e3.png

跳活三 :中间隔了一个空格的活三。

8d6e3d59190d17e00ada1c640a4c2ce6.png

眠三

只能够形成冲四的三,无外乎两种情况,一是一边被挡住了,一是当中有 2 个空格。(其实我在代码中仅考虑了第一种情况,即便形成冲四,也不是什么危险局面。)

ae16a8673d540ce6cba41a638aafa5b9.png

活二和眠二

活二,能够形成活三的二;眠二,能够形成眠三的二。这里就不放图了,参考活三眠三。

打分机制

理解了这些棋形,那么按我们之前的思路,就是如何打分了。

  • 首先,连五肯定是不存在的,出现连五胜负已分,所以只要棋局还在进行中,就不会出现连五。那么,什么优先级最高?自然就是活四了。
  • 其次是对方的“四”,对方活四,你防不防都一样输了,对方冲四,你就必须防守。
  • 再次是我方的活三或冲四,活三跟冲四其实是一个级别的,对方必须防守。
  • 再次是对方的活三或冲四。

以此类推下去。我们可以总结一点规律:

  • 相同的棋形,我方优于对方。
  • 冲四跟活三一个级别,眠三跟活二一个级别。
  • 如果中间有空格的话,肯定是要比没空格的略微低级一点,但不至于降级。

基本逻辑就是这样,这一块的代码我写得也不好,整个判断写了100多行,就不贴代码了,大家可以直接下源码看。

五子棋执黑是必赢的,代码中,玩家就是执黑先手,电脑执白后手,所以,下的好是完全可以赢电脑的,不过一个小小失误也很可能被电脑翻盘。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我了解你的问题。你想了解如何使用Python编写五子棋人机对战游戏,对吧? 首先,你需要安装Python的GUI库,如Tkinter或PyQt5,来创建游戏界面。接着,你需要编写五子棋游戏逻辑。这包括实现棋盘和棋子的绘制、落子、判断胜负等功能。 对于人机对战,你需要实现AI算法来让电脑能够下棋。可以使用经典的博弈树搜索算法,或者深度学习算法来训练神经网络来进行决策。 以下是一个简单的五子棋人机对战游戏的代码示例,供你参考: ```python import tkinter as tk import random class Gobang: def __init__(self): self.root = tk.Tk() self.root.title("五子棋-人机对战") self.chessboard = Chessboard(self.root, width=500, height=500) self.chessboard.pack() self.chessboard.bind("<Button-1>", self.play) self.player = 1 self.gameover = False self.ai = AI(self.chessboard) def play(self, event): if self.gameover: return x, y = self.chessboard.get_index(event.x, event.y) if self.chessboard.chess_map[x][y] != 0: return self.chessboard.draw_chess(x, y, self.player) self.chessboard.chess_map[x][y] = self.player if self.check_win(x, y): self.gameover = True tk.messagebox.showinfo("游戏结束", "恭喜玩家{}获胜!".format(self.player)) return self.player = 3 - self.player self.ai.play() if self.check_win(self.ai.last_x, self.ai.last_y): self.gameover = True tk.messagebox.showinfo("游戏结束", "恭喜电脑获胜!") def check_win(self, x, y): directions = [(0, 1), (1, 0), (1, 1), (1, -1)] for dx, dy in directions: count = 1 tx, ty = x, y while tx + dx >= 0 and tx + dx < 15 and ty + dy >= 0 and ty + dy < 15 and self.chessboard.chess_map[tx + dx][ty + dy] == self.player: count += 1 tx += dx ty += dy tx, ty = x, y while tx - dx >= 0 and tx - dx < 15 and ty - dy >= 0 and ty - dy < 15 and self.chessboard.chess_map[tx - dx][ty - dy] == self.player: count += 1 tx -= dx ty -= dy if count >= 5: return True return False def run(self): self.root.mainloop() class Chessboard(tk.Canvas): def __init__(self, master=None, cnf={}, **kw): super().__init__(master, cnf, **kw) self.chess_map = [[0 for i in range(15)] for j in range(15)] self.last_chess = None self.draw_board() def draw_board(self): for i in range(15): self.create_line(50, 50 + i * 30, 440, 50 + i * 30) self.create_line(50 + i * 30, 50, 50 + i * 30, 440) self.create_oval(83, 83, 117, 117, fill="black") self.create_oval(413, 83, 447, 117, fill="black") self.create_oval(83, 413, 117, 447, fill="black") self.create_oval(413, 413, 447, 447, fill="black") def draw_chess(self, x, y, player): color = "black" if player == 1 else "white" self.create_oval(50 + y * 30 - 13, 50 + x * 30 - 13, 50 + y * 30 + 13, 50 + x * 30 + 13, fill=color) self.last_chess = (x, y) def get_index(self, x, y): return (round((y - 50) / 30), round((x - 50) / 30)) class AI: def __init__(self, chessboard): self.chessboard = chessboard self.last_x = -1 self.last_y = -1 def play(self): empty_list = [] for i in range(15): for j in range(15): if self.chessboard.chess_map[i][j] == 0: empty_list.append((i, j)) if not empty_list: return x, y = self.random_play(empty_list) self.chessboard.draw_chess(x, y, 2) self.chessboard.chess_map[x][y] = 2 self.last_x, self.last_y = x, y def random_play(self, empty_list): return random.choice(empty_list) if __name__ == "__main__": game = Gobang() game.run() ``` 这段代码使用了Tkinter库来创建游戏界面,使用了Canvas组件来绘制棋盘和棋子。AI算法使用了随机算法,即在可落子的点中随机选择一个点进行下棋。 当你运行这段代码时,会弹出一个窗口,你可以在窗口中进行五子棋人机对战游戏

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值