% -*-prolog-*-

:- include('tplace.pl')

is_win(Board, Stone, Opponent, Score) :-
	count_discs_diff(Board, Stone, Opponent, R),
	Score is sign(R).

/*
is_win(Board, Stone, Opponent, Score) :-
	count_discs_diff(Board, Stone, Opponent, Score).
*/

%
% complete reading routine
%
surefire(Board, Self, IJLst, R) :-
	findall((S-(I, J)),
	        (member(I, [1, 2, 3, 4, 5, 6, 7, 8]),
		 member(J, [1, 2, 3, 4, 5, 6, 7, 8]),
		 nth(J, Board, Jth),
		 nth(I, Jth, IJth),
		 IJth = space,
		 eval_board(I, J, S)),
		PS),
	keysort(PS, Sorted),
	make_puttable(Sorted, Reversed),
	reverse(Reversed, Spaces),
	write(Spaces),
	surefire_sub(Spaces, Board, Self, _, (-1, 1), false, IJLst, R).


% no more puttable
surefire_sub([], Board, Stone, _, _, _, [], Score) :-
	flip_disc(Stone, Opponent),
	is_win(Board, Stone, Opponent, Score),
	!.

% there are where puttable.

surefire_sub(Spaces, Board, Stone, (PI, PJ), (Alpha, Beta), IsPassed, IJLst, Score) :-
	flip_disc(Stone, Opponent),
	% find all putable places
	findall((I, J, AfterBoard, Leaves),
	        (select((I, J), Spaces, Leaves),
	         place_disc(I, J, Stone, Opponent, Board, AfterBoard)),
		Putable),
	(Putable = [] ->
	    % in case no more puttable
	    (IsPassed = true ->
		(is_win(Board, Stone, Opponent, Score),
		 IJLst = [])
	        ;
		(MAlpha is -Alpha,
		 MBeta is -Beta,
		 surefire_sub(Spaces, Board, Opponent, (PI, PJ), (MBeta, MAlpha), true, IJLst, MScore),
		 Score is -MScore))
	    ;
	    % in case there are where puttable
	    findall((IJ, S),
	            surefire_sub_sub_first(Putable, Stone, Opponent, (Alpha, Beta), (IJ, S)),
		    [(IJLst, Score)])).


surefire_sub_sub_first([(I, J, After, Spaces)|Lst], Stone, Opponent, (Alpha, Beta), Result) :-
	MBeta is -Beta,
	MAlpha is -Alpha,
	surefire_sub(Spaces, After, Opponent, (I, J), (MBeta, MAlpha), false, IJLst, MScore),
	Score is -MScore,
	(Score >= Beta ->
	    Result = ([(I, J, Stone)|IJLst], Score)
	    ;
	    %surefire_sub_sub(Lst, Stone, Opponent, (Score, Beta), [(I, J, Stone)|IJLst], Result)).
	    (Score > Alpha ->
		surefire_sub_sub(Lst, Stone, Opponent, (Score, Beta), [(I, J, Stone)|IJLst], Result)
	        ;
		surefire_sub_sub(Lst, Stone, Opponent, (Alpha, Beta), [(I, J, Stone)|IJLst], Result))).

surefire_sub_sub([], _, _, (Alpha, _), PR, (PR, Alpha)).
surefire_sub_sub([(I, J, After, Spaces)|Lst], Stone, Opponent,  (Alpha, Beta), PrevResult, Result) :-
	MBeta is -Beta,
	MAlpha is -Alpha,
	surefire_sub(Spaces, After, Opponent, (I, J), (MBeta, MAlpha), false, IJLst, MScore),
	Score is -MScore,
	(Score >= Beta ->
	    Result = ([(I, J, Stone)|IJLst], Score)
	    ;
	    (Score > Alpha ->
		surefire_sub_sub(Lst, Stone, Opponent, (Score, Beta), [(I, J, Stone)|IJLst], Result)
	        ;
		surefire_sub_sub(Lst, Stone, Opponent, (Alpha, Beta), PrevResult, Result))).



make_puttable([], []).
make_puttable([_-S|Ls], [S|Rs]) :-
	make_puttable(Ls, Rs).

max_of(([], _), (IJ, S), (IJ, S)) :- !.
max_of((IJa, Sa), (IJb, Sb), (RIJ, RS)) :-
	(Sa > Sb ->
	    (RIJ = IJa,
	     RS = Sa)
	    ;
	    (RIJ = IJb,
	     RS = Sb)).

%
% complete reading routine
%

complete_reading_only_best_hand(Board, Self, IJLst, R) :-
	findall((S-(I, J)),
	        (member(I, [1, 2, 3, 4, 5, 6, 7, 8]),
		 member(J, [1, 2, 3, 4, 5, 6, 7, 8]),
		 nth(J, Board, Jth),
		 nth(I, Jth, IJth),
		 IJth = space,
		 eval_board(I, J, S)),
		PS),
	keysort(PS, Sorted),
	make_puttable(Sorted, Reversed),
	reverse(Reversed, Spaces),
	crobh_sub(Spaces, Board, Self, Self, _, (-100, 100), false, IJLst, R).

