% INLÄMNINGSUPPGIFT 3

:- use_module(library(lists)).

% PLAY TIC-TAC-TOE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

play :-
	play((max, [t,t,t,t,t,t,t,t,t]), 4).

% UTILITY %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

thing(max, x).
thing(min, o).
%--------

make_rows([A1,B1,C1,A2,B2,C2,A3,B3,C3], [[A1,B1,C1],
					[A2,B2,C2],
					[A3,B3,C3],
					[A1,A2,A3],					[A2,B2,C2],
					[B1,B2,B3],
					[C1,C2,C3],
					[A1,B2,C3],
					[A3,B2,C1]]).

count_elements(_, [], 0).
count_elements(E, [E|Rest], N) :- !,
	count_elements(E, Rest, N2),
	N is N2 + 1.
count_elements(E, [_|Rest], N) :- !,
	count_elements(E, Rest, N).

writegp([A1,B1,C1,A2,B2,C2,A3,B3,C3]) :-
	write(A1),write(' '),write(B1),write(' '),write(C1),write('   1'),nl,
	write(A2),write(' '),write(B2),write(' '),write(C2),write('   2'),nl,
	write(A3),write(' '),write(B3),write(' '),write(C3),write('   3'),nl,nl,
	write(a),write(' '),write(b),write(' '),write(c).

% MOVES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

move((Player1, N1), (Player2, N2)) :-
	opponent(Player1, Player2),
	append(X,[t|Y], N1),
	thing(Player1, Thing),
	append(X,[Thing|Y], N2).

moves(P, L) :-
	bagof(NP, move(P, NP), L).

opponent(max, min).
opponent(min, max).

% X_TO_MOVE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

min_to_move((min, _)).
max_to_move((max, _)).

% TERMINAL_X %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

terminal_won((_, S)) :-
	thing(max, Thing),
	make_rows(S, Rows),
	won(Thing, Rows).

terminal_lost((_, S)) :-
	thing(min, Thing),
	make_rows(S, Rows),
	won(Thing, Rows).

won(_, []) :- !,fail.
won(T, [[T,T,T]|_]).
won(T, [[_,_,_]|Rest]) :-
	won(T, Rest).

terminal_draw((P,S)) :-
	\+ member(t, S),
	\+ terminal_won((P,S)),
	\+ terminal_lost((P,S)).

% ASK_MOVE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

ask_move((min, S1), (max, S2)) :-
	repeat,
	writegp(S1),nl,nl,
	write('Ange koordinaten för ditt drag: '),
	read((X,Y)),
	transform(X, Y, N),
	move_ok(S1, N),
	thing(min, T),
	exchange_element_n(S1, N, T, S2).

exchange_element_n(S1, N, T, S2) :-
	exchange_element_n([], S1, N, T, S2).

exchange_element_n(Start, [_|End], 1, T, S) :-
	append(Start, [T|End], S).

exchange_element_n(Start, [X|END], N, T, S) :-
	N2 is N - 1,
	append(Start, [X], Start2),
	exchange_element_n(Start2, END, N2, T, S).

move_ok(S, I) :- 
	nth(I, S, t).
%	nth1(I, S, t).

move_ok(_, _) :-
	nl,
	write('Prolog säger SKÄRPNING, annars vill inte jag spela med dig!!! :0'),
	nl,nl,
	fail.
	
transform(a, Y, N) :-
	N is (Y - 1) * 3 + 1.
transform(b, Y, N) :-
	N is (Y - 1) * 3 + 2.
transform(c, Y, N) :-
	N is (Y - 1) * 3 + 3.

% STATIC_VAL %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

static_val((min,GS), V) :-
	max_static_val((min,GS), V),
	!.
static_val(Pos, V) :-
	min_static_val(Pos, V),
	!.	

max_static_val(Pos, 60) :-
	terminal_won(Pos).

max_static_val((_,GS), 20) :-
	make_rows(GS, Rows),
	threat(x,Rows),
	no_threat(x,o,Rows).

max_static_val((_,GS), 10) :-
	nth(5, GS, x),
%	nth1(5, GS, x),
	make_rows(GS, Rows),
	no_threat(x,o,Rows).

max_static_val((_,GS), -10) :-
	nth(5, GS, o),
%	nth1(5, GS, o),
	make_rows(GS, Rows),
	no_threat(o,x,Rows).

max_static_val((_,GS), -20) :-
	make_rows(GS, Rows),
	threat(o,Rows),
	no_threat(o,x,Rows).

max_static_val(Pos, -60) :-
	terminal_lost(Pos).

max_static_val(_,0).

min_static_val(Pos, -60) :-
	terminal_lost(Pos).

