Python 华容道 python数字华容道算法_角色名

 

华容道游戏胜利条件:曹操左上位置的块移动到下方出口(以1,3坐标位置为左上角的四格区域)即可胜利。

五虎将各占两个格子,兵占一个格子,曹操占四个格子。将人物设计成继承自Button类的自定义类Block。游戏过程中,通过点击鼠标移动块。如果移动时,会与别的块有交叉(重叠),则不允许移动,无交叉是才允许移动。

移动格子通过鼠标拖动(点击住并拉拽)实现格子(Block块)的移动。且需要判断鼠标的移动方向(上下左右)。且判断是否允许移动。

1.用四个数值代表四种形状:One表示一个格子(小兵),Twoheng表示横格形状(关羽),Twoshu表示竖状(张赵马黄)、Four表示大块(曹操)

from tkinter import *
from tkinter.messagebox import *

One = 1  # 表示士兵,数字代表方块类型
Twoheng = 2  # 表示横长方形
Twoshu = 3  # 表示竖长方形
Four = 4  # 表示曹操

2.设计点类Point来存储各个方块的坐标(x,y)

class Point:  # 点类,存储方块所在的棋盘坐标
    def __init__(self, x, y):
        self.x = x
        self.y = y

3.设计鼠标按下、拖动、松开的处理函数:

def btn_MouseDown(event):  # 鼠标按下的事件
    global mouseDownPoint, mouseDown  # mouseDownPoint为鼠标按下时,位置的坐标
    mouseDownPoint = Point(event.x, event.y)  # 获取坐标点
    mouseDown = True  # 鼠标是否按下


def btn_Realse(event):  # 释放时鼠标事件
    global mouseDownPoint, mouseDown
    print(event.x, event.y)  # 打印鼠标松开时像素坐标
    if not mouseDown:  # 鼠标之前未按下,什么都不发生
        return
    moveheng = event.x - mouseDownPoint.x  # 横向的偏移量
    moveshu = event.y - mouseDownPoint.y  # 竖向偏移量
    pass

4.完善设计块类Block。通过每个块左上角的坐标,即可得出其所占的全部坐标。每个块其实是一个按钮。

class Block(Button):
    def __init__(self, p, blockType, master, r, bm):  # p:左上角棋盘位置,点对象;blockType:方块类型;
        # master:父继承;r:方块角色名;
        # bm:图片文件
        Button.__init__(self, master)
        self.Location = p  # 方块左上角棋盘位置,一个点Point对象
        self.BType = blockType  # 方块类型:One、Twoheng、Twoshu、Four
        self['text'] = r  # 方块角色名,五虎将、兵、曹操
        self['image'] = bm  # 引用的角色图片
        self.bind('<ButtonPress>', btn_MouseDown)  # 绑定事件
        self.bind('<ButtonRelease>', btn_Realse)
        self.place(x=self.Location.x * 80, y=self.Location.y * 80)  # 点坐标*80->转为图形像素位置

    def GetPoints(self):  # 获取块中所有点坐标,返回一个该方块所占据的所有坐标点的列表
        pList = []
        if self.BType == One:  # 占一个方块(兵)
            pList.append(self.Location)
        elif self.BType == Twoheng:  # 多横向一方块(关羽)
            pList.append(self.Location)
            pList.append(Point(self.Location.X + 1, self.Location.Y))
        elif self.BType == Twoshu:  # 多竖向一方块(张赵马黄)
            pList.append(self.Location)
            pList.append(Point(self.Location.X, self.Location.Y + 1))
        elif self.BType == Four:  # 占四个,多三个方块(曹操)
            pList.append(self.Location)
            pList.append(Point(self.Location.X + 1, self.Location.Y))
            pList.append(Point(self.Location.X, self.Location.Y + 1))
            pList.append(Point(self.Location.X + 1, self.Location.Y + 1))
        return pList

    def Contains(self, point):  # 某块中是否包含某个点
        pList = self.GetPoints()
        for i in range(len(pList)):  # 遍历块中所有的点坐标,和参数点坐标比较,如果相同则包含
            if pList[i].x == point.x and pList[i].y == point.y:
                return True

    def Intersects(self, block):  # 是否和另一个块交叉
        myPoints = self.GetPoints()  # 获取自身块的点列表
        otherPoints = block.GetPoints()  # 获取参数块的点列表
        for i in range(len(otherPoints)):  # 遍历比较有无相同包含点,若有则说明两者交叉
            p = otherPoints[i]
            for j in range(len(myPoints)):
                if p.x == myPoints[j].x and p.y == myPoints[j].y:
                    return True

    def IsValid(self, width, height):  # 块是否在界限内:横向4*竖向5的游戏坐标格子中
        points = self.GetPoints()
        for i in range(len(points)):
            p = points[i]
            if p.x < 0 or p.x >= width or p.y < 0 or p.y >= height:
                return False
        return True

