I challenge you to a game of Sokoban Golf!

Graphics of my programmable Sokoban game which this was the prototype for, by Fiverr user applemoment
Graphics of my programmable Sokoban game which this was the prototype for, by Fiverr user applemoment

Before I dived in earnest into writing the new version of my computer game that teaches Python, this time with game mechanics based on the classical puzzle game “Sokoban”, I wanted to make sure that Programmable Sokoban is a fun concept. So I wrote a prototype in a couple of hours.

The programmers among you will enjoy it.

“Golf” is a game played by programmers, where you have to solve a problem in as few keystrokes as possible. I challenge you to solve these Programmable Sokoban levels in as few keystrokes as possible!

'''
Sokoban Golf by Aur Saraf
At line 145 you will find a very naive solution for level 1.
You can easily learn the API from it.
How few lines do you need for solutions to levels 1, 2, and 3?
Do not modify the game or the levels in any way. Just remove the
level 1 solution and uncomment the level 2 and level 3 boards,
and try to solve all three in as few characters as possible.
The challenge is intentionally on ALL THREE LEVELS TOGETHER. You
should be writing utility functions that help with more than one
level.
Good luck, and have fun!
'''
WALL = '#'
EMPTY = ' '
PLAYER = '@'
CRATE = '*'
EMPTY_DESTINATION = 'X'
CRATE_IN_DESTINATION = 'V'
EMPTY_SYMBOLS = (EMPTY, EMPTY_DESTINATION)
CRATE_SYMBOLS = (CRATE, CRATE_IN_DESTINATION)
DIRECTIONS = SOUTH, WEST, NORTH, EAST = 'SWNE'
_dirs = lambda values: dict(zip(DIRECTIONS, values))
DIRECTION_VECTOR = _dirs((
(1, 0),
(0, -1),
(-1, 0),
(0, 1),
))
LEFT = _dirs((DIRECTIONS[(i - 1) % 4] for i in xrange(4)))
RIGHT = _dirs((DIRECTIONS[(i + 1) % 4] for i in xrange(4)))
class QuietBoard(object):
def __init__(self, level):
self.board = [list(row) for row in level.splitlines()]
self.crates_at_large = level.count(CRATE)
self.player = self._player_position(self.board)
self.orientation = SOUTH
self.on_destination = False
def _player_position(self, board):
for i, row in enumerate(board):
if PLAYER in row:
return (i, row.index(PLAYER))
assert False
def __str__(self):
return '\n'.join(''.join(row) for row in self.board) + '\n' + str(self.orientation)
def is_win(self):
return self.crates_at_large == 0
def at(self, (row, column)):
return self.board[row][column]
def forward(self):
in_front = self._in_front(self.player)
if self._is_movable_crate(in_front):
self._slide_crate(in_front)
if self._is_empty(in_front):
self._slide_player()
def left(self):
self.orientation = LEFT[self.orientation]
def right(self):
self.orientation = RIGHT[self.orientation]
def _is_movable_crate(self, position):
return self._is_crate(position) and self._is_empty(self._in_front(position))
def _in_front(self, (row, column)):
drow, dcolumn = DIRECTION_VECTOR[self.orientation]
return row + drow, column + dcolumn
def _is_empty(self, position):
return self.at(position) in EMPTY_SYMBOLS
def _is_crate(self, position):
return self.at(position) in CRATE_SYMBOLS
def _slide_crate(self, position):
if self.at(position) == CRATE_IN_DESTINATION:
empty = EMPTY_DESTINATION
self.crates_at_large += 1
else:
empty = EMPTY
self._put(empty, position)
destination = self._in_front(position)
if self.at(destination) == EMPTY_DESTINATION:
crate = CRATE_IN_DESTINATION
self.crates_at_large -= 1
else:
crate = CRATE
self._put(crate, destination)
def _slide_player(self):
destination = self._in_front(self.player)
empty = EMPTY_DESTINATION if self.on_destination else EMPTY
self.on_destination = self.at(destination) == EMPTY_DESTINATION
self._put(empty, self.player)
self._put(PLAYER, destination)
self.player = destination
def _put(self, symbol, (row, column)):
assert (symbol in EMPTY_SYMBOLS) != self._is_empty((row, column))
self.board[row][column] = symbol
class Board(QuietBoard):
def __init__(self, level):
super(Board, self).__init__(level)
print self
print 'Start!'
def forward(self):
super(Board, self).forward()
print self
if self.is_win():
print 'You beat this level!'
print
LEVEL0 = '''\
#############
#@ * X#
#############'''
b = QuietBoard(LEVEL0)
b.left()
for _ in range(10): b.forward()
assert b.is_win()
LEVEL1 = '''\
#############
#@ X#
# ##*########
# ########
#############'''
b = Board(LEVEL1)
b.forward()
b.forward()
b.left()
b.forward()
b.forward()
b.forward()
b.left()
b.forward()
b.left()
b.left()
b.forward()
b.right()
b.forward()
b.forward()
b.forward()
b.right()
b.forward()
b.forward()
b.right()
for _ in range(9): b.forward()
LEVEL2 = '''\
#############
# ###### #
#@ #
##*#*# ####X#
## ####X#
#############'''
#b = Board(LEVEL2)
# Mini Cosmos 01 (c) Aymeric Du Peloux
LEVEL3 = '''\
#####
### #
# * # ##
# # X #
# # #
## # #
#@ ###
##### '''
#b = Board(LEVEL3)
view raw soko.py hosted with ❤ by GitHub