% no more spaces
crobh_sub([], Board, Stone, _, _, _, _, [], Score) :-
	flip_disc(Stone, Opponent),
	count_discs_diff(Board, Stone, Opponent, Score),
	!.

% there are where spaces
crobh_sub(Spaces, Board, Stone, Self, (PI, PJ), (Alpha, Beta), IsPassed, IJLst, Score) :-
	flip_disc(Stone, Opponent),
	% find all puttable places
	findall((I, J, AfterBoard, Leaves),
	        (select((I, J), Spaces, Leaves),
	         place_disc(I, J, Stone, Opponent, Board, AfterBoard)),
		Puttable),
	(Puttable = [] ->
	    % in case there isnt anywhere puttable
	    (IsPassed = true ->
		(count_discs_diff(Board, Stone, Opponent, Score),
		 IJLst = [])
	        ;
		(MAlpha is -Alpha,
		 MBeta is -Beta,
		 crobh_sub(Spaces, Board, Opponent, Self, (PI, PJ), (MBeta, MAlpha), true, IJLst, MScore),
		 Score is -MScore))
	    ;
	    % in case there are where puttable
	    findall((IJ, S),
	            crobh_sub_sub_first(Puttable, Stone, Opponent, Self, (Alpha, Beta), (IJ, S)),
		    [(IJLst, Score)])).

% first, try alpha-beta pruning only.
crobh_sub_sub_first([(I, J, After, Spaces)|Lst], Stone, Opponent, Self, 
	            (Alpha, Beta), Result) :-
	MBeta is -Beta,
	MAlpha is -Alpha,
	crobh_sub(Spaces, After, Opponent, Self, (I, J), (MBeta, MAlpha), false, IJLst, MScore),
	Score is -MScore,
	(Score >= Beta ->
	    Result = ([(I, J, Stone)|IJLst], Score)
	    ;
	    (Score > Alpha ->
		crobh_sub_sub(Lst, Stone, Opponent, Self, (Score, Beta), [(I, J, Stone)|IJLst], Result)
		;
		crobh_sub_sub(Lst, Stone, Opponent, Self, (Alpha, Beta), [(I, J, Stone)|IJLst], Result))).

crobh_sub_sub([], _, _, _, (Alpha, _), PR, (PR, Alpha)).

% use negascout method.
crobh_sub_sub([(I, J, After, Spaces)|Lst], Stone, Opponent, Self, (Alpha, Beta), PrevResult, Result) :-
	MAlpha is -Alpha,
	MAlpha1 is MAlpha - 1,
	MBeta is -Beta,
	crobh_sub(Spaces, After, Opponent, Self, (I, J), (MAlpha1, MAlpha), false, MIJLst, MScore1),
	Score1 is -MScore1,
	(Score1 >= Beta ->
	    Result = ([(I, J, Stone)|MIJLst], Score1)
	    ;
	    (Score1 > Alpha ->
		(crobh_sub(Spaces, After, Opponent, Self, (I, J), (MBeta, MScore1), false, IJLst, MScore2),
		 Score2 is -MScore2,
		 (Score2 >= Beta ->
		     Result = ([(I, J, Stone)|IJLst], Score2)
		     ;
		     crobh_sub_sub(Lst, Stone, Opponent, Self, (Score2, Beta), 
		                   [(I, J, Stone)|IJLst], Result)))
		     
		;
		crobh_sub_sub(Lst, Stone, Opponent, Self, (Alpha, Beta), PrevResult, Result))).





%
% 盤面評価関数達を定義する。
%

%
% 簡単な説明
% eval_puttable(+Spaces, +Board, +Stone, +Opponent, ?Score) 着手可能手数の評価
% eval_board(+I, +J, ?Score)                                盤面に付けられた点数を評価
% eval_open(+Board, +After, +I, +J, ?Score)                 開放度を評価
% eval_edge_block(+Board, +Stone, +Opponent, ?Score)        辺と角・確定石を評価
% 

eval_board(I, J, Score) :-
	Board =	[[9, 2, 7, 8, 8, 7, 2, 9],
	         [2, 1, 3, 4, 4, 3, 2, 1],
		 [7, 3, 5, 6, 6, 5, 3, 7],
		 [8, 4, 6, 0, 0, 6, 4, 8],
		 [8, 4, 6, 0, 0, 6, 4, 8],
		 [7, 3, 5, 6, 6, 5, 3, 7],
		 [2, 1, 3, 4, 4, 3, 2, 1],
		 [9, 2, 7, 8, 8, 7, 2, 9]],
	nth(I, Board, Ith),
	nth(J, Ith, Score).

%----------------------------------------------------------------------

%
% 着手可能手数を評価する。
% 最終深さのときにしか使わないのでは？それ以外のときは list の長さを求めれば良い。
%

eval_puttable(Spaces, Board, Stone, Opponent, Score) :-
	findall((I, J),
	        (select((I, J), Spaces, _),
		 place_disc(I, J, Stone, Opponent, Board, _)),
		Puttable),
	length(Puttable, Score).

