feat(sim): clock tick and events

This commit is contained in:
Victor Bodinaud committed 2026-10-08 12:40:10 +02:00
1 parent 3a8a110a2e
commit af10c3e8fd
2 files changed
+64

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
import { Catalog } from './catalog';
import { Customer, customersPerHour, spawnCustomer } from './customers';
import { random } from './rng';
import { CLOSE_MINUTES, GameState, OPEN_MINUTES } from './state';
export type SimEvent =
| { type: 'shop_opened' }
| { type: 'customer_arrived'; customer: Customer }
| { type: 'shop_closed' };
export function isOpen(state: GameState): boolean {
return state.minutes >= OPEN_MINUTES && state.minutes < CLOSE_MINUTES;
}
/** Advances the clock minute by minute, stopping at closing time. */
export function tick(state: GameState, catalog: Catalog, deltaMinutes: number): SimEvent[] {
const events: SimEvent[] = [];
for (let i = 0; i < deltaMinutes && state.minutes < CLOSE_MINUTES; i++) {
state.minutes += 1;
if (state.minutes === OPEN_MINUTES) events.push({ type: 'shop_opened' });
if (state.minutes > OPEN_MINUTES) {
state.customerAccumulator += (customersPerHour(state.reputation) / 60) * (0.5 + random(state));
while (state.customerAccumulator >= 1) {
state.customerAccumulator -= 1;
events.push({ type: 'customer_arrived', customer: spawnCustomer(state, catalog) });
}
}
if (state.minutes === CLOSE_MINUTES) events.push({ type: 'shop_closed' });
}
return events;
}
+33
View File
@@ -0,0 +1,33 @@
import { describe, it, expect } from 'vitest';
import { isOpen, tick } from '../../src/sim/clock';
import { CLOSE_MINUTES, createNewGame, OPEN_MINUTES } from '../../src/sim/state';
import { testCatalog } from './helpers';
const catalog = testCatalog();
describe('clock', () => {
it('opens the shop at 9:00 with no customers before', () => {
const s = createNewGame(1);
const events = tick(s, catalog, 60);
expect(s.minutes).toBe(OPEN_MINUTES);
expect(events).toEqual([{ type: 'shop_opened' }]);
expect(isOpen(s)).toBe(true);
});
it('brings customers during opening hours and closes at 19:00', () => {
const s = createNewGame(1);
s.reputation = 100;
const events = tick(s, catalog, 24 * 60);
expect(s.minutes).toBe(CLOSE_MINUTES);
expect(events.filter((e) => e.type === 'customer_arrived').length).toBeGreaterThan(10);
expect(events[events.length - 1]).toEqual({ type: 'shop_closed' });
expect(isOpen(s)).toBe(false);
});
it('does nothing once closed', () => {
const s = createNewGame(1);
tick(s, catalog, 24 * 60);
expect(tick(s, catalog, 60)).toEqual([]);
expect(s.minutes).toBe(CLOSE_MINUTES);
});
});