intercept mission

This commit is contained in:
Vasyl Horbachenko 2018-05-29 22:50:35 +03:00 committed by Vasyl Horbachenko
parent a61f487196
commit 781ddce09d
10 changed files with 256 additions and 96 deletions

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.6" jdkType="Python SDK" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.6 (venv)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">

2
.idea/misc.xml generated
View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (venv)" project-jdk-type="Python SDK" />
</project>

View File

@ -35,7 +35,22 @@ theater.senaki.base.aa = {
AirDefence.AAA_ZU_23_on_Ural_375: 2,
}
op = game.mission.CaptureOperation.playerless(m, theater.kutaisi, theater.senaki)
"""
op = game.mission.CaptureOperation(m, m.country("USA"), m.country("Russia"), theater.senaki, theater.batumi, {A_10C: 2}, {F_15C: 2}, {Armor.MBT_M1A2_Abrams: 4}, {Su_27: 4}, {Armor.MBT_T_55: 4}, {})
op.generate()
"""
op = game.mission.InterceptOperation(m,
m.country("USA"),
m.country("Russia"),
theater.batumi,
m.terrain.batumi(),
escort={Su_27: 2},
transport={An_26B: 2},
interceptors={M_2000C: 2})
op.generate()
if not os.path.exists("./build"):
os.mkdir("./build")
m.save("build/output.miz")

55
game/event.py Normal file
View File

@ -0,0 +1,55 @@
import typing
import dcs
from theater.controlpoint import *
from .mission import *
class Event:
silent = False
operation = None # type: Operation
def failure(self):
pass
def success(self):
pass
class InterceptEvent(Event):
pass
class CaptureEvent(Event):
silent = True
def __init__(self, from_cp: ControlPoint, to_cp: ControlPoint):
pass
def player_defending(self, from_cp: ControlPoint, to_cp: ControlPoint, interceptors: typing.Dict[PlaneType, int]):
assert not self.operation
cas = from_cp.base.scramble_cas(to_cp)
escort = from_cp.base.scramble_sweep(to_cp)
attackers = from_cp.base.assemble_cap(to_cp)
self.operation = CaptureOperation(from_cp=from_cp,
to_cp=to_cp,
cas=cas,
escort=escort,
attack=attackers,
intercept=interceptors,
defense=to_cp.base.armor,
aa=to_cp.base.aa)
def player_attacking(self, from_cp: ControlPoint, to_cp: ControlPoint, cas: typing.Dict[PlaneType, int], escort: typing.Dict[PlaneType, int], armor: typing.Dict[Armor, int]):
assert not self.operation
interceptors = to_cp.base.scramble_sweep()
self.operation = CaptureOperation(from_cp=from_cp,
to_cp=to_cp,
cas=cas,
escort=escort,
attack=armor,
intercept=interceptors,
defense=to_cp.base.armor,
aa=to_cp.base.aa)

View File

@ -2,9 +2,20 @@ import typing
from theater.conflicttheater import *
from theater.controlpoint import *
from .event import *
class Game:
events = [] # type: typing.List[Event]
def __init__(self, theater: ConflictTheater):
self.theater = theater
def _fill_cap_events(self):
for cp in [x for x in self.theater.controlpoints if x.captured]:
for connected_cp in [x for x in cp.connected_points if not x.captured]:
self.events.append(CaptureEvent(cp, connected_cp))
def pass_turn(self):
self.events = [] # type: typing.List[Event]
self._fill_cap_events()

View File

