This commit is contained in:
Vasiliy Horbachenko 2018-06-13 01:26:50 +03:00
commit 5f7724d44e
23 changed files with 392 additions and 385 deletions

View File

@ -1,90 +1,24 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import pickle
import dcs
import os
import gen
import theater.caucasus import theater.caucasus
import game.operation
import ui.window import ui.window
import ui.mainmenu import ui.mainmenu
from game.game import Game from game.game import Game
from theater import start_generator from theater import start_generator
from theater.controlpoint import * from userdata import persistency
from dcs.planes import * game = persistency.restore_game()
from dcs.vehicles import * if not game:
theater = theater.caucasus.CaucasusTheater()
start_generator.generate_initial(theater, "Russia")
m = dcs.Mission() game = Game(theater=theater)
theater = theater.caucasus.CaucasusTheater()
start_generator.generate_initial(theater, "Russia")
g = Game(theater=theater)
w = ui.window.Window() w = ui.window.Window()
m = ui.mainmenu.MainMenu(w, None, g) m = ui.mainmenu.MainMenu(w, None, game)
m.display() m.display()
w.run() w.run()
"""
selected_cp = None # type: ControlPoint
while True:
ptr = 0
print("Budget: {}m".format(g.budget))
if selected_cp is None:
print("Events:")
for event in g.events:
ptr += 1
print("{}. {} {}".format(ptr, event.attacker != g.side and "!" or " ", event))
print("Control Points:")
controlpoints = g.theater.controlpoints
controlpoints.sort(key=lambda x: x.captured)
for cp in g.theater.controlpoints:
ptr += 1
print("{}. [{}{}] {}{}{}{}".format(
ptr,
cp.captured and "x" or " ",
int(cp.base.readiness * 10),
cp.name,
"^" * cp.base.total_planes,
"." * cp.base.total_armor,
"*" * cp.base.total_aa))
events_boundary = len(g.events)
try:
selected_idx = int(input(">").strip()) - 1
except:
continue
if selected_idx == -1:
g.pass_turn()
continue
if selected_idx < events_boundary:
event = g.events[selected_idx]
else:
selected_cp = controlpoints[selected_idx - events_boundary]
else:
print("Units on the base: ")
for unit, count in selected_cp.base.all_units:
print("{} ({}) ".format(unit.name and unit.name or unit.id, count), end="")
print("")
try:
selected_idx = int(input(">").strip()) - 1
except:
continue
if selected_idx == -1:
selected_cp = None
if not os.path.exists("./build"):
os.mkdir("./build")
m.save("build/output.miz")
"""

View File

@ -1,7 +1,4 @@
import typing import typing
import dcs
import globals
from dcs.vehicles import * from dcs.vehicles import *
from dcs.unitgroup import * from dcs.unitgroup import *

View File

