Sokoban

Tags: pgzero, game

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()