comparison plugins/hangman/hangman.js @ 0:1158cffe5a5e

Initial import
author David Demelier <markand@malikania.fr>
date Mon, 08 Feb 2016 16:43:14 +0100
parents
children c4fe9a8b1a62
comparison
equal deleted inserted replaced
-1:000000000000 0:1158cffe5a5e
1 /*
2 * hangman.js -- hangman game for IRC
3 *
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /* Modules */
20 var Logger = Irccd.Logger;
21 var File = Irccd.File;
22 var Plugin = Irccd.Plugin;
23 var Server = Irccd.Server;
24 var Unicode = Irccd.Unicode
25 var Util = Irccd.Util;
26
27 /* Plugin information */
28 info = {
29 author: "David Demelier <markand@malikania.fr>",
30 license: "ISC",
31 summary: "A hangman game for IRC",
32 version: "@IRCCD_VERSION@"
33 };
34
35 /* Default options */
36 Plugin.config["collaborative"] = "true";
37
38 function Hangman(server, channel)
39 {
40 this.server = server;
41 this.channel = channel;
42 this.tries = 10;
43 this.select();
44 }
45
46 /**
47 * Map of games.
48 */
49 Hangman.map = {};
50
51 /**
52 * List of words
53 */
54 Hangman.words = [];
55
56 /**
57 * Formats for writing.
58 */
59 Hangman.formats = {
60 "asked": "#{nickname}, '#{letter}' was already asked.",
61 "dead": "#{nickname}, fail the word was: #{word}.",
62 "found": "#{nickname}, nice! the word is now #{word}",
63 "running": "#{nickname}, the game is already running.",
64 "start": "#{nickname}, the game is started, the word to find is: #{word}",
65 "win": "#{nickname}, congratulations, the word is #{word}.",
66 "wrong-word": "#{nickname}, this is not the word.",
67 "wrong-player": "#{nickname}, please wait until someone else proposes.",
68 "wrong-letter": "#{nickname}, there is no '#{letter}'."
69 };
70
71 /**
72 * Search for an existing game.
73 *
74 * @param server the server object
75 * @param channel the channel name
76 * @return the hangman instance or undefined if no one exists
77 */
78 Hangman.find = function (server, channel)
79 {
80 return Hangman.map[server.toString() + '@' + channel];
81 }
82
83 /**
84 * Create a new game, store it in the map and return it.
85 *
86 * @param server the server object
87 * @param channel the channel name
88 * @return the hangman object
89 */
90 Hangman.create = function (server, channel)
91 {
92 return Hangman.map[server.toString() + "@" + channel] = new Hangman(server, channel);
93 }
94
95 /**
96 * Remove the specified game from the map.
97 *
98 * @param game the game to remove
99 */
100 Hangman.remove = function (game)
101 {
102 delete Hangman.map[game.server + "@" + game.channel];
103 }
104
105 /**
106 * Check if the text is a valid word.
107 *
108 * @param word the word to check
109 * @return true if a word
110 */
111 Hangman.isWord = function (word)
112 {
113 if (word.length === 0)
114 return false;
115
116 for (var i = 0; i < word.length; ++i)
117 if (!Unicode.isLetter(word.charCodeAt(i)))
118 return false;
119
120 return true;
121 }
122
123 /**
124 * Load all words.
125 */
126 Hangman.loadWords = function ()
127 {
128 var path;
129
130 /* User specified file? */
131 if (Plugin.config["file"])
132 path = Plugin.config["file"];
133 else
134 path = Plugin.configPath + "words.conf";
135
136 try {
137 Logger.info("loading words...");
138
139 var file = new File(path, "r");
140 var line;
141
142 while ((line = file.readline()) !== undefined)
143 if (Hangman.isWord(line))
144 Hangman.words.push(line);
145 } catch (e) {
146 throw new Error("could not open '" + path + "'");
147 }
148
149 if (Hangman.words.length === 0)
150 throw new Error("empty word database");
151
152 Logger.info("number of words in database: " + Hangman.words.length);
153 }
154
155 /**
156 * Load all formats.
157 */
158 Hangman.loadFormats = function ()
159 {
160 for (var key in Hangman.formats) {
161 var optname = "format-" + key;
162
163 if (typeof (Plugin.config[optname]) !== "string")
164 continue;
165
166 if (Plugin.config[optname].length === 0)
167 Logger.warning("skipping empty '" + optname + "' format");
168 else
169 Hangman.formats[key] = Plugin.config[optname];
170 }
171 }
172
173 /**
174 * Select the next word for the game.
175 */
176 Hangman.prototype.select = function ()
177 {
178 /* Reload the words if empty */
179 if (!this.words || this.words.length === 0)
180 this.words = Hangman.words.slice(0);
181
182 var i = Math.floor(Math.random() * this.words.length);
183
184 this.word = this.words[i];
185 this.words.splice(i, 1);
186
187 /* Fill table */
188 this.table = {};
189
190 for (var j = 0; j < this.word.length; ++j)
191 this.table[this.word.charCodeAt(j)] = false;
192 }
193
194 /**
195 * Format the word with underscore and letters.
196 *
197 * @return the secret
198 */
199 Hangman.prototype.formatWord = function ()
200 {
201 var str = "";
202
203 for (var i = 0; i < this.word.length; ++i) {
204 var ch = this.word.charCodeAt(i);
205
206 if (!this.table[ch])
207 str += "_";
208 else
209 str += String.fromCharCode(ch);
210
211 if (i + 1 < this.word.length)
212 str += " ";
213 }
214
215 return str;
216 }
217
218 /**
219 * Propose a word or a letter.
220 *
221 * @param ch the code point or the unique word
222 * @param nickname the user trying
223 * @return the status of the game
224 */
225 Hangman.prototype.propose = function (ch, nickname)
226 {
227 var status = "found";
228
229 /* Check for collaborative mode */
230 if (Plugin.config["collaborative"] === "true") {
231 if (this.last !== undefined && this.last === nickname)
232 return "wrong-player";
233
234 this.last = nickname;
235 }
236
237 if (typeof(ch) == "number") {
238 if (this.table[ch] === undefined) {
239 this.tries -= 1;
240 status = "wrong-letter";
241 } else {
242 if (this.table[ch]) {
243 this.tries -= 1;
244 status = "asked";
245 } else {
246 this.table[ch] = true;
247 }
248 }
249 } else {
250 if (this.word != ch) {
251 this.tries -= 1;
252 status = "wrong-word";
253 } else {
254 status = "win";
255 }
256 }
257
258 /* Check if dead */
259 if (this.tries < 0)
260 status = "dead";
261
262 /* Check if win */
263 var win = true;
264
265 for (var i = 0; i < this.word.length; ++i) {
266 if (!this.table[this.word.charCodeAt(i)]) {
267 win = false;
268 break;
269 }
270 }
271
272 if (win)
273 status = "win";
274
275 return status;
276 }
277
278 function onLoad()
279 {
280 Hangman.loadFormats();
281 Hangman.loadWords();
282 }
283
284 onReload = onLoad;
285
286 function propose(server, channel, origin, game, proposition)
287 {
288 var kw = {
289 channel: channel,
290 command: server.info().commandChar,
291 nickname: Util.splituser(origin),
292 origin: origin,
293 plugin: Plugin.info().name,
294 server: server
295 };
296
297 var st = game.propose(proposition, kw.nickname);
298
299 switch (st) {
300 case "found":
301 kw.word = game.formatWord();
302 server.message(channel, Util.format(Hangman.formats["found"], kw));
303 break;
304 case "wrong-letter":
305 case "wrong-player":
306 case "wrong-word":
307 case "asked":
308 kw.letter = String.fromCharCode(proposition);
309 server.message(channel, Util.format(Hangman.formats[st], kw));
310 break;
311 case "dead":
312 case "win":
313 kw.word = game.word;
314 server.message(channel, Util.format(Hangman.formats[st], kw));
315
316 /* Remove the game */
317 Hangman.remove(game);
318 break;
319 default:
320 break;
321 }
322 }
323
324 function onCommand(server, origin, channel, message)
325 {
326 var game = Hangman.find(server, channel);
327 var kw = {
328 channel: channel,
329 command: server.info().commandChar,
330 nickname: Util.splituser(origin),
331 origin: origin,
332 plugin: Plugin.info().name,
333 server: server
334 };
335
336 if (game) {
337 var list = message.split(" \t");
338
339 if (list.length === 0 || String(list[0]).length === 0) {
340 server.message(channel, Util.format(Hangman.formats["running"], kw));
341 } else {
342 var word = String(list[0]);
343
344 if (Hangman.isWord(word))
345 propose(server, channel, origin, game, word);
346 }
347 } else {
348 game = Hangman.create(server, channel);
349 kw.word = game.formatWord();
350 server.message(channel, Util.format(Hangman.formats["start"], kw));
351 }
352 }
353
354 function onMessage(server, origin, channel, message)
355 {
356 var game = Hangman.find(server, channel);
357
358 if (!game)
359 return;
360
361 if (message.length === 1 && Unicode.isLetter(message.charCodeAt(0)))
362 propose(server, channel, origin, game, message.charCodeAt(0));
363 }