:- include('tplace.pl'). 

/*
  result_dir/1
    結果を書き出すディレクトリを返す.
*/
result_dir("/home/yourname/kekka/").

/*
  time_limit/1
    制限時間をミリ秒単位で返す.
*/
time_limit(1800000). /* 30 分 */

/*
  empty_board/1
    何も置いていない盤の状態を返す.
*/
empty_board([[X, X, X, X, X, X, X, X],
	     [X, X, X, X, X, X, X, X],
	     [X, X, X, X, X, X, X, X],
	     [X, X, X, X, X, X, X, X],
	     [X, X, X, X, X, X, X, X],
	     [X, X, X, X, X, X, X, X],
	     [X, X, X, X, X, X, X, X],
	     [X, X, X, X, X, X, X, X]]) :-
    blank(X).

/*
  init_board/1
    盤の初期状態 (石を 4 個置いた状態) を返す.
*/
init_board([[X, X, X, X, X, X, X, X],
	    [X, X, X, X, X, X, X, X],
	    [X, X, X, X, X, X, X, X],
	    [X, X, X, W, B, X, X, X],
	    [X, X, X, B, W, X, X, X],
	    [X, X, X, X, X, X, X, X],
	    [X, X, X, X, X, X, X, X],
	    [X, X, X, X, X, X, X, X]]) :-
    black(B), white(W), blank(X).

/*
  print_board/1
    盤を標準出力に表示する (1 バイト文字使用).
*/
print_board_sub([], _).
print_board_sub([Line|Lines], N1) :-
    format("~d~s\n", [N1, Line]),
    N2 is N1 + 1,
    print_board_sub(Lines, N2).

print_board(Board) :-
    format(" abcdefgh\n", []),
    print_board_sub(Board, 1).

/*
  print_board2/1
    盤を標準出力に表示する
*/
print_board2_sub4(Cell) :- black(Cell), format("X", []).
print_board2_sub4(Cell) :- white(Cell), format("O", []).
print_board2_sub4(Cell) :- blank(Cell), format("-", []).

print_board2_sub3([]) :- format("\n", []).
print_board2_sub3([Cell|Cells]) :-
    print_board2_sub4(Cell),
    print_board2_sub3(Cells).

print_board2_sub2(1) :- format("1", []).
print_board2_sub2(2) :- format("2", []).
print_board2_sub2(3) :- format("3", []).
print_board2_sub2(4) :- format("4", []).
print_board2_sub2(5) :- format("5", []).
print_board2_sub2(6) :- format("6", []).
print_board2_sub2(7) :- format("7", []).
print_board2_sub2(8) :- format("8", []).

print_board2_sub1([], _).
print_board2_sub1([Line|Lines], N1) :-
    print_board2_sub2(N1),
    print_board2_sub3(Line),
    N2 is N1 + 1,
    print_board2_sub1(Lines, N2).

print_board2(Board) :-
    format(" abcdefgh\n", []),
    print_board2_sub1(Board, 1).

/*
  print_move/3
    着手した一手の情報を表示する.
*/
print_move_sub(1, J, B) :- black(B), format("A~d", [J]).
print_move_sub(2, J, B) :- black(B), format("B~d", [J]).
print_move_sub(3, J, B) :- black(B), format("C~d", [J]).
print_move_sub(4, J, B) :- black(B), format("D~d", [J]).
print_move_sub(5, J, B) :- black(B), format("E~d", [J]).
print_move_sub(6, J, B) :- black(B), format("F~d", [J]).
print_move_sub(7, J, B) :- black(B), format("G~d", [J]).
print_move_sub(8, J, B) :- black(B), format("H~d", [J]).
print_move_sub(1, J, W) :- white(W), format("a~d", [J]).
print_move_sub(2, J, W) :- white(W), format("b~d", [J]).
print_move_sub(3, J, W) :- white(W), format("c~d", [J]).
print_move_sub(4, J, W) :- white(W), format("d~d", [J]).
print_move_sub(5, J, W) :- white(W), format("e~d", [J]).
print_move_sub(6, J, W) :- white(W), format("f~d", [J]).
print_move_sub(7, J, W) :- white(W), format("g~d", [J]).
print_move_sub(8, J, W) :- white(W), format("h~d", [J]).

print_move(I, J, Stone) :-
    format("[", []),
    print_move_sub(I, J, Stone),
    format("]\n", []).

/*
  print_all_moves/3
    棋譜を表示する.
*/
print_all_moves(N, N, _) :-
    !,
    C is N mod 10,
    (C == 1 -> true ; format("\n", [])).
