class Robot: def __init__(self, is_one: bool, team: Team): self.is_one = is_one self.team = team self.center = FIELD.spawn_center.mirror(team.is_blue, team.is_blue == is_one) self.rotation = 0. if team.is_blue else math.pi self.gimbal_yaw = 0. self.speed = Vector(0., 0.) self.rotation_speed = 0. self.gimbal_yaw_speed = 0. self.ammo = 50 if is_one else 0 self.is_shooting = False self.shot_cooldown = 0 self.heat = 0 self.hp = 2000 self.barrier_hits = 0 self.robot_hits = 0 self.can_move = True self.can_shoot = True self.debuff_timeout = 0 self._corners = [ c.transform(self.center, self.rotation) for c in ROBOT.outline.corners ] self._armor_lines = [ a.transform(self.center, self.rotation) for a in ROBOT.armor_lines ] def absorbs_bullet(self, trajectory: LineSegment): if self.hp: for armor_line, armor_damage in zip(self._armor_lines, ROBOT.armor_damages): if trajectory.intersects(armor_line): self.hp -= armor_damage self.team.take_damage(armor_damage) return True return ROBOT.outline.intersects( trajectory.inv_transform(self.center, self.rotation)) def hits_barrier(self, barrier: Box): if self.center.distance_to(barrier.center) < ROBOT.outline.radius + barrier.radius and \ (any(barrier.contains(p) for p in self._corners) or any(ROBOT.outline.contains(p.inv_transform(self.center, self.rotation)) for p in barrier.corners)): self.barrier_hits += 1 return True return False def hits_robot(self, robot: Robot): if self.center.distance_to(robot.center) < 2 * ROBOT.outline.radius and \ (any(ROBOT.outline.contains(p.inv_transform(robot.center, robot.rotation)) for p in self._corners) or any(ROBOT.outline.contains(p.inv_transform(self.center, self.rotation)) for p in robot._corners)): self.robot_hits += 1 return True return False def hits(self, robots: tuple[Robot, Robot, Robot, Robot]): return any([ any(not FIELD.outline.contains(c) for c in self._corners), any( self.hits_barrier(b) for b in [*FIELD.low_barriers, *FIELD.high_barriers]), any(self.hits_robot(r) for r in robots if r != self) ]) def shoot(self): if all([ self.is_shooting, not self.shot_cooldown, self.ammo, self.can_shoot ]): self.ammo -= 1 self.heat += BULLET.speed self.shot_cooldown = ROBOT.shot_cooldown return Bullet(self) def control(self, command: RobotCommand): if not (self.hp and self.can_move): command.x_speed = 0. command.y_speed = 0. command.rotation_speed = 0. if not (self.hp and self.can_shoot): command.gimbal_yaw_speed = 0. command.shoot = False x_accel = self._accel_required(self.speed.x, command.x_speed, ROBOT.drive_config) y_accel = self._accel_required(self.speed.y, command.y_speed, ROBOT.drive_config) rotation_accel = self._accel_required(self.rotation_speed, command.rotation_speed, ROBOT.rotation_config) gimbal_yaw_accel = self._accel_required(self.gimbal_yaw_speed, command.gimbal_yaw_speed, ROBOT.gimbal_yaw_config) x_accel, y_accel, rotation_accel = self._limit_holonomic( x_accel, y_accel, rotation_accel, ROBOT.drive_config.top_accel, ROBOT.drive_config.top_accel, ROBOT.rotation_config.top_accel) self.speed.x = self._new_speed(self.speed.x, x_accel, ROBOT.drive_config) self.speed.y = self._new_speed(self.speed.y, y_accel, ROBOT.drive_config) self.rotation_speed = self._new_speed(self.rotation_speed, rotation_accel, ROBOT.rotation_config) self.gimbal_yaw_speed = self._new_speed(self.gimbal_yaw_speed, gimbal_yaw_accel, ROBOT.gimbal_yaw_config) self.is_shooting = command.shoot def step(self, time_remaining: float, robots: tuple[Robot, Robot, Robot, Robot]): self.debuff_timeout -= min(1, self.debuff_timeout) self.shot_cooldown -= min(1, self.shot_cooldown) if not time_remaining % (0.1 * S): self.settle_heat() if not self.debuff_timeout: self.can_move = True self.can_shoot = True if not self.hp: return if any([self.speed.x, self.speed.y, self.rotation_speed]): old_center, old_rotation, old_corners = self.center.copy( ), self.rotation, self._corners.copy() self.center += self.speed.transform(angle=old_rotation) self.rotation = (self.rotation + self.rotation_speed) % (360 * D) self._corners = [ p.transform(self.center, self.rotation) for p in ROBOT.outline.corners ] if self.hits(robots): self.rotation_speed *= -ROBOT.rebound_coeff self.speed *= -ROBOT.rebound_coeff self.center, self.rotation, self._corners = old_center, old_rotation, old_corners self.gimbal_yaw = (self.gimbal_yaw + self.gimbal_yaw_speed) % (360 * D) self._armor_lines = [ a.transform(self.center, self.rotation) for a in ROBOT.armor_lines ] def settle_heat(self): # rules 4.1.2 self.heat = max(self.heat - (12 if self.hp >= 400 else 24), 0) if 240 < self.heat < 360: self.hp -= (self.heat - 240) * 4 elif 360 <= self.heat: self.hp -= (self.heat - 360) * 40 self.heat = 360 self.hp = max(self.hp, 0) def apply_hp_buff(self): self.hp += 200 def apply_ammo_buff(self): self.ammo += 100 def apply_move_debuff(self): self.can_move = False self.debuff_timeout = 10 * S def apply_shoot_debuff(self): self.can_shoot = False self.debuff_timeout = 10 * S @staticmethod def _new_speed(current_speed: float, accel: float, config: MotionConfig): new_speed_ideal = current_speed + accel new_speed_magnitude = abs( new_speed_ideal ) - config.friction_decel - abs(current_speed) * config.friction_coeff return math.copysign(max(0., new_speed_magnitude), new_speed_ideal) @staticmethod def _accel_required(current_speed: float, desired_speed: float, config: MotionConfig): if math.isclose(current_speed, 0, rel_tol=ROBOT.zero_tolerance) and desired_speed == 0.: return 0. new_speed = limit_magnitude(desired_speed, config.top_speed) accel = new_speed - current_speed + math.copysign( config.friction_decel, current_speed) + current_speed * config.friction_coeff return limit_magnitude(accel, config.top_accel) @staticmethod def _limit_holonomic(x: float, y: float, z: float, x_max: float, y_max: float, z_max: float): magnitude = max(abs(x / x_max) + abs(y / y_max) + abs(z / z_max), 1) return x / magnitude, y / magnitude, z / magnitude
def __init__(self, owner: Robot): self.owner = owner self.center = owner.center.copy() relative_speed = Vector(random.gauss(BULLET.speed, BULLET.sigma_x_speed), random.gauss(0, BULLET.sigma_y_speed)) self.speed = relative_speed.transform(owner.speed, owner.rotation + owner.gimbal_yaw)