read_file(Stream, []) :- at_end_of_stream(Stream). read_file(Stream, [X|L]) :- \+ at_end_of_stream(Stream), read_line_to_codes(Stream, C), string_chars(X, C), read_file(Stream, L). split1([], [], []). split1([""|As], [], As). % starts with "" -> [], seq end split1([A|As], [A|Bs], X) :- A \= "", split1(As, Bs, X). split2([], []). split2([A|As], [B|Bs]) :- split1([A|As], B, C), split2(C, Bs). remove_x(_, [], []). remove_x(X, [X|As], Bs) :- remove_x(X, As, Bs). remove_x(X, [A|As], [A|Bs]) :- A \= X, remove_x(X, As, Bs). numbers_strings([], []). numbers_strings([A|As], [B|Bs]) :- number_string(A, B), numbers_strings(As, Bs). strings_board([], []). strings_board([A|As], [B|Bs]) :- split_string(A, " ", "", C), remove_x("", C, D), numbers_strings(B, D), strings_board(As, Bs). stringss_boards([], []). stringss_boards([A|As], [B|Bs]) :- strings_board(A, B), stringss_boards(As, Bs). parse_boards(L, A) :- split2(L, B), remove_x([], B, C), stringss_boards(C, A). parse_file([X|L], N, B) :- split_string(X, ",", "", A), numbers_strings(N, A), parse_boards(L, B). main :- open('../input/04', read, Stream), read_file(Stream, Lines), !, close(Stream), parse_file(Lines, A, B), solve(A, B, C), print(C). mark_nums_row(_, [], []). mark_nums_row(N, [R|Rs], [1|Xs]) :- append([_, [R], _], N), mark_nums_row(N, Rs, Xs). mark_nums_row(N, [R|Rs], [0|Xs]) :- \+ append([_, [R], _], N), mark_nums_row(N, Rs, Xs). mark_nums_board(_, [], []). mark_nums_board(N, [B|Bs], [C|Cs]) :- mark_nums_row(N, B, C), mark_nums_board(N, Bs, Cs). rows_win([R|Rs]) :- (R = [1,1,1,1,1]; rows_win(Rs)). transpose([[]|_], []). transpose(A, [B|Bs]) :- first_column(A, B, C), transpose(C, Bs). first_column([], [], []). first_column([[B|C]|As], [B|Bs], [C|Cs]) :- first_column(As, Bs, Cs). marked_board_win(B) :- rows_win(B). marked_board_win(B) :- transpose(B, T), rows_win(T). unmarked_row_sum([], [], 0). unmarked_row_sum([_|As], [1|Ms], C) :- unmarked_row_sum(As, Ms, C). unmarked_row_sum([A|As], [0|Ms], C) :- unmarked_row_sum(As, Ms, C1), C is A + C1. unmarked_board_sum([], [], 0). unmarked_board_sum([A|As], [B|Bs], C) :- unmarked_row_sum(A, B, C1), unmarked_board_sum(As, Bs, C2), C is C1 + C2. last_num([R], R). last_num([_|Rs], X) :- last_num(Rs, X). board_win(N, B, X) :- mark_nums_board(N, B, C), marked_board_win(C), unmarked_board_sum(B, C, Z), last_num(N, Y), X is Y * Z. win_order(_, [], [], []). win_order(N, [B|Bs], C, [B|Rs]) :- \+ board_win(N, B, _), win_order(N, Bs, C, Rs). win_order(N, [B|Bs], [C|Cs], R) :- board_win(N, B, C), win_order(N, Bs, Cs, R). step_win_order(N, B, C) :- step_win_order(N, [], B, C). step_win_order(_, _, [], []). step_win_order([A|As], [], B, C) :- step_win_order(As, [A], B, C). step_win_order([A|As], [N|Ns], B, C) :- win_order([N|Ns], B, C0, R), append([N|Ns], [A], D), step_win_order(As, D, R, C1), append(C0, C1, C). step_win_order([], N, B, C) :- win_order(N, B, C, _). solve(N, B, C) :- step_win_order(N, B, D), last_num(D, C).