+ /* Update state (to be sent out to any future clients) */
+ this.state.players_judging.delete(player.name);
+ this.state.players_judged.push(player.name);
+
+ /* And notify all players this this player has judged. */
+ this.broadcast_event_object('player-judged', player.name);
+
+ return { valid: true };
+ }
+
+ receive_judging(prompt_id, session_id) {
+ const player = this.players_by_session[session_id];
+ if (! player)
+ return { valid: false, message: "Player not found" };
+
+ const prompt = this.state.prompts.find(p => p.id === prompt_id);
+ if (! prompt)
+ return { valid: false, message: "Prompt not found" };
+
+ if (prompt !== this.state.active_prompt)
+ return { valid: false, message: "Prompt no longer active" };
+
+ /* Notify all players this this player is actively judging. */
+ this.state.players_judging.add(player.name);
+ this.broadcast_event_object('player-judging', player.name);
+
+ return { valid: true };
+ }
+
+ /* Returns true if vote toggled, false for player or prompt not found */
+ toggle_end_judging(prompt_id, session_id) {
+ const player = this.players_by_session[session_id];
+
+ const prompt = this.state.prompts.find(p => p.id === prompt_id);
+ if (! prompt || ! player)
+ return false;
+
+ if (this.state.end_judging.has(player.name)) {
+ this.state.end_judging.delete(player.name);
+ this.broadcast_event_object('unvote-end-judging', player.name);
+ } else {
+ this.state.end_judging.add(player.name);
+ this.broadcast_event_object('vote-end-judging', player.name);
+ }
+
+ return true;
+ }
+
+ canonize(word) {
+ return word.trim().toLowerCase();
+ }
+
+ compute_scores() {
+ const word_submitters = {};
+
+ /* Perform a (non-strict) majority ruling on equivalencies,
+ * dropping all that didn't get enough votes. */
+ const quorum = Math.floor((this.players.length + 1)/2);
+ const agreed_equivalencies = Object.values(this.equivalencies).filter(
+ eq => eq.count >= quorum);
+
+ /* And with that agreed set of equivalencies, construct the full
+ * groups. */
+ const word_maps = {};
+
+ for (let e of agreed_equivalencies) {
+ const word0_canon = this.canonize(e.words[0]);
+ const word1_canon = this.canonize(e.words[1]);
+ let group = word_maps[word0_canon];
+ if (! group)
+ group = word_maps[word1_canon];
+ if (! group)
+ group = { words: [], players: new Set()};
+
+ if (! word_maps[word0_canon]) {
+ word_maps[word0_canon] = group;
+ group.words.push(e.words[0]);
+ }
+
+ if (! word_maps[word1_canon]) {
+ word_maps[word1_canon] = group;
+ group.words.push(e.words[1]);
+ }
+ }
+
+ /* Now go through answers from each player and add the player
+ * to the set corresponding to each word group. */