Skip to content

Commit 4698260

Browse files
committed
2024d13
1 parent 7609346 commit 4698260

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

input/2024/input13.nippy

7.68 KB
Binary file not shown.

notebooks/y2024/d13.clj

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
(ns y2024.d13
2+
(:require
3+
[clojure.core.match :as m]
4+
[numeric.expresso.core :as e]
5+
[instaparse.core :as insta]
6+
[advent-of-code-clj.input :as input]))
7+
8+
; 2024, dag 13
9+
10+
; ## Del 1
11+
12+
; I dag ser det ut til at man kan bruke matematikk for å
13+
; finne svaret på hvor mange ganger man må trykke på en av
14+
; to knapper for å få tak i belønningen.
15+
16+
; Klassisk formel med to ukjente, men å faktisk løse den er jo en
17+
; annen sak! Heldigvis har vi et bibliotek for dette.
18+
19+
; Starter med en liten overkomplisert parsing av dagens input:
20+
21+
(def test-data "Button A: X+94, Y+34
22+
Button B: X+22, Y+67
23+
Prize: X=8400, Y=5400
24+
25+
Button A: X+26, Y+66
26+
Button B: X+67, Y+21
27+
Prize: X=12748, Y=12176
28+
29+
Button A: X+17, Y+86
30+
Button B: X+84, Y+37
31+
Prize: X=7870, Y=6450
32+
33+
Button A: X+69, Y+23
34+
Button B: X+27, Y+71
35+
Prize: X=18641, Y=10279")
36+
37+
; Instaparse lar oss definere en parser for problemet:
38+
39+
(def game-parser
40+
(insta/parser "game = button <ws> button <ws> prize
41+
button = <'Button '> type <': X+'> num <', Y+'> num
42+
prize = <'Prize: X='> num <', Y='> num
43+
type = 'A'|'B'
44+
ws = #'[\\s\\n]*'
45+
num = #'\\d+'"))
46+
47+
(insta/parse game-parser (first (.split test-data "\n\n")))
48+
49+
; Og core.match lar oss pattern-matche på parset output for å
50+
; sette sammen dataene slik vi vil:
51+
52+
(defn game-map [game]
53+
(m/match game
54+
[:game button-a button-b prize] {:a (game-map button-a)
55+
:b (game-map button-b)
56+
:prize (game-map prize)}
57+
[:button [:type _] [:num x] [:num y]] {:x (parse-long x)
58+
:y (parse-long y)}
59+
[:prize [:num x] [:num y]] {:x (parse-long x) :y (parse-long y)}))
60+
61+
(defn get-games [input]
62+
(map #(game-map (insta/parse game-parser %))
63+
(.. input trim (split "\n\n"))))
64+
65+
(get-games test-data)
66+
67+
; For å løse spillet bruker vi expresso for å sette opp to
68+
; formler (løse for x-aksen og y-aksen):
69+
70+
(defn solve-game [{px :x py :y} {ax :x ay :y} {bx :x by :y}]
71+
(first (e/solve ['a 'b]
72+
(e/ex (= ~px (+ (* ~ax a) (* ~bx b))))
73+
(e/ex (= ~py (+ (* ~ay a) (* ~by b)))))))
74+
75+
(let [{:keys [a b prize]} (first (get-games test-data))]
76+
(solve-game prize a b))
77+
78+
; Med løsningsfunksjonen i hånd kan vi lage reduceren som løser
79+
; del 1. I del 1 er det ikke lov å trykke mer enn 100 ganger
80+
; på hver knapp, og det er ikke lov med "halve trykk", så vi
81+
; filtrerer vekk løsninger som gir ratio (feks `1/3`):
82+
83+
(defn part-1 [acc {:keys [prize a b]}]
84+
(let [{a 'a b 'b} (solve-game prize a b)]
85+
(if (or (< 100 a) (< 100 b)
86+
(ratio? a) (ratio? b))
87+
acc
88+
(+ acc (* a 3) b))))
89+
90+
(defn solve [solver input]
91+
(reduce solver 0
92+
(get-games input)))
93+
94+
; Nå kan vi løse del 1 for test-dataene:
95+
96+
(solve part-1 test-data)
97+
98+
; Og for reelle data:
99+
100+
(solve part-1 (input/get-input 2024 13))
101+
102+
; # Del 2
103+
104+
; I del 2 er belønningen i en posisjon som er `10000000000000`
105+
; lenger unna i hver akse!
106+
107+
; Nå er det også lov å trykke flere ganger på knappene, som er
108+
; reflektert i den nye reduceren for del 2:
109+
110+
(defn part-2 [acc {:keys [prize a b]}]
111+
(let [bigger-prize (-> prize
112+
(update :x + 10000000000000)
113+
(update :y + 10000000000000))
114+
{a 'a b 'b} (solve-game bigger-prize a b)]
115+
(if (or (ratio? a) (ratio? b))
116+
acc
117+
(+ acc (* a 3) b))))
118+
119+
; Ellers kan vi løse oppgaven på samme måte som i del 1 for test-input:
120+
121+
(solve part-2 test-data)
122+
123+
; Og faktiske input:
124+
125+
(solve part-2 (input/get-input 2024 13))

0 commit comments

Comments
 (0)