dummy_void: void;
solve_day12 :: (test: bool) {
    contents := read_entire_file(ifx test then "inputs/day12_test.txt" else "inputs/day12.txt");
    lines := split(contents, cast(u8) #char "\n");

    builder: String_Builder;

    grid: Grid;

    grid.height = lines.count;
    grid.width = lines[0].count;

    for lines {
        print_to_builder(*builder, it);
    }

    grid.grid = builder_to_string(*builder);

    end: Coord;
    start: Coord;
    start.height = #char "a";

    for lines {
        start_index := find_index_from_left(it, cast(u8) #char "S");
        end_index := find_index_from_left(it, cast(u8) #char "E");

        if start_index != -1 {
            start.x = start_index;
            start.y = it_index;
        }

        if end_index != -1 {
            end.x = end_index;
            end.y = it_index;
        }

    }

    visited: Table(s64, Coord);

    grid.grid[end.y * grid.width + end.x] = #char "z";
    grid.grid[start.y * grid.width + start.x] = #char "a";

    part1 := find_shortest_path(start, end, grid, S64_MAX, *visited);

    part2 := part1;

    for x: 0..grid.width - 1 {
        for y: 0..grid.height - 1 {
            if grid.grid[grid.width * y + x] == #char "a" {
                start.x = x;
                start.y = y;

                part2 = find_shortest_path(start, end, grid, part2, *visited);
            }
        }
    }

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

}

find_shortest_path :: (start: Coord, end: Coord, grid: Grid, shorter_than: s64, visited: *Table) -> s64 {

    queue: Priority_Queue(Coord, (a, b) => a.steps - b.steps);
    init(*queue);

    add(*queue, start);

    length := shorter_than;

    dummy_: [..]Coord;

    dirs := Coord.[.{1,0,0,0, dummy_}, .{-1,0,0,1, dummy_}, .{0,1,0,2, dummy_}, .{0,-1,0,3, dummy_}];

    final_pos: Coord = ---;

    while outer:= queue.count > 0 {
        removed,item := remove_top(*queue);
        defer array_free(item.visited);
        for dir: dirs {
            new_pos := item + dir;
            new_pos.steps += 1;

            if new_pos.steps > shorter_than  continue;

            visited_item, found_visited := table_find(visited, convert_to_s64(new_pos));

            if !inside(new_pos, grid) || (found_visited && new_pos.steps >= visited_item.steps)  continue;

            new_pos.height = grid.grid[new_pos.y * grid.width + new_pos.x];

            if new_pos.height <= item.height + 1 {

                array_copy(*new_pos.visited, item.visited);

                array_add(*new_pos.visited, new_pos);
                if new_pos.x == end.x && new_pos.y == end.y {
                    length = new_pos.steps;
                    final_pos = new_pos;

                    break outer;
                }


                table_set(visited, convert_to_s64(new_pos), new_pos);
                add(*queue, new_pos);
            }
        }
    }

    while queue.count > 0 {
        removed, item := remove_top(*queue);

        array_free(item.visited);
    }


    return length;
}

convert_to_arrow :: (from: Coord, to: Coord) -> u8 {
    g := from - to;

    if g.x == -1  return #char ">";
    if g.x == 1   return #char "<";
    if g.y == -1  return #char "v";
    if g.y == 1   return #char "^";

    return #char "?";
}

convert_to_s64 :: (c: Coord) -> s64 {
    result := (cast,no_check(u64)c.x) << 32 | cast(u64)(cast,no_check(u32)c.y);
    return cast,no_check(s64)result;
}

inside :: (pos: Coord, grid: Grid) -> bool {
    return pos.x >= 0 && pos.x < grid.width && pos.y >= 0 && pos.y < grid.height;
}

#scope_file
Coord :: struct {
    x: s64;
    y: s64;
    height: s64;
    steps: s64;
    visited: [..]Coord;
}

operator - :: (c1: Coord, c2: Coord) -> Coord {
    dummy: [..]Coord;
    return .{ c1.x - c2.x, c1.y - c2.y, c1.height, c1.steps, dummy };
}

operator + :: (c1: Coord, c2: Coord) -> Coord {
    dummy: [..]Coord;
    return .{ c1.x + c2.x, c1.y + c2.y, c1.height, c1.steps, dummy };
}