190 lines
5.3 KiB
Python
190 lines
5.3 KiB
Python
#!python3
|
|
"""this one should be a little more ordered and also work with suffixes."""
|
|
import random
|
|
|
|
def dots(something):
|
|
t = something[:]
|
|
for i in range(3, int(len(something)*4/3)-1, 4):
|
|
t = t[:i] + "." + t[i:]
|
|
return t
|
|
|
|
class HSNode:
|
|
def __init__(self, parent, code, description):
|
|
self.parent = parent
|
|
self.code = code
|
|
self.description = description
|
|
self.nexts = dict()
|
|
self.suffixes = None
|
|
|
|
def printout(self):
|
|
code_with_dots = dots(self.code)
|
|
print(f"{code_with_dots}: {self.description}")
|
|
|
|
if self.suffixes is not None:
|
|
self.suffixes.printout()
|
|
|
|
for n in sorted(self.nexts.keys()):
|
|
self.nexts[n].printout()
|
|
|
|
def add_code(self, code, description):
|
|
if len(code) == 0:
|
|
raise Exception(f"some kinda problem trying to add a blank code to a listing for {self.code}")
|
|
|
|
digit = code[0]
|
|
|
|
if len(code) == 1:
|
|
if digit not in self.nexts:
|
|
self.nexts[digit] = HSNode(self, self.code + digit, description)
|
|
else:
|
|
self.printout()
|
|
raise Exception(f"Collision found! {self.code}, adding {code}")
|
|
return self.nexts[digit]
|
|
else:
|
|
if digit not in self.nexts:
|
|
self.nexts[digit] = HSNode(self, self.code + digit, "HUH???")
|
|
return self.nexts[digit].add_code(code[1:], description)
|
|
|
|
def add_suffix(self, code, description):
|
|
if self.suffixes is None:
|
|
self.suffixes = HSNode(None, "-", f"suffixes for {self.code}")
|
|
|
|
return self.suffixes.add_code(code, description)
|
|
|
|
def random_child(self, stopping_chances):
|
|
this_threshhold, rest_chances = stopping_chances[0], stopping_chances[1:]
|
|
if random.random() < this_threshhold:
|
|
return self
|
|
|
|
if not self.nexts:
|
|
return self
|
|
|
|
digit, next_node = random.choice(list(self.nexts.items()))
|
|
|
|
return next_node.random_child(rest_chances)
|
|
|
|
|
|
# actually build the data from the data file
|
|
taxonomy = HSNode(None, "", "an instrument")
|
|
|
|
with open("Hornbostel-Sachs") as f:
|
|
current = taxonomy
|
|
|
|
for line in f.readlines():
|
|
code = line.split(" ")[0].replace(".", "")
|
|
description = " ".join(line.split(" ")[1:]).strip()
|
|
|
|
if code.startswith("#"):
|
|
pass
|
|
elif code.startswith("-"):
|
|
current.add_suffix(code.replace("-", ""), description)
|
|
else:
|
|
current = taxonomy.add_code(code, description)
|
|
|
|
def generate_instrument():
|
|
# this defines a threshhold for quitting early dependent on the length of the code so far.
|
|
# so there's a 0% chance of stopping with a zero-length code, 0% with a 1-length code, 1% with 2-length...
|
|
chance_of_stopping = [0, 0, 0.01, 0.01, 0.01, 0.05, 0.05, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]
|
|
|
|
base_code = taxonomy.random_child(chance_of_stopping)
|
|
|
|
candidate_suffix_trees = set()
|
|
current = base_code
|
|
|
|
while current is not None:
|
|
if current.suffixes:
|
|
candidate_suffix_trees.add(current.suffixes)
|
|
current = current.parent
|
|
|
|
additionals = []
|
|
# a 15% chance of adding a second code??
|
|
while random.random() < 0.15:
|
|
length = len(base_code.code) # possible points to bifurcate at
|
|
bifurcation_point = max(random.randrange(length), random.randrange(length)) # weighted towards the end
|
|
steps_back = length - bifurcation_point
|
|
new_base = base_code
|
|
for x in range(steps_back):
|
|
new_base = base_code.parent
|
|
|
|
new_code = new_base.random_child([0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1])
|
|
additionals.append(new_code)
|
|
|
|
current = new_code
|
|
while current is not None:
|
|
if current.suffixes:
|
|
candidate_suffix_trees.add(current.suffixes)
|
|
current = current.parent
|
|
|
|
suffixes = []
|
|
chance_of_suffix = 0.5
|
|
for s in candidate_suffix_trees:
|
|
if random.random() > 0.5:
|
|
continue
|
|
|
|
suffixes.append(s.random_child([0, 0.25, 0.5, 0.5, 0.5, 0.5, 0.5]))
|
|
|
|
return (base_code, additionals, suffixes)
|
|
|
|
def explain(hs):
|
|
"""returns a list of lines explaining the meaning of each digit in the code passed in"""
|
|
# output the base code
|
|
ancestry = []
|
|
c = hs
|
|
while c is not None:
|
|
ancestry.append(c)
|
|
c = c.parent
|
|
|
|
output = []
|
|
for c in reversed(ancestry):
|
|
output.append(f"{dots(c.code)}: {c.description}")
|
|
|
|
return output
|
|
|
|
def print_random_instrument():
|
|
multi = False
|
|
base_code, additionals, suffixes = generate_instrument()
|
|
|
|
# print out the whole code
|
|
base_string = dots(base_code.code)
|
|
adds_string = "+".join(map(lambda x: dots(x.code), additionals))
|
|
if adds_string:
|
|
adds_string = "+" + adds_string
|
|
suffixes_string = "".join(map(lambda x: x.code, suffixes))
|
|
print(f"Generated instrument: {base_string}{adds_string}{suffixes_string}")
|
|
print()
|
|
|
|
output = []
|
|
# output the base code
|
|
output.extend(explain(base_code))
|
|
|
|
# output additional codes:
|
|
for code in additionals:
|
|
output.append("but also:")
|
|
for line in explain(code):
|
|
if line not in output:
|
|
output.append(line)
|
|
|
|
# output suffixes:
|
|
for code in suffixes:
|
|
output.extend(explain(code))
|
|
|
|
for x in output:
|
|
print(x)
|
|
|
|
print()
|
|
shapes = "round,rectangular,blobby,like an animal,triangular,long,short,oblong,squat,curved,straight,arched,bell-like,tubular".split(',')
|
|
shape = random.choice(shapes)
|
|
print(f"Its shape is {shape}.")
|
|
if random.random() < 0.6:
|
|
style = random.choice("sharp,soft,stationary,hand-held,bent,simple,decorated,sitting,standing".split(','))
|
|
print(f"It is a {style} instrument.")
|
|
material = random.choice("bone,wood,metal,ceramic,leather,gourds,glass,fiber".split(','))
|
|
print(f"It is made of {material}.")
|
|
|
|
if __name__ == "__main__":
|
|
|
|
#taxonomy.printout() # print out the whole taxonomy
|
|
#print("\n\n\n")
|
|
|
|
print_random_instrument()
|
|
|