You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
8.4 KiB
245 lines
8.4 KiB
""" |
|
.. module:: Scramble |
|
:synopsis: Module to provide a word scramble game |
|
|
|
.. moduleauthor:: Dave Pedu <dave@davepedu.com> |
|
|
|
""" |
|
|
|
from pyircbot.modulebase import ModuleBase, hook |
|
from pyircbot.common import messageHasCommand |
|
import random |
|
import json |
|
import os |
|
from threading import Timer |
|
from operator import itemgetter |
|
|
|
|
|
class Scramble(ModuleBase): |
|
def __init__(self, bot, moduleName): |
|
# init the base module |
|
ModuleBase.__init__(self, bot, moduleName) |
|
|
|
# Dictionary |
|
self.wordsCount = 0 |
|
self.wordsFile = self.getFilePath("words.txt") |
|
print(self.wordsFile) |
|
wordsF = open(self.wordsFile, "r") |
|
while True: |
|
word = wordsF.readline() |
|
if not word: |
|
break |
|
self.wordsCount += 1 |
|
wordsF.close |
|
self.log.info("Scramble: Loaded %s words" % str(self.wordsCount)) |
|
# Load scores |
|
self.scoresFile = self.getFilePath("scores.json") |
|
if not os.path.exists(self.scoresFile): |
|
json.dump({}, open(self.scoresFile, 'w')) |
|
self.scores = json.load(open(self.scoresFile, 'r')) |
|
# Per channel games |
|
self.games = {} |
|
# Hook in |
|
|
|
@hook("PRIVMSG") |
|
def scramble(self, msg, cmd): |
|
channel = msg.args[0] |
|
if channel[0] == "#": |
|
if channel not in self.games: |
|
self.games[channel] = scrambleGame(self, channel) |
|
self.games[channel].scramble(msg.args, msg.prefix, msg.trailing) |
|
|
|
def saveScores(self): |
|
json.dump(self.scores, open(self.scoresFile, 'w')) |
|
|
|
def getScore(self, player, add=0): |
|
player = player.lower() |
|
if player not in self.scores: |
|
self.scores[player] = 0 |
|
if not add == 0: |
|
self.scores[player] += add |
|
self.saveScores() |
|
|
|
return self.scores[player] |
|
|
|
def getScoreNoWrite(self, player): |
|
if not player.lower() in self.scores: |
|
return 0 |
|
else: |
|
return self.getScore(player) |
|
|
|
def ondisable(self): |
|
self.log.info("Scramble: Unload requested, ending games...") |
|
for game in self.games: |
|
self.games[game].gameover() |
|
self.saveScores() |
|
|
|
|
|
class scrambleGame: |
|
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 |
|
self.guesses = 0 |
|
# How many games in a row where nobody guessed |
|
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"] |
|
|
|
def gameover(self): |
|
self.clearTimers() |
|
self.running = False |
|
|
|
def clearTimers(self): |
|
self.clearTimer(self.nextTimer) |
|
self.clearTimer(self.hintTimer) |
|
|
|
def clearTimer(self, timer): |
|
if timer: |
|
timer.cancel() |
|
|
|
def scramble(self, args, prefix, trailing): |
|
sender = prefix.nick |
|
cmd = messageHasCommand(".scrambleon", trailing) |
|
if cmd and not self.running: |
|
self.running = True |
|
self.startScramble() |
|
return |
|
cmd = messageHasCommand(".scrambleoff", trailing) |
|
if cmd and self.running: |
|
self.gameover() |
|
self.running = False |
|
return |
|
cmd = messageHasCommand(".scramble top", trailing) |
|
if cmd: |
|
sortedscores = [] |
|
for player in self.master.scores: |
|
sortedscores.append({'name': player, 'score': self.master.scores[player]}) |
|
sortedscores = sorted(sortedscores, key=itemgetter('score')) |
|
sortedscores.reverse() |
|
numScores = len(sortedscores) |
|
if numScores > 3: |
|
numScores = 3 |
|
resp = "Top %s: " % str(numScores) |
|
which = 1 |
|
while which <= numScores: |
|
resp += "%s: %s, " % (sortedscores[which - 1]["name"], sortedscores[which - 1]["score"]) |
|
which += 1 |
|
self.master.bot.act_PRIVMSG(self.channel, resp[:-2]) |
|
cmd = messageHasCommand(".scramble score", trailing) |
|
if cmd: |
|
someone = cmd.args.strip() |
|
if len(someone) > 0: |
|
self.master.bot.act_PRIVMSG(self.channel, "%s: %s has a score of %s" % |
|
(sender, someone, self.master.getScoreNoWrite(someone))) |
|
else: |
|
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) |
|
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)) |
|
self.currentWord = None |
|
self.clearTimers() |
|
self.hintsGiven = 0 |
|
self.nextTimer = Timer(self.delayNext, self.startNewWord) |
|
self.nextTimer.start() |
|
self.guesses = 0 |
|
else: |
|
self.guesses += 1 |
|
|
|
def startScramble(self): |
|
self.clearTimer(self.nextTimer) |
|
self.nextTimer = Timer(0, self.startNewWord) |
|
self.nextTimer.start() |
|
|
|
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)) |
|
|
|
self.clearTimer(self.hintTimer) |
|
self.hintTimer = Timer(self.delayHint, self.giveHint) |
|
self.hintTimer.start() |
|
|
|
def giveHint(self): |
|
self.hintsGiven += 1 |
|
|
|
if self.hintsGiven >= len(self.currentWord) or self.hintsGiven > self.maxHints: |
|
self.abortWord() |
|
return |
|
|
|
blanks = "" |
|
for letter in list(self.currentWord): |
|
if letter == " ": |
|
blanks += " " |
|
else: |
|
blanks += "_" |
|
partFromWord = self.currentWord[0:self.hintsGiven] |
|
partFromBlanks = blanks[self.hintsGiven:] |
|
hintstr = partFromWord + partFromBlanks |
|
|
|
self.master.bot.act_PRIVMSG(self.channel, "Hint: - %s" % (hintstr)) |
|
|
|
self.clearTimer(self.hintTimer) |
|
self.hintTimer = Timer(self.delayHint, self.giveHint) |
|
self.hintTimer.start() |
|
|
|
def abortWord(self): |
|
cur = self.currentWord |
|
self.currentWord = None |
|
self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was %s. Next word in %s seconds." % |
|
(cur, self.delayNext)) |
|
self.hintsGiven = 0 |
|
self.clearTimer(self.nextTimer) |
|
|
|
if self.guesses == 0: |
|
self.gamesWithoutGuesses += 1 |
|
if self.gamesWithoutGuesses >= self.abortAfterNoGuesses: |
|
self.master.bot.act_PRIVMSG(self.channel, "No one seems to be playing - type .scrambleon to " |
|
"start again.") |
|
self.gameover() |
|
return |
|
else: |
|
self.gamesWithoutGuesses = 0 |
|
|
|
self.nextTimer = Timer(self.delayNext, self.startNewWord) |
|
self.nextTimer.start() |
|
|
|
def pickWord(self): |
|
f = open(self.master.wordsFile, "r") |
|
skip = random.randint(0, self.master.wordsCount) |
|
while skip >= 0: |
|
f.readline() |
|
skip -= 1 |
|
picked = f.readline().strip().lower() |
|
f.close() |
|
return picked |
|
|
|
def scrambleWord(self, word): |
|
scrambled = "" |
|
for subword in word.split(" "): |
|
scrambled += self.scrambleIndividualWord(subword) + " " |
|
return scrambled.strip() |
|
|
|
def scrambleIndividualWord(self, word): |
|
scrambled = list(word) |
|
random.shuffle(scrambled) |
|
return ''.join(scrambled).lower()
|
|
|