@ -23,6 +23,8 @@ class Operation:
class CaptureOperation(Operation):
def __init__(self,
mission: Mission,
attacker: Country,
defender: Country,
from_cp: ControlPoint,
to_cp: ControlPoint,
cas: typing.Dict[PlaneType, int],
@ -31,13 +33,7 @@ class CaptureOperation(Operation):
intercept: typing.Dict[PlaneType, int],
defense: typing.Dict[Armor, int],
aa: typing.Dict[AirDefence, int]):
conflict = None
if from_cp.captured:
assert not to_cp.captured
conflict = to_cp.conflict_attack(from_cp, US, THEM)
else:
assert not from_cp.captured
conflict = to_cp.conflict_attack(from_cp, THEM, US)
conflict = to_cp.conflict_attack(from_cp, attacker, defender)
super(CaptureOperation, self).__init__(mission, conflict)
self.from_cp = from_cp
@ -51,49 +47,37 @@ class CaptureOperation(Operation):
self.aa = aa
@classmethod
def player_defending(self, from_cp: ControlPoint, to_cp: ControlPoint, interceptors: typing.Dict[PlaneType, int]):
cas = from_cp.base.scramble_cas(to_cp)
escort = from_cp.base.scramble_sweep(to_cp)
attackers = from_cp.base.assemble_cap(to_cp)
return CaptureOperation(from_cp=from_cp,
to_cp=to_cp,
cas=cas,
escort=escort,
attack=attackers,
intercept=interceptors,
defense=to_cp.base.armor,
aa=to_cp.base.aa)
@classmethod
def player_attacking(self, from_cp: ControlPoint, to_cp: ControlPoint, cas: typing.Dict[PlaneType, int], escort: typing.Dict[PlaneType, int], armor: typing.Dict[Armor, int]):
interceptors = to_cp.base.scramble_sweep()
return CaptureOperation(from_cp=from_cp,
to_cp=to_cp,
cas=cas,
escort=escort,
attack=armor,
intercept=interceptors,
defense=to_cp.base.armor,
aa=to_cp.base.aa)
@classmethod
def playerless(self, mission: Mission, from_cp: ControlPoint, to_cp: ControlPoint):
return CaptureOperation(mission=mission,
from_cp=from_cp,
to_cp=to_cp,
cas=from_cp.base.scramble_cas(to_cp),
escort=from_cp.base.scramble_sweep(to_cp),
attack=from_cp.base.assemble_cap(to_cp),
intercept=to_cp.base.scramble_interceptors(0.5),
defense=to_cp.base.assemble_defense(0.5),
aa=to_cp.base.aa)
def generate(self):
self.armorgen.generate(self.attack, self.defense)
self.airgen.generate_cas(self.cas)
self.airgen.generate_escort(self.escort)
self.airgen.generate_interceptors(self.intercept)
self.airgen.generate_cas_escort(self.escort)
self.airgen.generate_defense(self.intercept)
self.aagen.generate(self.aa)
class InterceptOperation(Operation):
def __init__(self,
mission: Mission,
attacker: Country,
defender: Country,
destination: ControlPoint,
destination_port: Airport,
escort: typing.Dict[PlaneType, int],
transport: typing.Dict[PlaneType, int],
interceptors: typing.Dict[PlaneType, int]):
conflict = Conflict.intercept_conflict(
attacker=attacker,
defender=defender,
position=destination.position,
heading=0
)
super(InterceptOperation, self).__init__(mission, conflict)
self.destination_port = destination_port
self.escort = escort
self.transport = transport
self.interceptors = interceptors
def generate(self):
self.airgen.generate_transport(self.transport, self.destination_port)
self.airgen.generate_transport_escort(self.escort)
self.airgen.generate_interception(self.interceptors)

View File

