feat(battle-sim): add battle simulation scripts and refactor schemas
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsdown -c ../../tsdown.config.ts -F @sammo-ts/game-api",
|
"build": "tsdown -c ../../tsdown.config.ts -F @sammo-ts/game-api",
|
||||||
"dev": "tsdown -c ../../tsdown.config.ts -F @sammo-ts/game-api --watch",
|
"dev": "tsdown -c ../../tsdown.config.ts -F @sammo-ts/game-api --watch",
|
||||||
|
"worker": "GAME_API_ROLE=battle-sim-worker node dist/index.js",
|
||||||
|
"worker:dev": "GAME_API_ROLE=battle-sim-worker tsdown -c ../../tsdown.config.ts -F @sammo-ts/game-api --watch",
|
||||||
"lint": "node -e \"console.log('lint not configured')\"",
|
"lint": "node -e \"console.log('lint not configured')\"",
|
||||||
"test": "vitest run --config vitest.config.ts",
|
"test": "vitest run --config vitest.config.ts",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import type { BattleSimRequestPayload } from './types.js';
|
||||||
|
|
||||||
|
export const zBattleSimGeneral = z.object({
|
||||||
|
no: z.number().int().positive(),
|
||||||
|
name: z.string().min(1),
|
||||||
|
nation: z.number().int().positive(),
|
||||||
|
turntime: z.string().min(1),
|
||||||
|
personal: z.string().nullable(),
|
||||||
|
special2: z.string().nullable(),
|
||||||
|
crew: z.number().int().min(0),
|
||||||
|
crewtype: z.number().int().positive(),
|
||||||
|
atmos: z.number().int().min(0),
|
||||||
|
train: z.number().int().min(0),
|
||||||
|
intel: z.number().int().min(0),
|
||||||
|
intel_exp: z.number().int().min(0),
|
||||||
|
book: z.string().nullable(),
|
||||||
|
strength: z.number().int().min(0),
|
||||||
|
strength_exp: z.number().int().min(0),
|
||||||
|
weapon: z.string().nullable(),
|
||||||
|
injury: z.number().int().min(0),
|
||||||
|
leadership: z.number().int().min(0),
|
||||||
|
leadership_exp: z.number().int().min(0),
|
||||||
|
horse: z.string().nullable(),
|
||||||
|
item: z.string().nullable(),
|
||||||
|
explevel: z.number().int().min(0),
|
||||||
|
experience: z.number().int().min(0),
|
||||||
|
dedication: z.number().int().min(0),
|
||||||
|
officer_level: z.number().int().min(1),
|
||||||
|
officer_city: z.number().int().positive(),
|
||||||
|
gold: z.number().int().min(0),
|
||||||
|
rice: z.number().int().min(0),
|
||||||
|
dex1: z.number().int().min(0),
|
||||||
|
dex2: z.number().int().min(0),
|
||||||
|
dex3: z.number().int().min(0),
|
||||||
|
dex4: z.number().int().min(0),
|
||||||
|
dex5: z.number().int().min(0),
|
||||||
|
recent_war: z.string().nullable(),
|
||||||
|
warnum: z.number().int().min(0),
|
||||||
|
killnum: z.number().int().min(0),
|
||||||
|
killcrew: z.number().int().min(0),
|
||||||
|
inheritBuff: z.union([z.record(z.string(), z.number()), z.array(z.number())]).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const zBattleSimCity = z.object({
|
||||||
|
city: z.number().int().positive(),
|
||||||
|
nation: z.number().int().min(0),
|
||||||
|
supply: z.number().int().min(0),
|
||||||
|
name: z.string().min(1),
|
||||||
|
pop: z.number().min(0),
|
||||||
|
agri: z.number().min(0),
|
||||||
|
comm: z.number().min(0),
|
||||||
|
secu: z.number().min(0),
|
||||||
|
def: z.number().min(0),
|
||||||
|
wall: z.number().min(0),
|
||||||
|
trust: z.number().min(0),
|
||||||
|
level: z.number().int().min(1),
|
||||||
|
pop_max: z.number().min(0),
|
||||||
|
agri_max: z.number().min(0),
|
||||||
|
comm_max: z.number().min(0),
|
||||||
|
secu_max: z.number().min(0),
|
||||||
|
def_max: z.number().min(0),
|
||||||
|
wall_max: z.number().min(0),
|
||||||
|
dead: z.number().min(0),
|
||||||
|
state: z.number().int().min(0),
|
||||||
|
conflict: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const zBattleSimNation = z.object({
|
||||||
|
type: z.string().min(1),
|
||||||
|
tech: z.number().min(0),
|
||||||
|
level: z.number().int().min(1),
|
||||||
|
capital: z.number().int().min(0),
|
||||||
|
nation: z.number().int().min(0),
|
||||||
|
name: z.string().min(1),
|
||||||
|
gold: z.number().min(0),
|
||||||
|
rice: z.number().min(0),
|
||||||
|
gennum: z.number().int().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const zBattleSimRequest: z.ZodType<BattleSimRequestPayload> = z.object({
|
||||||
|
action: z.enum(['reorder', 'battle']),
|
||||||
|
seed: z.string().optional(),
|
||||||
|
repeatCnt: z.number().int().min(1).max(1000),
|
||||||
|
year: z.number().int().min(0),
|
||||||
|
month: z.number().int().min(1).max(12),
|
||||||
|
attackerGeneral: zBattleSimGeneral,
|
||||||
|
attackerCity: zBattleSimCity,
|
||||||
|
attackerNation: zBattleSimNation,
|
||||||
|
defenderGenerals: z.array(zBattleSimGeneral),
|
||||||
|
defenderCity: zBattleSimCity,
|
||||||
|
defenderNation: zBattleSimNation,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const zBattleSimJobId = z.object({
|
||||||
|
jobId: z.string().min(1),
|
||||||
|
});
|
||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
type MessageView,
|
type MessageView,
|
||||||
} from './messages/store.js';
|
} from './messages/store.js';
|
||||||
import { buildBattleSimJobPayload } from './battleSim/environment.js';
|
import { buildBattleSimJobPayload } from './battleSim/environment.js';
|
||||||
import type { BattleSimRequestPayload } from './battleSim/types.js';
|
import { zBattleSimJobId, zBattleSimRequest } from './battleSim/schema.js';
|
||||||
|
|
||||||
const zRunReason = z.enum(['schedule', 'manual', 'poke']);
|
const zRunReason = z.enum(['schedule', 'manual', 'poke']);
|
||||||
const zMessageType = z.enum(['private', 'public', 'national', 'diplomacy']);
|
const zMessageType = z.enum(['private', 'public', 'national', 'diplomacy']);
|
||||||
@@ -39,96 +39,6 @@ const zTurnRunBudget = z.object({
|
|||||||
catchUpCap: z.number().int().positive(),
|
catchUpCap: z.number().int().positive(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const zBattleSimGeneral = z.object({
|
|
||||||
no: z.number().int().positive(),
|
|
||||||
name: z.string().min(1),
|
|
||||||
nation: z.number().int().positive(),
|
|
||||||
turntime: z.string().min(1),
|
|
||||||
personal: z.string().nullable(),
|
|
||||||
special2: z.string().nullable(),
|
|
||||||
crew: z.number().int().min(0),
|
|
||||||
crewtype: z.number().int().positive(),
|
|
||||||
atmos: z.number().int().min(0),
|
|
||||||
train: z.number().int().min(0),
|
|
||||||
intel: z.number().int().min(0),
|
|
||||||
intel_exp: z.number().int().min(0),
|
|
||||||
book: z.string().nullable(),
|
|
||||||
strength: z.number().int().min(0),
|
|
||||||
strength_exp: z.number().int().min(0),
|
|
||||||
weapon: z.string().nullable(),
|
|
||||||
injury: z.number().int().min(0),
|
|
||||||
leadership: z.number().int().min(0),
|
|
||||||
leadership_exp: z.number().int().min(0),
|
|
||||||
horse: z.string().nullable(),
|
|
||||||
item: z.string().nullable(),
|
|
||||||
explevel: z.number().int().min(0),
|
|
||||||
experience: z.number().int().min(0),
|
|
||||||
dedication: z.number().int().min(0),
|
|
||||||
officer_level: z.number().int().min(1),
|
|
||||||
officer_city: z.number().int().positive(),
|
|
||||||
gold: z.number().int().min(0),
|
|
||||||
rice: z.number().int().min(0),
|
|
||||||
dex1: z.number().int().min(0),
|
|
||||||
dex2: z.number().int().min(0),
|
|
||||||
dex3: z.number().int().min(0),
|
|
||||||
dex4: z.number().int().min(0),
|
|
||||||
dex5: z.number().int().min(0),
|
|
||||||
recent_war: z.string().nullable(),
|
|
||||||
warnum: z.number().int().min(0),
|
|
||||||
killnum: z.number().int().min(0),
|
|
||||||
killcrew: z.number().int().min(0),
|
|
||||||
inheritBuff: z.union([z.record(z.string(), z.number()), z.array(z.number())]).optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const zBattleSimCity = z.object({
|
|
||||||
city: z.number().int().positive(),
|
|
||||||
nation: z.number().int().min(0),
|
|
||||||
supply: z.number().int().min(0),
|
|
||||||
name: z.string().min(1),
|
|
||||||
pop: z.number().min(0),
|
|
||||||
agri: z.number().min(0),
|
|
||||||
comm: z.number().min(0),
|
|
||||||
secu: z.number().min(0),
|
|
||||||
def: z.number().min(0),
|
|
||||||
wall: z.number().min(0),
|
|
||||||
trust: z.number().min(0),
|
|
||||||
level: z.number().int().min(1),
|
|
||||||
pop_max: z.number().min(0),
|
|
||||||
agri_max: z.number().min(0),
|
|
||||||
comm_max: z.number().min(0),
|
|
||||||
secu_max: z.number().min(0),
|
|
||||||
def_max: z.number().min(0),
|
|
||||||
wall_max: z.number().min(0),
|
|
||||||
dead: z.number().min(0),
|
|
||||||
state: z.number().int().min(0),
|
|
||||||
conflict: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const zBattleSimNation = z.object({
|
|
||||||
type: z.string().min(1),
|
|
||||||
tech: z.number().min(0),
|
|
||||||
level: z.number().int().min(1),
|
|
||||||
capital: z.number().int().min(0),
|
|
||||||
nation: z.number().int().min(0),
|
|
||||||
name: z.string().min(1),
|
|
||||||
gold: z.number().min(0),
|
|
||||||
rice: z.number().min(0),
|
|
||||||
gennum: z.number().int().min(1),
|
|
||||||
});
|
|
||||||
|
|
||||||
const zBattleSimRequest: z.ZodType<BattleSimRequestPayload> = z.object({
|
|
||||||
action: z.enum(['reorder', 'battle']),
|
|
||||||
seed: z.string().optional(),
|
|
||||||
repeatCnt: z.number().int().min(1).max(1000),
|
|
||||||
year: z.number().int().min(0),
|
|
||||||
month: z.number().int().min(1).max(12),
|
|
||||||
attackerGeneral: zBattleSimGeneral,
|
|
||||||
attackerCity: zBattleSimCity,
|
|
||||||
attackerNation: zBattleSimNation,
|
|
||||||
defenderGenerals: z.array(zBattleSimGeneral),
|
|
||||||
defenderCity: zBattleSimCity,
|
|
||||||
defenderNation: zBattleSimNation,
|
|
||||||
});
|
|
||||||
|
|
||||||
const buildShiftAmountSchema = (maxTurns: number) =>
|
const buildShiftAmountSchema = (maxTurns: number) =>
|
||||||
z.number()
|
z.number()
|
||||||
@@ -177,11 +87,7 @@ export const appRouter = router({
|
|||||||
return ctx.battleSim.simulate(payload);
|
return ctx.battleSim.simulate(payload);
|
||||||
}),
|
}),
|
||||||
getSimulation: procedure
|
getSimulation: procedure
|
||||||
.input(
|
.input(zBattleSimJobId)
|
||||||
z.object({
|
|
||||||
jobId: z.string().min(1),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const result = await ctx.battleSim.getSimulationResult(input.jobId);
|
const result = await ctx.battleSim.getSimulationResult(input.jobId);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ export const createGameApiServer = async () => {
|
|||||||
profile: config.profileName,
|
profile: config.profileName,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
app.addHook('onClose', async () => {
|
app.addHook('onClose', async () => {
|
||||||
await flushSubscriber.stop();
|
await flushSubscriber.stop();
|
||||||
await flushSubscriberClient.quit();
|
await flushSubscriberClient.quit();
|
||||||
|
|||||||
Reference in New Issue
Block a user