5.设计游戏Game类,包括了游戏格子的大小(4*5),包含所有块的列表,结束点(1,3),初始化方块

class Game:
    Width = 4
    Height = 5  # 游戏格子区域
    WinFlag = False  # 判断是否结束
    Blocks = []  # 向游戏中添加块
    finishPoint = Point(1, 3)  # 游戏结束位置

    def AddBlock(self, block):  # 向区域中添加方块的方法
        if block in self.Blocks:  # 如果已经存在则添加失败
            return False
        if not block.IsValid(self.Width, self.Height):  # 如果超出区域则添加失败
            return False
        for i in range(len(self.Blocks)):
            if self.Blocks[i].Intersects(block):  # 如果区域大小不够(有交叉)则添加失败
                return False
        self.Blocks.append(block)  # 添加方块block到区域中

    def GetBlockByPos(self, p):  # 获取p位置的方块(由p获取方块)
        for i in range(len(self.Blocks)):
            if self.Blocks[i].Location.x == p.x and self.Blocks[i].Location.y == p.y:
                return self.Blocks[i]
        return False

    def MoveBlock(self, block, direction):  # block为块,direction为移动方向
        if block not in self.Blocks:
            print("非此游戏中的块")
            return
        oldx = block.Location.x  # 记录原来的位置
        oldy = block.Location.y

        if direction == 'Up':
            block.Location.y -= 1
        elif direction == 'Down':
            block.Location.y += 1
        elif direction == 'Left':
            block.Location.x -= 1
        elif direction == 'Right':
            block.Location.x += 1
        moveOK = True  # 假设可以移动
        if not block.IsValid(self.Width, self.Height):  # 如果移动越界
            moveOK = False
        else:
            for i in range(len(self.Blocks)):  # 遍历所有方块是否有交叉
                if block is not self.Blocks[i] and block.Intersects(self.Blocks[i]):  # 如交叉
                    moveOK = False
                    break
        if not moveOK:
            print('不能移动')
            print(block.Location.x, block.Location.y)
            block.Location = Point(oldx, oldy)  # 回到原来的位置
            print(block.Location.x, block.Location.y)
        if moveOK:
            print(block['text'], block.Location.x, block.Location.y)
            if block['text'] == '曹操' and block.Location.x == 1 and block.Location.y == 3:
                self.WinFlag = True
        return moveOK

    def GameWin(self):  # 胜利否
        if self.WinFlag == True:
            return True
        else:
            return False

 

全部完整代码:

# -*- coding: utf-8 -*-
# @Time : 2020/12/2 下午6:50
# @Author : Zhenghui Lyu
# @File : main.py
# @Software: PyCharm

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

One = 1  # 表示士兵,数字代表方块类型
Twoheng = 2  # 表示横长方形
Twoshu = 3  # 表示竖长方形
Four = 4  # 表示曹操


class Point:  # 点类,存储方块所在的棋盘坐标
    def __init__(self, x, y):
        self.x = x
        self.y = y


BlockSize = 80  # 游戏中块的显示大小

mouseDownPoint = Point(0, 0)  # 鼠标的按下的位置

mouseDown = False  # 鼠标默认未按下


def btn_MouseDown(event):  # 鼠标按下的事件
    global mouseDownPoint, mouseDown  # mouseDownPoint为鼠标按下时,位置的坐标
    mouseDownPoint = Point(event.x, event.y)  # 获取坐标点
    mouseDown = True  # 鼠标是否按下


def btn_Realse(event):  # 释放时鼠标事件
    global mouseDownPoint, mouseDown
    print(event.x, event.y)  # 打印鼠标松开时像素坐标
    if not mouseDown:  # 鼠标之前未按下,什么都不发生
        return
    moveheng = event.x - mouseDownPoint.x  # 横向的偏移量
    moveshu = event.y - mouseDownPoint.y  # 竖向偏移量
    x = int(event.widget.place_info()['x']) // 80
    y = int(event.widget.place_info()['y']) // 80
    block = game.GetBlockByPos(Point(x, y))
    if moveheng >= BlockSize * 1 / 3:
        game.MoveBlock(block, 'Right')
    elif moveheng <= -BlockSize * 1 / 3:
        game.MoveBlock(block, 'Left')
    elif moveshu >= BlockSize * 1 / 3:
        game.MoveBlock(block, 'Down')
    elif moveshu <= -BlockSize * 1 / 3:
        game.MoveBlock(block, 'Up')
    else:
        return
    event.widget.place(x=block.Location.x * 80, y=block.Location.y * 80)
    if game.GameWin():
        print('游戏胜利!')
        messagebox.showinfo('Info', '游戏胜利')
    mouseDown = False


class Block(Button):
    def __init__(self, p, blockType, master, r, bm):  # p:左上角棋盘位置,点对象;blockType:方块类型;
        # master:父继承;r:方块角色名;
        # bm:图片文件
        Button.__init__(self, master)
        self.Location = p  # 方块左上角棋盘位置,一个点Point对象
        self.BType = blockType  # 方块类型:One、Twoheng、Twoshu、Four
        self['text'] = r  # 方块角色名,五虎将、兵、曹操
        self['image'] = bm  # 引用的角色图片
        self.bind('<ButtonPress>', btn_MouseDown)  # 绑定事件
        self.bind('<ButtonRelease>', btn_Realse)
        self.place(x=self.Location.x * 80, y=self.Location.y * 80)  # 点坐标*80->转为图形像素位置

    def GetPoints(self):  # 获取块中所有点坐标,返回一个该方块所占据的所有坐标点的列表
        pList = []
        if self.BType == One:  # 占一个方块(兵)
            pList.append(self.Location)
        elif self.BType == Twoheng:  # 多横向一方块(关羽)
            pList.append(self.Location)
            pList.append(Point(self.Location.x + 1, self.Location.y))
        elif self.BType == Twoshu:  # 多竖向一方块(张赵马黄)
            pList.append(self.Location)
            pList.append(Point(self.Location.x, self.Location.y + 1))
        elif self.BType == Four:  # 占四个,多三个方块(曹操)
            pList.append(self.Location)
            pList.append(Point(self.Location.x + 1, self.Location.y))
            pList.append(Point(self.Location.x, self.Location.y + 1))
            pList.append(Point(self.Location.x + 1, self.Location.y + 1))
        return pList

    def Contains(self, point):  # 某块中是否包含某个点
        pList = self.GetPoints()
        for i in range(len(pList)):  # 遍历块中所有的点坐标,和参数点坐标比较,如果相同则包含
            if pList[i].x == point.x and pList[i].y == point.y:
                return True

    def Intersects(self, block):  # 是否和另一个块交叉
        myPoints = self.GetPoints()  # 获取自身块的点列表
        otherPoints = block.GetPoints()  # 获取参数块的点列表
        for i in range(len(otherPoints)):  # 遍历比较有无相同包含点,若有则说明两者交叉
            p = otherPoints[i]
            for j in range(len(myPoints)):
                if p.x == myPoints[j].x and p.y == myPoints[j].y:
                    return True

    def IsValid(self, width, height):  # 块是否在界限内:横向4*竖向5的游戏坐标格子中
        points = self.GetPoints()
        for i in range(len(points)):
            p = points[i]
            if p.x < 0 or p.x >= width or p.y < 0 or p.y >= height:
                return False
        return True


