Draw freight on the card, and teach the bot to build a railroad
This commit is contained in:
@@ -755,6 +755,26 @@ function shiftChange(s: GameState, events: GameEvent[]): AdvanceResult {
|
||||
if (s.clock.stage >= STAGES_PER_DAY) {
|
||||
// Telegraph/Telephone/Radio are each usable once a Day.
|
||||
for (const area of s.officeAreas.values()) area.dispatchUsedToday = [];
|
||||
|
||||
/**
|
||||
* THE CLASSIFICATION YARD GOES BACK INTO SERVICE OVERNIGHT.
|
||||
*
|
||||
* ASSUMPTION, flagged rather than derived: Gap 2c says engines and cabooses return to the
|
||||
* Division Yard and everything else to Classification, and the recovered rules never say how
|
||||
* Classification empties. It was write-only — seven places pushed into it and nothing ever read
|
||||
* it — so rolling stock drained one way and left the game. Measured at 30 cars a game dead by
|
||||
* the end, 37% of the 80 dealt at setup.
|
||||
*
|
||||
* Sorting cars for redistribution is what a classification yard is FOR, and a Day is its
|
||||
* natural cycle, so they return at the Day boundary. This does not bind a 5-Day game — the
|
||||
* Division Yard still held 16.6 loaded freight cars at the end and ran dry in 2 games of 100 —
|
||||
* but a campaign-length game would have starved.
|
||||
*/
|
||||
if (s.yards.classificationYard.length > 0) {
|
||||
s.yards.divisionYard.push(...s.yards.classificationYard);
|
||||
s.yards.classificationYard = [];
|
||||
}
|
||||
|
||||
s.clock.day += 1;
|
||||
s.clock.stage = 1;
|
||||
s.collisionsToday = 0;
|
||||
|
||||
+18
-4
@@ -149,9 +149,23 @@ export function hasFreightAgentOption(s: GameState, player: PlayerIndex): boolea
|
||||
* for an empty coach to be spotted on a hopper siding, and because a load parked on MEN|AT|WORK
|
||||
* strips the industry track of Operational Rail status (§9.3), the facility would jam permanently.
|
||||
*/
|
||||
/**
|
||||
* EVERY commodity a facility handles, not just the first one printed.
|
||||
*
|
||||
* Two of the six industries take two: a Power Plant burns coal OR oil (`['hopper','tank']`) and a
|
||||
* Grocer's Warehouse receives dry goods OR perishables (`['boxcar','reefer']`). This used to return
|
||||
* `carTypes[0]`, and because `freightAgent.stockOutbound` gates on it, the engine rejected the
|
||||
* second commodity as WRONG_CAR_TYPE — the sheet said a Power Plant takes tank cars and the code
|
||||
* said it did not. Measured effect: tank cars were dropped 0 times in 100 games.
|
||||
*/
|
||||
export function facilityCarTypes(f: Facility): readonly CarType[] {
|
||||
if (f.kind !== 'freight') return f.kind === 'passenger' ? ['coach'] : [];
|
||||
return FREIGHT_PROFILES.find((p) => p.kind === f.subtype)?.carTypes ?? [];
|
||||
}
|
||||
|
||||
/** The commodity to NAME a facility by, where one word is wanted. Legality must use the full set. */
|
||||
export function facilityCarType(f: Facility): CarType | null {
|
||||
if (f.kind !== 'freight') return f.kind === 'passenger' ? 'coach' : null;
|
||||
return FREIGHT_PROFILES.find((p) => p.kind === f.subtype)?.carTypes[0] ?? null;
|
||||
return facilityCarTypes(f)[0] ?? null;
|
||||
}
|
||||
|
||||
export function hasSwitchOption(s: GameState, player: PlayerIndex): boolean {
|
||||
@@ -433,8 +447,8 @@ export function check(s: GameState, player: PlayerIndex, i: Intent): RejectionCo
|
||||
if (!f) return 'NO_SUCH_FACILITY';
|
||||
if (!f.allows.outbound) return 'NO_SUCH_FACILITY';
|
||||
if (f.outboundBox.length >= f.capacity.outbound) return 'BOX_FULL';
|
||||
// §9.1 — the green box takes only this facility's commodity.
|
||||
if (facilityCarType(f) !== i.carType) return 'WRONG_CAR_TYPE';
|
||||
// §9.1 — the green box takes only this facility's commodities, of which it may have two.
|
||||
if (!facilityCarTypes(f).includes(i.carType)) return 'WRONG_CAR_TYPE';
|
||||
if (!s.yards.divisionYard.some((c) => c.type === i.carType && c.loaded)) {
|
||||
return 'NO_SUITABLE_CAR';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user