Beispiel #1
0
 def test_longest_path_to_tail(self):
     m = Map(6, 6)
     m.create_food(Pos(4, 4))
     s = Snake(m, Direc.RIGHT,
               [Pos(1, 3), Pos(1, 2), Pos(1, 1)],
               [PointType.HEAD_R, PointType.BODY_HOR, PointType.BODY_HOR])
     solver = PathSolver(s)
     # noinspection PyUnusedLocal
     act_path = solver.longest_path_to_tail()
     act_path = solver.longest_path_to_tail()  # Check idempotency
     expect_path = [
         Direc.RIGHT, Direc.DOWN, Direc.DOWN, Direc.DOWN, Direc.LEFT,
         Direc.LEFT, Direc.LEFT, Direc.UP, Direc.RIGHT, Direc.RIGHT,
         Direc.UP, Direc.LEFT, Direc.LEFT, Direc.UP
     ]
     assert m.point(s.tail()).type == PointType.BODY_HOR
     assert len(act_path) == len(expect_path)
     for i, direc in enumerate(act_path):
         assert direc == expect_path[i]
     # Empty path because the tail has not been 'emptied' yet,
     # so it is not a valid position to be added to the queue.
     # This means that after all the evaluations, None of them check the snake tail
     # Because it is not valid, according to the function, since
     # it is part of the body.
     # Therefore, we pass the path finders through a function called path_to
     # to remove the 'tail' at that location so it can be evaluated.
     assert not solver.longest_path_to(s.tail())
Beispiel #2
0
 def test_init(self):
     with pytest.raises(TypeError):
         _ = Map(5, 1.5)
     with pytest.raises(ValueError):
         _ = Map(4, 5)
     m = Map(12, 12)
     for i in range(m.num_rows):
         for j in range(m.num_cols):
             if i == 0 or i == m.num_rows - 1 or j == 0 or j == m.num_cols - 1:
                 assert m.point(Pos(i, j)).type == PointType.WALL
             else:
                 assert m.point(Pos(i, j)).type == PointType.EMPTY
Beispiel #3
0
 def test_arithmetic(self):
     p1 = Pos(-5, 10)
     p2 = Pos(5, -10)
     p3 = p1 + p2
     p4 = p1 - p2
     p5 = p2 - p1
     assert p3 == Pos(0, 0)
     assert p3 - p1 == p2
     assert p3 - p2 == p1
     assert p4 == Pos(-10, 20)
     assert p5 == -Pos(-10, 20)
     assert p4 + p2 == p1
     assert p5 + p1 == p2
Beispiel #4
0
 def __write_logs(self):
     """
     Writes game states to log.
     If picture logging is enabled, then an ascii rendering of the game state will be logged at each step.
     :return: None.
     """
     if self.__conf.picture_logging:
         self.__log_file.write('[Episode {} Step {}]\n'.format(
             self.__episode, self.__snake.steps))
         for i in range(self.__map.num_rows):
             for j in range(self.__map.num_cols):
                 pos = Pos(i, j)
                 t = self.__map.point(pos).type
                 if t == PointType.EMPTY:
                     self.__log_file.write("  ")
                 elif t == PointType.WALL:
                     self.__log_file.write("# ")
                 elif t == PointType.FOOD:
                     self.__log_file.write("F ")
                 elif t == PointType.HEAD_L or t == PointType.HEAD_U or \
                         t == PointType.HEAD_R or t == PointType.HEAD_D:
                     self.__log_file.write("H ")
                 elif pos == self.__snake.tail():
                     self.__log_file.write("T ")
                 else:
                     self.__log_file.write("B ")
             self.__log_file.write("\n")
     self.__log_file.write("[ last/next direction: {}/{} ]\n".format(
         self.__snake.direc, self.__snake.direc_next))
     self.__log_file.write("\n")
Beispiel #5
0
 def test_cycle(self):
     m = Map(6, 6)
     s = Snake(m, Direc.RIGHT, [Pos(1, 2), Pos(1, 1)],
               [PointType.HEAD_R, PointType.BODY_HOR])
     solver = HamiltonSolver(s, False)
     # No shortcuts please.
     table = solver.table
     cnt = 0
     original_head = s.head()
     while True:
         head = s.head()
         assert cnt == table[head.x][head.y].idx
         s.move(solver.next_direc())
         cnt += 1
         if s.head() == original_head:
             break
     assert cnt == m.capacity
