--- /dev/null
+#include_file "core/std/wasi"
+
+use package core
+use package core.string.reader as reader
+
+key_arena : alloc.arena.ArenaState;
+key_alloc : Allocator;
+
+score_player :: proc (p: ^[..] u32) -> u32 {
+ count := 0;
+ mul := p.count;
+ for c: *p {
+ count += c * mul;
+ mul -= 1;
+ }
+ return count;
+}
+
+combat :: proc (player1: ^[..] u32, player2: ^[..] u32) -> u32 {
+ while player1.count > 0 && player2.count > 0 {
+ p1 := player1.data[0];
+ p2 := player2.data[0];
+ array.delete(player1, 0);
+ array.delete(player2, 0);
+
+ if p1 > p2 {
+ array.push(player1, p1);
+ array.push(player1, p2);
+ } else {
+ array.push(player2, p2);
+ array.push(player2, p1);
+ }
+ }
+
+ if player1.count > 0 do return score_player(player1);
+ return score_player(player2);
+}
+
+// Encodes a hand state such as:
+// P1: 4 5 2 8
+// P2: 1 3 9 7
+// into:
+// 4,5,2,8,|1,3,9,7,
+// Larger numbers are encoded in base 64.
+encode_hands :: proc (alloc: Allocator, p1: ^[..] u32, p2: ^[..] u32) -> str {
+ use package core.string.builder as b
+
+ builder := b.make(200, alloc);
+ for n: *p1 {
+ b.add_i64(^builder, ~~n, 64);
+ b.add_str(^builder, ",");
+ }
+ b.add_str(^builder, "|");
+ for n: *p2 {
+ b.add_i64(^builder, ~~n, 64);
+ b.add_str(^builder, ",");
+ }
+
+ return b.to_str(^builder);
+}
+
+recursive_combat :: proc (player1: ^[..] u32, player2: ^[..] u32) -> u32 {
+ hand_seen : map.Map(str, bool);
+ map.init(^hand_seen, 31);
+ defer map.free(^hand_seen);
+
+ while player1.count > 0 && player2.count > 0 {
+ enc_hand := encode_hands(key_alloc, player1, player2);
+ if map.has(^hand_seen, enc_hand) do return 1;
+ map.put(^hand_seen, enc_hand, true);
+
+ p1 := player1.data[0];
+ p2 := player2.data[0];
+ array.delete(player1, 0);
+ array.delete(player2, 0);
+
+ round_win := 0;
+
+ if player1.count >= p1 && player2.count >= p2 {
+ p1_copy := array.copy_range(player1, 0 .. p1);
+ p2_copy := array.copy_range(player2, 0 .. p2);
+ defer array.free(^p1_copy);
+ defer array.free(^p2_copy);
+
+ round_win = recursive_combat(^p1_copy, ^p2_copy);
+ } else {
+ if p1 > p2 do round_win = 1;
+ else do round_win = 2;
+ }
+
+ if round_win == 1 {
+ array.push(player1, p1);
+ array.push(player1, p2);
+ } else {
+ array.push(player2, p2);
+ array.push(player2, p1);
+ }
+ }
+
+ if player1.count == 0 do return 2;
+ return 1;
+}
+
+main :: proc (args: [] cstr) {
+ contents := file.get_contents("input/day22.txt");
+ defer string.free(contents);
+
+ file := reader.make(contents);
+
+ player1: [..] u32;
+ player2: [..] u32;
+ array.init(^player1);
+ array.init(^player2);
+ defer array.free(^player1);
+ defer array.free(^player2);
+
+ reader.advance_line(^file); // 'Player 1:'
+ while *file.data != #char "\n" {
+ card := reader.read_u32(^file);
+ array.push(^player1, card);
+
+ reader.advance_line(^file);
+ }
+
+ reader.advance_line(^file); // '\n'
+ reader.advance_line(^file); // 'Player 2:'
+ while !reader.empty(^file) {
+ card := reader.read_u32(^file);
+ array.push(^player2, card);
+
+ reader.advance_line(^file);
+ }
+
+ {
+ player1_copy := array.copy(^player1);
+ player2_copy := array.copy(^player2);
+ defer array.free(^player1_copy);
+ defer array.free(^player2_copy);
+
+ combat_score := combat(^player1_copy, ^player2_copy);
+ printf("Answer: %i\n", combat_score);
+ }
+
+ {
+ key_arena = alloc.arena.make(context.allocator, 1 << 12);
+ key_alloc = alloc.arena.make_allocator(^key_arena);
+
+ player1_copy := array.copy(^player1);
+ player2_copy := array.copy(^player2);
+ defer array.free(^player1_copy);
+ defer array.free(^player2_copy);
+
+ winner := recursive_combat(^player1_copy, ^player2_copy);
+
+ score : u32;
+ if winner == 1 do score = score_player(^player1_copy);
+ if winner == 2 do score = score_player(^player2_copy);
+
+ printf("Recursive answer: %i\n", score);
+ }
+}