adamcrussell (adamcrussell) wrote,
adamcrussell
adamcrussell

2020 Advent Of Code Day 2

Prolog. Developed and tested with SWI-Prolog 8.0.3.

letter_counts_init(Counts):-
    letter_counts_init(0, [], Counts).
letter_counts_init(26, Counts, Counts).    
letter_counts_init(I, CountsAccum, Counts):-
    I0 is I + 1,
    letter_counts_init(I0, [0|CountsAccum], Counts).

letter_counts_update(Index, Value, Counts, NewCounts):-
    letter_counts_update(Index, -1, Value, Counts, [], NewCounts).
letter_counts_update(_, _, _, [], NewCounts, NewCounts).    
letter_counts_update(Index, Counter, Value, [_|T], CountsAccum, NewCounts):-
    C is Counter + 1,
    Index == C,
    append(CountsAccum, [Value], NewCountsAccum),
    letter_counts_update(Index, C, Value, T, NewCountsAccum, NewCounts).
letter_counts_update(Index, Counter, Value, [H|T], CountsAccum, NewCounts):-
    C is Counter + 1,
    append(CountsAccum, [H], NewCountsAccum),
    letter_counts_update(Index, C, Value, T, NewCountsAccum, NewCounts).

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

count_letters(Password, Counts):-
    letter_counts_init(CountsAccum),
    count_letters(Password, CountsAccum, Counts).
count_letters([], Counts, Counts).
count_letters([H|T], CountsAccum, Counts):-
    Index is H - 97,
    nth0(Index, CountsAccum, Count),
    C is Count + 1,
    letter_counts_update(Index, C, CountsAccum, NewCountsAccum),
    count_letters(T, NewCountsAccum, Counts).

letter_counts(Password, Counts):-
    atom_codes(Password, Chars),
    count_letters(Chars, Counts).

low(HighLow, Low):-
    low(HighLow, '', Low).
low([], Low, Low).
low([H|T], LowAccum, Low):-
    H @>= '0', H @=< '9',
    atom_concat(LowAccum, H, Accum),
    low(T, Accum, Low).
low([_|_], LowAccum, Low):-
    low([], LowAccum, Low).

high(HighLow, High):-
    high(HighLow, '', High).
high([], High, High).
high([H|T], HighAccum, High):-
    H @>= '0', H @=< '9',
    atom_concat(HighAccum, H, Accum),
    high(T, Accum, High).
high([_|T], _, High):-
    high(T, '', High).

process_lines(Lines, ProcessedLines):-
    process_lines(Lines, [], ProcessedLines).
process_lines([], ProcessedLines, ProcessedLines).
process_lines([C, L, Password|Tail], ProcessedAccum, ProcessedLines):-
    atom_chars(C, HighLow),
    high(HighLow, High),
    low(HighLow, Low),
    atom_chars(L, [Letter|_]),
    letter_counts(Password, Counts),
    process_lines(Tail, [Password, Low, High, Letter, Counts|ProcessedAccum], ProcessedLines).

valid_password_one(Letter, Low, High, Counts):-
    atom_codes(Letter, L),
    Index is L - 97,
    nth0(Index, Counts, Count),
    atom_codes(Low, I),
    number_codes(L0, I),
    Count >= L0,
    atom_codes(High, J),
    number_codes(H0, J),
    Count =< H0.

valid_passwords_one(ProcessedLines, ValidPasswords):-
    valid_passwords_one(ProcessedLines, [], ValidPasswords).
valid_passwords_one([], ValidPasswords, ValidPasswords).
valid_passwords_one([Password, Low, High, Letter, Counts|Tail], ValidPasswordAccum, ValidPasswords):-
    valid_password_one(Letter, Low, High, Counts),
    valid_passwords_one(Tail, [Password|ValidPasswordAccum], ValidPasswords).
valid_passwords_one([_, _, _, _, _|Tail], ValidPasswordAccum, ValidPasswords):-
    valid_passwords_one(Tail, ValidPasswordAccum, ValidPasswords).

validate_one(ProcessedLines, ValidPasswords):-
    valid_passwords_one(ProcessedLines, ValidPasswords).
    
valid_password_two(Letter, Low, High, Password):-
    atom_chars(Password, PasswordChars),
    atom_codes(Low, I),
    number_codes(L0, I),
    atom_codes(High, J),
    number_codes(H0, J),
    nth1(L0, PasswordChars, E0),
    nth1(H0, PasswordChars, E1),
    \+ (Letter == E0, Letter == E1),
    (Letter == E0; Letter == E1).

valid_passwords_two(ProcessedLines, ValidPasswords):-
    valid_passwords_two(ProcessedLines, [], ValidPasswords).
valid_passwords_two([], ValidPasswords, ValidPasswords).
valid_passwords_two([Password, Low, High, Letter, _|Tail], ValidPasswordAccum, ValidPasswords):-
    valid_password_two(Letter, Low, High, Password),
    valid_passwords_two(Tail, [Password|ValidPasswordAccum], ValidPasswords).
valid_passwords_two([_, _, _, _, _|Tail], ValidPasswordAccum, ValidPasswords):-
    valid_passwords_two(Tail, ValidPasswordAccum, ValidPasswords).

validate_two(ProcessedLines, ValidPasswords):-
    valid_passwords_two(ProcessedLines, ValidPasswords).    
 
main:-
    open('data', read, Stream),
    read_data(Stream, Lines),
    close(Stream),
    process_lines(Lines, ProcessedLines),
    validate_one(ProcessedLines, ValidPasswordsOne),
    length(ValidPasswordsOne, NumberValidOne),
    format("~d valid passwords with method one~n",[NumberValidOne]),
    validate_two(ProcessedLines, ValidPasswordsTwo),
    length(ValidPasswordsTwo, NumberValidTwo),
    format("~d valid passwords with method two~n",[NumberValidTwo]).
Tags: advent of code, logic programming, prolog, prolog programming
Subscribe

Recent Posts from This Journal

Comments for this post were disabled by the author