From 136e71eae9493f70cc6e22d9622a09d48261ae44 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 4 Sep 2022 22:28:50 +0300 Subject: [PATCH] refactor: generate relations --- src/scripts/generation/generation.ts | 17 +-------- .../pack/burgsAndStates/defineStateForm.ts | 22 ++++++++---- .../burgsAndStates/generateBurgsAndStates.ts | 27 +++----------- ...erateDiplomacy.ts => generateRelations.ts} | 26 +++++++------- .../pack/burgsAndStates/simulateWars.ts | 35 +++++++++++++++++++ .../pack/burgsAndStates/specifyStates.ts | 10 +++--- src/types/pack/states.d.ts | 14 ++++++-- 7 files changed, 85 insertions(+), 66 deletions(-) rename src/scripts/generation/pack/burgsAndStates/{generateDiplomacy.ts => generateRelations.ts} (88%) create mode 100644 src/scripts/generation/pack/burgsAndStates/simulateWars.ts diff --git a/src/scripts/generation/generation.ts b/src/scripts/generation/generation.ts index a1402328..5d90ff9a 100644 --- a/src/scripts/generation/generation.ts +++ b/src/scripts/generation/generation.ts @@ -7,7 +7,7 @@ import {initLayers, renderLayer, restoreLayers} from "layers"; // @ts-expect-error js module import {drawScaleBar, Rulers} from "modules/measurers"; // @ts-expect-error js module -import {unfog, downloadFile} from "modules/ui/editors"; +import {unfog} from "modules/ui/editors"; // @ts-expect-error js module import {applyMapSize, randomizeOptions} from "modules/ui/options"; // @ts-expect-error js module @@ -76,8 +76,6 @@ async function generate(options?: IGenerationOptions) { // if (route === 2) drawPoint(pack.cells.p[index], {color: "black"}); // }); - downloadDiplomacyData(); - WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`); // showStatistics(); INFO && console.groupEnd(); @@ -86,19 +84,6 @@ async function generate(options?: IGenerationOptions) { } } -function downloadDiplomacyData() { - const states = pack.states.filter(s => s.i && !s.removed); - const valid = states.map(s => s.i); - - let data = "," + states.map(s => s.name).join(",") + "\n"; // headers - states.forEach(s => { - const rels = s.diplomacy.filter((v, i) => valid.includes(i)); - data += s.name + "," + rels.join(",") + "\n"; - }); - - // downloadFile(data, "relations.csv"); -} - function showGenerationError(error: Error) { clearMainTip(); ERROR && console.error(error); diff --git a/src/scripts/generation/pack/burgsAndStates/defineStateForm.ts b/src/scripts/generation/pack/burgsAndStates/defineStateForm.ts index 2668ef59..67f27d9e 100644 --- a/src/scripts/generation/pack/burgsAndStates/defineStateForm.ts +++ b/src/scripts/generation/pack/burgsAndStates/defineStateForm.ts @@ -23,9 +23,16 @@ export function createAreaTiers(statistics: TStateStatistics) { }; } -export function defineStateForm(type: TCultureType, areaTier: AreaTiers, nameBase: number, burgsNumber: number) { +export function defineStateForm( + type: TCultureType, + areaTier: AreaTiers, + nameBase: number, + burgsNumber: number, + relations: TRelation[], + neighbors: number[] +) { const form = defineForm(type, areaTier); - const formName = defineFormName(form, nameBase, areaTier, burgsNumber); + const formName = defineFormName(form, nameBase, areaTier, burgsNumber, relations, neighbors); return {form, formName}; } @@ -51,9 +58,11 @@ function defineFormName( form: ReturnType, nameBase: number, areaTier: AreaTiers, - burgsNumber: number + burgsNumber: number, + relations: TRelation[], + neighbors: number[] ) { - if (form === "Monarchy") return defineMonarchyForm(nameBase, areaTier); + if (form === "Monarchy") return defineMonarchyForm(nameBase, areaTier, relations, neighbors); if (form === "Republic") return defineRepublicForm(areaTier, burgsNumber); if (form === "Union") return rw(StateForms.union); if (form === "Theocracy") return defineTheocracyForm(nameBase, areaTier); @@ -63,11 +72,10 @@ function defineFormName( } // Default name depends on area tier, some name bases have special names for tiers -function defineMonarchyForm(nameBase: number, areaTier: AreaTiers, diplomacy = [""], neighbors = []) { +function defineMonarchyForm(nameBase: number, areaTier: AreaTiers, relations: TRelation[], neighbors: number[]) { const form = StateForms.monarchy[areaTier]; - // TODO: specific names for vassals - const isVassal = diplomacy.includes("Vassal"); + const isVassal = relations.includes("Vassal"); if (isVassal) { if (areaTier === AreaTiers.DUCHY && neighbors.length > 1 && rand(6) < neighbors.length) return "Marches"; if (nameBase === NAMEBASE.English && P(0.3)) return "Dominion"; diff --git a/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts b/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts index 43be3e75..341e70c7 100644 --- a/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts +++ b/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts @@ -7,31 +7,12 @@ import {createCapitals} from "./createCapitals"; import {createStateData} from "./createStateData"; import {createTowns} from "./createTowns"; import {expandStates} from "./expandStates"; -import {generateDiplomacy} from "./generateDiplomacy"; +import {generateRelations} from "./generateRelations"; import {specifyBurgs} from "./specifyBurgs"; import {specifyStates} from "./specifyStates"; -type TCellsData = Pick< - IPack["cells"], - | "v" - | "c" - | "p" - | "b" - | "i" - | "g" - | "area" - | "h" - | "f" - | "t" - | "haven" - | "harbor" - | "r" - | "fl" - | "biome" - | "s" - | "pop" - | "culture" ->; +// prettier-ignore +type TCellsData = Pick; export function generateBurgsAndStates( cultures: TCultures, @@ -87,7 +68,7 @@ export function generateBurgsAndStates( const burgIds = assignBurgIds(burgs); const statistics = collectStatistics({...cells, state: stateIds, burg: burgIds}, burgs); - const {chronicle, diplomacy} = generateDiplomacy(statesData, statistics, pick(cells, "f")); + const diplomacy = generateRelations(statesData, statistics, pick(cells, "f")); const states = specifyStates(statesData, statistics, diplomacy, cultures, burgs); return {burgIds, stateIds, burgs, states}; diff --git a/src/scripts/generation/pack/burgsAndStates/generateDiplomacy.ts b/src/scripts/generation/pack/burgsAndStates/generateRelations.ts similarity index 88% rename from src/scripts/generation/pack/burgsAndStates/generateDiplomacy.ts rename to src/scripts/generation/pack/burgsAndStates/generateRelations.ts index 18f51dbf..2f84eae4 100644 --- a/src/scripts/generation/pack/burgsAndStates/generateDiplomacy.ts +++ b/src/scripts/generation/pack/burgsAndStates/generateRelations.ts @@ -1,34 +1,32 @@ import * as d3 from "d3"; import {TIME} from "config/logging"; -import {TStateStatistics} from "./collectStatistics"; -import {TStateData} from "./createStateData"; import {P, rw} from "utils/probabilityUtils"; import {relations} from "./config"; -export type TDiplomacy = {[key: number]: TRelations[]}; +import type {TStateStatistics} from "./collectStatistics"; +import type {TStateData} from "./createStateData"; + +export type TDiplomacy = {[key: number]: TRelation[]}; interface IDiplomacyData { i: number; type: TCultureType; center: number; + expansionism: number; area: number; neighbors: number[]; } -export function generateDiplomacy( +export function generateRelations( statesData: TStateData[], statistics: TStateStatistics, cells: Pick ) { - TIME && console.time("generateDiplomacy"); + TIME && console.time("generateRelations"); - const chronicle: string[] = []; const diplomacy = getBlankDiplomacyMatrix(statesData); - - if (statesData.length < 2) { - return {chronicle, diplomacy}; - } + if (statesData.length < 2) return diplomacy; const stateAreas = Object.values(statistics).map(({area}) => area); const averageStateArea = d3.mean(stateAreas)!; @@ -81,8 +79,8 @@ export function generateDiplomacy( } } - TIME && console.timeEnd("generateDiplomacy"); - return {chronicle, diplomacy}; + TIME && console.timeEnd("generateRelations"); + return diplomacy; } function getBlankDiplomacyMatrix(statesData: TStateData[]) { @@ -95,9 +93,9 @@ function getBlankDiplomacyMatrix(statesData: TStateData[]) { } function getDiplomacyData(stateData: TStateData, statistics: TStateStatistics): IDiplomacyData { - const {i, type, center} = stateData; + const {i, type, center, expansionism} = stateData; const {neighbors, area} = statistics[i]; - return {i, type, center, neighbors, area}; + return {i, type, center, expansionism, neighbors, area}; } function detectVassalState(from: IDiplomacyData, to: IDiplomacyData, averageStateArea: number) { diff --git a/src/scripts/generation/pack/burgsAndStates/simulateWars.ts b/src/scripts/generation/pack/burgsAndStates/simulateWars.ts new file mode 100644 index 00000000..abe3c2b7 --- /dev/null +++ b/src/scripts/generation/pack/burgsAndStates/simulateWars.ts @@ -0,0 +1,35 @@ +import {TIME} from "config/logging"; + +import type {TStateData} from "./createStateData"; +import type {TDiplomacy} from "./generateRelations"; + +export function simulateWars(statesData: TStateData[], diplomacy: TDiplomacy) { + TIME && console.time("simulateWars"); + + // declare wars + for (const {i} of statesData) { + const relations = diplomacy[i]; + if (!relations.includes("Rival")) continue; // no rivals to attack + if (relations.includes("Vassal")) continue; // not independent + if (relations.includes("Enemy")) continue; // already at war + + // select candidates to attack: rival independent states + const candidates = relations + .map((relation, index) => (relation === "Rival" && !diplomacy[index].includes("Vassal") ? index : 0)) + .filter(index => index); + if (!candidates.length) continue; + + const attacker = getDiplomacyData(statesData[i], statistics); + const defender = getDiplomacyData(statesData[ra(candidates)], statistics); + + const attackerPower = attacker.area * attacker.expansionism; + const defenderPower = defender.area * defender.expansionism; + if (attackerPower < defenderPower * gauss(1.6, 0.8, 0, 10, 2)) continue; // defender is too strong + + const attackers = [attacker]; + const defenders = [defender]; + } + + TIME && console.timeEnd("simulateWars"); + return null; +} diff --git a/src/scripts/generation/pack/burgsAndStates/specifyStates.ts b/src/scripts/generation/pack/burgsAndStates/specifyStates.ts index fcd921b4..bdc718a3 100644 --- a/src/scripts/generation/pack/burgsAndStates/specifyStates.ts +++ b/src/scripts/generation/pack/burgsAndStates/specifyStates.ts @@ -7,7 +7,7 @@ import {isBurg} from "utils/typeUtils"; import type {TStateStatistics} from "./collectStatistics"; import type {TStateData} from "./createStateData"; -import type {TDiplomacy} from "./generateDiplomacy"; +import type {TDiplomacy} from "./generateRelations"; export function specifyStates( statesData: TStateData[], @@ -24,16 +24,17 @@ export function specifyStates( const states: IState[] = statesData.map(stateData => { const {i, center, type, culture, capital} = stateData; - const {area, burgs: burgsNumber, ...stats} = statistics[i]; + const {area, burgs: burgsNumber, neighbors, ...stats} = statistics[i]; const color = colors[i]; const capitalBurg = burgs[capital]; const capitalName = isBurg(capitalBurg) ? capitalBurg.name : null; if (!capitalName) throw new Error("State capital is not a burg"); + const relations = diplomacy[i]; const nameBase = getNameBase(culture); const areaTier = getAreaTier(area); - const {form, formName} = defineStateForm(type, areaTier, nameBase, burgsNumber); + const {form, formName} = defineStateForm(type, areaTier, nameBase, burgsNumber, relations, neighbors); const name = defineStateName(center, capitalName, nameBase, formName); const fullName = defineFullStateName(name, formName); @@ -47,7 +48,8 @@ export function specifyStates( area, burgs: burgsNumber, ...stats, - diplomacy: diplomacy[i] + neighbors, + relations }; return state; }); diff --git a/src/types/pack/states.d.ts b/src/types/pack/states.d.ts index 73e06792..8136e6ce 100644 --- a/src/types/pack/states.d.ts +++ b/src/types/pack/states.d.ts @@ -18,7 +18,7 @@ interface IState { rural: number; urban: number; neighbors: number[]; - diplomacy: TRelations[]; + relations: TRelation[]; removed?: boolean; } @@ -38,4 +38,14 @@ interface ICoa { t1: string; } -type TRelations = "Ally" | "Friendly" | "Neutral" | "Suspicion" | "Rival" | "Unknown" | "Suzerain" | "Vassal" | "x"; +type TRelation = + | "Ally" + | "Friendly" + | "Neutral" + | "Suspicion" + | "Rival" + | "Unknown" + | "Suzerain" + | "Vassal" + | "Enemy" + | "x";