2014-10-02 14:48:59 -07:00
|
|
|
"""
|
|
|
|
.. module:: Scramble
|
2015-11-01 17:40:13 -08:00
|
|
|
:synopsis: Module to provide a word scramble game
|
2014-10-02 14:48:59 -07:00
|
|
|
|
|
|
|
.. moduleauthor:: Dave Pedu <dave@davepedu.com>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2018-01-16 17:05:03 -08:00
|
|
|
from pyircbot.modulebase import ModuleBase, hook
|
|
|
|
from pyircbot.common import messageHasCommand
|
2013-12-28 09:58:20 -08:00
|
|
|
import random
|
2015-11-01 17:40:13 -08:00
|
|
|
import json
|
2013-12-28 09:58:20 -08:00
|
|
|
import os
|
|
|
|
from threading import Timer
|
|
|
|
from operator import itemgetter
|
|
|
|
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2013-12-28 09:58:20 -08:00
|
|
|
class Scramble(ModuleBase):
|
2015-11-01 17:40:13 -08:00
|
|
|
def __init__(self, bot, moduleName):
|
|
|
|
# init the base module
|
2017-01-01 14:59:01 -08:00
|
|
|
ModuleBase.__init__(self, bot, moduleName)
|
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
# Dictionary
|
2017-01-01 14:59:01 -08:00
|
|
|
self.wordsCount = 0
|
2015-11-01 17:40:13 -08:00
|
|
|
self.wordsFile = self.getFilePath("words.txt")
|
|
|
|
print(self.wordsFile)
|
|
|
|
wordsF = open(self.wordsFile, "r")
|
|
|
|
while True:
|
|
|
|
word = wordsF.readline()
|
2017-01-01 14:59:01 -08:00
|
|
|
if not word:
|
2015-11-01 17:40:13 -08:00
|
|
|
break
|
2017-01-01 14:59:01 -08:00
|
|
|
self.wordsCount += 1
|
2015-11-01 17:40:13 -08:00
|
|
|
wordsF.close
|
|
|
|
self.log.info("Scramble: Loaded %s words" % str(self.wordsCount))
|
|
|
|
# Load scores
|
|
|
|
self.scoresFile = self.getFilePath("scores.json")
|
2017-01-01 14:59:01 -08:00
|
|
|
if not os.path.exists(self.scoresFile):
|
2015-11-01 17:40:13 -08:00
|
|
|
json.dump({}, open(self.scoresFile, 'w'))
|
|
|
|
self.scores = json.load(open(self.scoresFile, 'r'))
|
|
|
|
# Per channel games
|
|
|
|
self.games = {}
|
|
|
|
# Hook in
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2018-01-16 17:05:03 -08:00
|
|
|
@hook("PRIVMSG")
|
|
|
|
def scramble(self, msg, cmd):
|
|
|
|
channel = msg.args[0]
|
2015-11-01 17:40:13 -08:00
|
|
|
if channel[0] == "#":
|
2017-01-01 14:59:01 -08:00
|
|
|
if channel not in self.games:
|
|
|
|
self.games[channel] = scrambleGame(self, channel)
|
2018-01-16 17:05:03 -08:00
|
|
|
self.games[channel].scramble(msg.args, msg.prefix, msg.trailing)
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def saveScores(self):
|
|
|
|
json.dump(self.scores, open(self.scoresFile, 'w'))
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def getScore(self, player, add=0):
|
|
|
|
player = player.lower()
|
2017-01-01 14:59:01 -08:00
|
|
|
if player not in self.scores:
|
2015-11-01 17:40:13 -08:00
|
|
|
self.scores[player] = 0
|
|
|
|
if not add == 0:
|
2017-01-01 14:59:01 -08:00
|
|
|
self.scores[player] += add
|
2015-11-01 17:40:13 -08:00
|
|
|
self.saveScores()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
return self.scores[player]
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def getScoreNoWrite(self, player):
|
|
|
|
if not player.lower() in self.scores:
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
return self.getScore(player)
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def ondisable(self):
|
|
|
|
self.log.info("Scramble: Unload requested, ending games...")
|
|
|
|
for game in self.games:
|
|
|
|
self.games[game].gameover()
|
|
|
|
self.saveScores()
|
2013-12-28 09:58:20 -08:00
|
|
|
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2013-12-28 09:58:20 -08:00
|
|
|
class scrambleGame:
|
2015-11-01 17:40:13 -08:00
|
|
|
def __init__(self, master, channel):
|
|
|
|
self.master = master
|
|
|
|
self.channel = channel
|
|
|
|
# Running?
|
|
|
|
self.running = False
|
|
|
|
# Current word
|
|
|
|
self.currentWord = None
|
|
|
|
# Current word, scrambled
|
|
|
|
self.scrambled = None
|
|
|
|
# Online?
|
|
|
|
self.scrambleOn = False
|
|
|
|
# Count down to hints
|
|
|
|
self.hintTimer = None
|
|
|
|
# of hints given
|
|
|
|
self.hintsGiven = 0
|
|
|
|
# Cooldown between words
|
|
|
|
self.nextTimer = None
|
|
|
|
# How many gamesWithoutGuesses submitted this round
|
2017-01-01 14:59:01 -08:00
|
|
|
self.guesses = 0
|
2015-11-01 17:40:13 -08:00
|
|
|
# How many games in a row where nobody guessed
|
2017-01-01 14:59:01 -08:00
|
|
|
self.gamesWithoutGuesses = 0
|
|
|
|
|
|
|
|
self.delayHint = self.master.config["hintDelay"]
|
|
|
|
self.delayNext = self.master.config["delayNext"]
|
|
|
|
self.maxHints = self.master.config["maxHints"]
|
|
|
|
self.abortAfterNoGuesses = self.master.config["abortAfterNoGuesses"]
|
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def gameover(self):
|
2017-01-01 14:59:01 -08:00
|
|
|
self.clearTimers()
|
2015-11-01 17:40:13 -08:00
|
|
|
self.running = False
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def clearTimers(self):
|
|
|
|
self.clearTimer(self.nextTimer)
|
|
|
|
self.clearTimer(self.hintTimer)
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def clearTimer(self, timer):
|
|
|
|
if timer:
|
|
|
|
timer.cancel()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def scramble(self, args, prefix, trailing):
|
|
|
|
sender = prefix.nick
|
2018-01-16 17:05:03 -08:00
|
|
|
cmd = messageHasCommand(".scrambleon", trailing)
|
2015-11-01 17:40:13 -08:00
|
|
|
if cmd and not self.running:
|
|
|
|
self.running = True
|
|
|
|
self.startScramble()
|
|
|
|
return
|
2018-01-16 17:05:03 -08:00
|
|
|
cmd = messageHasCommand(".scrambleoff", trailing)
|
2015-11-01 17:40:13 -08:00
|
|
|
if cmd and self.running:
|
|
|
|
self.gameover()
|
|
|
|
self.running = False
|
|
|
|
return
|
2018-01-16 17:05:03 -08:00
|
|
|
cmd = messageHasCommand(".scramble top", trailing)
|
2015-11-01 17:40:13 -08:00
|
|
|
if cmd:
|
|
|
|
sortedscores = []
|
|
|
|
for player in self.master.scores:
|
2017-01-01 14:59:01 -08:00
|
|
|
sortedscores.append({'name': player, 'score': self.master.scores[player]})
|
2015-11-01 17:40:13 -08:00
|
|
|
sortedscores = sorted(sortedscores, key=itemgetter('score'))
|
|
|
|
sortedscores.reverse()
|
|
|
|
numScores = len(sortedscores)
|
2017-01-01 14:59:01 -08:00
|
|
|
if numScores > 3:
|
|
|
|
numScores = 3
|
2015-11-01 17:40:13 -08:00
|
|
|
resp = "Top %s: " % str(numScores)
|
|
|
|
which = 1
|
2017-01-01 14:59:01 -08:00
|
|
|
while which <= numScores:
|
|
|
|
resp += "%s: %s, " % (sortedscores[which - 1]["name"], sortedscores[which - 1]["score"])
|
|
|
|
which += 1
|
2015-11-01 17:40:13 -08:00
|
|
|
self.master.bot.act_PRIVMSG(self.channel, resp[:-2])
|
2018-01-16 17:05:03 -08:00
|
|
|
cmd = messageHasCommand(".scramble score", trailing)
|
2015-11-01 17:40:13 -08:00
|
|
|
if cmd:
|
|
|
|
someone = cmd.args.strip()
|
|
|
|
if len(someone) > 0:
|
2017-01-01 14:59:01 -08:00
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "%s: %s has a score of %s" %
|
|
|
|
(sender, someone, self.master.getScoreNoWrite(someone)))
|
|
|
|
else:
|
2015-11-01 17:40:13 -08:00
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "%s: %s" % (sender, self.master.getScore(sender)))
|
|
|
|
if self.currentWord and trailing.strip().lower() == self.currentWord:
|
|
|
|
playerScore = self.master.getScore(sender, 1)
|
2017-01-01 14:59:01 -08:00
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "%s guessed the word - %s! %s now has %s points. Next word in %s "
|
|
|
|
"seconds." % (sender, self.currentWord, sender, playerScore, self.delayNext))
|
2015-11-01 17:40:13 -08:00
|
|
|
self.currentWord = None
|
|
|
|
self.clearTimers()
|
|
|
|
self.hintsGiven = 0
|
|
|
|
self.nextTimer = Timer(self.delayNext, self.startNewWord)
|
|
|
|
self.nextTimer.start()
|
2017-01-01 14:59:01 -08:00
|
|
|
self.guesses = 0
|
2015-11-01 17:40:13 -08:00
|
|
|
else:
|
2017-01-01 14:59:01 -08:00
|
|
|
self.guesses += 1
|
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def startScramble(self):
|
|
|
|
self.clearTimer(self.nextTimer)
|
|
|
|
self.nextTimer = Timer(0, self.startNewWord)
|
|
|
|
self.nextTimer.start()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def startNewWord(self):
|
|
|
|
self.currentWord = self.pickWord()
|
|
|
|
self.master.log.info("Scramble: New word for %s: %s" % (self.channel, self.currentWord))
|
|
|
|
self.scrambled = self.scrambleWord(self.currentWord)
|
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "New word - %s " % (self.scrambled))
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
self.clearTimer(self.hintTimer)
|
|
|
|
self.hintTimer = Timer(self.delayHint, self.giveHint)
|
|
|
|
self.hintTimer.start()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def giveHint(self):
|
2017-01-01 14:59:01 -08:00
|
|
|
self.hintsGiven += 1
|
|
|
|
|
|
|
|
if self.hintsGiven >= len(self.currentWord) or self.hintsGiven > self.maxHints:
|
2015-11-01 17:40:13 -08:00
|
|
|
self.abortWord()
|
|
|
|
return
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
blanks = ""
|
|
|
|
for letter in list(self.currentWord):
|
|
|
|
if letter == " ":
|
2017-01-01 14:59:01 -08:00
|
|
|
blanks += " "
|
2015-11-01 17:40:13 -08:00
|
|
|
else:
|
2017-01-01 14:59:01 -08:00
|
|
|
blanks += "_"
|
2015-11-01 17:40:13 -08:00
|
|
|
partFromWord = self.currentWord[0:self.hintsGiven]
|
|
|
|
partFromBlanks = blanks[self.hintsGiven:]
|
2017-01-01 14:59:01 -08:00
|
|
|
hintstr = partFromWord + partFromBlanks
|
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "Hint: - %s" % (hintstr))
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
self.clearTimer(self.hintTimer)
|
|
|
|
self.hintTimer = Timer(self.delayHint, self.giveHint)
|
|
|
|
self.hintTimer.start()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def abortWord(self):
|
|
|
|
cur = self.currentWord
|
|
|
|
self.currentWord = None
|
2017-01-01 14:59:01 -08:00
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was %s. Next word in %s seconds." %
|
|
|
|
(cur, self.delayNext))
|
2015-11-01 17:40:13 -08:00
|
|
|
self.hintsGiven = 0
|
|
|
|
self.clearTimer(self.nextTimer)
|
2017-01-01 14:59:01 -08:00
|
|
|
|
|
|
|
if self.guesses == 0:
|
|
|
|
self.gamesWithoutGuesses += 1
|
2015-11-01 17:40:13 -08:00
|
|
|
if self.gamesWithoutGuesses >= self.abortAfterNoGuesses:
|
2017-01-01 14:59:01 -08:00
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "No one seems to be playing - type .scrambleon to "
|
|
|
|
"start again.")
|
2015-11-01 17:40:13 -08:00
|
|
|
self.gameover()
|
|
|
|
return
|
|
|
|
else:
|
2017-01-01 14:59:01 -08:00
|
|
|
self.gamesWithoutGuesses = 0
|
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
self.nextTimer = Timer(self.delayNext, self.startNewWord)
|
|
|
|
self.nextTimer.start()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def pickWord(self):
|
|
|
|
f = open(self.master.wordsFile, "r")
|
|
|
|
skip = random.randint(0, self.master.wordsCount)
|
2017-01-01 14:59:01 -08:00
|
|
|
while skip >= 0:
|
2015-11-01 17:40:13 -08:00
|
|
|
f.readline()
|
2017-01-01 14:59:01 -08:00
|
|
|
skip -= 1
|
2015-11-01 17:40:13 -08:00
|
|
|
picked = f.readline().strip().lower()
|
|
|
|
f.close()
|
|
|
|
return picked
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def scrambleWord(self, word):
|
|
|
|
scrambled = ""
|
|
|
|
for subword in word.split(" "):
|
2017-01-01 14:59:01 -08:00
|
|
|
scrambled += self.scrambleIndividualWord(subword) + " "
|
2015-11-01 17:40:13 -08:00
|
|
|
return scrambled.strip()
|
2017-01-01 14:59:01 -08:00
|
|
|
|
2015-11-01 17:40:13 -08:00
|
|
|
def scrambleIndividualWord(self, word):
|
|
|
|
scrambled = list(word)
|
|
|
|
random.shuffle(scrambled)
|
2017-01-01 14:59:01 -08:00
|
|
|
return ''.join(scrambled).lower()
|