Ejemplo n.º 1
0
    def run(self, drone: CarObject, agent: MyHivemind):
        target = agent.friend_goal.location + 2 * (agent.ball.location - agent.friend_goal.location) / 3
        car_to_target = target - drone.location
        distance_remaining = car_to_target.flatten().magnitude()

        agent.line(target - Vector3(0, 0, 500), target + Vector3(0, 0, 500), [255, 0, 255])

        if self.vector is not None:
            # See commends for adjustment in jump_shot or aerial for explanation
            side_of_vector = sign(self.vector.cross((0, 0, 1)).dot(car_to_target))
            car_to_target_perp = car_to_target.cross((0, 0, side_of_vector)).normalize()
            adjustment = car_to_target.angle2D(self.vector) * distance_remaining / 3.14
            final_target = target + (car_to_target_perp * adjustment)
        else:
            final_target = target

        # Some adjustment to the final target to ensure it's inside the field and
        # we don't try to drive through any goalposts to reach it
        if abs(drone.location[1]) > 5150:
            final_target[0] = cap(final_target[0], -750, 750)

        local_target = drone.local(final_target - drone.location)

        angles = defaultPD(drone, local_target, self.direction)
        defaultThrottle(drone, 2300, self.direction)

        drone.controller.boost = False
        drone.controller.handbrake = True if abs(angles[1]) > 2.3 else drone.controller.handbrake

        velocity = 1 + drone.velocity.magnitude()
        if distance_remaining < 350:
            drone.pop()
        elif abs(angles[1]) < 0.05 and 600 < velocity < 2150 and distance_remaining / velocity > 2.0:
            drone.push(Flip(local_target))
        # TODO Halfflip
        # elif abs(angles[1]) > 2.8 and velocity < 200:
        #     agent.push(flip(local_target, True))
        elif drone.airborne:
            drone.push(Recovery(target))
Ejemplo n.º 2
0
def defaultPD(drone: CarObject,
              local_target: Vector3,
              direction: float = 1.0) -> []:
    # points the car towards a given local target.
    # Direction can be changed to allow the car to steer towards a target while driving backwards
    local_target *= direction
    up = drone.local(Vector3(0, 0, 1))  # where "up" is in local coordinates
    target_angles = [
        math.atan2(local_target[2],
                   local_target[0]),  # angle required to pitch towards target
        math.atan2(local_target[1],
                   local_target[0]),  # angle required to yaw towards target
        math.atan2(up[1], up[2])
    ]  # angle required to roll upright
    # Once we have the angles we need to rotate, we feed them into PD loops to determing the controller inputs
    drone.controller.steer = steerPD(target_angles[1], 0) * direction
    drone.controller.pitch = steerPD(target_angles[0],
                                     drone.angular_velocity[1] / 4)
    drone.controller.yaw = steerPD(target_angles[1],
                                   -drone.angular_velocity[2] / 4)
    drone.controller.roll = steerPD(target_angles[2],
                                    drone.angular_velocity[0] / 2)
    # Returns the angles, which can be useful for other purposes
    return target_angles
