Exemple #1
0
class Refuel(Maneuver):
    def __init__(self, car: Car, info: GameInfo, target: vec3):
        super().__init__(car)
        self.info = info

        pos = (target + car.pos + info.my_goal.center * 2) / 4
        self.pad = self.nearest_boostpad(car, info, pos)
        self.travel = Travel(car, self.pad.pos, waste_boost=True)

    @staticmethod
    def nearest_boostpad(car: Car, info: GameInfo, pos: vec3):
        best_pad = None
        best_dist = 9999
        for pad in info.boost_pads:
            dist = distance(pos, pad.pos)
            if (pad.is_active or pad.timer < estimate_time(
                    car, pad.pos, estimate_max_car_speed(car))
                ) and dist < best_dist:
                best_pad = pad
                best_dist = dist
        return best_pad

    def step(self, dt):
        if norm(self.car.vel) > 1400:
            if distance(self.car, self.pad) < norm(self.car.vel) * 0.3:
                self.travel.action.target_speed = 1000
        self.travel.step(dt)
        self.controls = self.travel.controls
        self.finished = (not self.pad.is_active \
        and not self.pad.timer < estimate_time(self.car, self.pad.pos, estimate_max_car_speed(self.car))) \
        or self.car.boost > 99

    def render(self, draw: DrawingTool):
        self.travel.render(draw)
Exemple #2
0
    def __init__(self, car: Car, info: GameInfo, face_target: vec3,
                 distance_from_target: float):
        super().__init__(car)

        self.info = info
        self.face_target = face_target

        dist = min(distance_from_target,
                   ground_distance(face_target, self.info.my_goal.center) - 50)
        target_pos = ground(face_target) + ground_direction(
            face_target, self.info.my_goal.center) * dist

        near_goal = ground_distance(car, info.my_goal.center) < 3000
        side_shift = 400 if near_goal else 2000
        points = [
            target_pos + vec3(side_shift, 0, 0),
            target_pos - vec3(side_shift, 0, 0)
        ]
        target_pos = nearest_point(face_target,
                                   points) if near_goal else furthest_point(
                                       face_target, points)

        self.target = Arena.clamp(target_pos, 500)

        self.travel = Travel(car, self.target)
        self.travel.finish_distance = 800 if near_goal else 1500
        self.drive = Drive(car)

        self.start_time = car.time
        self.wait = Stop(car)
Exemple #3
0
    def __init__(self, car: Car, info: GameInfo, target: vec3):
        super().__init__(car)
        self.info = info

        pos = (target + car.pos + info.my_goal.center * 2) / 4
        self.pad = self.nearest_boostpad(car, info, pos)
        self.travel = Travel(car, self.pad.pos, waste_boost=True)
Exemple #4
0
class Arrive(Maneuver):
    def __init__(self,
                 car: Car,
                 target=vec3(0, 0, 0),
                 time=0,
                 target_direction: vec3 = None):
        super().__init__(car)

        self.target_direction = target_direction
        self.target = target
        self.time = time
        self.drive = Drive(car, target)
        self.travel = Travel(car)
        self.lerp_t = 0.6

    def step(self, dt):
        target = self.target
        car = self.car

        if self.target_direction is not None:
            car_vel = norm(car.vel)
            target_direction = normalize(self.target_direction)
            shift = clamp(
                distance(car.pos, target) * self.lerp_t, 0, car_vel * 1.5)
            if shift < turn_radius(clamp(car_vel, 1400, 2000) * 1.1):
                shift = 0
            translated_target = target - target_direction * shift

            translated_time = self.time - distance(
                translated_target, target) / max(1, clamp(car_vel, 500, 2300))
        else:
            translated_target = target
            translated_time = self.time

        self.drive.target_pos = translated_target
        dist_to_target = distance(car.pos, translated_target)
        target_speed = clamp(
            dist_to_target / max(0.001, translated_time - car.time), 0, 2300)

        self.drive.target_speed = target_speed

        if car.boost < 1 and dist_to_target > 3000:
            self.travel.target = target
            self.travel.step(dt)
            self.controls = self.travel.controls
        else:
            self.drive.step(dt)
            self.controls = self.drive.controls

        self.finished = self.car.time >= self.time
        return self.finished

    def render(self, draw: DrawingTool):
        self.drive.render(draw)

        if self.target_direction is not None:
            draw.color(draw.lime)
            draw.triangle(self.target - self.target_direction * 250,
                          self.target_direction)
