Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
08e6166
finish initial migration
Thunderbots Feb 26, 2026
c8eb98d
update to use AutoChipOrKick Enum instead
Feb 26, 2026
05ae35d
finish migrating to shoot or chip and kickofffriendly pplay runs
StarrryNight Mar 1, 2026
d49c262
finish migration to freekick play and penalty tactic
StarrryNight Mar 1, 2026
f1e41a5
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 1, 2026
4d462df
fix bugs in penalty kick fsm test
StarrryNight Mar 1, 2026
e9d3135
add tests for kick or chip fsm
StarrryNight Mar 1, 2026
3edad56
idk if this works
StarrryNight Mar 1, 2026
45ff662
merge conflicts
StarrryNight Mar 6, 2026
e830459
Adjust kick target calculation in kickoff play
StarrryNight Mar 6, 2026
f4c95de
Update AutoChipOrKick calculation for kickoff play
StarrryNight Mar 6, 2026
8b01f4e
Delete \
StarrryNight Mar 9, 2026
c60af76
clean up stray files
StarrryNight Mar 9, 2026
a44661e
Remove kick tactic and chip tactic
StarrryNight Mar 9, 2026
2f3cb93
update potos
StarrryNight Mar 10, 2026
0577bce
fix free kick play
StarrryNight Mar 10, 2026
927cbc4
change old protos to reserved
StarrryNight Mar 10, 2026
abe1ecf
fix proto issues:
StarrryNight Mar 10, 2026
7fb156e
Remove ChipTactic and KickTactic messages
StarrryNight Mar 11, 2026
128f186
changed passing sim test and passing field test
Thunderbots Mar 12, 2026
a1b8067
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 12, 2026
5031455
merge master
StarrryNight Apr 7, 2026
d44d75d
fix merge error
StarrryNight Apr 7, 2026
570e371
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Apr 7, 2026
d32183d
Merge branch 'samuel/combine_chip_and_kick' of github.com:UBC-Thunder…
StarrryNight Apr 25, 2026
165205c
i hate merging
StarrryNight Apr 25, 2026
b50a831
fix build errors
StarrryNight Apr 26, 2026
d35dadc
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Apr 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions docs/fsm-diagrams.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ Terminate:::terminate --> Terminate:::terminate : <i>SET_STOP_PRIMITIVE_ACTION</

```

## [KickFSM](/src/software/ai/hl/stp/tactic/kick/kick_fsm.h)
## [KickOrChipFSM](/src/software/ai/hl/stp/tactic/kick_or_chip/kick_or_chip_fsm.h)

```mermaid

Expand All @@ -438,10 +438,10 @@ classDef terminate fill:white,color:black,font-weight:bold
direction LR
[*] --> GetBehindBallFSM
GetBehindBallFSM --> GetBehindBallFSM : <i>updateGetBehindBall</i>
GetBehindBallFSM --> KickState
KickState --> GetBehindBallFSM : [shouldRealignWithBall]\n<i>updateGetBehindBall</i>
KickState --> KickState : [!ballChicked]\n<i>updateKick</i>
KickState --> Terminate:::terminate : [ballChicked]\n<i>SET_STOP_PRIMITIVE_ACTION</i>
GetBehindBallFSM --> KickOrChipState
KickOrChipState --> GetBehindBallFSM : [shouldRealignWithBall]\n<i>updateGetBehindBall</i>
KickOrChipState --> KickOrChipState : [!ballChicked]\n<i>kickOrChipBall</i>
KickOrChipState --> Terminate:::terminate : [ballChicked]\n<i>SET_STOP_PRIMITIVE_ACTION</i>
Terminate:::terminate --> Terminate:::terminate : <i>SET_STOP_PRIMITIVE_ACTION</i>

