[Python] 纯文本查看 复制代码
# -*- coding:utf-8 -*-
# @FileName :sokoban_52pj.py
# @ Author :dltest@52pj
import tkinter as tk
from tkinter import *
from tkinter import filedialog, ttk
import tkinter.messagebox as mbox
import tkinter.font as tkFont
import copy
image_target = None
image_wall = None
image_box = None
image_box_at_target = None
image_worker = None
image_worker_at_target = None
counter_tv = 0
def load_map(path):
maps = []
if not path:
path = 'levels/1.入门关卡/BoxWorld.xsb'
file_object = open(path, 'r')
Title =None
Author = None
try:
lines = file_object.readlines()
star = False
end = False
currentmap = []
level = 0
for line in lines:
if not line.replace('\n', ''):
continue
if line.startswith(";Level"):
star = True
end = False
line = line.replace('\n', '')
level = line.split(' ')[1]
elif line.startswith("Title:"):
Title = line.replace('\n', '')
Title = Title.split(':')[1]
elif line.startswith("Author:"):
Author = line.replace('\n', '')
Author = Author.split(' ')[1]
star = False
end = True
if star and not end:
currentmap.append(line.replace('\n', ''))
if level and not star and end:
maps.append({'level': int(level),
'map': currentmap[1:-1],
'Title': Title,
'Author': Author})
currentmap.clear()
finally:
file_object.close()
return maps
def replace_map_char(current_map, row, colum, char):
string = list(current_map[row])
string[colum] = char
current_map[row] = ''.join(string)
return current_map
class Sokoban:
def __init__(self, root):
self.init_ui(root)
self.maps = load_map("")
self.init_data()
self.level = 1
self.GLineEdit_926.insert(0 ,'1')
self.steps =[]
self.keylist=[]
self.get_map_by_level(self.maps, self.level)
self.finished = False
self.isnewlevel = True
def init_ui(self, root): # 界面初始化
root.title("推箱子 dltest@52pj")
self.width = 839
self.height = 586
screenwidth = root.winfo_screenwidth()
screenheight = root.winfo_screenheight()
alignstr = '%dx%d+%d+%d' % (
self.width, self.height, (screenwidth - self.width) / 2, (screenheight - self.height) / 2)
root.geometry(alignstr)
root.resizable(width=False, height=False)
self.width_frame = 738
self.height_frame = 525
self.Frame_342=tk.Frame(root)
self.Frame_342.place(x=50,y=50,width=self.width_frame,height=self.height_frame)
self.Frame_342["bg"] = "#90f090"
# self.canvas = tk.Canvas(root,bg='SystemButtonFace',height=self.height,width=self.width)
self.canvas = tk.Canvas(self.Frame_342, bg='white', width=self.width_frame, height=self.height_frame)
self.canvas.bind_all("<KeyPress>", self.keydown) # 监听按键
self.canvas.pack()
self.GButton_193=tk.Button(root)
self.GButton_193["bg"] = "#efefef"
ft = tkFont.Font(family='Times',size=10)
self.GButton_193["font"] = ft
self.GButton_193["fg"] = "#000000"
self.GButton_193["justify"] = "center"
self.GButton_193["text"] = "选择关卡文件 "
self.GButton_193["relief"] = "groove"
self.GButton_193.place(x=10,y=10,width=79,height=30)
self.GButton_193["command"] = self.GButton_193_command
self.GLineEdit_926=tk.Entry(root,validate='key',validatecommand=(root.register(self.check_input), '%P'))
self.GLineEdit_926["borderwidth"] = "1px"
ft = tkFont.Font(family='Times',size=10)
self.GLineEdit_926["font"] = ft
self.GLineEdit_926["fg"] = "#333333"
self.GLineEdit_926["justify"] = "center"
self.GLineEdit_926["text"] = "当前"
self.GLineEdit_926.place(x=390,y=10,width=41,height=30)
self.GLabel_356=tk.Label(root)
ft = tkFont.Font(family='Times',size=14)
self.GLabel_356["font"] = ft
self.GLabel_356["fg"] = "#333333"
self.GLabel_356["justify"] = "left"
self.GLabel_356["text"] = "/100"
self.GLabel_356.place(x=440,y=10,width=34,height=30)
self.GButton_747=tk.Button(root)
self.GButton_747["bg"] = "#efefef"
ft = tkFont.Font(family='Times',size=10)
self.GButton_747["font"] = ft
self.GButton_747["fg"] = "#000000"
self.GButton_747["justify"] = "center"
self.GButton_747["text"] = "上一关"
self.GButton_747["relief"] = "groove"
self.GButton_747.place(x=330,y=10,width=49,height=30)
self.GButton_747["command"] = self.GButton_747_command
self.GButton_963=tk.Button(root)
self.GButton_963["bg"] = "#efefef"
ft = tkFont.Font(family='Times',size=10)
self.GButton_963["font"] = ft
self.GButton_963["fg"] = "#000000"
self.GButton_963["justify"] = "center"
self.GButton_963["text"] = "下一关"
self.GButton_963["relief"] = "groove"
self.GButton_963.place(x=480,y=10,width=49,height=30)
self.GButton_963["command"] = self.GButton_963_command
self.GLabel_451=tk.Label(root)
ft = tkFont.Font(family='Times',size=10)
self.GLabel_451["font"] = ft
self.GLabel_451["fg"] = "#333333"
self.GLabel_451["justify"] = "center"
self.GLabel_451["text"] = "已完成"
self.GLabel_451.place(x=560,y=10,width=55,height=30)
self.GLabel_562=tk.Label(root)
ft = tkFont.Font(family='Times',size=14)
self.GLabel_562["font"] = ft
self.GLabel_562["fg"] = "#5fb878"
self.GLabel_562["justify"] = "left"
self.GLabel_562["text"] = "0/4"
self.GLabel_562.place(x=610,y=10,width=41,height=31)
self.GButton_976=tk.Button(root)
self.GButton_976["bg"] = "#efefef"
ft = tkFont.Font(family='Times',size=10)
self.GButton_976["font"] = ft
self.GButton_976["fg"] = "#000000"
self.GButton_976["justify"] = "center"
self.GButton_976["text"] = "重玩本关"
self.GButton_976.place(x=770,y=10,width=61,height=30)
self.GButton_976["command"] = self.GButton_976_command
self.GButton_263=tk.Button(root)
self.GButton_263["bg"] = "#efefef"
ft = tkFont.Font(family='Times',size=10)
self.GButton_263["font"] = ft
self.GButton_263["fg"] = "#000000"
self.GButton_263["justify"] = "center"
self.GButton_263["text"] = "后退"
self.GButton_263.place(x=790,y=50,width=41,height=30)
self.GButton_263["command"] = self.GButton_263_command
self.GLabel_645=tk.Label(root)
self.GLabel_645["bg"] = "#00ced1"
self.GLabel_645.place(x=790,y=90,width=42,height=484)
title=('历史' , '操作1','操作2','op1_url','op1_ur2','zbinfo' )
self.tvhistory=ttk.Treeview(self.GLabel_645,columns=title,style='Treeview',show='headings', height=25 )
self.tvhistory.pack()
self.tvhistory.heading('历史',text='历史' )
self.tvhistory.column('历史',width=40,anchor=CENTER)
self.Progressbar_698=ttk.Progressbar(root,orient=tk.VERTICAL)
self.Progressbar_698.place(x=10,y=50,width=30,height=527)
self.GLabel_88=tk.Label(root)
ft = tkFont.Font(family='Times',size=10)
self.GLabel_88["font"] = ft
self.GLabel_88["fg"] = "#333333"
self.GLabel_88["justify"] = "center"
self.GLabel_88["text"] = "关卡名"
self.GLabel_88.place(x=110,y=10,width=70,height=25)
self.GLineEdit_198=tk.Entry(root)
self.GLineEdit_198["borderwidth"] = "1px"
ft = tkFont.Font(family='Times',size=10)
self.GLineEdit_198["font"] = ft
self.GLineEdit_198["fg"] = "#333333"
self.GLineEdit_198["justify"] = "center"
self.GLineEdit_198["text"] = "title"
self.GLineEdit_198.place(x=190,y=10,width=117,height=30)
def init_data(self): # 数据初始化
self.offset_x = 0
self.offset_y = 0
self.imageWidth = 32
self.worker = {'name': 'worker', # 类型
'label': '@', # 当前位置符号
'walk': 1,
'x': 0, 'y': 0, # 当前位置
'tox': 0, 'toy': 0, # 目标位置
'id': 0} # 资源id
self.current_map = []
self.map = '####__\
#-.#__\
#--###\
#*@--#\
#--$-#\
#--###\
####__'
'''
地图说明
# 墙
- 空地
$ 箱子
@ 工人
. 目标位
* 箱子在目标位置
+ 工人在目标位置
'''
self.row = 7 # 行
self.colum = 6 # 列
self.boxes = []
def check_input(self,char):
if (char.isdigit() or char == "") :
return True
else:
return False
def GButton_193_command(self): #文件选择
print("command self.GButton_193")
selected_file_path = filedialog.askopenfilename(title='请选择标准的关卡类型文件',initialdir='./levels/',
filetypes=(("标准关卡", "*.xsb"),("自编关卡", "*.txt"),("其它关卡", "*.sok"),
("All files", "*.xsb;*.txt;*.sok") )) # 使用askopenfilename函数选择单个文件
self.maps = load_map(selected_file_path)
self.GLineEdit_926.delete(0,'end')
self.GLineEdit_926.insert(0,'1')
self.get_map_by_level(self.maps,1)
def GButton_747_command(self):
self.isnewlevel = True
self.steps.clear()
self.keylist.clear()
self.reset(self.tvhistory)
level = 1
try:
level =int(self.GLineEdit_926.get())-1
except:
return
if level >= 1 and level <= 100:
self.GLineEdit_926.delete(0,'end')
self.GLineEdit_926.insert(0,f'{level}')
self.get_map_by_level(self.maps,level)
def GButton_963_command(self): #下一关
self.isnewlevel = True
self.steps.clear()
self.keylist.clear()
self.reset(self.tvhistory)
level = 1
try:
level =int(self.GLineEdit_926.get())+1
except:
return
if level >= 1 and level <= 100:
self.GLineEdit_926.delete(0,'end')
self.GLineEdit_926.insert(0,f'{level}')
self.get_map_by_level(self.maps,level)
def GButton_976_command(self): #重玩
self.isnewlevel = True
self.steps.clear()
self.keylist.clear()
self.reset(self.tvhistory)
level = 1
try:
level =int(self.GLineEdit_926.get())
except:
return
if level >= 1 and level <= 100:
self.GLineEdit_926.delete(0,'end')
self.GLineEdit_926.insert(0,f'{level}')
self.get_map_by_level(self.maps,level)
def GButton_263_command(self): #后退
if len(self.steps) == 1 or len(self.steps) == 0:
return
tempstepslist =copy.deepcopy(self.steps)
print(tempstepslist)
temp = None
key = self.keylist[-1]
tempkeys = copy.deepcopy(self.keylist[:-1])
# print(key,end='')
self.steps.clear()
self.keylist.clear()
if key.isupper():
temp = tempstepslist[-3]
self.steps += tempstepslist[:-2]
else:
temp = tempstepslist[-2]
self.steps += tempstepslist[:-1]
print(tempkeys)
self.keylist += tempkeys
print(temp)
print(self.steps)
self.draw_map(temp,False)
self.reset(self.tvhistory)
tempkeys.reverse()
for i in tempkeys:
self.tvhistory.insert('','end',values=i)
def get_map_by_level(self, maps, level=1):
self.current_map.clear()
for item in maps:
if item['level'] == level:
self.current_map += item['map']
self.GLineEdit_198.delete(0,'end')
self.GLineEdit_198.insert(0,item['Title'])
self.draw_map(item['map'])
break
def draw_map(self, map,flag = True): # 绘制地图
if flag:
temp_map = copy.deepcopy(map)
self.steps.append(temp_map)
# print(map)
else:
self.current_map.clear()
self.current_map += map
pass
global image_target, image_wall, image_box, image_box_at_target, image_worker, image_worker_at_target
image_target = PhotoImage(file="skins/base/image_target.png")
image_wall = PhotoImage(file="skins/base/image_wall.png")
image_box = PhotoImage(file="skins/base/image_box.png")
image_box_at_target = PhotoImage(file="skins/base/image_box_at_target.png")
image_worker = PhotoImage(file="skins/base/image_worker.png")
image_worker_at_target = PhotoImage(file="skins/base/image_worker_at_target.png")
self.canvas.delete("all")
row =len(map)
colum = len(map[0])
self.row = row
self.colum = colum
self.boxes.clear()
self.offset_x = (self.width_frame - colum * self.imageWidth) / 2
self.offset_y = (self.height_frame - row * self.imageWidth) / 2
count_target = 0 # 记录没有箱子的目标位
count_box_on_target = 0
for i in range(0, row):
for j in range(0, colum):
if map[i][j] == ".":
self.canvas.create_image(j * self.imageWidth + self.offset_x,
i * self.imageWidth + self.offset_y,
image=image_target)
count_target += 1
elif map[i][j] == "#":
self.canvas.create_image(j * self.imageWidth + self.offset_x,
i * self.imageWidth + self.offset_y,
image=image_wall)
elif map[i][j] == "$": # 当前为箱子时,保存状态
box = {'name': 'box', 'label': '$', 'walk': 0, 'x': j, 'y': i, 'tox': 0, 'toy': 0,
'id': self.canvas.create_image(j * self.imageWidth + self.offset_x,
i * self.imageWidth + self.offset_y,
image=image_box)}
self.boxes.append(box)
elif map[i][j] == "*": # 当前为箱子在目标位时,保存状态
box = {'name': 'box', 'label': '*', 'walk': 0, 'x': j, 'y': i, 'tox': 0, 'toy': 0,
'id': self.canvas.create_image(j * self.imageWidth + self.offset_x,
i * self.imageWidth + self.offset_y,
image=image_box_at_target)}
self.boxes.append(box)
count_target += 1
count_box_on_target +=1
elif map[i][j] == "@": # 标记为工人时,记录工人位置、图片id
self.worker['id'] = self.canvas.create_image(j * self.imageWidth + self.offset_x,
i * self.imageWidth + self.offset_y,
image=image_worker)
self.worker['x'] = j
self.worker['y'] = i
self.worker['label'] = "@"
elif map[i][j] == "+": # 标记为工人在目标位置时,记录工人位置、图片id
count_target += 1
self.worker['id'] = self.canvas.create_image(j * self.imageWidth + self.offset_x,
i * self.imageWidth + self.offset_y,
image=image_worker_at_target)
self.worker['x'] = j
self.worker['y'] = i
self.worker['label'] = "+"
self.GLabel_562['text'] = f'{count_box_on_target}/{count_target}'
self.Progressbar_698['maximum'] = count_target
self.Progressbar_698['value'] = count_box_on_target
self.finished = False
if count_target == count_box_on_target and self.isnewlevel:
self.isnewlevel = False
mbox.askokcancel("提示", " 恭喜,你已完成本关! ")
# self.GButton_963_command()
def keydown(self, event): # 键盘事件
KeyCode = event.keysym
k = None
self.worker['tox'] = self.worker['x']
self.worker['toy'] = self.worker['y']
# 工人当前位置(x,y)
if KeyCode == "Up": # 分析按键消息
self.worker['toy'] -= 1
k='u'
elif KeyCode == "Down":
self.worker['toy'] += 1
k='d'
elif KeyCode == "Left":
self.worker['tox'] -= 1
k='l'
elif KeyCode == "Right":
self.worker['tox'] += 1
k='r'
else:
return
current_map = copy.deepcopy(self.current_map) # 当前地图
row = self.worker['toy'] # 需要检查的地图坐标
colum = self.worker['tox']
destination = current_map[row][colum] # 坐标位置内容
if destination == '$' or destination == '*': # 为箱子,或箱子在目标位
k=k.upper()
print(k)
if self.move2p(self.worker):
self.keylist.append(k)
self.reset(self.tvhistory)
templist = copy.deepcopy(self.keylist)
templist.reverse()
for i in templist:
self.tvhistory.insert('','end',values=i)
# print(self.keylist)
def reset(self,o):
global counter_tv
for item in o.get_children():
o.delete(item)
counter_tv = 0
def move2p(self, mover): # 移动及地图回写
if self.check_road(mover) : # 路况检查
row = mover['y']
colum = mover['x']
current_map = copy.deepcopy(self.current_map)
p = current_map[row][colum] # 当前位置变化
if p == '@':
p = '-' # 原位置置空
elif p == '+':
p = '.' # 原位置置目标位
elif p == '*':
p = '.' # 原位置置目标位
elif p == '$':
p = '-' # 原位置置空
self.current_map.clear()
replace_map_char(current_map,row,colum,p)
self.current_map += current_map
row = mover['toy']
colum = mover['tox']
p = current_map[row][colum] # 目标位置变化
if p == '-':
if mover['name'] == 'worker':
p = '@' # 移动位置为工人
else:
p = '$' # 移动位置为箱子
elif p == '.':
if mover['name'] == 'worker':
p = '+' # 移动位置为工人在目标位
else:
p = '*' # 移动位置为箱子在目标位
self.current_map.clear()
replace_map_char(current_map,row,colum,p)
self.current_map += current_map
self.draw_map(self.current_map)
return True
return False
def check_road(self, mover): # 目标位置检查
current_map = copy.deepcopy(self.current_map) # 当前地图
row = mover['toy'] # 需要检查的地图坐标
colum = mover['tox']
destination = current_map[row][colum] # 坐标位置内容
if destination == '#': # 为墙体时,禁止通过
return False
if destination == '-': # 空地允许通过
return True
if destination == '.': # 目标点允许通过
return True
if destination == '$' or destination == '*': # 箱子时,替换移动物为箱子,继续前进
if mover['name'] == 'box': # 箱子移动时,遇到箱子,停止移动
return False
elif mover['name'] == 'worker': # 查看箱子能否继续移动
setp_x = mover['tox'] - mover['x'] # 获取移动量
setp_y = mover['toy'] - mover['y']
xbox = mover['tox'] # 获取当前位置信息
ybox = mover['toy']
box2x = xbox + setp_x # 计算当前物体移动到的位置信息
box2y = ybox + setp_y
i = 0
for item in self.boxes:
if item['x'] == xbox and item['y'] == ybox:
item['tox'] = box2x
item['toy'] = box2y
return self.move2p(item) # 开始移动箱子
return False
return False
if __name__ == "__main__":
root = tk.Tk()
app = Sokoban(root)
root.mainloop()