refactor: generate relations

This commit is contained in:
Azgaar 2022-09-04 22:28:50 +03:00
parent 73431ec743
commit 136e71eae9
7 changed files with 85 additions and 66 deletions

View file

@ -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);

View file

@ -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<typeof defineForm>,
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";

View file

@ -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<IPack["cells"], | "v" | "c" | "p" | "b" | "i" | "g" | "area" | "h" | "f" | "t" | "haven" | "harbor" | "r" | "fl" | "biome" | "s" | "pop" | "culture">;
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};

View file

@ -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<IPack["cells"], "f">
) {
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) {

View file

@ -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;
}

View file

@ -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;
});

View file

@ -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";