/r/prolog Coding Challenge #27

Developed and tested with SWI-Prolog 8.2.3.

/*
* Prints the Lyrics to The Twelve Days of Christmas.
* Lyrics from https://www.lyricsmode.com/lyrics/c/christmas_carols/the_twelve_days_of_christmas.html
*/

day(1, first).
day(2, second).
day(3, third).
day(4, fourth).
day(5, fifth).
day(6, sixth).
day(7, seventh).
day(8, eighth).
day(9, ninth).
day(10, tenth).
day(11, eleventh).
day(12, twelfth).

truelove('My true love gave to me:').

items(1, 'A partridge in a pear tree.').
items(2, 'Two turtle doves and').
items(3, 'Three french hens').
items(4, 'Four calling birds').
items(5, 'Five golden rings').
items(6, 'Six geese a-laying').
items(7, 'Seven swans a-swimming').
items(8, 'Eight maids a-milking').
items(9, 'Nine ladies dancing').
items(10, 'Ten lords a-leaping').
items(11, 'Eleven pipers piping').
items(12, 'Twelve drummers drumming').

write_accum([]):-
    nl.
write_accum([H|T]):-
    write(H), nl,
    write_accum(T).

the_twelve_days_of_christmas:-
    the_twelve_days_of_christmas(1, []).
the_twelve_days_of_christmas(13, _).    
the_twelve_days_of_christmas(Day, ItemsAccum):-
    day(Day, D),
    truelove(TL),
    items(Day, Items),
    format("On the ~s day of Christmas~n~s~n~s~n", [D, TL, Items]),
    NextDay is Day + 1,
    write_accum(ItemsAccum),
    the_twelve_days_of_christmas(NextDay, [Items|ItemsAccum]).

2020 Advent Of Code Day 12 (Part 2)

Prolog. Developed and tested with SWI-Prolog 8.2.3.

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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).

adjust_coordinates_left(Value, OldCoordinates, RecalibratedCoordinates):-
    V is Value - 90,
    V \== 0,
    [Y, X] = OldCoordinates,
    X0 is X,
    Y0 is Y * -1,
    adjust_coordinates_left(V, [X0, Y0], RecalibratedCoordinates).
adjust_coordinates_left(Value, OldCoordinates, RecalibratedCoordinates):-
    V is Value - 90,
    V == 0,
    [Y, X] = OldCoordinates,
    X0 is X,
    Y0 is Y * -1,
    RecalibratedCoordinates = [X0, Y0].

adjust_coordinates_right(Value, OldCoordinates, RecalibratedCoordinates):-
    V is Value - 90,
    V \== 0,
    [Y, X] = OldCoordinates,
    X0 is X * -1,
    Y0 is Y,
    adjust_coordinates_right(V, [X0, Y0], RecalibratedCoordinates).
adjust_coordinates_right(Value, OldCoordinates, RecalibratedCoordinates):-
    V is Value - 90,
    V == 0,
    [Y, X] = OldCoordinates,
    X0 is X * -1,
    Y0 is Y,
    RecalibratedCoordinates = [X0, Y0].

move_waypoint_north(Value, WayPointPosition, UpdatedWayPointPosition):-
     [OldX, OldY] = WayPointPosition,
     NewX = OldX,
     NewY is OldY - Value,
     UpdatedWayPointPosition = [NewX, NewY].

move_waypoint_south(Value, WayPointPosition, UpdatedWayPointPosition):-
     [OldX, OldY] = WayPointPosition,
     NewX = OldX,
     NewY is OldY + ( Value),
     UpdatedWayPointPosition = [NewX, NewY].
     
move_waypoint_west(Value, WayPointPosition, UpdatedWayPointPosition):-
     [OldX, OldY] = WayPointPosition,
     NewY = OldY,
     NewX is OldX - (Value),
     UpdatedWayPointPosition = [NewX, NewY].         
         
move_waypoint_east(Value, WayPointPosition, UpdatedWayPointPosition):-
     [OldX, OldY] = WayPointPosition,
     NewY = OldY,
     NewX is OldX + Value,
     UpdatedWayPointPosition = [NewX, NewY].  
     
execute_command(Command, Value, WayPointPosition, ShipPosition, UpdatedWayPointPosition, UpdatedShipPosition):-
     (Command == 'R',
     UpdatedShipPosition = ShipPosition,
     adjust_coordinates_right(Value, WayPointPosition, UpdatedWayPointPosition));
     (Command == 'L',
     UpdatedShipPosition = ShipPosition,
     adjust_coordinates_left(Value, WayPointPosition, UpdatedWayPointPosition));
    (Command == 'N',
     UpdatedShipPosition = ShipPosition,
     move_waypoint_north(Value, WayPointPosition, UpdatedWayPointPosition));
    (Command == 'S',
     UpdatedShipPosition = ShipPosition,
     move_waypoint_south(Value, WayPointPosition, UpdatedWayPointPosition));
    (Command == 'E',
     UpdatedShipPosition = ShipPosition,
     move_waypoint_east(Value, WayPointPosition, UpdatedWayPointPosition));
    (Command == 'W',
     UpdatedShipPosition = ShipPosition,
     move_waypoint_west(Value, WayPointPosition, UpdatedWayPointPosition));
    (Command == 'F',
     UpdatedWayPointPosition = WayPointPosition,
     [X0, Y0] = WayPointPosition,
     [X1, Y1] = ShipPosition,
     X is X1 + (Value * X0 ),
     Y is Y1 + (Value * Y0 ),
     UpdatedShipPosition = [X, Y]).

