Skip to content

Commit

Permalink
Merge pull request #50 from dxw/start-game
Browse files Browse the repository at this point in the history
Start a new game
  • Loading branch information
richpjames authored Jan 22, 2024
2 parents e537d8b + 9c124f4 commit b46c043
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 175 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: oven-sh/setup-bun@v1
- uses: oven-sh/setup-bun@v1.1.1
with:
bun-version: 1.0.3
- name: Checkout repository
Expand Down
9 changes: 7 additions & 2 deletions client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,16 @@ const derenderNameForm = (): void => {
getElementById("name-form").remove();
};

const startButton = getElementById("start-button");

const showStartButton = (): void => {
const button = getElementById("start-button");
button.style.display = "block";
startButton.style.display = "block";
};

startButton.addEventListener("click", () => {
socket.emit("round:start");
});

const connectionStatusIconElement = getElementById("connection-status-icon");
const nameFormElement = getElementById("name-form") as NameFormElement;
const playerListElement = getElementById("player-list");
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
"license": "MIT",
"scripts": {
"compile": "bun build ./client/index.ts --outdir ./client/assets/scripts",
"prettier:format": "bunx prettier . --write",
"prettier:check": "bunx prettier . --check",
"lint": "eslint ./server --cache --max-warnings 0 --ignore-pattern node_modules",
"lint:fix": "eslint ./server --cache --max-warnings 0 --fix",
"lint:css": "bunx stylelint \"client/assets/styles/*.css\"",
"lint:css:fix": "bunx stylelint \"client/assets/styles/*.css\" --fix",
"start": "bun run compile && bun server/index.ts",
"start": "bun run compile && bun server/index.ts --watch",
"typecheck": "bun run tsc",
"test:unit": "bun test server",
"test:integration:cli": "bunx playwright test",
Expand Down
4 changes: 0 additions & 4 deletions script/test
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ echo "==> Linting the code"

bun run lint:css

echo "==> Formatting the code"

bun run prettier:check

echo "==> Typechecking the code"

bun run typecheck
Expand Down
10 changes: 6 additions & 4 deletions server/events/outbound.ts → server/events/clientbound.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Player, Question } from "../@types/models";
import Game from "../game";
import Lobby from "../lobby";

