2023-10-07 17:07:32 +02:00

174 lines
5.7 KiB
Python

from __future__ import annotations
from collections.abc import Iterator
from typing import Any
from shapely.geometry import MultiPolygon, Point
from shapely.geometry.base import BaseGeometry
from game.data.doctrine import Doctrine
from game.flightplan.waypointsolver import WaypointSolver
from game.flightplan.waypointstrategy import WaypointStrategy
from game.utils import meters, nautical_miles
MIN_DISTANCE_FROM_DEPARTURE = nautical_miles(5)
class ThreatTolerantIpStrategy(WaypointStrategy):
def __init__(
self,
departure: Point,
target: Point,
doctrine: Doctrine,
threat_zones: MultiPolygon,
) -> None:
super().__init__(threat_zones)
self.prerequisite(target).min_distance_from(
departure, doctrine.min_ingress_distance
)
self.require().at_least(MIN_DISTANCE_FROM_DEPARTURE).away_from(departure)
self.require().at_most(meters(departure.distance(target))).away_from(departure)
self.require().at_least(doctrine.min_ingress_distance).away_from(target)
max_ip_range = min(
doctrine.max_ingress_distance, meters(departure.distance(target))
)
self.require().at_most(max_ip_range).away_from(target)
self.threat_tolerance(target, max_ip_range, nautical_miles(5))
self.nearest(departure)
class UnsafeIpStrategy(WaypointStrategy):
def __init__(
self,
departure: Point,
target: Point,
doctrine: Doctrine,
threat_zones: MultiPolygon,
) -> None:
super().__init__(threat_zones)
self.prerequisite(target).min_distance_from(
departure, doctrine.min_ingress_distance
)
self.require().at_least(MIN_DISTANCE_FROM_DEPARTURE).away_from(
departure, "departure"
)
self.require().at_most(meters(departure.distance(target))).away_from(
departure, "departure"
)
self.require().at_least(doctrine.min_ingress_distance).away_from(
target, "target"
)
max_ip_range = min(
doctrine.max_ingress_distance, meters(departure.distance(target))
)
self.require().at_most(max_ip_range).away_from(target, "target")
self.nearest(departure)
class SafeIpStrategy(WaypointStrategy):
def __init__(
self,
departure: Point,
target: Point,
doctrine: Doctrine,
threat_zones: MultiPolygon,
) -> None:
super().__init__(threat_zones)
self.prerequisite(departure).is_safe()
self.prerequisite(target).min_distance_from(
departure, doctrine.min_ingress_distance
)
self.require().at_least(MIN_DISTANCE_FROM_DEPARTURE).away_from(
departure, "departure"
)
self.require().at_most(meters(departure.distance(target))).away_from(
departure, "departure"
)
self.require().at_least(doctrine.min_ingress_distance).away_from(
target, "target"
)
self.require().at_most(
min(doctrine.max_ingress_distance, meters(departure.distance(target)))
).away_from(target, "target")
self.require().safe()
self.nearest(departure)
class SafeBackTrackingIpStrategy(WaypointStrategy):
def __init__(
self,
departure: Point,
target: Point,
doctrine: Doctrine,
threat_zones: MultiPolygon,
) -> None:
super().__init__(threat_zones)
self.require().at_least(MIN_DISTANCE_FROM_DEPARTURE).away_from(
departure, "departure"
)
self.require().at_least(doctrine.min_ingress_distance).away_from(
target, "target"
)
self.require().at_most(doctrine.max_ingress_distance).away_from(
target, "target"
)
self.require().safe()
self.nearest(departure)
class UnsafeBackTrackingIpStrategy(WaypointStrategy):
def __init__(
self,
departure: Point,
target: Point,
doctrine: Doctrine,
threat_zones: MultiPolygon,
) -> None:
super().__init__(threat_zones)
self.require().at_least(MIN_DISTANCE_FROM_DEPARTURE).away_from(
departure, "departure"
)
self.require().at_least(doctrine.min_ingress_distance).away_from(
target, "target"
)
self.require().at_most(doctrine.max_ingress_distance).away_from(
target, "target"
)
self.nearest(departure)
class IpSolver(WaypointSolver):
def __init__(
self,
departure: Point,
target: Point,
doctrine: Doctrine,
threat_zones: MultiPolygon,
) -> None:
super().__init__()
self.departure = departure
self.target = target
self.doctrine = doctrine
self.threat_zones = threat_zones
self.add_strategy(SafeIpStrategy(departure, target, doctrine, threat_zones))
self.add_strategy(
ThreatTolerantIpStrategy(departure, target, doctrine, threat_zones)
)
self.add_strategy(UnsafeIpStrategy(departure, target, doctrine, threat_zones))
self.add_strategy(
SafeBackTrackingIpStrategy(departure, target, doctrine, threat_zones)
)
# TODO: The cases that require this are not covered by any tests.
self.add_strategy(
UnsafeBackTrackingIpStrategy(departure, target, doctrine, threat_zones)
)
def describe_metadata(self) -> dict[str, Any]:
return {"doctrine": self.doctrine.name}
def describe_inputs(self) -> Iterator[tuple[str, BaseGeometry]]:
yield "departure", self.departure
yield "target", self.target
yield "threat_zones", self.threat_zones