move_ship([]).
move_ship(Commands, FinalPosition):-
    move_ship(Commands, [10, -1], [0, 0], FinalPosition).
move_ship([], _, FinalPosition, FinalPosition):-!.    
move_ship([H|T], WayPointPosition, ShipPosition, FinalPosition):- 
    atom_chars(H, C),
    [Command|Number] = C,
    number_chars(Value, Number),
    execute_command(Command, Value, WayPointPosition, ShipPosition, UpdatedWayPointPosition, UpdatedShipPosition),
    move_ship(T, UpdatedWayPointPosition, UpdatedShipPosition, FinalPosition).

manhattan_distance(FinalPosition, ManhattanDistance):-
    [X, Y] = FinalPosition,
    ManhattanDistance is abs(X) + abs(Y).

main:-
    open('data', read, Stream),
    read_data(Stream, Commands),
    close(Stream),
    move_ship(Commands, FinalPosition),
    manhattan_distance(FinalPosition, ManhattanDistance), 
    format("Manhattan Distance from starting point: ~d.~n", [ManhattanDistance]),
    halt.   

2020 Advent Of Code Day 12 (Part 1)

Prolog. Developed and tested with SWI-Prolog 8.2.3.

right(0, east, east).
right(90, east, south).
right(180, east, west).
right(270, east, north).
right(360, east, east).
right(0, south, south).
right(90, south, west).
right(180, south, north).
right(270, south, east).
right(360, south, south).
right(0, west, west).
right(90, west, north).
right(180, west, east).
right(270, west, south).
right(360, west, west).
right(0, north, north).
right(90, north, east).
right(180, north, south).
right(270, north, west).
right(360, north, north).

left(Degree, Facing, NewFacing):-
    D is 360 - Degree,
    right(D, Facing, NewFacing).

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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).

execute_command(Command, Value, Position, Direction, NewDirection, UpdatedPosition):-
    (Command == 'R',
     right(Value, Direction, NewDirection),
     UpdatedPosition = Position
     );
    (Command == 'L',
     left(Value, Direction, NewDirection),
     UpdatedPosition = Position);
    (Command == 'N',
     NewDirection = Direction,
     [X0, Y0] = Position,
     Y1 is Y0 + Value,
     UpdatedPosition = [X0, Y1]);
    (Command == 'S',
     NewDirection = Direction,
     [X0, Y0] = Position,
     Y1 is Y0 - Value,
     UpdatedPosition = [X0, Y1]);
    (Command == 'E',
     NewDirection = Direction,
     [X0, Y0] = Position,
     X1 is X0 + Value,
     UpdatedPosition = [X1, Y0]);
    (Command == 'W',
     NewDirection = Direction,
     [X0, Y0] = Position,
     X1 is X0 - Value,
     UpdatedPosition = [X1, Y0]);
    (Command == 'F',
     (Direction == east, execute_command('E', Value, Position, Direction, NewDirection, UpdatedPosition));
     (Direction == south, execute_command('S', Value, Position, Direction, NewDirection, UpdatedPosition));
     (Direction == west, execute_command('W', Value, Position, Direction, NewDirection, UpdatedPosition));
     (Direction == north, execute_command('N', Value, Position, Direction, NewDirection, UpdatedPosition))
     ).

move_ship([]).
move_ship(Commands, FinalPosition):-
    move_ship(Commands, east, [0, 0], FinalPosition).
move_ship([], _, FinalPosition, FinalPosition):-!.    
move_ship([H|T], Direction, IntermediatePosition, FinalPosition):-
    atom_chars(H, C),
    [Command|Number] = C,
    number_chars(Value, Number),
    execute_command(Command, Value, IntermediatePosition, Direction, NewDirection, UpdatedPosition),
    move_ship(T, NewDirection, UpdatedPosition, FinalPosition).

manhattan_distance(FinalPosition, ManhattanDistance):-
    [X, Y] = FinalPosition,
    ManhattanDistance is abs(X) + abs(Y).

main:-
    open('data', read, Stream),
    read_data(Stream, Commands),
    close(Stream),
    move_ship(Commands, FinalPosition),
    manhattan_distance(FinalPosition, ManhattanDistance), 
    format("Manhattan Distance from starting point: ~d.~n", [ManhattanDistance]).   

2020 Advent Of Code Day 11 (Part 2)

Prolog. Developed and tested with SWI-Prolog 8.2.3.

line-size(91).

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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).

hashcount(List, HashCount):-
    hashcount(List, 0, HashCount).
hashcount([], HashCount, HashCount).
hashcount([H|T], HashCountPartial, HashCount):-  
    H == 35,
    HCP is HashCountPartial + 1,
    hashcount(T, HCP, HashCount).
