1
- use std:: collections:: { HashMap , VecDeque } ;
2
-
1
+ use aoc_lib:: vector:: AsTuple2 ;
3
2
use common:: { solution, Answer } ;
4
3
use nd_vec:: { vector, Vec2 } ;
5
4
6
5
solution ! ( "Claw Contraption" , 13 ) ;
7
6
8
7
fn part_a ( input : & str ) -> Answer {
9
- let problem = Problem :: parse ( input) ;
10
- problem
11
- . cases
12
- . iter ( )
13
- . map ( |x| x. cheapest ( ) )
14
- . filter ( |& x| x != u64:: MAX )
15
- . sum :: < u64 > ( )
16
- . into ( )
8
+ Problem :: parse ( input) . solve ( ) . into ( )
17
9
}
18
10
19
11
fn part_b ( input : & str ) -> Answer {
20
- /* i used mathematica for p2
21
-
22
- out = 0;
23
- Do[
24
- x = input[[i]];
25
- solve =
26
- Solve[x[[1]][[1]] == x[[2]][[1]]*a + x[[3]][[1]]*b &&
27
- x[[1]][[2]] == x[[2]][[2]]*a + x[[3]][[2]]*b, {a, b}, Integers];
28
- result = 3*solve[[All, 1, 2]] + solve[[All, 2, 2]];
29
- out += If[Length[result] == 0, {0}, result];
30
- , {i, 1, Length[input]}];
31
- out
32
-
33
- */
34
-
35
- Answer :: Unimplemented
12
+ Problem :: parse ( input) . part_b ( ) . solve ( ) . into ( )
36
13
}
37
14
38
- #[ derive( Debug ) ]
39
15
struct Problem {
40
16
cases : Vec < Case > ,
41
17
}
42
18
43
- #[ derive( Debug ) ]
44
19
struct Case {
45
20
a_button : Vec2 < u64 > ,
46
21
b_button : Vec2 < u64 > ,
47
22
goal : Vec2 < u64 > ,
48
23
}
49
24
50
- fn parse_button ( input : & str ) -> Vec2 < u64 > {
51
- let ( _, parts) = input. rsplit_once ( ": " ) . unwrap ( ) ;
52
- let ( x, y) = parts. split_once ( ", " ) . unwrap ( ) ;
53
- vector ! ( x[ 1 ..] . parse( ) . unwrap( ) , y[ 1 ..] . parse( ) . unwrap( ) )
25
+ impl Case {
26
+ fn cheapest ( & self ) -> u64 {
27
+ let cast = |x : Vec2 < u64 > | x. try_cast :: < i64 > ( ) . unwrap ( ) . as_tuple ( ) ;
28
+ let ( ( gx, gy) , ( ax, ay) , ( bx, by) ) =
29
+ ( cast ( self . goal ) , cast ( self . a_button ) , cast ( self . b_button ) ) ;
30
+
31
+ // The best a and b values for a case are the solutions to the following
32
+ // system of equations, where g is the goal position, a/b are the number
33
+ // of times you press the corespondent buttons (what we are solving
34
+ // for), and (a|b)(x|y) are the offsets applied by each button press.
35
+ //
36
+ // gx = ax * a + bx * b
37
+ // gy = ay * a + by * b
38
+ //
39
+ // By plugging those into Wolfram Alpha, I got the below equation for a,
40
+ // then used that to find the equation of b. Because this is integer
41
+ // math, we need to verify it by making sure a and b are greater than
42
+ // zero and checking if the solution actually solves the system. If it
43
+ // does, we return 3a + b, the price.
44
+
45
+ let a = ( by * gx - bx * gy) / ( ax * by - ay * bx) ;
46
+ let b = ( gx - ax * a) / bx;
47
+
48
+ if a <= 0 || b <= 0 || self . goal != self . a_button * a as u64 + self . b_button * b as u64 {
49
+ return 0 ;
50
+ }
51
+
52
+ a as u64 * 3 + b as u64
53
+ }
54
54
}
55
55
56
56
impl Problem {
@@ -76,59 +76,22 @@ impl Problem {
76
76
Self { cases }
77
77
}
78
78
79
- fn part_b ( mut self ) -> Self {
80
- for case in self . cases . iter_mut ( ) {
81
- case. goal += vector ! ( 10000000000000 , 10000000000000 ) ;
82
- }
79
+ fn solve ( & self ) -> u64 {
80
+ self . cases . iter ( ) . map ( |x| x. cheapest ( ) ) . sum :: < u64 > ( ) . into ( )
81
+ }
83
82
83
+ fn part_b ( mut self ) -> Self {
84
+ self . cases
85
+ . iter_mut ( )
86
+ . for_each ( |case| case. goal += vector ! ( 10000000000000 , 10000000000000 ) ) ;
84
87
self
85
88
}
86
89
}
87
90
88
- impl Case {
89
- fn cheapest ( & self ) -> u64 {
90
- // a->3, b->1
91
- fn inner (
92
- case : & Case ,
93
- memo : & mut HashMap < ( Vec2 < u64 > , ( u64 , u64 ) ) , u64 > ,
94
- pos : Vec2 < u64 > ,
95
- counts : ( u64 , u64 ) ,
96
- price : u64 ,
97
- ) -> u64 {
98
- if let Some ( & cache) = memo. get ( & ( pos, counts) ) {
99
- return cache;
100
- }
101
-
102
- if pos == case. goal {
103
- return price;
104
- }
105
-
106
- if counts. 0 > 100 || counts. 1 > 100 {
107
- return u64:: MAX ;
108
- }
109
-
110
- let min = inner (
111
- case,
112
- memo,
113
- pos + case. a_button ,
114
- ( counts. 0 + 1 , counts. 1 ) ,
115
- price + 3 ,
116
- )
117
- . min ( inner (
118
- case,
119
- memo,
120
- pos + case. b_button ,
121
- ( counts. 0 , counts. 1 + 1 ) ,
122
- price + 1 ,
123
- ) ) ;
124
-
125
- memo. insert ( ( pos, counts) , min) ;
126
-
127
- min
128
- }
129
-
130
- inner ( self , & mut HashMap :: new ( ) , vector ! ( 0 , 0 ) , ( 0 , 0 ) , 0 )
131
- }
91
+ fn parse_button ( input : & str ) -> Vec2 < u64 > {
92
+ let ( _, parts) = input. rsplit_once ( ": " ) . unwrap ( ) ;
93
+ let ( x, y) = parts. split_once ( ", " ) . unwrap ( ) ;
94
+ vector ! ( x[ 1 ..] . parse( ) . unwrap( ) , y[ 1 ..] . parse( ) . unwrap( ) )
132
95
}
133
96
134
97
#[ cfg( test) ]
@@ -160,6 +123,6 @@ mod test {
160
123
161
124
#[ test]
162
125
fn part_b ( ) {
163
- assert_eq ! ( super :: part_b( CASE ) , ( ) . into( ) ) ;
126
+ assert_eq ! ( super :: part_b( CASE ) , 875318608908_u64 . into( ) ) ;
164
127
}
165
128
}
0 commit comments