Step 10B.1: generate contract boards for any location
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ea37ce8ced
commit
dff2c576cf
|
|
@ -617,3 +617,7 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
|||
- **Change request CR-002 is stored in `memory-bank/change-requests/`** and inserted as Phase 10B, before Phase 11.
|
||||
- **Decision numbering:** the CR's new 0.5.27 (Ship delivery) and 0.5.28 (Undiscovered locations) are recorded as **0.5.35** and **0.5.36**, since 27–34 were already in use. Decision 0.5.5 is replaced and the glossary gains "Contract board" and "Deadhead".
|
||||
- **Error code:** the CR's `location_undiscovered` is the existing `location-undiscovered`.
|
||||
|
||||
## Step 10B.1
|
||||
|
||||
- **The board generator's input is `location`** (the freight market's location). Sim never knew about bays for boards; the old field name `origin` was the only "bay-board" naming, and comments now say "location board". Generation rules and seeds (`board:<location>:<period>`) are unchanged.
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export async function bayBoard(
|
|||
.where(eq(discoveredLocations.playerId, playerId));
|
||||
return generateContractBoard(
|
||||
{
|
||||
origin,
|
||||
location: origin,
|
||||
discoveredIds: new Set(discovered.map((d) => d.locationId)),
|
||||
periodIndex,
|
||||
worldSeed: player!.worldSeed,
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ Spec: `memory-bank/change-requests/CR-001-faster-start.md`. Decisions recorded a
|
|||
|
||||
Spec: `memory-bank/change-requests/CR-002-open-markets.md`. New decisions recorded as plan §0.5.35–0.5.36.
|
||||
|
||||
- [ ] Step 10B.1 — Sim: boards at any location
|
||||
- [x] **Step 10B.1 — Sim: boards at any location.** `BoardInput.location` (was `origin`) is the board's location; no bay notion in sim board generation. Moon board test added.
|
||||
- [ ] Step 10B.2 — Sim: deadhead validation
|
||||
- [ ] Step 10B.3 — Database: boards keyed by location
|
||||
- [ ] Step 10B.4 — Server: board endpoint for any discovered location
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ export function standardCargo(content: SimContent): CargoType {
|
|||
}
|
||||
|
||||
export interface BoardInput {
|
||||
origin: Location;
|
||||
/** The location whose freight market this is; every contract starts here (CR-002). */
|
||||
location: Location;
|
||||
discoveredIds: ReadonlySet<string>;
|
||||
periodIndex: number;
|
||||
worldSeed: Seed;
|
||||
|
|
@ -109,12 +110,13 @@ function pickTier(rng: Rng, cargoType: CargoType, content: SimContent): number {
|
|||
}
|
||||
|
||||
/**
|
||||
* Generates a bay's contract board for one period: `contractMassBands` decide how
|
||||
* Generates a location's contract board for one period (any discovered
|
||||
* location, bay or not, CR-002): `contractMassBands` decide how
|
||||
* many contracts fall in each ship-size mass band. Same inputs always give the
|
||||
* same board.
|
||||
*/
|
||||
export function generateContractBoard(input: BoardInput, content: SimContent): Contract[] {
|
||||
const { origin, discoveredIds, periodIndex, worldSeed } = input;
|
||||
const { location: origin, discoveredIds, periodIndex, worldSeed } = input;
|
||||
const { config } = content;
|
||||
const rng = createRng(deriveSeed(worldSeed, `board:${origin.id}:${periodIndex}`));
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export function resolveDispatch(input: ResolveInput, content: SimContent): Resol
|
|||
const { contract } = record;
|
||||
const base = { shipLocationId: record.destinationId };
|
||||
if (contract === null) {
|
||||
// Repositions earn nothing and only fly to bays, which are already discovered.
|
||||
// Deadheads earn nothing and only fly to discovered locations.
|
||||
return {
|
||||
status: "resolved",
|
||||
resolution: {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
const earth = content.location("earth")!;
|
||||
const discoveredIds = new Set(["earth", "moon"]);
|
||||
const periodIndex = boardPeriodIndex(Date.parse("2100-01-01T00:00:00Z"), content.config);
|
||||
const input: BoardInput = { origin: earth, discoveredIds, periodIndex, worldSeed: 12345 };
|
||||
const input: BoardInput = { location: earth, discoveredIds, periodIndex, worldSeed: 12345 };
|
||||
|
||||
describe("contract board generation", () => {
|
||||
it("produces an identical board from the same inputs", () => {
|
||||
|
|
@ -42,7 +42,10 @@ describe("contract board generation", () => {
|
|||
it("ties tier to skill: Standard is tier 0 with no skill, others 1–5 matching the skill level", () => {
|
||||
const moon = content.location("moon")!;
|
||||
for (let p = 0; p < 50; p++) {
|
||||
for (const c of generateContractBoard({ ...input, origin: moon, periodIndex: p }, content)) {
|
||||
for (const c of generateContractBoard(
|
||||
{ ...input, location: moon, periodIndex: p },
|
||||
content,
|
||||
)) {
|
||||
if (c.cargoTypeId === "standard") {
|
||||
expect(c.tier).toBe(0);
|
||||
expect(c.requiredSkill).toBeNull();
|
||||
|
|
@ -106,6 +109,20 @@ describe("contract board generation", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("location boards (CR-002)", () => {
|
||||
it("generates a deterministic Moon board from the location alone, with no bay data", () => {
|
||||
const moon = content.location("moon")!;
|
||||
const moonInput: BoardInput = { location: moon, discoveredIds, periodIndex, worldSeed: 12345 };
|
||||
const board = generateContractBoard(moonInput, content);
|
||||
expect(board).toHaveLength(content.config.contractBoardSize);
|
||||
for (const c of board) {
|
||||
expect(c.originId).toBe("moon");
|
||||
expect(c.destinationId).not.toBe("moon");
|
||||
}
|
||||
expect(generateContractBoard(moonInput, content)).toEqual(board);
|
||||
});
|
||||
});
|
||||
|
||||
describe("required Long Haul level", () => {
|
||||
it("counts the cut-offs a distance exceeds", () => {
|
||||
expect(requiredLongHaulLevel(0.4, content.config)).toBe(0);
|
||||
|
|
|
|||
Loading…
Reference in New Issue