hashcount([H|T], HashCountPartial, HashCount):-  
    H \== 35,
    hashcount(T, HashCountPartial, HashCount).

allseats(Records, AllSeats):-
    allseats(Records, [], AllSeats).
allseats([], AllSeats, AllSeats).
allseats([H|T], AllSeatsAccum, AllSeats):-
    string_codes(H, L),
    append(AllSeatsAccum, L, Accum),
    allseats(T, Accum, AllSeats).

adjacent_bottomleft(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat + LS1,
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M0 \== 0, M0 \==1,
      M1 < M0);
      (M0 == 0, M1 == LS1)),
    nth1(S, AllSeats, BLeft),
    ((BLeft \== 46, AdjacentSeat = BLeft);
      adjacent_bottomleft(S, AllSeats, AdjacentSeat)).
adjacent_bottomleft(_, _, AdjacentSeat):-
    AdjacentSeat = null.
    
adjacent_left(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat - 1,
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M0 \== 0, M0 \==1,
      M1 < M0);
      (M0 == 0, M1 == LS1)),
    nth1(S, AllSeats, Left),
    ((Left \== 46, AdjacentSeat = Left);
      adjacent_left(S, AllSeats, AdjacentSeat)).
adjacent_left(_, _, AdjacentSeat):-
    AdjacentSeat = null.   
adjacent_topleft(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat - (LS + 1),
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M0 \== 0, M0 \==1,
      M1 < M0);
      (M0 == 0, M1 == LS1)),
    nth1(S, AllSeats, TLeft),
    ((TLeft \== 46, AdjacentSeat = TLeft);
      adjacent_topleft(S, AllSeats, AdjacentSeat)).
adjacent_topleft(_, _, AdjacentSeat):-
    AdjacentSeat = null.  
adjacent_bottomright(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat + (LS + 1),
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M1 > M0,
     M0 \== 0);
     (M1 == 0, M0 == LS1)),
    nth1(S, AllSeats, BRight),
    ((BRight \== 46, AdjacentSeat = BRight);
      adjacent_bottomright(S, AllSeats, AdjacentSeat)).
adjacent_bottomright(_, _, AdjacentSeat):-
    AdjacentSeat = null.             
adjacent_right(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat + 1,
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M1 > M0,
     M0 \== 0);
     (M1 == 0, M0 == LS1)),
    nth1(S, AllSeats, Right),
    ((Right \== 46, AdjacentSeat = Right);
      adjacent_right(S, AllSeats, AdjacentSeat)).
adjacent_right(_, _, AdjacentSeat):-
    AdjacentSeat = null.
adjacent_topright(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat - LS1,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M1 > M0,
     M0 \== 0);
     (M1 == 0, M0 == LS1)),
    nth1(S, AllSeats, TRight),
    ((TRight \== 46, AdjacentSeat = TRight);
      adjacent_topright(S, AllSeats, AdjacentSeat)).
adjacent_topright(_, _, AdjacentSeat):-
    AdjacentSeat = null.    
