@@ -6,6 +6,7 @@ import "core:strconv"
6
6
import " core:strings"
7
7
import " core:testing"
8
8
import " core:text/match"
9
+ import " core:unicode"
9
10
10
11
main :: proc () {
11
12
data, ok := os.read_entire_file (" resources/2024/day03-input.txt" )
@@ -15,6 +16,8 @@ main :: proc() {
15
16
input := string (data)
16
17
17
18
fmt.println (part1 (input)) // 179834255
19
+ fmt.println (part2 (input)) // 80570939
20
+
18
21
}
19
22
20
23
part1 :: proc (input: string ) -> (sum: int ) {
@@ -24,15 +27,116 @@ part1 :: proc(input: string) -> (sum: int) {
24
27
matcher := match.matcher_init (input, pattern)
25
28
for s in match.matcher_gmatch (&matcher) {
26
29
ss := strings.split (s, " ," )
30
+ defer delete (ss)
27
31
sum += strconv.atoi (ss[0 ]) * strconv.atoi (ss[1 ])
28
32
}
29
- return
33
+ return sum
30
34
}
31
35
32
36
@(test)
33
37
part1_test :: proc (t: ^testing.T) {
34
- example := ` xmul(2,4)% &mul[3,7]!@^do_not_mul( 5,5)+mul(32,64]then (mul(11,8)mul(8,5))`
38
+ example := ` xmul(2,4)&mul[3,7]!^don't()_mul( 5,5)+mul(32,64](mul(11,8)undo()? mul(8,5))`
35
39
result := part1 (example)
36
- fmt.printf (" Test Result: %d\n " , result)
37
40
testing.expect_value (t, result, 161 )
38
41
}
42
+
43
+ // Helper to check for exact match of an instruction
44
+ check_instruction :: proc (text: string , pos: int ) -> (kind: enum {
45
+ None,
46
+ Do,
47
+ Dont,
48
+ Mul,
49
+ }, next_pos: int ) {
50
+ if pos >= len (text) {
51
+ return .None, pos
52
+ }
53
+
54
+ // Check for do()
55
+ if pos + 4 <= len (text) && text[pos:pos + 4 ] == " do()" {
56
+ return .Do, pos + 4
57
+ }
58
+
59
+ // Check for don't()
60
+ if pos + 7 <= len (text) && text[pos:pos + 7 ] == " don't()" {
61
+ return .Dont, pos + 7
62
+ }
63
+
64
+ // Check for mul(X,Y)
65
+ if pos + 5 <= len (text) && text[pos:pos + 4 ] == " mul(" {
66
+ new_pos := pos
67
+ new_pos += 4 // skip "mul("
68
+
69
+ // Parse first number
70
+ num1_start := new_pos
71
+ for new_pos < len (text) && unicode.is_digit (rune (text[new_pos])) {
72
+ new_pos += 1
73
+ }
74
+ if new_pos == num1_start || new_pos > num1_start + 3 { // check 1-3 digits
75
+ return .None, pos + 1
76
+ }
77
+
78
+ // Check for comma
79
+ if new_pos >= len (text) || text[new_pos] != ' ,' {
80
+ return .None, pos + 1
81
+ }
82
+ new_pos += 1
83
+
84
+ // Parse second number
85
+ num2_start := new_pos
86
+ for new_pos < len (text) && unicode.is_digit (rune (text[new_pos])) {
87
+ new_pos += 1
88
+ }
89
+ if new_pos == num2_start || new_pos > num2_start + 3 { // check 1-3 digits
90
+ return .None, pos + 1
91
+ }
92
+
93
+ // Check for closing parenthesis
94
+ if new_pos >= len (text) && text[new_pos] != ' )' {
95
+ return .None, pos + 1
96
+ }
97
+
98
+ return .Mul, new_pos + 1
99
+ }
100
+
101
+ return .None, pos + 1
102
+ }
103
+
104
+ part2 :: proc (input: string ) -> (sum: int ) {
105
+ sum = 0
106
+ mul_enabled := true
107
+ pos := 0
108
+
109
+ for pos < len (input) {
110
+ kind, new_pos := check_instruction (input, pos)
111
+
112
+ switch kind {
113
+ case .Do:
114
+ mul_enabled = true
115
+ case .Dont:
116
+ mul_enabled = false
117
+ case .Mul:
118
+ if mul_enabled {
119
+ // Extract and parse numbers
120
+ mul_str := input[pos + 4 :new_pos - 1 ]
121
+ nums := strings.split (mul_str, " ," )
122
+ defer delete (nums)
123
+ if len (nums) == 2 {
124
+ sum += strconv.atoi (nums[0 ]) * strconv.atoi (nums[1 ])
125
+ }
126
+ }
127
+ case .None:
128
+ // Continue to next character
129
+ }
130
+
131
+ pos = new_pos
132
+ }
133
+
134
+ return sum
135
+ }
136
+
137
+ @(test)
138
+ part2_test :: proc (t: ^testing.T) {
139
+ example := ` xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))`
140
+ result := part2 (example)
141
+ testing.expect_value (t, result, 48 )
142
+ }
0 commit comments