aoc23/05/part2.pl
2023-12-05 18:45:53 -08:00

109 lines
4.1 KiB
Prolog

answer(Answer) :-
combine([seed_to_soil, soil_to_fertilizer, fertilizer_to_water,
water_to_light, light_to_temperature, temperature_to_humidity,
humidity_to_location], Map),
seed_map(Seeds),
fold_down(Seeds, Map, MappedSeeds),
sort(MappedSeeds, [[Answer, _, _] | _]).
% combine all map predicates into one map
combine([MapPredicate], Map) :-
G =.. [MapPredicate, Map], G.
combine([Map1Predicate, Map2Predicate| RestOfMaps], CombinedMap) :-
G =.. [Map1Predicate, Map1], G,
H =.. [Map2Predicate, Map2], H,
process_map(Map1, ProcessedMap1),
process_map(Map2, ProcessedMap2),
fold_down(ProcessedMap1, ProcessedMap2, Map12),
retractall(map12pred(_)), assertz(map12pred(Map12)),
combine([map12pred | RestOfMaps], CombinedMap).
% combine 2 maps into one
fold_down([], _, []).
fold_down([Pair1 | RestOfMap1], Map2, FoldedMap) :-
split_pair(Pair1, Map2, NewPair1s),
fold_down(RestOfMap1, Map2, RestOfFoldedMap),
append(NewPair1s, RestOfFoldedMap, FoldedMap).
% split_pair(InputPair, OutputMap, SplittedInputPairs)
split_pair([Out1S, In1S, Len1], [], [[Out1S, In1S, Len1]]).
split_pair([Out1S, In1S, Len1],
[[Out2S, In2S, Len2] | _],
[[OutSNew, In1S, Len1]]) :-
% range1 completely inside
Out1S >= In2S, Out1S + Len1 =< In2S + Len2,
OutSNew is Out2S + (Out1S - In2S).
split_pair([Out1S, In1S, Len1],
[[_, In2S, Len2] | RestOf2],
Split) :-
% no overlap
(Out1S >= In2S + Len2; Out1S + Len1 =< In2S),
split_pair([Out1S, In1S, Len1], RestOf2, Split).
split_pair([Out1S, In1S, Len1],
[[Out2S, In2S, Len2] | RestOf2],
[[OutLeftS, In1S, LenLeft] | RestOfSplit]) :-
% range1 overlaps to the right
Out1S >= In2S, Out1S < In2S + Len2, Out1S + Len1 > In2S + Len2,
LenLeft is In2S + Len2 - Out1S,
OutLeftS is Out2S + (Out1S - In2S),
OutRightS is In2S + Len2,
InRightS is In1S + LenLeft,
LenRight is Len1 - LenLeft,
split_pair([OutRightS, InRightS, LenRight], RestOf2, RestOfSplit).
split_pair([Out1S, In1S, Len1],
[[Out2S, In2S, Len2] | RestOf2],
[[Out2S, InRightS, LenRight] | RestOfSplit]) :-
% range1 overlaps to the left
Out1S < In2S, Out1S + Len1 > In2S, Out1S + Len1 =< In2S + Len2,
LenLeft is In2S - Out1S,
InRightS is In1S + LenLeft,
LenRight is Len1 - LenLeft,
split_pair([Out1S, In1S, LenLeft], RestOf2, RestOfSplit).
split_pair([Out1S, In1S, Len1],
[[Out2S, In2S, Len2] | RestOf2],
[[Out2S, InMiddleS, Len2] | RestOfSplit]) :-
% range1 completely covers range2
Out1S < In2S, Out1S + Len1 > In2S + Len2,
LenLeft is In2S - Out1S,
InMiddleS is In1S + LenLeft,
split_pair([Out1S, In1S, LenLeft], RestOf2, SplitLeft),
OutRightS is In2S + Len2,
InRightS is In1S + LenLeft + Len2,
LenRight is Len1 - LenLeft - Len2,
split_pair([OutRightS, InRightS, LenRight], RestOf2, SplitRight),
append(SplitLeft, SplitRight, RestOfSplit).
% converts given ranges to sorted closed intervals starting at 0
process_map(Map, ProcessedMap) :-
sort(2, @=<, Map, SortedMap),
pad_start(SortedMap, Sorted0PaddedMap),
pad_end(Sorted0PaddedMap, ProcessedMap).
% add 0 range at the beginning if not exist. Range must be already sorted.
pad_start([[OutS, 0, Len] | RRanges], [[OutS, 0, Len] | RRanges]).
pad_start([[OutS, InS, Len] | RRanges],
[[0, 0, InS], [OutS, InS, Len] | RRanges]) :-
InS =\= 0.
% add MAXINT range at end. Range must be already sorted.
pad_end(Ranges, PaddedRanges) :-
reverse(Ranges, [[OutS, InS, Len] | RestOfRevRanges]),
NewInS is InS + Len,
NewLen is 4294967295 - NewInS,
(NewLen =:= 0 ->
reverse([[OutS, InS, Len] | RestOfRevRanges], PaddedRanges);
reverse([[NewInS, NewInS, NewLen], [OutS, InS, Len] | RestOfRevRanges],
PaddedRanges)
).
% convert seed ranges into a straight map
seed_map([], []).
seed_map([Start, Len | RestOfSeeds], [[Start, Start, Len] | RestOfMap]) :-
seed_map(RestOfSeeds, RestOfMap).
seed_map(Map) :- seeds(Seeds), seed_map(Seeds, Map).