Example #1
0
    def _add_wall(self):
        if self.player_hide.walls_counter < self.player_hide.walls_max and not self.player_hide.wall_timer:
            wall_pos = copy.deepcopy(self.player_hide.pos)
            wall_size = (max(int(self.player_hide.width / 10), 2),
                         max(int(self.player_hide.height / 2), 2))  # minimum 2x2 Wall
            vision_arc_range = np.sqrt((self.player_hide.vision_top.x - self.player_hide.pos.x) * (self.player_hide.vision_top.x - self.player_hide.pos.x) + (
                self.player_hide.vision_top.y - self.player_hide.pos.y) * (self.player_hide.vision_top.y - self.player_hide.pos.y))
            # vision arc range - 1.5 wall width, so the wall is always created inside PoV.
            wall_pos.x = wall_pos.x + vision_arc_range - \
                (1.5 * wall_size[0])
            wall_pos = Point.triangle_unit_circle_relative(
                self.player_hide.direction, self.player_hide.pos, wall_pos)

            wall = Wall(self.player_hide, wall_pos.x,
                        wall_pos.y, wall_size, self.cfg['graphics_path_wall_owner'])
            wall._rotate(self.player_hide.direction, wall_pos)
            if self._can_create_wall(wall, self.agent_env['p_hide']['enemy']):
                self.player_hide.walls_counter += 1
                self.walls_group.add(wall)
                self.player_hide.wall_timer = copy.deepcopy(
                    self.player_hide.wall_timer_init)
                return True
            else:
                del wall

        return False
Example #2
0
    def _rotate(self, angle, position):
        """
        Rotates the sprite by creating new Rectangle and updates its polygon points

        Parameters
        ----------
            angle : float
                player direction in radians
            position : Point
                center of the wall

        Returns
        -------
            None
        """
        self.direction = angle
        # Copy and then rotate the original image.
        copied_image = self.image.copy()
        self.image = pygame.transform.rotozoom(copied_image,
                                               -angle * 180 / math.pi, 1)
        self.image.set_colorkey((0, 0, 0))

        # Create a new rect with the center of the sprite.
        self.rect = self.image.get_rect()
        self.rect.center = (position.x, position.y)
        self.width = self.rect.width
        self.height = self.rect.height

        # Update the polygon points for collisions
        self.polygon_points = [
            Point.triangle_unit_circle_relative(angle, self.pos, polygon_point)
            for polygon_point in self.polygon_points
        ]
Example #3
0
    def _determine_new_ray_points(self, wall_edges):
        """
        Algorithm which calculates new possible ray points based on the current Agent Position and Agent Direction

        Parameters
        ----------
            None

        Returns
        -------
            ray_points : list of Point
                list of new, standard Ray Points
        """

        ray_points = []
        dir_t = self.direction - 2*math.pi if self.direction > math.pi else self.direction
        point_angle_0 = self.pos + Point((self.vision_radius, 0))
        angles = np.linspace(dir_t - self.vision_rad / 2, dir_t + self.vision_rad / 2, num=11,
                             endpoint=True)  # counter-clockwise
        angles_min, angles_max = min(angles), max(angles)

        for p in [pnt for wall_edge in wall_edges for pnt in wall_edge]:
            delta = p - self.pos
            theta_radians = math.atan2(delta.y, delta.x)

            # 3rd quartet (2nd in math, 3rd in Y being from top to bottom), fixes 2nd; from [-PI; PI] to [-2PI; 0]
            if angles_min < -math.pi and theta_radians > 0:
                theta_radians = theta_radians - 2*math.pi
            # 2nd quartet (3rd in math, 2nd in Y being from top to bottom), fixes 3rd; from [-PI; PI] to [0; 2PI]
            elif angles_max > math.pi and theta_radians < 0:
                theta_radians = theta_radians + 2*math.pi

            if theta_radians not in angles and theta_radians > angles_min and theta_radians < angles_max:
                angles = np.append(angles, theta_radians)
        angles = np.sort(angles)

        for angle in angles:
            ray_point = Point.triangle_unit_circle_relative(
                angle, self.pos, point_angle_0)
            ray_points.append(ray_point)

        return ray_points
