您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

133 行
3.4KB

  1. import collections
  2. import functools
  3. import itertools
  4. import math
  5. import re
  6. import sys
  7. def render(grid):
  8. lines = []
  9. for y in range(size):
  10. groups = [tiles[grid[y, x]].splitlines()[1:-1] for x in range(size)]
  11. for lns in zip(*groups):
  12. lines.append(''.join(ln[1:-1] for ln in lns))
  13. return '\n'.join(lines)
  14. def get_borders(content):
  15. a, *_, b = content.splitlines()
  16. c, *_, d = [''.join(ln) for ln in zip(*content.splitlines())]
  17. return [a, b, c, d, a[::-1], b[::-1], c[::-1], d[::-1]]
  18. def transpose(string):
  19. return '\n'.join(''.join(ln) for ln in zip(*string.splitlines()))
  20. def flipV(string):
  21. return '\n'.join(ln for ln in string.splitlines()[::-1])
  22. def flipH(string):
  23. return '\n'.join(ln[::-1] for ln in string.splitlines())
  24. def rot90(string):
  25. return flipH(transpose(string))
  26. def noop(string):
  27. return string
  28. def variants(string):
  29. alts = [noop, flipH], [noop, flipV], [noop, transpose]
  30. for ops in itertools.product(*alts):
  31. yield functools.reduce(lambda s, f: f(s), ops, string)
  32. def UD(A, B):
  33. return A.splitlines()[-1] == B.splitlines()[0]
  34. def LR(A, B):
  35. return UD(rot90(A), rot90(B))
  36. def search_in(source, target):
  37. source_lns = source.splitlines()
  38. target_lns = target.splitlines()
  39. h, w = len(target_lns), len(target_lns[0])
  40. out = []
  41. for y in range(len(source_lns)):
  42. for x in range(len(source_lns[0])):
  43. window = '\n'.join(ln[x:x + w] for ln in source_lns[y:y + h])
  44. if re.fullmatch(target, window):
  45. out.append((x, y))
  46. return out
  47. def reconcile(A, B, condition):
  48. tiles[A], tiles[B] = next(
  49. (X, Y)
  50. for X, Y in itertools.product(variants(tiles[A]), variants(tiles[B]))
  51. if condition(X, Y)
  52. )
  53. text = sys.stdin.read().strip()
  54. tiles = {}
  55. borders = {}
  56. for line in text.split('\n\n'):
  57. title, content = line.split(':\n')
  58. tid = int(title.split()[1])
  59. tiles[tid] = content
  60. borders[tid] = get_borders(content)
  61. size = int(len(tiles) ** 0.5)
  62. adj = collections.defaultdict(set)
  63. for (aid, A), (bid, B) in itertools.combinations(borders.items(), 2):
  64. if set(A) & set(B):
  65. adj[aid].add(bid)
  66. adj[bid].add(aid)
  67. corners = [v for v, ks in adj.items() if len(ks) == 2]
  68. print(math.prod(corners))
  69. A = min(corners)
  70. B, C = adj.pop(A)
  71. grid = {(y, x): None for x in range(size) for y in range(size)}
  72. grid[0, 0] = A
  73. grid[0, 1] = B
  74. grid[1, 0] = C
  75. reconcile(A, B, condition=LR)
  76. reconcile(A, C, condition=UD)
  77. seen = {A, B, C}
  78. for z in range(2, 2 * size - 1):
  79. # determine new pieces from intersection between 2 neighbors
  80. for y, x in {(y, z - y) for y in range(1, z)}.intersection(grid):
  81. U, L = (y - 1, x), (y, x - 1)
  82. grid[y, x], = adj[grid[U]] & adj[grid[L]] - seen
  83. reconcile(grid[L], grid[y, x], condition=LR)
  84. seen.add(grid[y, x])
  85. # determine new piece from only one alternative left
  86. for y, x in {(0, z), (z, 0)}.intersection(grid):
  87. opts = {(y - 1, x): UD, (y, x - 1): LR}
  88. P, fn = next(p for p in opts.items() if p[0] in grid)
  89. grid[y, x], = adj[grid[P]] - seen
  90. reconcile(grid[P], grid[y, x], condition=fn)
  91. seen.add(grid[y, x])
  92. monster = '''
  93. ..................#.
  94. #....##....##....###
  95. .#..#..#..#..#..#...
  96. '''[1:-1]
  97. src = render(grid)
  98. found = max([search_in(pic, monster) for pic in variants(src)], key=len)
  99. print(src.count('#') - len(found) * monster.count('#'))