3 Commits

Author SHA1 Message Date
2d49907692 Implement day 5 part 1 in terraform
I'm pretty sure part 2 is impossible
2024-12-07 12:45:41 +01:00
edb0767201 Optimize day 3
Avoid instantiating module and backtracking, instead use regex for
filtering. Much faster. Since we are not matching brackets, but rather
only care about the last instance, the limitations of regex don't apply.
2024-12-07 10:42:38 +01:00
c871a9ea24 Implement 2024 day 7 2024-12-07 10:35:58 +01:00
9 changed files with 163 additions and 30 deletions

View File

@@ -3,22 +3,17 @@ variable "input" {
}
locals {
muls = regexall("mul\\((\\d+),(\\d+)\\)", var.input)
ops = regexall("(don't\\(\\)|do\\(\\)|mul\\((\\d+),(\\d+)\\))", var.input)
}
filtered = replace(var.input, "/(?s)don't\\(\\).*?do\\(\\)/", "")
filtered2 = replace(local.filtered, "/(?s)don't\\(\\).*/", "")
module "should_execute" {
count = length(local.ops)
source = "./should_execute"
index = count.index
ops = local.ops
muls = regexall("mul\\((\\d+),(\\d+)\\)", var.input)
filtered_muls = regexall("mul\\((\\d+),(\\d+)\\)", local.filtered2)
}
output "part1" {
value = sum([for mul in local.muls : parseint(mul[1], 10) * parseint(mul[0], 10)])
value = sum([for mul in local.muls : tonumber(mul[1]) * tonumber(mul[0])])
}
output "part2" {
value = sum(module.should_execute[*].value)
value = sum([for mul in local.filtered_muls : tonumber(mul[1]) * tonumber(mul[0])])
}

View File

@@ -1,19 +0,0 @@
variable "ops" {
type = list(list(string))
}
variable "index" {
type = number
}
locals {
is_mul = startswith(var.ops[var.index][0], "mul")
subslice = reverse(slice(var.ops[*][0], 0, var.index))
do_pos = try(index(local.subslice, "do()"), var.index)
dont_pos = try(index(local.subslice, "don't()"), var.index + 1)
}
output "value" {
value = (local.is_mul && local.do_pos < local.dont_pos) ? (parseint(var.ops[var.index][1], 10) * parseint(var.ops[var.index][2], 10)) : 0
}

View File

@@ -0,0 +1,21 @@
variable "update" {
type = list(number)
}
variable "disallow_rules" {
type = map(list(number))
}
locals {
not_disallowed = alltrue([
for i in range(1, length(var.update)) :
!contains(
flatten([for j in range(i) : lookup(var.disallow_rules, var.update[j], [])]),
var.update[i]
)
])
}
output "valid" {
value = local.not_disallowed ? var.update[floor(length(var.update) / 2)] : 0
}

23
2024/bonus/day05/main.tf Normal file
View File

@@ -0,0 +1,23 @@
variable "input" {
type = string
}
locals {
parts = split("\n\n", chomp(var.input))
rules = [for rule_line in split("\n", local.parts[0]) : [for v in split("|", rule_line) : tonumber(v)]]
disallow_rules = { for rule in local.rules : rule[1] => rule[0]... }
updates = [for update_line in split("\n", local.parts[1]) : [for v in split(",", update_line) : tonumber(v)]]
}
module "is_valid" {
source = "./is_correct"
count = length(local.updates)
update = local.updates[count.index]
disallow_rules = local.disallow_rules
}
output "part1" {
value = sum(module.is_valid[*].valid)
}

View File

@@ -56,3 +56,12 @@ output "day03_2" {
# output "day04_2" {
# value = module.day04.part2
# }
module "day05" {
source = "./day05"
input = file("../inputs/05.txt")
}
output "day05_1" {
value = module.day05.part1
}

View File

@@ -113,3 +113,20 @@ run "day4" {
error_message = "Part2 output is wrong"
}
}
run "day5_1" {
command = plan
module {
source = "./day05"
}
variables {
input = file("../tests/samples/05.txt")
}
assert {
condition = output.part1 == 143
error_message = "Part1 output is wrong"
}
}

61
2024/src/aoc/days/day7.py Normal file
View File

@@ -0,0 +1,61 @@
from . import SeparateRunner
def parse_input(input: str) -> tuple[int, list[int]]:
result = []
for line in input.strip().split("\n"):
test, nums = line.split(": ")
result.append((int(test), list(map(int, nums.split(" ")))))
return result
def is_possible(target: int, nums: list[int], cur: int) -> bool:
if cur == target and not nums:
return True
if cur > target or not nums:
return False
head = nums[0]
remainder = nums[1:]
return is_possible(target, remainder, cur + head) or is_possible(
target, remainder, cur * head
)
def is_possible2(target: int, nums: list[int], cur: int) -> bool:
if cur == target and not nums:
return True
if cur > target or not nums:
return False
head = nums[0]
remainder = nums[1:]
return (
is_possible2(target, remainder, cur + head)
or is_possible2(target, remainder, cur * head)
or is_possible2(target, remainder, int(f"{cur}{head}"))
)
class DayRunner(SeparateRunner):
@classmethod
def part1(cls, input: str) -> int:
lines = parse_input(input)
return sum(
target for target, nums in lines if is_possible(target, nums[1:], nums[0])
)
@classmethod
def part2(cls, input: str) -> int:
lines = parse_input(input)
return sum(
target for target, nums in lines if is_possible2(target, nums[1:], nums[0])
)

View File

@@ -0,0 +1,9 @@
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20

17
2024/tests/test_day07.py Normal file
View File

@@ -0,0 +1,17 @@
import os
from aoc.days.day7 import DayRunner
def get_data() -> str:
sample = os.path.dirname(__file__) + "/samples/07.txt"
with open(sample, mode="rt", encoding="utf-8") as f:
return f.read()
def test_sample_part1() -> None:
assert DayRunner.part1(get_data()) == 3749
def test_sample_part2() -> None:
assert DayRunner.part2(get_data()) == 11387