人机井字棋python实现

实验过程与分析


问题描述及分析


设计一个带有图形用户界面的人机对战井字棋游戏。 游戏在九宫方格内进行, 如果一方抢先于
某方向(横、 竖、 斜) 连成 3 子, 则获取胜利。 游戏界面, 首先询问哪个玩家先走, 然后根据玩家
落子位置, 显示棋盘状态。 最终根据游戏规则, 评判哪个玩家获胜。
该问题的重点与难点在于: 如何在图形界面中实现下棋功能, 如何判断当前棋盘中是否有赢家,
以及电脑如何选择下棋位置。 前者可以通过在每一次下棋之后, 遍历棋盘中获胜的路线来判断某一
路线中的三个下棋点是否为同一玩家的棋子, 若相同, 则该玩家获胜, 游戏结束; 若不存在这样的
情况, 则此时没有玩家获胜, 游戏继续。 后者可以通过一个估价函数, 来帮助电脑判断当前的下棋
位置, 判断的优先级依次为: 赢得游戏、 不能输(平局) 、 随机下棋。


关键算法设计


· 关键点 1: 在图形界面中实现下棋功能。


代码如下:

def peo_go(po):
# 玩家走棋
if the_chess[po] == 0:
the_chess[po] = who
update_chess()
if is_win(the_chess, who) == who:
tips.set('你赢了! ')
elif count_zero(the_chess) == 0:
tips.set("平局! ")
else:
ai_go(who * -1)
def ai_go(w):
# 机器走棋
if count_zero(the_chess) < 9:
po = is_danger(the_chess, 1)
if po != -1:
the_chess[po] = w
update_chess()
elif not constraint(w):
pass
else:
the_chess[ \
get_next_o( \
the_chess, -1)] = w
update_chess()
if is_win(the_chess, -1) == -1:
tips.set("你输了! ")
if count_zero(the_chess) == 0:
tips.set("平局! ")

  代码中省略了按钮事件代码及调用代码的语句, 按钮事件利用 tkinter 中的 bind()函数绑定事件,并联系到 peo_go 函数。 在 peo_go 函数中, 局部变量 po 表示点击下棋的位置( 0~8) , the_chess 表示当前的棋盘。
  这里以玩家先下为例, 如果 po 指向的位置为 0(没有下过子) , 则将该位置的数值改为 who 的值, 同时通过 update_chess()函数更新图形界面上的棋盘。 此时, 如果玩家下棋后达成胜利条件, 则显示“你赢了” , 否则如果此时没有可以落子的地方了, 就显示“平局” , 如果条件都不满足, 则说明游戏还未结束, 切换至电脑操作(在玩家操作过程中不可能出现落子后玩家输的情况) 。
  机器走棋部分中, 首先判断当前剩余空格是否小于 9, 如果是, 那么判断拟下棋点下完后是否会让自己陷入危险状态(is_danger()函数的功能), 如果陷入(po != -1), 那么就落子在该位置来化解危机, 否则, 根据 evaluation 函数(具体功能在关键点 3 中介绍) 的优先级判断来落子, 同时更新棋盘。
  落子后, 判断此时电脑(-1) 是否达到获胜条件, 如果达到, 那么玩家输了, 显示提示信息; 如果此时没有空格可以下子, 则游戏以平局结束(在电脑操作的过程中不可能出现落子后玩家获胜的情况) 。


· 关键点 2: 判断当前棋盘中是否有赢家。


代码如下:

def is_win(now_chess, who):
temp = now_chess[:]
for chess in win_chess:
if (temp[chess[0]] == who) and \
(temp[chess[1]] == who) and \
(temp[chess[2]] == who):
return who
return 0

代码中 now_chess 为当前棋盘的情况(3*3 二维数组) , who 代表要判断的游戏方(在程序中以
整数 1 代表此位置为人类玩家下棋位置, -1 代表机器下棋位置, 每次交换下棋方后对 who 取反, 即可达到交换玩家的目的) 。 如果在 for 循环中没有执行到 return 语句, 则说明此时场上没有一方获胜,否则, 返回要判断的游戏方代表数字, 即该方获胜。


· 关键点 3: 电脑如何选择下棋位置。

代码如下:

def evaluation(now_chess):
temp = now_chess[:]
count = 0
for w_c in win_chess:
if temp[w_c[0]] >= 0 and temp[w_c[1]] >= 0 and temp[w_c[2]] >= 0:
if temp[w_c[0]] == 1 or temp[w_c[1]] == 1 or temp[w_c[2]] == 1:
count += 1
count += 1
if is_win(temp, 1) == 1:
count = count + 2
if is_win(temp, -1) == -1:
count = count - 2
return count

代码中 now_chess 为当前棋盘的情况( 3*3 二维数组) , temp 临时存储一个当前棋盘的副本,
count 作为计数器。 估价函数主体部分主要由两个 for 循环组成。 第一个 for 循环内的第一个 if 是判断可以赢的行数, 每有一个则 count+1; 如果可以赢得行数上有自己的旗子, 则 count+2; 如果有能够让自己赢的下棋点, 则 count+2; 如果可能会让对手赢, 则 count-2。 经过计算后, 根据 count 值来评估拟走的线路是否合适, 最后电脑选取值最高的线路走法下棋。


实现结果


程序运行时的界面如图 1 所示:


图 1: “人机井字棋” 运行界面
图中每个中括号代表下棋的位置, 此时仅为展示状态, 不可落子, 玩家点击“ 开始游戏” 键后
开始游戏。

图 2: “人机井字棋” 点击“开始游戏” 键后的界面
点击“开始游戏” 键后, 玩家首先要选择是否让机器先下(如上图所示) , 如果是, 则点击该
按钮, 不然可以直接点击任一空格下棋的位置落子。


图 3: “人机井字棋” 玩家平局、 胜利、 失败的界面
以上三图分别为玩家“胜利” 、 “平局” 和“失败” 的界面图。

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值