From 507128bb32b242b5a61a180fcb352486b5116280 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Thu, 11 Jun 2020 16:37:43 -0700 Subject: [PATCH] Empathy: Use word groups to assign scores to players, not their lists Previously, we were marching through each players list of words and looking each word up in the word groups to find a score for that word. This worked in general, but failed in a specific case: If a player had two different words that were considered by the group to be equivalent, that player received the score for that word twice. To avoid giving a palyer in this situation points that the group clearly didn't consider they deserved, we instead iterative over the worud groups themselves to assign points out to players. This means that when a player ends up having two (or more) words that end up being equivalent, (based on the team consensus when judging), all but one of those words will be worth nothing. --- empathy.js | 89 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/empathy.js b/empathy.js index 7966d72..1da9b8e 100644 --- a/empathy.js +++ b/empathy.js @@ -196,7 +196,6 @@ class Empathy extends Game { compute_scores() { const word_submitters = {}; - const scores = []; /* Perform a (non-strict) majority ruling on equivalencies, * dropping all that didn't get enough votes. */ @@ -206,74 +205,88 @@ class Empathy extends Game { /* And with that agreed set of equivalencies, construct the full * groups. */ - const word_groups = {}; + const word_maps = {}; for (let e of agreed_equivalencies) { - let group = word_groups[e.words[0]]; + let group = word_maps[e.words[0]]; if (! group) - group = word_groups[e.words[1]]; + group = word_maps[e.words[1]]; if (! group) group = { words: [], players: new Set()}; - if (! word_groups[e.words[0]]) { - word_groups[e.words[0]] = group; + if (! word_maps[e.words[0]]) { + word_maps[e.words[0]] = group; group.words.push(e.words[0]); } - if (! word_groups[e.words[1]]) { - word_groups[e.words[1]] = group; + if (! word_maps[e.words[1]]) { + word_maps[e.words[1]] = group; group.words.push(e.words[1]); } } - /* Now go through answers from each player and add the player name + /* Now go through answers from each player and add the player * to the set corresponding to each word group. */ for (let a of this.answers) { for (let word of a.answers) { /* If there's no group yet, this is a singleton word. */ - if (word_groups[word]) { - word_groups[word].players.add(a.player.name); + if (word_maps[word]) { + word_maps[word].players.add(a.player); } else { const group = { words: [word], players: new Set() }; - group.players.add(a.player.name); - word_groups[word] = group; + group.players.add(a.player); + word_maps[word] = group; } } } - /* Finally, go through the answers one more time, this time taking - * the length of the player set as a score for the player's - * submission of that word. */ - for (let a of this.answers) { - let score = 0; - for (let word of a.answers) { - score += word_groups[word].players.size; - } - scores.push({ - player: a.player.name, - score: score - }); + /* Now that we've assigned the players to these word maps, we now + * want to collapse the groups down to a single array of + * word_groups. + * + * The difference between "word_maps" and "word_groups" is that + * the former has a property for every word that maps to a group, + * (so if you iterate over the keys you will see the same group + * multiple times). In contrast, iterating over"word_groups" will + * have you visit each group only once. */ + const word_groups = Object.entries(word_maps).filter( + entry => entry[0] === entry[1].words[0]).map(entry => entry[1]); + + /* Now, go through each word group and assign the scores out to + * the corresponding players. + * + * Note: We do this by going through the word groups, (as opposed + * to the list of words from the players again), specifically to + * avoid giving a player points for a wrod group twice (in the + * case where a player submits two different words that the group + * ends up judging as equivalent). + */ + this.players.forEach(p => p.round_score = 0); + for (let group of word_groups) { + group.players.forEach(p => p.round_score += group.players.size); } + const scores = this.players.map(p => { + return { + player: p.name, + score: p.round_score + }; + }); + scores.sort((a,b) => { return b.score - a.score; }); /* Put the word groups into a form the client can consume. - * - * Most significantly, we only want one entry for each group (even - * though our current "word_groups" object has a property for each - * word considered equivalent). */ - const words_submitted = Object.entries(word_groups).filter( - group => group[0] === group[1].words[0]).map( - group => { - return { - word: group[1].words.join('/'), - players: Array.from(group[1].players) - }; - } - ); + const words_submitted = word_groups.map( + group => { + return { + word: group.words.join('/'), + players: Array.from(group.players).map(p => p.name) + }; + } + ); words_submitted.sort((a,b) => { return b.players.length - a.players.length; -- 2.43.0