ninelands-reddit/scripts/capture/plan.ts
Lukas Müllner d3ab781395 feat: Trailer, Devpost-Galerie und Vimeo-Cover per npm run capture:trailer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 19:57:04 +01:00

58 lines
2 KiB
TypeScript

// Partie für Trailer und Galerie vorab berechnen (läuft in Node, gebündelt von trailer.mjs).
// Der Bot spielt die Tageskarte; danach wird die Partie beim besten Stand abgeschnitten (wie „End game“).
import {BOTS} from '../../src/engine/core/bots.ts'
import {Game} from '../../src/engine/core/Game.ts'
import {starsOf} from '../../src/engine/core/goals.ts'
import {mulberry32} from '../../src/engine/core/rng.ts'
import type {UnitType} from '../../src/engine/core/units.ts'
export {UNIT_TYPES} from '../../src/engine/core/units.ts'
import {goalsOf} from '../../src/server/verify.ts'
import {dailyBoard, dailyMap} from '../../src/shared/daily.ts'
export interface Plan {
day: string
seed: number
trait: string
goals: {gold: number; lands: number}
/** net = Gold-Saldo des Zugs (für die Wahl des Galeriebilds) */
moves: {unit: UnitType; cell: number; net: number}[]
stars: number
gold: number
lands: number
/** Partie endet von selbst (kein Zug mehr) statt per „End game“ */
natural: boolean
}
export function plan(day: string, seed: number): Plan {
const map = dailyMap(new Date(`${day}T00:00:00Z`))
const goals = goalsOf(map)
const game = new Game(dailyBoard(map), {undoLimit: 0})
const rng = mulberry32(seed)
const all: Plan['moves'] = []
let best = {len: 0, key: -1}
while (!game.isGameOver()) {
const m = BOTS.smart(game, rng)
const before = game.score
game.place(m.unit, m.cell)
all.push({unit: m.unit, cell: m.cell, net: game.score - before})
const s = starsOf(game, goals)
const key = s.count * 100_000 + game.score
if (key >= best.key) best = {len: all.length, key}
}
const moves = all.slice(0, best.len)
const end = new Game(dailyBoard(map), {undoLimit: 0})
for (const m of moves) end.place(m.unit, m.cell)
return {
day,
seed,
trait: map.trait,
goals: {gold: goals.gold, lands: goals.lands},
moves,
stars: starsOf(end, goals).count,
gold: end.score,
lands: end.regionCounts()[0] ?? 0,
natural: end.isGameOver(),
}
}