-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutil.py
314 lines (250 loc) · 8.49 KB
/
util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
"""
Utilities for Puzzle parser.
Contains miscellaneous function definitions, variable declarations, and
handlers for all the different puzzle types.
"""
import re
def timestr_to_seconds(tstr: str) -> int | None:
"""
Convert a str on the form "xmys" to a number of seconds (e.g. 1m20s --> 80)
"""
try:
lst = tstr.split('m')
lst[1] = lst[1].split('s')
min = int(lst[0])
sec = int(lst[1][0])
return 60*min + sec
except:
return None
def get_fractional_score(score_str: str) -> int | None:
"""
Many games give score on the form "./.", e.g. wordle's 2/6, 3/6 etc.
This function takes a score string and returns the first number.
Alternatively if it says "X/6" for instance, return 0.
If the string does not match the regex "[1-6X]\/[4-6]", return None.
Arguments:
- score_str: Score string from parser
Returns:
- Score, cast as int.
"""
if re.match(r'[1-6X]\/[4-6]',score_str):
return 0 if score_str[0]=='X' else int(score_str[0])
else:
return None
def get_int_score(score_str: str) -> int | None:
"""
Get single-number score, e.g. from Countryle which has integer scoring.
Return None if score_str is not a number.
Arguments:
- score_str: Score string from parser
Returns:
- Numeric score
"""
try:
s = int(score_str)
except ValueError:
s = None
finally:
return s
def get_quordle_score(score_str: str) -> int | None:
"""
Get score from quordle game. Returns number of attemps (max digit in score_str).
If game failed (score_str contains at least one 0), return 0
If score_str does not conform to regex of four digits, return None.
Arguments:
- score_str: Score string from parser
Returns:
- Numeric score
"""
if re.match(r'^\d{4}$',score_str):
lst = [int(x) for x in score_str]
return 0 if 0 in lst else max(lst)
elif re.match(r'^\d{5}$',score_str):
return 0 if score_str[0]=="0" else 10
else:
return None
def score_converter(score_str: str) -> int | None:
"""
Master function to convert any score str in dataset to a numeric value.
Since results from different puzzles come in different forms, we would like them
to be cast to numeric forms for statistical purposes.
To convert scores from undefined forms, a new converter must be added here.
If the input does not adhere to defined forms, returns None
Arguments:
- score_str: A score string from some puzzle, e.g. "3/6" from wordle
Returns:
- s: Numeric score, should always be numeric type (int, float etc.)
"""
if (s:=get_fractional_score(score_str)) is not None:
return s
elif (s:=timestr_to_seconds(score_str)) is not None:
return s
elif (s:= get_quordle_score(score_str)) is not None:
return s
elif (s:= get_int_score(score_str)) is not None:
return s
else:
return None
def mini_handler(words: list[str]) -> tuple[str,None]:
"""
Return score and None-type game_num for Mini Crossword, given message
"""
return words[1], None
def wordle_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for wordle, given message
"""
return words[2], words[1]
def nerdle_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for nerdle, given message
"""
return words[2], words[1]
def mini_nerdle_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for mini nerdle, given message
"""
return words[3], words[2]
def micro_nerdle_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for micro nerdle, given message
"""
return words[3], words[2]
def instant_nerdle_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for instant nerdle, given message
"""
return words[-2]+words[-1][:-1], words[5]
def quordle_handler(words: list[str]) -> tuple[str,str,str]:
"""
Parser for quordle score because it's output weirdly.
Handles regular quordle and Sequence-variant.
Redirects to octordle handler in relevant cases since
first word identifier ("Daily") is the same.
Arguments:
- words: List of words in message
Returns:
- game_num: Game number for the day
- score: Sorted score, e.g. 4567. Any failed words will be zeros at the start, e.g. 0078
"""
# Pass to Octordle handler if applicable
if "Octordle" in words:
return octordle_handler(words)
# Handle Sequence-Quordle variant
if words[1] == 'Sequence':
words = words[1:]
game_type = 'Sequence Quordle'
else:
game_type = 'Quordle'
# Some early games had hashtags in the message, so clear this out
game_num = words[2] if '#' not in words[2] else words[2][1:]
line1 = [x for x in words[3]]
line2 = [x for x in words[4]]
# Check if first entry is fail
s1 = 0 if line1[0] == '🟥' else line1[0]
# If any other entries than first is fail, second score is 0. Set according to if first was fail
if '🟥' in line1[1:]:
s2 = 0
elif s1 == 0:
s2 = line1[1]
else:
s2 = line1[3]
s3 = 0 if line2[0] == '🟥' else line2[0]
if '🟥' in line2[1:]:
s4 = 0
elif s3 == 0:
s4 = line2[1]
else:
s4 = line2[3]
for i,s in enumerate(score_lst:= [s1,s2,s3,s4]):
if s == '🔟':
score_lst[i] = 10
lst = [int(s) for s in score_lst]
lst.sort()
lst_str = [str(x) for x in lst]
score = "".join(lst_str)
return game_type, score, game_num
def octordle_handler(words: list[str]) -> tuple[str,str,str]:
""""
Return type, score and game number for Octordle, or Sequence variant.
"""
if words[1] == 'Sequence':
words = words[1:]
game_type = 'Sequence Octordle'
else:
game_type = 'Octordle'
game_num = words[2][1:]
score = words[-1]
return game_type, score, game_num
def flagle_game_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for flagle-game, given message
"""
return words[3], words[1][1:]
def flagle_io_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for flagle.io, given message
"""
return words[2], words[1][1:]
def worldle_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for worldle, given message
"""
return words[2], words[1][1:]
def angle_wtf_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for angle.wtf, given message
"""
return words[2], words[1][1:]
def countryle_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for countryle, given message
No loss-condition, but you may give up, in which case score is 0
"""
if "Gave" in words:
return "0", words[1]
else:
return words[4], words[1]
def capitale_handler(words: list[str]) -> tuple[str,str]:
"""
Return score and game number for capitale, given message
No loss-condition, but you may give up, in which case score is 0
"""
if "Gave" in words:
return "0", words[1]
else:
return words[4], words[1]
# Dict with key being first word in a message, and value being corresponding game
game_dict = {
'Mini1:': 'Mini',
'Wordle': 'Wordle',
'nerdlegame': 'Nerdle',
'mini': 'Mini nerdle',
'micro': 'Micro nerdle',
'🟩': 'Instant nerdle',
'Daily': 'Quordle',
'Flagle': 'Flagle-game',
'#Flagle': 'Flagle.io',
'#Worldle': 'Worldle',
'#Angle': 'Angle.wtf',
'#Countryle': 'Countryle',
'#Capitale': 'Capitale'
}
# List of puzzle names
puzzle_list = list(game_dict.values())
# All handler functions. Keep in same order as puzzle_list!
handler_functions = [mini_handler,
wordle_handler,
nerdle_handler,
mini_nerdle_handler,
micro_nerdle_handler,
instant_nerdle_handler,
quordle_handler,
flagle_game_handler,
flagle_io_handler,
worldle_handler,
angle_wtf_handler,
countryle_handler,
capitale_handler]
# Zipper
handler_dict = dict(zip(puzzle_list,handler_functions))