diff --git a/docs/fsm-diagrams.md b/docs/fsm-diagrams.md index 8059b8e10d..c16f08b4d2 100644 --- a/docs/fsm-diagrams.md +++ b/docs/fsm-diagrams.md @@ -206,6 +206,20 @@ Terminate:::terminate --> Terminate:::terminate ``` +## [ShootOrChipPlayFSM](/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.h) + +```mermaid + +stateDiagram-v2 +classDef terminate fill:white,color:black,font-weight:bold +direction LR +[*] --> ShootOrChipState +ShootOrChipState --> ShootOrChipState : [!attackerDone]\nupdateShootOrChip +ShootOrChipState --> Terminate:::terminate : [attackerDone]\nupdateShootOrChip +Terminate:::terminate --> Terminate:::terminate : updateShootOrChip + +``` + ## [ShootOrPassPlayFSM](/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_fsm.h) ```mermaid @@ -225,6 +239,33 @@ Terminate:::terminate --> AttemptShotState : startLookingForPass ``` +## [HaltTestPlayFSM](/src/software/ai/hl/stp/play/test_plays/halt_test_play_fsm.h) + +```mermaid + +stateDiagram-v2 +classDef terminate fill:white,color:black,font-weight:bold +direction LR +[*] --> HaltTestState +HaltTestState --> HaltTestState : updateHalt +Terminate:::terminate --> Terminate:::terminate : updateHalt + +``` + +## [MoveTestPlayFSM](/src/software/ai/hl/stp/play/test_plays/move_test_play_fsm.h) + +```mermaid + +stateDiagram-v2 +classDef terminate fill:white,color:black,font-weight:bold +direction LR +[*] --> MoveTestState +MoveTestState --> MoveTestState : [!moveDone]\nupdateMove +MoveTestState --> Terminate:::terminate : [moveDone]\nupdateMove +Terminate:::terminate --> Terminate:::terminate : updateMove + +``` + ## [AttackerFSM](/src/software/ai/hl/stp/tactic/attacker/attacker_fsm.h) ```mermaid diff --git a/src/software/ai/hl/stp/play/BUILD b/src/software/ai/hl/stp/play/BUILD index 678a44e215..5de81bde83 100644 --- a/src/software/ai/hl/stp/play/BUILD +++ b/src/software/ai/hl/stp/play/BUILD @@ -40,30 +40,6 @@ cc_library( alwayslink = True, ) -cc_library( - name = "shoot_or_chip_play", - srcs = ["shoot_or_chip_play.cpp"], - hdrs = ["shoot_or_chip_play.h"], - deps = [ - ":play", - "//proto/message_translation:tbots_protobuf", - "//shared:constants", - "//software/ai/evaluation:enemy_threat", - "//software/ai/evaluation:find_open_areas", - "//software/ai/evaluation:possession", - "//software/ai/hl/stp/tactic/attacker:attacker_tactic", - "//software/ai/hl/stp/tactic/crease_defender:crease_defender_tactic", - "//software/ai/hl/stp/tactic/goalie:goalie_tactic", - "//software/ai/hl/stp/tactic/halt:halt_tactic", - "//software/ai/hl/stp/tactic/move:move_tactic", - "//software/ai/hl/stp/tactic/shadow_enemy:shadow_enemy_tactic", - "//software/logger", - "//software/util/generic_factory", - "//software/world:game_state", - ], - alwayslink = True, -) - cc_library( name = "stop_play", srcs = ["stop_play.cpp"], @@ -108,7 +84,6 @@ cc_library( deps = [ ":kickoff_enemy_play", ":kickoff_friendly_play", - ":shoot_or_chip_play", ":stop_play", "//software/ai/hl/stp/play/ball_placement:ball_placement_play", "//software/ai/hl/stp/play/crease_defense:crease_defense_play", @@ -125,6 +100,7 @@ cc_library( "//software/ai/hl/stp/play/offense:offense_play", "//software/ai/hl/stp/play/penalty_kick:penalty_kick_play", "//software/ai/hl/stp/play/penalty_kick_enemy:penalty_kick_enemy_play", + "//software/ai/hl/stp/play/shoot_or_chip:shoot_or_chip_play", "//software/ai/hl/stp/play/shoot_or_pass:shoot_or_pass_play", ], ) @@ -193,36 +169,6 @@ cc_test( ], ) -cc_test( - name = "shoot_or_chip_play_cpp_test", - srcs = ["shoot_or_chip_play_test.cpp"], - deps = [ - "//shared/test_util:tbots_gtest_main", - "//software/ai/hl/stp/play:shoot_or_chip_play", - "//software/simulated_tests:simulated_er_force_sim_play_test_fixture", - "//software/simulated_tests/validation:validation_function", - "//software/test_util", - "//software/time:duration", - "//software/world", - ], -) - -py_test( - name = "shoot_or_chip_play_test", - srcs = [ - "shoot_or_chip_play_test.py", - ], - # TODO (#2619) Remove tag to run in parallel - tags = [ - "exclusive", - ], - deps = [ - "//software:conftest", - "//software/simulated_tests:validation", - requirement("pytest"), - ], -) - cc_library( name = "assigned_tactics_play", srcs = ["assigned_tactics_play.cpp"], diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/BUILD b/src/software/ai/hl/stp/play/shoot_or_chip/BUILD new file mode 100644 index 0000000000..2e10e2e42e --- /dev/null +++ b/src/software/ai/hl/stp/play/shoot_or_chip/BUILD @@ -0,0 +1,62 @@ +load("@simulated_tests_deps//:requirements.bzl", "requirement") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "shoot_or_chip_play", + srcs = [ + "shoot_or_chip_play.cpp", + "shoot_or_chip_play_fsm.cpp", + ], + hdrs = [ + "shoot_or_chip_play.h", + "shoot_or_chip_play_fsm.h", + ], + deps = [ + "//proto/message_translation:tbots_protobuf", + "//shared:constants", + "//software/ai/evaluation:enemy_threat", + "//software/ai/evaluation:find_open_areas", + "//software/ai/evaluation:possession", + "//software/ai/hl/stp/play", + "//software/ai/hl/stp/tactic/attacker:attacker_tactic", + "//software/ai/hl/stp/tactic/crease_defender:crease_defender_tactic", + "//software/ai/hl/stp/tactic/halt:halt_tactic", + "//software/ai/hl/stp/tactic/move:move_tactic", + "//software/ai/hl/stp/tactic/shadow_enemy:shadow_enemy_tactic", + "//software/logger", + "//software/util/generic_factory", + "//software/world:game_state", + ], + alwayslink = True, +) + +cc_test( + name = "shoot_or_chip_play_cpp_test", + srcs = ["shoot_or_chip_play_test.cpp"], + deps = [ + ":shoot_or_chip_play", + "//shared/test_util:tbots_gtest_main", + "//software/simulated_tests:simulated_er_force_sim_play_test_fixture", + "//software/simulated_tests/validation:validation_function", + "//software/test_util", + "//software/time:duration", + "//software/world", + ], +) + +py_test( + name = "shoot_or_chip_play_test", + srcs = [ + "shoot_or_chip_play_test.py", + ], + # TODO (#2619) Remove tag to run in parallel + tags = [ + "exclusive", + ], + deps = [ + "//software:conftest", + "//software/simulated_tests:validation", + requirement("pytest"), + ], +) diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.cpp b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.cpp new file mode 100644 index 0000000000..5d1ec92377 --- /dev/null +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.cpp @@ -0,0 +1,31 @@ +#include "software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.h" + +#include "proto/message_translation/tbots_protobuf.h" +#include "shared/constants.h" +#include "software/logger/logger.h" +#include "software/util/generic_factory/generic_factory.h" +#include "software/world/game_state.h" + +ShootOrChipPlay::ShootOrChipPlay( + std::shared_ptr ai_config_ptr) + : PlayBase(ai_config_ptr, true) +{ +} + +void ShootOrChipPlay::getNextTactics(TacticCoroutine::push_type &yield, + const WorldPtr &world_ptr) +{ + // This function doesn't get called, it should be removed once coroutines + // are phased out +} + + +void ShootOrChipPlay::updateTactics(const PlayUpdate &play_update) +{ + fsm.process_event(ShootOrChipPlayFSM::Update(control_params, play_update)); +} + +// Register this play in the genericFactory +static TGenericFactory> + factory; diff --git a/src/software/ai/hl/stp/play/shoot_or_chip_play.h b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.h similarity index 65% rename from src/software/ai/hl/stp/play/shoot_or_chip_play.h rename to src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.h index f50b7e76c6..9690ef720b 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip_play.h +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.h @@ -2,16 +2,20 @@ #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/shoot_or_chip/shoot_or_chip_play_fsm.h" /** * The Defense Play tries to grab the ball from the enemy that has it, and all other * robots shadow the enemy robots in order of how threatening they are. */ -class ShootOrChipPlay : public Play +class ShootOrChipPlay : public PlayBase { public: ShootOrChipPlay(std::shared_ptr ai_config_ptr); void getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) override; + + void updateTactics(const PlayUpdate &play_update) override; }; diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.cpp b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.cpp new file mode 100644 index 0000000000..b5cafba8a9 --- /dev/null +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.cpp @@ -0,0 +1,97 @@ +#include "software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.h" + +#include "proto/message_translation/tbots_protobuf.h" +#include "shared/constants.h" +#include "software/ai/evaluation/enemy_threat.h" +#include "software/ai/evaluation/find_open_areas.h" +#include "software/ai/evaluation/possession.h" +#include "software/ai/hl/stp/tactic/attacker/attacker_tactic.h" +#include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h" +#include "software/ai/hl/stp/tactic/move/move_tactic.h" +#include "software/ai/hl/stp/tactic/shadow_enemy/shadow_enemy_tactic.h" + +ShootOrChipPlayFSM::ShootOrChipPlayFSM( + std::shared_ptr ai_config_ptr) + : PlayFSM(ai_config_ptr), + crease_defender_tactics{ + std::make_shared(ai_config_ptr), + std::make_shared(ai_config_ptr), + }, + move_to_open_area_tactics{ + std::make_shared(ai_config_ptr), + std::make_shared(ai_config_ptr), + }, + attacker(std::make_shared(ai_config_ptr)) +{ +} + +void ShootOrChipPlayFSM::updateShootOrChip(const Update& event) +{ + /** + * Our general strategy here is: + * - 1 goalie + * - 2 crease defenders moving around the friendly defense box + * - 2 robots moving into the first and second largest open free space on + * the enemy half + * - 1 robot trying to shoot on the goal. If an enemy gets too close to this + * robot, it will chip to right in front of the robot in the largest open free area + */ + + // Figure out where the fallback chip target is + // Experimentally determined to be a reasonable value + double fallback_chip_target_x_offset = 1.5; + + Point fallback_chip_target = event.common.world_ptr->field().enemyGoalCenter() - + Vector(fallback_chip_target_x_offset, 0); + + // Update chipper + std::optional chip_target = fallback_chip_target; + + + PriorityTacticVector result = {{}}; + + // Update crease defenders + std::get<0>(crease_defender_tactics) + ->updateControlParams(event.common.world_ptr->ball().position(), + TbotsProto::CreaseDefenderAlignment::LEFT); + result[0].emplace_back(std::get<0>(crease_defender_tactics)); + std::get<1>(crease_defender_tactics) + ->updateControlParams(event.common.world_ptr->ball().position(), + TbotsProto::CreaseDefenderAlignment::RIGHT); + result[0].emplace_back(std::get<1>(crease_defender_tactics)); + + // Update tactics moving to open areas + std::vector chip_targets = findGoodChipTargets(*event.common.world_ptr); + for (unsigned i = 0; i < chip_targets.size() && i < move_to_open_area_tactics.size(); + i++) + { + // Face towards the ball + Angle orientation = + (event.common.world_ptr->ball().position() - chip_targets[i].origin()) + .orientation(); + // Move a bit backwards to make it more likely we'll receive the chip + Point position = + chip_targets[i].origin() - + Vector::createFromAngle(orientation).normalize(ROBOT_MAX_RADIUS_METERS); + ; + move_to_open_area_tactics[i]->updateControlParams(position, orientation); + result[0].emplace_back(move_to_open_area_tactics[i]); + } + + if (!chip_targets.empty()) + { + chip_target = chip_targets[0].origin(); + } + attacker->updateControlParams(chip_target); + + // We want this second in priority only to the goalie + result[0].insert(result[0].begin() + 1, attacker); + + // set the the Tactics this Play wants to run, in order of priority + event.common.set_tactics(result); +} + +bool ShootOrChipPlayFSM::attackerDone(const Update& event) +{ + return attacker->done(); +} diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.h b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.h new file mode 100644 index 0000000000..e9edb745ac --- /dev/null +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_fsm.h @@ -0,0 +1,48 @@ +#pragma once + +#include "proto/parameters.pb.h" +#include "shared/constants.h" +#include "software/ai/hl/stp/play/play_fsm.hpp" +#include "software/ai/hl/stp/tactic/attacker/attacker_tactic.h" +#include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h" +#include "software/ai/hl/stp/tactic/move/move_tactic.h" + +struct ShootOrChipPlayFSM : PlayFSM +{ + struct ControlParams + { + }; + + class ShootOrChipState; + + explicit ShootOrChipPlayFSM( + std::shared_ptr ai_config_ptr); + + void updateShootOrChip(const Update& event); + + bool attackerDone(const Update& event); + + + + auto operator()() + { + using namespace boost::sml; + + DEFINE_SML_STATE(ShootOrChipState) + DEFINE_SML_EVENT(Update) + DEFINE_SML_ACTION(updateShootOrChip) + DEFINE_SML_GUARD(attackerDone) + + return make_transition_table( + // src_state + event [guard] / action = dest_state + *ShootOrChipState_S + Update_E[!attackerDone_G] / updateShootOrChip_A = + ShootOrChipState_S, + ShootOrChipState_S + Update_E[attackerDone_G] / updateShootOrChip_A = X, + X + Update_E / updateShootOrChip_A = X); + } + + private: + std::array, 2> crease_defender_tactics; + std::array, 2> move_to_open_area_tactics; + std::shared_ptr attacker; +}; diff --git a/src/software/ai/hl/stp/play/shoot_or_chip_play_test.cpp b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.cpp similarity index 97% rename from src/software/ai/hl/stp/play/shoot_or_chip_play_test.cpp rename to src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.cpp index c617194c6e..63ef6aeddc 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip_play_test.cpp +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.cpp @@ -1,4 +1,4 @@ -#include "software/ai/hl/stp/play/shoot_or_chip_play.h" +#include "software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play.h" #include diff --git a/src/software/ai/hl/stp/play/shoot_or_chip_play_test.py b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py similarity index 100% rename from src/software/ai/hl/stp/play/shoot_or_chip_play_test.py rename to src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py diff --git a/src/software/ai/hl/stp/play/shoot_or_chip_play.cpp b/src/software/ai/hl/stp/play/shoot_or_chip_play.cpp deleted file mode 100644 index 30234e0d28..0000000000 --- a/src/software/ai/hl/stp/play/shoot_or_chip_play.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "software/ai/hl/stp/play/shoot_or_chip_play.h" - -#include "proto/message_translation/tbots_protobuf.h" -#include "shared/constants.h" -#include "software/ai/evaluation/enemy_threat.h" -#include "software/ai/evaluation/find_open_areas.h" -#include "software/ai/evaluation/possession.h" -#include "software/ai/hl/stp/tactic/attacker/attacker_tactic.h" -#include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h" -#include "software/ai/hl/stp/tactic/halt/halt_tactic.h" -#include "software/ai/hl/stp/tactic/move/move_tactic.h" -#include "software/ai/hl/stp/tactic/shadow_enemy/shadow_enemy_tactic.h" -#include "software/logger/logger.h" -#include "software/util/generic_factory/generic_factory.h" -#include "software/world/game_state.h" - -ShootOrChipPlay::ShootOrChipPlay( - std::shared_ptr ai_config_ptr) - : Play(ai_config_ptr, true) -{ -} - -void ShootOrChipPlay::getNextTactics(TacticCoroutine::push_type &yield, - const WorldPtr &world_ptr) -{ - /** - * Our general strategy here is: - * - 1 goalie - * - 2 crease defenders moving around the friendly defense box - * - 2 robots moving into the first and second largest open free space on - * the enemy half - * - 1 robot trying to shoot on the goal. If an enemy gets too close to this - * robot, it will chip to right in front of the robot in the largest open free area - */ - - std::array, 2> crease_defender_tactics = { - std::make_shared(ai_config_ptr), - std::make_shared(ai_config_ptr), - }; - - std::array, 2> move_to_open_area_tactics = { - std::make_shared(ai_config_ptr), - std::make_shared(ai_config_ptr)}; - - // Figure out where the fallback chip target is - // Experimentally determined to be a reasonable value - double fallback_chip_target_x_offset = 1.5; - - Point fallback_chip_target = - world_ptr->field().enemyGoalCenter() - Vector(fallback_chip_target_x_offset, 0); - - auto attacker = std::make_shared(ai_config_ptr); - attacker->updateControlParams(fallback_chip_target); - - do - { - PriorityTacticVector result = {{}}; - - // Update crease defenders - std::get<0>(crease_defender_tactics) - ->updateControlParams(world_ptr->ball().position(), - TbotsProto::CreaseDefenderAlignment::LEFT); - result[0].emplace_back(std::get<0>(crease_defender_tactics)); - std::get<1>(crease_defender_tactics) - ->updateControlParams(world_ptr->ball().position(), - TbotsProto::CreaseDefenderAlignment::RIGHT); - result[0].emplace_back(std::get<1>(crease_defender_tactics)); - - // Update tactics moving to open areas - std::vector enemy_robot_points; - for (auto const &robot : world_ptr->enemyTeam().getAllRobots()) - { - enemy_robot_points.emplace_back(robot.position()); - } - std::vector chip_targets = findGoodChipTargets(*world_ptr); - for (unsigned i = 0; - i < chip_targets.size() && i < move_to_open_area_tactics.size(); i++) - { - // Face towards the ball - Angle orientation = - (world_ptr->ball().position() - chip_targets[i].origin()).orientation(); - // Move a bit backwards to make it more likely we'll receive the chip - Point position = - chip_targets[i].origin() - - Vector::createFromAngle(orientation).normalize(ROBOT_MAX_RADIUS_METERS); - ; - move_to_open_area_tactics[i]->updateControlParams(position, orientation); - result[0].emplace_back(move_to_open_area_tactics[i]); - } - - // Update chipper - std::optional chip_target = std::nullopt; - if (!chip_targets.empty()) - { - chip_target = chip_targets[0].origin(); - } - attacker->updateControlParams(chip_target); - - // We want this second in priority only to the goalie - result[0].insert(result[0].begin() + 1, attacker); - - // yield the Tactics this Play wants to run, in order of priority - yield(result); - - } while (!attacker->done()); -} - -// Register this play in the genericFactory -static TGenericFactory> - factory; diff --git a/src/software/ai/hl/stp/play/test_plays/BUILD b/src/software/ai/hl/stp/play/test_plays/BUILD index bc08c72b17..8197258f06 100644 --- a/src/software/ai/hl/stp/play/test_plays/BUILD +++ b/src/software/ai/hl/stp/play/test_plays/BUILD @@ -2,8 +2,13 @@ package(default_visibility = ["//visibility:public"]) cc_library( name = "halt_test_play", - srcs = ["halt_test_play.cpp"], - hdrs = ["halt_test_play.h"], + srcs = [ + "halt_test_play.cpp", + ], + hdrs = [ + "halt_test_play.h", + "halt_test_play_fsm.h", + ], deps = [ "//software/ai/hl/stp/play", "//software/ai/hl/stp/tactic/halt:halt_tactic", @@ -15,7 +20,10 @@ cc_library( cc_library( name = "move_test_play", srcs = ["move_test_play.cpp"], - hdrs = ["move_test_play.h"], + hdrs = [ + "move_test_play.h", + "move_test_play_fsm.h", + ], deps = [ "//software/ai/hl/stp/play", "//software/ai/hl/stp/tactic/move:move_tactic", @@ -23,3 +31,23 @@ cc_library( ], alwayslink = True, ) + +cc_test( + name = "halt_test_play_fsm_test", + srcs = ["halt_test_play_fsm_test.cpp"], + deps = [ + ":halt_test_play", + "//shared/test_util:tbots_gtest_main", + "//software/test_util", + ], +) + +cc_test( + name = "move_test_play_fsm_test", + srcs = ["move_test_play_fsm_test.cpp"], + deps = [ + ":move_test_play", + "//shared/test_util:tbots_gtest_main", + "//software/test_util", + ], +) diff --git a/src/software/ai/hl/stp/play/test_plays/halt_test_play.cpp b/src/software/ai/hl/stp/play/test_plays/halt_test_play.cpp index 50b7cc7dc9..b4a2e695a3 100644 --- a/src/software/ai/hl/stp/play/test_plays/halt_test_play.cpp +++ b/src/software/ai/hl/stp/play/test_plays/halt_test_play.cpp @@ -1,27 +1,23 @@ #include "software/ai/hl/stp/play/test_plays/halt_test_play.h" -#include "software/ai/hl/stp/tactic/halt/halt_tactic.h" -#include "software/geom/algorithms/contains.h" #include "software/util/generic_factory/generic_factory.h" HaltTestPlay::HaltTestPlay(std::shared_ptr ai_config_ptr) - : Play(ai_config_ptr, false) + : PlayBase(ai_config_ptr, false) { } void HaltTestPlay::getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) { - auto halt_test_tactic_1 = std::make_shared(ai_config_ptr); - auto halt_test_tactic_2 = std::make_shared(ai_config_ptr); - auto halt_test_tactic_3 = std::make_shared(ai_config_ptr); - - do - { - yield({{halt_test_tactic_1, halt_test_tactic_2, halt_test_tactic_3}}); - } while (true); + // This function doesn't get called and will be removed when coroutines are phased + // out. } +void HaltTestPlay::updateTactics(const PlayUpdate &play_update) +{ + fsm.process_event(HaltTestPlayFSM::Update(control_params, play_update)); +} // Register this play in the genericFactory static TGenericFactory> diff --git a/src/software/ai/hl/stp/play/test_plays/halt_test_play.h b/src/software/ai/hl/stp/play/test_plays/halt_test_play.h index aeeed0cfad..cbab38c25d 100644 --- a/src/software/ai/hl/stp/play/test_plays/halt_test_play.h +++ b/src/software/ai/hl/stp/play/test_plays/halt_test_play.h @@ -2,6 +2,8 @@ #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/test_plays/halt_test_play_fsm.h" /** * A test Play that halts 3 robots. @@ -11,7 +13,7 @@ * This play is applicable when the ball's y coordinate is >= 0 * This play's invariant holds while the ball is within the field */ -class HaltTestPlay : public Play +class HaltTestPlay : public PlayBase { public: /** @@ -23,4 +25,5 @@ class HaltTestPlay : public Play void getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) override; + void updateTactics(const PlayUpdate &play_update) override; }; diff --git a/src/software/ai/hl/stp/play/test_plays/halt_test_play_fsm.h b/src/software/ai/hl/stp/play/test_plays/halt_test_play_fsm.h new file mode 100644 index 0000000000..dcad1a6f03 --- /dev/null +++ b/src/software/ai/hl/stp/play/test_plays/halt_test_play_fsm.h @@ -0,0 +1,47 @@ +#pragma once + +#include "proto/parameters.pb.h" +#include "shared/constants.h" +#include "software/ai/hl/stp/play/play_fsm.hpp" +#include "software/ai/hl/stp/tactic/halt/halt_tactic.h" + +struct HaltTestPlayFSM : PlayFSM +{ + struct ControlParams + { + }; + + class HaltTestState; + + explicit HaltTestPlayFSM(std::shared_ptr ai_config_ptr) + : PlayFSM(ai_config_ptr), + halt_tactics({{ + std::make_shared(ai_config_ptr), + std::make_shared(ai_config_ptr), + std::make_shared(ai_config_ptr), + }}) + { + } + + void updateHalt(const Update& event) + { + event.common.set_tactics(halt_tactics); + } + + auto operator()() + { + using namespace boost::sml; + + DEFINE_SML_STATE(HaltTestState) + DEFINE_SML_EVENT(Update) + DEFINE_SML_ACTION(updateHalt) + + return make_transition_table( + // src_state + event [guard] / action = dest_state + *HaltTestState_S + Update_E / updateHalt_A = HaltTestState_S, + X + Update_E / updateHalt_A = X); + } + + private: + PriorityTacticVector halt_tactics; +}; diff --git a/src/software/ai/hl/stp/play/test_plays/halt_test_play_fsm_test.cpp b/src/software/ai/hl/stp/play/test_plays/halt_test_play_fsm_test.cpp new file mode 100644 index 0000000000..3af71370dc --- /dev/null +++ b/src/software/ai/hl/stp/play/test_plays/halt_test_play_fsm_test.cpp @@ -0,0 +1,29 @@ +#include "software/ai/hl/stp/play/test_plays/halt_test_play_fsm.h" + +#include + +#include "proto/parameters.pb.h" +#include "software/test_util/equal_within_tolerance.h" +#include "software/test_util/test_util.h" + +TEST(HaltTestPlayFSMTest, test_transitions) +{ + std::shared_ptr world = ::TestUtil::createBlankTestingWorld(); + FSM fsm(HaltTestPlayFSM{std::make_shared()}); + + // Verify initial state + EXPECT_TRUE(fsm.is(boost::sml::state)); + + // Capture tactics from callback + PriorityTacticVector received_tactics; + fsm.process_event(HaltTestPlayFSM::Update( + HaltTestPlayFSM::ControlParams{}, + PlayUpdate( + world, 3, + [&received_tactics](PriorityTacticVector new_tactics) + { received_tactics = std::move(new_tactics); }, + InterPlayCommunication{}, [](InterPlayCommunication) {}))); + + // Verify state stays the same (single-state loop) + EXPECT_TRUE(fsm.is(boost::sml::state)); +} diff --git a/src/software/ai/hl/stp/play/test_plays/move_test_play.cpp b/src/software/ai/hl/stp/play/test_plays/move_test_play.cpp index 21ca1555ad..6acaea6495 100644 --- a/src/software/ai/hl/stp/play/test_plays/move_test_play.cpp +++ b/src/software/ai/hl/stp/play/test_plays/move_test_play.cpp @@ -4,34 +4,20 @@ #include "software/util/generic_factory/generic_factory.h" MoveTestPlay::MoveTestPlay(std::shared_ptr ai_config_ptr) - : Play(ai_config_ptr, false) + : PlayBase(ai_config_ptr, false) { } void MoveTestPlay::getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) { - auto move_test_tactic_friendly_goal = std::make_shared(ai_config_ptr); - auto move_test_tactic_enemy_goal = std::make_shared(ai_config_ptr); - auto move_test_tactic_center_field = std::make_shared(ai_config_ptr); - - do - { - move_test_tactic_friendly_goal->updateControlParams( - world_ptr->field().friendlyGoalCenter(), Angle::zero(), - TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT, - TbotsProto::ObstacleAvoidanceMode::SAFE); - move_test_tactic_enemy_goal->updateControlParams( - world_ptr->field().enemyGoalCenter(), Angle::zero(), - TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT, - TbotsProto::ObstacleAvoidanceMode::SAFE); - move_test_tactic_center_field->updateControlParams( - Point(0, 0), Angle::zero(), TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT, - TbotsProto::ObstacleAvoidanceMode::SAFE); + // This function doesn't get called, it should be removed once coroutines + // are phased out +} - yield({{move_test_tactic_center_field, move_test_tactic_friendly_goal, - move_test_tactic_enemy_goal}}); - } while (!move_test_tactic_center_field->done()); +void MoveTestPlay::updateTactics(const PlayUpdate &play_update) +{ + fsm.process_event(MoveTestPlayFSM::Update(control_params, play_update)); } // Register this play in the genericFactory diff --git a/src/software/ai/hl/stp/play/test_plays/move_test_play.h b/src/software/ai/hl/stp/play/test_plays/move_test_play.h index f1d03b8a70..92b7e55783 100644 --- a/src/software/ai/hl/stp/play/test_plays/move_test_play.h +++ b/src/software/ai/hl/stp/play/test_plays/move_test_play.h @@ -2,6 +2,8 @@ #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/test_plays/move_test_play_fsm.h" /** * A test Play that moves a robot to the friendly goal, a robot to the enemy goal, and @@ -12,7 +14,7 @@ * This play is applicable when the ball's x and y coordinates are >= 0 * This play's invariant holds while the ball's x coordinate is >= 0 */ -class MoveTestPlay : public Play +class MoveTestPlay : public PlayBase { public: /** @@ -24,4 +26,6 @@ class MoveTestPlay : public Play void getNextTactics(TacticCoroutine::push_type &yield, const WorldPtr &world_ptr) override; + + void updateTactics(const PlayUpdate &play_update) override; }; diff --git a/src/software/ai/hl/stp/play/test_plays/move_test_play_fsm.h b/src/software/ai/hl/stp/play/test_plays/move_test_play_fsm.h new file mode 100644 index 0000000000..ee99fcfe3e --- /dev/null +++ b/src/software/ai/hl/stp/play/test_plays/move_test_play_fsm.h @@ -0,0 +1,69 @@ +#pragma once + +#include "proto/parameters.pb.h" +#include "shared/constants.h" +#include "software/ai/hl/stp/play/play_fsm.hpp" +#include "software/ai/hl/stp/tactic/move/move_tactic.h" + +struct MoveTestPlayFSM : PlayFSM +{ + struct ControlParams + { + }; + + class MoveTestState; + + explicit MoveTestPlayFSM(std::shared_ptr ai_config_ptr) + : PlayFSM(ai_config_ptr), + move_test_tactic_friendly_goal(std::make_shared(ai_config_ptr)), + move_test_tactic_enemy_goal(std::make_shared(ai_config_ptr)), + move_test_tactic_center_field(std::make_shared(ai_config_ptr)) + { + } + + void updateMove(const Update& event) + { + move_test_tactic_friendly_goal->updateControlParams( + event.common.world_ptr->field().friendlyGoalCenter(), Angle::zero(), + TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT, + TbotsProto::ObstacleAvoidanceMode::SAFE); + move_test_tactic_enemy_goal->updateControlParams( + event.common.world_ptr->field().enemyGoalCenter(), Angle::zero(), + TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT, + TbotsProto::ObstacleAvoidanceMode::SAFE); + move_test_tactic_center_field->updateControlParams( + Point(0, 0), Angle::zero(), TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT, + TbotsProto::ObstacleAvoidanceMode::SAFE); + + event.common.set_tactics( + {{move_test_tactic_center_field, move_test_tactic_friendly_goal, + move_test_tactic_enemy_goal}}); + } + + bool moveDone(const Update& event) + { + return move_test_tactic_center_field->done(); + } + + + auto operator()() + { + using namespace boost::sml; + + DEFINE_SML_STATE(MoveTestState) + DEFINE_SML_EVENT(Update) + DEFINE_SML_ACTION(updateMove) + DEFINE_SML_GUARD(moveDone) + + return make_transition_table( + // src_state + event [guard] / action = dest_state + *MoveTestState_S + Update_E[!moveDone_G] / updateMove_A = MoveTestState_S, + MoveTestState_S + Update_E[moveDone_G] / updateMove_A = X, + X + Update_E / updateMove_A = X); + } + + private: + std::shared_ptr move_test_tactic_friendly_goal; + std::shared_ptr move_test_tactic_enemy_goal; + std::shared_ptr move_test_tactic_center_field; +}; diff --git a/src/software/ai/hl/stp/play/test_plays/move_test_play_fsm_test.cpp b/src/software/ai/hl/stp/play/test_plays/move_test_play_fsm_test.cpp new file mode 100644 index 0000000000..e6a56b830a --- /dev/null +++ b/src/software/ai/hl/stp/play/test_plays/move_test_play_fsm_test.cpp @@ -0,0 +1,32 @@ +#include "software/ai/hl/stp/play/test_plays/move_test_play_fsm.h" + +#include + +#include "proto/parameters.pb.h" +#include "software/test_util/test_util.h" + +TEST(MoveTestPlayFSMTest, test_stays_in_move_state) +{ + std::shared_ptr world = ::TestUtil::createBlankTestingWorld(); + FSM fsm(MoveTestPlayFSM{std::make_shared()}); + + // Verify initial state + EXPECT_TRUE(fsm.is(boost::sml::state)); + + // Capture tactics from callback + PriorityTacticVector received_tactics; + fsm.process_event(MoveTestPlayFSM::Update( + MoveTestPlayFSM::ControlParams{}, + PlayUpdate( + world, 3, + [&received_tactics](PriorityTacticVector new_tactics) + { received_tactics = std::move(new_tactics); }, + InterPlayCommunication{}, [](InterPlayCommunication) {}))); + + // Verify state stays the same (tactic not done yet) + EXPECT_TRUE(fsm.is(boost::sml::state)); + + // Verify we got exactly 1 priority level with 3 tactics + ASSERT_EQ(received_tactics.size(), 1); + EXPECT_EQ(received_tactics[0].size(), 3); +}