const fs = require("fs"), readline = require("readline"), arrayPad = require('./strpad.js').arrayPad; Object.assign(global, require("./config.js")); function initWordList(filename) { return new Promise((ok, ko) => { console.log("[rapido] Reloading dictionnary"); var stream = fs.createReadStream(filename), reader = readline.createInterface({input: stream}), words = []; reader.on("line", w => { words.push(w); }); reader.on("close", () => { if (!words.length) { ko(); return; } ok(words); }); }); } function Rapido(config) { this.config = config; } Rapido.prototype.init = function(bot, chanName) { this.room = chanName; this.bot = bot; this.init = false; this.users = {}; this.wordRequest = []; this.resetScoresWrapper(); } Rapido.prototype.getName = function() { return "rapido"; } Rapido.prototype.onActivate = function() { }; Rapido.prototype.onSelfJoin = function() { this.init = true; this.reloadDb(); } Rapido.prototype.onJoin = function(nick) { } Rapido.prototype.onNameList = function(nicks) { } Rapido.prototype.onNickPart = function(nick) { } Rapido.prototype.onRemMode = function(user, mode) { } Rapido.prototype.onAddMode = function(user, mode) { } Rapido.prototype.onRename = function(oldNick, newNick) { if (this.users[oldNick.toLowerCase()]) { this.users[newNick.toLowerCase()] = this.users[oldNick.toLowerCase()]; delete this.users[oldNick.toLowerCase()]; } } Rapido.prototype.onMessage = function(user, text) { if (this.config.DISABLED) return; this.onMessageInternal(user, this.users[user.toLowerCase()], text.trimEnd().replace(/\s+/, ' ')); } Rapido.prototype.reloadDb = function() { var _this = this; this.reloading = true; initWordList(this.config.DICTIONARY_PATH).then(words => { _this.reloading = false; _this.words = words; console.info("[rapido] " +words.length +" words loaded from database"); _this.endWord(); }).catch(err => { console.error("[rapido]", err); _this.bot.sendMsg(this.room, err); }); } Rapido.prototype.resetScoresWrapper = function() { const now = Date.now(), nextTick = Math.floor((now / this.config.GAME_DURATION) +1) *this.config.GAME_DURATION, _this = this; setTimeout(() => { _this.resetScores(); _this.resetScoresWrapper(); }, nextTick -now); } Rapido.prototype.resetScores = function() { if (Object.keys(this.users).length > 0) { this.bot.sendMsg(this.room, "Fin de la manche ! Voici les scores finaux:"); this.sendScore(); } this.users = {}; } Rapido.prototype.endWord = function() { this.wordTimeout && clearTimeout(this.wordTimeout); this.wordTimeout = 0; this.currentWord = null; this.currentPoints = []; this.wordRequest = []; this.wordStart= 0; } Rapido.prototype.onWordTimeout = function() { this.wordTimeout = 0; ++(this.setProgression); if (this.currentPoints.length) { this.currentPoints = this.currentPoints.map((i, index) => { const time = i.time -this.wordStart; return { username: i.username, time: time, fps: Math.floor(100000 * this.currentWord.length / time) / 100, pts: this.computeScore(time, this.currentWord, index) }; }); this.currentPoints.forEach(i => { this.bot.sendMsg(this.room, "Mot trouvé en " +i.time +"ms (" +i.fps +" touches par seconde) par " +i.username); }); this.currentPoints.forEach(i => { this.users[i.username] = this.users[i.username] || this.bot.createUser(i.username); this.users[i.username].score += i.pts; this.bot.sendMsg(this.room, i.pts +" points pour " +i.username +", qui cumule un total de " +this.users[i.username].score +" points !"); }); } else { this.bot.sendMsg(this.room, "Bah alors ? " +this.wordRequest.join(", ") +" vous fichez quoi ?"); } this.endWord(); if (this.setProgression >= this.config.WORD_IN_SET) { this.bot.sendMsg(this.room, "fin du temps réglementaire, tapez !rapido pour une prochaine partie."); this.bot.endGame(this); } else this.startNextWordTimer(); } Rapido.prototype.startNewSet = function() { this.setProgression = 0; this.startNextWordTimer(); } Rapido.prototype.startNextWordTimer = function() { var _this = this; this.currentWord = this.words[Math.floor(Math.random() * this.words.length)]; console.log(`[rapido] ${this.currentWord}`); _this.bot.sendMsg(this.room, "Attention attention, prochain mot dans " +Math.floor(this.config.NEXT_WORD_DELAY / 1000) +" secondes"); if (this.setProgression == 0) _this.bot.sendMsg(this.room, "Rappel : il faut taper le plus vite possible en minuscule et sans espaces le mot proposé"); setTimeout(() => { var wordEsc = _this.currentWord.replace(/(\w)/g, ' $1').toUpperCase().trimStart(); _this.bot.sendMsg(_this.room, wordEsc); _this.wordStart = Date.now(); _this.wordTimeout = setTimeout(_this.onWordTimeout.bind(_this), Math.floor(1000 * this.currentWord.length / this.config.WORD_TIMEO_FPS)); }, this.config.NEXT_WORD_DELAY); } Rapido.prototype.sendScore = function() { var scores = []; for (var i in this.users) scores.push({name: this.users[i].name, score: this.users[i].score}); if (scores.length == 0) { this.bot.sendMsg(this.room, "Pas de points pour le moment"); return; } scores = scores.sort((a, b) => b.score - a.score).slice(0, 10); var index = 0; var scoreLines = arrayPad(scores.map(i => [ ((++index) +"."), i.name, (i.score +" points") ])); if (scoreLines[0].length < 30) { // merge score lines 2 by 2 var tmp = []; for (var i =0, len = scoreLines.length; i < len; i += 2) tmp.push((scoreLines[i] || "") +" - " +(scoreLines[i +1] || "")); scoreLines = tmp; } scoreLines.forEach(i => this.bot.sendMsg(this.room, i)); } Rapido.prototype.computeScore = function(ellapsed, word, index) { const fps = 1000 * word.length / ellapsed; var base = 2 +(index == 0 ? 2 : 0); for (var i =0, len = this.config.SCORE_MAP.length; i < len; ++i) { var scoreItem = this.config.SCORE_MAP[i]; if (fps >= scoreItem[0]) return base +scoreItem[1]; } return base; } Rapido.prototype.onMessageInternal = function(username, user, msg) { const lmsg = msg.toLowerCase(); msg = lmsg.substr(0, 1) + msg.substr(1); if (lmsg === "!rapido") { if (this.wordRequest.indexOf(username) === -1) this.wordRequest.push(username); if (!this.currentWord && this.wordRequest.length >= this.config.MIN_PLAYERS) this.startNewSet(); } else if (lmsg.startsWith("!score")) { this.sendScore(); } else if (this.currentWord && this.wordStart && msg === this.currentWord) { for (var i =0, len = this.currentPoints.length; i < len; ++i) if (this.currentPoints[i].username == username) return; this.currentPoints.push({ username: username, time: Date.now()}); this.bot.sendMsg(this.room, "Ok pour " +username); } else if (lmsg.startsWith("!help")) { this.bot.sendMsg(this.room, "Besoin d'aide ? Les règles ainsi que les commandes utiles sont ici : https://git.knacki.info/irc.knacki.info/ircbot-quizz/src/master/rapido.md"); } }; module.exports = Rapido;