print_all_moves(N1, N3, Stone1) :-
    number_atom(N1,NA),
    g_read(NA, (I, J)),
    print_move_sub(I, J, Stone1),
    C is N1 mod 10,
    (C == 0
     ->
     format("\n", [])
     ;
     format(" ", [])),
    N2 is N1 + 1,
    flip_disc(Stone1, Stone2),
    print_all_moves(N2, N3, Stone2).

/*
  print_result/1
    正常に試合が終了した場合の結果を表示する.
*/
print_result(Board) :-
    format("\n", []),
    black(Black), count_stones(Board, Black, CB),
    white(White), count_stones(Board, White, CW),
    (CB == CW
     ->
     format("Even (~d, ~d)\n", [CB, CW])
     ;
     (CB > CW
      ->
      D is CB - CW,
      format("Black Won! (~d (~d, ~d))\n", [D, CB, CW])
      ;
      D is CW - CB,
      format("White Won! (~d (~d, ~d))\n", [D, CB, CW]))).

/*
  print_result_error/1
    試合が異常終了した場合の結果を表示する.
*/
print_result_error(Error) :-
    format("--------------------\n", []),
    ((Error == black_timeout ; Error == black_illegal ; Error == black_failure)
     ->
     format("White Won! (Foul)\n", [])
     ;
     format("Black Won! (Foul)\n", [])).

/*****************************************************************************/

/*
  get_result/2
    終了した盤の状態から試合の結果を入手する.
*/
get_result(Board, Result) :-
    black(Black), count_stones(Board, Black, CB),
    white(White), count_stones(Board, White, CW),
    (CB == CW
     ->
     Result = [CB, CW, even]
     ;
     (CB > CW
      ->
      Result = [CB, CW, black]
      ;
      Result = [CB, CW, white])).

/*
  get_result_error/2
    異常終了した時、エラー情報から試合の結果を入手する.
*/
get_result_error(Error, Result) :-
    ((Error == black_timeout ; Error == black_illegal ; Error == black_failure)
     ->
     Result = [0, 64, white]
     ;
     Result = [64, 0, black]).

/*****************************************************************************/

/*
  toggle_print_mode/0
    試合中に結果を逐一表示するか, しないかを変える.
    デフォルトは「表示しない」.
*/
:- initialization(g_assign(print_mode, 0)).

toggle_print_mode :-
    g_read(print_mode, X),
    Y is 1 - X,
    g_assign(print_mode, Y).

/*****************************************************************************/

/*
  board_end_check/1
    盤にもう打つところがないか調べる.
*/
board_end_check(Board) :-
    blank(X),
    member(Line, Board),
    member(X, Line),
    !,
    fail.
board_end_check(_).

/*
  board_pass_check/2
    パスしなければならないか調べる.
*/
board_pass_check(Board, Stone) :-
    member(I, [1, 2, 3, 4, 5, 6, 7, 8]),
    member(J, [1, 2, 3, 4, 5, 6, 7, 8]),
    flip_disc(Stone, Opponent),
    place_disc(I, J, Stone, Opponent, Board, _),
    !,
    fail.
board_pass_check(_, _).

/*****************************************************************************/

/*
  match/5
    オセロの対戦をする.
*/
match_sub_pass(_, Stone2, _, _, _, _, N, N, Board, Board, Kifu, Kifu) :-
    board_pass_check(Board, Stone2),
    !.
match_sub_pass(Stone1, Stone2, CurrentPred, NextPred,
	       BlackRest, WhiteRest, N1, N2, Board1, Board2, Kifu1, Kifu2) :-
    match_sub(Stone2, Stone1, NextPred, CurrentPred, BlackRest, WhiteRest,
	      N1, N2, Board1, Board2, Kifu1, Kifu2).

match_sub_move(Stone, CurrentName, Board, BlackRest, _,
	       TimeOut, Time, N, Kifu, I, J) :-
    black(Stone),
    !,
    player_stream(CurrentName, InStream,OutStream),
    format(OutStream, "~w.~n",[turn(Board, Stone, N, Kifu, BlackRest, Time1)]),
    flush_output(OutStream),
%     (time_out(Module:Pred, BlackRest, TimeOut)
%      TimeOut = failure)
    read(InStream, move(I, J, Time)),
    !.
match_sub_move(Stone, CurrentName, Board, _, WhiteRest,
	       TimeOut, Time, N, Kifu, I, J) :-
    player_stream(CurrentName, InStream,OutStream),
    format(OutStream, "~w.~n", [turn(Board, Stone, N, Kifu, WhiteRest, Time1)]),
    flush_output(OutStream),
    read(InStream, move(I, J, Time)),
    !.

