language: swi prolog prompt: https://adventofcode.com/2022/day/8


#!/usr/bin/env -S swipl -fq
:- use_module(library(clpfd)).
:- initialization main.

main :-
    file_matrix("input", Trees),

    solve_1(Trees, Sum),
    writeln(Sum),

    solve_2(Trees, BestScore),
    writeln(BestScore),

    halt.

% --- Utility ---

rotate(Goal, As, Bs) :-
    transpose(As, AsRot), 
    call(Goal, AsRot, BsRot),
    transpose(BsRot, Bs).

mirror(Goal, As, Bs) :-
    maplist(reverse, As, AsRev),
    call(Goal, AsRev, BsRev),
    maplist(reverse, BsRev, Bs).

split(0, [It|After], [], It, After).
split(N, [Item|List], Before, It, After) :-
    NextN is N - 1,
    split(NextN, List, RestBefore, It, After),
    Before = [Item|RestBefore].

visible(Tallest, [Tree|Trees], Visibles) :-
    (   Tree > Tallest
    ->  visible(Tree, Trees, RestVisibles),
        Visibles = [1 | RestVisibles]
    ;   visible(Tallest, Trees, RestVisibles),
        Visibles = [0 | RestVisibles]
    ).
visible(_, _, []).

% --- Stuff for part 1 ---

solve_1(Trees, Sum) :-
    visible_from_w(Trees, W),
    visible_from_e(Trees, E),
    rotate(visible_from_w, Trees, N),
    rotate(visible_from_e, Trees, S),
    count_visibles(N, E, S, W, Sum).

count_visibles(A, B, C, D, Sum) :-
    maplist(merge_visibles, A, B, C, D, Merged),
    maplist(sum_list, Merged, RowSums),
    sum_list(RowSums, Sum).

merge_visibles([A|As], [B|Bs], [C|Cs], [D|Ds], [N|Ns]) :-
        merge_visibles(As, Bs, Cs, Ds, Ns),
        (member(1, [A,B,C,D]) -> N is 1 ; N is 0 ).
merge_visibles([], [], [], [], []).

visible_from_e(Trees, Visibles) :-
    mirror(visible_from_w, Trees, Visibles).

visible_from_w(Trees, Visibles) :-
    maplist(visible(-1), Trees, Visibles).

% --- Stuff for Part 2 ---

take_until(_, [], []).
take_until(Goal, [A|As], Bs) :-
    (   call(Goal, A)
    ->  Bs = [A]
    ;   take_until(Goal, As, RestBs),
        Bs = [A | RestBs]
    ).

solve_2(Trees, Score) :-
    nth0(0, Trees, Row1),
    length(Row1, NCol),
    length(Trees, NRow),
    LastCol is NCol - 2,
    LastRow is NRow - 2,
    scenic_score([1, 1], [LastRow, LastCol], 0, Trees, Score).

scenic_score([Row, Col], [LastRow, LastCol], PrevScore, Trees, BestScore) :-
    split(Row, Trees, AboveRows, TreeRow, BelowRows),
    maplist(nth0(Col), AboveRows, AboveRev),
    reverse(AboveRev, Above),
    maplist(nth0(Col), BelowRows, Below),
    split(Col, TreeRow, BeforeRev, It, After),
    reverse(BeforeRev, Before),
    take_until(=<(It), Above, Ns),
    take_until(=<(It), Below, Ss),
    take_until(=<(It), Before, Es),
    take_until(=<(It), After, Ws),
    length(Ns, N),
    length(Ss, S),
    length(Es, E),
    length(Ws, W),

    Score is N * S * E * W,

    (   Score > PrevScore
    ->  NextScore = Score
    ;   NextScore = PrevScore
    ),

    (   Col < LastCol
    ->  NextCol is Col + 1,
        scenic_score([Row, NextCol], [LastRow, LastCol], NextScore, Trees, BestScore)
    ;   (   Row < LastRow
        ->  NextRow is Row + 1,
            scenic_score([NextRow, 0], [LastRow, LastCol], NextScore, Trees, BestScore)
        ;   BestScore = NextScore
        )
    ).

% --- Reading the file into a number matrix ---

file_matrix(File, Matrix) :-
    setup_call_cleanup(open(File, read, In),
       stream_matrix(In, Matrix),
       close(In)).

stream_matrix(In, Matrix) :-
    read_string(In, _, Str),
    split_string(Str, "\n", "", Lines),
    exclude(=(""), Lines, Lines2),
    maplist(digits_numbers, Lines2, Matrix).

digits_numbers(String, Numbers) :-
    string_codes(String, Codes),
    maplist(number_code, Numbers, Codes).

number_code(Number, Code) :-
    number_codes(Number, [Code]).