adjacent_up(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    S is Seat - LS,
    S > 0,
    nth1(S, AllSeats, Up),
    ((Up \== 46, AdjacentSeat = Up);
      adjacent_up(S, AllSeats, AdjacentSeat)).
adjacent_up(_, _, AdjacentSeat):-
    AdjacentSeat = null.    
adjacent_down(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    S is Seat + LS,
    S > 0,
    nth1(S, AllSeats, Down),
    ((Down \== 46, AdjacentSeat = Down);
      adjacent_down(S, AllSeats, AdjacentSeat)).
adjacent_down(_, _, AdjacentSeat):-
    AdjacentSeat = null. 
    
adjacent(Seat, AllSeats, AdjacentSeats):-
    adjacent_left(Seat, AllSeats, AdjacentSeatLeft),
    adjacent_right(Seat, AllSeats, AdjacentSeatRight),
    adjacent_up(Seat, AllSeats, AdjacentSeatUp),
    adjacent_down(Seat, AllSeats, AdjacentSeatDown),
    adjacent_topleft(Seat, AllSeats, AdjacentSeatTopLeft),
    adjacent_topright(Seat, AllSeats, AdjacentSeatTopRight),
    adjacent_bottomleft(Seat, AllSeats, AdjacentSeatBottomLeft),
    adjacent_bottomright(Seat, AllSeats, AdjacentSeatBottomRight),
    AdjacentSeats = [AdjacentSeatLeft, AdjacentSeatRight, AdjacentSeatUp, AdjacentSeatDown,
                     AdjacentSeatTopLeft, AdjacentSeatTopRight, AdjacentSeatBottomLeft, 
                     AdjacentSeatBottomRight],!.

round(AllSeats, UpdatedSeats):-
    round(AllSeats, 0, AllSeats, [], UpdatedSeats).
round([], _, _, UpdatedSeats, UpdatedSeats).    
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == '.',
    append(UpdatedSeatsAccum, [H], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == '#',  
    adjacent(C, AllSeats, AdjacentSeats),
    hashcount(AdjacentSeats, HashCount),
    HashCount >= 5,
    H0 = 'L',
    atom_codes(H0, [A0]),
    append(UpdatedSeatsAccum, [A0], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == '#',  
    adjacent(C, AllSeats, AdjacentSeats),
    hashcount(AdjacentSeats, HashCount),
    HashCount \== 5,
    append(UpdatedSeatsAccum, [H], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).    
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == 'L',
    adjacent(C, AllSeats, AdjacentSeats), 
    \+ member(35, AdjacentSeats),
    H0 = '#',
    atom_codes(H0, [A0]),
    append(UpdatedSeatsAccum, [A0], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == 'L',
    adjacent(C, AllSeats, AdjacentSeats), 
    member(35, AdjacentSeats),
    append(UpdatedSeatsAccum, [H], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).

rounds(AllSeats, OccupiedSeats):-
    rounds(AllSeats, [], OccupiedSeats).
rounds([-1], _, _).
rounds(AllSeats, PreviousRound, OccupiedSeats):-
    round(AllSeats, UpdatedRound),!,
    write_seats(UpdatedRound), nl,
    ((
        UpdatedRound == PreviousRound,
        hashcount(UpdatedRound, OccupiedSeats),
        rounds([-1], UpdatedRound, OccupiedSeats)
    );
    (
        UpdatedRound \== PreviousRound,
        rounds(UpdatedRound, UpdatedRound, OccupiedSeats)
    )).  
    
write_seats([H|T]):-
    write_seats([H|T], 0).
write_seats([], _).    
write_seats([H|T], Counter):-
    line-size(LS),
    C is Counter + 1,
    M is C mod LS,
    M \== 0,
    atom_codes(A, [H]),
    write(A),
    write_seats(T, C).
write_seats([H|T], Counter):-
    line-size(LS),
    C is Counter + 1,
    M is C mod LS,
    M == 0,
    atom_codes(A, [H]),
    write(A),
    nl,
    write_seats(T, C).
    
main:-
    open('data', read, Stream),
    read_data(Stream, Records),
    close(Stream),
    allseats(Records, AllSeats),
    rounds(AllSeats, OccupiedSeats),
    write(OccupiedSeats), nl.

2020 Advent Of Code Day 11 (Part 1)

Prolog. Developed and tested with SWI-Prolog 8.2.3.

line-size(91).

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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).

hashcount(List, HashCount):-
    hashcount(List, 0, HashCount).
hashcount([], HashCount, HashCount).
hashcount([H|T], HashCountPartial, HashCount):-  
    H == 35,
    HCP is HashCountPartial + 1,
    hashcount(T, HCP, HashCount).
hashcount([H|T], HashCountPartial, HashCount):-  
    H \== 35,
    hashcount(T, HashCountPartial, HashCount).

allseats(Records, AllSeats):-
    allseats(Records, [], AllSeats).
allseats([], AllSeats, AllSeats).
allseats([H|T], AllSeatsAccum, AllSeats):-
    string_codes(H, L),
    append(AllSeatsAccum, L, Accum),
    allseats(T, Accum, AllSeats).

adjacent_bottomleft(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat + LS1,
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M0 \== 0, M0 \==1,
      M1 < M0);
      (M0 == 0, M1 == LS1)),
    nth1(S, AllSeats, Down),
    AdjacentSeat = Down.
adjacent_bottomleft(_, _, AdjacentSeat):-
    AdjacentSeat = null.
adjacent_left(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat - 1,
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M0 \== 0, M0 \==1,
      M1 < M0);
      (M0 == 0, M1 == LS1)),
    nth1(S, AllSeats, Left),
    AdjacentSeat = Left.
adjacent_left(_, _, AdjacentSeat):-
    AdjacentSeat = null.   
adjacent_topleft(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat - (LS + 1),
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M0 \== 0, M0 \==1,
      M1 < M0);
      (M0 == 0, M1 == LS1)),
    nth1(S, AllSeats, Down),
    AdjacentSeat = Down.
adjacent_topleft(_, _, AdjacentSeat):-
    AdjacentSeat = null.  
adjacent_bottomright(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat + (LS + 1),
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M1 > M0,
     M0 \== 0);
     (M1 == 0, M0 == LS1)),
    nth1(S, AllSeats, Down),
    AdjacentSeat = Down.
adjacent_bottomright(_, _, AdjacentSeat):-
    AdjacentSeat = null.             
adjacent_right(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat + 1,
    S > 0,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M1 > M0,
     M0 \== 0);
     (M1 == 0, M0 == LS1)),
    nth1(S, AllSeats, Right),
    AdjacentSeat = Right.
adjacent_right(_, _, AdjacentSeat):-
    AdjacentSeat = null.
adjacent_topright(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat - LS1,
    M0 is Seat mod LS,
    M1 is S mod LS,
    ((M1 > M0,
     M0 \== 0);
     (M1 == 0, M0 == LS1)),
    nth1(S, AllSeats, Down),
    AdjacentSeat = Down.
adjacent_topright(_, _, AdjacentSeat):-
    AdjacentSeat = null.    
adjacent_up(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat - LS,
    S > 0,
    nth1(S, AllSeats, Down),
    AdjacentSeat = Down.
adjacent_up(_, _, AdjacentSeat):-
    AdjacentSeat = null.    
adjacent_down(Seat, AllSeats, AdjacentSeat):-
    line-size(LS),
    LS1 is LS - 1,
    S is Seat + LS,
    S > 0,
    nth1(S, AllSeats, Down),
    AdjacentSeat = Down.
adjacent_down(_, _, AdjacentSeat):-
    AdjacentSeat = null. 
    
adjacent(Seat, AllSeats, AdjacentSeats):-
    adjacent_left(Seat, AllSeats, AdjacentSeatLeft),
    adjacent_right(Seat, AllSeats, AdjacentSeatRight),
    adjacent_up(Seat, AllSeats, AdjacentSeatUp),
    adjacent_down(Seat, AllSeats, AdjacentSeatDown),
    adjacent_topleft(Seat, AllSeats, AdjacentSeatTopLeft),
    adjacent_topright(Seat, AllSeats, AdjacentSeatTopRight),
    adjacent_bottomleft(Seat, AllSeats, AdjacentSeatBottomLeft),
    adjacent_bottomright(Seat, AllSeats, AdjacentSeatBottomRight),
    AdjacentSeats = [AdjacentSeatLeft, AdjacentSeatRight, AdjacentSeatUp, AdjacentSeatDown,
                     AdjacentSeatTopLeft, AdjacentSeatTopRight, AdjacentSeatBottomLeft, 
                     AdjacentSeatBottomRight],!.

round(AllSeats, UpdatedSeats):-
    round(AllSeats, 0, AllSeats, [], UpdatedSeats).
round([], _, _, UpdatedSeats, UpdatedSeats).    
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == '.',
    append(UpdatedSeatsAccum, [H], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == '#',  
    adjacent(C, AllSeats, AdjacentSeats),
    hashcount(AdjacentSeats, HashCount),
    HashCount >= 4,
    H0 = 'L',
    atom_codes(H0, [A0]),
    append(UpdatedSeatsAccum, [A0], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == '#',  
    adjacent(C, AllSeats, AdjacentSeats),
    hashcount(AdjacentSeats, HashCount),
    HashCount \== 4,
    append(UpdatedSeatsAccum, [H], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).    
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == 'L',
    adjacent(C, AllSeats, AdjacentSeats), 
    \+ member(35, AdjacentSeats),
    H0 = '#',
    atom_codes(H0, [A0]),
    append(UpdatedSeatsAccum, [A0], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).
round([H|T], Counter, AllSeats, UpdatedSeatsAccum, UpdatedSeats):-
    C is Counter + 1,
    atom_codes(A, [H]),
    A == 'L',
    adjacent(C, AllSeats, AdjacentSeats), 
    member(35, AdjacentSeats),
    append(UpdatedSeatsAccum, [H], Accum),
    round(T, C, AllSeats, Accum, UpdatedSeats).

rounds(AllSeats, OccupiedSeats):-
    rounds(AllSeats, [], OccupiedSeats).
rounds([-1], _, _).
rounds(AllSeats, PreviousRound, OccupiedSeats):-
    round(AllSeats, UpdatedRound),!,
    write_seats(UpdatedRound), nl,
    ((
        UpdatedRound == PreviousRound,
        hashcount(UpdatedRound, OccupiedSeats),
        rounds([-1], UpdatedRound, OccupiedSeats)
    );
    (
        UpdatedRound \== PreviousRound,
        rounds(UpdatedRound, UpdatedRound, OccupiedSeats)
    )).  
    
write_seats([H|T]):-
    write_seats([H|T], 0).
write_seats([], _).    
write_seats([H|T], Counter):-
    line-size(LS),
    LS1 is LS - 1,
    C is Counter + 1,
    M is C mod LS,
    M \== 0,
    atom_codes(A, [H]),
    write(A),
    write_seats(T, C).
write_seats([H|T], Counter):-
    line-size(LS),
    LS1 is LS - 1,
    C is Counter + 1,
    M is C mod LS,
    M == 0,
    atom_codes(A, [H]),
    write(A),
    nl,
    write_seats(T, C).
    
main:-
    open('data', read, Stream),
    read_data(Stream, Records),
    close(Stream),
    allseats(Records, AllSeats),
    rounds(AllSeats, OccupiedSeats),
    write(OccupiedSeats), nl.

2020 Advent Of Code Day 10

Prolog. Developed and tested with SWI-Prolog 8.0.3.

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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),
    number_codes(X, Chars),
    read_data(Stream, L).

differences([H|[]]):-
    H == 1,
    assert(difference(1, [H-0])).
differences([H|[]]):-
    H == 3,
    assert(difference(1, [H-0])).
differences([H0, H1|T]):-
    Delta is H0 - H1,
    Delta == 1,
    assert(difference(1, [H0-H1])),
    differences([H1|T]).
differences([H0, H1|T]):-
    Delta is H0 - H1,
    Delta == 3,
    assert(difference(3, [H0-H1])),
    differences([H1|T]).    
differences([H0, H1|[]]):-
    Delta is H0 - H1,
    Delta == 1,
    assert(difference(1, [H0-H1])).
differences([H0, H1|[]]):-
    Delta is H0 - H1,
    Delta == 1,
    assert(difference(1, [H0-H1])).
    
adapters(Adapters, AdapterChain):-
    sort(Adapters, SortedAdapters),
    adapters(SortedAdapters, 0, 0, [], AdapterChain).
adapters([], _, _, [PreviousAdapter|AdapterChainAccum], AdapterChain):-
    BuiltInAdapter is PreviousAdapter + 3,
    AdapterChain = [BuiltInAdapter, PreviousAdapter|AdapterChainAccum].
adapters(Adapters, CumulativeJoltage, PreviousAdapter, AdapterChainAccum, AdapterChain):-
    member(Adapter, Adapters),
    \+ member(Adapter, AdapterChainAccum),
    Delta is Adapter - PreviousAdapter,
    (Delta == 0; Delta == 1; Delta == 2; Delta == 3),
    C is CumulativeJoltage + Adapter,
    delete(Adapters, Adapter, RemainingAdapters),
    adapters(RemainingAdapters, C, Adapter, [Adapter|AdapterChainAccum], AdapterChain).

within3(Adapter, [H|T], Adapters):-
    within3(Adapter, [H|T], [], Adapters).
within3(_, [], Adapters, Adapters).
within3(Adapter, [H|T], AdaptersAccum, Adapters):-
    Delta is H - Adapter,
    between(1, 3, Delta),
    within3(Adapter, T, [H|AdaptersAccum], Adapters).
within3(Adapter, [_|T], AdaptersAccum, Adapters):-
    within3(Adapter, T, AdaptersAccum, Adapters).

add_branch(_, []).
add_branch(Adapter, [H|T]):-
    assert(branch(Adapter-H)),
    add_branch(Adapter, T).

adapter_tree([], _).
adapter_tree([H|T], Adapters):-
    within3(H, Adapters, Adapters3),
    add_branch(H, Adapters3),
    adapter_tree(T, Adapters),
    adapter_tree(Adapters3, Adapters).
adapter_tree(Adapter, Adapters):-
    within3(Adapter, Adapters, Adapters3),
    add_branch(Adapter, Adapters3),
    adapter_tree(Adapters3, Adapters).
    
difference3(Difference3):-
    findall(X, difference(3, X), Differences),
    length(Differences, Difference3).

difference1(Difference1):-
    findall(X, difference(1, X), Differences),
    length(Differences, Difference1).

chain_size(Records, ChainSize):-
    sort(Records, RecordsSorted),
    adapter_tree(0, RecordsSorted),
    last(RecordsSorted, Largest),
    findall(X, branch(X-Largest), Leaves), 
    length(Leaves, ChainSize).

main:-
    open('data', read, Stream),
    read_data(Stream, Records),
    close(Stream),
    adapters(Records, AdapterChain),
    differences(AdapterChain),
    difference3(Difference3),
    difference1(Difference1),
    DifferenceProduct is Difference1 * Difference3,
    format("1-jolt differences multiplied by the number of 3-jolt differences: ~d.~n", [DifferenceProduct]),
    chain_size(Records, ChainSize),
    format("total number of distinct ways you can arrange the adapters: ~d.~n", [ChainSize]).

2020 Advent Of Code Day 9

Prolog. Developed and tested with SWI-Prolog 8.0.3.

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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).

get_n(N, List, ListN):-
    get_n(N, List, 0, [], ListN).
get_n(N, [_|_], N, ListN, ListN).     
get_n(N, [H|T], Counter, ListAccum, ListN):- 
    C is Counter + 1,
    get_n(N, T, C, [H|ListAccum], ListN).

check_sum(X, List):-
    member(A, List),
    member(B, List),
    atom_number(A, N0),
    atom_number(B, N1),
    A \== B,
    X is N0 + N1.
    
check_sum_list(X, List, Sum):-
    check_sum_list(X, List, 0, Sum).
check_sum_list(_, [], Sum, Sum).    
check_sum_list(X, [H|T], SumAccum, Sum):-
    atom_number(H, H0),
    S is H0 + SumAccum,
    check_sum_list(X, T, S, Sum).

first_not_sum(Records, FirstNotSum):-
    get_n(5, Records, Preamble),
    first_not_sum(Preamble, 0, 25, Records, [], FirstNotSum). 
first_not_sum(_, _, _, _, [-1], _).    
first_not_sum(Preamble, Counter, Ignore, [H|T], Seen, FirstNotSum):-
    Counter >= Ignore,
    C is Counter + 1,
    atom_number(H, X),
    \+ check_sum(X, Seen),
    FirstNotSum is X,
    first_not_sum(Preamble, C, Ignore, T, [-1], FirstNotSum).
first_not_sum(Preamble, Counter, Ignore, [H|T], Seen, FirstNotSum):- 
    Counter < Ignore,
    C is Counter + 1,
    append(Seen, [H], Seent),
    first_not_sum(Preamble, C, Ignore, T, Seent, FirstNotSum).     
first_not_sum(Preamble, Counter, Ignore, [H|T], [_|ST], FirstNotSum):-
    append(ST, [H], STH),
    first_not_sum(Preamble, Counter, Ignore, T, STH, FirstNotSum).

sum_to_missing(Missing, Records, SumToMissing):-
    sum_to_missing(Missing, Records, Records, [], SumToMissing).
sum_to_missing(_, [], _, SumToMissing, SumToMissing).
sum_to_missing(Missing, [H|T], Records, SumToMissingAccum, SumToMissing):-
    check_sum_list(Missing, SumToMissingAccum, Sum),
    Sum < Missing,
    append(SumToMissingAccum, [H], S0),
    sum_to_missing(Missing, T, Records, S0, SumToMissing).
sum_to_missing(Missing, _, Records, SumToMissingAccum, SumToMissing):-
    check_sum_list(Missing, SumToMissingAccum, Sum),
    Sum == Missing,
    sum_to_missing(Missing, [], Records, SumToMissingAccum, SumToMissing).
sum_to_missing(Missing, [H|T], Records, SumToMissingAccum, SumToMissing):-
    check_sum_list(Missing, SumToMissingAccum, Sum),
    Sum > Missing,
    [_|TAccum] = SumToMissingAccum,
    sum_to_missing(Missing, [H|T], Records, TAccum, SumToMissing).
    
main:-
    open('data', read, Stream),
    read_data(Stream, Records),
    close(Stream),
    first_not_sum(Records, FirstNotSum),
    format("The first number not a sum of two previous numbers: ~d.~n",[FirstNotSum]),
    sum_to_missing(FirstNotSum, Records, SumToMissing),
    sort(SumToMissing, SumToMissingSorted),
    [L, S|_] = SumToMissingSorted,
    atom_number(S, Smallest),
    atom_number(L, Largest),
    SmallestLargestSum is Smallest + Largest,
    format("The sum of the smallest and largest of the list of numbers that sum to ~d ([~d, ~d]): ~d.~n",[FirstNotSum, Smallest, Largest, SmallestLargestSum]).

2020 Advent Of Code Day 8

Prolog. Developed and tested with SWI-Prolog 8.0.3.

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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).

label_instructions(Instructions, LabelledInstructions):-
    label_instructions(Instructions, 0, [], LabelledInstructions).
label_instructions([], _, LabelledInstructions, LabelledInstructions):-
    length(LabelledInstructions, L),
    assert(instruction(L-[null, null])).
label_instructions([Operation, Argument|T], Counter, LabelledInstructionsAccum, LabelledInstructions):-
    LabelledInstruction = Counter-[Operation, Argument],
    assert(instruction(Counter-[Operation, Argument])),
    C is Counter + 1,
    label_instructions(T, C, [LabelledInstruction|LabelledInstructionsAccum], LabelledInstructions).
label_instructions(_, Counter, LabelledInstructionsAccum, LabelledInstructions):-    
    label_instructions([], Counter, LabelledInstructionsAccum, LabelledInstructions).
    
process_instructions(LastCounter, LastAccumulatorValue):-
    process_instructions(0, LastCounter, [], 0, LastAccumulatorValue).
process_instructions(LastCounter, LastCounter, [0], LastAccumulatorValue, LastAccumulatorValue).    
process_instructions(LastCounter, LastCounter, [-1], LastAccumulatorValue, LastAccumulatorValue).   
process_instructions(Counter, LastCounter, ProcessedInstructions, AccumulatorValue, LastAccumulatorValue):-
    (
        instruction(Counter-[Operation, Argument]),
        member(Counter-[Operation, Argument], ProcessedInstructions),
        process_instructions(Counter, LastCounter, [-1], AccumulatorValue, LastAccumulatorValue)
    );
    instruction(Counter-[Operation, Argument]),
    Operation == acc,
    atom_number(Argument, N),
    A is AccumulatorValue + N,
    C is Counter + 1,
    process_instructions(C, LastCounter, [Counter-[Operation, Argument]|ProcessedInstructions], A, LastAccumulatorValue).
process_instructions(Counter, LastCounter, ProcessedInstructions, AccumulatorValue, LastAccumulatorValue):-
    (
        instruction(Counter-[Operation, Argument]),
        member(Counter-[Operation, Argument], ProcessedInstructions),
        process_instructions(-1, Counter, AccumulatorValue, LastAccumulatorValue)
    );
    instruction(Counter-[Operation, Argument]),
    Operation == jmp,
    atom_number(Argument, N),
    C is Counter + N,  
    process_instructions(C, LastCounter, [Counter-[Operation, Argument]|ProcessedInstructions], AccumulatorValue, LastAccumulatorValue).
process_instructions(Counter, LastCounter, ProcessedInstructions, AccumulatorValue, LastAccumulatorValue):-
    (
        instruction(Counter-[Operation, Argument]),
        member(Counter-[Operation, Argument], ProcessedInstructions),
        process_instructions(Counter, LastCounter, [-1], AccumulatorValue, LastAccumulatorValue)
    );
    instruction(Counter-[Operation, Argument]),
    Operation == nop,
    C is Counter + 1,  
    process_instructions(C, LastCounter, [Counter-[Operation, Argument]|ProcessedInstructions], AccumulatorValue, LastAccumulatorValue).  
process_instructions(Counter, LastCounter, _, AccumulatorValue, LastAccumulatorValue):-
    instruction(Counter-[Operation, _]),
    Operation == null,
    process_instructions(Counter, LastCounter, [0], AccumulatorValue, LastAccumulatorValue).

test_run(End, LastAccumulatorValue):-
    process_instructions(LastCounter, LastAccumulatorValue),!,
    LastCounter == End.
    
test_jmp_change([], _, LastAccumulatorValue).
test_jmp_change([[Counter, Argument]|T], End, LastAccumulatorValue):-
    (
        retract(instruction(Counter-[jmp, Argument])),
        assert(instruction(Counter-[nop, Argument])),
        test_run(End, LastAccumulatorValue)
    );
    (
        retract(instruction(Counter-[nop, Argument])),
        assert(instruction(Counter-[jmp, Argument])),
        test_jmp_change(T, End, LastAccumulatorValue)
    ).
    
fix_instruction(LastAccumulatorValue):-
    instruction(CounterNull-[null, _]),
    findall([Counter, Argument], instruction(Counter-[jmp, Argument]), JmpInstructions),
    test_jmp_change(JmpInstructions, CounterNull, LastAccumulatorValue).
    
main:-
    open('data', read, Stream),
    read_data(Stream, Records),
    close(Stream),
    label_instructions(Records, _),
    process_instructions(LastCounter, LastAccumulatorValue),
    format("Last counter value: ~d, last accumulator value: ~d.~n", [LastCounter, LastAccumulatorValue]),
    fix_instruction(FixedLastAccumulatorValue),
    format("Last accumulator value after fix applied: ~d.~n", [FixedLastAccumulatorValue]).

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]).