Ejemplo n.º 3
0
    def run(self, drone: CarObject, agent: MyHivemind):
        raw_time_remaining = self.intercept_time - agent.time
        # Capping raw_time_remaining above 0 to prevent division problems
        time_remaining = cap(raw_time_remaining, 0.01, 10.0)

        car_to_ball = self.ball_location - drone.location
        # whether we are to the left or right of the shot vector
        side_of_shot = sign(self.shot_vector.cross((0, 0, 1)).dot(car_to_ball))

        car_to_intercept = self.intercept - drone.location
        car_to_intercept_perp = car_to_intercept.cross((0, 0, side_of_shot))  # perpendicular
        distance_remaining = car_to_intercept.flatten().magnitude()

        speed_required = distance_remaining / time_remaining
        # When still on the ground we pretend gravity doesn't exist, for better or worse
        acceleration_required = backsolve(self.intercept, drone, time_remaining, 0 if self.jump_time == 0 else 325)
        local_acceleration_required = drone.local(acceleration_required)

        # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector
        # The adjustment slowly decreases to 0 as the bot nears the time to jump
        adjustment = car_to_intercept.angle(self.shot_vector) * distance_remaining / 1.57  # size of adjustment
        adjustment *= (cap(self.jump_threshold - (acceleration_required[2]), 0.0,
                           self.jump_threshold) / self.jump_threshold)  # factoring in how close to jump we are
        # we don't adjust the final target if we are already jumping
        final_target = self.intercept + ((car_to_intercept_perp.normalize() * adjustment) if self.jump_time == 0 else 0)

        # Some extra adjustment to the final target to ensure it's inside the field and
        # we don't try to drive through any goalposts to reach it
        if abs(drone.location[1] > 5150):
            final_target[0] = cap(final_target[0], -750, 750)

        local_final_target = drone.local(final_target - drone.location)

        # drawing debug lines to show the dodge point and final target (which differs due to the adjustment)
        agent.line(drone.location, self.intercept)
        agent.line(self.intercept - Vector3(0, 0, 100), self.intercept + Vector3(0, 0, 100), [255, 0, 0])
        agent.line(final_target - Vector3(0, 0, 100), final_target + Vector3(0, 0, 100), [0, 255, 0])

        angles = defaultPD(drone, local_final_target)

        if self.jump_time == 0:
            defaultThrottle(drone, speed_required)
            drone.controller.boost = False if abs(angles[1]) > 0.3 or drone.airborne else drone.controller.boost
            drone.controller.handbrake = True if abs(angles[1]) > 2.3 else drone.controller.handbrake
            if acceleration_required[2] > self.jump_threshold:
                # Switch into the jump when the upward acceleration required reaches our threshold,
                # hopefully we have aligned already...
                self.jump_time = agent.time
        else:
            time_since_jump = agent.time - self.jump_time

            # While airborne we boost if we're within 30 degrees of our local acceleration requirement
            if drone.airborne and local_acceleration_required.magnitude() * time_remaining > 100:
                angles = defaultPD(drone, local_acceleration_required)
                if abs(angles[0]) + abs(angles[1]) < 0.5:
                    drone.controller.boost = True
            if self.counter == 0 and (time_since_jump <= 0.2 and local_acceleration_required[2] > 0):
                # hold the jump button up to 0.2 seconds to get the most acceleration from the first jump
                drone.controller.jump = True
            elif time_since_jump > 0.2 and self.counter < 3:
                # Release the jump button for 3 ticks
                drone.controller.jump = False
                self.counter += 1
            elif local_acceleration_required[2] > 300 and self.counter == 3:
                # the acceleration from the second jump is instant, so we only do it for 1 frame
                drone.controller.jump = True
                drone.controller.pitch = 0
                drone.controller.yaw = 0
                drone.controller.roll = 0
                self.counter += 1

        if raw_time_remaining < -0.25 or not shot_valid(agent, self):
            drone.pop()
            drone.push(Recovery())
