| |
| |
|
|
| import { useReducer, useCallback } from 'react' |
| import { apiReset, apiStep } from '../services/api.js' |
|
|
| |
| const initialState = { |
| obs: null, |
| prevObs: null, |
| done: false, |
| loading: false, |
| error: null, |
| lastReward: null, |
| lastInfo: null, |
| rewardTrace: [], |
| cumReward: 0, |
| speed: 1.5, |
| paused: true, |
| seed: 42, |
| } |
|
|
| |
| function reducer(state, action) { |
| switch (action.type) { |
| case 'RESET_START': |
| return { ...initialState, speed: state.speed, loading: true, paused: true } |
|
|
| case 'RESET_SUCCESS': |
| return { |
| ...state, |
| loading: false, |
| obs: action.payload.observation, |
| prevObs: null, |
| done: action.payload.done, |
| lastReward: null, |
| lastInfo: action.payload.info, |
| rewardTrace: [], |
| cumReward: 0, |
| error: null, |
| } |
|
|
| case 'STEP_START': |
| return { ...state, loading: true } |
|
|
| case 'STEP_SUCCESS': { |
| const r = Number(action.payload.reward ?? 0) |
| return { |
| ...state, |
| loading: false, |
| prevObs: state.obs, |
| obs: action.payload.observation, |
| done: action.payload.done, |
| lastReward: action.payload.reward, |
| lastInfo: action.payload.info, |
| rewardTrace: [...state.rewardTrace, r], |
| cumReward: state.cumReward + r, |
| error: null, |
| } |
| } |
|
|
| case 'SET_SPEED': |
| return { ...state, speed: action.payload } |
|
|
| case 'TOGGLE_PAUSE': |
| return { ...state, paused: !state.paused } |
|
|
| case 'SET_PAUSED': |
| return { ...state, paused: action.payload } |
|
|
| case 'ERROR': |
| return { ...state, loading: false, error: action.payload, paused: true } |
|
|
| default: |
| return state |
| } |
| } |
|
|
| |
| export function useGameStore() { |
| const [state, dispatch] = useReducer(reducer, initialState) |
|
|
| const resetGame = useCallback(async (seed = 42) => { |
| dispatch({ type: 'RESET_START' }) |
| try { |
| const data = await apiReset(seed) |
| dispatch({ type: 'RESET_SUCCESS', payload: data }) |
| } catch (err) { |
| dispatch({ type: 'ERROR', payload: err.message }) |
| } |
| }, []) |
|
|
| const stepGame = useCallback(async (decision, pitch = '') => { |
| dispatch({ type: 'STEP_START' }) |
| try { |
| const data = await apiStep(decision, pitch) |
| dispatch({ type: 'STEP_SUCCESS', payload: data }) |
| if (data.done) dispatch({ type: 'SET_PAUSED', payload: true }) |
| return data |
| } catch (err) { |
| dispatch({ type: 'ERROR', payload: err.message }) |
| return null |
| } |
| }, []) |
|
|
| const setSpeed = useCallback((v) => dispatch({ type: 'SET_SPEED', payload: v }), []) |
| const togglePause = useCallback(() => dispatch({ type: 'TOGGLE_PAUSE' }), []) |
| const setPaused = useCallback((v) => dispatch({ type: 'SET_PAUSED', payload: v }), []) |
|
|
| return { state, resetGame, stepGame, setSpeed, togglePause, setPaused } |
| } |
|
|