-
-
Save SonOfLilit/1c7c20e3bbfd01c780c1 to your computer and use it in GitHub Desktop.
Pythonists, I challenge you to a game of Sokoban Golf!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
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!' | |
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment