Skip to content

Commit da152fa

Browse files
authored
Shadow Defense Base (UBC-Thunderbots#3450)
1 parent 7e9d289 commit da152fa

16 files changed

Lines changed: 551 additions & 295 deletions

docs/fsm-diagrams.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ stateDiagram-v2
7171
classDef terminate fill:white,color:black,font-weight:bold
7272
direction LR
7373
[*] --> DefenseState
74-
DefenseState --> DefenseState : <i>defendAgainstThreats</i>
74+
DefenseState --> AggressiveDefenseState : [shouldDefendAggressively]\n<i>shadowAndBlockShots</i>
75+
DefenseState --> DefenseState : <i>blockShots</i>
76+
AggressiveDefenseState --> DefenseState : [!shouldDefendAggressively]\n<i>blockShots</i>
77+
AggressiveDefenseState --> AggressiveDefenseState : <i>shadowAndBlockShots</i>
7578
Terminate:::terminate --> Terminate:::terminate
7679
7780
```
@@ -487,12 +490,16 @@ classDef terminate fill:white,color:black,font-weight:bold
487490
direction LR
488491
[*] --> MoveFSM
489492
MoveFSM --> BlockPassState : [!enemyThreatHasBall]\n<i>blockPass</i>
493+
MoveFSM --> GoAndStealState : [blockedShot]\n<i>goAndSteal</i>
490494
MoveFSM --> MoveFSM : <i>blockShot</i>
491-
MoveFSM --> StealAndChipState
495+
MoveFSM --> GoAndStealState
492496
BlockPassState --> BlockPassState : [!enemyThreatHasBall]\n<i>blockPass</i>
493497
BlockPassState --> MoveFSM : [enemyThreatHasBall]\n<i>blockShot</i>
494-
StealAndChipState --> StealAndChipState : [enemyThreatHasBall]\n<i>stealAndChip</i>
495-
StealAndChipState --> Terminate:::terminate : [!enemyThreatHasBall]\n<i>blockPass</i>
498+
GoAndStealState --> GoAndStealState : [enemyThreatHasBall && !contestedBall]\n<i>goAndSteal</i>
499+
GoAndStealState --> StealAndPullState : [enemyThreatHasBall && contestedBall]\n<i>goAndSteal</i>
500+
GoAndStealState --> Terminate:::terminate : [!enemyThreatHasBall]\n<i>blockPass</i>
501+
StealAndPullState --> StealAndPullState : [enemyThreatHasBall]\n<i>stealAndPull</i>
502+
StealAndPullState --> Terminate:::terminate : [!enemyThreatHasBall]\n<i>blockPass</i>
496503
Terminate:::terminate --> BlockPassState : [!enemyThreatHasBall]\n<i>blockPass</i>
497504
Terminate:::terminate --> MoveFSM : [enemyThreatHasBall]\n<i>blockShot</i>
498505
Terminate:::terminate --> Terminate:::terminate : <i>SET_STOP_PRIMITIVE_ACTION</i>

src/proto/parameters.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ message DefensePlayConfig
528528

529529
// The max number of crease defender can be created at a moment
530530
required uint32 max_num_crease_defenders = 6
531-
[default = 1, (bounds).min_int_value = 1, (bounds).max_int_value = 3];
531+
[default = 2, (bounds).min_int_value = 1, (bounds).max_int_value = 3];
532532
}
533533

534534
// The distance at which a threat is considered "immediate" if there is only 1

src/software/ai/evaluation/defender_assignment.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
enum DefenderAssignmentType
1111
{
1212
CREASE_DEFENDER,
13-
PASS_DEFENDER
13+
PASS_DEFENDER,
14+
SHADOW_ENEMY
1415
};
1516

1617
// This struct stores the concept of a defender assignment, which describes

src/software/ai/hl/stp/play/defense/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ cc_library(
2323
"//software/ai/hl/stp/play/defense:defense_play_base",
2424
"//software/ai/hl/stp/tactic/crease_defender:crease_defender_tactic",
2525
"//software/ai/hl/stp/tactic/pass_defender:pass_defender_tactic",
26+
"//software/ai/hl/stp/tactic/shadow_enemy:shadow_enemy_tactic",
2627
"//software/logger",
2728
"//software/util/generic_factory",
2829
"@sml",

src/software/ai/hl/stp/play/defense/defense_play_base.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "software/ai/evaluation/enemy_threat.h"
55

66
DefensePlayFSMBase::DefensePlayFSMBase(TbotsProto::AiConfig ai_config)
7-
: ai_config(ai_config), crease_defenders({}), pass_defenders({})
7+
: ai_config(ai_config), crease_defenders({}), pass_defenders({}), shadowers({})
88
{
99
}
1010

src/software/ai/hl/stp/play/defense/defense_play_base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,5 @@ class DefensePlayFSMBase
6969
TbotsProto::AiConfig ai_config;
7070
std::vector<std::shared_ptr<CreaseDefenderTactic>> crease_defenders;
7171
std::vector<std::shared_ptr<PassDefenderTactic>> pass_defenders;
72+
std::vector<std::shared_ptr<ShadowEnemyTactic>> shadowers;
7273
};

src/software/ai/hl/stp/play/defense/defense_play_fsm.cpp

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,71 @@
22

33
#include "software/ai/evaluation/defender_assignment.h"
44
#include "software/ai/evaluation/enemy_threat.h"
5+
#include "software/logger/logger.h"
6+
57

68
DefensePlayFSM::DefensePlayFSM(TbotsProto::AiConfig ai_config)
79
: DefensePlayFSMBase::DefensePlayFSMBase(ai_config)
810
{
911
}
1012

11-
void DefensePlayFSM::defendAgainstThreats(const Update& event)
13+
bool DefensePlayFSM::shouldDefendAggressively(const Update& event)
14+
{
15+
// If there is more attackers ahead of the ball than there
16+
// are our own defenders we won't press
17+
18+
auto attackers = std::count_if(
19+
event.common.world_ptr->enemyTeam().getAllRobots().begin(),
20+
event.common.world_ptr->enemyTeam().getAllRobots().end(),
21+
[event](const Robot& robot)
22+
{ return robot.position().x() < event.common.world_ptr->ball().position().x(); });
23+
24+
auto defenders = std::count_if(
25+
event.common.world_ptr->friendlyTeam().getAllRobots().begin(),
26+
event.common.world_ptr->friendlyTeam().getAllRobots().end(),
27+
[event](const Robot& robot)
28+
{ return robot.position().x() < event.common.world_ptr->ball().position().x(); });
29+
30+
return defenders > attackers;
31+
}
32+
33+
void DefensePlayFSM::blockShots(const Update& event)
1234
{
1335
auto enemy_threats = getAllEnemyThreats(
1436
event.common.world_ptr->field(), event.common.world_ptr->friendlyTeam(),
1537
event.common.world_ptr->enemyTeam(), event.common.world_ptr->ball(), false);
1638

39+
updateCreaseAndPassDefenders(event, enemy_threats);
40+
updateShadowers(event, {});
41+
42+
setTactics(event);
43+
}
44+
45+
void DefensePlayFSM::shadowAndBlockShots(const Update& event)
46+
{
47+
auto enemy_threats = getAllEnemyThreats(
48+
event.common.world_ptr->field(), event.common.world_ptr->friendlyTeam(),
49+
event.common.world_ptr->enemyTeam(), event.common.world_ptr->ball(), false);
50+
51+
updateCreaseAndPassDefenders(event, enemy_threats);
52+
53+
if (pass_defenders.size() > 0)
54+
{
55+
pass_defenders.erase(pass_defenders.begin());
56+
updateShadowers(event, {enemy_threats.front()});
57+
}
58+
59+
setTactics(event);
60+
}
61+
62+
void DefensePlayFSM::updateCreaseAndPassDefenders(
63+
const Update& event, const std::vector<EnemyThreat>& enemy_threats)
64+
{
1765
auto defender_assignment_config =
1866
ai_config.defense_play_config().defender_assignment_config();
1967
auto assignments = getAllDefenderAssignments(
2068
enemy_threats, event.common.world_ptr->field(), event.common.world_ptr->ball(),
2169
defender_assignment_config);
22-
2370
if (assignments.size() == 0)
2471
{
2572
return;
@@ -75,11 +122,43 @@ void DefensePlayFSM::defendAgainstThreats(const Update& event)
75122
setAlignment(event, crease_defender_assignments, TbotsProto::BallStealMode::STEAL);
76123
updatePassDefenderControlParams(pass_defender_assignments,
77124
TbotsProto::BallStealMode::STEAL);
125+
}
126+
127+
void DefensePlayFSM::updateShadowers(const Update& event,
128+
const std::vector<EnemyThreat>& threats_to_shadow)
129+
{
130+
setUpShadowers(static_cast<unsigned int>(threats_to_shadow.size()));
131+
132+
for (unsigned int i = 0; i < shadowers.size(); i++)
133+
{
134+
shadowers.at(i)->updateControlParams(threats_to_shadow.at(i),
135+
ROBOT_SHADOWING_DISTANCE_METERS);
136+
}
137+
}
138+
139+
140+
void DefensePlayFSM::setUpShadowers(int num_shadowers)
141+
{
142+
if (num_shadowers == static_cast<int>(shadowers.size()))
143+
{
144+
return;
145+
}
146+
147+
shadowers = std::vector<std::shared_ptr<ShadowEnemyTactic>>(num_shadowers);
148+
std::generate(shadowers.begin(), shadowers.end(),
149+
[this]() { return std::make_shared<ShadowEnemyTactic>(); });
150+
}
151+
152+
void DefensePlayFSM::setTactics(const Update& event)
153+
{
154+
PriorityTacticVector tactics_to_return = {{}, {}, {}};
78155

79-
PriorityTacticVector tactics_to_return = {{}, {}};
80156
tactics_to_return[0].insert(tactics_to_return[0].end(), crease_defenders.begin(),
81157
crease_defenders.end());
82158
tactics_to_return[1].insert(tactics_to_return[1].end(), pass_defenders.begin(),
83159
pass_defenders.end());
160+
tactics_to_return[2].insert(tactics_to_return[2].end(), shadowers.begin(),
161+
shadowers.end());
162+
84163
event.common.set_tactics(tactics_to_return);
85164
}

src/software/ai/hl/stp/play/defense/defense_play_fsm.h

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,110 @@
66
#include "software/ai/hl/stp/play/play_fsm.h"
77
#include "software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic.h"
88
#include "software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic.h"
9+
#include "software/ai/hl/stp/tactic/shadow_enemy/shadow_enemy_tactic.h"
910
#include "software/logger/logger.h"
1011

1112
struct DefensePlayFSM : public DefensePlayFSMBase
1213
{
1314
class DefenseState;
14-
15+
class AggressiveDefenseState;
1516
/**
1617
* Creates a defense play FSM
1718
*
1819
* @param ai_config the play config for this play FSM
1920
*/
2021
explicit DefensePlayFSM(TbotsProto::AiConfig ai_config);
2122

23+
/**
24+
* Guard to check whether we should be defending more aggressively
25+
*
26+
* @param event the FSM event
27+
*
28+
* @return whether we should be defending more aggressively
29+
*/
30+
bool shouldDefendAggressively(const Update& event);
31+
2232
/**
2333
* Action to identify all immediate enemy threats and assign
2434
* defenders to block enemy shots and passes
2535
*
2636
* @param event the FSM event
2737
*/
28-
void defendAgainstThreats(const Update& event);
38+
void blockShots(const Update& event);
39+
40+
/**
41+
* Action to shadow the primary enemy threat and assign
42+
* extra defenders to block enemy shots and passes
43+
*
44+
* @param event the FSM event
45+
*/
46+
void shadowAndBlockShots(const Update& event);
47+
48+
/**
49+
* Helper function to update crease and pass defenders to defend
50+
* against specified threats
51+
*
52+
* @param event the FSM event
53+
* @param enemy_threats the enemy threats to defend against
54+
*/
55+
void updateCreaseAndPassDefenders(const Update& event,
56+
const std::vector<EnemyThreat>& enemy_threats);
57+
58+
/**
59+
* Helper function to update shadowers to shadow specified threats
60+
*
61+
* @param event the FSM event
62+
* @param threats_to_shadow the enemy threats to shadow
63+
*/
64+
void updateShadowers(const Update& event,
65+
const std::vector<EnemyThreat>& threats_to_shadow);
66+
67+
68+
/**
69+
* Helper function to set up shadow enemy tactic vector members
70+
*
71+
* @param num_shadowers the number of shadow enemy tactics to set
72+
*/
73+
void setUpShadowers(int num_shadowers);
74+
75+
/**
76+
* Helper function to return the next tactics
77+
*
78+
* @param event the FSM event
79+
*/
80+
void setTactics(const Update& event);
81+
82+
2983

3084
auto operator()()
3185
{
3286
using namespace boost::sml;
3387

3488
DEFINE_SML_STATE(DefenseState)
89+
DEFINE_SML_STATE(AggressiveDefenseState)
3590

3691
DEFINE_SML_EVENT(Update)
3792

38-
DEFINE_SML_ACTION(defendAgainstThreats)
93+
DEFINE_SML_GUARD(shouldDefendAggressively)
94+
95+
DEFINE_SML_ACTION(blockShots)
96+
DEFINE_SML_ACTION(shadowAndBlockShots)
3997

4098
return make_transition_table(
4199
// src_state + event [guard] / action = dest_state
42-
*DefenseState_S + Update_E / defendAgainstThreats_A = DefenseState_S,
43-
X + Update_E = X);
100+
101+
*DefenseState_S + Update_E[shouldDefendAggressively_G] /
102+
shadowAndBlockShots_A = AggressiveDefenseState_S,
103+
DefenseState_S + Update_E / blockShots_A = DefenseState_S,
104+
AggressiveDefenseState_S +
105+
Update_E[!shouldDefendAggressively_G] / blockShots_A = DefenseState_S,
106+
AggressiveDefenseState_S + Update_E / shadowAndBlockShots_A =
107+
AggressiveDefenseState_S,
108+
X + Update_E = X);
44109
}
110+
111+
private:
112+
// Where to shadow, gathered experimentally after seeing
113+
// how far is far enough that the robot couldn't get dribbled past
114+
static constexpr double ROBOT_SHADOWING_DISTANCE_METERS = 0.36;
45115
};

src/software/ai/hl/stp/play/defense/defense_play_fsm_test.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,31 @@ TEST(DefensePlayFSMTest, test_transitions)
1515
FSM<DefensePlayFSM> fsm(DefensePlayFSM{ai_config});
1616
EXPECT_TRUE(fsm.is(boost::sml::state<DefensePlayFSM::DefenseState>));
1717

18+
// Place enemy robots behind the ball implying there is a large attack
19+
::TestUtil::setEnemyRobotPositions(world, {Point(0, 0), Point(-1, 0), Point(-2, 0)},
20+
Timestamp::fromSeconds(0));
21+
1822
fsm.process_event(DefensePlayFSM::Update(
1923
DefensePlayFSM::ControlParams{
2024
.max_allowed_speed_mode = TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT},
2125
PlayUpdate(
2226
world, 3, [](PriorityTacticVector new_tactics) {}, InterPlayCommunication{},
2327
[](InterPlayCommunication comm) {})));
2428

25-
// DefensePlayFSM always stays in the DefenseState
2629
EXPECT_TRUE(fsm.is(boost::sml::state<DefensePlayFSM::DefenseState>));
30+
31+
32+
// Place more friendly robots behind the ball implying this is a safe time
33+
// to aggressively defense
34+
::TestUtil::setFriendlyRobotPositions(
35+
world, {Point(-1, 0), Point(-3, 0), Point(-2, 0)}, Timestamp::fromSeconds(0));
36+
37+
fsm.process_event(DefensePlayFSM::Update(
38+
DefensePlayFSM::ControlParams{
39+
.max_allowed_speed_mode = TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT},
40+
PlayUpdate(
41+
world, 3, [](PriorityTacticVector new_tactics) {}, InterPlayCommunication{},
42+
[](InterPlayCommunication comm) {})));
43+
44+
EXPECT_TRUE(fsm.is(boost::sml::state<DefensePlayFSM::AggressiveDefenseState>));
2745
}

0 commit comments

Comments
 (0)