Example #4
0
    def update_vision(self, local_env):
        """
        Updates Agent Vision

        Parameters
        ----------
            local_env : dict
                contains Player Local Environment

        Returns
        -------
            None
        """

        new_point = Point.triangle_unit_circle(
            self.direction, side_size=self.vision_radius)
        self.vision_top = self.pos + new_point

        wall_edges = self.reduce_wall_edges(local_env['walls'])
        self.ray_points = self._determine_new_ray_points(
            wall_edges)

        # without center
        self.ray_points = self._find_intersections(wall_edges)[1:]

        # creates triangles
        self.ray_objects = [[self.pos, self.ray_points[i], self.ray_points[i + 1]]
                            for i in range(len(self.ray_points) - 1) if self.ray_points[i] != self.ray_points[i + 1]]

        # adds Agent Rectangle to Agent Ray Objects
        speed_dist_w = self.speed + 1 + self.width / 2
        speed_dist_h = self.speed + 1 + self.height / 2
        self.ray_objects.append([
            Point((self.pos.x - speed_dist_w, self.pos.y - speed_dist_h)),
            Point((self.pos.x + speed_dist_w, self.pos.y - speed_dist_h)),
            Point((self.pos.x + speed_dist_w, self.pos.y + speed_dist_h)),
            Point((self.pos.x - speed_dist_w, self.pos.y + speed_dist_h)),
        ])
Example #5
0
    def _perform_agent_action(self, agent, action, local_env):
        if action == 0:
            '''
            agent.image_index = 0
            agent.image = agent.images[agent.image_index]
            '''
            return self._calc_action_reward(agent, action)
        elif action in [1, 2]:
            # (1 - 1.5) * 2 = -1, so for Forward it needs to be * (-1)
            x = math.cos(agent.direction) * agent.speed * \
                (action - 1.5) * 2 * (-1)
            # (1 - 1.5) * 2 = -1, so for Forward it needs to be * (-1)
            y = math.sin(agent.direction) * agent.speed * \
                (action - 1.5) * 2 * (-1)
            old_pos = copy.deepcopy(agent.pos)
            new_pos = agent.pos + Point((x, y))

            self._move_agent(agent, new_pos)

            # outside game window
            if (
                new_pos.x < 0
                or new_pos.x > self.width
                or new_pos.y < 0
                or new_pos.y > self.height
            ):
                self._move_agent(agent, old_pos)
                return self._calc_action_reward(agent, action, success=False)

            for wall in local_env['walls']:
                if Collision.aabb(new_pos, (agent.width, agent.height), wall.pos, (wall.width, wall.height)):
                    if Collision.sat(agent.get_abs_vertices(), wall.get_abs_vertices()):
                        self._move_agent(agent, old_pos)
                        return self._calc_action_reward(agent, action, success=False)
            return self._calc_action_reward(agent, action)
        elif action in [3, 4]:
            # (3 - 3.5) * 2 = -1, so for Clockwise Rotate it needs to be * (-1)
            did_rotate = self._rotate_agent(agent, (action - 3.5) * 2 * (-1))
            return self._calc_action_reward(agent, action, success=did_rotate)
        elif action == 5:
            if isinstance(agent, Seeker):
                did_remove = self._remove_wall()
                return self._calc_action_reward(agent, action, success=did_remove)
            else:
                did_add = self._add_wall()
                return self._calc_action_reward(agent, action, success=did_add)

        raise Exception(
            f"Unknown action, available action space: {self.action_space}")
Example #6
0
    def get_abs_vertices(self):
        """
        Returns absolute coordinates of Vertices in Polygon

        Parameters
        ----------
            None

        Returns
        -------
            points : list of hidenseek.ext.supportive.Point
                self.pylogon_points mapped to the absolute coordinates system
        """

        return [Point((polygon_point.x + self.rect.left, polygon_point.y + self.rect.top)) for polygon_point in self.polygon_points]
Example #7
0
    def _find_intersections(self, wall_edges):
        """
        Algorithm which looks for new Ray Points, which are closer to the Agent Center than radius-distance Ray Points

        Parameters
        ----------
            wall_edges : list of [Point, Point]
                list of Wall Edges in Agent Local Environment

        Returns
        -------
            temp_ray_points : list of Point
                list of new Ray Points
        """

        edges_bounding_boxes = [
            {
                'center': (edge[0] + edge[1]) / 2,
                'size': (abs(edge[1].x - edge[0].x), abs(edge[1].y - edge[0].y))
            } for edge in wall_edges
        ]

        temp_ray_points = [Point(self.rect.center)]
        for vertex in self.ray_points:
            # first must be the center point
            line_segment = [self.pos.round(4), vertex.round(4)]
            new_point = copy.deepcopy(vertex.round(4))
            new_point_dist = self.pos.distance(new_point)
            bounding_box = {
                'center': (line_segment[0] + line_segment[1]) / 2,
                'size': (abs(line_segment[1].x - line_segment[0].x), abs(line_segment[1].y - line_segment[0].y))
            }
            for edge, edge_bounding_box in zip(wall_edges, edges_bounding_boxes):
                if not Collision.aabb(bounding_box['center'], bounding_box['size'], edge_bounding_box['center'], edge_bounding_box['size']):
                    continue
                p = Collision.line_intersection(line_segment, edge)
                if p and self.pos.distance(p) <= new_point_dist:
                    new_point = p
                    new_point_dist = self.pos.distance(p)
            temp_ray_points.append(new_point)
        return temp_ray_points
Example #8
0
    def __init__(self, owner, x, y, size, img_path, direction=0):
        """
        Constructs all neccesary attributes for the Wall Object

        Parameters
        ----------
            owner : None, hidenseek.objects.controllable.Hiding, hidenseek.objects.controllable.Seeker
                Wall owner, None for game environment
            x : float
                center of the rectangle in 'x' axis for absolute coordinate system (game screen)
            y : float
                center of the rectangle in 'y' axis for absolute coordinate system (game screen)
            size : tuple
                Wall size, at least 2x2
        """

        super().__init__()

        self.owner = owner

        self.width = size[0]
        self.height = size[1]

        self.pos = Point((x, y))
        self.pos_init = Point((x, y))

        image = pygame.Surface((self.width, self.height))
        image.fill((0, 0, 0, 0))
        image.set_colorkey((0, 0, 0))

        self.image = image

        self.filling = [
            pygame.image.load(os.path.join(img_path, file_))
            for file_ in os.listdir(img_path)
        ]

        self.rect = self.image.get_rect()
        self.rect.center = (self.pos.x, self.pos.y)

        filling_width = self.filling[0].get_width()
        filling_height = self.filling[0].get_height()

        img_full_size_w = self.width / filling_width
        img_rounded_size_w = math.ceil(img_full_size_w)
        img_full_size_h = self.height / filling_height
        img_rounded_size_h = math.ceil(img_full_size_h)

        blit_list = [(self.filling[0], (filling_width * i, j * filling_height))
                     for i in range(0, img_rounded_size_w)
                     for j in range(0, img_rounded_size_h)]
        image.blits(blit_list)

        self.polygon_points = [
            Point((self.rect.left, self.rect.top)),
            Point((self.rect.right, self.rect.top)),
            Point((self.rect.right, self.rect.bottom)),
            Point((self.rect.left, self.rect.bottom))
        ]

        self.direction = direction
Example #9
0
    def __init__(self, cfg, size, pos_ratio, SCREEN_WIDTH, SCREEN_HEIGHT):
        """
        Constructs all neccesary attributes for the Player Object

        Parameters
        ----------
            cfg : configparser Object
                Agent Config Object
            size : tuple
                Agent size
            pos_ratio : tuple
                used to calculate initial position of the Player in absolute coordinate system (game screen);
                if value < 1 then it's ratio in percentage, otherwise it's coord
            SCREEN_WIDTH : int
                width of the game window
            SCREEN_HEIGHT : int
                height of the game window
        """

        super().__init__()
        self.width = size[0]
        self.height = size[1]

        tmp_pos = list(pos_ratio)
        if tmp_pos[0] < 1:
            tmp_pos[0] *= SCREEN_WIDTH

        if tmp_pos[1] < 1:
            tmp_pos[1] *= SCREEN_HEIGHT

        self.pos = Point(tmp_pos)
        self.pos_init = Point(tmp_pos)

        self.SCREEN_WIDTH = SCREEN_WIDTH
        self.SCREEN_HEIGHT = SCREEN_HEIGHT

        self.cfg = cfg
        self.speed = cfg['speed_ratio']
        self.speed_rotate = cfg['speed_rotate_ratio']
        self.wall_timer_init = cfg['wall_action_timeout']
        self.wall_timer = cfg['wall_action_timeout']

        self.vision_radius = self.width * 2
        self.vision_top = None
        self.ray_objects = None
        self.vision_rad = math.pi
        self.ray_points = []
        self.direction = 0  # radians from which vision_rad is added/substracted

        """
        OCTAGON
            > 0.15 on every side because we need to cover only 0.7 of space, to be able to freely rotate without making bigger rectangle
            > 2*x / sqrt(2) + x = 0.7
            > x ~= 0.29
            > x / sqrt(2) ~= 0.205

            x/sqrt(2)   x     x/sqrt(2)
            ........---------........
            ....../           \......
            ...../             \.....
            ..../               \....
            .../                 \...
            ..|                   |..
            ..|                   |..
            ..|                   |..
            ..|                   |..
            ..|                   |..
            ...\                 /...
            ....\               /....
            .....\             /.....
            ......\           /......
            ........---------........

        """
        self.polygon_points = [
            Point((self.width * .355, self.height * .15)),
            Point((self.width * .645, self.height * .15)),
            Point((self.width * .85, self.height * .355)),
            Point((self.width * .85, self.height * .645)),
            Point((self.width * .645, self.height * .85)),
            Point((self.width * .355, self.height * .85)),
            Point((self.width * .15, self.height * .645)),
            Point((self.width * .15, self.height * .355)),
        ]

        self.sprites = [pygame.image.load(os.path.join(
            cfg['graphics_path'], file_)) for file_ in os.listdir(cfg['graphics_path'])]

        surface = pygame.Surface((self.width, self.height))
        surface.set_colorkey((0, 0, 0))

        self.image_index = 0
        self.image = surface
        self.rect = self.image.get_rect()
        self.rect.center = (self.pos.x, self.pos.y)
