This is a more flexible system than the previous goal variable. It makes it possible to conform to the association rules, without sacrificing backwards compatibility. Also, it makes it easier to add other changeable rules later on.
454 lines
14 KiB
JavaScript
454 lines
14 KiB
JavaScript
"use strict";
|
|
|
|
import { Round, Team } from "/models/round.js";
|
|
import RoundResult from "/models/round_result.js";
|
|
import Game from "/models/game.js";
|
|
import GameRules, { RaisingRule } from "./game_rules.js";
|
|
|
|
export default function() {
|
|
QUnit.module("game", function() {
|
|
QUnit.test("default construction", function(assert) {
|
|
let game = new Game();
|
|
assert.strictEqual(game.rounds.length, 0, "no past rounds");
|
|
assert.strictEqual(game.rules.goal, 11, "default goal");
|
|
assert.strictEqual(
|
|
game.rules.raising, RaisingRule.UnlessStricken, "default raising rule");
|
|
assert.notStrictEqual(game.currentRound, null, "current round there");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: null,
|
|
points: 0,
|
|
ourPoints: 0,
|
|
theirPoints: 0
|
|
},
|
|
"initial results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("invalid constructor", function(assert) {
|
|
assert.throws(
|
|
function() {new Game("nope", "absolutely", "not"); },
|
|
new TypeError("unknown form of Game constructor"));
|
|
});
|
|
|
|
QUnit.test("with non-default rules", function(assert) {
|
|
let rules = new GameRules();
|
|
rules.goal = 15;
|
|
rules.raising = RaisingRule.UntilEnough;
|
|
let game = new Game(rules);
|
|
assert.strictEqual(game.rounds.length, 0, "no past rounds");
|
|
assert.equal(game.rules.goal, 15, "higher goal");
|
|
assert.equal(game.rules.raising, RaisingRule.UntilEnough, "raising rule");
|
|
assert.notStrictEqual(game.currentRound, null, "current round there");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: null,
|
|
points: 0,
|
|
ourPoints: 0,
|
|
theirPoints: 0
|
|
},
|
|
"initial results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("single round played", function(assert) {
|
|
let game = new Game();
|
|
game.currentRound.winner = Team.We;
|
|
|
|
assert.equal(game.rounds.length, 1, "one round played");
|
|
assert.false(game.decided, "game is not decided");
|
|
assert.strictEqual(game.rounds[0].points, 2, "first round points");
|
|
assert.strictEqual(game.rounds[0].winner, Team.We, "first round winner");
|
|
assert.notStrictEqual(game.currentRound, null, "current round there");
|
|
assert.false(game.currentRound.decided, "current round is not decided");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: null,
|
|
points: 0,
|
|
ourPoints: 2,
|
|
theirPoints: 0
|
|
},
|
|
"intermediate results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("two rounds played", function(assert) {
|
|
let game = new Game();
|
|
game.currentRound.winner = Team.We;
|
|
game.currentRound.raise(Team.We);
|
|
game.currentRound.winner = Team.They;
|
|
|
|
assert.equal(game.rounds.length, 2, "two round played");
|
|
assert.false(game.decided, "game is not decided");
|
|
assert.strictEqual(game.rounds[1].points, 3, "second round points");
|
|
assert.strictEqual(
|
|
game.rounds[1].winner, Team.They, "second round winner");
|
|
assert.notStrictEqual(game.currentRound, null, "current round there");
|
|
assert.false(game.currentRound.decided, "current round is not decided");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: null,
|
|
points: 0,
|
|
ourPoints: 2,
|
|
theirPoints: 3
|
|
},
|
|
"intermediate results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("regular victory", function(assert) {
|
|
let game = new Game();
|
|
game.currentRound.winner = Team.We; // 2
|
|
game.currentRound.winner = Team.They; // 2
|
|
game.currentRound.winner = Team.We; // 4
|
|
game.currentRound.winner = Team.We; // 6
|
|
game.currentRound.winner = Team.We; // 8
|
|
game.currentRound.winner = Team.We; // 10
|
|
game.currentRound.winner = Team.We; // 12
|
|
|
|
assert.equal(game.rounds.length, 7, "seven rounds played");
|
|
assert.strictEqual(game.currentRound, null, "no further rounds");
|
|
assert.true(game.decided, "game is decided");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: Team.We,
|
|
points: 1,
|
|
ourPoints: 12,
|
|
theirPoints: 2,
|
|
},
|
|
"final results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("regular victory after near tailor", function(assert) {
|
|
let game = new Game();
|
|
game.currentRound.winner = Team.We; // 2
|
|
game.currentRound.winner = Team.We; // 4
|
|
game.currentRound.winner = Team.We; // 6
|
|
game.currentRound.winner = Team.We; // 8
|
|
game.currentRound.winner = Team.We; // 10
|
|
game.currentRound.winner = Team.They; // 2
|
|
game.currentRound.winner = Team.We; // 12
|
|
|
|
assert.equal(game.rounds.length, 7, "seven rounds played");
|
|
assert.true(game.decided, "no further rounds");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: Team.We,
|
|
points: 1,
|
|
ourPoints: 12,
|
|
theirPoints: 2,
|
|
},
|
|
"final results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("tailor victory", function(assert) {
|
|
let game = new Game();
|
|
game.currentRound.winner = Team.They; // 2
|
|
game.currentRound.winner = Team.They; // 4
|
|
game.currentRound.winner = Team.They; // 6
|
|
game.currentRound.winner = Team.They; // 8
|
|
game.currentRound.winner = Team.They; // 10
|
|
game.currentRound.winner = Team.They; // 12
|
|
|
|
assert.equal(game.rounds.length, 6, "seven rounds played");
|
|
assert.strictEqual(game.currentRound, null, "no further rounds");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: Team.They,
|
|
points: 2,
|
|
ourPoints: 0,
|
|
theirPoints: 12,
|
|
},
|
|
"final results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("reverse tailor victory", function(assert) {
|
|
let game = new Game();
|
|
game.currentRound.winner = Team.We; // 2
|
|
game.currentRound.winner = Team.We; // 4
|
|
game.currentRound.winner = Team.We; // 6
|
|
game.currentRound.winner = Team.We; // 8
|
|
game.currentRound.winner = Team.We; // 10
|
|
game.currentRound.winner = Team.They; // 2
|
|
game.currentRound.winner = Team.They; // 4
|
|
game.currentRound.winner = Team.They; // 6
|
|
game.currentRound.winner = Team.They; // 8
|
|
game.currentRound.winner = Team.They; // 10
|
|
game.currentRound.winner = Team.They; // 12
|
|
|
|
assert.equal(game.rounds.length, 11, "eleven rounds played");
|
|
assert.strictEqual(game.currentRound, null, "no further rounds");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: Team.They,
|
|
points: 4,
|
|
ourPoints: 10,
|
|
theirPoints: 12,
|
|
},
|
|
"final results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("reverse tailor victory with low goal", function(assert) {
|
|
let rules = new GameRules();
|
|
rules.goal = 3;
|
|
let game = new Game(rules);
|
|
game.currentRound.winner = Team.They; // 2
|
|
game.currentRound.winner = Team.We; // 2
|
|
game.currentRound.winner = Team.We; // 4
|
|
|
|
assert.equal(game.rounds.length, 3, "three rounds played");
|
|
assert.strictEqual(game.currentRound, null, "no further rounds");
|
|
assert.deepEqual(
|
|
game.result,
|
|
{
|
|
winner: Team.We,
|
|
points: 4,
|
|
ourPoints: 4,
|
|
theirPoints: 2,
|
|
},
|
|
"final results",
|
|
);
|
|
});
|
|
|
|
QUnit.test("raising rules effect - unless stricken", function(assert) {
|
|
let rules = new GameRules();
|
|
rules.goal = 3;
|
|
rules.raising = RaisingRule.UnlessStricken;
|
|
|
|
let game = new Game(rules);
|
|
game.currentRound.raise(Team.We);
|
|
game.currentRound.raise(Team.They);
|
|
assert.notStrictEqual(game.currentRound, null, "round still going");
|
|
|
|
game = new Game(rules);
|
|
game.currentRound.winner = Team.We;
|
|
game.currentRound.raise(Team.They);
|
|
assert.notStrictEqual(game.currentRound, null, "round still going");
|
|
game.currentRound.raise(Team.We);
|
|
assert.strictEqual(game.currentRound, null, "round ended");
|
|
});
|
|
|
|
QUnit.test("raising rules effect - until enough", function(assert) {
|
|
let rules = new GameRules();
|
|
rules.goal = 3;
|
|
rules.raising = RaisingRule.UntilEnough;
|
|
|
|
let game = new Game(rules);
|
|
game.currentRound.raise(Team.We);
|
|
game.currentRound.raise(Team.They);
|
|
assert.strictEqual(game.currentRound, null, "round ended");
|
|
});
|
|
|
|
QUnit.test("round change triggers event", function(assert) {
|
|
let rules = new GameRules();
|
|
rules.goal = 3;
|
|
let game = new Game();
|
|
game.addEventListener(Game.EVENT_CHANGE, function() {
|
|
assert.step("event");
|
|
});
|
|
game.currentRound.raise(Team.We);
|
|
game.currentRound.winner = Team.They;
|
|
assert.verifySteps(["event", "event"], "events were triggered");
|
|
});
|
|
|
|
QUnit.test("toStruct - unfinished", function(assert) {
|
|
let game = new Game();
|
|
game.currentRound.winner = Team.We;
|
|
game.currentRound.raise(Team.They);
|
|
game.currentRound.winner = Team.They;
|
|
game.currentRound.raise(Team.We);
|
|
let struct = game.toStruct();
|
|
|
|
let expected = {
|
|
rules: game.rules.toStruct(),
|
|
rounds: game.rounds.map((r) => r.toStruct()),
|
|
currentRound: game.currentRound.toStruct(),
|
|
};
|
|
|
|
assert.deepEqual(struct, expected, "successfull structurizing");
|
|
});
|
|
|
|
QUnit.test("toStruct - finished", function(assert) {
|
|
let rules = new GameRules();
|
|
rules.goal = 3;
|
|
let game = new Game(rules);
|
|
game.currentRound.winner = Team.We;
|
|
game.currentRound.raise(Team.They);
|
|
game.currentRound.winner = Team.They;
|
|
let struct = game.toStruct();
|
|
|
|
let expected = {
|
|
rules: game.rules.toStruct(),
|
|
rounds: game.rounds.map((r) => r.toStruct()),
|
|
currentRound: null,
|
|
};
|
|
|
|
assert.deepEqual(struct, expected, "successfull structurizing");
|
|
});
|
|
|
|
QUnit.test("fromStruct - current", function(assert) {
|
|
let rules = new GameRules();
|
|
rules.goal = 4;
|
|
let orig = new Game(rules);
|
|
orig.currentRound.raise(Team.We);
|
|
|
|
let copy = new Game(orig.toStruct());
|
|
assert.deepEqual(copy.rules, orig.rules, "rules match");
|
|
assert.strictEqual(
|
|
copy.currentRound.points,
|
|
orig.currentRound.points,
|
|
"current points match");
|
|
assert.strictEqual(
|
|
copy.rounds.length, orig.rounds.length, "rounds match");
|
|
|
|
orig.currentRound.winner = Team.We;
|
|
copy = new Game(orig.toStruct());
|
|
assert.strictEqual(
|
|
copy.rounds.length, orig.rounds.length, "rounds match");
|
|
assert.deepEqual(
|
|
copy.rounds[0].toStruct(), orig.rounds[0].toStruct(), "round matches");
|
|
|
|
orig.currentRound.winner = Team.We;
|
|
copy = new Game(orig.toStruct());
|
|
assert.deepEqual(copy.result, orig.result, "results match");
|
|
});
|
|
|
|
QUnit.test("fromStruct - invalid", function(assert) {
|
|
let struct = {};
|
|
function doIt(message, error) {
|
|
assert.throws(function() { new Game(struct); }, error, message);
|
|
}
|
|
|
|
doIt(
|
|
"no goal", new TypeError("struct must contain either rules or goal"));
|
|
struct.goal = "3";
|
|
doIt(
|
|
"string goal",
|
|
new TypeError("if struct contains goal, it must be a number"));
|
|
struct.goal = Math.PI;
|
|
doIt(
|
|
"non-int goal",
|
|
new RangeError("if struct contains goal, must be integer >= 1"));
|
|
struct.goal = 0;
|
|
doIt(
|
|
"small goal",
|
|
new RangeError("if struct contains goal, must be integer >= 1"));
|
|
struct.goal = 3;
|
|
|
|
struct.rules = "";
|
|
doIt(
|
|
"rules and goal",
|
|
new TypeError("struct cannot contain both rules and goal"));
|
|
delete struct.goal;
|
|
doIt(
|
|
"string rules",
|
|
new TypeError("if struct contains rules, they must be an object"));
|
|
let rules = new GameRules();
|
|
rules.goal = 3;
|
|
struct.rules = rules;
|
|
|
|
doIt("no rounds", new TypeError("struct must contain rounds"));
|
|
struct.rounds = "nope";
|
|
doIt(
|
|
"rounds not array",
|
|
new TypeError("struct must contain rounds as array"));
|
|
struct.rounds = ["nope", "again"];
|
|
doIt(
|
|
"string array rounds",
|
|
new TypeError("unknown form of RoundResult constructor"));
|
|
struct.rounds = [];
|
|
|
|
doIt(
|
|
"no currentRound",
|
|
new TypeError("struct must contain currentRound as object"));
|
|
struct.currentRound = "nope";
|
|
doIt(
|
|
"string currentRound",
|
|
new TypeError("struct must contain currentRound as object"));
|
|
struct.currentRound = null;
|
|
doIt(
|
|
"missing currentRound",
|
|
new Error("struct of ongoing game must contain current round"));
|
|
struct.currentRound = new Round().toStruct();
|
|
new Game(struct);
|
|
|
|
struct.rounds = [ new RoundResult(3, Team.They).toStruct() ];
|
|
doIt(
|
|
"unneeded currentRound",
|
|
new Error("struct of finished game must not contain current round"));
|
|
struct.currentRound = null;
|
|
new Game(struct);
|
|
});
|
|
|
|
// data for the version import tests
|
|
let round1 = new RoundResult(2, Team.We);
|
|
let round2 = new RoundResult(3, Team.They);
|
|
let current = new Round(2, 3);
|
|
let rules = new GameRules();
|
|
rules.goal = 3;
|
|
rules.raising = RaisingRule.UntilEnough;
|
|
|
|
QUnit.test.each(
|
|
"fromStruct - unfinished",
|
|
{
|
|
v1: {
|
|
goal: 3,
|
|
rounds: [ round1.toStruct() ],
|
|
currentRound: current.toStruct(),
|
|
},
|
|
v2: {
|
|
rules: rules.toStruct(),
|
|
rounds: [ round1.toStruct() ],
|
|
currentRound: current.toStruct(),
|
|
},
|
|
},
|
|
function(assert, input) {
|
|
let game = new Game(input);
|
|
let expeted = {
|
|
rules: rules.toStruct(),
|
|
rounds: [ round1.toStruct() ],
|
|
currentRound: current.toStruct(),
|
|
};
|
|
assert.deepEqual(game.toStruct(), expeted, "reexport matches");
|
|
}
|
|
);
|
|
|
|
QUnit.test.each(
|
|
"fromStruct - finished",
|
|
{
|
|
v1: {
|
|
goal: 3,
|
|
rounds: [ round1.toStruct(), round2.toStruct() ],
|
|
currentRound: null,
|
|
},
|
|
v2: {
|
|
rules: rules.toStruct(),
|
|
rounds: [ round1.toStruct(), round2.toStruct() ],
|
|
currentRound: null,
|
|
},
|
|
},
|
|
function(assert, input) {
|
|
let game = new Game(input);
|
|
let expeted = {
|
|
rules: rules.toStruct(),
|
|
rounds: [ round1.toStruct(), round2.toStruct() ],
|
|
currentRound: null,
|
|
};
|
|
assert.deepEqual(game.toStruct(), expeted, "reexport matches");
|
|
}
|
|
);
|
|
});
|
|
}
|