export default class OutboundEvents {
static getPlayers(game: Game): [string, { players: Array<Player["name"]> }] {
return ["players:get", { players: game.playerNames() }];
export default class ClientboundEvents {
static getPlayers(
lobby: Lobby,
): [string, { players: Array<Player["name"]> }] {
return ["players:get", { players: lobby.playerNames() }];
}

static getQuestion(question: Question): [string, { question: Question }] {
Expand Down
35 changes: 0 additions & 35 deletions server/events/incoming.ts

This file was deleted.

45 changes: 45 additions & 0 deletions server/events/severbound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Server, Socket } from "socket.io";
import Lobby from "../lobby";
import ClientboundEvents from "./clientbound";
import { SocketServer } from "../socketServer";

export default class ServerboundEvents {
static disconnect(
lobby: Lobby,
socket: Socket,
server: Server,
): [string, () => void] {
return [
"disconnect",
() => {
console.info(`disconnected: ${socket.id}`);
lobby.removePlayer(socket.id);
server.emit(...ClientboundEvents.getPlayers(lobby));
},
];
}

static postPlayers(
lobby: Lobby,
socket: Socket,
server: Server,
): [string, (data: { name: string }) => void] {
return [
"players:post",
(data: { name: string }) => {
const player = lobby.addPlayer(data.name, socket.id);
socket.emit(...ClientboundEvents.setPlayer(player));
server.emit(...ClientboundEvents.getPlayers(lobby));
},
];
}

static startRound(server: SocketServer): [string, () => void] {
return [
"round:start",
() => {
server.onRoundStarted();
},
];
}
}
19 changes: 2 additions & 17 deletions server/game.ts → server/lobby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,19 @@ import { Player } from "./@types/models";
import { SocketServer } from "./socketServer";
import { lobbyMachine, context, Context } from "./machines/lobby";
import { interpret } from "xstate";
import questions from "./data/questions.json";

export default class Game {
export default class Lobby {
server: SocketServer;
machine;

constructor(server: SocketServer) {
this.server = server;
this.machine = interpret(
lobbyMachine.withContext({ ...context, questions }),
).start();
this.machine = interpret(lobbyMachine.withContext({ ...context })).start();
this.machine.start();
this.machine.onTransition((state) => {
console.info({ state: state.value, context: state.context });

switch (state.value) {
case "GameStart":
this.emitQuestionSet(state.context.selectedQuestion);
break;
case "MultiplePlayers":
this.emitShowStartButton();
default:
Expand Down Expand Up @@ -59,16 +53,7 @@ export default class Game {
this.machine.send({ type: "playerLeaves", socketId });
};

start = (): void => {
this.machine.send({ type: "playerClicksStart" });
};

emitShowStartButton = (): void => {
this.server.onShowStartButton();
};

emitQuestionSet = (question: Context["selectedQuestion"]): void => {
if (!question) throw new Error("No question selected");
this.server.onQuestionSet(question);
};
}
70 changes: 0 additions & 70 deletions server/machines/lobby.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@ describe("lobbyMachine states", () => {
});
});

it("transitions from the MultiplePlayers to the GameStart state when sent playerClicksStart event", () => {
expect(
lobbyMachine.transition("MultiplePlayers", "playerClicksStart").value,
).toBe("GameStart");
});

it("transitions to OnePlayer if there is only one player left when playerLeaves", () => {
actor.send({ type: "playerLeaves", socketId: player1.socketId });
expect(actor.getSnapshot().value).toBe("OnePlayer");
Expand All @@ -126,70 +120,6 @@ describe("lobbyMachine states", () => {
expect(actor.getSnapshot().context.players).toEqual([player2]);
});
});

describe("GameStart", () => {
let actor: InterpreterFrom<typeof lobbyMachine>;

beforeEach(() => {
actor = interpret(lobbyMachine);
actor.start();

actor.send({
type: "playerJoins",
player: player1,
});

actor.send({
type: "playerJoins",
player: player2,
});
});

describe("given there are two players", () => {
beforeEach(() => {
actor.send({ type: "playerClicksStart" });
});

it("transitions from GameStart to OnePlayer when playerLeaves", () => {
actor.send({ type: "playerLeaves", socketId: player1.socketId });
expect(actor.getSnapshot().value).toBe("OnePlayer");
});

it("removes a player from the player list when it receives playerLeaves event", () => {
actor.send({
type: "playerLeaves",
socketId: "id",
});

expect(actor.getSnapshot().context.players).toEqual([player2]);
});
});

describe("given there are more than two players", () => {
beforeEach(() => {
actor.send({
type: "playerJoins",
player: player3,
});

actor.send({ type: "playerClicksStart" });
});

it("does not transition to OnePlayer if there is more than one player left when playerLeaves", () => {
actor.send({ type: "playerLeaves", socketId: player1.socketId });
expect(actor.getSnapshot().value).toBe("GameStart");
});

it("removes a player from the player list when it receives playerLeaves event", () => {
actor.send({
type: "playerLeaves",
socketId: "id",
});

expect(actor.getSnapshot().context.players).toEqual([player2, player3]);
});
});
});
});

describe("isNewPlayer", () => {
Expand Down
28 changes: 1 addition & 27 deletions server/machines/lobby.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import { createMachine, assign } from "xstate";
import { Player } from "../@types/models";

type Question = {
answer: Array<string>;
number: number;
question: string;
};

export const context = {
players: [] as Array<Player>,
questions: [] as Array<Question>,
selectedQuestion: {} as Question | undefined,
};

export type Context = typeof context;
Expand Down Expand Up @@ -40,6 +32,7 @@ export const isOnlyPlayer = ({ players }: { players: Array<Player> }) =>

export const lobbyMachine = createMachine(
{
/** @xstate-layout N4IgpgJg5mDOIC5QBsD2AjdBPAdAUQFsAHAFywGIjkBDLMAJwClUBLAO1gG0AGAXUVBFUsFiRao2AkAA9EARgAsAdhwAmJQGYFGgJwBWZUp0AOVQBoQWRKpM4lANg3cNxhXqVzuxuQF8fFtExcAHk2MAAFGjp6SiiGZnYuPikhETEJKVkEbR0cbQUdbjl7Ezk9VXt7CysEJzk1R2dXd09vPwCMbBxQiLiYqloGABkwagA3OB5+JBBU0XFJGazFFXUtXQMlI1Nq62ccPW4lU259IrlVBXaQQK6AWQBXZDEqXsH6WFj3gGFkFgBjADWsAAyiRqPQSFMUsJ5hkloh7MY9Dh7B5jMYlNpyuo5LsELicMYNCTlEVuPYKjprrdcI9nixXpF3p8BtERuNJskZnN0otQFklNxuGpMQ4lHpkaYNFVLPIHAdjhczjKNBcaZ06U8Xsg3tFWX0EhxoTzYXzMoghfYcDo0Qp7HJdLbkeY5QhvDgNKobHI5DpbY49HoNUEcPSdXqGJ8TYIzQsLQg9BoUUH7YU-a5iUp8aogzaFGtVM5lC0-P4QGxUBA4FJaTC0vGEQgALSymqtkNdQikGqxhvwgWIAv4tUaA72AuS+zOQpKVSdkJhZnRetw-kyRBOFEeU4KYlHSpuEfFHAUgu5hxaBRuK7l2lh7WM3XLqOr81NkoKHDFRQOjQePRHFdGovWtCdJSDCUg28akyyAA */
tsTypes: {} as import("./lobby.typegen").Typegen0,
schema: {
context: {} as Context,
Expand Down Expand Up @@ -72,21 +65,10 @@ export const lobbyMachine = createMachine(
cond: "isOnlyPlayer",
},
on: {
playerClicksStart: "GameStart",
playerLeaves: { actions: "removePlayer" },
playerJoins: { actions: "addPlayer" },
},
},
GameStart: {
entry: ["setQuestion"],
always: {
target: "OnePlayer",
cond: "isOnlyPlayer",
},
on: {
playerLeaves: { actions: "removePlayer" },
},
},
},
},
{
Expand All @@ -98,14 +80,6 @@ export const lobbyMachine = createMachine(
players: ({ players }, { socketId }) =>
players.filter((p) => p.socketId !== socketId),
}),
setQuestion: assign({
selectedQuestion: ({ questions }) => {
const questionIndex = Math.floor(
Math.random() * (questions.length - 1),
);
return questions[questionIndex];
},
}),
},
guards: {
isNewPlayer,
Expand Down
3 changes: 1 addition & 2 deletions server/machines/lobby.typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ export interface Typegen0 {
eventsCausingActions: {
addPlayer: "playerJoins";
removePlayer: "playerLeaves";
setQuestion: "playerClicksStart";
};
eventsCausingDelays: {};
eventsCausingGuards: {
isNewPlayer: "playerJoins";
isOnlyPlayer: "";
};
eventsCausingServices: {};
matchesStates: "Empty" | "GameStart" | "MultiplePlayers" | "OnePlayer";
matchesStates: "Empty" | "MultiplePlayers" | "OnePlayer";
tags: never;
}
Loading

0 comments on commit b46c043

Please sign in to comment.