```
Expand Down Expand Up @@ -489,11 +489,11 @@ classDef terminate fill:white,color:black,font-weight:bold
direction LR
[*] --> DribbleFSM
DribbleFSM --> DribbleFSM : [!takePenaltyShot]\n<i>updateApproachKeeper</i>
DribbleFSM --> KickFSM : [timeOutApproach]\n<i>shoot</i>
DribbleFSM --> KickOrChipFSM : [timeOutApproach]\n<i>shoot</i>
DribbleFSM --> DribbleFSM : <i>adjustOrientationForShot</i>
DribbleFSM --> KickFSM
KickFSM --> KickFSM : <i>shoot</i>
KickFSM --> Terminate:::terminate
DribbleFSM --> KickOrChipFSM
KickOrChipFSM --> KickOrChipFSM : <i>shoot</i>
KickOrChipFSM --> Terminate:::terminate
Terminate:::terminate --> Terminate:::terminate : <i>SET_STOP_PRIMITIVE_ACTION</i>

```
Expand Down
34 changes: 12 additions & 22 deletions src/proto/tactic.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ message AssignedTacticPlayControlParams

message Tactic
{
reserved 2, 7;
oneof tactic
{
AttackerTactic attacker = 1;
ChipTactic chip = 2;
CreaseDefenderTactic crease_defender = 3;
DribbleTactic dribble = 4;
GetBehindBallTactic get_behind_ball = 5;
GoalieTactic goalie = 6;
KickTactic kick = 7;
MoveGoalieToGoalLineTactic move_goalie_to_goal_line = 8;
MoveTactic move = 9;
PenaltyKickTactic penalty_kick = 10;
Expand All @@ -30,6 +29,7 @@ message Tactic
ShadowEnemyTactic shadow_enemy = 13;
HaltTactic halt = 14;
PassDefenderTactic pass_defender = 15;
KickOrChipTactic kick_or_chip = 16;
}
}

Expand All @@ -44,16 +44,6 @@ message AttackerTactic
optional Point chip_target = 3;
}

message ChipTactic
{
// The location where the chip will be taken from
required Point chip_origin = 1;
// The direction the Robot will chip in
required Angle chip_direction = 2;
// The distance the robot will chip to
required double chip_distance_meters = 3;
}

enum CreaseDefenderAlignment
{
LEFT = 0;
Expand Down Expand Up @@ -106,16 +96,6 @@ message GoalieTactic
required MaxAllowedSpeedMode max_allowed_speed_mode = 1;
}

message KickTactic
{
// The location where the kick will be taken from
required Point kick_origin = 1;
// The direction the Robot will kick in
required Angle kick_direction = 2;
// How fast the Robot will kick the ball in meters per second
required double kick_speed_meters_per_second = 3;
}

message MoveGoalieToGoalLineTactic {}

message MoveTactic
Expand Down Expand Up @@ -177,3 +157,13 @@ message ShadowEnemyTactic
}

message HaltTactic {}

message KickOrChipTactic
{
// The location where the kick or chip will be taken from
required Point kick_or_chip_origin = 1;
// The direction the Robot will kick or chip in
required Angle kick_or_chip_direction = 2;
// How the robot will chip or kick the ball
required AutoChipOrKick auto_chip_or_kick = 3;
}
2 changes: 1 addition & 1 deletion src/software/ai/hl/stp/play/free_kick/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ cc_library(
"//software/ai/evaluation:possession",
"//software/ai/hl/stp/play",
"//software/ai/hl/stp/play/defense:defense_play",
"//software/ai/hl/stp/tactic/chip:chip_tactic",
"//software/ai/hl/stp/tactic/crease_defender:crease_defender_tactic",
"//software/ai/hl/stp/tactic/goalie:goalie_tactic",
"//software/ai/hl/stp/tactic/kick_or_chip:kick_or_chip_tactic",
"//software/ai/hl/stp/tactic/move:move_tactic",
"//software/ai/hl/stp/tactic/receiver:receiver_tactic",
"//software/ai/passing:eighteen_zone_pitch_division",
Expand Down
29 changes: 16 additions & 13 deletions src/software/ai/hl/stp/play/free_kick/free_kick_play_fsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ FreeKickPlayFSM::FreeKickPlayFSM(
std::shared_ptr<const TbotsProto::AiConfig> ai_config_ptr)
: PlayFSM<FreeKickPlayFSM>(ai_config_ptr),
align_to_ball_tactic(std::make_shared<MoveTactic>(ai_config_ptr)),
shoot_tactic(std::make_shared<KickTactic>(ai_config_ptr)),
chip_tactic(std::make_shared<ChipTactic>(ai_config_ptr)),
passer_tactic(std::make_shared<KickTactic>(ai_config_ptr)),
kick_or_chip_tactic(std::make_shared<KickOrChipTactic>(ai_config_ptr)),
passer_tactic(std::make_shared<KickOrChipTactic>(ai_config_ptr)),
receiver_tactic(std::make_shared<ReceiverTactic>(ai_config_ptr)),
receiver_positioning_tactics({std::make_shared<MoveTactic>(ai_config_ptr),
std::make_shared<MoveTactic>(ai_config_ptr)}),
Expand Down Expand Up @@ -146,12 +145,14 @@ void FreeKickPlayFSM::shootBall(const Update& event)
LOG(INFO) << "Shooting ball.";
PriorityTacticVector tactics_to_run = {{}};

Point ball_pos = event.common.world_ptr->ball().position();
Point ball_pos = event.common.world_ptr->ball().position();
AutoChipOrKick auto_chip_or_kick = {AutoChipOrKickMode::AUTOKICK,
BALL_MAX_SPEED_METERS_PER_SECOND};

shoot_tactic->updateControlParams(
kick_or_chip_tactic->updateControlParams(
ball_pos, (shot->getPointToShootAt() - ball_pos).orientation(),
BALL_MAX_SPEED_METERS_PER_SECOND);
tactics_to_run[0].emplace_back(shoot_tactic);
auto_chip_or_kick);
tactics_to_run[0].emplace_back(kick_or_chip_tactic);

event.common.set_tactics(tactics_to_run);
}
Expand Down Expand Up @@ -200,9 +201,10 @@ void FreeKickPlayFSM::chipBall(const Update& event)
}
}