Beispiel #6
0
 def test_init(self):
     p = Pos(-5, 5)
     assert p == Pos(-5, 5)
     p.x = -10
     p.y = 10
     assert p != Pos(-5, 5)
     assert p == Pos(-10, 10)
Beispiel #7
0
 def test_shortest_path_to_food(self):
     m = Map(7, 7)
     m.create_food(Pos(5, 5))
     s = Snake(m, Direc.RIGHT,
               [Pos(2, 3), Pos(2, 2), Pos(2, 1)],
               [PointType.HEAD_R, PointType.BODY_HOR, PointType.BODY_HOR])
     solver = PathSolver(s)
     # noinspection PyUnusedLocal
     act_path = solver.shortest_path_to_food()
     act_path = solver.shortest_path_to_food()  # Check idempotency
     # Idempotency is pretty much checking for side effects.
     # You're checking whether or not the algorithm will produce the same result if it is run multiple times.
     # We do this multiple times to verify that the table has been properly reset.
     expect_path = [
         Direc.RIGHT, Direc.RIGHT, Direc.DOWN, Direc.DOWN, Direc.DOWN
     ]
     assert len(act_path) == len(expect_path)
     for i, direc in enumerate(act_path):
         assert direc == expect_path[i]
     assert solver.table[5][1].dist == 5
     assert solver.table[5][1].dist == solver.table[5][5].dist
     # Empty path
     assert not solver.shortest_path_to(s.tail())
Beispiel #8
0
    def next_direc(self):
        """
        Get the next direction to move in.
        :return: A direction of type Direc.
        """
        # Clone the snake.
        s_copy, m_copy = self.snake.copy()
        # Step 1: Get the path to the food. If path 1 exists, move to step 2.
        # Otherwise, move to step 4.
        self.__path_solver.snake = self.snake  # That's my snake you're looking at!
        path_to_food = self.__path_solver.shortest_path_to_food()
        if path_to_food:
            # Step 2: Make a virtual snake to eat the food along the path.
            s_copy.move_path(path_to_food)
            if m_copy.is_full():
                return path_to_food[0]
            # Step 3: Calculate the longest path from head to tail after eating food.
            # If that longest path exists, then move along that path.
            # Otherwise, go to step 4.
            self.__path_solver.snake = s_copy
            path_to_tail = self.__path_solver.longest_path_to_tail()
            if len(path_to_tail) > 1:
                return path_to_food[0]

        # Step 4: Calculate the longest path from head to tail.
        # Remember, there is no path to the food right now that will guarantee our survival.
        # Therefore, we do one full loop, and then check again.
        # If that path exists, then move along that path.
        # Else, move to step 5.
        self.__path_solver.snake = self.snake
        path_to_tail = self.__path_solver.longest_path_to_tail()
        if len(path_to_tail) > 1:
            return path_to_tail[0]

        # Step 5: RUN AWAY! No, seriously, get as far away as you can from the food.
        head = self.snake.head()
        direc, max_dist = self.snake.direc, -1
        for adj in head.all_adj():
            if self.map.is_safe(adj):
                dist = Pos.manhattan_distance(adj, self.map.food)
                if dist > max_dist:
                    max_dist = dist
                    direc = head.direction_to(adj)
        return direc
