Move generator only settings out of Settings.

This commit is contained in:
Dan Albert 2020-12-05 16:55:19 -08:00
parent b8e64d4369
commit f1a2602cfd
4 changed files with 107 additions and 87 deletions

View File

@ -6,12 +6,6 @@ from dcs.forcedoptions import ForcedOptions
@dataclass @dataclass
class Settings: class Settings:
# Generator settings
inverted: bool = False
do_not_generate_carrier: bool = False
do_not_generate_lha: bool = False
do_not_generate_player_navy: bool = False
do_not_generate_enemy_navy: bool = False
# Difficulty settings # Difficulty settings
player_skill: str = "Good" player_skill: str = "Good"
@ -25,7 +19,6 @@ class Settings:
supercarrier: bool = False supercarrier: bool = False
multiplier: float = 1.0 multiplier: float = 1.0
generate_marks: bool = True generate_marks: bool = True
sams: bool = True # Legacy parameter do not use
manpads: bool = True manpads: bool = True
cold_start: bool = False # Legacy parameter do not use cold_start: bool = False # Legacy parameter do not use
version: Optional[str] = None version: Optional[str] = None

View File

@ -4,7 +4,9 @@ import logging
import math import math
import pickle import pickle
import random import random
from typing import Any, Dict, Iterable, Optional, Set from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, Iterable, List, Optional, Set
from dcs.mapping import Point from dcs.mapping import Point
from dcs.task import CAP, CAS, PinpointStrike from dcs.task import CAP, CAS, PinpointStrike
@ -12,8 +14,7 @@ from dcs.vehicles import AirDefence
from game import Game, db from game import Game, db
from game.factions.faction import Faction from game.factions.faction import Faction
from game.settings import Settings from game.theater import Carrier, Lha, LocationType
from game.theater import LocationType
from game.theater.conflicttheater import IMPORTANCE_HIGH, IMPORTANCE_LOW from game.theater.conflicttheater import IMPORTANCE_HIGH, IMPORTANCE_LOW
from game.theater.theatergroundobject import ( from game.theater.theatergroundobject import (
BuildingGroundObject, BuildingGroundObject,
@ -44,9 +45,10 @@ from . import (
ConflictTheater, ConflictTheater,
ControlPoint, ControlPoint,
ControlPointType, ControlPointType,
OffMapSpawn,
Fob, Fob,
OffMapSpawn,
) )
from ..settings import Settings
GroundObjectTemplates = Dict[str, Dict[str, Any]] GroundObjectTemplates = Dict[str, Dict[str, Any]]
@ -62,18 +64,28 @@ COUNT_BY_TASK = {
} }
@dataclass(frozen=True)
class GeneratorSettings:
start_date: datetime
starting_budget: int
multiplier: float
midgame: bool
inverted: bool
no_carrier: bool
no_lha: bool
no_player_navy: bool
no_enemy_navy: bool
class GameGenerator: class GameGenerator:
def __init__(self, player: str, enemy: str, theater: ConflictTheater, def __init__(self, player: str, enemy: str, theater: ConflictTheater,
settings: Settings, start_date, starting_budget: int, settings: Settings,
multiplier: float, midgame: bool) -> None: generator_settings: GeneratorSettings) -> None:
self.player = player self.player = player
self.enemy = enemy self.enemy = enemy
self.theater = theater self.theater = theater
self.settings = settings self.settings = settings
self.start_date = start_date self.generator_settings = generator_settings
self.starting_budget = starting_budget
self.multiplier = multiplier
self.midgame = midgame
def generate(self) -> Game: def generate(self) -> Game:
# Reset name generator # Reset name generator
@ -83,35 +95,30 @@ class GameGenerator:
game = Game(player_name=self.player, game = Game(player_name=self.player,
enemy_name=self.enemy, enemy_name=self.enemy,
theater=self.theater, theater=self.theater,
start_date=self.start_date, start_date=self.generator_settings.start_date,
settings=self.settings) settings=self.settings)
GroundObjectGenerator(game).generate() GroundObjectGenerator(game, self.generator_settings).generate()
game.budget = self.starting_budget game.budget = self.generator_settings.starting_budget
game.settings.multiplier = self.multiplier
game.settings.sams = True
game.settings.version = VERSION game.settings.version = VERSION
return game return game
def prepare_theater(self) -> None: def prepare_theater(self) -> None:
to_remove = [] to_remove: List[ControlPoint] = []
# Auto-capture half the bases if midgame. # Auto-capture half the bases if midgame.
if self.midgame: if self.generator_settings.midgame:
control_points = self.theater.controlpoints control_points = self.theater.controlpoints
for control_point in control_points[:len(control_points) // 2]: for control_point in control_points[:len(control_points) // 2]:
control_point.captured = True control_point.captured = True
# Remove carrier and lha, invert situation if needed # Remove carrier and lha, invert situation if needed
for cp in self.theater.controlpoints: for cp in self.theater.controlpoints:
no_carrier = self.settings.do_not_generate_carrier if isinstance(cp, Carrier) and self.generator_settings.no_carrier:
no_lha = self.settings.do_not_generate_lha
if cp.cptype is ControlPointType.AIRCRAFT_CARRIER_GROUP and \
no_carrier:
to_remove.append(cp) to_remove.append(cp)
elif cp.cptype is ControlPointType.LHA_GROUP and no_lha: elif isinstance(cp, Lha) and self.generator_settings.no_lha:
to_remove.append(cp) to_remove.append(cp)
if self.settings.inverted: if self.generator_settings.inverted:
cp.captured = cp.captured_invert cp.captured = cp.captured_invert
# do remove # do remove
@ -120,7 +127,7 @@ class GameGenerator:
# TODO: Fix this. This captures all bases for blue. # TODO: Fix this. This captures all bases for blue.
# reapply midgame inverted if needed # reapply midgame inverted if needed
if self.midgame and self.settings.inverted: if self.generator_settings.midgame and self.generator_settings.inverted:
for i, cp in enumerate(reversed(self.theater.controlpoints)): for i, cp in enumerate(reversed(self.theater.controlpoints)):
if i > len(self.theater.controlpoints): if i > len(self.theater.controlpoints):
break break
@ -163,9 +170,10 @@ class GameGenerator:
count_log = math.log(control_point.importance + 0.01, count_log = math.log(control_point.importance + 0.01,
UNIT_COUNT_IMPORTANCE_LOG) UNIT_COUNT_IMPORTANCE_LOG)
count = max( count = max((COUNT_BY_TASK[task] *
COUNT_BY_TASK[task] * self.multiplier * (1 + count_log), 1 self.generator_settings.multiplier *
) (count_log + 1)),
1)
count_per_type = max(int(float(count) / len(unit_types)), 1) count_per_type = max(int(float(count) / len(unit_types)), 1)
for unit_type in unit_types: for unit_type in unit_types:
@ -316,8 +324,10 @@ class LocationFinder:
class ControlPointGroundObjectGenerator: class ControlPointGroundObjectGenerator:
def __init__(self, game: Game, control_point: ControlPoint) -> None: def __init__(self, game: Game, generator_settings: GeneratorSettings,
control_point: ControlPoint) -> None:
self.game = game self.game = game
self.generator_settings = generator_settings
self.control_point = control_point self.control_point = control_point
self.location_finder = LocationFinder(game, control_point) self.location_finder = LocationFinder(game, control_point)
@ -344,11 +354,11 @@ class ControlPointGroundObjectGenerator:
return True return True
def generate_navy(self) -> None: def generate_navy(self) -> None:
skip_player_navy = self.game.settings.do_not_generate_player_navy skip_player_navy = self.generator_settings.no_player_navy
if self.control_point.captured and skip_player_navy: if self.control_point.captured and skip_player_navy:
return return
skip_enemy_navy = self.game.settings.do_not_generate_enemy_navy skip_enemy_navy = self.generator_settings.no_enemy_navy
if not self.control_point.captured and skip_enemy_navy: if not self.control_point.captured and skip_enemy_navy:
return return
@ -538,6 +548,8 @@ class BaseDefenseGenerator:
return return
g.groups.append(group) g.groups.append(group)
self.control_point.base_defenses.append(g) self.control_point.base_defenses.append(g)
class FobDefenseGenerator(BaseDefenseGenerator): class FobDefenseGenerator(BaseDefenseGenerator):
def generate(self) -> None: def generate(self) -> None:
self.generate_garrison() self.generate_garrison()
@ -559,10 +571,12 @@ class FobDefenseGenerator(BaseDefenseGenerator):
else: else:
self.generate_garrison() self.generate_garrison()
class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
def __init__(self, game: Game, control_point: ControlPoint, def __init__(self, game: Game, generator_settings: GeneratorSettings,
control_point: ControlPoint,
templates: GroundObjectTemplates) -> None: templates: GroundObjectTemplates) -> None:
super().__init__(game, control_point) super().__init__(game, generator_settings, control_point)
self.templates = templates self.templates = templates
def generate(self) -> bool: def generate(self) -> bool:
@ -699,6 +713,7 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
self.control_point.connected_objectives.append(g) self.control_point.connected_objectives.append(g)
return return
class FobGroundObjectGenerator(AirbaseGroundObjectGenerator): class FobGroundObjectGenerator(AirbaseGroundObjectGenerator):
def generate(self) -> bool: def generate(self) -> bool:
self.generate_fob() self.generate_fob()
@ -729,9 +744,12 @@ class FobGroundObjectGenerator(AirbaseGroundObjectGenerator):
unit["heading"], self.control_point, unit["type"], airbase_group=True) unit["heading"], self.control_point, unit["type"], airbase_group=True)
self.control_point.connected_objectives.append(g) self.control_point.connected_objectives.append(g)
class GroundObjectGenerator: class GroundObjectGenerator:
def __init__(self, game: Game) -> None: def __init__(self, game: Game,
generator_settings: GeneratorSettings) -> None:
self.game = game self.game = game
self.generator_settings = generator_settings
with open("resources/groundobject_templates.p", "rb") as f: with open("resources/groundobject_templates.p", "rb") as f:
self.templates: GroundObjectTemplates = pickle.load(f) self.templates: GroundObjectTemplates = pickle.load(f)
@ -746,15 +764,20 @@ class GroundObjectGenerator:
def generate_for_control_point(self, control_point: ControlPoint) -> bool: def generate_for_control_point(self, control_point: ControlPoint) -> bool:
generator: ControlPointGroundObjectGenerator generator: ControlPointGroundObjectGenerator
if control_point.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP: if control_point.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
generator = CarrierGroundObjectGenerator(self.game, control_point) generator = CarrierGroundObjectGenerator(
self.game, self.generator_settings, control_point)
elif control_point.cptype == ControlPointType.LHA_GROUP: elif control_point.cptype == ControlPointType.LHA_GROUP:
generator = LhaGroundObjectGenerator(self.game, control_point) generator = LhaGroundObjectGenerator(
self.game, self.generator_settings, control_point)
elif isinstance(control_point, OffMapSpawn): elif isinstance(control_point, OffMapSpawn):
generator = NoOpGroundObjectGenerator(self.game, control_point) generator = NoOpGroundObjectGenerator(
self.game, self.generator_settings, control_point)
elif isinstance(control_point, Fob): elif isinstance(control_point, Fob):
generator = FobGroundObjectGenerator(self.game, control_point, generator = FobGroundObjectGenerator(
self.templates) self.game, self.generator_settings, control_point,
self.templates)
else: else:
generator = AirbaseGroundObjectGenerator(self.game, control_point, generator = AirbaseGroundObjectGenerator(
self.templates) self.game, self.generator_settings, control_point,
self.templates)
return generator.generate() return generator.generate()