@ -1,11 +1,3 @@
import typing
import random
import math
import dcs
from theater.controlpoint import *
from userdata.debriefing_parser import *
from game.operation import * from game.operation import *
DIFFICULTY_LOG_BASE = 1.5 DIFFICULTY_LOG_BASE = 1.5
@ -18,12 +10,12 @@ class Event:
difficulty = 1 # type: int difficulty = 1 # type: int
BONUS_BASE = 0 BONUS_BASE = 0
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint): def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
self.mission = dcs.mission.Mission() self.attacker_name = attacker_name
self.attacker = self.mission.country(attacker_name) self.defender_name = defender_name
self.defender = self.mission.country(defender_name)
self.to_cp = to_cp self.to_cp = to_cp
self.from_cp = from_cp self.from_cp = from_cp
self.theater = theater
def bonus(self) -> int: def bonus(self) -> int:
return math.ceil(math.log(self.difficulty, DIFFICULTY_LOG_BASE) * self.BONUS_BASE) return math.ceil(math.log(self.difficulty, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
@ -31,10 +23,19 @@ class Event:
def is_successfull(self, debriefing: Debriefing) -> bool: def is_successfull(self, debriefing: Debriefing) -> bool:
return self.operation.is_successfull(debriefing) return self.operation.is_successfull(debriefing)
def generate(self):
self.operation.prepare(is_quick=False)
self.operation.generate()
self.operation.mission.save("build/nextturn.miz")
self.operation.prepare(is_quick=True)
self.operation.generate()
self.operation.mission.save('build/nextturn_quick.miz')
def commit(self, debriefing: Debriefing): def commit(self, debriefing: Debriefing):
for country, losses in debriefing.destroyed_units.items(): for country, losses in debriefing.destroyed_units.items():
cp = None # type: ControlPoint cp = None # type: ControlPoint
if country == self.attacker.name: if country == self.attacker_name:
cp = self.from_cp cp = self.from_cp
else: else:
cp = self.to_cp cp = self.to_cp
@ -84,27 +85,29 @@ class GroundInterceptEvent(Event):
pass pass
def player_attacking(self, position: Point, strikegroup: db.PlaneDict, clients: db.PlaneDict): def player_attacking(self, position: Point, strikegroup: db.PlaneDict, clients: db.PlaneDict):
suitable_unittypes = db.find_unittype(CAP, self.defender.name) suitable_unittypes = db.find_unittype(CAP, self.defender_name)
random.shuffle(suitable_unittypes) random.shuffle(suitable_unittypes)
unittypes = suitable_unittypes[:self.TARGET_VARIETY] unittypes = suitable_unittypes[:self.TARGET_VARIETY]
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1) typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
self.targets = {unittype: typecount for unittype in unittypes} self.targets = {unittype: typecount for unittype in unittypes}
self.operation = GroundInterceptOperation(mission=self.mission,
attacker=self.attacker, op = GroundInterceptOperation(attacker_name=self.attacker_name,
defender=self.defender, defender_name=self.defender_name,
attacker_clients=clients, attacker_clients=clients,
defender_clients={}, defender_clients={},
from_cp=self.from_cp, from_cp=self.from_cp)
position=position, op.setup(position=position,
target=self.targets, target=self.targets,
strikegroup=strikegroup) strikegroup=strikegroup)
self.operation = op
class InterceptEvent(Event): class InterceptEvent(Event):
ESCORT_AMOUNT_FACTOR = 2 ESCORT_AMOUNT_FACTOR = 2
BONUS_BASE = 5 BONUS_BASE = 5
STRENGTH_INFLUENCE = 0.25 STRENGTH_INFLUENCE = 0.25
GLOBAL_STRENGTH_INFLUENCE = 0.05
AIRDEFENSE_COUNT = 3 AIRDEFENSE_COUNT = 3
transport_unit = None # type: FlyingType transport_unit = None # type: FlyingType
@ -123,7 +126,11 @@ class InterceptEvent(Event):
super(InterceptEvent, self).commit(debriefing) super(InterceptEvent, self).commit(debriefing)
if self.is_successfull(debriefing): if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(self.STRENGTH_INFLUENCE * float(self.from_cp.captured and -1 or 1)) if self.from_cp.is_global:
for cp in self.theater.enemy_points():
cp.base.affect_strength(-self.GLOBAL_STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(self.STRENGTH_INFLUENCE * float(self.from_cp.captured and -1 or 1))
else: else:
self.to_cp.base.affect_strength(self.STRENGTH_INFLUENCE * float(self.from_cp.captured and 1 or -1)) self.to_cp.base.affect_strength(self.STRENGTH_INFLUENCE * float(self.from_cp.captured and 1 or -1))
@ -133,39 +140,43 @@ class InterceptEvent(Event):
def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict): def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
escort = self.to_cp.base.scramble_sweep(self.to_cp) escort = self.to_cp.base.scramble_sweep(self.to_cp)
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender.name)) self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
assert self.transport_unit is not None assert self.transport_unit is not None
airdefense_unit = db.find_unittype(AirDefence, self.defender.name)[0] airdefense_unit = db.find_unittype(AirDefence, self.defender_name)[0]
self.operation = InterceptOperation(mission=self.mission, op = InterceptOperation(attacker_name=self.attacker_name,
attacker=self.attacker, defender_name=self.defender_name,
defender=self.defender, attacker_clients=clients,
attacker_clients=clients, defender_clients={},
defender_clients={}, from_cp=self.from_cp,
from_cp=self.from_cp, to_cp=self.to_cp)
to_cp=self.to_cp,
escort=escort, op.setup(escort=escort,
transport={self.transport_unit: 1}, transport={self.transport_unit: 1},
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT}, airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
interceptors=interceptors) interceptors=interceptors)
self.operation = op
def player_defending(self, escort: db.PlaneDict, clients: db.PlaneDict): def player_defending(self, escort: db.PlaneDict, clients: db.PlaneDict):
interceptors = self.from_cp.base.scramble_interceptors_count(self.difficulty * self.ESCORT_AMOUNT_FACTOR) interceptors = self.from_cp.base.scramble_interceptors_count(self.difficulty * self.ESCORT_AMOUNT_FACTOR)
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender.name)) self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
assert self.transport_unit is not None assert self.transport_unit is not None
self.operation = InterceptOperation(mission=self.mission, op = InterceptOperation(attacker_name=self.attacker_name,
attacker=self.attacker, defender_name=self.defender_name,
defender=self.defender, attacker_clients={},
attacker_clients={}, defender_clients=clients,
defender_clients=clients, from_cp=self.from_cp,
from_cp=self.from_cp, to_cp=self.to_cp)
to_cp=self.to_cp,
escort=escort, op.setup(escort=escort,
transport={self.transport_unit: 1}, transport={self.transport_unit: 1},
interceptors=interceptors, interceptors=interceptors,
airdefense={}) airdefense={})
self.operation = op
class CaptureEvent(Event): class CaptureEvent(Event):
@ -188,6 +199,9 @@ class CaptureEvent(Event):
if self.is_successfull(debriefing): if self.is_successfull(debriefing):
if self.from_cp.captured: if self.from_cp.captured:
self.to_cp.captured = True self.to_cp.captured = True
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
else: else:
if not self.from_cp.captured: if not self.from_cp.captured:
self.to_cp.captured = False self.to_cp.captured = False
@ -202,47 +216,52 @@ class CaptureEvent(Event):
escort = self.from_cp.base.scramble_sweep(self.to_cp) escort = self.from_cp.base.scramble_sweep(self.to_cp)
attackers = self.from_cp.base.assemble_cap(self.to_cp) attackers = self.from_cp.base.assemble_cap(self.to_cp)
self.operation = CaptureOperation(mission=self.mission, op = CaptureOperation(attacker_name=self.attacker_name,
attacker=self.attacker, defender_name=self.defender_name,
defender=self.defender, attacker_clients={},
attacker_clients={}, defender_clients=clients,
defender_clients=clients, from_cp=self.from_cp,
from_cp=self.from_cp, to_cp=self.to_cp)
to_cp=self.to_cp,
cas=cas, op.setup(cas=cas,
escort=escort, escort=escort,
attack=attackers, attack=attackers,
intercept=interceptors, intercept=interceptors,
defense=self.to_cp.base.armor, defense=self.to_cp.base.armor,
aa=self.to_cp.base.aa) aa=self.to_cp.base.aa)
self.operation = op
def player_attacking(self, cas: db.PlaneDict, escort: db.PlaneDict, armor: db.ArmorDict, clients: db.PlaneDict): def player_attacking(self, cas: db.PlaneDict, escort: db.PlaneDict, armor: db.ArmorDict, clients: db.PlaneDict):
interceptors = self.to_cp.base.scramble_sweep(for_target=self.to_cp) interceptors = self.to_cp.base.scramble_sweep(for_target=self.to_cp)
self.operation = CaptureOperation(mission=self.mission, op = CaptureOperation(attacker_name=self.attacker_name,
attacker=self.attacker, defender_name=self.defender_name,
defender=self.defender, attacker_clients=clients,
attacker_clients=clients, defender_clients={},
defender_clients={}, from_cp=self.from_cp,
from_cp=self.from_cp, to_cp=self.to_cp)
to_cp=self.to_cp,
cas=cas, op.setup(cas=cas,
escort=escort, escort=escort,
attack=armor, attack=armor,
intercept=interceptors, intercept=interceptors,
defense=self.to_cp.base.armor, defense=self.to_cp.base.armor,
aa=self.to_cp.base.aa) aa=self.to_cp.base.aa)
self.operation = op
class UnitsDeliveryEvent(Event): class UnitsDeliveryEvent(Event):
informational = True informational = True
units = None # type: typing.Dict[UnitType, int] units = None # type: typing.Dict[UnitType, int]
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint): def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
super(UnitsDeliveryEvent, self).__init__(attacker_name=attacker_name, super(UnitsDeliveryEvent, self).__init__(attacker_name=attacker_name,
defender_name=defender_name, defender_name=defender_name,
from_cp=from_cp, from_cp=from_cp,
to_cp=to_cp) to_cp=to_cp,
theater=theater)
self.units = {} self.units = {}

