fix(sim): autoplay avoids unaffordable trades

This commit is contained in:
Victor Bodinaud committed 2026-10-08 12:50:06 +02:00
1 parent a7d3be7e1b
commit 70599d59b1
2 files changed
+27 -6

No files matched your search

+11 -5
View File
@@ -3,8 +3,8 @@ import { tick } from './clock';
import { Customer, chooseShelfItem } from './customers'; import { Customer, chooseShelfItem } from './customers';
import { DayReport, endDay } from './day'; import { DayReport, endDay } from './day';
import { demandWeight } from './demand'; import { demandWeight } from './demand';
import { customerMinimumAsk, MONTHLY_RENT_CENTS, wholesaleCost } from './economy'; import { customerMinimumAsk, MONTHLY_RENT_CENTS, priceOf, wholesaleCost } from './economy';
import { itemKey, parseItemKey, stockSlot, totalQty } from './inventory'; import { getSlot, itemKey, parseItemKey, stockSlot, totalQty } from './inventory';
import { placeOrder } from './orders'; import { placeOrder } from './orders';
import { CLOSE_MINUTES, GameState } from './state'; import { CLOSE_MINUTES, GameState } from './state';
import { buyBack, recordLostRequest, recordSatisfiedRequest, sell, trade } from './transactions'; import { buyBack, recordLostRequest, recordSatisfiedRequest, sell, trade } from './transactions';
@@ -25,7 +25,7 @@ export function restock(state: GameState): void {
} }
} }
function serve(state: GameState, catalog: Catalog, customer: Customer): void { export function serveCustomer(state: GameState, catalog: Catalog, customer: Customer): void {
switch (customer.intent) { switch (customer.intent) {
case 'buy': { case 'buy': {
const slotId = chooseShelfItem(state, catalog, customer); const slotId = chooseShelfItem(state, catalog, customer);
@@ -54,7 +54,13 @@ function serve(state: GameState, catalog: Catalog, customer: Customer): void {
const slotId = chooseShelfItem(state, catalog, { ...customer, intent: 'buy' }); const slotId = chooseShelfItem(state, catalog, { ...customer, intent: 'buy' });
if (!slotId) return; if (!slotId) return;
const game = getGame(catalog, customer.offered.gameId); const game = getGame(catalog, customer.offered.gameId);
trade(state, catalog, customer, slotId, customerMinimumAsk(game, customer.offered.condition, 'trade')); const credit = customerMinimumAsk(game, customer.offered.condition, 'trade');
const slotItem = getSlot(state.inventory, slotId).item;
if (slotItem && !customer.cardId) {
const diff = priceOf(state.prices, catalog, slotItem) - credit;
if (diff < 0 && -diff > state.cash) return;
}
trade(state, catalog, customer, slotId, credit);
return; return;
} }
} }
@@ -77,7 +83,7 @@ export function autoplayDay(state: GameState, catalog: Catalog): DayReport {
restock(state); restock(state);
while (state.minutes < CLOSE_MINUTES) { while (state.minutes < CLOSE_MINUTES) {
for (const event of tick(state, catalog, 60)) { for (const event of tick(state, catalog, 60)) {
if (event.type === 'customer_arrived') serve(state, catalog, event.customer); if (event.type === 'customer_arrived') serveCustomer(state, catalog, event.customer);
} }
} }
autoOrder(state, catalog); autoOrder(state, catalog);
+16 -1
View File
@@ -1,9 +1,11 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { autoplayDay } from '../../src/sim/autoplay'; import { autoplayDay, serveCustomer } from '../../src/sim/autoplay';
import { createNewGame } from '../../src/sim/state'; import { createNewGame } from '../../src/sim/state';
import { compareDates } from '../../src/sim/calendar'; import { compareDates } from '../../src/sim/calendar';
import { loadDefaultCatalog } from '../../src/sim/catalog'; import { loadDefaultCatalog } from '../../src/sim/catalog';
import { STARTING_CASH_CENTS } from '../../src/sim/economy'; import { STARTING_CASH_CENTS } from '../../src/sim/economy';
import { getSlot } from '../../src/sim/inventory';
import { makeCustomer, stock, testCatalog } from './helpers';
describe('autoplay', () => { describe('autoplay', () => {
it('plays one day end to end', () => { it('plays one day end to end', () => {
@@ -32,4 +34,17 @@ describe('autoplay', () => {
expect(s.reputation).toBeGreaterThan(0); expect(s.reputation).toBeGreaterThan(0);
} }
}); });
it('skips trades the shop cannot afford instead of throwing', () => {
const catalog = testCatalog();
const s = createNewGame(1);
const tetrisNew = { gameId: 'tetris', condition: 'new' as const };
stock(s, 'slot-0', tetrisNew, 1);
s.prices['tetris|new'] = 100;
s.cash = 0;
const customer = makeCustomer({ intent: 'trade', offered: { gameId: 'sml', condition: 'complete' } });
expect(() => serveCustomer(s, catalog, customer)).not.toThrow();
expect(getSlot(s.inventory, 'slot-0').qty).toBe(1);
expect(s.day.trades).toBe(0);
});
}); });