comparison plugins/hangman/hangman.js @ 773:8c44bbcbbab9

Misc: style, cleanup and update
author David Demelier <markand@malikania.fr>
date Fri, 26 Oct 2018 13:01:00 +0200
parents 3e816cebed2c
children 06cc2f95f479
comparison
equal deleted inserted replaced
772:f5ccf65ae929 773:8c44bbcbbab9
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */ 17 */
18 18
19 // Plugin information. 19 // Plugin information.
20 info = { 20 info = {
21 name: "hangman", 21 name: "hangman",
22 author: "David Demelier <markand@malikania.fr>", 22 author: "David Demelier <markand@malikania.fr>",
23 license: "ISC", 23 license: "ISC",
24 summary: "A hangman game for IRC", 24 summary: "A hangman game for IRC",
25 version: "@IRCCD_VERSION@" 25 version: "@IRCCD_VERSION@"
26 }; 26 };
27 27
28 // Modules. 28 // Modules.
29 var Logger = Irccd.Logger; 29 var Logger = Irccd.Logger;
30 var File = Irccd.File; 30 var File = Irccd.File;
36 // Default options. 36 // Default options.
37 Plugin.config["collaborative"] = "true"; 37 Plugin.config["collaborative"] = "true";
38 38
39 // Formats. 39 // Formats.
40 Plugin.format = { 40 Plugin.format = {
41 "asked": "#{nickname}, '#{letter}' was already asked.", 41 "asked": "#{nickname}, '#{letter}' was already asked.",
42 "dead": "#{nickname}, fail the word was: #{word}.", 42 "dead": "#{nickname}, fail the word was: #{word}.",
43 "found": "#{nickname}, nice! the word is now #{word}", 43 "found": "#{nickname}, nice! the word is now #{word}",
44 "running": "#{nickname}, the game is already running and the word is: #{word}", 44 "running": "#{nickname}, the game is already running and the word is: #{word}",
45 "start": "#{nickname}, the game is started, the word to find is: #{word}", 45 "start": "#{nickname}, the game is started, the word to find is: #{word}",
46 "win": "#{nickname}, congratulations, the word is #{word}.", 46 "win": "#{nickname}, congratulations, the word is #{word}.",
47 "wrong-word": "#{nickname}, this is not the word.", 47 "wrong-word": "#{nickname}, this is not the word.",
48 "wrong-player": "#{nickname}, please wait until someone else proposes.", 48 "wrong-player": "#{nickname}, please wait until someone else proposes.",
49 "wrong-letter": "#{nickname}, there is no '#{letter}'." 49 "wrong-letter": "#{nickname}, there is no '#{letter}'."
50 }; 50 };
51 51
52 function Hangman(server, channel) 52 function Hangman(server, channel)
53 { 53 {
54 this.server = server; 54 this.server = server;
55 this.channel = channel; 55 this.channel = channel;
56 this.tries = 10; 56 this.tries = 10;
57 this.select(); 57 this.select();
58 } 58 }
59 59
60 /** 60 /**
61 * Map of games. 61 * Map of games.
62 */ 62 */
64 64
65 /** 65 /**
66 * List of words. 66 * List of words.
67 */ 67 */
68 Hangman.words = { 68 Hangman.words = {
69 all: [], //!< All words, 69 all: [], //!< All words,
70 registry: {} //!< Words list per server/channel. 70 registry: {} //!< Words list per server/channel.
71 }; 71 };
72 72
73 /** 73 /**
74 * Search for an existing game. 74 * Search for an existing game.
75 * 75 *
77 * @param channel the channel name 77 * @param channel the channel name
78 * @return the hangman instance or undefined if no one exists 78 * @return the hangman instance or undefined if no one exists
79 */ 79 */
80 Hangman.find = function (server, channel) 80 Hangman.find = function (server, channel)
81 { 81 {
82 return Hangman.map[server.toString() + '@' + channel]; 82 return Hangman.map[server.toString() + '@' + channel];
83 } 83 }
84 84
85 /** 85 /**
86 * Create a new game, store it in the map and return it. 86 * Create a new game, store it in the map and return it.
87 * 87 *
89 * @param channel the channel name 89 * @param channel the channel name
90 * @return the hangman object 90 * @return the hangman object
91 */ 91 */
92 Hangman.create = function (server, channel) 92 Hangman.create = function (server, channel)
93 { 93 {
94 return Hangman.map[server.toString() + "@" + channel] = new Hangman(server, channel); 94 return Hangman.map[server.toString() + "@" + channel] = new Hangman(server, channel);
95 } 95 }
96 96
97 /** 97 /**
98 * Remove the specified game from the map. 98 * Remove the specified game from the map.
99 * 99 *
100 * @param game the game to remove 100 * @param game the game to remove
101 */ 101 */
102 Hangman.remove = function (game) 102 Hangman.remove = function (game)
103 { 103 {
104 delete Hangman.map[game.server + "@" + game.channel]; 104 delete Hangman.map[game.server + "@" + game.channel];
105 } 105 }
106 106
107 /** 107 /**
108 * Check if the text is a valid word. 108 * Check if the text is a valid word.
109 * 109 *
110 * @param word the word to check 110 * @param word the word to check
111 * @return true if a word 111 * @return true if a word
112 */ 112 */
113 Hangman.isWord = function (word) 113 Hangman.isWord = function (word)
114 { 114 {
115 if (word.length === 0) 115 if (word.length === 0)
116 return false; 116 return false;
117 117
118 for (var i = 0; i < word.length; ++i) 118 for (var i = 0; i < word.length; ++i)
119 if (!Unicode.isLetter(word.charCodeAt(i))) 119 if (!Unicode.isLetter(word.charCodeAt(i)))
120 return false; 120 return false;
121 121
122 return true; 122 return true;
123 } 123 }
124 124
125 /** 125 /**
126 * Load all words. 126 * Load all words.
127 */ 127 */
128 Hangman.loadWords = function () 128 Hangman.loadWords = function ()
129 { 129 {
130 var path; 130 var path;
131 131
132 // User specified file? 132 // User specified file?
133 if (Plugin.config["file"]) 133 if (Plugin.config["file"])
134 path = Plugin.config["file"]; 134 path = Plugin.config["file"];
135 else 135 else
136 path = Plugin.paths.config + "/words.conf"; 136 path = Plugin.paths.config + "/words.conf";
137 137
138 try { 138 try {
139 Logger.info("loading words..."); 139 Logger.info("loading words...");
140 140
141 var file = new File(path, "r"); 141 var file = new File(path, "r");
142 var line; 142 var line;
143 143
144 while ((line = file.readline()) !== undefined) 144 while ((line = file.readline()) !== undefined)
145 if (Hangman.isWord(line)) 145 if (Hangman.isWord(line))
146 Hangman.words.all.push(line); 146 Hangman.words.all.push(line);
147 } catch (e) { 147 } catch (e) {
148 throw new Error("could not open '" + path + "'"); 148 throw new Error("could not open '" + path + "'");
149 } 149 }
150 150
151 if (Hangman.words.all.length === 0) 151 if (Hangman.words.all.length === 0)
152 throw new Error("empty word database"); 152 throw new Error("empty word database");
153 153
154 Logger.info("number of words in database: " + Hangman.words.all.length); 154 Logger.info("number of words in database: " + Hangman.words.all.length);
155 }
156
157 /**
158 * Load all formats.
159 */
160 Hangman.loadFormats = function ()
161 {
162 // --- DEPRECATED -------------------------------------------
163 //
164 // This code will be removed.
165 //
166 // Since: 2.1.0
167 // Until: 3.0.0
168 // Reason: new [format] section replaces it.
169 //
170 // ----------------------------------------------------------
171 for (var key in Plugin.format) {
172 var optname = "format-" + key;
173
174 if (typeof (Plugin.config[optname]) !== "string")
175 continue;
176
177 if (Plugin.config[optname].length === 0)
178 Logger.warning("skipping empty '" + optname + "' format");
179 else
180 Plugin.format[key] = Plugin.config[optname];
181 }
182 } 155 }
183 156
184 /** 157 /**
185 * Select the next word for the game. 158 * Select the next word for the game.
186 */ 159 */
187 Hangman.prototype.select = function () 160 Hangman.prototype.select = function ()
188 { 161 {
189 var id = this.server.toString() + "@" + this.channel; 162 var id = this.server.toString() + "@" + this.channel;
190 163
191 // Reload the words if empty. 164 // Reload the words if empty.
192 if (!Hangman.words.registry[id] || Hangman.words.registry[id].length === 0) 165 if (!Hangman.words.registry[id] || Hangman.words.registry[id].length === 0)
193 Hangman.words.registry[id] = Hangman.words.all.slice(0); 166 Hangman.words.registry[id] = Hangman.words.all.slice(0);
194 167
195 var i = Math.floor(Math.random() * Hangman.words.registry[id].length); 168 var i = Math.floor(Math.random() * Hangman.words.registry[id].length);
196 169
197 this.word = Hangman.words.registry[id][i]; 170 this.word = Hangman.words.registry[id][i];
198 171
199 // Erase words from the registry. 172 // Erase words from the registry.
200 Hangman.words.registry[id].splice(i, 1); 173 Hangman.words.registry[id].splice(i, 1);
201 174
202 // Fill table. 175 // Fill table.
203 this.table = {}; 176 this.table = {};
204 177
205 for (var j = 0; j < this.word.length; ++j) 178 for (var j = 0; j < this.word.length; ++j)
206 this.table[this.word.charCodeAt(j)] = false; 179 this.table[this.word.charCodeAt(j)] = false;
207 } 180 }
208 181
209 /** 182 /**
210 * Format the word with underscore and letters. 183 * Format the word with underscore and letters.
211 * 184 *
212 * @return the secret 185 * @return the secret
213 */ 186 */
214 Hangman.prototype.formatWord = function () 187 Hangman.prototype.formatWord = function ()
215 { 188 {
216 var str = ""; 189 var str = "";
217 190
218 for (var i = 0; i < this.word.length; ++i) { 191 for (var i = 0; i < this.word.length; ++i) {
219 var ch = this.word.charCodeAt(i); 192 var ch = this.word.charCodeAt(i);
220 193
221 if (!this.table[ch]) 194 if (!this.table[ch])
222 str += "_"; 195 str += "_";
223 else 196 else
224 str += String.fromCharCode(ch); 197 str += String.fromCharCode(ch);
225 198
226 if (i + 1 < this.word.length) 199 if (i + 1 < this.word.length)
227 str += " "; 200 str += " ";
228 } 201 }
229 202
230 return str; 203 return str;
231 } 204 }
232 205
233 /** 206 /**
234 * Propose a word or a letter. 207 * Propose a word or a letter.
235 * 208 *
237 * @param nickname the user trying 210 * @param nickname the user trying
238 * @return the status of the game 211 * @return the status of the game
239 */ 212 */
240 Hangman.prototype.propose = function (ch, nickname) 213 Hangman.prototype.propose = function (ch, nickname)
241 { 214 {
242 var status = "found"; 215 var status = "found";
243 216
244 // Check for collaborative mode. 217 // Check for collaborative mode.
245 if (Plugin.config["collaborative"] === "true" && !this.query) { 218 if (Plugin.config["collaborative"] === "true" && !this.query) {
246 if (this.last !== undefined && this.last === nickname) 219 if (this.last !== undefined && this.last === nickname)
247 return "wrong-player"; 220 return "wrong-player";
248 221
249 this.last = nickname; 222 this.last = nickname;
250 } 223 }
251 224
252 if (typeof(ch) == "number") { 225 if (typeof(ch) == "number") {
253 if (this.table[ch] === undefined) { 226 if (this.table[ch] === undefined) {
254 this.tries -= 1; 227 this.tries -= 1;
255 status = "wrong-letter"; 228 status = "wrong-letter";
256 } else { 229 } else {
257 if (this.table[ch]) { 230 if (this.table[ch]) {
258 this.tries -= 1; 231 this.tries -= 1;
259 status = "asked"; 232 status = "asked";
260 } else 233 } else
261 this.table[ch] = true; 234 this.table[ch] = true;
262 } 235 }
263 } else { 236 } else {
264 if (this.word != ch) { 237 if (this.word != ch) {
265 this.tries -= 1; 238 this.tries -= 1;
266 status = "wrong-word"; 239 status = "wrong-word";
267 } else 240 } else
268 status = "win"; 241 status = "win";
269 } 242 }
270 243
271 // Check if dead. 244 // Check if dead.
272 if (this.tries <= 0) 245 if (this.tries <= 0)
273 status = "dead"; 246 status = "dead";
274 247
275 // Check if win. 248 // Check if win.
276 var win = true; 249 var win = true;
277 250
278 for (var i = 0; i < this.word.length; ++i) { 251 for (var i = 0; i < this.word.length; ++i) {
279 if (!this.table[this.word.charCodeAt(i)]) { 252 if (!this.table[this.word.charCodeAt(i)]) {
280 win = false; 253 win = false;
281 break; 254 break;
282 } 255 }
283 } 256 }
284 257
285 if (win) 258 if (win)
286 status = "win"; 259 status = "win";
287 260
288 return status; 261 return status;
289 } 262 }
290 263
291 function onLoad() 264 function onLoad()
292 { 265 {
293 Hangman.loadFormats(); 266 Hangman.loadWords();
294 Hangman.loadWords();
295 } 267 }
296 268
297 onReload = onLoad; 269 onReload = onLoad;
298 270
299 function propose(server, channel, origin, game, proposition) 271 function propose(server, channel, origin, game, proposition)
300 { 272 {
301 var kw = { 273 var kw = {
302 channel: channel, 274 channel: channel,
303 command: server.info().commandChar + Plugin.info().name, 275 command: server.info().commandChar + Plugin.info().name,
304 nickname: Util.splituser(origin), 276 nickname: Util.splituser(origin),
305 origin: origin, 277 origin: origin,
306 plugin: Plugin.info().name, 278 plugin: Plugin.info().name,
307 server: server.toString() 279 server: server.toString()
308 }; 280 };
309 281
310 var st = game.propose(proposition, kw.nickname); 282 var st = game.propose(proposition, kw.nickname);
311 283
312 switch (st) { 284 switch (st) {
313 case "found": 285 case "found":
314 kw.word = game.formatWord(); 286 kw.word = game.formatWord();
315 server.message(channel, Util.format(Plugin.format["found"], kw)); 287 server.message(channel, Util.format(Plugin.format["found"], kw));
316 break; 288 break;
317 case "wrong-letter": 289 case "wrong-letter":
318 case "wrong-player": 290 case "wrong-player":
319 case "wrong-word": 291 case "wrong-word":
320 kw.word = proposition; 292 kw.word = proposition;
321 case "asked": 293 case "asked":
322 kw.letter = String.fromCharCode(proposition); 294 kw.letter = String.fromCharCode(proposition);
323 server.message(channel, Util.format(Plugin.format[st], kw)); 295 server.message(channel, Util.format(Plugin.format[st], kw));
324 break; 296 break;
325 case "dead": 297 case "dead":
326 case "win": 298 case "win":
327 kw.word = game.word; 299 kw.word = game.word;
328 server.message(channel, Util.format(Plugin.format[st], kw)); 300 server.message(channel, Util.format(Plugin.format[st], kw));
329 301
330 // Remove the game. 302 // Remove the game.
331 Hangman.remove(game); 303 Hangman.remove(game);
332 break; 304 break;
333 default: 305 default:
334 break; 306 break;
335 } 307 }
336 } 308 }
337 309
338 function onCommand(server, origin, channel, message) 310 function onCommand(server, origin, channel, message)
339 { 311 {
340 var isquery = server.isSelf(channel); 312 var isquery = server.isSelf(channel);
341 313
342 if (isquery) 314 if (isquery)
343 channel = origin; 315 channel = origin;
344 else 316 else
345 channel = channel.toLowerCase(); 317 channel = channel.toLowerCase();
346 318
347 var game = Hangman.find(server, channel); 319 var game = Hangman.find(server, channel);
348 var kw = { 320 var kw = {
349 channel: channel, 321 channel: channel,
350 command: server.info().commandChar + Plugin.info().name, 322 command: server.info().commandChar + Plugin.info().name,
351 nickname: Util.splituser(origin), 323 nickname: Util.splituser(origin),
352 origin: origin, 324 origin: origin,
353 plugin: Plugin.info().name, 325 plugin: Plugin.info().name,
354 server: server.toString() 326 server: server.toString()
355 }; 327 };
356 328
357 if (game) { 329 if (game) {
358 var list = message.split(" \t"); 330 var list = message.split(" \t");
359 331
360 if (list.length === 0 || String(list[0]).length === 0) { 332 if (list.length === 0 || String(list[0]).length === 0) {
361 kw.word = game.formatWord(); 333 kw.word = game.formatWord();
362 server.message(channel, Util.format(Plugin.format["running"], kw)); 334 server.message(channel, Util.format(Plugin.format["running"], kw));
363 } else { 335 } else {
364 var word = String(list[0]); 336 var word = String(list[0]);
365 337
366 if (Hangman.isWord(word)) 338 if (Hangman.isWord(word))
367 propose(server, channel, origin, game, word); 339 propose(server, channel, origin, game, word);
368 } 340 }
369 } else { 341 } else {
370 game = Hangman.create(server, channel); 342 game = Hangman.create(server, channel);
371 game.query = isquery; 343 game.query = isquery;
372 kw.word = game.formatWord(); 344 kw.word = game.formatWord();
373 server.message(channel, Util.format(Plugin.format["start"], kw)); 345 server.message(channel, Util.format(Plugin.format["start"], kw));
374 } 346 }
375 347
376 return game; 348 return game;
377 } 349 }
378 350
379 function onMessage(server, origin, channel, message) 351 function onMessage(server, origin, channel, message)
380 { 352 {
381 if (server.isSelf(channel)) 353 if (server.isSelf(channel))
382 channel = origin; 354 channel = origin;
383 else 355 else
384 channel = channel.toLowerCase(); 356 channel = channel.toLowerCase();
385 357
386 var game = Hangman.find(server, channel); 358 var game = Hangman.find(server, channel);
387 359
388 if (!game) 360 if (!game)
389 return; 361 return;
390 362
391 if (message.length === 1 && Unicode.isLetter(message.charCodeAt(0))) 363 if (message.length === 1 && Unicode.isLetter(message.charCodeAt(0)))
392 propose(server, channel, origin, game, message.charCodeAt(0)); 364 propose(server, channel, origin, game, message.charCodeAt(0));
393 } 365 }