第五回オフラインリアルタイムどう書くの回答例(Prolog)

泣きそうになりながら丸1日かけて書きました。
お題はこちら http://qiita.com/items/5c10c132e1f78131563f
patterns.tsvは`#1	DJ,	-`というタブ区切りの行が並ぶテキストファイルを想定しています。

```prolog:answer.prolog

rank(0'3,  0).
rank(0'4,  1).
rank(0'5,  2).
rank(0'6,  3).
rank(0'7,  4).
rank(0'8,  5).
rank(0'9,  6).
rank(0'T,  7).
rank(0'J,  8).
rank(0'Q,  9).
rank(0'K, 10).
rank(0'A, 11).
rank(0'2, 12).
rank(0'o, 13).

join([], []).
join([[Sx, Rx]|Xs], [Sx,Ry|Ys]) :-
    rank(Ry, Rx),
    join(Xs, Ys).

join(_, [], "-").
join(_, [X], Y) :-
    join(X, Y),
    !.
join(C, [X|Xs], Ys) :-
    join(X, Z),
    join(C, Xs, Zs),
    append(Z, [C], Y),
    append(Y, Zs, Ys).

permutation(_, 0, []).
permutation(Xs, N, [Y|Ys]) :-
    N > 0,
    select(Y, Xs, Zs),
    N1 is N - 1,
    permutation(Zs, N1, Ys).

combination(Xs, N, Ys) :-
    findall(Z, permutation(Xs, N, Z), Zs),
    maplist(sort, Zs, Ws),
    sort(Ws, Ys).

same_rank([[_,R]], R).
same_rank([[_,R1]|Xs], R2) :- same_rank(Xs, R2), R1 == 13.
same_rank([[_,R1]|Xs], R1) :- same_rank(Xs, R2), R2 == 13.
same_rank([[_,R1]|Xs], R1) :- same_rank(Xs, R2), R1 == R2.

filter(_, [], []).
filter(R, [X|Xs], [X|Ys]) :-
    same_rank(X, R1),
    R < R1,
    filter(R, Xs, Ys),
    !.
filter(R, [_|Xs], Ys) :-
    filter(R, Xs, Ys).

select_cards(R, N, Hs, Rs) :- % R:ランク N:場の枚数 Hs:手札 Rs:出せるカードの組み合わせのリスト
    combination(Hs, N, Cs),   % 手札からN枚の組み合わせのリストCsを得る
    filter(R, Cs, Zs),        % 手札の組み合わせCsのうち、場に出せるもの（ランクが高いもの）を選ぶ
    join(44, Zs, Rs).         % 結果を連結して文字列にする

input_hands([], []).
input_hands([S,Ri|Is], [[S,R]|Hs]) :-
    rank(Ri, R),
    input_hands(Is, Hs).

input([44|Is], [], Hs) :-
    input_hands(Is, Hs).
input([S,Ri|Is], [[S,R]|Fs], Hs) :-
    rank(Ri, R),
    input(Is, Fs, Hs).

rank_and_number([[_,R]], R, 1).
rank_and_number([[_,Rx]|Xs], R, N) :-
    rank_and_number(Xs, R1, N1),
    R is min(R1, Rx),
    N is N1 + 1.

solve(Input, Actual) :-
    input(Input, Fs, Hs),
    rank_and_number(Fs, R, N),
    select_cards(R, N, Hs, Rs),
    format_to_chars("~s", [Rs], Actual).
```

```prolog:test.prolog

read_contents(Cs) :-
    get_code(C),
    read_contents(C, Cs).

read_contents(-1, []). % ストリームの終端検出（get_code は -1 を返す）
read_contents(C, [C|Cs]) :-
    C > 0, 
    read_contents(Cs).

split(_, [], [[]]).
split(C, [C], [[]]) :- !. % 末尾の区切り文字は無視する
split(C, [X|Xs], [[X|Zs]|Ys]) :-
    C \== X,
    split(C, Xs, [Zs|Ys]).
split(C, [C|Xs], [[]|Ys]) :-
    split(C, Xs, Ys).

lines(Xs, Ys) :- split(10, Xs, Ys).

words(Xs, Ys) :- split(9, Xs, Ys).

card([], []).
card("-", "-").
card([Sx,Rx|Xs], [[Sx,Rx]|Ys]) :- card(Xs, Ys).

cards(Xs, Ys) :-
    card(Xs, Zs), % 文字列をカード（スートとランクの組み合わせ）のリストに分解する
    sort(Zs, Ys). % ソートする

sort_cards(Xs, Ys) :-       % カードの組み合わせのリストをソートする
    maplist(cards, Xs, Zs), % 各組み合わせの中のカードをソートする
    sort(Zs, Ys).

test([Name, Input, Expected|_], R) :-
    format("Test ~s~n", [Name]),
    solve(Input, Actual),
    split(44, Actual, As),
    split(44, Expected, Es),
    sort_cards(As, Ass),
    sort_cards(Es, Ess),
    report(Name, Actual, Expected, Ass, Ess, R).

report(_, _, _, Expected, Expected, success).
report(Name, Actual, Expected, Ass, Ess, failure) :-
    Ass \== Ess, 
    format("Failure in ~s~nexpected: ~s~n  actual: ~s~n", [Name, Expected, Actual]).

count_failure([], 0).
count_failure([R|Rs], C) :-
    count_failure(Rs, C1),
    (R == failure -> C is C1 + 1; C is C1).

tests :-
    consult('answer.prolog'),
    see('patterns.tsv'),
    read_contents(Cs),
    seen,
    lines(Cs, Ls),
    maplist(words, Ls, Ps),
    maplist(test, Ps, Rs),
    length(Rs, Cases),
    count_failure(Rs, Failures),
    format("~nCases: ~d  Failures: ~d~n", [Cases, Failures]).
```