19.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. from util import get_input
  2. from itertools import product
  3. input = []
  4. for line in get_input("19.input"):
  5. if line == "":
  6. continue
  7. elif line.startswith("---"):
  8. input.append([])
  9. else:
  10. input[-1].append(tuple([int(i) for i in line.split(",")]))
  11. def sub(a, b):
  12. return (a[0] - b[0], a[1] - b[1], a[2] - b[2])
  13. def add(a, b):
  14. return (a[0] + b[0], a[1] + b[1], a[2] + b[2])
  15. def rotate(a, rot):
  16. if rot == 0:
  17. return (a[0], a[1], a[2])
  18. elif rot == 1:
  19. return (a[0], -a[2], a[1])
  20. elif rot == 2:
  21. return (a[0], -a[1], -a[2])
  22. else:
  23. return (a[0], a[2], -a[1])
  24. def orient(a, o):
  25. (dir, neg, rot) = o
  26. if dir == 0:
  27. x = neg * a[0]
  28. if neg == 1:
  29. y = a[1]
  30. z = a[2]
  31. else:
  32. y = a[2]
  33. z = a[1]
  34. elif dir == 1:
  35. x = neg * a[1]
  36. if neg == 1:
  37. y = a[2]
  38. z = a[0]
  39. else:
  40. y = a[0]
  41. z = a[2]
  42. else:
  43. x = neg * a[2]
  44. if neg == 1:
  45. y = a[0]
  46. z = a[1]
  47. else:
  48. y = a[1]
  49. z = a[0]
  50. return rotate((x, y, z), rot)
  51. def dist(a):
  52. return abs(a[0]) + abs(a[1]) + abs(a[2])
  53. def dist2(a):
  54. return a[0] ** 2 + a[1] ** 2 + a[2] ** 2
  55. def try_match(existing, new):
  56. for ebase in list(existing)[:-11]:
  57. edists = set([dist2(sub(ebase, e)) for e in existing])
  58. for nbase in new[:-11]:
  59. ndists = set([dist2(sub(nbase, n)) for n in new])
  60. matching = edists.intersection(ndists)
  61. if len(matching) >= 12:
  62. for o in product([0, 1, 2], [-1, 1], [0, 1, 2, 3]):
  63. relative = sub(ebase, orient(nbase, o))
  64. maxx = len(new)
  65. found = 0
  66. for i, n in enumerate(new):
  67. if (found + (maxx - i)) < 12:
  68. break
  69. if add(orient(n, o), relative) in existing:
  70. found += 1
  71. if found >= 12:
  72. return relative, o
  73. print("Found dist match, but no orient")
  74. return None
  75. known = {0: (0, 0, 0)}
  76. tried = set()
  77. existing = {0: set(input[0])}
  78. while len(input) > len(known):
  79. before = len(known)
  80. for j in list(known.keys()):
  81. if j in tried:
  82. continue
  83. for i, probe in enumerate(input):
  84. if i in known.keys():
  85. continue
  86. rel = try_match(existing[j], probe)
  87. if rel:
  88. rel, o = rel
  89. existing[i] = set([add(orient(n, o), rel) for n in probe])
  90. known[i] = rel
  91. tried.add(j)
  92. if before == len(known):
  93. print("No solution")
  94. break
  95. print("Progress: ", len(known), "/", len(input))
  96. tot = set()
  97. for v in existing.values():
  98. tot = tot.union(v)
  99. print("Part 1:", len(tot))
  100. print("Part 2:", max(dist(sub(a, b)) for [a, b] in product(list(known.values()), repeat=2)))