Skip to content

Commit 9b9ad76

Browse files
authored
Merge pull request #12 from leifgehrmann/day12
Day 12 - Hill Climbing Algorithm ⛰
2 parents cae50f6 + d93f934 commit 9b9ad76

File tree

5 files changed

+232
-0
lines changed

5 files changed

+232
-0
lines changed

.github/workflows/Day-12.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Day-12
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
paths:
7+
- '**12*'
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v2
15+
- name: Build
16+
run: cargo build --release --verbose
17+
- name: Run
18+
run: time target/release/aoc 12

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This is primarily a learning experience, and the code may not be following best
1919
| 9 | Rope Bridge | | | |
2020
| 10 | Cathode-Ray Tube | [src/day_10.rs](src/day_10.rs) | [src/day_10.data](src/day_10.data) | `0m0.003s` | [![Day-10](https://github.com/leifgehrmann/advent-of-code-2022/actions/workflows/Day-10.yml/badge.svg?branch=main)](https://github.com/leifgehrmann/advent-of-code-2022/actions/workflows/Day-10.yml?query=branch%3Amain)
2121
| 11 | Monkey in the Middle | [src/day_11.rs](src/day_11.rs) | [src/day_11.data](src/day_11.data) | `0m0.030s` | [![Day-11](https://github.com/leifgehrmann/advent-of-code-2022/actions/workflows/Day-11.yml/badge.svg?branch=main)](https://github.com/leifgehrmann/advent-of-code-2022/actions/workflows/Day-11.yml?query=branch%3Amain)
22+
| 12 | Hill Climbing Algorithm | [src/day_12.rs](src/day_12.rs) | [src/day_12.data](src/day_12.data) | `0m0.053s` | [![Day-12](https://github.com/leifgehrmann/advent-of-code-2022/actions/workflows/Day-12.yml/badge.svg?branch=main)](https://github.com/leifgehrmann/advent-of-code-2022/actions/workflows/Day-12.yml?query=branch%3Amain)
2223

2324
_The measured execution time in GitHub Actions_
2425

src/day_12.data

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
abccccccccaaaaaaaccaaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccaaaaaa
2+
abccccccccaaaaaaaccaaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccaaaaaa
3+
abccccccccccaaaaaaccaaaaaaaaaaaaaaaaccccccccccccccccacccccccccccccccccccaaaaa
4+
abcccccaaaacaaaaaaccaaaaaaaaaaaaaaaaacccccccccccccccaaaccccaccccccccccccccaaa
5+
abccccaaaaacaaccccccaaaaaacaaacaacaaaaaaacccccccccccaaaacccaacccccccccccccaaa
6+
abaaccaaaaaaccccaaacaaaacacaaacaaccaaaaaacccccccccccaklaccccccccccccccccccaac
7+
abaaccaaaaaaccaaaaaacccccccaaacccaaaaaaaccccccccccckkkllllccccccccccccccccccc
8+
abaaccaaaaaaccaaaaaacccccccaaaaacaaaaaaacccccccccckkkklllllcccccccaaaccaccccc
9+
abacccccaacccccaaaaacccccccaaaaaccaaaaaaacccccccckkkkpppllllccccccaaaaaaccccc
10+
abacccccccccccaaaaacccccccccaaaacccaaaaaaccccccckkkkpppppplllccccddddaaaccccc
11+
abccccccccccccaaaaaccccccccccaaaccaaaccccccccccckkkppppppppllllldddddddaccccc
12+
abccacccccccccccccccccccccccccccccaaccccccccccckkkopppupppplllmmmmdddddaacccc
13+
abccaaacaaaccccccccccccccccccccaaaaaaaaccccccckkkkopuuuuupppllmmmmmmddddacccc
14+
abccaaaaaaaccccccccccccccccccccaaaaaaaacccccjjkkkooouuuuuuppqqqqqmmmmddddcccc
15+
abccaaaaaacccccccccccccccaaccccccaaaacccccjjjjjjoooouuxuuuppqqqqqqmmmmdddcccc
16+
abcaaaaaaaacccccccccccccaaacccccaaaaaccccjjjjoooooouuuxxuuvvvvvqqqqmmmdddcccc
17+
abaaaaaaaaaacccccccaaaaaaacaacccaacaaacccjjjooooouuuuxxxxvvvvvvvqqqmmmdddcccc
18+
abaaaaaaaaaacccaaacaaaaaaaaaacccacccaaccjjjooootttuuuxxxyyvyyvvvqqqmmmeeecccc
19+
abcccaaacaaacccaaaaaaacaaaaaccccccccccccjjjooottttxxxxxxyyyyyyvvqqqmmmeeccccc
20+
abcccaaacccccccaaaaaacaaaaaccccaaccaacccjjjnnntttxxxxxxxyyyyyvvvqqqnneeeccccc
21+
SbccccaacccccccaaaaaaaaacaaacccaaaaaacccjjjnnntttxxxEzzzzyyyyvvqqqnnneeeccccc
22+
abcccccccccccccaaaaaaaaacaaccccaaaaaccccjjjnnnttttxxxxyyyyyvvvrrrnnneeecccccc
23+
abcccaacccccccaaaaaaaaaccccccccaaaaaacccciiinnnttttxxxyyyyywvvrrrnnneeecccccc
24+
abcccaaaaaaccaaaaaaaacccccccccaaaaaaaaccciiiinnnttttxyyywyyywvrrrnnneeecccccc
25+
abcccaaaaaaccaaaaaaaacccccccccaaaaaaaacccciiinnnntttxwywwyyywwwrrnnneeecccccc
26+
abcaaaaaaaccaaaaaaaaaccccccccccccaacccccccciiinnnttwwwwwwwwwwwwrrnnneeecccccc
27+
abcaaaaaaaccaaaaaacccccccccccccccaaccccccaaiiiinnttwwwwwwwwwwwrrrnnnffecccccc
28+
abcccaaaaaaccaaaaaccccccccccccccccccccaaaaaciiinnssswwwssssrwwrrrnnnfffcccccc
29+
abaacaaccaaccaaaccccccccaacccccccccccccaaaaaiiinnssssssssssrrrrrronnfffcccccc
30+
abaccaaccaacccccccccaaacaacccccccccccccaaaaaiiimmmssssssmoosrrrrooonffaaacccc
31+
abaaaccccaaaaaaccccccaaaaaccccccccccccaaaaaccihmmmmsssmmmoooooooooofffaaacccc
32+
abaaaccccaaaaaacccccccaaaaaacccccccccccccaacchhhmmmmmmmmmoooooooooffffaaccccc
33+
abaacccaaaaaaaccccccaaaaaaaaccccaaccccccccccchhhhmmmmmmmgggggooofffffaaaccccc
34+
abaacccaaaaaaaccccccaaaaaaaccccaaaaccccccccccchhhhmmmmhggggggggfffffaaaaccccc
35+
abccccccaaaaaaacccccaacaaaaacccaaaaccccccccccchhhhhhhhggggggggggfffaacaaccccc
36+
abccaacccaaaaaaccccccccaaaaaccaaaaacccccccccccchhhhhhhggaaaaaaccccccccccccccc
37+
abccaaaccaaccccccccccccccaaaaaaaaaccccccccccccccchhhhaaaccaaaacccccccccccccaa
38+
abaaaaaaaccccccccccccccccaaaaaaaaccccccccccccccccccccaaaccccaaccccccccccccaaa
39+
abaaaaaaaccccccccaaaccccacaaaaaacccccccccccccccccccccaaaccccccccccccccccccaaa
40+
abaaaaaacccccccaaaaacaaaaaaaaaaacccccccccccccccccccccaaccccccccccccccccaaaaaa
41+
abaaaaaacccccccaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccccccaaaaaa

src/day_12.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use crate::input_reader;
2+
use std::cmp::Ordering;
3+
use std::collections::VecDeque;
4+
use std::collections::HashMap;
5+
use std::hash::Hash;
6+
use std::hash::Hasher;
7+
8+
type Cost = usize;
9+
10+
#[derive(Debug, Clone, Copy)]
11+
struct Pos {
12+
x: i32,
13+
y: i32,
14+
}
15+
16+
impl Hash for Pos {
17+
fn hash<H: Hasher>(&self, state: &mut H) {
18+
self.x.hash(state);
19+
self.y.hash(state);
20+
}
21+
}
22+
23+
impl PartialEq for Pos {
24+
fn eq(&self, other: &Self) -> bool {
25+
self.x == other.x && self.y == other.y
26+
}
27+
}
28+
impl Eq for Pos {}
29+
30+
#[derive(Debug, Eq)]
31+
struct Processable {
32+
pos: Pos,
33+
net_cost: Cost
34+
}
35+
36+
impl Processable {
37+
pub fn new(pos: Pos, net_cost: Cost) -> Self {
38+
Processable {
39+
pos,
40+
net_cost
41+
}
42+
}
43+
}
44+
45+
impl PartialOrd for Processable {
46+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47+
Some(self.cmp(other))
48+
}
49+
}
50+
51+
impl Ord for Processable {
52+
fn cmp(&self, other: &Self) -> Ordering {
53+
self.net_cost.cmp(&other.net_cost)
54+
}
55+
}
56+
57+
impl PartialEq for Processable {
58+
fn eq(&self, other: &Self) -> bool {
59+
self.net_cost == other.net_cost
60+
}
61+
}
62+
63+
fn get_neighbors(map: &Vec<Vec<i32>>, pos: Pos) -> Vec<Pos> {
64+
let w = map[0].len() as i32;
65+
let h = map.len() as i32;
66+
let points: Vec<(i32, i32)> = vec![(1, 0), (-1, 0), (0, 1), (0, -1)];
67+
return points.iter().map(|&point| {
68+
let new_pos = Pos { x: pos.x + point.0, y: pos.y + point.1 };
69+
if new_pos.x < 0 ||
70+
new_pos.x >= w ||
71+
new_pos.y < 0 ||
72+
new_pos.y >= h
73+
{
74+
return None;
75+
}
76+
let a = map[pos.y as usize][pos.x as usize];
77+
let b = map[new_pos.y as usize][new_pos.x as usize];
78+
if a + 1 < b {
79+
return None;
80+
}
81+
return Some(new_pos);
82+
}).filter_map(|e| e).collect()
83+
}
84+
85+
fn compute_cost(map: &Vec<Vec<i32>>, start: Pos) -> HashMap<Pos, Cost> {
86+
// A simplified version of Dijkstra.
87+
let mut processed: HashMap<Pos, Cost> = HashMap::new();
88+
let mut to_process: VecDeque<Processable> = VecDeque::new();
89+
to_process.push_back(Processable::new(start, 0));
90+
processed.insert(start, 0);
91+
92+
while to_process.len() > 0 {
93+
// Ensure elements are from least costly to most costly.
94+
to_process.make_contiguous().sort();
95+
let processable = to_process.pop_front().unwrap();
96+
for neighbor in get_neighbors(map, processable.pos) {
97+
let new_cost = processable.net_cost + 1;
98+
// Skip points that have already been processed
99+
if processed.contains_key(&neighbor) && processed[&neighbor] <= new_cost {
100+
continue;
101+
}
102+
processed.insert(neighbor, new_cost);
103+
to_process.push_back(Processable::new(neighbor, new_cost));
104+
}
105+
}
106+
return processed;
107+
}
108+
109+
fn part1(map: &Vec<Vec<i32>>, start: Pos, end: Pos) {
110+
let processed = compute_cost(&map, start);
111+
println!("Part 1: {}", processed.get(&end).unwrap());
112+
}
113+
114+
fn part2(map: &Vec<Vec<i32>>, end: Pos) {
115+
// Let's cheese this solution be observing that the only 'b' steps are
116+
// on the left hand side of the map. Therefore one can only get up
117+
// the hill from the left-most positions.
118+
let mut lowest_cost: usize = std::usize::MAX;
119+
for y in 0..map.len() {
120+
let start = Pos { x: 0, y: y as i32 };
121+
let processed = compute_cost(map, start);
122+
// println!("best 1: {}", y);
123+
let cost = *processed.get(&end).unwrap();
124+
// println!("best 1: {}", cost);
125+
if cost < lowest_cost {
126+
// println!("best 2: {}", y);
127+
lowest_cost = cost;
128+
}
129+
}
130+
131+
println!("Part 2: {}", lowest_cost);
132+
}
133+
134+
pub fn run() {
135+
let input = input_reader::read_file_in_cwd("src/day_12.data");
136+
137+
let lines: Vec<&str> = input.split("\n").collect();
138+
let mut start_y = 0;
139+
let mut start_x = 0;
140+
let mut end_y = 0;
141+
let mut end_x = 0;
142+
let mut y = 0;
143+
let mut x = 0;
144+
let map: Vec<Vec<i32>> = lines.iter().map(|&val| {
145+
y += 1;
146+
x = 0;
147+
let cells: Vec<char> = val.chars().collect();
148+
return cells.iter().map(|&val| {
149+
x += 1;
150+
// Make comparing values easier by converting special cases like S and E
151+
// as numeric values.
152+
if val == 'S' {
153+
start_x = x - 1;
154+
start_y = y - 1;
155+
return 'a' as i32 - 1
156+
} else if val == 'E' {
157+
end_x = x - 1;
158+
end_y = y - 1;
159+
return 'z' as i32 + 1
160+
}
161+
return val as i32
162+
}).collect();
163+
}).collect();
164+
165+
let start = Pos { x: start_x, y: start_y };
166+
let end = Pos { x: end_x, y: end_y };
167+
168+
part1(&map, start, end);
169+
part2(&map, end);
170+
}

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod day_08;
1010
// mod day_09;
1111
mod day_10;
1212
mod day_11;
13+
mod day_12;
1314

1415
fn main() {
1516
let day: String = std::env::args().nth(1).expect(
@@ -29,6 +30,7 @@ fn main() {
2930
// "09" => day_09::run(),
3031
"10" => day_10::run(),
3132
"11" => day_11::run(),
33+
"12" => day_12::run(),
3234
_ => println!("No valid day given. Possible options are: 01-25."),
3335
};
3436
}

0 commit comments

Comments
 (0)