Exemple #5
0
    def __init__(self, car: Car):
        super().__init__(car)

        self.target_direction: vec3 = None
        self.target: vec3 = None
        self.time: float = 0
        self.drive = Drive(car)
        self.travel = Travel(car)
        self.lerp_t = 0.6
        self.allow_dodges_and_wavedashes: bool = True
        self.additional_shift = 0
Exemple #6
0
class ShadowDefense(Maneuver):

    DURATION = 0.5

    def __init__(self, car: Car, info: GameInfo, face_target: vec3, distance_from_target: float):
        super().__init__(car)

        self.info = info
        self.face_target = face_target

        dist = min(distance_from_target, ground_distance(face_target, self.info.my_goal.center) - 50)
        target_pos = ground(face_target) + ground_direction(face_target, self.info.my_goal.center) * dist

        near_goal = ground_distance(car, info.my_goal.center) < 3000
        side_shift = 400 if near_goal else 2000
        points = [target_pos + vec3(side_shift, 0, 0), target_pos - vec3(side_shift, 0, 0)]
        target_pos = nearest_point(face_target, points) if near_goal else farthest_point(face_target, points)
        target_pos = Arena.clamp(target_pos, 500)

        self.travel = Travel(car, target_pos)
        self.travel.finish_distance = 800 if near_goal else 1500
        self.drive = Drive(car)
        self.drive.target_speed = 1000
        self.stop = Stop(car)

        self.start_time = car.time

    def interruptible(self) -> bool:
        return self.travel.interruptible()

    def step(self, dt):
        self.travel.step(dt)
        self.controls = self.travel.controls

        if ground_distance(self.car, self.travel.target) < 3000:
            self.controls.boost = False

        if self.travel.finished:
            if angle_to(self.car, self.face_target) > 0.3:
                self.drive.target_pos = self.face_target
                self.drive.step(dt)
                self.controls = self.drive.controls
                self.controls.handbrake = False
            else:
                self.stop.step(dt)
                self.controls = self.stop.controls

        self.controls.boost = False

        self.finished = self.travel.driving and self.car.time > self.start_time + self.DURATION

    def render(self, draw: DrawingTool):
        self.travel.render(draw)
Exemple #7
0
class ShadowDefense(Maneuver):

    def __init__(self, car: Car, info: GameInfo, target: vec3, distance_from_target: float):
        super().__init__(car)

        self.info = info
        self.target = target

        ball = info.ball
        dist = min(distance_from_target, ground_distance(target, self.info.my_goal.center) - 50)
        target_pos = ground(target) + ground_direction(target, self.info.my_goal.center) * dist

        side_shift = distance_from_target / 4 if ground_distance(car, info.my_goal.center) > 2500 else 400
        points = [target_pos + vec3(side_shift, 0, 0), target_pos - vec3(side_shift, 0, 0)]
        target_pos = nearest_point(car.pos, points)

        self.target = Arena.clamp(target_pos, 700)

        self.travel = Travel(car, self.target)
        self.drive = Drive(car)

        self.start_time = car.time
        self.wait = Stop(car)

    def step(self, dt):
        ball = self.info.ball

        if (
            distance(self.car, ball) < 1000
            and align(self.car.pos, ball, self.info.my_goal.center) > 0.2
        ):
            shift = normalize(cross(direction(ball, self.car), vec3(0, 0, 1))) * 1000
            self.travel.target = nearest_point(self.car.pos, [ball.pos + shift, ball.pos - shift])
        else:
            self.travel.target = self.target

        self.travel.step(dt)
        self.controls = self.travel.controls
        if self.travel.finished:
            if angle_to(self.car, self.target) > 0.2 and norm(self.car.vel) < 600:
                self.drive.target_pos = self.target
                self.drive.step(dt)
                self.drive.target_speed = 500
                self.drive.controls.handbrake = False
                self.controls = self.drive.controls
            else:
                self.wait.step(dt)
                self.controls = self.wait.controls
        self.finished = self.travel._driving and self.car.time > self.start_time + 0.5

    def render(self, draw: DrawingTool):
        self.travel.render(draw)
