debriefing log parsing

This commit is contained in:
Vasiliy Horbachenko 2018-06-13 02:42:44 +03:00
parent 5f7724d44e
commit 481a5922b4
9 changed files with 107 additions and 49 deletions

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import pickle import sys
import theater.caucasus import theater.caucasus
import ui.window import ui.window
@ -9,6 +9,14 @@ from game.game import Game
from theater import start_generator from theater import start_generator
from userdata import persistency from userdata import persistency
from dcs.lua.parse import loads
with open("/Users/sp/Downloads/won_cap.log", "r") as f:
s = f.read()
print(loads(s))
#sys.exit(0)
game = persistency.restore_game() game = persistency.restore_game()
if not game: if not game:
theater = theater.caucasus.CaucasusTheater() theater = theater.caucasus.CaucasusTheater()

View File

@ -42,19 +42,20 @@ PRICES = {
Armor.APC_BTR_80: 6, Armor.APC_BTR_80: 6,
AirDefence.AAA_ZU_23_on_Ural_375: 4, AirDefence.AAA_ZU_23_on_Ural_375: 4,
AirDefence.SAM_Avenger_M1097: 10,
} }
UNIT_BY_TASK = { UNIT_BY_TASK = {
FighterSweep: [Su_27, Su_33, Su_25, F_15C, MiG_15bis, MiG_21Bis, MiG_29A, F_A_18C, AV8BNA], FighterSweep: [Su_27, Su_33, Su_25, F_15C, MiG_15bis, MiG_21Bis, MiG_29A, F_A_18C, AV8BNA],
CAS: [Su_25T, A_10A, A_10C, ], CAS: [Su_25T, A_10A, A_10C, ],
CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ], CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ],
AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, ], AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, AirDefence.SAM_Avenger_M1097 ],
Transport: [IL_76MD, S_3B_Tanker, ], Transport: [IL_76MD, S_3B_Tanker, ],
} }
UNIT_BY_COUNTRY = { UNIT_BY_COUNTRY = {
"Russia": [Su_25T, Su_27, Su_33, Su_25, MiG_15bis, MiG_21Bis, MiG_29A, AirDefence.AAA_ZU_23_on_Ural_375, Armor.APC_BTR_80, Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, IL_76MD, ], "Russia": [Su_25T, Su_27, Su_33, Su_25, MiG_15bis, MiG_21Bis, MiG_29A, AirDefence.AAA_ZU_23_on_Ural_375, Armor.APC_BTR_80, Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, IL_76MD, ],
"USA": [F_15C, A_10C, F_A_18C, AV8BNA, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, S_3B_Tanker], "USA": [F_15C, A_10C, F_A_18C, AV8BNA, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, S_3B_Tanker, AirDefence.SAM_Avenger_M1097],
} }
UnitsDict = typing.Dict[UnitType, int] UnitsDict = typing.Dict[UnitType, int]

View File

@ -56,12 +56,12 @@ class GroundInterceptEvent(Event):
targets = None # type: db.ArmorDict targets = None # type: db.ArmorDict
def __str__(self): def __str__(self):
return "Ground intercept from {} at {} ({})".format(self.from_cp, self.to_cp, "*" * self.difficulty) return "Ground intercept from {} at {}".format(self.from_cp, self.to_cp)
def is_successfull(self, debriefing: Debriefing): def is_successfull(self, debriefing: Debriefing):
total_targets = sum(self.targets.values()) total_targets = sum(self.targets.values())
destroyed_targets = 0 destroyed_targets = 0
for unit, count in debriefing.destroyed_units[self.defender.name].items(): for unit, count in debriefing.destroyed_units[self.defender_name].items():
if unit in self.targets: if unit in self.targets:
destroyed_targets += count destroyed_targets += count
@ -113,10 +113,10 @@ class InterceptEvent(Event):
transport_unit = None # type: FlyingType transport_unit = None # type: FlyingType
def __str__(self): def __str__(self):
return "Intercept from {} at {} ({})".format(self.from_cp, self.to_cp, "*" * self.difficulty) return "Intercept from {} at {}".format(self.from_cp, self.to_cp)
def is_successfull(self, debriefing: Debriefing): def is_successfull(self, debriefing: Debriefing):
intercepted = self.transport_unit in debriefing.destroyed_units[self.defender.name].keys() intercepted = self.transport_unit in debriefing.destroyed_units[self.defender_name].keys()
if self.from_cp.captured: if self.from_cp.captured:
return intercepted return intercepted
else: else:
@ -185,10 +185,10 @@ class CaptureEvent(Event):
STRENGTH_RECOVERY = 0.35 STRENGTH_RECOVERY = 0.35
def __str__(self): def __str__(self):
return "Attack from {} to {} ({})".format(self.from_cp, self.to_cp, "*" * self.difficulty) return "Attack from {} to {}".format(self.from_cp, self.to_cp)
def is_successfull(self, debriefing: Debriefing): def is_successfull(self, debriefing: Debriefing):
attackers_success = len(debriefing.destroyed_units[self.defender.name]) > len(debriefing.destroyed_units[self.attacker.name]) attackers_success = len(debriefing.destroyed_units[self.defender_name]) > len(debriefing.destroyed_units[self.attacker_name])
if self.from_cp.captured: if self.from_cp.captured:
return attackers_success return attackers_success
else: else:

View File

@ -21,8 +21,8 @@ ENEMY_INTERCEPT_PROBABILITY_BASE = 10
ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE = 1 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 = 100
PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30 PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 100
PLAYER_GLOBALINTERCEPT_PROBABILITY_BASE = 100 PLAYER_GLOBALINTERCEPT_PROBABILITY_BASE = 100
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 50 PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 50
@ -40,8 +40,8 @@ class Game:
def __init__(self, theater: ConflictTheater): def __init__(self, theater: ConflictTheater):
self.events = [] self.events = []
self.theater = theater self.theater = theater
self.player = "USA" self.player = "Russia"
self.enemy = "Russia" self.enemy = "USA"
def _roll(self, prob, mult): def _roll(self, prob, mult):
return random.randint(0, 100) <= prob * mult return random.randint(0, 100) <= prob * mult
@ -132,16 +132,6 @@ class Game:
theater=self.theater)) theater=self.theater))
break break
def _generate_global(self):
for cp in self.theater.player_points():
if cp.is_global:
if self._roll(PLAYER_GLOBALINTERCEPT_PROBABILITY_BASE, cp.base.strength):
enemy_points = list(set(self.theater.enemy_bases()) - set(self.theater.conflicts(False)))
self.events.append(InterceptEvent(attacker_name=self.player,
defender_name=self.enemy,
from_cp=cp,
to_cp=random.choice(enemy_points)))
def _commision_units(self, cp: ControlPoint): def _commision_units(self, cp: ControlPoint):
for for_task in [CAP, CAS, FighterSweep, AirDefence]: for for_task in [CAP, CAS, FighterSweep, AirDefence]:
limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE)
@ -207,5 +197,4 @@ class Game:
self._generate_interceptions() self._generate_interceptions()
self._generate_globalinterceptions() self._generate_globalinterceptions()
self._generate_groundinterceptions() self._generate_groundinterceptions()
self._generate_global()

View File

@ -159,7 +159,8 @@ class GroundInterceptOperation(Operation):
radials=ALL_RADIALS radials=ALL_RADIALS
) )
super(GroundInterceptOperation, self).__init__(mission, conflict) self.initialize(mission=mission,
conflict=conflict)
def generate(self): def generate(self):
self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.starting_position) self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.starting_position)

View File

@ -23,29 +23,33 @@ class EventMenu(Menu):
self.window.clear_right_pane() self.window.clear_right_pane()
row = 0 row = 0
def label(text): def label(text, _row=None, _column=None):
nonlocal row nonlocal row
Label(self.frame, text=text).grid() Label(self.frame, text=text).grid(row=_row and _row or row, column=_column and _column or 0)
row += 1 if _row is None:
row += 1
def scrable_row(unit_type, unit_count): def scrable_row(unit_type, unit_count):
nonlocal row nonlocal row
Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row) Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row, sticky=W)
scramble_entry = Entry(self.frame) scramble_entry = Entry(self.frame, width=10)
scramble_entry.grid(column=1, row=row) scramble_entry.grid(column=1, row=row)
scramble_entry.insert(0, "0")
self.aircraft_scramble_entries[unit_type] = scramble_entry self.aircraft_scramble_entries[unit_type] = scramble_entry
client_entry = Entry(self.frame) client_entry = Entry(self.frame, width=10)
client_entry.grid(column=2, row=row) client_entry.grid(column=2, row=row)
client_entry.insert(0, "0")
self.aircraft_client_entries[unit_type] = client_entry self.aircraft_client_entries[unit_type] = client_entry
row += 1 row += 1
def scramble_armor_row(unit_type, unit_count): def scramble_armor_row(unit_type, unit_count):
nonlocal row nonlocal row
Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row) Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row, sticky=W)
scramble_entry = Entry(self.frame) scramble_entry = Entry(self.frame, width=10)
scramble_entry.insert(0, "0")
scramble_entry.grid(column=1, row=row) scramble_entry.grid(column=1, row=row)
self.armor_scramble_entries[unit_type] = scramble_entry self.armor_scramble_entries[unit_type] = scramble_entry
@ -58,13 +62,23 @@ class EventMenu(Menu):
base = self.event.to_cp.base base = self.event.to_cp.base
label("Aircraft") label("Aircraft")
label("Amount", row, 1)
label("Client slots", row, 2)
row+=1
for unit_type, count in base.aircraft.items(): for unit_type, count in base.aircraft.items():
scrable_row(unit_type, count) scrable_row(unit_type, count)
if not base.total_planes:
label("None")
label("Armor") label("Armor")
for unit_type, count in base.armor.items(): for unit_type, count in base.armor.items():
scramble_armor_row(unit_type, count) scramble_armor_row(unit_type, count)
if not base.total_armor:
label("None")
Button(self.frame, text="Commit", command=self.start).grid(column=0, row=row) Button(self.frame, text="Commit", command=self.start).grid(column=0, row=row)
Button(self.frame, text="Back", command=self.dismiss).grid(column=2, row=row) Button(self.frame, text="Back", command=self.dismiss).grid(column=2, row=row)

