Day 5

Author

Julia Romanowska

The input

There are several maps hidden in the file. All this mapping helps find out how to plant the new seeds.

root = dirname(@__FILE__);
input_file = joinpath(root, "..", "..", "DATA", "2023", "input_day05.txt");
input_data = open(input_file, "r");
for i in 1:5
    println(readline(input_data))
end
close(input_data)
seeds: 630335678 71155519 260178142 125005421 1548082684 519777283 4104586697 30692976 1018893962 410959790 3570781652 45062110 74139777 106006724 3262608046 213460151 3022784256 121993130 2138898608 36769984

seed-to-soil map:
2977255263 3423361099 161177662
3464809483 1524036300 40280620

Part 1

First, we need to extract the different mappings from this one file.

Let’s get all the seed numbers (our input):

input_data = open(input_file, "r");

seeds = readline(input_data) |>
    x -> (split(x, " "))[2:end] |>
    x -> parse.(Int64, x)
20-element Vector{Int64}:
  630335678
   71155519
  260178142
  125005421
 1548082684
  519777283
 4104586697
   30692976
 1018893962
  410959790
 3570781652
   45062110
   74139777
  106006724
 3262608046
  213460151
 3022784256
  121993130
 2138898608
   36769984

…and then all the maps in one String:

all_maps = read(input_data, String);
close(input_data)

…so that we can split:

split_maps = split(all_maps, "\n\n")

all_maps_names = String[];
all_maps_data = DataFrame[];
for each_map in split_maps
    map_name , map_data = split(each_map, ":\n");
    # get the name without explicit " map"
    map_name = (strip(map_name))[1:(end - 4)];
    push!(all_maps_names, map_name);

    map_data = CSV.read(
        IOBuffer(map_data),
        DataFrame;
        header = ["dest_start", "src_start", "range"],
        delim = " "
    );
    sort!(map_data, :src_start);
    map_data = transform(
        map_data,
        [:dest_start, :range] =>
            ((d, r) -> d + r) =>
        :dest_end,
        [:src_start, :range] =>
            ((s, r) -> s + r) =>
        :src_end
    );
    push!(all_maps_data, map_data);
end

Now I have all these maps: [“seed-to-soil”, “soil-to-fertilizer”, “fertilizer-to-water”, “water-to-light”, “light-to-temperature”, “temperature-to-humidity”, “humidity-to-location”].

The problem

I need to find which seed goes with which soil, which soil goes with which fertilizer, etc. And then, find the smallest location number!

function get_dest_number(src_number::Int64, map_number::Int64)
    cur_map = all_maps_data[map_number];
    filtered_map = subset(cur_map, [:src_start, :src_end] => (x,y) -> x .<= src_number .<= y);
    if nrow(filtered_map) == 0
        # the destination number is the same as src number
        return src_number
    end
    offset = src_number - filtered_map.src_start[1];
    return filtered_map.dest_start[1] + offset
end
get_dest_number (generic function with 1 method)
all_locations = Int64[];
for s_number in seeds
    start_number = s_number;
    next_number = Int64;
    for m_number in 1:length(all_maps_names)
        next_number = get_dest_number(start_number, m_number);
        start_number = next_number;
    end
    push!(all_locations, next_number);
end

The solution

The smallest location is: 51580674.

Part 2

Range of seeds instead of list of seeds.

all_locations = Int64[];
for i in 1:2:(lastindex(seeds) - 1)
    seed_start = seeds[i];
    seed_range = seeds[i + 1];
    for s_number in seed_start:(seed_start + seed_range)
        start_number = s_number;
        next_number = Int64;
        for m_number in 1:length(all_maps_names)
            next_number = get_dest_number(start_number, m_number);
            start_number = next_number;
        end
        push!(all_locations, next_number);
    end
end

This solution takes too much time to run!