-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday10.rs
155 lines (142 loc) · 4.34 KB
/
day10.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use aoc_lib::{
answer::Answer,
directions::{Advance, Cardinal, Direction},
matrix::Matrix,
solution::Solution,
vec2::Vec2,
};
pub struct Day10;
impl Solution for Day10 {
fn part_a(&self, input: &[String]) -> Answer {
let grid = parse(input);
(grid.walk().len() / 2).into()
}
fn part_b(&self, input: &[String]) -> Answer {
let grid = parse(input);
// use of the shoelace polygon area formula
let mut path = grid.walk();
path.push(grid.start_tile.position);
let len = path.len();
let area = path
.iter()
.enumerate()
.fold(0i32, |acc, (i, p)| {
let l = (i + 1) % len;
acc + (p.x as i32 * path[l].y as i32 - p.y as i32 * path[l].x as i32)
})
.abs()
/ 2;
(area - (len as i32 / 2) + 1).into()
}
}
#[derive(Debug)]
struct Grid {
grid: Matrix<Tile>,
start_tile: Tile,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Tile {
position: Vec2<usize>,
tile: char,
}
fn parse(input: &[String]) -> Grid {
let mut grid = vec![];
for (y, row) in input.iter().enumerate() {
let mut row_buffer = vec![];
for (x, ch) in row.chars().enumerate() {
let tile = Tile {
position: Vec2::new(x, y),
tile: ch,
};
row_buffer.push(tile);
}
grid.push(row_buffer);
}
let start_tile = grid
.iter()
.flatten()
.find(|tile| tile.tile == 'S')
.unwrap()
.clone();
Grid {
grid: Matrix::<Tile>::from(grid),
start_tile,
}
}
impl Grid {
fn walk(&self) -> Vec<Vec2<usize>> {
let mut visited = vec![self.start_tile.position];
let mut current_tile = self.start_tile.clone();
loop {
let mut next_tile: Option<Tile> = None;
for direction in Cardinal::all_clockwise() {
let new_pos = direction.advance(Vec2::<isize>::from(current_tile.position));
if let Some(adjacent) = self.grid.get(&new_pos) {
if !visited.contains(&adjacent.position)
&& current_tile.is_connected_to(adjacent, direction)
{
next_tile = Some(adjacent.clone());
break;
}
}
}
match next_tile {
Some(tile) => {
current_tile = tile;
}
None => break,
}
if current_tile == self.start_tile && !visited.is_empty() {
break;
}
visited.push(current_tile.position);
}
visited
}
}
impl Tile {
fn is_connected_to(&self, other: &Tile, direction: Cardinal) -> bool {
if other.tile == '.' {
return false;
}
let directions = self.get_connections();
let other_directions = other.get_connections();
directions.contains(&direction) && other_directions.contains(&direction.opposite())
}
fn get_connections(&self) -> Vec<Cardinal> {
match self.tile {
'|' => vec![Cardinal::South, Cardinal::North],
'-' => vec![Cardinal::East, Cardinal::West],
'L' => vec![Cardinal::North, Cardinal::East],
'J' => vec![Cardinal::North, Cardinal::West],
'7' => vec![Cardinal::South, Cardinal::West],
'F' => vec![Cardinal::South, Cardinal::East],
'S' => vec![
Cardinal::North,
Cardinal::South,
Cardinal::East,
Cardinal::West,
],
_ => vec![],
}
}
}
#[cfg(test)]
mod test {
use aoc_lib::{self, answer::Answer, input, solution::Solution};
use super::Day10;
#[test]
fn test_a() {
let input =
input::read_file(&format!("{}day_10_a_test.txt", crate::FILES_PREFIX_TEST)).unwrap();
let answer = Day10.part_a(&input);
assert_eq!(<i32 as Into<Answer>>::into(4), answer);
}
#[test]
fn test_b() {
let input =
input::read_file(&format!("{}day_10_b_test.txt", crate::FILES_PREFIX_TEST)).unwrap();
let answer = Day10.part_b(&input);
assert_eq!(<i32 as Into<Answer>>::into(10), answer);
}
}