implement model for sessions of multiple games
This commit is contained in:
parent
6971a2b20c
commit
220a9b40e4
138
models/session.js
Normal file
138
models/session.js
Normal 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
242
models/session.test.js
Normal 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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -14,6 +14,7 @@
|
|||||||
<script src="models/round.test.js" type="module"></script>
|
<script src="models/round.test.js" type="module"></script>
|
||||||
<script src="models/round_result.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/game.test.js" type="module"></script>
|
||||||
|
<script src="models/session.test.js" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Loading…
Reference in New Issue
Block a user