1- import { setupMainStreetGame , type MainStreetState } from './MainStreetState' ;
1+ import { createSeededRng } from '../../src/core-engine' ;
2+ import { setupMainStreetGame , seedToNumber , type MainStreetState } from './MainStreetState' ;
23import { executeAction , executeDayStart , processEndOfTurn , type PlayerAction } from './MainStreetEngine' ;
34import { canPurchaseEvent , getAffordableBusinessCards , getAffordableUpgradeCards , getEmptySlots } from './MainStreetMarket' ;
5+ import { GreedyStrategy , RandomStrategy , MainStreetAiPlayer } from './MainStreetAiStrategy' ;
46
57export interface MonteCarloRunSummary {
68 seed : string ;
@@ -39,7 +41,7 @@ export interface RunMonteCarloOptions {
3941 strategy ?: MonteCarloStrategy ;
4042}
4143
42- export type MonteCarloStrategy = 'market-greedy' | 'demo-greedy' ;
44+ export type MonteCarloStrategy = 'market-greedy' | 'demo-greedy' | 'greedy' | 'random' ;
4345
4446function chooseMarketGreedyActions ( state : MainStreetState ) : PlayerAction [ ] {
4547 const actions : PlayerAction [ ] = [ ] ;
@@ -123,8 +125,25 @@ function chooseActionsForStrategy(state: MainStreetState, strategy: MonteCarloSt
123125 return chooseMarketGreedyActions ( state ) ;
124126}
125127
128+ /**
129+ * Creates a `MainStreetAiPlayer` bound to the named strategy and a deterministic
130+ * RNG derived from the run seed. Returns a `MainStreetAiPlayer` for `greedy` and
131+ * `random` strategies, or `null` for legacy harness strategies (`market-greedy`,
132+ * `demo-greedy`) that use their own action choosers.
133+ */
134+ function createAiPlayerForStrategy ( strategy : MonteCarloStrategy , seed : string ) : MainStreetAiPlayer | null {
135+ if ( strategy === 'greedy' ) {
136+ return new MainStreetAiPlayer ( GreedyStrategy , createSeededRng ( seedToNumber ( `${ seed } -ai` ) ) ) ;
137+ }
138+ if ( strategy === 'random' ) {
139+ return new MainStreetAiPlayer ( RandomStrategy , createSeededRng ( seedToNumber ( `${ seed } -ai` ) ) ) ;
140+ }
141+ return null ;
142+ }
143+
126144function runSeed ( seed : string , maxTurns : number , strategy : MonteCarloStrategy ) : MonteCarloRunSummary {
127145 const state = setupMainStreetGame ( { seed } ) ;
146+ const aiPlayer = createAiPlayerForStrategy ( strategy , seed ) ;
128147
129148 let turns = 0 ;
130149 let noActionTurns = 0 ;
@@ -133,16 +152,27 @@ function runSeed(seed: string, maxTurns: number, strategy: MonteCarloStrategy):
133152
134153 while ( state . gameResult === 'playing' && turns < maxTurns ) {
135154 executeDayStart ( state ) ;
136- const planned = chooseActionsForStrategy ( state , strategy ) ;
137155 let executedAction = false ;
138156
139- for ( const action of planned ) {
140- if ( action . type === 'end-turn' ) break ;
141- try {
157+ if ( aiPlayer !== null ) {
158+ // AI strategy: choose actions one at a time until end-turn or game ends.
159+ let action = aiPlayer . chooseAction ( state ) ;
160+ while ( action . type !== 'end-turn' && state . gameResult === 'playing' ) {
142161 executeAction ( state , action ) ;
143162 executedAction = true ;
144- } catch {
145- // Ignore illegal actions selected by greedy strategy.
163+ action = aiPlayer . chooseAction ( state ) ;
164+ }
165+ } else {
166+ // Legacy harness strategies: plan a list of actions upfront.
167+ const planned = chooseActionsForStrategy ( state , strategy ) ;
168+ for ( const action of planned ) {
169+ if ( action . type === 'end-turn' ) break ;
170+ try {
171+ executeAction ( state , action ) ;
172+ executedAction = true ;
173+ } catch {
174+ // Ignore illegal actions selected by legacy strategy.
175+ }
146176 }
147177 }
148178
0 commit comments