Noëlle Anthony 2 тижднів тому
джерело
коміт
dd6d9da938
4 змінених файлів з 660 додано та 0 видалено
  1. 231
    0
      day04-1.py
  2. 302
    0
      day04-2.py
  3. 42
    0
      day05-1.py
  4. 85
    0
      day05-2.py

+ 231
- 0
day04-1.py Переглянути файл

@@ -0,0 +1,231 @@
import logging
import random
import re

from enum import IntEnum
from sys import stdout
from typing import List, Tuple

# As the search for the Chief continues, a small Elf who lives on the
# station tugs on your shirt; she'd like to know if you could help her
# with her word search (your puzzle input). She only has to find one word:
# XMAS.

# This word search allows words to be horizontal, vertical, diagonal,
# written backwards, or even overlapping other words. It's a little unusual,
# though, as you don't merely need to find one instance of XMAS - you need to
# find all of them.

LOG_FILENAME = "./day04-1.log"
INPUT_FILENAME = "./input04.txt"

logger = logging.Logger(__name__)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
sh = logging.StreamHandler(stdout)
sh.setLevel(logging.INFO)
sh.setFormatter(formatter)
fh = logging.FileHandler(LOG_FILENAME, mode="w", encoding="utf-8")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(sh)
logger.addHandler(fh)

class LetterValues(IntEnum):
X = 1
M = 2
A = 4
S = 8

def get_word_value(word):
total = 0
for i, letter in enumerate(word):
total += ((3**i) * LetterValues[letter])
return total

XMAS_VALUE = get_word_value("XMAS")
SAMX_VALUE = get_word_value("SAMX")

TEST_LINES = """MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX"""

class WSGrid:
def __init__(self, lines: List[List[str]], word: str, print_intermediate_grids: bool=False):
self.grid = [list(line) for line in lines]
self.width = len(self.grid[0])
self.height = len(self.grid)
self.final_grid = [['.' for _ in range(self.width)] for _ in range(self.height)]
self.word = word
self.words_found = 0
self.found_positions = []
self.print_intermediate_grids = print_intermediate_grids
self.width

def add_found_position(self: object, position: Tuple[int,int], direction: str) -> None:
self.found_positions.append((position, direction))
logger.info(f"Found a match for {self.word} at {position} going {direction}.")
if self.print_intermediate_grids:
print("\n".join(["".join(line) for line in self.final_grid]))

