/* -*- Prolog -*- */

 /* ペントミノパズルを解くPrologプログラム
    ペントミノパズルが何であるかについては、Googleなどで検索して下さい。
    動かすには、このプログラムを読み込んで、?- pent(X). とします。
    最初の解が出た後、別解を出力するには「;」を入力、解探索を打ち切る
    にはエンターキーを入力します。
    SWISHを利用している場合は、タブを開いてこのプログラムをカット&ペースト
    し、画面右下queryのところにpent(X).と入力してRunをクリックします */

 /* 駒の形状のデータ */
koma(a, [1,0, 2,0, 3,0, 4,0]).
koma(a, [0,1, 0,2, 0,3, 0,4]).

koma(b, [0,1, 1,1, 2,1, 3,1]).
koma(b, [-3,1, -2,1, -1,1, 0,1]).
koma(b, [1,0, 2,0, 3,0, 0,1]).
koma(b, [1,0, 2,0, 3,0, 3,1]).
koma(b, [1,0, 0,1, 0,2, 0,3]).
koma(b, [1,0, 1,1, 1,2, 1,3]).
koma(b, [0,1, 0,2, -1,3, 0,3]).
koma(b, [0,1, 0,2, 0,3, 1,3]).

koma(c, [-1,1, 0,1, 1,1, 2,1]).
koma(c, [-2,1, -1,1, 0,1, 1,1]).
koma(c, [1,0, 2,0, 3,0, 1,1]).
koma(c, [1,0, 2,0, 3,0, 2,1]).
koma(c, [0,1, 1,1, 0,2, 0,3]).
koma(c, [-1,1, 0,1, 0,2, 0,3]).
koma(c, [0,1, 0,2, 1,2, 0,3]).
koma(c, [0,1, -1,2, 0,2, 0,3]).

koma(d, [1,0, 0,1, 1,1, 2,1]).
koma(d, [1,0, -1,1, 0,1, 1,1]).
koma(d, [1,0, 2,0, 0,1, 1,1]).
koma(d, [1,0, 2,0, 1,1, 2,1]).
koma(d, [0,1, 1,1, 0,2, 1,2]).
koma(d, [-1,1, 0,1, -1,2, 0,2]).
koma(d, [1,0, 0,1, 1,1, 0,2]).
koma(d, [1,0, 0,1, 1,1, 1,2]).

koma(e, [2,0, 0,1, 1,1, 2,1]).
koma(e, [1,0, 2,0, 0,1, 2,1]).
koma(e, [1,0, 0,1, 0,2, 1,2]).
koma(e, [1,0, 1,1, 0,2, 1,2]).

koma(f, [1,0, -2,1, -1,1, 0,1]).
koma(f, [1,0, 1,1, 2,1, 3,1]).
koma(f, [1,0, 2,0, 2,1, 3,1]).
koma(f, [1,0, 2,0, -1,1, 0,1]).
koma(f, [0,1, 0,2, 1,2, 1,3]).
koma(f, [0,1, -1,2, 0,2, -1,3]).
koma(f, [0,1, 1,1, 1,2, 1,3]).
koma(f, [-1,1, 0,1, -1,2, -1,3]).

koma(g, [0,1, -1,2, 0,2, 1,2]).
koma(g, [1,0, 2,0, 1,1, 1,2]).
koma(g, [0,1, 1,1, 2,1, 0,2]).
koma(g, [-2,1, -1,1, 0,1, 0,2]).

koma(h, [-1,1, 0,1, 1,1, 0,2]).

koma(i, [-1,1, 0,1, 1,1, 1,2]).
koma(i, [-1,1, 0,1, 1,1, -1,2]).
koma(i, [0,1, 1,1, 2,1, 1,2]).
koma(i, [-2,1, -1,1, 0,1, -1,2]).
koma(i, [1,0, 1,1, 2,1, 1,2]).
koma(i, [0,1, 1,1, -1,2, 0,2]).
koma(i, [1,0, -1,1, 0,1, 0,2]).
koma(i, [-1,1, 0,1, 0,2, 1,2]).

koma(j, [0,1, 1,1, 2,1, 2,2]).
koma(j, [-2,1, -1,1, 0,1, -2,2]).
koma(j, [1,0, 1,1, 1,2, 2,2]).
koma(j, [1,0, 0,1, -1,2, 0,2]).

koma(k, [-1,1, 0,1, -2,2, -1,2]).
koma(k, [0,1, 1,1, 1,2, 2,2]).
koma(k, [1,0, -1,1, 0,1, -1,2]).
koma(k, [1,0, 1,1, 2,1, 2,2]).

koma(l, [0,1, 0,2, 1,2, 2,2]).
koma(l, [0,1, -2,2, -1,2, 0,2]).
koma(l, [1,0, 2,0, 0,1, 0,2]).
koma(l, [1,0, 2,0, 2,1, 2,2]).



/* X×Yの2次元配列のように使える複合項を作る */

make2Darray(X, Y, A) :- 
    functor(A, row, Y),
    makeEachRow(A, Y, X).

/* make2Darrayの下請け */

makeEachRow(A, N, X) :- N > 0,
    functor(B, col, X),
    arg(N, A, B), 
    N1 is N-1, makeEachRow(A, N1, X).
makeEachRow(_, 0, _).

/* make2Darrayで作った複合項の要素にアクセス */

getElement(A, X, Y, P) :-
    Y >= 0, arg(Y, A, B), X >= 0, arg(X, B, P).

/* 駒を置く */

hamekomi(A, X, Y, L, P) :-
    getElement(A, X, Y, P),
    hamekomi_sub(A, X, Y, L, P).
    

hamekomi_sub(A, X, Y, L, P) :-
    L = [L1, L2|L3], 
    X1 is X + L1, Y1 is Y + L2,
    getElement(A, X1, Y1, P),
    hamekomi_sub(A, X, Y, L3, P).

hamekomi_sub(_, _, _, [], _).

/* 盤Aを画面に出力 */

disp_ban(A) :- arg(_, A, B) , disp_col(B), fail.
disp_ban(_).

/* disp_banの下請け、盤の1行を画面に出力し改行 */

disp_col(B) :- arg(_, B, C), disp_pt(C), fail.
disp_col(_) :- nl.

/* disp_colの下請け、盤の1マスを画面に出力 */

disp_pt(C) :- var(C), write('.').
disp_pt(C) :- atom(C), write(C).

/* 空いているマスの座標 */

next_aki(A, X, Y, X1, Y1) :-
    getElement(A, X, Y, P), var(P), !,
    X1 = X, Y1 = Y.
    
next_aki(A, X, Y, X1, Y1) :-
    getElement(A, X, Y, P), atom(P), !,
    X2 is X + 1, 
    next_aki(A, X2, Y, X1, Y1).

next_aki(A, X, Y, X1, Y1) :-
    \+getElement(A, X, Y, _),
    Y2 is Y + 1, X2 is 1,
    next_aki(A, X2, Y2, X1, Y1).


/* 駒置き盤 */


ume(_, _, _, []).

ume(Ban, X, Y, Komalist) :- \+Komalist = [],
    next_aki(Ban, X, Y, NX, NY),
    select(P, Komalist, Rest),
    koma(P, L),
    hamekomi(Ban, NX, NY, L, P),
    ume(Ban, NX, NY, Rest).



pent(X) :-
    make2Darray(6, 10, X),
    ume(X, 1, 1, [a, b, c, d, e, f, g, h, i, j, k, l]),
    disp_ban(X).