View File

@ -1,9 +1,3 @@
import typing
import random
from theater.conflicttheater import *
from theater.controlpoint import *
from userdata.debriefing_parser import *
from game.event import * from game.event import *
COMMISION_LIMITS_SCALE = 2 COMMISION_LIMITS_SCALE = 2
@ -24,12 +18,16 @@ COMMISION_AMOUNTS_FACTORS = {
ENEMY_INTERCEPT_PROBABILITY_BASE = 10 ENEMY_INTERCEPT_PROBABILITY_BASE = 10
ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE = 1
ENEMY_CAPTURE_PROBABILITY_BASE = 3 ENEMY_CAPTURE_PROBABILITY_BASE = 3
PLAYER_INTERCEPT_PROBABILITY_BASE = 30 PLAYER_INTERCEPT_PROBABILITY_BASE = 30
PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30 PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30
PLAYER_GLOBALINTERCEPT_PROBABILITY_BASE = 100 PLAYER_GLOBALINTERCEPT_PROBABILITY_BASE = 100
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 50
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
PLAYER_BUDGET_BASE = 25 PLAYER_BUDGET_BASE = 25
PLAYER_BUDGET_IMPORTANCE_LOG = 2 PLAYER_BUDGET_IMPORTANCE_LOG = 2
@ -54,7 +52,8 @@ class Game:
self.events.append(CaptureEvent(attacker_name=self.player, self.events.append(CaptureEvent(attacker_name=self.player,
defender_name=self.enemy, defender_name=self.enemy,
from_cp=from_cp, from_cp=from_cp,
to_cp=to_cp)) to_cp=to_cp,
theater=self.theater))
def _generate_enemy_caps(self): def _generate_enemy_caps(self):
for from_cp, to_cp in self.theater.conflicts(False): for from_cp, to_cp in self.theater.conflicts(False):
@ -65,10 +64,12 @@ class Game:
self.events.append(CaptureEvent(attacker_name=self.enemy, self.events.append(CaptureEvent(attacker_name=self.enemy,
defender_name=self.player, defender_name=self.player,
from_cp=from_cp, from_cp=from_cp,
to_cp=to_cp)) to_cp=to_cp,
theater=self.theater))
break break
def _generate_interceptions(self): def _generate_interceptions(self):
enemy_interception = False
for from_cp, to_cp in self.theater.conflicts(False): for from_cp, to_cp in self.theater.conflicts(False):
if from_cp.base.total_units(FighterSweep) == 0: if from_cp.base.total_units(FighterSweep) == 0:
continue continue
@ -77,15 +78,36 @@ class Game:
self.events.append(InterceptEvent(attacker_name=self.enemy, self.events.append(InterceptEvent(attacker_name=self.enemy,
defender_name=self.player, defender_name=self.player,
from_cp=from_cp, from_cp=from_cp,
to_cp=to_cp)) to_cp=to_cp,
theater=self.theater))
enemy_interception = True
break break
for to_cp in self.theater.player_points():
if enemy_interception:
break
if to_cp in self.theater.conflicts(False):
continue
if self._roll(ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE, 1):
for from_cp, _ in self.theater.conflicts(False):
if from_cp.base.total_units(FighterSweep) > 0:
self.events.append(InterceptEvent(attacker_name=self.enemy,
defender_name=self.player,
from_cp=from_cp,
to_cp=to_cp,
theater=self.theater))
enemy_interception = True
break
for from_cp, to_cp in self.theater.conflicts(True): for from_cp, to_cp in self.theater.conflicts(True):
if self._roll(PLAYER_INTERCEPT_PROBABILITY_BASE, from_cp.base.strength): if self._roll(PLAYER_INTERCEPT_PROBABILITY_BASE, from_cp.base.strength):
self.events.append(InterceptEvent(attacker_name=self.player, self.events.append(InterceptEvent(attacker_name=self.player,
defender_name=self.enemy, defender_name=self.enemy,
from_cp=from_cp, from_cp=from_cp,
to_cp=to_cp)) to_cp=to_cp,
theater=self.theater))
break break
def _generate_groundinterceptions(self): def _generate_groundinterceptions(self):
@ -94,7 +116,20 @@ class Game:
self.events.append(GroundInterceptEvent(attacker_name=self.player, self.events.append(GroundInterceptEvent(attacker_name=self.player,
defender_name=self.enemy, defender_name=self.enemy,
from_cp=from_cp, from_cp=from_cp,
to_cp=to_cp)) to_cp=to_cp,
theater=self.theater))
break
def _generate_globalinterceptions(self):
for from_cp in [x for x in self.theater.player_points() if x.is_global]:
probability = PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE * math.log(len(self.theater.player_points()) + 1, PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG)
if self._roll(probability, from_cp.base.strength):
to_cp = random.choice([x for x in self.theater.enemy_points() if x not in self.theater.conflicts()])
self.events.append(InterceptEvent(attacker_name=self.player,
defender_name=self.enemy,
from_cp=from_cp,
to_cp=to_cp,
theater=self.theater))
break break
def _generate_global(self): def _generate_global(self):
@ -134,7 +169,8 @@ class Game:
event = UnitsDeliveryEvent(attacker_name=self.player, event = UnitsDeliveryEvent(attacker_name=self.player,
defender_name=self.player, defender_name=self.player,
from_cp=to_cp, from_cp=to_cp,
to_cp=to_cp) to_cp=to_cp,
theater=self.theater)
self.events.append(event) self.events.append(event)
return event return event
@ -143,8 +179,8 @@ class Game:
self.events.remove(event) self.events.remove(event)
def initiate_event(self, event: Event): def initiate_event(self, event: Event):
event.operation.generate() assert event in self.events
event.mission.save("build/next_mission.miz") event.generate()
def finish_event(self, event: Event, debriefing: Debriefing): def finish_event(self, event: Event, debriefing: Debriefing):
event.commit(debriefing) event.commit(debriefing)
@ -154,7 +190,7 @@ class Game:
self.events.remove(event) self.events.remove(event)
def is_player_attack(self, event: Event): def is_player_attack(self, event: Event):
return event.attacker.name == self.player return event.attacker_name == self.player
def pass_turn(self, no_action=False): def pass_turn(self, no_action=False):
for event in self.events: for event in self.events:
@ -162,13 +198,14 @@ class Game:
if not no_action: if not no_action:
self._budget_player() self._budget_player()
for cp in self.theater.enemy_bases(): for cp in self.theater.enemy_points():
self._commision_units(cp) self._commision_units(cp)
self.events = [] # type: typing.List[Event] self.events = [] # type: typing.List[Event]
self._fill_cap_events() self._fill_cap_events()
self._generate_enemy_caps() self._generate_enemy_caps()
self._generate_interceptions() self._generate_interceptions()
self._generate_globalinterceptions()
self._generate_groundinterceptions() self._generate_groundinterceptions()
self._generate_global() self._generate_global()

View File

@ -1,14 +1,6 @@
import typing from userdata.debriefing import *
from globals import *
from userdata.debriefing_parser import *
from dcs.mission import *
from dcs.unitgroup import *
from dcs.vehicles import *
from theater.controlpoint import *
from theater.conflicttheater import * from theater.conflicttheater import *
from theater.base import * from theater.base import *
from shop import *
from gen.armor import * from gen.armor import *
from gen.aircraft import * from gen.aircraft import *
@ -18,13 +10,42 @@ from gen.conflictgen import *
class Operation: class Operation:
def __init__(self, mission: Mission, conflict: Conflict): starting_position = None # type: db.StartingPosition
mission = None # type: dcs.Mission
conflict = None # type: Conflict
armorgen = None # type: ArmorConflictGenerator
airgen = None # type: AircraftConflictGenerator
aagen = None # type: AAConflictGenerator
shipgen = None # type: ShipGenerator
def __init__(self,
attacker_name: str,
defender_name: str,
attacker_clients: db.PlaneDict,
defender_clients: db.PlaneDict,
from_cp: ControlPoint,
to_cp: ControlPoint = None):
self.attacker_name = attacker_name
self.defender_name = defender_name
self.attacker_clients = attacker_clients
self.defender_clients = defender_clients
self.from_cp = from_cp
self.to_cp = to_cp
def initialize(self, mission: Mission, conflict: Conflict):
self.mission = mission self.mission = mission
self.conflict = conflict self.conflict = conflict
self.armorgen = ArmorConflictGenerator(self.mission, self.conflict)
self.airgen = AircraftConflictGenerator(self.mission, self.conflict) self.armorgen = ArmorConflictGenerator(mission, conflict)
self.aagen = AAConflictGenerator(self.mission, self.conflict) self.airgen = AircraftConflictGenerator(mission, conflict)
self.shipgen = ShipGenerator(self.mission, self.conflict) self.aagen = AAConflictGenerator(mission, conflict)
self.shipgen = ShipGenerator(mission, conflict)
def prepare(self, is_quick: bool):
self.starting_position = is_quick and self.from_cp.at or None
def generate(self):
pass
def units_of(self, country_name: str) -> typing.Collection[UnitType]: def units_of(self, country_name: str) -> typing.Collection[UnitType]:
return [] return []
@ -32,85 +53,81 @@ class Operation:
def is_successfull(self, debriefing: Debriefing) -> bool: def is_successfull(self, debriefing: Debriefing) -> bool:
return True return True
def generate(self):
pass
class CaptureOperation(Operation): class CaptureOperation(Operation):
def __init__(self, cas = None # type: db.PlaneDict
mission: Mission, escort = None # type: db.PlaneDict
attacker: Country, intercept = None # type: db.PlaneDict
defender: Country, attack = None # type: db.ArmorDict
attacker_clients: db.PlaneDict, defense = None # type: db.ArmorDict
defender_clients: db.PlaneDict, aa = None # type: db.AirDefenseDict
from_cp: ControlPoint,
to_cp: ControlPoint,
cas: db.PlaneDict,
escort: db.PlaneDict,
attack: db.ArmorDict,
intercept: db.PlaneDict,
defense: db.ArmorDict,
aa: db.AirDefenseDict):
conflict = to_cp.conflict_attack(from_cp, attacker, defender)
super(CaptureOperation, self).__init__(mission, conflict) def setup(self,
self.from_cp = from_cp cas: db.PlaneDict,
self.to_cp = to_cp escort: db.PlaneDict,
self.attacker_clients = attacker_clients attack: db.ArmorDict,
self.defender_clients = defender_clients intercept: db.PlaneDict,
self.cas = cas defense: db.ArmorDict,
self.escort = escort aa: db.AirDefenseDict):
self.intercept = intercept self.cas = cas
self.escort = escort
self.intercept = intercept
self.attack = attack
self.defense = defense
self.aa = aa
self.attack = attack def prepare(self, is_quick: bool):
self.defense = defense super(CaptureOperation, self).prepare(is_quick)
mission = dcs.Mission()
self.aa = aa self.initialize(mission=mission,
conflict=self.to_cp.conflict_attack(self.from_cp,
mission.country(self.attacker_name),
mission.country(self.defender_name)))
def generate(self): def generate(self):
self.armorgen.generate(self.attack, self.defense) self.armorgen.generate(self.attack, self.defense)
self.aagen.generate(self.aa) self.aagen.generate(self.aa)
self.airgen.generate_defense(self.intercept, clients=self.defender_clients) self.airgen.generate_defense(self.intercept, clients=self.defender_clients)
self.airgen.generate_cas(self.cas, clients=self.attacker_clients, at=self.from_cp.at) self.airgen.generate_cas(self.cas, clients=self.attacker_clients, at=self.starting_position)
self.airgen.generate_cas_escort(self.escort, clients=self.attacker_clients, at=self.from_cp.at) self.airgen.generate_cas_escort(self.escort, clients=self.attacker_clients, at=self.starting_position)
class InterceptOperation(Operation): class InterceptOperation(Operation):
def __init__(self, escort = None # type: db.PlaneDict
mission: Mission, transport = None # type: db.PlaneDict
attacker: Country, interceptors = None # type: db.PlaneDict
defender: Country, airdefense = None # type: db.AirDefenseDict
attacker_clients: db.PlaneDict,
defender_clients: db.PlaneDict,
from_cp: ControlPoint,
to_cp: ControlPoint,
escort: db.PlaneDict,
transport: db.PlaneDict,
airdefense: db.AirDefenseDict,
interceptors: db.PlaneDict):
heading = from_cp.position.heading_between_point(to_cp.position)
distance = from_cp.position.distance_to_point(to_cp.position)
position = from_cp.position.point_from_heading(heading, distance/2)
conflict = Conflict.intercept_conflict(
attacker=attacker,
defender=defender,
position=position,
heading=heading,
radials=ALL_RADIALS
)
super(InterceptOperation, self).__init__(mission, conflict) def setup(self,
self.to_cp = to_cp escort: db.PlaneDict,
self.from_cp = from_cp transport: db.PlaneDict,
self.attacker_clients = attacker_clients airdefense: db.AirDefenseDict,
self.defender_clients = defender_clients interceptors: db.PlaneDict):
self.escort = escort self.escort = escort
self.transport = transport self.transport = transport
self.airdefense = airdefense self.airdefense = airdefense
self.interceptors = interceptors self.interceptors = interceptors
def prepare(self, is_quick: bool):
super(InterceptOperation, self).prepare(is_quick)
mission = dcs.Mission()
heading = self.from_cp.position.heading_between_point(self.to_cp.position)
distance = self.from_cp.position.distance_to_point(self.to_cp.position)
position = self.from_cp.position.point_from_heading(heading, distance/2)
conflict = Conflict.intercept_conflict(
attacker=mission.country(self.attacker_name),
defender=mission.country(self.defender_name),
position=position,
heading=randint(0, 360),
radials=ALL_RADIALS
)
self.initialize(mission=mission,
conflict=conflict)
def generate(self): def generate(self):
self.airgen.generate_transport(self.transport, self.to_cp.at) self.airgen.generate_transport(self.transport, self.to_cp.at)
self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients) self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients)
@ -119,35 +136,31 @@ class InterceptOperation(Operation):
if self.from_cp.is_global: if self.from_cp.is_global:
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.shipgen.generate(self.from_cp.at)) self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.shipgen.generate(self.from_cp.at))
else: else:
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.from_cp.at) self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.starting_position)
class GroundInterceptOperation(Operation): class GroundInterceptOperation(Operation):
def __init__(self, def setup(self,
mission: Mission, position: Point,
attacker: Country, target: db.ArmorDict,
defender: Country, strikegroup: db.PlaneDict):
from_cp: ControlPoint, self.position = position
attacker_clients: db.PlaneDict, self.strikegroup = strikegroup
defender_clients: db.PlaneDict, self.target = target
position: Point,
target: db.ArmorDict, def prepare(self, is_quick: bool):
strikegroup: db.PlaneDict): super(GroundInterceptOperation, self).prepare(is_quick)
mission = dcs.Mission()
conflict = Conflict.ground_intercept_conflict( conflict = Conflict.ground_intercept_conflict(
attacker=attacker, attacker=mission.country(self.defender_name),
defender=defender, defender=mission.country(self.defender_name),
position=position, position=self.position,
heading=randint(0, 360), heading=randint(0, 360),
radials=ALL_RADIALS radials=ALL_RADIALS
) )
super(GroundInterceptOperation, self).__init__(mission, conflict) super(GroundInterceptOperation, self).__init__(mission, conflict)
self.attacker_clients = attacker_clients
self.defender_clients = defender_clients
self.from_cp = from_cp
self.strikegroup = strikegroup
self.target = target
def generate(self): def generate(self):
self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.from_cp.at) self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.starting_position)
self.armorgen.generate({}, self.target) self.armorgen.generate({}, self.target)

