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));
+}