-
Notifications
You must be signed in to change notification settings - Fork 14
/
perchance.py
120 lines (103 loc) · 3.84 KB
/
perchance.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
import random
def perchance_parse(text):
definitions = {'output': []}
current_def = definitions['output']
for line_orig in text.split('\n'):
try:
line = line_orig
if line_orig.startswith('//'):
continue
line = line[:len(line.split(' //')[0])].rstrip()
if not line:
continue
if line[0] not in ' \t':
if line not in definitions:
definitions[line] = []
current_def = definitions[line]
else:
current_def.append(parse_line(line))
except Exception as err:
print('in line', line_orig)
raise err
return definitions
def parse_line(line):
line, weight, *_ = (line+'^1').split('^')
line = line.strip()
weight = weight.strip()
if '/' in weight:
a, b = weight.split('/')
a = a.strip()
b = b.strip()
weight = int(a) / int(b)
else:
weight = int(weight)
segments = [] # XXX parse from line
while '[' in line or '{' in line:
i_def = line.index('[') if '[' in line else 99999
i_multi = line.index('{') if '{' in line else 99999
if i_def < i_multi: # []
left, inner, line = extract_inside_paren(line, '[]')
segments.append({'type': 'raw', 'text': left})
if '.' in inner:
inner, mod = inner.split('.', 1)
else:
mod = ''
segments.append({'type': 'def', 'name': inner, 'mod': mod})
else: # {}
left, inner, line = extract_inside_paren(line, '{}')
segments.append({'type': 'raw', 'text': left})
if '|' in inner:
options = [s.strip() for s in inner.split('|')]
segments.append({'type': 'multi', 'options': options})
elif '-' in inner:
min_val, max_val = (int(s) for s in inner.split('-'))
segments.append(
{'type': 'range', 'min': min_val, 'max': max_val})
else:
segments.append({'type': 'raw', 'text': inner})
# remainder
segments.append({'type': 'raw', 'text': line})
return {
'weight': weight,
'segments': segments,
}
def extract_inside_paren(line, open_close):
c_open, c_close = open_close
left, line = line.split(c_open, 1)
inner, line = line.split(c_close, 1)
return left, inner, line
def perchance_gen(definitions):
return perchance_gen_inner(definitions, 'output').replace(r'\s', ' ')
def perchance_gen_inner(definitions, name):
options = definitions[name]
try:
choices = random.choices(options, [o['weight'] for o in options], k=1)
choice = choices[0]
except Exception as err:
print('in', name, 'options', options)
raise err
result = ''
for segment in choice['segments']:
try:
if segment['type'] == 'raw':
text = segment['text']
elif segment['type'] == 'def':
text = perchance_gen_inner(definitions, segment['name'])
if segment['mod'] == 'titleCase':
text = text.title()
if segment['mod'] == "upperCase":
text = text.upper()
elif segment['type'] == 'multi':
text = random.choice(segment['options'])
elif segment['type'] == 'range':
text = str(random.randrange(segment['min'], segment['max']))
else:
raise ValueError('Unknown segment ' + segment)
result += text
except Exception as err:
print('in segment', segment)
raise err
return result
if __name__ == "__main__":
definitions = perchance_parse(open('resources/perchance.txt').read())
print(perchance_gen(definitions))