120 std::span<const LocalMoveRequest> requests,
121 std::span<const LocalMoveOption> options, CanEnterFn&& can_enter,
123 scratch.request_order_.clear();
124 scratch.option_owned_.assign(options.size(), 0);
125 scratch.option_feasible_.assign(options.size(), 0);
126 scratch.demand_coords_.clear();
127 scratch.congestion_.clear();
128 scratch.claimed_.clear();
129 scratch.decisions_.clear();
131 scratch.request_order_.resize(requests.size());
132 for (std::size_t i = 0; i < requests.size(); ++i) {
133 const auto& request = requests[i];
134 if (request.option_offset > options.size() ||
135 request.option_count > options.size() - request.option_offset) {
136 return {LocalCoordinationStatus::InvalidInput, 0, {}, {}};
138 scratch.request_order_[i] = i;
139 for (std::size_t j = 0; j < request.option_count; ++j) {
140 const auto option_index = request.option_offset + j;
141 if (scratch.option_owned_[option_index] != 0) {
142 return {LocalCoordinationStatus::InvalidInput, 0, {}, {}};
144 scratch.option_owned_[option_index] = 1;
148 std::sort(scratch.request_order_.begin(), scratch.request_order_.end(),
149 [&](std::size_t lhs, std::size_t rhs) {
150 return requests[lhs].agent < requests[rhs].agent;
152 for (std::size_t i = 1; i < scratch.request_order_.size(); ++i) {
153 if (requests[scratch.request_order_[i - 1]].agent ==
154 requests[scratch.request_order_[i]].agent) {
155 return {LocalCoordinationStatus::InvalidInput, 0, {}, {}};
159 scratch.decisions_.resize(requests.size());
160 for (std::size_t i = 0; i < requests.size(); ++i) {
161 const auto& request = requests[i];
167 LocalMoveDecisionStatus::Wait,
169 for (std::size_t j = 0; j < request.option_count; ++j) {
170 const auto option_index = request.option_offset + j;
171 const auto& option = options[option_index];
172 if (!
static_cast<bool>(std::invoke(can_enter, request, option))) {
175 scratch.option_feasible_[option_index] = 1;
176 auto duplicate =
false;
177 for (std::size_t previous = 0; previous < j; ++previous) {
178 const auto previous_index = request.option_offset + previous;
179 if (scratch.option_feasible_[previous_index] != 0 &&
180 options[previous_index].to == option.to) {
186 scratch.demand_coords_.push_back(option.to);
191 std::sort(scratch.demand_coords_.begin(), scratch.demand_coords_.end(),
192 detail::local_coord_less);
193 for (
const auto coord : scratch.demand_coords_) {
194 if (scratch.congestion_.empty() ||
195 scratch.congestion_.back().coord != coord) {
197 }
else if (scratch.congestion_.back().demand !=
198 std::numeric_limits<std::uint32_t>::max()) {
199 ++scratch.congestion_.back().demand;
203 std::sort(scratch.request_order_.begin(), scratch.request_order_.end(),
204 [&](std::size_t lhs, std::size_t rhs) {
205 if (requests[lhs].priority != requests[rhs].priority) {
206 return requests[lhs].priority > requests[rhs].priority;
208 return requests[lhs].agent < requests[rhs].agent;
211 auto reserved_count = std::size_t{};
212 for (
const auto request_index : scratch.request_order_) {
213 const auto& request = requests[request_index];
214 auto best = options.size();
215 for (std::size_t j = 0; j < request.option_count; ++j) {
216 const auto option_index = request.option_offset + j;
217 const auto& option = options[option_index];
218 if (scratch.option_feasible_[option_index] == 0 ||
219 std::binary_search(scratch.claimed_.begin(), scratch.claimed_.end(),
220 option.to, detail::local_coord_less)) {
223 if (best == options.size() ||
224 option.preference < options[best].preference ||
225 (option.preference == options[best].preference &&
226 detail::local_coord_less(option.to, options[best].to))) {
230 if (best == options.size()) {
233 const auto& chosen = options[best];
235 std::lower_bound(scratch.claimed_.begin(), scratch.claimed_.end(),
236 chosen.to, detail::local_coord_less);
237 scratch.claimed_.insert(claimed, chosen.to);
243 LocalMoveDecisionStatus::Reserved,
245 const auto congestion =
246 std::lower_bound(scratch.congestion_.begin(), scratch.congestion_.end(),
248 return detail::local_coord_less(lhs.coord, rhs);
250 if (congestion != scratch.congestion_.end() &&
251 congestion->coord == chosen.to &&
252 congestion->reserved != std::numeric_limits<std::uint32_t>::max()) {
253 ++congestion->reserved;
258 const auto status = reserved_count == requests.size()
259 ? LocalCoordinationStatus::Complete
260 : LocalCoordinationStatus::Partial;
261 return {status, reserved_count,
262 std::span<const LocalMoveDecision>{scratch.decisions_},
263 std::span<const LocalCongestion>{scratch.congestion_}};