refactor: enforce package boundaries
This commit is contained in:
@@ -7,7 +7,7 @@ const hasWorkspaceMarker = (dir: string): boolean =>
|
||||
WORKSPACE_MARKERS.some((marker) => fs.existsSync(path.join(dir, marker)));
|
||||
|
||||
export const resolveWorkspaceRoot = (
|
||||
startDir: string = process.env.GATEWAY_WORKSPACE_ROOT ?? process.cwd(),
|
||||
startDir: string = process.env.GAME_WORKSPACE_ROOT ?? process.env.GATEWAY_WORKSPACE_ROOT ?? process.cwd(),
|
||||
maxDepth = 6
|
||||
): string => {
|
||||
let current = path.resolve(startDir);
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import type { City, MapDefinition } from '@sammo-ts/logic';
|
||||
|
||||
const buildConnectionMap = (map: MapDefinition): Map<number, number[]> => {
|
||||
const result = new Map<number, number[]>();
|
||||
for (const city of map.cities) {
|
||||
result.set(city.id, city.connections ?? []);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const searchAllDistanceByCityList = (
|
||||
map: MapDefinition,
|
||||
cityIds: number[]
|
||||
): Record<number, Record<number, number>> => {
|
||||
if (cityIds.length === 0) {
|
||||
return {};
|
||||
}
|
||||
const connectionMap = buildConnectionMap(map);
|
||||
const citySet = new Set(cityIds);
|
||||
const result: Record<number, Record<number, number>> = {};
|
||||
|
||||
for (const startId of citySet) {
|
||||
const distances: Record<number, number> = { [startId]: 0 };
|
||||
const queue: Array<[number, number]> = [[startId, 0]];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const [currentId, dist] = queue.shift()!;
|
||||
const connections = connectionMap.get(currentId) ?? [];
|
||||
for (const nextId of connections) {
|
||||
if (!citySet.has(nextId)) {
|
||||
continue;
|
||||
}
|
||||
if (distances[nextId] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
distances[nextId] = dist + 1;
|
||||
queue.push([nextId, dist + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
result[startId] = distances;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const searchAllDistanceByNationList = (
|
||||
map: MapDefinition,
|
||||
cities: City[],
|
||||
nationIds: number[],
|
||||
suppliedCityOnly: boolean
|
||||
): Record<number, Record<number, number>> => {
|
||||
if (nationIds.length === 0) {
|
||||
return {};
|
||||
}
|
||||
const cityIds = cities
|
||||
.filter((city) => nationIds.includes(city.nationId))
|
||||
.filter((city) => !suppliedCityOnly || city.supplyState > 0)
|
||||
.map((city) => city.id);
|
||||
return searchAllDistanceByCityList(map, cityIds);
|
||||
};
|
||||
|
||||
export const isNeighbor = (
|
||||
map: MapDefinition,
|
||||
cities: City[],
|
||||
nationA: number,
|
||||
nationB: number,
|
||||
includeNoSupply = true
|
||||
): boolean => {
|
||||
if (nationA === nationB) {
|
||||
return false;
|
||||
}
|
||||
const connectionMap = buildConnectionMap(map);
|
||||
const nationACities = new Set(
|
||||
cities
|
||||
.filter((city) => city.nationId === nationA)
|
||||
.filter((city) => includeNoSupply || city.supplyState > 0)
|
||||
.map((city) => city.id)
|
||||
);
|
||||
|
||||
const nationBCities = cities
|
||||
.filter((city) => city.nationId === nationB)
|
||||
.filter((city) => includeNoSupply || city.supplyState > 0)
|
||||
.map((city) => city.id);
|
||||
|
||||
for (const cityId of nationBCities) {
|
||||
const connections = connectionMap.get(cityId) ?? [];
|
||||
for (const adjId of connections) {
|
||||
if (nationACities.has(adjId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
valueFit,
|
||||
withCanonicalArgumentAliases,
|
||||
} from '../aiUtils.js';
|
||||
import { searchAllDistanceByNationList } from '../distance.js';
|
||||
import { searchAllDistanceByNationList } from '@sammo-ts/logic/world/distance.js';
|
||||
import { generalActionHandlers } from '../generalAiGeneralActions.js';
|
||||
import { nationActionHandlers } from '../generalAiNationActions.js';
|
||||
import { resolveConstraintEnv, type ConstraintEnv } from './constraint.js';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { GeneralAI } from '../core.js';
|
||||
import { GAME_TICKS_PER_TURN } from '@sammo-ts/common';
|
||||
import { calcCityDevRatio } from '../../aiUtils.js';
|
||||
import { searchAllDistanceByCityList } from '../../distance.js';
|
||||
import { searchAllDistanceByCityList } from '@sammo-ts/logic/world/distance.js';
|
||||
|
||||
export const do천도 = (ai: GeneralAI) => {
|
||||
if (!ai.nation || !ai.nation.capitalCityId) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { GeneralAI } from '../core.js';
|
||||
import { asRecord, joinYearMonth, parseYearMonth, readMetaNumber } from '../../aiUtils.js';
|
||||
import { isNeighbor } from '../../distance.js';
|
||||
import { isNeighbor } from '@sammo-ts/logic/world/distance.js';
|
||||
import { resolveNationIncome } from './helpers.js';
|
||||
|
||||
const isTechLimited = (ai: GeneralAI, tech: number): boolean => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
loadActionModuleBundle,
|
||||
} from '@sammo-ts/logic';
|
||||
import { asRecord } from '@sammo-ts/common';
|
||||
import { createRuntimeTrace } from './runtimeTrace.js';
|
||||
|
||||
// legacy GameConstBase 기본값
|
||||
const DEFAULT_GENERAL_GOLD = 1000;
|
||||
@@ -80,6 +81,7 @@ export const buildCommandEnv = (config: ScenarioConfig, unitSet?: UnitSetDefinit
|
||||
const constValues = asRecord(config.const);
|
||||
|
||||
return {
|
||||
trace: createRuntimeTrace(),
|
||||
...(unitSet ? { unitSet } : {}),
|
||||
scenarioEffect: config.environment.scenarioEffect ?? null,
|
||||
develCost: resolveNumber(constValues, ['develCost', 'develcost', 'develrate'], 0),
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { TraceEvent, TracePort, TraceSubject } from '@sammo-ts/logic/ports/trace.js';
|
||||
|
||||
const parseIds = (value: string | undefined): Set<string> => new Set(value?.split(',') ?? []);
|
||||
|
||||
const intersects = (configured: ReadonlySet<string>, requested: readonly number[] | undefined): boolean =>
|
||||
requested?.some((id) => configured.has(String(id))) ?? false;
|
||||
|
||||
export const createRuntimeTrace = (
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
write: (line: string) => void = (line) => process.stdout.write(line)
|
||||
): TracePort => {
|
||||
const generalIds = parseIds(env.CORE_AI_TRACE_GENERAL_IDS);
|
||||
const nationIds = parseIds(env.CORE_AI_TRACE_NATION_IDS);
|
||||
const warTechNationIds = parseIds(env.CORE_WAR_TECH_TRACE_NATION_IDS);
|
||||
|
||||
return {
|
||||
isEnabled(event: TraceEvent, subject: TraceSubject = {}): boolean {
|
||||
switch (event) {
|
||||
case 'AI_ACTION_PATCH_TRACE':
|
||||
return intersects(generalIds, subject.generalIds) || intersects(nationIds, subject.nationIds);
|
||||
case 'AI_WAR_TRACE':
|
||||
return intersects(generalIds, subject.generalIds);
|
||||
case 'AI_WAR_FIXTURE_CORE':
|
||||
return env.CORE_BATTLE_FIXTURE_TRACE === '1';
|
||||
case 'WAR_TECH_TRACE':
|
||||
return intersects(warTechNationIds, subject.nationIds);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
write(event: TraceEvent, payload: unknown): void {
|
||||
write(`${event} ${JSON.stringify(payload)}\n`);
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { asNumber, asRecord, JosaUtil, LiteHashDRBG, RandUtil } from '@sammo-ts/common';
|
||||
import { GamePrisma, LogCategory, LogScope } from '@sammo-ts/infra';
|
||||
import { GamePrisma } from '@sammo-ts/infra';
|
||||
import {
|
||||
EventDomesticTraitLoader,
|
||||
isEventDomesticTraitKey,
|
||||
isPersonalityTraitKey,
|
||||
LogCategory,
|
||||
LogScope,
|
||||
PERSONALITY_TRAIT_KEYS,
|
||||
simpleSerialize,
|
||||
} from '@sammo-ts/logic';
|
||||
|
||||
Reference in New Issue
Block a user