コード例 #1
0
ファイル: car.py プロジェクト: wojtekkw10/MLCars2D
class Car:
    def __init__(self, x, y, screen):
        super().__init__()

        self.image = pygame.image.load('resources/images/car.png')
        self.car_width = self.image.get_width()
        self.car_height = self.image.get_height()
        self.rect = self.image.get_rect()
        self.screen = screen
        self.position_x = x
        self.position_y = y
        self.angle = Angle(0)  # 0 is E, 90 is N, 180 is W, 270 is S
        self.car_points = {}
        self.speed = constants.CAR_SPEED
        self.turn = 0.0  # axis [-1.0,1.0]
        self.sensors = Sensors(pygame.Vector2(x, y))
        self.neural_network = CarNeuralNetwork(1, [6, 4, 1])
        self.distance_traveled = 0
        self.collision_happened = False

        self.distance_counter = 0
        self.should_check_collisions = True

    def handle_neural_network(self):
        nn_input = self.get_nn_input_from_sensors()
        output = self.neural_network.calc(nn_input)
        self.update_turn_based_on_nn(output)

    def get_nn_input_from_sensors(self):
        sensor_distances = self.sensors.get_states_of_sensors()
        sensor_distances = scale_to_bounds(0, 100, sensor_distances)
        sensor_distances.append(1.0)
        sensor_distances = [3.8 - value for value in sensor_distances]
        return sensor_distances

    def update_turn_based_on_nn(self, nn_output):
        threshold = 0.7
        if nn_output < threshold:
            self.turn = 2 * nn_output
        else:
            self.turn = 2 * (-nn_output + threshold)

    def fixed_update(self):
        self.angle.degree -= constants.DIVIDER_ANGLE * self.turn

        nx = cos(self.angle.radians) * self.speed
        ny = sin(self.angle.radians) * self.speed

        self.position_x += nx
        self.position_y += ny * -1
        self.rect[0] = self.position_x
        self.rect[1] = self.position_y

        dst = sqrt(nx**2 + ny**2)
        self.distance_traveled += dst
        self.distance_counter += dst
        if self.distance_counter > constants.DISTANCE_TO_TRIGGER_CHECKS:
            self.distance_counter = 0
            self.should_check_collisions = True

    def update(self, camera):
        if self.collision_happened:
            return

        self.fixed_update()

        (x, y, w, h) = camera.apply(self)
        rotated = pygame.transform.rotate(self.image, self.angle.degree)
        rect = rotated.get_rect().move(pygame.Vector2(x - w / 2, y - h / 2))
        self.car_center_x, self.car_center_y = rect.center
        self.rotate_car_points()

        if self.should_check_collisions:
            self.sensors.setup_sensors(
                self.angle, pygame.Vector2(self.car_center_x,
                                           self.car_center_y))

    def draw(self, surface, camera, other_cars):
        if len(other_cars) < 15:
            if self.check_if_drawn(other_cars):
                return False

        rotated = pygame.transform.rotate(self.image, self.angle.degree)
        image_dest = pygame.Vector2(self.position_x - self.car_width / 2,
                                    self.position_y - self.car_height / 2)
        self.screen.blit(rotated, image_dest)

        self.sensors.draw_sensors(surface, camera)
        return True

    def check_if_drawn(self, cars):
        for car in cars:
            if floor(car.position_x) == floor(self.position_x) and floor(
                    car.position_y) == floor(self.position_y):
                return True

        return False

    def rotate_car_points(self):

        rotation_angle = constants.MAX_RADIANS - self.angle.radians

        (flx, fly) = self.car_center_x + self.car_width / \
            2.0, self.car_center_y - self.car_height / 2.0
        self.car_points['front_left'] = self.get_rotated_point(
            flx, fly, rotation_angle)

        (frx, fry) = self.car_center_x + self.car_width / \
            2.0, self.car_center_y + self.car_height / 2.0
        self.car_points['front_right'] = self.get_rotated_point(
            frx, fry, rotation_angle)

        (rlx, rly) = self.car_center_x - self.car_width / \
            2.0, self.car_center_y - self.car_height / 2.0
        self.car_points['rear_left'] = self.get_rotated_point(
            rlx, rly, rotation_angle)

        (rrx, rry) = self.car_center_x - self.car_width / \
            2.0, self.car_center_y + self.car_height / 2.0
        self.car_points['rear_right'] = self.get_rotated_point(
            rrx, rry, rotation_angle)

    def get_rotated_point(self, x, y, angle):
        rx = int(self.car_center_x + (x - self.car_center_x) * cos(angle) -
                 (y - self.car_center_y) * sin(angle))
        ry = int(self.car_center_y + (x - self.car_center_x) * sin(angle) +
                 (y - self.car_center_y) * cos(angle))
        return rx, ry

    def detect_collision(self, sectors):
        if not self.collision_happened and self.should_check_collisions:
            self.sensors.check_collision(sectors)

        if self.should_check_collisions:
            self.should_check_collisions = False

            end_points = [
                (self.car_points['front_left'], self.car_points['rear_left']),
                (self.car_points['front_left'],
                 self.car_points['front_right']),
                (self.car_points['front_right'],
                 self.car_points['rear_right']),
                (self.car_points['rear_left'], self.car_points['rear_right'])
            ]

            for end_point in end_points:
                for point in get_line_points(end_point[0], end_point[1]):
                    if self.is_within_bounds(point):
                        return True

                    (x, y) = point
                    x_sector = x // constants.X_SECTOR_SIZE
                    y_sector = y // constants.Y_SECTOR_SIZE
                    if (x, y) in sectors[y_sector][x_sector]:
                        self.collision_happened = True
                        return True

        return False

    def is_within_bounds(self, point):
        is_within_bounds = False
        (x, y) = point
        if y >= self.screen.get_height() - 2:
            self.position_y -= 2
            is_within_bounds = True
        if y <= 2:
            self.position_y += 2
            is_within_bounds = True
        if x >= self.screen.get_width() - 2:
            self.position_x -= 2
            is_within_bounds = True
        if x <= 2:
            self.position_x += 2
            is_within_bounds = True

        if is_within_bounds:
            self.speed = 0
            self.distance_traveled -= 2

        return is_within_bounds