def game_tick_packet_to_rl_state(self, game_tick): my_car = game_tick.game_cars[0] car_location = Vec3(my_car.physics.location) car_rotation = Orientation(my_car.physics.rotation) car_velocity = Vec3(my_car.physics.velocity) ball_location = Vec3(game_tick.game_ball.physics.location) ball_location_rel = relative_location(car_location, car_rotation, ball_location) goal_location = Vec3(0, GameValues.GOAL_CENTER_Y, GameValues.GOAL_CENTER_Z) car_location_rel = relative_location(goal_location, car_rotation, car_location) dist = car_location.dist(ball_location)
def preprocess(self, gamePacket: GameTickPacket): """Calculates a set of values that may be useful. This function runs every tick, so it should not contain any operations that are slow. Additionally, the operations should be limited to what is necessary to have on every tick. Args: gamePacket (GameTickPacket): set of current information about the game Returns: This function updates the attributes of the class and therefore has no return type. """ #load data about self self.me.location = Vec3(gamePacket.game_cars[self.index].physics.location) self.me.velocity = Vec3(gamePacket.game_cars[self.index].physics.velocity) self.me.rotation = Orientation(gamePacket.game_cars[self.index].physics.rotation) self.me.rvelocity = Vec3(gamePacket.game_cars[self.index].physics.angular_velocity) self.me.boost = gamePacket.game_cars[self.index].boost #load data about the ball self.ball.location = Vec3(gamePacket.game_ball.physics.location) self.ball.velocity = Vec3(gamePacket.game_ball.physics.velocity) self.ball.rotation = Orientation(gamePacket.game_ball.physics.rotation) self.ball.rvelocity = Vec3(gamePacket.game_ball.physics.angular_velocity) self.ball.local_location = relative_location(self.me.location, self.me.rotation, self.ball.location)
def tick(self, info: MyInfo): print("first {}".format(self.done)) target = Vec3(self.info.ball_path[0].physics.location) relative = relative_location(Vec3(self.info.car.loc), Orientation(self.car.rot), target) if self.start_time + FLIP_TIME > info.seconds_elapsed: print("first jump") self.controls.jump = True #self.start_time = self.info.seconds_elapsed elif self.start_time + 2 * FLIP_TIME > self.info.seconds_elapsed: print("not jump") self.controls.jump = False self.controls.yaw = cos(relative.y / relative.x) elif self.start_time + 3 * FLIP_TIME > info.seconds_elapsed: print("second jump") if relative.x > 0: self.controls.pitch = -1 else: self.controls.pitch = 1 self.controls.yaw = cos(relative.y / relative.x) self.controls.jump = True if self.new_touch: # or self.start_time + 1.1 < info.seconds_elapsed: #print("start time: {}, current time: {}".format(self.start_time, info.seconds_elapsed)) self.done = True print(self.done) return self.controls
def execute(self, game_info: GameInformation): """Attempts to drive into the ball in a direction that hits the ball towards the goal. Overrides the State class's execute function. The ground controller is automatically used. The target location is on the outside of the ball on a line connecting the ball and the opponents goal. Attributes: game_info: information detailing the current status of various game objects """ team = util.sign(game_info.me.team) target_goal = util.GOAL_HOME * -team ball_to_goal = target_goal - game_info.ball.location # distance_to_goal = ball_to_goal.length() direction_to_goal = ball_to_goal.normalized() aim_location = game_info.ball.location - (direction_to_goal * util.BALL_RADIUS) local_target = relative_location(game_info.me.location, game_info.me.rotation, aim_location) self.debug['target'] = aim_location self.next_controller_state = ground_controller(game_info, local_target)
def checkExpired(self, agent, team): """If the ball is not reasonably close to being between the car and the goal, the state expires""" ballDirection = agent.ball.local_location goal_location = relative_location(agent.me.location, agent.me.rotation, util.GOAL_HOME*-team) angle = ballDirection.ang_to(goal_location) if angle < (math.pi / 2): return False return True
def checkAvailable(self, agent): """If the ball is between the car and the goal, it is possible to shoot""" ballDirection = agent.ball.local_location goal_location = relative_location(agent.me.location, agent.me.rotation, util.GOAL_HOME*-util.sign(agent.team)) angle = ballDirection.ang_to(goal_location) if angle < (math.pi / 2): return True return False
def car_location_angle_flattened(car, loc: Vec3): car_orientation = Orientation(car.rotation) relative_target = relative_location(car.location, car_orientation, loc).flat() # car_orientation_flat = car_orientation.forward.flat() car_direction = Vec3(10, 0, 0) angle_cos = (relative_target.dot(car_direction)) / ( car_direction.length() * relative_target.length()) return math.acos( angle_cos) if relative_target.y < 0 else -math.acos(angle_cos)
def check_expired(self, game_info: GameInformation): """If the ball is not reasonably close to being between the car and the goal, the state expires""" ball_direction = game_info.ball.local_location goal_location = relative_location( game_info.me.location, game_info.me.rotation, util.GOAL_HOME * -util.sign(game_info.me.team)) angle = ball_direction.ang_to(goal_location) if angle < (math.pi / 2): return False return True
def execute(self, agent): self.checkExpired(agent) team = util.sign(agent.team) ball_path = predict_ball_path(agent) danger = False for loc in ball_path: if(math.fabs(loc.y) > math.fabs(util.FIELD_LENGTH / 2)): danger = True target_location = agent.ball.local_location if danger: #aim to hit ball to the side #detect of ball is east or west of bot east_multiplier = util.sign(agent.ball.location.x - agent.me.location.x) #aim for side of the ball aim_location = agent.ball.location + Vec3(east_multiplier * util.BALL_RADIUS, 0, 0) target_location = relative_location(agent.me.location, agent.me.rotation, aim_location) elif agent.ball.local_location.length() > 1500: #get in goal target_location = relative_location(agent.me.location, agent.me.rotation, util.GOAL_HOME * team) elif agent.ball.local_location.length() < 500: return shotController(agent, util.GOAL_HOME * -team) return groundController(agent, target_location)
def tick(self, info : MyInfo) -> SimpleControllerState: self.new_touch(info) target = Vec3(self.info.ball_path[0].physics.location) offset = (Vec3(target) - self.info.their_goal.center).rescale(OFFSET_LEN) #print("in tick: target = {}".format(target)) relative = relative_location(Vec3(self.car.loc), Orientation(self.car.rot), target + offset) angle = atan2(relative.y, relative.x) other_ang = self.car.vel.ang_to(target) #print("relative: {}".format(relative)) #print("angle to ball: {}, other angle: {}".format(angle * 180 / pi, other_ang * 180 / pi)) self.controls.steer = self.steeringMagic(angle * 5) self.controls.throttle = 1 return self.controls
def execute(self, game_info): # aim to hit ball to the side # detect of ball is east or west of bot east_multiplier = util.sign(game_info.me.location.x - game_info.ball.location.x) # aim for side of the ball aim_location = game_info.ball.location + Vec3( east_multiplier * util.BALL_RADIUS, 0, 0) target_location = relative_location(game_info.me.location, game_info.me.rotation, aim_location) self.debug['target'] = aim_location self.next_controller_state = ground_controller(game_info, target_location)
def execute(self, agent): """Attempts to hit the ball in a way that pushes it toward the goal""" self.checkExpire(agent) team = util.sign(agent.team) targetGoal = util.GOAL_HOME * -team ball_to_goal = targetGoal - agent.ball.location #distance_to_goal = ball_to_goal.length() direction_to_goal = ball_to_goal.normalized() aim_location = agent.ball.location - (direction_to_goal * util.BALL_RADIUS) local_target = relative_location(agent.me.location, agent.me.rotation, aim_location) return groundController(agent, local_target)
def gym_to_rl_state(self, state): player = state.players[0] my_car = player.car_data ball = state.ball # car_location = Vec3(my_car.physics.location) car_location = Vec3(*my_car.position) car_rotation = OrientationGym(my_car) car_velocity = Vec3(*my_car.linear_velocity) ball_location = Vec3(*ball.position) ball_location_rel = relative_location(car_location, car_rotation, ball_location) goal_location = Vec3(0, GameValues.GOAL_CENTER_Y, GameValues.GOAL_CENTER_Z) car_location_rel = relative_location(goal_location, car_rotation, car_location) dist = car_location.dist(ball_location) self.game_tick = state # self.should_execute = self.update_throttle(state) or not self.executed self.rl_state = { InputOptions.BALL_DISTANCE: [dist], InputOptions.CAR_ORIENTATION: [ car_rotation.up.as_arr(), car_rotation.right.as_arr(), car_rotation.forward.as_arr() ], InputOptions.CAR_HEIGHT: [car_location.z], InputOptions.CAR_VELOCITY: car_velocity.as_arr(), InputOptions.JUMPED: [player.on_ground], InputOptions.DOUBLE_JUMPED: [player.has_flip], InputOptions.BALL_POSITION: ball_location.as_arr(), InputOptions.CAR_POSITION: car_location.as_arr(), InputOptions.BALL_POSITION_REL: ball_location_rel.as_arr(), InputOptions.BALL_DIRECTION: ball_location_rel.normalized().as_arr(), InputOptions.CAR_POSITION_REL: car_location_rel.as_arr(), InputOptions.CAR_VELOCITY_MAG: [car_velocity.length()], } return self.filter_states(self.rl_state)
def tick(self, info: MyInfo): self.new_touch(info) if self.can_challenge(): self.done = True #print("last touch: {}".format(self.last_touch)) ball = self.info.ball_path[0].physics target = Vec3(self.info.my_goal.center) - ball.location relative = relative_location(Vec3(self.car.loc), Orientation(self.car.rot), Vec3(ball.location)) angle = atan2(relative.y, relative.x) self.controls.steer = self.steeringMagic(angle) boost, throttle = self.match_speed(self.car, ball) self.controls.boost = boost self.controls.throttle = throttle return self.controls
def reward(self, rl_state): goal_location = Vec3(0, GameValues.GOAL_CENTER_Y, GameValues.GOAL_CENTER_Z) ball_velocity = Vec3(*self.game_tick.ball.linear_velocity) ball_position = Vec3(*rl_state[InputOptions.BALL_POSITION]) goal_direction = relative_location(ball_position, LeftOrientation(), goal_location).normalized() ball_velocity_towards_goal = max(ball_velocity.dot(goal_direction), 0) # Distance from ball (smallesr) ball_dist_value = reward_value( rl_state[InputOptions.BALL_DISTANCE][0], max_value=GameValues.FIELD_MAX_DISTANCE / 4, factor=0.5, invert=True, ) # Ball velocity in direction of goal (medium) ball_velocity_value = reward_value( ball_velocity_towards_goal, max_value=GameValues.BALL_MAX_VELOCITY - 2000, power=2, factor=3, ) # Goal value based on distance with time decay and min (largest) base_goal_value = 50 goals = self.game_tick.blue_score new_goal_scored = goals > self.goals_scored goal_value = reward_value( # ~x10 at 1sec -> x1 at end of episode base_goal_value * (self.max_timesteps / self.timestep), min_value=base_goal_value * int(new_goal_scored)) if new_goal_scored else 0 self.goals_scored = goals self.new_goal_scored = new_goal_scored self.throttled_log("Ball Dist: {0:.2f}".format(ball_dist_value)) self.throttled_log( "Ball Velocity: {0:.2f}".format(ball_velocity_value)) self.throttled_log("Goal: {0:.2f}".format(goal_value)) return ball_dist_value + ball_velocity_value + goal_value
def steer_toward_target(car: PlayerInfo, target: Vector) -> float: relative = relative_location(Vector(car.physics.location), Orientation(car.physics.rotation), target) angle = math.atan2(relative.y, relative.x) return limit_to_safe_range(angle * 5)
def shotController(agent, shotTarget): """Gives a set of commands to make the car shoot the ball This function will flip the car into the ball in order to make a shot and it will adjust the car's speed and positioning to help make the shot. Attributes: shotTarget (Vec3): The position that we want to hit the ball toward Returns: SimpleControllerState: the set of commands to achieve the goal """ controllerState = SimpleControllerState() #get ball distance and angle from car ball_direction = agent.ball.local_location ball_distance = agent.ball.local_location.flat().length() ball_angle = -math.atan2(ball_direction.y,ball_direction.x) if ball_angle > math.pi: ball_angle -= 2*math.pi elif ball_angle < -math.pi: ball_angle += 2*math.pi #get target distance and angle from ball ball_to_target = shotTarget - agent.ball.location target_distance = ball_to_target.length() ball_to_target_unit = ball_to_target.normalized() if(ball_distance < 400): flipReady = True else: flipReady = False #flipping if(flipReady): time_diff = time.time() - agent.timer1 if time_diff > 2.2: agent.timer1 = time.time() elif time_diff <= 0.1: #jump and turn toward the ball controllerState.jump = True if ball_angle > 0: controllerState.yaw = -1 elif ball_angle < 0: controllerState.yaw = 1 elif time_diff >= 0.1 and time_diff <= 0.15: #keep turning controllerState.jump = False if ball_angle > 0: controllerState.yaw = -1 elif ball_angle < 0: controllerState.yaw = 1 elif time_diff > 0.15 and time_diff < 1: #flip controllerState.jump = True if ball_angle > 0: controllerState.yaw = -1 elif ball_angle < 0: controllerState.yaw = 1 if math.fabs(ball_angle) > math.pi: controllerState.pitch = 1 else: controllerState.pitch = -1 else: flipReady = False controllerState.jump = False else: aim_location = agent.ball.location - (ball_to_target_unit * util.BALL_RADIUS) local_target = relative_location(agent.me.location, agent.me.rotation, aim_location) controllerState = groundController(agent, local_target) return controllerState
def steer_toward_target(car, target: Vec3) -> float: relative = relative_location(Vec3(car.location), Orientation(car.rotation), target) angle = math.atan2(relative.y, relative.x) return limit_to_safe_range(angle * 5)
def get_output(self, packet: GameTickPacket) -> SimpleControllerState: """ This function will be called by the framework many times per second. This is where you can see the motion of the ball, etc. and return controls to drive your car. """ # Keep our boost pad info updated with which pads are currently active self.boost_pad_tracker.update_boost_status(packet) # This is good to keep at the beginning of get_output. It will allow you to continue # any sequences that you may have started during a previous call to get_output. if self.active_sequence is not None and not self.active_sequence.done: controls = self.active_sequence.tick(packet) if controls is not None: return controls # Gather some information about our car and the ball my_car = packet.game_cars[self.index] car_location = Vec3(my_car.physics.location) car_velocity = Vec3(my_car.physics.velocity) ball_location = Vec3(packet.game_ball.physics.location) ball_velocity = Vec3(packet.game_ball.physics.velocity) # By default we will chase the ball, but target_location can be changed later target_location = ball_location ball_on_floor = target_location ball_prediction = self.get_ball_prediction_struct() # This can predict bounces, etc # dynamic time to the ball depending on the car's speed time_in_future = self.time_to_ball(car_location, car_velocity.length(), ball_location) seconds_in_future = packet.game_info.seconds_elapsed + time_in_future ball_in_future = find_slice_at_time(ball_prediction, seconds_in_future) ball_on_floor = find_matching_slice(ball_prediction, 0, lambda s: s.physics.location.z < 150 and s.game_seconds >= packet.game_info.seconds_elapsed + time_in_future, search_increment=1) time_to_floor = 0 # ball_in_future might be None if we don't have an adequate ball prediction right now, like during # replays, so check it to avoid errors. if ball_in_future is not None: target_location = Vec3(ball_in_future.physics.location) time_in_future = self.time_to_ball(car_location, car_velocity.length(), target_location) self.renderer.draw_line_3d(ball_location, target_location, self.renderer.cyan()) # gets the next time when the ball is on the floor if ball_on_floor is not None: floor_location = Vec3(ball_on_floor.physics.location) time_to_floor = ball_on_floor.game_seconds - packet.game_info.seconds_elapsed self.renderer.draw_line_3d(ball_location, floor_location, self.renderer.orange()) self.renderer.draw_rect_3d(floor_location, 8, 8, True, self.renderer.orange(), centered=True) # Draw some things to help understand what the bot is thinking self.renderer.draw_line_3d(car_location, target_location, self.renderer.white()) self.renderer.draw_rect_3d(target_location, 8, 8, True, self.renderer.cyan(), centered=True) orientation = Orientation(my_car.physics.rotation) relative = relative_location(car_location, orientation, ball_location) controls = SimpleControllerState() self.renderer.draw_string_2d(10, 10 + self.team * 100, 5, 5, f"{controls.throttle}", self.renderer.team_color()) # makes the car rotate to be more straight if not my_car.has_wheel_contact: # roll to land on all four wheels if orientation.roll < -0.1: controls.roll = 1 elif orientation.roll > 0.1: controls.roll = -1 # pitch to land on all four wheels if orientation.pitch < -0.1: controls.pitch = 1 elif orientation.pitch > 0.1: controls.pitch = -1 deg = math.degrees(car_location.ang_to(ball_location)) # yaw to correct angle towards ball if deg < 85: controls.yaw = 1 elif deg > 95: controls.yaw = -1 # jump if another car is close to not get stuck if self.location_to_nearest_car(car_location, my_car.team, packet).dist(car_location) < 200 and car_velocity.length() < 50: controls.jump = True self.set_kickoff_state(car_velocity, ball_location, ball_velocity) self.decide_state(controls, packet, my_car, car_location, car_velocity, ball_location, ball_velocity, target_location, ball_prediction, orientation, relative, time_in_future, time_to_floor) return controls