Compare commits
6 Commits
8badd4a416
...
21dfc7f630
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21dfc7f630 | ||
|
|
719db128cf | ||
|
|
12f32ed232 | ||
|
|
768d328e87 | ||
|
|
31075fa449 | ||
|
|
d5aa1ddc74 |
@@ -1,4 +1,4 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
aoc_2018::tasks::day20::task1();
|
aoc_2018::tasks::day22::both();
|
||||||
// aoc_2018::tasks::day15::task2();
|
// aoc_2018::tasks::day15::task2();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ fn fuel_level(x: i32, y: i32, serial: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test {
|
mod test {
|
||||||
use super::fuel_level;
|
|
||||||
#[test]
|
#[test]
|
||||||
fn name() {
|
fn name() {
|
||||||
|
use super::fuel_level;
|
||||||
assert_eq!(fuel_level(3, 5, 8), 4);
|
assert_eq!(fuel_level(3, 5, 8), 4);
|
||||||
assert_eq!(fuel_level(122, 79, 57), -5);
|
assert_eq!(fuel_level(122, 79, 57), -5);
|
||||||
assert_eq!(fuel_level(217, 196, 39), 0);
|
assert_eq!(fuel_level(217, 196, 39), 0);
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ fn add_default_neighbors_for_room(map: &mut HashMap<Point, Tile>, position: Poin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn print_map(map: &HashMap<Point, Tile>) {
|
fn print_map(map: &HashMap<Point, Tile>) {
|
||||||
let xmin = map.keys().min_by_key(|it| it.0).unwrap().0;
|
let xmin = map.keys().min_by_key(|it| it.0).unwrap().0;
|
||||||
let xmax = map.keys().max_by_key(|it| it.0).unwrap().0;
|
let xmax = map.keys().max_by_key(|it| it.0).unwrap().0;
|
||||||
|
|||||||
249
src/tasks/day22.rs
Normal file
249
src/tasks/day22.rs
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
use crate::tasks::day22::Equipment::*;
|
||||||
|
use core::cmp::Ordering;
|
||||||
|
use std::collections::BinaryHeap;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
pub fn both() {
|
||||||
|
let start = Instant::now();
|
||||||
|
let mut cave = Cave::create(3879, Node(8, 713, Torch));
|
||||||
|
println!("#{:?}", Instant::now() - start);
|
||||||
|
let x = Instant::now();
|
||||||
|
println!("Sum of erosion indexes: {}", cave.erosion_sum());
|
||||||
|
println!("#{:?}", Instant::now() - x);
|
||||||
|
let x = Instant::now();
|
||||||
|
println!("Shortest path length: {}", cave.shortest_path_length());
|
||||||
|
println!("#{:?}", Instant::now() - x);
|
||||||
|
println!("#Overall run time: {:?}", Instant::now() - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Copy, Debug, Eq, Hash)]
|
||||||
|
enum Equipment {
|
||||||
|
Torch,
|
||||||
|
Climbing,
|
||||||
|
Neither,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
struct Node(usize, usize, Equipment);
|
||||||
|
|
||||||
|
struct Cave {
|
||||||
|
map: HashMap<(usize, usize), usize>,
|
||||||
|
target: Node,
|
||||||
|
depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cave {
|
||||||
|
fn create(depth: usize, target: Node) -> Self {
|
||||||
|
if target.2 != Torch {
|
||||||
|
panic!("A valid target point needs the torch equipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
let map = HashMap::with_capacity((target.0 + 1) * (target.1 + 1));
|
||||||
|
|
||||||
|
Cave { map, target, depth }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn erosion_sum(&mut self) -> usize {
|
||||||
|
(0..=self.target.0)
|
||||||
|
.map(|x| {
|
||||||
|
(0..=self.target.1)
|
||||||
|
.map(|y| self.field_type(x, y))
|
||||||
|
.sum::<usize>()
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_type(&mut self, x: usize, y: usize) -> usize {
|
||||||
|
self.erosion_index(x, y) % 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn erosion_index(&mut self, x: usize, y: usize) -> usize {
|
||||||
|
if let Some(value) = self.map.get(&(x, y)) {
|
||||||
|
return *value;
|
||||||
|
} else {
|
||||||
|
let geo_index = match (x, y) {
|
||||||
|
(0, 0) => 0,
|
||||||
|
_ if self.target.0 == x && self.target.1 == y => 0,
|
||||||
|
(x, 0) => x * 16807,
|
||||||
|
(0, y) => y * 48271,
|
||||||
|
(x, y) => self.erosion_index(x - 1, y) * self.erosion_index(x, y - 1),
|
||||||
|
};
|
||||||
|
let erosion_index = (geo_index + self.depth) % 20183;
|
||||||
|
self.map.insert((x, y), erosion_index);
|
||||||
|
return erosion_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shortest_path_length(&mut self) -> usize {
|
||||||
|
self.map.reserve(
|
||||||
|
std::cmp::max(self.target.0, self.target.1)
|
||||||
|
* std::cmp::max(self.target.0, self.target.1)
|
||||||
|
- self.map.len(),
|
||||||
|
);
|
||||||
|
let start = Node(0, 0, Torch);
|
||||||
|
let mut open: BinaryHeap<State> = BinaryHeap::new();
|
||||||
|
let mut distances: HashMap<Node, usize> = HashMap::new();
|
||||||
|
// let mut rightmost = 0;
|
||||||
|
// let mut lowest = 0;
|
||||||
|
// let mut visited_counter = 0;
|
||||||
|
|
||||||
|
distances.insert(start, 0);
|
||||||
|
open.push(State {
|
||||||
|
cost: 0,
|
||||||
|
position: start,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Examine the frontier with lower cost nodes first (min-heap)
|
||||||
|
while let Some(State { cost, position }) = open.pop() {
|
||||||
|
// if position.0 > rightmost {
|
||||||
|
// rightmost = position.0;
|
||||||
|
// }
|
||||||
|
// if position.1 > lowest {
|
||||||
|
// lowest = position.1;
|
||||||
|
// }
|
||||||
|
// visited_counter += 1;
|
||||||
|
|
||||||
|
// Alternatively we could have continued to find all shortest paths
|
||||||
|
if position == self.target {
|
||||||
|
// println!(
|
||||||
|
// "Visited {} nodes ({}, {})",
|
||||||
|
// visited_counter, rightmost, lowest
|
||||||
|
// );
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important as we may have already found a better way
|
||||||
|
if cost > *distances.entry(position).or_insert(usize::max_value()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each node we can reach, see if we can find a way with
|
||||||
|
// a lower cost going through this node
|
||||||
|
for edge in self.neighbors(position) {
|
||||||
|
let next = State {
|
||||||
|
cost: cost + edge.cost,
|
||||||
|
position: edge.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If so, add it to the frontier and continue
|
||||||
|
let current_distance = distances.entry(edge.node).or_insert(usize::max_value());
|
||||||
|
if next.cost < *current_distance {
|
||||||
|
open.push(next);
|
||||||
|
// Relaxation, we have now found a better way
|
||||||
|
*current_distance = next.cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!("There is always a path");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn neighbors(&mut self, position: Node) -> Vec<Edge> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
// add all variants of the current position
|
||||||
|
result.push(Edge {
|
||||||
|
cost: 7,
|
||||||
|
node: self.other_node_for_region(position),
|
||||||
|
});
|
||||||
|
|
||||||
|
// for any neighbor position: if it allows the same equipment and is within bounds: add it
|
||||||
|
[
|
||||||
|
(position.0 as i32 - 1, position.1 as i32),
|
||||||
|
(position.0 as i32 + 1, position.1 as i32),
|
||||||
|
(position.0 as i32, position.1 as i32 - 1),
|
||||||
|
(position.0 as i32, position.1 as i32 + 1),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(x, y)| {
|
||||||
|
if *x >= 0 && *y >= 0 {
|
||||||
|
Some(Node(*x as usize, *y as usize, position.2))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_each(|node| {
|
||||||
|
if self.equipment_allowed_for_region(node.0, node.1, node.2) {
|
||||||
|
result.push(Edge {
|
||||||
|
cost: 1,
|
||||||
|
node: node,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn equipment_allowed_for_region(&mut self, x: usize, y: usize, equipment: Equipment) -> bool {
|
||||||
|
let field_type = self.field_type(x, y);
|
||||||
|
match field_type {
|
||||||
|
// rocky
|
||||||
|
0 => equipment == Torch || equipment == Climbing,
|
||||||
|
// wet
|
||||||
|
1 => equipment == Climbing || equipment == Neither,
|
||||||
|
//narrow
|
||||||
|
2 => equipment == Torch || equipment == Neither,
|
||||||
|
_ => panic!("not a valid type!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn other_node_for_region(&mut self, position: Node) -> Node {
|
||||||
|
let field_type = self.field_type(position.0, position.1);
|
||||||
|
Node(
|
||||||
|
position.0,
|
||||||
|
position.1,
|
||||||
|
match field_type {
|
||||||
|
0 => match position.2 {
|
||||||
|
Climbing => Torch,
|
||||||
|
Torch => Climbing,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
1 => match position.2 {
|
||||||
|
Climbing => Neither,
|
||||||
|
Neither => Climbing,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
2 => match position.2 {
|
||||||
|
Torch => Neither,
|
||||||
|
Neither => Torch,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
_ => panic!("not a valid type"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Edge {
|
||||||
|
cost: usize,
|
||||||
|
node: Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
|
struct State {
|
||||||
|
cost: usize,
|
||||||
|
position: Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The priority queue depends on `Ord`.
|
||||||
|
// Explicitly implement the trait so the queue becomes a min-heap
|
||||||
|
// instead of a max-heap.
|
||||||
|
impl Ord for State {
|
||||||
|
fn cmp(&self, other: &State) -> Ordering {
|
||||||
|
// Notice that the we flip the ordering on costs.
|
||||||
|
// In case of a tie we compare positions - this step is necessary
|
||||||
|
// to make implementations of `PartialEq` and `Ord` consistent.
|
||||||
|
other
|
||||||
|
.cost
|
||||||
|
.cmp(&self.cost)
|
||||||
|
.then_with(|| self.position.0.cmp(&other.position.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `PartialOrd` needs to be implemented as well.
|
||||||
|
impl PartialOrd for State {
|
||||||
|
fn partial_cmp(&self, other: &State) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,3 +14,4 @@ pub mod day13;
|
|||||||
pub mod day14;
|
pub mod day14;
|
||||||
pub mod day15;
|
pub mod day15;
|
||||||
pub mod day20;
|
pub mod day20;
|
||||||
|
pub mod day22;
|
||||||
Reference in New Issue
Block a user