非常喜歡推箱子的游戲,想用代碼寫個解決方案,但是沒有足夠的時間自己想解決方案了。參考了某博客的代碼:https://blog.csdn.net/weixin_33316051/article/details/113670665
代碼看是看了,但是沒看懂,也沒跑通orz……
level_file_path = 'level_file.txt'
class gameshortest:
def __init__(self, line, col=10):
"""
給一個圖,長度為100的字符串表示。
0空地 1牆 2箱子起始位置 3箱子終點位置 4人的起始位置
:param line: 地圖,用字符串表示,每一行表示每一關的地圖。
:param col: 地圖的長寬,由於設定為10*10,默認為10
"""
self.line = line
# 0-路 1-牆 2-箱子開始 3-箱子結束 4-人
# sta和en 表示開始的狀態,結束的狀態
# sta只有2,4,0, 2表示箱子開始位置,4表示人的位置,0表示其他。
# en只有1,3,0, 1表示牆,3表示箱子結束位置,0表示其他。
# 現在只需要把sta狀態中的2位置移動到en的3的位置即滿足條件
self.sta = ''
self.en = ''
self.col = col
# px, py表示人的位置
self.px, self.py = -1, -1
# paths記錄最短路徑(可能有多條)
self.paths = []
# len記錄最短路徑長度
self.len = -1
self.pre()
self.bfs()
print(self.paths)
def pre(self):
"""
1.獲得sta開始狀態和en結束狀態
2.獲得人的起始位置px,py
第一關的地圖可視化為
1111111111
1111111111
1110001111
1110221111
1114201111
1111100111
1111300111
1113300111
1111111111
1111111111
"""
# mp = []
# for pos in range(0, 100, 10):
# mp.append(self.line[pos : pos + 10])
# print(self.line)
# for x in mp:
# print(x)
for pos, enum in enumerate(self.line):
cx, cy = pos // 10, pos % 10 # 當前位置坐標
if enum == '4':
self.px, self.py = cx, cy # 人的起始位置
# 0-路 1-牆 2-箱子開始 3-箱子結束 4-人
# 開始狀態只有 2,4,0,結束狀態只有 1,3,0
# 現在只需要把sta開始的狀態中的2位置移動到en的3的位置即滿足條件
# stadic:0-0 路是路 1-0 ?? 2-2 箱子開始是箱子 3-0 箱子結束位置是路 4-4 人初始位置是人
# endic:0-0 路是路 1-1 牆是牆 2-0 箱子開始位置變成路 3-3 箱子結束是箱子 4-0 人初始位置變成路
stadic = {'0': '0', '1': '0', '2': '2', '3': '0', '4': '4'}
endic = {'0': '0', '1': '1', '2': '0', '3': '3', '4': '0'}
# 字符串拼接,得到起止狀態
for x in self.line:
self.sta += stadic[x]
self.en += endic[x]
print(self.sta)
print(self.en)
def is_ok(self, sta):
"""
sta狀態中的2位置移動到en的3的位置。
:param sta:
:return:
"""
for s, e in zip(sta, self.en):
if e == '3' and s != '2':
return False
return True # 當前狀態 sta 就是滿足要求的最終狀態
def bfs(self):
"""
bfs獲得最短路徑保存到paths中
:return:
"""
# 4個方向,小寫代表只是人移動,大寫表示人推着箱子一起移動
dirs = [[-1,0,'u','u'],[1,0,'d','d'],[0,1,'r','r'],[0,-1,'l','l']]
# 把開始的狀態進入隊列(list模擬),狀態包括字符串表示的當前狀態、當前的路徑、當前人的位置
states = [[self.sta, '', self.px, self.py]]
# 訪問數組(dict模擬),訪問過的狀態(字符串)不再訪問
visi = {}
visi[self.sta] = 1
s_len = 1000
while len(states) > 0:
print(states)
sta, path, px, py = states[0] # 初始狀態:狀態、路徑、人的位置
# 4狀態的位置
ppos = px * self.col + py # 人的真實位置(0-100)
states = states[1:] # 把前面的狀態剔掉
if len(path) > s_len: # 如果路徑長度大於 1000,就……不玩了??
break
print(sta)
# 保存最短路徑到paths中
if self.is_ok(sta):
if self.len == -1 or len(path) == self.len: # 應該是 <= 吧?
self.paths.append(path)
self.len = len(path)
continue
for dir in dirs:
# 向四個方向移動一步后人的坐標
cx, cy = px + dir[0], py + dir[1]
pos = cx * self.col + cy
# 向四個方向移動兩步后人的坐標
nx, ny = px + 2*dir[0], py + 2*dir[1]
npos = nx*self.col + ny
# 0-路 1-牆 2-箱子 3-箱子結束 4-人
if not (nx>=0 and nx==0 and ny):
continue
# 括號中條件為真(nx=0,ny≠0),繼續執行下面的語句
# 括號中條件為假,跳過下面的語句
# python中字符串不可更改,於是把字符串變成list更改狀態后再轉換為字符串
if sta[pos] == '2' and sta[npos] == '0' and self.en[npos] != '1':
# 人和箱子一起推動,sta中連着的狀態為4 2 0,en中第三個不能為1。推完之后sta變為0 4 2
digits = [int(x) for x in sta]
digits[ppos], digits[pos], digits[npos] = 0,4,2
new_sta = ''.join(str(x) for x in digits)
if new_sta not in visi:
visi[new_sta] = 1
states.append([new_sta, path+dir[3], cx, cy])
elif sta[pos] == '0' and self.en[pos] !='1':
# 人動箱子不動,sta中連着的狀態為4 0,en中第二個不能為1。
digits = [int(x) for x in sta]
digits[ppos], digits[pos] = 0, 4
new_sta = ''.join(str(x) for x in digits)
if new_sta not in visi:
visi[new_sta] = 1
states.append([new_sta, path + dir[2], cx, cy])
if __name__ == '__main__':
f = open(level_file_path, encoding='utf-8')
cnt = 0
while(1):
line = f.readline()
line = line.strip('\n')
if len(line)==0:
break
gs = gameshortest(line)
level_file.txt 文件中的內容是這樣的:
1111111111111111111111100011111110221111111420111111111001111111300111111330011111111111111111111111
1111111111104000000110000200111001101011100100101110010010111001011001110030000111111111111111111111
1111111111111111111111111111111110311111140020001110230020111111311111111111111111111111111111111111
1111111111111111111111100011111110221111111013311111102301111110040111111111111111111111111111111111
1111111111111111111111111111111100000111112111001113030020111400100011111111111111111111111111111111
1111111111111111111111111111111110011111100000011111001220111140300311111111111111111111111111111111
1111111111110040001110000000111001110011110011001111020130111100002011111311111111111111111111111111
1111111111111111111111111111111100111111100320001110131210111000000411111111111111111111111111111111
1111111111111111111111111111111100000111100111011110002020111000133411111111111111111111111111111111
1111111111111111111111100111111110004111111101011111312100111132001011113000201111111111111111111111
1111111111111111111111000001111131103111110402011111001201111100100111111111111111111111111111111111
1111111111111111111111100001111113112011110234001111001000111100001111111111111111111111111111111111
1111111111111111111111111001111110204111111020011111300101111130000111111111111111111111111111111111
1111111111111111111111143001111100000111110010211111001203111111101011111110001111111111111111111111
1111111111111111111111111111111100001111110202111111033420111111130011111111111111111111111111111111
1111111111111111111111110001111100230111114032301111110120111111000111111111111111111111111111111111
1111111111111111111111110031111102010111110020011111031401111100001111111111111111111111111111111111
1111111111111111111111111104111113110011110330201111021200111100001111111111111111111111111111111111
1111111111111111111111000111111100330111110011211111100100111112000411111001111111111111111111111111
1111111111111111111111100001111103032111110024001111111010111111100011111111111111111111111111111111
note
- 看代碼遇到看不懂的行,不要死磕,試着從運行結果推斷其作用。
- 希望有一天能完完全全實現一個自己的解決方案。