%----------------------------------------------------------------------

%
% 開放度を評価する。
% Board で (I, J) に打つと、After になる。
%

% global variable に、盤面を入れる。
set_line_global([], _, _, _).
set_line_global([L|Ls], [A|As], N, M) :-
	g_assign(eval_befor(M, N), L),
	g_assign(eval_after(M, N), A),
	M1 is M + 1,
	set_line_global(Ls, As, N, M1).

set_board_global([], _, _).
set_board_global([B|Bs], [A|As], N) :-
	N1 is inc(N),
	set_line_global(B, A, N, 1),
	set_board_global(Bs, As, N1).

% リスト中 V であるものの数を数える。
count_if([], _, 0).
count_if([L|Ls], V, R) :-
	count_if(Ls, V, R1),
	(L = V ->
	    R is inc(R1)
	    ;
	    R is R1).
	    
% (I, J) の周りの開放度を計算する。
open_score(I, J, Score) :-
	g_read(eval_befor(I, J), B),
	g_read(eval_after(I, J), A),
	flip_disc(A, B), % A, B が flip されてないと駄目。
	MI is I - 1,
	PI is I + 1,
	MJ is J - 1,
	PJ is J + 1,
	g_read(eval_after(MI, MJ), L1),
	g_read(eval_after(MI,  J), L2),
	g_read(eval_after(MI, PJ), L3),
	g_read(eval_after(I , MJ), L4),
	g_read(eval_after(I , PJ), L5),
	g_read(eval_after(PI, MJ), L6),
	g_read(eval_after(PI,  J), L7),
	g_read(eval_after(PI, PJ), L8),
	count_if([L1, L2, L3, L4, L5, L6, L7, L8], space, Score).

% (DI, DJ) 方向に開放度を可能な限り計算する
eval_open_sub((I, J), (DI, DJ), Prev, Score) :-
	(open_score(I, J, R) ->
	    (Prev1 is R + Prev,
	     I1 is I + DI,
	     J1 is J + DJ,
	     eval_open_sub((I1, J1), (DI, DJ), Prev1, Score))
	    ;
	    Prev = Score).
	    
% Board を (I, J) に打った後 After になるとする。
% このときの開放度を計算し、それを Score とする。
eval_open(Board, After, I, J, Score) :-
	WW = wall,
	WL = [WW, WW, WW, WW, WW, WW, WW, WW, WW, WW],
	g_assign(eval_befor, g_array(10, g_array(WL))),
	g_assign(eval_after, g_array(10, g_array(WL))),
	set_board_global(Board, After, 1),
	PI is I + 1,
	PJ is J + 1,
	MI is I - 1,
	MJ is J - 1,
	eval_open_sub((MI, MJ), (-1, -1), 0, R1),
	eval_open_sub((MI,  J), (-1,  0), 0, R2),
	eval_open_sub((MI, PJ), (-1,  1), 0, R3),
	eval_open_sub(( I, MJ), ( 0, -1), 0, R4),
	eval_open_sub(( I, PJ), ( 0,  1), 0, R5),
	eval_open_sub((PI, MJ), ( 1, -1), 0, R6),
	eval_open_sub((PI,  J), ( 1,  0), 0, R7),
	eval_open_sub((PI, PJ), ( 1,  1), 0, R8),
	sum_list([R1, R2, R3, R4, R5, R6, R7, R8], Score).

%----------------------------------------------------------------------

%
% 辺 + 4 の好形・悪形の判断をする。
% ついでに、確定石(近似値)も計算する。
% S だけ考えておけばよい。反対側も同様に調査することになるから。

%
%    C9 C10      C11C12
% C1 C2 C3 C4 C5 C6 C7 C8
%

% 山は、好形であるので加点する。
edge_score([space, S, S, S, S, S, S, space, space, _, _, space], S, _, 10) :- !.

% ウィングは、基本的に悪形であるので、減点する。
edge_score([space, S, S, S, S, S, space, space, space, _, S, space], S, _, -8) :- !.
edge_score([space, space, S, S, S, S, S, space, space, S, _, space], S, _, -8) :- !.

% ブロックは、悪い形であることも多いので減点しておく。
edge_score([space, space, S, S, S, S, space, space, space, S, S, space], S, _, -5) :- !.

% 単独 C 打ちは大きく減点する。
edge_score([space, S, space, space, space, space, space, space|_], S, _, -40) :- !.
edge_score([space, S, S,     space, space, space, space, space|_], S, _, -32) :- !.
edge_score([space, space, space, space, space, space, S, space|_], S, _, -40) :- !.
edge_score([space, space, space, space, space, S,     S, space|_], S, _, -32) :- !.

% A, C 打ちは大きく減点する。
edge_score([space, space, space, space, S,     space, S, space|_], S, _, -45) :- !.
edge_score([space, S,     space, S, space, space, space, space|_], S, _, -40) :- !.