Exemple #8
0
    def __init__(self,
                 car: Car,
                 target=vec3(0, 0, 0),
                 time=0,
                 target_direction: vec3 = None):
        super().__init__(car)

        self.target_direction = target_direction
        self.target = target
        self.time = time
        self.drive = Drive(car, target)
        self.travel = Travel(car)
        self.lerp_t = 0.6
Exemple #9
0
    def __init__(self,
                 car: Car,
                 info: GameInfo,
                 target: vec3,
                 forbidden_pads: Set[Pad] = set()):
        super().__init__(car)
        self.info = info

        pos = (target + car.position * 2 + info.my_goal.center * 2) / 5
        pads = set(info.large_boost_pads) - forbidden_pads
        self.pad = self.best_boostpad_to_pickup(car, pads, pos)
        self.pad_was_active = self.pad and self.pad.is_active

        self.travel = Travel(
            car,
            self.pad.position if self.pad else info.my_goal.center,
            waste_boost=True)
Exemple #10
0
    def __init__(self, car: Car, info: GameInfo, target: vec3, distance_from_target: float):
        super().__init__(car)

        self.info = info
        self.target = target

        ball = info.ball
        dist = min(distance_from_target, ground_distance(target, self.info.my_goal.center) - 50)
        target_pos = ground(target) + ground_direction(target, self.info.my_goal.center) * dist

        side_shift = distance_from_target / 4 if ground_distance(car, info.my_goal.center) > 2500 else 400
        points = [target_pos + vec3(side_shift, 0, 0), target_pos - vec3(side_shift, 0, 0)]
        target_pos = nearest_point(car.pos, points)

        self.target = Arena.clamp(target_pos, 700)

        self.travel = Travel(car, self.target)
        self.drive = Drive(car)

        self.start_time = car.time
        self.wait = Stop(car)
Exemple #11
0
    def __init__(self,
                 car: Car,
                 info: GameInfo,
                 face_target: vec3,
                 distance_from_target: float,
                 force_nearest=False):
        super().__init__(car)

        self.info = info
        self.face_target = face_target

        dist = min(distance_from_target,
                   ground_distance(face_target, self.info.my_goal.center) - 50)
        target_pos = ground(face_target) + ground_direction(
            face_target, self.info.my_goal.center) * dist

        near_goal = abs(car.position[1] - info.my_goal.center[1]) < 3000
        side_shift = 400 if near_goal else 1800
        points = target_pos + vec3(side_shift, 0, 0), target_pos - vec3(
            side_shift, 0, 0)
        if abs(self.car.position.x) > 3000:
            force_nearest = True
        target_pos = nearest_point(
            face_target,
            points) if near_goal or force_nearest else farthest_point(
                face_target, points)
        if abs(face_target[0]) < 1000 or ground_distance(car,
                                                         face_target) < 1000:
            target_pos = nearest_point(car.position, points)
        target_pos = Arena.clamp(target_pos, 500)

        self.travel = Travel(car, target_pos)
        self.travel.finish_distance = 800 if near_goal else 1500
        self.drive = Drive(car)
        self.stop = Stop(car)

        self.start_time = car.time

        self.pad = None
