diff --git a/2023/README.md b/2023/README.md
index eafbd8a..a02854f 100644
--- a/2023/README.md
+++ b/2023/README.md
@@ -7,7 +7,7 @@
 | [01] |   ✓    |   ✓    | [Zig]    |
 | [02] |   ✓    |   ✓    | [Zig]    |
 | [03] |   ✓    |   ✓    | [Zig]    |
-| [04] |   ✓    |        | [Zig]    |
+| [04] |   ✓    |   ✓    | [Zig]    |
 | [05] |        |        |          |
 | [06] |        |        |          |
 | [07] |        |        |          |
diff --git a/2023/day-04/part_two.zig b/2023/day-04/part_two.zig
new file mode 100644
index 0000000..2a7b8bf
--- /dev/null
+++ b/2023/day-04/part_two.zig
@@ -0,0 +1,67 @@
+const std = @import("std");
+const common = @import("common.zig");
+const alloc = std.heap.page_allocator;
+const List = std.ArrayList;
+const HashMap = std.AutoHashMap(usize, u8);
+
+const data = @embedFile("inputs/puzzle.txt");
+
+pub fn main() !void {
+    const stdout = std.io.getStdOut().writer();
+
+    const cards = try common.parse(data);
+    defer cards.deinit();
+
+    const last_card_id = cards.items.len - 1;
+
+    var matches = HashMap.init(alloc);
+    defer matches.deinit();
+
+    var card_stack = List(usize).init(alloc);
+    defer card_stack.deinit();
+
+    {
+        var card_id = cards.items.len;
+        var cards_iter = std.mem.reverseIterator(cards.items);
+        while (cards_iter.next()) |card| {
+            card_id -= 1;
+
+            try matches.put(card_id, 0);
+            try card_stack.append(card_id);
+
+            for (card.winning.items) |winning_num| {
+                for (card.having.items) |having_num| {
+                    if (winning_num == having_num) {
+                        if (matches.get(card_id)) |match| {
+                            try matches.put(card_id, match + 1);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    var total_cards: u32 = 0;
+
+    while (card_stack.popOrNull()) |card_id| {
+        total_cards += 1;
+
+        const card_matches = matches.get(card_id).?;
+
+        if (card_matches != 0 and card_id < last_card_id) {
+            const remaining_cards = last_card_id - card_id;
+
+            if (remaining_cards > 0) {
+                const remaining_matches = if (remaining_cards >= card_matches) card_matches else remaining_cards;
+                const new_cards_start = card_id + 1;
+                const new_cards_end = card_id + remaining_matches + 1;
+
+                for (new_cards_start..new_cards_end) |new_card_id| {
+                    try card_stack.append(new_card_id);
+                }
+            }
+        }
+    }
+
+    try stdout.print("{d}\n", .{total_cards});
+}
diff --git a/README.md b/README.md
index 304452e..cc8c34f 100644
--- a/README.md
+++ b/README.md
@@ -10,4 +10,4 @@
 -   [2020](2020/README.md) (20% completed)
 -   [2021](2021/README.md) (68% completed)
 -   [2022](2022/README.md) (54% completed)
--   [2023](2023/README.md) (14% completed)
+-   [2023](2023/README.md) (16% completed)