Ejemplo n.º 4
0
    def run(self, drone: CarObject, agent: MyHivemind):
        raw_time_remaining = self.intercept_time - agent.time
        # Capping raw_time_remaining above 0 to prevent division problems
        time_remaining = cap(raw_time_remaining, 0.001, 10.0)
        car_to_ball = self.ball_location - drone.location
        # whether we are to the left or right of the shot vector
        side_of_shot = sign(self.shot_vector.cross((0, 0, 1)).dot(car_to_ball))

        car_to_dodge_point = self.dodge_point - drone.location
        car_to_dodge_perp = car_to_dodge_point.cross((0, 0, side_of_shot))  # perpendicular
        distance_remaining = car_to_dodge_point.magnitude()

        speed_required = distance_remaining / time_remaining
        acceleration_required = backsolve(self.dodge_point, drone, time_remaining, 0 if not self.jumping else 650)
        local_acceleration_required = drone.local(acceleration_required)

        # The adjustment causes the car to circle around the dodge point in an effort to line up with the shot vector
        # The adjustment slowly decreases to 0 as the bot nears the time to jump
        adjustment = car_to_dodge_point.angle(self.shot_vector) * distance_remaining / 2.0  # size of adjustment
        adjustment *= (cap(self.jump_threshold - (acceleration_required[2]), 0.0,
                           self.jump_threshold) / self.jump_threshold)  # factoring in how close to jump we are
        # we don't adjust the final target if we are already jumping
        final_target = self.dodge_point + (
            (car_to_dodge_perp.normalize() * adjustment) if not self.jumping else 0) + Vector3(0, 0, 50)
        # Ensuring our target isn't too close to the sides of the field,
        # where our car would get messed up by the radius of the curves

        # Some adjustment to the final target to ensure it's inside the field and
        # we don't try to dirve through any goalposts to reach it
        if abs(drone.location[1]) > 5150:
            final_target[0] = cap(final_target[0], -750, 750)

        local_final_target = drone.local(final_target - drone.location)

        # drawing debug lines to show the dodge point and final target (which differs due to the adjustment)
        agent.line(drone.location, self.dodge_point)
        agent.line(self.dodge_point - Vector3(0, 0, 100), self.dodge_point + Vector3(0, 0, 100), [255, 0, 0])
        agent.line(final_target - Vector3(0, 0, 100), final_target + Vector3(0, 0, 100), [0, 255, 0])

        # Calling our drive utils to get us going towards the final target
        angles = defaultPD(drone, local_final_target, self.direction)
        defaultThrottle(drone, speed_required, self.direction)

        agent.line(drone.location, drone.location + (self.shot_vector * 200), [255, 255, 255])

        drone.controller.boost = False if abs(angles[1]) > 0.3 or drone.airborne else drone.controller.boost
        drone.controller.handbrake = True if abs(
            angles[1]) > 2.3 and self.direction == 1 else drone.controller.handbrake

        if not self.jumping:
            if raw_time_remaining <= 0.0 or (speed_required - 2300) * time_remaining > 45 or not shot_valid(agent,
                                                                                                            self):
                # If we're out of time or not fast enough to be within 45 units of target at the intercept time, we pop
                drone.pop()
                if drone.airborne:
                    drone.push(Recovery())
            elif local_acceleration_required[2] > self.jump_threshold \
                    and local_acceleration_required[2] > local_acceleration_required.flatten().magnitude():
                # Switch into the jump when the upward acceleration required reaches our threshold,
                # and our lateral acceleration is negligible
                self.jumping = True
        else:
            if (raw_time_remaining > 0.2 and not shot_valid(agent, self, 150)) or raw_time_remaining <= -0.9 or (
                    not drone.airborne and self.counter > 0):
                drone.pop()
                drone.push(Recovery())
            elif self.counter == 0 and local_acceleration_required[2] > 0.0 and raw_time_remaining > 0.083:
                # Initial jump to get airborne + we hold the jump button for extra power as required
                drone.controller.jump = True
            elif self.counter < 3:
                # make sure we aren't jumping for at least 3 frames
                drone.controller.jump = False
                self.counter += 1
            elif 0.1 >= raw_time_remaining > -0.9:
                # dodge in the direction of the shot_vector
                drone.controller.jump = True
                if not self.dodging:
                    vector = drone.local(self.shot_vector)
                    self.p = abs(vector[0]) * -sign(vector[0])
                    self.y = abs(vector[1]) * sign(vector[1]) * self.direction
                    self.dodging = True
                # simulating a deadzone so that the dodge is more natural
                drone.controller.pitch = self.p if abs(self.p) > 0.2 else 0
                drone.controller.yaw = self.y if abs(self.y) > 0.3 else 0
Ejemplo n.º 5
0
 def run(self, drone: CarObject, agent: MyHivemind):
     relative_target = agent.ball.location - drone.location
     local_target = drone.local(relative_target)
     defaultPD(drone, local_target)
     defaultThrottle(drone, 2300)
Ejemplo n.º 6
0
def push_shot(drone: CarObject, agent: MyHivemind):
    left = Vector3(4200 * -agent.side(), agent.side() * 5120, 0)
    right = Vector3(4200 * agent.side(), agent.side() * 5120, 0)
    targets = {"goal": (agent.foe_goal.left_post, agent.foe_goal.right_post)}
    if not agent.conceding:
        drones = copy(agent.drones)
        drones.remove(drone)
        team = agent.friends + drones
        for teammate in team:
            a = teammate.location
            b = teammate.location + 2000 * teammate.forward
            local_a = drone.local(a)
            angle_a = math.atan2(local_a.y, local_a.x)
            if angle_a > 0:
                targets["teammate" + str(team.index(teammate))] = (b, a)
            else:
                targets["teammate" + str(team.index(teammate))] = (a, b)
    targets["upfield"] = (left, right)
    shots = find_hits(drone, agent, targets)
    if len(shots["goal"]) > 0:
        drone.clear()
        drone.push(shots["goal"][0])
        drone.action = Action.Going
    elif shots.get("teammate0") is not None and len(
            shots.get("teammate0")) > 0:
        drone.clear()
        drone.push(shots["teammate0"][0])
        drone.action = Action.Going
    elif shots.get("teammate1") is not None and len(
            shots.get("teammate1")) > 0:
        drone.clear()
        drone.push(shots["teammate1"][0])
        drone.action = Action.Going
    elif len(shots["upfield"]) > 0:
        drone.clear()
        drone.push(shots["upfield"][0])
        drone.action = Action.Going
