| from __future__ import annotations |
|
|
| from dataclasses import dataclass |
| from typing import Any |
|
|
|
|
| ActionDict = dict[str, list[Any]] |
| Point = tuple[int, int] |
|
|
|
|
| @dataclass(frozen=True) |
| class CandidateAssignment: |
| agent_id: str |
| order_id: str |
| score: float |
| estimated_cost: int |
| finish_time: int |
| slack: int |
| reward_value: float |
| reward_density: float |
| feasible_now: bool |
|
|
|
|
| @dataclass(frozen=True) |
| class CandidateRejection: |
| order_id: str |
| score: float |
| estimated_best_cost: int |
| slack_to_cutoff: int |
| reward_value: float |
| has_idle_agents: bool |
|
|
|
|
| def _as_point(value: Any) -> Point: |
| x, y = value |
| return int(x), int(y) |
|
|
|
|
| def _grid_congested_set(state: dict[str, Any]) -> set[Point]: |
| grid = state.get("grid", {}) |
| return {_as_point(point) for point in grid.get("congested_zones", [])} |
|
|
|
|
| def _hotspot_points(state: dict[str, Any]) -> tuple[Point, ...]: |
| grid = state.get("grid", {}) |
| return tuple(_as_point(point) for point in grid.get("hotspots", [])) |
|
|
|
|
| def _point_cost(point: Point, congested_zones: set[Point]) -> int: |
| return 2 if point in congested_zones else 1 |
|
|
|
|
| def _route_cost(start: Point, end: Point, congested_zones: set[Point]) -> int: |
| if start == end: |
| return 0 |
|
|
| x1, y1 = start |
| x2, y2 = end |
|
|
| def walk(horizontal_first: bool) -> int: |
| cost = 0 |
| x, y = x1, y1 |
|
|
| def step_to(nx: int, ny: int) -> None: |
| nonlocal x, y, cost |
| if (nx, ny) == (x, y): |
| return |
| x, y = nx, ny |
| cost += _point_cost((x, y), congested_zones) |
|
|
| if horizontal_first: |
| dx = 1 if x2 >= x else -1 |
| while x != x2: |
| step_to(x + dx, y) |
| dy = 1 if y2 >= y else -1 |
| while y != y2: |
| step_to(x, y + dy) |
| else: |
| dy = 1 if y2 >= y else -1 |
| while y != y2: |
| step_to(x, y + dy) |
| dx = 1 if x2 >= x else -1 |
| while x != x2: |
| step_to(x + dx, y) |
|
|
| return cost |
|
|
| return min(walk(True), walk(False)) |
|
|
|
|
| def estimate_job_cost( |
| agent_location: Point, |
| pickup_location: Point, |
| drop_location: Point, |
| congested_zones: set[Point], |
| service_time: int = 1, |
| ) -> int: |
| return ( |
| _route_cost(agent_location, pickup_location, congested_zones) |
| + _route_cost(pickup_location, drop_location, congested_zones) |
| + service_time |
| ) |
|
|
|
|
| def _distance_to_nearest_hotspot(point: Point, hotspots: tuple[Point, ...]) -> int: |
| if not hotspots: |
| return 0 |
| x, y = point |
| return min(abs(x - hx) + abs(y - hy) for hx, hy in hotspots) |
|
|
|
|
| def _visible_orders(state: dict[str, Any]) -> list[dict[str, Any]]: |
| return [order for order in state.get("orders", []) if order.get("status") == "unassigned"] |
|
|
|
|
| def _idle_agents(state: dict[str, Any]) -> list[dict[str, Any]]: |
| return [agent for agent in state.get("agents", []) if agent.get("status") == "idle"] |
|
|
|
|
| def _service_cutoff(order: dict[str, Any]) -> int: |
| cutoff = order.get("service_cutoff_time") |
| if cutoff is not None: |
| return int(cutoff) |
| return int(order["deadline"]) |
|
|
|
|
| def _baseline_candidate( |
| agent: dict[str, Any], |
| order: dict[str, Any], |
| congested_zones: set[Point], |
| current_time: int, |
| ) -> tuple[int, int, float, str, str]: |
| agent_location = _as_point(agent["location"]) |
| pickup = _as_point(order["pickup_location"]) |
| drop = _as_point(order["drop_location"]) |
| estimated_cost = estimate_job_cost(agent_location, pickup, drop, congested_zones) |
| slack = int(order["deadline"]) - (current_time + estimated_cost) |
| return ( |
| int(order["deadline"]), |
| estimated_cost, |
| -float(order["reward_value"]), |
| str(order["order_id"]), |
| str(agent["agent_id"]), |
| ) |
|
|
|
|
| def _score_candidate( |
| agent: dict[str, Any], |
| order: dict[str, Any], |
| congested_zones: set[Point], |
| hotspots: tuple[Point, ...], |
| current_time: int, |
| ) -> CandidateAssignment: |
| agent_location = _as_point(agent["location"]) |
| pickup = _as_point(order["pickup_location"]) |
| drop = _as_point(order["drop_location"]) |
| reward_value = float(order["reward_value"]) |
| deadline = int(order["deadline"]) |
| estimated_cost = estimate_job_cost(agent_location, pickup, drop, congested_zones) |
| finish_time = current_time + estimated_cost |
| slack = deadline - finish_time |
| feasible_now = slack >= 0 |
| reward_density = reward_value / max(estimated_cost, 1) |
|
|
| lateness_penalty = abs(slack) * 4.0 if slack < 0 else 0.0 |
| urgency_bonus = max(0.0, min(slack, 6)) * 0.8 |
| feasible_bonus = 8.0 if feasible_now else -6.0 |
| hotspot_bonus = max(0, 6 - _distance_to_nearest_hotspot(drop, hotspots)) * 0.7 |
| congestion_drag = max(0, estimated_cost - (_route_cost(agent_location, pickup, set()) + _route_cost(pickup, drop, set()) + 1)) |
|
|
| score = ( |
| (2.6 * reward_value) |
| + (11.0 * reward_density) |
| + urgency_bonus |
| + feasible_bonus |
| + hotspot_bonus |
| - (1.45 * estimated_cost) |
| - (1.2 * congestion_drag) |
| - lateness_penalty |
| ) |
|
|
| return CandidateAssignment( |
| agent_id=str(agent["agent_id"]), |
| order_id=str(order["order_id"]), |
| score=score, |
| estimated_cost=estimated_cost, |
| finish_time=finish_time, |
| slack=slack, |
| reward_value=reward_value, |
| reward_density=reward_density, |
| feasible_now=feasible_now, |
| ) |
|
|
|
|
| def _score_rejection_candidate( |
| order: dict[str, Any], |
| idle_agents: list[dict[str, Any]], |
| congested_zones: set[Point], |
| current_time: int, |
| ) -> CandidateRejection: |
| reward_value = float(order["reward_value"]) |
| cutoff = _service_cutoff(order) |
| estimated_costs = [ |
| estimate_job_cost( |
| _as_point(agent["location"]), |
| _as_point(order["pickup_location"]), |
| _as_point(order["drop_location"]), |
| congested_zones, |
| ) |
| for agent in idle_agents |
| ] |
| estimated_best_cost = min(estimated_costs) if estimated_costs else 999 |
| best_finish = current_time + estimated_best_cost |
| slack_to_cutoff = cutoff - best_finish |
| reject_pressure = 0.0 |
|
|
| if not idle_agents: |
| reject_pressure += 4.0 |
| if slack_to_cutoff < 0: |
| reject_pressure += 9.0 + (2.2 * abs(slack_to_cutoff)) |
| elif slack_to_cutoff <= 1: |
| reject_pressure += 4.5 |
|
|
| if reward_value <= 8: |
| reject_pressure += 2.4 |
| elif reward_value <= 10: |
| reject_pressure += 1.6 |
| elif reward_value >= 16: |
| reject_pressure -= 2.2 |
|
|
| reward_density = reward_value / max(estimated_best_cost, 1) |
| if reward_density < 1.0: |
| reject_pressure += 3.6 |
| elif reward_density < 1.35: |
| reject_pressure += 2.4 |
| elif reward_density > 2.5: |
| reject_pressure -= 1.0 |
|
|
| return CandidateRejection( |
| order_id=str(order["order_id"]), |
| score=reject_pressure, |
| estimated_best_cost=estimated_best_cost, |
| slack_to_cutoff=slack_to_cutoff, |
| reward_value=reward_value, |
| has_idle_agents=bool(idle_agents), |
| ) |
|
|
|
|
| def build_action(assignments: list[tuple[str, str]], rejections: list[str] | None = None) -> ActionDict: |
| return { |
| "assignments": [ |
| {"agent_id": agent_id, "order_id": order_id} |
| for agent_id, order_id in assignments |
| ], |
| "rejections": list(rejections or []), |
| } |
|
|
|
|
| def baseline_policy(state: dict[str, Any]) -> ActionDict: |
| current_time = int(state.get("time", 0)) |
| congested_zones = _grid_congested_set(state) |
| idle_agents = sorted(_idle_agents(state), key=lambda agent: str(agent["agent_id"])) |
| remaining_orders = list(_visible_orders(state)) |
| assignments: list[tuple[str, str]] = [] |
|
|
| for agent in idle_agents: |
| if not remaining_orders: |
| break |
| ranked_orders = sorted( |
| remaining_orders, |
| key=lambda order: _baseline_candidate(agent, order, congested_zones, current_time), |
| ) |
| chosen = ranked_orders[0] |
| assignments.append((str(agent["agent_id"]), str(chosen["order_id"]))) |
| remaining_orders = [ |
| order for order in remaining_orders if str(order["order_id"]) != str(chosen["order_id"]) |
| ] |
|
|
| return build_action(assignments) |
|
|
|
|
| def target_policy(state: dict[str, Any]) -> ActionDict: |
| current_time = int(state.get("time", 0)) |
| congested_zones = _grid_congested_set(state) |
| hotspots = _hotspot_points(state) |
| idle_agents = list(_idle_agents(state)) |
| available_orders = list(_visible_orders(state)) |
|
|
| candidates = [ |
| _score_candidate(agent, order, congested_zones, hotspots, current_time) |
| for agent in idle_agents |
| for order in available_orders |
| ] |
| candidates.sort( |
| key=lambda item: ( |
| -item.score, |
| not item.feasible_now, |
| -item.reward_density, |
| item.estimated_cost, |
| item.agent_id, |
| item.order_id, |
| ) |
| ) |
|
|
| chosen_agents: set[str] = set() |
| chosen_orders: set[str] = set() |
| assignments: list[tuple[str, str]] = [] |
|
|
| for candidate in candidates: |
| if candidate.agent_id in chosen_agents or candidate.order_id in chosen_orders: |
| continue |
| if candidate.score < 0 and assignments: |
| continue |
| chosen_agents.add(candidate.agent_id) |
| chosen_orders.add(candidate.order_id) |
| assignments.append((candidate.agent_id, candidate.order_id)) |
|
|
| remaining_orders = [ |
| order |
| for order in available_orders |
| if str(order["order_id"]) not in chosen_orders |
| ] |
| rejection_candidates = [ |
| _score_rejection_candidate(order, idle_agents, congested_zones, current_time) |
| for order in remaining_orders |
| ] |
| rejection_candidates.sort( |
| key=lambda item: ( |
| -item.score, |
| item.slack_to_cutoff, |
| item.estimated_best_cost, |
| item.order_id, |
| ) |
| ) |
|
|
| rejections: list[str] = [] |
| for candidate in rejection_candidates: |
| if candidate.score < 6.5: |
| continue |
| if candidate.reward_value >= 16 and candidate.slack_to_cutoff >= -2: |
| continue |
| if candidate.reward_value >= 12 and candidate.slack_to_cutoff >= 0 and candidate.estimated_best_cost <= 8: |
| continue |
| rejections.append(candidate.order_id) |
|
|
| return build_action(assignments, rejections) |
|
|
|
|
| __all__ = [ |
| "ActionDict", |
| "CandidateAssignment", |
| "CandidateRejection", |
| "baseline_policy", |
| "build_action", |
| "estimate_job_cost", |
| "target_policy", |
| ] |
|
|