From 827138fc6a459603428f933a212886eeaa2cfe44 Mon Sep 17 00:00:00 2001 From: Khopa Date: Tue, 15 Oct 2019 23:52:43 +0200 Subject: [PATCH] AI Flight planner generate CAS & SEAD --- gen/flights/ai_flight_planner.py | 123 ++++++++++++++++++++++++---- gen/flights/ai_flight_planner_db.py | 1 + gen/flights/flight.py | 4 + 3 files changed, 114 insertions(+), 14 deletions(-) diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index c3ca7cca..a7d29754 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -3,17 +3,20 @@ import operator import typing import random -from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE +from game import db +from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE from gen.flights.flight import Flight, FlightType # TODO : Ideally should be based on the aircraft type instead / Availability of fuel -STRIKE_MAX_RANGE = 30000 -SEAD_MAX_RANGE = 30000 +STRIKE_MAX_RANGE = 150000 +SEAD_MAX_RANGE = 150000 MAX_NUMBER_OF_INTERCEPTION_GROUP = 3 MISSION_DURATION = 120 # in minutes CAP_EVERY_X_MINUTES = 20 +CAS_EVERY_X_MINUTES = 30 +SEAD_EVERY_X_MINUTES = 40 class FlightPlanner: @@ -28,6 +31,9 @@ class FlightPlanner: sead_flights = [] flights = [] + potential_sead_targets = [] + potential_strike_targets = [] + def __init__(self, from_cp, game): # TODO : have the flight planner depend on a 'stance' setting : [Defensive, Aggresive... etc] and faction doctrine self.from_cp = from_cp @@ -45,10 +51,14 @@ class FlightPlanner: self.strike_flights = [] self.sead_flights = [] self.flights = [] + self.potential_sead_targets = [] + self.potential_strike_targets = [] def plan_flights(self): self.reset() + self.compute_sead_targets() + self.compute_strike_targets() # The priority is to assign air-superiority fighter or interceptor to interception roles, so they can scramble if there is an attacker self.commision_interceptors() @@ -56,9 +66,11 @@ class FlightPlanner: # Then some CAP patrol for the next 2 hours self.commision_barcap() - # TODO : commision CAS / BAI + # Then setup cas + self.commision_cas() - # TODO : commision SEAD + # Then prepare some sead flights if required + self.commision_sead() # TODO : commision STRIKE / ANTISHIP @@ -130,13 +142,95 @@ class FlightPlanner: for k, v in inventory.items(): self.aircraft_inventory[k] = v - def _get_strike_targets_in_range(self): + def commision_cas(self): + """ + Pick some aircraft to assign them to CAS + """ + + possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in CAS_CAPABLE and v >= 2] + inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft}) + cas_location = self._get_cas_locations() + + if len(cas_location) > 0: + + offset = random.randint(0,5) + for i in range(int(MISSION_DURATION/CAS_EVERY_X_MINUTES)): + + try: + unit = random.choice([k for k, v in inventory.items() if v >= 2]) + except IndexError: + break + + inventory[unit] = inventory[unit] - 2 + flight = Flight(unit, 2, self.from_cp, FlightType.CAS) + + # Flight path : fly over each ground object (TODO : improve) + flight.points = [] + flight.scheduled_in = offset + i*random.randint(CAS_EVERY_X_MINUTES-5, CAS_EVERY_X_MINUTES+5) + + location = random.choice(cas_location) + flight.points.append([location[0], location[1], 1000]) # TODO : Egress / Ingress points + + self.cas_flights.append(flight) + self.flights.append(flight) + + # Update inventory + for k, v in inventory.items(): + self.aircraft_inventory[k] = v + + def commision_sead(self): + """ + Pick some aircraft to assign them to SEAD tasks + """ + + possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in SEAD_CAPABLE and v >= 2] + inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft}) + + if len(self.potential_sead_targets) > 0: + + offset = random.randint(0,5) + for i in range(int(MISSION_DURATION/SEAD_EVERY_X_MINUTES)): + + if len(self.potential_sead_targets) <= 0: + break + + try: + unit = random.choice([k for k, v in inventory.items() if v >= 2]) + except IndexError: + break + + inventory[unit] = inventory[unit] - 2 + flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD])) + + # Flight path : fly over each ground object (TODO : improve) + flight.points = [] + flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5) + + location = self.potential_sead_targets[0][0] + self.potential_sead_targets.pop(0) + flight.points.append([location.position.x, location.position.y, 1000]) # TODO : Egress / Ingress points + + self.cas_flights.append(flight) + self.flights.append(flight) + + # Update inventory + for k, v in inventory.items(): + self.aircraft_inventory[k] = v + + def _get_cas_locations(self): + cas_locations = [] + for cp in self.from_cp.connected_points: + if cp.captured != self.from_cp.captured: + cas_locations.append(((self.from_cp.position.x + cp.position.x)/2, (self.from_cp.position.y + cp.position.y)/2)) + return cas_locations + + def compute_strike_targets(self): """ @return a list of potential strike targets in range """ # target, distance - potential_targets = [] + self.potential_strike_targets = [] for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]: @@ -157,18 +251,18 @@ class FlightPlanner: cp.position.y - self.from_cp.position.y) if distance < SEAD_MAX_RANGE: - potential_targets.append((g, distance)) + self.potential_strike_targets.append((g, distance)) added_group.append(g) - return potential_targets.sort(key=operator.itemgetter(1)) + self.potential_strike_targets.sort(key=operator.itemgetter(1)) - def _get_sead_targets_in_range(self): + def compute_sead_targets(self): """ @return a list of potential sead targets in range """ # target, distance - potential_targets = [] + self.potential_sead_targets = [] for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]: @@ -185,7 +279,8 @@ class FlightPlanner: if g.dcs_identifier == "AA": # Check that there is at least one unit with a radar in the ground objects unit groups - number_of_units = sum([len([r for r in group.units if hasattr(r, "detection_range") and r.detection_range > 1000]) for group in g.groups]) + number_of_units = sum([len([r for r in group.units if hasattr(db.unit_type_from_name(r.type), "detection_range") + and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups]) if number_of_units <= 0: continue @@ -194,9 +289,9 @@ class FlightPlanner: cp.position.y - self.from_cp.position.y) if distance < SEAD_MAX_RANGE: - potential_targets.append((g, distance)) + self.potential_sead_targets.append((g, distance)) - return potential_targets.sort(key=operator.itemgetter(1)) + self.potential_sead_targets.sort(key=operator.itemgetter(1)) def __repr__(self): return "-"*40 + "\n" + self.from_cp.name + " planned flights :\n"\ diff --git a/gen/flights/ai_flight_planner_db.py b/gen/flights/ai_flight_planner_db.py index f3754bd8..4ea54af4 100644 --- a/gen/flights/ai_flight_planner_db.py +++ b/gen/flights/ai_flight_planner_db.py @@ -69,6 +69,7 @@ CAS_CAPABLE = [ Su_24MR, Su_25, Su_25T, + Su_25TM, Su_34, M_2000C, diff --git a/gen/flights/flight.py b/gen/flights/flight.py index d35e8d92..f16bd68b 100644 --- a/gen/flights/flight.py +++ b/gen/flights/flight.py @@ -21,6 +21,10 @@ class FlightType(Enum): LOGISTICS = 12 EVAC = 13 + ELINT = 14 + RECON = 15 + EWAR = 16 + class Flight: