From c345cca15cf9ac0b6fa4d3dd4e3b72276c893d4a Mon Sep 17 00:00:00 2001 From: "Khalil H." <81334438+Khalil-Hawari@users.noreply.github.com> Date: Tue, 4 Feb 2025 02:06:42 -0500 Subject: [PATCH] Revised bootcamp code --- modules/bootcamp/decision_example.py | 168 ++++----- modules/bootcamp/decision_simple_waypoint.py | 176 +++++----- .../decision_waypoint_landing_pads.py | 326 +++++++++--------- 3 files changed, 327 insertions(+), 343 deletions(-) diff --git a/modules/bootcamp/decision_example.py b/modules/bootcamp/decision_example.py index 74f3d3ae..b928f068 100644 --- a/modules/bootcamp/decision_example.py +++ b/modules/bootcamp/decision_example.py @@ -1,84 +1,84 @@ -""" -BOOTCAMPERS DO NOT MODIFY THIS FILE. - -Example decision with figure 8. -""" - -from .. import commands -from .. import drone_report -from .. import drone_status -from .. import location -from ..private.decision import base_decision - - -# Disable for bootcamp use -# No enable -# pylint: disable=duplicate-code,unused-argument - - -class DecisionExample(base_decision.BaseDecision): - """ - Example of sending commands to the drone. - """ - - def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: - """ - Initialize all persistent variables here with self. - """ - self.waypoint = waypoint - print(f"Waypoint: {waypoint}") - - self.acceptance_radius = acceptance_radius - - self.command_index = 0 - self.commands = [ - commands.Command.create_set_relative_destination_command(50.0, 37.5), - commands.Command.create_set_relative_destination_command(0.0, -75.0), - commands.Command.create_set_relative_destination_command(-50.0, 37.5), - commands.Command.create_set_relative_destination_command(-50.0, -37.5), - commands.Command.create_set_relative_destination_command(0.0, 75.0), - commands.Command.create_set_relative_destination_command(50.0, -37.5), - commands.Command.create_set_relative_destination_command(-50.0, 0.0), - commands.Command.create_set_relative_destination_command(50.0, 0.0), - ] - - self.has_sent_landing_command = False - - self.counter = 0 - - def run( - self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" - ) -> commands.Command: - """ - Makes the drone fly in a figure 8, then land. - - This method will be called in an infinite loop, something like this: - - ```py - while True: - report, landing_pad_locations = get_input() - command = Decision.run(report, landing_pad_locations) - put_output(command) - ``` - """ - # Default command - command = commands.Command.create_null_command() - - if report.status == drone_status.DroneStatus.HALTED and self.command_index < len( - self.commands - ): - # Print some information for debugging - print(self.counter) - print(self.command_index) - print(f"Halted at: {report.position}") - - command = self.commands[self.command_index] - self.command_index += 1 - elif report.status == drone_status.DroneStatus.HALTED and not self.has_sent_landing_command: - command = commands.Command.create_land_command() - - self.has_sent_landing_command = True - - self.counter += 1 - - return command +""" +BOOTCAMPERS DO NOT MODIFY THIS FILE. + +Example decision with figure 8. +""" + +from .. import commands +from .. import drone_report +from .. import drone_status +from .. import location +from ..private.decision import base_decision + + +# Disable for bootcamp use +# No enable +# pylint: disable=duplicate-code,unused-argument + + +class DecisionExample(base_decision.BaseDecision): + """ + Example of sending commands to the drone. + """ + + def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: + """ + Initialize all persistent variables here with self. + """ + self.waypoint = waypoint + print(f"Waypoint: {waypoint}") + + self.acceptance_radius = acceptance_radius + + self.command_index = 0 + self.commands = [ + commands.Command.create_set_relative_destination_command(50.0, 37.5), + commands.Command.create_set_relative_destination_command(0.0, -75.0), + commands.Command.create_set_relative_destination_command(-50.0, 37.5), + commands.Command.create_set_relative_destination_command(-50.0, -37.5), + commands.Command.create_set_relative_destination_command(0.0, 75.0), + commands.Command.create_set_relative_destination_command(50.0, -37.5), + commands.Command.create_set_relative_destination_command(-50.0, 0.0), + commands.Command.create_set_relative_destination_command(50.0, 0.0), + ] + + self.has_sent_landing_command = False + + self.counter = 0 + + def run( + self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" + ) -> commands.Command: + """ + Makes the drone fly in a figure 8, then land. + + This method will be called in an infinite loop, something like this: + + ```py + while True: + report, landing_pad_locations = get_input() + command = Decision.run(report, landing_pad_locations) + put_output(command) + ``` + """ + # Default command + command = commands.Command.create_null_command() + + if report.status == drone_status.DroneStatus.HALTED and self.command_index < len( + self.commands + ): + # Print some information for debugging + print(self.counter) + print(self.command_index) + print(f"Halted at: {report.position}") + + command = self.commands[self.command_index] + self.command_index += 1 + elif report.status == drone_status.DroneStatus.HALTED and not self.has_sent_landing_command: + command = commands.Command.create_land_command() + + self.has_sent_landing_command = True + + self.counter += 1 + + return command diff --git a/modules/bootcamp/decision_simple_waypoint.py b/modules/bootcamp/decision_simple_waypoint.py index a5838a70..8923e494 100644 --- a/modules/bootcamp/decision_simple_waypoint.py +++ b/modules/bootcamp/decision_simple_waypoint.py @@ -1,90 +1,86 @@ -""" -BOOTCAMPERS TO COMPLETE. - -Travel to designated waypoint. -""" - -from .. import commands -from .. import drone_report - -# Disable for bootcamp use -# pylint: disable-next=unused-import -from .. import drone_status -from .. import location -from ..private.decision import base_decision - - -# Disable for bootcamp use -# No enable -# pylint: disable=duplicate-code,unused-argument - - -class DecisionSimpleWaypoint(base_decision.BaseDecision): - """ - Travel to the designed waypoint. - """ - - def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: - """ - Initialize all persistent variables here with self. - """ - self.waypoint = waypoint - print(f"Waypoint: {waypoint}") - - self.acceptance_radius = acceptance_radius - - # ============ - # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ - # ============ - - # Write here - - # ============ - # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ - # ============ - - def run( - self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" - ) -> commands.Command: - """ - Make the drone fly to the waypoint. - - You are allowed to create as many helper methods as you want, - as long as you do not change the __init__() and run() signatures. - - This method will be called in an infinite loop, something like this: - - ```py - while True: - report, landing_pad_locations = get_input() - command = Decision.run(report, landing_pad_locations) - put_output(command) - ``` - """ - # Default command - command = commands.Command.create_null_command() - - # ============ - # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ - # ============ - - # Do something based on the report and the state of this class... - - if report.status == drone_status.DroneStatus.HALTED: - delta_x = report.position.location_x - self.waypoint.location_x - delta_y = report.position.location_y - self.waypoint.location_y - on_waypoint = delta_x**2 + delta_y**2 < self.acceptance_radius**2 - - if on_waypoint: - command = commands.Command.create_land_command() - - else: - command = commands.Command.create_set_relative_destination_command( - self.waypoint.location_x, self.waypoint.location_y - ) - - # ============ - # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ - # ============ - - return command +""" +BOOTCAMPERS TO COMPLETE. + +Travel to designated waypoint. +""" + +from .. import commands +from .. import drone_report + +# Disable for bootcamp use +# pylint: disable-next=unused-import +from .. import drone_status +from .. import location +from ..private.decision import base_decision + + +# Disable for bootcamp use +# No enable +# pylint: disable=duplicate-code,unused-argument + + +class DecisionSimpleWaypoint(base_decision.BaseDecision): + """ + Travel to the designed waypoint. + """ + + def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: + """ + Initialize all persistent variables here with self. + """ + self.waypoint = waypoint + print(f"Waypoint: {waypoint}") + + self.acceptance_radius = acceptance_radius + + # ============ + # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ + # ============ + + # Write here + + # ============ + # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ + # ============ + + def run( + self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" + ) -> commands.Command: + """ + Make the drone fly to the waypoint. + + You are allowed to create as many helper methods as you want, + as long as you do not change the __init__() and run() signatures. + + This method will be called in an infinite loop, something like this: + + ```py + while True: + report, landing_pad_locations = get_input() + command = Decision.run(report, landing_pad_locations) + put_output(command) + ``` + """ + # Default command + command = commands.Command.create_null_command() + + # ============ + # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ + # ============ + + if report.status == drone_status.DroneStatus.HALTED: + delta_x = self.waypoint.location_x - report.position.location_x + delta_y = self.waypoint.location_y - report.position.location_y + on_waypoint = delta_x**2 + delta_y**2 < self.acceptance_radius**2 + + if on_waypoint: + command = commands.Command.create_land_command() + + else: + command = commands.Command.create_set_relative_destination_command(delta_x, delta_y) + + # ============ + # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ + # ============ + + return command diff --git a/modules/bootcamp/decision_waypoint_landing_pads.py b/modules/bootcamp/decision_waypoint_landing_pads.py index d4030cba..977045fd 100644 --- a/modules/bootcamp/decision_waypoint_landing_pads.py +++ b/modules/bootcamp/decision_waypoint_landing_pads.py @@ -1,169 +1,157 @@ -""" -BOOTCAMPERS TO COMPLETE. - -Travel to designated waypoint and then land at a nearby landing pad. -""" - -from .. import commands -from .. import drone_report - -# Disable for bootcamp use -# pylint: disable-next=unused-import -from .. import drone_status -from .. import location -from ..private.decision import base_decision - - -# Disable for bootcamp use -# No enable -# pylint: disable=duplicate-code,unused-argument - - -class DecisionWaypointLandingPads(base_decision.BaseDecision): - """ - Travel to the designed waypoint and then land at the nearest landing pad. - """ - - def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: - """ - Initialize all persistent variables here with self. - """ - self.waypoint = waypoint - print(f"Waypoint: {waypoint}") - - self.acceptance_radius = acceptance_radius - - # ============ - # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ - # ============ - - self.above_destination = False - self.target_lp = None - - self.approaching_wp = False - self.approaching_lp = False - - # ============ - # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ - # ============ - - def run( - self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" - ) -> commands.Command: - """ - Make the drone fly to the waypoint and then land at the nearest landing pad. - - You are allowed to create as many helper methods as you want, - as long as you do not change the __init__() and run() signatures. - - This method will be called in an infinite loop, something like this: - - ```py - while True: - report, landing_pad_locations = get_input() - command = Decision.run(report, landing_pad_locations) - put_output(command) - ``` - """ - # ============ - # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ - # ============ - - # print("RUN") - # Default command - command = commands.Command.create_null_command() - - if report.status == drone_status.DroneStatus.HALTED: - print("HALTED AT: ", report.position.location_x, report.position.location_y) - print( - "DESTINATION WAS: ", report.destination.location_x, report.destination.location_y - ) - - # Handle unexpected halts - delta_x = report.position.location_x - report.destination.location_x - delta_y = report.position.location_y - report.destination.location_y - self.above_destination = delta_x**2 + delta_y**2 < self.acceptance_radius**2 - - if not self.above_destination: - print("^^UNEXPECTED HALT") - command = commands.Command.create_set_relative_destination_command( - report.destination.location_x - report.position.location_x, - report.destination.location_y - report.position.location_y, - ) - - elif not self.approaching_wp: - command = commands.Command.create_set_relative_destination_command( - self.waypoint.location_x, self.waypoint.location_y - ) - self.approaching_wp = True - print("APPROACHING WP: ", self.waypoint.location_x, self.waypoint.location_y) - - elif not self.approaching_lp: - self.target_lp = self.find_closest_lp(report, landing_pad_locations) - lp_relative_x, lp_relative_y = self.relative_target(self.target_lp, report) - - command = commands.Command.create_set_relative_destination_command( - lp_relative_x, lp_relative_y - ) - self.approaching_lp = True - print("APPROACHING LP: ", self.target_lp.location_x, self.target_lp.location_y) - - else: - command = commands.Command.create_land_command() - print("LANDING") - - return command - - # ============ - # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ - # ============ - - def find_closest_lp( - self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" - ) -> int: - """ - Given a list of landing pads, - determines the index of the closest one to the waypoint. - """ - if len(landing_pad_locations) == 1: - return landing_pad_locations[0] - - drone_x = report.position.location_x - drone_y = report.position.location_y - - i = 0 - winning_pad = landing_pad_locations[0] - winning_sq_dist = float("inf") - - for lp in landing_pad_locations: - lp_x = lp.location_x - lp_y = lp.location_y - - delta_x = lp_x - drone_x - delta_y = lp_y - drone_y - - sq_dist = delta_x**2 + delta_y**2 - - if sq_dist < winning_sq_dist: - winning_sq_dist = sq_dist - winning_pad = landing_pad_locations[i] - - i += 1 - - # print("TARGET LP FOUND, INDEX: ", winning_i) - # print("^^SQDIST FROM WP: ", winning_dist) - return winning_pad - - @staticmethod - def relative_target( - target: location.Location, report: drone_report.DroneReport - ) -> tuple[int, int]: - """ - Returns the x and y of a target relative to the drone's current position - """ - - diff_x = target.location_x - report.position.location_x - diff_y = target.location_y - report.position.location_y - - # print("RELATIVE TARGET: ", diff_x, diff_y) - return diff_x, diff_y +""" +BOOTCAMPERS TO COMPLETE. + +Travel to designated waypoint and then land at a nearby landing pad. +""" + +from .. import commands +from .. import drone_report + +# Disable for bootcamp use +# pylint: disable-next=unused-import +from .. import drone_status +from .. import location +from ..private.decision import base_decision + + +# Disable for bootcamp use +# No enable +# pylint: disable=duplicate-code,unused-argument + + +class DecisionWaypointLandingPads(base_decision.BaseDecision): + """ + Travel to the designed waypoint and then land at the nearest landing pad. + """ + + def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: + """ + Initialize all persistent variables here with self. + """ + self.waypoint = waypoint + print(f"Waypoint: {waypoint}") + + self.acceptance_radius = acceptance_radius + + # ============ + # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ + # ============ + + self.above_destination = False + self.target_lp = None + + self.approaching_wp = False + self.approaching_lp = False + + # ============ + # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ + # ============ + + def run( + self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" + ) -> commands.Command: + """ + Make the drone fly to the waypoint and then land at the nearest landing pad. + + You are allowed to create as many helper methods as you want, + as long as you do not change the __init__() and run() signatures. + + This method will be called in an infinite loop, something like this: + + ```py + while True: + report, landing_pad_locations = get_input() + command = Decision.run(report, landing_pad_locations) + put_output(command) + ``` + """ + # Default command + command = commands.Command.create_null_command() + + # ============ + # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ + # ============ + + if report.status == drone_status.DroneStatus.HALTED: + print(f"HALTED AT: ({report.position.location_x}, {report.position.location_y})") + print( + f"DESTINATION WAS: ({report.destination.location_x}, {report.destination.location_y})" + ) + + # Handle unexpected halts + delta_x = report.destination.location_x - report.position.location_x + delta_y = report.destination.location_y - report.position.location_y + self.above_destination = delta_x**2 + delta_y**2 < self.acceptance_radius**2 + + if not self.above_destination: + print("^^UNEXPECTED HALT") + command = commands.Command.create_set_relative_destination_command(delta_x, delta_y) + + # Go to waypoint + elif not self.approaching_wp: + waypoint_delta_x = self.waypoint.location_x - report.position.location_x + waypoint_delta_y = self.waypoint.location_y - report.position.location_y + + command = commands.Command.create_set_relative_destination_command( + waypoint_delta_x, waypoint_delta_y + ) + self.approaching_wp = True + print("APPROACHING WP: ", self.waypoint.location_x, self.waypoint.location_y) + + # Find and go to nearest landing pad + elif not self.approaching_lp: + self.target_lp = self.find_closest_lp(report, landing_pad_locations) + + if self.target_lp: + lp_delta_x = self.target_lp.location_x - report.position.location_x + lp_delta_y = self.target_lp.location_y - report.position.location_y + + command = commands.Command.create_set_relative_destination_command( + lp_delta_x, lp_delta_y + ) + self.approaching_lp = True + print("APPROACHING LP: ", self.target_lp.location_x, self.target_lp.location_y) + + # Give a null command if there are no landing pads in landing_pad_locations + else: + command = commands.Command.create_null_command() + + # Landing + else: + command = commands.Command.create_land_command() + print("LANDING") + + return command + + # ============ + # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ + # ============ + + def find_closest_lp( + self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" + ) -> int: + """ + Given a list of landing pads, + determines the index of the closest one to the waypoint. + """ + # If there's only one pad, default to that one + if len(landing_pad_locations) == 1: + return landing_pad_locations[0] + + i = 0 + winning_pad = None + winning_sq_dist = float("inf") + + for lp in landing_pad_locations: + delta_x = report.position.location_x - lp.location_x + delta_y = report.position.location_y - lp.location_y + + sq_dist = delta_x**2 + delta_y**2 + + if sq_dist < winning_sq_dist: + winning_sq_dist = sq_dist + winning_pad = landing_pad_locations[i] + + i += 1 + + return winning_pad