吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4363|回复: 23
收起左侧

[Python 转载] [推箱子-Sokoban】文曲星经典小游戏搬到电脑上了,我卡在13关看看你能过几关

 关闭 [复制链接]
dltest 发表于 2022-8-10 11:00
本帖最后由 dltest 于 2022-8-12 09:02 编辑

推箱子 -Sokoban
网上有很多题材了,地图也很多
本次用python的tkinter模块Canvas(画布)组件,一步一步实现经典

┌-------------------指挥部消息-----------------------------┐
| 从step 9开始,功能就比较完备了,可以正常进行游戏了    |
| 着急摸鱼的可以直接从第9个文件后选一个下载直接开玩。 |
└----------------------------------------------------------------┘


STEP 1 构建地图
网上找的地图
[Asm] 纯文本查看 复制代码
####__
#-.#__
#--###
#*@--#
#--$-#
#--###
####__
Title:
Author:David W. Skinner

地图说明# - 空地$ 箱子@ 工人. 目标位* 箱子在目标位置+ 工人在目标位置它长这个样子 2022-08-10_102420.png

让我们用python翻译一下
[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None


class Sokoban:
    def __init__(self, root):
        root.title("推箱子 dltest@52pj")
        self.width=600
        self.height=400
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(root,bg='white',height=self.height,width=self.width)
        self.canvas.pack()
        self.drawMap()

    def drawMap(self):
        global image_target ,image_wall ,image_box ,image_box_at_target ,image_worker ,image_worker_at_target
        image_target = PhotoImage(file="image_target.png")
        image_wall = PhotoImage(file="image_wall.png")
        image_box = PhotoImage(file="image_box.png")
        image_box_at_target = PhotoImage(file="image_box_at_target.png")
        image_worker = PhotoImage(file="image_worker.png")
        image_worker_at_target = PhotoImage(file="image_worker_at_target.png")
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''
        map =  '####__\
                #-.#__\
                #--###\
                #*@--#\
                #--$-#\
                #--###\
                ####__'
        row = 7  #行
        colum = 6 # 列
        w = 32 #图片宽
        offset_x = (self.width - colum * w)/2
        offset_y = (self.height - row * w)/2
        map = map.replace(' ','').replace('\r\n','')
        for i in range(0,row):
            for j in range(0,colum):
                if map[i*colum + j] == ".":
                    self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_target)
                elif map[i*colum + j] == "#":
                    self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_wall)
                elif map[i*colum + j] == "$":
                    self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_box)
                elif map[i*colum + j] == "*":
                    self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_box_at_target)
                elif map[i*colum + j] == "@":
                    self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_worker)
                elif map[i*colum + j] == "+":
                    self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_worker_at_target)



if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()

运行一下,我们的地图构建完成。
2022-08-10_095940.png

蔬菜贴图,自己下载重命名一下。自己在网上找的,如果你有好看的蔬菜,欢迎交流。
image_wall.png image_wall.png image_worker.png image_worker.png image_worker_at_target.png image_worker_at_target.png image_box.png image_box.png image_box_at_target.png image_box_at_target.png image_target.png image_target.png

懒人网盘直接下载蔬菜包及map包地址:
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj

虽然目前这个程序啥也不能干,但还是要提供一下打包好的下载,也放在上边的共享内。

后续的更新,如无特殊,都存放在上边的共享内,请按标题查找。
本次上传为 :1.step1构建地图.rar




免费评分

参与人数 6吾爱币 +12 热心值 +5 收起 理由
shengforever + 1 谢谢@Thanks!
iokeyz + 1 + 1 sokoban 我的最爱!
DAshi0527 + 1 + 1 谢谢@Thanks!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
wapjltb + 1 + 1 用心讨论,共获提升!
kololi + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| dltest 发表于 2022-8-12 21:45
setp10.加载更多关卡文件

可以自由选择关卡文件,丰富你的摸鱼时间。
GIF 2022-8-12 21-41-58.gif
本次网盘上传文件为: 10.step10加载更多关卡文件.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj

[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *
from tkinter import filedialog, ttk
import tkinter.messagebox as mbox
import tkinter.font as tkFont
import copy

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None
counter_tv = 0

def load_map(path):
    maps = []
    if not path:
        path = 'levels/1.入门关卡/BoxWorld.xsb'
    file_object = open(path, 'r')
    Title =None
    Author = None
    try:
        lines = file_object.readlines()
        star = False
        end = False
        currentmap = []
        level = 0
        for line in lines:
            if not line.replace('\n', ''):
                continue
            if line.startswith(";Level"):
                star = True
                end = False
                line = line.replace('\n', '')
                level = line.split(' ')[1]
            elif line.startswith("Title:"):
                Title = line.replace('\n', '')
                Title = Title.split(':')[1]
            elif line.startswith("Author:"):
                Author = line.replace('\n', '')
                Author = Author.split(' ')[1]
                star = False
                end = True
            if star and not end:
                currentmap.append(line.replace('\n', ''))
            if level and not star and end:
                maps.append({'level': int(level),
                               'map': currentmap[1:-1],
                               'Title': Title,
                               'Author': Author})
                currentmap.clear()
    finally:
        file_object.close()
    return maps


def replace_map_char(current_map, row, colum, char):
    string = list(current_map[row])
    string[colum] = char
    current_map[row] = ''.join(string)
    return current_map


class Sokoban:
    def __init__(self, root):
        self.init_ui(root)
        self.maps = load_map("")
        self.init_data()
        self.level = 1
        self.GLineEdit_926.insert(0 ,'1')
        self.steps =[]
        self.keylist=[]
        self.get_map_by_level(self.maps, self.level)
        self.finished =  False
        self.isnewlevel = True



    def init_ui(self, root):  # 界面初始化
        root.title("推箱子 dltest@52pj")
        self.width = 839
        self.height = 586
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (
        self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)

        self.width_frame = 738
        self.height_frame = 525
        self.Frame_342=tk.Frame(root)
        self.Frame_342.place(x=50,y=50,width=self.width_frame,height=self.height_frame)
        self.Frame_342["bg"] = "#90f090"
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(self.Frame_342, bg='white', width=self.width_frame, height=self.height_frame)
        self.canvas.bind_all("<KeyPress>", self.keydown)  # 监听按键
        self.canvas.pack()

        self.GButton_193=tk.Button(root)
        self.GButton_193["bg"] = "#efefef"
        ft = tkFont.Font(family='Times',size=10)
        self.GButton_193["font"] = ft
        self.GButton_193["fg"] = "#000000"
        self.GButton_193["justify"] = "center"
        self.GButton_193["text"] = "选择关卡文件 "
        self.GButton_193["relief"] = "groove"
        self.GButton_193.place(x=10,y=10,width=79,height=30)
        self.GButton_193["command"] = self.GButton_193_command

        self.GLineEdit_926=tk.Entry(root,validate='key',validatecommand=(root.register(self.check_input), '%P'))
        self.GLineEdit_926["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_926["font"] = ft
        self.GLineEdit_926["fg"] = "#333333"
        self.GLineEdit_926["justify"] = "center"
        self.GLineEdit_926["text"] = "当前"
        self.GLineEdit_926.place(x=390,y=10,width=41,height=30)

        self.GLabel_356=tk.Label(root)
        ft = tkFont.Font(family='Times',size=14)
        self.GLabel_356["font"] = ft
        self.GLabel_356["fg"] = "#333333"
        self.GLabel_356["justify"] = "left"
        self.GLabel_356["text"] = "/100"
        self.GLabel_356.place(x=440,y=10,width=34,height=30)

        self.GButton_747=tk.Button(root)
        self.GButton_747["bg"] = "#efefef"
        ft = tkFont.Font(family='Times',size=10)
        self.GButton_747["font"] = ft
        self.GButton_747["fg"] = "#000000"
        self.GButton_747["justify"] = "center"
        self.GButton_747["text"] = "上一关"
        self.GButton_747["relief"] = "groove"
        self.GButton_747.place(x=330,y=10,width=49,height=30)
        self.GButton_747["command"] = self.GButton_747_command

        self.GButton_963=tk.Button(root)
        self.GButton_963["bg"] = "#efefef"
        ft = tkFont.Font(family='Times',size=10)
        self.GButton_963["font"] = ft
        self.GButton_963["fg"] = "#000000"
        self.GButton_963["justify"] = "center"
        self.GButton_963["text"] = "下一关"
        self.GButton_963["relief"] = "groove"
        self.GButton_963.place(x=480,y=10,width=49,height=30)
        self.GButton_963["command"] = self.GButton_963_command

        self.GLabel_451=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        self.GLabel_451["font"] = ft
        self.GLabel_451["fg"] = "#333333"
        self.GLabel_451["justify"] = "center"
        self.GLabel_451["text"] = "已完成"
        self.GLabel_451.place(x=560,y=10,width=55,height=30)

        self.GLabel_562=tk.Label(root)
        ft = tkFont.Font(family='Times',size=14)
        self.GLabel_562["font"] = ft
        self.GLabel_562["fg"] = "#5fb878"
        self.GLabel_562["justify"] = "left"
        self.GLabel_562["text"] = "0/4"
        self.GLabel_562.place(x=610,y=10,width=41,height=31)

        self.GButton_976=tk.Button(root)
        self.GButton_976["bg"] = "#efefef"
        ft = tkFont.Font(family='Times',size=10)
        self.GButton_976["font"] = ft
        self.GButton_976["fg"] = "#000000"
        self.GButton_976["justify"] = "center"
        self.GButton_976["text"] = "重玩本关"
        self.GButton_976.place(x=770,y=10,width=61,height=30)
        self.GButton_976["command"] = self.GButton_976_command

        self.GButton_263=tk.Button(root)
        self.GButton_263["bg"] = "#efefef"
        ft = tkFont.Font(family='Times',size=10)
        self.GButton_263["font"] = ft
        self.GButton_263["fg"] = "#000000"
        self.GButton_263["justify"] = "center"
        self.GButton_263["text"] = "后退"
        self.GButton_263.place(x=790,y=50,width=41,height=30)
        self.GButton_263["command"] = self.GButton_263_command

        self.GLabel_645=tk.Label(root)
        self.GLabel_645["bg"] = "#00ced1"
        self.GLabel_645.place(x=790,y=90,width=42,height=484)

        title=('历史' , '操作1','操作2','op1_url','op1_ur2','zbinfo' )
        self.tvhistory=ttk.Treeview(self.GLabel_645,columns=title,style='Treeview',show='headings',  height=25 )
        self.tvhistory.pack()
        self.tvhistory.heading('历史',text='历史' )
        self.tvhistory.column('历史',width=40,anchor=CENTER)


        self.Progressbar_698=ttk.Progressbar(root,orient=tk.VERTICAL)
        self.Progressbar_698.place(x=10,y=50,width=30,height=527)

        self.GLabel_88=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        self.GLabel_88["font"] = ft
        self.GLabel_88["fg"] = "#333333"
        self.GLabel_88["justify"] = "center"
        self.GLabel_88["text"] = "关卡名"
        self.GLabel_88.place(x=110,y=10,width=70,height=25)

        self.GLineEdit_198=tk.Entry(root)
        self.GLineEdit_198["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_198["font"] = ft
        self.GLineEdit_198["fg"] = "#333333"
        self.GLineEdit_198["justify"] = "center"
        self.GLineEdit_198["text"] = "title"
        self.GLineEdit_198.place(x=190,y=10,width=117,height=30)

    def init_data(self):  # 数据初始化
        self.offset_x = 0
        self.offset_y = 0
        self.imageWidth = 32
        self.worker = {'name': 'worker',  # 类型
                       'label': '@',  # 当前位置符号
                       'walk': 1,
                       'x': 0, 'y': 0,  # 当前位置
                       'tox': 0, 'toy': 0,  # 目标位置
                       'id': 0}  # 资源id
        self.current_map = []
        self.map = '####__\
                    #-.#__\
                    #--###\
                    #*@--#\
                    #--$-#\
                    #--###\
                    ####__'
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''
        self.row = 7  # 行
        self.colum = 6  # 列
        self.boxes = []

    def check_input(self,char):
        if (char.isdigit() or char == "") :
            return True
        else:
            return False

    def GButton_193_command(self): #文件选择
        print("command self.GButton_193")
        selected_file_path = filedialog.askopenfilename(title='请选择标准的关卡类型文件',initialdir='./levels/',
                                                        filetypes=(("标准关卡", "*.xsb"),("自编关卡", "*.txt"),("其它关卡", "*.sok"),
                                       ("All files", "*.xsb;*.txt;*.sok") ))  # 使用askopenfilename函数选择单个文件
        self.maps = load_map(selected_file_path)
        self.GLineEdit_926.delete(0,'end')
        self.GLineEdit_926.insert(0,'1')
        self.get_map_by_level(self.maps,1)

    def GButton_747_command(self):
        self.isnewlevel = True
        self.steps.clear()
        self.keylist.clear()
        self.reset(self.tvhistory)
        level = 1
        try:
            level =int(self.GLineEdit_926.get())-1
        except:
            return
        if level >= 1 and level <= 100:
            self.GLineEdit_926.delete(0,'end')
            self.GLineEdit_926.insert(0,f'{level}')
            self.get_map_by_level(self.maps,level)


    def GButton_963_command(self): #下一关
        self.isnewlevel = True
        self.steps.clear()
        self.keylist.clear()
        self.reset(self.tvhistory)
        level = 1
        try:
            level =int(self.GLineEdit_926.get())+1
        except:
            return
        if level >= 1 and level <= 100:
            self.GLineEdit_926.delete(0,'end')
            self.GLineEdit_926.insert(0,f'{level}')
            self.get_map_by_level(self.maps,level)



    def GButton_976_command(self): #重玩
        self.isnewlevel = True
        self.steps.clear()
        self.keylist.clear()
        self.reset(self.tvhistory)
        level = 1
        try:
            level =int(self.GLineEdit_926.get())
        except:
            return
        if level >= 1 and level <= 100:
            self.GLineEdit_926.delete(0,'end')
            self.GLineEdit_926.insert(0,f'{level}')
            self.get_map_by_level(self.maps,level)


    def GButton_263_command(self): #后退
        if len(self.steps) == 1 or len(self.steps) == 0:
            return
        tempstepslist =copy.deepcopy(self.steps)
        print(tempstepslist)
        temp = None
        key = self.keylist[-1]
        tempkeys = copy.deepcopy(self.keylist[:-1])
        # print(key,end='')
        self.steps.clear()
        self.keylist.clear()
        if key.isupper():
            temp = tempstepslist[-3]
            self.steps += tempstepslist[:-2]
        else:
            temp = tempstepslist[-2]
            self.steps += tempstepslist[:-1]
        print(tempkeys)
        self.keylist += tempkeys
        print(temp)
        print(self.steps)
        self.draw_map(temp,False)
        self.reset(self.tvhistory)
        tempkeys.reverse()
        for i in tempkeys:
            self.tvhistory.insert('','end',values=i)

    def get_map_by_level(self, maps, level=1):
        self.current_map.clear()
        for item in maps:
            if item['level'] == level:
                self.current_map += item['map']
                self.GLineEdit_198.delete(0,'end')
                self.GLineEdit_198.insert(0,item['Title'])
                self.draw_map(item['map'])
                break

    def draw_map(self, map,flag = True):  # 绘制地图
        if flag:
            temp_map = copy.deepcopy(map)
            self.steps.append(temp_map)
            # print(map)
        else:
            self.current_map.clear()
            self.current_map += map
            pass
        global image_target, image_wall, image_box, image_box_at_target, image_worker, image_worker_at_target
        image_target = PhotoImage(file="skins/base/image_target.png")
        image_wall = PhotoImage(file="skins/base/image_wall.png")
        image_box = PhotoImage(file="skins/base/image_box.png")
        image_box_at_target = PhotoImage(file="skins/base/image_box_at_target.png")
        image_worker = PhotoImage(file="skins/base/image_worker.png")
        image_worker_at_target = PhotoImage(file="skins/base/image_worker_at_target.png")
        self.canvas.delete("all")
        row =len(map)
        colum = len(map[0])
        self.row = row
        self.colum = colum
        self.boxes.clear()
        self.offset_x = (self.width_frame - colum * self.imageWidth) / 2
        self.offset_y = (self.height_frame - row * self.imageWidth) / 2
        count_target = 0  # 记录没有箱子的目标位
        count_box_on_target = 0
        for i in range(0, row):
            for j in range(0, colum):
                if map[i][j] == ".":
                    self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                             i * self.imageWidth + self.offset_y,
                                             image=image_target)
                    count_target += 1
                elif map[i][j] == "#":
                    self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                             i * self.imageWidth + self.offset_y,
                                             image=image_wall)
                elif map[i][j] == "$":  # 当前为箱子时,保存状态
                    box = {'name': 'box', 'label': '$', 'walk': 0, 'x': j, 'y': i, 'tox': 0, 'toy': 0,
                           'id': self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                                          i * self.imageWidth + self.offset_y,
                                                          image=image_box)}
                    self.boxes.append(box)
                elif map[i][j] == "*":  # 当前为箱子在目标位时,保存状态
                    box = {'name': 'box', 'label': '*', 'walk': 0, 'x': j, 'y': i, 'tox': 0, 'toy': 0,
                           'id': self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                                          i * self.imageWidth + self.offset_y,
                                                          image=image_box_at_target)}
                    self.boxes.append(box)
                    count_target += 1
                    count_box_on_target +=1
                elif map[i][j] == "@":  # 标记为工人时,记录工人位置、图片id
                    self.worker['id'] = self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                                                 i * self.imageWidth + self.offset_y,
                                                                 image=image_worker)
                    self.worker['x'] = j
                    self.worker['y'] = i
                    self.worker['label'] = "@"
                elif map[i][j] == "+":  # 标记为工人在目标位置时,记录工人位置、图片id
                    count_target += 1
                    self.worker['id'] = self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                                                 i * self.imageWidth + self.offset_y,
                                                                 image=image_worker_at_target)
                    self.worker['x'] = j
                    self.worker['y'] = i
                    self.worker['label'] = "+"

        self.GLabel_562['text'] = f'{count_box_on_target}/{count_target}'
        self.Progressbar_698['maximum'] = count_target
        self.Progressbar_698['value'] = count_box_on_target

        self.finished = False
        if count_target == count_box_on_target and self.isnewlevel:
            self.isnewlevel = False
            mbox.askokcancel("提示", " 恭喜,你已完成本关! ")
                # self.GButton_963_command()

    def keydown(self, event):  # 键盘事件
        KeyCode = event.keysym
        k = None
        self.worker['tox'] = self.worker['x']
        self.worker['toy'] = self.worker['y']
        # 工人当前位置(x,y)
        if KeyCode == "Up":  # 分析按键消息
            self.worker['toy'] -= 1
            k='u'
        elif KeyCode == "Down":
            self.worker['toy'] += 1
            k='d'
        elif KeyCode == "Left":
            self.worker['tox'] -= 1
            k='l'
        elif KeyCode == "Right":
            self.worker['tox'] += 1
            k='r'
        else:
            return
        current_map = copy.deepcopy(self.current_map)  # 当前地图
        row = self.worker['toy']  # 需要检查的地图坐标
        colum = self.worker['tox']
        destination = current_map[row][colum]  # 坐标位置内容
        if destination == '$' or destination == '*':  # 为箱子,或箱子在目标位
            k=k.upper()
        print(k)
        if self.move2p(self.worker):
            self.keylist.append(k)
            self.reset(self.tvhistory)
            templist = copy.deepcopy(self.keylist)
            templist.reverse()
            for i in templist:
                self.tvhistory.insert('','end',values=i)
            # print(self.keylist)

    def reset(self,o):
        global counter_tv
        for item in o.get_children():
            o.delete(item)
        counter_tv = 0

    def move2p(self, mover):  # 移动及地图回写
        if self.check_road(mover) :  # 路况检查
            row = mover['y']
            colum = mover['x']
            current_map = copy.deepcopy(self.current_map)
            p = current_map[row][colum]  # 当前位置变化
            if p == '@':
                p = '-'  # 原位置置空
            elif p == '+':
                p = '.'  # 原位置置目标位
            elif p == '*':
                p = '.'  # 原位置置目标位
            elif p == '$':
                p = '-'  # 原位置置空
            self.current_map.clear()
            replace_map_char(current_map,row,colum,p)
            self.current_map += current_map
            row = mover['toy']
            colum = mover['tox']
            p = current_map[row][colum]  # 目标位置变化
            if p == '-':
                if mover['name'] == 'worker':
                    p = '@'  # 移动位置为工人
                else:
                    p = '$'  # 移动位置为箱子
            elif p == '.':
                if mover['name'] == 'worker':
                    p = '+'  # 移动位置为工人在目标位
                else:
                    p = '*'  # 移动位置为箱子在目标位
            self.current_map.clear()
            replace_map_char(current_map,row,colum,p)
            self.current_map += current_map
            self.draw_map(self.current_map)
            return True
        return False

    def check_road(self, mover):  # 目标位置检查
        current_map = copy.deepcopy(self.current_map)  # 当前地图
        row = mover['toy']  # 需要检查的地图坐标
        colum = mover['tox']
        destination = current_map[row][colum]  # 坐标位置内容
        if destination == '#':  # 为墙体时,禁止通过
            return False
        if destination == '-':  # 空地允许通过
            return True
        if destination == '.':  # 目标点允许通过
            return True
        if destination == '$' or destination == '*':  # 箱子时,替换移动物为箱子,继续前进
            if mover['name'] == 'box':  # 箱子移动时,遇到箱子,停止移动
                return False
            elif mover['name'] == 'worker':  # 查看箱子能否继续移动
                setp_x = mover['tox'] - mover['x']  # 获取移动量
                setp_y = mover['toy'] - mover['y']
                xbox = mover['tox']  # 获取当前位置信息
                ybox = mover['toy']
                box2x = xbox + setp_x  # 计算当前物体移动到的位置信息
                box2y = ybox + setp_y
                i = 0
                for item in self.boxes:
                    if item['x'] == xbox and item['y'] == ybox:
                        item['tox'] = box2x
                        item['toy'] = box2y
                        return self.move2p(item)  # 开始移动箱子
            return False
        return False


