This commit is contained in:
Hamcha 2024-12-13 14:38:24 +01:00
parent f17388b5ec
commit 49a5fd7b52
3 changed files with 221 additions and 0 deletions

15
sample/day13-sample.txt Normal file
View file

@ -0,0 +1,15 @@
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279

105
src/day13_p1.gleam Normal file
View file

@ -0,0 +1,105 @@
import gleam/int
import gleam/io
import gleam/iterator
import gleam/list.{map, reduce}
import gleam/option.{type Option}
import gleam/string
import stdin.{stdin}
pub fn main() {
stdin()
|> read_input
|> map(calculate_win_cost)
|> option.values
|> reduce(int.add)
|> io.debug
}
fn calculate_win_cost(machine: ClawMachine) -> Option(Int) {
let Machine(a, b, prize) = machine
let #(a_x, a_y) = a
let #(b_x, b_y) = b
let #(p_x, p_y) = prize
let a_min = min_presses(a, prize)
list.range(0, a_min)
|> list.filter_map(fn(a_times) {
let prize_rem_x = p_x - a_x * a_times
let prize_rem_y = p_y - a_y * a_times
let b_times = min_presses(b, #(prize_rem_x, prize_rem_y))
case prize_rem_x - b_x * b_times, prize_rem_y - b_y * b_times {
0, 0 -> Ok(#(a_times, b_times))
_, _ -> Error(Nil)
}
})
|> map(calculate_cost)
|> reduce(int.min)
|> option.from_result
}
fn calculate_cost(combo: #(Int, Int)) -> Int {
let #(a, b) = combo
a * 3 + b
}
fn min_presses(advance: #(Int, Int), target: #(Int, Int)) -> Int {
let #(a_x, a_y) = advance
let #(t_x, t_y) = target
let assert Ok(a_min_x) = int.divide(t_x, a_x)
let assert Ok(a_min_y) = int.divide(t_y, a_y)
int.min(a_min_x, a_min_y)
}
pub type ClawMachine {
Machine(a: #(Int, Int), b: #(Int, Int), prize: #(Int, Int))
}
fn read_input(it: iterator.Iterator(String)) -> List(ClawMachine) {
it
|> iterator.fold([], fn(acc, line) {
case string.trim(line) {
"Button A: " <> rest -> [
Machine(parse_button(rest), #(0, 0), #(0, 0)),
..acc
]
"Button B: " <> rest -> acc |> patch_b(parse_button(rest))
"Prize: " <> rest -> acc |> patch_prize(parse_prize(rest))
_ -> acc
}
})
}
fn parse_button(text) -> #(Int, Int) {
let assert [Ok(x), Ok(y)] =
text
|> string.drop_start(2)
|> string.split(", Y+")
|> map(int.parse)
#(x, y)
}
fn parse_prize(text) -> #(Int, Int) {
let assert [Ok(x), Ok(y)] =
text
|> string.drop_start(2)
|> string.split(", Y=")
|> map(int.parse)
#(x, y)
}
fn patch_b(acc: List(ClawMachine), b: #(Int, Int)) -> List(ClawMachine) {
case acc {
[] -> [Machine(#(0, 0), b, #(0, 0))]
[Machine(a, _, p), ..rest] -> [Machine(a, b, p), ..rest]
}
}
fn patch_prize(acc: List(ClawMachine), p: #(Int, Int)) -> List(ClawMachine) {
case acc {
[] -> [Machine(#(0, 0), #(0, 0), p)]
[Machine(a, b, _), ..rest] -> [Machine(a, b, p), ..rest]
}
}

101
src/day13_p2.gleam Normal file
View file

@ -0,0 +1,101 @@
import gleam/int
import gleam/io
import gleam/iterator
import gleam/list.{map, reduce}
import gleam/option.{type Option, None, Some}
import gleam/string
import stdin.{stdin}
pub fn main() {
stdin()
|> read_input
|> map(calculate_win_cost)
|> option.values
|> reduce(int.add)
|> io.debug
}
fn calculate_win_cost(machine: ClawMachine) -> Option(Int) {
let Machine(a, b, prize) = machine
let #(a_x, a_y) = a
let #(b_x, b_y) = b
let #(p_x, p_y) = prize
let assert Ok(a_times) =
int.divide(b_y * p_x - b_x * p_y, a_x * b_y - a_y * b_x)
let prize_rem_x = p_x - a_x * a_times
let prize_rem_y = p_y - a_y * a_times
let b_times = min_presses(b, #(prize_rem_x, prize_rem_y))
case prize_rem_x - b_x * b_times, prize_rem_y - b_y * b_times {
0, 0 -> Some(#(a_times, b_times))
_, _ -> None
}
|> option.map(calculate_cost)
}
fn calculate_cost(combo: #(Int, Int)) -> Int {
let #(a, b) = combo
a * 3 + b
}
fn min_presses(advance: #(Int, Int), target: #(Int, Int)) -> Int {
let #(a_x, a_y) = advance
let #(t_x, t_y) = target
let assert Ok(a_min_x) = int.divide(t_x, a_x)
let assert Ok(a_min_y) = int.divide(t_y, a_y)
int.min(a_min_x, a_min_y)
}
pub type ClawMachine {
Machine(a: #(Int, Int), b: #(Int, Int), prize: #(Int, Int))
}
fn read_input(it: iterator.Iterator(String)) -> List(ClawMachine) {
it
|> iterator.fold([], fn(acc, line) {
case string.trim(line) {
"Button A: " <> rest -> [
Machine(parse_button(rest), #(0, 0), #(0, 0)),
..acc
]
"Button B: " <> rest -> acc |> patch_b(parse_button(rest))
"Prize: " <> rest -> acc |> patch_prize(parse_prize(rest))
_ -> acc
}
})
}
fn parse_button(text) -> #(Int, Int) {
let assert [Ok(x), Ok(y)] =
text
|> string.drop_start(2)
|> string.split(", Y+")
|> map(int.parse)
#(x, y)
}
fn parse_prize(text) -> #(Int, Int) {
let assert [Ok(x), Ok(y)] =
text
|> string.drop_start(2)
|> string.split(", Y=")
|> map(int.parse)
#(x + 10_000_000_000_000, y + 10_000_000_000_000)
}
fn patch_b(acc: List(ClawMachine), b: #(Int, Int)) -> List(ClawMachine) {
case acc {
[] -> [Machine(#(0, 0), b, #(0, 0))]
[Machine(a, _, p), ..rest] -> [Machine(a, b, p), ..rest]
}
}
fn patch_prize(acc: List(ClawMachine), p: #(Int, Int)) -> List(ClawMachine) {
case acc {
[] -> [Machine(#(0, 0), #(0, 0), p)]
[Machine(a, b, _), ..rest] -> [Machine(a, b, p), ..rest]
}
}