% 単独 C 打ちに対する反対側 A 打ちは、大きく加点する。
edge_score([space, O, space, space, space, S, space, space|_], S, O, 24) :- !.
edge_score([space, O, space, space,     S, S, space, space|_], S, O, 12) :- !.
edge_score([space, O,     O, space, space, S, space, space|_], S, O, 12) :- !.
edge_score([space, space, S, space, space, space, O, space|_], S, O, 24) :- !.
edge_score([space, space, S,     S, space, space, O, space|_], S, O, 12) :- !. 
edge_score([space, space, S, space, space,     O, O, space|_], S, O, 12) :- !.
edge_score([space, space, S, space,     S,     O, O, space|_], S, O, 60) :- !.
edge_score([space, space, S, space,     S,     S, O, space|_], S, O, 60) :- !.
edge_score([space, space, S, space,     S, space, O, space|_], S, O, 45) :- !.
edge_score([space, O,     O,     S, space, S, space, space|_], S, O, 60) :- !.
edge_score([space, O,     S,     S, space, S, space, space|_], S, O, 60) :- !.
edge_score([space, O,     space, S, space, S, space, space|_], S, O, 45) :- !.

% 確定石は加点する
edge_score([S, S, S, space, space, S, S, S|_], S, _, 240) :- !.
edge_score([S, S, S, S, space, space, S, S|_], S, _, 240) :- !.
edge_score([S, S, space, space, S, S, S, S|_], S, _, 240) :- !.
edge_score([S, S, S, S, S, space, space, S|_], S, _, 240) :- !.
edge_score([S, space, space, S, S, S, S, S|_], S, _, 240) :- !.
edge_score([S, space, space, space, S, S, S, S|_], S, _, 150) :- !.
edge_score([S, S, space, space, space, S, S, S|_], S, _, 150) :- !.
edge_score([S, S, S, space, space, space, S, S|_], S, _, 150) :- !.
edge_score([S, S, S, S, space, space, space, S|_], S, _, 150) :- !.

edge_score([S, S, S, S, S, S, S, S|_], S, _, 300) :- !.
edge_score([S, S, S, S, S, S, S, _|_], S, _, 270) :- !.
edge_score([_, S, S, S, S, S, S, S|_], S, _, 270) :- !.
edge_score([S, S, S, S, S, S, _, _|_], S, _, 220) :- !.
edge_score([_, _, S, S, S, S, S, S|_], S, _, 220) :- !.
edge_score([S, S, S, S, S, _, _, _|_], S, _, 200) :- !.
edge_score([_, _, _, S, S, S, S, S|_], S, _, 200) :- !.
edge_score([S, S, S, S, _, _, _, _|_], S, _, 150) :- !.
edge_score([_, _, _, _, S, S, S, S|_], S, _, 150) :- !.
edge_score([S, S, S, _, _, _, _, _|_], S, _, 100) :- !.
edge_score([_, _, _, _, _, S, S, S|_], S, _, 100) :- !.
edge_score([S, S, _, _, _, _, _, _|_], S, _,  70) :- !.
edge_score([_, _, _, _, _, _, S, S|_], S, _,  70) :- !.
edge_score([S, _, _, _, _, _, _, _|_], S, _,  60) :- !.
edge_score([_, _, _, _, _, _, _, S|_], S, _,  60) :- !.

% 以上のどれにも当てはまらない場合、零点。

edge_score(_, _, _, 0) :- !.

%
% 角の 3 * 3 + 2について評価を行なう。
% ここはまだそれほど手を入れてない。もうすこし評価を追加して欲しい。
%

% …
% C10
% C7 C8 C9
% C4 C5 C6
% C1 C2 C3 C11 …
%

% 確定石となるものは、加点する。

block_score([S, S, S, S, S, S, S, S, _, S, S], S, _, 200) :- !.
block_score([S, S, S, S, S, _, S, _, _, _, _], S, _, 150) :- !. 
block_score([S, S, _, S, _, _, _, _, _, _, _], S, _,  50) :- !.

% ハイパー偶数は加点する。
block_score([space, space, S, space, space, S, S, S, S, _, _], S, _, 8) :- !.

% 星打ちは、減点する。但し、角が既にふさがっている場合は、減点しない。
block_score([space, _, _, _, S|_], S, _, -45) :- !.


% 以上のどれでもなければ、零点とする。

block_score(_, _, _, 0) :- !.

