3 Commits

Author SHA1 Message Date
f8c6f4e01f Refactor out common code 2022-12-12 22:46:30 +01:00
e2d1ec8c91 Bugfix that probably won't affect any actual input 2022-12-12 22:32:41 +01:00
d92e77cb88 Reinstroduce the humble index set 2022-12-12 21:52:11 +01:00
2 changed files with 62 additions and 53 deletions

View File

@@ -133,3 +133,42 @@ pub fn get_both<T>(slice: &mut [T], first: usize, second: usize) -> (&mut T, &mu
Ordering::Equal => panic!("Tried to get the same index twice {first}"), Ordering::Equal => panic!("Tried to get the same index twice {first}"),
} }
} }
#[derive(Default)]
pub struct IndexSet(Vec<u32>);
impl IndexSet {
pub fn with_capacity(capacity: usize) -> Self {
Self(Vec::with_capacity(
capacity / std::mem::size_of::<u32>() / 8,
))
}
fn ensure_item(&mut self, item: usize) -> &mut u32 {
if self.0.len() <= item {
self.0.resize(item + 1, 0);
}
&mut self.0[item]
}
#[inline]
fn index(index: usize) -> (usize, u8) {
const PER_ENTRY: usize = 8 * std::mem::size_of::<u32>();
(index / PER_ENTRY, (index % PER_ENTRY) as u8)
}
pub fn insert(&mut self, index: usize) -> bool {
let (entry, pos) = Self::index(index);
let item = self.ensure_item(entry);
if *item & (1 << pos) != 0 {
false
} else {
*item |= 1 << pos;
true
}
}
}

View File

@@ -3,16 +3,23 @@ use std::collections::VecDeque;
use anyhow::Context; use anyhow::Context;
use anyhow::Result; use anyhow::Result;
use crate::common::IndexSet;
fn can_travel(from: u8, to: u8) -> bool { fn can_travel(from: u8, to: u8) -> bool {
match (from, to) { match (from, to) {
(b'S', b'a'..=b'z') => true, (b'S', b'a'..=b'b') => true,
(b'y'..=b'z', b'E') => true, (b'y'..=b'z', b'E') => true,
(b'a'..=b'z', b'a'..=b'z') => to <= from || to - from == 1, (b'a'..=b'z', b'a'..=b'z') => to <= from || to - from == 1,
_ => false, _ => false,
} }
} }
pub fn part1(input: &[u8]) -> Result<String> { fn parts_common(
input: &[u8],
starting_symbol: u8,
is_end: impl Fn(u8) -> bool,
accessible: impl Fn(u8, u8) -> bool,
) -> Result<String> {
let width = input let width = input
.iter() .iter()
.position(|&c| c == b'\n') .position(|&c| c == b'\n')
@@ -21,22 +28,21 @@ pub fn part1(input: &[u8]) -> Result<String> {
let starting_pos = input let starting_pos = input
.iter() .iter()
.position(|&c| c == b'S') .position(|&c| c == starting_symbol)
.context("Could not find starting position")?; .context("Could not find starting position")?;
let mut visited = vec![false; input.len()]; let mut visited = IndexSet::with_capacity(input.len());
let mut todo = VecDeque::new(); let mut todo = VecDeque::new();
todo.push_back((0, starting_pos)); todo.push_back((0, starting_pos));
while let Some((dist, pos)) = todo.pop_front() { while let Some((dist, pos)) = todo.pop_front() {
if input[pos] == b'E' { if is_end(input[pos]) {
return Ok(dist.to_string()); return Ok(dist.to_string());
} }
let mut add_todo = |new: usize| { let mut add_todo = |new: usize| {
if can_travel(input[pos], input[new]) && !visited[new] { if accessible(input[pos], input[new]) && visited.insert(new) {
visited[new] = true;
todo.push_back((dist + 1, new)); todo.push_back((dist + 1, new));
} }
}; };
@@ -61,53 +67,17 @@ pub fn part1(input: &[u8]) -> Result<String> {
anyhow::bail!("Did not find a valid route") anyhow::bail!("Did not find a valid route")
} }
pub fn part1(input: &[u8]) -> Result<String> {
parts_common(input, b'S', |b| b == b'E', can_travel)
}
pub fn part2(input: &[u8]) -> Result<String> { pub fn part2(input: &[u8]) -> Result<String> {
let width = input parts_common(
.iter() input,
.position(|&c| c == b'\n') b'E',
.context("No newlines in input")? |b| b == b'a' || b == b'S',
+ 1; |a, b| can_travel(b, a),
)
let starting_pos = input
.iter()
.position(|&c| c == b'E')
.context("Could not find starting position")?;
let mut visited = vec![false; input.len()];
let mut todo = VecDeque::new();
todo.push_back((0, starting_pos));
while let Some((dist, pos)) = todo.pop_front() {
if input[pos] == b'a' || input[pos] == b'S' {
return Ok(dist.to_string());
}
let mut add_todo = |new: usize| {
if can_travel(input[new], input[pos]) && !visited[new] {
visited[new] = true;
todo.push_back((dist + 1, new));
}
};
if pos % width != 0 {
add_todo(pos - 1);
}
if pos % width != width - 1 {
add_todo(pos + 1);
}
if pos >= width {
add_todo(pos - width);
}
if pos + width < input.len() {
add_todo(pos + width);
}
}
anyhow::bail!("Did not find a valid route")
} }
#[cfg(test)] #[cfg(test)]