34 TbotsProto::PassingConfig passing_config);
52 const World &world,
unsigned int num_positions,
53 const std::vector<Point> &existing_receiver_positions = {},
54 const std::optional<Point> &pass_origin_override = std::nullopt);
69 void updateBestReceiverPositions(
70 std::map<ZoneEnum, PassWithRating> &best_receiving_positions,
const World &world,
71 const Point &pass_origin,
const std::vector<ZoneEnum> &zones_to_sample,
72 unsigned int num_samples_per_zone);
86 std::vector<ZoneEnum> getTopZones(
87 const std::map<ZoneEnum, PassWithRating> &best_receiving_positions,
88 unsigned int num_positions,
const Point &pass_origin,
89 const std::vector<Point> &existing_receiver_positions);
96 void visualizeBestReceivingPositionsAndZones(
97 const std::map<ZoneEnum, PassWithRating> &best_receiving_positions,
98 const std::vector<ZoneEnum> &top_zones);
101 std::shared_ptr<const FieldPitchDivision<ZoneEnum>> pitch_division_;
104 std::map<ZoneEnum, Point> prev_best_receiving_positions;
107 TbotsProto::PassingConfig passing_config_;
110 std::vector<TbotsProto::DebugShapes::DebugShape> debug_shapes;
113 std::mt19937 random_num_gen_;
116 static constexpr int RNG_SEED = 1010;
119template <
class ZoneEnum>
122 TbotsProto::PassingConfig passing_config)
123 : pitch_division_(pitch_division),
124 passing_config_(passing_config),
125 random_num_gen_(RNG_SEED)
129template <
class ZoneEnum>
131 const World &world,
unsigned int num_positions,
132 const std::vector<Point> &existing_receiver_positions,
133 const std::optional<Point> &pass_origin_override)
135 std::map<ZoneEnum, PassWithRating> best_receiving_positions;
136 debug_shapes.clear();
139 const auto &receiver_config = passing_config_.receiver_position_generator_config();
145 LOG(WARNING) <<
"Not enough friendly robots to assign " << num_positions
146 <<
" receiver positions. Assigning "
148 existing_receiver_positions.size()
149 <<
" receiver positions instead";
151 existing_receiver_positions.size());
155 for (
const auto &[zone, prev_best_receiving_position] : prev_best_receiving_positions)
161 double receiver_position_rating =
162 rateReceivingPosition(world, pass, passing_config_) *
163 receiver_config.previous_best_receiver_position_score_multiplier();
164 best_receiving_positions.insert_or_assign(
170 updateBestReceiverPositions(best_receiving_positions, world, pass_origin,
171 pitch_division_->getAllZoneIds(),
172 receiver_config.num_initial_samples_per_zone());
175 std::vector<ZoneEnum> top_zones =
176 getTopZones(best_receiving_positions, num_positions, pass_origin,
177 existing_receiver_positions);
180 updateBestReceiverPositions(best_receiving_positions, world, pass_origin, top_zones,
181 receiver_config.num_additional_samples_per_top_zone());
182 std::sort(top_zones.begin(), top_zones.end(),
183 [&](
const ZoneEnum &z1,
const ZoneEnum &z2) {
184 return best_receiving_positions.find(z1)->second.rating >
185 best_receiving_positions.find(z2)->second.rating;
189 std::vector<Point> best_positions;
190 prev_best_receiving_positions.clear();
191 for (
const auto zone : top_zones)
193 Point best_position =
194 best_receiving_positions.find(zone)->second.pass.receiverPoint();
195 best_positions.push_back(best_position);
196 prev_best_receiving_positions.insert_or_assign(zone, best_position);
200 if (receiver_config.receiver_vis_config()
201 .visualize_best_receiving_positions_and_zones())
203 visualizeBestReceivingPositionsAndZones(best_receiving_positions, top_zones);
206 return best_positions;
209template <
class ZoneEnum>
211 const std::map<ZoneEnum, PassWithRating> &best_receiving_positions,
212 const std::vector<ZoneEnum> &top_zones)
214 for (
unsigned int i = 0; i < top_zones.size(); i++)
216 debug_shapes.push_back(*createDebugShape(pitch_division_->getZone(top_zones[i]),
217 std::to_string(i + 1),
218 std::to_string(i + 1)));
220 debug_shapes.push_back(*createDebugShape(
222 best_receiving_positions.find(top_zones[i])->second.pass.receiverPoint(),
224 std::to_string(i + 1) +
"rpg", std::to_string(i + 1) +
"rpg"));
227 LOG(VISUALIZE) << *createDebugShapes(debug_shapes);
230template <
class ZoneEnum>
232 std::map<ZoneEnum, PassWithRating> &best_receiving_positions,
const World &world,
233 const Point &pass_origin,
const std::vector<ZoneEnum> &zones_to_sample,
234 unsigned int num_samples_per_zone)
236 for (
const auto &zone_id : zones_to_sample)
238 auto zone = pitch_division_->getZone(zone_id);
239 std::uniform_real_distribution x_distribution(zone.xMin(), zone.xMax());
240 std::uniform_real_distribution y_distribution(zone.yMin(), zone.yMax());
245 const auto &best_sampled_pass_iter = best_receiving_positions.find(zone_id);
246 if (best_sampled_pass_iter != best_receiving_positions.end())
248 best_pass_for_receiving = best_sampled_pass_iter->second;
252 for (
unsigned int i = 0; i < num_samples_per_zone; ++i)
256 Point(x_distribution(random_num_gen_), y_distribution(random_num_gen_)),
258 double rating = rateReceivingPosition(world, pass, passing_config_);
260 if (rating > best_pass_for_receiving.rating)
266 best_receiving_positions.insert_or_assign(zone_id, best_pass_for_receiving);
270template <
class ZoneEnum>
272 const std::map<ZoneEnum, PassWithRating> &best_receiving_positions,
273 unsigned int num_positions,
const Point &pass_origin,
274 const std::vector<Point> &existing_receiver_positions)
276 std::vector<ZoneEnum> top_zones;
279 auto all_zones = pitch_division_->getAllZoneIds();
280 std::sort(all_zones.begin(), all_zones.end(),
281 [&](
const ZoneEnum &z1,
const ZoneEnum &z2) {
282 return best_receiving_positions.find(z1)->second.rating >
283 best_receiving_positions.find(z2)->second.rating;
288 const Angle min_angle_diff_between_receivers =
290 .min_angle_between_receivers_deg());
292 for (
unsigned int i = 0; i < all_zones.size() && top_zones.size() < num_positions;
295 Angle curr_pass_angle =
296 best_receiving_positions.find(all_zones[i])->second.pass.passerOrientation();
300 bool no_prev_receivers_close =
301 std::none_of(top_zones.begin(), top_zones.end(), [&](
const ZoneEnum &zone) {
302 return curr_pass_angle.minDiff(best_receiving_positions.find(zone)
303 ->second.pass.passerOrientation()) <
304 min_angle_diff_between_receivers;
309 no_prev_receivers_close =
310 no_prev_receivers_close &&
312 existing_receiver_positions.begin(), existing_receiver_positions.end(),
313 [&](
const Point &existing_receiver_position) {
314 return curr_pass_angle.minDiff(
315 (existing_receiver_position - pass_origin).orientation()) <
316 min_angle_diff_between_receivers;
319 if (no_prev_receivers_close)
321 top_zones.push_back(all_zones[i]);
326 if (top_zones.size() < num_positions)
329 <<
"Not enough receiver positions were found. Expected to find "
330 << num_positions <<
" receiver positions, but only found " << top_zones.size()
331 <<
". Consider reducing 'min_angle_between_receivers_deg' in the dynamic parameters";
332 for (
unsigned int i = 0; i < all_zones.size() && top_zones.size() < num_positions;
335 if (std::find(top_zones.begin(), top_zones.end(), all_zones[i]) ==
338 top_zones.push_back(all_zones[i]);