eval_edge_block(Board, Stone, Opponent, Score) :-
	Board = [[A1, B1, C1, D1, E1, F1, G1, H1],
	         [A2, B2, C2,  _,  _, F2, G2, H2],
		 [A3, B3, C3,  _,  _, F3, G3, H3],
		 [A4,  _,  _,  _,  _,  _,  _, H4],
		 [A5,  _,  _,  _,  _,  _,  _, H5],
		 [A6, B6, C6,  _,  _, F6, G6, H6],
		 [A7, B7, C7,  _,  _, F7, G7, H7],
		 [A8, B8, C8, D8, E8, F8, G8, H8]],
	edge_score([A1, B1, C1, D1, E1, F1, G1, H1, B2, C2, F2, G2], Stone, Opponent, R1),
	edge_score([A1, A2, A3, A4, A5, A6, A7, A8, B2, B3, B6, B7], Stone, Opponent, R2),
	edge_score([H1, H2, H3, H4, H5, H6, H7, H8, G2, G3, G6, G7], Stone, Opponent, R3),
	edge_score([A8, B8, C8, D8, E8, F8, G8, H8, B7, C7, F7, G7], Stone, Opponent, R4),
	block_score([A8, B8, C8, A7, B7, C7, A6, B6, C6, A5, D8], Stone, Opponent, R5),
	block_score([H8, G8, F8, H7, G7, F7, H6, G6, F6, H5, E8], Stone, Opponent, R6),
	block_score([A1, B1, C1, A2, B2, C2, A3, B3, C3, A4, D1], Stone, Opponent, R7),
	block_score([H1, G1, F1, H2, G2, F2, H3, G3, F3, H4, E1], Stone, Opponent, R8),
	Score is R1 + R2 + R3 + R4 + R5 + R6 + R7 + R8.
	

%----------------------------------------------------------------------

%
% 評価関数
%

%
% alpha-beta を利用して N 手読む
%

eval(Board, N, Self, IJLst, R) :-
	findall((I, J),
	        (member(I, [1, 2, 3, 4, 5, 6, 7, 8]),
		 member(J, [1, 2, 3, 4, 5, 6, 7, 8]),
		 nth(J, Board, Jth),
		 nth(I, Jth, IJth),
		 IJth = space),
		Spaces),
	eval_sub_first(N, Spaces, Board, Self, Self, (_, _, 0), (-10000000, 10000000), false, IJLst, R).


eval_sub_first(N, Spaces, Board, Stone, Self, (_, _, OScore), (Alpha, Beta), _, IJLst, Score) :-
	flip_disc(Stone, Opponent),
	NextN is dec(N),
	% find all puttable places
	findall(PS-(I, J, OpenScore, AfterBoard, Leaves),
	        (select((I, J), Spaces, Leaves),
	         place_disc(I, J, Stone, Opponent, Board, AfterBoard),
		 eval_open(Board, AfterBoard, I, J, S),
		 OpenScore is S - OScore,
		 eval_sub_sub_first([(I, J, OpenScore, AfterBoard, Leaves)], 2, Stone, Opponent, Self, (Alpha, Beta), (_, PS1)),
		 PS is -PS1),
		Unsorted),
	keysort(Unsorted, Sorted),
	make_puttable(Sorted, Puttable),
	write('presearch end\n'),
	% Puttable should not be empty (because of my turn!!)
	findall((IJL, Sc),
	        eval_sub_first_sub_first(Puttable, NextN, Stone, Opponent, Self, (Alpha, Beta), (IJL, Sc)),
		[(IJLst, Score)]).

%
% 打ったあとの盤面を判定する。
% 大抵 Stone は、この時点で相手の番になっているはずである。
%
eval_sub(0, Spaces, Board, Stone, _, (_, _, OpenScore), _, _, [], Score) :-
	flip_disc(Stone, Opponent),
	eval_puttable(Spaces, Board, Stone, Opponent, PS),
	eval_puttable(Spaces, Board, Opponent, Stone, MPS),
	eval_edge_block(Board, Stone, Opponent, ES),
	eval_edge_block(Board, Opponent, Stone, MES), 
	Score is 3 * PS + 2 * ES + 2 * OpenScore - (2 * MES + 2 * MPS),
	% format("(~d, ~d) : ~d, ~d, ~d, ~d, ~d~n", [I, J, PS, MPS, OpenScore, ES, MES]),
	!.
%
%
%
eval_sub(N, Spaces, Board, Stone, Self, (PI, PJ, OScore), (Alpha, Beta), IsPassed, IJLst, Score) :-
	flip_disc(Stone, Opponent),
	NextN is dec(N),
	% find all puttable places
	findall((I, J, OpenScore, AfterBoard, Leaves),
	        (select((I, J), Spaces, Leaves),
	         place_disc(I, J, Stone, Opponent, Board, AfterBoard),
		 eval_open(Board, AfterBoard, I, J, S),
		 OpenScore is S - OScore),
		Puttable),
	(Puttable = [] ->
	    % in case there isn't anywhere puttable
	    (IsPassed = true ->
		(count_discs_diff(Board, Stone, Opponent, PScore),
		 Score is 1000000 * PScore,
		 IJLst = [])
	        ;
		(MAlpha is -Alpha,
		 MBeta is -Beta,
		 eval_sub(NextN, Spaces, Board, Opponent, Self, (PI, PJ, OScore), (MBeta, MAlpha), true, IJLst, MScore),
		 Score is -MScore))
	    ;
	    % in case there are where puttable
	    findall((IJ, S),
	            eval_sub_sub_first(Puttable, NextN, Stone, Opponent, Self, (Alpha, Beta), (IJ, S)),
		    [(IJLst, Score)])).


