Мартин обнови решението на 15.05.2013 02:26 (преди над 11 години)
+from collections import deque
+import operator
+
+
+class Death(Exception):
+ pass
+
+
+class MyValidatingList(list):
+ def __getitem__(self, key):
+ if not key in range(len(self)):
+ raise IndexError()
+ return super(MyValidatingList, self).__getitem__(key)
+
+ def __setitem__(self, key, value):
+ if not key in range(len(self)):
+ raise IndexError()
+ return super(MyValidatingList, self).__setitem__(key, value)
+
+
+class World:
+ def __init__(self, width):
+ self.width = width
+ self.__init_board()
+
+ def __init_board(self):
+ self.__board = MyValidatingList()
+ for i in range(self.width):
+ column = MyValidatingList()
+ for j in range(self.width):
+ column.append(Cell())
+ self.__board.append(column)
+
+ def __len__(self):
+ return self.width
+
+ def __str__(self):
+ s = ''
+ for row in reversed(tuple(zip(*self.__board))):
+ s += ''.join(str(cell) for cell in row) + '\n'
+ return s
+
+ def __getitem__(self, key):
+ return self.__board[key]
+
+
+class Cell:
+ EMPTY_CELL = '..'
+
+ def __init__(self, contents=None):
+ if not isinstance(contents, WorldObject) and not contents is None:
+ self.contents = None
+ raise TypeError("Invalid cell contents type")
+ self.contents = contents
+
+ def is_empty(self):
+ return self.contents is None
+
+ def __str__(self):
+ if self.is_empty():
+ return self.EMPTY_CELL
+ else:
+ return str(self.contents)
+
+
+class WorldObject:
+
+ def __str__(self):
+ return self.CHARS
+
+ def __repr__(self):
+ return '%s at v(%d, %d)' % (self.__class__.__name__,
+ self.coords.x,
+ self.coords.y)
+
+
+class Food(WorldObject):
+ CHARS = ':3'
+
+ def __init__(self, energy=0):
+ self.__energy = energy
+
+ @property
+ def energy(self):
+ return self.__energy
+
+
+class Vec2D(tuple):
+ x = property(operator.itemgetter(0))
+ y = property(operator.itemgetter(1))
+
+ def __new__(cls, x, y):
+ return super(Vec2D, cls).__new__(Vec2D, (x, y))
+
+ def __add__(self, other):
+ return Vec2D(self.x+other.x, self.y+other.y)
+
+ def __mul__(self, scalar):
+ if not isinstance(scalar, int):
+ raise TypeError("Can't multiply Vec2D with a non-int type object")
+ return Vec2D(self.x*scalar, self.y*scalar)
+
+ def fits_in_world(self, world):
+ valid_coords = range(len(world))
+ return self.x in valid_coords and self.y in valid_coords
+
+
+class PythonPart(WorldObject):
+ CHARS = '##'
+
+ def __init__(self, coords):
+ super(PythonPart, self).__init__()
+ self.coords = coords
+
+
+class PythonHead(PythonPart):
+ CHARS = '@@'
+
+
+class Python:
+ LEFT = Vec2D(-1, 0)
+ RIGHT = Vec2D(1, 0)
+ UP = Vec2D(0, 1)
+ DOWN = Vec2D(0, -1)
+
+ OPPOSITE_DIRECTION = {LEFT: RIGHT,
+ RIGHT: LEFT,
+ UP: DOWN,
+ DOWN: UP}
+
+ def __init__(self, world, coords, size, direction, energy=0):
+ self.world = world
+ self.coords = coords
+ self.size = size
+ self.direction = direction
+ self.energy = energy
+ ## Init the head
+ self.head = PythonHead(coords)
+ self.world[coords.x][coords.y] = Cell(self.head)
+ ## Init the body
+ self.__init_body(self.OPPOSITE_DIRECTION[direction])
+
+ def move(self, direction):
+ self.__validate_move(direction)
+ self.__move_head(direction)
+ self.__move_body(direction)
+
+ def __init_body(self, direction):
+ self.body = deque()
+ for i in range(1, self.size + 1):
+ part_coords = self.coords+direction*i
+ part = PythonPart(part_coords)
+ self.body.append(part)
+ self.world[part_coords.x][part_coords.y] = Cell(part)
+
+ def __move_head(self, direction):
+ self.head.coords = self.coords = self.coords + direction
+ self.world[self.coords.x][self.coords.y] = Cell(self.head)
+
+ def __move_body(self, direction):
+ old_coords = self.head.coords + self.OPPOSITE_DIRECTION[direction]
+ if self.size != len(self.body): # Body has increased
+ part = PythonPart(old_coords)
+ self.world[old_coords.x][old_coords.y] = Cell(part)
+ else:
+ last_part = self.body[-1]
+ self.world[last_part.coords.x][last_part.coords.y] = Cell()
+ last_part.coords = old_coords
+ self.body.appendleft(last_part)
+ self.body.pop()
+
+ def __validate_move(self, direction):
+ new_coords = self.coords + direction
+ if not new_coords.fits_in_world(self.world):
+ raise Death()
+ new_cell = self.world[new_coords.x][new_coords.y]
+ if isinstance(new_cell.contents, PythonPart):
+ raise Death()
+ elif isinstance(new_cell.contents, Food):
+ self.size += 1
+ self.energy += new_cell.contents.energy