1
0

added grid utility and used it to speed up day 6

This commit is contained in:
2024-12-06 15:33:57 +01:00
parent 476f34e2b4
commit 2636f4a6c8
4 changed files with 124 additions and 16 deletions

View File

@@ -3,6 +3,8 @@ use std::{
fs::read_to_string, fs::read_to_string,
}; };
use crate::utils::grid::Grid;
pub fn day_main() { pub fn day_main() {
let input = read_to_string("input/day06.txt").unwrap(); let input = read_to_string("input/day06.txt").unwrap();
let input = input.trim(); let input = input.trim();
@@ -11,7 +13,7 @@ pub fn day_main() {
} }
type RiddleResult = usize; type RiddleResult = usize;
type Point = (i32, i32); type Point = (i64, i64);
fn part1(input: &str) -> RiddleResult { fn part1(input: &str) -> RiddleResult {
let (m, pos) = parse(input); let (m, pos) = parse(input);
@@ -21,13 +23,9 @@ fn part1(input: &str) -> RiddleResult {
visited.len() visited.len()
} }
fn get_visited( fn get_visited(m: &Grid<char>, mut pos: Point, mut dir: char) -> HashSet<Point> {
m: &HashMap<(i32, i32), char>,
mut pos: (i32, i32),
mut dir: char,
) -> HashSet<(i32, i32)> {
let mut visited = HashSet::new(); let mut visited = HashSet::new();
while m.contains_key(&pos) { while m.contains_key(pos) {
let (x, y) = pos; let (x, y) = pos;
visited.insert(pos); visited.insert(pos);
let next = match dir { let next = match dir {
@@ -37,7 +35,7 @@ fn get_visited(
'>' => (x + 1, y), '>' => (x + 1, y),
_ => unreachable!(), _ => unreachable!(),
}; };
if let Some('#') = m.get(&next) { if m.contains_key(next) && '#' == m[next] {
dir = match dir { dir = match dir {
'^' => '>', '^' => '>',
'v' => '<', 'v' => '<',
@@ -62,13 +60,13 @@ fn part2(input: &str) -> RiddleResult {
.count() .count()
} }
fn parse(input: &str) -> (HashMap<Point, char>, Point) { fn parse(input: &str) -> (Grid<char>, Point) {
let mut m = HashMap::new(); let mut m = HashMap::new();
let mut pos = None; let mut pos = None;
input.lines().enumerate().for_each(|(y, line)| { input.lines().enumerate().for_each(|(y, line)| {
line.char_indices().for_each(|(x, c)| { line.char_indices().for_each(|(x, c)| {
let x = x as i32; let x = x as i64;
let y = y as i32; let y = y as i64;
if c == '^' { if c == '^' {
pos = Some((x, y)); pos = Some((x, y));
m.insert((x, y), '.'); m.insert((x, y), '.');
@@ -77,10 +75,10 @@ fn parse(input: &str) -> (HashMap<Point, char>, Point) {
} }
}); });
}); });
(m, pos.unwrap()) (Grid::from(m), pos.unwrap())
} }
fn is_loop(m: &HashMap<Point, char>, block: Point, mut pos: Point, mut dir: char) -> bool { fn is_loop(m: &Grid<char>, block: Point, mut pos: Point, mut dir: char) -> bool {
let mut visited = HashSet::new(); let mut visited = HashSet::new();
loop { loop {
let (x, y) = pos; let (x, y) = pos;
@@ -92,10 +90,9 @@ fn is_loop(m: &HashMap<Point, char>, block: Point, mut pos: Point, mut dir: char
'>' => (x + 1, y), '>' => (x + 1, y),
_ => unreachable!(), _ => unreachable!(),
}; };
let next_tile = m.get(&next); if !m.contains_key(next) {
if None == next_tile {
return false; return false;
} else if next == block || Some(&'#') == next_tile { } else if next == block || '#' == m[next] {
// we only check for loops on a collision to speed things up // we only check for loops on a collision to speed things up
if visited.contains(&(pos, dir)) { if visited.contains(&(pos, dir)) {
return true; return true;

View File

@@ -4,3 +4,4 @@ pub mod day03;
pub mod day04; pub mod day04;
pub mod day05; pub mod day05;
pub mod day06; pub mod day06;
pub mod utils;

109
src/utils/grid.rs Normal file
View File

@@ -0,0 +1,109 @@
use std::{
collections::HashMap,
ops::{Index, IndexMut},
};
/// A grid structure, indexed by (x, y) tuples. The top-left coordinate is (0, 0).
pub struct Grid<T> {
content_width: i64,
content_height: i64,
content: Vec<T>,
}
type Coord = (i64, i64);
impl<T> Grid<T> {
pub fn from(mut source: HashMap<Coord, T>) -> Grid<T> {
let x_max = source.keys().max_by_key(|c| c.0).unwrap().0;
let y_max = source.keys().max_by_key(|c| c.1).unwrap().1;
let content_width = x_max + 1;
let content_height = y_max + 1;
let mut content = Vec::with_capacity((content_width * content_height) as usize);
for y in 0..content_height {
for x in 0..content_width {
let v = source
.remove(&(x, y))
.unwrap_or_else(|| panic!("no entry for {x}, {y}"));
content.push(v);
}
}
Grid {
content_width,
content_height,
content,
}
}
fn index_of(&self, c: Coord) -> usize {
(c.1 * self.content_width + c.0) as usize
}
pub fn get(&self, c: Coord) -> &T {
&self.content[self.index_of(c)]
}
pub fn get_mut(&mut self, c: Coord) -> &mut T {
let index_of = self.index_of(c);
&mut self.content[index_of]
}
pub fn set(&mut self, c: Coord, value: T) {
let index_of = self.index_of(c);
self.content[index_of] = value;
}
pub fn contains_key(&self, c: Coord) -> bool {
0 <= c.0 && c.0 < self.content_width && 0 <= c.1 && c.1 < self.content_height
}
}
impl<T> Index<Coord> for Grid<T> {
type Output = T;
#[inline]
fn index(&self, index: Coord) -> &Self::Output {
self.get(index)
}
}
impl<T> IndexMut<Coord> for Grid<T> {
fn index_mut(&mut self, index: Coord) -> &mut Self::Output {
self.get_mut(index)
}
}
#[cfg(test)]
mod test {
use std::collections::HashMap;
use super::Grid;
#[test]
fn init_and_read() {
let grid: Grid<char> = Grid::from(HashMap::from_iter([
((0, 0), '.'),
((0, 1), '.'),
((1, 0), '.'),
((1, 1), '#'),
]));
assert_eq!(&'.', grid.get((0, 0)));
assert_eq!(&'.', grid.get((0, 1)));
assert_eq!(&'.', grid.get((1, 0)));
assert_eq!(&'#', grid.get((1, 1)));
}
#[test]
fn mutate_by_index() {
let mut grid = generate();
grid[(0, 1)] = 'x';
assert_eq!(&'x', grid.get((0, 1)));
}
fn generate() -> Grid<char> {
let grid: Grid<char> = Grid::from(HashMap::from_iter([
((0, 0), '.'),
((0, 1), '.'),
((1, 0), '.'),
((1, 1), '.'),
]));
grid
}
}

1
src/utils/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod grid;