adamcrussell (adamcrussell) wrote,
adamcrussell
adamcrussell

2020 Advent Of Code Day 7

Prolog. Developed and tested with SWI-Prolog 8.0.3.

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(end_of_file, [], _):-
    !.
check_and_read(Char, [Char|Chars], Stream):-
    get_code(Stream, NextChar),
    check_and_read(NextChar, Chars, Stream).
    
read_data(Stream, []):-
    at_end_of_stream(Stream).
read_data(Stream, [X|L]):-
    \+ at_end_of_stream(Stream),
    get_code(Stream, Char),
    check_and_read(Char, Chars, Stream),
    atom_codes(X, Chars),
    read_data(Stream, L).

make_atoms(S, A):-
    make_atoms(S, [], A).
make_atoms([], A, A).
make_atoms([H|T], Accum, A):-
    atom_string(A0, H),
    append(Accum, [A0], NewAccum),
    make_atoms(T, NewAccum, A).

clean_records(Records, CleanedRecords):-
    clean_records(Records, [], CleanedRecords).
clean_records([], CleanedRecords, CleanedRecords).
clean_records([[]|T], CleanedRecordsAccum, CleanedRecords):-
    clean_records(T, CleanedRecordsAccum, CleanedRecords).
clean_records([H|T], CleanedRecordsAccum, CleanedRecords):-
    [H0|_] = H,
    atom_codes(H0, C),
    delete(C, 44, C0),
    atom_codes(A, C0),
    split_string(A, ' ', '', S),
    make_atoms(S, A0),
    clean_records(T, [A0|CleanedRecordsAccum], CleanedRecords).
    
records(Lines, Records):-
    records(Lines, [[]], Records).
records([], Records, Records).
records([H|T], [HAccum|TAccum], Records):-
    atom_codes(H, C),
    last(C, Last),
    \+ Last == 46,
    append(HAccum, [H], NewHAccum),
    records(T, [NewHAccum|TAccum], Records).
records([H|T], [HAccum|TAccum], Records):-    
    records(T, [[],[H|HAccum]|TAccum], Records).

generate_contains(_, []).
generate_contains(Bag, [X, Adjective, Color, _|T]):-
    atom_concat(Adjective, ' ', K),
    atom_concat(K, Color, Kind),
    assert(kind(Kind)),
    assert(contains_bag(Bag, X, Kind)),
    generate_contains(Bag, T).
generate_contains(Bag, [no, other, _|T]):-
    assert(kind(null)),
    assert(contains_bag(Bag, 0, null)),
    generate_contains(Bag, T).    

generate_kind([Adjective, Color, bags, contain|T]):-
    atom_concat(Adjective, ' ', K),
    atom_concat(K, Color, Kind),
    assert(kind(Kind)),
    generate_contains(Kind, T).

generate_terms([]).
generate_terms([H|T]):-
    generate_kind(H),
    generate_terms(T).

contains_bags(Bag, OtherBags):-
    contains_bag(Bag, _, OtherBags);
    (contains_bag(Bag, _, SomeOtherBags),
     contains_bags(SomeOtherBags, OtherBags)).

unique(L, Unique):-
    unique(L, [], Unique).
unique([], Unique, Unique).
unique([H|T], UniqueAccum, Unique):-
    \+ member(H, UniqueAccum),
    unique(T, [H|UniqueAccum], Unique).
unique([_|T], UniqueAccum, Unique):-
    unique(T, UniqueAccum, Unique).

add_parent(Parent, Contained, ParentContained):-
    add_parent(Parent, Contained, [], ParentContained).
add_parent(_, [], ParentContained, ParentContained).
add_parent(Parent, [H|T], ParentContainedAccum, ParentContained):-
    append(ParentContainedAccum, [Parent-H], Accum), 
    add_parent(Parent, T, Accum, ParentContained).

update_parent(Parent, Contained, ParentContained):-
    update_parent(Parent, Contained, [], ParentContained).
update_parent(_, [], ParentContained, ParentContained).
update_parent(Parent, [_-H|T], ParentContainedAccum, ParentContained):-
    append(ParentContainedAccum, [Parent-H], Accum), 
    update_parent(Parent, T, Accum, ParentContained).

contains_x_bags(Bag, Contained):-
    setof([Has, X], contains_bag(Bag, X, Has), Contained0),
    add_parent(Bag, Contained0, PC),
    contains_x_bags(Contained0, PC, Contained).
contains_x_bags([], Contained, Contained):-!.
contains_x_bags([[H,_]|_], ContainedAccum, Contained):-
    setof([Has, X], contains_bag(H, X, Has), Contained0),
    \+ Contained0 == [[null, 0]],
    add_parent(H, Contained0, PC),
    append(ContainedAccum, PC, Accum),
    contains_x_bags(Contained0, Accum, Contained).
contains_x_bags([[_,_]|T], ContainedAccum, Contained):-
    contains_x_bags(T, ContainedAccum, Contained).    

bag_match(Bag, Bags, Matches):-
    bag_match(Bag, Bags, [], Matches).
bag_match(_, [], Matches, Matches).
bag_match(Bag, [B-H|T], MatchesAccum, Matches):-
    Bag == B,
    bag_match(Bag, T, [H|MatchesAccum], Matches).
bag_match(Bag, [_-_|T], MatchesAccum, Matches):-
    bag_match(Bag, T, MatchesAccum, Matches).

remaining_bags([], Remaining, Remaining).
remaining_bags([H|T], Bags, Remaining):-
    delete(Bags, H, R),
    remaining_bags(T, R, Remaining).

count_x_bags(Bag, Count):-
    setof(Contained,contains_x_bags(Bag, Contained),All), flatten(All, Flat), unique(Flat, U),
    bag_match(Bag, U, Matches),
    remaining_bags(Matches, U, Remaining),
    bag_count(Matches, Remaining, Count).  
bag_count([], _, 0). 
bag_count([[Bag, X]|T], Bags, Count):-
    number_chars(N, [X]),
    bag_match(Bag, Bags, Matches),
    bag_count(Matches, Bags, CountAccum),
    bag_count(T, Bags, NextCount),
    Count is N + N * CountAccum + NextCount.
bag_count([[_, _]|T], _, Count):-
    bag_count(T, _, Count).
    
main:-
    open('data', read, Stream),
    read_data(Stream, Lines),
    close(Stream),
    records(Lines, Records),
    clean_records(Records, CleanedRecords),
    generate_terms(CleanedRecords),
    setof(B0, contains_bags(B0, 'shiny gold'), Bag0),
    length(Bag0, Bag0Length),
    format("~d bag colors can eventually contain at least one shiny gold bag.~n", [Bag0Length]),
    count_x_bags('shiny gold', BagCount),
    format("~d individual bags are required inside a single shiny gold bag.~n", [BagCount]).
Tags: advent of code, logic programming, prolog, prolog programming
Subscribe

Recent Posts from This Journal

Comments for this post were disabled by the author