Skip to content

Commit

Permalink
Solutions day 6-9
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Kopeć authored and mkopec87 committed Dec 9, 2023
1 parent a318b5c commit 6547d1b
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/2023/06/2023_06.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import operator
import re
from functools import reduce

from src.utils.data import load_data
from src.utils.submission import submit_or_print


def main(debug: bool) -> None:
input_data = load_data(debug)

# part 1
lines = input_data.splitlines()
times = map(int, re.findall(r"\d+", lines[0]))
distances = map(int, re.findall(r"\d+", lines[1]))
numbers = []
for time, record_distance in zip(times, distances):
ways = 0
for hold_time in range(1, time + 1):
distance = hold_time * (time - hold_time)
if distance > record_distance:
ways += 1
numbers.append(ways)
result_part1 = reduce(operator.mul, numbers)

# part 2
lines = input_data.splitlines()
time = int("".join(re.findall(r"\d", lines[0])))
record_distance = int("".join(re.findall(r"\d", lines[1])))
ways = 0
for hold_time in range(1, time + 1):
distance = hold_time * (time - hold_time)
if distance > record_distance:
ways += 1
result_part2 = ways

submit_or_print(result_part1, result_part2, debug)


if __name__ == "__main__":
debug_mode = True
# debug_mode = False
main(debug_mode)
2 changes: 2 additions & 0 deletions src/2023/06/sample_input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Time: 6 56 20
Distance: 5 33 100
112 changes: 112 additions & 0 deletions src/2023/07/2023_07.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import dataclasses
import enum
from collections import Counter
from functools import cmp_to_key, partial
from typing import Callable, List

from src.utils.data import load_data
from src.utils.submission import submit_or_print

CARD_ORDER = ["A", "K", "Q", "T", *[str(i) for i in range(9, 1, -1)], "J"]


def card_rank(card: str) -> int:
return CARD_ORDER.index(card)


class Type(enum.IntEnum):
FIVE = 1
FOUR = 2
FULL = 3
THREE = 4
TWO = 5
ONE = 6
HIGH = 7


@dataclasses.dataclass
class Hand:
cards: List[str]
bid: int

def hand_type_part1(self) -> Type:
return self._hand_type(self.cards)

def hand_type_part2(self) -> Type:
non_joker_counts = Counter(filter(lambda c: c != "J", self.cards))
if not non_joker_counts:
return Type.FIVE
ordered = non_joker_counts.most_common()
most_common = ordered[0]
cards = [(most_common[0] if c == "J" else c) for c in self.cards]
return self._hand_type(cards)

@staticmethod
def _hand_type(cards: List[str]) -> Type:
counts = Counter(cards)
most_common = counts.most_common()[0]
if most_common[1] == 5:
return Type.FIVE
if most_common[1] == 4:
return Type.FOUR
second_most_common = counts.most_common()[1]
if most_common[1] == 3:
if second_most_common[1] == 2:
return Type.FULL
return Type.THREE
if most_common[1] == 2:
if second_most_common[1] == 2:
return Type.TWO
return Type.ONE
return Type.HIGH

@staticmethod
def compare(first: "Hand", second: "Hand", hand_type_function) -> bool:
first_type = hand_type_function(first)
second_type = hand_type_function(second)
if first_type == second_type:
for c1, c2 in zip(first.cards, second.cards):
c1_rank = card_rank(c1)
c2_rank = card_rank(c2)
if c1_rank == c2_rank:
continue
return c1_rank > c2_rank
return first_type > second_type


def main(debug: bool) -> None:
input_data = load_data(debug)
hands = parse_input(input_data)

result_part1 = solve(
hands, partial(Hand.compare, hand_type_function=Hand.hand_type_part1)
)
result_part2 = solve(
hands, partial(Hand.compare, hand_type_function=Hand.hand_type_part2)
)

submit_or_print(result_part1, result_part2, debug)


def parse_input(input_data: str) -> List[Hand]:
hands = []
for line in input_data.splitlines():
cards = [c for c in line[:5]]
bid = int(line.split(" ")[1])
hands.append(Hand(cards, bid))
return hands


def solve(hands: List[Hand], compare_function: Callable[[Hand, Hand], bool]) -> int:
total = 0
for rank, hand in enumerate(
sorted(hands, key=cmp_to_key(compare_function)), start=1
):
total += rank * hand.bid
return total