2020 Advent Of Code Day 6

Prolog. Developed and tested with SWI-Prolog 8.0.3.

check_and_read(10, [] ,_):-
    !.
check_and_read(13, [], _):-
    !.
check_and_read(32, [], _):-
    !.
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).
 
records(Lines, Records):-
    records(Lines, [[]], Records).
records([], Records, Records).
records([H|T], [HAccum|TAccum], Records):-
    atom_codes(H, [C|_]),
    \+ C == 32,
    append(HAccum, [H], NewHAccum),
    records(T, [NewHAccum|TAccum], Records).
records([_|T], [HAccum|TAccum], Records):-    
    records(T, [[],HAccum|TAccum], Records).

seent(Codes, Seent, NotSeent):-
    seent(Codes, Seent, [], NotSeent).
seent([], _, NotSeent, NotSeent).
seent([H|T], Seent, NotSeentAccum, NotSeent):-
    \+ member(H, Seent),
    seent(T, Seent, [H|NotSeentAccum], NotSeent).
seent([_|T], Seent, NotSeentAccum, NotSeent):-
    seent(T, Seent, NotSeentAccum, NotSeent).
    
unique_yes(Answers, Yeses):-
    unique_yes(Answers, [], Yeses).
unique_yes([], AnswerAccum, Yeses):-
    length(AnswerAccum, Yeses).    
