aboutsummaryrefslogtreecommitdiffstats
path: root/day12
diff options
context:
space:
mode:
authorOrfeas <38209077+0xfea5@users.noreply.github.com>2023-12-14 06:37:53 +0200
committerOrfeas <38209077+0xfea5@users.noreply.github.com>2025-10-28 23:20:45 +0200
commitdc94337dce312e771169577045d92bd2e06c68a1 (patch)
tree7a90dd533961b6ddc504e01adc8a56d896f9d9f6 /day12
parentday11 (diff)
downloadaoc23-dc94337dce312e771169577045d92bd2e06c68a1.tar.gz
aoc23-dc94337dce312e771169577045d92bd2e06c68a1.zip
day12
Diffstat (limited to 'day12')
-rw-r--r--day12/example.txt6
-rw-r--r--day12/solution.zig199
2 files changed, 205 insertions, 0 deletions
diff --git a/day12/example.txt b/day12/example.txt
new file mode 100644
index 0000000..e925935
--- /dev/null
+++ b/day12/example.txt
@@ -0,0 +1,6 @@
1???.### 1,1,3
2.??..??...?##. 1,1,3
3?#?#?#?#?#?#?#? 1,3,1,6
4????.#...#... 4,1,1
5????.######..#####. 1,6,5
6?###???????? 3,2,1
diff --git a/day12/solution.zig b/day12/solution.zig
new file mode 100644
index 0000000..bb335ba
--- /dev/null
+++ b/day12/solution.zig
@@ -0,0 +1,199 @@
1const std = @import("std");
2const print = std.debug.print;
3const assert = std.debug.assert;
4const ArrayList = std.ArrayList;
5const HashMap = std.HashMap;
6const mem = std.mem;
7
8const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace);
9var gpa = std.heap.GeneralPurposeAllocator(.{}){};
10const allocator = gpa.allocator();
11
12const Record = struct {
13 springs: []const u8,
14 arrangement: []const u64,
15};
16
17const State = struct {
18 i: u8,
19 j: u8,
20 streak: u8,
21 prev: u8,
22
23 pub fn print(self: @This(), springs: []const u8, arrangement: []const u64) void {
24 std.debug.print("streak = {d}, prev = {c} springs = '{s}' | ", .{ self.streak, self.prev, springs[self.i..] });
25 if (self.j <= arrangement.len) {
26 for (arrangement[self.j..]) |arr| {
27 std.debug.print("{d}, ", .{arr});
28 }
29 std.debug.print("\n", .{});
30 }
31 }
32
33 pub const ctx = struct {
34 pub fn hash(_: @This(), s: State) u64 {
35 return (@as(u32, s.i) << 24 | @as(u32, s.j) << 16 | @as(u32, s.prev) << 8 | s.streak);
36 }
37
38 pub fn eql(_: @This(), lhs: State, rhs: State) bool {
39 return lhs.i == rhs.i and
40 lhs.j == rhs.j and
41 lhs.prev == rhs.prev and
42 lhs.streak == rhs.streak;
43 }
44 };
45};
46
47const DPHashMap = HashMap(State, u64, State.ctx, 80);
48
49fn evaluate(dp: *DPHashMap, springs: []const u8, arrangement: []const u64, state: State) u64 {
50 var ans: u64 = 0;
51 var prev = state.prev;
52 var streak = state.streak;
53 var j = state.j;
54
55 if (dp.get(state)) |val| {
56 return val;
57 }
58
59 defer {
60 dp.put(state, ans) catch unreachable;
61 }
62
63 if (j > arrangement.len or
64 j == arrangement.len and streak > 0 or
65 j < arrangement.len and streak > arrangement[j])
66 {
67 ans = 0;
68 return 0;
69 }
70
71 for (springs[state.i..], state.i..) |c, i| {
72 switch (c) {
73 '?' => {
74 // Assume current = '.'
75 const e1 = if (prev == '.')
76 evaluate(dp, springs, arrangement, State{
77 .i = @as(u8, @intCast(i)) + 1,
78 .j = j,
79 .streak = 0,
80 .prev = '.',
81 })
82 else if (j < arrangement.len and streak == arrangement[j])
83 evaluate(dp, springs, arrangement, State{
84 .i = @as(u8, @intCast(i)) + 1,
85 .j = j + 1,
86 .streak = 0,
87 .prev = '.',
88 })
89 else
90 0;
91
92 // Assume current = '#'
93 const e2 = evaluate(dp, springs, arrangement, State{
94 .i = @as(u8, @intCast(i)) + 1,
95 .j = j,
96 .streak = streak + 1,
97 .prev = '#',
98 });
99
100 ans = e1 + e2;
101 return ans;
102 },
103 '#' => {
104 streak += 1;
105 },
106 '.' => {
107 if (prev == '#') {
108 if (j < arrangement.len and streak != arrangement[j] or j >= arrangement.len) {
109 ans = 0;
110 return ans;
111 }
112 j += 1;
113 }
114 streak = 0;
115 },
116 else => unreachable,
117 }
118
119 prev = c;
120 }
121
122 ans = if (j >= arrangement.len and streak == 0 or
123 j == arrangement.len - 1 and streak == arrangement[j])
124 1
125 else
126 0;
127
128 return ans;
129}
130
131pub fn solve(records: []const Record) !void {
132 var part1: u64 = 0;
133
134 for (records) |record| {
135 const state = State{
136 .i = 0,
137 .j = 0,
138 .streak = 0,
139 .prev = '.',
140 };
141 var dp = DPHashMap.init(allocator);
142 const e = evaluate(&dp, record.springs, record.arrangement, state);
143 part1 += e;
144 }
145
146 print("{d}\n", .{part1});
147
148 var part2: u64 = 0;
149
150 for (records) |record| {
151 const unfolded_springs = try allocator.alloc(u8, record.springs.len * 5 + 4);
152 _ = try std.fmt.bufPrint(unfolded_springs, "{s}?{s}?{s}?{s}?{s}", .{
153 record.springs,
154 record.springs,
155 record.springs,
156 record.springs,
157 record.springs,
158 });
159
160 var unfolded_arrangement = ArrayList(u64).init(allocator);
161 for (0..5) |_| {
162 try unfolded_arrangement.appendSlice(record.arrangement);
163 }
164
165 const state = State{
166 .i = 0,
167 .j = 0,
168 .streak = 0,
169 .prev = '.',
170 };
171 var dp = DPHashMap.init(allocator);
172 const e = evaluate(&dp, unfolded_springs, unfolded_arrangement.items, state);
173 part2 += e;
174 }
175 print("{d}\n", .{part2});
176}
177
178pub fn main() !void {
179 var splitLines = mem.splitScalar(u8, fin, '\n');
180 var records = ArrayList(Record).init(allocator);
181 defer records.deinit();
182
183 while (splitLines.next()) |line| {
184 var toks = mem.tokenizeAny(u8, line, " ,");
185
186 const springs = toks.next().?;
187 var arrangement = ArrayList(u64).init(allocator);
188
189 while (toks.next()) |tok| {
190 try arrangement.append(try std.fmt.parseInt(u64, tok, 10));
191 }
192
193 try records.append(.{
194 .springs = springs,
195 .arrangement = arrangement.items,
196 });
197 }
198 try solve(records.items);
199}