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