Ejemplo n.º 7
0
    def run(self, drone: CarObject, agent: MyHivemind):
        if self.time == -1:
            elapsed = 0
            self.time = agent.time
        else:
            elapsed = agent.time - self.time
        T = self.intercept_time - agent.time
        xf = drone.location + drone.velocity * T + 0.5 * gravity * T ** 2
        vf = drone.velocity + gravity * T
        if self.jumping:
            if self.jump_time == -1:
                jump_elapsed = 0
                self.jump_time = agent.time
            else:
                jump_elapsed = agent.time - self.jump_time
            tau = jump_max_duration - jump_elapsed
            if jump_elapsed == 0:
                vf += drone.up * jump_speed
                xf += drone.up * jump_speed * T

            vf += drone.up * jump_acc * tau
            xf += drone.up * jump_acc * tau * (T - 0.5 * tau)

            vf += drone.up * jump_speed
            xf += drone.up * jump_speed * (T - tau)

            if jump_elapsed < jump_max_duration:
                drone.controller.jump = True
            elif elapsed >= jump_max_duration and self.counter < 3:
                drone.controller.jump = False
                self.counter += 1
            elif elapsed < 0.3:
                drone.controller.jump = True
            else:
                self.jumping = jump_elapsed <= 0.3
        else:
            drone.controller.jump = 0

        delta_x = self.ball_location - xf
        direction = delta_x.normalize()
        if delta_x.magnitude() > 50:
            defaultPD(drone, drone.local(delta_x))
        else:
            if self.target is not None:
                defaultPD(drone, drone.local(self.target))
            else:
                defaultPD(drone, drone.local(self.ball_location - drone.location))

        if jump_max_duration <= elapsed < 0.3 and self.counter == 3:
            drone.controller.roll = 0
            drone.controller.pitch = 0
            drone.controller.yaw = 0
            drone.controller.steer = 0

        if drone.forward.angle3D(direction) < 0.3:
            if delta_x.magnitude() > 50:
                drone.controller.boost = 1
                drone.controller.throttle = 0
            else:
                drone.controller.boost = 0
                drone.controller.throttle = cap(0.5 * throttle_accel * T ** 2, 0, 1)
        else:
            drone.controller.boost = 0
            drone.controller.throttle = 0

        if T <= 0 or not shot_valid(agent, self, threshold=150):
            drone.pop()
            drone.push(Recovery(agent.friend_goal.location))
Ejemplo n.º 8
0
def find_shot(drone: CarObject,
              target,
              cap_=6,
              can_aerial=True,
              can_double_jump=True,
              can_jump=True,
              can_ground=True):
    if not can_aerial and not can_double_jump and not can_jump and not can_ground:
        return

    # Takes a tuple of (left,right) target pairs and finds routines that could hit the ball between those target pairs
    # Only meant for routines that require a defined intercept time/place in the future

    # Assemble data in a form that can be passed to C
    targets = (tuple(target[0]), tuple(target[1]))

    me = drone.get_raw()

    game_info = (drone.boost_accel, 92.75)

    gravity = tuple(drone.gravity)

    is_on_ground = not drone.airborne
    can_ground = is_on_ground and can_ground
    can_jump = is_on_ground and can_jump
    can_double_jump = is_on_ground and can_double_jump

    if not can_ground and not can_jump and not can_double_jump and not can_aerial:
        return

    # Here we get the slices that need to be searched - by defining a cap, we can reduce the number of slices and improve search times
    slices = get_slices(drone, cap_)

    if slices is None:
        return

    # Loop through the slices
    for ball_slice in slices:
        # Gather some data about the slice
        intercept_time = ball_slice.game_seconds
        T = intercept_time - drone.time - (1 / 120)

        if T <= 0:
            return

        ball_location = (ball_slice.physics.location.x,
                         ball_slice.physics.location.y,
                         ball_slice.physics.location.z)

        if abs(ball_location[1]) > 5212.75:
            return  # abandon search if ball is scored at/after this point

        ball_info = (ball_location, (ball_slice.physics.velocity.x,
                                     ball_slice.physics.velocity.y,
                                     ball_slice.physics.velocity.z))

        # Check if we can make a shot at this slice
        # This operation is very expensive, so we use C to improve run time
        shot = virxrlcu.parse_slice_for_shot_with_target(
            can_ground, can_jump, can_double_jump, can_aerial, T, *game_info,
            gravity, ball_info, me, targets)

        if shot['found'] == 1:
            shot_type = ShotType(shot["shot_type"])
            if shot_type == ShotType.AERIAL:
                return Aerial(intercept_time, (Vector3(*shot['targets'][0]),
                                               Vector3(*shot['targets'][1])),
                              shot['fast'])

            return SHOT_SWITCH[shot_type](
                intercept_time,
                (Vector3(*shot['targets'][0]), Vector3(*shot['targets'][1])))