def predict(self): """Method which uses ball prediction to fill in future data""" self.bounces = [] self.ball_bouncing = False ball_prediction = self.get_ball_prediction_struct() if ball_prediction is not None: prev_ang_velocity = normalize(self.info.ball.angular_velocity) for i in range(ball_prediction.num_slices): prediction_slice = ball_prediction.slices[i] physics = prediction_slice.physics if physics.location.y * sign(self.team) > 5120: self.conceding = True if physics.location.z > 180: self.ball_bouncing = True continue current_ang_velocity = normalize( vec3(physics.angular_velocity.x, physics.angular_velocity.y, physics.angular_velocity.z)) if physics.location.z < 125 and prev_ang_velocity != current_ang_velocity: self.bounces.append( (vec3(physics.location.x, physics.location.y, physics.location.z), prediction_slice.game_seconds - self.time)) if len(self.bounces) > 15: return prev_ang_velocity = current_ang_velocity
def simulate_landing(self): dummy = Car(self.car) self.trajectory = [vec3(dummy.position)] self.landing = False collision_normal: Optional[vec3] = None dt = 1 / 60 simulation_duration = 0.8 for i in range(int(simulation_duration / dt)): dummy.step(Input(), dt) self.trajectory.append(vec3(dummy.position)) collision_sphere = sphere(dummy.position, 50) collision_ray = Field.collide(collision_sphere) collision_normal = collision_ray.direction if (norm(collision_normal) > 0.0 or dummy.position[2] < 0) and i > 20: self.landing = True self.landing_pos = dummy.position break if self.landing: u = collision_normal f = normalize(dummy.velocity - dot(dummy.velocity, u) * u) l = normalize(cross(u, f)) self.aerial_turn.target = mat3(f[0], l[0], u[0], f[1], l[1], u[1], f[2], l[2], u[2]) else: target_direction = normalize( normalize(self.car.velocity) - vec3(0, 0, 3)) self.aerial_turn.target = look_at(target_direction, vec3(0, 0, 1))
def step(self, dt): ball = Ball(self.ball) car = self.car # simulate ball until it gets near the floor while (ball.position[2] > 120 or ball.velocity[2] > 0) and ball.time < car.time + 10: ball.step(1/60) ball_local = local(car, ground(ball.position)) target = local(car, self.target) shift = ground(direction(ball_local, target)) shift[1] *= 1.8 shift = normalize(shift) max_turn = clamp(norm(car.velocity) / 800, 0, 1) max_shift = normalize(vec3(1 - max_turn, max_turn * sign(shift[1]), 0)) if abs(shift[1]) > abs(max_shift[1]) or shift[0] < 0: shift = max_shift shift *= clamp(car.boost, 40, 60) shift[1] *= clamp(norm(car.velocity)/1000, 1, 2) self._shift_direction = normalize(world(car, shift) - car.position) target = world(car, ball_local - shift) speed = distance(car.position, target) / max(0.001, ball.time - car.time) self.drive.target_speed = speed self.drive.target_pos = target self.drive.step(dt) self.controls = self.drive.controls self.finished = self.ball.position[2] < 100 or ground_distance(self.ball, self.car) > 2000
def simulate_landing(self): pos = vec3(self.car.position) vel = vec3(self.car.velocity) grav = vec3(0, 0, -650) self.trajectory = [vec3(pos)] self.landing = False collision_normal: Optional[vec3] = None dt = 1 / 60 simulation_duration = 0.8 for i in range(int(simulation_duration / dt)): pos += vel * dt vel += grav * dt if norm(vel) > 2300: vel = normalize(vel) * 2300 self.trajectory.append(vec3(pos)) collision_sphere = sphere(pos, 50) collision_ray = Field.collide(collision_sphere) collision_normal = collision_ray.direction if (norm(collision_normal) > 0.0 or pos[2] < 0) and i > 20: self.landing = True self.landing_pos = pos break if self.landing: u = collision_normal f = normalize(vel - dot(vel, u) * u) l = normalize(cross(u, f)) self.aerial_turn.target = three_vec3_to_mat3(f, l, u) else: target_direction = normalize( normalize(self.car.velocity) - vec3(0, 0, 3)) self.aerial_turn.target = look_at(target_direction, vec3(0, 0, 1))
def step(self, packet: GameTickPacket, drone: Drone, index: int): drone.hover.up = normalize(drone.position) clockwise_rotation = axis_to_rotation(vec3(0, 0, 0.5)) position_on_circle = normalize(xy(drone.position)) * 2000 drone.hover.target = dot(clockwise_rotation, position_on_circle) drone.hover.target[2] = 1000 drone.hover.step(self.dt) drone.controls = drone.hover.controls
def step(self, packet: GameTickPacket, drone: Drone, index: int): for i, layer in enumerate(self.layers): if index in layer: drone.hover.up = normalize(drone.position) clockwise_rotation = axis_to_rotation(vec3(0, 0, 0.3)) position_on_circle = normalize(xy( drone.position)) * self.radii[i] drone.hover.target = dot(clockwise_rotation, position_on_circle) drone.hover.target[2] = self.heights[i] break drone.hover.step(self.dt) drone.controls = drone.hover.controls
def step(self, packet: GameTickPacket, drone: Drone, index: int): drone.hover.up = normalize(drone.position) rotation = axis_to_rotation(vec3(0, 0, self.spin * (-1)**(index // 16))) position_on_circle = normalize(xy(drone.position)) * ( self.radius + self.radius_increase * self.time_since_start) drone.hover.target = dot(rotation, position_on_circle) drone.hover.target[ 2] = self.height + self.height_increase * self.time_since_start drone.hover.target[2] += (index // 16 - 2) * ( self.height_diff + self.height_diff_increase * self.time_since_start) drone.hover.step(self.dt) drone.controls = drone.hover.controls
def step(self, packet: GameTickPacket, drone: Drone, index: int): # Just ask me in discord about this. layer = (2 * (index // 16) + (index % 2) - 3.5) / 4 height = self.height + layer * self.radius radius = math.sqrt(1 - layer**2) * self.radius spin = 0 if self.time_since_start < 3.0 else 0.5 drone.hover.up = normalize(drone.position) rotation = axis_to_rotation(vec3(0, 0, spin)) position_on_circle = normalize(xy(drone.position)) * radius drone.hover.target = dot(rotation, position_on_circle) drone.hover.target[2] = height drone.hover.step(self.dt) drone.controls = drone.hover.controls
def tick(self, packet: GameTickPacket) -> bool: if self.agent.car.on_ground: self.controller.jump = not self.controller.jump self.controller.throttle = 1 return True else: self.controller.jump = self.agent.car.velocity[2] > 100 self.controller.boost = dot(self.agent.car.forward(), vec3( 0, 0, -1)) * dot(self.agent.car.velocity, vec3(0, 0, -1)) < 0 self.controller.throttle = self.controller.boost and self.agent.car.position[ 2] > 150 hitbox = self.agent.car.hitbox() target = normalize( flatten(dot(self.agent.car.orientation, hitbox.half_width))) tmp = vec3(-hitbox.half_width[1], 0, 0) target = dot(self.agent.car.orientation, tmp) + vec3( 0, 0, -hitbox.half_width[0]) # self.agent.draw.vector(self.agent.car.position, target) self.agent.reorientML.target_orientation = look_at( target, self.direction * self.agent.car.left() if abs(self.agent.car.velocity[2]) < 20 else self.agent.car.velocity) self.agent.reorientML.step(1 / self.agent.FPS) self.controller.yaw = self.agent.reorientML.controls.yaw self.controller.pitch = self.agent.reorientML.controls.pitch self.controller.roll = self.agent.reorientML.controls.roll return True
def step(self, packet: GameTickPacket, drone: Drone, index: int): self.finished = (100 + self.big_radius - self.time_since_start * self.speed) <= 0 # Get centre of small circle. direction_angle = (math.pi / 4) + ((math.pi / 2) * (index // 16)) centre = vec3(dot(rotation(direction_angle), vec2(1, 0))) # Crossing action. centre *= (self.big_radius - self.time_since_start * self.speed) centre[2] = self.height # Angle for the small circle. angle = (2 * math.pi / 16) * (index % 16) angle += (2 * math.pi / 4) * (index // 16 - 1) angle += self.time_since_start * self.spin target = vec3(dot(rotation(angle), vec2(self.small_radius, 0))) target += centre # Different heights. target[2] += (index // 16 - 2) * self.height_diff # Hover controls. drone.hover.up = normalize(drone.position - centre) drone.hover.target = target drone.hover.step(self.dt) drone.controls = drone.hover.controls
def step(self, packet: GameTickPacket, drone: Drone, index: int): # Get centre of small circle. direction_angle = (math.pi / 4) + ((math.pi / 2) * (index // 16)) centre = vec3(dot(rotation(direction_angle), vec2( 1, 0))) * self.big_radius centre[2] = self.height # Angle for the small circle. # Wraps nicely around the small circle. t = self.time_since_start + 2.5 if t > 2 * math.pi: t = 2 * math.pi angle = (t / 16) * (index % 16 - 8) angle += (2 * math.pi / 4) * (index // 16) + (math.pi / 4) target = vec3(dot(rotation(angle), vec2(self.small_radius, 0))) target += centre # Hover controls. drone.hover.up = normalize(drone.position - centre) drone.hover.target = target drone.hover.step(self.dt) drone.controls = drone.hover.controls # If any bot got lost, now they have a chance to recover. if drone.on_ground: drone.controls.jump = True else: drone.controls.jump = False
def configure(self, intercept: Intercept): super().configure(intercept) self.aerial.target = intercept.position - direction( intercept, self.target) * 80 self.aerial.up = normalize( ground_direction(intercept, self.car) + vec3(0, 0, 0.5)) self.aerial.arrival_time = intercept.time
def step(self, packet: GameTickPacket, drone: Drone, index: int): up = normalize(drone.position) drone.aerial_turn.target = look_at(vec3(0, 0, 1), up) drone.aerial_turn.step(self.dt) drone.controls = drone.aerial_turn.controls drone.controls.boost = angle_between(vec3(0, 0, 1), drone.forward()) < 0.5 drone.controls.jump = True
def predict_car_drive(self, index, time_limit=2.0, dt=1 / 60) -> List[vec3]: """Simple prediction of a driving car assuming no acceleration.""" car = self.cars[index] time_steps = int(time_limit / dt) speed = norm(car.velocity) ang_vel_z = car.angular_velocity[2] # predict circular path if ang_vel_z != 0 and car.on_ground: radius = speed / ang_vel_z centre = car.position - cross(normalize(xy(car.velocity)), vec3(0, 0, 1)) * radius centre_to_car = vec2(car.position - centre) return [ vec3(dot(rotation(ang_vel_z * dt * i), centre_to_car)) + centre for i in range(time_steps) ] # predict straight path return [ car.position + car.velocity * dt * i for i in range(time_steps) ]
def step(self, packet: GameTickPacket, drone: Drone, index: int): if drone.position[2] < 25: drone.since_jumped = 0.0 # Go forward drone.controls.throttle = 1.0 if abs( drone.velocity[1]) < 500 else 0.01 # If near half-line if abs(drone.position[1]) < 200: drone.controls.jump = True else: drone.since_jumped += self.dt height = 50 + drone.since_jumped * 150 angle = 1.0 + drone.since_jumped * 1.2 if index % 2 == 0: angle += math.pi rot = rotation(angle) v = vec3(dot(rot, vec2(1, 0))) drone.hover.target = v * self.radius drone.hover.target[2] = height drone.hover.up = normalize(drone.position) drone.hover.step(self.dt) drone.controls = drone.hover.controls
def shooting_target(agent): """"Method that gives the target for the shooting strategy""" ball = agent.info.ball car = agent.info.my_car car_to_ball = ball.location - car.location backline_intersect = line_backline_intersect(agent.their_goal.center[1], vec2(car.location), vec2(car_to_ball)) if abs(backline_intersect) < 700: goal_to_ball = normalize(car.location - ball.location) error = 0 else: # Right of the ball if -500 > backline_intersect: target = agent.their_goal.corners[3] + vec3(400, 0, 0) # Left of the ball elif backline_intersect > 500: target = agent.their_goal.corners[2] - vec3(400, 0, 0) goal_to_ball = normalize(ball.location - target) # Subtract the goal to car vector difference = goal_to_ball - normalize(car.location - target) error = cap(abs(difference[0]) + abs(difference[1]), 0, 5) goal_to_ball_2d = vec2(goal_to_ball[0], goal_to_ball[1]) test_vector_2d = dot(rotation(0.5 * math.pi), goal_to_ball_2d) test_vector = vec3(test_vector_2d[0], test_vector_2d[1], 0) distance = cap( (40 + distance_2d(ball.location, car.location) * (error**2)) / 1.8, 0, 4000) location = ball.location + vec3( (goal_to_ball[0] * distance), goal_to_ball[1] * distance, 0) # this adjusts the target based on the ball velocity perpendicular # to the direction we're trying to hit it multiplier = cap(distance_2d(car.location, location) / 1500, 0, 2) distance_modifier = cap( dot(test_vector, ball.velocity) * multiplier, -1000, 1000) location += vec3(test_vector[0] * distance_modifier, test_vector[1] * distance_modifier, 0) # another target adjustment that applies if the ball is close to the wall extra = 3850 - abs(location[0]) if extra < 0: location[0] = cap(location[0], -3850, 3850) location[1] = location[1] + (-sign(agent.team) * cap(extra, -800, 800)) return location
def step(self, packet: GameTickPacket, drones: List[Drone]): for drone in drones: drone.hover.up = normalize(drone.position) for i, layer in enumerate(self.layers): if drone.id in layer: # Calculate radius if self.time_since_start < self.radius_shrink_start: radius = 2000 elif self.time_since_start < self.radius_shrink_start + self.radius_shrink_duration: diff = 2000 - self.radii[i] radius = 2000 - diff * ( (self.time_since_start - self.radius_shrink_start) / self.radius_shrink_duration) else: radius = self.radii[i] # Calculate xy position if self.time_since_start > self.recirculation_start: a = layer.index(drone.id) angle = a * math.pi * 2 / len(layer) rot = rotation(angle) pos_xy = vec3(dot(rot, vec2(1, 0))) else: pos_xy = xy(drone.position) # Combine xy and radius drone.hover.target = normalize(pos_xy) * radius # Get height if self.time_since_start < self.separation_duration: diff = 1000 - self.heights[i] height = 1000 - diff * (self.time_since_start / self.separation_duration) else: height = self.heights[i] drone.hover.target[2] = height break drone.hover.step(self.dt) drone.controls = drone.hover.controls
def get_bounce(agent): """Returns the first reachable bounce""" for i in range(len(agent.bounces)): goal_to_ball = normalize( z_0(agent.bounces[i][0] - agent.their_goal.center)) target = agent.bounces[i][0] + 40 * goal_to_ball if is_reachable(agent, target, agent.bounces[i][1]): return [target, agent.bounces[i][1]] return None
def t_nearest(self, pos: vec3) -> float: t = 0.5 for i in range(6): r = self.position(t) - pos dr = self.tangent(t) if abs(dot(r, normalize(dr))) < 10.0: break t -= dot(r, dr) / dot(dr, dr) return t
def step(self, dt): if self.aerialing: self.aerial.target_orientation = look_at( direction(self.car, self.target), vec3(0, 0, -1)) self.aerial.step(dt) self.controls = self.aerial.controls self.finished = self.aerial.finished else: super().step(dt) # simulate aerial from current state simulated_car = self.simulate_flight(self.car) # if the car ended up too far, we're too fast and we need to slow down if ground_distance(self.car, self.aerial.target) + 100 < ground_distance( self.car, simulated_car): # self.controls.throttle = -1 pass # if it ended up near the target, we could take off elif distance(simulated_car, self.aerial.target) < self.MAX_DISTANCE_ERROR: if angle_to(self.car, self.aerial.target) < 0.1 or norm( self.car.velocity) < 1000: if self.DELAY_TAKEOFF: # extrapolate current state a small amount of time future_car = Car(self.car) time = 0.2 future_car.time += time displacement = future_car.velocity * time if norm(future_car.velocity) > 500\ else normalize(future_car.velocity) * 500 * time future_car.position += displacement # simulate aerial fot the extrapolated car again future_simulated_car = self.simulate_flight( future_car, write_to_flight_path=False) # if the aerial is also successful, that means we should continue driving instead of taking off # this makes sure that we go for the most late possible aerials, which are the most effective if distance( future_simulated_car, self.aerial.target) > self.MAX_DISTANCE_ERROR: self.aerialing = True else: self.too_early = True else: self.aerialing = True else: # self.controls.boost = True self.controls.throttle = 1
def step(self, dt): target = self.target car = self.car if self.target_direction is not None: car_speed = norm(car.velocity) target_direction = normalize(self.target_direction) # in order to arrive in a direction, we need to shift the target in the opposite direction # the magnitude of the shift is based on how far are we from the target shift = clamp( ground_distance(car.position, target) * self.lerp_t, 0, car_speed * 1.5) # if we're too close to the target, aim for the actual target so we don't miss it if shift - self.additional_shift < Drive.turn_radius( clamp(car_speed, 1400, 2000) * 1.1): shift = 0 else: shift += self.additional_shift shifted_target = target - target_direction * shift time_shift = ground_distance(shifted_target, target) * 0.7 / clamp( car_speed, 500, 2300) shifted_arrival_time = self.arrival_time - time_shift else: shifted_target = target shifted_arrival_time = self.arrival_time self.drive.target_pos = shifted_target self.travel.target = shifted_target dist_to_target = ground_distance(car.position, shifted_target) time_left = nonzero(shifted_arrival_time - car.time) target_speed = clamp(dist_to_target / time_left, 0, 2300) self.drive.target_speed = target_speed self.drive.backwards = self.backwards # dodges and wavedashes can mess up correctly arriving, so we use them only if we really need them if ((self.allow_dodges_and_wavedashes and norm(car.velocity) < target_speed - 600 and car.boost < 20 and not self.backwards) or not self.travel.driving # a dodge/wavedash is in progress ): self.action = self.travel else: self.action = self.drive self.action.step(dt) self.controls = self.action.controls self.finished = self.car.time >= self.arrival_time
def step(self, packet: GameTickPacket, drone: Drone, index: int): radius = 1050 + 150 * self.time_since_start angle = math.pi + self.time_since_start * (math.pi / 5) angle += (2 * math.pi / 64) * index target = vec3(dot(rotation(angle), vec2(radius, 0))) target[2] = self.height drone.hover.up = normalize(-1 * xy(drone.position)) drone.hover.target = target drone.hover.step(self.dt) drone.controls = drone.hover.controls
def step(self, dt): recovery_time = 0.0 if (self.target is None) else 0.4 if not self.jump.finished: self.jump.step(dt) self.controls = self.jump.controls else: if self.counter == 0: # double jump if self.target is None: self.controls.roll = 0 self.controls.pitch = 0 self.controls.yaw = 0 # air dodge else: target_local = dot(self.target - self.car.position, self.car.orientation) target_local[2] = 0 target_direction = normalize(target_local) self.controls.roll = 0 self.controls.pitch = -target_direction[0] self.controls.yaw = clamp11( sgn(self.car.orientation[2, 2]) * target_direction[1]) if target_local[0] > 0 and dot(self.car.velocity, self.car.forward()) > 500: self.controls.pitch = self.controls.pitch * 0.8 self.controls.yaw = clamp11(self.controls.yaw * 5) elif self.counter == 2: self.controls.jump = 1 elif self.counter >= 4: self.controls.roll = 0 self.controls.pitch = 0 self.controls.yaw = 0 self.controls.jump = 0 self.counter += 1 self.state_timer += dt self.finished = self.jump.finished and self.state_timer > recovery_time and self.counter >= 6
def step(self, dt): if self.jumping: self.controls = Input() # first jump for full 0.2 sec if self.timer <= 0.2: self.controls.jump = True # single tick between jumps elif self.timer <= 0.2 + dt * JUMP_FALSE_TICKS: self.controls.jump = False # second jump else: self.controls.jump = True self.jumping = False self.timer += dt else: self.finished = self.intercept.time < self.info.time if self.car.on_ground: # manage speed before jump distance_to_target = ground_distance(self.car.position, self.intercept.position) if distance_to_target < MIN_DIST_BEFORE_SPEED_CONTROL: target_speed = distance_to_target / self.time_for_jump self.drive.target_speed = -target_speed if self._should_strike_backwards else target_speed self.drive.step(dt) self.controls = self.drive.controls else: super().step(dt) # decide when to jump ground_vel = ground(self.car.velocity) direction_to_target = ground_direction(self.car.position, self.intercept.position) alignment = dot(normalize(ground_vel), direction_to_target) # check alignment if alignment >= MIN_ALIGNMENT: # check that speed is correct speed_in_direction = dot(ground_vel, direction_to_target) time_to_target = distance_to_target / speed_in_direction if self.time_for_jump - ALLOWED_TIME_ERROR <= time_to_target <= self.time_for_jump + ALLOWED_TIME_ERROR: self.jumping = True # after jump (when the car is in the air) else: # face the ball for some additional height self.reorient.target_orientation = look_at( direction(self.car.position, self.info.ball), vec3(0, 0, 1)) self.reorient.step(dt) self.controls = self.reorient.controls
def step(self, dt): super().step(dt) if not self.jump.finished and not self.car.on_ground: target_direction = direction(self.car, self.target + vec3(0, 0, 200)) up = target_direction * (-1) up[2] = 1 up = normalize(up) self.reorient.target_orientation = look_at(target_direction, up) self.reorient.step(dt) self.controls.pitch = self.reorient.controls.pitch self.controls.yaw = self.reorient.controls.yaw self.controls.roll = self.reorient.controls.roll
def toPoint(car: Car, target: vec3): direction = normalize(target - car.position) turnAngleAmount = math.acos( max(-1, min(1, dot(car.forward(), direction)))) rotateToZeroAngle = -atan2(direction) towardsZeroVector = rotate2(car.forward(), rotateToZeroAngle) turnDirection = math.copysign(1, -towardsZeroVector[1]) return PID.fromAngle(car, turnAngleAmount * turnDirection)
def step(self, dt): delta_target = self.target - self.car.position target_direction = normalize( vec3((delta_target[0]) * self.P - self.car.velocity[0] * self.D, (delta_target[1]) * self.P - self.car.velocity[1] * self.D, 1000)) self.turn.target = look_at(target_direction, self.up) self.turn.step(dt) self.controls = self.turn.controls self.controls.boost = 0 # tap boost to keep height if (delta_target[2] - self.car.velocity[2] * 0.5) > 0: self.controls.boost = 1
def step(self, packet: GameTickPacket, drone: Drone, index: int): # you have access to these class attributes self.time # the current game time self.start_time # game time when this step started self.time_since_start # for how long this step has been running self.dt # time since last frame self.finished = False # set to True if you want to finish this step early # lets make the drones fly up with the Aerial mechanic drone.aerial.target = drone.position + vec3(0, 0, 500) drone.aerial.up = normalize(drone.position) # aerial.up is where the car should orient its roof when flying drone.aerial.arrival_time = self.time + 0.5 # some time in the near future so it flies fast drone.aerial.step(self.dt) drone.controls = drone.aerial.controls drone.controls.jump = True # you can modify the controls
def step(self, packet: GameTickPacket, drone: Drone, index: int): angle = (2 * math.pi / 64) * index target = vec3(dot(rotation(angle), vec2(self.radius, 0))) target[2] = self.height # Hover controls drone.hover.target = target drone.hover.up = normalize(drone.position) drone.hover.step(self.dt) drone.controls = drone.hover.controls # If any bot got lost, now they have a chance to recover. if drone.on_ground: drone.controls.jump = True else: drone.controls.jump = False
def step(self, dt): if self.dodging: self.dodge.step(dt) self.controls = self.dodge.controls else: super().step(dt) if (self.arrive.arrival_time - self.car.time < self.dodge.jump.duration + 0.13 and abs(self.arrive.drive.target_speed - norm(self.car.velocity)) < 1000 and (dot(normalize(self.car.velocity), ground_direction(self.car, self.arrive.target)) > 0.95 or norm(self.car.velocity) < 500)): self.dodging = True if self.dodge.finished: self.finished = True