Skip to content

Shadow Defense Base #3450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8261493
Final_speed and Target_spins_per_s
zuperzane Sep 30, 2024
876a823
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Oct 1, 2024
170ad51
Field chancge
zuperzane Oct 2, 2024
b0afec3
Update src/proto/tactic.proto
zuperzane Oct 2, 2024
b4796a3
Update tactic.proto
zuperzane Oct 3, 2024
846c0a5
simulated hrvo fix
zuperzane Oct 5, 2024
91d4868
Basic Shadow Tactic - Always have take a pass defender if there is on…
zuperzane Dec 28, 2024
446c9c1
Compiling new shadow enemy states, designed to steal ball rather than…
zuperzane Dec 30, 2024
224e740
Implementation that tries to strip the ball if it is blocking the net
zuperzane Jan 1, 2025
f4c8344
Editing constants to provide proper ball stealing, and to ensure play…
zuperzane Feb 2, 2025
3e11017
After field testing has_ball fails due to some noise so maybe increas…
zuperzane Mar 2, 2025
c40e56f
final before merging?
Mar 15, 2025
fb92099
merged hopefully, will be tested before push
Mar 15, 2025
aba0e86
formatting
Mar 15, 2025
30622ad
accidentally added these files
Mar 15, 2025
515c5cb
Merge branch 'master' of https://github.com/UBC-Thunderbots/Software
Mar 15, 2025
8ed5f65
comments and poor merging fixes
Mar 15, 2025
2f1e25f
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 15, 2025
32362cc
irrelevant tests that are broken
Mar 15, 2025
3a39637
Merge branch 'master' of github.com:zuperzane/zane_kaber_software
Mar 15, 2025
66b43cb
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 15, 2025
b900d26
fixed defense test
Mar 22, 2025
adbd527
Merge branch 'master' of github.com:zuperzane/zane_kaber_software
Mar 22, 2025
e5eff5e
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 22, 2025
4f6a92c
this is needed in real life most likely but may break tests so im jus…
Mar 22, 2025
977514a
Merge branch 'master' of github.com:zuperzane/zane_kaber_software
Mar 22, 2025
551e554
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 22, 2025
4381165
Update src/software/ai/hl/stp/tactic/shadow_enemy/shadow_enemy_fsm.cpp
zuperzane May 3, 2025
228fc57
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] May 3, 2025
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
15 changes: 11 additions & 4 deletions docs/fsm-diagrams.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ stateDiagram-v2
classDef terminate fill:white,color:black,font-weight:bold
direction LR
[*] --> DefenseState
DefenseState --> DefenseState : <i>defendAgainstThreats</i>
DefenseState --> AggressiveDefenseState : [shouldDefendAggressively]\n<i>shadowAndBlockShots</i>
DefenseState --> DefenseState : <i>blockShots</i>
AggressiveDefenseState --> DefenseState : [!shouldDefendAggressively]\n<i>blockShots</i>
AggressiveDefenseState --> AggressiveDefenseState : <i>shadowAndBlockShots</i>
Terminate:::terminate --> Terminate:::terminate

