Георги обнови решението на 12.05.2013 12:27 (преди над 11 години)
+import math
+
+
+class Matrix:
+ class IndexProxy:
+ def __init__(self, matrix, index_x):
+ self.matrix = matrix
+ self.index_x = index_x
+
+ def __getitem__(self, index_y):
+ try:
+ return self.matrix[(self.index_x, index_y)]
+ except KeyError:
+ raise IndexError('X or Y coordinate is out of range')
+
+ def __setitem__(self, index_y, value):
+ if (self.index_x, index_y) not in self.matrix:
+ raise IndexError('X or Y coordinate is out of range')
+
+ self.matrix[(self.index_x, index_y)] = value
+
+ def __init__(self, width, height):
+ self._width = width
+ self._height = height
+ self._matrix = {
+ (x, y): None
+ for x in range(width)
+ for y in range(height)
+ }
+
+ def __getitem__(self, index_x):
+ return self.IndexProxy(self._matrix, index_x)
+
+ def __len__(self):
+ return self._width
+
+
+class WorldObject:
+ def __str__(self):
+ return "??"
+
+
+class Food(WorldObject):
+ def __init__(self, energy=0):
+ self.energy = energy
+
+ def __str__(self):
+ return ":3"
+
+
+class PythonPart(WorldObject):
+ def __init__(self):
+ self.pos = None
+
+ def __str__(self):
+ return "##"
+
+
+class PythonHead(PythonPart):
+ def __str__(self):
+ return "@@"
+
+
+class Death(Exception):
+ REASON_OUT_OF_WORLD = "left the world"
+ REASON_BITE = "ate a python part"
+
+ def __init__(self, reason):
+ self.reason = reason
+
+ super().__init__("The python died! Reason: {}. RIP!".format(reason))
+
+
+class DifferentDimensionsError(Exception):
+ pass
+
+
+class Vec2D:
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+ @property
+ def reversed(self):
+ return Vec2D(-self.x, -self.y)
+
+ @property
+ def normalized(self):
+ length = self.length
+ return Vec2D(self.x / length, self.y / length)
+
+ @property
+ def length(self):
+ return math.sqrt(self.x ** 2 + self.y ** 2)
+
+ def __add__(self, other):
+ return Vec2D(self.x + other.x, self.y + other.y)
+
+ def __sub__(self, other):
+ return self + other.reversed
+
+ def __mul__(self, scalar):
+ return Vec2D(self.x * scalar, self.y * scalar)
+
+ def __neg__(self):
+ return self.reversed
+
+ def __eq__(self, other):
+ return (self.x, self.y) == (other.x, other.y)
+
+ def __bool__(self):
+ return (self.x, self.y) != (0, 0)
+
+ def __iter__(self):
+ return iter((self.x, self.y))
+
+
+class Python:
+ UP = Vec2D(0, -1)
+ RIGHT = Vec2D(1, 0)
+ DOWN = Vec2D(0, 1)
+ LEFT = Vec2D(-1, 0)
+
+ def __init__(self, world, coords, size, direction):
+ self.world = world
+ self.size = size
+ self.direction = direction
+
+ if direction not in (self.UP, self.LEFT, self.DOWN, self.RIGHT):
+ raise ValueError("The python needs a proper direction")
+
+ self.head = PythonHead()
+ self.parts = [PythonPart() for _ in range(self.size)]
+ self.energy = 0
+
+ self._draw_parts(coords, direction)
+
+ def _draw_parts(self, position, direction):
+ self.world.add_object(self.head, position.x, position.y)
+
+ pos = position - direction
+ for part in self.parts:
+ self.world.add_object(part, pos.x, pos.y)
+ pos -= direction
+
+ def move(self, direction):
+ current_pos = self.head.pos
+ new_pos = self.head.pos + direction
+
+ if new_pos == self.parts[0].pos:
+ raise ValueError("Cannot go backwards")
+
+ if not self.world.are_coords_inside(new_pos.x, new_pos.y):
+ raise Death(Death.REASON_OUT_OF_WORLD)
+
+ old_contents = self.world[new_pos.x][new_pos.y].contents
+
+ if isinstance(old_contents, PythonPart):
+ raise Death(Death.REASON_BITE)
+ elif isinstance(old_contents, Food):
+ self.energy += old_contents.energy
+ self.parts.append(PythonPart())
+
+ self.direction = direction
+ new_positions = [self.head.pos] + [part.pos for part in self.parts]
+
+ self.world.move_object(self.head, new_pos.x, new_pos.y)
+
+ for index, part in enumerate(self.parts):
+ self.world.move_object(part, new_positions[index].x,
+ new_positions[index].y)
+
+
+class Cell:
+ def __init__(self, contents=None):
+ if contents is not None and not isinstance(contents, WorldObject):
+ raise TypeError("{} is not an instance of WorldObject"
+ .format(type(contents)))
+
+ self.contents = contents
+
+ def is_empty(self):
+ return not self.contents
+
+ def __str__(self):
+ return ".." if self.contents is None else str(self.contents)
+
+
+class World:
+ def __init__(self, width):
+ self._width = width
+ self._matrix = Matrix(width, width)
+
+ for x in range(width):
+ for y in range(width):
+ self._matrix[x][y] = Cell()
+
+ def are_coords_inside(self, x, y):
+ return 0 <= x <= self._width and 0 <= y <= self._width
+
+ def add_object(self, obj, x, y):
+ self._matrix[x][y].contents = obj
+ obj.pos = Vec2D(x, y)
+
+ def move_object(self, obj, new_x, new_y):
+ self.del_object(obj)
+ self.add_object(obj, new_x, new_y)
+
+ def del_object(self, obj):
+ if obj.pos is not None:
+ self._matrix[obj.pos.x][obj.pos.y].contents = None
+
+ def __getitem__(self, index_x):
+ """
+ Return cell object for the row and column. Supports double indexing
+ world[x][y].
+
+ Raises IndexError if index is invalid.
+ """
+ return self._matrix[index_x]
+
+ def __len__(self):
+ return len(self._matrix)
+
+ def __str__(self):
+ rows = (
+ ''.join(str(self._matrix[x][y]) for x in range(self._width))
+ for y in range(self._width)
+ )
+
+ return '\n'.join(rows)
+
+if __name__ == '__main__':
+ world = World(10)
+ py = Python(world, Vec2D(5, 5), 3, Python.RIGHT)
+
+ print(world)
Интересно си го направил това с Matrix и IndexProxy, но може и спокойно да се получи с един 2D списък.
Може :)