dltest 发表于 2022-8-10 11:00

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

本帖最后由 dltest 于 2022-8-12 09:02 编辑

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

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


STEP 1 构建地图
网上找的地图

####__
#-.#__
#--###
#*@--#
#--$-#
#--###
####__
Title:
Author:David W. Skinner

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

让我们用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 == ".":
                  self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_target)
                elif map == "#":
                  self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_wall)
                elif map == "$":
                  self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_box)
                elif map == "*":
                  self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_box_at_target)
                elif map == "@":
                  self.canvas.create_image(j*w+offset_x,i*w+offset_y,image = image_worker)
                elif map == "+":
                  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()

运行一下,我们的地图构建完成。


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

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

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

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




dltest 发表于 2022-8-12 21:45

setp10.加载更多关卡文件

可以自由选择关卡文件,丰富你的摸鱼时间。

本次网盘上传文件为: 10.step10加载更多关卡文件.rar
外链:https://wwz.lanzoub.com/b03d9ge6h 密码:52pj

# -*- 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(' ')
            elif line.startswith("Title:"):
                Title = line.replace('\n', '')
                Title = Title.split(':')
            elif line.startswith("Author:"):
                Author = line.replace('\n', '')
                Author = Author.split(' ')
                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,
                               '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)
    string = char
    current_map = ''.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)
      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 == ".":
                  self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                             i * self.imageWidth + self.offset_y,
                                             image=image_target)
                  count_target += 1
                elif map == "#":
                  self.canvas.create_image(j * self.imageWidth + self.offset_x,
                                             i * self.imageWidth + self.offset_y,
                                             image=image_wall)
                elif map == "$":# 当前为箱子时,保存状态
                  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 == "*":# 当前为箱子在目标位时,保存状态
                  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 == "@":# 标记为工人时,记录工人位置、图片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 == "+":# 标记为工人在目标位置时,记录工人位置、图片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# 坐标位置内容
      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# 当前位置变化
            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# 目标位置变化
            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# 坐标位置内容
      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 让人物动起来。



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

# -*- 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 == ".":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map == "#":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map == "$":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map == "*":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map == "@":
                  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 == "+":
                  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 画地为牢

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



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


# -*- 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 == ".":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map == "#":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map == "$":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map == "*":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map == "@": #标记为工人时,记录工人位置、图片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 == "+":
                  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 #坐标位置内容
      if destination =='#': #为墙体时,禁止通过,其它放行
            returnFalse
      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



# -*- 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 == ".":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map == "#":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map == "$":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map == "*":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map == "@": #标记为工人时,记录工人位置、图片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 == "+":
                  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 #坐标位置内容
      if destination =='#': #为墙体时,禁止通过,其它放行
            returnFalse
      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操作可以省略由地图刷新)



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


# -*- 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 == ".":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map == "#":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map == "$":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map == "*":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map == "@": #标记为工人时,记录工人位置、图片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 == "+":
                  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.worker['toy']
            colum = self.worker['tox']
            map = '@' #移动位置为工人
            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 #坐标位置内容
      if destination =='#': #为墙体时,禁止通过,其它放行
            returnFalse
      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状态机制
上节操作,当工人进入目标位时,会刷掉目标位提示,及当工人走出时,目标位消失。
所以,我们在更新地图时,需要查看进入时前后两个状态,根据情况进行恢复或更新。



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

# -*- 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 == ".":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                elif map == "#":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map == "$":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box)
                elif map == "*":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_box_at_target)
                elif map == "@": #标记为工人时,记录工人位置、图片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 == "+":
                  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
            if p == '@':
                map = '-' #原位置置空
            elif p == '+':
                map = '.' #原位置置目标位
            row = self.worker['toy']
            colum = self.worker['tox']
            p = map
            if p == '-':
                map = '@' #移动位置为工人
            elif p =='.':
                map = '+' #移动位置为工人在目标位
            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 #坐标位置内容
      if destination =='#': #为墙体时,禁止通过,其它放行
            returnFalse
      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,进入正题,推箱子
当检测目标位置为箱子时,以此箱子为工人,继续向前前进,但不同的是,如果箱子前为箱子,则不能通行,其它与工人一样。



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

# -*- 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 == ".":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_target)
                  count_target += 1
                elif map == "#":
                  self.canvas.create_image(j*self.imageWidth+self.offset_x,i*self.imageWidth+self.offset_y,image = image_wall)
                elif map == "$":#当前为箱子时,保存状态
                  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 == "*":#当前为箱子在目标位时,保存状态
                  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 == "@": #标记为工人时,记录工人位置、图片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 == "+": #标记为工人在目标位置时,记录工人位置、图片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 #当前位置变化
            if p == '@':
                map = '-' #原位置置空
            elif p == '+':
                map = '.' #原位置置目标位
            elif p == '*':
                map = '.' #原位置置目标位
            elif p =='$':
                map = '-' #原位置置空
            row = mover['toy']
            colum = mover['tox']
            p = map#目标位置变化
            if p == '-':
                if mover['name'] == 'worker':
                  map = '@' #移动位置为工人
                else:
                  map = '$' #移动位置为箱子
            elif p =='.':
                if mover['name'] == 'worker':
                  map = '+' #移动位置为工人在目标位
                else:
                  map = '*' #移动位置为箱子在目标位
            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 #坐标位置内容
      if destination =='#': #为墙体时,禁止通过,其它放行
            returnFalse
      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()


页: [1] 2 3
查看完整版本: [推箱子-Sokoban】文曲星经典小游戏搬到电脑上了,我卡在13关看看你能过几关