]> jfr.im git - irc/rizon/acid.git/blame - pyva/pyva/src/main/python/trivia/trivia_engine.py
Cleanup e-Sim a little bit
[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 164 try:
384e3a00 165 # Issue #10: do not accept '200' when the correct answer is '20'
166 float(a)
167 if answer == a:
168 found = True
169 break
170 except ValueError:
969da989
O
171 try:
172 if a.lower() in answer.lower():
173 found = True
174 break
175 except:
176 pass # XXX unicode
685e346e
A
177
178 if not found:
179 return
180
181 if self.winner == nick:
182 self.streak += 1
183 else:
184 self.winner = nick
185 self.streak = 1
186
187 if self.hints_given == 0:
188 points = 5
189 else:
190 points = 4 - self.hints_given
191
192 points *= self.streak
193
194 # XXX: We're COMPLETELY ignoring RFC1459 casemapping here
195 cid = self.module.get_cid(self.cname)
196 try:
197 self.dbp.execute('SELECT points, fastest_time, highest_streak FROM trivia_scores WHERE nick = %s AND channel = %s',
198 (nick, cid))
199 except Exception, ex:
200 self.module.elog.error('Unable to look up trivia scores: %s' % ex)
201 return
202 res = self.dbp.fetchone()
203 totalpoints = 0
204 fastest_time = 61 # greater than all of the timers added
205 highest_streak = 0
206 if res is not None:
207 (totalpoints, fastest_time, highest_streak) = res
208
209 totalpoints += points
210
211 self.module.msg(self.cname, "Winner: %s; Answer: %s; Time: %.3fs; Streak: %d; Points: %d; Total: %d"
212 % (nick, self.answer[0], tdelta, self.streak, points, totalpoints))
213
214 if fastest_time > tdelta:
215 fastest_time = tdelta
216 if self.streak > highest_streak:
217 highest_streak = self.streak
218
219 if res is not None:
220 self.dbp.execute('UPDATE trivia_scores SET points = %s, fastest_time = %s, highest_streak = %s WHERE nick = %s AND channel = %s',
221 (totalpoints, fastest_time, highest_streak, nick.lower(), cid))
222 else:
223 self.dbp.execute('INSERT INTO trivia_scores(points, fastest_time, highest_streak, nick, channel) VALUES (%s, %s, %s, %s, %s)',
224 (totalpoints, fastest_time, highest_streak, nick.lower(), cid))
225
226 self.question_timer.stop()
227 self.hint_timer.stop()
228 self.answer = []
229 self.hints_given = 0
230 self.hint_timer = task.LoopingCall(self.ask)
231 self.hint_timer.start(5, False)
232
233 def stop(self, forced):
234 '''Stops a round of trivia. This stops all timers associated with this
235 instance and sends a message to the channel that the round is done.
236 If forced is True, the message "Trivia stopped." will be shown,
237 otherwise the message will be "Round of trivia complete."
238 The calling instance is responsible of removing any references to us,
239 this includes the dict entry.'''
240 msg = ""
241
242 try:
243 self.question_timer.stop()
244 self.hint_timer.stop()
245 except AttributeError:
246 pass # We might be called before hint_timer's set up
247
248 if forced:
249 msg = "Trivia stopped."
250 else:
251 msg = "Round of trivia complete."
252 self.module.msg(self.cname,
253 msg + " '.trivia [number]' to start playing again.")
254