Exemple #12
0
    def __init__(self, car: Car):
        super().__init__(car)
        self.drive = Drive(car)
        self.travel = Travel(car)
        self.action = self.drive

        self.target_direction: Optional[None] = None
        self.target: vec3 = None
        self.arrival_time: float = 0
        self.backwards: bool = False

        self.lerp_t = 0.57
        self.allow_dodges_and_wavedashes: bool = True
        self.additional_shift = 0
Exemple #13
0
class PickupBoostPad(Maneuver):
    """Pickup a boostpad. Abort when picked up by someone else."""
    def __init__(self, car: Car, pad: BoostPad):
        super().__init__(car)
        self.pad = pad
        self.pad_was_active = self.pad.state == BoostPadState.Available

        self.travel = Travel(car, self.pad.position, waste_boost=True)

    def interruptible(self) -> bool:
        return self.travel.interruptible()

    def step(self, dt):
        # slow down when we're about to pick up the boost, so we can turn faster afterwards
        if distance(self.car, self.pad) < norm(self.car.velocity) * 0.2:
            self.travel.drive.target_speed = 1400

        self.travel.step(dt)
        self.controls = self.travel.controls

        # finish when someone picks up the pad
        if self.pad_was_active and self.pad.state == BoostPadState.Unavailable:
            self.finished = True
        self.pad_was_active = self.pad.state == BoostPadState.Available

        # finish when we picked the boost up but the previous condition somehow wasn't true
        if self.car.boost > 99 or distance(self.car, self.pad) < 100:
            self.finished = True

    def render(self, draw: DrawingTool):
        self.travel.render(draw)

        # draw timer if boost isn't available yet
        if self.pad and not self.pad.state == BoostPadState.Available:
            draw.color(draw.yellow)
            draw.string(self.pad.position + vec3(0, 0, 100),
                        f"spawns in: {self.pad.timer:.1f}s")
Exemple #14
0
class Refuel(Maneuver):
    """
    Choose a large boost pad and go pick it up.
    """
    def __init__(self,
                 car: Car,
                 info: GameInfo,
                 target: vec3,
                 forbidden_pads: Set[Pad] = set()):
        super().__init__(car)
        self.info = info

        pos = (target + car.position * 2 +
               info.my_goal.center * 2) / 5  # TODO: make this better

        pads = set(info.large_boost_pads) - forbidden_pads
        self.pad = self.best_boostpad_to_pickup(car, pads, pos)

        self.pad_was_active = self.pad and self.pad.is_active

        self.travel = Travel(
            car,
            self.pad.position if self.pad else info.my_goal.center,
            waste_boost=True)

    @staticmethod
    def best_boostpad_to_pickup(car: Car, pads: Set[Pad],
                                pos: vec3) -> Optional[Pad]:
        best_pad = None
        best_dist = math.inf

        for pad in pads:
            dist = distance(pos, pad.position)
            time_estimate = estimate_time(car, pad.position,
                                          estimate_max_car_speed(car))

            if dist < best_dist and (pad.is_active
                                     or pad.timer < time_estimate):
                best_pad = pad
                best_dist = dist

        return best_pad

    def interruptible(self) -> bool:
        return self.travel.interruptible()

    def step(self, dt):
        if self.pad is None:
            self.finished = True
            return

        # slow down when we're about to pick up the boost, so we can turn faster afterwards
        if distance(self.car, self.pad) < norm(self.car.velocity) * 0.2:
            self.travel.drive.target_speed = 1400

        self.travel.step(dt)
        self.controls = self.travel.controls

        # finish when someone picks up the pad
        if not self.pad.is_active and self.pad_was_active:
            self.finished = True
        self.pad_was_active = self.pad.is_active

        # finish when we picked the boost up but the previous condition somehow wasn't true
        if self.car.boost > 99 or distance(self.car, self.pad) < 100:
            self.finished = True

    def render(self, draw: DrawingTool):
        self.travel.render(draw)

        if self.pad and not self.pad.is_active:
            draw.color(draw.yellow)
            draw.string(self.pad.position + vec3(0, 0, 100),
                        int(self.pad.timer * 100) / 100)