View File

@ -13,7 +13,7 @@ from PySide2.QtWidgets import QApplication, QSplashScreen
from game import Game, db, persistency, VERSION from game import Game, db, persistency, VERSION
from game.settings import Settings from game.settings import Settings
from game.theater.start_generator import GameGenerator from game.theater.start_generator import GameGenerator, GeneratorSettings
from qt_ui import ( from qt_ui import (
liberation_install, liberation_install,
liberation_theme, liberation_theme,
@ -128,11 +128,21 @@ def parse_args() -> argparse.Namespace:
def create_game(campaign_path: Path, blue: str, red: str, def create_game(campaign_path: Path, blue: str, red: str,
supercarrier: bool) -> Game: supercarrier: bool) -> Game:
campaign = Campaign.from_json(campaign_path) campaign = Campaign.from_json(campaign_path)
generator = GameGenerator(blue, red, campaign.load_theater(), generator = GameGenerator(
Settings(supercarrier=supercarrier), blue, red, campaign.load_theater(),
start_date=datetime.today(), Settings(supercarrier=supercarrier),
starting_budget=650, GeneratorSettings(
multiplier=1, midgame=False) start_date=datetime.today(),
starting_budget=650,
multiplier=1.0,
midgame=False,
inverted=False,
no_carrier=False,
no_lha=False,
no_player_navy=False,
no_enemy_navy=False
)
)
return generator.generate() return generator.generate()

View File

@ -15,7 +15,7 @@ from qt_ui.windows.newgame.QCampaignList import (
QCampaignList, QCampaignList,
load_campaigns, load_campaigns,
) )
from game.theater.start_generator import GameGenerator from game.theater.start_generator import GameGenerator, GeneratorSettings
jinja_env = Environment( jinja_env = Environment(
loader=FileSystemLoader("resources/ui/templates"), loader=FileSystemLoader("resources/ui/templates"),
@ -51,43 +51,37 @@ class NewGameWizard(QtWidgets.QWizard):
logging.info("New Game Wizard accept") logging.info("New Game Wizard accept")
logging.info("======================") logging.info("======================")
blueFaction = [c for c in db.FACTIONS][self.field("blueFaction")] campaign = self.field("selectedCampaign")
redFaction = [c for c in db.FACTIONS][self.field("redFaction")] if campaign is None:
campaign = self.campaigns[0]
selectedCampaign = self.field("selectedCampaign")
if selectedCampaign is None:
selectedCampaign = self.campaigns[0]
conflictTheater = selectedCampaign.load_theater()
timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]]
midGame = self.field("midGame")
# QSlider forces integers, so we use 1 to 50 and divide by 10 to give
# 0.1 to 5.0.
multiplier = self.field("multiplier") / 10
no_carrier = self.field("no_carrier")
no_lha = self.field("no_lha")
supercarrier = self.field("supercarrier")
no_player_navy = self.field("no_player_navy")
no_enemy_navy = self.field("no_enemy_navy")
invertMap = self.field("invertMap")
starting_money = int(self.field("starting_money"))
player_name = blueFaction
enemy_name = redFaction
settings = Settings( settings = Settings(
inverted=invertMap, supercarrier=self.field("supercarrier"),
supercarrier=supercarrier, )
do_not_generate_carrier=no_carrier, generator_settings = GeneratorSettings(
do_not_generate_lha=no_lha, start_date=db.TIME_PERIODS[
do_not_generate_player_navy=no_player_navy, list(db.TIME_PERIODS.keys())[self.field("timePeriod")]],
do_not_generate_enemy_navy=no_enemy_navy starting_budget=int(self.field("starting_money")),
# QSlider forces integers, so we use 1 to 50 and divide by 10 to
# give 0.1 to 5.0.
multiplier=self.field("multiplier") / 10,
midgame=self.field("midGame"),
inverted=self.field("invertMap"),
no_carrier=self.field("no_carrier"),
no_lha=self.field("no_lha"),
no_player_navy=self.field("no_player_navy"),
no_enemy_navy=self.field("no_enemy_navy")
) )
generator = GameGenerator(player_name, enemy_name, conflictTheater, blue_faction = [c for c in db.FACTIONS][self.field("blueFaction")]
settings, timePeriod, starting_money, red_faction = [c for c in db.FACTIONS][self.field("redFaction")]
multiplier, midGame) generator = GameGenerator(
blue_faction,
red_faction,
campaign.load_theater(),
settings,
generator_settings
)
self.generatedGame = generator.generate() self.generatedGame = generator.generate()
super(NewGameWizard, self).accept() super(NewGameWizard, self).accept()