diff --git a/day14/Cargo.lock b/day14/Cargo.lock new file mode 100644 index 0000000..65b2381 --- /dev/null +++ b/day14/Cargo.lock @@ -0,0 +1,59 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "day14" +version = "0.1.0" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" +"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" +"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" diff --git a/day14/Cargo.toml b/day14/Cargo.toml new file mode 100644 index 0000000..1681011 --- /dev/null +++ b/day14/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "day14" +version = "0.1.0" +authors = ["Tyler Hallada "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lazy_static = "1.4.0" +regex = "1.3.3" diff --git a/day14/input/input.txt b/day14/input/input.txt new file mode 100644 index 0000000..d16eaf1 --- /dev/null +++ b/day14/input/input.txt @@ -0,0 +1,62 @@ +1 HJDM, 1 BMPDP, 8 DRCX, 2 TCTBL, 1 KGWDJ, 16 BRLF, 2 LWPB, 7 KDFQ => 6 ZSPL +1 PVRCK, 3 RSLR, 4 JBZD => 6 LCHRC +10 FCBVC, 1 TSJSJ, 20 SQCQ => 9 PNQLP +1 MBVL => 6 TSZJ +1 HWGQF => 4 ZSLVH +1 TBDSC, 13 TSZJ => 1 HRZH +1 RSLR, 1 LJWM => 3 RSFJR +1 VMZFB => 2 MBVL +4 DSTHJ, 2 TSZJ, 13 MBVL => 4 ZWLGK +1 MKTZ, 18 RVFJB, 1 RSLR, 2 HRZH, 14 ZWLGK, 4 RJFTV => 1 ZCVL +6 KDFQ, 1 PNQLP, 1 HRZH => 9 DLPMH +1 DSVT, 22 DRCX, 18 RJFTV, 2 MKTZ, 13 FVZBX, 15 SLTNZ, 7 ZSLVH => 5 GWJC +2 JZSJ, 3 ZSLVH, 6 HNRXC => 8 RJFTV +1 TSZJ => 7 GFVG +5 VMZFB => 4 JBZD +1 PBFZ, 23 JBZD, 2 LJWM => 1 TSJSJ +7 ZPQD => 7 VMZFB +2 LCHRC => 8 PXHK +2 TSZJ, 1 KCXMF, 1 FKJGC => 6 HWGQF +4 PBFZ => 1 FCBVC +1 GMWHM, 4 JQBKW => 8 SQCQ +5 SHMP => 5 PVRCK +10 KCXMF => 3 DRCX +15 VMZFB, 2 RSFJR => 6 KDFQ +35 HNRXC => 2 CJLG +8 MKTZ, 1 FCBVC, 12 HJDM => 9 BRLF +171 ORE => 8 GMWHM +8 RVFJB, 3 CJLG, 9 SLTNZ => 3 LWPB +1 PXHK, 2 RSFJR => 3 FVZBX +1 CJLG, 1 HRZH, 10 MKTZ => 8 KGWDJ +1 RSFJR => 3 FKJGC +1 NXCZM, 31 FKJGC => 2 MKTZ +18 XLWBP => 6 MBLWL +22 HNRXC => 8 FTGK +3 KGWDJ, 1 MLBJ, 5 HJDM => 7 DSVT +9 KDFQ => 5 NXCZM +2 RVFJB, 4 LGDKL, 1 PXHK => 5 CVTR +1 RSFJR, 6 GMWHM, 20 TSJSJ => 9 LGDKL +5 KCXMF => 9 RBDP +6 GWJC, 16 ZCVL, 29 JZSJ, 1 ZSPL, 35 MBLWL, 30 BWFRH, 2 MSFDB, 13 BMPDP, 11 FTGK, 1 ZWLGK => 1 FUEL +6 GFVG, 2 TVQP => 8 HJDM +1 CJLG, 13 PBFZ => 6 JZSJ +3 CVTR => 3 BMPDP +16 FPKMV, 1 ZSLVH => 8 MSFDB +9 JBZD, 12 LCHRC => 8 TBDSC +133 ORE => 3 LJWM +107 ORE => 7 SHMP +1 KDFQ, 1 LJWM => 9 FPKMV +3 PXHK => 4 BWFRH +123 ORE => 4 JQBKW +2 FVZBX, 1 JZSJ => 8 XLWBP +117 ORE => 2 ZPQD +7 NXCZM => 7 HNRXC +1 MLBJ, 22 RSLR => 8 KCXMF +2 TBDSC => 8 RVFJB +1 KDFQ, 23 DSTHJ => 7 SLTNZ +3 RSFJR => 6 MLBJ +5 PVRCK, 2 SQCQ => 9 RSLR +1 LGDKL, 17 MBVL, 6 PNQLP => 5 TVQP +3 RBDP => 6 TCTBL +1 DLPMH, 1 GFVG, 3 MBVL => 2 DSTHJ +21 VMZFB, 2 LJWM => 1 PBFZ diff --git a/day14/input/test1.txt b/day14/input/test1.txt new file mode 100644 index 0000000..65ad5cc --- /dev/null +++ b/day14/input/test1.txt @@ -0,0 +1,6 @@ +10 ORE => 10 A +1 ORE => 1 B +7 A, 1 B => 1 C +7 A, 1 C => 1 D +7 A, 1 D => 1 E +7 A, 1 E => 1 FUEL diff --git a/day14/input/test2.txt b/day14/input/test2.txt new file mode 100644 index 0000000..85c4b1e --- /dev/null +++ b/day14/input/test2.txt @@ -0,0 +1,7 @@ +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 diff --git a/day14/input/test3.txt b/day14/input/test3.txt new file mode 100644 index 0000000..4d6b9f0 --- /dev/null +++ b/day14/input/test3.txt @@ -0,0 +1,9 @@ +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 diff --git a/day14/input/test4.txt b/day14/input/test4.txt new file mode 100644 index 0000000..c670978 --- /dev/null +++ b/day14/input/test4.txt @@ -0,0 +1,12 @@ +2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG +17 NVRVD, 3 JNWZP => 8 VPVL +53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL +22 VJHF, 37 MNCFX => 5 FWMGM +139 ORE => 4 NVRVD +144 ORE => 7 JNWZP +5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC +5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV +145 ORE => 6 MNCFX +1 NVRVD => 8 CXFTF +1 VJHF, 6 MNCFX => 4 RFSQX +176 ORE => 6 VJHF diff --git a/day14/input/test5.txt b/day14/input/test5.txt new file mode 100644 index 0000000..a5ca2c3 --- /dev/null +++ b/day14/input/test5.txt @@ -0,0 +1,17 @@ +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 diff --git a/day14/src/main.rs b/day14/src/main.rs new file mode 100644 index 0000000..cfe3182 --- /dev/null +++ b/day14/src/main.rs @@ -0,0 +1,298 @@ +#[macro_use] +extern crate lazy_static; + +use std::collections::HashMap; +use std::error::Error; +use std::fs::read_to_string; +use std::result; +use std::str::FromStr; + +use regex::Regex; + +type Result = result::Result>; + +const INPUT: &str = "input/input.txt"; + +#[derive(Debug, PartialEq)] +struct Reactions { + reactions: HashMap, +} + +#[derive(Debug, PartialEq)] +struct Reaction { + output: ChemicalAmount, + inputs: Vec, +} + +#[derive(Debug, Hash, PartialEq, Eq)] +struct ChemicalAmount { + chemical: String, + amount: u32, +} + +impl FromStr for Reactions { + type Err = Box; + + fn from_str(s: &str) -> Result { + lazy_static! { + static ref SOURCE_CHEMICALS: Regex = + Regex::new(concat!(r"(?P\d+) (?P\w+),? ",)).unwrap(); + static ref OUTPUT_CHEMICAL: Regex = Regex::new(concat!( + r"=> (?P\d+) (?P\w+)" + )) + .unwrap(); + } + + let mut reactions = HashMap::new(); + for line in s.trim().split('\n') { + let mut inputs: Vec = vec![]; + for captures in SOURCE_CHEMICALS.captures_iter(line) { + inputs.push(ChemicalAmount { + chemical: captures["input_chemical"].to_string(), + amount: captures["input_amount"].parse()?, + }); + } + match OUTPUT_CHEMICAL.captures(line) { + None => { + return Err(From::from( + "Malformed reactions, no output chemical could be found", + )); + } + Some(captures) => { + let output = ChemicalAmount { + chemical: captures["output_chemical"].to_string(), + amount: captures["output_amount"].parse()?, + }; + reactions.insert(output.chemical.clone(), Reaction { inputs, output }); + } + }; + } + + Ok(Reactions { reactions }) + } +} + +impl Reactions { + fn new() -> Reactions { + Reactions { + reactions: HashMap::new(), + } + } +} + +fn calculate_ore_required( + reactions: &Reactions, + produced_chemical: &ChemicalAmount, + left_overs: &mut HashMap, +) -> u32 { + let reaction = &reactions.reactions[&produced_chemical.chemical]; + let mut needed_amount = produced_chemical.amount; + let mut left_over = 0; + if let Some(left_over_amount) = left_overs.get(&produced_chemical.chemical) { + left_over = *left_over_amount; + } + + if left_over > 0 { + if left_over >= needed_amount { + left_overs.insert( + produced_chemical.chemical.clone(), + left_over - needed_amount, + ); + return 0; + } else { + left_overs.insert(produced_chemical.chemical.clone(), 0); + needed_amount -= left_over; + } + } + + let ratio: f32 = needed_amount as f32 / reaction.output.amount as f32; + let production_count = ratio.ceil() as u32; + left_overs.insert( + produced_chemical.chemical.clone(), + (reaction.output.amount * production_count) - needed_amount, + ); + + if reaction.inputs.len() == 1 && reaction.inputs[0].chemical == "ORE" { + return reaction.inputs[0].amount * production_count; + } else { + return reaction + .inputs + .iter() + .map(|input| { + calculate_ore_required( + reactions, + &ChemicalAmount { + chemical: input.chemical.clone(), + amount: input.amount * production_count, + }, + left_overs, + ) + }) + .sum(); + } +} + +fn read_reactions(filename: &str) -> Result { + let reactions = read_to_string(filename)?.parse()?; + Ok(reactions) +} + +fn solve_part1(filename: &str) -> Result { + let reactions = read_reactions(filename)?; + let mut left_overs = HashMap::new(); + Ok(calculate_ore_required( + &reactions, + &ChemicalAmount { + chemical: "FUEL".to_string(), + amount: 1, + }, + &mut left_overs, + )) +} + +fn solve_part2(filename: &str) -> Result { + Ok(1) +} + +fn main() -> Result<()> { + println!("Part 1: {}", solve_part1(INPUT)?); + println!("Part 2: {}", solve_part2(INPUT)?); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_INPUT1: &str = "input/test1.txt"; + const TEST_INPUT2: &str = "input/test2.txt"; + const TEST_INPUT3: &str = "input/test3.txt"; + const TEST_INPUT4: &str = "input/test4.txt"; + const TEST_INPUT5: &str = "input/test5.txt"; + + fn reactions_1() -> Reactions { + Reactions { + reactions: vec![ + ( + "E".to_string(), + Reaction { + output: ChemicalAmount { + chemical: "E".to_string(), + amount: 1, + }, + inputs: vec![ + ChemicalAmount { + chemical: "A".to_string(), + amount: 7, + }, + ChemicalAmount { + chemical: "D".to_string(), + amount: 1, + }, + ], + }, + ), + ( + "A".to_string(), + Reaction { + output: ChemicalAmount { + chemical: "A".to_string(), + amount: 10, + }, + inputs: vec![ChemicalAmount { + chemical: "ORE".to_string(), + amount: 10, + }], + }, + ), + ( + "D".to_string(), + Reaction { + output: ChemicalAmount { + chemical: "D".to_string(), + amount: 1, + }, + inputs: vec![ + ChemicalAmount { + chemical: "A".to_string(), + amount: 7, + }, + ChemicalAmount { + chemical: "C".to_string(), + amount: 1, + }, + ], + }, + ), + ( + "FUEL".to_string(), + Reaction { + output: ChemicalAmount { + chemical: "FUEL".to_string(), + amount: 1, + }, + inputs: vec![ + ChemicalAmount { + chemical: "A".to_string(), + amount: 7, + }, + ChemicalAmount { + chemical: "E".to_string(), + amount: 1, + }, + ], + }, + ), + ( + "B".to_string(), + Reaction { + output: ChemicalAmount { + chemical: "B".to_string(), + amount: 1, + }, + inputs: vec![ChemicalAmount { + chemical: "ORE".to_string(), + amount: 1, + }], + }, + ), + ( + "C".to_string(), + Reaction { + output: ChemicalAmount { + chemical: "C".to_string(), + amount: 1, + }, + inputs: vec![ + ChemicalAmount { + chemical: "A".to_string(), + amount: 7, + }, + ChemicalAmount { + chemical: "B".to_string(), + amount: 1, + }, + ], + }, + ), + ] + .into_iter() + .collect(), + } + } + + #[test] + fn reads_reactions() { + assert_eq!(read_reactions(TEST_INPUT1).unwrap(), reactions_1()); + } + + #[test] + fn solves_part1() { + assert_eq!(solve_part1(TEST_INPUT1).unwrap(), 31); + assert_eq!(solve_part1(TEST_INPUT2).unwrap(), 165); + assert_eq!(solve_part1(TEST_INPUT3).unwrap(), 13312); + assert_eq!(solve_part1(TEST_INPUT4).unwrap(), 180697); + assert_eq!(solve_part1(TEST_INPUT5).unwrap(), 2210736); + } +}