diff --git a/README.md b/README.md index 2c94e44..df93fa8 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,4 @@ Below are a list of the programming languages used to solve each day: - Day 5 - Python - Day 6 - Rust - Day 7 - Python +- Day 8 - Rust diff --git a/day5/README.md b/day5/README.md index 4727a7b..343af2f 100644 --- a/day5/README.md +++ b/day5/README.md @@ -1,4 +1,4 @@ -# Day 4 +# Day 5 ## Instructions diff --git a/day6/README.md b/day6/README.md new file mode 100644 index 0000000..6baf519 --- /dev/null +++ b/day6/README.md @@ -0,0 +1,12 @@ +# Day 6 + +## Instructions + +From the root directory of the crate (where this README is) just execute: + +``` +cargo run +``` + +The solution will be printed to the terminal output. + diff --git a/day7/README.md b/day7/README.md new file mode 100644 index 0000000..1c25b23 --- /dev/null +++ b/day7/README.md @@ -0,0 +1,16 @@ +# Day 7 + +## Instructions + +From the root directory of the crate (where this README is) just execute: + +``` +cargo run +``` + +The solution will be printed to the terminal output. + +## Notes + +Was unable to get to part 2. Hopefully will be able to revisit this. + diff --git a/day8/Cargo.toml b/day8/Cargo.toml new file mode 100644 index 0000000..a3d1d8d --- /dev/null +++ b/day8/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "day8" +version = "0.1.0" +edition = "2021" + +[dependencies] +itertools = "*" diff --git a/day8/README.md b/day8/README.md new file mode 100644 index 0000000..908d768 --- /dev/null +++ b/day8/README.md @@ -0,0 +1,12 @@ +# Day 8 + +## Instructions + +From the root directory of the crate (where this README is) just execute: + +``` +cargo run +``` + +The solution will be printed to the terminal output. + diff --git a/day8/src/lib.rs b/day8/src/lib.rs new file mode 100644 index 0000000..b66686d --- /dev/null +++ b/day8/src/lib.rs @@ -0,0 +1,220 @@ +use std::{ + collections::{HashMap, HashSet}, + error::Error, + fmt::Display, + fs::File, + io::{BufRead, BufReader}, + rc::Rc, +}; + +use itertools::Itertools; + +/// Basic error type +#[derive(Debug)] +enum ParsingError { + ParserFailure, + IndexOutOfBounds, + InconsistentGrid, +} + +impl Display for ParsingError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParsingError::ParserFailure => write!(f, "Failed to parse segment"), + ParsingError::IndexOutOfBounds => write!(f, "Index out of bounds for CharGrid"), + ParsingError::InconsistentGrid => write!(f, "File is not a consistent char grid"), + } + } +} + +type MyResult = Result>; + +impl Error for ParsingError {} + +/// Struct that flattens out the input grid +#[derive(Clone)] +pub struct CharGrid { + pub chars: Vec, + pub m: usize, + pub n: usize, + pub same_freq_positions: Vec>, +} + +impl CharGrid { + /// Take a file and split it into a CharGrid. Since input has only ASCII characters this should + /// be fine + pub fn new(input_file: &str) -> MyResult { + let file = File::open(input_file)?; + let reader = BufReader::new(file); + + let mut m = 0; + let mut n: Option = None; + let chars: Vec = reader + .lines() + .map(|line| -> MyResult> { + let parsed_line = Vec::from_iter(line?.chars()); + m += 1; + if n.is_none() { + n = Some(parsed_line.len()); + } else if n.unwrap() != parsed_line.len() { + return Err(Box::new(ParsingError::InconsistentGrid)); + } + Ok(parsed_line) + }) + .collect::>>>()? + .into_iter() + .flatten() + .collect(); + + let n = n.unwrap(); + // Now loop and get all the unique indices + let mut char_indices: HashMap = HashMap::new(); + let mut curr_idx = 0; + let mut same_freq_positions: Vec> = Vec::new(); + + for (i, c) in chars.iter().enumerate() { + if *c != '.' { + let idx = match char_indices.get(c) { + Some(idx) => idx, + None => { + char_indices.insert(*c, curr_idx); + curr_idx += 1; + same_freq_positions.push(vec![]); + &(curr_idx - 1) + } + }; + + same_freq_positions[*idx].push(i); + } + } + Ok(CharGrid { + chars, + m, + n, + same_freq_positions, + }) + } + + /// Get an index within the grid + pub fn get(&self, i: i32, j: i32) -> MyResult { + let idx = self.two2one_index(i, j)?; + Ok(self.chars[idx]) + } + + // Convert the one dimensional index to two dimensional index + pub fn one2two_index(&self, idx: usize) -> (usize, usize) { + let i = idx / self.n; + let j = idx % self.n; + (i, j) + } + + // Convert the two dimensional index to one dimensional index + pub fn two2one_index(&self, i: i32, j: i32) -> MyResult { + if i < 0 || j < 0 { + Err(Box::new(ParsingError::IndexOutOfBounds)) + } else if i as usize >= self.m || j as usize >= self.n { + Err(Box::new(ParsingError::IndexOutOfBounds)) + } else { + Ok((i as usize) * self.n + (j as usize)) + } + } +} + +/// Get the antinodes for a single frequency assuming only resonant frequencies +pub fn get_antinodes_single_freq_part1(grid: &CharGrid, positions: &[usize]) -> HashSet { + let grid_iter = positions.iter(); + let mut unique_position = HashSet::new(); + for (node1, node2) in grid_iter.clone().cartesian_product(grid_iter) { + if node1 != node2 { + let (x1, y1) = grid.one2two_index(*node1); + let (x2, y2) = grid.one2two_index(*node2); + + let x_antinode = (2 * x1).checked_sub(x2); + let y_antinode = (2 * y1).checked_sub(y2); + + if let (Some(x_antinode), Some(y_antinode)) = (x_antinode, y_antinode) { + if let Ok(idx) = grid.two2one_index(x_antinode as i32, y_antinode as i32) { + unique_position.insert(idx); + } + } + } + } + + unique_position +} + +/// Get the antinodes for a single frequency assuming resonant harmonics +/// NOTE: We shouldn't need two separate functions since part1 is a subset of part2 but running out +/// of time again so leaving it as a separate function for now +pub fn get_antinodes_single_freq_part2(grid: &CharGrid, positions: &[usize]) -> HashSet { + let grid_iter = positions.iter(); + let mut unique_position = HashSet::new(); + for (node1, node2) in grid_iter.clone().cartesian_product(grid_iter) { + if node1 != node2 { + let mut step = 1; + loop { + let (x1, y1) = grid.one2two_index(*node1); + let (x2, y2) = grid.one2two_index(*node2); + + let xstep = x1 as i32 - x2 as i32; + let ystep = y1 as i32 - y2 as i32; + + let x_antinode: MyResult = (x1 as i32 - (step * xstep)) + .try_into() + .map_err(|err| -> Box { Box::new(err) }); + + let y_antinode: MyResult = (y1 as i32 - (step * ystep)) + .try_into() + .map_err(|err| -> Box { Box::new(err) }); + + if let (Ok(x_antinode), Ok(y_antinode)) = (x_antinode, y_antinode) { + if let Ok(idx) = grid.two2one_index(x_antinode as i32, y_antinode as i32) { + unique_position.insert(idx); + } else { + break; + } + } else { + break; + } + step += 1; + } + } + } + + unique_position +} +pub enum Mode { + Part1, + Part2, +} +/// Get antinodes for all frequencies +pub fn get_antinode_count(grid: &CharGrid, mode: &Mode) -> usize { + let mut unique_positions: HashSet = HashSet::new(); + for positions in grid.same_freq_positions.iter() { + match mode { + Mode::Part1 => { + unique_positions.extend(get_antinodes_single_freq_part1(grid, positions)) + } + Mode::Part2 => { + unique_positions.extend(get_antinodes_single_freq_part2(grid, positions)) + } + } + } + unique_positions.len() +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_antinode_calc() { + let grid = CharGrid::new("test_inputs/test_input.txt").unwrap(); + assert_eq!(get_antinode_count(&grid, &Mode::Part1), 14); + assert_eq!(get_antinode_count(&grid, &Mode::Part2), 34); + } + #[test] + fn simple_part2_test() { + let grid = CharGrid::new("./test_inputs/simple_part2.txt").unwrap(); + assert_eq!(get_antinode_count(&grid, &Mode::Part2), 9); + } +} diff --git a/day8/src/main.rs b/day8/src/main.rs new file mode 100644 index 0000000..7134de2 --- /dev/null +++ b/day8/src/main.rs @@ -0,0 +1,20 @@ +use day8::{get_antinode_count, CharGrid, Mode}; + +fn main() { + let grid = match CharGrid::new("test_inputs/input.txt") { + Ok(grid) => grid, + Err(err) => { + println!("Failed because of \n{err}"); + panic!("Program failed to execute") + } + }; + + println!( + "Solution to part 1 is {}", + get_antinode_count(&grid, &Mode::Part1) + ); + println!( + "Solution to part 2 is {}", + get_antinode_count(&grid, &Mode::Part2) + ); +} diff --git a/day8/test_inputs/input.txt b/day8/test_inputs/input.txt new file mode 100644 index 0000000..b8ddb39 --- /dev/null +++ b/day8/test_inputs/input.txt @@ -0,0 +1,50 @@ +.C...............w.......................M.E...... +...............G........V.............Q....M...... +u........k...........V.y..3........Q..........4.a. +..........c.9........k..................i..7..a... +..............y.......................o....a...... +.......C...........6.......y.............E........ +.............................5....x............i.. +...............c.....wy..V.......5..............E. +........k.......c....G..I............o.........m.. +............C....s......G......o..........5....... +......................Q...............5....e...4i. +.....I.....................................m.....j +....9K.T.....I...c......w...................X..... +................I.........w....f............3..e.N +C............9..........6..............7...3...... +...Z........K.......T.................6........... +......Z..................6...............HN.E.m... +...K...........................1....N...e.o..X.... +............hz......................7........j.... +.........9......U.R......n.....4.Q..L...X......... +..................A...........S.......0........... +...............l.........p...........2.3M.......x. +.h........................U.................g..... +...Hld...........A..W.......................1x.... +.....Z.....n.......lp...e............Xj...L....... +........hU................7...j...S............... +......n............U..........D....S..q........... +....H.....d.r..T..............0..........L.S...... +......H......A..T...lp.........LK....1.....2.f.x.. +....Z............................g....4........... +..d..r............V...............f..g....2....... +.rn.........D............Pp........q....g......... +.................................................. +...................D...0.........Y..t...P.q....... +.......R.s.......................q.P..1........... +...........h..........................2.........f. +........................W......................... +...8...........O................k................. +....rY...........D................P............... +....................O...u......................... +..s..................F............................ +...................R......F....................... +......8...........z0....F................J.W...... +...................F..z................u.......... +..............R.........O.............v.Jt........ +s.............8.........m........J.t............v. +......Y.....M........................u..tv........ +.................................................v +.................................................. +.................z.W..................J........... diff --git a/day8/test_inputs/simple_part2.txt b/day8/test_inputs/simple_part2.txt new file mode 100644 index 0000000..b8439c3 --- /dev/null +++ b/day8/test_inputs/simple_part2.txt @@ -0,0 +1,10 @@ +T......... +...T...... +.T........ +.......... +.......... +.......... +.......... +.......... +.......... +.......... diff --git a/day8/test_inputs/test_input.txt b/day8/test_inputs/test_input.txt new file mode 100644 index 0000000..78a1e91 --- /dev/null +++ b/day8/test_inputs/test_input.txt @@ -0,0 +1,12 @@ +............ +........0... +.....0...... +.......0.... +....0....... +......A..... +............ +............ +........A... +.........A.. +............ +............