Beispiel #9
0
    def test_game_window(self):
        game_conf = GameConfig()
        game_conf.map_rows = 15
        game_conf.map_cols = game_conf.map_rows
        game_conf.show_grid_line = True
        game_conf.show_info_panel = False

        game_map = Map(game_conf.map_rows + 2, game_conf.map_cols + 2)
        contents = (
            # Walls. Don't they look nice?
            (Pos(1, 6), PointType.WALL),
            (Pos(1, 7), PointType.WALL),
            (Pos(1, 8), PointType.WALL),
            (Pos(1, 9), PointType.WALL),
            (Pos(1, 10), PointType.WALL),
            (Pos(15, 6), PointType.WALL),
            (Pos(15, 7), PointType.WALL),
            (Pos(15, 8), PointType.WALL),
            (Pos(15, 9), PointType.WALL),
            (Pos(15, 10), PointType.WALL),
            (Pos(6, 1), PointType.WALL),
            (Pos(7, 1), PointType.WALL),
            (Pos(8, 1), PointType.WALL),
            (Pos(9, 1), PointType.WALL),
            (Pos(10, 1), PointType.WALL),
            (Pos(6, 15), PointType.WALL),
            (Pos(7, 15), PointType.WALL),
            (Pos(8, 15), PointType.WALL),
            (Pos(9, 15), PointType.WALL),
            (Pos(10, 15), PointType.WALL),
            # Food. Very nicely distributed food.
            (Pos(4, 6), PointType.FOOD),
            (Pos(4, 10), PointType.FOOD),
            (Pos(6, 4), PointType.FOOD),
            (Pos(10, 4), PointType.FOOD),
            (Pos(6, 12), PointType.FOOD),
            (Pos(10, 12), PointType.FOOD),
            (Pos(12, 6), PointType.FOOD),
            (Pos(12, 10), PointType.FOOD),
            # Top-left
            (Pos(2, 2), PointType.BODY_VER),
            (Pos(3, 2), PointType.BODY_VER),
            (Pos(4, 2), PointType.BODY_UR),
            (Pos(4, 3), PointType.BODY_LU),
            (Pos(3, 3), PointType.BODY_VER),
            (Pos(2, 3), PointType.BODY_RD),
            (Pos(2, 4), PointType.BODY_DL),
            (Pos(2, 4), PointType.BODY_DL),
            (Pos(3, 4), PointType.BODY_VER),
            (Pos(4, 4), PointType.HEAD_D),
            # Top-right
            (Pos(2, 14), PointType.BODY_VER),
            (Pos(3, 14), PointType.BODY_VER),
            (Pos(4, 14), PointType.BODY_LU),
            (Pos(4, 13), PointType.BODY_UR),
            (Pos(3, 13), PointType.BODY_VER),
            (Pos(2, 13), PointType.BODY_DL),
            (Pos(2, 12), PointType.BODY_RD),
            (Pos(3, 12), PointType.BODY_VER),
            (Pos(4, 12), PointType.HEAD_D),
            # Bottom-left
            (Pos(14, 2), PointType.BODY_VER),
            (Pos(13, 2), PointType.BODY_VER),
            (Pos(12, 2), PointType.BODY_RD),
            (Pos(12, 3), PointType.BODY_DL),
            (Pos(13, 3), PointType.BODY_VER),
            (Pos(14, 3), PointType.BODY_UR),
            (Pos(14, 4), PointType.BODY_LU),
            (Pos(13, 4), PointType.BODY_VER),
            (Pos(12, 4), PointType.HEAD_U),
            # Bottom-right
            (Pos(14, 14), PointType.BODY_VER),
            (Pos(13, 14), PointType.BODY_VER),
            (Pos(12, 14), PointType.BODY_DL),
            (Pos(12, 13), PointType.BODY_RD),
            (Pos(13, 13), PointType.BODY_VER),
            (Pos(14, 13), PointType.BODY_LU),
            (Pos(14, 12), PointType.BODY_UR),
            (Pos(13, 12), PointType.BODY_VER),
            (Pos(12, 12), PointType.HEAD_U),
            # Middle
            (Pos(10, 6), PointType.HEAD_L),
            (Pos(10, 7), PointType.BODY_HOR),
            (Pos(10, 8), PointType.BODY_HOR),
            (Pos(10, 9), PointType.BODY_HOR),
            (Pos(10, 10), PointType.BODY_LU),
            (Pos(9, 10), PointType.BODY_VER),
            (Pos(8, 10), PointType.BODY_DL),
            (Pos(8, 9), PointType.BODY_HOR),
            (Pos(8, 8), PointType.BODY_HOR),
            (Pos(8, 7), PointType.BODY_HOR),
            (Pos(8, 6), PointType.BODY_UR),
            (Pos(7, 6), PointType.BODY_VER),
            (Pos(6, 6), PointType.BODY_RD),
            (Pos(6, 7), PointType.BODY_HOR),
            (Pos(6, 8), PointType.BODY_HOR),
            (Pos(6, 9), PointType.BODY_HOR),
            (Pos(6, 10), PointType.HEAD_R))
        for content in contents:
            game_map.point(content[0]).type = content[1]

        GameWindow(game_conf, game_map, "Basic Elements").show()
