
11 changed files with 651 additions and 0 deletions
@ -0,0 +1,10 @@
|
||||
[package] |
||||
name = "day12" |
||||
version = "0.1.0" |
||||
authors = ["Stefan Bühler <stbuehler@web.de>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
num-integer = "0.1.41" |
@ -0,0 +1,4 @@
|
||||
<x=-2, y=9, z=-5> |
||||
<x=16, y=19, z=9> |
||||
<x=0, y=3, z=6> |
||||
<x=11, y=0, z=11> |
@ -0,0 +1,166 @@
|
||||
fn strip_prefix<'a>(prefix: &str, input: &'a str) -> &'a str { |
||||
assert!(input.starts_with(prefix)); |
||||
&input[prefix.len()..] |
||||
} |
||||
|
||||
fn strip_suffix<'a>(suffix: &str, input: &'a str) -> &'a str { |
||||
assert!(input.ends_with(suffix)); |
||||
&input[..input.len() - suffix.len()] |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] |
||||
pub struct Position { |
||||
pub x: i32, |
||||
pub y: i32, |
||||
pub z: i32, |
||||
} |
||||
|
||||
impl Position { |
||||
pub fn parse(input: &str) -> Self { |
||||
let input = strip_suffix(">", strip_prefix("<", input.trim())); |
||||
let mut parts = input.split(","); |
||||
let x = strip_prefix("x=", parts.next().expect("need x component").trim()).parse().unwrap(); |
||||
let y = strip_prefix("y=", parts.next().expect("need y component").trim()).parse().unwrap(); |
||||
let z = strip_prefix("z=", parts.next().expect("need z component").trim()).parse().unwrap(); |
||||
assert!(parts.next().is_none(), "unexpected data"); |
||||
Position { x, y, z } |
||||
} |
||||
|
||||
pub fn potential_energy(self) -> u32 { |
||||
self.x.abs() as u32 + self.y.abs() as u32 + self.z.abs() as u32 |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] |
||||
pub struct Velocity { |
||||
pub dx: i32, |
||||
pub dy: i32, |
||||
pub dz: i32, |
||||
} |
||||
|
||||
impl Velocity { |
||||
pub fn kinetic_energy(self) -> u32 { |
||||
self.dx.abs() as u32 + self.dy.abs() as u32 + self.dz.abs() as u32 |
||||
} |
||||
} |
||||
|
||||
impl std::ops::AddAssign<Velocity> for Position { |
||||
fn add_assign(&mut self, rhs: Velocity) { |
||||
self.x += rhs.dx; |
||||
self.y += rhs.dy; |
||||
self.z += rhs.dz; |
||||
} |
||||
} |
||||
|
||||
fn parse_moons(input: &str) -> Vec<(Position, Velocity)> { |
||||
input.lines().map(Position::parse).map(|p| (p, Velocity::default())).collect() |
||||
} |
||||
|
||||
fn step(moons: &mut [(Position, Velocity)]) { |
||||
for i in 0..moons.len() - 1 { |
||||
// change velocity based on "gravity"
|
||||
for j in i + 1..moons.len() { |
||||
let dx = (moons[j].0.x - moons[i].0.x).signum(); |
||||
moons[i].1.dx += dx; moons[j].1.dx -= dx; |
||||
let dy = (moons[j].0.y - moons[i].0.y).signum(); |
||||
moons[i].1.dy += dy; moons[j].1.dy -= dy; |
||||
let dz = (moons[j].0.z - moons[i].0.z).signum(); |
||||
moons[i].1.dz += dz; moons[j].1.dz -= dz; |
||||
} |
||||
} |
||||
for moon in moons { |
||||
moon.0 += moon.1; |
||||
} |
||||
} |
||||
|
||||
fn energy(moons: &[(Position, Velocity)]) -> u32 { |
||||
moons.iter().map(|(p, v)| p.potential_energy() * v.kinetic_energy()).sum() |
||||
} |
||||
|
||||
fn energy_after_steps(mut moons: Vec<(Position, Velocity)>, steps: u32) -> u32 { |
||||
for _ in 0..steps { |
||||
step(&mut moons); |
||||
} |
||||
energy(&moons) |
||||
} |
||||
|
||||
type ExtractedAxis = Vec<(i32, i32)>; |
||||
fn extract_x(moons: &[(Position, Velocity)]) -> ExtractedAxis { |
||||
moons.iter().map(|(p, v)| (p.x, v.dx)).collect() |
||||
} |
||||
fn extract_y(moons: &[(Position, Velocity)]) -> ExtractedAxis { |
||||
moons.iter().map(|(p, v)| (p.y, v.dy)).collect() |
||||
} |
||||
fn extract_z(moons: &[(Position, Velocity)]) -> ExtractedAxis { |
||||
moons.iter().map(|(p, v)| (p.z, v.dz)).collect() |
||||
} |
||||
|
||||
fn repeats_after_steps(mut moons: Vec<(Position, Velocity)>) -> u64 { |
||||
// as `step` is reversible the first repeated state will always be the initial state
|
||||
|
||||
// find cycle per axis
|
||||
let mut result_x = None; |
||||
let initial_x = extract_x(&moons); |
||||
let mut result_y = None; |
||||
let initial_y = extract_y(&moons); |
||||
let mut result_z = None; |
||||
let initial_z = extract_z(&moons); |
||||
|
||||
for current in 1u64.. { |
||||
step(&mut moons); |
||||
if result_x.is_none() && initial_x == extract_x(&moons) { |
||||
result_x = Some(current); |
||||
} |
||||
if result_y.is_none() && initial_y == extract_y(&moons) { |
||||
result_y = Some(current); |
||||
} |
||||
if result_z.is_none() && initial_z == extract_z(&moons) { |
||||
result_z = Some(current); |
||||
} |
||||
if let (Some(x), Some(y), Some(z)) = (result_x, result_y, result_z) { |
||||
return num_integer::lcm(num_integer::lcm(x, y), z); |
||||
} |
||||
} |
||||
unreachable!() |
||||
} |
||||
|
||||
fn main() { |
||||
let moons = parse_moons(include_str!("input.txt")); |
||||
let energy = energy_after_steps(moons.clone(), 1000); |
||||
println!("Energy after 1000 steps: {}", energy); |
||||
println!("Repeats initial state after {} steps", repeats_after_steps(moons)); |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod day12test { |
||||
use super::*; |
||||
|
||||
#[test] |
||||
fn examples1() { |
||||
assert_eq!(179, energy_after_steps(parse_moons( |
||||
"<x=-1, y=0, z=2> |
||||
<x=2, y=-10, z=-7> |
||||
<x=4, y=-8, z=8> |
||||
<x=3, y=5, z=-1>"), 10)); |
||||
assert_eq!(1940, energy_after_steps(parse_moons( |
||||
"<x=-8, y=-10, z=0> |
||||
<x=5, y=5, z=10> |
||||
<x=2, y=-7, z=3> |
||||
<x=9, y=-8, z=-3> |
||||
"), 100)); |
||||
} |
||||
|
||||
#[test] |
||||
fn examples2() { |
||||
assert_eq!(2772, repeats_after_steps(parse_moons( |
||||
"<x=-1, y=0, z=2> |
||||
<x=2, y=-10, z=-7> |
||||
<x=4, y=-8, z=8> |
||||
<x=3, y=5, z=-1>"))); |
||||
assert_eq!(4686774924, repeats_after_steps(parse_moons( |
||||
"<x=-8, y=-10, z=0> |
||||
<x=5, y=5, z=10> |
||||
<x=2, y=-7, z=3> |
||||
<x=9, y=-8, z=-3>"))); |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
[package] |
||||
name = "day13" |
||||
version = "0.1.0" |
||||
authors = ["Stefan Bühler <stbuehler@web.de>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
shared = { path = "../shared" } |
File diff suppressed because one or more lines are too long
@ -0,0 +1,147 @@
|
||||
use shared::intcode::{IntCode, SimulateStep, CELL}; |
||||
use std::collections::HashSet; |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
||||
pub enum Tile { |
||||
Empty, |
||||
Wall, |
||||
Block, |
||||
HorizontalPaddle, |
||||
Ball, |
||||
} |
||||
|
||||
impl Tile { |
||||
pub fn new(t: CELL) -> Self { |
||||
match t { |
||||
0 => Self::Empty, |
||||
1 => Self::Wall, |
||||
2 => Self::Block, |
||||
3 => Self::HorizontalPaddle, |
||||
4 => Self::Ball, |
||||
_ => panic!("Unknown tile {}", t), |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
||||
pub struct Position { |
||||
pub x: CELL, |
||||
pub y: CELL, |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
||||
pub enum Update { |
||||
Tile { |
||||
position: Position, |
||||
tile: Tile, |
||||
}, |
||||
Score { |
||||
score: CELL, |
||||
}, |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
||||
#[repr(i8)] |
||||
pub enum Joystick { |
||||
Left = -1, |
||||
Neutral = 0, |
||||
Right = 1, |
||||
} |
||||
|
||||
fn view_game(mut ic: IntCode) -> impl Iterator<Item=(Position, Tile)> { |
||||
std::iter::from_fn(move || { |
||||
let x = match ic.simulate_step() { |
||||
SimulateStep::Finished => return None, |
||||
SimulateStep::WaitForInput => panic!("no input for game"), |
||||
SimulateStep::Output(n) => n, |
||||
}; |
||||
let y = ic.simulate_step().expect_output(); |
||||
let p = Position { x, y }; |
||||
let tile = Tile::new(ic.simulate_step().expect_output()); |
||||
Some((p, tile)) |
||||
}) |
||||
} |
||||
|
||||
fn find_blocks<I>(tiles: I) -> HashSet<Position> |
||||
where |
||||
I: Iterator<Item=(Position, Tile)>, |
||||
{ |
||||
let mut blocks = HashSet::new(); |
||||
for (pos, tile) in tiles { |
||||
match tile { |
||||
Tile::Block => { blocks.insert(pos); } |
||||
_ => { blocks.remove(&pos); } |
||||
} |
||||
} |
||||
blocks |
||||
} |
||||
|
||||
fn get_update(ic: &mut IntCode, joystick: Joystick) -> Option<Update> { |
||||
let joystick = joystick as i8 as CELL; |
||||
ic.next_input = Some(joystick); |
||||
let x = match ic.simulate_step() { |
||||
SimulateStep::Finished => { |
||||
ic.next_input = None; |
||||
return None; |
||||
}, |
||||
SimulateStep::WaitForInput => panic!("no input for game"), |
||||
SimulateStep::Output(n) => n, |
||||
}; |
||||
let y = ic.simulate_step().expect_output(); |
||||
if x == -1 && y == 0 { |
||||
Some(Update::Score { score: ic.simulate_step().expect_output() }) |
||||
} else { |
||||
let position = Position { x, y }; |
||||
let tile = Tile::new(ic.simulate_step().expect_output()); |
||||
Some(Update::Tile { position, tile }) |
||||
} |
||||
} |
||||
|
||||
fn break_all_blocks(mut ic: IntCode) -> CELL { |
||||
ic.data[0] = 2; // "play for free"
|
||||
let mut last_score = None; |
||||
let mut last_paddle = None; |
||||
let mut last_ball = None; |
||||
let mut blocks = HashSet::new(); |
||||
let mut had_blocks = false; |
||||
let mut joystick = Joystick::Neutral; |
||||
while let Some(update) = get_update(&mut ic, joystick) { |
||||
match update { |
||||
Update::Score { score } => last_score = Some(score), |
||||
Update::Tile { position, tile } => { |
||||
blocks.remove(&position); |
||||
match tile { |
||||
Tile::Empty => (), |
||||
Tile::Wall => (), |
||||
Tile::Block => { blocks.insert(position); had_blocks = true; }, |
||||
Tile::HorizontalPaddle => last_paddle = Some(position), |
||||
Tile::Ball => last_ball = Some(position), |
||||
} |
||||
} |
||||
} |
||||
if let (Some(paddle), Some(ball)) = (&last_paddle, &last_ball) { |
||||
let diff_x = ball.x - paddle.x; |
||||
joystick = if diff_x > 0 { |
||||
Joystick::Right |
||||
} else if diff_x < 0 { |
||||
Joystick::Left |
||||
} else { |
||||
Joystick::Neutral |
||||
}; |
||||
} |
||||
} |
||||
if had_blocks && blocks.is_empty() { |
||||
println!("Won game, all blocks destroyed"); |
||||
} else { |
||||
println!("Lost game"); |
||||
} |
||||
last_score.expect("no score written") |
||||
} |
||||
|
||||
fn main() { |
||||
let ic = include_str!("input.txt").parse::<IntCode>().unwrap(); |
||||
let blocks = find_blocks(view_game(ic.clone())); |
||||
println!("Number of blocks: {}", blocks.len()); |
||||
let score = break_all_blocks(ic); |
||||
println!("Score: {}", score); |
||||
} |
@ -0,0 +1,9 @@
|
||||
[package] |
||||
name = "day14" |
||||
version = "0.1.0" |
||||
authors = ["Stefan Bühler <stbuehler@web.de>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
@ -0,0 +1,63 @@
|
||||
12 VJWQR, 1 QTBC => 6 BGXJV |
||||
12 BGTMN, 2 DRKQR => 2 TVSF |
||||
2 FTFK => 2 THNDN |
||||
13 LKRTN, 7 MDPN, 12 NZKQZ => 5 LPWPD |
||||
1 HDKX, 3 DWZS, 1 RCBQS => 1 DCRK |
||||
14 ZCMF => 6 ZKHLC |
||||
3 ZFVH, 2 ZCMF, 1 SCJG, 1 LQWJ, 4 BGBJ, 1 NHPR, 3 VKZFJ, 7 FWFXZ => 4 QVJMP |
||||
11 TNMLB => 7 NVDCR |
||||
1 LPWPD, 1 BGBJ => 2 SCJG |
||||
3 DFCVF, 1 QGSN => 6 PQXG |
||||
1 BGXJV, 1 THNDN => 4 BCQN |
||||
3 LKRTN => 9 MDPN |
||||
2 THNDN, 13 RCKZ, 10 FQSLN => 8 VKZFJ |
||||
4 LBCZ, 9 LWHS => 1 FQSLN |
||||
6 WSRVZ => 9 TNMLB |
||||
8 FQSLN, 14 JQRF, 4 BGTMN => 5 QGSN |
||||
4 ZCMF, 4 PLSM, 2 ZHTX => 8 TDHPM |
||||
2 RSKC, 10 SHBC, 8 MDPN => 6 FMSZ |
||||
2 VJWQR => 2 FPTV |
||||
12 DRKQR => 6 NHPR |
||||
35 QJLF, 22 BGTMN, 11 VJWTR, 1 QVJMP, 8 LQWJ, 1 TWLC, 16 NXZCH, 18 THKF, 42 JBLM => 1 FUEL |
||||
2 BGTMN, 4 XJKN => 8 ZCMF |
||||
4 TVSF => 3 RSKC |
||||
7 HRWS, 1 TVSF => 3 ZHTX |
||||
134 ORE => 4 WSRVZ |
||||
1 VKZFJ, 1 TWLC, 4 ZHTX, 5 THNDN, 12 PLVN, 1 ZFXNP, 1 PQXG, 6 CWHV => 7 VJWTR |
||||
20 XJKN, 1 LCKW, 3 NZKQZ => 7 HDKX |
||||
1 LPWPD => 8 RCKZ |
||||
4 RCBQS, 1 NVDCR => 5 BGBJ |
||||
8 BGXJV => 4 BGTMN |
||||
13 QBDX, 16 BGXJV => 6 NZKQZ |
||||
2 LPWPD => 3 DRKQR |
||||
4 QBDX => 7 XJKN |
||||
12 LCKW, 9 NVDCR => 3 RCBQS |
||||
142 ORE => 3 QBDX |
||||
1 WXHJF => 1 XKDJ |
||||
2 RSKC => 2 CWHV |
||||
2 ZHTX, 1 ZFXNP => 6 JQRF |
||||
1 FTFK, 1 TVSF, 1 QBDX => 2 JBLM |
||||
1 TDHPM, 14 NHPR, 3 QPSF => 5 ZFVH |
||||
3 GDTPC, 1 ZKHLC => 8 ZFXNP |
||||
5 DWZS => 3 LQWJ |
||||
1 FTFK, 4 LBCZ, 13 NHPR => 1 FWFXZ |
||||
1 RCBQS, 12 SHBC => 9 FTFK |
||||
1 WSRVZ, 1 XKDJ => 5 LKRTN |
||||
2 BGTMN, 1 MDPN => 5 PLSM |
||||
2 BGXJV, 17 XKDJ, 4 FPTV => 9 LCKW |
||||
148 ORE => 2 QTBC |
||||
110 ORE => 2 VJWQR |
||||
42 ZFXNP, 15 RCKZ, 8 GDTPC => 3 QJLF |
||||
13 HRWS => 4 GDTPC |
||||
34 HRWS => 4 DFCVF |
||||
2 VKZFJ, 2 NHPR, 16 PLVN, 1 QPSF, 13 LBCZ, 4 DCRK, 10 LWHS => 7 NXZCH |
||||
3 CWHV, 1 THNDN => 7 LWHS |
||||
1 BGXJV, 2 QBDX => 5 DWZS |
||||
9 LQWJ => 8 QPSF |
||||
21 BCQN, 3 FMSZ, 1 RSKC => 5 THKF |
||||
118 ORE => 6 WXHJF |
||||
11 FMSZ => 9 TWLC |
||||
28 PLSM => 5 SHBC |
||||
1 ZKHLC, 23 SCJG => 7 LBCZ |
||||
17 DWZS, 16 THNDN => 9 PLVN |
||||
7 HDKX => 9 HRWS |
@ -0,0 +1,195 @@
|
||||
use std::collections::HashMap; |
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
||||
pub struct ChemicalAmount<'a> { |
||||
pub amount: u64, |
||||
pub name: &'a str, |
||||
} |
||||
|
||||
impl<'a> ChemicalAmount<'a> { |
||||
pub fn parse(input: &'a str) -> Self { |
||||
let input = input.trim(); |
||||
let mut elems = input.split_whitespace(); |
||||
let amount = elems.next().expect("need amount").trim().parse::<u64>().expect("number for amount"); |
||||
let name = elems.next().expect("need amount").trim(); |
||||
assert!(elems.next().is_none(), "unexpected data"); |
||||
ChemicalAmount { |
||||
amount, |
||||
name, |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub type Inputs<'a> = Vec<ChemicalAmount<'a>>; |
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
||||
pub struct Reaction<'a> { |
||||
pub inputs: Inputs<'a>, |
||||
pub output: ChemicalAmount<'a>, |
||||
} |
||||
|
||||
impl<'a> Reaction<'a> { |
||||
pub fn parse(input: &'a str) -> Self { |
||||
let mut parts = input.split("=>"); |
||||
let inputs = parts.next().expect("need inputs").trim().split(",").map(ChemicalAmount::parse).collect(); |
||||
let output = ChemicalAmount::parse(parts.next().expect("need output").trim()); |
||||
assert!(parts.next().is_none(), "unexpected data"); |
||||
Reaction { |
||||
inputs, |
||||
output, |
||||
} |
||||
} |
||||
|
||||
pub fn runs_for(&self, required_output: u64) -> u64 { |
||||
(required_output + self.output.amount - 1) / self.output.amount |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Debug)] |
||||
pub struct Reactions<'a>(pub HashMap<&'a str, Reaction<'a>>); |
||||
|
||||
impl<'a> Reactions<'a> { |
||||
pub fn parse(input: &'a str) -> Self { |
||||
Self(input.lines().map(Reaction::parse).map(|reaction| (reaction.output.name.into(), reaction)).collect()) |
||||
} |
||||
|
||||
fn gather_ore(&self, needs: &mut HashMap<&'a str, u64>, name: &'a str, additional: u64) -> u64 { |
||||
if name == "ORE" { return additional; } |
||||
let previous_need; |
||||
let updated_need; |
||||
{ |
||||
let need = needs.entry(&name).or_default(); |
||||
previous_need = *need; |
||||
updated_need = previous_need + additional; |
||||
*need = updated_need; |
||||
} |
||||
let reaction = &self.0[name]; |
||||
let previous_runs = reaction.runs_for(previous_need); |
||||
let updated_runs = reaction.runs_for(updated_need); |
||||
let mut ore = 0; |
||||
if updated_runs > previous_runs { |
||||
let new_runs = updated_runs - previous_runs; |
||||
for input in &reaction.inputs { |
||||
ore += self.gather_ore(needs, input.name, input.amount * new_runs); |
||||
} |
||||
} |
||||
ore |
||||
} |
||||
|
||||
fn ore_for_fuel(&self, fuel: u64) -> u64 { |
||||
let mut needs = HashMap::new(); |
||||
self.gather_ore(&mut needs, "FUEL", fuel) |
||||
} |
||||
} |
||||
|
||||
// check must be monotone, there must be a `n` such that for all `i`: `i <= n <=> check(i)`
|
||||
// returns `None` if this `n` is less than 0 (i.e. check is never true)
|
||||
fn binary_search_max<F>(check: F, start: u64, max_step: u64) -> Option<u64> |
||||
where |
||||
F: Fn(u64) -> bool, |
||||
{ |
||||
let mut upper; |
||||
let mut lower; |
||||
|
||||
// find boundaries
|
||||
if !check(start) { |
||||
// find lower limit
|
||||
if start == 0 { return None; } |
||||
upper = start - 1; |
||||
loop { |
||||
lower = start / 2; |
||||
if check(lower) { break; } |
||||
if lower == 0 { return None; } |
||||
upper = lower - 1; |
||||
} |
||||
} else { |
||||
lower = start; |
||||
// find upper limit
|
||||
upper = start; |
||||
loop { |
||||
upper += max_step; |
||||
if !check(upper) { |
||||
upper -= 1; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// invariant: check(lower) && !check(upper + 1)
|
||||
while lower != upper { |
||||
debug_assert!(lower < upper); |
||||
let mid = (lower + upper + 1) / 2; |
||||
debug_assert!(mid > lower); |
||||
if check(mid) { |
||||
lower = mid; |
||||
} else { |
||||
upper = mid - 1; |
||||
} |
||||
} |
||||
Some(lower) |
||||
} |
||||
|
||||
fn main() { |
||||
let reactions = Reactions::parse(include_str!("input.txt")); |
||||
let ore_one_fuel; |
||||
{ |
||||
ore_one_fuel = reactions.ore_for_fuel(1); |
||||
println!("Need {} ORE for one FUEL", ore_one_fuel); |
||||
} |
||||
|
||||
let have_ore = 1000000000000u64; |
||||
let max_fuel = binary_search_max(|fuel| { |
||||
reactions.ore_for_fuel(fuel) <= have_ore |
||||
}, have_ore / ore_one_fuel, have_ore / ore_one_fuel).unwrap(); |
||||
println!("Can get {} fuel for {} ore", max_fuel, have_ore); |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod day14test { |
||||
use super::*; |
||||
|
||||
fn calculate_needed_ore(input: &str) -> u64 { |
||||
let reactions = Reactions::parse(input); |
||||
reactions.ore_for_fuel(1) |
||||
} |
||||
|
||||
#[test] |
||||
fn examples1() { |
||||
assert_eq!(165, calculate_needed_ore( |
||||
"9 ORE => 2 A |
||||
8 ORE => 3 B |
||||
7 ORE => 5 C |
||||
3 A, 4 B => 1 AB |
||||
5 B, 7 C => 1 BC |
||||
4 C, 1 A => 1 CA |
||||
2 AB, 3 BC, 4 CA => 1 FUEL")); |
||||
assert_eq!(13312, calculate_needed_ore( |
||||
"157 ORE => 5 NZVS |
||||
165 ORE => 6 DCFZ |
||||
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL |
||||
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ |
||||
179 ORE => 7 PSHF |
||||
177 ORE => 5 HKGWZ |
||||
7 DCFZ, 7 PSHF => 2 XJWVT |
||||
165 ORE => 2 GPVTF |
||||
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT")); |
||||
assert_eq!(2210736, calculate_needed_ore( |
||||
"171 ORE => 8 CNZTR |
||||
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL |
||||
114 ORE => 4 BHXH |
||||
14 VRPVC => 6 BMBT |
||||
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL |
||||
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT |
||||
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW |
||||
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW |
||||
5 BMBT => 4 WPTQ |
||||
189 ORE => 9 KTJDG |
||||
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP |
||||
12 VRPVC, 27 CNZTR => 2 XDBXC |
||||
15 KTJDG, 12 BHXH => 5 XCVML |
||||
3 BHXH, 2 VRPVC => 7 MZWV |
||||
121 ORE => 7 VRPVC |
||||
7 XCVML => 6 RJRHP |
||||
5 BHXH, 4 VRPVC => 5 LTCX")); |
||||
} |
||||
} |
Loading…
Reference in new issue