if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()
iawyxkdn8 发表于 2022-8-10 13:05
HUAJIEN 发表于 2022-8-10 14:07
 楼主| dltest 发表于 2022-8-10 14:27
本帖最后由 dltest 于 2022-8-10 14:30 编辑

step2 让人物动起来。

GIF 2022-8-10 14-19-01.gif

本次网盘上传文件为: 2.step2让人物动起来.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj

[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None


class Sokoban:
    def __init__(self,root):
        self.initUI(root)
        self.initData()
        self.drawMap()

    def initUI(self,root):
        root.title("推箱子 dltest@52pj")
        self.width=600
        self.height=400
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(root,bg='white',height=self.height,width=self.width)
        self.canvas.bind_all("<KeyPress>", self.keydown) #监听按键
        self.canvas.pack()


    def initData(self):
        self.offset_x = 0
        self.offset_y = 0
        self.imageWidth = 32
        self.worker = {'name':'worker',#类型
                       'lable':'@', #当前位置符号
                       'walk':1,
                       'x':0,'y':0, #当前位置
                       'tox':0,'toy':0, #目标位置
                       'id':0} #资源id

    def drawMap(self):
        global image_target ,image_wall ,image_box ,image_box_at_target ,image_worker ,image_worker_at_target
        image_target = PhotoImage(file="image_target.png")
        image_wall = PhotoImage(file="image_wall.png")
        image_box = PhotoImage(file="image_box.png")
        image_box_at_target = PhotoImage(file="image_box_at_target.png")
        image_worker = PhotoImage(file="image_worker.png")
        image_worker_at_target = PhotoImage(file="image_worker_at_target.png")
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''
        map =  '####__\
                #-.#__\
                #--###\
                #*@--#\
                #--$-#\
                #--###\
                ####__'
        row = 7  #行
        colum = 6 # 列
        self.imageWidth = 32 #图片宽
        self.offset_x = (self.width - colum * self.imageWidth)/2
        self.offset_y = (self.height - row * self.imageWidth)/2
        map = map.replace(' ','').replace('\r\n','')
        for i in range(0,row):
            for j in range(0,colum):
                if map[i*colum + j] == ".":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map[i*colum + j] == "#":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map[i*colum + j] == "$":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map[i*colum + j] == "*":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map[i*colum + j] == "@":
                    self.worker['id'] = self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker)
                    self.worker['x']=j
                    self.worker['y']=i
                    self.worker['lable'] = "@"
                elif map[i*colum + j] == "+":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker_at_target)

    def keydown(self,event):
        KeyCode = event.keysym
        self.worker['tox'] = self.worker['x']
        self.worker['toy'] = self.worker['y']
        # 工人当前位置(x,y)
        if KeyCode == "Up":  # 分析按键消息
            self.worker['toy'] -=1
        elif KeyCode == "Down":
            self.worker['toy'] +=1
        elif KeyCode == "Left":
            self.worker['tox'] -=1
        elif KeyCode == "Right":
            self.worker['tox'] +=1
        else:
            return
        setp_x = self.worker['tox']-self.worker['x']
        setp_y = self.worker['toy']-self.worker['y']
        self.canvas.move(self.worker['id'],setp_x*self.imageWidth,setp_y*self.imageWidth)