match_sub_time(Stone1, Time, BlackRest1, BlackRest2, WhiteRest1, WhiteRest2) :-
    black(Stone1),
    !,
    BlackRest2 is BlackRest1 - Time,
    WhiteRest2 = WhiteRest1.
match_sub_time(_, Time, BlackRest1, BlackRest2, WhiteRest1, WhiteRest2) :-
    WhiteRest2 is WhiteRest1 - Time,
    BlackRest2 = BlackRest1.

match_sub_timeout_check(Stone1, TimeOut, BlackRest, WhiteRest) :-
    format('BLACKrest=~D  WHITErest=~D~n',[BlackRest,WhiteRest]),
    fail.
match_sub_timeout_check(Stone1, TimeOut, BlackRest2, _) :-
    black(Stone1), 
    !,
    (TimeOut == time_out ; BlackRest2 =< 0
     ->
     g_assign(error, black_timeout)
     ;
     (TimeOut == failure
      ->
      g_assign(error, black_failure)
      ;
      fail)).

match_sub_timeout_check(_, TimeOut, _, WhiteRest2) :-
    (TimeOut == time_out ; WhiteRest2 =< 0
     ->
     g_assign(error, white_timeout)
     ;
     (TimeOut == failure
      ->
      g_assign(error,white_failure)
      ;
      fail)).

match_sub(_, _, _, _, _, _, N, N, Board, Board, Kifu, Kifu) :-
    board_end_check(Board), !.
match_sub(Stone1, Stone2, CurrentName, NextName,
	  BlackRest1, WhiteRest1, N1, N2, Board1, Board2, Kifu1, Kifu2) :-
    board_pass_check(Board1, Stone1),
    !,
    match_sub_pass(Stone1, Stone2, CurrentName, NextName,
		   BlackRest1, WhiteRest1,
		   N1, N2, Board1, Board2, Kifu1, Kifu2).
match_sub(Stone1, Stone2, CurrentName, NextName,
	  BlackRest1, WhiteRest1, N1, N3, Board1, Board3, Kifu1, Kifu3) :-
    match_sub_move(Stone1, CurrentName, Board1, BlackRest1, WhiteRest1,
		   TimeOut, Time, N1, Kifu1, I, J),
    !,
    match_sub_time(Stone1, Time,
		   BlackRest1, BlackRest2, WhiteRest1, WhiteRest2),
    g_assign(error, 0),
    (match_sub_timeout_check(Stone1, TimeOut, BlackRest2, WhiteRest2)
     ->
     true
     ;
     ((flip_disc(Stone1, Opponent1),
       place_disc(I, J, Stone1, Opponent1, Board1, Board2))
      ->
      true
      ;
      (black(Stone1) ->
       g_assign(error, black_illegal) ;
       g_assign(error, white_illegal)))),
    number_atom(N1,NA),
    g_assign(NA, (I, J)),
    N2 is N1 + 1,
    (g_read(print_mode, 1)
     ->
     format("--------------------\n", []),
     print_move(I, J, Stone1),
     print_board2(Board2)
     ;
     true),
    (g_read(error, 0)
     ->
     Kifu2 = [[Stone1, I, J, Time]|Kifu1],
     match_sub(Stone2, Stone1, NextName, CurrentName, BlackRest2, WhiteRest2,
	       N2, N3, Board2, Board3, Kifu2, Kifu3)
     ;
     g_read(error, Error),
     Kifu3 = [[Error, I, J, Time]|Kifu1],
     N3 = N1, Board3 = Board1).

match(NameB, NameW, Board2, Kifu, Result) :-
    time_limit(Limit),
    init_board(Board1),
    (g_read(print_mode, 1) -> print_board2(Board1) ; true),
    black(Black), white(White),
    match_sub(Black, White, NameB, NameW, Limit, Limit,
	      1, N, Board1, Board2, [], Kifu),
    g_read(error, Error),
    (Error == 0
     ->
     get_result(Board2, Result)
     ;
     get_result_error(Error, Result)),
    true.

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

/*
  load_player/1
    Player で表されるアトムをモジュール名とするモジュールを読み込む.
*/
connect_player(Atom, InStream,OutStream) :-
    name(Atom, Name), name(Num, Name),
    socket('AF_INET', Socket),
    socket_bind(Socket, 'AF_INET'(_, Num)),
    socket_listen(Socket, 100),
    socket_accept(Socket, InStream, OutStream).

