% An Othello playing Program 
% by Ito Ko, ko_ito@hp.com, ported to B-Prolog by Wenju Cheng
% Black : Human(1), White : Computer(2)

go :-
    cgWindow(W,"Othello Game"),
    W^width #= 780, W^height #= 680,
    W^leftMargin #= 20, W^topMargin #= 20,
    handleWindowClosing(W),

	cgButton(P,"pass"),
    	P^x #= 630, P^y #= 300,
    	P^width #= 100, P^height #= 50,
    	handleButtonPassClick(W,P),

	cgButton(R,"reset"),
    	R^x #= 630, R^y #= 400,
    	R^width #= 100, R^height #= 50,
    	handleButtonResetClick(W,R),
    cgStartRecord(othello),
	drawBoard(8,Comps,[]),
    	cgSame([P,R|Comps],window,W),
    	cgShow([P,R|Comps]),
	put(28,1,W),put(29,2,W),put(36,2,W),put(37,1,W),
    cgStopRecord,
        global_set(state,0,[0,0,0,0,0,0,0,0,
                  0,0,0,0,0,0,0,0,
                  0,0,0,0,0,0,0,0,
                  0,0,0,1,2,0,0,0,
                  0,0,0,2,1,0,0,0,
                  0,0,0,0,0,0,0,0,
                  0,0,0,0,0,0,0,0,
                  0,0,0,0,0,0,0,0]),

	handleMouseEvent(W).	

handleWindowClosing(Win),{windowClosing(Win)} => abort.

handleButtonResetClick(W,R),{actionPerformed(R)} =>
	cgClose(W),
	go.
	
handleButtonPassClick(W,P),{actionPerformed(P)} =>
	main(com,W).

handleMouseEvent(W),{mousePressed(W,E)}=>
	E^x #= X, E^y #= Y,
	mouseClick(W,X,Y).
	
mouseClick(W,X,Y):-
	X > 100, X < 580,
	Y > 100, Y < 580,!,
	J is (Y-100)//60*8+(X-100)//60+1,
	put(J,1,W),global_set(next,0,J),main(man,W).
mouseClick(W,X,Y).

main(com,W):-
	global_get(state,0,C),
        next(C,2,J), !, put2(J,2,W), 
	change(J,2,C,C1), update(J,C1,2,1,C2,W),global_set(state,0,C2),final(W,N).
main(com,W):-
	write('Your turn again.'),nl.
	
main(man,W):-
	global_get(next,0,J),
	global_get(state,0,C),
	J>0,
	find(0,C,J), check(J,C,1,_,0),!,
	change(J,1,C,C1), update(J,C1,1,1,C2,W),
	global_set(state,0,C2),con(W).
main(man,W):-
	write('You can not put there.'),nl,
	global_get(state,0,C),
	global_get(next,0,J),
	find(N,C,J), put(J,N,W).

con(W):-
	final(W,1),!.
con(W):-
	main(com,W).

final(W,1):-
	global_get(state,0,C), 
        not( member(0,C) ), !, count(2,C,N), N1 is 64 - N,
	mas(N,N1,W),
	write('Computer : Human = '),write(N),write(' : '),write(N1),nl.
final(W,0).

mas(N,N1,W):-
	N>N1,!,
	cgLabel(R,"I Win!"),
	R^window #= W,
	R^fontSize #= 50,
	R^fontStyle #= 'bold',
	R^color #= red,
	cgShow(R).
mas(N,N1,W):-
	cgLabel(R,"You Win!"),
	R^window #= W,
	R^fontSize #= 50,
	R^fontStyle #= 'bold',
	R^color #= red,
	cgShow(R).

	
% Check availability / check(Place,Current,Color,Direction,Flag).
check(N,C,M,1,_) :- 
	not( 0 is N mod 8 ), N1 is N + 1, M1 is 3 - M, 
	find(M1,C,N1), check(N1,C,M,1,1).
check(N,C,M,1,1) :- 
	not( 0 is N mod 8 ), N1 is N + 1, find(M,C,N1).
check(N,C,M,2,_) :- 
	not( 1 is N mod 8 ), N1 is N - 1, M1 is 3 - M, 
	find(M1,C,N1), check(N1,C,M,2,1).
check(N,C,M,2,1) :- 
	not( 1 is N mod 8 ), N1 is N - 1, find(M,C,N1).
check(N,C,M,3,_) :- 
	N > 8, N1 is N - 8, M1 is 3 - M, find(M1,C,N1), check(N1,C,M,3,1).
check(N,C,M,3,1) :- 
	N > 8, N1 is N - 8, find(M,C,N1).
check(N,C,M,4,_) :- 
	N < 57, N1 is N + 8, M1 is 3 - M, find(M1,C,N1), check(N1,C,M,4,1).
check(N,C,M,4,1) :- 
	N < 57, N1 is N + 8, find(M,C,N1).
check(N,C,M,5,_) :- 
	not( 0 is N mod 8 ), N > 8, N1 is N - 7, M1 is 3 - M, 
	find(M1,C,N1), check(N1,C,M,5,1).
check(N,C,M,5,1) :- 
	not( 0 is N mod 8 ), N > 8, N1 is N - 7, find(M,C,N1).
check(N,C,M,6,_) :- 
	not( 0 is N mod 8 ), N < 57, N1 is N + 9, M1 is 3 - M, 
	find(M1,C,N1), check(N1,C,M,6,1).
check(N,C,M,6,1) :- 
	not( 0 is N mod 8 ), N < 57, N1 is N + 9, find(M,C,N1).
