tangled
alpha
login
or
join now
bwc9876.dev
/
advent
0
fork
atom
Advent of Code solutions
0
fork
atom
overview
issues
pulls
pipelines
Day 12,13,14
bwc9876.dev
1 year ago
ad295a7e
56ee21ce
verified
This commit was signed with the committer's
known signature
.
bwc9876.dev
SSH Key Fingerprint:
SHA256:DanMEP/RNlSC7pAVbnXO6wzQV00rqyKj053tz4uH5gQ=
+424
-22
9 changed files
expand all
collapse all
unified
split
.gitignore
macros
src
lib.rs
utils
src
dir.rs
grid.rs
years
2024
src
da_tree.txt
day_11.rs
day_12.rs
day_13.rs
day_14.rs
+4
.gitignore
···
1
1
target
2
2
scratch/
3
3
result
4
4
+
5
5
+
# For 2024 Day 14 - I am in hell
6
6
+
trees*.txt
7
7
+
+1
-1
macros/src/lib.rs
···
6
6
7
7
fn make_day_mods() -> String {
8
8
(1..=MAX_DAY)
9
9
-
.map(|day| format!("mod day_{day};", day = day))
9
9
+
.map(|day| format!("pub mod day_{day};", day = day))
10
10
.collect::<Vec<_>>()
11
11
.join("\n")
12
12
}
+4
utils/src/dir.rs
···
127
127
(Self::West, false) => Self::South,
128
128
}
129
129
}
130
130
+
131
131
+
pub fn is_horizontal(&self) -> bool {
132
132
+
matches!(self, Direction::East | Direction::West)
133
133
+
}
130
134
}
131
135
132
136
impl From<Position> for Direction {
+9
utils/src/grid.rs
···
434
434
.filter_map(move |(pos, dir)| self.get(pos).map(|v| (dir, pos, v)))
435
435
}
436
436
437
437
+
pub fn relatives_valid<'a, M: Movement>(
438
438
+
&'a self,
439
439
+
pos: Position,
440
440
+
kernels: &'a [M],
441
441
+
) -> impl Iterator<Item = (M, Option<(Position, &'a T)>)> + 'a {
442
442
+
pos.relatives(kernels)
443
443
+
.map(move |(pos, dir)| (dir, self.get(pos).map(|v| (pos, v))))
444
444
+
}
445
445
+
437
446
/// Get all positions relative to the given position, returning [None] if the relatives would
438
447
/// go out of bounds
439
448
///
+33
years/2024/src/da_tree.txt
···
1
1
+
.*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.*
2
2
+
.*X_____________________________X.*
3
3
+
.*X_____________________________X.*
4
4
+
.*X_____________________________X.*
5
5
+
.*X_____________________________X.*
6
6
+
.*X______________X______________X.*
7
7
+
.*X_____________XXX_____________X.*
8
8
+
.*X____________XXXXX____________X.*
9
9
+
.*X___________XXXXXXX___________X.*
10
10
+
.*X__________XXXXXXXXX__________X.*
11
11
+
.*X____________XXXXX____________X.*
12
12
+
.*X___________XXXXXXX___________X.*
13
13
+
.*X__________XXXXXXXXX__________X.*
14
14
+
.*X_________XXXXXXXXXXX_________X.*
15
15
+
.*X________XXXXXXXXXXXXX________X.*
16
16
+
.*X__________XXXXXXXXX__________X.*
17
17
+
.*X_________XXXXXXXXXXX_________X.*
18
18
+
.*X________XXXXXXXXXXXXX________X.*
19
19
+
.*X_______XXXXXXXXXXXXXXX_______X.*
20
20
+
.*X______XXXXXXXXXXXXXXXXX______X.*
21
21
+
.*X________XXXXXXXXXXXXX________X.*
22
22
+
.*X_______XXXXXXXXXXXXXXX_______X.*
23
23
+
.*X______XXXXXXXXXXXXXXXXX______X.*
24
24
+
.*X_____XXXXXXXXXXXXXXXXXXX_____X.*
25
25
+
.*X____XXXXXXXXXXXXXXXXXXXXX____X.*
26
26
+
.*X_____________XXX_____________X.*
27
27
+
.*X_____________XXX_____________X.*
28
28
+
.*X_____________XXX_____________X.*
29
29
+
.*X_____________________________X.*
30
30
+
.*X_____________________________X.*
31
31
+
.*X_____________________________X.*
32
32
+
.*X_____________________________X.*
33
33
+
.*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.*
+2
-2
years/2024/src/day_11.rs
···
5
5
6
6
pub struct Day11;
7
7
8
8
-
fn do_blinks(stones: Vec<usize>, blinks: usize) -> usize {
8
8
+
pub fn do_blinks(stones: Vec<usize>, blinks: usize) -> usize {
9
9
let l = stones.len();
10
10
let mut stone_map = stones
11
11
.into_iter()
12
12
.fold(HashMap::with_capacity(l), |mut acc, stone| {
13
13
-
acc.entry(stone).and_modify(|c| *c += 1).or_insert(1);
13
13
+
*acc.entry(stone).or_insert(0) += 1;
14
14
acc
15
15
});
16
16
+170
-6
years/2024/src/day_12.rs
···
1
1
+
use std::collections::HashSet;
1
2
2
2
-
use advent_core::{Day, day_stuff, ex_for_day};
3
3
+
use advent_core::{day_stuff, ex_for_day, Day};
4
4
+
use utils::{
5
5
+
dir::{Direction, Movement, CARDINALS},
6
6
+
pos::Position,
7
7
+
prelude::GridCursor,
8
8
+
};
3
9
4
10
pub struct Day12;
5
11
12
12
+
type Grid = utils::grid::Grid<char>;
13
13
+
14
14
+
fn trace_perim(
15
15
+
grid: &Grid,
16
16
+
starting_pos: Position,
17
17
+
initial_dir: Direction,
18
18
+
c: char,
19
19
+
) -> (usize, HashSet<Position>) {
20
20
+
let mut perims = HashSet::with_capacity(100);
21
21
+
let mut turns = 0;
22
22
+
let mut curs = GridCursor::new(&grid, starting_pos, initial_dir);
23
23
+
let mut init = false;
24
24
+
25
25
+
while (curs.pos, curs.dir) != (starting_pos, initial_dir) || !init {
26
26
+
perims.insert(curs.pos);
27
27
+
28
28
+
let look_left = curs.pos.add(&curs.dir.ninety_deg(false).get_kernel());
29
29
+
30
30
+
if grid.get(look_left).is_some_and(|c2| *c2 == c) {
31
31
+
// Interior corner, turn left!
32
32
+
curs.turn(false);
33
33
+
curs.move_forward();
34
34
+
turns += 1;
35
35
+
} else if curs.peek_forward().is_none_or(|(_, c2)| *c2 != c) {
36
36
+
// Exterior corner, turn right!
37
37
+
curs.turn(true);
38
38
+
turns += 1;
39
39
+
} else {
40
40
+
// Valid, keep going!
41
41
+
curs.move_forward();
42
42
+
}
43
43
+
44
44
+
init = true;
45
45
+
}
46
46
+
47
47
+
(turns, perims)
48
48
+
}
49
49
+
6
50
impl Day for Day12 {
51
51
+
fn part_1(input: Self::Input) -> Option<String> {
52
52
+
let mut visited = HashSet::with_capacity(50);
53
53
+
let mut total = 0;
7
54
8
8
-
day_stuff!(12, "", "");
55
55
+
for (pos, c) in input.iter() {
56
56
+
if !visited.contains(&pos) {
57
57
+
let mut flood = vec![pos];
9
58
10
10
-
fn part_1(_input: Self::Input) -> Option<String> {
11
11
-
None
59
59
+
let (mut area, mut perim) = (0, 0);
60
60
+
61
61
+
while !flood.is_empty() {
62
62
+
let mut next = vec![];
63
63
+
64
64
+
for pos2 in flood.iter() {
65
65
+
if let Some(c2) = input.get(*pos2)
66
66
+
&& c2 == c
67
67
+
&& !visited.contains(pos2)
68
68
+
{
69
69
+
area += 1;
70
70
+
let mut rel = input
71
71
+
.relatives(*pos2, &CARDINALS)
72
72
+
.filter_map(
73
73
+
|(_, pos3, c3)| if c2 == c3 { Some(pos3) } else { None },
74
74
+
)
75
75
+
.collect::<Vec<_>>();
76
76
+
77
77
+
if rel.len() != 4 {
78
78
+
perim += 4 - rel.len();
79
79
+
}
80
80
+
81
81
+
visited.insert(*pos2);
82
82
+
next.append(&mut rel);
83
83
+
}
84
84
+
}
85
85
+
86
86
+
flood = next;
87
87
+
}
88
88
+
89
89
+
println!("{c} @ {pos}: area = {area}; perim = {perim};");
90
90
+
total += area * perim;
91
91
+
92
92
+
visited.insert(pos);
93
93
+
}
94
94
+
}
95
95
+
96
96
+
Some(total.to_string())
12
97
}
13
98
14
14
-
fn part_2(_input: Self::Input) -> Option<String> {
15
15
-
None
99
99
+
// TODO: Still working on the ""nice"" way of doing this one
100
100
+
fn part_2(input: Self::Input) -> Option<String> {
101
101
+
let mut visited = HashSet::with_capacity(50);
102
102
+
let mut shapes = Vec::<(usize, usize, HashSet<Position>)>::with_capacity(30);
103
103
+
104
104
+
for (pos, c) in input.iter() {
105
105
+
if !visited.contains(&pos) {
106
106
+
let (turns, perimeters) = trace_perim(&input, pos, Direction::East, *c);
107
107
+
108
108
+
let mut all_tiles = HashSet::with_capacity(turns);
109
109
+
110
110
+
let mut flood = vec![pos];
111
111
+
112
112
+
while !flood.is_empty() {
113
113
+
let mut next = vec![];
114
114
+
115
115
+
for pos2 in flood.iter() {
116
116
+
if let Some(c2) = input.get(*pos2)
117
117
+
&& c2 == c
118
118
+
&& !visited.contains(pos2)
119
119
+
{
120
120
+
all_tiles.insert(*pos2);
121
121
+
122
122
+
let mut rel = input
123
123
+
.relatives(*pos2, &CARDINALS)
124
124
+
.filter_map(
125
125
+
|(_, pos3, c3)| if c2 == c3 { Some(pos3) } else { None },
126
126
+
)
127
127
+
.collect::<Vec<_>>();
128
128
+
129
129
+
if !perimeters.contains(pos2) {
130
130
+
let _inner_shape = if input
131
131
+
.get(pos2.add(&Direction::East.get_kernel()))
132
132
+
.is_some_and(|c2| c2 != c)
133
133
+
{
134
134
+
Some(trace_perim(&input, *pos2, Direction::North, *c))
135
135
+
} else if input
136
136
+
.get(pos2.add(&Direction::North.get_kernel()))
137
137
+
.is_some_and(|c2| c2 != c)
138
138
+
{
139
139
+
Some(trace_perim(&input, *pos2, Direction::East, *c))
140
140
+
} else {
141
141
+
None
142
142
+
};
143
143
+
}
144
144
+
145
145
+
visited.insert(*pos2);
146
146
+
next.append(&mut rel);
147
147
+
}
148
148
+
}
149
149
+
150
150
+
flood = next;
151
151
+
}
152
152
+
153
153
+
shapes.push((all_tiles.len(), turns, all_tiles))
154
154
+
}
155
155
+
}
156
156
+
157
157
+
let _ = shapes
158
158
+
.iter()
159
159
+
.inspect(|(a, s, p)| {
160
160
+
println!(
161
161
+
"{:?}: {a}; {s}",
162
162
+
input.get(*p.iter().next().unwrap()).unwrap()
163
163
+
)
164
164
+
})
165
165
+
.collect::<Vec<_>>();
166
166
+
167
167
+
Some(
168
168
+
shapes
169
169
+
.into_iter()
170
170
+
.map(|(area, turns, _)| area * turns)
171
171
+
.sum::<usize>()
172
172
+
.to_string(),
173
173
+
)
174
174
+
}
175
175
+
176
176
+
day_stuff!(12, "", "", Grid);
177
177
+
178
178
+
fn parse_input(input: &str) -> Self::Input {
179
179
+
Grid::parse(input.trim())
16
180
}
17
181
}
+97
-7
years/2024/src/day_13.rs
···
1
1
+
use advent_core::{day_stuff, ex_for_day, Day};
1
2
2
2
-
use advent_core::{Day, day_stuff, ex_for_day};
3
3
+
pub struct Day13;
4
4
+
5
5
+
#[derive(Debug, Clone, Copy)]
6
6
+
pub struct Machine {
7
7
+
a: (isize, isize),
8
8
+
b: (isize, isize),
9
9
+
goal: (isize, isize),
10
10
+
}
3
11
4
4
-
pub struct Day13;
12
12
+
impl Machine {
13
13
+
pub fn parse(raw: &str) -> Self {
14
14
+
let mut l = raw.lines();
15
15
+
let (ax, ay) = l
16
16
+
.next()
17
17
+
.unwrap()
18
18
+
.split_once(": ")
19
19
+
.unwrap()
20
20
+
.1
21
21
+
.split_once(", ")
22
22
+
.unwrap();
23
23
+
let (bx, by) = l
24
24
+
.next()
25
25
+
.unwrap()
26
26
+
.split_once(": ")
27
27
+
.unwrap()
28
28
+
.1
29
29
+
.split_once(", ")
30
30
+
.unwrap();
31
31
+
let (ax, ay) = (
32
32
+
ax.split_once("+").unwrap().1.parse::<isize>().unwrap(),
33
33
+
ay.split_once("+").unwrap().1.parse::<isize>().unwrap(),
34
34
+
);
35
35
+
36
36
+
let (bx, by) = (
37
37
+
bx.split_once("+").unwrap().1.parse::<isize>().unwrap(),
38
38
+
by.split_once("+").unwrap().1.parse::<isize>().unwrap(),
39
39
+
);
40
40
+
41
41
+
let (px, py) = l
42
42
+
.next()
43
43
+
.unwrap()
44
44
+
.split_once(": ")
45
45
+
.unwrap()
46
46
+
.1
47
47
+
.split_once(", ")
48
48
+
.unwrap();
49
49
+
let (px, py) = (
50
50
+
px.split_once("=").unwrap().1.parse::<isize>().unwrap(),
51
51
+
py.split_once("=").unwrap().1.parse::<isize>().unwrap(),
52
52
+
);
53
53
+
54
54
+
Self {
55
55
+
a: (ax, ay),
56
56
+
b: (bx, by),
57
57
+
goal: (px, py),
58
58
+
}
59
59
+
}
60
60
+
61
61
+
pub fn presses_needed_for_prizes(&self) -> Option<isize> {
62
62
+
let a_presses = (self.b.0 * self.goal.1 - self.b.1 * self.goal.0) as f64
63
63
+
/ (self.b.0 * self.a.1 - self.b.1 * self.a.0) as f64;
64
64
+
65
65
+
let b_left = self.goal.0 as f64 - self.a.0 as f64 * a_presses;
66
66
+
let b_presses = b_left / self.b.0 as f64;
67
67
+
68
68
+
if a_presses % 1.0 == 0.0 && b_presses % 1.0 == 0.0 {
69
69
+
Some((3.0 * a_presses + b_presses) as isize)
70
70
+
} else {
71
71
+
None
72
72
+
}
73
73
+
}
74
74
+
}
5
75
6
76
impl Day for Day13 {
77
77
+
day_stuff!(13, "", "", Vec<Machine>);
7
78
8
8
-
day_stuff!(13, "", "");
79
79
+
fn part_1(input: Self::Input) -> Option<String> {
80
80
+
Some(
81
81
+
input
82
82
+
.iter()
83
83
+
.filter_map(Machine::presses_needed_for_prizes)
84
84
+
.sum::<isize>()
85
85
+
.to_string(),
86
86
+
)
87
87
+
}
9
88
10
10
-
fn part_1(_input: Self::Input) -> Option<String> {
11
11
-
None
89
89
+
fn part_2(mut input: Self::Input) -> Option<String> {
90
90
+
input.iter_mut().for_each(|m| {
91
91
+
m.goal.0 += 10000000000000;
92
92
+
m.goal.1 += 10000000000000;
93
93
+
});
94
94
+
95
95
+
Some(
96
96
+
input
97
97
+
.iter()
98
98
+
.filter_map(Machine::presses_needed_for_prizes)
99
99
+
.sum::<isize>()
100
100
+
.to_string(),
101
101
+
)
12
102
}
13
103
14
14
-
fn part_2(_input: Self::Input) -> Option<String> {
15
15
-
None
104
104
+
fn parse_input(input: &str) -> Self::Input {
105
105
+
input.split("\n\n").map(Machine::parse).collect()
16
106
}
17
107
}
+104
-6
years/2024/src/day_14.rs
···
1
1
+
use std::{cmp::Ordering, collections::HashSet};
1
2
2
2
-
use advent_core::{Day, day_stuff, ex_for_day};
3
3
+
use advent_core::{day_stuff, ex_for_day, Day};
4
4
+
use regex::Regex;
5
5
+
use utils::{ipos, pos::Position};
3
6
4
7
pub struct Day14;
5
8
9
9
+
fn robot_go(pos: Position, vel: Position, times: isize, bounds: Position) -> Position {
10
10
+
let new_pos = pos.add(&vel.multiply_comp(times));
11
11
+
let x_r = new_pos.x % bounds.x;
12
12
+
let y_r = new_pos.y % bounds.y;
13
13
+
ipos!(
14
14
+
if x_r < 0 { x_r + bounds.x } else { x_r },
15
15
+
if y_r < 0 { y_r + bounds.y } else { y_r }
16
16
+
)
17
17
+
}
18
18
+
6
19
impl Day for Day14 {
20
20
+
day_stuff!(14, "", "", Vec<(Position, Position)>);
7
21
8
8
-
day_stuff!(14, "", "");
22
22
+
fn part_1(input: Self::Input) -> Option<String> {
23
23
+
let bounds = Position::new(101, 103);
24
24
+
let times = 100;
25
25
+
let (ur, ul, ll, lr) = input
26
26
+
.into_iter()
27
27
+
.map(move |(pos, vel)| robot_go(pos, vel, times, bounds))
28
28
+
.fold((0, 0, 0, 0), move |mut acc, robo| {
29
29
+
let is_upper = match robo.y.cmp(&(bounds.y / 2)) {
30
30
+
Ordering::Equal => None,
31
31
+
Ordering::Greater => Some(false),
32
32
+
Ordering::Less => Some(true),
33
33
+
};
34
34
+
let is_left = match robo.x.cmp(&(bounds.x / 2)) {
35
35
+
Ordering::Equal => None,
36
36
+
Ordering::Greater => Some(false),
37
37
+
Ordering::Less => Some(true),
38
38
+
};
39
39
+
if let (Some(is_upper), Some(is_left)) = (is_upper, is_left) {
40
40
+
let to_inc = match (is_upper, is_left) {
41
41
+
(true, true) => &mut acc.1,
42
42
+
(true, false) => &mut acc.0,
43
43
+
(false, true) => &mut acc.2,
44
44
+
(false, false) => &mut acc.3,
45
45
+
};
46
46
+
47
47
+
*to_inc += 1;
48
48
+
}
49
49
+
acc
50
50
+
});
9
51
10
10
-
fn part_1(_input: Self::Input) -> Option<String> {
11
11
-
None
52
52
+
Some((dbg!(ur) * dbg!(ul) * dbg!(ll) * dbg!(lr)).to_string())
12
53
}
13
54
14
14
-
fn part_2(_input: Self::Input) -> Option<String> {
15
15
-
None
55
55
+
fn part_2(input: Self::Input) -> Option<String> {
56
56
+
let bounds = Position::new(101, 103);
57
57
+
58
58
+
let re = Regex::new(include_str!("da_tree.txt")).unwrap();
59
59
+
60
60
+
for i in 0..i32::MAX {
61
61
+
let bots = input
62
62
+
.iter()
63
63
+
.map(move |r| robot_go(r.0, r.1, i as isize, bounds))
64
64
+
.collect::<HashSet<_>>();
65
65
+
66
66
+
let hay = (0..bounds.y)
67
67
+
.flat_map(|y| {
68
68
+
let bots = &bots;
69
69
+
(0..bounds.x)
70
70
+
.map(move |x| {
71
71
+
let pos = Position::new(x, y);
72
72
+
if bots.contains(&pos) {
73
73
+
'X'
74
74
+
} else {
75
75
+
'_'
76
76
+
}
77
77
+
})
78
78
+
.chain(['\n'])
79
79
+
})
80
80
+
.collect::<String>();
81
81
+
82
82
+
if re.is_match(&hay) {
83
83
+
return Some(i.to_string());
84
84
+
}
85
85
+
}
86
86
+
87
87
+
Some("FUCK".to_string())
88
88
+
}
89
89
+
90
90
+
fn parse_input(input: &str) -> Self::Input {
91
91
+
input
92
92
+
.trim()
93
93
+
.lines()
94
94
+
.map(|l| {
95
95
+
let (pr, vr) = l.split_once(" ").unwrap();
96
96
+
let p = pr
97
97
+
.split_once("=")
98
98
+
.unwrap()
99
99
+
.1
100
100
+
.split(",")
101
101
+
.map(|s| s.parse::<isize>().unwrap())
102
102
+
.collect::<Vec<_>>();
103
103
+
let v = vr
104
104
+
.split_once("=")
105
105
+
.unwrap()
106
106
+
.1
107
107
+
.split(",")
108
108
+
.map(|s| s.parse::<isize>().unwrap())
109
109
+
.collect::<Vec<_>>();
110
110
+
111
111
+
(Position::new(p[0], p[1]), Position::new(v[0], v[1]))
112
112
+
})
113
113
+
.collect()
16
114
}
17
115
}