| t | from collections import OrderedDict | t | from collections import OrderedDict |
| | | |
| class Spiral: | | class Spiral: |
| | | |
| def __init__(self, s): | | def __init__(self, s): |
| counts = OrderedDict() | | counts = OrderedDict() |
| for char in s: | | for char in s: |
| counts[char] = counts.get(char, 0) + 1 | | counts[char] = counts.get(char, 0) + 1 |
| self.groups = [(ch, n) for ch, n in counts.items()] | | self.groups = [(ch, n) for ch, n in counts.items()] |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| new_groups = [] | | new_groups = [] |
| other_dict = dict(other.groups) | | other_dict = dict(other.groups) |
| for ch, n in self.groups: | | for ch, n in self.groups: |
| new_n = n + other_dict.get(ch, 0) | | new_n = n + other_dict.get(ch, 0) |
| new_groups.append((ch, new_n)) | | new_groups.append((ch, new_n)) |
| for ch, n in other.groups: | | for ch, n in other.groups: |
| if ch not in dict(self.groups): | | if ch not in dict(self.groups): |
| new_groups.append((ch, n)) | | new_groups.append((ch, n)) |
| return Spiral.from_groups(new_groups) | | return Spiral.from_groups(new_groups) |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| new_groups = [] | | new_groups = [] |
| other_dict = dict(other.groups) | | other_dict = dict(other.groups) |
| for ch, n in self.groups: | | for ch, n in self.groups: |
| new_n = n - other_dict.get(ch, 0) | | new_n = n - other_dict.get(ch, 0) |
| if new_n > 0: | | if new_n > 0: |
| new_groups.append((ch, new_n)) | | new_groups.append((ch, new_n)) |
| return Spiral.from_groups(new_groups) | | return Spiral.from_groups(new_groups) |
| | | |
| def __mul__(self, num): | | def __mul__(self, num): |
| new_groups = [(ch, n * num) for ch, n in self.groups] | | new_groups = [(ch, n * num) for ch, n in self.groups] |
| return Spiral.from_groups(new_groups) | | return Spiral.from_groups(new_groups) |
| | | |
| def __iter__(self): | | def __iter__(self): |
| for ch, n in self.groups: | | for ch, n in self.groups: |
| for _ in range(n): | | for _ in range(n): |
| yield ch | | yield ch |
| | | |
| def __str__(self): | | def __str__(self): |
| total = sum((n for _, n in self.groups)) | | total = sum((n for _, n in self.groups)) |
| if total == 0: | | if total == 0: |
| return '' | | return '' |
| coords = list(Spiral.spiral_coordinates(total)) | | coords = list(Spiral.spiral_coordinates(total)) |
| assigned = {} | | assigned = {} |
| index = 0 | | index = 0 |
| for ch, n in self.groups: | | for ch, n in self.groups: |
| for i in range(n): | | for i in range(n): |
| assigned[coords[index]] = ch | | assigned[coords[index]] = ch |
| index += 1 | | index += 1 |
| xs = [x for x, y in coords] | | xs = [x for x, y in coords] |
| ys = [y for x, y in coords] | | ys = [y for x, y in coords] |
| min_x, max_x = (min(xs), max(xs)) | | min_x, max_x = (min(xs), max(xs)) |
| min_y, max_y = (min(ys), max(ys)) | | min_y, max_y = (min(ys), max(ys)) |
| grid = [] | | grid = [] |
| for x in range(min_x, max_x + 1): | | for x in range(min_x, max_x + 1): |
| row = [] | | row = [] |
| for y in range(min_y, max_y + 1): | | for y in range(min_y, max_y + 1): |
| row.append(assigned.get((x, y), ' ')) | | row.append(assigned.get((x, y), ' ')) |
| grid.append(''.join(row)) | | grid.append(''.join(row)) |
| return '\n'.join(grid) | | return '\n'.join(grid) |
| | | |
| @staticmethod | | @staticmethod |
| def spiral_coordinates(total): | | def spiral_coordinates(total): |
| x, y = (0, 0) | | x, y = (0, 0) |
| yield (x, y) | | yield (x, y) |
| if total == 1: | | if total == 1: |
| return | | return |
| steps = 1 | | steps = 1 |
| directions = [(0, 1), (-1, 0), (0, -1), (1, 0)] | | directions = [(0, 1), (-1, 0), (0, -1), (1, 0)] |
| count = 1 | | count = 1 |
| while count < total: | | while count < total: |
| for dx, dy in directions: | | for dx, dy in directions: |
| for i in range(steps): | | for i in range(steps): |
| if count >= total: | | if count >= total: |
| return | | return |
| x += dx | | x += dx |
| y += dy | | y += dy |
| yield (x, y) | | yield (x, y) |
| count += 1 | | count += 1 |
| steps += 1 | | steps += 1 |
| | | |
| @classmethod | | @classmethod |
| def from_groups(cls, groups): | | def from_groups(cls, groups): |
| instance = cls('') | | instance = cls('') |
| instance.groups = groups | | instance.groups = groups |
| return instance | | return instance |