You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 line
3.7KB

  1. # flake8: noqa
  2. import re
  3. import collections
  4. import itertools
  5. import sys
  6. import math
  7. from functools import reduce
  8. import toolkit
  9. def apply(cell):
  10. string, *fns = cell
  11. for fn in fns:
  12. if fn is not None:
  13. string = fn(string)
  14. return string
  15. def apply2(string, fns):
  16. for fn in fns:
  17. if fn is not None:
  18. string = fn(string)
  19. return string
  20. def render(grid):
  21. return '\n'.join(
  22. '\n'.join(''.join([n[1:-1]
  23. for n in ln])
  24. for ln in zip(*[apply(grid[y, x]).splitlines()[1:-1] for x in range(size)]))
  25. for y in range(size)
  26. )
  27. def get_borders(content):
  28. a, *_, b = content.splitlines()
  29. c, *_, d = [''.join(ln) for ln in zip(*content.splitlines())]
  30. return [a, b, c, d, a[::-1], b[::-1], c[::-1], d[::-1]]
  31. def flipV(string):
  32. return '\n'.join(ln for ln in string.splitlines()[::-1])
  33. def flipH(string):
  34. return '\n'.join(ln[::-1] for ln in string.splitlines())
  35. def rot90(n):
  36. def inner(string):
  37. return toolkit.render({k * 1j ** n: v for k, v in toolkit.read_image(string)[0].items()})
  38. return inner
  39. def noop(string):
  40. return string
  41. def variants(string):
  42. alts = [noop, flipH], [noop, flipV], [noop, rot90(1), rot90(2), rot90(3)]
  43. for ops in itertools.product(*alts):
  44. yield reduce(lambda s, f: f(s), ops, string), ops
  45. def search_in(source, target):
  46. source_lns = source.splitlines()
  47. target_lns = target.splitlines()
  48. h, w = len(target_lns), len(target_lns[0])
  49. out = []
  50. for y in range(len(source_lns)):
  51. for x in range(len(source_lns[0])):
  52. window = '\n'.join(ln[x:x + w] for ln in source_lns[y:y + h])
  53. if re.fullmatch(target, window):
  54. out.append((x, y))
  55. return out
  56. text = sys.stdin.read().strip()
  57. tiles = {}
  58. borders = {}
  59. for line in text.split('\n\n'):
  60. title, content = line.split(':\n')
  61. tid = int(title.split()[1])
  62. tiles[tid] = content
  63. borders[tid] = get_borders(content)
  64. size = int(len(tiles) ** 0.5)
  65. adj = collections.defaultdict(set)
  66. for (aid, A), (bid, B) in itertools.combinations(borders.items(), 2):
  67. if set(A) & set(B):
  68. adj[aid].add(bid)
  69. adj[bid].add(aid)
  70. corn = [v for v, ks in adj.items() if len(ks) == 2]
  71. print(math.prod(corn))
  72. gids = [[None for _ in range(size)] for _ in range(size)]
  73. gids[0][0] = corn[0]
  74. gids[1][0], gids[0][1] = adj[gids[0][0]]
  75. gids[1][1], = adj[gids[1][0]] & adj[gids[0][1]] - {gids[0][0]}
  76. for x in range(2, size):
  77. gids[0][x], = adj[gids[0][x - 1]] - {gids[0][x - 2], gids[1][x - 1]}
  78. gids[1][x], = adj[gids[0][x]] & adj[gids[1][x - 1]] - {gids[0][x - 1]}
  79. for y in range(2, size):
  80. gids[y][0], = adj[gids[y - 1][0]] - {gids[y - 2][0], gids[y - 1][1]}
  81. for x in range(1, size):
  82. gids[y][x], = adj[gids[y][x - 1]] & adj[gids[y - 1][x]] - {gids[y - 1][x - 1]}
  83. options = list(itertools.product([None, flipH], [None, flipV], [None] + [rot90(n) for n in range(3)]))
  84. grid = {(y, x): (tiles[gids[y][x]],) for x in range(size) for y in range(size)}
  85. grid[0, 0] = (flipV(grid[0, 0][0]),)
  86. for x in range(1, size):
  87. goal = [ln[-1] for ln in apply(grid[0, x - 1]).splitlines()]
  88. for chain in options:
  89. if goal == [ln[0] for ln in apply(grid[0, x] + chain).splitlines()]:
  90. grid[0, x] += chain
  91. break
  92. for y in range(1, size):
  93. for x in range(size):
  94. goal = apply(grid[y - 1, x]).splitlines()[-1]
  95. for chain in options:
  96. if goal == apply(grid[y, x] + chain).splitlines()[0]:
  97. grid[y, x] += chain
  98. break
  99. monster = '''
  100. ..................#.
  101. #....##....##....###
  102. .#..#..#..#..#..#...
  103. '''[1:-1]
  104. src = render(grid)
  105. found = max([search_in(pic, monster) for pic, _ in variants(src)], key=len)
  106. print(src.count('#') - len(found) * monster.count('#'))