Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8565c50

Browse files
committed
feat(2024): Day 12 && Day 13 clean
1 parent 4dafe5d commit 8565c50

File tree

7 files changed

+390
-2
lines changed

7 files changed

+390
-2
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
RRRRIICCFF
2+
RRRRIICCCF
3+
VVRRRCCFFF
4+
VVRCCCJFFF
5+
VVVVCJJCFE
6+
VVIVCCJJEE
7+
VVIIICJJEE
8+
MIIIIIJJEE
9+
MIIISIJEEE
10+
MMMISSJEEE
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Button A: X+94, Y+34
2+
Button B: X+22, Y+67
3+
Prize: X=8400, Y=5400
4+
5+
Button A: X+26, Y+66
6+
Button B: X+67, Y+21
7+
Prize: X=12748, Y=12176
8+
9+
Button A: X+17, Y+86
10+
Button B: X+84, Y+37
11+
Prize: X=7870, Y=6450
12+
13+
Button A: X+69, Y+23
14+
Button B: X+27, Y+71
15+
Prize: X=18641, Y=10279

‎aoc_2024/src/day11.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl Solution for Day11 {
1717
}
1818

1919
fn solve(stones: &mut Stones, iterations: usize) -> u64 {
20-
for t in 0..iterations {
20+
for _ in 0..iterations {
2121
stones.blink();
2222
}
2323
stones.stones_count.values().sum::<u64>()

‎aoc_2024/src/day12.rs‎

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
use std::collections::{HashSet, VecDeque};
2+
3+
use aoc_lib::{
4+
answer::Answer,
5+
directions::{Advance, Cardinal, Direction},
6+
matrix::Matrix,
7+
solution::Solution,
8+
vec2::Vec2,
9+
};
10+
11+
pub struct Day12;
12+
13+
impl Solution for Day12 {
14+
fn part_a(&self, input: &[String]) -> Answer {
15+
let mut map = Map::from_input(input);
16+
map.fence_cost(false).into()
17+
}
18+
19+
fn part_b(&self, input: &[String]) -> Answer {
20+
let mut map = Map::from_input(input);
21+
map.fence_cost(true).into()
22+
}
23+
}
24+
25+
struct Map {
26+
map: Matrix<char>,
27+
regions: Option<Vec<Region>>,
28+
}
29+
30+
struct Region {
31+
area: HashSet<Vec2<usize>>,
32+
perimeter: usize,
33+
corners: usize,
34+
}
35+
36+
impl Region {
37+
fn new() -> Self {
38+
Self {
39+
area: HashSet::new(),
40+
perimeter: 0,
41+
corners: 0,
42+
}
43+
}
44+
}
45+
46+
impl Map {
47+
fn from_input(input: &[String]) -> Self {
48+
Self {
49+
map: Matrix::from_chars(input),
50+
regions: None,
51+
}
52+
}
53+
54+
fn compute_regions(&mut self) {
55+
let mut all = vec![];
56+
let mut visited = HashSet::new();
57+
for y in 0..self.map.rows {
58+
for x in 0..self.map.cols {
59+
let pos = Vec2::new(x, y);
60+
if !visited.contains(&pos) {
61+
let region = self.flood(pos, &mut visited);
62+
all.push(region);
63+
}
64+
}
65+
}
66+
self.regions = Some(all);
67+
}
68+
69+
fn flood(&self, initial: Vec2<usize>, visited: &mut HashSet<Vec2<usize>>) -> Region {
70+
let mut region = Region::new();
71+
let mut queue: VecDeque<Vec2<usize>> = VecDeque::new();
72+
queue.push_front(initial);
73+
visited.insert(initial);
74+
75+
while let Some(pos) = queue.pop_front() {
76+
region.area.insert(pos);
77+
78+
for direction in Cardinal::all_clockwise() {
79+
let next: Vec2<isize> = direction.advance(pos.into());
80+
81+
let Some(&next_plant_type) = self.map.get(&next) else {
82+
region.perimeter += 1;
83+
continue;
84+
};
85+
86+
if next_plant_type != self.map[pos] {
87+
region.perimeter += 1;
88+
continue;
89+
}
90+
91+
if let Ok(n) = Vec2::<usize>::try_from(next) {
92+
if !visited.contains(&n) {
93+
visited.insert(n);
94+
queue.push_back(n);
95+
}
96+
}
97+
}
98+
}
99+
region
100+
}
101+
102+
// The corners can be counted only after all the connected components of the graph
103+
// has been fully computed
104+
fn count_corners(&mut self) {
105+
for region in self.regions.as_mut().unwrap() {
106+
let area = region
107+
.area
108+
.iter()
109+
.map(|&p| Vec2::<isize>::from(p))
110+
.collect::<HashSet<_>>();
111+
for tile in &region.area {
112+
let sized_tile = Vec2::<isize>::from(tile);
113+
// Check for corners going inside exterior (Concave from the point of view of the
114+
// tile)
115+
// Exemple : You don't have front and right, you are at a corner.
116+
for direction in Cardinal::all_clockwise() {
117+
if !area.contains(&direction.advance(sized_tile))
118+
&& !area.contains(&direction.turn_right().advance(sized_tile))
119+
{
120+
region.corners += 1;
121+
}
122+
}
123+
124+
// Check for corners going inside a hole (Convex from the point of view of the tile)
125+
// A convex corner is when you have both neighbors but missing the diagonal
126+
// Exemple : You have front and right but don't have front right, you are at a
127+
// corner
128+
for direction in Cardinal::all_clockwise() {
129+
let diagonal = direction
130+
.turn_right()
131+
.advance(direction.advance(sized_tile));
132+
if area.contains(&direction.advance(sized_tile))
133+
&& area.contains(&direction.turn_right().advance(sized_tile))
134+
&& !area.contains(&diagonal)
135+
{
136+
region.corners += 1;
137+
}
138+
}
139+
}
140+
}
141+
}
142+
143+
fn fence_cost(&mut self, discount: bool) -> usize {
144+
self.compute_regions();
145+
if discount {
146+
self.count_corners();
147+
}
148+
let mut total = 0;
149+
for region in self.regions.as_ref().unwrap() {
150+
let mul = if discount {
151+
region.corners
152+
} else {
153+
region.perimeter
154+
};
155+
total += region.area.len() * mul;
156+
}
157+
total
158+
}
159+
}
160+
161+
#[cfg(test)]
162+
mod test {
163+
use aoc_lib::{answer::Answer, input, solution::Solution};
164+
165+
use super::Day12;
166+
167+
#[test]
168+
fn test_a() {
169+
let input =
170+
input::read_file(&format!("{}day_12_test.txt", crate::FILES_PREFIX_TEST)).unwrap();
171+
let answer = Day12.part_a(&input);
172+
assert_eq!(<i32 as Into<Answer>>::into(1930), answer);
173+
}
174+
175+
#[test]
176+
fn test_b() {
177+
let input =
178+
input::read_file(&format!("{}day_12_test.txt", crate::FILES_PREFIX_TEST)).unwrap();
179+
let answer = Day12.part_b(&input);
180+
assert_eq!(<i32 as Into<Answer>>::into(1206), answer);
181+
}
182+
}

‎aoc_2024/src/day13.rs‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use aoc_lib::{answer::Answer, solution::Solution, vec2::Vec2};
2+
3+
pub struct Day13;
4+
5+
impl Solution for Day13 {
6+
fn part_a(&self, input: &[String]) -> Answer {
7+
let mut machines = parse(input);
8+
solve(&mut machines, Some(100), 0).into()
9+
}
10+
11+
fn part_b(&self, input: &[String]) -> Answer {
12+
let mut machines = parse(input);
13+
solve(&mut machines, None, 10000000000000).into()
14+
}
15+
}
16+
17+
fn solve(machines: &mut [ClawMachine], press_limit: Option<usize>, shift: u64) -> u64 {
18+
let mut total = 0;
19+
for machine in machines.iter_mut() {
20+
machine.prize += Vec2::new(shift, shift);
21+
if let Some(tokens) = machine.solve(press_limit) {
22+
total += tokens;
23+
}
24+
}
25+
total
26+
}
27+
28+
struct ClawMachine {
29+
a: Vec2<u64>,
30+
b: Vec2<u64>,
31+
prize: Vec2<u64>,
32+
}
33+
34+
impl ClawMachine {
35+
/// We know from the problem statement than:
36+
/// ka * ax + kb * bx = px
37+
/// ka * ay + kb * by = py
38+
/// So we isolate the expression of ka in the first equation, and then plug it into the second
39+
/// equation.
40+
/// Then we solve and see if the solution work (because the solution should be integers and it
41+
/// is simpler to check than to handle floats -> integer casts and checks).
42+
fn solve(&self, press_limit: Option<usize>) -> Option<u64> {
43+
let (ax, bx, ay, by, px, py) = (
44+
self.a.x as i64,
45+
self.b.x as i64,
46+
self.a.y as i64,
47+
self.b.y as i64,
48+
self.prize.x as i64,
49+
self.prize.y as i64,
50+
);
51+
52+
let denom = by * ax - bx * ay;
53+
54+
if denom == 0 {
55+
return None;
56+
}
57+
58+
let kb = (ax * py - px * ay).checked_div(denom)?;
59+
let ka = (px - kb * bx).checked_div(ax)?;
60+
61+
if ka < 0 || kb < 0 {
62+
return None;
63+
}
64+
65+
if let Some(limit) = press_limit {
66+
if ka > limit as i64 || kb > limit as i64 {
67+
return None;
68+
}
69+
}
70+
71+
if ka * ax + kb * bx != px || ka * ay + kb * by != py {
72+
return None;
73+
}
74+
75+
Some(ka as u64 * 3 + kb as u64)
76+
}
77+
}
78+
79+
fn parse(input: &[String]) -> Vec<ClawMachine> {
80+
let mut machines = vec![];
81+
for machine in input
82+
.split(|line| line.is_empty())
83+
.filter(|&g| !g.is_empty())
84+
.map(|g| g.to_vec())
85+
.collect::<Vec<Vec<String>>>()
86+
{
87+
let (ax, ay) = parse_button(&machine[0]);
88+
let (bx, by) = parse_button(&machine[1]);
89+
90+
let (_, p) = machine[2].split_once(": ").unwrap();
91+
let (px, py) = p.trim().split_once(", ").unwrap();
92+
let (px, py) = (
93+
px[2..].parse::<u64>().unwrap(),
94+
py[2..].parse::<u64>().unwrap(),
95+
);
96+
97+
machines.push(ClawMachine {
98+
a: Vec2::new(ax, ay),
99+
b: Vec2::new(bx, by),
100+
prize: Vec2::new(px, py),
101+
})
102+
}
103+
machines
104+
}
105+
106+
fn parse_button(button: &str) -> (u64, u64) {
107+
let (_, b) = button.split_once(": ").unwrap();
108+
let (bx, by) = b.split_once(", ").unwrap();
109+
let (bx, by) = (
110+
bx[2..].parse::<u64>().unwrap(),
111+
by[2..].parse::<u64>().unwrap(),
112+
);
113+
(bx, by)
114+
}
115+
116+
#[cfg(test)]
117+
mod test {
118+
use aoc_lib::{answer::Answer, input, solution::Solution};
119+
120+
use super::Day13;
121+
122+
#[test]
123+
fn test_a() {
124+
let input =
125+
input::read_file(&format!("{}day_13_test.txt", crate::FILES_PREFIX_TEST)).unwrap();
126+
let answer = Day13.part_a(&input);
127+
assert_eq!(<i32 as Into<Answer>>::into(480), answer);
128+
}
129+
130+
#[test]
131+
fn test_b() {
132+
let input =
133+
input::read_file(&format!("{}day_13_test.txt", crate::FILES_PREFIX_TEST)).unwrap();
134+
let answer = Day13.part_b(&input);
135+
assert_eq!(<u64 as Into<Answer>>::into(875318608908), answer);
136+
}
137+
}

‎aoc_2024/src/lib.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub mod day08;
1111
pub mod day09;
1212
pub mod day10;
1313
pub mod day11;
14+
pub mod day12;
15+
pub mod day13;
1416

1517
pub const ALL: &[&dyn Solution] = &[
1618
&day01::Day1,
@@ -24,6 +26,8 @@ pub const ALL: &[&dyn Solution] = &[
2426
&day09::Day9,
2527
&day10::Day10,
2628
&day11::Day11,
29+
&day12::Day12,
30+
&day13::Day13,
2731
];
2832

2933
pub const FILES_PREFIX_TEST: &str = "resources/test/";

0 commit comments

Comments
 (0)