1
0

implement model for sessions of multiple games

This commit is contained in:
Adrian Wannenmacher 2026-02-09 23:16:23 +01:00
parent 6971a2b20c
commit 220a9b40e4
Signed by: tfld
GPG Key ID: 19D986ECB1E492D5
3 changed files with 381 additions and 0 deletions

138
models/session.js Normal file
View File

@ -0,0 +1,138 @@
"use strict";
import Game from "./game.js";
import { Team } from "./round.js";
export default class Session {
/** The amout of points at which individual games are won.
*
* Only applies to new games.
*/
goal = 11;
/** The name or members of the "we" team. */
ourTeam = "";
/** The name or members of the "they" team. */
theirTeam = "";
/** The finished games.
* @type {Game[]}
*/
#games = [];
/** Get the finished games.
*
* DO NOT write to the returned object.
*/
get games() {
return this.#games;
}
/** The currently played game.
* @type {?Game}
*/
#currentGame = null;
/** Get the currently played game. */
get currentGame() {
return this.#currentGame;
}
/** Add another round if there is no current one. */
anotherGame() {
if (this.#currentGame === null) {
this.#currentGame = new Game(this.goal);
this.#currentGame.addEventListener(
Game.finishedEvent, this.#boundGameFinishedHandler);
}
}
/** Get the current amouts of points.
*
* Note that on this level points are a punishment.
*/
get result() {
let ourPoints = 0;
let theirPoints = 0;
for (let g of this.#games) {
let r = g.result;
if (r.winner === Team.We) {
theirPoints += r.points;
} else if (r.winner === Team.They) {
ourPoints += r.points;
}
}
return { ourPoints, theirPoints };
}
/** Handle it when the current game is finished. */
#gameFinishedHandler() {
this.#currentGame.removeEventListener(
Game.finishedEvent, this.#boundGameFinishedHandler);
this.#games.push(this.#currentGame);
this.#currentGame = null;
}
#boundGameFinishedHandler = this.#gameFinishedHandler.bind(this);
constructor(value) {
if (value === undefined) {
this.anotherGame();
} else if (typeof value === "object") {
if (!("goal" in value))
throw new TypeError("missing goal in deserialization object");
if (typeof value.goal !== "number")
throw new TypeError("goal in deserialization object must be number");
this.goal = value.goal;
if (!("ourTeam" in value))
throw new TypeError("missing ourTeam in deserialization object");
if (typeof value.ourTeam !== "string")
throw new TypeError(
"ourTeam in deserialization object must be string");
this.ourTeam = value.ourTeam;
if (!("theirTeam" in value))
throw new TypeError("missing theirTeam in deserialization object");
if (typeof value.theirTeam !== "string")
throw new TypeError(
"theirTeam in deserialization object must be string");
this.theirTeam = value.theirTeam;
if (!("games" in value))
throw new TypeError("missing games in deserialization object");
if (!Array.isArray(value.games))
throw new TypeError("games in deserialization object must be array");
for (let g of value.games) {
let game = new Game (g);
if (game.result.winner === null)
throw new TypeError("past game cannot be unfinished");
this.#games.push(game);
}
if (!("currentGame" in value))
throw new TypeError("missing currentGame in deserialization object");
if (value.currentGame !== null) {
this.#currentGame = new Game(value.currentGame);
if (this.#currentGame.result.winner !== null)
throw new Error("currentGame cannot be finished");
}
} else {
throw new TypeError("unknown form of Session constructor");
}
}
/** Export needed data for JSON serialization. */
toJSON() {
return {
goal: this.goal,
ourTeam: this.ourTeam,
theirTeam: this.theirTeam,
games: this.#games,
currentGame: this.#currentGame,
}
}
}

242
models/session.test.js Normal file
View File

