Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions docs/fsm-diagrams.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ stateDiagram-v2
classDef terminate fill:white,color:black,font-weight:bold
direction LR
[*] --> WaitingForPassState
WaitingForPassState --> WaitingForPassState : [shouldMoveAwayFromShot]\n<i>moveAwayFromShot</i>
WaitingForPassState --> WaitingForPassState : [!passStarted]\n<i>updateReceive</i>
WaitingForPassState --> OneTouchShotState : [passStarted && onetouchPossible]\n<i>updateOnetouch</i>
WaitingForPassState --> ReceiveAndDribbleState : [passStarted && !onetouchPossible]\n<i>updateReceive</i>
Expand Down
2 changes: 1 addition & 1 deletion src/proto/tactic.proto
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ message PivotKickTactic
message ReceiverTactic
{
// The pass to receive
optional Pass pass = 1;
optional Pass pass_ = 1; // needs the _ because pass is a keyword in python
// If set to true, we will only receive and dribble
required bool disable_one_touch_shot = 2;
}
Expand Down
20 changes: 19 additions & 1 deletion src/software/ai/hl/stp/play/shoot_or_pass/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
load("@simulated_tests_deps//:requirements.bzl", "requirement")

package(default_visibility = ["//visibility:public"])

# We force linking for all plays so that the static variables required for the
Expand Down Expand Up @@ -46,7 +48,7 @@ cc_test(
)

cc_test(
name = "shoot_or_pass_play_test",
name = "shoot_or_pass_play_test_cpp",
srcs = ["shoot_or_pass_play_test.cpp"],
deps = [
":shoot_or_pass_play",
Expand All @@ -59,3 +61,19 @@ cc_test(
"//software/world",
],
)

py_test(
name = "shoot_or_pass_play_test",
srcs = [
"shoot_or_pass_play_test.py",
],
# TODO (#2619) Remove tag to run in parallel
tags = [
"exclusive",
],
deps = [
"//software:conftest",
"//software/simulated_tests:validation",
requirement("pytest"),
],
)
102 changes: 102 additions & 0 deletions src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import software.python_bindings as tbots_cpp
from proto.play_pb2 import Play, PlayName
from software.simulated_tests.friendly_team_scored import *
from software.simulated_tests.ball_enters_region import *
from software.simulated_tests.friendly_has_ball_possession import *
from proto.message_translation.tbots_protobuf import create_world_state
from proto.ssl_gc_common_pb2 import Team
from software.simulated_tests.simulated_test_fixture import (
pytest_main,
)


def test_shoot_or_pass_play(simulated_test_runner):
def setup(start_point):
# starting point must be Point
ball_initial_pos = start_point
# placement point must be Vector2 to work with game controller
tbots_cpp.Point(-3, -2)
tbots_cpp.Field.createSSLDivisionBField()

# Setup Bots
blue_bots = [
tbots_cpp.Point(-4.5, 3.0),
tbots_cpp.Point(-2, 1.5),
tbots_cpp.Point(-2, 0.5),
tbots_cpp.Point(-2, -1.7),
tbots_cpp.Point(-2, -1.5),
tbots_cpp.Point(-2, -0.5),
]

yellow_bots = [
tbots_cpp.Point(1, 0),
tbots_cpp.Point(1, 2.5),
tbots_cpp.Point(1, -2.5),
tbots_cpp.Field.createSSLDivisionBField().enemyGoalCenter(),
tbots_cpp.Field.createSSLDivisionBField()
.enemyDefenseArea()
.negXNegYCorner(),
tbots_cpp.Field.createSSLDivisionBField()
.enemyDefenseArea()
.negXPosYCorner(),
]

# 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.OffensePlay

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
)

# 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),
),
)

field = tbots_cpp.Field.createSSLDivisionBField()

# Always Validation
inv_always_validation_sequence_set = [
[BallAlwaysStaysInRegion(regions=[field.fieldBoundary()])]
]

ag_always_validation_sequence_set = [[FriendlyAlwaysHasBallPossession()]]

# Eventually Validation
inv_eventually_validation_sequence_set = [[]]
ag_eventually_validation_sequence_set = [[FriendlyTeamEventuallyScored()]]

simulated_test_runner.run_test(
params=[tbots_cpp.Point(-4.4, 2.9)],
setup=setup,
inv_eventually_validation_sequence_set=inv_eventually_validation_sequence_set,
inv_always_validation_sequence_set=inv_always_validation_sequence_set,
ag_eventually_validation_sequence_set=ag_eventually_validation_sequence_set,
ag_always_validation_sequence_set=ag_always_validation_sequence_set,
test_timeout_s=15,
run_till_end=True,
)


