Mercurial > irccd
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/hangman/hangman.js Mon Feb 08 16:43:14 2016 +0100 @@ -0,0 +1,363 @@ +/* + * hangman.js -- hangman game for IRC + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Modules */ +var Logger = Irccd.Logger; +var File = Irccd.File; +var Plugin = Irccd.Plugin; +var Server = Irccd.Server; +var Unicode = Irccd.Unicode +var Util = Irccd.Util; + +/* Plugin information */ +info = { + author: "David Demelier <markand@malikania.fr>", + license: "ISC", + summary: "A hangman game for IRC", + version: "@IRCCD_VERSION@" +}; + +/* Default options */ +Plugin.config["collaborative"] = "true"; + +function Hangman(server, channel) +{ + this.server = server; + this.channel = channel; + this.tries = 10; + this.select(); +} + +/** + * Map of games. + */ +Hangman.map = {}; + +/** + * List of words + */ +Hangman.words = []; + +/** + * Formats for writing. + */ +Hangman.formats = { + "asked": "#{nickname}, '#{letter}' was already asked.", + "dead": "#{nickname}, fail the word was: #{word}.", + "found": "#{nickname}, nice! the word is now #{word}", + "running": "#{nickname}, the game is already running.", + "start": "#{nickname}, the game is started, the word to find is: #{word}", + "win": "#{nickname}, congratulations, the word is #{word}.", + "wrong-word": "#{nickname}, this is not the word.", + "wrong-player": "#{nickname}, please wait until someone else proposes.", + "wrong-letter": "#{nickname}, there is no '#{letter}'." +}; + +/** + * Search for an existing game. + * + * @param server the server object + * @param channel the channel name + * @return the hangman instance or undefined if no one exists + */ +Hangman.find = function (server, channel) +{ + return Hangman.map[server.toString() + '@' + channel]; +} + +/** + * Create a new game, store it in the map and return it. + * + * @param server the server object + * @param channel the channel name + * @return the hangman object + */ +Hangman.create = function (server, channel) +{ + return Hangman.map[server.toString() + "@" + channel] = new Hangman(server, channel); +} + +/** + * Remove the specified game from the map. + * + * @param game the game to remove + */ +Hangman.remove = function (game) +{ + delete Hangman.map[game.server + "@" + game.channel]; +} + +/** + * Check if the text is a valid word. + * + * @param word the word to check + * @return true if a word + */ +Hangman.isWord = function (word) +{ + if (word.length === 0) + return false; + + for (var i = 0; i < word.length; ++i) + if (!Unicode.isLetter(word.charCodeAt(i))) + return false; + + return true; +} + +/** + * Load all words. + */ +Hangman.loadWords = function () +{ + var path; + + /* User specified file? */ + if (Plugin.config["file"]) + path = Plugin.config["file"]; + else + path = Plugin.configPath + "words.conf"; + + try { + Logger.info("loading words..."); + + var file = new File(path, "r"); + var line; + + while ((line = file.readline()) !== undefined) + if (Hangman.isWord(line)) + Hangman.words.push(line); + } catch (e) { + throw new Error("could not open '" + path + "'"); + } + + if (Hangman.words.length === 0) + throw new Error("empty word database"); + + Logger.info("number of words in database: " + Hangman.words.length); +} + +/** + * Load all formats. + */ +Hangman.loadFormats = function () +{ + for (var key in Hangman.formats) { + var optname = "format-" + key; + + if (typeof (Plugin.config[optname]) !== "string") + continue; + + if (Plugin.config[optname].length === 0) + Logger.warning("skipping empty '" + optname + "' format"); + else + Hangman.formats[key] = Plugin.config[optname]; + } +} + +/** + * Select the next word for the game. + */ +Hangman.prototype.select = function () +{ + /* Reload the words if empty */ + if (!this.words || this.words.length === 0) + this.words = Hangman.words.slice(0); + + var i = Math.floor(Math.random() * this.words.length); + + this.word = this.words[i]; + this.words.splice(i, 1); + + /* Fill table */ + this.table = {}; + + for (var j = 0; j < this.word.length; ++j) + this.table[this.word.charCodeAt(j)] = false; +} + +/** + * Format the word with underscore and letters. + * + * @return the secret + */ +Hangman.prototype.formatWord = function () +{ + var str = ""; + + for (var i = 0; i < this.word.length; ++i) { + var ch = this.word.charCodeAt(i); + + if (!this.table[ch]) + str += "_"; + else + str += String.fromCharCode(ch); + + if (i + 1 < this.word.length) + str += " "; + } + + return str; +} + +/** + * Propose a word or a letter. + * + * @param ch the code point or the unique word + * @param nickname the user trying + * @return the status of the game + */ +Hangman.prototype.propose = function (ch, nickname) +{ + var status = "found"; + + /* Check for collaborative mode */ + if (Plugin.config["collaborative"] === "true") { + if (this.last !== undefined && this.last === nickname) + return "wrong-player"; + + this.last = nickname; + } + + if (typeof(ch) == "number") { + if (this.table[ch] === undefined) { + this.tries -= 1; + status = "wrong-letter"; + } else { + if (this.table[ch]) { + this.tries -= 1; + status = "asked"; + } else { + this.table[ch] = true; + } + } + } else { + if (this.word != ch) { + this.tries -= 1; + status = "wrong-word"; + } else { + status = "win"; + } + } + + /* Check if dead */ + if (this.tries < 0) + status = "dead"; + + /* Check if win */ + var win = true; + + for (var i = 0; i < this.word.length; ++i) { + if (!this.table[this.word.charCodeAt(i)]) { + win = false; + break; + } + } + + if (win) + status = "win"; + + return status; +} + +function onLoad() +{ + Hangman.loadFormats(); + Hangman.loadWords(); +} + +onReload = onLoad; + +function propose(server, channel, origin, game, proposition) +{ + var kw = { + channel: channel, + command: server.info().commandChar, + nickname: Util.splituser(origin), + origin: origin, + plugin: Plugin.info().name, + server: server + }; + + var st = game.propose(proposition, kw.nickname); + + switch (st) { + case "found": + kw.word = game.formatWord(); + server.message(channel, Util.format(Hangman.formats["found"], kw)); + break; + case "wrong-letter": + case "wrong-player": + case "wrong-word": + case "asked": + kw.letter = String.fromCharCode(proposition); + server.message(channel, Util.format(Hangman.formats[st], kw)); + break; + case "dead": + case "win": + kw.word = game.word; + server.message(channel, Util.format(Hangman.formats[st], kw)); + + /* Remove the game */ + Hangman.remove(game); + break; + default: + break; + } +} + +function onCommand(server, origin, channel, message) +{ + var game = Hangman.find(server, channel); + var kw = { + channel: channel, + command: server.info().commandChar, + nickname: Util.splituser(origin), + origin: origin, + plugin: Plugin.info().name, + server: server + }; + + if (game) { + var list = message.split(" \t"); + + if (list.length === 0 || String(list[0]).length === 0) { + server.message(channel, Util.format(Hangman.formats["running"], kw)); + } else { + var word = String(list[0]); + + if (Hangman.isWord(word)) + propose(server, channel, origin, game, word); + } + } else { + game = Hangman.create(server, channel); + kw.word = game.formatWord(); + server.message(channel, Util.format(Hangman.formats["start"], kw)); + } +} + +function onMessage(server, origin, channel, message) +{ + var game = Hangman.find(server, channel); + + if (!game) + return; + + if (message.length === 1 && Unicode.isLetter(message.charCodeAt(0))) + propose(server, channel, origin, game, message.charCodeAt(0)); +}