python3+tkinter实现的黑白棋,代码完整 100%能运行

今天分享给大家的是采用Python3+tkinter制作而成的小项目——黑白棋

tkinter是Python内置的图形化模块,简单易用,一般的小型UI程序可以快速用它实现,具体的tkinter相关知识王老师会在以后开辟专栏单独讲解

我们先来看看这个黑白棋项目吧

一、项目演示

二、代码

完整代码如下,用到的素材(图片等)下载地址为:www.itprojects.cn/detail.html…

from tkinter import *
from tkinter.messagebox import *
import random

root = Tk('黑白棋')
root.title("黑白棋(更多项目实例请访问www.itprojects.cn)")
# 加载图片
imgs = [PhotoImage(file='black.png'), PhotoImage(file='white.png'), PhotoImage(file='board.png'), PhotoImage(file='info2.png')]


def resetBoard(board):
    """重置棋盘"""
    for x in range(8):
        for y in range(8):
            board[x][y] = 'none'
    # Starting pieces:
    board[3][3] = 'black'
    board[3][4] = 'white'
    board[4][3] = 'white'
    board[4][4] = 'black'


def getNewBoard():
    """开局时建立新棋盘"""
    board = []
    for i in range(8):
        board.append(['none'] * 8)
    return board


def isValidMove(board, tile, xstart, ystart):
    """是否是合法走法,如果合法返回需要翻转的棋子列表"""
    # 如果该位置已经有棋子或者出界了,返回False
    if not isOnBoard(xstart, ystart) or board[xstart][ystart] != 'none':
        return False
    # 临时将tile 放到指定的位置
    board[xstart][ystart] = tile
    if tile == 'black':
        otherTile = 'white'
    else:
        otherTile = 'black'
    # 要被翻转的棋子
    tilesToFlip = []
    for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
        x, y = xstart, ystart
        x += xdirection
        y += ydirection
        if isOnBoard(x, y) and board[x][y] == otherTile:
            x += xdirection
            y += ydirection
            if not isOnBoard(x, y):
                continue
            # 一直走到出界或不是对方棋子的位置
            while board[x][y] == otherTile:
                x += xdirection
                y += ydirection
                if not isOnBoard(x, y):
                    break
            # 出界了,则没有棋子要翻转OXXXXX
            if not isOnBoard(x, y):
                continue
            # 是自己的棋子OXXXXXXO
            if board[x][y] == tile:
                while True:
                    x -= xdirection
                    y -= ydirection
                    # 回到了起点则结束
                    if x == xstart and y == ystart:
                        break
                    # 需要翻转的棋子
                    tilesToFlip.append([x, y])
    # 将前面临时放上的棋子去掉,即还原棋盘
    board[xstart][ystart] = 'none'  # restore the empty space
    # 没有要被翻转的棋子,则走法非法。翻转棋的规则。
    if len(tilesToFlip) == 0:  # If no tiles were flipped, this is not a valid move.
        return False
    return tilesToFlip


def isOnBoard(x, y):
    """是否出界"""
    return x >= 0 and x <= 7 and y >= 0 and y <= 7


def getValidMoves(board, tile):
    """获取可落子的位置"""
    validMoves = []
    for x in range(8):
        for y in range(8):
            if isValidMove(board, tile, x, y) != False:
                validMoves.append([x, y])
    return validMoves


def getScoreOfBoard(board):
    """获取棋盘上黑白双方的棋子数"""
    xscore = 0
    oscore = 0
    for x in range(8):
        for y in range(8):
            if board[x][y] == 'black':
                xscore += 1
            if board[x][y] == 'white':
                oscore += 1
    return {'black': xscore, 'white': oscore}


def whoGoesFirst():
    """决定谁先走"""
    if random.randint(0, 1) == 0:
        return 'computer'
    else:
        return 'player'


