GRID_SIDE :: 100;
GRID_SIZE :: GRID_SIDE * GRID_SIDE * GRID_SIDE;

solve_day18 :: (test: bool) {
    contents := read_entire_file(ifx test then "inputs/day18_test.txt" else "inputs/day18.txt");
    lines    := split(contents, cast(u8) #char "\n");
    part1 := 0;
    part2 := 0;

    cubes: []Cube = NewArray(GRID_SIZE, Cube);
    defer array_free(cubes);
    for 0..cubes.count - 1  cubes[it] = .{};

    max_coord: Coord = .{ 0, 0, 0 };

    for line: lines {
        comma := split(line, cast(u8) #char ",");
        
        coord := Coord.{
            x = string_to_int(comma[0], 10, s64) + 1,
            y = string_to_int(comma[1], 10, s64) + 1,
            z = string_to_int(comma[2], 10, s64) + 1,
        };

        max_coord.x = max(max_coord.x, coord.x);
        max_coord.y = max(max_coord.y, coord.y);
        max_coord.z = max(max_coord.z, coord.z);

        part1 += 6;

        cubes[calc_grid_index(coord)].has_lava = true;

        for f: Coord.[.{1,0,0},.{-1,0,0},.{0,1,0},.{0,-1,0},.{0,0,1},.{0,0,-1}] {
            if coord.x + f.x >= GRID_SIDE  continue;
            if coord.y + f.y >= GRID_SIDE  continue;
            if coord.z + f.z >= GRID_SIDE  continue;
            if coord.x + f.x < 0  continue;
            if coord.y + f.y < 0  continue;
            if coord.z + f.z < 0  continue;
            cube := *cubes[calc_grid_index(f + coord)];

            if cube.has_lava {
                part1 -= 2;
            }
        }
    }

    max_coord += .{1,1,1};

    start_coord := Coord.{0,0,0};

    queue: Deque(Coord);
    defer deinit(*queue);
    init(*queue);

    deque_add_last(*queue, start_coord);

    while queue.count > 0 {
        item := deque_remove_first(*queue);

        if cubes[calc_grid_index(item)].visited  continue;

        cubes[calc_grid_index(item)].visited = true;

        for f: Coord.[.{1,0,0},.{-1,0,0},.{0,1,0},.{0,-1,0},.{0,0,1},.{0,0,-1}] {
            test_coord := f + item;
            if !inside(test_coord, max_coord)  continue;

            cube := *cubes[calc_grid_index(test_coord)];

            if cube.has_lava {
                part2 += 1;
            } else if !cube.visited {
                deque_add_last(*queue, test_coord);
            }
        }
    }

    print("Part 1: %\n", part1);
    print("Part 2: %\n", part2);
}

inside :: (point: Coord, max: Coord) -> bool {
    return point.x >= 0 && point.x <= max.x &&
           point.y >= 0 && point.y <= max.y &&
           point.z >= 0 && point.z <= max.z;
}

calc_grid_index :: (coord: Coord) -> s64 {
    return GRID_SIDE * GRID_SIDE * coord.z + GRID_SIDE * coord.y + coord.x;
}

#scope_file
Coord :: struct {
    x: s64;
    y: s64;
    z: s64;
}

Cube :: struct {
    has_lava: bool = false;
    visited:  bool = false;
}

operator + :: (c1: Coord, c2: Coord) -> Coord {
    return .{ c1.x + c2.x, c1.y + c2.y, c1.z + c2.z };
}

cmp :: (c1: Coord, c2: Coord) -> bool {
    return c1.x == c2.x && c1.y == c2.y && c1.z == c2.z;
}

hash :: (c: Coord) -> u32 {
    return get_hash(c.x) ^ get_hash(c.y) ^ get_hash(c.z);
}