]> git.cworth.org Git - lmno-server/blobdiff - empathy.js
Empathy: Use word groups to assign scores to players, not their lists
[lmno-server] / empathy.js
index 7966d72db185bf4846a83c3b811cb3d457c88d27..1da9b8ef07ce8fde9b221fcb65715ef11c00e9d4 100644 (file)
@@ -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;