if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()



屏幕截图GIF软件件来源于本论坛
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1651913
fangben518 发表于 2022-8-10 14:50
这个游戏有点历史了,上初中的时候玩的
 楼主| dltest 发表于 2022-8-10 15:31
step3 画地为牢

上一节中编译的人物虽然可以动了,但是跑出地图之外
这次,我们加上移动目的地检查,为墙时,禁止移动,这样就可以把运动范围缩至地图内了。

GIF 2022-8-10 15-29-07.gif

本次网盘上传文件为: 3.step3画地为牢.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj


[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None


class Sokoban:
    def __init__(self,root):
        self.initUI(root)
        self.initData()
        self.drawMap()

    def initUI(self,root): #界面初始化
        root.title("推箱子 dltest@52pj")
        self.width=600
        self.height=400
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(root,bg='white',height=self.height,width=self.width)
        self.canvas.bind_all("<KeyPress>", self.keydown) #监听按键
        self.canvas.pack()


    def initData(self): #数据初始化
        self.offset_x = 0
        self.offset_y = 0
        self.imageWidth = 32
        self.worker = {'name':'worker',#类型
                       'lable':'@', #当前位置符号
                       'walk':1,
                       'x':0,'y':0, #当前位置
                       'tox':0,'toy':0, #目标位置
                       'id':0} #资源id
        self.map =  '####__\
                    #-.#__\
                    #--###\
                    #*@--#\
                    #--$-#\
                    #--###\
                    ####__'
        self.row = 7  #行
        self.colum = 6 # 列

    def drawMap(self): #绘制地图
        global image_target ,image_wall ,image_box ,image_box_at_target ,image_worker ,image_worker_at_target
        image_target = PhotoImage(file="image_target.png")
        image_wall = PhotoImage(file="image_wall.png")
        image_box = PhotoImage(file="image_box.png")
        image_box_at_target = PhotoImage(file="image_box_at_target.png")
        image_worker = PhotoImage(file="image_worker.png")
        image_worker_at_target = PhotoImage(file="image_worker_at_target.png")
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''

        row = self.row= 7  #行
        colum = self.colum   #行

        self.imageWidth = 32 #图片宽
        self.offset_x = (self.width - colum * self.imageWidth)/2
        self.offset_y = (self.height - row * self.imageWidth)/2
        map = self.map.replace(' ','').replace('\r\n','')
        for i in range(0,row):
            for j in range(0,colum):
                if map[i*colum + j] == ".":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map[i*colum + j] == "#":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map[i*colum + j] == "$":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map[i*colum + j] == "*":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map[i*colum + j] == "@": #标记为工人时,记录工人位置、图片id
                    self.worker['id'] = self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker)
                    self.worker['x']=j
                    self.worker['y']=i
                    self.worker['lable'] = "@"
                elif map[i*colum + j] == "+":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker_at_target)


    def keydown(self,event): #键盘事件
        KeyCode = event.keysym
        self.worker['tox'] = self.worker['x']
        self.worker['toy'] = self.worker['y']
        # 工人当前位置(x,y)
        if KeyCode == "Up":  # 分析按键消息
            self.worker['toy'] -=1
        elif KeyCode == "Down":
            self.worker['toy'] +=1
        elif KeyCode == "Left":
            self.worker['tox'] -=1
        elif KeyCode == "Right":
            self.worker['tox'] +=1
        else:
            return
        setp_x = self.worker['tox']-self.worker['x']
        setp_y = self.worker['toy']-self.worker['y']
        if self.checkRoad(self.worker): #路况检查
            self.canvas.move(self.worker['id'],setp_x*self.imageWidth,setp_y*self.imageWidth) #移动当前物体至目标位置
            self.worker['x'] = self.worker['tox'] #工人位置修改为目标位置
            self.worker['y'] = self.worker['toy']

    def checkRoad(self,mover): #目标位置检查
        map = self.map.replace(' ','').replace('\r\n','') #当前地图
        row = mover['toy'] #需要检查的地图坐标
        colum = mover['tox']
        destination = map[row*self.colum+colum] #坐标位置内容
        if destination =='#': #为墙体时,禁止通过,其它放行
            return  False
        return True



if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()

 楼主| dltest 发表于 2022-8-10 15:57
step4 道路通畅,可以通过

上一节中,虽然不能出地图了,但是还会穿过箱子,这次增加条件,前方为空地或目标点时,可以通过
本次网盘上传文件为:4.step4 道路通畅,可以通过.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj

GIF 2022-8-10 15-53-32.gif

[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None


class Sokoban:
    def __init__(self,root):
        self.initUI(root)
        self.initData()
        self.drawMap()

    def initUI(self,root): #界面初始化
        root.title("推箱子 dltest@52pj")
        self.width=600
        self.height=400
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(root,bg='white',height=self.height,width=self.width)
        self.canvas.bind_all("<KeyPress>", self.keydown) #监听按键
        self.canvas.pack()


    def initData(self): #数据初始化
        self.offset_x = 0
        self.offset_y = 0
        self.imageWidth = 32
        self.worker = {'name':'worker',#类型
                       'lable':'@', #当前位置符号
                       'walk':1,
                       'x':0,'y':0, #当前位置
                       'tox':0,'toy':0, #目标位置
                       'id':0} #资源id
        self.map =  '####__\
                    #-.#__\
                    #--###\
                    #*@--#\
                    #--$-#\
                    #--###\
                    ####__'
        self.row = 7  #行
        self.colum = 6 # 列

    def drawMap(self): #绘制地图
        global image_target ,image_wall ,image_box ,image_box_at_target ,image_worker ,image_worker_at_target
        image_target = PhotoImage(file="image_target.png")
        image_wall = PhotoImage(file="image_wall.png")
        image_box = PhotoImage(file="image_box.png")
        image_box_at_target = PhotoImage(file="image_box_at_target.png")
        image_worker = PhotoImage(file="image_worker.png")
        image_worker_at_target = PhotoImage(file="image_worker_at_target.png")
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''

        row = self.row= 7  #行
        colum = self.colum   #行

        self.imageWidth = 32 #图片宽
        self.offset_x = (self.width - colum * self.imageWidth)/2
        self.offset_y = (self.height - row * self.imageWidth)/2
        map = self.map.replace(' ','').replace('\r\n','')
        for i in range(0,row):
            for j in range(0,colum):
                if map[i*colum + j] == ".":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map[i*colum + j] == "#":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map[i*colum + j] == "$":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map[i*colum + j] == "*":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map[i*colum + j] == "@": #标记为工人时,记录工人位置、图片id
                    self.worker['id'] = self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker)
                    self.worker['x']=j
                    self.worker['y']=i
                    self.worker['lable'] = "@"
                elif map[i*colum + j] == "+":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker_at_target)


    def keydown(self,event): #键盘事件
        KeyCode = event.keysym
        self.worker['tox'] = self.worker['x']
        self.worker['toy'] = self.worker['y']
        # 工人当前位置(x,y)
        if KeyCode == "Up":  # 分析按键消息
            self.worker['toy'] -=1
        elif KeyCode == "Down":
            self.worker['toy'] +=1
        elif KeyCode == "Left":
            self.worker['tox'] -=1
        elif KeyCode == "Right":
            self.worker['tox'] +=1
        else:
            return
        setp_x = self.worker['tox']-self.worker['x']
        setp_y = self.worker['toy']-self.worker['y']
        if self.checkRoad(self.worker): #路况检查
            self.canvas.move(self.worker['id'],setp_x*self.imageWidth,setp_y*self.imageWidth) #移动当前物体至目标位置
            self.worker['x'] = self.worker['tox'] #工人位置修改为目标位置
            self.worker['y'] = self.worker['toy']

    def checkRoad(self,mover): #目标位置检查
        map = self.map.replace(' ','').replace('\r\n','') #当前地图
        row = mover['toy'] #需要检查的地图坐标
        colum = mover['tox']
        destination = map[row*self.colum+colum] #坐标位置内容
        if destination =='#': #为墙体时,禁止通过,其它放行
            return  False
        if destination =='-': #空地允许通过
            return True
        if destination =='.': #目标点允许通过
            return True
        return False



