# Sokoban # https://workshop.numworks.com/python/arthurjacquin/sokoban # arthur@jacquin.xyz from kandinsky import * from ion import keydown from time import sleep BACK, TEXT = (120,)*3, (42,)*3 COL = { '#': TEXT, # Wall '@': (0, 153, 255), # Player '+': (0, 122, 204), # Player + Goal '$': (172, 115, 57), # Box '*': (134, 89, 45), # Box + Goal '.': (70, 185, 70), # Goal '-': BACK, # Floor } def decompress(rne): # Décompresse le niveau grid = [''] while rne: if rne[0] == '|': grid.append('') elif 47 < ord(rne[0]) < 58: number = '' while 47 < ord(rne[0]) < 58: number += rne[0]; rne = rne[1:] grid[-1] += int(number)*rne[0] else: grid[-1] += rne[0] rne = rne[1:] return grid def sett(x, y, test, valid, default): # Actualise la valeur d'une case value = valid if grid[y][x] in test else default grid[y] = grid[y][:x] + value + grid[y][x+1:] fill_rect(X+d*x, Y+d*y, d, d, COL[value]) def win(): # Vérifie le niveau est terminé for y in grid: for char in y: if char=='$': return False return True def wait(buttons = range(53)): # Attends qu'une touche soit pressée while True: for i in buttons: if keydown(i): sleep(0.1) return i def menu(): # Règles, commandes, visuels def iter(*t): for i in list(range(len(t)))[::3]: draw_string(t[i], t[i+1], t[i+2], TEXT, BACK) fill_rect(0, 0, 320, 222, BACK) draw_string('SOKOBAN', 125, 6, TEXT, BACK) draw_string('Press a button to continue.', 25, 190, TEXT, BACK) iter("You are a pusher employee in", 20, 40, "a store room.", 95, 60, "Push the boxes to their goal", 20, 90, "while minimizing moves.", 45, 110, "You can't push two boxes at", 20, 140, "once or pull them.", 70, 160) wait() fill_rect(0, 40, 320, 140, BACK) iter(" COMMANDS VISUALS", 25, 35, " Move : ARROWS Wall :", 25, 60, " Undo : CLEAR Player :", 25, 80, "Reset : EXE + Goal :", 25, 100, " Help : ANS Box :", 25, 120, "Level : +/- + Goal :", 25, 140, " Quit : BACK Goal :", 25, 160) for j in range(6): fill_rect(275, 60 + 20*j + 4, 10, 10, [COL['#'], COL['@'], COL['+'], COL['$'], COL['*'], COL['.']][j]) wait() LEVELS = ( '4-5#10-|4-#3-#10-|4-#$--#10-|--3#--$##9-|--#--$-$-#9-|3#-#-##-#3-6#|#3-#-##-5#--..#|#-$--$10-..#|5#-3#-#@##--..#|4-#5-9#|4-7#8-', '12#--|#..--#5-3#|#..--#-$--$--#|#..--#$4#--#|#..4-@-##--#|#..--#-#--$-##|6#-##$-$-#|--#-$--$-$-$-#|--#4-#5-#|--12#', '8-8#-|8-#5-@#-|8-#-$#$-##-|8-#-$--$#--|8-##$-$-#--|9#-$-#-3#|#4.--##-$--$--#|##3.4-$--$3-#|#4.--10#|8#9-', '11-8#|11-#--4.#|12#--4.#|#4-#--$-$3-4.#|#-3$#$--$-#--4.#|#--$5-$-#--4.#|#-$$-#$-$-$8#|#--$-#5-#7-|##-9#7-|#4-#4-##7-|#5-$3-##7-|#--$$#$$--@#7-|#4-#4-##7-|11#8-', '8-5#4-|8-#3-5#|8-#-#$##--#|8-#5-$-#|9#-3#3-#|#4.--##-$--$3#|#4.4-$-$$-##-|#4.--##$--$-@#-|9#--$--##-|8-#-$-$--#-|8-3#-##-#-|10-#4-#-|10-6#-') menu() i, count = 0, 5 while True: level = LEVELS[i] grid, moves, his = decompress(level), 0, '' # Initialisation fill_rect(0, 0, 320, 222, BACK) draw_string('SOKOBAN', 125, 6, TEXT, BACK) lev = str(i+1) + '/' + str(count) draw_string(lev, 314-10*len(lev), 6, TEXT, BACK) draw_string('0', 6, 6, TEXT, BACK) X, Y, WIDTH, HEIGTH = 10, 33, 300, 180 # Cadre de jeu d = max(6, min(WIDTH//len(grid[0]), HEIGTH//len(grid), 12)) X, Y = X + int((WIDTH - len(grid[0])*d)/2), Y + int((HEIGTH - len(grid)*d)/2) for y in range(len(grid)): for x in range(len(grid[y])): fill_rect(X+d*x, Y+d*y, d, d, COL[grid[y][x]]) # Tracage initial if grid[y][x] in '+@': XP, YP = x, y while True: key = wait((0, 1, 2, 3, 17, 45, 46, 51, 52)) if key in (0, 1, 2, 3, 17): if key == 17: if not(his): continue XN, YN = XP + (his[-1] in ('l', 'L')) - (his[-1] in ('r', 'R')), YP + (his[-1] in ('u', 'U')) - (his[-1] in ('d', 'D')) XN2, YN2 = 2*XP - XN, 2*YP - YN else: XN, YN = XP + (key==3) - (key==0), YP + (key==2) - (key==1) XN2, YN2 = 2*XN - XP, 2*YN - YP if (grid[YN][XN] == '#') or ((grid[YN][XN] in ('$', '*')) and (grid[YN2][XN2] in ('#', '$', '*'))): continue move = 'l'*(key==0) + 'u'*(key==1) + 'd'*(key==2) + 'r'*(key==3) if grid[YN][XN] in ('$', '*'): move = move.upper() if key != 17 or his[-1].islower(): sett(XP, YP, '+', '.', '-') else: sett(XP, YP, '+', '*', '$'); sett(XN2, YN2, '*', '.', '-') XP, YP = XN, YN if key != 17 and grid[YP][XP] in ('*', '$'): sett(XN2, YN2, '.', '*', '$') sett(XP, YP, '.*', '+', '@') moves = moves + 1 - 2*(key==17) his = his[:-1] if key==17 else (his + move)[-100:] draw_string(str(moves)+' ', 6, 6, TEXT, BACK) if win(): draw_string('YOU WON !', 115, 6, TEXT, BACK) continue elif key == 51: menu() else: i = (i + (key==45) - (key==46))%count del level break