Example #10
0
class Player(pygame.sprite.Sprite):
    """
    Parent Player Class for Hide'n'Seek Game, inherits from pygame.sprite.Sprite.
    Shouldn't be used because it doesn't have implementation of few methods

    Attributes
    ----------
        width : int
            width of the Player Rectangle
        height : int
            height of the Player Rectangle
        SCREEN_WIDTH : int
            width of the game window
        SCREEN_HEIGHT : int
            height of the game window
        pos : hidenseek.ext.supportive.Point
            object position on the game display
        speed : int
            speed ratio for Player movement (in ticks)
        speed_rotate : float
            speed ratio for Player rotate
        wall_timer_init : int
            init cooldown (in frames) for any wall-specific action
        wall_timer : int
            cooldown (in frames)for any wall-specific action
        vision_radius : float
            Player POV radius
        vision_rad : float
            Player POV angle
        vision_top : Point
            Player Top POV Point
        ray_points : list of Point
            Player Ray POV in Points representation
        ray_objects : list of Objects (3-el list of Point)
            Player Ray POV in Triangles representation
        direction : float
            POV angle in radians (Z = 2 * PI)
        image_index : int
            determines which image should be drawn
        images : list of pygame.Surface
            objects with sprite/images from which the proper one will be drawn
        image : pygame.Surface
            object with sprite/image, chosen by 'image_index'
        rect : pygame.Rect
            object Rectangle, to be drawn
        polygon_points : list of tuples
            Agent vertices, used for collision check in SAT
        actions : list of dict
            contains all possible Player actions

    Methods
    -------
        _rotate(turn, local_env):
            rotates the object, accordingly to the value, along its axis
        get_abs_vertices():
            returns absolute vertices coordinates (in game screen coordinates system)
        _move_action(new_pos):
            algorithm which moves the Player object to given poisition
        update_vision(local_env):
            updates Agent POV
        update(local_env):
            Not implemented in Parent Class
    """

    def __init__(self, cfg, size, pos_ratio, SCREEN_WIDTH, SCREEN_HEIGHT):
        """
        Constructs all neccesary attributes for the Player Object

        Parameters
        ----------
            cfg : configparser Object
                Agent Config Object
            size : tuple
                Agent size
            pos_ratio : tuple
                used to calculate initial position of the Player in absolute coordinate system (game screen);
                if value < 1 then it's ratio in percentage, otherwise it's coord
            SCREEN_WIDTH : int
                width of the game window
            SCREEN_HEIGHT : int
                height of the game window
        """

        super().__init__()
        self.width = size[0]
        self.height = size[1]

        tmp_pos = list(pos_ratio)
        if tmp_pos[0] < 1:
            tmp_pos[0] *= SCREEN_WIDTH

        if tmp_pos[1] < 1:
            tmp_pos[1] *= SCREEN_HEIGHT

        self.pos = Point(tmp_pos)
        self.pos_init = Point(tmp_pos)

        self.SCREEN_WIDTH = SCREEN_WIDTH
        self.SCREEN_HEIGHT = SCREEN_HEIGHT

        self.cfg = cfg
        self.speed = cfg['speed_ratio']
        self.speed_rotate = cfg['speed_rotate_ratio']
        self.wall_timer_init = cfg['wall_action_timeout']
        self.wall_timer = cfg['wall_action_timeout']

        self.vision_radius = self.width * 2
        self.vision_top = None
        self.ray_objects = None
        self.vision_rad = math.pi
        self.ray_points = []
        self.direction = 0  # radians from which vision_rad is added/substracted

        """
        OCTAGON
            > 0.15 on every side because we need to cover only 0.7 of space, to be able to freely rotate without making bigger rectangle
            > 2*x / sqrt(2) + x = 0.7
            > x ~= 0.29
            > x / sqrt(2) ~= 0.205

            x/sqrt(2)   x     x/sqrt(2)
            ........---------........
            ....../           \......
            ...../             \.....
            ..../               \....
            .../                 \...
            ..|                   |..
            ..|                   |..
            ..|                   |..
            ..|                   |..
            ..|                   |..
            ...\                 /...
            ....\               /....
            .....\             /.....
            ......\           /......
            ........---------........

        """
        self.polygon_points = [
            Point((self.width * .355, self.height * .15)),
            Point((self.width * .645, self.height * .15)),
            Point((self.width * .85, self.height * .355)),
            Point((self.width * .85, self.height * .645)),
            Point((self.width * .645, self.height * .85)),
            Point((self.width * .355, self.height * .85)),
            Point((self.width * .15, self.height * .645)),
            Point((self.width * .15, self.height * .355)),
        ]

        self.sprites = [pygame.image.load(os.path.join(
            cfg['graphics_path'], file_)) for file_ in os.listdir(cfg['graphics_path'])]

        surface = pygame.Surface((self.width, self.height))
        surface.set_colorkey((0, 0, 0))

        self.image_index = 0
        self.image = surface
        self.rect = self.image.get_rect()
        self.rect.center = (self.pos.x, self.pos.y)

    def act(self, obs, reward, game_end, action_space):
        """
        Decides on the action

        Parameters
        ----------
            obs : PLACEHOLDER
                What agent sees.
            reward : float
                Reward for previous action.
            game_end : boolean
                contains Player Local Environment
            action_space : spaces.Discrete
                actions possible to perform.

        Returns
        -------
            None
        """
        action = action_space.sample()
        return action

    def get_abs_vertices(self):
        """
        Returns absolute coordinates of Vertices in Polygon

        Parameters
        ----------
            None

        Returns
        -------
            points : list of hidenseek.ext.supportive.Point
                self.pylogon_points mapped to the absolute coordinates system
        """

        return [Point((polygon_point.x + self.rect.left, polygon_point.y + self.rect.top)) for polygon_point in self.polygon_points]

    def _determine_new_ray_points(self, wall_edges):
        """
        Algorithm which calculates new possible ray points based on the current Agent Position and Agent Direction

        Parameters
        ----------
            None

        Returns
        -------
            ray_points : list of Point
                list of new, standard Ray Points
        """

        ray_points = []
        dir_t = self.direction - 2*math.pi if self.direction > math.pi else self.direction
        point_angle_0 = self.pos + Point((self.vision_radius, 0))
        angles = np.linspace(dir_t - self.vision_rad / 2, dir_t + self.vision_rad / 2, num=11,
                             endpoint=True)  # counter-clockwise
        angles_min, angles_max = min(angles), max(angles)

        for p in [pnt for wall_edge in wall_edges for pnt in wall_edge]:
            delta = p - self.pos
            theta_radians = math.atan2(delta.y, delta.x)

            # 3rd quartet (2nd in math, 3rd in Y being from top to bottom), fixes 2nd; from [-PI; PI] to [-2PI; 0]
            if angles_min < -math.pi and theta_radians > 0:
                theta_radians = theta_radians - 2*math.pi
            # 2nd quartet (3rd in math, 2nd in Y being from top to bottom), fixes 3rd; from [-PI; PI] to [0; 2PI]
            elif angles_max > math.pi and theta_radians < 0:
                theta_radians = theta_radians + 2*math.pi

            if theta_radians not in angles and theta_radians > angles_min and theta_radians < angles_max:
                angles = np.append(angles, theta_radians)
        angles = np.sort(angles)

        for angle in angles:
            ray_point = Point.triangle_unit_circle_relative(
                angle, self.pos, point_angle_0)
            ray_points.append(ray_point)

        return ray_points

    def reduce_wall_edges(self, walls):
        """
        Algorithm which reduces wall edges from 4 per Wall to only 2 (closest ones)

        Parameters
        ----------
            walls : list of Wall
                list of Walls in Agent Local Environment

        Returns
        -------
            proper_walls_lines : list of [Point, Point]
                list of closest wall edges
        """

        walls_lines = [[[wall.get_abs_vertices()[i % 4], wall.get_abs_vertices()[
            (i + 1) % 4]] for i in range(4)] for wall in walls]

        # get only closer parallel wall edge, reduces computation by half
        proper_walls_lines = []
        for wall_lines in walls_lines:
            if self.pos.distance(wall_lines[0][0] + (wall_lines[0][1] - wall_lines[0][0]) / 2) < self.pos.distance(wall_lines[2][0] + (wall_lines[2][1] - wall_lines[2][0]) / 2):
                proper_walls_lines.append(wall_lines[0])
            else:
                proper_walls_lines.append(wall_lines[2])

            if self.pos.distance(wall_lines[1][0] + (wall_lines[1][1] - wall_lines[1][0]) / 2) < self.pos.distance(wall_lines[3][0] + (wall_lines[3][1] - wall_lines[3][0]) / 2):
                proper_walls_lines.append(wall_lines[1])
            else:
                proper_walls_lines.append(wall_lines[3])
        return proper_walls_lines

    def _find_intersections(self, wall_edges):
        """
        Algorithm which looks for new Ray Points, which are closer to the Agent Center than radius-distance Ray Points

        Parameters
        ----------
            wall_edges : list of [Point, Point]
                list of Wall Edges in Agent Local Environment

        Returns
        -------
            temp_ray_points : list of Point
                list of new Ray Points
        """

        edges_bounding_boxes = [
            {
                'center': (edge[0] + edge[1]) / 2,
                'size': (abs(edge[1].x - edge[0].x), abs(edge[1].y - edge[0].y))
            } for edge in wall_edges
        ]

        temp_ray_points = [Point(self.rect.center)]
        for vertex in self.ray_points:
            # first must be the center point
            line_segment = [self.pos.round(4), vertex.round(4)]
            new_point = copy.deepcopy(vertex.round(4))
            new_point_dist = self.pos.distance(new_point)
            bounding_box = {
                'center': (line_segment[0] + line_segment[1]) / 2,
                'size': (abs(line_segment[1].x - line_segment[0].x), abs(line_segment[1].y - line_segment[0].y))
            }
            for edge, edge_bounding_box in zip(wall_edges, edges_bounding_boxes):
                if not Collision.aabb(bounding_box['center'], bounding_box['size'], edge_bounding_box['center'], edge_bounding_box['size']):
                    continue
                p = Collision.line_intersection(line_segment, edge)
                if p and self.pos.distance(p) <= new_point_dist:
                    new_point = p
                    new_point_dist = self.pos.distance(p)
            temp_ray_points.append(new_point)
        return temp_ray_points

    def update_vision(self, local_env):
        """
        Updates Agent Vision

        Parameters
        ----------
            local_env : dict
                contains Player Local Environment

        Returns
        -------
            None
        """

        new_point = Point.triangle_unit_circle(
            self.direction, side_size=self.vision_radius)
        self.vision_top = self.pos + new_point

        wall_edges = self.reduce_wall_edges(local_env['walls'])
        self.ray_points = self._determine_new_ray_points(
            wall_edges)

        # without center
        self.ray_points = self._find_intersections(wall_edges)[1:]

        # creates triangles
        self.ray_objects = [[self.pos, self.ray_points[i], self.ray_points[i + 1]]
                            for i in range(len(self.ray_points) - 1) if self.ray_points[i] != self.ray_points[i + 1]]

        # adds Agent Rectangle to Agent Ray Objects
        speed_dist_w = self.speed + 1 + self.width / 2
        speed_dist_h = self.speed + 1 + self.height / 2
        self.ray_objects.append([
            Point((self.pos.x - speed_dist_w, self.pos.y - speed_dist_h)),
            Point((self.pos.x + speed_dist_w, self.pos.y - speed_dist_h)),
            Point((self.pos.x + speed_dist_w, self.pos.y + speed_dist_h)),
            Point((self.pos.x - speed_dist_w, self.pos.y + speed_dist_h)),
        ])

    def reset(self):
        self.pos = copy.deepcopy(self.pos_init)
        self.wall_timer = copy.deepcopy(self.wall_timer_init)
        self.vision_top = None
        self.ray_objects = None
        self.direction = 0

        surface = pygame.Surface((self.width, self.height))
        surface.set_colorkey((0, 0, 0))

        self.image_index = 0
        self.image = surface
        self.rect = self.image.get_rect()
        self.rect.center = (self.pos.x, self.pos.y)