Exemple #15
0
class Arrive(Maneuver):
    '''
    Arrive at a target position at a certain time (game seconds).
    You can also specify `target_direction`, and it will try to arrive
    at an angle. However this does work well only if the car is already
    roughly facing the specified direction, and only if it's far enough.
    '''
    def __init__(self, car: Car):
        super().__init__(car)

        self.target_direction: vec3 = None
        self.target: vec3 = None
        self.time: float = 0
        self.drive = Drive(car)
        self.travel = Travel(car)
        self.lerp_t = 0.6
        self.allow_dodges_and_wavedashes: bool = True
        self.additional_shift = 0

    def step(self, dt):
        target = self.target
        car = self.car

        if self.target_direction is not None:
            car_vel = norm(car.velocity)
            target_direction = normalize(self.target_direction)
            shift = clamp(
                distance(car.position, target) * self.lerp_t, 0, car_vel * 1.5)
            if shift - self.additional_shift < turn_radius(
                    clamp(car_vel, 1400, 2000) * 1.1):
                shift = 0
            else:
                shift += self.additional_shift
            translated_target = target - target_direction * shift

            translated_time = self.time - distance(
                translated_target, target) * 0.7 / max(
                    1, clamp(car_vel, 500, 2300))
        else:
            translated_target = target
            translated_time = self.time

        self.drive.target_pos = translated_target
        dist_to_target = distance(car.position, translated_target)
        target_speed = clamp(
            dist_to_target / max(0.001, translated_time - car.time), 0, 2300)

        self.drive.target_speed = target_speed

        if (self.allow_dodges_and_wavedashes and car.boost < 5 and
                dist_to_target > clamp(norm(car.velocity) + 600, 1400, 2300)
                and norm(car.velocity) < target_speed - 600
                or not self.travel.driving):
            self.travel.target = target
            self.travel.step(dt)
            self.controls = self.travel.controls
        else:
            self.drive.step(dt)
            self.controls = self.drive.controls

        self.finished = self.car.time >= self.time
        return self.finished

    def render(self, draw: DrawingTool):
        self.drive.render(draw)

        if self.target_direction is not None:
            draw.color(draw.lime)
            draw.triangle(self.target - self.target_direction * 250,
                          self.target_direction)
Exemple #16
0
class GeneralDefense(Maneuver):
    """
    First, attempt to rotate on the far side, and when far away enough from the target (usually the ball),
    turn around to face it. If already far enough and facing the target, just stop and wait.
    Also try to pickup boost pads along the way.
    This state expires after a short amount of time, so we can look if there's something better to do. If not,
    it can be simply instantiated again.
    """

    DURATION = 0.5

    BOOST_LOOK_RADIUS = 1200
    BOOST_LOOK_ANGLE = 0.5

    def __init__(self, car: Car, info: GameInfo, face_target: vec3,
                 distance_from_target: float):
        super().__init__(car)

        self.info = info
        self.face_target = face_target

        dist = min(distance_from_target,
                   ground_distance(face_target, self.info.my_goal.center) - 50)
        target_pos = ground(face_target) + ground_direction(
            face_target, self.info.my_goal.center) * dist

        near_goal = ground_distance(car, info.my_goal.center) < 3000
        side_shift = 400 if near_goal else 2500
        points = [
            target_pos + vec3(side_shift, 0, 0),
            target_pos - vec3(side_shift, 0, 0)
        ]
        target_pos = nearest_point(face_target,
                                   points) if near_goal else farthest_point(
                                       face_target, points)
        target_pos = Arena.clamp(target_pos, 500)

        self.travel = Travel(car, target_pos)
        self.travel.finish_distance = 800 if near_goal else 1500
        self.drive = Drive(car)
        self.stop = Stop(car)

        self.start_time = car.time

        self.pad = None

    def interruptible(self) -> bool:
        return self.travel.interruptible()

    def step(self, dt):
        # update finished state even if we are not using the controls
        self.travel.step(dt)

        if self.travel.finished:
            # turn around to face the target direction
            if angle_to(self.car, self.face_target) > 0.3:
                self.drive.target_pos = self.face_target
                self.drive.target_speed = 1000
                self.drive.step(dt)
                self.controls = self.drive.controls
                self.controls.handbrake = False
            else:
                self.stop.step(dt)
                self.controls = self.stop.controls

        else:
            self.pad = None

            # collect boost pads on the way (greedy algorithm, assumes first found is best)
            if self.car.boost < 90 and self.travel.interruptible():
                to_target = ground_direction(self.car, self.travel.target)

                for pad in self.info.large_boost_pads + self.info.small_boost_pads:
                    to_pad = ground_direction(self.car, pad)

                    if (pad.is_active and
                            distance(self.car, pad) < self.BOOST_LOOK_RADIUS
                            and angle_between(to_target,
                                              to_pad) < self.BOOST_LOOK_ANGLE):
                        self.pad = pad
                        self.drive.target_pos = pad.position
                        self.drive.target_speed = 2200
                        self.drive.step(dt)
                        self.controls = self.drive.controls
                        break

            # go to the actual target
            if self.pad is None:
                self.controls = self.travel.controls

        # don't waste boost during downtime
        self.controls.boost = False

        self.finished = self.travel.driving and self.car.time > self.start_time + self.DURATION

    def render(self, draw: DrawingTool):
        self.travel.render(draw)

        # render target pad
        if self.pad:
            draw.color(draw.blue)
            draw.circle(self.pad.position, 50)
Exemple #17
0
    def __init__(self, car: Car, pad: BoostPad):
        super().__init__(car)
        self.pad = pad
        self.pad_was_active = self.pad.state == BoostPadState.Available

        self.travel = Travel(car, self.pad.position, waste_boost=True)
Exemple #18
0
class ShadowDefense(Maneuver):
    def __init__(self, car: Car, info: GameInfo, face_target: vec3,
                 distance_from_target: float):
        super().__init__(car)

        self.info = info
        self.face_target = face_target

        dist = min(distance_from_target,
                   ground_distance(face_target, self.info.my_goal.center) - 50)
        target_pos = ground(face_target) + ground_direction(
            face_target, self.info.my_goal.center) * dist

        near_goal = ground_distance(car, info.my_goal.center) < 3000
        side_shift = 400 if near_goal else 2000
        points = [
            target_pos + vec3(side_shift, 0, 0),
            target_pos - vec3(side_shift, 0, 0)
        ]
        target_pos = nearest_point(face_target,
                                   points) if near_goal else furthest_point(
                                       face_target, points)

        self.target = Arena.clamp(target_pos, 500)

        self.travel = Travel(car, self.target)
        self.travel.finish_distance = 800 if near_goal else 1500
        self.drive = Drive(car)

        self.start_time = car.time
        self.wait = Stop(car)

    def step(self, dt):
        ball = self.info.ball

        # if (
        #     distance(self.car, ball) < 1000
        #     and align(self.car.position, ball, self.info.my_goal.center) > 0.2
        # ):
        #     shift = normalize(cross(direction(ball, self.car), vec3(0, 0, 1))) * 1000
        #     self.travel.target = nearest_point(self.car.position, [ball.position + shift, ball.position - shift])
        # else:
        #     self.travel.target = self.target

        self.travel.step(dt)
        self.controls = self.travel.controls

        if ground_distance(self.car, self.travel.target) < 3000:
            self.controls.boost = False

        if self.travel.finished:
            if angle_to(self.car, self.face_target) > 0.3:
                self.drive.target_pos = self.face_target
                self.drive.step(dt)
                self.drive.target_speed = 700
                self.drive.controls.handbrake = False
                self.controls = self.drive.controls
            else:
                self.wait.step(dt)
                self.controls = self.wait.controls
        self.finished = self.travel.driving and self.car.time > self.start_time + 0.5

    def render(self, draw: DrawingTool):
        self.travel.render(draw)