]> jfr.im git - irc/rizon/acid.git/blame - pyva/pyva/src/main/python/trivia/trivia_engine.py
Remote stray debug println
[irc/rizon/acid.git] / pyva / pyva / src / main / python / trivia / trivia_engine.py
CommitLineData
685e346e
A
1# part of psm_trivia
2# provides the Trivia class for channels
3# Also I hate trivia bots.
4
5import string
6from datetime import datetime, timedelta
7import task
8
9class Trivia(object):
10 # state vars:
11 # constructor:
12 ## string cname: channel name
13 ## PSModule mod: the module calling us
14 ## string theme: the name of the theme table
15 ## int question_number: the number of questions to ask (if 0, unlimited)
16 #
17 # initialized elsewhere:
18 # string[] answer: the answer to the question being asked
19 # int asked_questions: the amount of questions already asked
20 # int hints_given: the number of hints already given; reset to 0 by ask and
21 # give_hint.
22 # string winner: winner of the last round
23 # int streak: number of times the winner responded to a question
24 # datetime started: time when we asked the question
25 asked_questions = 0
26 hints_given = 0
27 winner = ""
28 streak = 0
29 started = None
30
31 def __init__(self, cname, mod, theme, qnum):
32 self.cname = cname
33 self.module = mod
34 self.theme = theme
35 self.question_number = qnum
36 self.dbp = mod.dbp
37
38 # Not repeating so people don't have to wait until forever
39 # between answered questions
40 self.question_timer = task.LoopingCall(self.ask)
41 self.question_timer.start(3, False)
42 if qnum == 0:
43 self.module.msg(self.cname, "Starting round of trivia. Questions: unlimited")
44 else:
45 self.module.msg(self.cname, "Starting round of trivia. Questions: %d" % qnum)
46
47 def ask(self):
48 '''Asks a question to the channel.'''
49 if self.question_number > 0:
50 if self.asked_questions >= self.question_number:
51 # call parent to remove reference from dict so GC cleans us up
52 self.module.stop_trivia(self.cname, False)
53 return
54
55 # Silly db design is silly
56 try:
57 self.dbp.execute("SELECT question, answer FROM `trivia_questions_%s` ORDER BY rand() LIMIT 1"
58 % self.theme)
59 except Exception, ex:
60 self.module.elog.error('Unable to look up trivia question: %s' % ex)
61 return
62 resultset = self.dbp.fetchone()
63 # If there is a *, multiple answers may be possible. Silly Trivia DB doing
64 # silly things.
65 self.answer = unicode(resultset[1]).split('*')
66
67 self.asked_questions += 1
68 self.hints_given = 0
69
70 self.hint_timer = task.LoopingCall(self.give_hint)
71 self.hint_timer.start(10, False)
72
73 # SOME questions have a question mark already appended, some don't.
74 # We're gonna keep compatibility here and append a question mark.
75 self.module.msg(self.cname, "%d. %s?" % (self.asked_questions, resultset[0]))
76 self.started = datetime.now()
77
78 def give_hint(self):
79 '''Gives a hint, increases the hints_given variable and sets timer for
80 the next hint being given.'''
81 factor = 0
82 end = 0
83
84 if self.hints_given == 0:
85 factor = 0.25
86 self.hint_timer = task.LoopingCall(self.give_hint)
87 self.hint_timer.start(10, False)
88 elif self.hints_given == 1:
89 if len(self.answer[0]) < 20:
90 factor = 0.5
91 else:
92 factor = 0.4
93 self.hint_timer = task.LoopingCall(self.give_hint)
94 self.hint_timer.start(20, False)
95 # Also show some parts of the ending if last hint
96 elif self.hints_given == 2:
97 if len(self.answer[0]) < 20:
98 factor = 0.5
99 else:
100 factor = 0.4
101 if len(self.answer[0]) >= 32:
102 end = 7
103 elif len(self.answer[0]) >= 24:
104 end = 5
105 elif len(self.answer[0]) >= 16:
106 end = 3
107 elif len(self.answer[0]) >= 8:
108 end = 2
109 elif len(self.answer[0]) >= 4:
110 end = 1
111 self.hint_timer = task.LoopingCall(self.give_hint)
112 self.hint_timer.start(20, False)
113 elif self.hints_given == 3:
114 #End the question!
115 self.module.msg(self.cname, "Time's up! The answer was: " + self.answer[0])
116 self.answer = []
117 self.hints_given = 0
118 self.hint_timer = task.LoopingCall(self.ask)
119 self.hint_timer.start(5, False)
120 self.streak = 0
121 return
122
123 # Length from the beginning of the string to be shown
124 length = (int)(len(self.answer[0]) * factor)
125 # substring from the beginning to length to be shown
126 showstr = self.answer[0][0:length]
127 # substring from the middle of the string to be hidden
128 hidestr = ""
129 # if "end", the number of characters to show at the end is not 0 only go until there
130 if end > 0:
131 hidestr = self.answer[0][length:len(self.answer[0])-end]
132 else:
133 hidestr = self.answer[0][length:]
134 tltable = dict(zip(map(ord, u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), map(ord, u'**************************************************************')))
135 hidestr = hidestr.translate(tltable)
136 # substring to be shown if there's an end
137 endstr = ""
138 if end > 0:
139 endstr = self.answer[0][len(self.answer[0])-end:]
140 # Hint to be sent
141 hint = showstr + hidestr + endstr
142 self.module.msg(self.cname, "Hint: " + hint)
143
144 self.hints_given += 1
145
146 def check_answer(self, nick, answer):
147 '''checks if a given answer is the one we're looking for. self.module
148 will need to call this; we'll only check for validity and they must
149 check if they even want to run this.
150 We'll also apply points/streak, though.'''
151 # Hey, smartass, don't answer before you know the question!
152 if self.started == None:
153 return
154
155 now = datetime.now()
156 tdiff = now - self.started
157 tdelta = tdiff.seconds + tdiff.microseconds/1000000.
158 # No human can answer in one second or less (compat with orig Trivia)
159 if tdelta <= 1:
160 return
161
162 found = False
163 for a in self.answer:
4f1ef484
A
164 try:
165 if a.lower() in answer.lower():
166 found = True
167 break
168 except:
169 pass # XXX unicode
685e346e
A
170
171 if not found:
172 return
173
174 if self.winner == nick:
175 self.streak += 1
176 else:
177 self.winner = nick
178 self.streak = 1
179
180 if self.hints_given == 0:
181 points = 5
182 else:
183 points = 4 - self.hints_given
184
185 points *= self.streak
186
187 # XXX: We're COMPLETELY ignoring RFC1459 casemapping here
188 cid = self.module.get_cid(self.cname)
189 try:
190 self.dbp.execute('SELECT points, fastest_time, highest_streak FROM trivia_scores WHERE nick = %s AND channel = %s',
191 (nick, cid))
192 except Exception, ex:
193 self.module.elog.error('Unable to look up trivia scores: %s' % ex)
194 return
195 res = self.dbp.fetchone()
196 totalpoints = 0
197 fastest_time = 61 # greater than all of the timers added
198 highest_streak = 0
199 if res is not None:
200 (totalpoints, fastest_time, highest_streak) = res
201
202 totalpoints += points
203
204 self.module.msg(self.cname, "Winner: %s; Answer: %s; Time: %.3fs; Streak: %d; Points: %d; Total: %d"
205 % (nick, self.answer[0], tdelta, self.streak, points, totalpoints))
206
207 if fastest_time > tdelta:
208 fastest_time = tdelta
209 if self.streak > highest_streak:
210 highest_streak = self.streak
211
212 if res is not None:
213 self.dbp.execute('UPDATE trivia_scores SET points = %s, fastest_time = %s, highest_streak = %s WHERE nick = %s AND channel = %s',
214 (totalpoints, fastest_time, highest_streak, nick.lower(), cid))
215 else:
216 self.dbp.execute('INSERT INTO trivia_scores(points, fastest_time, highest_streak, nick, channel) VALUES (%s, %s, %s, %s, %s)',
217 (totalpoints, fastest_time, highest_streak, nick.lower(), cid))
218
219 self.question_timer.stop()
220 self.hint_timer.stop()
221 self.answer = []
222 self.hints_given = 0
223 self.hint_timer = task.LoopingCall(self.ask)
224 self.hint_timer.start(5, False)
225
226 def stop(self, forced):
227 '''Stops a round of trivia. This stops all timers associated with this
228 instance and sends a message to the channel that the round is done.
229 If forced is True, the message "Trivia stopped." will be shown,
230 otherwise the message will be "Round of trivia complete."
231 The calling instance is responsible of removing any references to us,
232 this includes the dict entry.'''
233 msg = ""
234
235 try:
236 self.question_timer.stop()
237 self.hint_timer.stop()
238 except AttributeError:
239 pass # We might be called before hint_timer's set up
240
241 if forced:
242 msg = "Trivia stopped."
243 else:
244 msg = "Round of trivia complete."
245 self.module.msg(self.cname,
246 msg + " '.trivia [number]' to start playing again.")
247