configWith let the day count and the Revenue floor disagree
minCombinedRevenue fell back to SOLO_CONFIG's constant — the floor for a
FIVE-Day game — whatever days said. configWith({ days: 1 }) asked a
one-Day game to clear 15, which a full five-Day game averages barely
half of; configWith({ days: 10 }) asked for that same 15. It derives
from the days it was given now.
Not a live fault: createLocalSession is the only caller and the page
always writes the floor itself, so no dealt game was ever wrong. Found
by a throwaway probe that passed only days — which is how the next
caller would reach for it. Unchanged at the default day count, since
SOLO_CONFIG's floor is this same formula at DEFAULT_DAYS.
Stays in the unshipped v0.7.9 per Jesse — no version bump for the next
several fixes. 873 tests pass, three new.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e035dda5a3
commit
45cf521a40
+14
-1
@@ -76,6 +76,19 @@ who reopens it with "see the full results" comes back to.
|
|||||||
the end of a timetable. Same shape as the three attempts before it: the thing that was verified was
|
the end of a timetable. Same shape as the three attempts before it: the thing that was verified was
|
||||||
not the thing the player uses.
|
not the thing the player uses.
|
||||||
|
|
||||||
|
### `configWith` let the day count and the Revenue floor disagree
|
||||||
|
|
||||||
|
`minCombinedRevenue` fell back to `SOLO_CONFIG`'s constant — the floor for a *five*-Day game —
|
||||||
|
whatever `days` said. So `configWith({ days: 1 })` asked a one-Day game to clear **15**, a figure a
|
||||||
|
full five-Day game averages barely half of, and `configWith({ days: 10 })` asked for the same 15 a
|
||||||
|
five-Day game does. It derives from the days it was actually given now.
|
||||||
|
|
||||||
|
Not a live fault: `createLocalSession` is the only caller, and the page always writes the floor
|
||||||
|
itself, so no dealt game was ever wrong. It was found by a throwaway probe written to reproduce the
|
||||||
|
extension bug above, which passed only `days` — which is exactly how the next caller would reach for
|
||||||
|
it. At the default day count the answer is unchanged, since `SOLO_CONFIG`'s own floor is this same
|
||||||
|
formula at `DEFAULT_DAYS`; the three new tests pin that as well as the derivation.
|
||||||
|
|
||||||
### Three wording and layout fixes
|
### Three wording and layout fixes
|
||||||
|
|
||||||
- **The collision entries** on all three screens now read "The game ends immediately and results in
|
- **The collision entries** on all three screens now read "The game ends immediately and results in
|
||||||
@@ -90,7 +103,7 @@ not the thing the player uses.
|
|||||||
cost, not a danger, and red would outrank the actual rules above it. The buttons say **Continue
|
cost, not a danger, and red would outrank the actual rules above it. The buttons say **Continue
|
||||||
Existing Saved Game** and **Deal New Game** rather than "Continue saved game" and "Deal".
|
Existing Saved Game** and **Deal New Game** rather than "Continue saved game" and "Deal".
|
||||||
|
|
||||||
870 tests pass, three of them new; one existing test asserted the opposite of the collision ruling
|
873 tests pass, six of them new; one existing test asserted the opposite of the collision ruling
|
||||||
above and says so where it was reversed.
|
above and says so where it was reversed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+17
-2
@@ -134,10 +134,25 @@ export type NewGameOptions = {
|
|||||||
|
|
||||||
/** The same config with the New Game dialog's answers in it. */
|
/** The same config with the New Game dialog's answers in it. */
|
||||||
export function configWith(opts: NewGameOptions): GameConfig {
|
export function configWith(opts: NewGameOptions): GameConfig {
|
||||||
|
const days = opts.days ?? SOLO_CONFIG.days;
|
||||||
return {
|
return {
|
||||||
...SOLO_CONFIG,
|
...SOLO_CONFIG,
|
||||||
days: opts.days ?? SOLO_CONFIG.days,
|
days,
|
||||||
minCombinedRevenue: opts.minCombinedRevenue ?? SOLO_CONFIG.minCombinedRevenue,
|
/**
|
||||||
|
* DERIVED FROM THE DAYS ACTUALLY IN PLAY, not from `SOLO_CONFIG`'s five-Day constant.
|
||||||
|
*
|
||||||
|
* It fell back to the constant until 2026-08-30, so `configWith({ days: 1 })` asked a one-Day
|
||||||
|
* game to clear **15** — a floor a five-Day game averages barely half of — and
|
||||||
|
* `configWith({ days: 10 })` asked for the same 15 a five-Day game does. The two fields silently
|
||||||
|
* disagreed, which is the one thing a "build me a config" helper must not let happen.
|
||||||
|
*
|
||||||
|
* Not a live fault when it was found: `createLocalSession` is the only caller, and the page
|
||||||
|
* always writes `minCombinedRevenue` itself (`solitaireDefaults` re-derives it from the preset).
|
||||||
|
* Found by a throwaway probe that passed only `days` — which is exactly how the next caller
|
||||||
|
* would use this. At the default day count the answer is unchanged, since `SOLO_CONFIG`'s own
|
||||||
|
* floor is this same formula at `DEFAULT_DAYS`.
|
||||||
|
*/
|
||||||
|
minCombinedRevenue: opts.minCombinedRevenue ?? collectiveRevenueFloor(1, days),
|
||||||
maxCollisionsPerDay: opts.maxCollisionsPerDay ?? SOLO_CONFIG.maxCollisionsPerDay,
|
maxCollisionsPerDay: opts.maxCollisionsPerDay ?? SOLO_CONFIG.maxCollisionsPerDay,
|
||||||
maxCollisionsTotal: opts.maxCollisionsTotal ?? SOLO_CONFIG.maxCollisionsTotal,
|
maxCollisionsTotal: opts.maxCollisionsTotal ?? SOLO_CONFIG.maxCollisionsTotal,
|
||||||
optionalRules: { ...SOLO_CONFIG.optionalRules, ...(opts.optionalRules ?? {}) },
|
optionalRules: { ...SOLO_CONFIG.optionalRules, ...(opts.optionalRules ?? {}) },
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import { createGame as createEngineGame } from '../src/engine/setup.ts';
|
|||||||
import { advance as advanceEngine } from '../src/engine/advance.ts';
|
import { advance as advanceEngine } from '../src/engine/advance.ts';
|
||||||
import type { GameConfig } from '../src/engine/state.ts';
|
import type { GameConfig } from '../src/engine/state.ts';
|
||||||
import {
|
import {
|
||||||
|
configWith,
|
||||||
|
SOLO_CONFIG,
|
||||||
overHandLimit,
|
overHandLimit,
|
||||||
actionGroups,
|
actionGroups,
|
||||||
actionMenu,
|
actionMenu,
|
||||||
@@ -3339,6 +3341,34 @@ describe('the Day rolling over says so (Gitea#10)', () => {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('configWith', () => {
|
||||||
|
/**
|
||||||
|
* The Revenue floor is a function of how long the game is, so a helper that takes `days` and
|
||||||
|
* defaults the floor has to derive it. It fell back to `SOLO_CONFIG`'s five-Day constant until
|
||||||
|
* 2026-08-30, which let the two fields disagree: a one-Day game was asked to clear 15, a floor a
|
||||||
|
* five-Day game averages barely half of. Found by a probe that passed only `days` — which is how
|
||||||
|
* the next caller would reach for it.
|
||||||
|
*/
|
||||||
|
it('derives the Revenue floor from the days it was given', () => {
|
||||||
|
assert.equal(configWith({ days: 1 }).minCombinedRevenue, 3, 'a one-Day game kept the five-Day floor');
|
||||||
|
assert.equal(configWith({ days: 10 }).minCombinedRevenue, 30, 'a ten-Day game kept the five-Day floor');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves the default day count exactly where it was', () => {
|
||||||
|
// The fix must not move the floor anybody is actually playing against: SOLO_CONFIG's own floor
|
||||||
|
// is this same formula at DEFAULT_DAYS, so the default path is unchanged.
|
||||||
|
assert.equal(configWith({}).minCombinedRevenue, SOLO_CONFIG.minCombinedRevenue);
|
||||||
|
assert.equal(configWith({ days: SOLO_CONFIG.days }).minCombinedRevenue, SOLO_CONFIG.minCombinedRevenue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still lets a caller name a floor that has nothing to do with the length', () => {
|
||||||
|
// Deriving is the DEFAULT, not a rule — the New Game dialog and the lobby both let a player set
|
||||||
|
// a floor directly, and that has to survive being passed through here.
|
||||||
|
assert.equal(configWith({ days: 10, minCombinedRevenue: 4 }).minCombinedRevenue, 4);
|
||||||
|
assert.equal(configWith({ days: 3, minCombinedRevenue: 0 }).minCombinedRevenue, 0, 'an off floor was re-derived');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('the end-of-game results screen (Gitea#16)', () => {
|
describe('the end-of-game results screen (Gitea#16)', () => {
|
||||||
/**
|
/**
|
||||||
* A finished game, built by running the clock off the end of a real one rather than by hand — the
|
* A finished game, built by running the clock off the end of a real one rather than by hand — the
|
||||||
|
|||||||
Reference in New Issue
Block a user