def build_cycle(self, env: Environment) -> Optional[List[Vector]]: # Attempt to build the list of next actions head = env.snake.head() tail = env.snake.tail() # We build a cycle by building the longest path from the snakes # head to it's tail. If the snake is only 1 tile long, then we # make an 'adjustment' and choose a tile next to the head # as the target, essentially faking a tail. # This is necessary as our algorithm won't return any actions # if the from tile is the same as the target tile. if head == tail: if tail.x > 1: adjustment = Vector(-1, 0) else: adjustment = Vector(1, 0) tail = tail + adjustment built = self._bfsl.longest_path(env, head, tail, env.snake.action.vector) if built is None: return None # We've built the longest path from head to tail, but we need to # check that it covers all vectors. if len(built) != env.available_tiles_count(): return None built.append(head) return built
def setUp(self) -> None: self._vectors = [ Vector(1, 1), Vector(1, 2), Vector(1, 3), ] self._o = objects.Object(self._vectors)
def _unseen_adjacent_vectors(self, env: Environment, vector: Vector, seen_vectors: List[Vector]) -> List[Vector]: adjacent_vectors = [ Vector( vector.x - 1, vector.y, ), Vector( vector.x, vector.y - 1, ), Vector( vector.x + 1, vector.y, ), Vector( vector.x, vector.y + 1, ), ] searchable_vectors = [] for v in adjacent_vectors: if v in seen_vectors: continue if self._should_search_vector(env, v): searchable_vectors.append(v) return searchable_vectors
def test_unit(self): """ L{Vector.unit} returns a L{Vector} in the same direction as the original, but with magnitude 1. """ v = Vector(1, 2, 3) u = v.unit() scale = (1 ** 2 + 2 ** 2 + 3 ** 2) ** 0.5 self.assertEquals(u, Vector(1 / scale, 2 / scale, 3 / scale))
def test_move_to_not_adjacent(self): invalid_vectors = [ Vector(5, 3), Vector(7, 5), Vector(5, 7), Vector(3, 5) ] for v in invalid_vectors: s = objects.Snake([Vector(5, 5)]) self.assertFalse(s.move_to(v)) self.assertEqual(len(s), 1)
def _vectors_of(self, t: tile.Tile) -> List[Vector]: vectors = [] for y in range(0, self._height): for x in range(0, self._width): if self._tiles[y][x] == t: vectors.append(Vector(x, y)) return vectors
def _random_available_position(self) -> Vector: t = None rand_x, rand_y = 0, 0 while t is None or t != tile.EMPTY: rand_x = random.randint(1, self._height - 1) rand_y = random.randint(1, self._width - 1) t = self._tiles[rand_x][rand_y] return Vector(rand_x, rand_y)
def test_move_to_existing_vector(self): invalid_vectors = [Vector(6, 5), Vector(5, 6)] for v in invalid_vectors: s = objects.Snake( [Vector(5, 5), Vector(6, 5), Vector(6, 6), Vector(5, 6)]) self.assertFalse(s.move_to(v)) self.assertEqual(len(s), 4)
def shortest_path(self, environment: Environment, from_vector: Vector, to_vector: Vector, first_move: Vector) -> Optional[List[Vector]]: # Search for path for fruit. Returned vector has the next vector. fruit_vector = self._search_from(environment, from_vector, to_vector, first_move) if fruit_vector is None: return None vector_steps = [] # Traverse backwards from the fruit towards to snake current_step = fruit_vector while True: vector_steps.append(Vector(current_step.x, current_step.y)) if isinstance(current_step, OwnedVector): current_step = current_step.owner else: break # We traversed from fruit to snake, so reverse the list vector_steps.reverse() return vector_steps
def _search_from(self, env: Environment, start_vector: Vector, end_vector: Vector, first_move: Vector) -> Optional[OwnedVector]: seen_vectors = [start_vector] # Don't process more than once queue = [start_vector] # First-in-first-out queue while queue: # Get the first vector from the queue vector = queue.pop(0) # Check if the tile at the vector is the goal if vector == end_vector: return vector # Get adjacent vectors that we haven't seen yet adjacent_vectors = self._unseen_adjacent_vectors( env, vector, seen_vectors) # Add each adjacent vector to the queue for v in adjacent_vectors: if vector == start_vector: dir_vec = v - vector if first_move.is_reverse(dir_vec): continue queue.append(OwnedVector(v.x, v.y, vector)) seen_vectors.append(v)
def _vector_of(self, t: tile.Tile) -> Optional[Vector]: for y in range(0, self._height): for x in range(0, self._width): if self._tiles[y][x] == t: return Vector(x, y) return None
def setUp(self) -> None: self._vector = Vector(4, 3) self._f = objects.Fruit(self._vector)
def test_at_vector(self): self.assertTrue(self._f.at_vector(Vector(4, 3))) self.assertFalse(self._f.at_vector(Vector(1, 2))) self.assertFalse(self._f.at_vector(Vector(1, 3)))
from typing import List from game.vector import Vector class Action: def __init__(self, vector: Vector, description: str): self.vector = vector self.description = description def __eq__(self, other: 'Action'): return self.vector == other.vector NONE = Action(Vector(0, 0), "none") LEFT = Action(Vector(-1, 0), "left") UP = Action(Vector(0, -1), "up") RIGHT = Action(Vector(1, 0), "right") DOWN = Action(Vector(0, 1), "down") ALL = ( LEFT, UP, RIGHT, DOWN, ) def vector_to_action(vector: Vector) -> Action: for a in ALL: if a.vector == vector:
def test_vectors_to_action(self): vectors = [Vector(-1, 0), Vector(1, 0), Vector(0, -1), Vector(0, 1)] self.assertEqual(action.vectors_to_action(vectors), [action.LEFT, action.RIGHT, action.UP, action.DOWN])
def test_vector_to_action(self): self.assertEqual(action.vector_to_action(Vector(-1, 0)), action.LEFT) self.assertEqual(action.vector_to_action(Vector(1, 0)), action.RIGHT) self.assertEqual(action.vector_to_action(Vector(0, -1)), action.UP) self.assertEqual(action.vector_to_action(Vector(0, 1)), action.DOWN)
def _normalise_vector(self, vector: Vector) -> Vector: return Vector(vector.x * self.tile_width, vector.y * self.tile_height)
def test_move_to_valid_vectors(self): valid_vectors = [Vector(4, 5), Vector(5, 4), Vector(5, 6)] for v in valid_vectors: s = objects.Snake([Vector(5, 5), Vector(6, 5)]) self.assertTrue(s.move_to(v)) self.assertEqual(len(s), 3)
def test_tail(self): self.assertEqual(self._s.tail(), Vector(3, 0))
def test_head(self): self.assertEqual(self._s.head(), Vector(1, 2))
def setUp(self) -> None: ###### # TSS #HSS S # S S # S S # SSS ###### self._s = objects.Snake([ Vector(1, 2), Vector(2, 2), Vector(3, 2), Vector(3, 3), Vector(3, 4), Vector(3, 5), Vector(4, 5), Vector(5, 5), Vector(5, 4), Vector(5, 3), Vector(5, 2), Vector(5, 1), Vector(5, 0), Vector(4, 0), Vector(3, 0), ])