Skip to content

Rock-Paper-Scissors

Game state machine managing complete game flow from player selection through winner determination.

selectMovecomputerSelectMovejudgenextRoundgameOvernewGameWaitingForPlayerPlayerChoseJudgingRoundCompleteGameOver
You0
CPU0
Choose your move
WaitingForPlayer
import { createMachine, effect, eventApi, setup } from "matchina";
import { randomMove } from "./game-utils";
import { states } from "./states";
import { createStore } from "./store";
export function createRPSMachine() {
const store = createStore();
const baseMachine = createMachine(
states,
{
WaitingForPlayer: {
selectMove: "PlayerChose",
},
PlayerChose: {
computerSelectMove: "Judging",
},
Judging: {
judge: "RoundComplete",
},
RoundComplete: {
nextRound: "WaitingForPlayer",
gameOver: "GameOver",
},
GameOver: {
newGame: "WaitingForPlayer",
},
},
"WaitingForPlayer"
);
const machine = Object.assign(baseMachine, { store }, eventApi(baseMachine));
// bind machine to store
setup(machine)(
effect((ev) => {
ev.match(
{
selectMove: store.setPlayerMove,
computerSelectMove: () => {
store.setComputerMove(randomMove());
},
judge: () => {
store.judgeRound();
store.checkGameOver();
if (store.getState().gameWinner) {
machine.gameOver();
}
},
nextRound: store.clearRound,
newGame: store.reset,
},
false
);
})
);
return machine;
}
export type RPSMachine = ReturnType<typeof createRPSMachine>;

Turn-based games with distinct phases and rules. Interactive applications that need to enforce business logic. Scenarios where actions are only valid in certain states, or where complex conditional logic depends on current state.

State machines enforce game rules through transitions — preventing invalid actions by restricting available events per state. Each phase is explicit and independently testable.

import { createMachine, effect, eventApi, setup } from "matchina";
import { randomMove } from "./game-utils";
import { states } from "./states";
import { createStore } from "./store";
export function createRPSMachine() {
const store = createStore();
const baseMachine = createMachine(
states,
{
WaitingForPlayer: {
selectMove: "PlayerChose",
},
PlayerChose: {
computerSelectMove: "Judging",
},
Judging: {
judge: "RoundComplete",
},
RoundComplete: {
nextRound: "WaitingForPlayer",
gameOver: "GameOver",
},
GameOver: {
newGame: "WaitingForPlayer",
},
},
"WaitingForPlayer"
);
const machine = Object.assign(baseMachine, { store }, eventApi(baseMachine));
// bind machine to store
setup(machine)(
effect((ev) => {
ev.match(
{
selectMove: store.setPlayerMove,
computerSelectMove: () => {
store.setComputerMove(randomMove());
},
judge: () => {
store.judgeRound();
store.checkGameOver();
if (store.getState().gameWinner) {
machine.gameOver();
}
},
nextRound: store.clearRound,
newGame: store.reset,
},
false
);
})
);
return machine;
}
export type RPSMachine = ReturnType<typeof createRPSMachine>;