View File

@ -21,14 +21,14 @@ class EventResultsMenu(Menu):
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)).grid()
Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1, False)).grid(row=1, column=1) Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1)).grid(row=1, column=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, succ", command=self.simulate_result(0.5, 0.5)).grid(row=2, )
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="half losses, fail", command=self.simulate_result(0.5, 0.5)).grid(row=2, column=1)
Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0, True)).grid(row=3, ) Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0)).grid(row=3, )
Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0, False)).grid(row=3, column=1) Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0)).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) Label(self.frame, text="Play the mission and save debriefing to {}".format(debriefing_directory_location())).grid(row=0, column=0)
else: else:
@ -55,6 +55,10 @@ 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): def process_debriefing(self, debriefing: Debriefing):
debriefing.calculate_destroyed_units(mission=self.event.operation.mission,
player_name=self.game.player,
enemy_name=self.game.enemy)
self.game.finish_event(event=self.event, debriefing=debriefing) self.game.finish_event(event=self.event, debriefing=debriefing)
self.game.pass_turn() self.game.pass_turn()
@ -63,7 +67,7 @@ class EventResultsMenu(Menu):
self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy, {}) self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy, {})
self.display() self.display()
def simulate_result(self, player_factor: float, enemy_factor: float, result: bool): def simulate_result(self, player_factor: float, enemy_factor: float):
def action(): def action():
debriefing = Debriefing() debriefing = Debriefing()

View File

@ -34,9 +34,9 @@ class MainMenu(Menu):
def event_button(event): def event_button(event):
nonlocal row nonlocal row
Message(self.frame, text="{}{}".format( Message(self.frame, text="{}{}".format(
event.defender.name == self.game.player and "Enemy attacking: " or "", event.defender_name == self.game.player and "Enemy attacking: " or "",
event event
), aspect=500).grid(column=0, row=row, sticky=NW) ), aspect=800).grid(column=0, row=row, sticky=NW)
Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S) Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S)
row += 1 row += 1
Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1

View File

@ -1,26 +1,67 @@
import typing import typing
import json
import threading import threading
import time import time
import os import os
from datetime import datetime from dcs.lua import parse
from dcs.mission import Mission
from dcs.unitgroup import FlyingGroup
from dcs.unit import UnitType
from game import db
DEBRIEFING_LOG_EXTENSION = "log" DEBRIEFING_LOG_EXTENSION = "log"
class Debriefing: class Debriefing:
def __init__(self): def __init__(self, alive_units):
self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[str, int]] self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[str, int]]
self.alive_units = alive_units # type: typing.Dict[str, typing.Dict[str, int]]
@classmethod @classmethod
def parse(cls, path: str): def parse(cls, path: str, mission: Mission):
with open(path, "r") as f: with open(path, "r") as f:
events = json.load(f) table_string = f.read()
table = parse.loads(table_string)
units = table.get("debriefing", {}).get("world_state", {})
alive_units = {}
return Debriefing() for unit in units:
type = unit["type"] # type: str
country_id = int(unit["country"])
if type:
country_dict = alive_units.get(unit[country_id], {})
country_dict[type] = country_dict.get(type, 0) + 1
alive_units[unit[country_id]] = country_dict
return Debriefing(alive_units)
def calculate_destroyed_units(self, mission: Mission, player_name: str, enemy_name: str):
def count_groups(groups: typing.List[UnitType]) -> typing.Dict[UnitType, int]:
result = {}
for group in groups:
for unit in group.units:
result[unit.unit_type] = result.get(unit.unit_type, 0) + 1
return result
def calculate_losses(all_units: typing.Dict[UnitType, int], alive_units: typing.Dict[str, int]) -> typing.Dict[UnitType, int]:
result = {}
for t, count in all_units.items():
result[t] = count - alive_units[db.unit_type_name(t)]
return result
player = mission.country(player_name)
enemy = mission.country(enemy_name)
player_units = count_groups(player.plane_group + player.vehicle_group)
enemy_units = count_groups(enemy.plane_group + enemy.vehicle_group)
self.destroyed_units = {
player.name: calculate_losses(player_units, self.alive_units[player.id]),
enemy.name: calculate_losses(enemy_units, self.alive_units[enemy.id]),
}
def debriefing_directory_location() -> str: def debriefing_directory_location() -> str:
return "build/debriefing" return "build/debriefing"