diff --git a/Cargo.lock b/Cargo.lock index 90137cc..0ceb48e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,7 @@ dependencies = [ "itertools", "pico-args", "regex", + "regex-split", "tinyjson", ] @@ -352,6 +353,15 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-split" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "544861d1810a3e429bb5e80266e537138b0e69e59bed9334326ae129a4c3e676" +dependencies = [ + "regex", +] + [[package]] name = "regex-syntax" version = "0.8.8" diff --git a/Cargo.toml b/Cargo.toml index 4887e4d..b866718 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ dhat = { version = "0.3.3", optional = true } itertools = "0.14.0" pico-args = "0.5.0" regex = "1.12.2" +regex-split = "0.1.0" tinyjson = "2.5.1" # Solution dependencies diff --git a/README.md b/README.md index 615410f..e685ba8 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,10 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | [Day 3](./src/bin/03.rs) | `85.9µs` | `120.1µs` | | [Day 4](./src/bin/04.rs) | `227.4µs` | `1.9ms` | | [Day 5](./src/bin/05.rs) | `159.6µs` | `21.4µs` | +| [Day 6](./src/bin/06.rs) | `50.5µs` | `2.5ms` | +| [Day 7](./src/bin/07.rs) | `65.4µs` | `55.4µs` | -**Total: 169.94ms** +**Total: 172.62ms** --- diff --git a/data/examples/06.txt b/data/examples/06.txt new file mode 100644 index 0000000..337b837 --- /dev/null +++ b/data/examples/06.txt @@ -0,0 +1,4 @@ +123 328 51 64 + 45 64 387 23 + 6 98 215 314 +* + * + diff --git a/data/examples/07.txt b/data/examples/07.txt new file mode 100644 index 0000000..57a2466 --- /dev/null +++ b/data/examples/07.txt @@ -0,0 +1,16 @@ +.......S....... +............... +.......^....... +............... +......^.^...... +............... +.....^.^.^..... +............... +....^.^...^.... +............... +...^.^...^.^... +............... +..^...^.....^.. +............... +.^.^.^.^.^...^. +............... diff --git a/src/bin/06.rs b/src/bin/06.rs new file mode 100644 index 0000000..bb4b1e1 --- /dev/null +++ b/src/bin/06.rs @@ -0,0 +1,95 @@ +advent_of_code::solution!(6); +use itertools::{Itertools, izip}; +use regex::Regex; +use regex_split::RegexSplit; + +pub fn part_one(input: &str) -> Option { + let lines = input.lines().filter(|l| !l.is_empty()).rev().collect_vec(); + let commands = lines[0].split_ascii_whitespace().map(|c| c.chars().nth(0).unwrap()).collect_vec(); + // println!("{commands:?}"); + let values = &lines[1..]; + let starting_values = commands.iter().map(|c| match c { + '+' => 0u64, + '*' => 1u64, + _ => 0u64, + }).collect_vec(); + let subtotals = values.iter().fold(starting_values, |totals, row| { + let row_values = row.split_ascii_whitespace().map(|v| v.parse::().unwrap()).collect_vec(); + // println!("{row_values:?}"); + izip!(totals, row_values, commands.clone()).map(|(total, row_value, command)| { + match command { + '+' => total + row_value, + '*' => total * row_value, + _ => total, + } + }).collect_vec() + // totals.iter().zip(row_values) + }); + // println!("{subtotals:?}"); + + Some(subtotals.iter().sum()) +} + +pub fn part_two(input: &str) -> Option { + let lines = input.lines().filter(|l| !l.is_empty()).collect_vec(); + // println!("{lines:?}"); + let re = Regex::new(r"(.\s+)").unwrap(); + let commands_raw = re.split_inclusive(lines.last().unwrap()).filter(|c| !c.is_empty()).collect_vec(); + let commands = commands_raw.iter().enumerate().map(|(i, c)| { + // println!("'{c}'"); + // all columns except the last one will have an extra space that is used to split that column from the next one. the last column does not have that space. + if i == commands_raw.len() -1 { (c.chars().nth(0).unwrap(), c.len()) } else { (c.chars().nth(0).unwrap(), c.len() - 1) } + // (c.chars().nth(0).unwrap(), c.len()) + }).collect_vec(); + // println!("{commands:?}"); + let values = &lines[..lines.len()-1]; + let mut grand_total = 0; + // iterate through each column, reading the rows multiple time. this will not be optimal but i think it will work + let mut cur_offset = 0; + for (command, offsets) in commands { + // values.iter().for_each(|item| println!("{item:?}")); + let mut column_values: Vec = vec![0; offsets]; + // the lines ["123", " 45", " 6"] should be read right to left and transformed into [356, 24, 1] + for row in values { + // println!("reading from {} to {}", cur_offset, cur_offset+offsets - 1); + for vidx in cur_offset..cur_offset+offsets { + let c = row.to_string().chars().nth(vidx).unwrap(); + // println!("{c}"); + let cidx = vidx - cur_offset; + if c == ' ' { continue } + let cval = c.to_digit(10).unwrap() as u64; + column_values[cidx] = if column_values[cidx] == 0 { cval } else { + column_values[cidx] * 10 + cval + }; + // println!("{column_values:?}"); + } + } + cur_offset = cur_offset + offsets + 1; + + let subtotal: u64 = if command == '*' { column_values.iter().product() } else { column_values.iter().sum() }; + // print!("command: {}", command); + // print!(" - values: {:?}", column_values); + // print!(" - subtotal: {}", subtotal); + // println!(); + grand_total += subtotal; + } + + Some(grand_total) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(4277556)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(3263827)); + } +} diff --git a/src/bin/07.rs b/src/bin/07.rs new file mode 100644 index 0000000..bccda32 --- /dev/null +++ b/src/bin/07.rs @@ -0,0 +1,80 @@ +advent_of_code::solution!(7); +use itertools::Itertools; + +#[derive(Clone)] +#[derive(PartialEq)] +#[derive(Debug)] +enum State { + Off = 0, + On = 1 +} + +pub fn part_one(input: &str) -> Option { + let lines = input.lines().filter(|l| !l.is_empty()).collect_vec(); + let width = lines[0].len(); + let mut beams = vec![State::Off; width]; + + let mut splits = 0; + for line in lines { + let values = line.chars().collect_vec(); + for (idx, value) in values.iter().enumerate() { + if *value == 'S' { + beams[idx] = State::On; + } else if *value == '^' && beams[idx] == State::On { + beams[idx] = State::Off; + if idx > 0 { + beams[idx-1] = State::On; + } + if idx + 1 < width { + beams[idx+1] = State::On; + } + splits += 1; + } + } + // println!("{beams:?}"); + } + Some(splits) +} + +pub fn part_two(input: &str) -> Option { + let lines = input.lines().filter(|l| !l.is_empty()).collect_vec(); + let width = lines[0].len(); + let mut beams = vec![0; width]; + + for line in lines { + let values = line.chars().collect_vec(); + for (idx, value) in values.iter().enumerate() { + if *value == 'S' { + beams[idx] = 1; + } else if *value == '^' && beams[idx] > 0 { + if idx > 0 { + beams[idx-1] += beams[idx]; + } + if idx + 1 < width { + beams[idx+1] += beams[idx]; + } + beams[idx] = 0; + } + } + // println!("{beams:?}"); + } + let timelines = beams.iter().sum(); + Some(timelines) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(21)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(40)); + } +}