day17: problem 2 - an ugly mess but finally working
This commit is contained in:
211
17/src/main.rs
211
17/src/main.rs
@ -150,12 +150,18 @@ impl<'a> WalkCost<'a> {
|
||||
// if cur_move.weight >= self.cost_to[cur_move.new_pos.1][cur_move.new_pos.0] {
|
||||
// continue;
|
||||
// }
|
||||
if let Some(last_weight) = self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0].get(&(*cur_move.dir, cur_move.consecutive)) {
|
||||
if let Some(last_weight) = self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0]
|
||||
.get(&(*cur_move.dir, cur_move.consecutive))
|
||||
{
|
||||
if cur_move.weight < *last_weight {
|
||||
self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0].insert((*cur_move.dir, cur_move.consecutive), cur_move.weight);
|
||||
} else { continue; }// visited before at lower cost }
|
||||
self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0]
|
||||
.insert((*cur_move.dir, cur_move.consecutive), cur_move.weight);
|
||||
} else {
|
||||
continue;
|
||||
} // visited before at lower cost }
|
||||
} else {
|
||||
self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0].insert((*cur_move.dir, cur_move.consecutive), cur_move.weight);
|
||||
self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0]
|
||||
.insert((*cur_move.dir, cur_move.consecutive), cur_move.weight);
|
||||
}
|
||||
// println!("state at {:?}: {:?}", cur_move, self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0]);
|
||||
|
||||
@ -175,17 +181,21 @@ impl<'a> WalkCost<'a> {
|
||||
})
|
||||
})
|
||||
.filter(|m| m.consecutive != 4 && *m.dir != cur_move.dir.opposite())
|
||||
.filter(|m| {
|
||||
m.weight < *self.cost_from[m.new_pos.1][m.new_pos.0].get(&(*m.dir, m.consecutive)).unwrap_or(&u64::MAX)
|
||||
})
|
||||
.filter(|m| {
|
||||
m.weight
|
||||
< *self.cost_from[m.new_pos.1][m.new_pos.0]
|
||||
.get(&(*m.dir, m.consecutive))
|
||||
.unwrap_or(&u64::MAX)
|
||||
})
|
||||
}); // valid positions
|
||||
// println!("valid moves with {:?}", cur_move);
|
||||
// println!("valid moves with {:?}", cur_move);
|
||||
for next_move in valid_moves {
|
||||
// println!(" {:?}", next_move);
|
||||
unvisited_next_moves.push(next_move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shortest_path_to(&self, to: Position) -> Vec<(Position, Direction)> {
|
||||
let mut path = Vec::new();
|
||||
let mut cur_pos = to;
|
||||
@ -219,19 +229,140 @@ impl<'a> WalkCost<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for WalkCost<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// for y in 0..self.cost_to.len() {
|
||||
// for cost in &self.cost_to[y] {
|
||||
// f.write_fmt(format_args!("{:3} ", cost))?;
|
||||
// }
|
||||
// f.write_str(" ")?;
|
||||
// for input in &self.map.map[y] {
|
||||
// f.write_fmt(format_args!("{:3} ", input))?;
|
||||
// }
|
||||
// writeln!(f)?;
|
||||
// }
|
||||
Ok(())
|
||||
struct WalkCost2<'a> {
|
||||
start: Position,
|
||||
cost_from: Vec<Vec<HashMap<(Direction, usize), u64>>>,
|
||||
map: &'a CityMap,
|
||||
}
|
||||
|
||||
impl<'a> WalkCost2<'a> {
|
||||
fn from_map(map: &'a CityMap, start: Position) -> Self {
|
||||
Self {
|
||||
map,
|
||||
start,
|
||||
cost_from: map
|
||||
.map
|
||||
.iter()
|
||||
.map(|row| repeat(HashMap::new()).take(row.len()).collect())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn compute(&mut self, to: Position) {
|
||||
println!("computing to: {:?}", to);
|
||||
let mut unvisited_next_moves: BinaryHeap<Move> = BinaryHeap::new();
|
||||
let valid_start_moves: Vec<Move> = Direction::all()
|
||||
.iter()
|
||||
.filter_map(|dir| {
|
||||
self.map.offset_pos(self.start, dir).and_then(|pos| {
|
||||
Some(Move {
|
||||
new_pos: pos,
|
||||
dir,
|
||||
consecutive: 1,
|
||||
weight: self.map.map[pos.1][pos.0],
|
||||
})
|
||||
})
|
||||
}) // valid positions
|
||||
.collect();
|
||||
|
||||
for m in valid_start_moves {
|
||||
unvisited_next_moves.push(m);
|
||||
}
|
||||
|
||||
while let Some(cur_move) = unvisited_next_moves.pop() {
|
||||
// we've been here already at a lower cost
|
||||
// if cur_move.weight >= self.cost_to[cur_move.new_pos.1][cur_move.new_pos.0] {
|
||||
// continue;
|
||||
// }
|
||||
if let Some(last_weight) = self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0]
|
||||
.get(&(*cur_move.dir, cur_move.consecutive))
|
||||
{
|
||||
if cur_move.weight < *last_weight {
|
||||
self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0]
|
||||
.insert((*cur_move.dir, cur_move.consecutive), cur_move.weight);
|
||||
// println!("move {:?} inserted {:?}", cur_move, (*cur_move.dir, cur_move.consecutive));
|
||||
} else {
|
||||
continue;
|
||||
} // visited before at lower cost }
|
||||
} else {
|
||||
// println!("move {:?} inserted {:?}", cur_move, (*cur_move.dir, cur_move.consecutive));
|
||||
self.cost_from[cur_move.new_pos.1][cur_move.new_pos.0]
|
||||
.insert((*cur_move.dir, cur_move.consecutive), cur_move.weight);
|
||||
}
|
||||
|
||||
if cur_move.new_pos == to {
|
||||
// println!("reached end pos {:?} via {:?}", to, cur_move);
|
||||
continue;
|
||||
}
|
||||
let valid_moves = Direction::all().iter().filter_map(|dir| {
|
||||
self.map
|
||||
.offset_pos(cur_move.new_pos, dir)
|
||||
.and_then(|new_pos| {
|
||||
Some(Move {
|
||||
new_pos,
|
||||
dir,
|
||||
consecutive: if cur_move.dir == dir {
|
||||
cur_move.consecutive + 1
|
||||
} else {
|
||||
1
|
||||
},
|
||||
weight: cur_move.weight + self.map.map[new_pos.1][new_pos.0],
|
||||
})
|
||||
})
|
||||
.filter(|m| *m.dir != cur_move.dir.opposite())
|
||||
.filter(|m| {
|
||||
if m.dir == cur_move.dir {
|
||||
m.consecutive < 11
|
||||
} else {
|
||||
cur_move.consecutive >= 4
|
||||
}
|
||||
})
|
||||
.filter(|m| m.new_pos != to || m.consecutive >= 4)
|
||||
.filter(|m| {
|
||||
m.weight
|
||||
< *self.cost_from[m.new_pos.1][m.new_pos.0]
|
||||
.get(&(*m.dir, m.consecutive))
|
||||
.unwrap_or(&u64::MAX)
|
||||
})
|
||||
}); // valid positions
|
||||
// println!("valid moves with {:?}", cur_move);
|
||||
for next_move in valid_moves {
|
||||
// println!(" {:?}", next_move);
|
||||
unvisited_next_moves.push(next_move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn shortest_path_to(&self, to: Position) -> Vec<(Position, Direction)> {
|
||||
let mut path = Vec::new();
|
||||
let mut cur_pos = to;
|
||||
// start at the end, walk backwards
|
||||
while cur_pos != self.start {
|
||||
let (m, val) = self.cost_from[cur_pos.1][cur_pos.0].iter().min_by(|a, b| a.1.cmp(b.1)).unwrap();
|
||||
path.push((cur_pos, m.0));
|
||||
cur_pos = self.map.offset_pos(cur_pos, &m.0.opposite()).unwrap();
|
||||
}
|
||||
path
|
||||
}
|
||||
fn print_shortest_path(&self, to: Position) {
|
||||
let path = self.shortest_path_to(to);
|
||||
let map: HashMap<_, _, RandomState> = HashMap::from_iter(path.into_iter());
|
||||
for y in 0..self.cost_from.len() {
|
||||
for x in 0..self.map.map[y].len() {
|
||||
if let Some(to_dir) = map.get(&(x, y)) {
|
||||
let c = match to_dir {
|
||||
Direction::Left => '<',
|
||||
Direction::Right => '>',
|
||||
Direction::Up => '^',
|
||||
Direction::Down => 'v',
|
||||
};
|
||||
print!("{}", c);
|
||||
} else {
|
||||
print!("{}", self.map.map[y][x]);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,21 +383,35 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
let mut costs = WalkCost::from_map(&map, (0, 0));
|
||||
costs.compute();
|
||||
|
||||
println!("{}", costs);
|
||||
// println!("{}", costs);
|
||||
// costs.print_shortest_path((costs.cost_to[0].len() - 1, costs.cost_to.len() - 1));
|
||||
|
||||
*costs.cost_from[costs.cost_from.len()-1][costs.cost_from[0].len() - 1].values().min().unwrap()
|
||||
*costs.cost_from[costs.cost_from.len() - 1][costs.cost_from[0].len() - 1]
|
||||
.values()
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// PROBLEM 2 solution
|
||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
0
|
||||
let map = CityMap::from(input);
|
||||
let mut costs = WalkCost2::from_map(&map, (0, 0));
|
||||
costs.compute((map.map[0].len() - 1, map.map.len() - 1));
|
||||
|
||||
// println!("{}", costs);
|
||||
costs.print_shortest_path((costs.cost_from[0].len() - 1, costs.cost_from.len() - 1));
|
||||
|
||||
*costs.cost_from[costs.cost_from.len() - 1][costs.cost_from[0].len() - 1]
|
||||
.values()
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
use std::io::Cursor;
|
||||
use test_case::test_case;
|
||||
|
||||
const EXAMPLE: &str = &"2413432311323
|
||||
3215453535623
|
||||
@ -280,8 +425,13 @@ mod tests {
|
||||
4564679986453
|
||||
1224686865563
|
||||
2546548887735
|
||||
4322674655533
|
||||
";
|
||||
4322674655533";
|
||||
|
||||
const EXAMPLE2: &str = &"111111111111
|
||||
999999999991
|
||||
999999999991
|
||||
999999999991
|
||||
999999999991";
|
||||
|
||||
#[test]
|
||||
fn problem1_example() {
|
||||
@ -289,9 +439,10 @@ mod tests {
|
||||
assert_eq!(problem1(c.lines()), 102);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn problem2_example() {
|
||||
let c = Cursor::new(EXAMPLE);
|
||||
assert_eq!(problem2(c.lines()), 0);
|
||||
#[test_case(EXAMPLE, 94)]
|
||||
#[test_case(EXAMPLE2, 71)]
|
||||
fn problem2_example(example: &str, expect: u64) {
|
||||
let c = Cursor::new(example);
|
||||
assert_eq!(problem2(c.lines()), expect);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user