コード全体を表示するにはここをクリック
import pygame, sys, os
from pygame.locals import *
from collections import deque
def to_box(level, index):
if level[index] == "-" or level[index] == "@":
level[index] = "$"
else:
level[index] = "*"
def to_man(level, i):
if level[i] == "-" or level[i] == "$":
level[i] = "@"
else:
level[i] = "+"
def to_floor(level, i):
if level[i] == "@" or level[i] == "$":
level[i] = "-"
else:
level[i] = "."
def to_offset(d, width):
d4 = [-1, -width, 1, width]
m4 = ["l", "u", "r", "d"]
return d4[m4.index(d.lower())]
def b_manto(level, width, b, m, t):
maze = list(level)
maze[b] = "#"
if m == t:
return 1
queue = deque([])
queue.append(m)
d4 = [-1, -width, 1, width]
m4 = ["l", "u", "r", "d"]
while len(queue) > 0:
pos = queue.popleft()
for i in range(4):
newpos = pos + d4[i]
if maze[newpos] in ["-", "."]:
if newpos == t:
return 1
maze[newpos] = i
queue.append(newpos)
return 0
def b_manto_2(level, width, b, m, t):
maze = list(level)
maze[b] = "#"
maze[m] = "@"
if m == t:
return []
queue = deque([])
queue.append(m)
d4 = [-1, -width, 1, width]
m4 = ["l", "u", "r", "d"]
while len(queue) > 0:
pos = queue.popleft()
for i in range(4):
newpos = pos + d4[i]
if maze[newpos] in ["-", "."]:
maze[newpos] = i
queue.append(newpos)
if newpos == t:
path = []
while maze[t]!= "@":
path.append(m4[maze[t]])
t = t - d4[maze[t]]
return path
return []
class Sokoban:
def __init__(self):
self.level = list(
"----#####--------------#---#--------------#$--#------------###--$##-----------#--$-$-#---------###-#-##-#---#######---#-##-#####--..##-$--$----------..######-###-#@##--..#----#-----#########----#######--------"
)
self.w = 19
self.h = 11
self.man = 163
self.hint = list(self.level)
self.solution = []
self.push = 0
self.todo = []
self.auto = 0
self.sbox = 0
self.queue = []
def draw(self, screen, skin):
w = skin.get_width() / 4
offset = (w - 4) / 2
for i in range(0, self.w):
for j in range(0, self.h):
if self.level[j * self.w + i] == "#":
screen.blit(skin, (i * w, j * w), (0, 2 * w, w, w))
elif self.level[j * self.w + i] == "-":
screen.blit(skin, (i * w, j * w), (0, 0, w, w))
elif self.level[j * self.w + i] == "@":
screen.blit(skin, (i * w, j * w), (w, 0, w, w))
elif self.level[j * self.w + i] == "$":
screen.blit(skin, (i * w, j * w), (2 * w, 0, w, w))
elif self.level[j * self.w + i] == ".":
screen.blit(skin, (i * w, j * w), (0, w, w, w))
elif self.level[j * self.w + i] == "+":
screen.blit(skin, (i * w, j * w), (w, w, w, w))
elif self.level[j * self.w + i] == "*":
screen.blit(skin, (i * w, j * w), (2 * w, w, w, w))
if self.sbox!= 0 and self.hint[j * self.w + i] == "1":
screen.blit(
skin, (i * w + offset, j * w + offset), (3 * w, 3 * w, 4, 4)
)
def move(self, d):
self._move(d)
self.todo = []
def _move(self, d):
self.sbox = 0
h = to_offset(d, self.w)
h2 = 2 * h
if self.level[self.man + h] == "-" or self.level[self.man + h] == ".":
## move
to_man(self.level, self.man + h)
to_floor(self.level, self.man)
self.man += h
self.solution += d
elif self.level[self.man + h] == "*" or self.level[self.man + h] == "$":
if self.level[self.man + h2] == "-" or self.level[self.man + h2] == ".":
## push
to_box(self.level, self.man + h2)
to_man(self.level, self.man + h)
to_floor(self.level, self.man)
self.man += h
self.solution += d.upper()
self.push += 1
def undo(self):
if self.solution.__len__() > 0:
self.todo.append(self.solution[-1])
self.solution.pop()
h = to_offset(self.todo[-1], self.w) * -1
if self.todo[-1].islower():
## undo a move
to_man(self.level, self.man + h)
to_floor(self.level, self.man)
self.man += h
else:
## undo a push
to_floor(self.level, self.man - h)
to_box(self.level, self.man)
to_man(self.level, self.man + h)
self.man += h
self.push -= 1
def redo(self):
if self.todo.__len__() > 0:
self._move(self.todo[-1].lower())
self.todo.pop()
def manto(self, x, y):
maze = list(self.level)
maze[self.man] = "@"
queue = deque([])
queue.append(self.man)
d4 = [-1, -self.w, 1, self.w]
m4 = ["l", "u", "r", "d"]
while len(queue) > 0:
pos = queue.popleft()
for i in range(4):
newpos = pos + d4[i]
if maze[newpos] in ["-", "."]:
maze[newpos] = i
queue.append(newpos)
t = y * self.w + x
if maze[t] in range(4):
self.todo = []
while maze[t]!= "@":
self.todo.append(m4[maze[t]])
t = t - d4[maze[t]]
self.auto = 1
def automove(self):
if self.auto == 1 and self.todo.__len__() > 0:
self._move(self.todo[-1].lower())
self.todo.pop()
else:
self.auto = 0
def boxhint(self, x, y):
d4 = [-1, -self.w, 1, self.w]
m4 = ["l", "u", "r", "d"]
b = y * self.w + x
maze = list(self.level)
to_floor(maze, b)
to_floor(maze, self.man)
mark = maze * 4
size = self.w * self.h
self.queue = []
head = 0
for i in range(4):
if b_manto(maze, self.w, b, self.man, b + d4[i]):
if len(self.queue) == 0:
self.queue.append((b, i, -1))
mark[i * size + b] = "1"
while head < len(self.queue):
pos = self.queue[head]
head += 1
for i in range(4):
if mark[pos[0] + i * size] == "1" and maze[pos[0] - d4[i]] in [
"-",
".",
]:
if mark[pos[0] - d4[i] + i * size]!= "1":
self.queue.append((pos[0] - d4[i], i, head - 1))
for j in range(4):
if b_manto(
maze,
self.w,
pos[0] - d4[i],
pos[0],
pos[0] - d4[i] + d4[j],
):
mark[j * size + pos[0] - d4[i]] = "1"
for i in range(size):
self.hint[i] = "0"
for j in range(4):
if mark[j * size + i] == "1":
self.hint[i] = "1"
def boxto(self, x, y):
d4 = [-1, -self.w, 1, self.w]
m4 = ["l", "u", "r", "d"]
om4 = ["r", "d", "l", "u"]
b = y * self.w + x
maze = list(self.level)
to_floor(maze, self.sbox)
to_floor(
maze, self.man
) ## make a copy of working maze by removing the selected box and the man
for i in range(len(self.queue)):
if self.queue[i][0] == b:
self.todo = []
j = i
while self.queue[j][2]!= -1:
self.todo.append(om4[self.queue[j][1]].upper())
k = self.queue[j][2]
if self.queue[k][2]!= -1:
self.todo += b_manto_2(
maze,
self.w,
self.queue[k][0],
self.queue[k][0] + d4[self.queue[k][1]],
self.queue[k][0] + d4[self.queue[j][1]],
)
else:
self.todo += b_manto_2(
maze,
self.w,
self.queue[k][0],
self.man,
self.queue[k][0] + d4[self.queue[j][1]],
)
j = k
self.auto = 1
return
print("not found!")
def mouse(self, x, y):
if x >= self.w or y >= self.h:
return
m = y * self.w + x
if self.level[m] in ["-", "."]:
if self.sbox == 0:
self.manto(x, y)
else:
self.boxto(x, y)
elif self.level[m] in ["$", "*"]:
if self.sbox == m:
self.sbox = 0
else:
self.sbox = m
self.boxhint(x, y)
elif self.level[m] in ["-", ".", "@", "+"]:
self.boxto(x, y)
## start pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
## load skin
skinfilename = os.path.join("borgar.png")
try:
skin = pygame.image.load(skinfilename)
except pygame.error as msg:
print("cannot load skin")
raise SystemExit(msg)
skin = skin.convert()
## screen.fill((255,255,255))
screen.fill(skin.get_at((0, 0)))
pygame.display.set_caption("sokoban.py")
## create Sokoban object
skb = Sokoban()
skb.draw(screen, skin)
clock = pygame.time.Clock()
pygame.key.set_repeat(200, 50)
## main game loop
while True:
clock.tick(60)
if skb.auto == 0:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
skb.move("l")
skb.draw(screen, skin)
elif event.key == K_UP:
skb.move("u")
skb.draw(screen, skin)
elif event.key == K_RIGHT:
skb.move("r")
skb.draw(screen, skin)
elif event.key == K_DOWN:
skb.move("d")
skb.draw(screen, skin)
elif event.key == K_BACKSPACE:
skb.undo()
skb.draw(screen, skin)
elif event.key == K_SPACE:
skb.redo()
skb.draw(screen, skin)
elif event.type == MOUSEBUTTONUP and event.button == 1:
mousex, mousey = event.pos
mousex /= skin.get_width() / 4
mousey /= skin.get_width() / 4
skb.mouse(mousex, mousey)
skb.draw(screen, skin)
else:
skb.automove()
skb.draw(screen, skin)
pygame.display.update()
pygame.display.set_caption(
skb.solution.__len__().__str__() + "/" + skb.push.__str__() + " - sokoban.py"
)