Trivia: enhance code, and testStartStop()
parent
957a5532b3
commit
4f04c5cc13
|
@ -1,5 +1,6 @@
|
||||||
###
|
###
|
||||||
# Copyright (c) 2010, quantumlemur
|
# Copyright (c) 2010, quantumlemur
|
||||||
|
# Copyright (c) 2011, Valentin Lorentz
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -46,36 +47,47 @@ Trivia = conf.registerPlugin('Trivia')
|
||||||
# registry.Boolean(False, """Help for someConfigVariableName."""))
|
# registry.Boolean(False, """Help for someConfigVariableName."""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'blankChar',
|
conf.registerChannelValue(Trivia, 'blankChar',
|
||||||
registry.String('*', """The character used for a blank when displaying hints"""))
|
registry.String('*', """The character used for a blank when
|
||||||
|
displaying hints"""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'numHints',
|
conf.registerChannelValue(Trivia, 'numHints',
|
||||||
registry.PositiveInteger(3, """The number of hints to be given for each question"""))
|
registry.PositiveInteger(3, """The number of hints to be given for
|
||||||
|
each question"""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'timeout',
|
conf.registerChannelValue(Trivia, 'timeout',
|
||||||
registry.PositiveInteger(90, """The number of seconds to allow for each question"""))
|
registry.PositiveInteger(90, """The number of seconds to allow for
|
||||||
|
each question"""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'hintPercentage',
|
conf.registerChannelValue(Trivia, 'hintPercentage',
|
||||||
registry.Probability(0.25, """The fraction of the answer that should be revealed with each hint"""))
|
registry.Probability(0.25, """The fraction of the answer that
|
||||||
|
should be revealed with each hint"""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'flexibility',
|
conf.registerChannelValue(Trivia, 'flexibility',
|
||||||
registry.PositiveInteger(8, """The flexibility of the trivia answer checker. One typo will be allowed for every __ characters."""))
|
registry.PositiveInteger(8, """The flexibility of the trivia answer
|
||||||
|
checker. One typo will be allowed for every __ characters."""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'color',
|
conf.registerChannelValue(Trivia, 'color',
|
||||||
registry.PositiveInteger(10, """The mIRC color to use for trivia questions"""))
|
registry.PositiveInteger(10, """The mIRC color to use for trivia
|
||||||
|
questions"""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'inactiveShutoff',
|
conf.registerChannelValue(Trivia, 'inactiveShutoff',
|
||||||
registry.Integer(6, """The number of questions that can go unanswered before the trivia stops automatically."""))
|
registry.Integer(6, """The number of questions that can go
|
||||||
|
unanswered before the trivia stops automatically."""))
|
||||||
|
|
||||||
conf.registerGlobalValue(Trivia, 'scoreFile',
|
conf.registerGlobalValue(Trivia, 'scoreFile',
|
||||||
registry.String('scores.txt', """The path to the scores file. If it doesn't exist, it will be created."""))
|
registry.String('scores.txt', """The path to the scores file.
|
||||||
|
If it doesn't exist, it will be created."""))
|
||||||
|
|
||||||
conf.registerGlobalValue(Trivia, 'questionFile',
|
conf.registerGlobalValue(Trivia, 'questionFile',
|
||||||
registry.String('questions.txt', """The path to the questions file. If it doesn't exist, it will be created."""))
|
registry.String('questions.txt', """The path to the questions file.
|
||||||
|
If it doesn't exist, it will be created."""))
|
||||||
|
|
||||||
conf.registerChannelValue(Trivia, 'defaultRoundLength',
|
conf.registerChannelValue(Trivia, 'defaultRoundLength',
|
||||||
registry.PositiveInteger(10, """The default number of questions to be asked in a round of trivia."""))
|
registry.PositiveInteger(10, """The default number of questions to
|
||||||
|
be asked in a round of trivia."""))
|
||||||
|
|
||||||
conf.registerGlobalValue(Trivia, 'questionFileSeparator',
|
conf.registerGlobalValue(Trivia, 'questionFileSeparator',
|
||||||
registry.String('*', """The separator used between the questions and answers in your trivia file."""))
|
registry.String('*', """The separator used between the questions
|
||||||
|
and answers in your trivia file."""))
|
||||||
|
|
||||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
###
|
###
|
||||||
# Copyright (c) 2010, quantumlemur
|
# Copyright (c) 2010, quantumlemur
|
||||||
|
# Copyright (c) 2011, Valentin Lorentz
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -49,7 +50,7 @@ class Trivia(callbacks.Plugin):
|
||||||
This should describe *how* to use this plugin."""
|
This should describe *how* to use this plugin."""
|
||||||
threaded = True
|
threaded = True
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, irc):
|
def __init__(self, irc):
|
||||||
self.__parent = super(Trivia, self)
|
self.__parent = super(Trivia, self)
|
||||||
self.__parent.__init__(irc)
|
self.__parent.__init__(irc)
|
||||||
|
@ -58,7 +59,11 @@ class Trivia(callbacks.Plugin):
|
||||||
questionfile = self.registryValue('questionFile')
|
questionfile = self.registryValue('questionFile')
|
||||||
if not os.path.exists(questionfile):
|
if not os.path.exists(questionfile):
|
||||||
f = open(questionfile, 'w')
|
f = open(questionfile, 'w')
|
||||||
f.write('If you\'re seeing this question, it means that the questions file that you specified wasn\'t found, and a new one has been created. Go get some questions!%sNo questions found' % self.registryValue('questionFileSeparator'))
|
f.write(('If you\'re seeing this question, it means that the '
|
||||||
|
'questions file that you specified wasn\'t found, and '
|
||||||
|
'a new one has been created. Go get some questions!%s'
|
||||||
|
'No questions found') %
|
||||||
|
self.registryValue('questionFileSeparator'))
|
||||||
f.close()
|
f.close()
|
||||||
self.scorefile = self.registryValue('scoreFile')
|
self.scorefile = self.registryValue('scoreFile')
|
||||||
if not os.path.exists(self.scorefile):
|
if not os.path.exists(self.scorefile):
|
||||||
|
@ -82,20 +87,20 @@ class Trivia(callbacks.Plugin):
|
||||||
if channel in self.games:
|
if channel in self.games:
|
||||||
self.games[channel].answer(msg)
|
self.games[channel].answer(msg)
|
||||||
|
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
def __init__(self, irc, channel, num, registryValue, games, scores, scorefile):
|
def __init__(self, irc, channel, num, plugin):
|
||||||
self.rng = random.Random()
|
self.rng = random.Random()
|
||||||
self.rng.seed()
|
self.rng.seed()
|
||||||
self.registryValue = registryValue
|
self.registryValue = plugin.registryValue
|
||||||
self.irc = irc
|
self.irc = irc
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
self.num = num
|
self.num = num
|
||||||
self.numAsked = 0
|
self.numAsked = 0
|
||||||
self.hints = 0
|
self.hints = 0
|
||||||
self.games = games
|
self.games = plugin.games
|
||||||
self.scores = scores
|
self.scores = plugin.scores
|
||||||
self.scorefile = scorefile
|
self.scorefile = plugin.scorefile
|
||||||
self.questionfile = self.registryValue('questionFile')
|
self.questionfile = self.registryValue('questionFile')
|
||||||
self.total = num
|
self.total = num
|
||||||
self.active = True
|
self.active = True
|
||||||
|
@ -113,12 +118,14 @@ class Trivia(callbacks.Plugin):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
self.newquestion()
|
self.newquestion()
|
||||||
|
|
||||||
|
|
||||||
def newquestion(self):
|
def newquestion(self):
|
||||||
|
inactiveShutoff = self.registryValue('inactiveShutoff',
|
||||||
|
self.channel)
|
||||||
if self.num == 0:
|
if self.num == 0:
|
||||||
self.active = False
|
self.active = False
|
||||||
elif self.unanswered > self.registryValue('inactiveShutoff', self.channel) and self.registryValue('inactiveShutoff', self.channel) >= 0:
|
elif self.unanswered > inactiveShutoff and inactiveShutoff >= 0:
|
||||||
self.reply('Seems like no one\'s playing any more.')
|
self.reply('Seems like no one\'s playing any more.')
|
||||||
self.active = False
|
self.active = False
|
||||||
elif len(self.questions) == 0:
|
elif len(self.questions) == 0:
|
||||||
|
@ -135,10 +142,14 @@ class Trivia(callbacks.Plugin):
|
||||||
sep = self.registryValue('questionFileSeparator')
|
sep = self.registryValue('questionFileSeparator')
|
||||||
self.q = q[:q.find(sep)]
|
self.q = q[:q.find(sep)]
|
||||||
self.a = q[q.find(sep)+len(sep):].split(sep)
|
self.a = q[q.find(sep)+len(sep):].split(sep)
|
||||||
self.reply('\x03%s#%d of %d: %s' % (self.registryValue('color', self.channel), self.numAsked, self.total, self.q))
|
color = self.registryValue('color', self.channel)
|
||||||
|
self.reply('\x03%s#%d of %d: %s' % (color, self.numAsked,
|
||||||
|
self.total, self.q))
|
||||||
def event():
|
def event():
|
||||||
self.timedEvent()
|
self.timedEvent()
|
||||||
eventTime = time.time() + self.registryValue('timeout', self.channel) / (self.registryValue('numHints', self.channel) + 1)
|
timeout = self.registryValue('timeout', self.channel)
|
||||||
|
numHints = self.registryValue('numHints', self.channel)
|
||||||
|
eventTime = time.time() + timeout / (numHints + 1)
|
||||||
if self.active:
|
if self.active:
|
||||||
schedule.addEvent(event, eventTime, 'next_%s' % self.channel)
|
schedule.addEvent(event, eventTime, 'next_%s' % self.channel)
|
||||||
|
|
||||||
|
@ -161,7 +172,7 @@ class Trivia(callbacks.Plugin):
|
||||||
max = 3
|
max = 3
|
||||||
if len(sorted) < max:
|
if len(sorted) < max:
|
||||||
max = len(sorted)
|
max = len(sorted)
|
||||||
# self.reply('max: %d. len: %d' % (max, len(sorted)))
|
#self.reply('max: %d. len: %d' % (max, len(sorted)))
|
||||||
s = 'Top finishers: '
|
s = 'Top finishers: '
|
||||||
if max > 0:
|
if max > 0:
|
||||||
recipients = []
|
recipients = []
|
||||||
|
@ -171,7 +182,7 @@ class Trivia(callbacks.Plugin):
|
||||||
s = '%s %s %s.' % (s, item[0], item[1])
|
s = '%s %s %s.' % (s, item[0], item[1])
|
||||||
self.reply(s)
|
self.reply(s)
|
||||||
del self.games[self.channel]
|
del self.games[self.channel]
|
||||||
|
|
||||||
|
|
||||||
def timedEvent(self):
|
def timedEvent(self):
|
||||||
if self.hints >= self.registryValue('numHints', self.channel):
|
if self.hints >= self.registryValue('numHints', self.channel):
|
||||||
|
@ -185,16 +196,20 @@ class Trivia(callbacks.Plugin):
|
||||||
def hint(self):
|
def hint(self):
|
||||||
self.hints += 1
|
self.hints += 1
|
||||||
ans = self.a[0]
|
ans = self.a[0]
|
||||||
divider = int( math.ceil( len(ans) * self.registryValue('hintPercentage', self.channel) * self.hints ) )
|
hintPercentage = self.registryValue('hintPercentage', self.channel)
|
||||||
|
divider = int(math.ceil(len(ans) * hintPercentage * self.hints ))
|
||||||
if divider == len(ans):
|
if divider == len(ans):
|
||||||
divider -= 1
|
divider -= 1
|
||||||
show = ans[ : divider]
|
show = ans[ : divider]
|
||||||
blank = ans[divider : ]
|
blank = ans[divider : ]
|
||||||
blank = re.sub('\w', self.registryValue('blankChar', self.channel), blank)
|
blankChar = self.registryValue('blankChar', self.channel)
|
||||||
|
blank = re.sub('\w', blankChar, blank)
|
||||||
self.reply('HINT: %s%s' % (show, blank))
|
self.reply('HINT: %s%s' % (show, blank))
|
||||||
def event():
|
def event():
|
||||||
self.timedEvent()
|
self.timedEvent()
|
||||||
eventTime = time.time() + self.registryValue('timeout', self.channel) / (self.registryValue('numHints', self.channel)+1)
|
timeout = self.registryValue('timeout', self.channel)
|
||||||
|
numHints = self.registryValue('numHints', self.channel)
|
||||||
|
eventTime = time.time() + timeout / (numHints + 1)
|
||||||
if self.active:
|
if self.active:
|
||||||
schedule.addEvent(event, eventTime, 'next_%s' % self.channel)
|
schedule.addEvent(event, eventTime, 'next_%s' % self.channel)
|
||||||
|
|
||||||
|
@ -203,10 +218,11 @@ class Trivia(callbacks.Plugin):
|
||||||
correct = False
|
correct = False
|
||||||
for ans in self.a:
|
for ans in self.a:
|
||||||
dist = self.DL(str.lower(msg.args[1]), str.lower(ans))
|
dist = self.DL(str.lower(msg.args[1]), str.lower(ans))
|
||||||
if dist <= len(ans)/self.registryValue('flexibility', self.channel):
|
flexibility = self.registryValue('flexibility', self.channel)
|
||||||
|
if dist <= len(ans) / flexibility:
|
||||||
correct = True
|
correct = True
|
||||||
# if self.registryValue('debug'):
|
#if self.registryValue('debug'):
|
||||||
# self.reply('Distance: %d' % dist)
|
# self.reply('Distance: %d' % dist)
|
||||||
if correct:
|
if correct:
|
||||||
if not msg.nick in self.scores:
|
if not msg.nick in self.scores:
|
||||||
self.scores[msg.nick] = 0
|
self.scores[msg.nick] = 0
|
||||||
|
@ -215,7 +231,8 @@ class Trivia(callbacks.Plugin):
|
||||||
self.roundscores[msg.nick] = 0
|
self.roundscores[msg.nick] = 0
|
||||||
self.roundscores[msg.nick] += 1
|
self.roundscores[msg.nick] += 1
|
||||||
self.unanswered = 0
|
self.unanswered = 0
|
||||||
self.reply('%s got it! The full answer was: %s. Points: %d' % (msg.nick, self.a[0], self.scores[msg.nick]))
|
self.reply('%s got it! The full answer was: %s. Points: %d' %
|
||||||
|
(msg.nick, self.a[0], self.scores[msg.nick]))
|
||||||
schedule.removeEvent('next_%s' % self.channel)
|
schedule.removeEvent('next_%s' % self.channel)
|
||||||
self.writeScores()
|
self.writeScores()
|
||||||
self.newquestion()
|
self.newquestion()
|
||||||
|
@ -241,27 +258,30 @@ class Trivia(callbacks.Plugin):
|
||||||
# Python lists wrap around for negative indices, so put the
|
# Python lists wrap around for negative indices, so put the
|
||||||
# leftmost column at the *end* of the list. This matches with
|
# leftmost column at the *end* of the list. This matches with
|
||||||
# the zero-indexed strings and saves extra calculation.
|
# the zero-indexed strings and saves extra calculation.
|
||||||
twoago, oneago, thisrow = oneago, thisrow, [0] * len(seq2) + [x + 1]
|
twoago, oneago, thisrow = oneago, thisrow, [0]*len(seq2)+[x+1]
|
||||||
for y in xrange(len(seq2)):
|
for y in xrange(len(seq2)):
|
||||||
delcost = oneago[y] + 1
|
delcost = oneago[y] + 1
|
||||||
addcost = thisrow[y - 1] + 1
|
addcost = thisrow[y - 1] + 1
|
||||||
subcost = oneago[y - 1] + (seq1[x] != seq2[y])
|
subcost = oneago[y - 1] + (seq1[x] != seq2[y])
|
||||||
thisrow[y] = min(delcost, addcost, subcost)
|
thisrow[y] = min(delcost, addcost, subcost)
|
||||||
# This block deals with transpositions
|
# This block deals with transpositions
|
||||||
if (x > 0 and y > 0 and seq1[x] == seq2[y - 1] and seq1[x-1] == seq2[y] and seq1[x] != seq2[y]):
|
if x > 0 and y > 0 and seq1[x] == seq2[y - 1] and \
|
||||||
|
seq1[x-1] == seq2[y] and seq1[x] != seq2[y]:
|
||||||
thisrow[y] = min(thisrow[y], twoago[y - 2] + 1)
|
thisrow[y] = min(thisrow[y], twoago[y - 2] + 1)
|
||||||
return thisrow[len(seq2) - 1]
|
return thisrow[len(seq2) - 1]
|
||||||
|
|
||||||
|
|
||||||
def trivia(self, irc, msg, args, channel, num):
|
def start(self, irc, msg, args, channel, num):
|
||||||
"""[<channel>] [<number of questions>]
|
"""[<channel>] [<number of questions>]
|
||||||
|
|
||||||
Starts a game of trivia. <channel> is only necessary if the message isn't sent in the channel itself."""
|
Starts a game of trivia. <channel> is only necessary if the message
|
||||||
|
isn't sent in the channel itself."""
|
||||||
if num == None:
|
if num == None:
|
||||||
num = self.registryValue('defaultRoundLength', channel)
|
num = self.registryValue('defaultRoundLength', channel)
|
||||||
# elif num > 100:
|
#elif num > 100:
|
||||||
# irc.reply('sorry, for now, you can\'t start games with more than 100 questions :(')
|
# irc.reply('sorry, for now, you can\'t start games with more '
|
||||||
# num = 100
|
# 'than 100 questions :(')
|
||||||
|
# num = 100
|
||||||
channel = ircutils.toLower(channel)
|
channel = ircutils.toLower(channel)
|
||||||
if channel in self.games:
|
if channel in self.games:
|
||||||
if not self.games[channel].active:
|
if not self.games[channel].active:
|
||||||
|
@ -276,20 +296,21 @@ class Trivia(callbacks.Plugin):
|
||||||
self.games[channel].total += num
|
self.games[channel].total += num
|
||||||
irc.reply('%d questions added to active game!' % num)
|
irc.reply('%d questions added to active game!' % num)
|
||||||
else:
|
else:
|
||||||
self.games[channel] = self.Game(irc, channel, num, self.registryValue, self.games, self.scores, self.scorefile)
|
self.games[channel] = self.Game(irc, channel, num, self)
|
||||||
irc.noReply()
|
irc.noReply()
|
||||||
trivia = wrap(trivia, ['channel', optional('positiveInt')])
|
start = wrap(start, ['channel', optional('positiveInt')])
|
||||||
|
|
||||||
|
|
||||||
def strivia(self, irc, msg, args, channel):
|
def stop(self, irc, msg, args, channel):
|
||||||
"""[<channel>]
|
"""[<channel>]
|
||||||
|
|
||||||
Stops a running game of trivia. <channel> is only necessary if the message isn't sent in the channel itself."""
|
Stops a running game of trivia. <channel> is only necessary if the
|
||||||
|
message isn't sent in the channel itself."""
|
||||||
channel = ircutils.toLower(channel)
|
channel = ircutils.toLower(channel)
|
||||||
try:
|
try:
|
||||||
schedule.removeEvent('next_%s' % channel)
|
schedule.removeEvent('next_%s' % channel)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
irc.error('No trivia started')
|
||||||
if channel in self.games:
|
if channel in self.games:
|
||||||
if self.games[channel].active:
|
if self.games[channel].active:
|
||||||
self.games[channel].stop()
|
self.games[channel].stop()
|
||||||
|
@ -298,8 +319,8 @@ class Trivia(callbacks.Plugin):
|
||||||
irc.reply('Trivia stopped')
|
irc.reply('Trivia stopped')
|
||||||
else:
|
else:
|
||||||
irc.noReply()
|
irc.noReply()
|
||||||
strivia = wrap(strivia, ['channel'])
|
stop = wrap(stop, ['channel'])
|
||||||
|
|
||||||
|
|
||||||
Class = Trivia
|
Class = Trivia
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
###
|
###
|
||||||
# Copyright (c) 2010, quantumlemur
|
# Copyright (c) 2011, Valentin Lorentz
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -30,8 +30,13 @@
|
||||||
|
|
||||||
from supybot.test import *
|
from supybot.test import *
|
||||||
|
|
||||||
class TriviaTestCase(PluginTestCase):
|
class TriviaTestCase(ChannelPluginTestCase):
|
||||||
plugins = ('Trivia',)
|
plugins = ('Trivia',)
|
||||||
|
|
||||||
|
def testStartStop(self):
|
||||||
|
self.assertRegexp('start', '...#1 of 10:.*')
|
||||||
|
self.assertResponse('stop', 'Trivia stopping.')
|
||||||
|
self.assertError('stop')
|
||||||
|
|
||||||
|
|
||||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
||||||
|
|
Loading…
Reference in New Issue