add 24
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
use std::collections::{BTreeSet, HashSet, VecDeque};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
const TO_VISIT: usize = 4;
|
||||
#[cfg(debug_assertions)]
|
||||
const TO_VISIT_CHAR: char = '4';
|
||||
#[cfg(debug_assertions)]
|
||||
const FILENAME: &str = "example.txt";
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
const TO_VISIT: usize = 7;
|
||||
#[cfg(not(debug_assertions))]
|
||||
const TO_VISIT_CHAR: char = '7';
|
||||
#[cfg(not(debug_assertions))]
|
||||
const FILENAME: &str = "input.txt";
|
||||
|
||||
fn main() {
|
||||
let mut position = (0, 0);
|
||||
let mut targets = HashSet::new();
|
||||
|
||||
let map: Vec<Vec<_>> = std::fs::read_to_string(FILENAME)
|
||||
.unwrap()
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(y, l)| {
|
||||
l.chars()
|
||||
.enumerate()
|
||||
.map({
|
||||
|(x, c)| match c {
|
||||
'#' => Item::Wall,
|
||||
'0' => {
|
||||
position = (x, y);
|
||||
Item::Passage
|
||||
}
|
||||
'1'..=TO_VISIT_CHAR => {
|
||||
targets.insert((x, y));
|
||||
Item::Passage
|
||||
}
|
||||
_ => Item::Passage,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let grid = Grid::new(map, targets);
|
||||
|
||||
let state = State::new(position);
|
||||
|
||||
let result = bfs(state.clone(), &grid) - 1; // 460
|
||||
println!("{result}");
|
||||
|
||||
let result = bfs_2(state, &grid); // 668
|
||||
println!("{result}");
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
struct State {
|
||||
position: (usize, usize),
|
||||
visited: BTreeSet<(usize, usize)>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn is_goal_state(&self) -> bool {
|
||||
self.visited.len() == TO_VISIT
|
||||
}
|
||||
|
||||
fn is_goal_state_2(&self, start_position: (usize, usize)) -> bool {
|
||||
self.visited.len() == TO_VISIT && self.position == start_position
|
||||
}
|
||||
|
||||
fn get_neighbor_states(&self, grid: &Grid) -> impl Iterator<Item = State> {
|
||||
[
|
||||
(self.position.0 - 1, self.position.1),
|
||||
(self.position.0, self.position.1 - 1),
|
||||
(self.position.0, self.position.1 + 1),
|
||||
(self.position.0 + 1, self.position.1),
|
||||
]
|
||||
.into_iter()
|
||||
.filter(|(x, y)| grid.is_passage(*x, *y))
|
||||
.map(|(x, y)| {
|
||||
let mut state = self.clone();
|
||||
state.position = (x, y);
|
||||
state
|
||||
})
|
||||
}
|
||||
|
||||
fn new(position: (usize, usize)) -> Self {
|
||||
let visited = BTreeSet::new();
|
||||
Self { position, visited }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Grid {
|
||||
map: Vec<Vec<Item>>,
|
||||
targets: HashSet<(usize, usize)>,
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
fn is_target_position(&self, position: (usize, usize)) -> bool {
|
||||
self.targets.contains(&position)
|
||||
}
|
||||
|
||||
fn is_passage(&self, x: usize, y: usize) -> bool {
|
||||
match self.map[y][x] {
|
||||
Item::Passage => true,
|
||||
Item::Wall => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn new(map: Vec<Vec<Item>>, targets: HashSet<(usize, usize)>) -> Self {
|
||||
Self { map, targets }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Item {
|
||||
Passage,
|
||||
Wall,
|
||||
}
|
||||
|
||||
fn bfs(start: State, grid: &Grid) -> usize {
|
||||
let mut queue = VecDeque::new();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
visited.insert(start.clone());
|
||||
|
||||
queue.push_back((start, 0));
|
||||
|
||||
while let Some((mut state, distance)) = queue.pop_front() {
|
||||
if state.is_goal_state() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
if grid.is_target_position(state.position) {
|
||||
state.visited.insert(state.position);
|
||||
}
|
||||
|
||||
for neighbor in state.get_neighbor_states(grid) {
|
||||
if !visited.contains(&neighbor) {
|
||||
visited.insert(neighbor.clone());
|
||||
queue.push_back((neighbor, distance + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("goal unreachable");
|
||||
}
|
||||
|
||||
fn bfs_2(start: State, grid: &Grid) -> usize {
|
||||
let mut queue = VecDeque::new();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
visited.insert(start.clone());
|
||||
|
||||
let start_position = start.position;
|
||||
|
||||
queue.push_back((start, 0));
|
||||
|
||||
while let Some((mut state, distance)) = queue.pop_front() {
|
||||
if state.is_goal_state_2(start_position) {
|
||||
return distance;
|
||||
}
|
||||
|
||||
if grid.is_target_position(state.position) {
|
||||
state.visited.insert(state.position);
|
||||
}
|
||||
|
||||
for neighbor in state.get_neighbor_states(grid) {
|
||||
if !visited.contains(&neighbor) {
|
||||
visited.insert(neighbor.clone());
|
||||
queue.push_back((neighbor, distance + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("goal unreachable");
|
||||
}
|
||||
Reference in New Issue
Block a user