Sokoban
https://simplegametutorials.github.io/pygamezero/
sokoban.py
1import os
2os.environ['SDL_video_window_pos'] = '%d,%d' % (20, 100)
3import pgzrun
4import copy
5
6# {{{
7# @ Player
8# + PlayerPlayer on storage
9# $ Box
10# * BoxBox on storage
11# . Storage
12# # StorageWall
13
14
15#"""
16# ###
17# #.#
18# # ####
19####$ $.#
20##. $@###
21#####$#
22# #.#
23# ###
24#"""
25# }}}
26
27
28import copy
29
30
31
32
33
34player = '@'
35player_on_storage = '+'
36box = '$'
37box_on_storage = '*'
38storage = '.'
39wall = '#'
40empty = ' '
41current_level = 0
42
43LEVELS = [ # {{{
44 [
45 [' ', ' ', '#', '#', '#'],
46 [' ', ' ', '#', '.', '#'],
47 [' ', ' ', '#', ' ', '#', '#', '#', '#'],
48 ['#', '#', '#', '$', ' ', '$', '.', '#'],
49 ['#', '.', ' ', '$', '@', '#', '#', '#'],
50 ['#', '#', '#', '#', '$', '#'],
51 [' ', ' ', ' ', '#', '.', '#'],
52 [' ', ' ', ' ', '#', '#', '#'],
53 ],
54 [
55 ['#', '#', '#', '#', '#'],
56 ['#', ' ', ' ', ' ', '#'],
57 ['#', '@', '$', '$', '#', ' ', '#', '#', '#'],
58 ['#', ' ', '$', ' ', '#', ' ', '#', '.', '#'],
59 ['#', '#', '#', ' ', '#', '#', '#', '.', '#'],
60 [' ', '#', '#', ' ', ' ', ' ', ' ', '.', '#'],
61 [' ', '#', ' ', ' ', ' ', '#', ' ', ' ', '#'],
62 [' ', '#', ' ', ' ', ' ', '#', '#', '#', '#'],
63 [' ', '#', '#', '#', '#', '#'],
64 ],
65 [
66 [' ', '#', '#', '#', '#', '#', '#', '#'],
67 [' ', '#', ' ', ' ', ' ', ' ', ' ', '#', '#', '#'],
68 ['#', '#', '$', '#', '#', '#', ' ', ' ', ' ', '#'],
69 ['#', ' ', '@', ' ', '$', ' ', ' ', '$', ' ', '#'],
70 ['#', ' ', '.', '.', '#', ' ', '$', ' ', '#', '#'],
71 ['#', '#', '.', '.', '#', ' ', ' ', ' ', '#'],
72 [' ', '#', '#', '#', '#', '#', '#', '#', '#'],
73 ],
74] # }}}
75C_EMPTY = ((255 , 255 , 230))
76C_STORAGE = ((230 , 220 , 170))
77C_WALL = ((33 , 33 , 33 ))
78C_BOX = ((60 , 115 , 20 ))
79C_PERSON = ((190 , 30 ,65 ))
80C_BG = { # {{{
81 ' ': C_EMPTY,
82 '@': C_EMPTY,
83 '$': C_EMPTY,
84 '.': C_STORAGE,
85 '+': C_STORAGE,
86 '*': C_STORAGE,
87 '#': C_WALL,
88} # }}}
89C_FG = { # {{{
90 '$': C_BOX,
91 '*': C_BOX,
92 '@': C_PERSON,
93 '+': C_PERSON,
94} # }}}
95CELL_SIZE = 30
96GAP = 8
97
98
99
100def draw(): # {{{
101 screen.fill(C_EMPTY)
102
103 for y, row in enumerate(level):
104 for x, cell in enumerate(row):
105 screen.draw.filled_rect(
106 Rect ( (x*CELL_SIZE, y*CELL_SIZE), (CELL_SIZE, CELL_SIZE)),
107 color = C_BG[cell]
108 )
109 if cell in ('@', '+', '$', '*'):
110 if cell in ('$', '*'):
111 screen.draw.filled_rect(
112 Rect (
113 (x*CELL_SIZE + GAP, y*CELL_SIZE + GAP),
114 (CELL_SIZE - 2*GAP, CELL_SIZE - 2*GAP)),
115 color = C_FG[cell]
116 )
117 else:
118 screen.draw.filled_circle(
119 ((x+0.5)*CELL_SIZE, (y+0.5)*CELL_SIZE),
120 (CELL_SIZE - 2*GAP)/2,
121 color = C_FG[cell]
122 )
123
124# }}}
125def on_key_down(key): # {{{
126 global current_level
127 if key in (keys.W, keys.A, keys.S, keys.D):
128 for test_y, row in enumerate(level):
129 for test_x, cell in enumerate(row):
130 if cell in (player, player_on_storage):
131 player_x = test_x
132 player_y = test_y
133 dx = 0
134 dy = 0
135
136 if key == keys.A: dx = -1
137 elif key == keys.D: dx = 1
138 elif key == keys.W: dy = -1
139 elif key == keys.S: dy = 1
140
141
142
143 current = level[player_y][player_x]
144 adjacent = level[player_y + dy][player_x + dx]
145
146 beyond = ''
147 if (
148 0 <= player_y + dy + dy < len(level)
149 and
150 0 <= player_x + dx + dx < len(level[player_y + dy + dy])
151 ):
152 beyond = level[player_y + dy + dy][player_x + dx + dx]
153
154 next_adjacent = {
155 empty: player,
156 storage: player_on_storage,
157 }
158
159 next_current = {
160 player: empty,
161 player_on_storage: storage,
162 }
163
164 next_beyond = {
165 empty: box,
166 storage: box_on_storage,
167 }
168
169 next_adjacent_push = {
170 box: player,
171 box_on_storage: player_on_storage,
172 }
173
174 if adjacent in next_adjacent:
175 level[player_y][player_x] = next_current[current]
176 level[player_y + dy][player_x + dx] = next_adjacent[adjacent]
177 elif beyond in next_beyond and adjacent in next_adjacent_push:
178 level[player_y][player_x] = next_current[current]
179 level[player_y + dy][player_x + dx] = next_adjacent_push[adjacent]
180 level[player_y + dy + dy][player_x + dx + dx] = next_beyond[beyond]
181
182 complete = True
183 for y, row in enumerate(level):
184 for x, cell in enumerate(row):
185 if cell == box:
186 complete = False
187
188 if complete:
189 current_level += 1
190 load_level()
191
192
193 elif key == keys.R:
194 load_level()
195 elif key == keys.N:
196 current_level += 1
197 load_level()
198 elif key == keys.P:
199 current_level -= 1
200 load_level()
201
202
203# }}}
204def load_level(): # {{{
205 global current_level
206 global level
207 current_level = current_level % len(LEVELS)
208 level = copy.deepcopy(LEVELS[current_level])
209 # }}}
210
211
212
213
214
215load_level()
216pgzrun.go()