diff --git a/Cargo.lock b/Cargo.lock index bbedd73..a45f174 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,3 +5,50 @@ version = 3 [[package]] name = "advent-of-code-2024" version = "0.1.0" +dependencies = [ + "regex", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" diff --git a/Cargo.toml b/Cargo.toml index 6cdd29c..3f298fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +regex = "1.11.1" diff --git a/apollo.py b/apollo.py new file mode 100644 index 0000000..b05bbc7 --- /dev/null +++ b/apollo.py @@ -0,0 +1,23 @@ +import re +with open("input-xlarge", "r") as inputFile: + memory = inputFile.read() + do_memory = memory.split("don't()") + + +# part 1 +def apply_multipliers(s: str): + prods = [] + multipliers = re.findall("mul\(\d{1,3},\d{1,3}\)", s) + for m in multipliers: + nums = list(map(int, re.findall("\d+", m))) + prods.append(nums[0] * nums[1]) + return sum(prods) + + +# part 2 +sum_dos = apply_multipliers(do_memory[0]) +for line in do_memory[1:]: + if "do()" in line: + split_line = "".join(line.split('do()')[1:]) + sum_dos += apply_multipliers(split_line) +print(sum_dos) diff --git a/d/README.md b/d/README.md deleted file mode 100644 index ee303f5..0000000 --- a/d/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Advent of code 2024 - -Usage: - - rustc dp1.rs - cat input.txt | dp1.rs - -## Day - diff --git a/d/d1p1.rs b/d/d1p1.rs deleted file mode 100644 index fdb696a..0000000 --- a/d/d1p1.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::collections::BinaryHeap; -use std::io; - -fn main() -> io::Result<()> { - let mut l1 = BinaryHeap::::new(); - let mut l2 = BinaryHeap::::new(); - for line in io::stdin().lines() { - let line = line?; - let parts: Vec<&str> = line.trim().split_whitespace().collect(); - let (n1, n2) = (parts[0], parts[1]); - l1.push(n1.parse().unwrap()); - l2.push(n2.parse().unwrap()); - } - let l1 = l1.into_sorted_vec(); - let l2 = l2.into_sorted_vec(); - let mut sum = 0; - for (n1, n2) in l1.into_iter().zip(l2.into_iter()) { - sum += n1.abs_diff(n2); - } - println!("{}", sum); - Ok(()) -} diff --git a/d/d1p1.txt b/d/d1p1.txt deleted file mode 100644 index dfca0b1..0000000 --- a/d/d1p1.txt +++ /dev/null @@ -1,6 +0,0 @@ -3 4 -4 3 -2 5 -1 3 -3 9 -3 3 \ No newline at end of file diff --git a/d/d1p2.rs b/d/d1p2.rs deleted file mode 100644 index 0c7f9e2..0000000 --- a/d/d1p2.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashMap; -use std::io; - -fn main() -> io::Result<()> { - let mut l1: Vec = vec!(); - let mut l2: HashMap = HashMap::default(); - for line in io::stdin().lines() { - let line = line?; - let parts: Vec<&str> = line.trim().split_whitespace().collect(); - let (n1, n2) = (parts[0], parts[1]); - l1.push(n1.parse().unwrap()); - let v = l2.entry(n2.parse::().unwrap()).or_insert(0); - *v += 1; - } - let mut sum = 0; - for n1 in l1.into_iter() { - sum += n1*l2.get(&n1).unwrap_or(&0); - } - println!("{}", sum); - Ok(()) -} diff --git a/d/d2p1.rs b/d/d2p1.rs deleted file mode 100644 index 627f603..0000000 --- a/d/d2p1.rs +++ /dev/null @@ -1,58 +0,0 @@ -use advent_of_code_2024::{make_main, SResult}; - -make_main!(); - -// CODE - -#[derive(PartialEq)] -enum Direction { - Up, - Down, -} - -impl Direction { - fn from(a: usize, b: usize) -> Self { - if a > b { - Direction::Down - } else { - Direction::Up - } - } -} - -fn solve(lines: Vec) -> SResult { - let mut failed = 0; - for line in lines.iter() { - let reports: Vec = line.split_whitespace().map(|v| v.parse::().unwrap()).collect(); - let mut i = 0; - let direction = Direction::from(reports[0], reports[1]); - while i + 1 < reports.len() { - let delta = reports[i].abs_diff(reports[i+1]); - if delta < 1 || delta > 3 { - failed += 1; - break; - } - if direction != Direction::from(reports[i], reports[i+1]) { - failed += 1; - break; - } - i += 1; - } - } - Ok(lines.len() - failed) -} - -// CODE - -#[cfg(test)] -mod tests { - use advent_of_code_2024::input; - - use super::*; - #[test] - fn sample_input() { - let strings: Vec = input!("d2p1.txt"); - let got = solve(strings).unwrap(); - assert_eq!(got, 2); - } -} \ No newline at end of file diff --git a/d/d2p1.txt b/d/d2p1.txt deleted file mode 100644 index 82cd679..0000000 --- a/d/d2p1.txt +++ /dev/null @@ -1,6 +0,0 @@ -7 6 4 2 1 -1 2 7 8 9 -9 7 6 2 1 -1 3 2 4 5 -8 6 4 4 1 -1 3 6 7 9 \ No newline at end of file diff --git a/d/d2p2.rs b/d/d2p2.rs deleted file mode 100644 index 9962ddf..0000000 --- a/d/d2p2.rs +++ /dev/null @@ -1,78 +0,0 @@ -use advent_of_code_2024::{make_main, SResult}; - -make_main!(); - -#[derive(PartialEq, Clone, Copy)] -enum Direction { - Up, - Down, -} - -impl Direction { - fn from(a: usize, b: usize) -> Self { - if a > b { - Direction::Down - } else { - Direction::Up - } - } -} - -fn solve(lines: Vec) -> SResult { - let mut fail_count = 0; - for line in lines.iter() { - let reports: Vec = line - .split_whitespace() - .map(|v| v.parse::().unwrap()) - .collect(); - let problems = match test(&reports) { - // Cool, nothing to see here - Ok(_) => continue, - Err(e) => e, - }; - let mut pass = false; - for problem in problems { - let mut l1 = reports.clone(); - l1.remove(problem); - if test(&l1).is_ok() { - pass = true; - break; - } - } - if !pass { - fail_count += 1; - } - } - Ok(lines.len() - fail_count) -} - -// Tests the report, and returns indexes around where a failure is detected. -fn test(reports: &[usize]) -> Result<(), Vec> { - let mut i = 0; - let direction = Direction::from(reports[0], reports[1]); - while i + 1 < reports.len() { - let delta = reports[i].abs_diff(reports[i + 1]); - if delta < 1 || delta > 3 || direction != Direction::from(reports[i], reports[i + 1]) { - let mut errs = vec![i, i + 1]; - if i > 0 { - errs.push(i - 1); - } - return Err(errs); - } - i += 1; - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use advent_of_code_2024::input; - - use super::*; - #[test] - fn sample_input() { - let strings: Vec = input!("d2p1.txt"); - let got = solve(strings).unwrap(); - assert_eq!(got, 4); - } -} diff --git a/src/bin/d3p1.rs b/src/bin/d3p1.rs new file mode 100644 index 0000000..a3e8bd5 --- /dev/null +++ b/src/bin/d3p1.rs @@ -0,0 +1,29 @@ +use advent_of_code_2024::{make_main, SResult}; +use regex::Regex; + +make_main!(); + +fn solve(lines: Vec) -> SResult { + let mut result = 0; + let re = Regex::new(r"mul\((?\d{1,3}),(?\d{1,3})\)")?; + for line in lines { + for caps in re.captures_iter(&line) { + result += caps["mul1"].parse::().unwrap() * caps["mul2"].parse::().unwrap(); + } + } + Ok(result) +} + + +#[cfg(test)] +mod tests { + use advent_of_code_2024::input; + + use super::*; + #[test] + fn sample_input() { + let strings: Vec = input!("d3p1.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, 161); + } +} diff --git a/src/bin/d3p1.txt b/src/bin/d3p1.txt new file mode 100644 index 0000000..2e1a90a --- /dev/null +++ b/src/bin/d3p1.txt @@ -0,0 +1 @@ +xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5)) \ No newline at end of file diff --git a/src/bin/d3p1hard.rs b/src/bin/d3p1hard.rs new file mode 100644 index 0000000..7019f14 --- /dev/null +++ b/src/bin/d3p1hard.rs @@ -0,0 +1,94 @@ +use advent_of_code_2024::{make_main, SResult}; + +make_main!(); + +type PResult<'a, T> = Result<(&'a str, T), ()>; + +fn literal<'a, 'b>(p: &'a str, lit: &'b str) -> PResult<'a, ()> { + if p.len() < lit.len() { + Err(()) + } else { + let (found, rest) = p.split_at(lit.len()); + if found == lit { + Ok((rest, ())) + } else { + Err(()) + } + } +} + +fn next(p: &str) -> PResult { + let mut chars = p.chars(); + let next = chars.next().ok_or(())?; + Ok((chars.as_str(), next)) +} + +fn int(mut p: &str) -> PResult { + let (_p, total) = next(p)?; + let mut total = total.to_digit(10).ok_or(())?; + p = _p; + let mut i = 1; + while let Ok((_p, digit)) = next(p) { + if !digit.is_digit(10) { + break; + } + total = (total * 10) + digit.to_digit(10).ok_or(())?; + p = _p; + + // We only accept integers between 1 and 3 digits + i += 1; + if i > 3 { + return Err(()); + } + } + Ok((p, total as usize)) +} + +fn mul(p: &str) -> PResult { + let (p, _) = literal(p, "mul(")?; + let (p, mul1) = int(p)?; + let (p, _) = literal(p, ",")?; + let (p, mul2) = int(p)?; + let (p, _) = literal(p, ")")?; + Ok((p, mul1*mul2)) +} + +fn solve(lines: Vec) -> SResult { + let mut result = 0; + let mut enabled = true; + for line in lines { + let mut line = line.as_str(); + while line.len() > 0 { + if let Ok(val) = mul(&line) { + if enabled { + result += val.1; + } + line = val.0; + } else if let Ok(val) = literal(&line, "do()") { + enabled = true; + line = val.0; + } else if let Ok(val) = literal(&line, "don't()") { + enabled = false; + line = val.0; + } else { + let n = next(line).unwrap(); + line = n.0; + } + } + } + Ok(result) +} + + +#[cfg(test)] +mod tests { + use advent_of_code_2024::input; + + use super::*; + #[test] + fn sample_input() { + let strings: Vec = input!("d3p2.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, 48); + } +} diff --git a/src/bin/d3p2.rs b/src/bin/d3p2.rs new file mode 100644 index 0000000..536c48e --- /dev/null +++ b/src/bin/d3p2.rs @@ -0,0 +1,36 @@ +use advent_of_code_2024::{make_main, SResult}; +use regex::Regex; + +make_main!(); + +fn solve(lines: Vec) -> SResult { + let mut result = 0; + let re = Regex::new(r"(?do\(\))|(?don't\(\))|mul\(((?\d{1,3})),((?\d{1,3}))\)")?; + let mut enabled = true; + for line in lines { + for caps in re.captures_iter(&line) { + if caps.name("do").is_some() { + enabled = true; + } else if caps.name("dont").is_some() { + enabled = false; + } else if caps.name("mul1").is_some() && enabled { + result += caps["mul1"].parse::().unwrap() * caps["mul2"].parse::().unwrap(); + } + } + } + Ok(result) +} + + +#[cfg(test)] +mod tests { + use advent_of_code_2024::input; + + use super::*; + #[test] + fn sample_input() { + let strings: Vec = input!("d3p2.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, 48); + } +} diff --git a/src/bin/d3p2.txt b/src/bin/d3p2.txt new file mode 100644 index 0000000..b774ec9 --- /dev/null +++ b/src/bin/d3p2.txt @@ -0,0 +1 @@ +xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5)) \ No newline at end of file diff --git a/src/bin/d4p1.rs b/src/bin/d4p1.rs new file mode 100644 index 0000000..2140d72 --- /dev/null +++ b/src/bin/d4p1.rs @@ -0,0 +1,61 @@ +use advent_of_code_2024::{make_main, SResult}; + +make_main!(); + +fn solve(lines: Vec) -> SResult { + // Convert to a grid of chars for easy searching + let mut grid = vec!(); + for line in lines { + let line: Vec = line.chars().collect(); + grid.push(line); + } + let mut total = 0; + let word: Vec = "XMAS".chars().collect(); + for x in 0..grid.len() { + for y in 0..grid[x].len() { + for vel in [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (-1, 1), (1, -1)] { + let mut matches = 0; + let mut pos = (x, y); + for c in &word { + if grid[pos.0][pos.1] != *c { + break; + } + matches += 1; + if let Some(_pos) = next(pos, vel, grid.len()) { + pos = _pos; + } else { + break; + } + } + if matches == word.len() { + total += 1; + } + } + } + } + Ok(total) +} + +fn next((x, y): (usize, usize), (xvel, yvel): (i64, i64), width: usize) -> Option<(usize, usize)> { + let x = x as i64 + xvel; + let y = y as i64 + yvel; + if x < 0 || y < 0 || x == width as i64 || y == width as i64 { + None + } else { + Some((x as usize, y as usize)) + } +} + + +#[cfg(test)] +mod tests { + use advent_of_code_2024::input; + + use super::*; + #[test] + fn sample_input() { + let strings: Vec = input!("d4p1.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, 18); + } +} diff --git a/src/bin/d4p1.txt b/src/bin/d4p1.txt new file mode 100644 index 0000000..c41c5ea --- /dev/null +++ b/src/bin/d4p1.txt @@ -0,0 +1,10 @@ +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX \ No newline at end of file diff --git a/src/bin/d4p2.rs b/src/bin/d4p2.rs new file mode 100644 index 0000000..6c046ae --- /dev/null +++ b/src/bin/d4p2.rs @@ -0,0 +1,73 @@ +use advent_of_code_2024::{make_main, SResult}; + +make_main!(); + +fn solve(lines: Vec) -> SResult { + // Convert to a grid of chars for easy searching + let mut grid = vec!(); + for line in lines { + let line: Vec = line.chars().collect(); + grid.push(line); + } + let mut total = 0; + let word: Vec = "XMAS".chars().collect(); + for x in 0..grid.len() { + for y in 0..grid[x].len() { + if grid[x][y] != 'A' { + continue; + } + let mut count = 0; + for vel in [(1, 1), (1, -1), (-1, -1), (-1, 1)] { + if let Some(pos) = next((x, y), vel, grid.len()) { + if check_mas(&grid, pos, (-1*vel.0, -1*vel.1)) { + count += 1; + } + } + } + if count == 2 { + total += 1; + } + } + } + Ok(total) +} + +fn check_mas(grid: &[Vec], mut pos: (usize, usize), vel: (i64, i64)) -> bool { + let mut matches = 0; + for c in "MAS".chars() { + if c != grid[pos.0][pos.1] { + break; + } + matches += 1; + if let Some(_pos) = next(pos, vel, grid.len()) { + pos = _pos; + } else { + break; + } + } + matches == "MAS".len() +} + +fn next((x, y): (usize, usize), (xvel, yvel): (i64, i64), width: usize) -> Option<(usize, usize)> { + let x = x as i64 + xvel; + let y = y as i64 + yvel; + if x < 0 || y < 0 || x == width as i64 || y == width as i64 { + None + } else { + Some((x as usize, y as usize)) + } +} + + +#[cfg(test)] +mod tests { + use advent_of_code_2024::input; + + use super::*; + #[test] + fn sample_input() { + let strings: Vec = input!("d4p1.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, 9); + } +} diff --git a/src/lib.rs b/src/lib.rs index 5d615af..5a0c2ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,4 +18,16 @@ macro_rules! input { }; } -pub type SResult = Result>; \ No newline at end of file +pub type SResult = Result>; + +pub type Pair = (T, T); + +pub fn next((x, y): Pair, (xvel, yvel): Pair, width: usize) -> Option { + let x = x as i64 + xvel; + let y = y as i64 + yvel; + if x < 0 || y < 0 || x == width as i64 || y == width as i64 { + None + } else { + Some((x as usize, y as usize)) + } +} \ No newline at end of file