From dbd64ef740338a058a5bb52fbf145c614448bf38 Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sat, 21 Feb 2026 12:28:09 -0800 Subject: [PATCH 01/21] convert stop play to fsm --- src/software/ai/hl/stp/play/BUILD | 12 +- src/software/ai/hl/stp/play/play.h | 26 +++++ src/software/ai/hl/stp/play/stop_play.cpp | 109 ++---------------- src/software/ai/hl/stp/play/stop_play.h | 3 +- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 67 +++++++++++ src/software/ai/hl/stp/play/stop_play_fsm.h | 62 ++++++++++ 6 files changed, 176 insertions(+), 103 deletions(-) create mode 100644 src/software/ai/hl/stp/play/stop_play_fsm.cpp create mode 100644 src/software/ai/hl/stp/play/stop_play_fsm.h diff --git a/src/software/ai/hl/stp/play/BUILD b/src/software/ai/hl/stp/play/BUILD index 4980c32a0d..455c84c3bf 100644 --- a/src/software/ai/hl/stp/play/BUILD +++ b/src/software/ai/hl/stp/play/BUILD @@ -66,16 +66,20 @@ cc_library( cc_library( name = "stop_play", - srcs = ["stop_play.cpp"], - hdrs = ["stop_play.h"], + srcs = [ + "stop_play.cpp", + "stop_play_fsm.cpp", + ], + hdrs = [ + "stop_play.h", + "stop_play_fsm.h", + ], deps = [ ":play", "//shared:constants", - "//software/ai/evaluation:enemy_threat", "//software/ai/hl/stp/tactic/crease_defender:crease_defender_tactic", "//software/ai/hl/stp/tactic/goalie:goalie_tactic", "//software/ai/hl/stp/tactic/move:move_tactic", - "//software/logger", "//software/util/generic_factory", ], alwayslink = True, diff --git a/src/software/ai/hl/stp/play/play.h b/src/software/ai/hl/stp/play/play.h index 5eee4f25ba..f5002f02ac 100644 --- a/src/software/ai/hl/stp/play/play.h +++ b/src/software/ai/hl/stp/play/play.h @@ -190,3 +190,29 @@ class Play RobotNavigationObstacleFactory obstacle_factory; }; + +/** + * Base class for plays that are driven by an FSM. Holds the FSM instance and + * control params, and implements updateTactics by processing PlayUpdate events. + * + * @tparam FSM_T The FSM struct type (e.g. StopPlayFSM). Must define ControlParams + * and an Update struct, and be constructible from TbotsProto::AiConfig. + */ +template +class PlayBase : public Play +{ + public: + explicit PlayBase(TbotsProto::AiConfig ai_config, bool requires_goalie) + : Play(ai_config, requires_goalie), fsm(FSM_T(ai_config)), control_params{} + { + } + + void updateTactics(const PlayUpdate& play_update) override + { + fsm.process_event(FSM_T::Update(control_params, play_update)); + } + + protected: + FSM fsm; + typename FSM_T::ControlParams control_params; +}; diff --git a/src/software/ai/hl/stp/play/stop_play.cpp b/src/software/ai/hl/stp/play/stop_play.cpp index 4488bfea79..b1cf16d309 100644 --- a/src/software/ai/hl/stp/play/stop_play.cpp +++ b/src/software/ai/hl/stp/play/stop_play.cpp @@ -1,111 +1,24 @@ #include "software/ai/hl/stp/play/stop_play.h" -#include "shared/constants.h" -#include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h" #include "software/ai/hl/stp/tactic/goalie/goalie_tactic.h" -#include "software/ai/hl/stp/tactic/move/move_tactic.h" #include "software/util/generic_factory/generic_factory.h" -StopPlay::StopPlay(TbotsProto::AiConfig config) : Play(config, true) {} +StopPlay::StopPlay(TbotsProto::AiConfig config) : PlayBase(config, true) +{ + goalie_tactic = + std::make_shared(ai_config, + TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND); +} void StopPlay::getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) { - // Robot assignments for the Stop Play - // - 1 robot will be the goalie - // - 2 robots will assist the goalie in blocking the ball, they will snap - // to the best fit semicircle around the defense area - // - 3 robots will stay within 0.5m of the ball, evenly spaced, also blocking the - // goal - // - // If x represents the ball and G represents the goalie, the following, also blocking - // the goal diagram depicts a possible outcome of this play - // - // +--------------------+--------------------+ - // | | | - // | 4 x | | - // | 0 2 | | - // +--+ 1 3 | +--+ - // | | | | | - // |G | +-+-+ | | - // | | | | | | - // | | +-+-+ | | - // | | | | | - // +--+ | +--+ - // | | | - // | | | - // | | | - // +--------------------+--------------------+ - - - TbotsProto::MaxAllowedSpeedMode stop_mode = - TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND; - - std::vector> move_tactics = { - std::make_shared(), std::make_shared(), - std::make_shared()}; - - goalie_tactic = std::make_shared(ai_config, stop_mode); - std::array, 2> crease_defender_tactics = { - std::make_shared(ai_config), - std::make_shared(ai_config), - }; - - do + // This function doesn't get called and will be removed when coroutines are phased + // out. + while (true) { - PriorityTacticVector result = {{}}; - - // a unit vector from the center of the goal to the ball, this vector will be used - // for positioning all the robots (excluding the goalie). The positioning vector - // will be used to position robots tangent to the goal_to_ball_unit_vector - Vector goal_to_ball_unit_vector = - (world_ptr->field().friendlyGoalCenter() - world_ptr->ball().position()) - .normalize(); - Vector robot_positioning_unit_vector = goal_to_ball_unit_vector.perpendicular(); - - // ball_defense_point_center is a point on the circle around the ball that the - // line from the center of the goal to the ball intersects. A robot will be placed - // on that line, and the other two will be on either side - // We add an extra robot radius as a buffer to be extra safe we don't break any - // rules by getting too close - Point ball_defense_point_center = - world_ptr->ball().position() + - (0.5 + 2 * ROBOT_MAX_RADIUS_METERS) * goal_to_ball_unit_vector; - Point ball_defense_point_left = - ball_defense_point_center - - robot_positioning_unit_vector * 4 * ROBOT_MAX_RADIUS_METERS; - Point ball_defense_point_right = - ball_defense_point_center + - robot_positioning_unit_vector * 4 * ROBOT_MAX_RADIUS_METERS; - - move_tactics.at(0)->updateControlParams( - ball_defense_point_center, - (world_ptr->ball().position() - ball_defense_point_center).orientation(), - stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); - move_tactics.at(1)->updateControlParams( - ball_defense_point_left, - (world_ptr->ball().position() - ball_defense_point_left).orientation(), - stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); - move_tactics.at(2)->updateControlParams( - ball_defense_point_right, - (world_ptr->ball().position() - ball_defense_point_right).orientation(), - stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); - - std::get<0>(crease_defender_tactics) - ->updateControlParams(world_ptr->ball().position(), - TbotsProto::CreaseDefenderAlignment::LEFT, stop_mode, - TbotsProto::BallStealMode::IGNORE); - std::get<1>(crease_defender_tactics) - ->updateControlParams(world_ptr->ball().position(), - TbotsProto::CreaseDefenderAlignment::RIGHT, stop_mode, - TbotsProto::BallStealMode::IGNORE); - - // insert all the tactics to the result - result[0].emplace_back(std::get<0>(crease_defender_tactics)); - result[0].emplace_back(std::get<1>(crease_defender_tactics)); - result[0].insert(result[0].end(), move_tactics.begin(), move_tactics.end()); - yield(result); - } while (true); + yield({{}}); + } } // Register this play in the genericFactory diff --git a/src/software/ai/hl/stp/play/stop_play.h b/src/software/ai/hl/stp/play/stop_play.h index d528357494..2cf23a526b 100644 --- a/src/software/ai/hl/stp/play/stop_play.h +++ b/src/software/ai/hl/stp/play/stop_play.h @@ -2,13 +2,14 @@ #include "proto/parameters.pb.h" #include "software/ai/hl/stp/play/play.h" +#include "software/ai/hl/stp/play/stop_play_fsm.h" /** * This Play moves our robots in a formation while keeping them at least 0.5m from the * ball. Additionally, the robots are limited to moving no more than 1.5m/s. This Play is * used during the referee "Stop" command. */ -class StopPlay : public Play +class StopPlay : public PlayBase { public: StopPlay(TbotsProto::AiConfig config); diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp new file mode 100644 index 0000000000..f729cdd7c7 --- /dev/null +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -0,0 +1,67 @@ +#include "software/ai/hl/stp/play/stop_play_fsm.h" + +#include "shared/constants.h" + +StopPlayFSM::StopPlayFSM(TbotsProto::AiConfig ai_config) + : ai_config(ai_config), + move_tactics{std::make_shared(), std::make_shared(), + std::make_shared()}, + crease_defender_tactics{std::make_shared(ai_config), + std::make_shared(ai_config)} +{ +} + +void StopPlayFSM::updateStopPosition(const Update& event) +{ + const WorldPtr& world_ptr = event.common.world_ptr; + TbotsProto::MaxAllowedSpeedMode stop_mode = + TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND; + + // A unit vector from the center of the goal to the ball; used for + // positioning all non-goalie robots. The perpendicular is used to place + // robots tangent to the goal-to-ball line. + Vector goal_to_ball_unit_vector = + (world_ptr->field().friendlyGoalCenter() - world_ptr->ball().position()) + .normalize(); + Vector robot_positioning_unit_vector = goal_to_ball_unit_vector.perpendicular(); + + // Points on the circle around the ball: center on the goal-ball line, + // left and right offset. Extra robot radius buffer to stay within rules. + Point ball_defense_point_center = + world_ptr->ball().position() + + (0.5 + 2 * ROBOT_MAX_RADIUS_METERS) * goal_to_ball_unit_vector; + Point ball_defense_point_left = + ball_defense_point_center - + robot_positioning_unit_vector * 4 * ROBOT_MAX_RADIUS_METERS; + Point ball_defense_point_right = + ball_defense_point_center + + robot_positioning_unit_vector * 4 * ROBOT_MAX_RADIUS_METERS; + + move_tactics.at(0)->updateControlParams( + ball_defense_point_center, + (world_ptr->ball().position() - ball_defense_point_center).orientation(), + stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); + move_tactics.at(1)->updateControlParams( + ball_defense_point_left, + (world_ptr->ball().position() - ball_defense_point_left).orientation(), + stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); + move_tactics.at(2)->updateControlParams( + ball_defense_point_right, + (world_ptr->ball().position() - ball_defense_point_right).orientation(), + stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); + + std::get<0>(crease_defender_tactics) + ->updateControlParams(world_ptr->ball().position(), + TbotsProto::CreaseDefenderAlignment::LEFT, stop_mode, + TbotsProto::BallStealMode::IGNORE); + std::get<1>(crease_defender_tactics) + ->updateControlParams(world_ptr->ball().position(), + TbotsProto::CreaseDefenderAlignment::RIGHT, stop_mode, + TbotsProto::BallStealMode::IGNORE); + + PriorityTacticVector result = {{}}; + result[0].emplace_back(std::get<0>(crease_defender_tactics)); + result[0].emplace_back(std::get<1>(crease_defender_tactics)); + result[0].insert(result[0].end(), move_tactics.begin(), move_tactics.end()); + event.common.set_tactics(result); +} diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.h b/src/software/ai/hl/stp/play/stop_play_fsm.h new file mode 100644 index 0000000000..0c6f21b4f3 --- /dev/null +++ b/src/software/ai/hl/stp/play/stop_play_fsm.h @@ -0,0 +1,62 @@ +#pragma once + +#include "proto/parameters.pb.h" +#include "shared/constants.h" +#include "software/ai/hl/stp/play/play_fsm.h" +#include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h" +#include "software/ai/hl/stp/tactic/move/move_tactic.h" + +struct StopPlayFSM +{ + struct ControlParams + { + }; + class StopState; + + struct Update + { + Update(const ControlParams& control_params, const PlayUpdate& common) + : control_params(control_params), common(common) + { + } + ControlParams control_params; + PlayUpdate common; + }; + + /** + * Creates a Stop Play FSM + * + * @param ai_config the play config for this FSM + */ + explicit StopPlayFSM(TbotsProto::AiConfig ai_config); + + /** + * Action to position robots during Stop (goalie handled by Play; this sets + * crease defenders and robots near the ball). + * + * @param event the StopPlayFSM Update event + */ + void updateStopPosition(const Update& event); + + auto operator()() + { + using namespace boost::sml; + + DEFINE_SML_STATE(StopState) + + DEFINE_SML_EVENT(Update) + + DEFINE_SML_ACTION(updateStopPosition) + + return make_transition_table( + // src_state + event [guard] / action = dest_state + *StopState_S + Update_E / updateStopPosition_A = StopState_S, + + X + Update_E / updateStopPosition_A = X); + } + + private: + TbotsProto::AiConfig ai_config; + std::vector> move_tactics; + std::array, 2> crease_defender_tactics; +}; From 890bad8e8eda6556ac783b7a6f565965b63092c6 Mon Sep 17 00:00:00 2001 From: Annie Sun Date: Sat, 21 Feb 2026 12:51:48 -0800 Subject: [PATCH 02/21] fix error --- src/software/ai/hl/stp/play/play.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/software/ai/hl/stp/play/play.h b/src/software/ai/hl/stp/play/play.h index f5002f02ac..0a28fbe16a 100644 --- a/src/software/ai/hl/stp/play/play.h +++ b/src/software/ai/hl/stp/play/play.h @@ -209,7 +209,7 @@ class PlayBase : public Play void updateTactics(const PlayUpdate& play_update) override { - fsm.process_event(FSM_T::Update(control_params, play_update)); + fsm.process_event(typename FSM_T::Update(control_params, play_update)); } protected: From e730154cf8c2452b2585c558d923fc9ae01e1154 Mon Sep 17 00:00:00 2001 From: Annie Sun Date: Sat, 21 Feb 2026 13:58:02 -0800 Subject: [PATCH 03/21] remove guard from getNextTactic --- src/software/ai/hl/stp/play/stop_play.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play.cpp b/src/software/ai/hl/stp/play/stop_play.cpp index b1cf16d309..77e1b81644 100644 --- a/src/software/ai/hl/stp/play/stop_play.cpp +++ b/src/software/ai/hl/stp/play/stop_play.cpp @@ -13,12 +13,6 @@ StopPlay::StopPlay(TbotsProto::AiConfig config) : PlayBase(config, void StopPlay::getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) { - // This function doesn't get called and will be removed when coroutines are phased - // out. - while (true) - { - yield({{}}); - } } // Register this play in the genericFactory From f3b89a6464810d33e6eab460a6f8ec6fc9368742 Mon Sep 17 00:00:00 2001 From: Annie Sun Date: Sat, 21 Feb 2026 14:03:46 -0800 Subject: [PATCH 04/21] add logic comment back --- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index f729cdd7c7..463560a4e7 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -13,6 +13,31 @@ StopPlayFSM::StopPlayFSM(TbotsProto::AiConfig ai_config) void StopPlayFSM::updateStopPosition(const Update& event) { + // Robot assignments for the Stop Play + // - 1 robot will be the goalie + // - 2 robots will assist the goalie in blocking the ball, they will snap + // to the best fit semicircle around the defense area + // - 3 robots will stay within 0.5m of the ball, evenly spaced, also blocking the + // goal + // + // If x represents the ball and G represents the goalie, the following, also blocking + // the goal diagram depicts a possible outcome of this play + // + // +--------------------+--------------------+ + // | | | + // | 4 x | | + // | 0 2 | | + // +--+ 1 3 | +--+ + // | | | | | + // |G | +-+-+ | | + // | | | | | | + // | | +-+-+ | | + // | | | | | + // +--+ | +--+ + // | | | + // | | | + // | | | + // +--------------------+--------------------+ const WorldPtr& world_ptr = event.common.world_ptr; TbotsProto::MaxAllowedSpeedMode stop_mode = TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND; From 0c41f78959472c313715e72aaba1d38719de277a Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sat, 21 Feb 2026 14:13:42 -0800 Subject: [PATCH 05/21] call stop play fsm --- src/software/ai/hl/stp/play/stop_play.cpp | 5 +++++ src/software/ai/hl/stp/play/stop_play.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/software/ai/hl/stp/play/stop_play.cpp b/src/software/ai/hl/stp/play/stop_play.cpp index 77e1b81644..fda4a95552 100644 --- a/src/software/ai/hl/stp/play/stop_play.cpp +++ b/src/software/ai/hl/stp/play/stop_play.cpp @@ -15,5 +15,10 @@ void StopPlay::getNextTactics(TacticCoroutine::push_type &yield, { } +void StopPlay::updateTactics(const PlayUpdate &play_update) +{ + fsm.process_event(StopPlayFSM::Update(control_params, play_update)); +} + // Register this play in the genericFactory static TGenericFactory factory; diff --git a/src/software/ai/hl/stp/play/stop_play.h b/src/software/ai/hl/stp/play/stop_play.h index 2cf23a526b..3f14a7d60b 100644 --- a/src/software/ai/hl/stp/play/stop_play.h +++ b/src/software/ai/hl/stp/play/stop_play.h @@ -16,4 +16,5 @@ class StopPlay : public PlayBase void getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) override; + void updateTactics(const PlayUpdate &play_update) override; }; From 863068b1ea876e1aff03be9b9eff4104e885841b Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sat, 28 Feb 2026 17:52:55 -0800 Subject: [PATCH 06/21] update constructor --- src/software/ai/hl/stp/play/play.h | 26 ------------------- src/software/ai/hl/stp/play/stop_play.cpp | 5 ++-- src/software/ai/hl/stp/play/stop_play.h | 1 + src/software/ai/hl/stp/play/stop_play_fsm.cpp | 8 +++--- src/software/ai/hl/stp/play/stop_play_fsm.h | 10 ++++--- 5 files changed, 14 insertions(+), 36 deletions(-) diff --git a/src/software/ai/hl/stp/play/play.h b/src/software/ai/hl/stp/play/play.h index eb7358aac6..b66f240b05 100644 --- a/src/software/ai/hl/stp/play/play.h +++ b/src/software/ai/hl/stp/play/play.h @@ -192,29 +192,3 @@ class Play RobotNavigationObstacleFactory obstacle_factory; }; - -/** - * Base class for plays that are driven by an FSM. Holds the FSM instance and - * control params, and implements updateTactics by processing PlayUpdate events. - * - * @tparam FSM_T The FSM struct type (e.g. StopPlayFSM). Must define ControlParams - * and an Update struct, and be constructible from TbotsProto::AiConfig. - */ -template -class PlayBase : public Play -{ - public: - explicit PlayBase(TbotsProto::AiConfig ai_config, bool requires_goalie) - : Play(ai_config, requires_goalie), fsm(FSM_T(ai_config)), control_params{} - { - } - - void updateTactics(const PlayUpdate& play_update) override - { - fsm.process_event(typename FSM_T::Update(control_params, play_update)); - } - - protected: - FSM fsm; - typename FSM_T::ControlParams control_params; -}; diff --git a/src/software/ai/hl/stp/play/stop_play.cpp b/src/software/ai/hl/stp/play/stop_play.cpp index ad7ed9e503..bb676191d4 100644 --- a/src/software/ai/hl/stp/play/stop_play.cpp +++ b/src/software/ai/hl/stp/play/stop_play.cpp @@ -3,10 +3,11 @@ #include "software/ai/hl/stp/tactic/goalie/goalie_tactic.h" #include "software/util/generic_factory/generic_factory.h" -StopPlay::StopPlay(TbotsProto::AiConfig config) : PlayBase(config, true) +StopPlay::StopPlay(std::shared_ptr ai_config_ptr) + : PlayBase(ai_config_ptr, true) { goalie_tactic = - std::make_shared(ai_config, + std::make_shared(ai_config_ptr, TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND); } diff --git a/src/software/ai/hl/stp/play/stop_play.h b/src/software/ai/hl/stp/play/stop_play.h index 1151c5acd8..a572918d1f 100644 --- a/src/software/ai/hl/stp/play/stop_play.h +++ b/src/software/ai/hl/stp/play/stop_play.h @@ -2,6 +2,7 @@ #include "proto/parameters.pb.h" #include "software/ai/hl/stp/play/play.h" +#include "software/ai/hl/stp/play/play_base.hpp" #include "software/ai/hl/stp/play/stop_play_fsm.h" /** diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index 463560a4e7..8e0c8473fc 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -2,12 +2,12 @@ #include "shared/constants.h" -StopPlayFSM::StopPlayFSM(TbotsProto::AiConfig ai_config) - : ai_config(ai_config), +StopPlayFSM::StopPlayFSM(std::shared_ptr ai_config_ptr) + : ai_config_ptr(ai_config_ptr), move_tactics{std::make_shared(), std::make_shared(), std::make_shared()}, - crease_defender_tactics{std::make_shared(ai_config), - std::make_shared(ai_config)} + crease_defender_tactics{std::make_shared(ai_config_ptr), + std::make_shared(ai_config_ptr)} { } diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.h b/src/software/ai/hl/stp/play/stop_play_fsm.h index 0c6f21b4f3..fff3f2fc9e 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.h +++ b/src/software/ai/hl/stp/play/stop_play_fsm.h @@ -1,8 +1,10 @@ #pragma once +#include + #include "proto/parameters.pb.h" #include "shared/constants.h" -#include "software/ai/hl/stp/play/play_fsm.h" +#include "software/ai/hl/stp/play/play_fsm.hpp" #include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h" #include "software/ai/hl/stp/tactic/move/move_tactic.h" @@ -26,9 +28,9 @@ struct StopPlayFSM /** * Creates a Stop Play FSM * - * @param ai_config the play config for this FSM + * @param ai_config_ptr shared pointer to the play config for this FSM */ - explicit StopPlayFSM(TbotsProto::AiConfig ai_config); + explicit StopPlayFSM(std::shared_ptr ai_config_ptr); /** * Action to position robots during Stop (goalie handled by Play; this sets @@ -56,7 +58,7 @@ struct StopPlayFSM } private: - TbotsProto::AiConfig ai_config; + std::shared_ptr ai_config_ptr; std::vector> move_tactics; std::array, 2> crease_defender_tactics; }; From 89c1b3bdc0e7044e20c27cf54630c1968b024bad Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sat, 28 Feb 2026 18:04:09 -0800 Subject: [PATCH 07/21] Passing ai_config_ptr into each MoveTactic constructor --- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index 8e0c8473fc..750ff51c1e 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -4,8 +4,9 @@ StopPlayFSM::StopPlayFSM(std::shared_ptr ai_config_ptr) : ai_config_ptr(ai_config_ptr), - move_tactics{std::make_shared(), std::make_shared(), - std::make_shared()}, + move_tactics{std::make_shared(ai_config_ptr), + std::make_shared(ai_config_ptr), + std::make_shared(ai_config_ptr)}, crease_defender_tactics{std::make_shared(ai_config_ptr), std::make_shared(ai_config_ptr)} { From dd18187b7b954097d1425b9e7014fc509be4437f Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sat, 28 Feb 2026 18:06:49 -0800 Subject: [PATCH 08/21] fix error --- src/software/ai/hl/stp/play/stop_play.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play.cpp b/src/software/ai/hl/stp/play/stop_play.cpp index bb676191d4..7bd27bc54f 100644 --- a/src/software/ai/hl/stp/play/stop_play.cpp +++ b/src/software/ai/hl/stp/play/stop_play.cpp @@ -6,9 +6,8 @@ StopPlay::StopPlay(std::shared_ptr ai_config_ptr) : PlayBase(ai_config_ptr, true) { - goalie_tactic = - std::make_shared(ai_config_ptr, - TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND); + goalie_tactic = std::make_shared(ai_config_ptr); + goalie_tactic->updateMaxSpeedMode(TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND); } void StopPlay::getNextTactics(TacticCoroutine::push_type &yield, From 674f549c3bda4abbd759907b4e7ad69110378e95 Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sun, 1 Mar 2026 09:16:16 -0800 Subject: [PATCH 09/21] removeal from transition table --- src/software/ai/hl/stp/play/stop_play_fsm.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.h b/src/software/ai/hl/stp/play/stop_play_fsm.h index fff3f2fc9e..6d21b7bbc0 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.h +++ b/src/software/ai/hl/stp/play/stop_play_fsm.h @@ -52,9 +52,7 @@ struct StopPlayFSM return make_transition_table( // src_state + event [guard] / action = dest_state - *StopState_S + Update_E / updateStopPosition_A = StopState_S, - - X + Update_E / updateStopPosition_A = X); + *StopState_S + Update_E / updateStopPosition_A = StopState_S); } private: From ab8d2a268cf25cc7e616a6b5ee6d44a35126fd5b Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sun, 1 Mar 2026 15:58:01 -0800 Subject: [PATCH 10/21] add stop play python test --- src/software/ai/hl/stp/play/BUILD | 17 ++++ src/software/ai/hl/stp/play/stop_play_test.py | 86 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/software/ai/hl/stp/play/stop_play_test.py diff --git a/src/software/ai/hl/stp/play/BUILD b/src/software/ai/hl/stp/play/BUILD index 1f431c4a04..df30dded27 100644 --- a/src/software/ai/hl/stp/play/BUILD +++ b/src/software/ai/hl/stp/play/BUILD @@ -193,6 +193,23 @@ cc_test( ], ) +py_test( + name = "stop_play_py_test", + srcs = [ + "stop_play_test.py", + ], + # TODO (#2619) Remove tag to run in parallel + tags = [ + "exclusive", + ], + deps = [ + "//software:conftest", + "//software/simulated_tests:speed_threshold_helpers", + "//software/simulated_tests:validation", + requirement("pytest"), + ], +) + cc_test( name = "shoot_or_chip_play_cpp_test", srcs = ["shoot_or_chip_play_test.cpp"], diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py new file mode 100644 index 0000000000..c51b3bf261 --- /dev/null +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -0,0 +1,86 @@ +import software.python_bindings as tbots_cpp +from proto.import_all_protos import * +from proto.play_pb2 import Play, PlayName +from proto.message_translation.tbots_protobuf import create_world_state +from proto.ssl_gc_common_pb2 import Team +from software.simulated_tests.robot_speed_threshold import ( + RobotSpeedEventuallyBelowThreshold, +) +from software.simulated_tests.simulated_test_fixture import ( + pytest_main, +) + + +def test_stop_play(simulated_test_runner): + """Test stop play: robots slow down and avoid the ball (STOP referee state).""" + + def setup(*args): + # Ball at centre (matches C++ test_stop_play_ball_at_centre_robots_spread_out) + ball_initial_pos = tbots_cpp.Point(0, 0) + + field = tbots_cpp.Field.createSSLDivisionBField() + + blue_bots = [ + tbots_cpp.Point(-4, 0), + tbots_cpp.Point(-0.3, 0), + tbots_cpp.Point(0.3, 0), + tbots_cpp.Point(0, 0.3), + tbots_cpp.Point(-3, -1.5), + tbots_cpp.Point(4.6, -3.1), + ] + + yellow_bots = [ + tbots_cpp.Point(1, 0), + tbots_cpp.Point(1, 2.5), + tbots_cpp.Point(1, -2.5), + field.enemyGoalCenter(), + field.enemyDefenseArea().negXNegYCorner(), + field.enemyDefenseArea().negXPosYCorner(), + ] + + # Game controller: STOP (both teams) - stop play behaviour + simulated_test_runner.gamecontroller.send_gc_command( + gc_command=Command.Type.STOP, team=Team.UNKNOWN + ) + simulated_test_runner.gamecontroller.send_gc_command( + gc_command=Command.Type.FORCE_START, team=Team.UNKNOWN + ) + + # Force play override: blue runs StopPlay, yellow runs HaltPlay + blue_play = Play() + blue_play.name = PlayName.StopPlay + + yellow_play = Play() + yellow_play.name = PlayName.HaltPlay + + simulated_test_runner.blue_full_system_proto_unix_io.send_proto(Play, blue_play) + simulated_test_runner.yellow_full_system_proto_unix_io.send_proto( + Play, yellow_play + ) + + simulated_test_runner.simulator_proto_unix_io.send_proto( + WorldState, + create_world_state( + yellow_robot_locations=yellow_bots, + blue_robot_locations=blue_bots, + ball_location=ball_initial_pos, + ball_velocity=tbots_cpp.Vector(0, 0), + ), + ) + + # After ~8s the C++ test expects robots to be slow; allow 10s for test + simulated_test_runner.run_test( + setup=setup, + params=[0], + inv_always_validation_sequence_set=[[]], + inv_eventually_validation_sequence_set=[[]], + ag_always_validation_sequence_set=[[]], + ag_eventually_validation_sequence_set=[ + [RobotSpeedEventuallyBelowThreshold(1.5)] + ], + test_timeout_s=10, + ) + + +if __name__ == "__main__": + pytest_main(__file__) From a04b21feb47eb71d24a5452a4572a43410cbf7cc Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sun, 1 Mar 2026 16:11:34 -0800 Subject: [PATCH 11/21] override cpp test --- src/software/ai/hl/stp/play/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/software/ai/hl/stp/play/BUILD b/src/software/ai/hl/stp/play/BUILD index df30dded27..da0730f32b 100644 --- a/src/software/ai/hl/stp/play/BUILD +++ b/src/software/ai/hl/stp/play/BUILD @@ -198,6 +198,8 @@ py_test( srcs = [ "stop_play_test.py", ], + # The default main would be stop_play_py_test.py; override to our file. + main = "stop_play_test.py", # TODO (#2619) Remove tag to run in parallel tags = [ "exclusive", From b9ec397c3546895f864e2926ca4683bd097b0948 Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sun, 1 Mar 2026 16:13:45 -0800 Subject: [PATCH 12/21] fix failure --- src/software/ai/hl/stp/play/stop_play_test.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index c51b3bf261..54cbecd94b 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -73,9 +73,7 @@ def setup(*args): setup=setup, params=[0], inv_always_validation_sequence_set=[[]], - inv_eventually_validation_sequence_set=[[]], - ag_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=[ + inv_eventually_validation_sequence_set=[ [RobotSpeedEventuallyBelowThreshold(1.5)] ], test_timeout_s=10, From b2a23f35e34fb71ed837e57262f2f0a71572a7ee Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sun, 1 Mar 2026 16:45:19 -0800 Subject: [PATCH 13/21] increase wait time for slowdown --- src/software/ai/hl/stp/play/stop_play_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index 54cbecd94b..1eaf8bcc3f 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -68,15 +68,16 @@ def setup(*args): ), ) - # After ~8s the C++ test expects robots to be slow; allow 10s for test + # C++ test waits 8s before checking; use 15s timeout so robots have time to slow. + # Threshold 2.0 m/s: expect robots to eventually slow below this (stop play). simulated_test_runner.run_test( setup=setup, params=[0], inv_always_validation_sequence_set=[[]], inv_eventually_validation_sequence_set=[ - [RobotSpeedEventuallyBelowThreshold(1.5)] + [RobotSpeedEventuallyBelowThreshold(2.0)] ], - test_timeout_s=10, + test_timeout_s=15, ) From eafbaf946089b29a889c16a6563c0f9eb24ddb11 Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Mon, 2 Mar 2026 22:47:07 -0800 Subject: [PATCH 14/21] rename py test --- src/software/ai/hl/stp/play/BUILD | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/software/ai/hl/stp/play/BUILD b/src/software/ai/hl/stp/play/BUILD index da0730f32b..2ef29d5e59 100644 --- a/src/software/ai/hl/stp/play/BUILD +++ b/src/software/ai/hl/stp/play/BUILD @@ -179,7 +179,7 @@ py_test( ) cc_test( - name = "stop_play_test", + name = "stop_play_cpp_test", srcs = ["stop_play_test.cpp"], deps = [ "//shared/test_util:tbots_gtest_main", @@ -194,11 +194,11 @@ cc_test( ) py_test( - name = "stop_play_py_test", + name = "stop_play_test", srcs = [ "stop_play_test.py", ], - # The default main would be stop_play_py_test.py; override to our file. + # The default main would be stop_play_test.py; override to our file. main = "stop_play_test.py", # TODO (#2619) Remove tag to run in parallel tags = [ From 9e3b88f3214dce1bb165b871cfba00065c10495c Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Tue, 3 Mar 2026 11:31:18 -0800 Subject: [PATCH 15/21] optimization of stop play positioning --- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index 750ff51c1e..d486af87b5 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -18,27 +18,28 @@ void StopPlayFSM::updateStopPosition(const Update& event) // - 1 robot will be the goalie // - 2 robots will assist the goalie in blocking the ball, they will snap // to the best fit semicircle around the defense area - // - 3 robots will stay within 0.5m of the ball, evenly spaced, also blocking the - // goal + // - 2 robots will stay within 0.5m of the ball, curved around the goal-to-ball + // line + // - 1 robot will position more centrally in our half to help stretch the field // - // If x represents the ball and G represents the goalie, the following, also blocking - // the goal diagram depicts a possible outcome of this play + // If x represents the ball and G represents the goalie, the following diagram + // depicts a possible outcome of this play // - // +--------------------+--------------------+ - // | | | - // | 4 x | | - // | 0 2 | | - // +--+ 1 3 | +--+ - // | | | | | - // |G | +-+-+ | | - // | | | | | | - // | | +-+-+ | | - // | | | | | - // +--+ | +--+ - // | | | - // | | | - // | | | - // +--------------------+--------------------+ + // +--------------------+--------------------+ + // | | | + // | 4 x | | + // | 0 2 | | + // +--+ 1 | +--+ + // | | | | | + // |G | +-+-+ | | + // | | | | | | + // | | +-+-+ | | + // | | 3 | | | + // +--+ | +--+ + // | | | + // | | | + // | | | + // +--------------------+--------------------+ const WorldPtr& world_ptr = event.common.world_ptr; TbotsProto::MaxAllowedSpeedMode stop_mode = TbotsProto::MaxAllowedSpeedMode::STOP_COMMAND; @@ -52,29 +53,32 @@ void StopPlayFSM::updateStopPosition(const Update& event) Vector robot_positioning_unit_vector = goal_to_ball_unit_vector.perpendicular(); // Points on the circle around the ball: center on the goal-ball line, - // left and right offset. Extra robot radius buffer to stay within rules. + // and one offset. Extra robot radius buffer to stay within rules. Point ball_defense_point_center = world_ptr->ball().position() + (0.5 + 2 * ROBOT_MAX_RADIUS_METERS) * goal_to_ball_unit_vector; - Point ball_defense_point_left = - ball_defense_point_center - - robot_positioning_unit_vector * 4 * ROBOT_MAX_RADIUS_METERS; Point ball_defense_point_right = ball_defense_point_center + robot_positioning_unit_vector * 4 * ROBOT_MAX_RADIUS_METERS; + // A more central point in our half that is further from the ball than the + // curved defenders, to help keep the team spread out and prepared to + // receive or intercept a pass once play resumes. + Point central_support_point = + (world_ptr->field().friendlyGoalCenter() + world_ptr->ball().position()) * 0.5; + move_tactics.at(0)->updateControlParams( ball_defense_point_center, (world_ptr->ball().position() - ball_defense_point_center).orientation(), stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); move_tactics.at(1)->updateControlParams( - ball_defense_point_left, - (world_ptr->ball().position() - ball_defense_point_left).orientation(), - stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); - move_tactics.at(2)->updateControlParams( ball_defense_point_right, (world_ptr->ball().position() - ball_defense_point_right).orientation(), stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); + move_tactics.at(2)->updateControlParams( + central_support_point, + (world_ptr->ball().position() - central_support_point).orientation(), + stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); std::get<0>(crease_defender_tactics) ->updateControlParams(world_ptr->ball().position(), From cf75597ef8e6fb490796b436442ea05a79f1e3ff Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Tue, 3 Mar 2026 11:43:23 -0800 Subject: [PATCH 16/21] we can't add 2 points --- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index d486af87b5..1d645396b5 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -63,9 +63,12 @@ void StopPlayFSM::updateStopPosition(const Update& event) // A more central point in our half that is further from the ball than the // curved defenders, to help keep the team spread out and prepared to - // receive or intercept a pass once play resumes. + // receive or intercept a pass once play resumes. We place this point + // halfway between the friendly goal center and the ball along the line + // joining them. Point central_support_point = - (world_ptr->field().friendlyGoalCenter() + world_ptr->ball().position()) * 0.5; + world_ptr->field().friendlyGoalCenter() + + (world_ptr->ball().position() - world_ptr->field().friendlyGoalCenter()) * 0.5; move_tactics.at(0)->updateControlParams( ball_defense_point_center, From 362c8d753fbd4e78501c11984b669c8ae699118d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 19:57:29 +0000 Subject: [PATCH 17/21] [pre-commit.ci lite] apply automatic fixes --- docs/fsm-diagrams.md | 12 ++++++++++++ src/software/ai/hl/stp/play/stop_play_fsm.cpp | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/fsm-diagrams.md b/docs/fsm-diagrams.md index 8059b8e10d..555ad57164 100644 --- a/docs/fsm-diagrams.md +++ b/docs/fsm-diagrams.md @@ -25,6 +25,18 @@ Terminate:::terminate --> Terminate:::terminate ``` +## [StopPlayFSM](/src/software/ai/hl/stp/play/stop_play_fsm.h) + +```mermaid + +stateDiagram-v2 +classDef terminate fill:white,color:black,font-weight:bold +direction LR +[*] --> StopState +StopState --> StopState : updateStopPosition + +``` + ## [BallPlacementPlayFSM](/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_fsm.h) ```mermaid diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index 1d645396b5..efa5e0ac5a 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -80,8 +80,8 @@ void StopPlayFSM::updateStopPosition(const Update& event) stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); move_tactics.at(2)->updateControlParams( central_support_point, - (world_ptr->ball().position() - central_support_point).orientation(), - stop_mode, TbotsProto::ObstacleAvoidanceMode::SAFE); + (world_ptr->ball().position() - central_support_point).orientation(), stop_mode, + TbotsProto::ObstacleAvoidanceMode::SAFE); std::get<0>(crease_defender_tactics) ->updateControlParams(world_ptr->ball().position(), From d35de68f0002273aa408d8fddd149be271fcfde0 Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Thu, 5 Mar 2026 23:27:34 -0800 Subject: [PATCH 18/21] nits --- src/software/ai/hl/stp/play/stop_play_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index 1eaf8bcc3f..5b41cfc8fb 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -69,13 +69,16 @@ def setup(*args): ) # C++ test waits 8s before checking; use 15s timeout so robots have time to slow. - # Threshold 2.0 m/s: expect robots to eventually slow below this (stop play). + # Threshold 1.4 m/s: expect robots to eventually slow below the 1.5 m/s STOP limit. + # TODO: add an eventually-validation that friendly robots stay at least 0.5 m away + # from the ball once pytest fixtures support delaying validations (to mirror the + # C++ robotsAvoidBall(0.5, ...) check). simulated_test_runner.run_test( setup=setup, params=[0], inv_always_validation_sequence_set=[[]], inv_eventually_validation_sequence_set=[ - [RobotSpeedEventuallyBelowThreshold(2.0)] + [RobotSpeedEventuallyBelowThreshold(1.4)] ], test_timeout_s=15, ) From 867e2cfc722d95ac1a7aece84aae621b28b6949b Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Thu, 5 Mar 2026 23:38:59 -0800 Subject: [PATCH 19/21] make value a constant --- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index efa5e0ac5a..f9afb9a489 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -64,11 +64,13 @@ void StopPlayFSM::updateStopPosition(const Update& event) // A more central point in our half that is further from the ball than the // curved defenders, to help keep the team spread out and prepared to // receive or intercept a pass once play resumes. We place this point - // halfway between the friendly goal center and the ball along the line - // joining them. + // between the friendly goal center and the ball along the line joining + // them. + constexpr double CENTRAL_SUPPORT_FRACTION = 0.5; Point central_support_point = world_ptr->field().friendlyGoalCenter() + - (world_ptr->ball().position() - world_ptr->field().friendlyGoalCenter()) * 0.5; + (world_ptr->ball().position() - world_ptr->field().friendlyGoalCenter()) * + CENTRAL_SUPPORT_FRACTION; move_tactics.at(0)->updateControlParams( ball_defense_point_center, From e25b3bf26a749176dfb1f7110216266b86fd03cd Mon Sep 17 00:00:00 2001 From: annieisawesome2 Date: Sat, 7 Mar 2026 22:44:22 -0800 Subject: [PATCH 20/21] reposition central bot and added tests for visualizing positions --- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 15 ++++--- src/software/ai/hl/stp/play/stop_play_test.py | 44 ++++++++++++++++++- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index f9afb9a489..44cd720519 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -61,17 +61,20 @@ void StopPlayFSM::updateStopPosition(const Update& event) ball_defense_point_center + robot_positioning_unit_vector * 4 * ROBOT_MAX_RADIUS_METERS; - // A more central point in our half that is further from the ball than the - // curved defenders, to help keep the team spread out and prepared to - // receive or intercept a pass once play resumes. We place this point - // between the friendly goal center and the ball along the line joining - // them. + // A spread-out position for the support robot: we take the point along the + // goal-to-ball line at CENTRAL_SUPPORT_FRACTION, then mirror it across the + // field center so the support robot is on the opposite side of the field + // from the ball (stretching the field). constexpr double CENTRAL_SUPPORT_FRACTION = 0.5; - Point central_support_point = + Point point_along_goal_ball = world_ptr->field().friendlyGoalCenter() + (world_ptr->ball().position() - world_ptr->field().friendlyGoalCenter()) * CENTRAL_SUPPORT_FRACTION; + Point field_center = world_ptr->field().centerPoint(); + Point central_support_point = + field_center + (field_center - point_along_goal_ball); + move_tactics.at(0)->updateControlParams( ball_defense_point_center, (world_ptr->ball().position() - ball_defense_point_center).orientation(), diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index 5b41cfc8fb..c2ba47b362 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -3,12 +3,52 @@ from proto.play_pb2 import Play, PlayName from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team +from software.py_constants import ROBOT_MAX_RADIUS_METERS from software.simulated_tests.robot_speed_threshold import ( RobotSpeedEventuallyBelowThreshold, ) from software.simulated_tests.simulated_test_fixture import ( pytest_main, ) +from software.simulated_tests.validation import ( + Validation, + create_validation_geometry, +) +from software.simulated_tests.validation import ValidationStatus, ValidationType +from typing import override + + +class FriendlyRobotPositionsVisualization(Validation): + """Always-passing validation that draws circles at each friendly robot position. + Add to inv_always_validation_sequence_set and run with --enable_thunderscope to + visualize robot positions during the test. + """ + + @override + def get_validation_status(self, world) -> ValidationStatus: + return ValidationStatus.PASSING + + @override + def get_validation_type(self, world) -> ValidationType: + return ValidationType.ALWAYS + + @override + def get_validation_geometry(self, world) -> ValidationGeometry: + return create_validation_geometry( + [ + tbots_cpp.Circle( + tbots_cpp.Point( + robot.current_state.global_position.x_meters, + robot.current_state.global_position.y_meters, + ), + ROBOT_MAX_RADIUS_METERS + 0.05, + ) + for robot in world.friendly_team.team_robots + ] + ) + + def __repr__(self): + return "Friendly robot positions (visualization only)" def test_stop_play(simulated_test_runner): @@ -76,7 +116,9 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, params=[0], - inv_always_validation_sequence_set=[[]], + inv_always_validation_sequence_set=[ + [FriendlyRobotPositionsVisualization()], + ], inv_eventually_validation_sequence_set=[ [RobotSpeedEventuallyBelowThreshold(1.4)] ], From b72cfa769c140941335304d5271f1e3f447b2aff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sun, 8 Mar 2026 06:57:18 +0000 Subject: [PATCH 21/21] [pre-commit.ci lite] apply automatic fixes --- src/software/ai/hl/stp/play/stop_play_fsm.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/software/ai/hl/stp/play/stop_play_fsm.cpp b/src/software/ai/hl/stp/play/stop_play_fsm.cpp index 44cd720519..53bfccb253 100644 --- a/src/software/ai/hl/stp/play/stop_play_fsm.cpp +++ b/src/software/ai/hl/stp/play/stop_play_fsm.cpp @@ -71,9 +71,8 @@ void StopPlayFSM::updateStopPosition(const Update& event) (world_ptr->ball().position() - world_ptr->field().friendlyGoalCenter()) * CENTRAL_SUPPORT_FRACTION; - Point field_center = world_ptr->field().centerPoint(); - Point central_support_point = - field_center + (field_center - point_along_goal_ball); + Point field_center = world_ptr->field().centerPoint(); + Point central_support_point = field_center + (field_center - point_along_goal_ball); move_tactics.at(0)->updateControlParams( ball_defense_point_center,