Implemented most of gather model

This commit is contained in:
Chris Blanchard 2015-07-24 14:34:02 +01:00
parent 11b2ad7bd3
commit 703944a709
5 changed files with 457 additions and 20 deletions

View file

@ -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
*
*/

View file

@ -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;

View file

@ -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;

View file

@ -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);
});
});
});
});

View file

@ -41,4 +41,8 @@ var createUser = helpers.createUser = (function () {
}
})()
var random = helpers.random = function (n) {
return Math.floor(Math.random () * n);
}
module.exports = helpers;