chip_tactic->updateControlParams(event.common.world_ptr->ball().position(),
chip_target);
tactics_to_run[0].emplace_back(chip_tactic);
AutoChipOrKick auto_chip_or_kick = {AutoChipOrKickMode::AUTOCHIP,
(chip_target - ball_pos).length()};
kick_or_chip_tactic->updateControlParams(ball_pos, chip_target, auto_chip_or_kick);
tactics_to_run[0].emplace_back(kick_or_chip_tactic);

event.common.set_tactics(tactics_to_run);
}
Expand Down Expand Up @@ -288,8 +290,9 @@ void FreeKickPlayFSM::passBall(const Update& event)

Pass pass = best_pass_and_score_so_far.pass;

AutoChipOrKick auto_chip_or_kick = {AutoChipOrKickMode::AUTOKICK, pass.speed()};
passer_tactic->updateControlParams(pass.passerPoint(), pass.passerOrientation(),
pass.speed());
auto_chip_or_kick);
receiver_tactic->updateControlParams(pass);
tactics_to_run[0].emplace_back(passer_tactic);
tactics_to_run[0].emplace_back(receiver_tactic);
Expand All @@ -304,7 +307,7 @@ void FreeKickPlayFSM::passBall(const Update& event)

bool FreeKickPlayFSM::shotDone(const Update& event)
{
return shoot_tactic->done();
return kick_or_chip_tactic->done();
}

bool FreeKickPlayFSM::passDone(const FreeKickPlayFSM::Update& event)
Expand All @@ -314,5 +317,5 @@ bool FreeKickPlayFSM::passDone(const FreeKickPlayFSM::Update& event)