if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()

 楼主| dltest 发表于 2022-8-10 17:02
step5 回写并刷新地图
上一节,当人物往初始人物位置移动时,不能通过,原因是地图数据显示当前位置为工人,所以不能能过。
所以我们要及时刷新人物所在位置,回写地图数据,重刷新地图,就可以通过。
(当启用刷新后,canvas.mover操作可以省略由地图刷新)

GIF 2022-8-10 16-59-16.gif

本次网盘上传文件为: 5.step5回写并刷新地图.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj


[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None


class Sokoban:
    def __init__(self,root):
        self.initUI(root)
        self.initData()
        self.drawMap()

    def initUI(self,root): #界面初始化
        root.title("推箱子 dltest@52pj")
        self.width=600
        self.height=400
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(root,bg='white',height=self.height,width=self.width)
        self.canvas.bind_all("<KeyPress>", self.keydown) #监听按键
        self.canvas.pack()


    def initData(self): #数据初始化
        self.offset_x = 0
        self.offset_y = 0
        self.imageWidth = 32
        self.worker = {'name':'worker',#类型
                       'lable':'@', #当前位置符号
                       'walk':1,
                       'x':0,'y':0, #当前位置
                       'tox':0,'toy':0, #目标位置
                       'id':0} #资源id
        self.map =  '####__\
                    #-.#__\
                    #--###\
                    #*@--#\
                    #--$-#\
                    #--###\
                    ####__'
        self.row = 7  #行
        self.colum = 6 # 列

    def drawMap(self): #绘制地图
        global image_target ,image_wall ,image_box ,image_box_at_target ,image_worker ,image_worker_at_target
        image_target = PhotoImage(file="image_target.png")
        image_wall = PhotoImage(file="image_wall.png")
        image_box = PhotoImage(file="image_box.png")
        image_box_at_target = PhotoImage(file="image_box_at_target.png")
        image_worker = PhotoImage(file="image_worker.png")
        image_worker_at_target = PhotoImage(file="image_worker_at_target.png")
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''

        row = self.row= 7  #行
        colum = self.colum   #行

        self.imageWidth = 32 #图片宽
        self.offset_x = (self.width - colum * self.imageWidth)/2
        self.offset_y = (self.height - row * self.imageWidth)/2
        map = self.map.replace(' ','').replace('\r\n','')
        for i in range(0,row):
            for j in range(0,colum):
                if map[i*colum + j] == ".":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map[i*colum + j] == "#":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map[i*colum + j] == "$":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map[i*colum + j] == "*":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map[i*colum + j] == "@": #标记为工人时,记录工人位置、图片id
                    self.worker['id'] = self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker)
                    self.worker['x']=j
                    self.worker['y']=i
                    self.worker['lable'] = "@"
                elif map[i*colum + j] == "+":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker_at_target)


    def keydown(self,event): #键盘事件
        KeyCode = event.keysym
        self.worker['tox'] = self.worker['x']
        self.worker['toy'] = self.worker['y']
        # 工人当前位置(x,y)
        if KeyCode == "Up":  # 分析按键消息
            self.worker['toy'] -=1
        elif KeyCode == "Down":
            self.worker['toy'] +=1
        elif KeyCode == "Left":
            self.worker['tox'] -=1
        elif KeyCode == "Right":
            self.worker['tox'] +=1
        else:
            return
        setp_x = self.worker['tox']-self.worker['x']
        setp_y = self.worker['toy']-self.worker['y']
        if self.checkRoad(self.worker): #路况检查
            # self.canvas.move(self.worker['id'],setp_x*self.imageWidth,setp_y*self.imageWidth) #移动当前物体至目标位置  #启用地图刷新机制时,此句已不需要
            row = self.worker['y']
            colum = self.worker['x']
            map= list(self.map.replace(' ','').replace('\r\n',''))
            map[row*self.colum+colum] = '-' #原位置置空
            row = self.worker['toy']
            colum = self.worker['tox']
            map[row*self.colum+colum] = '@' #移动位置为工人
            self.map = ''.join(map)
            self.worker['x'] = self.worker['tox'] #工人位置修改为目标位置
            self.worker['y'] = self.worker['toy']
            self.drawMap()



    def checkRoad(self,mover): #目标位置检查
        map = self.map.replace(' ','').replace('\r\n','') #当前地图
        row = mover['toy'] #需要检查的地图坐标
        colum = mover['tox']
        destination = map[row*self.colum+colum] #坐标位置内容
        if destination =='#': #为墙体时,禁止通过,其它放行
            return  False
        if destination =='-': #空地允许通过
            return True
        if destination =='.': #目标点允许通过
            return True
        return False