@ -0,0 +1,242 @@
"use strict";
import { Round, Team } from "./round.js";
import Game from "./game.js";
import Session from "./session.js";
QUnit.module("models", function() {
QUnit.module("session", function() {
QUnit.test("initial state", function(assert) {
let session = new Session();
assert.strictEqual(session.goal, 11, "initial goal");
assert.strictEqual(session.games.length, 0, "no finished games");
assert.notStrictEqual(session.currentGame, null, "game in progress");
assert.deepEqual(
session.result,
{ ourPoints: 0, theirPoints: 0 },
"initially no points");
assert.strictEqual(session.ourTeam, "", "our team name");
assert.strictEqual(session.theirTeam, "", "their team name");
});
QUnit.test("single game finished", function(assert) {
let session = new Session();
session.currentGame.currentRound.winner = Team.We;
for (let i = 0; i < session.goal; i += 2)
session.currentGame.currentRound.winner = Team.They;
assert.strictEqual(session.games.length, 1, "single game");
assert.deepEqual(
session.games[0].result,
{
winner: Team.They,
points: 1,
ourPoints: 2,
theirPoints: 12,
});
assert.strictEqual(session.currentGame, null, "no game in progress");
assert.deepEqual(
session.result,
{ ourPoints: 1, theirPoints: 0 },
"one point for losing team");
});
QUnit.test("two games finished", function(assert) {
let session = new Session();
session.currentGame.currentRound.winner = Team.We;
for (let i = 0; i < session.goal; i += 2)
session.currentGame.currentRound.winner = Team.They;
session.anotherGame();
for (let i = 0; i < session.goal; i += 2)
session.currentGame.currentRound.winner = Team.We;
assert.strictEqual(session.games.length, 2, "two games")
assert.deepEqual(
session.games[1].result,
{
winner: Team.We,
points: 2,
ourPoints: 12,
theirPoints: 0,
});
assert.strictEqual(session.currentGame, null, "no game in progress");
assert.deepEqual(
session.result,
{ ourPoints: 1, theirPoints: 2 },
"one point for losing team");
});
QUnit.test("new game doesn't overwrite existing", function(assert) {
let session = new Session();
session.currentGame.currentRound.winner = Team.We;
assert.notStrictEqual(session.currentGame, null, "ongoing game");
session.anotherGame();
assert.deepEqual(
session.currentGame.result,
{
winner: null,
points: 0,
ourPoints: 2,
theirPoints: 0,
},
"initial game still current");
});
QUnit.test("serialization - new session", function(assert) {
let session = new Session();
let json = session.toJSON();
json.currentGame = session.currentGame.toJSON();
assert.deepEqual(
json,
{
goal: 11,
ourTeam: "",
theirTeam: "",
games: [],
currentGame: session.currentGame.toJSON()
},
"correct serialization");
});
QUnit.test("serialization - finished & unfinished game", function(assert) {
let session = new Session();
session.currentGame.currentRound.winner = Team.We;
for (
let i = 0;
session.currentGame !== null && i < session.currentGame.goal;
i += 2
)
session.currentGame.currentRound.winner = Team.They;
session.goal = 15;
session.anotherGame();
session.currentGame.currentRound.winner = Team.They;
for (
let i = 0;
session.currentGame !== null && i < session.currentGame.goal - 2;
i += 2
)
session.currentGame.currentRound.winner = Team.We;
session.goal = 5;
session.ourTeam = "This is us!";
session.theirTeam = "This is them!";
let json = session.toJSON();
json.games = [];
for (let i = 0; i < session.games.length; i++)
json.games.push(session.games[i].toJSON());
json.currentGame = session.currentGame.toJSON();
assert.deepEqual(
json,
{
goal: 5,
ourTeam: "This is us!",
theirTeam: "This is them!",
games: [
session.games[0].toJSON(),
],
currentGame: session.currentGame.toJSON(),
},
"correct serialization");
assert.strictEqual(json.games[0].goal, 11, "first goal");
assert.strictEqual(json.currentGame.goal, 15, "second goal");
});
QUnit.test("deserialization - new session", function(assert) {
let game = new Game();
let json = {
goal: 11,
ourTeam: "",
theirTeam: "",
games: [],
currentGame: game.toJSON(),
};
json.currentGame.currentRound = game.currentRound.toJSON();
let session = new Session(json);
assert.strictEqual(session.goal, 11, "goal");
assert.strictEqual(session.ourTeam, "", "our team name");
assert.strictEqual(session.theirTeam, "", "their team name");
assert.strictEqual(session.games.length, 0, "no past games");
assert.deepEqual(session.currentGame.toJSON(), game.toJSON());
});
QUnit.test("deserialization - un- and finished games", function(assert) {
let finished = new Game(2);
finished.currentRound.winner = Team.We;
let unfinished = new Game(3);
unfinished.currentRound.winner = Team.They;
let json = {
goal: 4,
ourTeam: "This is us!",
theirTeam: "This is them!",
games: [finished],
currentGame: unfinished,
};
let deso = JSON.parse(JSON.stringify(json));
let session = new Session(deso);
assert.strictEqual(session.goal, 4, "goal");
assert.strictEqual(session.ourTeam, "This is us!", "our team name");
assert.strictEqual(session.theirTeam, "This is them!", "their team");
assert.strictEqual(session.games.length, 1, "one past game");
assert.deepEqual(
session.games[0].toJSON(), finished.toJSON(), "finished game");
assert.notStrictEqual(session.currentGame, null, "unfinished game here");
assert.deepEqual(
session.currentGame.toJSON(), unfinished.toJSON(), "unfinished game");
});
QUnit.test("deserialization - invalid", function(assert) {
let deso = {};
assert.throws(function() { new Session(deso); }, "no goal");
deso.goal = "11";
assert.throws(function() { new Session(deso); }, "string goal");
deso.goal = 11;
assert.throws(function() { new Session(deso); }, "no ourTeam");
deso.ourTeam = 11;
assert.throws(function() { new Session(deso); }, "number ourTeam");
deso.ourTeam = "";
assert.throws(function() { new Session(deso); }, "no theirTeam");
deso.theirTeam = 11;
assert.throws(function() { new Session(deso); }, "number theirTeam");
deso.theirTeam = "";
assert.throws(function() { new Session(deso); }, "no games");
deso.games = null;
assert.throws(function() { new Session(deso); }, "null games");
deso.games = [];
assert.throws(function() { new Session(deso); }, "no currentGame");
deso.currentGame = {
goal: 3,
rounds: [{ winner: Team.They, points: 3 }],
currentRound: null,
};
assert.throws(function() { new Session(deso); }, "finished currentGame");
deso.currentGame = null;
new Session(deso);
deso.games = [{
goal: 3,
rounds: [{ winner: Team.They, points: 2}],
currentRound: (new Round(3, 2)).toJSON(),
}];
assert.throws(function() { new Session(deso); }, "unfinished past");
});
});
});

View File

@ -14,6 +14,7 @@
<script src="models/round.test.js" type="module"></script>
<script src="models/round_result.test.js" type="module"></script>
<script src="models/game.test.js" type="module"></script>
<script src="models/session.test.js" type="module"></script>
</body>
</html>