Created
November 19, 2012 03:14
-
-
Save zahlman/4108758 to your computer and use it in GitHub Desktop.
gimste simulation thingy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from itertools import product | |
| from random import choice | |
| collisions = { | |
| 'b': 'pv', 'c': 'js', 'd': 't' , 'f': 'pv', | |
| 'g': 'kx', 'j': 'cz', 'k': 'gx', 'l': 'r', | |
| 'm': 'n' , 'n': 'm' , 'p': 'bf', 'r': 'l', | |
| 's': 'cz', 't': 'd' , 'v': 'bf', 'x': 'gk', | |
| 'z': 'js' | |
| } | |
| voiced = set('bdgvjz') | |
| liquid = set('mnlr') | |
| unvoiced = set('ptkfcsx') | |
| vowels = set('aeiou') | |
| consonants = voiced | unvoiced | liquid | |
| non_voiced = unvoiced | liquid | |
| non_unvoiced = liquid | voiced | |
| def pairs(*combinations, prefix = ''): | |
| return set().union(*( | |
| (prefix + x + y for x, y in product(*combination)) | |
| for combination in combinations | |
| )) | |
| medial_clusters = pairs( | |
| (non_unvoiced, non_unvoiced), | |
| (non_voiced, non_voiced) | |
| ) - ( | |
| set().union( | |
| (x+x for x in consonants), | |
| ('cs', 'jz', 'sc', 'zj', 'cx', 'kx', 'xc', 'xk', 'mz') | |
| ) | |
| ) | |
| initial_clusters = set( | |
| '#' + x for x in ( | |
| 'bl', 'br', 'cf', 'ck', 'cl', 'cm', 'cn', 'cp', | |
| 'cr', 'ct', 'dj', 'dr', 'dz', 'fl', 'fr', 'gl', | |
| 'gr', 'jb', 'jd', 'jg', 'jm', 'jv', 'kl', 'kr', | |
| 'ml', 'mr', 'pl', 'pr', 'sf', 'sk', 'sl', 'sm', | |
| 'sn', 'sp', 'sr', 'st', 'tc', 'tr', 'ts', 'vl', | |
| 'vr', 'xl', 'xr', 'zb', 'zd', 'zg', 'zm', 'zv' | |
| ) | |
| ) | |
| def similar(pagbu): | |
| count = len(pagbu) | |
| if count == 1: # lerfu | |
| return set(collisions.get(pagbu, '')) | |
| elif count == 2: # medial cluster | |
| x, y = pagbu | |
| return pairs((collisions[x], y), (x, collisions[y])) & medial_clusters | |
| elif count == 3: # initial cluster | |
| _, x, y = pagbu | |
| return pairs((collisions[x], y), (x, collisions[y]), prefix = _) & initial_clusters | |
| similar_lookup = { | |
| p: similar(p) | |
| for p in vowels | consonants | initial_clusters | medial_clusters | |
| } | |
| other_vowels = { | |
| v: vowels.difference(v) | |
| for v in vowels | |
| } | |
| class Gismu(tuple): | |
| instances = {} | |
| def __new__(cls, a, b, c, d): | |
| args = (a, b, c, d) | |
| return Gismu.instances.get(args, tuple.__new__(cls, args)) | |
| def __init__(self, a, b, c, d): | |
| args = (a, b, c, d) | |
| super().__init__(self, args) | |
| Gismu.instances[args] = self | |
| @property | |
| def collisions(self): | |
| if not hasattr(self, '_collisions'): | |
| self._collisions = set(self._calc_collisions()) | |
| return self._collisions | |
| def __str__(self): return ''.join(self).strip('#') | |
| __repr__ = __str__ | |
| def _calc_collisions(self): | |
| a, b, c, d = self | |
| for s in similar_lookup[a]: | |
| yield Gismu(s, b, c, d) | |
| for s in similar_lookup[b]: | |
| yield Gismu(a, s, c, d) | |
| for s in similar_lookup[c]: | |
| yield Gismu(a, b, s, d) | |
| if not (a == '#br' and b == 'o' and c == 'd'): | |
| # except for the broda-series, also generate as similar | |
| # any gismu that just differ in the final vowel. | |
| for s in other_vowels[d]: | |
| yield Gismu(a, b, c, s) | |
| def all_gismu(): | |
| for a, b, c, d in product(initial_clusters, vowels, consonants, vowels): | |
| yield Gismu(a, b, c, d) | |
| for a, b, c, d in product(consonants, vowels, medial_clusters, vowels): | |
| yield Gismu(a, b, c, d) | |
| class Hat: | |
| # Get around the fact that random.choice doesn't work for sets. :( | |
| def __init__(self, rabbits): | |
| self._num_to_rabbit = list(rabbits) | |
| self._rabbit_to_num = {rabbit: i for i, rabbit in enumerate(self._num_to_rabbit)} | |
| def pull(self): | |
| rabbit = choice(self._num_to_rabbit) | |
| self.remove(rabbit) | |
| return rabbit | |
| def remove(self, rabbit): | |
| num_to_rabbit = self._num_to_rabbit | |
| rabbit_to_num = self._rabbit_to_num | |
| if rabbit not in rabbit_to_num: return # already removed. | |
| num = rabbit_to_num[rabbit] | |
| del rabbit_to_num[rabbit] | |
| replacement = num_to_rabbit.pop() | |
| if num < len(num_to_rabbit): | |
| # If the rabbit didn't come from the end of the list, then | |
| # put the rabbit that was at the end into the spot where | |
| # this one came from. | |
| num_to_rabbit[num] = replacement | |
| rabbit_to_num[replacement] = num | |
| def __repr__(self): | |
| return repr((self._num_to_rabbit, self._rabbit_to_num)) | |
| def __len__(self): | |
| return len(self._num_to_rabbit) | |
| def main(): | |
| gismu_list = list(all_gismu()) | |
| # Preload. | |
| for gismu in gismu_list: gismu.collisions | |
| print("made list.") | |
| for trial in range(10000): | |
| candidates = Hat(gismu_list) | |
| count = 0 | |
| while candidates: | |
| count += 1 | |
| result = candidates.pull() | |
| for collision in result.collisions: | |
| candidates.remove(collision) | |
| print(count) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment