[推箱子-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
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()
沙发 ,,,,,,, 前来学习学习 本帖最后由 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 这个游戏有点历史了,上初中的时候玩的 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()
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()
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()
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()
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()