Skip to content

Commit

Permalink
WIP turn machine iterations
Browse files Browse the repository at this point in the history
- split turn machine into its own file
- start turn machine when starting round, then infinitely cycle until
  win conditions are met; set a question each time we enter the turn
  state and pass it down to the turn machine as input/context
- we're invoking the turn machine from the round machine rather than
  instantiating a Turn model, like we do with the Round machine. This
  means we're currently not getting the same kind of logging for the
  turn machine as other machines - we might want to revisit this later
- the turn machine currently isn't triggering the client to render the
  question or accept answers
- the round machine doesn't currently type check - it has an action that
  needs moving to the turn machine
- we might need to work out how to connect up with the work in
  `emit-timer-value-from-machine` which changes how the countdown works

Co-authored-by: Rich James <[email protected]>
  • Loading branch information
2 people authored and Gweaton committed May 8, 2024
1 parent fef9301 commit b5fc3b2
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 89 deletions.
109 changes: 21 additions & 88 deletions server/machines/round.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,14 @@
import { assign, createMachine, setup } from "xstate";
import type { Answer } from "../@types/entities";
import { assign, createMachine } from "xstate";
import type { Question } from "../@types/entities";
import questions from "../data/questions.json";
import { turnMachine } from "./turn";

type Question = {
answer: string[];
number: number;
question: string;
};

const turnContext = {
answers: [] as Answer[],
selectedQuestion: {} as Question | undefined,
};

const roundContext = {
questions: [] as Question[],
const context = {
questions: questions as Question[],
selectedQuestion: {} as Question | undefined,
};

type TurnContext = typeof turnContext;
type RoundContext = typeof roundContext;

type Event = {
type: "playerSubmitsAnswer";
answer: Answer;
};

const turnMachine = createMachine(
{
context: turnContext,
id: "turn",
initial: "turnStart",
types: {
context: {} as TurnContext,
events: {} as Event,
typegen: {} as import("./round.typegen").Typegen0,
},
states: {
turnStart: {
entry: ["setQuestion"],
on: {
playerSubmitsAnswer: { actions: "addAnswer", target: "countdown" },
},
},
countdown: {
on: {
playerSubmitsAnswer: { actions: "addAnswer" },
},
after: {
[15000]: { target: "finished" },
},
},
finished: {
type: "final",
},
},
},
{
actions: {
addAnswer: assign({
answers: (args) => [...args.context.answers, args.event.answer],
}),
},
},
);
type Context = typeof context;

// players are awarded one point for each person who guesses wrong plus any points from the bonus round
// show what answers every gave
Expand All @@ -75,46 +21,31 @@ const turnMachine = createMachine(
// if all answers are correct ++bonus points -> check win conditions -> next question
// if there are some correct and some incorrect answers add the number of incorrect answers (+ any bonus points - then reset bonus points) to the scores of the players with correct answers -> check win conditions -> next question

const roundMachine = setup({
actors: {
turn: turnMachine,
},
}).createMachine(
const roundMachine = createMachine(
{
context: roundContext,
context: context,
id: "round",
initial: "roundStart",
initial: "turn",
types: {
context: {} as RoundContext,
context: {} as Context,
events: {} as Event,
typegen: {} as import("./round.typegen").Typegen0,
// typegen: {} as import("./round.typegen").Typegen0,
},
states: {
roundStart: {
entry: ["setQuestion"],
turn: {
entry: ["setQuestion"], // keep track of which questions have been asked in round and/or entire game/lobby
invoke: {
id: "turn",
src: "turn",
src: turnMachine,
input: ({ context }) => ({
selectedQuestion: context.selectedQuestion,
}),
onDone: {
target: "success",
actions: assign({ user: ({ event }) => event.output }),
target: "turn",
actions: "processTurn", // need to receive answers from turn machine
},
onError: {
target: "failure",
actions: assign({ error: ({ event }) => event.error }),
},
},
},
countdown: {
on: {
playerSubmitsAnswer: { actions: "addAnswer" },
},
after: {
[15000]: { target: "finished" },
},
// guard: if win conditions, finished
},
finished: {
type: "final",
Expand All @@ -128,11 +59,13 @@ const roundMachine = setup({
const questionIndex = Math.floor(
Math.random() * (args.context.questions.length - 1),
);
// we can splice the selected question out of the questions array with args.context.questions.splice[questionIndex, 1]
return args.context.questions[questionIndex];
},
}),
processTurn: () => console.log("processing turn"),
},
},
);

export { turnContext as context, turnMachine as roundMachine };
export { context, roundMachine };
55 changes: 55 additions & 0 deletions server/machines/turn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { assign, createMachine } from "xstate";
import type { Answer, Question } from "../@types/entities";

const context = {
answers: [] as Answer[],
selectedQuestion: {} as Question | undefined,
};

type Context = typeof context;

type Event = {
type: "playerSubmitsAnswer";
answer: Answer;
};

const turnMachine = createMachine(
{
context: context,
id: "turn",
initial: "turnStart",
types: {
context: {} as Context,
events: {} as Event,
typegen: {} as import("./round.typegen").Typegen0,
},
states: {
turnStart: {
on: {
playerSubmitsAnswer: { actions: "addAnswer", target: "countdown" },
},
},
countdown: {
on: {
playerSubmitsAnswer: { actions: "addAnswer" },
},
after: {
[15000]: { target: "finished" },
},
},
finished: {
type: "final",
// pass answers back to round machine
},
},
},
{
actions: {
addAnswer: assign({
answers: (args) => [...args.context.answers, args.event.answer],
}),
},
},
);

export { context, turnMachine };
2 changes: 1 addition & 1 deletion server/models/round.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Round {
});

switch (state.value) {
case "roundStart": {
case "turn": {
this.server.onQuestionSet(
this.machine.getSnapshot().context.selectedQuestion as Question,
);
Expand Down

0 comments on commit b5fc3b2

Please sign in to comment.