View File

@ -1,21 +1,9 @@
import typing from game import db
import pdb
import dcs
from random import randint
import globals
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
from dcs.mission import * from dcs.mission import *
from dcs.vehicles import *
from dcs.unitgroup import *
from dcs.unittype import *
from dcs.mapping import *
from dcs.point import *
from dcs.task import *
DISTANCE_FACTOR = 4, 5 DISTANCE_FACTOR = 4, 5
@ -24,7 +12,7 @@ class AAConflictGenerator:
self.m = mission self.m = mission
self.conflict = conflict self.conflict = conflict
def generate(self, units: typing.Dict[UnitType, int]): def generate(self, units: db.AirDefenseDict):
for type, count in units.items(): for type, count in units.items():
for _, radial in zip(range(count), self.conflict.radials): for _, radial in zip(range(count), self.conflict.radials):
distance = randint(self.conflict.size * DISTANCE_FACTOR[0], self.conflict.size * DISTANCE_FACTOR[1]) distance = randint(self.conflict.size * DISTANCE_FACTOR[0], self.conflict.size * DISTANCE_FACTOR[1])

View File

@ -1,21 +1,10 @@
import typing from game import db
import pdb
import dcs
from random import randint
import globals
from shop import db
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
from dcs.mission import * from dcs.mission import *
from dcs.vehicles import *
from dcs.unitgroup import * from dcs.unitgroup import *
from dcs.unittype import * from dcs.unittype import *
from dcs.mapping import *
from dcs.point import *
from dcs.task import * from dcs.task import *
SPREAD_DISTANCE_FACTOR = 1, 2 SPREAD_DISTANCE_FACTOR = 1, 2