min_static_val((_,GS), -20) :-
	make_rows(GS, Rows),
	threat(o,Rows),
	no_threat(o,x,Rows).

min_static_val((_,GS), -10) :-
	nth(5, GS, o),
%	nth1(5, GS, o),
	make_rows(GS, Rows),
	no_threat(o,x,Rows).

min_static_val((_,GS), 10) :-
	nth(5, GS, x),
%	nth1(5, GS, x),
	make_rows(GS, Rows),
	no_threat(x,o,Rows).

min_static_val((_,GS), 20) :-
	make_rows(GS, Rows),
	threat(x,Rows),
	no_threat(x,o,Rows).

min_static_val(Pos, 60) :-
	terminal_won(Pos).

min_static_val(_,0).

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

no_threat(_,_,[]).
no_threat(X,O,[First|Rest]):-
	count_elements(O, First, N),
	N < 2,
	no_threat(X,O,Rest).
no_threat(X,O,[First|Rest]):-
	count_elements(O, First, N),
	N =:= 2,
	count_elements(X, First, N2),
	N2 =:=1,
	no_threat(X,O,Rest).

threat(_, []) :- 
	fail.
threat(T,[[T,T,t]|_]).
threat(T,[[T,t,T]|_]).
threat(T,[[t,T,T]|_]).
threat(T,[_|Rows]) :-
	threat(T, Rows).

% MINIMAX %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

play((X,Pos), _):-
	terminal_won((X,Pos)),
	!, 
	writegp(Pos),nl,
	write('Prolog segrade :)'), nl.

play((X,Pos), _):-
	terminal_lost((X,Pos)),
	!, 
	writegp(Pos),nl,
	write('Prolog förlorade :('), nl.

play((X,Pos), _):-
	terminal_draw((X,Pos)),
	!, 
	writegp(Pos),nl,
	write('Prolog spelade lika :|'), nl.

play(Pos1, Depth):-
	make_move(Pos1, Depth, Pos2), 
	play(Pos2, Depth).

make_move(Pos1, Depth, Pos2):- 
	max_to_move(Pos1), 
	!, 
	minimax(Pos1, Depth, Pos2, V),
	write(V),nl.

make_move(Pos1, _, Pos2):- 
	ask_move(Pos1,Pos2).

minimax(Pos, Depth, Best, Val) :-
	Depth > 0,
	moves(Pos, Poslist),
	!,
	Depth2 is Depth - 1,
	best(Poslist, Depth2, Best, Val).

minimax(Pos, _, _, Val) :-
	static_val(Pos, Val).

best([Pos], Depth, Pos, Val) :-
	minimax(Pos, Depth, _ ,Val).

best([Pos1|Poslist], Depth, Best, Val) :-
	minimax(Pos1, Depth, _, Val1),
	best(Poslist, Depth, Pos2, Val2),
	better_of(Pos1, Val1, Pos2, Val2, Best, Val).

better_of(Pos1, Val1, _, Val2, Pos1, Val1) :-
	min_to_move(Pos1),
	Val1 > Val2,
	!.

better_of(Pos1, Val1, _, Val2, Pos1, Val1) :-
	max_to_move(Pos1),
	Val1 < Val2,
	!.

better_of(_, _, Pos2, Val2, Pos2, Val2).

% TESTPREDIKAT %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
sv1 :- static_val((max,[x,x,t,o,x,o,t,o,t]), X), write(X), nl.
tw1 :- terminal_won((max,[x,x,t,o,x,o,t,o,t])).
tl1 :- terminal_lost((min,[x,x,t,o,x,o,t,o,t])).
ms(X) :- moves((max, [t,t,t,t]), X).
mo(X) :- move((max, [t,t,t,t,t,t]), X).
mn :- min_to_move((min, [t,t,t,t,t,t])).
mx :- max_to_move((min, [t,t,t,t,t,t])).
tw2 :- terminal_won((max,[o,x,o,x,x,o,x,o,o])).
tl2 :- terminal_lost((min,[o,x,o,x,x,o,x,o,o])).
dr1 :- draw((min,[x,o,x,o,x,x,o,o,x])).
dr2 :- draw((min,[o,x,o,x,x,o,x,o,o])).
am(X) :- ask_move((min,[t,t,t,t,t,x,t,t,t]), X).
mr1(X) :- make_rows([o,o,x,x,x,o,x,o,x], X).
mr2(X) :- make_rows([o,x,o,x,x,o,x,o,o], X).
mr3(X) :- make_rows([a1,b1,c1,a2,b2,c2,a3,b3,c3], X).

l :- [inl3].
p:-play.