How can we get next possible states? #2175
-
I understand that we can get next possible events from Another related question is how can we get next possible events/states in N steps. Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
You can get the next state in a running service via: // Pure method that does not update the state
service.nextState(event); // returns State So you can iterate through events: currentState.nextEvents.map(event => {
return service.nextState(event); // and/or add some payload
});
Using |
Beta Was this translation helpful? Give feedback.
-
Here is the solution for xstate v5: export function predictNextTurnAction(predictionStateMachine) {
if (predictionStateMachine) {
const currentState = predictionStateMachine.getSnapshot();
const [nextState] = transition(predictionStateMachine.logic, currentState, {
type: 'NEXT',
});
return nextState.value;
}
} If you want to do multiple step prediction: // where AIComponents & PendingDamage is an object contains state-machine and some other infomation
export function predictNextTurnAction(monster: AIComponents & PendingDamage) {
if (monster.stateMachine) {
const currentSnapshot = monster.stateMachine.getSnapshot();
let nextSnapshot;
if (monster.damagedLastTurn) {
[nextSnapshot] = transition(monster.stateMachine.logic, currentSnapshot, {
type: 'DAMAGE_TAKEN',
});
}
[nextSnapshot] = transition(monster.stateMachine.logic, nextSnapshot ?? currentSnapshot, {
type: 'NEXT',
});
return nextSnapshot.value;
}
} And here is my example state machine, if you are curious: const enemyMachineSetup = setup({
types: {
context: { heavyStrikeCounter: 0 } as { heavyStrikeCounter: number },
events: {} as AIPossibleEvents,
},
actions: {
gainShield: () => {
for (const enemy of activeEnemyQuery) {
ECS.world.addComponent(enemy, 'skillPrepareToExecute', 'sleep');
}
},
executeHeavyStrike: () => {
targetRandomPlayer();
for (const enemy of activeEnemyQuery) {
ECS.world.addComponent(enemy, 'skillPrepareToExecute', 'heavyStrike');
}
},
executeSoulDrain: () => {
targetAllPlayers();
for (const enemy of activeEnemyQuery) {
ECS.world.addComponent(enemy, 'skillPrepareToExecute', 'soulDrain');
}
},
incrementHeavyStrikeCounter: assign({
heavyStrikeCounter: ({ context }) => context.heavyStrikeCounter + 1,
}),
resetHeavyStrikeCounter: assign({
heavyStrikeCounter: () => 0,
}),
},
guards: {
hasStruckTwice: ({ context }) => {
return context.heavyStrikeCounter >= 2;
},
},
});
const enemyStateMachine = enemyMachineSetup.createMachine({
id: 'enemyAI',
initial: 'sleeping',
context: {
heavyStrikeCounter: 0,
},
states: {
sleeping: {
entry: 'gainShield',
on: { DAMAGE_TAKEN: 'awakened', NEXT: 'sleep' },
},
sleep: {
entry: ['gainShield'],
on: {
DAMAGE_TAKEN: 'awakened',
NEXT: 'awake',
},
},
awakened: {
on: { NEXT: 'heavyStrike' },
},
awake: {
on: { NEXT: 'heavyStrike' },
},
heavyStrike: {
entry: ['executeHeavyStrike', 'incrementHeavyStrikeCounter'],
on: {
NEXT: [
{ target: 'soulDrain', guard: 'hasStruckTwice' },
{ target: 'heavyStrike', actions: ['executeHeavyStrike', 'incrementHeavyStrikeCounter'] },
],
},
},
soulDrain: {
entry: ['executeSoulDrain', 'resetHeavyStrikeCounter'],
on: { NEXT: 'heavyStrike' },
},
},
});
export const enemyActor1 = createActor(enemyStateMachine).start();
const predicatedNextState: string = predictNextTurnAction(enemyActor1); |
Beta Was this translation helpful? Give feedback.
You can get the next state in a running service via:
So you can iterate through events:
Using
machine.transition(nextState, event)
as normal (recursively for N steps).