unique_yes([H|T], AnswerAccum, Yeses):-
    atom_codes(H, C),
    seent(C, AnswerAccum, NotSeent),
    append(NotSeent, AnswerAccum, NewAccum),
    unique_yes(T, NewAccum, Yeses).

process_groups_unique(Records, Yeses):-
    process_groups_unique(Records, [], Yeses).
process_groups_unique([], Yeses, Yeses).
process_groups_unique([H|T], YesesAccum, Yeses):-
    unique_yes(H, Ys),
    process_groups_unique(T, [Ys|YesesAccum], Yeses).

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).
    
check_yes([H|T], AllYes):-
    atom_codes(H, C),
    unique(C, U),
    check_yes(T, U, AllYes).
check_yes([], AllYes, AllYes).   
check_yes([H|T], AllYesAccum, AllYes):-
    atom_codes(H, C),
    unique(C, U),
    intersection(U, AllYesAccum, NewAccum),
    check_yes(T, NewAccum, AllYes).

process_groups_every(Records, Yeses):-
    process_groups_every(Records, [], Yeses).
process_groups_every([], Yeses, Yeses).
process_groups_every([H|T], YesesAccum, Yeses):-
    check_yes(H, Ys),
    length(Ys, LY),
    process_groups_every(T, [LY|YesesAccum], Yeses).

sum_yes(Yeses, Sum):-
    sum_yes(Yeses, 0, Sum).
sum_yes([], Sum, Sum).
sum_yes([H|T], PartialSum, Sum):-
    S is H + PartialSum,
    sum_yes(T, S, Sum).
 
main:-
    open('data', read, Stream),
    read_data(Stream, Lines),
    close(Stream),
    records(Lines, Records),
    process_groups_unique(Records, YesesUnique),
    sum_yes(YesesUnique, SumUnique),
    format("Sum of yes answers across all groups is ~d.~n", [SumUnique]),
    process_groups_every(Records, YesesEvery),
    sum_yes(YesesEvery, SumEvery),
    format("Sum of unanimous yes answers across all groups is ~d.~n", [SumEvery]).