def search_east(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if (self.width - x) < len(self.word):
return False
try:
found_word = "".join([self.grid[y][x+i] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y][x+i] = char
self.add_found_position(position, "east")
return found_word == self.word
def search_west(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if x+1 < len(self.word):
return False
try:
found_word = "".join([self.grid[y][x-i] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y][x-i] = char
self.add_found_position(position, "west")
return True
return False
def search_south(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if (self.height - y) < len(self.word):
return False
try:
found_word = "".join([self.grid[y+i][x] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y+i][x] = char
self.add_found_position(position, "south")
return found_word == self.word
def search_north(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if y+1 < len(self.word):
return False
try:
found_word = "".join([self.grid[y-i][x] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y-i][x] = char
self.add_found_position(position, "north")
return found_word == self.word
def search_northwest(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if y+1 < len(self.word) or x+1 < len(self.word):
return False
try:
found_word = "".join([self.grid[y-i][x-i] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y-i][x-i] = char
self.add_found_position(position, "northwest")
return found_word == self.word

def search_northeast(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if y+1 < len(self.word) or (self.width - x) < len(self.word):
return False
try:
found_word = "".join([self.grid[y-i][x+i] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y-i][x+i] = char
self.add_found_position(position, "northeast")
return found_word == self.word
def search_southwest(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if (self.height - y)+1 < len(self.word) or x+1 < len(self.word):
return False
try:
found_word = "".join([self.grid[y+i][x-i] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y+i][x-i] = char
self.add_found_position(position, "southwest")
return found_word == self.word

def search_southeast(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if (self.height - y)+1 < len(self.word) or (self.width - x) < len(self.word):
return False
try:
found_word = "".join([self.grid[y+i][x+i] for i in range(4)])
except IndexError:
return False
if found_word == self.word:
self.words_found += 1
for i, char in enumerate(self.word):
self.final_grid[y+i][x+i] = char
self.add_found_position(position, "southeast")
return found_word == self.word

def find_word_at_position(self: object, position: Tuple[int,int]) -> int:
return sum([
1 if self.search_north(position) else 0,
1 if self.search_south(position) else 0,
1 if self.search_east(position) else 0,
1 if self.search_west(position) else 0,
1 if self.search_northwest(position) else 0,
1 if self.search_northeast(position) else 0,
1 if self.search_southwest(position) else 0,
1 if self.search_southeast(position) else 0
])
def find_all_words(self: object) -> int:
for y in range(len(self.grid)):
for x in range(len(self.grid[0])):
self.words_found += self.find_word_at_position((x,y))
return self.words_found

def main041(run_test=False, print_intermediate_grids=False, print_final_grid=False):
SEARCH_WORD = "XMAS"
if run_test:
lines = TEST_LINES.split("\n")
else:
with open("./input04.txt", "r", encoding="utf-8") as f:
lines = [l.strip() for l in f.readlines()]
finder = WSGrid(lines, SEARCH_WORD, print_intermediate_grids)
total_found = finder.find_all_words()
logger.info(f"Found {total_found} instances of {SEARCH_WORD}.")
if print_final_grid:
print("\n".join(["".join(line) for line in finder.final_grid]))

if __name__ == "__main__":
main041(run_test=False, print_intermediate_grids=False, print_final_grid=False)

+ 302
- 0
day04-2.py Переглянути файл

@@ -0,0 +1,302 @@
import logging
import random
import re

from enum import IntEnum
from sys import stdout
from typing import List, Tuple

# As the search for the Chief continues, a small Elf who lives on the
# station tugs on your shirt; she'd like to know if you could help her
# with her word search (your puzzle input). She only has to find one word:
# XMAS.

# This word search allows words to be horizontal, vertical, diagonal,
# written backwards, or even overlapping other words. It's a little unusual,
# though, as you don't merely need to find one instance of XMAS - you need to
# find all of them.

LOG_FILENAME = "./day04-1.log"
INPUT_FILENAME = "./input04.txt"

logger = logging.Logger(__name__)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
sh = logging.StreamHandler(stdout)
sh.setLevel(logging.INFO)
sh.setFormatter(formatter)
fh = logging.FileHandler(LOG_FILENAME, mode="w", encoding="utf-8")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(sh)
logger.addHandler(fh)

class LetterValues(IntEnum):
X = 1
M = 2
A = 4
S = 8

def get_word_value(word):
total = 0
for i, letter in enumerate(word):
total += ((3**i) * LetterValues[letter])
return total

XMAS_VALUE = get_word_value("XMAS")
SAMX_VALUE = get_word_value("SAMX")

TEST_LINES = """MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX"""

class WSGrid:
def __init__(self, lines: List[List[str]], word: str, print_intermediate_grids: bool=False):
self.grid = [list(line) for line in lines]
self.width = len(self.grid[0])
self.height = len(self.grid)
self.final_grid = [['.' for _ in range(self.width)] for _ in range(self.height)]
self.word = word
self.word_edges = (0-(len(self.word)//2), len(self.word)//2+1)
self.words_found = 0
self.found_positions = []
self.print_intermediate_grids = print_intermediate_grids
self.width

def add_found_position(self: object, position: Tuple[int,int], direction: str) -> None:
self.found_positions.append((position, direction))
logger.info(f"Found a match for {self.word} at {position} going {direction}.")
if self.print_intermediate_grids:
print("\n".join(["".join(line) for line in self.final_grid]))

def check_edges(self: object, x: int, y: int) -> bool:
# x and y can never be along the edge; they have to be at least 1 away
return not (
x > self.width-2
or x < 1
or y < 1
or y > self.height-2
)

def search_east(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y][x+i] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "east")
return found_word == self.word
def search_west(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y][x-i] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "west")
return True
return False
def search_south(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y+i][x] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "south")
return found_word == self.word
def search_north(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y-i][x] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "north")
return found_word == self.word
def search_northwest(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y-i][x-i] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "northwest")
return found_word == self.word

def search_northeast(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y-i][x+i] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "northeast")
return found_word == self.word
def search_southwest(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y+i][x-i] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "southwest")
return found_word == self.word

def search_southeast(self: object, position: Tuple[int,int]) -> bool:
x, y = position
if not self.check_edges(x, y):
return False
try:
found_word = "".join([self.grid[y+i][x+i] for i in range(self.word_edges[0],self.word_edges[1])])
except IndexError:
return False
if found_word == self.word:
self.add_found_position(position, "southeast")
return found_word == self.word

def check_left(self: object, x: int, y: int, letter: str) -> bool:
return (
self.grid[y-1][x-1] == letter
and self.grid[y+1][x-1] == letter
)

def check_top(self: object, x: int, y: int, letter: str) -> bool:
return (
self.grid[y-1][x-1] == letter
and self.grid[y-1][x+1] == letter
)

def check_right(self: object, x: int, y: int, letter: str) -> bool:
return (
self.grid[y-1][x+1] == letter
and self.grid[y+1][x+1] == letter
)

def check_bottom(self: object, x: int, y: int, letter: str) -> bool:
return (
self.grid[y+1][x-1] == letter
and self.grid[y+1][x+1] == letter
)

def set_fg_mas(self: object, x: int, y: int, start_dir: str) -> bool:
def logit():
logger.info(f"Found a match for {self.word} at {(x,y)} starting from the {start_dir}.")
match start_dir:
case "left":
self.final_grid[y][x] = "A"
self.final_grid[y-1][x-1] = "M"
self.final_grid[y+1][x-1] = "M"
self.final_grid[y-1][x+1] = "S"
self.final_grid[y+1][x+1] = "S"
logit()
case "top":
self.final_grid[y][x] = "A"
self.final_grid[y-1][x-1] = "M"
self.final_grid[y-1][x+1] = "M"
self.final_grid[y+1][x-1] = "S"
self.final_grid[y+1][x+1] = "S"
logit()
case "right":
self.final_grid[y][x] = "A"
self.final_grid[y-1][x+1] = "M"
self.final_grid[y+1][x+1] = "M"
self.final_grid[y-1][x-1] = "S"
self.final_grid[y+1][x-1] = "S"
logit()
case "bottom":
self.final_grid[y][x] = "A"
self.final_grid[y+1][x+1] = "M"
self.final_grid[y+1][x-1] = "M"
self.final_grid[y-1][x+1] = "S"
self.final_grid[y-1][x-1] = "S"
logit()
case _:
logger.warning(f"dir was not a valid value: {start_dir}")
if self.print_intermediate_grids:
print("\n".join(["".join(line) for line in self.final_grid]))

def find_word_at_position(self: object, position: Tuple[int,int]) -> int:
x, y = position
# fuck it
if self.grid[y][x] != "A":
return
if (
self.check_left(x, y, "M")
and self.check_right(x, y, "S")
):
self.words_found += 1
self.set_fg_mas(x, y, "left")
if (
self.check_right(x, y, "M")
and self.check_left(x, y, "S")
):
self.words_found += 1
self.set_fg_mas(x, y, "right")
if (
self.check_top(x, y, "M")
and self.check_bottom(x, y, "S")
):
self.words_found += 1
self.set_fg_mas(x, y, "top")
if (
self.check_bottom(x, y, "M")
and self.check_top(x, y, "S")
):
self.words_found += 1
self.set_fg_mas(x, y, "bottom")

def find_all_words(self: object) -> int:
for y in range(1,len(self.grid)-1):
for x in range(1,len(self.grid[0])-1):
self.find_word_at_position((x,y))
return self.words_found

def main042(run_test=False, print_intermediate_grids=False, print_final_grid=False):
SEARCH_WORD = "MAS"
if run_test:
lines = TEST_LINES.split("\n")
else:
with open("./input04.txt", "r", encoding="utf-8") as f:
lines = [l.strip() for l in f.readlines()]
finder = WSGrid(lines, SEARCH_WORD, print_intermediate_grids)
total_found = finder.find_all_words()
logger.info(f"Found {total_found} instances of {SEARCH_WORD}.")
if print_final_grid:
print("\n".join(["".join(line) for line in finder.final_grid]))

if __name__ == "__main__":
main042(
run_test=False,
print_intermediate_grids=False,
print_final_grid=False
)

+ 42
- 0
day05-1.py Переглянути файл

@@ -0,0 +1,42 @@
# The first section specifies the page ordering rules, one per line.
# The first rule, 47|53, means that if an update includes both
# page number 47 and page number 53, then page number 47 must be
# printed at some point before page number 53. (47 doesn't necessarily
# need to be immediately before 53; other pages are allowed to be
# between them.)

# The second section specifies the page numbers of each update.
# Because most safety manuals are different, the pages needed in the
# updates are different too. The first update, 75,47,61,53,29, means
# that the update consists of page numbers 75, 47, 61, 53, and 29.

def main051():
with open("./input05.txt", "r", encoding="utf-8") as f:
lines = [l.strip() for l in f.readlines()]
rules = []
updates = []
for i, line in enumerate(lines):
if "|" not in line:
first_update = i + 1
break
rules.append([int(x) for x in line.split("|")])
for i, line in enumerate(lines[first_update:]):
updates.append([int(x) for x in line.split(",")])
valid_updates = []
total_vu = 0
for u in updates:
relevant_rules = [r for r in rules if all([x in u for x in r])]
if all([u.index(rule[0]) < u.index(rule[1]) for rule in relevant_rules]):
valid_updates.append(u)
total_vu += u[(len(u)-1)//2]

print(total_vu)




if __name__ == "__main__":
main051()

+ 85
- 0
day05-2.py Переглянути файл

@@ -0,0 +1,85 @@
import logging

from sys import stdout

# The first section specifies the page ordering rules, one per line.
# The first rule, 47|53, means that if an update includes both
# page number 47 and page number 53, then page number 47 must be
# printed at some point before page number 53. (47 doesn't necessarily
# need to be immediately before 53; other pages are allowed to be
# between them.)

# The second section specifies the page numbers of each update.
# Because most safety manuals are different, the pages needed in the
# updates are different too. The first update, 75,47,61,53,29, means
# that the update consists of page numbers 75, 47, 61, 53, and 29.

# For each of the incorrectly-ordered updates, use the page ordering
# rules to put the page numbers in the right order.
# Find the updates which are not in the correct order. What do you get
# if you add up the middle page numbers after correctly ordering
# just those updates?

LOG_FILENAME = "./day04-1.log"
INPUT_FILENAME = "./input04.txt"

logger = logging.Logger(__name__)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
sh = logging.StreamHandler(stdout)
sh.setLevel(logging.INFO)
sh.setFormatter(formatter)
fh = logging.FileHandler(LOG_FILENAME, mode="w", encoding="utf-8")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(sh)
logger.addHandler(fh)

def main052():
with open("./input05.txt", "r", encoding="utf-8") as f:
lines = [l.strip() for l in f.readlines()]
rules = []
updates = []
for i, line in enumerate(lines):
if "|" not in line:
first_update = i + 1
break
rules.append([int(x) for x in line.split("|")])
for i, line in enumerate(lines[first_update:]):
updates.append([int(x) for x in line.split(",")])
valid_updates = []
invalid_updates = []
new_invalid_updates = []
total_vu = 0
total_iu = 0
for u in updates:
relevant_rules = [r for r in rules if all([x in u for x in r])]
all_valid = all([u.index(rule[0]) < u.index(rule[1]) for rule in relevant_rules])
if all_valid:
valid_updates.append(u)
total_vu += u[(len(u)-1)//2]
else:
invalid_updates.append(u)
logger.info(f"Applying rules to {u}...")
while not all_valid:
logger.info(f"{u} still doesn't meet all the rules.")
for rule in relevant_rules:
i1 = u.index(rule[0])
i2 = u.index(rule[1])
if i1 > i2:
u[i1] = rule[1]
u[i2] = rule[0]
logger.info(f"Swapping {rule[0]} and {rule[1]}, u is now {u}.")
all_valid = all([u.index(rule[0]) < u.index(rule[1]) for rule in relevant_rules])
new_invalid_updates.append(u)
middle_val = u[(len(u)-1)//2]
logger.info(f"Adding {middle_val} to {total_iu} to get {total_iu + middle_val}.")
total_iu += middle_val

print(total_vu)
logger.info(f"Total of invalid update centers is {total_iu}.")


if __name__ == "__main__":
main052()

Завантаження…
Відмінити
Зберегти