mirror of
https://github.com/ENSL/ensl_gathers.git
synced 2024-11-22 20:51:23 +00:00
Implemented most of gather model
This commit is contained in:
parent
11b2ad7bd3
commit
703944a709
5 changed files with 457 additions and 20 deletions
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
* Client API
|
||||
* gather:join - Assigns user to gather
|
||||
* gather:vote - Assigns vote to map, captain or server
|
||||
* gather:vote - Assigns vote to map, leader or server
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
@ -4,23 +4,63 @@
|
|||
* Implements Gather Model
|
||||
*
|
||||
* Gather States
|
||||
* - Picking
|
||||
* - Election
|
||||
* - Selection
|
||||
* - Gathering
|
||||
* - Election (Electing leaders)
|
||||
* - Selection (Selecting teams)
|
||||
* - Done
|
||||
*
|
||||
*/
|
||||
|
||||
var Gatherer = require("./gatherer");
|
||||
var StateMachine = require("javascript-state-machine");
|
||||
|
||||
function Gather () {
|
||||
if (!(this instanceof Gather)) {
|
||||
return new Gather();
|
||||
}
|
||||
|
||||
this.TEAM_SIZE = 6;
|
||||
this.gatherers = [];
|
||||
this.initState();
|
||||
}
|
||||
|
||||
Gather.prototype.confirmTeam = function (user) {
|
||||
this.modifyGatherer(user, function (gatherer) {
|
||||
if (!gatherer.leader) return gatherer
|
||||
gatherer.confirm = true;
|
||||
return gatherer;
|
||||
});
|
||||
};
|
||||
|
||||
Gather.prototype.alienLeader = function () {
|
||||
return this.gatherers.reduce(function (acc, gatherer) {
|
||||
if (gatherer.team === "alien" && gatherer.leader) acc.push(gatherer);
|
||||
return acc;
|
||||
}, []).pop();
|
||||
};
|
||||
|
||||
Gather.prototype.marineLeader = function () {
|
||||
return this.gatherers.reduce(function (acc, gatherer) {
|
||||
if (gatherer.team === "marine" && gatherer.leader) acc.push(gatherer);
|
||||
return acc;
|
||||
}, []).pop();
|
||||
};
|
||||
|
||||
Gather.prototype.assignMarineLeader = function (id) {
|
||||
this.modifyGatherer({id: id}, function (gatherer) {
|
||||
gatherer.leader = true;
|
||||
gatherer.team = "marine";
|
||||
return gatherer;
|
||||
});
|
||||
};
|
||||
|
||||
Gather.prototype.assignAlienLeader = function (id) {
|
||||
this.modifyGatherer({id: id}, function (gatherer) {
|
||||
gatherer.leader = true;
|
||||
gatherer.team = "alien";
|
||||
return gatherer;
|
||||
});
|
||||
};
|
||||
|
||||
Gather.prototype.containsUser = function (user) {
|
||||
return this.gatherers.some(function (gatherer) {
|
||||
return gatherer.id === user.id;
|
||||
|
@ -42,21 +82,28 @@ Gather.prototype.removeUser = function (user) {
|
|||
});
|
||||
};
|
||||
|
||||
Gather.prototype.moveToMarine = function (user) {
|
||||
Gather.prototype.modifyGatherer = function (user, callback) {
|
||||
this.gatherers.forEach(function (gatherer, index, array) {
|
||||
if (gatherer.id === user.id) {
|
||||
gatherer.team = "marine";
|
||||
array[index] = gatherer;
|
||||
var modifiedUser = callback(gatherer);
|
||||
array[index] = modifiedUser;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Gather.prototype.moveToMarine = function (user) {
|
||||
if (this.marines().length >= this.TEAM_SIZE) return;
|
||||
this.modifyGatherer(user, function (gatherer) {
|
||||
gatherer.team = "marine";
|
||||
return gatherer;
|
||||
});
|
||||
};
|
||||
|
||||
Gather.prototype.moveToAlien = function (user) {
|
||||
this.gatherers.forEach(function (gatherer, index, array) {
|
||||
if (gatherer.id === user.id) {
|
||||
if (this.aliens().length >= this.TEAM_SIZE) return;
|
||||
this.modifyGatherer(user, function (gatherer) {
|
||||
gatherer.team = "alien";
|
||||
array[index] = gatherer;
|
||||
}
|
||||
return gatherer;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -93,6 +140,103 @@ Gather.prototype.toJson = function () {
|
|||
marines: this.marines(),
|
||||
aliens: this.aliens()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Returns an array of IDs representing votes for leaders
|
||||
Gather.prototype.leaderVotes = function () {
|
||||
var self = this;
|
||||
return self.gatherers.map(function (gatherer) {
|
||||
return gatherer.leaderVote
|
||||
}).filter(function (leaderId) {
|
||||
return (typeof leaderId === 'number');
|
||||
}).filter(function (candidate) {
|
||||
return self.gatherers.some(function (gatherer) {
|
||||
return gatherer.id === candidate;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Gather.prototype.voteForLeader = function (voter, candidate) {
|
||||
this.gatherers.forEach(function (gatherer, index, array) {
|
||||
if (gatherer.id === voter.id) {
|
||||
array[index].voteForLeader(candidate);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Gather States
|
||||
// - Gathering
|
||||
// - Election
|
||||
// - Selection
|
||||
// - Done
|
||||
|
||||
// gather.current - contains the current state
|
||||
// gather.is(s) - return true if state s is the current state
|
||||
// gather.can(e) - return true if event e can be fired in the current state
|
||||
// gather.cannot(e) - return true if event e cannot be fired in the current state
|
||||
// gather.transitions() - return list of events that are allowed from the current state
|
||||
|
||||
StateMachine.create({
|
||||
target: Gather.prototype,
|
||||
events: [
|
||||
{ name: "initState", from: "none", to: "gathering" },
|
||||
{ name: "addGatherer", from: "gathering", to: "election" },
|
||||
{ name: "selectLeader", from: "election", to: "selection" },
|
||||
{ name: "confirmSelection", from: "selection", to: "done" },
|
||||
{ name: "removeGatherer", from: ["election", "selection"], to: "gathering" }
|
||||
],
|
||||
callbacks: {
|
||||
onbeforeaddGatherer: function (event, from, to, user) {
|
||||
this.addUser(user);
|
||||
if (this.gatherers.length !== 12) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
onbeforeselectLeader: function (event, from, to, voter, candidate) {
|
||||
this.voteForLeader(voter, candidate);
|
||||
if (this.leaderVotes().length !== 12) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
onbeforeRemoveGatherer: function (event, from, to, user) {
|
||||
this.removeUser(user);
|
||||
},
|
||||
onenterselection: function () {
|
||||
// Remove all leaders and teams
|
||||
this.gatherers.forEach(function (gatherer, index, array) {
|
||||
array[index].leader = false;
|
||||
array[index].confirm = false;
|
||||
array[index].team = "lobby";
|
||||
});
|
||||
|
||||
// Assign leaders based on vote
|
||||
// 1st place alien comm
|
||||
// 2nd place marine comm
|
||||
var voteCount = {};
|
||||
this.gatherers.forEach(function (gatherer) {
|
||||
voteCount[gatherer.id] = 0;
|
||||
});
|
||||
this.leaderVotes().forEach(function (candidateId) {
|
||||
voteCount[candidateId]++;
|
||||
});
|
||||
var rank = [];
|
||||
for (var candidate in voteCount) {
|
||||
rank.push({ candidate: candidate, count: voteCount[candidate] });
|
||||
}
|
||||
rank.sort(function (a, b) {
|
||||
return a.count - b.count;
|
||||
});
|
||||
this.assignAlienLeader(parseInt(rank.pop().candidate, 0));
|
||||
this.assignMarineLeader(parseInt(rank.pop().candidate, 0));
|
||||
},
|
||||
onbeforeconfirmSelection: function (event, from, to, leader) {
|
||||
this.confirmTeam(leader);
|
||||
return (this.marineLeader().confirm
|
||||
&& this.alienLeader().confirm
|
||||
&& this.aliens().length === this.TEAM_SIZE
|
||||
&& this.marines().length === this.TEAM_SIZE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Gather;
|
|
@ -13,11 +13,18 @@
|
|||
var User = require("../user/user");
|
||||
|
||||
function Gatherer (user) {
|
||||
this.votes = {};
|
||||
this.leaderVote = null;
|
||||
this.mapVote = null;
|
||||
this.serverVote = null;
|
||||
this.confirm = false;
|
||||
this.id = user.id;
|
||||
this.user = user;
|
||||
this.captain = false;
|
||||
this.leader = false;
|
||||
this.team = "lobby";
|
||||
}
|
||||
|
||||
Gatherer.prototype.voteForLeader = function (user) {
|
||||
this.leaderVote = user.id;
|
||||
};
|
||||
|
||||
module.exports = Gatherer;
|
288
spec/gather.js
288
spec/gather.js
|
@ -6,12 +6,147 @@ var Gather = helper.Gather;
|
|||
var Gatherer = helper.Gatherer;
|
||||
var assert = require("chai").assert;
|
||||
|
||||
describe("Gather model", function () {
|
||||
var user, gather;
|
||||
describe("Gather Model:", function () {
|
||||
var user, gather, gatherers;
|
||||
|
||||
beforeEach(function () {
|
||||
user = helper.createUser();
|
||||
gatherers = [];
|
||||
for (var i = 0; i < 12; i++)
|
||||
gatherers.push(helper.createUser());
|
||||
gather = Gather();
|
||||
});
|
||||
|
||||
describe("Internal State", function () {
|
||||
|
||||
describe("Gathering", function () {
|
||||
it ("has initial state of 'gathering'", function () {
|
||||
assert.equal(gather.current, 'gathering');
|
||||
});
|
||||
describe("#addGatherer", function () {
|
||||
it ("adds users to the gather", function () {
|
||||
gather.addGatherer(user);
|
||||
assert.equal(gather.current, 'gathering');
|
||||
assert.equal(gather.gatherers.length, 1);
|
||||
});
|
||||
it ("retains 'gathering' state until number of players is 12", function () {
|
||||
assert.equal(gather.gatherers.length, 0);
|
||||
gatherers.forEach(function (user, index, array) {
|
||||
gather.addGatherer(user);
|
||||
if (index === 11) {
|
||||
assert.equal(gather.current, 'election');
|
||||
} else {
|
||||
assert.equal(gather.current, 'gathering');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Election", function () {
|
||||
beforeEach(function () {
|
||||
gatherers.forEach(function (gatherer) {
|
||||
gather.addGatherer(gatherer);
|
||||
});
|
||||
});
|
||||
describe("selectLeader", function () {
|
||||
it ("adds vote for a leader", function () {
|
||||
var voter = gather.gatherers[helper.random(12)];
|
||||
var candidate = gather.gatherers[helper.random(12)];
|
||||
gather.selectLeader(voter, candidate);
|
||||
assert.equal(gather.current, "election");
|
||||
assert.equal(gather.leaderVotes().length, 1);
|
||||
assert.equal(gather.leaderVotes()[0], candidate.id);
|
||||
});
|
||||
it ("retains state of 'election' until all votes cast", function () {
|
||||
var candidate = gatherers[0];
|
||||
gatherers.forEach(function (voter, index) {
|
||||
gather.selectLeader(voter, candidate);
|
||||
if (index !== 11) {
|
||||
assert.equal(gather.current, 'election');
|
||||
} else {
|
||||
assert.equal(gather.current, 'selection');
|
||||
}
|
||||
});
|
||||
});
|
||||
it ("produces leaders based on votes on transition", function () {
|
||||
var candidateA = gatherers[0];
|
||||
var candidateB = gatherers[1];
|
||||
assert.isUndefined(gather.alienLeader());
|
||||
assert.isUndefined(gather.marineLeader());
|
||||
gatherers.forEach(function (voter, index) {
|
||||
if (index % 2 === 0) {
|
||||
gather.selectLeader(voter, candidateA);
|
||||
} else {
|
||||
gather.selectLeader(voter, candidateB);
|
||||
}
|
||||
});
|
||||
assert.equal(gather.current, 'selection');
|
||||
assert.isTrue(gather.alienLeader().id === candidateA.id
|
||||
|| gather.alienLeader().id === candidateB.id);
|
||||
assert.isTrue(gather.marineLeader().id === candidateA.id
|
||||
|| gather.marineLeader().id === candidateB.id);
|
||||
});
|
||||
it ("returns to 'gathering' state if player leaves", function () {
|
||||
assert.equal(gather.current, 'election');
|
||||
var voter = gather.gatherers[helper.random(12)];
|
||||
var candidate = gather.gatherers[helper.random(12)];
|
||||
gather.selectLeader(voter, candidate);
|
||||
gather.removeGatherer(voter);
|
||||
assert.equal(gather.current, 'gathering');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Selection", function () {
|
||||
var leaderA, leaderB;
|
||||
beforeEach(function () {
|
||||
gatherers.forEach(function (gatherer) {
|
||||
gather.addGatherer(gatherer);
|
||||
});
|
||||
leaderA = gatherers[0];
|
||||
leaderB = gatherers[1];
|
||||
gatherers.forEach(function (voter, index) {
|
||||
if (index % 2 === 0) {
|
||||
gather.selectLeader(voter, leaderA);
|
||||
} else {
|
||||
gather.selectLeader(voter, leaderB);
|
||||
}
|
||||
});
|
||||
|
||||
gatherers.forEach(function (gatherer, index) {
|
||||
if (gatherer.leader) return;
|
||||
if (index % 2 === 0) {
|
||||
gather.moveToAlien(gatherer);
|
||||
} else {
|
||||
gather.moveToMarine(gatherer);
|
||||
}
|
||||
});
|
||||
});
|
||||
it ("transitions to 'done' when players selected and leaders confirm", function () {
|
||||
assert.equal(gather.current, "selection");
|
||||
gather.confirmSelection(leaderA);
|
||||
assert.equal(gather.current, "selection");
|
||||
gather.confirmSelection(leaderB);
|
||||
assert.equal(gather.current, "done");
|
||||
});
|
||||
it ("does not transition to 'done' unless all players selected", function () {
|
||||
var lobbyPlayer = gather.gatherers[11];
|
||||
gather.moveToLobby(lobbyPlayer);
|
||||
assert.equal(gather.current, "selection");
|
||||
gather.confirmSelection(leaderA);
|
||||
assert.equal(gather.current, "selection");
|
||||
gather.confirmSelection(leaderB);
|
||||
assert.equal(gather.current, "selection");
|
||||
});
|
||||
it ("transitions to picking if a player leaves", function () {
|
||||
var leaver = gather.gatherers[11];
|
||||
assert.equal(gather.current, "selection");
|
||||
gather.removeGatherer(leaver);
|
||||
assert.equal(gather.current, "gathering");
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("addUser", function () {
|
||||
it ("adds gatherer to lobby", function () {
|
||||
gather.addUser(user);
|
||||
|
@ -40,6 +175,13 @@ describe("Gather model", function () {
|
|||
assert.equal(gather.marines().length, 1);
|
||||
assert.equal(gather.marines()[0].id, user.id);
|
||||
});
|
||||
it ("will not move player if team is full", function () {
|
||||
gatherers.forEach(function (gatherer, index) {
|
||||
gather.addUser(gatherer);
|
||||
gather.moveToMarine(gatherer);
|
||||
assert.isTrue(gather.marines().length <= gather.TEAM_SIZE);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("moveToAlien", function () {
|
||||
it ("moves a player to alien", function () {
|
||||
|
@ -48,6 +190,13 @@ describe("Gather model", function () {
|
|||
assert.equal(gather.aliens().length, 1);
|
||||
assert.equal(gather.aliens()[0].id, user.id);
|
||||
});
|
||||
it ("will not move player if team is full", function () {
|
||||
gatherers.forEach(function (gatherer, index) {
|
||||
gather.addUser(gatherer);
|
||||
gather.moveToAlien(gatherer);
|
||||
assert.isTrue(gather.aliens().length <= gather.TEAM_SIZE);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("moveToLobby", function () {
|
||||
it ("moves a player to lobby", function () {
|
||||
|
@ -80,6 +229,139 @@ describe("Gather model", function () {
|
|||
});
|
||||
});
|
||||
describe("toJson", function () {
|
||||
it ("returns a json representation of the gather instance");
|
||||
it ("returns a json representation of the gather instance", function () {
|
||||
var output = gather.toJson();
|
||||
assert.isArray(output.lobby);
|
||||
assert.isArray(output.marines);
|
||||
assert.isArray(output.aliens);
|
||||
});
|
||||
});
|
||||
describe("leaderVotes", function () {
|
||||
beforeEach(function () {
|
||||
gatherers.forEach(function (user) {
|
||||
gather.addUser(user);
|
||||
});
|
||||
});
|
||||
it ("initialises with an empty array", function () {
|
||||
assert.isArray(gather.leaderVotes());
|
||||
assert.equal(gather.leaderVotes(), 0);
|
||||
});
|
||||
it ("returns an array of user ids", function () {
|
||||
var candidate = gatherers[0];
|
||||
var voter = gatherers[1];
|
||||
gather.voteForLeader(voter, candidate);
|
||||
assert.isArray(gather.leaderVotes());
|
||||
assert.equal(gather.leaderVotes().length, 1);
|
||||
assert.equal(gather.leaderVotes()[0], candidate.id);
|
||||
});
|
||||
it ("ignores candidates who have left the gather", function () {
|
||||
var candidate = gatherers[0];
|
||||
var voter = gatherers[1];
|
||||
gather.voteForLeader(voter, candidate);
|
||||
gather.removeUser(candidate);
|
||||
assert.equal(gather.leaderVotes().length, 0);
|
||||
});
|
||||
});
|
||||
describe("voteForLeader", function () {
|
||||
beforeEach(function () {
|
||||
gatherers.forEach(function (user) {
|
||||
gather.addUser(user);
|
||||
});
|
||||
});
|
||||
it ("assigns vote for a leader", function () {
|
||||
var candidate = gatherers[0];
|
||||
var voter = gatherers[1];
|
||||
gather.voteForLeader(voter, candidate);
|
||||
var votes = gather.leaderVotes();
|
||||
assert.equal(votes.length, 1);
|
||||
assert.equal(votes[0], candidate.id);
|
||||
});
|
||||
it ("reassigns vote if already voted", function () {
|
||||
var candidate = gatherers[0];
|
||||
var secondCandidate = gatherers[2];
|
||||
var voter = gatherers[1];
|
||||
gather.voteForLeader(voter, candidate);
|
||||
var votes = gather.leaderVotes();
|
||||
assert.equal(votes.length, 1);
|
||||
assert.equal(votes[0], candidate.id);
|
||||
gather.voteForLeader(voter, secondCandidate);
|
||||
votes = gather.leaderVotes();
|
||||
assert.equal(votes.length, 1);
|
||||
assert.equal(votes[0], secondCandidate.id);
|
||||
});
|
||||
});
|
||||
describe("alienLeader", function () {
|
||||
beforeEach(function () {
|
||||
gatherers.forEach(function (gatherer) {
|
||||
gather.addGatherer(gatherer);
|
||||
});
|
||||
});
|
||||
it ("returns alien leader", function () {
|
||||
gather.gatherers[0].team = "alien";
|
||||
gather.gatherers[0].leader = true;
|
||||
assert.equal(gather.alienLeader().id, gather.gatherers[0].id);
|
||||
});
|
||||
it ("returns undefined if no alien leader", function () {
|
||||
assert.isUndefined(gather.alienLeader());
|
||||
});
|
||||
});
|
||||
describe("marineLeader", function () {
|
||||
beforeEach(function () {
|
||||
gatherers.forEach(function (gatherer) {
|
||||
gather.addGatherer(gatherer);
|
||||
});
|
||||
});
|
||||
it ("returns marine leader", function () {
|
||||
gather.gatherers[0].team = "marine";
|
||||
gather.gatherers[0].leader = true;
|
||||
assert.equal(gather.marineLeader().id, gather.gatherers[0].id);
|
||||
});
|
||||
it ("returns undefined if no marine leader", function () {
|
||||
assert.isUndefined(gather.marineLeader());
|
||||
});
|
||||
});
|
||||
describe("assignMarineLeader", function () {
|
||||
it ("assigns a marine leader", function () {
|
||||
gather.addUser(user);
|
||||
gather.assignMarineLeader(user.id);
|
||||
var leader = gather.marineLeader();
|
||||
assert.equal(leader.id, user.id);
|
||||
});
|
||||
});
|
||||
describe("assignAlienLeader", function () {
|
||||
it ("assigns an alien leader", function () {
|
||||
gather.addUser(user);
|
||||
gather.assignAlienLeader(user.id);
|
||||
var leader = gather.alienLeader();
|
||||
assert.equal(leader.id, user.id);
|
||||
});
|
||||
});
|
||||
describe("confirmTeam", function () {
|
||||
var leader;
|
||||
beforeEach(function () {
|
||||
gatherers.forEach(function (gatherer) {
|
||||
gather.addGatherer(gatherer);
|
||||
});
|
||||
leader = gather.gatherers[0];
|
||||
gather.assignMarineLeader(leader.id);
|
||||
});
|
||||
it ("marks leader as confirmed", function () {
|
||||
gather.confirmTeam(leader);
|
||||
gather.gatherers.forEach(function (gatherer) {
|
||||
if (gatherer.leader) {
|
||||
assert.isTrue(gatherer.confirm);
|
||||
} else {
|
||||
assert.isFalse(gatherer.confirm);
|
||||
}
|
||||
});
|
||||
});
|
||||
it ("does nothing if user is not leader", function () {
|
||||
var player = gather.gatherers[1];
|
||||
assert.isFalse(player.leader);
|
||||
gather.confirmTeam(player);
|
||||
gather.gatherers.forEach(function (gatherer) {
|
||||
assert.isFalse(gatherer.confirm);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -41,4 +41,8 @@ var createUser = helpers.createUser = (function () {
|
|||
}
|
||||
})()
|
||||
|
||||
var random = helpers.random = function (n) {
|
||||
return Math.floor(Math.random () * n);
|
||||
}
|
||||
|
||||
module.exports = helpers;
|
Loading…
Reference in a new issue