if __name__ == "__main__":
debug_mode = True
# debug_mode = False
main(debug_mode)
3 changes: 3 additions & 0 deletions src/2023/07/sample_input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
255JJ 123
JAQDT 333
22222 258
95 changes: 95 additions & 0 deletions src/2023/08/2023_08.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import dataclasses
import math
import re
from typing import Dict, List, Optional, Tuple

from src.utils.data import load_data
from src.utils.submission import submit_or_print


@dataclasses.dataclass
class Node:
name: str
left: Optional["Node"]
right: Optional["Node"]

def next(self, direction: str):
if direction == "L":
return self.left
elif direction == "R":
return self.right
else:
raise AttributeError(f"Unknown direction {direction}")

def __eq__(self, other):
return self.name == other.name

def __hash__(self):
return hash(self.name)

def is_final_part1(self):
return self.name == "ZZZ"

def is_final_part2(self):
return self.name.endswith("Z")


def main(debug: bool) -> None:
input_data = load_data(debug)

instructions, nodes = parse_input(input_data)
print(f"{len(instructions)} instructions")
print(f"{len(nodes)} nodes")

# part 1
node = nodes["AAA"]
instruction_index = 0
step = 0
while node.name != "ZZZ":
node = node.next(instructions[instruction_index])
instruction_index = (instruction_index + 1) % len(instructions)
step += 1
result_part1 = step

# part 2
starting_nodes = {node for node in nodes.values() if node.name[-1] == "A"}
steps_with_final_node = set()
for node in starting_nodes:
current = node
visited = set()
instruction_index = 0
step = 0
while (instruction_index, current.name) not in visited:
visited.add((instruction_index, current.name))
if current.is_final_part2():
steps_with_final_node.add(step)
current = current.next(instructions[instruction_index])
instruction_index = (instruction_index + 1) % len(instructions)
step += 1
result_part2 = math.lcm(*steps_with_final_node)

submit_or_print(result_part1, result_part2, debug)


def parse_input(input_data: str) -> Tuple[List[str], Dict[str, Node]]:
lines = input_data.splitlines()
instructions = list(lines[0])
nodes = {}
nodes_str = {}
for line in lines[2:]:
m = re.match(r"([A-Z0-9]+) = \(([A-Z0-9]+), ([A-Z0-9]+)\)", line)
name = m.group(1)
left = m.group(2)
right = m.group(3)
nodes[name] = Node(name, None, None)
nodes_str[name] = (left, right)
for name, node in nodes.items():
node.left = nodes[nodes_str[node.name][0]]
node.right = nodes[nodes_str[node.name][1]]
return instructions, nodes


if __name__ == "__main__":
debug_mode = True
# debug_mode = False
main(debug_mode)
11 changes: 11 additions & 0 deletions src/2023/08/sample_input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
LRLR

AAA = (ZZZ, ZZZ)
BBA = (BBB, YYY)
BBB = (YYY, XXZ)
XXZ = (BBB, YYY)
CCA = (CCB, YYY)
CCB = (CCZ, CCZ)
CCZ = (CCB, CCB)
YYY = (YYY, YYY)
ZZZ = (ZZZ, ZZZ)
57 changes: 57 additions & 0 deletions src/2023/09/2023_09.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from typing import Callable, List

from src.utils.data import load_data
from src.utils.submission import submit_or_print


def main(debug: bool) -> None:
input_data = load_data(debug)

seqs = parse_input(input_data)

result_part1 = solve(seqs, score_part1)
result_part2 = solve(seqs, score_part2)

submit_or_print(result_part1, result_part2, debug)


def parse_input(input_data: str) -> List[List[int]]:
lines = input_data.splitlines()
seqs = [[int(s) for s in l.split(" ")] for l in lines]
return seqs


def solve(
seqs: List[List[int]], scoring_function: Callable[[List[List[int]]], int]
) -> int:
total = 0
for seq in seqs:
rows = []
row = seq
while not all([n == 0 for n in row]):
rows.append(row)
row = [(v[1] - v[0]) for v in zip(row, row[1:])]
total += scoring_function(rows)
return total


def score_part1(rows: List[List[int]]) -> int:
inc = 0
for row in reversed(rows):
row.append(row[-1] + inc)
inc = row[-1]
return rows[0][-1]


def score_part2(rows: List[List[int]]) -> int:
inc = 0
for row in reversed(rows):
row.insert(0, (row[0] - inc))
inc = row[0]
return rows[0][0]


if __name__ == "__main__":
debug_mode = True
# debug_mode = False
main(debug_mode)
2 changes: 2 additions & 0 deletions src/2023/09/sample_input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1 4 5 8 23
0 9 22 33

0 comments on commit 6547d1b

Please sign in to comment.