From eacf990729ba9210933cf0664b544bb0db2dbaac Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Fri, 1 Nov 2024 16:52:33 +0100 Subject: [PATCH 01/12] Script to prepare days and years --- Cargo.lock | 4 +++ Cargo.toml | 5 ++++ src/main.rs | 61 +++++++++++++++++++++++++++++++++++++++++ template/bin/d.rs.tmpl | 20 ++++++++++++++ template/days/d.rs.tmpl | 26 ++++++++++++++++++ y2016/src/lib.rs | 1 + y2017/src/lib.rs | 1 + y2018/src/lib.rs | 1 + y2019/src/lib.rs | 1 + y2020/src/lib.rs | 1 + y2021/src/lib.rs | 1 + y2023/src/lib.rs | 1 + y2024/src/lib.rs | 1 + 13 files changed, 124 insertions(+) create mode 100644 src/main.rs create mode 100644 template/bin/d.rs.tmpl create mode 100644 template/days/d.rs.tmpl diff --git a/Cargo.lock b/Cargo.lock index ca953c6..62ef042 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,10 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "genaoc" +version = "0.1.0" + [[package]] name = "md5" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 1eb683a..1bf1cb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,8 @@ +[package] +name = "genaoc" +version = "0.1.0" +edition = "2021" + [workspace] resolver = "2" members = [ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b17644a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,61 @@ +use std::{env, fs, path::Path, process::Command}; + +fn main() -> Result<(), std::io::Error> { + let args: Vec = env::args().collect(); + + if args.len() == 1 { + println!("Too few arguments"); + println!("Please enter a year, or a year and day"); + return Err(std::io::ErrorKind::InvalidInput.into()); + } else if args.len() > 3 { + println!("Too many arguments"); + println!("Please enter a year, or a year and day"); + return Err(std::io::ErrorKind::InvalidInput.into()); + } else if args.len() == 2 { + prepare_year(&args[1])?; + } else if args.len() == 3 { + prepare_day(&args[1], &args[2])?; + } + match Command::new("cargo").arg("fmt").status() { + Ok(_) => Ok(()), + Err(e) => Err(e), + } +} + +fn prepare_year(year: &str) -> Result<(), std::io::Error> { + let root = env!("CARGO_MANIFEST_DIR"); + if Path::new(&format!("{root}/y{year}")).exists() { + panic!("Folder y{year} already exists") + } + // create workspace + match Command::new("cargo") + .arg("new") + .arg(format!("y{year}")) + .status() + { + Ok(_status) => Ok(()), + Err(e) => Err(e), + }?; + // remove main.rs + fs::remove_file(format!("{root}/y{year}/src/main.rs"))?; + fs::create_dir(format!("{root}/y{year}/src/bin"))?; + fs::create_dir(format!("{root}/y{year}/src/days"))?; + fs::write(format!("{root}/y{year}/src/lib.rs"), "pub mod days;")?; + fs::write(format!("{root}/y{year}/src/days/mod.rs"), "")?; + prepare_day(year, "1") +} + +fn prepare_day(year: &str, day: &str) -> Result<(), std::io::Error> { + let root = env!("CARGO_MANIFEST_DIR"); + let bin = fs::read_to_string(format!("{root}/template/bin/d.rs.tmpl"))? + .replace("{{YEAR}}", year) + .replace("{{DAY}}", day); + let dayfile = fs::read_to_string(format!("{root}/template/days/d.rs.tmpl"))?; + let mut modfile = fs::read_to_string(format!("{root}/y{year}/src/days/mod.rs"))?; + modfile.push_str(format!("\npub mod d{day};").as_str()); + let bin_path = format!("{root}/y{year}/src/bin/d{day}.rs"); + fs::write(bin_path, bin)?; + fs::write(format!("{root}/y{year}/src/days/d{day}.rs"), dayfile)?; + fs::write(format!("{root}/y{year}/src/days/mod.rs"), modfile)?; + Ok(()) +} diff --git a/template/bin/d.rs.tmpl b/template/bin/d.rs.tmpl new file mode 100644 index 0000000..b907d64 --- /dev/null +++ b/template/bin/d.rs.tmpl @@ -0,0 +1,20 @@ +use std::fs; + +use y{{YEAR}}::days::d{{DAY}}; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/{{DAY}}_input.txt")).unwrap(); + println!("{}", d{{DAY}}::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/{{DAY}}_input.txt")).unwrap(); + println!("{}", d{{DAY}}::process_part2(&content)); +} diff --git a/template/days/d.rs.tmpl b/template/days/d.rs.tmpl new file mode 100644 index 0000000..9025dcc --- /dev/null +++ b/template/days/d.rs.tmpl @@ -0,0 +1,26 @@ +pub fn process_part1(input: &str) -> i32 { + 0 +} + +pub fn process_part2(input: &str) -> i32 { + 0 +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = "{{EXAMPLE}}"; + + #[test] + fn part1() { + let result = process_part1(INPUT); + assert_eq!(result, 0); + } + + #[test] + fn part2() { + let result = process_part2(INPUT); + assert_eq!(result, 0); + } +} diff --git a/y2016/src/lib.rs b/y2016/src/lib.rs index e69de29..8b13789 100644 --- a/y2016/src/lib.rs +++ b/y2016/src/lib.rs @@ -0,0 +1 @@ + diff --git a/y2017/src/lib.rs b/y2017/src/lib.rs index e69de29..8b13789 100644 --- a/y2017/src/lib.rs +++ b/y2017/src/lib.rs @@ -0,0 +1 @@ + diff --git a/y2018/src/lib.rs b/y2018/src/lib.rs index e69de29..8b13789 100644 --- a/y2018/src/lib.rs +++ b/y2018/src/lib.rs @@ -0,0 +1 @@ + diff --git a/y2019/src/lib.rs b/y2019/src/lib.rs index e69de29..8b13789 100644 --- a/y2019/src/lib.rs +++ b/y2019/src/lib.rs @@ -0,0 +1 @@ + diff --git a/y2020/src/lib.rs b/y2020/src/lib.rs index e69de29..8b13789 100644 --- a/y2020/src/lib.rs +++ b/y2020/src/lib.rs @@ -0,0 +1 @@ + diff --git a/y2021/src/lib.rs b/y2021/src/lib.rs index e69de29..8b13789 100644 --- a/y2021/src/lib.rs +++ b/y2021/src/lib.rs @@ -0,0 +1 @@ + diff --git a/y2023/src/lib.rs b/y2023/src/lib.rs index e69de29..8b13789 100644 --- a/y2023/src/lib.rs +++ b/y2023/src/lib.rs @@ -0,0 +1 @@ + diff --git a/y2024/src/lib.rs b/y2024/src/lib.rs index e69de29..8b13789 100644 --- a/y2024/src/lib.rs +++ b/y2024/src/lib.rs @@ -0,0 +1 @@ + From 19d548018a2f04481fbd42071340d9aaa351c1b0 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Sat, 2 Nov 2024 13:35:50 +0100 Subject: [PATCH 02/12] y2015d16 --- y2015/resources/16_input.txt | 500 +++++++++++++++++++++++++++++++++++ y2015/src/bin/d16.rs | 20 ++ y2015/src/days/d16.rs | 74 ++++++ y2015/src/days/mod.rs | 2 + 4 files changed, 596 insertions(+) create mode 100644 y2015/resources/16_input.txt create mode 100644 y2015/src/bin/d16.rs create mode 100644 y2015/src/days/d16.rs diff --git a/y2015/resources/16_input.txt b/y2015/resources/16_input.txt new file mode 100644 index 0000000..b664da3 --- /dev/null +++ b/y2015/resources/16_input.txt @@ -0,0 +1,500 @@ +Sue 1: goldfish: 9, cars: 0, samoyeds: 9 +Sue 2: perfumes: 5, trees: 8, goldfish: 8 +Sue 3: pomeranians: 2, akitas: 1, trees: 5 +Sue 4: goldfish: 10, akitas: 2, perfumes: 9 +Sue 5: cars: 5, perfumes: 6, akitas: 9 +Sue 6: goldfish: 10, cats: 9, cars: 8 +Sue 7: trees: 2, samoyeds: 7, goldfish: 10 +Sue 8: cars: 8, perfumes: 6, goldfish: 1 +Sue 9: cats: 4, pomeranians: 0, trees: 0 +Sue 10: trees: 2, children: 10, samoyeds: 10 +Sue 11: akitas: 10, perfumes: 4, vizslas: 1 +Sue 12: akitas: 1, trees: 0, goldfish: 3 +Sue 13: perfumes: 6, goldfish: 10, cars: 8 +Sue 14: cats: 8, akitas: 5, vizslas: 0 +Sue 15: cars: 8, trees: 3, samoyeds: 5 +Sue 16: vizslas: 6, cats: 6, pomeranians: 10 +Sue 17: akitas: 6, cats: 2, perfumes: 9 +Sue 18: children: 9, goldfish: 2, akitas: 10 +Sue 19: trees: 3, perfumes: 0, goldfish: 6 +Sue 20: vizslas: 3, akitas: 0, trees: 1 +Sue 21: vizslas: 3, cars: 7, akitas: 3 +Sue 22: perfumes: 7, children: 1, pomeranians: 7 +Sue 23: trees: 10, cars: 9, akitas: 10 +Sue 24: akitas: 5, goldfish: 6, vizslas: 6 +Sue 25: samoyeds: 3, trees: 8, vizslas: 5 +Sue 26: vizslas: 4, pomeranians: 2, trees: 1 +Sue 27: cars: 9, goldfish: 2, trees: 4 +Sue 28: vizslas: 6, goldfish: 10, perfumes: 7 +Sue 29: vizslas: 6, pomeranians: 3, akitas: 6 +Sue 30: trees: 0, samoyeds: 5, akitas: 9 +Sue 31: vizslas: 1, perfumes: 0, trees: 6 +Sue 32: cars: 7, vizslas: 1, children: 10 +Sue 33: vizslas: 1, cars: 1, perfumes: 7 +Sue 34: vizslas: 9, trees: 10, akitas: 9 +Sue 35: akitas: 3, vizslas: 5, cars: 10 +Sue 36: cats: 3, children: 9, samoyeds: 3 +Sue 37: vizslas: 5, pomeranians: 7, cars: 6 +Sue 38: cars: 10, akitas: 5, vizslas: 8 +Sue 39: akitas: 5, trees: 9, children: 2 +Sue 40: vizslas: 0, cats: 7, akitas: 0 +Sue 41: cars: 9, trees: 10, perfumes: 8 +Sue 42: akitas: 4, trees: 2, goldfish: 3 +Sue 43: goldfish: 1, cats: 1, akitas: 8 +Sue 44: goldfish: 8, akitas: 9, vizslas: 4 +Sue 45: perfumes: 3, goldfish: 4, trees: 0 +Sue 46: trees: 7, perfumes: 1, goldfish: 8 +Sue 47: pomeranians: 10, cars: 7, trees: 2 +Sue 48: trees: 2, akitas: 1, cars: 4 +Sue 49: goldfish: 5, perfumes: 7, akitas: 8 +Sue 50: akitas: 9, vizslas: 9, trees: 2 +Sue 51: cars: 0, samoyeds: 0, vizslas: 8 +Sue 52: trees: 0, perfumes: 6, pomeranians: 4 +Sue 53: vizslas: 1, cats: 6, akitas: 3 +Sue 54: samoyeds: 8, akitas: 1, vizslas: 4 +Sue 55: goldfish: 10, perfumes: 2, pomeranians: 10 +Sue 56: trees: 9, perfumes: 3, goldfish: 5 +Sue 57: akitas: 3, perfumes: 0, cats: 2 +Sue 58: perfumes: 4, vizslas: 4, cars: 8 +Sue 59: goldfish: 7, children: 5, pomeranians: 8 +Sue 60: cars: 1, trees: 1, perfumes: 10 +Sue 61: trees: 4, samoyeds: 4, cars: 6 +Sue 62: akitas: 10, trees: 2, vizslas: 6 +Sue 63: goldfish: 3, perfumes: 7, vizslas: 10 +Sue 64: pomeranians: 5, children: 10, cars: 0 +Sue 65: vizslas: 10, cars: 8, perfumes: 3 +Sue 66: children: 5, vizslas: 4, akitas: 10 +Sue 67: children: 6, perfumes: 7, cars: 3 +Sue 68: goldfish: 8, cars: 6, children: 1 +Sue 69: vizslas: 5, perfumes: 3, cars: 9 +Sue 70: goldfish: 0, cats: 6, perfumes: 0 +Sue 71: trees: 2, samoyeds: 3, cars: 1 +Sue 72: cats: 3, akitas: 8, vizslas: 7 +Sue 73: akitas: 3, vizslas: 2, goldfish: 6 +Sue 74: pomeranians: 10, samoyeds: 9, cats: 8 +Sue 75: vizslas: 7, cars: 7, akitas: 10 +Sue 76: children: 3, cats: 6, vizslas: 3 +Sue 77: goldfish: 7, pomeranians: 10, trees: 0 +Sue 78: vizslas: 9, children: 7, trees: 10 +Sue 79: trees: 6, pomeranians: 8, samoyeds: 1 +Sue 80: vizslas: 5, children: 6, pomeranians: 5 +Sue 81: cars: 9, vizslas: 9, akitas: 9 +Sue 82: vizslas: 3, cars: 8, akitas: 1 +Sue 83: vizslas: 4, trees: 2, cats: 1 +Sue 84: children: 3, akitas: 0, vizslas: 1 +Sue 85: cats: 6, vizslas: 5, akitas: 2 +Sue 86: cars: 3, akitas: 7, goldfish: 8 +Sue 87: samoyeds: 8, vizslas: 3, goldfish: 8 +Sue 88: vizslas: 4, children: 0, cats: 7 +Sue 89: goldfish: 9, pomeranians: 10, samoyeds: 0 +Sue 90: trees: 6, akitas: 3, cars: 7 +Sue 91: samoyeds: 3, akitas: 7, perfumes: 10 +Sue 92: cars: 7, pomeranians: 10, trees: 2 +Sue 93: samoyeds: 1, children: 3, cars: 3 +Sue 94: samoyeds: 8, akitas: 7, vizslas: 0 +Sue 95: goldfish: 7, children: 2, cars: 6 +Sue 96: cars: 3, perfumes: 9, akitas: 10 +Sue 97: akitas: 9, cars: 10, vizslas: 10 +Sue 98: trees: 4, goldfish: 8, pomeranians: 7 +Sue 99: samoyeds: 6, pomeranians: 0, vizslas: 7 +Sue 100: akitas: 7, perfumes: 8, vizslas: 3 +Sue 101: cars: 5, perfumes: 1, trees: 0 +Sue 102: akitas: 6, pomeranians: 10, trees: 0 +Sue 103: trees: 3, perfumes: 5, cats: 9 +Sue 104: goldfish: 10, perfumes: 8, akitas: 0 +Sue 105: goldfish: 6, vizslas: 5, trees: 2 +Sue 106: pomeranians: 9, samoyeds: 10, perfumes: 10 +Sue 107: cars: 8, vizslas: 4, akitas: 2 +Sue 108: cats: 0, goldfish: 7, trees: 0 +Sue 109: cars: 3, pomeranians: 6, trees: 2 +Sue 110: perfumes: 4, goldfish: 5, akitas: 10 +Sue 111: cars: 3, perfumes: 4, pomeranians: 4 +Sue 112: cats: 2, goldfish: 10, akitas: 0 +Sue 113: cats: 10, children: 0, trees: 1 +Sue 114: akitas: 10, vizslas: 3, goldfish: 0 +Sue 115: samoyeds: 3, goldfish: 6, vizslas: 1 +Sue 116: cars: 3, perfumes: 5, trees: 6 +Sue 117: akitas: 9, samoyeds: 8, goldfish: 8 +Sue 118: pomeranians: 5, perfumes: 10, trees: 1 +Sue 119: goldfish: 6, perfumes: 3, children: 1 +Sue 120: trees: 1, children: 3, pomeranians: 6 +Sue 121: akitas: 7, cars: 10, vizslas: 9 +Sue 122: trees: 4, akitas: 8, samoyeds: 10 +Sue 123: cats: 4, cars: 8, vizslas: 9 +Sue 124: cars: 10, children: 1, trees: 0 +Sue 125: goldfish: 5, pomeranians: 5, trees: 2 +Sue 126: goldfish: 1, vizslas: 8, akitas: 10 +Sue 127: vizslas: 4, cars: 9, akitas: 1 +Sue 128: goldfish: 8, perfumes: 3, cars: 9 +Sue 129: goldfish: 9, pomeranians: 9, perfumes: 1 +Sue 130: trees: 1, vizslas: 9, perfumes: 3 +Sue 131: children: 6, trees: 8, vizslas: 8 +Sue 132: cars: 1, vizslas: 3, children: 7 +Sue 133: cars: 7, children: 1, perfumes: 6 +Sue 134: trees: 8, vizslas: 3, samoyeds: 2 +Sue 135: cats: 9, perfumes: 4, pomeranians: 7 +Sue 136: perfumes: 0, akitas: 8, vizslas: 6 +Sue 137: goldfish: 5, trees: 0, vizslas: 7 +Sue 138: trees: 1, perfumes: 2, cars: 10 +Sue 139: samoyeds: 8, goldfish: 8, trees: 0 +Sue 140: vizslas: 10, perfumes: 9, goldfish: 0 +Sue 141: perfumes: 7, cars: 9, cats: 5 +Sue 142: trees: 2, samoyeds: 2, cars: 0 +Sue 143: cars: 1, perfumes: 1, akitas: 1 +Sue 144: vizslas: 9, cars: 7, pomeranians: 10 +Sue 145: pomeranians: 2, samoyeds: 7, children: 7 +Sue 146: vizslas: 6, cars: 9, goldfish: 7 +Sue 147: trees: 2, vizslas: 1, cats: 9 +Sue 148: perfumes: 9, trees: 4, pomeranians: 5 +Sue 149: samoyeds: 8, children: 1, vizslas: 9 +Sue 150: cats: 3, trees: 2, vizslas: 4 +Sue 151: goldfish: 7, akitas: 10, trees: 3 +Sue 152: perfumes: 4, vizslas: 7, cars: 4 +Sue 153: pomeranians: 4, akitas: 0, vizslas: 3 +Sue 154: samoyeds: 8, trees: 2, vizslas: 10 +Sue 155: vizslas: 7, cats: 7, pomeranians: 5 +Sue 156: goldfish: 10, pomeranians: 1, vizslas: 1 +Sue 157: cars: 6, perfumes: 7, trees: 9 +Sue 158: trees: 5, samoyeds: 9, goldfish: 3 +Sue 159: pomeranians: 4, akitas: 6, vizslas: 8 +Sue 160: goldfish: 7, children: 0, cats: 0 +Sue 161: vizslas: 5, akitas: 0, samoyeds: 2 +Sue 162: akitas: 4, children: 0, vizslas: 3 +Sue 163: samoyeds: 2, perfumes: 0, goldfish: 9 +Sue 164: cars: 9, vizslas: 8, akitas: 6 +Sue 165: samoyeds: 9, vizslas: 9, perfumes: 5 +Sue 166: cars: 5, pomeranians: 4, samoyeds: 8 +Sue 167: cars: 10, perfumes: 3, samoyeds: 6 +Sue 168: pomeranians: 8, goldfish: 9, trees: 9 +Sue 169: vizslas: 7, akitas: 3, samoyeds: 4 +Sue 170: cats: 2, goldfish: 0, vizslas: 4 +Sue 171: perfumes: 3, goldfish: 10, cats: 3 +Sue 172: goldfish: 7, akitas: 6, cars: 0 +Sue 173: cars: 9, goldfish: 7, akitas: 5 +Sue 174: goldfish: 6, cats: 0, vizslas: 8 +Sue 175: perfumes: 7, cats: 10, cars: 10 +Sue 176: samoyeds: 9, vizslas: 4, pomeranians: 10 +Sue 177: perfumes: 0, trees: 0, cars: 10 +Sue 178: vizslas: 6, children: 7, samoyeds: 1 +Sue 179: vizslas: 8, children: 6, trees: 0 +Sue 180: cars: 1, vizslas: 6, trees: 1 +Sue 181: vizslas: 10, perfumes: 3, cars: 1 +Sue 182: trees: 8, samoyeds: 9, cars: 7 +Sue 183: cars: 6, vizslas: 2, perfumes: 7 +Sue 184: trees: 5, samoyeds: 9, akitas: 0 +Sue 185: cars: 8, goldfish: 8, trees: 4 +Sue 186: samoyeds: 6, goldfish: 1, trees: 2 +Sue 187: perfumes: 1, trees: 2, akitas: 7 +Sue 188: samoyeds: 5, cars: 6, perfumes: 2 +Sue 189: samoyeds: 8, goldfish: 3, perfumes: 5 +Sue 190: akitas: 2, cats: 1, samoyeds: 1 +Sue 191: trees: 5, akitas: 1, goldfish: 7 +Sue 192: vizslas: 3, trees: 0, perfumes: 4 +Sue 193: cars: 3, perfumes: 4, akitas: 3 +Sue 194: perfumes: 4, vizslas: 8, children: 4 +Sue 195: vizslas: 1, samoyeds: 3, cars: 6 +Sue 196: cars: 5, perfumes: 6, vizslas: 2 +Sue 197: vizslas: 8, akitas: 8, cats: 6 +Sue 198: cars: 9, akitas: 2, pomeranians: 7 +Sue 199: cats: 9, akitas: 6, cars: 10 +Sue 200: vizslas: 10, pomeranians: 2, goldfish: 9 +Sue 201: vizslas: 9, samoyeds: 4, akitas: 3 +Sue 202: akitas: 5, cats: 2, vizslas: 0 +Sue 203: perfumes: 1, children: 3, akitas: 10 +Sue 204: trees: 4, vizslas: 7, akitas: 9 +Sue 205: trees: 8, perfumes: 9, cars: 1 +Sue 206: goldfish: 6, trees: 5, cars: 8 +Sue 207: akitas: 3, vizslas: 8, trees: 8 +Sue 208: vizslas: 4, perfumes: 7, akitas: 10 +Sue 209: cars: 9, perfumes: 7, goldfish: 9 +Sue 210: vizslas: 2, cats: 2, akitas: 10 +Sue 211: akitas: 1, trees: 3, cars: 2 +Sue 212: goldfish: 5, trees: 0, vizslas: 7 +Sue 213: akitas: 3, perfumes: 1, vizslas: 5 +Sue 214: perfumes: 3, pomeranians: 6, cars: 0 +Sue 215: goldfish: 1, cats: 9, cars: 3 +Sue 216: goldfish: 9, pomeranians: 6, samoyeds: 0 +Sue 217: cars: 6, trees: 2, perfumes: 2 +Sue 218: vizslas: 3, goldfish: 8, akitas: 5 +Sue 219: cats: 9, perfumes: 7, cars: 5 +Sue 220: pomeranians: 5, vizslas: 4, cats: 5 +Sue 221: trees: 0, akitas: 7, goldfish: 10 +Sue 222: akitas: 2, cars: 3, vizslas: 5 +Sue 223: goldfish: 3, perfumes: 7, akitas: 4 +Sue 224: samoyeds: 2, cars: 4, vizslas: 7 +Sue 225: trees: 5, cars: 0, perfumes: 0 +Sue 226: trees: 2, goldfish: 10, perfumes: 6 +Sue 227: cars: 8, trees: 9, akitas: 6 +Sue 228: goldfish: 10, trees: 10, perfumes: 0 +Sue 229: children: 7, samoyeds: 4, goldfish: 6 +Sue 230: vizslas: 9, perfumes: 1, children: 10 +Sue 231: vizslas: 8, trees: 5, akitas: 9 +Sue 232: akitas: 5, goldfish: 9, trees: 1 +Sue 233: vizslas: 3, trees: 2, children: 9 +Sue 234: samoyeds: 8, perfumes: 0, cats: 0 +Sue 235: perfumes: 4, vizslas: 3, akitas: 5 +Sue 236: pomeranians: 5, vizslas: 3, akitas: 9 +Sue 237: cats: 1, trees: 7, vizslas: 5 +Sue 238: children: 5, cats: 4, samoyeds: 5 +Sue 239: trees: 3, akitas: 2, goldfish: 6 +Sue 240: goldfish: 9, trees: 1, perfumes: 1 +Sue 241: cars: 2, pomeranians: 1, samoyeds: 2 +Sue 242: akitas: 2, trees: 3, cars: 4 +Sue 243: vizslas: 6, akitas: 2, samoyeds: 7 +Sue 244: trees: 0, perfumes: 5, cars: 7 +Sue 245: goldfish: 10, perfumes: 5, vizslas: 8 +Sue 246: akitas: 0, perfumes: 0, cars: 1 +Sue 247: samoyeds: 8, goldfish: 0, cars: 6 +Sue 248: perfumes: 0, children: 10, trees: 10 +Sue 249: perfumes: 6, akitas: 5, cats: 5 +Sue 250: vizslas: 7, akitas: 4, cats: 5 +Sue 251: samoyeds: 4, akitas: 1, trees: 8 +Sue 252: perfumes: 8, pomeranians: 5, cars: 1 +Sue 253: akitas: 10, trees: 4, cats: 3 +Sue 254: perfumes: 2, cats: 2, goldfish: 9 +Sue 255: cars: 4, trees: 1, akitas: 4 +Sue 256: samoyeds: 9, goldfish: 0, akitas: 9 +Sue 257: vizslas: 9, perfumes: 2, goldfish: 2 +Sue 258: perfumes: 1, cars: 9, samoyeds: 1 +Sue 259: trees: 0, goldfish: 0, samoyeds: 3 +Sue 260: perfumes: 7, cars: 1, goldfish: 0 +Sue 261: cars: 0, trees: 5, goldfish: 6 +Sue 262: akitas: 7, vizslas: 3, pomeranians: 5 +Sue 263: trees: 1, vizslas: 3, goldfish: 3 +Sue 264: akitas: 7, vizslas: 4, children: 0 +Sue 265: samoyeds: 5, trees: 0, akitas: 4 +Sue 266: perfumes: 9, goldfish: 9, cars: 8 +Sue 267: cars: 7, perfumes: 10, pomeranians: 8 +Sue 268: cars: 0, akitas: 7, perfumes: 4 +Sue 269: pomeranians: 0, cars: 9, perfumes: 10 +Sue 270: samoyeds: 10, perfumes: 10, cars: 9 +Sue 271: akitas: 2, vizslas: 8, cats: 5 +Sue 272: akitas: 3, children: 9, samoyeds: 10 +Sue 273: perfumes: 2, cars: 10, goldfish: 8 +Sue 274: cars: 3, children: 10, perfumes: 10 +Sue 275: cats: 9, akitas: 5, trees: 0 +Sue 276: akitas: 6, children: 2, vizslas: 1 +Sue 277: pomeranians: 6, trees: 10, samoyeds: 3 +Sue 278: cars: 7, perfumes: 10, trees: 1 +Sue 279: cars: 6, pomeranians: 8, trees: 2 +Sue 280: pomeranians: 9, cats: 0, perfumes: 7 +Sue 281: vizslas: 10, goldfish: 9, pomeranians: 5 +Sue 282: perfumes: 4, samoyeds: 7, cars: 9 +Sue 283: cars: 9, vizslas: 6, trees: 5 +Sue 284: cars: 7, trees: 1, vizslas: 4 +Sue 285: samoyeds: 4, goldfish: 10, cats: 4 +Sue 286: samoyeds: 0, akitas: 4, children: 5 +Sue 287: trees: 1, perfumes: 3, goldfish: 10 +Sue 288: pomeranians: 10, akitas: 3, cars: 2 +Sue 289: trees: 7, pomeranians: 4, goldfish: 10 +Sue 290: samoyeds: 10, perfumes: 0, cars: 9 +Sue 291: akitas: 0, pomeranians: 7, vizslas: 4 +Sue 292: cats: 2, vizslas: 8, goldfish: 5 +Sue 293: vizslas: 6, pomeranians: 9, perfumes: 0 +Sue 294: akitas: 6, cars: 7, vizslas: 5 +Sue 295: goldfish: 0, akitas: 9, cats: 0 +Sue 296: goldfish: 1, trees: 0, cars: 6 +Sue 297: perfumes: 6, cats: 8, pomeranians: 6 +Sue 298: cats: 0, goldfish: 6, perfumes: 2 +Sue 299: cars: 4, akitas: 1, samoyeds: 10 +Sue 300: goldfish: 9, samoyeds: 6, cats: 5 +Sue 301: cars: 0, vizslas: 7, trees: 0 +Sue 302: goldfish: 9, samoyeds: 1, children: 6 +Sue 303: cars: 6, perfumes: 7, samoyeds: 8 +Sue 304: trees: 8, goldfish: 9, children: 9 +Sue 305: perfumes: 0, cars: 5, goldfish: 4 +Sue 306: cats: 3, cars: 7, vizslas: 7 +Sue 307: pomeranians: 4, perfumes: 6, cars: 2 +Sue 308: cars: 9, akitas: 6, goldfish: 4 +Sue 309: pomeranians: 2, vizslas: 10, goldfish: 10 +Sue 310: children: 0, cats: 4, akitas: 7 +Sue 311: children: 10, akitas: 8, vizslas: 2 +Sue 312: children: 5, cars: 0, vizslas: 4 +Sue 313: perfumes: 10, trees: 3, pomeranians: 9 +Sue 314: samoyeds: 3, goldfish: 2, trees: 9 +Sue 315: cars: 2, cats: 5, pomeranians: 10 +Sue 316: cats: 6, pomeranians: 6, children: 9 +Sue 317: cats: 2, vizslas: 3, perfumes: 1 +Sue 318: akitas: 1, perfumes: 3, vizslas: 10 +Sue 319: cars: 7, perfumes: 0, trees: 0 +Sue 320: goldfish: 6, samoyeds: 6, pomeranians: 4 +Sue 321: trees: 2, goldfish: 6, children: 0 +Sue 322: goldfish: 0, trees: 2, akitas: 8 +Sue 323: pomeranians: 2, samoyeds: 9, vizslas: 1 +Sue 324: trees: 4, goldfish: 6, pomeranians: 6 +Sue 325: trees: 2, pomeranians: 3, goldfish: 1 +Sue 326: perfumes: 4, goldfish: 6, trees: 5 +Sue 327: akitas: 3, cars: 8, cats: 2 +Sue 328: cats: 6, vizslas: 0, akitas: 2 +Sue 329: perfumes: 3, goldfish: 10, akitas: 3 +Sue 330: goldfish: 3, vizslas: 1, akitas: 6 +Sue 331: perfumes: 4, trees: 1, goldfish: 5 +Sue 332: goldfish: 7, vizslas: 9, akitas: 1 +Sue 333: children: 8, cars: 8, trees: 4 +Sue 334: cars: 1, vizslas: 6, trees: 0 +Sue 335: goldfish: 2, cars: 2, akitas: 1 +Sue 336: goldfish: 5, akitas: 5, trees: 9 +Sue 337: cars: 5, vizslas: 6, goldfish: 6 +Sue 338: cats: 9, akitas: 3, goldfish: 9 +Sue 339: akitas: 3, cats: 2, children: 7 +Sue 340: goldfish: 0, pomeranians: 8, perfumes: 9 +Sue 341: trees: 0, pomeranians: 1, goldfish: 5 +Sue 342: goldfish: 10, trees: 3, vizslas: 4 +Sue 343: cats: 3, samoyeds: 1, children: 6 +Sue 344: perfumes: 3, children: 4, samoyeds: 2 +Sue 345: children: 6, trees: 2, goldfish: 1 +Sue 346: trees: 2, pomeranians: 3, goldfish: 5 +Sue 347: akitas: 10, vizslas: 7, trees: 1 +Sue 348: perfumes: 4, akitas: 2, vizslas: 7 +Sue 349: perfumes: 8, goldfish: 3, vizslas: 5 +Sue 350: trees: 4, pomeranians: 5, akitas: 10 +Sue 351: perfumes: 5, cars: 9, trees: 0 +Sue 352: akitas: 6, children: 8, trees: 10 +Sue 353: samoyeds: 7, akitas: 6, vizslas: 4 +Sue 354: children: 9, goldfish: 7, perfumes: 5 +Sue 355: trees: 1, perfumes: 4, cars: 1 +Sue 356: samoyeds: 1, perfumes: 4, pomeranians: 8 +Sue 357: trees: 7, goldfish: 10, akitas: 0 +Sue 358: akitas: 1, vizslas: 6, cars: 7 +Sue 359: vizslas: 3, goldfish: 8, trees: 4 +Sue 360: akitas: 10, vizslas: 2, trees: 3 +Sue 361: samoyeds: 6, pomeranians: 1, perfumes: 0 +Sue 362: samoyeds: 3, cars: 1, trees: 0 +Sue 363: vizslas: 0, pomeranians: 9, akitas: 4 +Sue 364: perfumes: 9, pomeranians: 8, vizslas: 9 +Sue 365: vizslas: 7, cars: 4, perfumes: 10 +Sue 366: cars: 0, samoyeds: 5, goldfish: 10 +Sue 367: children: 4, vizslas: 5, akitas: 4 +Sue 368: samoyeds: 9, perfumes: 4, vizslas: 6 +Sue 369: perfumes: 5, cars: 4, samoyeds: 5 +Sue 370: akitas: 3, vizslas: 2, perfumes: 1 +Sue 371: cars: 8, cats: 7, children: 5 +Sue 372: vizslas: 9, perfumes: 2, akitas: 10 +Sue 373: trees: 10, pomeranians: 9, goldfish: 3 +Sue 374: children: 4, cars: 10, perfumes: 2 +Sue 375: children: 7, samoyeds: 5, cats: 0 +Sue 376: akitas: 10, samoyeds: 5, vizslas: 5 +Sue 377: goldfish: 8, trees: 3, perfumes: 3 +Sue 378: goldfish: 10, vizslas: 0, perfumes: 2 +Sue 379: trees: 1, vizslas: 7, pomeranians: 4 +Sue 380: samoyeds: 8, vizslas: 3, trees: 2 +Sue 381: goldfish: 2, perfumes: 5, samoyeds: 9 +Sue 382: cats: 3, vizslas: 10, akitas: 5 +Sue 383: cars: 7, goldfish: 5, akitas: 8 +Sue 384: children: 6, goldfish: 10, trees: 1 +Sue 385: cats: 2, akitas: 6, samoyeds: 7 +Sue 386: cars: 10, children: 4, goldfish: 2 +Sue 387: cats: 0, perfumes: 5, akitas: 9 +Sue 388: pomeranians: 7, akitas: 0, samoyeds: 9 +Sue 389: trees: 0, akitas: 9, vizslas: 8 +Sue 390: cars: 0, trees: 10, perfumes: 9 +Sue 391: cats: 9, goldfish: 10, perfumes: 10 +Sue 392: cars: 3, vizslas: 6, cats: 3 +Sue 393: vizslas: 10, perfumes: 4, goldfish: 5 +Sue 394: perfumes: 4, akitas: 10, trees: 2 +Sue 395: pomeranians: 5, cars: 4, perfumes: 3 +Sue 396: pomeranians: 9, vizslas: 5, akitas: 2 +Sue 397: cars: 10, goldfish: 8, trees: 2 +Sue 398: perfumes: 7, children: 9, goldfish: 9 +Sue 399: akitas: 6, cats: 2, goldfish: 7 +Sue 400: goldfish: 9, perfumes: 0, cars: 2 +Sue 401: children: 4, vizslas: 0, trees: 2 +Sue 402: akitas: 4, cars: 8, pomeranians: 4 +Sue 403: vizslas: 8, perfumes: 7, goldfish: 1 +Sue 404: goldfish: 10, samoyeds: 7, vizslas: 3 +Sue 405: akitas: 1, vizslas: 6, perfumes: 6 +Sue 406: pomeranians: 8, goldfish: 6, cats: 3 +Sue 407: goldfish: 2, vizslas: 4, akitas: 7 +Sue 408: cars: 10, perfumes: 10, vizslas: 3 +Sue 409: vizslas: 7, pomeranians: 4, perfumes: 4 +Sue 410: goldfish: 4, vizslas: 7, trees: 5 +Sue 411: cars: 8, trees: 0, goldfish: 4 +Sue 412: cars: 8, perfumes: 5, vizslas: 4 +Sue 413: vizslas: 3, akitas: 7, samoyeds: 6 +Sue 414: trees: 0, perfumes: 6, cars: 10 +Sue 415: pomeranians: 4, trees: 1, perfumes: 6 +Sue 416: cars: 10, perfumes: 6, akitas: 2 +Sue 417: perfumes: 6, samoyeds: 0, akitas: 0 +Sue 418: children: 1, perfumes: 9, vizslas: 3 +Sue 419: goldfish: 9, samoyeds: 3, perfumes: 8 +Sue 420: goldfish: 4, cars: 10, vizslas: 7 +Sue 421: samoyeds: 7, vizslas: 7, cats: 2 +Sue 422: trees: 1, goldfish: 8, perfumes: 0 +Sue 423: cars: 3, perfumes: 2, trees: 3 +Sue 424: samoyeds: 6, vizslas: 0, akitas: 6 +Sue 425: trees: 3, akitas: 7, goldfish: 1 +Sue 426: cars: 9, trees: 1, perfumes: 0 +Sue 427: pomeranians: 0, children: 5, perfumes: 8 +Sue 428: cars: 0, perfumes: 6, children: 4 +Sue 429: akitas: 7, pomeranians: 9, cats: 6 +Sue 430: cats: 6, trees: 1, cars: 0 +Sue 431: children: 8, akitas: 5, perfumes: 9 +Sue 432: perfumes: 5, akitas: 10, trees: 9 +Sue 433: akitas: 4, perfumes: 10, vizslas: 7 +Sue 434: trees: 3, children: 10, samoyeds: 4 +Sue 435: vizslas: 5, goldfish: 2, akitas: 2 +Sue 436: samoyeds: 3, trees: 2, cars: 6 +Sue 437: children: 9, akitas: 0, pomeranians: 3 +Sue 438: perfumes: 10, akitas: 2, cars: 7 +Sue 439: perfumes: 10, samoyeds: 6, akitas: 10 +Sue 440: vizslas: 10, trees: 2, akitas: 8 +Sue 441: perfumes: 8, akitas: 2, pomeranians: 7 +Sue 442: cars: 8, trees: 3, goldfish: 6 +Sue 443: cars: 1, goldfish: 5, vizslas: 5 +Sue 444: vizslas: 2, akitas: 10, samoyeds: 4 +Sue 445: vizslas: 2, akitas: 10, perfumes: 9 +Sue 446: akitas: 3, vizslas: 8, goldfish: 1 +Sue 447: vizslas: 7, pomeranians: 5, trees: 10 +Sue 448: cats: 6, perfumes: 10, children: 6 +Sue 449: trees: 2, cars: 5, goldfish: 8 +Sue 450: trees: 0, goldfish: 6, samoyeds: 3 +Sue 451: perfumes: 0, cars: 8, trees: 1 +Sue 452: akitas: 4, trees: 8, perfumes: 9 +Sue 453: goldfish: 1, perfumes: 7, akitas: 6 +Sue 454: vizslas: 3, cars: 1, perfumes: 6 +Sue 455: trees: 1, akitas: 7, goldfish: 10 +Sue 456: samoyeds: 4, vizslas: 2, cars: 9 +Sue 457: perfumes: 10, children: 1, trees: 8 +Sue 458: perfumes: 0, vizslas: 9, cars: 8 +Sue 459: cats: 0, children: 7, trees: 3 +Sue 460: vizslas: 4, cats: 6, perfumes: 2 +Sue 461: trees: 3, children: 5, cars: 8 +Sue 462: goldfish: 7, vizslas: 7, children: 5 +Sue 463: cars: 5, akitas: 3, goldfish: 5 +Sue 464: vizslas: 0, pomeranians: 5, cars: 0 +Sue 465: goldfish: 4, akitas: 0, cats: 5 +Sue 466: cars: 5, trees: 1, goldfish: 6 +Sue 467: perfumes: 10, trees: 8, cars: 1 +Sue 468: perfumes: 4, akitas: 3, cars: 0 +Sue 469: vizslas: 3, cars: 7, pomeranians: 1 +Sue 470: perfumes: 1, vizslas: 7, akitas: 8 +Sue 471: goldfish: 10, samoyeds: 10, pomeranians: 5 +Sue 472: goldfish: 6, trees: 0, perfumes: 0 +Sue 473: goldfish: 5, vizslas: 0, children: 5 +Sue 474: cars: 3, vizslas: 7, perfumes: 10 +Sue 475: vizslas: 5, trees: 9, goldfish: 8 +Sue 476: akitas: 2, goldfish: 6, children: 7 +Sue 477: samoyeds: 0, perfumes: 1, pomeranians: 5 +Sue 478: trees: 2, goldfish: 9, vizslas: 0 +Sue 479: perfumes: 1, cars: 6, goldfish: 9 +Sue 480: pomeranians: 3, perfumes: 5, trees: 9 +Sue 481: cats: 3, akitas: 0, vizslas: 8 +Sue 482: pomeranians: 10, akitas: 8, trees: 5 +Sue 483: goldfish: 6, akitas: 10, perfumes: 2 +Sue 484: cats: 0, goldfish: 0, children: 9 +Sue 485: children: 4, akitas: 10, vizslas: 8 +Sue 486: vizslas: 3, goldfish: 9, children: 10 +Sue 487: children: 8, cats: 6, vizslas: 10 +Sue 488: cars: 7, akitas: 10, samoyeds: 5 +Sue 489: vizslas: 9, akitas: 6, trees: 2 +Sue 490: vizslas: 5, akitas: 1, children: 5 +Sue 491: vizslas: 8, goldfish: 3, perfumes: 6 +Sue 492: trees: 3, samoyeds: 1, pomeranians: 6 +Sue 493: akitas: 1, vizslas: 5, cars: 8 +Sue 494: akitas: 4, cars: 4, vizslas: 9 +Sue 495: vizslas: 1, akitas: 2, cats: 2 +Sue 496: trees: 7, vizslas: 5, akitas: 6 +Sue 497: akitas: 8, trees: 2, perfumes: 6 +Sue 498: akitas: 1, trees: 1, samoyeds: 4 +Sue 499: cars: 0, akitas: 5, vizslas: 3 +Sue 500: cats: 2, goldfish: 9, children: 8 diff --git a/y2015/src/bin/d16.rs b/y2015/src/bin/d16.rs new file mode 100644 index 0000000..68a4586 --- /dev/null +++ b/y2015/src/bin/d16.rs @@ -0,0 +1,20 @@ +use std::fs; + +use y2015::days::d16; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/16_input.txt")).unwrap(); + println!("{}", d16::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/16_input.txt")).unwrap(); + println!("{}", d16::process_part2(&content)); +} diff --git a/y2015/src/days/d16.rs b/y2015/src/days/d16.rs new file mode 100644 index 0000000..dbaea1e --- /dev/null +++ b/y2015/src/days/d16.rs @@ -0,0 +1,74 @@ +use std::collections::HashMap; + +const TAPE: &str = "children: 3 +cats: 7 +samoyeds: 2 +pomeranians: 3 +akitas: 0 +vizslas: 0 +goldfish: 5 +trees: 3 +cars: 2 +perfumes: 1"; + +pub fn process_part1(input: &str) -> String { + let mut tape = HashMap::new(); + TAPE.lines().for_each(|line| { + let (k, v) = line.split_once(": ").unwrap(); + tape.insert(k, v.parse::().unwrap()); + }); + for line in input.lines() { + let (sue, params) = line.split_once(": ").unwrap(); + let mut is_correct = true; + params.split(", ").for_each(|param| { + let (k, v) = param.split_once(": ").unwrap(); + if v.parse::().unwrap() != *tape.get(k).unwrap() { + is_correct = false; + } + }); + if is_correct { + return sue.to_string(); + } + } + "".to_string() +} + +pub fn process_part2(input: &str) -> String { + let mut tape = HashMap::new(); + TAPE.lines().for_each(|line| { + let (k, v) = line.split_once(": ").unwrap(); + tape.insert(k, v.parse::().unwrap()); + }); + for line in input.lines() { + let (sue, params) = line.split_once(": ").unwrap(); + let mut is_correct = true; + params.split(", ").for_each(|param| { + let (k, v) = param.split_once(": ").unwrap(); + let v = v.parse::().unwrap(); + let tape_value = *tape.get(k).unwrap(); + if (["cats", "trees"].contains(&k) && v <= tape_value) + || (["pomeranians", "goldfish"].contains(&k) && v >= tape_value) + || (!["cats", "trees", "pomeranians", "goldfish"].contains(&k) && v != tape_value) + { + is_correct = false; + } + }); + if is_correct { + return sue.to_string(); + } + } + "".to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = "{{EXAMPLE}}"; + + #[test] + fn part2() { + let result = process_part2(INPUT); + assert_eq!(result, 0); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index bdf8d7f..6bd83e0 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -13,3 +13,5 @@ pub mod d6; pub mod d7; pub mod d8; pub mod d9; + +pub mod d16; From ca267835d196df536714b258e0d2d0cd10924606 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 4 Nov 2024 10:16:20 +0100 Subject: [PATCH 03/12] y2015d17 + fix some warnings --- y2015/resources/17_input.txt | 20 +++++++ y2015/src/bin/d17.rs | 20 +++++++ y2015/src/days/d15.rs | 4 -- y2015/src/days/d16.rs | 13 ---- y2015/src/days/d17.rs | 113 +++++++++++++++++++++++++++++++++++ y2015/src/days/mod.rs | 2 + 6 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 y2015/resources/17_input.txt create mode 100644 y2015/src/bin/d17.rs create mode 100644 y2015/src/days/d17.rs diff --git a/y2015/resources/17_input.txt b/y2015/resources/17_input.txt new file mode 100644 index 0000000..f796965 --- /dev/null +++ b/y2015/resources/17_input.txt @@ -0,0 +1,20 @@ +11 +30 +47 +31 +32 +36 +3 +1 +5 +3 +32 +36 +15 +11 +46 +26 +28 +1 +19 +3 diff --git a/y2015/src/bin/d17.rs b/y2015/src/bin/d17.rs new file mode 100644 index 0000000..d6bf4ec --- /dev/null +++ b/y2015/src/bin/d17.rs @@ -0,0 +1,20 @@ +use std::fs; + +use y2015::days::d17; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/17_input.txt")).unwrap(); + println!("{}", d17::process_part1(&content, 150)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/17_input.txt")).unwrap(); + println!("{}", d17::process_part2(&content, 150)); +} diff --git a/y2015/src/days/d15.rs b/y2015/src/days/d15.rs index 9e27d04..565568b 100644 --- a/y2015/src/days/d15.rs +++ b/y2015/src/days/d15.rs @@ -173,10 +173,6 @@ pub fn process_part1(input: &str) -> i64 { biggest_score } -pub fn process_part2(input: &str) -> u32 { - 0 -} - #[cfg(test)] mod tests { use super::*; diff --git a/y2015/src/days/d16.rs b/y2015/src/days/d16.rs index dbaea1e..edd6690 100644 --- a/y2015/src/days/d16.rs +++ b/y2015/src/days/d16.rs @@ -59,16 +59,3 @@ pub fn process_part2(input: &str) -> String { } "".to_string() } - -#[cfg(test)] -mod tests { - use super::*; - - const INPUT: &str = "{{EXAMPLE}}"; - - #[test] - fn part2() { - let result = process_part2(INPUT); - assert_eq!(result, 0); - } -} diff --git a/y2015/src/days/d17.rs b/y2015/src/days/d17.rs new file mode 100644 index 0000000..fa608b9 --- /dev/null +++ b/y2015/src/days/d17.rs @@ -0,0 +1,113 @@ +use std::{error::Error, fmt::Debug}; + +pub fn process_part1(input: &str, litres: u32) -> u32 { + let containers = input + .lines() + .map(|container| container.parse::().unwrap()) + .collect::>(); + let mut correct_combinations = Vec::new(); + for k in 1..=containers.len() { + let num_combinations = binomial(containers.len(), k); + for i in 1..=num_combinations { + let res = combination(containers.clone(), k, i).unwrap(); + if res.iter().sum::() == litres { + correct_combinations.push(res); + } + } + } + correct_combinations.len() as u32 +} + +pub fn process_part2(input: &str, litres: u32) -> u32 { + let containers = input + .lines() + .map(|container| container.parse::().unwrap()) + .collect::>(); + let mut correct_combinations = Vec::new(); + for k in 1..=containers.len() { + let num_combinations = binomial(containers.len(), k); + for i in 1..=num_combinations { + let res = combination(containers.clone(), k, i).unwrap(); + if res.iter().sum::() == litres { + correct_combinations.push(res); + } + } + if !correct_combinations.is_empty() { + break; + } + } + correct_combinations.len() as u32 +} + +fn combination( + elements: Vec, + k: usize, + nth: usize, +) -> Result, Box> { + let num_elements = elements.len(); + let num_combinations = binomial(num_elements, k); + if nth > num_combinations || k > num_elements || nth == 0 || k == 0 { + return Err(Box::from("Out of bounds")); + } + let mut i = 0; + let mut remaining_k = k; + let mut comb = Vec::new(); + let mut remainder = nth - 1; + while remaining_k > 0 { + // Count the number of combinations that start with elements[i] + // example with n = 5, k = 2 + // nth <= 4 select first + // nth <= 7 select second + // nth <= 9 select third + // nth == 10 select fourth + let count = binomial(num_elements - i - 1, remaining_k - 1); + if remainder < count { + // If the nth combination is within the count, pick this element + comb.push(elements[i].clone()); + remaining_k -= 1; + } else { + remainder -= count; + } + i += 1; + } + Ok(comb) +} + +fn factorial(num: usize) -> usize { + let mut fact = 1; + for n in 1..=num { + fact *= n; + } + fact +} + +fn binomial(n: usize, k: usize) -> usize { + if k > n { + 0 + } else { + factorial(n) / (factorial(k) * factorial(n - k)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = "20 +15 +10 +5 +5"; + + #[test] + fn part1() { + let result = process_part1(INPUT, 25); + assert_eq!(result, 4); + } + + #[test] + fn part2() { + let result = process_part2(INPUT, 25); + assert_eq!(result, 3); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index 6bd83e0..7e6bd80 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -15,3 +15,5 @@ pub mod d8; pub mod d9; pub mod d16; + +pub mod d17; From d31ff0dd3a94abce9e29e36ffffb42a5db209a29 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 4 Nov 2024 11:32:48 +0100 Subject: [PATCH 04/12] Format y2015d15 and add check to prevent overriding days... --- src/main.rs | 3 ++ y2015/resources/18_input.txt | 100 +++++++++++++++++++++++++++++++++++ y2015/src/bin/d15.rs | 3 +- y2015/src/bin/d18.rs | 20 +++++++ y2015/src/days/d15.rs | 6 --- y2015/src/days/d18.rs | 26 +++++++++ y2015/src/days/mod.rs | 6 +++ 7 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 y2015/resources/18_input.txt create mode 100644 y2015/src/bin/d18.rs create mode 100644 y2015/src/days/d18.rs diff --git a/src/main.rs b/src/main.rs index b17644a..fab1029 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,6 +47,9 @@ fn prepare_year(year: &str) -> Result<(), std::io::Error> { fn prepare_day(year: &str, day: &str) -> Result<(), std::io::Error> { let root = env!("CARGO_MANIFEST_DIR"); + if Path::new(&format!("{root}/y{year}/src/bin/d{day}.rs")).exists() { + panic!("Day y{year}d{day} already exists") + } let bin = fs::read_to_string(format!("{root}/template/bin/d.rs.tmpl"))? .replace("{{YEAR}}", year) .replace("{{DAY}}", day); diff --git a/y2015/resources/18_input.txt b/y2015/resources/18_input.txt new file mode 100644 index 0000000..83f3cb2 --- /dev/null +++ b/y2015/resources/18_input.txt @@ -0,0 +1,100 @@ +#..####.##..#...#..#...#...###.#.#.#..#....#.##..#...##...#..#.....##..#####....#.##..##....##.#.... +.#..#..#..#.###...##..#.##.....#...#..##....#####.##............####.#..######..#.#.##.#...#..#...## +#.....##.##.##.#..##.#..###...#.#.#..##..###.####.####.#.####.#...##.#..###.........#.###...#....### +#.###..#######..##..#.....##.#.#.###.#.##..#.##..##.##.#.##...###.#...#.#####.#.##..#.#####..#.##### +#.##.##.###.##..###.#.##.##...##.#.#..##..###.########.#.####..####...#####...#..#...##....##.##.##. +..#.#.#.#..#.#.###....###...#...#.##..####.###.....#.####.###.###.#......#.#.###..#..#.#....#.#####. +...#.###.#....#.###...#.#.#...#...#.#####....#....#...#####..#..#.#..######..#.##.#.##.#..###.#...## +.###...#...#.#..#.#.####.#...#.....##...###.#....#..##.###....#.##....###..#.#####...###.#.##.####.. +#.#....##.#.....#####.#.##..#######.#.####..###.##.#####.##.#...###...#.#...###..#...#.#.###.###.### +...##.##.....##..#.##...#.#...#...#.#####.#...#.#.#.#####.##.#...#.#..##.##..#...#....####..###.###. +#..#....######...#...###.#....#####....#.#.#....#....#.#######.#####..#....#....#.##..#.##.###..#... +#####.#.######.#.#####.#..##..##..####..#....#...#######....##..##.#..###..###.###..###...#...###### +#...##..##...###....##..##.##..#.#.#.#....##.#.......###..###..###...###..##.##.##.#.#.#..#.#..#..#. +..###....##.###..#.#..########...###...##..#######....##..###..#####.##.#....###..##.##.##.#...##.#. +###..#.#..#.#.##.##...##.....#..###.#..##.##.#....##.#.######..##..#.#.##.###...#..####...#.#..#.### +.######....#..##..#.####.##..#.#..#.#..#....#..##.#..#.#...####..#....#.####.#.###.#...####.#...#.#. +#.######.##..###.###..#..###.#...#..#...#...###.##....#.#......#...#.##.#.###..#.#####.#.#..###..#.# +...#..#...####..###.########.....###.###.#..##.##....######..#..#.....#.##.##.#..##..#..##...#..#..# +#..#..##..#.#.########.##.#.####..#.#####.#.###.##....###..##..#.#.###..#.##..##.##.####...######.## +.######.###....#...##...#..#....##..#.#...###.######.##...#....##.##.#.#.##..#...###.###.#....#..##. +####.#.##..##.##.###...#.###.##..##....###..####.##..#.#.##..###.#..##...####...#..####.#.#..##...#. +.#.#..#.....##...#..#...#.#...#.#.##..#....#..#......#####.#######....#.#..#..###..##.#.########..## +.##.#..#..##..#..####.#...####...#...#..##.#..###.#..######..#.#...###.##...#..#####..##.#..##.#.##. +.###..##.##.##....###.###..#.#...##.#.#...#.#######.####..#..###.#######.#...#.#...#.##...#..####..# +##.########..#..#....#.###..##.##.#.##.#..#......####..##.##.#..####..#####..#.....#####.###..#.#.#. +.#..####..##.#.#..#####.##..#..#.#....#.#####.#####...######........##.##..##.#.#.###..#.#.#.#..##.# +.##..##..#.######..###....#.#.###.#........#..###..#.########.....#.##...#.....#..#...##...#..#.###. +##.##.#..####....####.#######.....#.#.#...#.######.#.....####.####...###..####.##.##....###..#..#... +#.#..####...#......#...###...##....##.#######..#.###.#...###.##.##...####..#.####..#......##..#####. +.#.#...##...#....#.####.##.....#....#.#.#######..###.#.....#.....####...##...#.#.##.####..##.###.#.# +####.#.#.####...#...####.#.....#.#######.#.......####......###..###.#...######..#.##.#.##..#..##..## +..##.###..#..####..####.......######.##..#.....##.##...##.##......#.###..###...#.##.#####.#.######.# +.###..####.###..#..#.......#.##...##...##.######.....#..####.#......#.#...#...#...###...#.#.##..#### +.####....##.##.#.....##.###.####.#.......#.......#.#..#.#.#.....###.#.#####.#..#.#.#####.#####.###.# +.##.#.###.#####..#..#....###.#.#.#..#..###..##..####..##.###....#..####.####.#..###.#..######.###### +####.#.....##..###....#.....#.##.#.##..##..########.#####..###.####....##.....######.#.#.##.......#. +#.#.##.....#.....##.###.#..#.##.##....#..##....##.#.###.##.#..#..##.##.###.#..##.###...##..###.##### +#.###.#.#.#.#.#.#.#...#..#.###..####.##...#..####.###....#..#..##.#....####..##.##....#.#.##.##....# +...######....#..####...#.#..#.#.#..#.##.#.#.......#..#......##..#...#..#..##...##.#...#.#.#...##.##. +.#####..#...####....#..###..##....#####..###.#.#...###..###.###..##...#......#...#...#.#.#...#.##..# +......#####.#...#.#.#.##..#.###..##..#.#...###..###....##..#####..#######.#..#.###....###...##.#..#. +..##.########.##..#....##.#...##.##.#.#..#.##..#.#.#.##....#.#.#.#.##....##....#....#####.##..#.##.# +####...#....##.#.###......##.##.#..##...#..#####..#.#....##..#####...#.#.##...#.####.####..##.###### +.##.###.##.#...#.#....###.#######...##...##..#..##.###.#.####..#..###......#.#.##.#.#....#..##...#.. +.#.###.#.###.###.#.##.#..#......####.##...#..##.#..####.....#...#.###.##.##.#..#.##..#.###......#..# +...##.####......#.#.#..###..#....###....#.##.#####..#..#..#...#.#.###...#.#.#.##....###.####..###.#. +##..#.#.#.#....####...#.##.###..####....#..#####.######..#.##.##..#####.#.....#.#...##.#.##.##.#.#.. +#..##.#.#.#.###.#.#.###...#.#...##..#..#.#.#.##..###...#..##.#..#.#.#..#.....#.######.#.###..###.#.. +....#.#.##.###.##...#.##.#....#..##.#..##...#...#.##.####...##..####.#.........#..##..#...#...##.#.. +.##.......##...###.##.#.##.###.##.#..#..#..####...#...#....#####...###..##..#..#..##...#....#..##### +..####..#...#...#..###....##.#.#####..#..#.....#......#...#.......##....####...##....##.##.#.#####.# +##.#.#.#..##..##..#.####.##.##.###.#...###.#....#.....#.###...#######..###.####.###.####.##...##.#.. +..#.#...##.#....#..#..##.####.....#.#.#...#..#..###.#..###.#####.#.#####.#.#.#.#.###.##.###..#....## +.###.#...#....###..#...####....####..#.##..#..##.###..#.#.#.#..#...###.#.#...#......#...#.##.##.#... +..####.####.##.#.##....#...##....#..#....#..###..#...#..###.#####.....#####..##.#.#.#.#.#.##.####... +...##.#.##.####..##.###..#.#.#.#.#.#.#..###...#.##..#.####.##...#.#.##......###..#...###....#.#.###. +##...##..#.#.##..#.#.#....#.####.......#.#.#######.#..#....#.###.#...###.##....###.#.#..#.#.##.####. +...##.......######.....##....#...#..#.##.###.#..#.##.###.#.###.#.#.#...#.#...##.##.##..#.##########. +###..#....#.#.....#....###.#...##.......##.#.#..#.#...########......###..##.#..#..####.##..####...#. +......##.###.#.###.....#..#...#.#......##....#....#........#..#...##.##.....#...##.##.........##.... +.##.##.#.#...#....######..##....##..##.#.#.##.#.##..##...#..###......##......#.#....#.#.#.......###. +.......#.##..##.#...#.##..#..#####.#..#.######.........###.#####.####.#...##...........##...##..#### +#......#.#..#...#...##..#.#.###.##.##.#.#..#.###.##.#..###..#.###..#...###.##..###..#...#..###...#.. +####.##..#####..####.#...#..#..###..##.#.#...#...#...#.##.####.##.###....###...#.#.#..####.######.## +.....#..####...#.#.#.####..####..##.###......#.....########.#...#.#..#..#...#.###..##.#####..###.### +.#######.#.##..###.#...###.#####............##.###...#.##.#.##..##.#.#..#.######..######..#..#..#### +...##..#.####...#..#.#.##.#....#.####..#..###.###..#.#...#....##.##.#......##..##..#.#.#.###..#..#.. +........#...#.##.#.#..#....####....#.##...###..####...###.#.#..######..###..##.#####.###.###.#.#...# +##......##.#..###.####.##.#.###.#.......#.##..####..#.###.##..##..##...##...#.###...#.#..#..#.#####. +##..#.#.....##.####.#..##.#.##.#.#...#...#.#...####.#.#.##...##....##.###..###.####.#...#.###..##### +.#####.####.####.####.#.##.##......###....###.####...###...#...#..#.##.#.#####.###..##.#..###...##.. +.#...#..##...##...#....#.#.#..##..#.##..#.###.#.###..###.#.#.###.#....#######.####.##..#..#...####.. +..##.##..#.##..#.#.###..#.##.########...####.#.###.##..#..###.###...##..##.#..#.######.##.#....###.# +##.#####.###.##.#.##.##.##.###..##..##..#.#.#.#.####..#......#.#.#.#.#.#.##...#####.####...#.#...#.# +.#..###..##.#####.#.##.#..##...##..##...#####.#.####..#...##.....######.#.#...##.#..#######.###.###. +#.#..##.#.#####.#.#.....###.###.#..##.#####....#.###.##.##.#.#..##..#.#....#######.###.#.#.....#.### +....###...#.###.####....###.....##....#####.##.###.###.##.##.##.#..###..######...####.#.#..####..#.. +###.....#..####..#.####..#..#...##.##..##.######.####.....#...##....#..#.##.#####..###.##.#.####...# +.##.##.#...#..####...##.##.###...#...#..#.#.#####.....####...#.#.#..#.####...####.#...###.#......### +###.##....#.#.#...#.###....####..##...##.##.##.#..#...####..#..#..##...#####.####.####...##.#..###.# +..####.....##..###.#.#.###.########..#...#.##..#.#.#.......#.##.#..#...####.##.#..#.######..#.#...#. +#.#.##.#.#.##.#....##......##......#######.#..#.##...##..#.#.###...#.#..#..###...#..###.....##.....# +..#.##.#.##.#.##..##.....#.#..#.#..#...##..#..#.#....###.#####....####.####..#####.##.###...#..###.# +#....#.###..#..########.###..#.#.#.##...##.#..##.###..#..#..#.#.##..###...###.#.##..#.##.#..#.#.#### +#.......#######......#...#...##.##...###.#....##.#..#....####.#.##.###...#.#####...##.###........##. +.##.####.....###.##......####.###.########..#.####..#.##.#.####.....#...#.##....#######.##..#......# +#.#.##.##....##..##.#.###..#.##.#..#..#.#..##.....###..###.##.##.####.##.#.#.##...####..#.#..##.#.#. +...##.#.#.#...###.#.......#.#.....#.#...##....##.##.##.####...#.#..#..#..#.#.##.#..#.#.#....###..#.# +....#.#.###.#####.##..###..##..#...#.##.#......##.####.#..####.#.##..####.#.#...##..#####..##.#.#... +..###.#.##..#....#..#.#.....##.#####..##....#.#...#.##..##.#.#..#...##.##..##..##....#...#..#..#..## +##.#.##.#...#.###.##.##.##.##..##.##...#..##.#..#######.#..#...#.#.##..#....##.#..####.###........#. +.##.#..#.....#####..##.#.#.#.#..###.#######.###.###....##....#.#.#.###....###.#..#.#....#.#..###...# +...###.#.#.###..#...#..###.######..##.#.#..#...####.#####.##..#..###...#..#..#..###..##.#.#...#.###. +#......#.#..#..##.##.#.##.#.###.#.##.#.#..#....#.##..#..##..##.#.#.#....##.###.###.####.#.#####...## +...#.##..#.######.......#.#.###.....#####....##.#.#.###........#.#.###.#.#########.##.##.#..##..#... +##..###..###....####.##.##..##.###....####..##...####.####..####..###.####..##.#...###.#####.##.##.# +###...##.#.#.#####..#..#####...##.#...#.#.###.#..##..###.##.#.#.....####.##.#..##.###.#...##.##...## +...#.#.##.##..##....#..#.#####.##.###..#.#.#........####.###.##....##....####..#.#....#.#.#.###..#.# +..#.#.#.#.###...#....##..######.##....#.#.##..###..#.#.###..#.##..#.#.###......#..#..#.####..#...##. +.....####.#.....###.#.##.#..##.#..###.#####.#..##...###.#..###..#..##....###.#..##.#..#.##.#..#...## diff --git a/y2015/src/bin/d15.rs b/y2015/src/bin/d15.rs index e8df673..8d30aeb 100644 --- a/y2015/src/bin/d15.rs +++ b/y2015/src/bin/d15.rs @@ -16,5 +16,6 @@ fn part1() { fn part2() { let root = env!("CARGO_MANIFEST_DIR"); let content = fs::read_to_string(format!("{root}/resources/15_input.txt")).unwrap(); - println!("{}", d15::process_part2(&content)); + // change comments in part1 + //println!("{}", d15::process_part2(&content)); } diff --git a/y2015/src/bin/d18.rs b/y2015/src/bin/d18.rs new file mode 100644 index 0000000..fb3f4d5 --- /dev/null +++ b/y2015/src/bin/d18.rs @@ -0,0 +1,20 @@ +use std::fs; + +use y2015::days::d18; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/18_input.txt")).unwrap(); + println!("{}", d18::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/18_input.txt")).unwrap(); + println!("{}", d18::process_part2(&content)); +} diff --git a/y2015/src/days/d15.rs b/y2015/src/days/d15.rs index 565568b..5916d1d 100644 --- a/y2015/src/days/d15.rs +++ b/y2015/src/days/d15.rs @@ -185,10 +185,4 @@ Cinnamon: capacity 2, durability 3, flavor -2, texture -1, calories 3"; let result = process_part1(INPUT); assert_eq!(result, 62842880); } - - #[test] - fn part2() { - let result = process_part2(INPUT); - assert_eq!(result, 0); - } } diff --git a/y2015/src/days/d18.rs b/y2015/src/days/d18.rs new file mode 100644 index 0000000..9025dcc --- /dev/null +++ b/y2015/src/days/d18.rs @@ -0,0 +1,26 @@ +pub fn process_part1(input: &str) -> i32 { + 0 +} + +pub fn process_part2(input: &str) -> i32 { + 0 +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = "{{EXAMPLE}}"; + + #[test] + fn part1() { + let result = process_part1(INPUT); + assert_eq!(result, 0); + } + + #[test] + fn part2() { + let result = process_part2(INPUT); + assert_eq!(result, 0); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index 7e6bd80..0e75767 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -17,3 +17,9 @@ pub mod d9; pub mod d16; pub mod d17; + +pub mod d18; + +pub mod d18; + +pub mod d18; From 2f37793f029f65489a8568e5d6cf7e47774ebba4 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 4 Nov 2024 12:44:22 +0100 Subject: [PATCH 05/12] y2015d18 --- y2015/src/bin/d15.rs | 4 +- y2015/src/bin/d18.rs | 4 +- y2015/src/days/d18.rs | 171 +++++++++++++++++++++++++++++++++++++++--- y2015/src/days/mod.rs | 13 +--- 4 files changed, 169 insertions(+), 23 deletions(-) diff --git a/y2015/src/bin/d15.rs b/y2015/src/bin/d15.rs index 8d30aeb..6b9c277 100644 --- a/y2015/src/bin/d15.rs +++ b/y2015/src/bin/d15.rs @@ -14,8 +14,8 @@ fn part1() { } fn part2() { - let root = env!("CARGO_MANIFEST_DIR"); - let content = fs::read_to_string(format!("{root}/resources/15_input.txt")).unwrap(); + //let root = env!("CARGO_MANIFEST_DIR"); + //let content = fs::read_to_string(format!("{root}/resources/15_input.txt")).unwrap(); // change comments in part1 //println!("{}", d15::process_part2(&content)); } diff --git a/y2015/src/bin/d18.rs b/y2015/src/bin/d18.rs index fb3f4d5..ad1e0bd 100644 --- a/y2015/src/bin/d18.rs +++ b/y2015/src/bin/d18.rs @@ -10,11 +10,11 @@ fn main() { fn part1() { let root = env!("CARGO_MANIFEST_DIR"); let content = fs::read_to_string(format!("{root}/resources/18_input.txt")).unwrap(); - println!("{}", d18::process_part1(&content)); + println!("{}", d18::process_part1(&content, 100)); } fn part2() { let root = env!("CARGO_MANIFEST_DIR"); let content = fs::read_to_string(format!("{root}/resources/18_input.txt")).unwrap(); - println!("{}", d18::process_part2(&content)); + println!("{}", d18::process_part2(&content, 100)); } diff --git a/y2015/src/days/d18.rs b/y2015/src/days/d18.rs index 9025dcc..de59209 100644 --- a/y2015/src/days/d18.rs +++ b/y2015/src/days/d18.rs @@ -1,26 +1,179 @@ -pub fn process_part1(input: &str) -> i32 { - 0 +use core::panic; + +pub fn process_part1(input: &str, steps: u32) -> i32 { + let mut grid = input + .lines() + .map(|line| line.chars().map(Light::from).collect::>()) + .collect::>>(); + let height = grid.len(); + let width = grid[0].len(); + let mut new_grid = vec![vec![Light::Off; width]; height]; + for _step in 0..steps { + for (y, grid_line) in grid.iter().enumerate() { + for (x, light) in grid_line.iter().enumerate() { + let neighbors = Light::get_neighbors(grid.clone(), (x, y)); + new_grid[y][x] = light.new_state(neighbors); + } + } + grid = new_grid.clone(); + } + grid.iter() + .map(|line| { + line.iter() + .map(|&light| if light == Light::On { 1 } else { 0 }) + .sum::() + }) + .sum() } -pub fn process_part2(input: &str) -> i32 { - 0 +pub fn process_part2(input: &str, steps: u32) -> i32 { + let mut grid = input + .lines() + .map(|line| line.chars().map(Light::from).collect::>()) + .collect::>>(); + let height = grid.len(); + let width = grid[0].len(); + let corners = vec![ + (0, 0), + (width - 1, 0), + (0, height - 1), + (width - 1, height - 1), + ]; + for (corner_x, corner_y) in corners.clone() { + grid[corner_y][corner_x] = Light::On; + } + let mut new_grid = vec![vec![Light::Off; width]; height]; + for _step in 0..steps { + for (y, grid_line) in grid.iter().enumerate() { + for (x, light) in grid_line.iter().enumerate() { + let neighbors = Light::get_neighbors(grid.clone(), (x, y)); + new_grid[y][x] = light.new_state(neighbors); + } + } + grid = new_grid.clone(); + for (corner_x, corner_y) in corners.clone() { + grid[corner_y][corner_x] = Light::On; + } + } + grid.iter() + .map(|line| { + line.iter() + .map(|&light| if light == Light::On { 1 } else { 0 }) + .sum::() + }) + .sum() +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Light { + On, + Off, +} + +impl Light { + fn new_state(self, neighbors: Vec) -> Self { + let mut light = self; + match self { + Light::On => { + if ![2, 3].contains(&Self::count_on(neighbors)) { + light = Light::Off; + } + } + Light::Off => { + if Self::count_on(neighbors) == 3 { + light = Light::On; + } + } + }; + light + } + + fn count_on(neighbors: Vec) -> u32 { + neighbors + .iter() + .map(|&light| if light == Light::On { 1 } else { 0 }) + .sum() + } + + fn get_neighbors(grid: Vec>, coords: (usize, usize)) -> Vec { + let relative_coords = vec![ + (-1, -1), + (0, -1), + (1, -1), + (-1, 0), + (1, 0), + (-1, 1), + (0, 1), + (1, 1), + ]; + let height = grid.len(); + let width = grid[0].len(); + let (x, y) = coords; + let mut neighbors = Vec::new(); + for (relative_x, relative_y) in relative_coords { + let neighbor_x = x.checked_add_signed(relative_x); + if neighbor_x.is_none() { + continue; + } + let neighbor_x = neighbor_x.unwrap(); + let neighbor_y = y.checked_add_signed(relative_y); + if neighbor_y.is_none() { + continue; + } + let neighbor_y = neighbor_y.unwrap(); + if neighbor_x >= width || neighbor_y >= height { + continue; + } + neighbors.push(grid[neighbor_y][neighbor_x]); + } + neighbors + } +} + +impl From for Light { + fn from(value: char) -> Self { + match value { + '#' => Light::On, + '.' => Light::Off, + _ => panic!("Wrong character"), + } + } +} + +#[allow(unused)] +fn print_grid(grid: Vec>) { + grid.iter().for_each(|line| { + line.iter().for_each(|&light| { + if light == Light::On { + print!("#"); + } else { + print!("."); + } + }); + println!(); + }); } #[cfg(test)] mod tests { use super::*; - const INPUT: &str = "{{EXAMPLE}}"; + const INPUT: &str = ".#.#.# +...##. +#....# +..#... +#.#..# +####.."; #[test] fn part1() { - let result = process_part1(INPUT); - assert_eq!(result, 0); + let result = process_part1(INPUT, 4); + assert_eq!(result, 4); } #[test] fn part2() { - let result = process_part2(INPUT); - assert_eq!(result, 0); + let result = process_part2(INPUT, 5); + assert_eq!(result, 17); } } diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index 0e75767..592f839 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -5,6 +5,9 @@ pub mod d12; pub mod d13; pub mod d14; pub mod d15; +pub mod d16; +pub mod d17; +pub mod d18; pub mod d2; pub mod d3; pub mod d4; @@ -13,13 +16,3 @@ pub mod d6; pub mod d7; pub mod d8; pub mod d9; - -pub mod d16; - -pub mod d17; - -pub mod d18; - -pub mod d18; - -pub mod d18; From fcf0a0866cc036ec32ecc2c3e7c76ca27e21ac92 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 4 Nov 2024 14:41:15 +0100 Subject: [PATCH 06/12] y2015d19 I should look at the input more --- y2015/resources/19_input.txt | 45 ++++++++++++++ y2015/src/bin/d19.rs | 20 +++++++ y2015/src/days/d19.rs | 110 +++++++++++++++++++++++++++++++++++ y2015/src/days/mod.rs | 2 + 4 files changed, 177 insertions(+) create mode 100644 y2015/resources/19_input.txt create mode 100644 y2015/src/bin/d19.rs create mode 100644 y2015/src/days/d19.rs diff --git a/y2015/resources/19_input.txt b/y2015/resources/19_input.txt new file mode 100644 index 0000000..806774f --- /dev/null +++ b/y2015/resources/19_input.txt @@ -0,0 +1,45 @@ +Al => ThF +Al => ThRnFAr +B => BCa +B => TiB +B => TiRnFAr +Ca => CaCa +Ca => PB +Ca => PRnFAr +Ca => SiRnFYFAr +Ca => SiRnMgAr +Ca => SiTh +F => CaF +F => PMg +F => SiAl +H => CRnAlAr +H => CRnFYFYFAr +H => CRnFYMgAr +H => CRnMgYFAr +H => HCa +H => NRnFYFAr +H => NRnMgAr +H => NTh +H => OB +H => ORnFAr +Mg => BF +Mg => TiMg +N => CRnFAr +N => HSi +O => CRnFYFAr +O => CRnMgAr +O => HP +O => NRnFAr +O => OTi +P => CaP +P => PTi +P => SiRnFAr +Si => CaSi +Th => ThCa +Ti => BP +Ti => TiTi +e => HF +e => NAl +e => OMg + +CRnCaSiRnBSiRnFArTiBPTiTiBFArPBCaSiThSiRnTiBPBPMgArCaSiRnTiMgArCaSiThCaSiRnFArRnSiRnFArTiTiBFArCaCaSiRnSiThCaCaSiRnMgArFYSiRnFYCaFArSiThCaSiThPBPTiMgArCaPRnSiAlArPBCaCaSiRnFYSiThCaRnFArArCaCaSiRnPBSiRnFArMgYCaCaCaCaSiThCaCaSiAlArCaCaSiRnPBSiAlArBCaCaCaCaSiThCaPBSiThPBPBCaSiRnFYFArSiThCaSiRnFArBCaCaSiRnFYFArSiThCaPBSiThCaSiRnPMgArRnFArPTiBCaPRnFArCaCaCaCaSiRnCaCaSiRnFYFArFArBCaSiThFArThSiThSiRnTiRnPMgArFArCaSiThCaPBCaSiRnBFArCaCaPRnCaCaPMgArSiRnFYFArCaSiThRnPBPMgAr diff --git a/y2015/src/bin/d19.rs b/y2015/src/bin/d19.rs new file mode 100644 index 0000000..b3919f5 --- /dev/null +++ b/y2015/src/bin/d19.rs @@ -0,0 +1,20 @@ +use std::fs; + +use y2015::days::d19; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/19_input.txt")).unwrap(); + println!("{}", d19::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/19_input.txt")).unwrap(); + println!("{}", d19::process_part2(&content)); +} diff --git a/y2015/src/days/d19.rs b/y2015/src/days/d19.rs new file mode 100644 index 0000000..86d9ae1 --- /dev/null +++ b/y2015/src/days/d19.rs @@ -0,0 +1,110 @@ +use std::collections::HashSet; + +pub fn process_part1(input: &str) -> u32 { + let (replacements_str, molecule) = input.split_once("\n\n").unwrap(); + let mut replacements = Vec::new(); + replacements_str.lines().for_each(|line| { + let (from, to) = line.split_once(" => ").unwrap(); + replacements.push((from, to)); + }); + let mut possible = HashSet::new(); + for (from, to) in replacements { + molecule.match_indices(from).for_each(|(idx, _)| { + let mut new_possible = molecule.to_string(); + new_possible.replace_range(idx..(idx + from.len()), to); + possible.insert(new_possible); + }); + } + possible.len() as u32 +} + +// https://www.reddit.com/r/adventofcode/comments/3xflz8/comment/cy4etju/ +// broken test +//First insight +// +//There are only two types of productions: +// +// e => XX and X => XX (X is not Rn, Y, or Ar) +// +// X => X Rn X Ar | X Rn X Y X Ar | X Rn X Y X Y X Ar +// +//Second insight +// +//You can think of Rn Y Ar as the characters ( , ): +// +//X => X(X) | X(X,X) | X(X,X,X) +// +//Whenever there are two adjacent "elements" in your "molecule", you apply the first production. This reduces your molecule length by 1 each time. +// +//And whenever you have T(T) T(T,T) or T(T,T,T) (T is a literal token such as "Mg", i.e. not a nonterminal like "TiTiCaCa"), you apply the second production. This reduces your molecule length by 3, 5, or 7. +//Third insight +// +//Repeatedly applying X => XX until you arrive at a single token takes count(tokens) - 1 steps: +// +//ABCDE => XCDE => XDE => XE => X +//count("ABCDE") = 5 +//5 - 1 = 4 steps +// +//Applying X => X(X) is similar to X => XX, except you get the () for free. This can be expressed as count(tokens) - count("(" or ")") - 1. +// +//A(B(C(D(E)))) => A(B(C(X))) => A(B(X)) => A(X) => X +//count("A(B(C(D(E))))") = 13 +//count("(((())))") = 8 +//13 - 8 - 1 = 4 steps +// +//You can generalize to X => X(X,X) by noting that each , reduces the length by two (,X). The new formula is count(tokens) - count("(" or ")") - 2*count(",") - 1. +// +//A(B(C,D),E(F,G)) => A(B(C,D),X) => A(X,X) => X +//count("A(B(C,D),E(F,G))") = 16 +//count("(()())") = 6 +//count(",,,") = 3 +//16 - 6 - 2*3 - 1 = 3 steps +// +//This final formula works for all of the production types (for X => XX, the (,) counts are zero by definition.) +pub fn process_part2(input: &str) -> u32 { + let (_replacements_str, molecule) = input.split_once("\n\n").unwrap(); + // An element is always at least an uppercase character and possibly an additional lowercase + // character + let total_elements = molecule.chars().filter(|char| char.is_uppercase()).count(); + let num_rn = molecule.matches("Rn").count(); + let num_ar = molecule.matches("Ar").count(); + let num_y = molecule.matches("Y").count(); + (total_elements - (num_rn + num_ar) - 2 * num_y - 1) as u32 +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT1: &str = "H => HO +H => OH +O => HH +e => H +e => O + +HOH"; + + const INPUT2: &str = "H => HO +H => OH +O => HH +e => H +e => O + +HOHOHO"; + + #[test] + fn part1() { + let result = process_part1(INPUT1); + assert_eq!(result, 4); + let result = process_part1(INPUT2); + assert_eq!(result, 7); + } + + #[test] + fn part2() { + let result = process_part2(INPUT1); + assert_eq!(result, 3); + let result = process_part2(INPUT2); + assert_eq!(result, 6); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index 592f839..301410f 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -16,3 +16,5 @@ pub mod d6; pub mod d7; pub mod d8; pub mod d9; + +pub mod d19; From 2d6e0155bc797ee17447fab8033ee71fd1bb2cfa Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 4 Nov 2024 15:56:30 +0100 Subject: [PATCH 07/12] y2015d20 --- y2015/src/bin/d20.rs | 14 +++++++++ y2015/src/days/d20.rs | 70 +++++++++++++++++++++++++++++++++++++++++++ y2015/src/days/mod.rs | 2 ++ 3 files changed, 86 insertions(+) create mode 100644 y2015/src/bin/d20.rs create mode 100644 y2015/src/days/d20.rs diff --git a/y2015/src/bin/d20.rs b/y2015/src/bin/d20.rs new file mode 100644 index 0000000..4f28bcb --- /dev/null +++ b/y2015/src/bin/d20.rs @@ -0,0 +1,14 @@ +use y2015::days::d20; + +fn main() { + part1(); + part2(); +} + +fn part1() { + println!("{}", d20::process_part1(36000000)); +} + +fn part2() { + println!("{}", d20::process_part2(36000000)); +} diff --git a/y2015/src/days/d20.rs b/y2015/src/days/d20.rs new file mode 100644 index 0000000..9b8d2bb --- /dev/null +++ b/y2015/src/days/d20.rs @@ -0,0 +1,70 @@ +pub fn process_part1(input: u32) -> u32 { + // slow + //let mut house = 1; + //loop { + // let mut presents = 0; + // for elf in 1..=house { + // if house % elf == 0 { + // presents += elf * 10; + // println!("{house} {presents}"); + // } + // } + // if presents >= input { + // break; + // } + // house += 1; + //} + //house + let mut house = 0; + loop { + house += 1; + let mut divisors = get_divisors(house); + divisors.push(house); + if divisors.iter().sum::() * 10 >= input as usize { + break; + } + } + house as u32 +} + +fn get_divisors(n: usize) -> Vec { + let mut divisors = vec![1]; + let mut potential_divisor = 2; + while (potential_divisor * potential_divisor) < n { + if n % potential_divisor == 0 { + divisors.push(potential_divisor); + divisors.push(n / potential_divisor); + } + potential_divisor += 1; + } + // This almost made me go mad + if potential_divisor * potential_divisor == n { + divisors.push(potential_divisor) + } + divisors +} + +pub fn process_part2(input: u32) -> u32 { + let mut house = 0; + loop { + house += 1; + let mut divisors = get_divisors(house); + divisors.push(house); + let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::(); + if sum * 11 >= input as usize { + break; + } + } + house as u32 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1() { + let result = process_part1(100); + assert_eq!(result, 6); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index 301410f..b715254 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -18,3 +18,5 @@ pub mod d8; pub mod d9; pub mod d19; + +pub mod d20; From 36bb36803a5524db72dad902adf797be17ac9bcf Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Tue, 5 Nov 2024 15:44:23 +0100 Subject: [PATCH 08/12] y2015d21 --- y2015/resources/21_input.txt | 3 + y2015/src/bin/d21.rs | 20 +++ y2015/src/days/d21.rs | 290 +++++++++++++++++++++++++++++++++++ y2015/src/days/mod.rs | 2 + 4 files changed, 315 insertions(+) create mode 100644 y2015/resources/21_input.txt create mode 100644 y2015/src/bin/d21.rs create mode 100644 y2015/src/days/d21.rs diff --git a/y2015/resources/21_input.txt b/y2015/resources/21_input.txt new file mode 100644 index 0000000..e749051 --- /dev/null +++ b/y2015/resources/21_input.txt @@ -0,0 +1,3 @@ +Hit Points: 103 +Damage: 9 +Armor: 2 diff --git a/y2015/src/bin/d21.rs b/y2015/src/bin/d21.rs new file mode 100644 index 0000000..977185c --- /dev/null +++ b/y2015/src/bin/d21.rs @@ -0,0 +1,20 @@ +use std::fs; + +use y2015::days::d21; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/21_input.txt")).unwrap(); + println!("{}", d21::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/21_input.txt")).unwrap(); + println!("{}", d21::process_part2(&content)); +} diff --git a/y2015/src/days/d21.rs b/y2015/src/days/d21.rs new file mode 100644 index 0000000..ea8583c --- /dev/null +++ b/y2015/src/days/d21.rs @@ -0,0 +1,290 @@ +use core::panic; + +// 1 Weapon +// 0-1 Armor +// 0-2 Rings +const SHOP: &str = "Weapons: Cost Damage Armor +Dagger 8 4 0 +Shortsword 10 5 0 +Warhammer 25 6 0 +Longsword 40 7 0 +Greataxe 74 8 0 + +Armor: Cost Damage Armor +Leather 13 0 1 +Chainmail 31 0 2 +Splintmail 53 0 3 +Bandedmail 75 0 4 +Platemail 102 0 5 + +Rings: Cost Damage Armor +Damage +1 25 1 0 +Damage +2 50 2 0 +Damage +3 100 3 0 +Defense +1 20 0 1 +Defense +2 40 0 2 +Defense +3 80 0 3"; + +pub fn process_part1(input: &str) -> u32 { + let player = Character { + hp: 100, + ..Default::default() + }; + let mut boss = Character::default(); + input.lines().for_each(|line| { + let (attribute, value) = line.split_once(": ").unwrap(); + match attribute { + "Hit Points" => { + boss.hp = value.parse::().unwrap(); + } + "Damage" => { + boss.damage = value.parse::().unwrap(); + } + "Armor" => { + boss.armor = value.parse::().unwrap(); + } + _ => panic!("Should not happen"), + } + }); + + let mut weapons = Vec::new(); + let mut armors = Vec::new(); + let mut rings = Vec::new(); + + SHOP.split("\n\n").for_each(|category| { + let mut items = category.lines(); + let (category_name, _) = items.next().unwrap().split_once(":").unwrap(); + let item_vec = items + .map(|item| { + let item_vec = item.split_whitespace().collect::>(); + let cols = item_vec.len(); + let mut name = item_vec[0].to_string(); + if item_vec[1].contains("+") { + name.push_str(item_vec[1]); + } + Item { + name, + cost: item_vec[cols - 3].parse::().unwrap(), + damage: item_vec[cols - 2].parse::().unwrap(), + armor: item_vec[cols - 1].parse::().unwrap(), + } + }) + .collect::>(); + match category_name { + "Weapons" => weapons = item_vec, + "Armor" => armors = item_vec, + "Rings" => rings = item_vec, + _ => panic!("Should not happen"), + } + }); + + //println!("{weapons:#?}"); + //println!("{armors:#?}"); + //println!("{rings:#?}"); + + rings.sort_by(|ring_a, ring_b| ring_a.cost.cmp(&ring_b.cost)); + + let choices = create_item_choices(weapons, armors, rings); + let mut lowest_cost = u32::MAX; + for (weapon, armor, rings) in choices { + let mut current_player = player; + current_player.equip(weapon); + if let Some(armor) = armor { + current_player.equip(armor); + } + if !rings.is_empty() { + for ring in rings { + current_player.equip(ring); + } + } + if current_player.wins_against(&boss) && current_player.items_value < lowest_cost { + lowest_cost = current_player.items_value; + } + } + lowest_cost +} + +pub fn process_part2(input: &str) -> u32 { + let player = Character { + hp: 100, + ..Default::default() + }; + let mut boss = Character::default(); + input.lines().for_each(|line| { + let (attribute, value) = line.split_once(": ").unwrap(); + match attribute { + "Hit Points" => { + boss.hp = value.parse::().unwrap(); + } + "Damage" => { + boss.damage = value.parse::().unwrap(); + } + "Armor" => { + boss.armor = value.parse::().unwrap(); + } + _ => panic!("Should not happen"), + } + }); + + let mut weapons = Vec::new(); + let mut armors = Vec::new(); + let mut rings = Vec::new(); + + SHOP.split("\n\n").for_each(|category| { + let mut items = category.lines(); + let (category_name, _) = items.next().unwrap().split_once(":").unwrap(); + let item_vec = items + .map(|item| { + let item_vec = item.split_whitespace().collect::>(); + let cols = item_vec.len(); + let mut name = item_vec[0].to_string(); + if item_vec[1].contains("+") { + name.push_str(item_vec[1]); + } + Item { + name, + cost: item_vec[cols - 3].parse::().unwrap(), + damage: item_vec[cols - 2].parse::().unwrap(), + armor: item_vec[cols - 1].parse::().unwrap(), + } + }) + .collect::>(); + match category_name { + "Weapons" => weapons = item_vec, + "Armor" => armors = item_vec, + "Rings" => rings = item_vec, + _ => panic!("Should not happen"), + } + }); + + //println!("{weapons:#?}"); + //println!("{armors:#?}"); + //println!("{rings:#?}"); + + rings.sort_by(|ring_a, ring_b| ring_a.cost.cmp(&ring_b.cost)); + + let choices = create_item_choices(weapons, armors, rings); + let mut highest_cost = 0; + for (weapon, armor, rings) in choices { + let mut current_player = player; + current_player.equip(weapon); + if let Some(armor) = armor { + current_player.equip(armor); + } + if !rings.is_empty() { + for ring in rings { + current_player.equip(ring); + } + } + if !current_player.wins_against(&boss) && current_player.items_value > highest_cost { + highest_cost = current_player.items_value; + } + } + highest_cost +} + +fn create_item_choices( + weapons: Vec, + armors: Vec, + rings: Vec, +) -> Vec<(Item, Option, Vec)> { + let mut choices = Vec::new(); + + for weapon in &weapons { + choices.push((weapon.clone(), None, vec![])); + for armor in &armors { + choices.push((weapon.clone(), Some(armor.clone()), vec![])); + for ring1 in &rings { + choices.push((weapon.clone(), Some(armor.clone()), vec![ring1.clone()])); + for ring2 in &rings { + if ring2.name == ring1.name { + continue; + } + choices.push(( + weapon.clone(), + Some(armor.clone()), + vec![ring1.clone(), ring2.clone()], + )); + } + } + } + for ring1 in &rings { + choices.push((weapon.clone(), None, vec![ring1.clone()])); + for ring2 in &rings { + if ring2.name == ring1.name { + continue; + } + choices.push((weapon.clone(), None, vec![ring1.clone(), ring2.clone()])); + } + } + } + choices +} + +#[derive(Debug, Clone)] +struct Item { + name: String, + cost: u32, + damage: u32, + armor: u32, +} + +#[derive(Debug, Default, Clone, Copy)] +struct Character { + hp: u32, + damage: u32, + armor: u32, + items_value: u32, +} + +impl Character { + fn hit_damage(&self, other: &Self) -> u32 { + let damage = self.damage.saturating_sub(other.armor); + if damage > 0 { + damage + } else { + 1 + } + } + + fn wins_against(&self, other: &Self) -> bool { + let damage_to_other = self.hit_damage(other); + let mut turns_to_win = other.hp / damage_to_other; + if other.hp % damage_to_other != 0 { + turns_to_win += 1; + } + let damage_to_self = other.hit_damage(self); + let mut turns_to_lose = self.hp / damage_to_self; + if self.hp % damage_to_self != 0 { + turns_to_lose += 1; + } + turns_to_win <= turns_to_lose + } + + fn equip(&mut self, item: Item) { + self.damage += item.damage; + self.armor += item.armor; + self.items_value += item.cost; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = "Hit Points: 12 +Damage: 7 +Armor: 2"; + + #[test] + fn part1() { + let result = process_part1(INPUT); + assert_eq!(result, 0); + } + + #[test] + fn part2() { + let result = process_part2(INPUT); + assert_eq!(result, 0); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index b715254..fac8109 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -20,3 +20,5 @@ pub mod d9; pub mod d19; pub mod d20; + +pub mod d21; From 501547474510b062c591d39790b821c910b46e5d Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Sun, 10 Nov 2024 19:51:54 +0100 Subject: [PATCH 09/12] refactoring --- y2015/src/days/d13.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/y2015/src/days/d13.rs b/y2015/src/days/d13.rs index adb2b7f..96d45f8 100644 --- a/y2015/src/days/d13.rs +++ b/y2015/src/days/d13.rs @@ -71,11 +71,11 @@ fn permutation(mut people: Vec, nth: usize) -> Result, Box Date: Fri, 15 Nov 2024 13:23:25 +0100 Subject: [PATCH 10/12] y2015d22 tests work, solution incorrect and slow --- justfile | 8 + y2015/resources/22_input.txt | 2 + y2015/src/bin/d22.rs | 20 +++ y2015/src/days/d21.rs | 21 --- y2015/src/days/d22.rs | 319 +++++++++++++++++++++++++++++++++++ y2015/src/days/mod.rs | 2 + 6 files changed, 351 insertions(+), 21 deletions(-) create mode 100644 justfile create mode 100644 y2015/resources/22_input.txt create mode 100644 y2015/src/bin/d22.rs create mode 100644 y2015/src/days/d22.rs diff --git a/justfile b/justfile new file mode 100644 index 0000000..8ae16d2 --- /dev/null +++ b/justfile @@ -0,0 +1,8 @@ +gen year day: + cargo run -- {{year}} {{day}} + +run year day: + cargo run --package y{{year}} --bin d{{day}} + +test year day *part='1': + cargo test --package y{{year}} days::d{{day}}::tests::part{{part}} -- --nocapture diff --git a/y2015/resources/22_input.txt b/y2015/resources/22_input.txt new file mode 100644 index 0000000..db93bd0 --- /dev/null +++ b/y2015/resources/22_input.txt @@ -0,0 +1,2 @@ +Hit Points: 71 +Damage: 10 diff --git a/y2015/src/bin/d22.rs b/y2015/src/bin/d22.rs new file mode 100644 index 0000000..7b23363 --- /dev/null +++ b/y2015/src/bin/d22.rs @@ -0,0 +1,20 @@ +use std::fs; + +use y2015::days::d22; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/22_input.txt")).unwrap(); + println!("{}", d22::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/22_input.txt")).unwrap(); + println!("{}", d22::process_part2(&content)); +} diff --git a/y2015/src/days/d21.rs b/y2015/src/days/d21.rs index ea8583c..de8f310 100644 --- a/y2015/src/days/d21.rs +++ b/y2015/src/days/d21.rs @@ -267,24 +267,3 @@ impl Character { self.items_value += item.cost; } } - -#[cfg(test)] -mod tests { - use super::*; - - const INPUT: &str = "Hit Points: 12 -Damage: 7 -Armor: 2"; - - #[test] - fn part1() { - let result = process_part1(INPUT); - assert_eq!(result, 0); - } - - #[test] - fn part2() { - let result = process_part2(INPUT); - assert_eq!(result, 0); - } -} diff --git a/y2015/src/days/d22.rs b/y2015/src/days/d22.rs new file mode 100644 index 0000000..d5b904c --- /dev/null +++ b/y2015/src/days/d22.rs @@ -0,0 +1,319 @@ +pub fn process_part1(input: &str) -> u32 { + let mut boss = Character::default(); + input.lines().for_each(|line| { + let (attribute, value) = line.split_once(": ").unwrap(); + if attribute == "Hit Points" { + boss.hp = value.parse::().unwrap(); + } else if attribute == "Damage" { + boss.damage = value.parse::().unwrap(); + } + }); + let player = Character { + hp: 50, + mana: 500, + ..Default::default() + }; + let mut used_mana = Vec::new(); + for spell in Spell::get_all() { + used_mana.push(round(player.clone(), boss.clone(), spell, 0)); + } + let mut smallest = u32::MAX; + for state in used_mana { + if state.get_value() < smallest { + smallest = state.get_value(); + } + } + smallest +} + +pub fn process_part2(input: &str) -> u32 { + 0 +} + +fn round(mut player: Character, mut boss: Character, spell: Spell, spent_so_far: u32) -> State { + // Player turn + for effect in player.status_effects.iter_mut() { + if effect.name == SpellID::Recharge { + player.mana += effect.mana; + } + effect.duration -= 1; + if effect.duration > 0 && effect.name == SpellID::Shield { + player.armor = effect.armor; + } else if effect.name == SpellID::Shield { + player.armor = 0; + } + } + for effect in boss.status_effects.iter_mut() { + boss.hp = boss.hp.saturating_sub(effect.damage); + effect.duration -= 1; + } + boss.status_effects = boss + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + player.status_effects = player + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + let used_mana = spell.cost + spent_so_far; + let mut dmg = 0; + match spell.name { + SpellID::MagicMissile => { + player.mana = player.mana.saturating_sub(spell.cost); + dmg = spell.damage; + } + SpellID::Drain => { + player.mana = player.mana.saturating_sub(spell.cost); + dmg = spell.damage; + player.hp += spell.hp; + } + SpellID::Shield => { + player.mana = player.mana.saturating_sub(spell.cost); + player.status_effects.push(spell); + player.armor = spell.armor; + } + SpellID::Poison => { + player.mana = player.mana.saturating_sub(spell.cost); + boss.status_effects.push(spell); + } + SpellID::Recharge => { + player.mana = player.mana.saturating_sub(spell.cost); + player.status_effects.push(spell); + } + } + boss.hp = boss.hp.saturating_sub(dmg); + if boss.hp == 0 { + return State::Win(used_mana); + } + if player.mana == 0 { + return State::Loss(0); + } + + // Boss turn + for effect in player.status_effects.iter_mut() { + if effect.name == SpellID::Recharge { + player.mana += effect.mana; + } + effect.duration -= 1; + if effect.duration > 0 && effect.name == SpellID::Shield { + player.armor = effect.armor; + } else if effect.name == SpellID::Shield { + player.armor = 0; + } + } + player.status_effects = player + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + for effect in boss.status_effects.iter_mut() { + boss.hp = boss.hp.saturating_sub(effect.damage); + effect.duration -= 1; + } + if boss.hp == 0 { + return State::Win(used_mana); + } + boss.status_effects = boss + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + let dmg = boss.hit_damage(&player); + player.hp = player.hp.saturating_sub(dmg); + + if player.hp == 0 { + return State::Loss(0); + } + + // next round + let mut smallest_used_mana = State::Playing(u32::MAX); + for spell in Spell::get_all() { + let next_state = round(player.clone(), boss.clone(), spell, used_mana); + match next_state { + State::Playing(mana) => { + if mana < smallest_used_mana.get_value() { + smallest_used_mana = State::Playing(mana); + } + } + State::Win(mana) => { + if mana < smallest_used_mana.get_value() { + smallest_used_mana = State::Win(mana); + } + } + State::Loss(_) => {} + }; + } + smallest_used_mana +} + +#[derive(Debug)] +enum State { + Playing(u32), + Win(u32), + Loss(u32), +} + +impl State { + fn get_value(&self) -> u32 { + match self { + State::Playing(value) => *value, + State::Win(value) => *value, + State::Loss(value) => *value, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum SpellID { + MagicMissile, + Drain, + Shield, + Poison, + Recharge, +} + +#[derive(Debug, Clone, Copy)] +struct Spell { + name: SpellID, + hp: u32, + damage: u32, + mana: u32, + armor: u32, + duration: u32, + cost: u32, +} + +impl Spell { + fn get_all() -> Vec { + vec![ + Spell { + name: SpellID::MagicMissile, + hp: 0, + damage: 4, + mana: 0, + armor: 0, + duration: 0, + cost: 53, + }, + Spell { + name: SpellID::Drain, + hp: 2, + damage: 2, + mana: 0, + armor: 0, + duration: 0, + cost: 73, + }, + Spell { + name: SpellID::Shield, + hp: 0, + damage: 0, + mana: 0, + armor: 7, + duration: 6, + cost: 113, + }, + Spell { + name: SpellID::Poison, + hp: 0, + damage: 3, + mana: 0, + armor: 0, + duration: 6, + cost: 173, + }, + Spell { + name: SpellID::Recharge, + hp: 0, + damage: 0, + mana: 101, + armor: 0, + duration: 5, + cost: 229, + }, + ] + } +} + +#[derive(Debug, Default, Clone)] +struct Character { + hp: u32, + damage: u32, + armor: u32, + mana: u32, + status_effects: Vec, +} + +impl Character { + fn hit_damage(&self, other: &Self) -> u32 { + let damage = self.damage.saturating_sub(other.armor); + if damage > 0 { + damage + } else { + 1 + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_1() { + let boss = Character { + hp: 13, + damage: 8, + ..Default::default() + }; + let player = Character { + hp: 10, + mana: 250, + ..Default::default() + }; + let mut used_mana = Vec::new(); + for spell in Spell::get_all() { + used_mana.push(round(player.clone(), boss.clone(), spell, 0)); + } + let mut result = u32::MAX; + for state in used_mana { + if state.get_value() < result { + result = state.get_value(); + } + } + assert_eq!(result, 226); + } + + #[test] + fn part1_2() { + let boss = Character { + hp: 14, + damage: 8, + ..Default::default() + }; + let player = Character { + hp: 10, + mana: 250, + ..Default::default() + }; + let mut used_mana = Vec::new(); + for spell in Spell::get_all() { + used_mana.push(round(player.clone(), boss.clone(), spell, 0)); + } + let mut result = u32::MAX; + for state in used_mana { + if state.get_value() < result { + result = state.get_value(); + } + } + assert_eq!(result, 641); + } + + #[test] + fn part2() { + let result = process_part2(""); + assert_eq!(result, 0); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index fac8109..60520cf 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -22,3 +22,5 @@ pub mod d19; pub mod d20; pub mod d21; + +pub mod d22; From a875d889fa0e040f4de28d1e56d7637caa6c361d Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Fri, 15 Nov 2024 16:01:09 +0100 Subject: [PATCH 11/12] y2015d22 new approach, no recursion, still broken --- y2015/src/days/d22.rs | 459 ++++++++++++++++++++++++++++-------------- 1 file changed, 306 insertions(+), 153 deletions(-) diff --git a/y2015/src/days/d22.rs b/y2015/src/days/d22.rs index d5b904c..f0c7a19 100644 --- a/y2015/src/days/d22.rs +++ b/y2015/src/days/d22.rs @@ -1,3 +1,5 @@ +use core::panic; + pub fn process_part1(input: &str) -> u32 { let mut boss = Character::default(); input.lines().for_each(|line| { @@ -13,15 +15,62 @@ pub fn process_part1(input: &str) -> u32 { mana: 500, ..Default::default() }; - let mut used_mana = Vec::new(); - for spell in Spell::get_all() { - used_mana.push(round(player.clone(), boss.clone(), spell, 0)); - } + // Initial state, nothing happened yet + let start = RoundNode { + player, + boss, + spent_mana: 0, + state: State::Playing, + }; let mut smallest = u32::MAX; - for state in used_mana { - if state.get_value() < smallest { - smallest = state.get_value(); + let mut leafs = vec![start]; + let mut test = 0; + println!("start"); + loop { + println!("iter: {test},"); + let current_leafs = leafs.clone(); + println!("leaf count: {}", current_leafs.len()); + leafs.clear(); + // create potential rounds + for leaf in current_leafs { + if leaf.state == State::Playing { + //println!("try for leaf"); + for spell in Spell::get_all() { + leafs.push(leaf.use_spell(spell)); + } + } } + for leaf in &leafs { + if leaf.state == State::Win && leaf.spent_mana < smallest { + smallest = leaf.spent_mana; + } + //println!( + // "player hp: {}, boss hp: {}, spent mana so far: {}", + // leaf.player.hp, leaf.boss.hp, leaf.spent_mana + //); + } + leafs = leafs + .clone() + .iter() + .filter(|&leaf| leaf.state == State::Playing || leaf.spent_mana <= smallest) + .map(|leaf| leaf.to_owned()) + .collect(); + if leafs.is_empty() { + break; + } + // already know it's higher than this + if smallest <= 2000 { + let mut count_losses = 0; + for leaf in leafs { + if leaf.state == State::Loss { + count_losses += 1; + } + } + println!("Losses: {count_losses}"); + break; + } + test += 1; + println!(" smallest: {smallest}"); } smallest } @@ -30,139 +79,179 @@ pub fn process_part2(input: &str) -> u32 { 0 } -fn round(mut player: Character, mut boss: Character, spell: Spell, spent_so_far: u32) -> State { - // Player turn - for effect in player.status_effects.iter_mut() { - if effect.name == SpellID::Recharge { - player.mana += effect.mana; - } - effect.duration -= 1; - if effect.duration > 0 && effect.name == SpellID::Shield { - player.armor = effect.armor; - } else if effect.name == SpellID::Shield { - player.armor = 0; - } - } - for effect in boss.status_effects.iter_mut() { - boss.hp = boss.hp.saturating_sub(effect.damage); - effect.duration -= 1; - } - boss.status_effects = boss - .status_effects - .into_iter() - .filter(|&effect| effect.duration > 0) - .collect::>(); - player.status_effects = player - .status_effects - .into_iter() - .filter(|&effect| effect.duration > 0) - .collect::>(); - let used_mana = spell.cost + spent_so_far; - let mut dmg = 0; - match spell.name { - SpellID::MagicMissile => { - player.mana = player.mana.saturating_sub(spell.cost); - dmg = spell.damage; - } - SpellID::Drain => { - player.mana = player.mana.saturating_sub(spell.cost); - dmg = spell.damage; - player.hp += spell.hp; - } - SpellID::Shield => { - player.mana = player.mana.saturating_sub(spell.cost); - player.status_effects.push(spell); - player.armor = spell.armor; - } - SpellID::Poison => { - player.mana = player.mana.saturating_sub(spell.cost); - boss.status_effects.push(spell); - } - SpellID::Recharge => { - player.mana = player.mana.saturating_sub(spell.cost); - player.status_effects.push(spell); - } - } - boss.hp = boss.hp.saturating_sub(dmg); - if boss.hp == 0 { - return State::Win(used_mana); - } - if player.mana == 0 { - return State::Loss(0); - } - - // Boss turn - for effect in player.status_effects.iter_mut() { - if effect.name == SpellID::Recharge { - player.mana += effect.mana; - } - effect.duration -= 1; - if effect.duration > 0 && effect.name == SpellID::Shield { - player.armor = effect.armor; - } else if effect.name == SpellID::Shield { - player.armor = 0; - } - } - player.status_effects = player - .status_effects - .into_iter() - .filter(|&effect| effect.duration > 0) - .collect::>(); - for effect in boss.status_effects.iter_mut() { - boss.hp = boss.hp.saturating_sub(effect.damage); - effect.duration -= 1; - } - if boss.hp == 0 { - return State::Win(used_mana); - } - boss.status_effects = boss - .status_effects - .into_iter() - .filter(|&effect| effect.duration > 0) - .collect::>(); - let dmg = boss.hit_damage(&player); - player.hp = player.hp.saturating_sub(dmg); - - if player.hp == 0 { - return State::Loss(0); - } - - // next round - let mut smallest_used_mana = State::Playing(u32::MAX); - for spell in Spell::get_all() { - let next_state = round(player.clone(), boss.clone(), spell, used_mana); - match next_state { - State::Playing(mana) => { - if mana < smallest_used_mana.get_value() { - smallest_used_mana = State::Playing(mana); - } - } - State::Win(mana) => { - if mana < smallest_used_mana.get_value() { - smallest_used_mana = State::Win(mana); - } - } - State::Loss(_) => {} - }; - } - smallest_used_mana -} - -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] enum State { - Playing(u32), - Win(u32), - Loss(u32), + Playing, + Win, + Loss, } -impl State { - fn get_value(&self) -> u32 { - match self { - State::Playing(value) => *value, - State::Win(value) => *value, - State::Loss(value) => *value, +#[derive(Debug, Clone)] +struct RoundNode { + player: Character, + boss: Character, + spent_mana: u32, + state: State, +} + +impl RoundNode { + fn use_spell(&self, spell: Spell) -> RoundNode { + if self.state != State::Playing { + eprintln!("State: {:#?}", self.state); + eprintln!("spell: {spell:#?}"); + panic!("State should be Playing"); + } + let mut player = self.player.clone(); + let mut boss = self.boss.clone(); + // Player turn + for effect in player.status_effects.iter_mut() { + if effect.name == SpellID::Recharge { + player.mana += effect.mana; + } + effect.duration -= 1; + if effect.duration > 0 && effect.name == SpellID::Shield { + player.armor = effect.armor; + } else if effect.name == SpellID::Shield { + player.armor = 0; + } + } + for effect in boss.status_effects.iter_mut() { + boss.hp = boss.hp.saturating_sub(effect.damage); + effect.duration -= 1; + } + boss.status_effects = boss + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + if boss.hp == 0 { + return RoundNode { + player, + boss, + spent_mana: self.spent_mana, + state: State::Win, + }; + } + player.status_effects = player + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + let used_mana = spell.cost; + let mut dmg = 0; + match spell.name { + SpellID::MagicMissile => { + player.mana = player.mana.saturating_sub(spell.cost); + dmg = spell.damage; + } + SpellID::Drain => { + player.mana = player.mana.saturating_sub(spell.cost); + dmg = spell.damage; + player.hp += spell.hp; + } + SpellID::Shield => { + player.mana = player.mana.saturating_sub(spell.cost); + player.status_effects.push(spell); + player.armor = spell.armor; + } + SpellID::Poison => { + player.mana = player.mana.saturating_sub(spell.cost); + boss.status_effects.push(spell); + } + SpellID::Recharge => { + player.mana = player.mana.saturating_sub(spell.cost); + player.status_effects.push(spell); + } + } + boss.hp = boss.hp.saturating_sub(dmg); + if boss.hp == 0 { + return RoundNode { + player, + boss, + spent_mana: used_mana + self.spent_mana, + state: State::Win, + }; + } + if player.mana == 0 { + return RoundNode { + player, + boss, + spent_mana: used_mana + self.spent_mana, + state: State::Loss, + }; + } + + // Boss turn + for effect in player.status_effects.iter_mut() { + if effect.name == SpellID::Recharge { + player.mana += effect.mana; + } + effect.duration -= 1; + if effect.duration > 0 && effect.name == SpellID::Shield { + player.armor = effect.armor; + } else if effect.name == SpellID::Shield { + player.armor = 0; + } + } + player.status_effects = player + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + for effect in boss.status_effects.iter_mut() { + boss.hp = boss.hp.saturating_sub(effect.damage); + effect.duration -= 1; + } + if boss.hp == 0 { + return RoundNode { + player, + boss, + spent_mana: used_mana + self.spent_mana, + state: State::Win, + }; + } + boss.status_effects = boss + .status_effects + .into_iter() + .filter(|&effect| effect.duration > 0) + .collect::>(); + let dmg = boss.hit_damage(&player); + player.hp = player.hp.saturating_sub(dmg); + + if player.hp == 0 { + return RoundNode { + player, + boss, + spent_mana: used_mana + self.spent_mana, + state: State::Loss, + }; + } + RoundNode { + player, + boss, + spent_mana: used_mana + self.spent_mana, + state: State::Playing, } } + + //fn get_leafs(&self) -> Vec { + // let mut leafs = Vec::new(); + // match self.state { + // State::Playing => { + // if self.next_rounds.is_empty() { + // return vec![self.clone()]; + // } + // let mut round_leafs = Vec::new(); + // for round in self.next_rounds.clone() { + // round_leafs = round.get_leafs(); + // } + // leafs.append(&mut round_leafs); + // leafs + // } + // State::Win => vec![self.clone()], + // State::Loss => vec![self.clone()], + // } + //} } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -273,17 +362,49 @@ mod tests { mana: 250, ..Default::default() }; - let mut used_mana = Vec::new(); - for spell in Spell::get_all() { - used_mana.push(round(player.clone(), boss.clone(), spell, 0)); - } - let mut result = u32::MAX; - for state in used_mana { - if state.get_value() < result { - result = state.get_value(); + // Initial state, nothing happened yet + let start = RoundNode { + player, + boss, + spent_mana: 0, + state: State::Playing, + }; + let mut smallest = u32::MAX; + let mut leafs = vec![start]; + let mut test = 0; + println!("start"); + loop { + println!("iter: {test},"); + let current_leafs = leafs.clone(); + leafs.clear(); + // create potential rounds + for leaf in current_leafs { + if leaf.state == State::Playing { + for spell in Spell::get_all() { + leafs.push(leaf.use_spell(spell)); + } + } } + let mut all_done = true; + for leaf in &leafs { + if leaf.state == State::Win && leaf.spent_mana < smallest { + smallest = leaf.spent_mana; + } + if leaf.state == State::Playing { + all_done = false; + } + println!( + "player hp: {}, boss hp: {}, spent mana so far: {}", + leaf.player.hp, leaf.boss.hp, leaf.spent_mana + ); + } + if all_done { + break; + } + test += 1; + println!(" smallest: {smallest}"); } - assert_eq!(result, 226); + assert_eq!(smallest, 226); } #[test] @@ -298,17 +419,49 @@ mod tests { mana: 250, ..Default::default() }; - let mut used_mana = Vec::new(); - for spell in Spell::get_all() { - used_mana.push(round(player.clone(), boss.clone(), spell, 0)); - } - let mut result = u32::MAX; - for state in used_mana { - if state.get_value() < result { - result = state.get_value(); + // Initial state, nothing happened yet + let start = RoundNode { + player, + boss, + spent_mana: 0, + state: State::Playing, + }; + let mut smallest = u32::MAX; + let mut leafs = vec![start]; + let mut test = 0; + println!("start"); + loop { + println!("iter: {test},"); + let current_leafs = leafs.clone(); + leafs.clear(); + // create potential rounds + for leaf in current_leafs { + if leaf.state == State::Playing { + for spell in Spell::get_all() { + leafs.push(leaf.use_spell(spell)); + } + } } + let mut all_done = true; + for leaf in &leafs { + if leaf.state == State::Win && leaf.spent_mana < smallest { + smallest = leaf.spent_mana; + } + if leaf.state == State::Playing { + all_done = false; + } + println!( + "player hp: {}, boss hp: {}, spent mana so far: {}", + leaf.player.hp, leaf.boss.hp, leaf.spent_mana + ); + } + if all_done { + break; + } + test += 1; + println!(" smallest: {smallest}"); } - assert_eq!(result, 641); + assert_eq!(smallest, 641); } #[test] From 3bb1616d7e479d5218dd4a61eefe37d66fb751eb Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 18 Nov 2024 11:52:30 +0100 Subject: [PATCH 12/12] Use PuzzleUtils --- Cargo.lock | 6 +++++ Cargo.toml | 3 +++ y2015/Cargo.toml | 1 + y2015/src/days/d13.rs | 40 +++++---------------------- y2015/src/days/d17.rs | 63 ++++++------------------------------------- y2015/src/days/d20.rs | 25 ++++------------- y2015/src/days/d9.rs | 43 +++++------------------------ 7 files changed, 37 insertions(+), 144 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62ef042..d7c455f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,11 +12,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +[[package]] +name = "utils" +version = "0.1.0" +source = "git+https://git.plobos.xyz/projects/PuzzleUtils.git#49b0f24c1bdc2c04df237634607df15f19fb3ead" + [[package]] name = "y2015" version = "0.1.0" dependencies = [ "md5", + "utils", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1bf1cb9..ab40e13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ members = [ "y2023", "y2024", ] + +[workspace.dependencies] +utils = { git = "https://git.plobos.xyz/projects/PuzzleUtils.git" } diff --git a/y2015/Cargo.toml b/y2015/Cargo.toml index c30cc93..303c651 100644 --- a/y2015/Cargo.toml +++ b/y2015/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] md5 = "0.7.0" +utils = { workspace = true } diff --git a/y2015/src/days/d13.rs b/y2015/src/days/d13.rs index 96d45f8..90a14e1 100644 --- a/y2015/src/days/d13.rs +++ b/y2015/src/days/d13.rs @@ -1,5 +1,7 @@ use core::panic; -use std::{collections::HashMap, error::Error}; +use std::collections::HashMap; + +use utils::{math::factorial, permutation}; pub fn process_part1(input: &str) -> i32 { let mut happiness_table = HashMap::new(); @@ -22,12 +24,12 @@ pub fn process_part1(input: &str) -> i32 { let num_people = people.len(); - let possible_permutations = factorial(num_people); + let possible_permutations = factorial(num_people.try_into().unwrap()); let mut highest_happiness = 0; for idx in 1..possible_permutations { let mut happiness = 0; - let permutation = permutation(people.clone(), idx).unwrap(); + let permutation = permutation::nth_lex(people.clone(), idx.try_into().unwrap()).unwrap(); for (pos, person) in permutation.clone().iter().enumerate() { let left_neighbor = { @@ -62,34 +64,6 @@ pub fn process_part1(input: &str) -> i32 { highest_happiness } -fn permutation(mut people: Vec, nth: usize) -> Result, Box> { - people.sort(); - if nth == 1 { - return Ok(people); - } - if nth > factorial(people.len()) || nth == 0 { - return Err(Box::from("Out of bounds")); - } - let mut perm = Vec::new(); - let num_unique_people = people.len(); - let mut remainder = nth - 1; - for idx in 1..=people.len() { - let permutations = remainder / factorial(num_unique_people - idx); - remainder %= factorial(num_unique_people - idx); - perm.push(people[permutations].clone()); - people.remove(permutations); - } - Ok(perm) -} - -fn factorial(num: usize) -> usize { - let mut fact = 1; - for n in 1..=num { - fact *= n; - } - fact -} - pub fn process_part2(input: &str) -> i32 { let mut happiness_table = HashMap::new(); let mut people = Vec::new(); @@ -118,12 +92,12 @@ pub fn process_part2(input: &str) -> i32 { let num_people = people.len(); - let possible_permutations = factorial(num_people); + let possible_permutations = factorial(num_people.try_into().unwrap()); let mut highest_happiness = 0; for idx in 1..possible_permutations { let mut happiness = 0; - let permutation = permutation(people.clone(), idx).unwrap(); + let permutation = permutation::nth_lex(people.clone(), idx.try_into().unwrap()).unwrap(); for (pos, person) in permutation.clone().iter().enumerate() { let left_neighbor = { diff --git a/y2015/src/days/d17.rs b/y2015/src/days/d17.rs index fa608b9..d5492c7 100644 --- a/y2015/src/days/d17.rs +++ b/y2015/src/days/d17.rs @@ -1,4 +1,5 @@ -use std::{error::Error, fmt::Debug}; +use utils::combination; +use utils::math::binomial; pub fn process_part1(input: &str, litres: u32) -> u32 { let containers = input @@ -7,9 +8,10 @@ pub fn process_part1(input: &str, litres: u32) -> u32 { .collect::>(); let mut correct_combinations = Vec::new(); for k in 1..=containers.len() { - let num_combinations = binomial(containers.len(), k); + let num_combinations = + binomial(containers.len().try_into().unwrap(), k.try_into().unwrap()); for i in 1..=num_combinations { - let res = combination(containers.clone(), k, i).unwrap(); + let res = combination::nth_lex(containers.clone(), k, i.try_into().unwrap()).unwrap(); if res.iter().sum::() == litres { correct_combinations.push(res); } @@ -25,9 +27,10 @@ pub fn process_part2(input: &str, litres: u32) -> u32 { .collect::>(); let mut correct_combinations = Vec::new(); for k in 1..=containers.len() { - let num_combinations = binomial(containers.len(), k); + let num_combinations = + binomial(containers.len().try_into().unwrap(), k.try_into().unwrap()); for i in 1..=num_combinations { - let res = combination(containers.clone(), k, i).unwrap(); + let res = combination::nth_lex(containers.clone(), k, i.try_into().unwrap()).unwrap(); if res.iter().sum::() == litres { correct_combinations.push(res); } @@ -39,56 +42,6 @@ pub fn process_part2(input: &str, litres: u32) -> u32 { correct_combinations.len() as u32 } -fn combination( - elements: Vec, - k: usize, - nth: usize, -) -> Result, Box> { - let num_elements = elements.len(); - let num_combinations = binomial(num_elements, k); - if nth > num_combinations || k > num_elements || nth == 0 || k == 0 { - return Err(Box::from("Out of bounds")); - } - let mut i = 0; - let mut remaining_k = k; - let mut comb = Vec::new(); - let mut remainder = nth - 1; - while remaining_k > 0 { - // Count the number of combinations that start with elements[i] - // example with n = 5, k = 2 - // nth <= 4 select first - // nth <= 7 select second - // nth <= 9 select third - // nth == 10 select fourth - let count = binomial(num_elements - i - 1, remaining_k - 1); - if remainder < count { - // If the nth combination is within the count, pick this element - comb.push(elements[i].clone()); - remaining_k -= 1; - } else { - remainder -= count; - } - i += 1; - } - Ok(comb) -} - -fn factorial(num: usize) -> usize { - let mut fact = 1; - for n in 1..=num { - fact *= n; - } - fact -} - -fn binomial(n: usize, k: usize) -> usize { - if k > n { - 0 - } else { - factorial(n) / (factorial(k) * factorial(n - k)) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/y2015/src/days/d20.rs b/y2015/src/days/d20.rs index 9b8d2bb..e321899 100644 --- a/y2015/src/days/d20.rs +++ b/y2015/src/days/d20.rs @@ -1,3 +1,5 @@ +use utils::math::get_divisors; + pub fn process_part1(input: u32) -> u32 { // slow //let mut house = 1; @@ -20,38 +22,21 @@ pub fn process_part1(input: u32) -> u32 { house += 1; let mut divisors = get_divisors(house); divisors.push(house); - if divisors.iter().sum::() * 10 >= input as usize { + if divisors.iter().sum::() * 10 >= input.into() { break; } } house as u32 } -fn get_divisors(n: usize) -> Vec { - let mut divisors = vec![1]; - let mut potential_divisor = 2; - while (potential_divisor * potential_divisor) < n { - if n % potential_divisor == 0 { - divisors.push(potential_divisor); - divisors.push(n / potential_divisor); - } - potential_divisor += 1; - } - // This almost made me go mad - if potential_divisor * potential_divisor == n { - divisors.push(potential_divisor) - } - divisors -} - pub fn process_part2(input: u32) -> u32 { let mut house = 0; loop { house += 1; let mut divisors = get_divisors(house); divisors.push(house); - let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::(); - if sum * 11 >= input as usize { + let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::(); + if sum * 11 >= input.into() { break; } } diff --git a/y2015/src/days/d9.rs b/y2015/src/days/d9.rs index c3319f4..ff8e0f5 100644 --- a/y2015/src/days/d9.rs +++ b/y2015/src/days/d9.rs @@ -1,8 +1,7 @@ use core::panic; -use std::{ - collections::{HashMap, HashSet}, - error::Error, -}; +use std::collections::{HashMap, HashSet}; + +use utils::{math::factorial, permutation::nth_lex}; pub fn process_part1(input: &str) -> u32 { let mut distances = HashMap::new(); @@ -19,10 +18,10 @@ pub fn process_part1(input: &str) -> u32 { .iter() .map(|place| place.to_string()) .collect::>(); - let num_permutations = factorial(locations.len()); + let num_permutations = factorial(locations.len().try_into().unwrap()); let mut shortest = u32::MAX; for idx in 1..=num_permutations { - let perm = permutation(locations.clone(), idx).unwrap(); + let perm = nth_lex(locations.clone(), idx.try_into().unwrap()).unwrap(); let route_length = get_route_length(perm, &distances); if route_length < shortest { shortest = route_length; @@ -48,34 +47,6 @@ fn get_route_length(route: Vec, distances: &HashMap<(String, String), u3 .sum() } -fn permutation(mut locations: Vec, nth: usize) -> Result, Box> { - locations.sort(); - if nth == 1 { - return Ok(locations); - } - if nth > factorial(locations.len()) || nth == 0 { - return Err(Box::from("Out of bounds")); - } - let mut perm = Vec::new(); - let num_unique_locations = locations.len(); - let mut remainder = nth - 1; - for idx in 1..=locations.len() { - let permutations = remainder / factorial(num_unique_locations - idx); - remainder %= factorial(num_unique_locations - idx); - perm.push(locations[permutations].clone()); - locations.remove(permutations); - } - Ok(perm) -} - -fn factorial(num: usize) -> usize { - let mut fact = 1; - for n in 1..=num { - fact *= n; - } - fact -} - pub fn process_part2(input: &str) -> u32 { let mut distances = HashMap::new(); let mut locations = HashSet::new(); @@ -91,10 +62,10 @@ pub fn process_part2(input: &str) -> u32 { .iter() .map(|place| place.to_string()) .collect::>(); - let num_permutations = factorial(locations.len()); + let num_permutations = factorial(locations.len().try_into().unwrap()); let mut longest = 0; for idx in 1..=num_permutations { - let perm = permutation(locations.clone(), idx).unwrap(); + let perm = nth_lex(locations.clone(), idx.try_into().unwrap()).unwrap(); let route_length = get_route_length(perm, &distances); if route_length > longest { longest = route_length;