Beispiel #10
0
    def __init__(self):
        # Size #
        self.map_rows = 10
        self.map_cols = self.map_rows
        self.map_width = 500  # pixels
        self.map_height = self.map_width
        self.info_panel_width = 170  # pixels
        self.window_width = self.map_width + self.info_panel_width
        self.window_height = self.map_height
        self.grid_pad_ratio = 0.25

        # AI #
        self.enable_AI = False
        self.solver_name = 'GreedySolver'
        # This isn't very important since the solver type is changed in the run_script.

        # Visuals #
        self.show_gui = True
        # Enable show_gui to see a visual representation of the snake.
        # Disable it to do tests for consistency and accuracy.
        self.show_grid_line = True
        # Enable show_grid_line to draw grid lines, and vice-versa.
        self.show_info_panel = True
        # Enable show_info_panel to get information about the current solver,
        # the length of the snake, as well as other miscellaneous information.
        self.picture_logging = False
        # Enable this to log the position of the snake at every step. Slows down performance.

        # Delay #
        self.interval_draw = 40  # ms
        self.interval_draw_max = 200  # ms

        # Color #
        self.colour_bg = '#000000'  # Black
        self.colour_txt = '#F5F5F5'  # Close to white, but not entirely.
        self.colour_line = '#424242'  # Gray. Or is it grey?
        self.colour_wall = '#F5F5F5'  # Close to white.
        self.colour_food = '#FFF59D'  # A light shade of yellow.
        self.colour_head = '#F5F5F5'  # Close to white.
        self.colour_body = '#F5F5F5'  # Close to white.

        # Initial snake #
        self.init_direc = Direc.RIGHT
        # This is so that the snake position is not randomized.
        # The next direction of the snake is still none, so the snake will not move.
        self.init_bodies = [Pos(1, 2), Pos(1, 1)]
        # Set the initial positions of the snakes and the appearance of their bodies.
        self.init_types = [PointType.HEAD_R, PointType.BODY_HOR]

        # Font #
        self.font_info = ('Helvetica', 9)

        # Info #
        self.info_str = (
            "<w/a/s/d>: up/left/down/right\n"
            "<space>: pause/resume\n"
            "<r>: restart    <esc>: exit\n"
            "---------------------------------\n"
            "solver: %s\n"
            "status: %s\n"
            "steps: %d\n"
            "length: %d/%d (" + str(self.map_rows) + "x" + str(self.map_cols) +
            ")\n"
            # @formatter:off
            "---------------------------------\n"
            "move delay:"
            # @formatter:on
        )
        self.info_status = [
            'eating', 'dead', 'full'
        ]  # The different statuses for the snake. Very polar, I know.
Beispiel #11
0
 def test_adj(self):
     p = Pos(0, 0)
     adjacents = p.all_adj()
     assert len(adjacents) == 4
     hit = [False] * 4
     assert hit.count(False) == 4
     for adj in adjacents:
         if adj == Pos(-1, 0):
             assert p.direction_to(adj) == Direc.UP
             hit[0] = True
         elif adj == Pos(1, 0):
             assert p.direction_to(adj) == Direc.DOWN
             hit[1] = True
         elif adj == Pos(0, 1):
             assert p.direction_to(adj) == Direc.RIGHT
             hit[2] = True
         elif adj == Pos(0, -1):
             assert p.direction_to(adj) == Direc.LEFT
             hit[3] = True
         else:
             raise ValueError("error adj Pos")
     assert hit.count(False) == 0
Beispiel #12
0
 def test_dist(self):
     p1 = Pos(-5, 20)
     p2 = Pos(10, 8)
     assert Pos.manhattan_distance(p1, p2) == 27