check(N,C,M,7,_) :- 
	not( 1 is N mod 8 ), N > 8, N1 is N - 9, M1 is 3 - M, 
	find(M1,C,N1), check(N1,C,M,7,1).
check(N,C,M,7,1) :- 
	not( 1 is N mod 8 ), N > 8, N1 is N - 9, find(M,C,N1).
check(N,C,M,8,_) :- 
	not( 1 is N mod 8 ), N < 57, N1 is N + 7, M1 is 3 - M, 
	find(M1,C,N1), check(N1,C,M,8,1).
check(N,C,M,8,1) :- not( 1 is N mod 8 ), N < 57, N1 is N + 7, find(M,C,N1).
    
% Priority
p([1,8,64,57,27,35,20,21,44,45,30,38,19,22,43,46,17,41,3,59,
   6,62,24,48,25,33,4,60,5,61,32,40,26,34,52,53,12,13,31,39,42,
   51,11,18,54,47,14,23,9,49,2,58,7,63,16,56,10,50,15,55]).

next(C,N,J):-
	p(P),next(C,N,P,J).
next(C,N,[H|_],H) :- find(0,C,H),check(H,C,N,_,0), !.
next(C,N,[_|T],J) :- next(C,N,T,J).

% Update board / update(Place, Current, Color, Direction, Updated).
update(_,C,_,9,C,Win).
update(N,C,W,D,C2,Win) :- 
        D1 is D + 1, 
        update(N,C,W,D1,C1,Win),
        ( check(N,C,W,D,0) -> reverse(N,C1,W,D,C2,Win) ; C2 = C1 ).

reverse(N,C,W,1,X,Win) :- % east
        not( 0 is N mod 8 ), N1 is N + 1, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,1,X,Win).
reverse(N,C,W,2,X,Win) :- % west
        not( 1 is N mod 8 ), N1 is N - 1, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,2,X,Win).
reverse(N,C,W,3,X,Win) :- % north
        N > 8, N1 is N - 8, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,3,X,Win).
reverse(N,C,W,4,X,Win) :- % south
        N < 57, N1 is N + 8, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,4,X,Win).
reverse(N,C,W,5,X,Win) :- % north-east
        not( 0 is N mod 8 ), N > 8, N1 is N - 7, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,5,X,Win).
reverse(N,C,W,6,X,Win) :- % south-east
        not( 0 is N mod 8 ), N < 57, N1 is N + 9, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,6,X,Win).
reverse(N,C,W,7,X,Win) :- % north-west
        not( 1 is N mod 8 ), N > 8, N1 is N - 9, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,7,X,Win).
reverse(N,C,W,8,X,Win) :- % south-west
        not( 1 is N mod 8 ), N < 57, N1 is N + 7, change(N1,W,C,C1), put(N1,W,Win), 
	reverse(N1,C1,W,8,X,Win).
reverse(_,C,_,_,C,Win).
        
% Tools
count(_,[],0).
count(H,[H|T],X) :- !, count(H,T,W), X is W + 1.
count(E,[_|T],X) :- count(E,T,X).

change(1,E,[X|T],[E|T]) :- E \== X, !.
change(N,E,[H|T],[H|X]) :- N > 1, N1 is N - 1, change(N1,E,T,X).

find(H,[H|_],1).
find(E,[_|T],N1) :- find(E,T,N), N1 is N + 1.

% Graphics
drawBoard(N,[S|Comps],CompsR):-
	drawHorizontalLines(0,N,Comps,Comps1),
    	drawVerticalLines(0,N,Comps1,CompsR),
	cgSquare(S),
	S^x #= 80, S^y #= 80,
	S^width #= 520, S^color #= orange.

drawHorizontalLines(I,N,Comps,CompsR):-I>N,!,Comps=CompsR.
drawHorizontalLines(I,N,Comps,CompsR):-
    	cgLine(Line),
    	LM #= 100, TM #= 100,
    	Line^x1 #= LM, Line^y1 #= 60*I+TM,
    	Line^x2 #= 60*N+LM, Line^y2 #= Line^y1,
    	Comps=[Line|Comps1],
    	I1 is I+1,
    	drawHorizontalLines(I1,N,Comps1,CompsR).

drawVerticalLines(I,N,Comps,CompsR):-I>N,!,Comps=CompsR.
drawVerticalLines(I,N,Comps,CompsR):-
    	cgLine(Line),
    	LM #= 100, TM #= 100,
    	Line^x1 #= 60*I+LM, Line^y1 #= TM,
    	Line^x2 #= Line^x1, Line^y2 #= N*60+TM,
    	Comps=[Line|Comps1],
    	I1 is I+1,
    	drawVerticalLines(I1,N,Comps1,CompsR).
	
put(N,M,W) :-
	X is (N - 1) mod 8 + 1, Y is (N - 1) // 8 + 1,
	color(M,C),
	cgCircle(S),
	S^window #= W,
    	S^centerX #= X*60+100-30,
    	S^centerY #= Y*60+100-30,
    	S^diameter #= 50,
    	S^color #= C,
	cgSleep(200),
	cgShow(S).

put2(N,M,W) :-
	X is (N - 1) mod 8 + 1, Y is (N - 1) // 8 + 1,
	color(M,C),
	cgCircle(S),
	S^window #= W,
    	S^centerX #= X*60+100-30,
    	S^centerY #= Y*60+100-30,
    	S^diameter #= 50,
    	S^color #= C,
	cgSleep(2000),
	cgShow(S).

color(0,orange).
color(1,black).
color(2,white).