open_output_file(X, Y, OStream) :-
    atom_codes(X, XStr), 
    atom_codes(Y, YStr), 
    append(":", YStr, Tmp1),
    append(XStr, Tmp1, Tmp2),
    result_dir(Dir), 
    append(Dir, Tmp2, Tmp3), 
    atom_codes(File, Tmp3),
    open(File, write, OStream).

get_new_result([CB, CW, Which], Black, White, [Who, Which, D]) :-
    (Which == black
     ->
     Who = Black
     ;
     (Which == white
      ->
      Who = White
      ;
      Who = even)),
    (CB > CW -> D is CB - CW ; D is CW - CB).

get_counts([], _, _, 0.0, 0.0).
get_counts([Who|Whos], X, Y, CX1, CY1) :-
    get_counts(Whos, X, Y, CX2, CY2),
    (Who == X
     ->
     CX1 is CX2 + 1, CY1 is CY2
     ;
     (Who == Y
      ->
      CY1 is CY2 + 1, CX1 is CX2
      ;
      CX1 is CX2 + 0.5, CY1 is CY2 + 0.5)).

one_game(N, OStream, NameB, NameW, NewResult1) :-
/*    print('aaa'), nl, */
    player_stream(NameB, InStreamB,OutStreamB),
    player_stream(NameW, InStreamW,OutStreamW),
    black(B), white(W), time_limit(Limit),
    !,
    format(OutStreamB, "~w.~n", [game_init(B, Limit)]),
    flush_output(OutStreamB),
    format(OutStreamW, "~w.~n", [game_init(W, Limit)]),
    flush_output(OutStreamW),
    !,
    match(NameB, NameW, Board1, Kifu1, Result1),
    !,
    format(OutStreamB, "~w.~n", [game_end(Board1, B, Kifu1, Result1)]),
    flush_output(OutStreamB),
    format(OutStreamW, "~w.~n", [game_end(Board1, W, Kifu1, Result1)]),
    flush_output(OutStreamW),
    !,
    get_new_result(Result1, NameB, NameW, NewResult1),
    !,
    format(OStream, "~d:\n", [N]),
    print(OStream, NewResult1), nl(OStream),
    print(OStream, Result1), nl(OStream),
    print(OStream, Kifu1), nl(OStream).

/*
  games/0
    オセロの対戦をする.
*/
games :- toggle_print_mode,
    (argument_list([NameA, NameB, NumA, NumB|REST])
     ->
     randomize,
     random(Random1),
     Random2 is (floor(Random1 * 1000000) mod 2),
     (Random2 == 0 ->
      Name1 = NameA, Name2 = NameB, Num1 = NumA, Num2 = NumB ;
      Name1 = NameB, Name2 = NameA, Num1 = NumB, Num2 = NumA),
     %%
     connect_player(Num1, InStream1,OutStream1),
     connect_player(Num2, InStream2,OutStream2),
     asserta(player_stream(Name1, InStream1,OutStream1)),
     asserta(player_stream(Name2, InStream2,OutStream2)),
     %%
     (REST = [test]
      ->
      OStream = user_output
      ;
      open_output_file(Name1, Name2, OStream)),
     black(B), white(W),
     format(OutStream1, "~w.~n", [games_init(Name2, B)]),
     flush_output(OutStream1),
     format(OutStream2, "~w.~n", [games_init(Name1, W)]),
     flush_output(OutStream2),
     %%
     !,
     one_game(1, OStream, Name1, Name2, NewResult1),
     !,
     one_game(2, OStream, Name2, Name1, NewResult2),
     !,
     one_game(3, OStream, Name1, Name2, NewResult3),
     !,
     one_game(4, OStream, Name2, Name1, NewResult4),
     !,
     %%
     NewResult1 = [Who1, _, _],
     NewResult2 = [Who2, _, _],
     NewResult3 = [Who3, _, _],
     NewResult4 = [Who4, _, _],
     get_counts([Who1, Who2, Who3, Who4], Name1, Name2, CX, CY),
     !,
     format(OutStream1, "~w.~n", [games_end(CX, CY)]),
     flush_output(OutStream1),
     format(OutStream2, "~w.~n", [games_end(CY, CX)]),
     flush_output(OutStream2),
     !,
     %%
     format(OStream, "Total:\n", []),
     print(OStream, [CX, CY]), nl(OStream),
     %%
     close(OStream),
     halt
     ;
     true).

%:- initialization(games).
