選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

p20.py 3.4KB

4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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('#'))