
华容道游戏胜利条件:曹操左上位置的块移动到下方出口(以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()
浙公网安备 33010602011771号