@ -19,9 +19,12 @@ from dcs.task import *
SPREAD_DISTANCE_FACTOR = 1, 2
ESCORT_MAX_DIST = 30000
WORKAROUND_WAYP_DIST = 1000
WARM_START_ALTITUDE = 6000
WARM_START_AIRSPEED = 300
INTERCEPT_ALT = 15000
CAS_ALTITUDE = 3000
INTERCEPT_MAX_DISTANCE_FACTOR = 15
@ -49,6 +52,7 @@ class AircraftConflictGenerator:
at: Point = None,
airport: Airport = None) -> PlaneGroup:
starttype = airport == None and StartType.Warm or StartType.Cold
print("generating {} ({}) at {} {}".format(unit, count, at, airport, side))
return self.m.flight_group(
country=side,
name=name,
@ -61,52 +65,112 @@ class AircraftConflictGenerator:
start_type=starttype,
group_size=count)
def _generate_escort(self, units: typing.Dict[PlaneType, int], airport: Airport, side: Country, location: Point):
assert len(self.escort_targets) > 0
for type, count in units.items():
group = self._generate_group(
name=namegen.next_escort_group_name(),
side=side,
unit=type,
count=count,
at=location,
airport=airport)
group.task = Escort.name
group.load_task_default_loadout(dcs.task.Escort)
heading = group.position.heading_between_point(self.conflict.position)
position = group.position # type: Point
wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE)
for group in self.escort_targets:
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
def generate_cas(self, attackers: typing.Dict[PlaneType, int], airport: Airport = None):
assert len(self.escort_targets) == 0
for type, count in attackers.items():
group = self._generate_group(
name=namegen.next_cas_group_name(),
side=self.conflict.attackers_side,
unit=type,
count=count,
at=airport == None and self._group_point(self.conflict.air_attackers_location) or None,
at=airport is None and self._group_point(self.conflict.air_attackers_location) or None,
airport=airport)
self.escort_targets.append(group)
group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
group.task = CAS.name
group.load_task_default_loadout(CAS)
def generate_escort(self, attackers: typing.Dict[PlaneType, int], airport: Airport = None):
for type, count in attackers.items():
group = self._generate_group(
name=namegen.next_escort_group_name(),
side=self.conflict.attackers_side,
unit=type,
count=count,
at=airport == None and self._group_point(self.conflict.air_attackers_location) or None,
airport=airport)
def generate_cas_escort(self, attackers: typing.Dict[PlaneType, int], airport: Airport = None):
self._generate_escort(
units=attackers,
airport=airport,
side=self.conflict.attackers_side,
location=airport is None and self._group_point(self.conflict.air_attackers_location) or None
)
group.task = Escort.name
group.load_task_default_loadout(dcs.task.Escort.name)
def generate_transport_escort(self, escort: typing.Dict[PlaneType, int], airport: Airport = None):
self._generate_escort(
units=escort,
airport=airport,
side=self.conflict.defenders_side,
location=airport is None and self._group_point(self.conflict.air_defenders_location) or None
)
heading = group.position.heading_between_point(self.conflict.position)
position = group.position # type: Point
wayp = group.add_waypoint(position.point_from_heading(heading, 3000), CAS_ALTITUDE)
for group in self.escort_targets:
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
def generate_interceptors(self, defenders: typing.Dict[PlaneType, int], airport: Airport = None):
def generate_defense(self, defenders: typing.Dict[PlaneType, int], airport: Airport = None):
for type, count in defenders.items():
group = self._generate_group(
name=namegen.next_intercept_group_name(),
side=self.conflict.defenders_side,
unit=type,
count=count,
at=airport == None and self._group_point(self.conflict.air_defenders_location) or None,
at=airport is None and self._group_point(self.conflict.air_defenders_location) or None,
airport=airport)
group.task = FighterSweep.name
group.load_task_default_loadout(dcs.task.FighterSweep())
group.load_task_default_loadout(FighterSweep)
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
wayp.tasks.append(dcs.task.EngageTargets(max_distance=self.conflict.size * INTERCEPT_MAX_DISTANCE_FACTOR))
wayp.tasks.append(dcs.task.OrbitAction())
def generate_transport(self, transport: typing.Dict[PlaneType, int], destination: Airport):
assert len(self.escort_targets) == 0
for type, count in transport.items():
group = self._generate_group(
name=namegen.next_transport_group_name(),
side=self.conflict.defenders_side,
unit=type,
count=count,
at=self._group_point(self.conflict.air_defenders_location),
airport=None
)
group.task = Transport.name
self.escort_targets.append(group)
group.land_at(destination)
def generate_interception(self, interceptors: typing.Dict[PlaneType, int], airport: Airport = None):
for type, count in interceptors.items():
group = self._generate_group(
name=namegen.next_intercept_group_name(),
side=self.conflict.attackers_side,
unit=type,
count=count,
at=airport is None and self._group_point(self.conflict.air_attackers_location) or None,
airport=airport
)
group.task = FighterSweep.name
group.load_task_default_loadout(FighterSweep)
heading = group.position.heading_between_point(self.conflict.position)
initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPT_ALT)
initial_wayp.tasks.append(EngageTargets())
wayp = group.add_waypoint(self.conflict.position, 0)
wayp.tasks.append(EngageTargets())

View File

