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.

117 line
2.9KB

  1. import builtins
  2. import collections
  3. import functools
  4. import hashlib
  5. import importlib
  6. import itertools
  7. import math
  8. import operator
  9. import os
  10. import re
  11. import string
  12. import sys
  13. from pathlib import Path
  14. import requests
  15. product = functools.partial(functools.reduce, operator.mul)
  16. def lcm(a, b):
  17. return abs(a * b) // math.gcd(a, b)
  18. def render(grid, brush=None):
  19. if brush is None:
  20. brush = {v: v for v in grid.values()}
  21. if isinstance(brush, str):
  22. brush = {i: c for i, c in enumerate(brush)}
  23. xmin, *_, xmax = sorted(int(p.real) for p in grid)
  24. ymin, *_, ymax = sorted(int(p.imag) for p in grid)
  25. brush[None] = ' '
  26. rendered = ''
  27. for y in range(ymin, ymax + 1):
  28. for x in range(xmin, xmax + 1):
  29. rendered += brush[grid.get(complex(x, y))]
  30. rendered += '\n'
  31. return rendered
  32. def read_image(text):
  33. grid = collections.defaultdict(str)
  34. for y, line in enumerate(text.splitlines()):
  35. for x, cell in enumerate(line):
  36. grid[complex(x, y)] = cell
  37. return grid, x + 1, y + 1
  38. def shortest_path(start, end, move):
  39. seen = {}
  40. edge = {start: None}
  41. while edge:
  42. seen.update(edge)
  43. edge = {
  44. adj: pos
  45. for pos in edge
  46. for adj in move(pos)
  47. if adj not in seen
  48. }
  49. if end in seen:
  50. break
  51. else:
  52. raise RuntimeError('Path not found', seen)
  53. path = []
  54. while end:
  55. path.append(end)
  56. end = seen[end]
  57. return path[::-1]
  58. def ensure_data(path):
  59. if not path.exists():
  60. year, qn = map(int, re.findall(r'\d+', sys.argv[1]))
  61. url = f'https://adventofcode.com/{year}/day/{qn}/input'
  62. cookies = {'session': os.environ['SESSION']}
  63. response = requests.get(url, cookies=cookies)
  64. response.raise_for_status()
  65. path.write_bytes(response.content)
  66. def batch(iterable, size):
  67. count = itertools.count()
  68. for _, sub in itertools.groupby(iterable, lambda _: next(count) // size):
  69. yield sub
  70. def md5(string):
  71. return hashlib.md5(string.encode()).hexdigest()
  72. def loop_consume(lines, handler):
  73. instructions = collections.deque(lines)
  74. count = 0
  75. while instructions:
  76. ok = handler(instructions[0])
  77. if ok:
  78. instructions.popleft()
  79. count = 0
  80. elif count < len(instructions):
  81. instructions.rotate(1)
  82. count += 1
  83. else:
  84. raise RuntimeError('Reached steady state')
  85. if __name__ == '__main__':
  86. data_file = Path(sys.argv[1]).with_suffix('.dat')
  87. ensure_data(data_file)
  88. builtins.df = data_file
  89. builtins.text = data_file.read_text()
  90. builtins.string = string
  91. builtins.re = re
  92. rel = re.sub(r'.+(y\d+)/(p\d+).+', r'\1.\2', os.environ['FILE'])
  93. mod = importlib.import_module(rel)
  94. print('ans1', getattr(mod, 'ans1', None))
  95. print('ans2', getattr(mod, 'ans2', None))