View File

@ -1,20 +1,9 @@
import typing from game import db
import pdb
import dcs
from random import randint
import globals
from shop import db
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
from dcs.mission import * from dcs.mission import *
from dcs.vehicles import *
from dcs.unitgroup import *
from dcs.unittype import * from dcs.unittype import *
from dcs.mapping import *
from dcs.point import * from dcs.point import *
from dcs.task import * from dcs.task import *
from dcs.country import * from dcs.country import *

View File

@ -1,20 +1,8 @@
import typing
import pdb
import dcs
from random import randint
import globals
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
from dcs.mission import * from dcs.mission import *
from dcs.vehicles import *
from dcs.unitgroup import * from dcs.unitgroup import *
from dcs.unittype import *
from dcs.mapping import *
from dcs.point import *
from dcs.task import * from dcs.task import *

View File

@ -1,5 +0,0 @@
import dcs
MISSION = dcs.mission.Mission()
US = MISSION.country("USA")
THEM = MISSION.country("Russia")

View File

View File

@ -1,11 +1,8 @@
import typing import typing
import math import math
import random
import itertools import itertools
import dcs from game import db
from shop import db
from theater.controlpoint import ControlPoint from theater.controlpoint import ControlPoint
from dcs.planes import * from dcs.planes import *
@ -105,6 +102,10 @@ class Base:
return 0 return 0
def filter_units(self, applicable_units: typing.Collection):
self.aircraft = {k: v for k, v in self.aircraft.items() if k in applicable_units}
self.armor = {k: v for k, v in self.aircraft.items() if k in applicable_units}
def commision_units(self, units: typing.Dict[typing.Any, int]): def commision_units(self, units: typing.Dict[typing.Any, int]):
for value in units.values(): for value in units.values():
assert value > 0 assert value > 0

