feat(sim): clock tick and events
This commit is contained in:
1 parent
3a8a110a2e
commit
af10c3e8fd
2 files changed
+64
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user