@ -2,6 +2,7 @@ import typing
import pdb
import dcs
from random import randint
from dcs import Mission
from dcs.mission import *
@ -17,18 +18,42 @@ def _opposite_heading(h):
return h+180
GROUND_DISTANCE_FACTOR = 2
AIR_DISTANCE_FACTOR = 5
AIR_DISTANCE = 8000
INTERCEPT_ATTACKERS_HEADING = -45, 45
INTERCEPT_DEFENDERS_HEADING = -10, 10
INTERCEPT_ATTACKERS_DISTANCE = 60000
INTERCEPT_DEFENDERS_DISTANCE = 30000
class Conflict:
def __init__(self, attacker: Country, attack_heading: int, defender: Country, defense_heading: int, position: Point, size: int):
self.attackers_side = attacker
self.defenders_side = defender
self.position = position
self.size = size
@classmethod
def capture_conflict(self, attacker: Country, attack_heading: int, defender: Country, defense_heading: int, position: Point, size: int):
instance = self()
instance.attackers_side = attacker
instance.defenders_side = defender
instance.position = position
instance.size = size
self.ground_attackers_location = self.position.point_from_heading(attack_heading, self.size * GROUND_DISTANCE_FACTOR)
self.ground_defenders_location = self.position.point_from_heading(defense_heading, self.size * GROUND_DISTANCE_FACTOR)
instance.ground_attackers_location = instance.position.point_from_heading(attack_heading, instance.size * GROUND_DISTANCE_FACTOR)
instance.ground_defenders_location = instance.position.point_from_heading(defense_heading, instance.size * GROUND_DISTANCE_FACTOR)
self.air_attackers_location = self.position.point_from_heading(attack_heading, self.size * AIR_DISTANCE_FACTOR)
self.air_defenders_location = self.position.point_from_heading(defense_heading, self.size * AIR_DISTANCE_FACTOR)
instance.air_attackers_location = instance.position.point_from_heading(attack_heading, AIR_DISTANCE)
instance.air_defenders_location = instance.position.point_from_heading(defense_heading, AIR_DISTANCE)
return instance
@classmethod
def intercept_conflict(self, attacker: Country, defender: Country, position: Point, heading: int):
from theater.conflicttheater import SIZE_REGULAR
instance = self()
instance.attackers_side = attacker
instance.defenders_side = defender
instance.position = position
instance.size = SIZE_REGULAR
instance.air_attackers_location = instance.position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, INTERCEPT_ATTACKERS_DISTANCE)
instance.air_defenders_location = instance.position.point_from_heading(random.randint(*INTERCEPT_DEFENDERS_HEADING) + heading, INTERCEPT_DEFENDERS_DISTANCE)
return instance

View File

@ -21,6 +21,10 @@ class NameGenerator:
self.number += 1
return "AA Unit {}".format(self.number)
def next_transport_group_name(self):
self.number += 1
return "Transport Unit {}".format(self.number)
namegen = NameGenerator()

View File

@ -5,7 +5,7 @@ import math
from dcs.mapping import *
from dcs.country import *
from gen.conflictgen import Conflict
from gen.conflictgen import *
class ControlPoint:
connected_points = [] # type: typing.List[ControlPoint]
@ -27,12 +27,12 @@ class ControlPoint:
def connect(self, to):
self.connected_points.append(to)
def find_radial(self, heading: int):
def find_radial(self, heading: int, ignored_radial: int = None):
closest_radial = 0
closest_radial_delta = 360
for radial in self.radials:
for radial in [x for x in self.radials if x != ignored_radial]:
delta = math.fabs(radial - heading)
if closest_radial_delta < delta:
if delta < closest_radial_delta:
closest_radial = radial
closest_radial_delta = delta
@ -42,13 +42,13 @@ class ControlPoint:
cp = from_cp # type: ControlPoint
attack_radial = self.find_radial(cp.position.heading_between_point(self.position))
defense_radial = self.find_radial(self.position.heading_between_point(cp.position))
defense_radial = self.find_radial(self.position.heading_between_point(cp.position), ignored_radial=attack_radial)
return Conflict(attacker=attacker,
attack_heading=attack_radial,
defender=defender,
defense_heading=defense_radial,
position=self.position,
size=self.size)
return Conflict.capture_conflict(attacker=attacker,
attack_heading=attack_radial,
defender=defender,
defense_heading=defense_radial,
position=self.position,
size=self.size)