def _to_state(board: PodBoard, pod: PodState) -> Tuple[int, int, int, int]: vel = pod.vel.rotate(-pod.angle) check1 = (board.get_check(pod.nextCheckId) - pod.pos).rotate(-pod.angle) return ( _discretize(vel.x / Constants.max_vel(), 10), _discretize(vel.y / Constants.max_vel(), 10), _discretize(check1.x / MAX_DIST, 30), _discretize(check1.y / MAX_DIST, 30), )
def play(self, pod: PodState) -> PlayOutput: check1 = self.board.checkpoints[pod.nextCheckId] check2 = self.board.get_check(pod.nextCheckId + 1) c1_to_p = (pod.pos - check1) c1_to_p_len = c1_to_p.length() c1_to_c2 = (check2 - check1) c1_to_c2_len = c1_to_c2.length() midpoint = ((c1_to_p / c1_to_c2_len) - (c1_to_c2 / c1_to_c2_len)).normalize() target = check1 if c1_to_p_len > Constants.max_vel() * 6: # Still far away. Aim for a point that will help us turn toward the next check target = target + (midpoint * Constants.check_radius() * 2) # else: We're getting close to the check. Stop fooling around and go to it. # OK, now we've got a target point. Do whatever it takes to get there. pod_to_target = target - pod.pos ang_diff_to_target = math.fabs( clean_angle(math.fabs(pod.angle - pod_to_target.angle()))) if ang_diff_to_target < 2 * Constants.max_turn(): thrust = Constants.max_thrust() elif ang_diff_to_target < 4 * Constants.max_turn(): thrust = (ang_diff_to_target - (4 * Constants.max_turn())) / ( 2 * Constants.max_turn()) * Constants.max_thrust() else: thrust = 0 return PlayOutput(target - (2 * pod.vel), thrust)
def to_vector(self, board: PodBoard, pod: PodState) -> List[float]: # Velocity is already relative to the pod, so it just needs to be rotated vel = pod.vel.rotate(-pod.angle) / Constants.max_vel() check1 = (board.get_check(pod.nextCheckId) - pod.pos).rotate(-pod.angle) / MAX_DIST return [vel.x, vel.y, check1.x, check1.y]
def test_state_to_vector_works2(self): # A pod at (-100, -100) pointing up +Y, moving 45 degrees down-left pod = PodState(Vec2(-100, -100), Vec2(-3, -3), math.pi / 2) # The target checkpoint is directly in front board = PodBoard([Vec2(-100, 1000), ORIGIN]) state = state_to_vector(pod, board) self.assertEqual(len(state), STATE_VECTOR_LEN) self.assertAlmostEqual(state[0], -3 / Constants.max_vel(), msg="velocity x") self.assertAlmostEqual(state[1], 3 / Constants.max_vel(), msg="velocity y") self.assertAlmostEqual(state[2], 1100 / MAX_DIST, msg="check1 x") self.assertAlmostEqual(state[3], 0, msg="check1 y")
def speed_reward(board: PodBoard, next_pod: PodState) -> float: """ Indicates how much the speed is taking us toward the next check (scaled). """ pod_to_check = board.checkpoints[next_pod.nextCheckId] - next_pod.pos dist_to_check = pod_to_check.length() # a*b = |a|*|b|*cos # Thus, vel*check / dist = how much the vel is taking us toward the check return (next_pod.vel * pod_to_check) / (dist_to_check * Constants.max_vel())
def test_state_to_vector_works1(self): # A pod at (100, 100) pointing down -X, moving full speed +Y pod = PodState(Vec2(100, 100), Vec2(0, Constants.max_vel()), -math.pi) # The target checkpoint is directly behind it board = PodBoard([Vec2(100 + MAX_DIST, 100), ORIGIN]) state = state_to_vector(pod, board) self.assertEqual(len(state), STATE_VECTOR_LEN) self.assertAlmostEqual(state[0], 0, msg="velocity x") self.assertAlmostEqual(state[1], -1, msg="velocity y") self.assertAlmostEqual(state[2], -1, msg="check1 x") self.assertAlmostEqual(state[3], 0, msg="check1 y")
def random() -> 'PodState': return PodState(pos=Vec2.random(Constants.world_x(), Constants.world_y()), vel=UNIT.rotate(2 * math.pi * random()) * (random() * Constants.max_vel()), angle=2 * math.pi * random())