Sokoban

Seo Sanghyeon unendliche at hanmail.net
Mon Aug 12 09:16:03 EDT 2002


# Sokoban in Python

import Tkinter

WALL   = 'black'
BOX    = 'blue'
EMPTY  = 'yellow'
FULL   = 'green'
PLAYER = 'red'

size = 20
margin = 5

def loadmap(mapdata):
    lines = filter(None, mapdata.split('\n'))
    xs, ys = len(lines[0]), len(lines)
    wall, depot, box, player = {}, {}, {}, {}
    mapstruct = wall, depot, box, player
    switch = {' ': (),
              '#': (wall,),
              '$': (box,),
              '.': (depot,),
              '*': (depot, box),
              '@': (player,),
              '+': (depot, player)}
    def setter(dic):
        dic[x, y] = 1
    for y, line in zip(range(ys), lines):
        for x, char in zip(range(xs), line):
            map(setter, switch[char])
    return xs, ys, mapstruct

def makecanvas(parent, xs, ys):
    width, height = xs*size+margin*2, ys*size+margin*2
    canvas = Tkinter.Canvas(
        parent, width=width, height=height)
    return canvas

def drawmap(canvas, mapstruct):
    def square(x, y, fill):
        return canvas.create_rectangle(
            (x+0)*size+margin, (y+0)*size+margin,
            (x+1)*size+margin, (y+1)*size+margin,
            width=0, fill=fill)
    def circle(x, y, fill):
        return canvas.create_oval(
            (x+0.2)*size+margin, (y+0.2)*size+margin,
            (x+0.8)*size+margin, (y+0.8)*size+margin,
            width=0, fill=fill)
    switch = {'s': square,
              'c': circle}
    def setter(dic, kind, fill):
        def set(pos):
            x, y = pos
            dic[x, y] = switch[kind](x, y, fill)
        return set
    idstruct = {}, {}, {}, {}
    kinds = 'sscc'
    colors = WALL, EMPTY, BOX, PLAYER
    for dic, kind, fill, data in zip(
        idstruct, kinds, colors, mapstruct):
        map(setter(dic, kind, fill), data)
    return idstruct

def filldepot(canvas, mapstruct, idstruct):
    depot = mapstruct[1]
    depotid = idstruct[1]
    box = mapstruct[2]
    for pos in depot:
        if pos in box:
            canvas.itemconfigure(depotid[pos], fill=FULL)

def makemover(canvas, mapstruct, idstruct):
    def delta(event):
        key = event.keysym
        switch = {'Up': (0, -1),
                  'Left': (-1, 0),
                  'Down': (0, 1),
                  'Right': (1, 0)}
        return switch[key]
    def move(dic, idic, pos, dd):
        x, y, dx, dy = pos + dd
        canvas.move(idic[x, y], dx*size, dy*size)
        idic[x+dx, y+dy] = idic[x, y]
        del idic[x, y]
        dic[x+dx, y+dy] = dic[x, y]
        del dic[x, y]
    def fill(idic, pos, fill):
        canvas.itemconfigure(idic[pos], fill=fill)
    def mover(event):
        wall, depot, box, player = mapstruct
        wallid, depotid, boxid, playerid = idstruct
        pos = player.keys()[0]
        dd = delta(event)
        x, y, dx, dy = pos + dd
        dpos, ddpos = (x+dx, y+dy), (x+dx+dx, y+dy+dy)
        if dpos not in wall:
            if dpos not in box:
                move(player, playerid, pos, dd)
            elif ddpos not in wall and ddpos not in box:
                move(player, playerid, pos, dd)
                move(box, boxid, dpos, dd)
                if dpos in depot:
                    fill(depotid, dpos, EMPTY)
                if ddpos in depot:
                    fill(depotid, ddpos, FULL)
    return mover

def makegame(parent, mapdata):
    xs, ys, mapstruct = loadmap(mapdata)
    canvas = makecanvas(parent, xs, ys)
    idstruct = drawmap(canvas, mapstruct)
    filldepot(canvas, mapstruct, idstruct)
    mover = makemover(canvas, mapstruct, idstruct)
    parent.bind('<Key>', mover)
    canvas.pack(padx=margin, pady=margin)

def main():
    filename = raw_input('> ')
    mapdata = file(filename).read()
    root = Tkinter.Tk()
    makegame(root, mapdata)
    root.mainloop()

if __name__ == '__main__':
    main()



More information about the Python-list mailing list