if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()

 楼主| dltest 发表于 2022-8-10 17:58
step6状态机制
上节操作,当工人进入目标位时,会刷掉目标位提示,及当工人走出时,目标位消失。
所以,我们在更新地图时,需要查看进入时前后两个状态,根据情况进行恢复或更新。

GIF 2022-8-10 17-56-03.gif

本次网盘上传文件为: 6.step6状态机制.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj

[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None


class Sokoban:
    def __init__(self,root):
        self.initUI(root)
        self.initData()
        self.drawMap()

    def initUI(self,root): #界面初始化
        root.title("推箱子 dltest@52pj")
        self.width=600
        self.height=400
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(root,bg='white',height=self.height,width=self.width)
        self.canvas.bind_all("<KeyPress>", self.keydown) #监听按键
        self.canvas.pack()


    def initData(self): #数据初始化
        self.offset_x = 0
        self.offset_y = 0
        self.imageWidth = 32
        self.worker = {'name':'worker',#类型
                       'lable':'@', #当前位置符号
                       'walk':1,
                       'x':0,'y':0, #当前位置
                       'tox':0,'toy':0, #目标位置
                       'id':0} #资源id
        self.map =  '####__\
                    #-.#__\
                    #--###\
                    #*@--#\
                    #--$-#\
                    #--###\
                    ####__'
        self.row = 7  #行
        self.colum = 6 # 列

    def drawMap(self): #绘制地图
        global image_target ,image_wall ,image_box ,image_box_at_target ,image_worker ,image_worker_at_target
        image_target = PhotoImage(file="image_target.png")
        image_wall = PhotoImage(file="image_wall.png")
        image_box = PhotoImage(file="image_box.png")
        image_box_at_target = PhotoImage(file="image_box_at_target.png")
        image_worker = PhotoImage(file="image_worker.png")
        image_worker_at_target = PhotoImage(file="image_worker_at_target.png")
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''

        row = self.row= 7  #行
        colum = self.colum   #行

        self.imageWidth = 32 #图片宽
        self.offset_x = (self.width - colum * self.imageWidth)/2
        self.offset_y = (self.height - row * self.imageWidth)/2
        map = self.map.replace(' ','').replace('\r\n','')
        for i in range(0,row):
            for j in range(0,colum):
                if map[i*colum + j] == ".":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map[i*colum + j] == "#":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map[i*colum + j] == "$":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map[i*colum + j] == "*":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map[i*colum + j] == "@": #标记为工人时,记录工人位置、图片id
                    self.worker['id'] = self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker)
                    self.worker['x']=j
                    self.worker['y']=i
                    self.worker['lable'] = "@"
                elif map[i*colum + j] == "+":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker_at_target)


    def keydown(self,event): #键盘事件
        KeyCode = event.keysym
        self.worker['tox'] = self.worker['x']
        self.worker['toy'] = self.worker['y']
        # 工人当前位置(x,y)
        if KeyCode == "Up":  # 分析按键消息
            self.worker['toy'] -=1
        elif KeyCode == "Down":
            self.worker['toy'] +=1
        elif KeyCode == "Left":
            self.worker['tox'] -=1
        elif KeyCode == "Right":
            self.worker['tox'] +=1
        else:
            return
        setp_x = self.worker['tox']-self.worker['x']
        setp_y = self.worker['toy']-self.worker['y']
        if self.checkRoad(self.worker): #路况检查
            # self.canvas.move(self.worker['id'],setp_x*self.imageWidth,setp_y*self.imageWidth) #移动当前物体至目标位置  #启用地图刷新机制时,此句已不需要
            row = self.worker['y']
            colum = self.worker['x']
            map= list(self.map.replace(' ','').replace('\r\n',''))
            p = map[row*self.colum+colum]
            if p == '@':
                map[row*self.colum+colum] = '-' #原位置置空
            elif p == '+':
                map[row*self.colum+colum] = '.' #原位置置目标位
            row = self.worker['toy']
            colum = self.worker['tox']
            p = map[row*self.colum+colum]
            if p == '-':
                map[row*self.colum+colum] = '@' #移动位置为工人
            elif p =='.':
                map[row*self.colum+colum] = '+' #移动位置为工人在目标位
            self.map = ''.join(map)
            self.worker['x'] = self.worker['tox'] #工人位置修改为目标位置
            self.worker['y'] = self.worker['toy']
            self.drawMap()



    def checkRoad(self,mover): #目标位置检查
        map = self.map.replace(' ','').replace('\r\n','') #当前地图
        row = mover['toy'] #需要检查的地图坐标
        colum = mover['tox']
        destination = map[row*self.colum+colum] #坐标位置内容
        if destination =='#': #为墙体时,禁止通过,其它放行
            return  False
        if destination =='-': #空地允许通过
            return True
        if destination =='.': #目标点允许通过
            return True
        return False



if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()

 楼主| dltest 发表于 2022-8-10 22:38
step7,进入正题,推箱子
当检测目标位置为箱子时,以此箱子为工人,继续向前前进,但不同的是,如果箱子前为箱子,则不能通行,其它与工人一样。

GIF 2022-8-10 22-35-01.gif

本次网盘上传文件为: 7.step7,进入正题,推箱子.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj

[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName  :sokoban_52pj.py
# @ Author   :dltest@52pj
import tkinter as tk
from tkinter import *
import tkinter.messagebox as mbox

image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None


class Sokoban:
    def __init__(self,root):
        self.initUI(root)
        self.initData()
        self.drawMap()

    def initUI(self,root): #界面初始化
        root.title("推箱子 dltest@52pj")
        self.width=600
        self.height=400
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)
        # self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
        self.canvas = tk.Canvas(root,bg='white',height=self.height,width=self.width)
        self.canvas.bind_all("<KeyPress>", self.keydown) #监听按键
        self.canvas.pack()


    def initData(self): #数据初始化
        self.offset_x = 0
        self.offset_y = 0
        self.imageWidth = 32
        self.worker = {'name':'worker',#类型
                       'lable':'@', #当前位置符号
                       'walk':1,
                       'x':0,'y':0, #当前位置
                       'tox':0,'toy':0, #目标位置
                       'id':0} #资源id
        self.map =  '####__\
                    #-.#__\
                    #--###\
                    #*@--#\
                    #--$-#\
                    #--###\
                    ####__'
        '''
        地图说明
        # 墙
        - 空地
        $ 箱子
        @ 工人
        . 目标位
        * 箱子在目标位置
        + 工人在目标位置
        '''
        self.row = 7  #行
        self.colum = 6 # 列
        self.boxes =[]

    def drawMap(self): #绘制地图
        global image_target ,image_wall ,image_box ,image_box_at_target ,image_worker ,image_worker_at_target
        image_target = PhotoImage(file="image_target.png")
        image_wall = PhotoImage(file="image_wall.png")
        image_box = PhotoImage(file="image_box.png")
        image_box_at_target = PhotoImage(file="image_box_at_target.png")
        image_worker = PhotoImage(file="image_worker.png")
        image_worker_at_target = PhotoImage(file="image_worker_at_target.png")
        self.canvas.delete("all")

        row = self.row= 7  #行
        colum = self.colum   #行
        self.boxes.clear()

        self.imageWidth = 32 #图片宽
        self.offset_x = (self.width - colum * self.imageWidth)/2
        self.offset_y = (self.height - row * self.imageWidth)/2
        map = self.map.replace(' ','').replace('\r\n','')
        count_target = 0 #记录没有箱子的目标位
        for i in range(0,row):
            for j in range(0,colum):
                if map[i*colum + j] == ".":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                    count_target += 1
                elif map[i*colum + j] == "#":
                    self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map[i*colum + j] == "$":#当前为箱子时,保存状态
                    box ={'name':'box',
                           'lable':'$',
                           'walk':0,
                           'x':j,'y':i,'tox':0,'toy':0,
                           'id':0}
                    box['id']=self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                    self.boxes.append(box)
                elif map[i*colum + j] == "*":#当前为箱子在目标位时,保存状态
                    box ={'name':'box',
                           'lable':'$',
                           'walk':0,
                           'x':j,'y':i,'tox':0,'toy':0,
                           'id':0}
                    box['id']=self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                    self.boxes.append(box)
                elif map[i*colum + j] == "@": #标记为工人时,记录工人位置、图片id
                    self.worker['id'] = self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker)
                    self.worker['x']=j
                    self.worker['y']=i
                    self.worker['lable'] = "@"
                elif map[i*colum + j] == "+": #标记为工人在目标位置时,记录工人位置、图片id
                    self.worker['id'] = self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_worker_at_target)
                    self.worker['x']=j
                    self.worker['y']=i
                    self.worker['lable'] = "+"
        if count_target == 0:
            result=mbox.askokcancel ("提示"," 恭喜,你已完成本关! ")


    def keydown(self,event): #键盘事件
        KeyCode = event.keysym
        self.worker['tox'] = self.worker['x']
        self.worker['toy'] = self.worker['y']
        # 工人当前位置(x,y)
        if KeyCode == "Up":  # 分析按键消息
            self.worker['toy'] -=1
        elif KeyCode == "Down":
            self.worker['toy'] +=1
        elif KeyCode == "Left":
            self.worker['tox'] -=1
        elif KeyCode == "Right":
            self.worker['tox'] +=1
        else:
            return
        self.move2p(self.worker)

    def move2p(self,mover): #移动及地图回写
        if self.checkRoad(mover): #路况检查
            row = mover['y']
            colum = mover['x']
            map= list(self.map.replace(' ','').replace('\r\n',''))
            p = map[row*self.colum+colum] #当前位置变化
            if p == '@':
                map[row*self.colum+colum] = '-' #原位置置空
            elif p == '+':
                map[row*self.colum+colum] = '.' #原位置置目标位
            elif p == '*':
                map[row*self.colum+colum] = '.' #原位置置目标位
            elif p =='$':
                map[row*self.colum+colum] = '-' #原位置置空
            row = mover['toy']
            colum = mover['tox']
            p = map[row*self.colum+colum]  #目标位置变化
            if p == '-':
                if mover['name'] == 'worker':
                    map[row*self.colum+colum] = '@' #移动位置为工人
                else:
                    map[row*self.colum+colum] = '$' #移动位置为箱子
            elif p =='.':
                if mover['name'] == 'worker':
                    map[row*self.colum+colum] = '+' #移动位置为工人在目标位
                else:
                    map[row*self.colum+colum] = '*' #移动位置为箱子在目标位
            self.map = ''.join(map)
            self.drawMap()
            return True
        return False



    def checkRoad(self,mover): #目标位置检查
        map = self.map.replace(' ','').replace('\r\n','') #当前地图
        row = mover['toy'] #需要检查的地图坐标
        colum = mover['tox']
        destination = map[row*self.colum+colum] #坐标位置内容
        if destination =='#': #为墙体时,禁止通过,其它放行
            return  False
        if destination =='-': #空地允许通过
            return True
        if destination =='.': #目标点允许通过
            return True
        if destination =='$' or destination =='*': #箱子时,替换移动物为箱子,继续前进
            if mover['name'] =='box': #箱子移动时,遇到箱子,停止移动
                return False
            elif mover['name'] =='worker': #查看箱子能否继续移动
                setp_x = mover['tox']-mover['x'] #获取移动量
                setp_y = mover['toy']-mover['y']
                xbox = mover['tox'] #获取当前位置信息
                ybox = mover['toy']
                box2x =xbox+setp_x #计算当前物体移动到的位置信息
                box2y =ybox+setp_y
                i = 0
                for item in self.boxes:
                    if item['x'] == xbox and item['y'] == ybox:
                        item['tox'] = box2x
                        item['toy'] = box2y
                        return self.move2p(item) #开始移动箱子
            return False
        return False



if __name__ == "__main__":
    root = tk.Tk()
    app = Sokoban(root)
    root.mainloop()


您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 16:45

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表