View File

@ -40,5 +40,5 @@ class ConflictTheater:
for connected_point in [x for x in cp.connected_points if x.captured != from_player]: for connected_point in [x for x in cp.connected_points if x.captured != from_player]:
yield (cp, connected_point) yield (cp, connected_point)
def enemy_bases(self) -> typing.Collection[ControlPoint]: def enemy_points(self) -> typing.Collection[ControlPoint]:
return [point for point in self.controlpoints if not point.captured] return [point for point in self.controlpoints if not point.captured]

View File

@ -1,10 +1,3 @@
import typing
import random
import dcs
from shop import db
from theater.controlpoint import *
from theater.base import * from theater.base import *
from theater.conflicttheater import * from theater.conflicttheater import *
@ -13,7 +6,7 @@ UNIT_AMOUNT_FACTOR = 1
def generate_initial(theater: ConflictTheater, enemy: str): def generate_initial(theater: ConflictTheater, enemy: str):
for cp in theater.enemy_bases(): for cp in theater.enemy_points():
if cp.captured: if cp.captured:
continue continue

View File

@ -1,7 +1,3 @@
from shop import db
from tkinter import *
from ui.window import *
from ui.eventmenu import * from ui.eventmenu import *
from game.game import * from game.game import *

View File

@ -1,10 +1,7 @@
from tkinter import *
from ui.window import *
from ui.eventresultsmenu import * from ui.eventresultsmenu import *
from shop import db
from game.game import * from game.game import *
from game import event from game import event, db
class EventMenu(Menu): class EventMenu(Menu):
@ -55,7 +52,7 @@ class EventMenu(Menu):
row += 1 row += 1
base = None # type: Base base = None # type: Base
if self.event.attacker.name == self.game.player: if self.event.attacker_name == self.game.player:
base = self.event.from_cp.base base = self.event.from_cp.base
else: else:
base = self.event.to_cp.base base = self.event.to_cp.base

View File

@ -1,13 +1,7 @@
import math
import itertools
from tkinter import *
from tkinter.ttk import * from tkinter.ttk import *
from ui.window import * from ui.window import *
from userdata.debriefing_parser import *
from game.game import * from game.game import *
from game import event
class EventResultsMenu(Menu): class EventResultsMenu(Menu):
@ -21,18 +15,22 @@ class EventResultsMenu(Menu):
self.event = event self.event = event
self.finished = False self.finished = False
wait_for_debriefing(callback=self.process_debriefing)
def display(self): def display(self):
self.window.clear_right_pane() self.window.clear_right_pane()
if not self.finished: if not self.finished:
Button(self.frame, text="no losses, succ", command=self.simulate_result(0, 1, True)).grid() Button(self.frame, text="no losses, succ", command=self.simulate_result(0, 1, True)).grid()
Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1, False)).grid(column=1) Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1, False)).grid(row=1, column=1)
Button(self.frame, text="half losses, succ", command=self.simulate_result(0.5, 0.5, True)).grid(row=1, ) Button(self.frame, text="half losses, succ", command=self.simulate_result(0.5, 0.5, True)).grid(row=2, )
Button(self.frame, text="half losses, fail", command=self.simulate_result(0.5, 0.5, False)).grid(row=1, column=1) Button(self.frame, text="half losses, fail", command=self.simulate_result(0.5, 0.5, False)).grid(row=2, column=1)
Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0, True)).grid(row=2, ) Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0, True)).grid(row=3, )
Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0, False)).grid(row=2, column=1) Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0, False)).grid(row=3, column=1)
Label(self.frame, text="Play the mission and save debriefing to {}".format(debriefing_directory_location())).grid(row=0, column=0)
else: else:
row = 0 row = 0
if self.event.is_successfull(self.debriefing): if self.event.is_successfull(self.debriefing):
@ -56,6 +54,15 @@ class EventResultsMenu(Menu):
Button(self.frame, text="Okay", command=self.dismiss).grid(columnspan=1, row=row); row += 1 Button(self.frame, text="Okay", command=self.dismiss).grid(columnspan=1, row=row); row += 1
def process_debriefing(self, debriefing: Debriefing):
self.game.finish_event(event=self.event, debriefing=debriefing)
self.game.pass_turn()
self.finished = True
self.player_losses = debriefing.destroyed_units.get(self.game.player, {})
self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy, {})
self.display()
def simulate_result(self, player_factor: float, enemy_factor: float, result: bool): def simulate_result(self, player_factor: float, enemy_factor: float, result: bool):
def action(): def action():
debriefing = Debriefing() debriefing = Debriefing()

