From 0b51be82f7e05b24474f16f6f903bec42464acb5 Mon Sep 17 00:00:00 2001 From: Zane Kaber Date: Sat, 3 Jan 2026 22:18:04 -0800 Subject: [PATCH 1/4] chip block logic --- .../ai/evaluation/defender_assignment.cpp | 20 ++++++++++++++++--- src/software/ai/evaluation/enemy_threat.cpp | 16 ++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/software/ai/evaluation/defender_assignment.cpp b/src/software/ai/evaluation/defender_assignment.cpp index 6358f08a2f..9685e4bbf4 100644 --- a/src/software/ai/evaluation/defender_assignment.cpp +++ b/src/software/ai/evaluation/defender_assignment.cpp @@ -3,6 +3,7 @@ #include "software/geom/algorithms/convex_angle.h" #include "software/geom/algorithms/distance.h" #include "software/geom/algorithms/intersection.h" +#include "software/geom/algorithms/intersects.h" #include "software/math/math_functions.h" std::vector getAllDefenderAssignments( @@ -45,9 +46,22 @@ std::vector getAllDefenderAssignments( Segment(primary_threat_position, filtered_threats.at(i).robot.position()); double threat_rating = static_cast(filtered_threats.size()) - i; passing_lanes.emplace_back(ShootingLane{lane, threat_rating}); - assignments.emplace_back( - DefenderAssignment{PASS_DEFENDER, lane.midPoint(), threat_rating}); - } + + Point intercept_point; + if(distance(lane.midPoint(),filtered_threats.at(i).robot.position()) < 1.2 || intersects(field.friendlyDefenseArea(),lane)){ + //We can use the midpoint if it is close enough to the reciever to prevent + //a chip pass or the segment goes through the goalie box + intercept_point = lane.midPoint(); + }else{ + //Otherwise we will position the defender closer to the + intercept_point = filtered_threats.at(i).robot.position() + (primary_threat_position-filtered_threats.at(i).robot.position()).normalize(1.2); + } + + assignments.emplace_back(DefenderAssignment{PASS_DEFENDER, intercept_point, threat_rating}); + + + +} // Construct goal lanes. // Using full list of threats (not filtered threats) since we need to diff --git a/src/software/ai/evaluation/enemy_threat.cpp b/src/software/ai/evaluation/enemy_threat.cpp index 3717cbe208..e5e9576dc7 100644 --- a/src/software/ai/evaluation/enemy_threat.cpp +++ b/src/software/ai/evaluation/enemy_threat.cpp @@ -7,6 +7,7 @@ #include "software/ai/evaluation/intercept.h" #include "software/ai/evaluation/possession.h" #include "software/geom/algorithms/intersects.h" +#include "software/geom/algorithms/contains.h" #include "software/world/team.h" std::map, Robot::cmpRobotByID> findAllReceiverPasserPairs( @@ -42,7 +43,20 @@ std::map, Robot::cmpRobotByID> findAllReceiverPasserPa Segment(passer.position(), receiver.position())); }); - if (!pass_blocked) + bool chick_pass_blocked = + std::any_of(obstacles.begin(), obstacles.end(), + [passer, receiver](const Robot &obstacle) + { + Segment pass_lane = Segment(passer.position(), receiver.position()); + return contains( + Circle(pass_lane.midPoint(), pass_lane.length()/2.0), + obstacle.position()) //Here we check if the obstacle is between the reciever and passer + && contains( + Circle(receiver.position(), 1.2), + obstacle.position()); //Here we check if the obstacle is too close to the reciever + }); + + if (!pass_blocked || !chick_pass_blocked) { if (receiver_passer_pairs.count(receiver) > 0) { From 2379fb86bcbec4c1392cbc42ea598550b48dc267 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 06:31:24 +0000 Subject: [PATCH 2/4] [pre-commit.ci lite] apply automatic fixes --- .../ai/evaluation/defender_assignment.cpp | 33 +++++++++++-------- src/software/ai/evaluation/enemy_threat.cpp | 28 ++++++++-------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/software/ai/evaluation/defender_assignment.cpp b/src/software/ai/evaluation/defender_assignment.cpp index 9685e4bbf4..3f0b64b995 100644 --- a/src/software/ai/evaluation/defender_assignment.cpp +++ b/src/software/ai/evaluation/defender_assignment.cpp @@ -47,21 +47,26 @@ std::vector getAllDefenderAssignments( double threat_rating = static_cast(filtered_threats.size()) - i; passing_lanes.emplace_back(ShootingLane{lane, threat_rating}); - Point intercept_point; - if(distance(lane.midPoint(),filtered_threats.at(i).robot.position()) < 1.2 || intersects(field.friendlyDefenseArea(),lane)){ - //We can use the midpoint if it is close enough to the reciever to prevent - //a chip pass or the segment goes through the goalie box - intercept_point = lane.midPoint(); - }else{ - //Otherwise we will position the defender closer to the - intercept_point = filtered_threats.at(i).robot.position() + (primary_threat_position-filtered_threats.at(i).robot.position()).normalize(1.2); - } - - assignments.emplace_back(DefenderAssignment{PASS_DEFENDER, intercept_point, threat_rating}); - - + Point intercept_point; + if (distance(lane.midPoint(), filtered_threats.at(i).robot.position()) < 1.2 || + intersects(field.friendlyDefenseArea(), lane)) + { + // We can use the midpoint if it is close enough to the receiver to prevent + // a chip pass or the segment goes through the goalie box + intercept_point = lane.midPoint(); + } + else + { + // Otherwise we will position the defender closer to the + intercept_point = + filtered_threats.at(i).robot.position() + + (primary_threat_position - filtered_threats.at(i).robot.position()) + .normalize(1.2); + } -} + assignments.emplace_back( + DefenderAssignment{PASS_DEFENDER, intercept_point, threat_rating}); + } // Construct goal lanes. // Using full list of threats (not filtered threats) since we need to diff --git a/src/software/ai/evaluation/enemy_threat.cpp b/src/software/ai/evaluation/enemy_threat.cpp index e5e9576dc7..4be8d4bfed 100644 --- a/src/software/ai/evaluation/enemy_threat.cpp +++ b/src/software/ai/evaluation/enemy_threat.cpp @@ -6,8 +6,8 @@ #include "software/ai/evaluation/calc_best_shot.h" #include "software/ai/evaluation/intercept.h" #include "software/ai/evaluation/possession.h" -#include "software/geom/algorithms/intersects.h" #include "software/geom/algorithms/contains.h" +#include "software/geom/algorithms/intersects.h" #include "software/world/team.h" std::map, Robot::cmpRobotByID> findAllReceiverPasserPairs( @@ -43,18 +43,20 @@ std::map, Robot::cmpRobotByID> findAllReceiverPasserPa Segment(passer.position(), receiver.position())); }); - bool chick_pass_blocked = - std::any_of(obstacles.begin(), obstacles.end(), - [passer, receiver](const Robot &obstacle) - { - Segment pass_lane = Segment(passer.position(), receiver.position()); - return contains( - Circle(pass_lane.midPoint(), pass_lane.length()/2.0), - obstacle.position()) //Here we check if the obstacle is between the reciever and passer - && contains( - Circle(receiver.position(), 1.2), - obstacle.position()); //Here we check if the obstacle is too close to the reciever - }); + bool chick_pass_blocked = std::any_of( + obstacles.begin(), obstacles.end(), + [passer, receiver](const Robot &obstacle) + { + Segment pass_lane = Segment(passer.position(), receiver.position()); + return contains( + Circle(pass_lane.midPoint(), pass_lane.length() / 2.0), + obstacle.position()) // Here we check if the obstacle is + // between the receiver and passer + && contains( + Circle(receiver.position(), 1.2), + obstacle.position()); // Here we check if the obstacle + // is too close to the receiver + }); if (!pass_blocked || !chick_pass_blocked) { From e17b09c5600edebed4fa9f7d7e8a8b8bffbc3ddd Mon Sep 17 00:00:00 2001 From: Zane Kaber Date: Sun, 4 Jan 2026 12:08:32 -0800 Subject: [PATCH 3/4] enemy threat tests --- .../ai/evaluation/enemy_threat_test.cpp | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/software/ai/evaluation/enemy_threat_test.cpp b/src/software/ai/evaluation/enemy_threat_test.cpp index dd9869d821..7dac5b1a16 100644 --- a/src/software/ai/evaluation/enemy_threat_test.cpp +++ b/src/software/ai/evaluation/enemy_threat_test.cpp @@ -43,10 +43,10 @@ TEST(FindAllPasserReceiverPairsTest, AngularVelocity::zero(), Timestamp::fromSeconds(0)); Robot friendly_robot_1 = Robot(1, Point(3, 2), Vector(0, 0), Angle::zero(), AngularVelocity::zero(), Timestamp::fromSeconds(0)); - Robot friendly_robot_2 = Robot(2, Point(5, 0), Vector(0, 0), Angle::zero(), + Robot friendly_robot_2 = Robot(2, Point(3, 0), Vector(0, 0), Angle::zero(), AngularVelocity::zero(), Timestamp::fromSeconds(0)); - // Blocks the pass from robot 0 to robot 2 + // Blocks the pass from robot 0 to robot 2, is close enough to robot 2 to prevent a chip pass Robot enemy_robot_0 = Robot(0, Point(2, 0), Vector(0, 0), Angle::zero(), AngularVelocity::zero(), Timestamp::fromSeconds(0)); @@ -62,6 +62,32 @@ TEST(FindAllPasserReceiverPairsTest, EXPECT_EQ(result, expected_result); } +TEST(FindAllPasserReceiverPairsTest, + two_passers_one_receiver_with_no_pass_blocked_by_obstacles) +{ + Robot friendly_robot_0 = Robot(0, Point(0, 0), Vector(0, 0), Angle::zero(), + AngularVelocity::zero(), Timestamp::fromSeconds(0)); + Robot friendly_robot_1 = Robot(1, Point(3, 2), Vector(0, 0), Angle::zero(), + AngularVelocity::zero(), Timestamp::fromSeconds(0)); + Robot friendly_robot_2 = Robot(2, Point(5, 0), Vector(0, 0), Angle::zero(), + AngularVelocity::zero(), Timestamp::fromSeconds(0)); + + // Is too far awar from robot 2 to block a chip pass + Robot enemy_robot_0 = Robot(0, Point(2, 0), Vector(0, 0), Angle::zero(), + AngularVelocity::zero(), Timestamp::fromSeconds(0)); + + std::vector all_robots{friendly_robot_0, friendly_robot_1, friendly_robot_2, + enemy_robot_0}; + + auto result = findAllReceiverPasserPairs({friendly_robot_0, friendly_robot_1}, + {friendly_robot_2}, all_robots); + + std::map, Robot::cmpRobotByID> expected_result = { + std::make_pair(friendly_robot_2, std::vector{friendly_robot_0,friendly_robot_1})}; + + EXPECT_EQ(result, expected_result); +} + TEST(FindAllPasserReceiverPairsTest, all_passes_blocked) { Robot friendly_robot_0 = Robot(0, Point(0, 0), Vector(0, 0), Angle::zero(), @@ -70,7 +96,7 @@ TEST(FindAllPasserReceiverPairsTest, all_passes_blocked) AngularVelocity::zero(), Timestamp::fromSeconds(0)); // Blocks the pass from robot 0 to robot 1 - Robot enemy_robot_0 = Robot(0, Point(2, 0), Vector(0, 0), Angle::zero(), + Robot enemy_robot_0 = Robot(0, Point(4, 0), Vector(0, 0), Angle::zero(), AngularVelocity::zero(), Timestamp::fromSeconds(0)); std::vector all_robots{friendly_robot_0, friendly_robot_1, enemy_robot_0}; @@ -81,6 +107,28 @@ TEST(FindAllPasserReceiverPairsTest, all_passes_blocked) EXPECT_TRUE(result.empty()); } +TEST(FindAllPasserReceiverPairsTest, chip_pass_not_blocked) +{ + Robot friendly_robot_0 = Robot(0, Point(0, 0), Vector(0, 0), Angle::zero(), + AngularVelocity::zero(), Timestamp::fromSeconds(0)); + Robot friendly_robot_1 = Robot(1, Point(5, 0), Vector(0, 0), Angle::zero(), + AngularVelocity::zero(), Timestamp::fromSeconds(0)); + + // Not close enough to robot 1 to block the pass + Robot enemy_robot_0 = Robot(0, Point(2, 0), Vector(0, 0), Angle::zero(), + AngularVelocity::zero(), Timestamp::fromSeconds(0)); + + std::vector all_robots{friendly_robot_0, friendly_robot_1, enemy_robot_0}; + + auto result = + findAllReceiverPasserPairs({friendly_robot_0}, {friendly_robot_1}, all_robots); + + std::map, Robot::cmpRobotByID> expected_result = { + std::make_pair(friendly_robot_1, std::vector{friendly_robot_0})}; + + EXPECT_EQ(result, expected_result); +} + TEST(FindAllPasserReceiverPairsTest, receiver_with_multiple_passers) { Robot friendly_robot_0 = Robot(0, Point(0, 0), Vector(0, 0), Angle::zero(), @@ -152,7 +200,7 @@ TEST(GetNumPassesToRobotTest, one_simple_pass_to_robot_with_no_obstacles) EXPECT_EQ(passer.value(), friendly_robot_0); } -// TODO: Re-enable as part of https://github.com/UBC-Thunderbots/Software/issues/642 +// TODO: Re-enable as part of + // TEST(GetNumPassesToRobotTest, two_passes_around_a_single_obstacle) //{ // Robot friendly_robot_0 = Robot(0, Point(0, 0), Vector(0, 0), Angle::zero(), From 9c6c7191d038855b8811ce3d604d608be3609568 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 20:17:43 +0000 Subject: [PATCH 4/4] [pre-commit.ci lite] apply automatic fixes --- src/software/ai/evaluation/enemy_threat_test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/software/ai/evaluation/enemy_threat_test.cpp b/src/software/ai/evaluation/enemy_threat_test.cpp index 7dac5b1a16..3ff62fc3f0 100644 --- a/src/software/ai/evaluation/enemy_threat_test.cpp +++ b/src/software/ai/evaluation/enemy_threat_test.cpp @@ -46,7 +46,8 @@ TEST(FindAllPasserReceiverPairsTest, Robot friendly_robot_2 = Robot(2, Point(3, 0), Vector(0, 0), Angle::zero(), AngularVelocity::zero(), Timestamp::fromSeconds(0)); - // Blocks the pass from robot 0 to robot 2, is close enough to robot 2 to prevent a chip pass + // Blocks the pass from robot 0 to robot 2, is close enough to robot 2 to prevent a + // chip pass Robot enemy_robot_0 = Robot(0, Point(2, 0), Vector(0, 0), Angle::zero(), AngularVelocity::zero(), Timestamp::fromSeconds(0)); @@ -83,7 +84,8 @@ TEST(FindAllPasserReceiverPairsTest, {friendly_robot_2}, all_robots); std::map, Robot::cmpRobotByID> expected_result = { - std::make_pair(friendly_robot_2, std::vector{friendly_robot_0,friendly_robot_1})}; + std::make_pair(friendly_robot_2, + std::vector{friendly_robot_0, friendly_robot_1})}; EXPECT_EQ(result, expected_result); }