solution.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. #!/usr/bin/python3
  2. import sys
  3. from collections import deque
  4. def boundary(elves):
  5. minx = miny = sys.maxsize
  6. maxx = maxy = 0
  7. for ex, ey in elves:
  8. minx = min(minx, ex)
  9. miny = min(miny, ey)
  10. maxx = max(maxx, ex)
  11. maxy = max(maxy, ey)
  12. return minx, miny, maxx, maxy
  13. def print_locations(elves):
  14. minx, miny, maxx, maxy = boundary(elves)
  15. for y in range(miny, maxy + 1):
  16. for x in range(minx, maxx + 1):
  17. print('#' if (x, y) in elves else '.', end='')
  18. print()
  19. def count_ground_tiles(elves):
  20. minx, miny, maxx, maxy = boundary(elves)
  21. count = 0
  22. for y in range(miny, maxy + 1):
  23. for x in range(minx, maxx + 1):
  24. count += 1 if (x, y) not in elves else 0
  25. return count
  26. def solution(endless=True, rounds=None):
  27. elves = set()
  28. y = 0
  29. for line in sys.stdin:
  30. stripped = line.strip()
  31. if len(stripped) == 0: continue
  32. [elves.add((x, y)) for x in range(len(stripped)) if stripped[x] == '#']
  33. y += 1
  34. print(elves)
  35. #print_locations(elves)
  36. NW = lambda pair: (pair[0] - 1, pair[1] - 1)
  37. NO = lambda pair: (pair[0], pair[1] - 1)
  38. NE = lambda pair: (pair[0] + 1, pair[1] - 1)
  39. SW = lambda pair: (pair[0] - 1, pair[1] + 1)
  40. SO = lambda pair: (pair[0], pair[1] + 1)
  41. SE = lambda pair: (pair[0] + 1, pair[1] + 1)
  42. WE = lambda pair: (pair[0] - 1, pair[1])
  43. EA = lambda pair: (pair[0] + 1, pair[1])
  44. considerations = deque([(NW, NO, NE), (SW, SO, SE), (NW, WE, SW), (NE, EA, SE)])
  45. r = 1
  46. while True:
  47. proposals = []
  48. proposal_count = {}
  49. for elf in elves:
  50. alone = True
  51. for rules in considerations:
  52. for subrules in rules:
  53. if subrules(elf) in elves:
  54. alone = False
  55. if alone:
  56. #print(elf, 'alone')
  57. continue
  58. for rules in considerations:
  59. #print(elf, 'looking at', rules[0](elf), rules[1](elf), rules[2](elf))
  60. p0 = rules[0](elf)
  61. p1 = rules[1](elf)
  62. p2 = rules[2](elf)
  63. if p0 not in elves and p1 not in elves and p2 not in elves:
  64. #print(elf, 'proposing', rules[1](elf))
  65. proposals.append((elf, p1))
  66. if p1 not in proposal_count:
  67. proposal_count[p1] = 1
  68. else:
  69. proposal_count[p1] += 1
  70. break
  71. #print(len(proposals), 'proposals')
  72. #print(proposal_count)
  73. moves = 0
  74. considerations.rotate(-1)
  75. for proposal in proposals:
  76. elf, next_elf = proposal
  77. if proposal_count[next_elf] == 1:
  78. elves.remove(elf)
  79. elves.add(next_elf)
  80. moves += 1
  81. print_locations(elves)
  82. print(f'{moves} elves moved')
  83. print(f'found {count_ground_tiles(elves)} ground tiles (round {r})')
  84. r += 1
  85. if not endless and r > rounds: break
  86. if moves == 0: break
  87. if sys.argv[1] in '1':
  88. solution(endless=False, rounds=10)
  89. else:
  90. solution()