Skip to content

Guess the number

🏷️ state data
🏷️ specific state + event transition
🏷️ conditional transitions
🏷️ immediate (always) transition

About

Models the “Guess the random number from 1 to 10” game, showing state-data and conditional transitions.

Try it

Guess the number!

Definition

guessMachine.ts
import { defineMachine } from "yay-machine";
interface GuessState {
readonly name:
| "pickNumber"
| "playing"
| "guessedCorrectly"
| "tooManyIncorrectGuesses";
readonly answer: number;
readonly numGuesses: number;
readonly maxGuesses: number;
}
interface GuessEvent {
readonly type: "GUESS";
readonly guess: number;
}
interface NewGameEvent {
readonly type: "NEW_GAME";
}
const incrementNumGuesses = ({
state,
}: {
readonly state: GuessState;
}): GuessState => ({
...state,
numGuesses: state.numGuesses + 1,
});
/**
* Guess the number from 1 to 10
*/
export const guessMachine = defineMachine<
GuessState,
GuessEvent | NewGameEvent
>({
initialState: { name: "pickNumber", answer: 0, numGuesses: 0, maxGuesses: 5 },
states: {
pickNumber: {
always: {
to: "playing",
data: ({ state }) => ({
...state,
answer: Math.ceil(Math.random() * 10),
numGuesses: 0,
}),
},
},
playing: {
on: {
GUESS: [
{
to: "guessedCorrectly",
when: ({ state, event }) => state.answer === event.guess,
data: incrementNumGuesses,
},
{
to: "tooManyIncorrectGuesses",
when: ({ state }) => state.numGuesses + 1 === state.maxGuesses,
data: incrementNumGuesses,
},
{
to: "playing",
data: incrementNumGuesses,
},
],
},
},
guessedCorrectly: {
on: {
NEW_GAME: { to: "pickNumber", data: ({ state }) => state },
},
},
tooManyIncorrectGuesses: {
on: {
NEW_GAME: { to: "pickNumber", data: ({ state }) => state },
},
},
},
});

Usage

import assert from "assert";
import { guessMachine } from "./guessMachine";
const guess = guessMachine.newInstance().start();
while (guess.state.name === "playing") {
guess.send({ type: "GUESS", guess: Math.ceil(Math.random() * 10) });
}
if (guess.state.name === "guessedCorrectly") {
console.log("yay, we won :)");
} else if (guess.state.name === "tooManyIncorrectGuesses") {
console.log("boo, we lost :(");
} else {
assert.fail(`Invalid state: ${guess.state.name}`);
}