bool FreeKickPlayFSM::chipDone(const FreeKickPlayFSM::Update& event)
{
return chip_tactic->done();
return kick_or_chip_tactic->done();
}
8 changes: 3 additions & 5 deletions src/software/ai/hl/stp/play/free_kick/free_kick_play_fsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
#include "software/ai/evaluation/calc_best_shot.h"
#include "software/ai/hl/stp/play/defense/defense_play.h"
#include "software/ai/hl/stp/play/play.h"
#include "software/ai/hl/stp/tactic/chip/chip_tactic.h"
#include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h"
#include "software/ai/hl/stp/tactic/kick/kick_tactic.h"
#include "software/ai/hl/stp/tactic/kick_or_chip/kick_or_chip_tactic.h"
#include "software/ai/hl/stp/tactic/move/move_tactic.h"
#include "software/ai/hl/stp/tactic/receiver/receiver_tactic.h"
#include "software/ai/passing/eighteen_zone_pitch_division.h"
Expand Down Expand Up @@ -266,9 +265,8 @@ struct FreeKickPlayFSM : PlayFSM<FreeKickPlayFSM>
private:
std::optional<Shot> shot;
std::shared_ptr<MoveTactic> align_to_ball_tactic;
std::shared_ptr<KickTactic> shoot_tactic;
std::shared_ptr<ChipTactic> chip_tactic;
std::shared_ptr<KickTactic> passer_tactic;
std::shared_ptr<KickOrChipTactic> kick_or_chip_tactic;
std::shared_ptr<KickOrChipTactic> passer_tactic;
std::shared_ptr<ReceiverTactic> receiver_tactic;
std::vector<std::shared_ptr<MoveTactic>> receiver_positioning_tactics;
std::shared_ptr<DefensePlay> defense_play;
Expand Down
2 changes: 1 addition & 1 deletion src/software/ai/hl/stp/play/kickoff_friendly/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ cc_library(
"//software/ai/evaluation:enemy_threat",
"//software/ai/evaluation:find_open_areas",
"//software/ai/hl/stp/play",
"//software/ai/hl/stp/tactic/chip:chip_tactic",
"//software/ai/hl/stp/tactic/kick_or_chip:kick_or_chip_tactic",
"//software/ai/hl/stp/tactic/move:move_tactic",
"//software/logger",
"//software/util/generic_factory",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ void KickoffFriendlyPlayFSM::chipBall(const Update& event)
selected_target = best_target_it->origin();
}

kickoff_chip_tactic->updateControlParams(ball_position, selected_target);
kickoff_chip_tactic->updateControlParams(
ball_position, selected_target,
AutoChipOrKick{AutoChipOrKickMode::AUTOCHIP,
(selected_target - ball_position).length()});
tactics_to_run[0].emplace_back(kickoff_chip_tactic);
event.common.set_tactics(tactics_to_run);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include "software/ai/evaluation/find_open_areas.h"
#include "software/ai/hl/stp/play/play.h"
#include "software/ai/hl/stp/play/play_fsm.hpp"
#include "software/ai/hl/stp/tactic/chip/chip_tactic.h"
#include "software/ai/hl/stp/tactic/kick_or_chip/kick_or_chip_tactic.h"
#include "software/ai/hl/stp/tactic/move/move_tactic.h"
#include "software/logger/logger.h"

Expand Down
146 changes: 146 additions & 0 deletions src/software/ai/hl/stp/play/kickoff_friendly_play.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include "software/ai/hl/stp/play/kickoff_friendly_play.h"

#include "shared/constants.h"
#include "software/ai/evaluation/enemy_threat.h"
#include "software/ai/hl/stp/tactic/kick_or_chip/kick_or_chip_tactic.h"
#include "software/ai/hl/stp/tactic/move/move_tactic.h"
#include "software/util/generic_factory/generic_factory.h"

KickoffFriendlyPlay::KickoffFriendlyPlay(
std::shared_ptr<const TbotsProto::AiConfig> ai_config_ptr)
: Play(ai_config_ptr, true)
{
}

