mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Compare commits
3 Commits
e0d458c972
...
3e07c8563e
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e07c8563e | |||
| 608f3dc621 | |||
| ff2e421437 |
49
2024/bonus/README.md
Normal file
49
2024/bonus/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Bonus Challenge
|
||||||
|
|
||||||
|
This part of the repo holds the bonus challenge for 2024: implement as much as possible in pure
|
||||||
|
Terraform.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The infrastructure definitions expect the input files to be located at `../inputs/[\d]{2}.txt`.
|
||||||
|
After storing the input files there, the code should be runnable as follows:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ terraform init
|
||||||
|
< a lot of modules being installed >
|
||||||
|
$ terraform plan
|
||||||
|
|
||||||
|
Changes to Outputs:
|
||||||
|
+ day01_1 = 42
|
||||||
|
+ day01_2 = 12
|
||||||
|
+ day02_1 = …
|
||||||
|
|
||||||
|
You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that Terraform may freeze for tens of seconds while it's running the computations. This is
|
||||||
|
normal, and all Terraform code ought to think a little before doing anything. It would save people
|
||||||
|
some bad rollbacks.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
I use [Terraform tests](https://developer.hashicorp.com/terraform/language/tests) to automatically
|
||||||
|
run my terraform code on the sample inputs. It's almost a normal development workflow.
|
||||||
|
|
||||||
|
The only rule is that the code should be all terraform, no cheating by shelling out to external
|
||||||
|
programs. Using providers is allowed, as long as the providers don't actually interact with external
|
||||||
|
systems and are reasonably self-contained. I will try to limit my use of those regardless.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
DevOps will continue until morale improves. But really, a friend remarked that my Python solutions
|
||||||
|
were strangely normal for me, so I opted to use a language that I do use professionally.
|
||||||
|
|
||||||
|
Terraform is a unique beast. It can do a lot, but it is also very limited, and intentionally so.
|
||||||
|
There's a standard library of functions you might want to use, but all of them work in strange ways
|
||||||
|
and there isn't that much to begin with. You can never mutate any variables, you can only declare
|
||||||
|
new ones. You don't have recursion and your only source of loops are list- and map comprehensions,
|
||||||
|
or multiple instantiations or a module.
|
||||||
|
|
||||||
|
These make for a very constrained programming environment, and constrained programming is fun. It
|
||||||
|
makes you think outside the box.
|
||||||
@@ -3,20 +3,20 @@ variable "input" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
cleaned_input = replace(var.input, "/ +/", " ")
|
cleaned_input = replace(var.input, "/ +/", " ")
|
||||||
lines = split("\n", trim(local.cleaned_input, "\n"))
|
lines = split("\n", trim(local.cleaned_input, "\n"))
|
||||||
lines_split = [for line in local.lines: split(" ", line)]
|
lines_split = [for line in local.lines : split(" ", line)]
|
||||||
left = [for line in local.lines_split: parseint(line[0], 10)]
|
left = [for line in local.lines_split : parseint(line[0], 10)]
|
||||||
right = [for line in local.lines_split: parseint(line[1], 10)]
|
right = [for line in local.lines_split : parseint(line[1], 10)]
|
||||||
|
|
||||||
left_sorted = sort(local.left)
|
left_sorted = sort(local.left)
|
||||||
right_sorted = sort(local.right)
|
right_sorted = sort(local.right)
|
||||||
|
|
||||||
diffs = [for i in range(length(local.left_sorted)): abs(local.left_sorted[i] - local.right_sorted[i])]
|
diffs = [for i in range(length(local.left_sorted)) : abs(local.left_sorted[i] - local.right_sorted[i])]
|
||||||
|
|
||||||
counts = {for num in local.right: num => num...}
|
counts = { for num in local.right : num => num... }
|
||||||
|
|
||||||
matching = [for left in local.left: left * length(lookup(local.counts, left, []))]
|
matching = [for left in local.left : left * try(length(local.counts[left]), 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
output "part1" {
|
output "part1" {
|
||||||
@@ -24,5 +24,5 @@ output "part1" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
output "part2" {
|
output "part2" {
|
||||||
value = sum(local.matching)
|
value = sum(local.matching)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module "is_valid" {
|
|||||||
|
|
||||||
report = concat(
|
report = concat(
|
||||||
count.index > 0 ? slice(var.report, 0, count.index) : [],
|
count.index > 0 ? slice(var.report, 0, count.index) : [],
|
||||||
count.index < length(var.report) - 1 ? slice(var.report, count.index + 1, length(var.report)) : []
|
try(slice(var.report, count.index + 1, length(var.report)), [])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,21 @@ variable "input" {
|
|||||||
|
|
||||||
locals {
|
locals {
|
||||||
muls = regexall("mul\\((\\d+),(\\d+)\\)", var.input)
|
muls = regexall("mul\\((\\d+),(\\d+)\\)", var.input)
|
||||||
|
ops = regexall("(don't\\(\\)|do\\(\\)|mul\\((\\d+),(\\d+)\\))", var.input)
|
||||||
|
}
|
||||||
|
|
||||||
|
module "should_execute" {
|
||||||
|
count = length(local.ops)
|
||||||
|
source = "./should_execute"
|
||||||
|
|
||||||
|
index = count.index
|
||||||
|
ops = local.ops
|
||||||
}
|
}
|
||||||
|
|
||||||
output "part1" {
|
output "part1" {
|
||||||
value = sum([for mul in local.muls : parseint(mul[1], 10) * parseint(mul[0], 10)])
|
value = sum([for mul in local.muls : parseint(mul[1], 10) * parseint(mul[0], 10)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output "part2" {
|
||||||
|
value = sum(module.should_execute[*].value)
|
||||||
|
}
|
||||||
|
|||||||
19
2024/bonus/day03/should_execute/main.tf
Normal file
19
2024/bonus/day03/should_execute/main.tf
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -36,3 +36,7 @@ module "day03" {
|
|||||||
output "day03_1" {
|
output "day03_1" {
|
||||||
value = module.day03.part1
|
value = module.day03.part1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output "day03_2" {
|
||||||
|
value = module.day03.part2
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,3 +57,20 @@ run "day3_1" {
|
|||||||
error_message = "Part1 output is wrong"
|
error_message = "Part1 output is wrong"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run "day3_2" {
|
||||||
|
command = plan
|
||||||
|
|
||||||
|
module {
|
||||||
|
source = "./day03"
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
input = "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
condition = output.part2 == 48
|
||||||
|
error_message = "Part2 output is wrong"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user