python实现井字棋人机对战(图形窗口,博弈论)

        本程序实现了井字棋人机对战小游戏,先选择玩家先手,电脑先手,再开始游戏,点击重玩会重置游戏界面,鼠标移动上去变成能点击样式的才能点

代码如下:

import tkinter as tk
import numpy as np
import copy


# 游戏结束
def game_over(B1, B2, B3, ):
    global B
    for i in range(3):
        for j in range(3):
            B[i][j]['state'] = 'disabled'  # 设置为禁用
            B[i][j]['cursor'] = 'arrow'  # 鼠标样式更改
    if B1['text'] == 'x':
        B1['text'] = '玩'
        B2['text'] = '家'
        B3['text'] = '胜'
    elif B1['text'] == 'o' or B2['text'] == 'o':
        B1['text'] = '电'
        B2['text'] = '脑'
        B3['text'] = '胜'
    else:
        global b2
        b2['text'] = '和棋'


# 绝杀点/关键点
def Key_points(M, t):
    x = []
    s_r = M.sum(axis=1)  # 各行的和
    s_c = M.sum(axis=0)  # 各列的和
    m_d = M[0, 0] + M[1, 1] + M[2, 2]  # 主对角线
    b_d = M[0, 2] + M[1, 1] + M[2, 0]  # 反对角线
    for i in range(3):
        for j in range(3):
            if M[i][j] == 0:  # 这是个空位
                c = 0  # 行,列,或对角和为1的个数
                if i == j and m_d == t: c += 1  # 如果是对角线或反对角线
                if i + j == 2 and b_d == t: c += 1
                if s_r[i] == t: c += 1
                if s_c[j] == t: c += 1
                if c >= 2:
                    x.append([i, j])  # 能绝杀,放置
    return x


# 电脑下棋+判断输赢
def aiplay():
    global B, b1, b2, b3
    mark = []  # 记号,储存棋盘
    t = 0  # 记录空位个数
    for i in range(3):
        mark.append([])
        for j in range(3):
            if B[i][j]['text'] == 'o':
                mark[i].append(1)
            elif B[i][j]['text'] == 'x':
                mark[i].append(-1)
            else:
                mark[i].append(0)
                t += 1
        if sum(mark[i]) == -3:  # 如果某一排有三个x玩家赢
            game_over(B[i][0], B[i][1], B[i][2])
            return
    if not t:  # 没有空位,和棋
        game_over(b1, b2, b3)
    mark = np.array(mark)
    sum_row = mark.sum(axis=1)  # 各行的和
    sum_column = mark.sum(axis=0)  # 各列的和
    main_diagonal = mark[0, 0] + mark[1, 1] + mark[2, 2]  # 主对角线
    back_diagonal = mark[0, 2] + mark[1, 1] + mark[2, 0]  # 反对角线
    for j in range(3):
        if sum_column[j] == -3:  # 如果某一列有三个x玩家赢
            game_over(B[0][j], B[1][j], B[2][j])
            return
    if main_diagonal == -3:  # 单独判断对角线
        game_over(B[0][0], B[1][1], B[2][2])
        return
    if back_diagonal == -3:  # 单独判断反对角线
        game_over(B[0][2], B[1][1], B[2][0])
        return
    # 电脑胜判断======================================================
    for i in range(3):  # 有2个o电脑再下一个,电脑胜
        if sum_row[i] == 2:
            game_over(B[i][0], B[i][1], B[i][2])
            return
        if sum_column[i] == 2:
            game_over(B[0][i], B[1][i], B[2][i])
            return
    if main_diagonal == 2:  # 单独判断对角线
        game_over(B[0][0], B[1][1], B[2][2])
        return
    if back_diagonal == 2:  # 单独判断反对角线
        game_over(B[0][2], B[1][1], B[2][0])
        return
    # 没法直接赢,看看有没有能堵住的==================================
    for i in range(3):  # 有2个x电脑堵一下
        if sum_row[i] == -2:
            aiplace(B[i][0]), aiplace(B[i][1]), aiplace(B[i][2])
            return
        if sum_column[i] == -2:
            aiplace(B[0][i]), aiplace(B[1][i]), aiplace(B[2][i])
            return
    if main_diagonal == -2:  # 单独判断对角线
        aiplace(B[0][0]), aiplace(B[1][1]), aiplace(B[2][2])
        return
    if back_diagonal == -2:  # 单独判断反对角线
        aiplace(B[0][2]), aiplace(B[1][1]), aiplace(B[2][0])
        return
    # 多想一步,如果有地方下了能有两个值为2的地方,就能绝杀================================
    K = Key_points(mark, 1)
    for i in K:
        aiplace(B[i[0]][i[1]])  # 能绝杀,放置
        return
    # 再多想一步,如果对方有能把自己绝杀的点,抢先堵住=======================================
    K = Key_points(mark, -1)
    for i in K:
        aiplace(B[i[0]][i[1]])  # 能绝杀,放置
        return
    # 多想两步,看有没有位置放了之后可以出现两个绝杀点
    for i in range(3):
        for j in range(3):
            if mark[i][j] == 0:  # 这是个空位
                mark_new = copy.deepcopy(mark)  # 深拷贝
                mark_new[i][j] = 1
                K = Key_points(mark_new, 1)
                if len(K) >= 2:
                    aiplace(B[i][j])  # 或许能绝杀,放置
                    return
    # 再多想两步,看对面有没有位置放了之后可以出现两个绝杀点
    for i in range(3):
        for j in range(3):
            if mark[i][j] == 0:  # 这是个空位
                mark_new = copy.deepcopy(mark)  # 深拷贝
                mark_new[i][j] = -1
                K = Key_points(mark_new, -1)
                if len(K) >= 2:
                    aiplace(B[i][j])  # 或许能绝杀,放置
                    return
    # 这还没办法,看哪有空位下在哪
    for i in range(3):
        for j in range(3):
            if mark[i][j] == 0:  # 这是个空位
                aiplace(B[i][j])  # 放置
                return
    # 没有空位了,平局
    game_over(b1, b2, b3)


