]> git.cworth.org Git - empires-server/blob - empathy.js
Add scoring once all players have submitted
[empires-server] / empathy.js
1 const express = require("express");
2 const Game = require("./game.js");
3
4 class Empathy extends Game {
5   constructor(id) {
6     super(id);
7     this.state = {
8       prompts: [],
9       active_prompt: null,
10       players_answered: 0
11     };
12     this.answers = [];
13     this.next_prompt_id = 1;
14   }
15
16   reset() {
17     this.state.active_prompt = null;
18     this.state.players_answered = 0;
19     this.answers = [];
20
21     this.broadcast_event_object('game-state', this.state);
22   }
23
24   add_prompt(items, prompt_string) {
25     const prompt = new Prompt(this.next_prompt_id, items, prompt_string);
26     this.next_prompt_id++;
27
28     this.state.prompts.push(prompt);
29
30     this.broadcast_event_object('prompt', prompt);
31
32     return prompt;
33   }
34
35   /* Returns true if vote toggled, false for player or prompt not found */
36   toggle_vote(prompt_id, session_id) {
37     const player = this.players_by_session[session_id];
38
39     const prompt = this.state.prompts.find(p => p.id === prompt_id);
40     if (! prompt || ! player)
41       return false;
42
43     prompt.toggle_vote(player.name);
44
45     this.broadcast_event_object('prompt', prompt);
46
47     return true;
48   }
49
50   /* Returns true on success, false for prompt not found. */
51   start(prompt_id) {
52     const prompt = this.state.prompts.find(p => p.id === prompt_id);
53     if (! prompt)
54       return false;
55
56     /* Ignore any start request that comes in while a prompt is
57      * already being played. */
58     if (this.state.active_prompt)
59       return false;
60
61     this.state.active_prompt = prompt;
62
63     this.broadcast_event_object('start', prompt);
64
65     return true;
66   }
67
68   receive_answer(prompt_id, session_id, answers) {
69     const player = this.players_by_session[session_id];
70     if (! player)
71       return { valid: false, message: "Player not found" };
72
73     const prompt = this.state.prompts.find(p => p.id === prompt_id);
74     if (! prompt)
75       return { valid: false, message: "Prompt not found" };
76
77     if (prompt !== this.state.active_prompt)
78       return { valid: false, message: "Prompt no longer active" };
79
80     /* Save the complete answers for our own use later. */
81     this.answers.push({
82       player: player,
83       answers: answers
84     });
85
86     /* And notify players how many players have answered. */
87     this.state.players_answered++;
88     this.broadcast_event_object('answered', this.state.players_answered);
89
90     return { valid: true };
91   }
92
93   compute_scores() {
94     const word_submitters = {};
95     const scores = [];
96
97     for (let a of this.answers) {
98       for (let word of a.answers) {
99         if (word_submitters[word])
100           word_submitters[word].push(a.player.name);
101         else
102           word_submitters[word] = [a.player.name];
103       }
104     }
105
106     for (let a of this.answers) {
107       let score = 0;
108       for (let word of a.answers) {
109         score += word_submitters[word].length;
110       }
111       scores.push({
112         player: a.player.name,
113         score: score
114       });
115     }
116
117     scores.sort((a,b) => {
118       return b.score - a.score;
119     });
120
121     const word_submitters_arr = [];
122     for (let word in word_submitters)
123       word_submitters_arr.push({word: word, players: word_submitters[word]});
124
125     word_submitters_arr.sort((a,b) => {
126       return b.players.length - a.players.length;
127     });
128
129     this.state.scores = {
130       scores: scores,
131       words: word_submitters_arr
132     };
133
134     this.broadcast_event_object('scores', this.state.scores);
135   }
136 }
137
138 Empathy.router = express.Router();
139 const router = Empathy.router;
140
141 class Prompt {
142   constructor(id, items, prompt) {
143     this.id = id;
144     this.items = items;
145     this.prompt = prompt;
146     this.votes = [];
147   }
148
149   toggle_vote(player_name) {
150     if (this.votes.find(v => v === player_name))
151       this.votes = this.votes.filter(v => v !== player_name);
152     else
153       this.votes.push(player_name);
154   }
155 }
156
157 router.post('/prompts', (request, response) => {
158   const game = request.game;
159
160   game.add_prompt(request.body.items, request.body.prompt);
161 });
162
163 router.post('/vote/:prompt_id([0-9]+)', (request, response) => {
164   const game = request.game;
165   const prompt_id = parseInt(request.params.prompt_id, 10);
166
167   if (game.toggle_vote(prompt_id, request.session.id))
168     response.sendStatus(200);
169   else
170     response.sendStatus(404);
171 });
172
173 router.post('/start/:prompt_id([0-9]+)', (request, response) => {
174   const game = request.game;
175   const prompt_id = parseInt(request.params.prompt_id, 10);
176
177   if (game.start(prompt_id))
178     response.sendStatus(200);
179   else
180     response.sendStatus(404);
181 });
182
183 router.post('/answer/:prompt_id([0-9]+)', (request, response) => {
184   const game = request.game;
185   const prompt_id = parseInt(request.params.prompt_id, 10);
186
187   const result = game.receive_answer(prompt_id,
188                                      request.session.id,
189                                      request.body.answers);
190   response.json(result);
191
192   if (game.answers.length >= game.players.length)
193     game.compute_scores();
194 });
195
196 router.post('/reset', (request, response) => {
197   const game = request.game;
198   game.reset();
199 });
200
201 Empathy.meta = {
202   name: "Empathy",
203   identifier: "empathy",
204 };
205
206 exports.Game = Empathy;