class Game:
    Width = 4
    Height = 5  # 游戏格子区域
    WinFlag = False  # 判断是否结束
    Blocks = []  # 向游戏中添加块
    finishPoint = Point(1, 3)  # 游戏结束位置

    def AddBlock(self, block):  # 向区域中添加方块的方法
        if block in self.Blocks:  # 如果已经存在则添加失败
            return False
        if not block.IsValid(self.Width, self.Height):  # 如果超出区域则添加失败
            return False
        for i in range(len(self.Blocks)):
            if self.Blocks[i].Intersects(block):  # 如果区域大小不够(有交叉)则添加失败
                return False
        self.Blocks.append(block)  # 添加方块block到区域中

    def GetBlockByPos(self, p):  # 获取p位置的方块(由p获取方块)
        for i in range(len(self.Blocks)):
            if self.Blocks[i].Location.x == p.x and self.Blocks[i].Location.y == p.y:
                return self.Blocks[i]
        return False

    def MoveBlock(self, block, direction):  # block为块,direction为移动方向
        if block not in self.Blocks:
            print("非此游戏中的块")
            return
        oldx = block.Location.x  # 记录原来的位置
        oldy = block.Location.y

        if direction == 'Up':
            block.Location.y -= 1
        elif direction == 'Down':
            block.Location.y += 1
        elif direction == 'Left':
            block.Location.x -= 1
        elif direction == 'Right':
            block.Location.x += 1
        moveOK = True  # 假设可以移动
        if not block.IsValid(self.Width, self.Height):  # 如果移动越界
            moveOK = False
        else:
            for i in range(len(self.Blocks)):  # 遍历所有方块是否有交叉
                if block is not self.Blocks[i] and block.Intersects(self.Blocks[i]):  # 如交叉
                    moveOK = False
                    break
        if not moveOK:
            print('不能移动')
            print(block.Location.x, block.Location.y)
            block.Location = Point(oldx, oldy)  # 回到原来的位置
            print(block.Location.x, block.Location.y)
        if moveOK:
            print(block['text'], block.Location.x, block.Location.y)
            if block['text'] == '曹操' and block.Location.x == 1 and block.Location.y == 3:
                self.WinFlag = True
        return moveOK

    def GameWin(self):  # 胜利否
        if self.WinFlag == True:
            return True
        else:
            return False


win = Tk()
win.title('华容道游戏')
win.geometry('320x400')
game = Game()
bm = [PhotoImage(file='cao.gif'), PhotoImage(file='guan.gif'), PhotoImage(file='huang.gif'),
      PhotoImage(file='ma.gif'), PhotoImage(file='zhang.gif'), PhotoImage(file='zhao.gif'),
      PhotoImage(file='bing.gif')]  # 注意tkinter只支持gif格式
b0 = Block(Point(1, 0), Four, win, '曹操', bm[0])
b1 = Block(Point(1, 2), Twoheng, win, '关羽', bm[1])
b2 = Block(Point(3, 2), Twoshu, win, '黄忠', bm[2])
b3 = Block(Point(0, 0), Twoshu, win, '马超', bm[3])
b4 = Block(Point(0, 2), Twoshu, win, '张飞', bm[4])
b5 = Block(Point(3, 0), Twoshu, win, '赵云', bm[5])
b6 = Block(Point(0, 4), One, win, '兵', bm[6])
b7 = Block(Point(1, 3), One, win, '兵', bm[6])
b8 = Block(Point(2, 3), One, win, '兵', bm[6])
b9 = Block(Point(3, 4), One, win, '兵', bm[6])
game.AddBlock(b0)
game.AddBlock(b1)
game.AddBlock(b2)
game.AddBlock(b3)
game.AddBlock(b4)
game.AddBlock(b5)
game.AddBlock(b6)
game.AddBlock(b7)
game.AddBlock(b8)
game.AddBlock(b9)
win.mainloop()