if __name__ == "__main__":
pytest_main(__file__)
18 changes: 17 additions & 1 deletion src/software/ai/hl/stp/tactic/receiver/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
load("@simulated_tests_deps//:requirements.bzl", "requirement")

package(default_visibility = ["//visibility:public"])

cc_library(
Expand Down Expand Up @@ -34,7 +36,7 @@ cc_test(
)

cc_test(
name = "receiver_tactic_test",
name = "receiver_tactic_cpp_test",
srcs = ["receiver_tactic_test.cpp"],
deps = [
":receiver_tactic",
Expand All @@ -47,3 +49,17 @@ cc_test(
"//software/world",
],
)

py_test(
name = "receiver_tactic_test",
srcs = ["receiver_tactic_test.py"],
tags = [
"exclusive",
],
deps = [
"//software:conftest",
"//software/simulated_tests:speed_threshold_helpers",
"//software/simulated_tests:validation",
requirement("pytest"),
],
)
35 changes: 35 additions & 0 deletions src/software/ai/hl/stp/tactic/receiver/receiver_fsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,26 @@ void ReceiverFSM::adjustReceive(const Update& event)
}
}

void ReceiverFSM::moveAwayFromShot(const Update& event)
{
Point ball = event.common.world_ptr->ball().position();
Point robot = event.common.robot.position();

Vector between_robot_and_ball = ball - robot;
Vector side_step_direction = between_robot_and_ball.normalize().perpendicular();

Point side_step_position = robot + side_step_direction;

Angle orientation = (side_step_position - ball).orientation();

event.common.set_primitive(std::make_unique<MovePrimitive>(
event.common.robot, side_step_position, orientation,
TbotsProto::MaxAllowedSpeedMode::PHYSICAL_LIMIT,
TbotsProto::ObstacleAvoidanceMode::AGGRESSIVE,
TbotsProto::DribblerMode::MAX_FORCE, TbotsProto::BallCollisionType::ALLOW,
AutoChipOrKick{AutoChipOrKickMode::OFF, {0}}));
}

bool ReceiverFSM::passStarted(const Update& event)
{
return event.common.world_ptr->ball().hasBallBeenKicked(
Expand Down Expand Up @@ -207,3 +227,18 @@ bool ReceiverFSM::strayPass(const Update& event)

return stray_pass;
}

bool ReceiverFSM::shouldMoveAwayFromShot(const Update& event)
{
Point ball = event.common.world_ptr->ball().position();
Point goal = event.common.world_ptr->field().enemyGoalpostPos();

Vector ball_expand = (ball - goal).normalize(0.5).perpendicular();
Vector goal_expand = Vector(0.5, 0.0);

Point robot = event.common.robot.position();

return contains(Polygon{ball - ball_expand, ball + ball_expand, goal + goal_expand,
goal - goal_expand},
robot);
}
22 changes: 21 additions & 1 deletion src/software/ai/hl/stp/tactic/receiver/receiver_fsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ struct ReceiverFSM
*/
void adjustReceive(const Update& event);

/**
* Move the robot away from the planned shot path by moving
* perpendicular to the path by a metre.
*
* @param event ReceiverFSM::Update event
*/
void moveAwayFromShot(const Update& event);

/**
* Guard that checks if the ball has been kicked
*
Expand Down Expand Up @@ -141,6 +149,14 @@ struct ReceiverFSM
*/
bool strayPass(const Update& event);

/**
* Checks if attacker wants to shoot and receiver is in the way.
*
* @param event ReceiverFSM::Update event
* @return true if should move away from shot attempt
*/
bool shouldMoveAwayFromShot(const Update& event);

auto operator()()
{
using namespace boost::sml;
Expand All @@ -154,14 +170,18 @@ struct ReceiverFSM
DEFINE_SML_GUARD(passStarted)
DEFINE_SML_GUARD(passFinished)
DEFINE_SML_GUARD(strayPass)
DEFINE_SML_GUARD(shouldMoveAwayFromShot)

DEFINE_SML_ACTION(updateOnetouch)
DEFINE_SML_ACTION(updateReceive)
DEFINE_SML_ACTION(adjustReceive)
DEFINE_SML_ACTION(moveAwayFromShot)

return make_transition_table(
// src_state + event [guard] / action = dest_state
*WaitingForPassState_S + Update_E[!passStarted_G] / updateReceive_A,
*WaitingForPassState_S +
Update_E[shouldMoveAwayFromShot_G] / moveAwayFromShot_A,
WaitingForPassState_S + Update_E[!passStarted_G] / updateReceive_A,
WaitingForPassState_S + Update_E[passStarted_G && onetouchPossible_G] /
updateOnetouch_A = OneTouchShotState_S,
WaitingForPassState_S + Update_E[passStarted_G && !onetouchPossible_G] /
Expand Down
Loading