View File

@ -1,12 +1,10 @@
from tkinter import * import pickle
from tkinter.ttk import *
from ui.window import *
from ui.eventmenu import *
from ui.basemenu import * from ui.basemenu import *
from ui.overviewcanvas import * from ui.overviewcanvas import *
from game.game import * from game.game import *
from userdata import persistency
class MainMenu(Menu): class MainMenu(Menu):
@ -22,9 +20,10 @@ class MainMenu(Menu):
self.frame.grid_columnconfigure(0, weight=1) self.frame.grid_columnconfigure(0, weight=1)
def display(self): def display(self):
persistency.save_game(self.game)
self.window.clear_right_pane() self.window.clear_right_pane()
self.upd.update() self.upd.update()
row = 1 row = 1
def label(text): def label(text):

View File

@ -79,7 +79,9 @@ class OverviewCanvas:
extent=extent) extent=extent)
self.canvas.tag_bind(cp_id, "<Button-1>", self.display(cp)) self.canvas.tag_bind(cp_id, "<Button-1>", self.display(cp))
self.create_cp_title((coords[0] + arc_size/2, coords[1] + arc_size/2), cp) self.create_cp_title((coords[0] + arc_size/2, coords[1] + arc_size/2), cp)
self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text="8/4/2", font=("Helvetica", 10))
units_title = "{}/{}/{}".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa)
self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text=units_title, font=("Helvetica", 10))
def display(self, cp: ControlPoint): def display(self, cp: ControlPoint):
def action(_): def action(_):

View File

@ -1,15 +0,0 @@
import dcs
money = 2000
aircraft = []
armor = []
control_points = []
def add_aircraft(plane: dcs.planes.PlaneType):
aircraft.append(plane)
def add_armor(vehicle: dcs.vehicles.Armor):
armor.append(vehicle)
def add_control_point(cp):
control_points.append(cp)

52
userdata/debriefing.py Normal file
View File

@ -0,0 +1,52 @@
import typing
import json
import threading
import time
import os
from datetime import datetime
DEBRIEFING_LOG_EXTENSION = "log"
class Debriefing:
def __init__(self):
self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[str, int]]
@classmethod
def parse(cls, path: str):
with open(path, "r") as f:
events = json.load(f)
return Debriefing()
def debriefing_directory_location() -> str:
return "build/debriefing"
def _logfiles_snapshot() -> typing.Dict[str, float]:
result = {}
for file in os.listdir(debriefing_directory_location()):
fullpath = os.path.join(debriefing_directory_location(), file)
result[file] = os.path.getmtime(fullpath)
return result
def _poll_new_debriefing_log(snapshot: typing.Dict[str, float], callback: typing.Callable):
should_run = True
while should_run:
for file, timestamp in _logfiles_snapshot().items():
if file not in snapshot or timestamp != snapshot[file]:
callback(Debriefing.parse(os.path.join(debriefing_directory_location(), file)))
should_run = False
break
time.sleep(1)
def wait_for_debriefing(callback: typing.Callable):
threading.Thread(target=_poll_new_debriefing_log, args=(_logfiles_snapshot(), callback)).start()

View File

@ -1,12 +0,0 @@
import typing
import json
class Debriefing:
def __init__(self):
self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[str, int]]
def parse(self, path: str):
with open(path, "r") as f:
events = json.load(f)

38
userdata/persistency.py Normal file
View File

@ -0,0 +1,38 @@
import pickle
import os
import shutil
from game.game import Game
def _save_file() -> str:
return "build/save"
def _temporary_save_file() -> str:
return "build/save_tmp"
def _save_file_exists() -> bool:
return os.path.exists(_save_file())
def restore_game() -> Game:
if not _save_file_exists():
return None
try:
with open(_save_file(), "rb") as f:
return pickle.load(f)
except:
return None
def save_game(game: Game) -> bool:
try:
with open(_temporary_save_file(), "wb") as f:
pickle.dump(game, f)
shutil.copy(_temporary_save_file(), _save_file())
return True
except:
return False