def makeMove(board, tile, xstart, ystart):
    """将一个tile棋子放到(xstart, ystart)"""
    tilesToFlip = isValidMove(board, tile, xstart, ystart)
    if tilesToFlip == False:
        return False
    board[xstart][ystart] = tile
    for x, y in tilesToFlip:  # tilesToFlip是需要翻转的棋子列表
        board[x][y] = tile  # 翻转棋子
    return True


def getBoardCopy(board):
    """复制棋盘"""
    dupeBoard = getNewBoard()
    for x in range(8):
        for y in range(8):
            dupeBoard[x][y] = board[x][y]
    return dupeBoard


def isOnCorner(x, y):
    """是否在角上"""
    return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y == 7) or (x == 7 and y == 7)


def getComputerMove(board, computerTile):
    """电脑走法,AI"""
    # 获取所以合法走法
    possibleMoves = getValidMoves(board, computerTile)
    if not possibleMoves:  # 如果没有合法走法
        print("电脑没有合法走法")
        return None

    # 打乱所有合法走法
    random.shuffle(possibleMoves)
    # [x, y]在角上,则优先走,因为角上的不会被再次翻转
    for x, y in possibleMoves:
        if isOnCorner(x, y):
            return [x, y]
    bestScore = -1
    for x, y in possibleMoves:
        dupeBoard = getBoardCopy(board)
        makeMove(dupeBoard, computerTile, x, y)
        # 按照分数选择走法,优先选择翻转后分数最多的走法
        score = getScoreOfBoard(dupeBoard)[computerTile]
        if score > bestScore:
            bestMove = [x, y]
            bestScore = score
    return bestMove


def isGameOver(board):
    """是否游戏结束"""
    for x in range(8):
        for y in range(8):
            if board[x][y] == 'none':
                return False
    return True


def drawQiPan():
    """画棋盘"""
    img1 = imgs[2]
    cv.create_image((360, 360), image=img1)
    cv.pack()


def callback(event):
    """走棋"""
    global turn
    # print ("clicked at", event.x, event.y,turn)
    # x=(event.x)//40  #换算棋盘坐标
    # y=(event.y)//40
    if (gameOver == False and turn == 'computer'):  # 没轮到玩家走棋
        return
    col = int((event.x - 40) / 80)  # 换算棋盘坐标
    row = int((event.y - 40) / 80)
    if mainBoard[col][row] != "none":
        showinfo(title="提示", message="已有棋子")
    if makeMove(mainBoard, playerTile, col, row) == True:  # 将一个玩家棋子放到(col, row)
        if getValidMoves(mainBoard, computerTile) != []:
            turn = 'computer'
    # 电脑走棋
    if getComputerMove(mainBoard, computerTile) == None:
        turn = 'player'
        showinfo(title="玩家继续", message="玩家继续")
    else:
        computerGo()
        # 重画所有的棋子和棋盘
    drawAll()
    drawCanGo()
    if isGameOver(mainBoard):  # 游戏结束,显示双方棋子数量
        scorePlayer = getScoreOfBoard(mainBoard)[playerTile]
        scoreComputer = getScoreOfBoard(mainBoard)[computerTile]
        outputStr = gameoverStr + "玩家:" + str(scorePlayer) + ":" + "电脑:" + str(scoreComputer)
        showinfo(title="游戏结束提示", message=outputStr)


def computerGo():
    """电脑走棋"""
    global turn
    if (gameOver == False and turn == 'computer'):
        x, y = getComputerMove(mainBoard, computerTile)  # 电脑AI走法
        makeMove(mainBoard, computerTile, x, y)
        savex, savey = x, y
        # 玩家没有可行的走法了,则电脑继续,否则切换到玩家走
        if getValidMoves(mainBoard, playerTile) != []:
            turn = 'player'
        else:
            if getValidMoves(mainBoard, computerTile) != []:
                showinfo(title="电脑继续", message="电脑继续")
                computerGo()


def drawAll():
    """重画所有的棋子和棋盘"""
    drawQiPan()
    for x in range(8):
        for y in range(8):
            if mainBoard[x][y] == 'black':
                cv.create_image((x * 80 + 80, y * 80 + 80), image=imgs[0])
                cv.pack()
            elif mainBoard[x][y] == 'white':
                cv.create_image((x * 80 + 80, y * 80 + 80), image=imgs[1])
                cv.pack()


def drawCanGo():
    """画提示位置"""
    list1 = getValidMoves(mainBoard, playerTile)
    for m in list1:
        x = m[0]
        y = m[1]
        cv.create_image((x * 80 + 80, y * 80 + 80), image=imgs[3])
        cv.pack()


if __name__ == '__main__':
    # 初始化
    gameOver = False
    gameoverStr = 'Game Over Score '
    mainBoard = getNewBoard()
    resetBoard(mainBoard)
    turn = whoGoesFirst()
    showinfo(title="游戏开始提示", message=turn + "先走!")
    print(turn, "先走!")
    if turn == 'player':
        playerTile = 'black'
        computerTile = 'white'
    else:
        playerTile = 'white'
        computerTile = 'black'
        computerGo()

    # 设置窗口
    cv = Canvas(root, bg='green', width=720, height=780)
    # 重画所有的棋子和棋盘
    drawAll()
    drawCanGo()
    cv.bind("<Button-1>", callback)
    cv.pack()
    root.mainloop()

本文首发于python黑洞网,csdn同步跟新

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
•Alpha-Beta剪枝(Alpha-Beta pruning) 对于一般的最大最小搜索,即使每一步只有很少的下法,搜索的位置也会增长非常快;在大多数的中局棋形中,每步平均有十个位置可以下棋,于是假设搜索九步(程序术语称为搜索深度为九),就要搜索十亿个位置(十的九次方),极大地限制了电脑的棋力。于是采用了一个方法,叫“alpha-beta剪枝”,它大为减少了检测的数目,提高电脑搜索的速度。各种各样的这种算法用于所有的强力Othello程序。(同样用于其他棋类游戏,如国际象棋和跳棋)。为了搜索九步,一个好的程序只用搜索十万到一百万个位置,而不是没用前的十亿次。 •估值 这是一个程序中最重要的部分,如果这个模块太弱,则就算算法再好也没有用。我将要叙述三种不同的估值函数范例。我相信,大多数的Othello程序都可以归结于此。 棋格表:这种算法的意思是,不同的棋格有不同的值,角的值大而角旁边的格子值要小。忽视对称的话,棋盘上有10个不同的位置,每个格子根据三种可能性赋值:黑棋、白棋和空。更有经验的逼近是在游戏的不同阶段对格子赋予不同的值。例如,角在开局阶段和中局开始阶段比终局阶段更重要。采用这种算法的程序总是很弱(我这样认为),但另一方面,它很容易实现,于是许多程序开始采用这种逼近。 基于行动力的估值:这种更久远的接近有很强的全局观,而不像棋格表那样局部化。观察表明,许多人类玩者努力获得最大的行动力(可下棋的数目)和潜在行动力(临近对手棋子的空格,见技巧篇)。如果代码有效率的话,可以很快发现,它们提高棋力很多。 基于模版的估值 :正如上面提及的,许多中等力量的程序经常合并一些边角判断的知识,最大行动力和潜在行动力是全局特性,但是他们可以被切割成局部配置,再加在一起。棋子最少化也是如此。这导致了以下的概括:在估值函数中仅用局部配置(模版),这通常用单独计算每一行、一列、斜边和角落判断,再加在一起来实现。 估值合并:一般程序的估值基于许多的参数,如行动力、潜在行动力、余裕手、边角判断、稳定子。但是怎么样将他们合并起来得到一个估值呢?一般采用线性合并。设a1,a2,a3,a4为参数,则估值s:=n1*a1+n2*a2+n3*a3+n4*a4。其中n1,n2,n3,n4为常数,术语叫“权重”(weight),它决定了参数的重要性,它们取决于统计值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值