```
Expand Down Expand Up @@ -457,12 +460,16 @@ classDef terminate fill:white,color:black,font-weight:bold
direction LR
[*] --> MoveFSM
MoveFSM --> BlockPassState : [!enemyThreatHasBall]\n<i>blockPass</i>
MoveFSM --> GoAndStealState : [blockedShot]\n<i>goAndSteal</i>
MoveFSM --> MoveFSM : <i>blockShot</i>
MoveFSM --> StealAndChipState
MoveFSM --> GoAndStealState
BlockPassState --> BlockPassState : [!enemyThreatHasBall]\n<i>blockPass</i>
BlockPassState --> MoveFSM : [enemyThreatHasBall]\n<i>blockShot</i>
StealAndChipState --> StealAndChipState : [enemyThreatHasBall]\n<i>stealAndChip</i>
StealAndChipState --> Terminate:::terminate : [!enemyThreatHasBall]\n<i>blockPass</i>
GoAndStealState --> GoAndStealState : [enemyThreatHasBall && !contestedBall]\n<i>goAndSteal</i>
GoAndStealState --> StealAndPullState : [enemyThreatHasBall && contestedBall]\n<i>goAndSteal</i>
GoAndStealState --> Terminate:::terminate : [!enemyThreatHasBall]\n<i>blockPass</i>
StealAndPullState --> StealAndPullState : [enemyThreatHasBall]\n<i>stealAndPull</i>
StealAndPullState --> Terminate:::terminate : [!enemyThreatHasBall]\n<i>blockPass</i>
Terminate:::terminate --> BlockPassState : [!enemyThreatHasBall]\n<i>blockPass</i>
Terminate:::terminate --> MoveFSM : [enemyThreatHasBall]\n<i>blockShot</i>
Terminate:::terminate --> Terminate:::terminate : <i>SET_STOP_PRIMITIVE_ACTION</i>
Expand Down
3 changes: 2 additions & 1 deletion src/software/ai/evaluation/defender_assignment.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
enum DefenderAssignmentType
{
CREASE_DEFENDER,
PASS_DEFENDER
PASS_DEFENDER,
SHADOW_ENEMY
};

// This struct stores the concept of a defender assignment, which describes
Expand Down
1 change: 1 addition & 0 deletions src/software/ai/hl/stp/play/defense/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ cc_library(
"//software/ai/hl/stp/play/defense:defense_play_base",
"//software/ai/hl/stp/tactic/crease_defender:crease_defender_tactic",
"//software/ai/hl/stp/tactic/pass_defender:pass_defender_tactic",
"//software/ai/hl/stp/tactic/shadow_enemy:shadow_enemy_tactic",
"//software/logger",
"//software/util/generic_factory",
"@sml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "software/ai/evaluation/enemy_threat.h"

DefensePlayFSMBase::DefensePlayFSMBase(TbotsProto::AiConfig ai_config)
: ai_config(ai_config), crease_defenders({}), pass_defenders({})
: ai_config(ai_config), crease_defenders({}), pass_defenders({}), shadowers({})
{
}

Expand Down
1 change: 1 addition & 0 deletions src/software/ai/hl/stp/play/defense/defense_play_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ class DefensePlayFSMBase
TbotsProto::AiConfig ai_config;
std::vector<std::shared_ptr<CreaseDefenderTactic>> crease_defenders;
std::vector<std::shared_ptr<PassDefenderTactic>> pass_defenders;
std::vector<std::shared_ptr<ShadowEnemyTactic>> shadowers;
};
71 changes: 69 additions & 2 deletions src/software/ai/hl/stp/play/defense/defense_play_fsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,55 @@

#include "software/ai/evaluation/defender_assignment.h"
#include "software/ai/evaluation/enemy_threat.h"
#include "software/logger/logger.h"


DefensePlayFSM::DefensePlayFSM(TbotsProto::AiConfig ai_config)
: DefensePlayFSMBase::DefensePlayFSMBase(ai_config)
{
}

void DefensePlayFSM::defendAgainstThreats(const Update& event)
bool DefensePlayFSM::shouldDefendAggressively(const Update& event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lmao why have this check at all?

{
return true;
}

void DefensePlayFSM::blockShots(const Update& event)
{
auto enemy_threats = getAllEnemyThreats(
event.common.world_ptr->field(), event.common.world_ptr->friendlyTeam(),
event.common.world_ptr->enemyTeam(), event.common.world_ptr->ball(), false);

updateCreaseAndPassDefenders(event, enemy_threats);
updateShadowers(event, {});

setTactics(event);
}

void DefensePlayFSM::shadowAndBlockShots(const Update& event)
{
auto enemy_threats = getAllEnemyThreats(
event.common.world_ptr->field(), event.common.world_ptr->friendlyTeam(),
event.common.world_ptr->enemyTeam(), event.common.world_ptr->ball(), false);


updateCreaseAndPassDefenders(event, enemy_threats);

if (pass_defenders.size() > 0)
{
pass_defenders.erase(pass_defenders.begin());
updateShadowers(event, {enemy_threats.front()});
}

setTactics(event);
}

void DefensePlayFSM::updateCreaseAndPassDefenders(
const Update& event, const std::vector<EnemyThreat>& enemy_threats)
{
auto assignments = getAllDefenderAssignments(
enemy_threats, event.common.world_ptr->field(), event.common.world_ptr->ball(),

ai_config.defense_play_config().defender_assignment_config());

if (assignments.size() == 0)
Expand Down Expand Up @@ -68,11 +103,43 @@ void DefensePlayFSM::defendAgainstThreats(const Update& event)
setAlignment(event, crease_defender_assignments, TbotsProto::BallStealMode::STEAL);
updatePassDefenderControlParams(pass_defender_assignments,
TbotsProto::BallStealMode::STEAL);
}

void DefensePlayFSM::updateShadowers(const Update& event,
const std::vector<EnemyThreat>& threats_to_shadow)
{
setUpShadowers(static_cast<unsigned int>(threats_to_shadow.size()));

for (unsigned int i = 0; i < shadowers.size(); i++)
{
shadowers.at(i)->updateControlParams(threats_to_shadow.at(i),
ROBOT_SHADOWING_DISTANCE_METERS);
}
}


void DefensePlayFSM::setUpShadowers(unsigned int num_shadowers)
{
if (num_shadowers == shadowers.size())
{
return;
}

shadowers = std::vector<std::shared_ptr<ShadowEnemyTactic>>(num_shadowers);
std::generate(shadowers.begin(), shadowers.end(),
[this]() { return std::make_shared<ShadowEnemyTactic>(); });
}

void DefensePlayFSM::setTactics(const Update& event)
{
PriorityTacticVector tactics_to_return = {{}, {}, {}};

PriorityTacticVector tactics_to_return = {{}, {}};
tactics_to_return[0].insert(tactics_to_return[0].end(), crease_defenders.begin(),
crease_defenders.end());
tactics_to_return[1].insert(tactics_to_return[1].end(), pass_defenders.begin(),
pass_defenders.end());
tactics_to_return[2].insert(tactics_to_return[2].end(), shadowers.begin(),
shadowers.end());

event.common.set_tactics(tactics_to_return);
}
78 changes: 73 additions & 5 deletions src/software/ai/hl/stp/play/defense/defense_play_fsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,108 @@
#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/pass_defender/pass_defender_tactic.h"
#include "software/ai/hl/stp/tactic/shadow_enemy/shadow_enemy_tactic.h"
#include "software/logger/logger.h"

struct DefensePlayFSM : public DefensePlayFSMBase
{
class DefenseState;

class AggressiveDefenseState;
/**
* Creates a defense play FSM
*
* @param ai_config the play config for this play FSM
*/
explicit DefensePlayFSM(TbotsProto::AiConfig ai_config);

// Where to shadow
const double ROBOT_SHADOWING_DISTANCE_METERS = ROBOT_MAX_RADIUS_METERS * 3;

/**
* Guard to check whether we should be defending more aggressively
*
* @param event the FSM event
*
* @return whether we should be defending more aggressively
*/
bool shouldDefendAggressively(const Update& event);

/**
* Action to identify all immediate enemy threats and assign
* defenders to block enemy shots and passes
*
* @param event the FSM event
*/
void defendAgainstThreats(const Update& event);
void blockShots(const Update& event);

/**
* Action to shadow the primary enemy threat and assign
* extra defenders to block enemy shots and passes
*
* @param event the FSM event
*/
void shadowAndBlockShots(const Update& event);

/**
* Helper function to update crease and pass defenders to defend
* against specified threats
*
* @param event the FSM event
* @param enemy_threats the enemy threats to defend against
*/
void updateCreaseAndPassDefenders(const Update& event,
const std::vector<EnemyThreat>& enemy_threats);

/**
* Helper function to update shadowers to shadow specified threats
*
* @param event the FSM event
* @param threats_to_shadow the enemy threats to shadow
*/
void updateShadowers(const Update& event,
const std::vector<EnemyThreat>& threats_to_shadow);


/**
* Helper function to set up shadow enemy tactic vector members
*
* @param num_shadowers the number of shadow enemy tactics to set
*/
void setUpShadowers(unsigned int num_shadowers);

/**
* Helper function to return the next tactics
*
* @param event the FSM event
*/
void setTactics(const Update& event);



auto operator()()
{
using namespace boost::sml;

DEFINE_SML_STATE(DefenseState)
DEFINE_SML_STATE(AggressiveDefenseState)

DEFINE_SML_EVENT(Update)

DEFINE_SML_ACTION(defendAgainstThreats)
DEFINE_SML_GUARD(shouldDefendAggressively)

DEFINE_SML_ACTION(blockShots)
DEFINE_SML_ACTION(shadowAndBlockShots)

return make_transition_table(
// src_state + event [guard] / action = dest_state
*DefenseState_S + Update_E / defendAgainstThreats_A = DefenseState_S,
X + Update_E = X);

*DefenseState_S + Update_E[shouldDefendAggressively_G] /
shadowAndBlockShots_A = AggressiveDefenseState_S,
DefenseState_S + Update_E / blockShots_A = DefenseState_S,
AggressiveDefenseState_S +
Update_E[!shouldDefendAggressively_G] / blockShots_A = DefenseState_S,
AggressiveDefenseState_S + Update_E / shadowAndBlockShots_A =
AggressiveDefenseState_S,
X + Update_E = X);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ TEST(DefensePlayFSMTest, test_transitions)
[](InterPlayCommunication comm) {})));

// DefensePlayFSM always stays in the DefenseState
EXPECT_TRUE(fsm.is(boost::sml::state<DefensePlayFSM::DefenseState>));
EXPECT_TRUE(fsm.is(boost::sml::state<DefensePlayFSM::DefenseState>) ||
fsm.is(boost::sml::state<DefensePlayFSM::AggressiveDefenseState>));
}
85 changes: 83 additions & 2 deletions src/software/ai/hl/stp/play/defense/defense_play_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,89 @@
import software.python_bindings as tbots_cpp
from proto.play_pb2 import Play, PlayName
from software.simulated_tests.ball_enters_region import *
from software.simulated_tests.friendly_has_ball_possession import (
FriendlyEventuallyHasBallPossession,
)
from proto.message_translation.tbots_protobuf import create_world_state
from proto.ssl_gc_common_pb2 import Team


@pytest.mark.parametrize(
"blue_bots,yellow_bots",
[
(
[
tbots_cpp.Point(-3, 1.5),
tbots_cpp.Point(-3, 0.5),
tbots_cpp.Point(-3, -0.5),
tbots_cpp.Point(-3, -1.5),
tbots_cpp.Point(-3, 1),
tbots_cpp.Point(-0.75, 0),
],
[
tbots_cpp.Point(3, 0),
tbots_cpp.Point(1, -1),
tbots_cpp.Point(1, 0),
tbots_cpp.Point(2, -1.25),
tbots_cpp.Point(2, 1),
],
)
],
)
def test_defense_play_ball_steal(simulated_test_runner, blue_bots, yellow_bots):
def setup(*args):
# Starting point must be Point
ball_initial_pos = tbots_cpp.Point(0.93, 0)
# Placement point must be Vector2 to work with game controller
tbots_cpp.Point(-3, -2)

# Game Controller Setup
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.BLUE
)

# Force play override here
blue_play = Play()
blue_play.name = PlayName.DefensePlay

params = AssignedTacticPlayControlParams()

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(
AssignedTacticPlayControlParams, params
)

# Create world state
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),
),
)

simulated_test_runner.run_test(
setup=setup,
params=[0, 1, 2, 3, 4], # The aggregate test runs 5 times
inv_always_validation_sequence_set=[[]],
inv_eventually_validation_sequence_set=[[]],
ag_always_validation_sequence_set=[
[
BallNeverEntersRegion(
regions=[tbots_cpp.Field.createSSLDivisionBField().friendlyGoal()]
)
]
],
ag_eventually_validation_sequence_set=[[]],
test_timeout_s=12,
)


@pytest.mark.parametrize(
"blue_bots,yellow_bots",
[
Expand Down Expand Up @@ -81,8 +160,10 @@ def setup(*args):
)
]
],
ag_eventually_validation_sequence_set=[[]],
test_timeout_s=30,
ag_eventually_validation_sequence_set=[
[FriendlyEventuallyHasBallPossession(tolerance=0.05)]
],
test_timeout_s=6,
)


Expand Down
Loading