% first
eval_sub_sub_first([(I, J, OpenScore, After, Spaces)|Lst], N, Stone, Opponent, Self, (Alpha, Beta), Result) :-
	MAlpha is -Alpha,
	MBeta is -Beta,
	eval_sub(N, Spaces, After, Opponent, Self, (I, J, OpenScore), (MBeta, MAlpha), false, IJLst, MScore),
	Score is -MScore,
	(Score >= Beta ->
	    Result = ([(I, J, Stone)|IJLst], Score)
	    ;
	    (Score > Alpha ->
		eval_sub_sub(Lst, N, Stone, Opponent, Self, (Score, Beta), [(I, J, Stone)|IJLst], Result)
		;
		eval_sub_sub(Lst, N, Stone, Opponent, Self, (Alpha, Beta), [(I, J, Stone)|IJLst], Result))).

eval_sub_sub([], _, _, _, _, (Alpha, _), PR, (PR, Alpha)).

% with alpha-beta pruning 
eval_sub_sub([(I, J, OpenScore, After, Spaces)|Lst], N, Stone, Opponent, Self, (Alpha, Beta), PrevResult, Result) :-
	MAlpha is -Alpha,
	MBeta is -Beta,
	eval_sub(N, Spaces, After, Opponent, Self, (I, J, OpenScore), (MBeta, MAlpha), false, IJLst, MScore),
	Score is -MScore,
	(Score >= Beta ->
	    Result = ([(I, J, Stone)|IJLst], Score)
	    ;
	    (Score > Alpha ->
		eval_sub_sub(Lst, N, Stone, Opponent, Self, (Score, Beta), [(I, J, Stone)|IJLst], Result)
	        ;
		eval_sub_sub(Lst, N, Stone, Opponent, Self, (Alpha, Beta), PrevResult, Result))).

eval_sub_first_sub_first([(I, J, OpenScore, After, Spaces)|Lst], N, Stone, Opponent, Self, (Alpha, Beta), Result) :-
	MBeta is -Beta,
	MAlpha is -Alpha,
	eval_sub(N, Spaces, After, Opponent, Self, (I, J, OpenScore), (MBeta, MAlpha), false, IJLst, MScore),
	Score is -MScore,
	print_score([(I, J, Stone)|IJLst], Score),
	(Score >= Beta ->
	    Result = ([(I, J, Stone)|IJLst], Score)
	    ;
	    (Score > Alpha ->
		eval_sub_first_sub(Lst, N, Stone, Opponent, Self, (Score, Beta), [(I, J, Stone)|IJLst], Result)
		;
		eval_sub_first_sub(Lst, N, Stone, Opponent, Self, (Alpha, Beta), [(I, J, Stone)|IJLst], Result))).

eval_sub_first_sub([], _, _, _, _, (Alpha, _), PR, (PR, Alpha)).

eval_sub_first_sub([(I, J, OpenScore, After, Spaces)|Lst], N, Stone, Opponent, Self, (Alpha, Beta), PrevResult, Result) :-
	MAlpha is -Alpha,
	MBeta is -Beta,
	eval_sub(N, Spaces, After, Opponent, Self, (I, J, OpenScore), (MBeta, MAlpha), false, IJLst, MScore),
	Score is -MScore,
	print_score([(I, J, Stone)|IJLst], Score),
	(Score >= Beta ->
	    Result = ([(I, J, Stone)|IJLst], Score)
	    ;
	    (Score > Alpha ->
		eval_sub_first_sub(Lst, N, Stone, Opponent, Self, (Score, Beta), [(I, J, Stone)|IJLst], Result)
	        ;
		eval_sub_first_sub(Lst, N, Stone, Opponent, Self, (Alpha, Beta), PrevResult, Result))).

print_score(Lst, Score) :-
	format("~d : ~p\n", [Score, Lst]).


























set_sequence(nezumi,[(6, 5, black), (6, 4, white), (5, 3, black), (6, 6, white), (4, 3, black)], 10).
set_sequence(nezumi,[(6, 5, black), (6, 4, white), (5, 3, black), (4, 6, white), (6, 3, black)], 10).
set_sequence(usagi,[(6, 5, black), (4, 6, white), (3, 5, black), (6, 4, white), (5, 3, black)], 0).
set_sequence(tora,[(6, 5, black), (4, 6, white), (3, 3, black), (4, 3, white), (3, 4, black)], 0).
set_sequence(tora,[(6, 5, black), (4, 6, white), (3, 4, black), (4, 3, white), (3, 3, black)], 0).
set_sequence(uma,[(6, 5, black), (4, 6, white), (3, 5, black), (6, 4, white), (4, 3, black)], -2).
set_sequence(neko,[(6, 5, black), (4, 6, white), (3, 4, black), (4, 3, white), (3, 4, black)], -2).
set_sequence(hituji,[(6, 5, black), (4, 6, white), (3, 4, black), (4, 3, white), (5, 6, black)], -2).
set_sequence(tubame1,[(6, 5, black), (4, 6, white), (3, 4, black), (7, 5, white), (6, 6, black)], -2).
set_sequence(tubame2,[(6, 5, black), (4, 6, white), (3, 4, black), (7, 5, white), (3, 6, black)], -2).
set_sequence(tora2,[(6, 5, black), (4, 6, white), (3, 3, black), (7, 5, white), (3, 6, black)], 2).
set_sequence(nousagi,[(6, 5, black), (4, 6, white), (3, 6, black), (6, 4, white), (5, 6, black)], -2).
set_sequence(usi,[(6, 5, black), (6, 6, white), (5, 6, black), (6, 4, white), (5, 3, black)], 0).
set_sequence(tougyuu,[(6, 5, black), (6, 6, white), (5, 6, black), (6, 4, white), (7, 5, black)], 0).
set_sequence(mougyuu,[(6, 5, black), (6, 6, white), (5, 6, black), (6, 4, white), (3, 3, black)], -2).
set_sequence(hebi,[(6, 5, black), (6, 6, white), (5, 6, black), (6, 4, white), (7, 6, black)], -2).
set_sequence(usi,[(6, 5, black), (6, 6, white), (5, 6, black), (4, 6, white), (3, 5, black)], 0).
set_sequence(tougyuu,[(6, 5, black), (6, 6, white), (5, 6, black), (4, 6, white), (5, 7, black)], 0).
set_sequence(mougyuu,[(6, 5, black), (6, 6, white), (5, 6, black), (4, 6, white), (3, 3, black)], -2).
set_sequence(hebi,[(6, 5, black), (6, 6, white), (5, 6, black), (4, 6, white), (6, 7, black)], -2).


%
% 規格化ルーチン (一手目が、f5 になるように規格化する。)
%

normalize(0, K, K).
normalize(1, (I, J), (J, I)).
normalize(2, (I, J), (I1, I2)) :-
	I1 is 9 - I, 
	I2 is 9 - J.
normalize(3, (I, J), (I1, I2)) :-
	I1 is 9 - J,
	I2 is 9 - I.


normalize_sub0([], []).
normalize_sub0([[C, I, J, _]|Ks], [(I, J, C)|Rs]) :-
	normalize_sub0(Ks, Rs).

normalize_sub1([], []).
normalize_sub1([[C, I, J, _]|Ks], [(J, I, C)|Rs]) :-
	normalize_sub1(Ks, Rs).

normalize_sub2([], []).
normalize_sub2([[C, I, J, _]|Ks], [(I1, J1, C)|Rs]) :-
	I1 is 9 - I,
	J1 is 9 - J,
	normalize_sub2(Ks, Rs).

normalize_sub3([], []).
normalize_sub3([[C, I, J, _]|Ks], [(I1, J1, C)|Rs]) :-
	I1 is 9 - J,
	J1 is 9 - I,
	normalize_sub3(Ks, Rs).

normalize_list([], [], 0).
normalize_list([[black, 6, 5, _]|Ks], [(6, 5, black)|Rs], 0) :-
	normalize_sub0(Ks, Rs).
normalize_list([[black, 5, 6, _]|Ks], [(6, 5, black)|Rs], 1) :-
	normalize_sub1(Ks, Rs).
normalize_list([[black, 3, 4, _]|Ks], [(6, 5, black)|Rs], 2) :-
	normalize_sub2(Ks, Rs).
normalize_list([[black, 4, 3, _]|Ks], [(6, 5, black)|Rs], 3) :-
	normalize_sub3(Ks, Rs).

%
% 定石選択ルーチン
% ものすごく冗長なことをしているが、とりあえず仕方なし。
%

% 'sequence' には、定石の名前を入れておく。
% 'past_seq' には、とり得る定石全てを入れておく。
% 'select_sequence' は、見つからなければ fail する。

% N は、N 手目。

sequence_initialize :-
	g_assign(nezumi, 0),
	g_assign(usagi, 0),
	g_assign(tora, 0),
	g_assign(uma, 0),
	g_assign(neko, 0),
	g_assign(hituji, 0),
	g_assign(tubame1, 0),
	g_assign(tubame2, 0),
	g_assign(tora2, 0),
	g_assign(nousagi, 0),
	g_assign(usi, 0),
	g_assign(tougyuu, 0),
	g_assign(mougyuu, 0),
	g_assign(hebi, 0).

% 一手目は、(6, 5) におく。
select_sequence(1, _, (6, 5)) :- !.

% 二手目はランダム。但し、並び取りはさける。
select_sequence(2, Kifu, (I, J)) :-
	reverse(Kifu, Reversed),
	normalize_list(Reversed, _, Rotate),
	random(1, 3, R),
	nth(R, [(4, 6), (6, 6)], (I1, I2)),
	normalize(Rotate, (I1, I2), (I, J)).	

% 三手目からは定石通りに打つ。
select_sequence(3, Kifu, (I, J)) :-
	reverse(Kifu, Reversed),
	normalize_list(Reversed, Normalized, Rotate),
	findall((Name, FI, FJ),
	        (set_sequence(Name, X, R),
		 R >= 0,
		 g_read(Name, Used),
		 Used = 0,
		 prefix(Normalized, X),
		 nth(3, X, (FI, FJ, _))),
		Sequence),
	length(Sequence, Len),
	Len1 is inc(Len),
	random(1, Len1, Rand),
	nth(Rand, Sequence, (_, I1, J1)),
	normalize(Rotate, (I1, J1), (I, J)).


