solve_day23 :: (test:bool) {
    contents := read_entire_file(ifx test then "inputs/day23_test.txt" else "inputs/day23.txt");
    lines := split(contents, cast(u8) #char "\n");

    max_rounds := 1500;

    map_width  := lines[0].count + max_rounds * 2;
    map_height := lines.count + max_rounds * 2;

    elves: [..]Elf;

    map_current := NewArray(map_width * map_height, u8);
    map_next := NewArray(map_width * map_height, u8);

    memset(map_current.data, 0, map_current.count);

    for line, line_index: lines {
        for 0..line.count - 1 {
            if line[it] == #char "#" {
                elf := Elf.{ pos = .{ x = it + max_rounds, y = line_index + max_rounds } };
                array_add(*elves, elf);
                map_current[elf.pos.y * map_width + elf.pos.x] = 1;
            }
        }
    }

    directions := Direction.[
        .{
            p1 = .{ -1, -1 },
            p2 = .{ 0, -1 },
            p3 = .{ 1, -1 },
        },
        .{
            p1 = .{ -1, 1 },
            p2 = .{ 0, 1 },
            p3 = .{ 1, 1 },
        },
        .{
            p1 = .{ -1, 1 },
            p2 = .{ -1, 0 },
            p3 = .{ -1, -1 },
        },
        .{
            p1 = .{ 1, -1 },
            p2 = .{ 1, 0 },
            p3 = .{ 1, 1 },
        },
    ];

    l := NewArray(map_width * map_height, u8);

    memset(l.data, cast(u8) #char ".", l.count);

    for elf: elves {
        assert(l[elf.pos.y * map_width + elf.pos.x] != #char "#");
        l[elf.pos.y * map_width + elf.pos.x] = #char "#";
    }

    look_at_elf := -2;
    part1 := 0;
    part2 := 0;

    for round: 0..max_rounds {
        no_move := 0;
        memset(map_next.data, 0, map_width * map_height);
        for *elf, elf_i: elves {

            elf.maybe_move_to = elf.pos;

            found := false;

            for x: -1..1 {
                for y: -1..1 {
                    if x == 0 && y == 0  continue;
                    if map_current[(elf.pos.y + y) * map_width + elf.pos.x + x] == 1 {
                        found = true;
                        break x;
                    }
                }
            }

            if !found {
                no_move += 1;
                continue;
            }

            found = false;

            if elf_i == look_at_elf  print("%\n", map_current[(elf.pos.y - 1) * map_width + elf.pos.x]);
            for 0..3 {
                dir := directions[(round + it) % 4];
                cell1 := map_current[(elf.pos.y + dir.p1.y) * map_width + elf.pos.x + dir.p1.x];
                cell2 := map_current[(elf.pos.y + dir.p2.y) * map_width + elf.pos.x + dir.p2.x];
                cell3 := map_current[(elf.pos.y + dir.p3.y) * map_width + elf.pos.x + dir.p3.x];

                if elf_i == look_at_elf print("Round before: %, %, %\n", cell1, cell2, cell3);

                if cell1 == 0 && cell2 == 0 && cell3 == 0 {
                    elf.maybe_move_to = elf.pos + dir.p2;
                    if elf_i == look_at_elf  print("%, %\n", elf.pos, elf.maybe_move_to);

                    map_next[elf.maybe_move_to.y * map_width + elf.maybe_move_to.x] += 1;
                    found = true;
                    break;
                }
            }
            
            if !found {
                no_move += 1;
            }
        }

        memset(map_current.data, 0, map_width * map_height);
        for *elf, elf_i: elves {
            if elf_i == look_at_elf  print("%\n", elf.pos);
            if map_next[elf.maybe_move_to.y * map_width + elf.maybe_move_to.x] == 1 {
                elf.pos = elf.maybe_move_to;
            }
            map_current[elf.pos.y * map_width + elf.pos.x] = 1;
            if elf_i == look_at_elf  print("%\n", elf.pos);
        }


        if round == 9 {
            max_on_map: Vec2 = .{ 0, 0 };
            min_on_map: Vec2 = .{ map_width, map_height };
            for elves {
                min_on_map.x = min(min_on_map.x, it.pos.x);
                min_on_map.y = min(min_on_map.y, it.pos.y);
                max_on_map.x = max(max_on_map.x, it.pos.x);
                max_on_map.y = max(max_on_map.y, it.pos.y);
            }
            part1 = (max_on_map.x - min_on_map.x + 1) * (max_on_map.y - min_on_map.y + 1) - elves.count;
        }
        
        if no_move == elves.count {
            part2 = round + 1;
            break;
        }
    }
    print("Part 1: %\n", part1);
    print("Part 2: %\n", part2);
}

#scope_file
Vec2 :: struct {
    x: s64;
    y: s64;
}

operator + :: (v1: Vec2, v2: Vec2) -> Vec2 {
    return .{ x = v1.x + v2.x, y = v1.y + v2.y };
}

Elf :: struct {
    pos: Vec2;
    maybe_move_to: Vec2;
}

Cell :: struct {
    round: s8;
    count: s8;
}

Direction :: struct {
    p1: Vec2;
    p2: Vec2;
    p3: Vec2;
}