[Advent of Code 2024](https://adventofcode.com/2024)
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

2週間前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import logging
  2. import random
  3. import re
  4. from enum import IntEnum
  5. from sys import stdout
  6. from typing import List, Tuple
  7. # As the search for the Chief continues, a small Elf who lives on the
  8. # station tugs on your shirt; she'd like to know if you could help her
  9. # with her word search (your puzzle input). She only has to find one word:
  10. # XMAS.
  11. # This word search allows words to be horizontal, vertical, diagonal,
  12. # written backwards, or even overlapping other words. It's a little unusual,
  13. # though, as you don't merely need to find one instance of XMAS - you need to
  14. # find all of them.
  15. LOG_FILENAME = "./day04-1.log"
  16. INPUT_FILENAME = "./input04.txt"
  17. logger = logging.Logger(__name__)
  18. formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
  19. sh = logging.StreamHandler(stdout)
  20. sh.setLevel(logging.INFO)
  21. sh.setFormatter(formatter)
  22. fh = logging.FileHandler(LOG_FILENAME, mode="w", encoding="utf-8")
  23. fh.setLevel(logging.DEBUG)
  24. fh.setFormatter(formatter)
  25. logger.addHandler(sh)
  26. logger.addHandler(fh)
  27. class LetterValues(IntEnum):
  28. X = 1
  29. M = 2
  30. A = 4
  31. S = 8
  32. def get_word_value(word):
  33. total = 0
  34. for i, letter in enumerate(word):
  35. total += ((3**i) * LetterValues[letter])
  36. return total
  37. XMAS_VALUE = get_word_value("XMAS")
  38. SAMX_VALUE = get_word_value("SAMX")
  39. TEST_LINES = """MMMSXXMASM
  40. MSAMXMSMSA
  41. AMXSXMAAMM
  42. MSAMASMSMX
  43. XMASAMXAMM
  44. XXAMMXXAMA
  45. SMSMSASXSS
  46. SAXAMASAAA
  47. MAMMMXMMMM
  48. MXMXAXMASX"""
  49. class WSGrid:
  50. def __init__(self, lines: List[List[str]], word: str, print_intermediate_grids: bool=False):
  51. self.grid = [list(line) for line in lines]
  52. self.width = len(self.grid[0])
  53. self.height = len(self.grid)
  54. self.final_grid = [['.' for _ in range(self.width)] for _ in range(self.height)]
  55. self.word = word
  56. self.words_found = 0
  57. self.found_positions = []
  58. self.print_intermediate_grids = print_intermediate_grids
  59. self.width
  60. def add_found_position(self: object, position: Tuple[int,int], direction: str) -> None:
  61. self.found_positions.append((position, direction))
  62. logger.info(f"Found a match for {self.word} at {position} going {direction}.")
  63. if self.print_intermediate_grids:
  64. print("\n".join(["".join(line) for line in self.final_grid]))
  65. def search_east(self: object, position: Tuple[int,int]) -> bool:
  66. x, y = position
  67. if (self.width - x) < len(self.word):
  68. return False
  69. try:
  70. found_word = "".join([self.grid[y][x+i] for i in range(4)])
  71. except IndexError:
  72. return False
  73. if found_word == self.word:
  74. self.words_found += 1
  75. for i, char in enumerate(self.word):
  76. self.final_grid[y][x+i] = char
  77. self.add_found_position(position, "east")
  78. return found_word == self.word
  79. def search_west(self: object, position: Tuple[int,int]) -> bool:
  80. x, y = position
  81. if x+1 < len(self.word):
  82. return False
  83. try:
  84. found_word = "".join([self.grid[y][x-i] for i in range(4)])
  85. except IndexError:
  86. return False
  87. if found_word == self.word:
  88. self.words_found += 1
  89. for i, char in enumerate(self.word):
  90. self.final_grid[y][x-i] = char
  91. self.add_found_position(position, "west")
  92. return True
  93. return False
  94. def search_south(self: object, position: Tuple[int,int]) -> bool:
  95. x, y = position
  96. if (self.height - y) < len(self.word):
  97. return False
  98. try:
  99. found_word = "".join([self.grid[y+i][x] for i in range(4)])
  100. except IndexError:
  101. return False
  102. if found_word == self.word:
  103. self.words_found += 1
  104. for i, char in enumerate(self.word):
  105. self.final_grid[y+i][x] = char
  106. self.add_found_position(position, "south")
  107. return found_word == self.word
  108. def search_north(self: object, position: Tuple[int,int]) -> bool:
  109. x, y = position
  110. if y+1 < len(self.word):
  111. return False
  112. try:
  113. found_word = "".join([self.grid[y-i][x] for i in range(4)])
  114. except IndexError:
  115. return False
  116. if found_word == self.word:
  117. self.words_found += 1
  118. for i, char in enumerate(self.word):
  119. self.final_grid[y-i][x] = char
  120. self.add_found_position(position, "north")
  121. return found_word == self.word
  122. def search_northwest(self: object, position: Tuple[int,int]) -> bool:
  123. x, y = position
  124. if y+1 < len(self.word) or x+1 < len(self.word):
  125. return False
  126. try:
  127. found_word = "".join([self.grid[y-i][x-i] for i in range(4)])
  128. except IndexError:
  129. return False
  130. if found_word == self.word:
  131. self.words_found += 1
  132. for i, char in enumerate(self.word):
  133. self.final_grid[y-i][x-i] = char
  134. self.add_found_position(position, "northwest")
  135. return found_word == self.word
  136. def search_northeast(self: object, position: Tuple[int,int]) -> bool:
  137. x, y = position
  138. if y+1 < len(self.word) or (self.width - x) < len(self.word):
  139. return False
  140. try:
  141. found_word = "".join([self.grid[y-i][x+i] for i in range(4)])
  142. except IndexError:
  143. return False
  144. if found_word == self.word:
  145. self.words_found += 1
  146. for i, char in enumerate(self.word):
  147. self.final_grid[y-i][x+i] = char
  148. self.add_found_position(position, "northeast")
  149. return found_word == self.word
  150. def search_southwest(self: object, position: Tuple[int,int]) -> bool:
  151. x, y = position
  152. if (self.height - y)+1 < len(self.word) or x+1 < len(self.word):
  153. return False
  154. try:
  155. found_word = "".join([self.grid[y+i][x-i] for i in range(4)])
  156. except IndexError:
  157. return False
  158. if found_word == self.word:
  159. self.words_found += 1
  160. for i, char in enumerate(self.word):
  161. self.final_grid[y+i][x-i] = char
  162. self.add_found_position(position, "southwest")
  163. return found_word == self.word
  164. def search_southeast(self: object, position: Tuple[int,int]) -> bool:
  165. x, y = position
  166. if (self.height - y)+1 < len(self.word) or (self.width - x) < len(self.word):
  167. return False
  168. try:
  169. found_word = "".join([self.grid[y+i][x+i] for i in range(4)])
  170. except IndexError:
  171. return False
  172. if found_word == self.word:
  173. self.words_found += 1
  174. for i, char in enumerate(self.word):
  175. self.final_grid[y+i][x+i] = char
  176. self.add_found_position(position, "southeast")
  177. return found_word == self.word
  178. def find_word_at_position(self: object, position: Tuple[int,int]) -> int:
  179. return sum([
  180. 1 if self.search_north(position) else 0,
  181. 1 if self.search_south(position) else 0,
  182. 1 if self.search_east(position) else 0,
  183. 1 if self.search_west(position) else 0,
  184. 1 if self.search_northwest(position) else 0,
  185. 1 if self.search_northeast(position) else 0,
  186. 1 if self.search_southwest(position) else 0,
  187. 1 if self.search_southeast(position) else 0
  188. ])
  189. def find_all_words(self: object) -> int:
  190. for y in range(len(self.grid)):
  191. for x in range(len(self.grid[0])):
  192. self.words_found += self.find_word_at_position((x,y))
  193. return self.words_found
  194. def main041(run_test=False, print_intermediate_grids=False, print_final_grid=False):
  195. SEARCH_WORD = "XMAS"
  196. if run_test:
  197. lines = TEST_LINES.split("\n")
  198. else:
  199. with open("./input04.txt", "r", encoding="utf-8") as f:
  200. lines = [l.strip() for l in f.readlines()]
  201. finder = WSGrid(lines, SEARCH_WORD, print_intermediate_grids)
  202. total_found = finder.find_all_words()
  203. logger.info(f"Found {total_found} instances of {SEARCH_WORD}.")
  204. if print_final_grid:
  205. print("\n".join(["".join(line) for line in finder.final_grid]))
  206. if __name__ == "__main__":
  207. main041(run_test=False, print_intermediate_grids=False, print_final_grid=False)