基于Python的五子棋人机对战

人机对战

在之前的博文基于tkinter的五子棋游戏中使用tkinter做了一个简单的五子棋游戏,只能实现人人对战,后来想着加上人机对战的功能。
不过,最初想想还是挺麻烦的,计算机怎么评估当前的棋局,找到最佳或者较佳的落子点呢,脑子真是越来越不灵光了。站在巨人的肩膀上,科学技术才在这几百年发展的如此迅速,先看看别人怎么做的吧,果然别人实现起来也就那么几行代码,真是惭愧啊,也感谢这个开源的时代,让我们可以站得更高,看得更远。

计算机算法

计算机根据黑白双方现有落子情况进行棋局评估,给出各空白落子点处的分数。五子棋的几种基本棋形包括连五,活四,冲四,活三,眠三,活二,眠二等,不同的棋形打不同的分数。这个分数不但要考虑到了己方的棋子,还要考虑对方的局势,即要分别给双方打分,并求和,这样就兼具了攻击和防守的能力。不过,这里算法上与原算法相比略去一部分,这大概也导致了这个计算机有点傻😂
对于空位的遍历也是很费资源的,所以这里参考了引用博文中做法,仅遍历了已有棋子周边的空位。

源码

保留了原程序的架构,有些地方又偷懒,所以代码有点乱

from tkinter import *
import random

offset = [(1, 0), (0, 1), (1, 1), (1, -1)]