void KickoffFriendlyPlay::getNextTactics(TacticCoroutine::push_type& yield,
const WorldPtr& world_ptr)
{
// Since we only have 6 robots at the maximum, the number one priority
// is the robot doing the kickoff up front. The goalie is the second most
// important, followed by 3 and 4 setup for offense. 5 and 6 will stay
// back near the goalie just in case the ball quickly returns to the friendly
// side of the field.
//
// +--------------------+--------------------+
// | | |
// | 3 | |
// | | |
// +--+ 5 | +--+
// | | | | |
// | | +-+-+ | |
// |2 | |1 | | |
// | | +-+-+ | |
// | | | | |
// +--+ 6 | +--+
// | | |
// | 4 | |
// | | |
// +--------------------+--------------------+
//
// This is a two part play:
// Part 1: Get into position, but don't touch the ball (ref kickoff)
// Part 2: Chip the ball over the defender (ref normal start)

// the following positions are in the same order as the positions shown above,
// excluding the goalie for part 1 of this play
std::vector<Point> kickoff_setup_positions = {
// Robot 1
Point(world_ptr->field().centerPoint() +
Vector(-world_ptr->field().centerCircleRadius(), 0)),
// Robot 2
// Goalie positions will be handled by the goalie tactic
// Robot 3
Point(
world_ptr->field().centerPoint() +
Vector(-world_ptr->field().centerCircleRadius() - 4 * ROBOT_MAX_RADIUS_METERS,
-1.0 / 3.0 * world_ptr->field().yLength())),
// Robot 4
Point(
world_ptr->field().centerPoint() +
Vector(-world_ptr->field().centerCircleRadius() - 4 * ROBOT_MAX_RADIUS_METERS,
1.0 / 3.0 * world_ptr->field().yLength())),
// Robot 5
Point(world_ptr->field().friendlyGoalpostPos().x() +
world_ptr->field().defenseAreaXLength() + 2 * ROBOT_MAX_RADIUS_METERS,
world_ptr->field().friendlyGoalpostPos().y()),
// Robot 6
Point(world_ptr->field().friendlyGoalpostNeg().x() +
world_ptr->field().defenseAreaXLength() + 2 * ROBOT_MAX_RADIUS_METERS,
world_ptr->field().friendlyGoalpostNeg().y()),
};

// move tactics to use to move to positions defined above
std::vector<std::shared_ptr<MoveTactic>> move_tactics = {
std::make_shared<PrepareKickoffMoveTactic>(ai_config_ptr),
std::make_shared<MoveTactic>(ai_config_ptr),
std::make_shared<MoveTactic>(ai_config_ptr),
std::make_shared<MoveTactic>(ai_config_ptr),
std::make_shared<MoveTactic>(ai_config_ptr)};

// specific tactics
auto kickoff_chip_tactic = std::make_shared<KickoffChipTactic>(ai_config_ptr);

// Part 1: setup state (move to key positions)
while (world_ptr->gameState().isSetupState())
{
auto enemy_threats =
getAllEnemyThreats(world_ptr->field(), world_ptr->friendlyTeam(),
world_ptr->enemyTeam(), world_ptr->ball(), false);

PriorityTacticVector result = {{}};

// set the requirement that Robot 1 must be able to kick and chip
move_tactics.at(0)->mutableRobotCapabilityRequirements() = {
RobotCapability::Kick, RobotCapability::Chip};

// setup 5 kickoff positions in order of priority
for (unsigned i = 0; i < kickoff_setup_positions.size(); i++)
{
move_tactics.at(i)->updateControlParams(kickoff_setup_positions.at(i),
Angle::zero());
result[0].emplace_back(move_tactics.at(i));
}

// yield the Tactics this Play wants to run, in order of priority
yield(result);
}

// Part 2: not normal play, currently ready state (chip the ball)
while (!world_ptr->gameState().isPlaying())
{
auto enemy_threats =
getAllEnemyThreats(world_ptr->field(), world_ptr->friendlyTeam(),
world_ptr->enemyTeam(), world_ptr->ball(), false);

PriorityTacticVector result = {{}};

// TODO (#2612): This needs to be adjusted post field testing, ball needs to land
// exactly in the middle of the enemy field
Point ball_position = world_ptr->ball().position();
Point kick_target = world_ptr->field().centerPoint() +
Vector(world_ptr->field().xLength() / 6, 0);
AutoChipOrKick auto_chip_or_kick = {AutoChipOrKickMode::AUTOCHIP,
(kick_target - ball_position).length()};
kickoff_chip_tactic->updateControlParams(ball_position, kick_target,
auto_chip_or_kick);
result[0].emplace_back(kickoff_chip_tactic);

// the robot at position 0 will be closest to the ball, so positions starting from
// 1 will be assigned to the rest of the robots
for (unsigned i = 1; i < kickoff_setup_positions.size(); i++)
{
move_tactics.at(i)->updateControlParams(kickoff_setup_positions.at(i),
Angle::zero());
result[0].emplace_back(move_tactics.at(i));
}

// yield the Tactics this Play wants to run, in order of priority
yield(result);
}
}


// Register this play in the genericFactory
static TGenericFactory<std::string, Play, KickoffFriendlyPlay,
std::shared_ptr<const TbotsProto::AiConfig>>
factory;
Loading
Loading