select_sequence(4, Kifu, (I, J)) :-
	reverse(Kifu, Reversed),
	normalize_list(Reversed, Normalized, Rotate),
	write(Normalized),
	findall((Name, FI, FJ),
	        (set_sequence(Name, X, R),
		 R =< 0,
		 g_read(Name, Used),
		 Used = 0,
		 prefix(Normalized, X),
		 nth(4, X, (FI, FJ, _))),
		Sequence),
	length(Sequence, Len),
	Len1 is inc(Len),
	random(1, Len1, Rand),
	nth(Rand, Sequence, (_, I1, J1)),
	normalize(Rotate, (I1, J1), (I, J)).

select_sequence(5, Kifu, (I, J)) :-
	reverse(Kifu, Reversed),
	normalize_list(Reversed, Normalized, Rotate),
	findall((Nam, FI, FJ),
	        (set_sequence(Nam, X, R),
		 R >= 0,
		 g_read(Nam, Used),
		 Used = 0,
		 prefix(Normalized, X),
		 nth(5, X, (FI, FJ, _))),
		Sequence),
	length(Sequence, Len),
	Len1 is inc(Len),
	random(1, Len1, Rand),
	nth(Rand, Sequence, (Name, I1, J1)),
	g_assign(Name, 1),
	normalize(Rotate, (I1, J1), (I, J)).

% 六手目は、定石を作成してないが、定石を覚えるために必要。	
select_sequence(6, Kifu, _) :-
	reverse(Kifu, Reversed),
	normalize_list(Reversed, Normalized, _),
	set_sequence(Name, Normalized, _),
	g_assign(Name, 1),
	fail.
		 




	
	


% 定石に従う
player_yuha(Board, Self, N, Kifu, _, _, I, J) :-
	N < 7,
	write(Kifu),
	(select_sequence(N, Kifu, (I, J)) ->
	    true
	    ;
	    (write('-------------------------- evaling...\n'),
	     eval(Board, 4, Self, [(I, J, _)|_], _),
	     write('-------------------------- evaled.\n'))),
	!.


% 完全読み
player_yuha(Board, Self, N, _, _, _, I, J) :-
	N > 47,
	write('-------------------------- complete reading\n'),
	complete_reading_only_best_hand(Board, Self, IJLst, R),
	format("selected : ~d : ~p\n", [R, IJLst]),
	[(I, J, _)|_] = IJLst,
	!.

% 必勝読み
player_yuha(Board, Self, N, _, _, _, I, J) :-
	N > 45,
	write('-------------------------- surefire\n'),
	surefire(Board, Self, IJLst, R),
	format("selected : ~d : ~p\n", [R, IJLst]),
	[(I, J, _)|_] = IJLst,
	!.	

% 通常
player_yuha(Board, Self, _, _, _, _, I, J) :-
	write('-------------------------- evaling...\n'),
	eval(Board, 5, Self, IJLst, R),
	format("selected : ~d : ~p\n", [R, IJLst]),
	[(I, J, _)|_] = IJLst,
	write('-------------------------- evaled.\n'),
	!.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

games_init(_, _) :-
	sequence_initialize,
	randomize.
	
games_end(_, _).
game_init(_, _).
game_end(_, _, _, _).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

connect_server(HostName,Address, InStream, OutStream) :-
     number_atom(Num, Address),
    socket('AF_INET', Socket),
    socket_connect(Socket, 'AF_INET'(HostName, Num), InStream, OutStream),
    asserta(player_stream(InStream,OutStream)).

/* それぞれ対応する述語を呼び出す */
games_sub(games_init(Who, Stone)) :- games_init(Who, Stone).
games_sub(games_end(Victories, Defeats)) :-
    games_end(Victories, Defeats),
    halt.
games_sub(game_init(Stone, Rest)) :- game_init(Stone, Rest).
games_sub(game_end(Board, Stone, Kifu, Result)) :-
    game_end(Board, Stone, Kifu, Result).
/* 思考ルーチンを呼び出して指し手を審判に通信する */
games_sub(turn(Board, Stone, N, Kifu, Rest, Start)) :-
    player_stream(_,OutStream),
    statistics(user_time,[Time1,_]),
    player_yuha(Board, Stone, N, Kifu, Rest, Start, I, J),
    statistics(user_time,[Time2,_]),
    Time is Time2 - Time1,
    format(OutStream, "~w.~n", [move(I, J, Time)]),
    flush_output(OutStream).

/* 審判に接続して試合を行う */
games :-
    argument_list([HostName,Address|_]),
    !,
    repeat,
    (catch(connect_server(HostName,Address, InStream,_), _, fail)
     ->
     true
     ;
     sleep(3),
     fail),
    !,
    repeat,
    read(InStream, Term),
    games_sub(Term),
    fail.

%:- initialization(games).