# 电脑放置
def aiplace(Bu):
    if not Bu['text'] == 'x':
        Bu['text'] = 'o'  # 打上o
        Bu['state'] = 'disabled'  # 设置为禁用
        Bu['cursor'] = 'arrow'  # 鼠标样式更改


# 玩家放置
def place(Bu):
    Bu['text'] = 'x'  # 打上x
    Bu['state'] = 'disabled'  # 设置为禁用
    Bu['cursor'] = 'arrow'  # 鼠标样式更改
    aiplay()


# 重玩
def restart():
    global b1, b2, b3, B
    for i in range(3):
        for j in range(3):
            B[i][j]['text'] = ''  # 棋盘重置
            B[i][j]['state'] = 'disabled'  # 设置为禁用
            B[i][j]['cursor'] = 'arrow'  # 鼠标样式更改
    b1['state'] = 'disabled'  # 设置为禁用
    b1['cursor'] = 'arrow'  # 鼠标样式更改
    b2['text'] = '开始'  # 和棋时需要重置
    b2['state'] = 'normal'  # 设置为启用
    b2['cursor'] = 'hand2'  # 鼠标样式更改
    b3['state'] = 'normal'  # 设置为启用
    b3['cursor'] = 'hand2'  # 鼠标样式更改


# 开始
def play():
    global b1, b2, b3, B, player_first
    for i in range(3):
        for j in range(3):
            B[i][j]['text'] = ''  # 棋盘重置
            B[i][j]['state'] = 'normal'  # 设置为启用
            B[i][j]['cursor'] = 'hand2'  # 鼠标样式更改
    b1['state'] = 'normal'  # 设置为启用
    b1['cursor'] = 'hand2'  # 鼠标样式更改
    b2['state'] = 'disabled'  # 设置为禁用
    b2['cursor'] = 'arrow'  # 鼠标样式更改
    b3['state'] = 'disabled'  # 设置为禁用
    b3['cursor'] = 'arrow'  # 鼠标样式更改
    if not player_first:
        aiplay()  # AI先下


# 先手判断
def Go_first():
    global player_first
    if player_first:
        b3['text'] = '电脑先手'
    else:
        b3['text'] = '玩家先手'
    player_first = not player_first


global player_first, B, b1, b2, b3
player_first = True  # 玩家先手
main_window = tk.Tk()  # 调用Tk()创建主窗口
main_window.title("井字棋")  # 给主窗口起一个名字
main_window.geometry("450x450")  # 大小
# main_window.resizable(0,0)  # 不允许拉伸改变大小
# 按钮 text:按钮文本,width、height:按钮大小,cursor:鼠标样式,command:调用函数
B = []  # 棋盘按钮组
for i in range(3):
    B.append([])
    main_window.grid_rowconfigure(i, weight=1)  # row为i,缩放比为1
    for j in range(3):
        main_window.grid_columnconfigure(j, weight=1)  # column为i,缩放比为1
        B[i].append(tk.Button(main_window, text="", width=15, height=5, ))  # 添加按钮
        B[i][j]['command'] = lambda i=i, j=j: place(B[i][j])  # 增加点击函数
        B[i][j]['state'] = 'disabled'  # 初始禁用
        B[i][j].grid(row=i, column=j, sticky=tk.N + tk.S + tk.W + tk.E)  # 添加到主窗口显示
b1 = tk.Button(main_window, text="重玩", width=15, height=5, command=restart)  # 添加按钮,点击调用重启函数
b2 = tk.Button(main_window, text="开始", width=15, height=5, cursor='hand2', command=play)  # 添加按钮,点击调用开始函数
b3 = tk.Button(main_window, text='玩家先手', width=15, height=5, cursor='hand2', command=Go_first)  # 添加按钮,点击调用先手函数
b1['state'] = 'disabled'  # 初始为禁用
b1.grid(row=3, column=0, sticky=tk.N + tk.S + tk.W + tk.E)
b2.grid(row=3, column=1, sticky=tk.N + tk.S + tk.W + tk.E)
b3.grid(row=3, column=2, sticky=tk.N + tk.S + tk.W + tk.E)
main_window.mainloop()  # 开启主循环,让窗口处于显示状态

结果示例:

 

 拉伸不错位

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值