class GoBang():
    def __int__(self):
        self.window = Tk()
        self.window.title("五子棋")
        self.window.iconbitmap('./gobang.ico')
        self.window.geometry("500x560")
        self.window.canvas = Canvas(self.window, width=480, height=480, bg='peachpuff')
        self.window.canvas.pack()
        for num in range(1, 16):
            if num == 1 or num == 15:
                self.window.canvas.create_line(num * 30, 30,
                                               num * 30, 450,
                                               width=2)
            else:
                self.window.canvas.create_line(num * 30, 30,
                                               num * 30, 450,
                                               width=1)
        for num in range(1, 16):
            if num == 1 or num == 15:
                self.window.canvas.create_line(30, num * 30,
                                               450, num * 30,
                                               width=2)
            else:
                self.window.canvas.create_line(30, num * 30,
                                               450, num * 30,
                                               width=1)

        self.window.canvas.create_oval(8 * 30 - 2, 8 * 30 - 2, 8 * 30 + 2,
                                       8 * 30 + 2, fill='black')
        self.window.canvas.create_oval(5 * 30 - 2, 5 * 30 - 2, 5 * 30 + 2,
                                       5 * 30 + 2, fill='black')
        self.window.canvas.create_oval(5 * 30 - 2, 11 * 30 - 2, 5 * 30 + 2,
                                       11 * 30 + 2, fill='black')
        self.window.canvas.create_oval(11 * 30 - 2, 5 * 30 - 2, 11 * 30 + 2,
                                       5 * 30 + 2, fill='black')
        self.window.canvas.create_oval(11 * 30 - 2, 11 * 30 - 2, 11 * 30 + 2,
                                       11 * 30 + 2, fill='black')
        self.startBtn = Button(self.window, text='Start', bg="DeepSkyBlue", width=8, height=1, command=self.chessGo)
        self.quitBtn = Button(self.window, text='Quit', bg="OrangeRed", width=8, height=1, command=self.window.quit)
        self.withDrawBtn = Button(self.window, text='Withdraw', bg="cyan", width=8, height=1, command=self.withDraw)
        self.startBtn.place(relx=0.2, rely=0.89)
        self.quitBtn.place(relx=0.6, rely=0.89)
        self.withDrawBtn.place(relx=0.4, rely=0.89)

        self.game_print = StringVar()
        self.labelInfo = Label(self.window, width=12, textvariable=self.game_print, font=("Arial", 10, "bold"),
                               justify="center")
        self.labelInfo.place(relx=0.36, rely=0.95)
        self.window.canvas.bind("<Button-1>", self.place)
        self.game_print.set("请开始新局")
        self.chess = [([0] * 15) for i in range(15)]
        # 悔棋用的顺序列表
        self.order = []

        # 棋子颜色
        self.color_count = 0
        self.color = 'black'

        self.flag_win = 0
        self.flag_go = 0

        self.window.mainloop()

    def chessGo(self):
        if self.startBtn['text'] == "Start":
            #
            self.flag_go = 1
            self.startBtn['text'] = "Going"
            self.game_print.set('请black落子')
            self.labelInfo['fg'] = 'black'

        elif self.startBtn['text'] == "New":
            self.startBtn['text'] = "Start"
            self.newGame()

    def newGame(self):
        self.window.canvas.delete("chessman")
        self.chess = [([0] * 15) for i in range(15)]
        self.order = []
        self.color_count = 0
        self.color = 'black'
        self.flag_win = 0
        self.flag_go = 0
        self.game_print.set('请开始新局')
        self.labelInfo['fg'] = 'black'

    def withDraw(self):
        if len(self.order) == 0 or self.flag_win == 1:
            self.game_print.set("Can't Withdraw")
            self.labelInfo['fg'] = 'sandybrown'
            return
        else:
            self.window.canvas.delete("chessman")
            # add for machine
            xylist = self.order.pop()
            self.chess[xylist[0]][xylist[1]] = 0
            #####
            xylist = self.order.pop()
            self.chess[xylist[0]][xylist[1]] = 0
            self.color_count = 0
            for xylist in self.order:
                y = xylist[0]
                x = xylist[1]
                if self.color_count == 0:
                    self.chess[y][x] = 1
                    self.color = 'black'

                elif self.color_count == 1:
                    self.chess[y][x] = 2
                    self.color = 'white'
                self.color_count = 1 - self.color_count
                self.window.canvas.create_oval((x + 1) * 30 - 12, (y + 1) * 30 - 12, (x + 1) * 30 + 12,
                                               (y + 1) * 30 + 12, fill=self.color,
                                               tags="chessman")
            if self.color_count:
                self.color = 'white'
            else:
                self.color = 'black'
            self.game_print.set("请" + self.color + "落子")
            self.labelInfo['fg'] = 'black'

    def place(self, event):
        if (self.flag_win == 0 and self.flag_go == 1):
            print("enter place")
            x, y = event.x, event.y
            x = round(x / 30 - 1)
            y = round(y / 30 - 1)
            if x < 0 or x > 14 or y < 0 or y > 14:
                #            tkinter.messagebox.INFO('提示', '请在棋盘上落子!')
                self.game_print.set('越界落子!')
                self.labelInfo['fg'] = "darkorange"
                return
            # add for machine 221109
            self.color_count = 0
            if self.chess[y][x] == 0:
                print("enter paint oval")
                if self.color_count == 0:
                    self.chess[y][x] = 1
                    self.color = 'black'

                elif self.color_count == 1:
                    self.chess[y][x] = 2
                    self.color = 'white'
                self.color_count = 1 - self.color_count
                self.window.canvas.create_oval((x + 1) * 30 - 12, (y + 1) * 30 - 12, (x + 1) * 30 + 12,
                                               (y + 1) * 30 + 12, fill=self.color,
                                               tags="chessman")
                self.order.append([y, x])
            else:
                #            tkinter.messagebox.INFO('提示', '此处已有棋子!')
                self.game_print.set('请别处落子!')
                self.labelInfo['fg'] = "darkorange"
                return

            if self.win(x, y):
                self.flag_win = 1
                self.startBtn['text'] = "New"
                if self.color_count == 1:
                    self.color = 'black'

                else:
                    self.color = 'white'
                    # self.messagebox.INFO('提示', '白棋胜!')
                self.game_print.set(self.color + "胜!")
                self.labelInfo['fg'] = "red"

            else:
                if self.color_count:
                    self.color = 'white'
                else:
                    self.color = 'black'

                self.game_print.set("请" + self.color + "落子")
                self.labelInfo['fg'] = "black"
                point = self.machine_drop()

                y = point[0]
                x = point[1]
                self.chess[y][x] = 2
                self.color = 'white'
                self.color_count = 0

                self.window.canvas.create_oval((x + 1) * 30 - 12, (y + 1) * 30 - 12, (x + 1) * 30 + 12,
                                               (y + 1) * 30 + 12, fill=self.color,
                                               tags="chessman")
                self.order.append([y, x])

                if self.win(x, y):
                    self.flag_win = 1
                    self.startBtn['text'] = "New"
                    if self.color_count == 1:
                        self.color = 'black'

                    else:
                        self.color = 'white'
                        # self.messagebox.INFO('提示', '白棋胜!')
                    self.game_print.set(self.color + "胜!")
                    self.labelInfo['fg'] = "red"
                else:
                    if self.color_count:
                        self.color = 'white'
                    else:
                        self.color = 'black'

                    self.game_print.set("请" + self.color + "落子")
                    self.labelInfo['fg'] = "black"
    def machine_drop(self):

        _score = 0
        point = [-1,-1]
        for i in range(0,15):
            for j in range(0,15):
                if self.can_drop(i,j):
                    score = self.get_point_score(i,j)
                    print(i,j,score)
                    if score > _score:
                        point[0] = i
                        point[1] = j
                        _score = score
                    elif score == _score:
                        r = random.randint(0, 100)
                        if r % 2 == 0:
                            point[0] = i
                            point[1] = j
        print(point)
        return point

    def can_drop(self, y, x):
        if self.chess[y][x] != 0:
            return False
        else:
            for i in (-1, 0, 1):
                for j in (-1, 0, 1):
                    if y + i >= 0 and y + i <= 14 and x + j >= 0 and x + j <= 14:
                        if self.chess[y + i][x + j] != 0:
                            return True
        return False

    def get_point_score(self, y, x):
        score = 0
        for os in offset:
            score += self.get_direction_score(y, x, os[0], os[1])
        return score

    def get_direction_score(self, y, x, y_offset, x_offset):
        count = 0  # 落子处我方连续子数
        _count = 0  # 落子处对方连续子数
        space = None  # 我方连续子中有无空格
        _space = None  # 对方连续子中有无空格
        both = 0  # 我方连续子两端有无阻挡
        _both = 0  # 对方连续子两端有无阻挡

        tmp_x = x
        tmp_y = y
        # 如果是 2 表示是边上是我方子,1 表示敌方子
        if 0 <= y+y_offset <=14 and 0 <= x + x_offset <=14:
            flag = self.chess[y + y_offset][x + x_offset]
            if flag != 0:
                for step in range(1, 6):
                    x += x_offset
                    y += y_offset
                    if 0 <= x < 15 and 0 <= y < 15:
                        if flag == 2:
                            if self.chess[y][x] == 2:
                                count += 1
                                if space is False:
                                    space = True
                            elif self.chess[y][x] == 1:
                                _both += 1
                                break
                            else:
                                if space is None:
                                    space = False
                                else:
                                    break  # 遇到第二个空格退出
                        elif flag == 1:
                            if self.chess[y][x] == 2:
                                _both += 1
                                break
                            elif self.chess[y][x] == 1:
                                _count += 1
                                if _space is False:
                                    _space = True
                            else:
                                if _space is None:
                                    _space = False
                                else:
                                    break
                    else:
                        # 遇到边也就是阻挡
                        if flag == 2:
                            both += 1
                        elif flag == 1:
                            _both += 1

            if space is False:
                space = None
            if _space is False:
                _space = None

        x = tmp_x
        y = tmp_y
        if 0 <= y - y_offset <= 14 and 0 <= x - x_offset <= 14:
            _flag = self.chess[y - y_offset][x - x_offset]
            if _flag != 0:
                for step in range(1, 6):
                    x -= x_offset
                    y -= y_offset
                    if 0 <= x < 15 and 0 <= y < 15:
                        if _flag == 2:
                            if self.chess[y][x] == 2:
                                count += 1
                                if space is False:
                                    space = True
                            elif self.chess[y][x] == 1:
                                _both += 1
                                break
                            else:
                                if space is None:
                                    space = False
                                else:
                                    break  # 遇到第二个空格退出
                        elif _flag == 1:
                            if self.chess[y][x] == 2:
                                _both += 1
                                break
                            elif self.chess[y][x] == 1:
                                _count += 1
                                if _space is False:
                                    _space = True
                            else:
                                if _space is None:
                                    _space = False
                                else:
                                    break
                    else:
                        # 遇到边也就是阻挡
                        if _flag == 1:
                            both += 1
                        elif _flag == 2:
                            _both += 1

        score = 0
        if count == 4:
            score = 10000
        elif _count == 4:
            score = 9000
        elif count == 3:
            if both == 0:
                score = 1000
            elif both == 1:
                score = 100
            else:
                score = 0
        elif _count == 3:
            if _both == 0:
                score = 900
            elif _both == 1:
                score = 90
            else:
                score = 0
        elif count == 2:
            if both == 0:
                score = 100
            elif both == 1:
                score = 10
            else:
                score = 0
        elif _count == 2:
            if _both == 0:
                score = 90
            elif _both == 1:
                score = 9
            else:
                score = 0
        elif count == 1:
            score = 10
        elif _count == 1:
            score = 9
        else:
            score = 0

        if space or _space:
            score /= 2

        return score

    def win(self, x, y):
        count = 1
        # -计算
        for i in range(-1, -5, -1):
            if x + i < 0:
                break
            else:
                if self.chess[y][x + i] == self.chess[y][x]:
                    count += 1
                else:
                    break
        for i in range(1, 5, 1):
            if x + i > 14:
                break
            else:
                if self.chess[y][x + i] == self.chess[y][x]:
                    count += 1
                else:
                    break
        if count >= 5:
            return True
        # |计算
        count = 1
        for i in range(-1, -5, -1):
            if y + i < 0:
                break
            else:
                if self.chess[y + i][x] == self.chess[y][x]:
                    count += 1
                else:
                    break
        for i in range(1, 5, 1):
            if y + i > 14:
                break
            else:
                if self.chess[y + i][x] == self.chess[y][x]:
                    count += 1
                else:
                    break
        if count >= 5:
            return True
        # /计算
        count = 1
        for i in range(-1, -5, -1):
            if y + i < 0 or x + i < 0:
                break
            else:
                if self.chess[y + i][x + i] == self.chess[y][x]:
                    count += 1
                else:
                    break
        for i in range(1, 5, 1):
            if y + i > 14 or x + i > 14:
                break
            else:
                if self.chess[y + i][x + i] == self.chess[y][x]:
                    count += 1
                else:
                    break
        if count >= 5:
            return True
        # \计算
        count = 1
        for i in range(-1, -5, -1):
            if y + i < 0 or x - i > 14:
                break
            else:
                if self.chess[y + i][x - i] == self.chess[y][x]:
                    count += 1
                else:
                    break
        for i in range(1, 5, 1):
            if y + i > 14 or x - i < 0:
                break
            else:
                if self.chess[y + i][x - i] == self.chess[y][x]:
                    count += 1
                else:
                    break
        if count >= 5:
            return True


if __name__ == "__main__":
    myBang = GoBang()
    myBang.__int__()

运行结果

人机对战取代了人人对战,不是可选模式,人执黑先手,计算机执白后手,可以悔棋。
在这里插入图片描述

引用

看了不少博文,有些算法应该出自一处,谢谢!
1.Python五子棋人机对战
2.python五子棋之人机对战
3.基于java的人机五子棋

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值