/*

lijs: [AanZet,1,2,3,4,5,6,7,8,9]

 1 | 2 | 3 
---+---+---
 4 | 5 | 6 
---+---+---
 7 | 8 | 9 

 o begint altijd! (dus is max)
 x is dus min
*/
:- dynamic(aanzet/1).
:- dynamic(speelstijl/3).
:- dynamic(states/1).
:- assert(states(0)).
:- dynamic(statestotaal/2).

beginstate([o,l,l,l,l,l,l,l,l,l]).

go :-
	sp(3/simpel,1/dynamisch,_).
go2 :-
	sp(6/simpel,2/dynamisch,_).	%testen van local stack :)
					% diepte 6 kan met stacksize 16mb
					
go3(Sp1,Sp2,Aantal) :- meer_keer(Sp1,Sp2,Aantal,0,0,0,0,0,0).	%lui als we zijn, gaan we niet zelf kijken hoe goed iets werkt :)
					% go3(1/simpel,1/simpel,10). om te kijken wat het resultaat van 10 keer doen is.
meer_keer(_,_,0,O,X,G,StateO,StateX,Cpu) :-
	writeln(' '),writeln('totaal resultaten afgelopen keren: '),!,
	write('aantal keren o / x / gelijk / totaal cpu verbruik / states o / states x : '),
	write(O),
	write('/'),
	write(X),
	write('/'),
	write(G),
	write('/'),
	write(Cpu),
	write('/'),
	write(StateO),
	write('/'),
	writeln(StateX).

meer_keer(Sp1,Sp2,A,O,X,G,OldStateO,OldStateX,OldCpu) :-
	sp(Sp1,Sp2,Winnaar,Stateso,Statesx,Cpu),
	tel_op(O,X,G,Winnaar,Onew,Xnew,Gnew),
	Anew is A - 1,
	TotaalStateO is Stateso + OldStateO,
	TotaalStateX is Statesx + OldStateX,
	TotaalCpu is Cpu + OldCpu,
	
	meer_keer(Sp1,Sp2,Anew,Onew,Xnew,Gnew,TotaalStateO,TotaalStateX,TotaalCpu).

tel_op(O,X,G,gelijk,O,X,Gnew) :-
	Gnew is G + 1.

tel_op(O,X,G,x,O,Xnew,G) :-
	Xnew is X + 1.

tel_op(O,X,G,o,Onew,X,G) :-
	Onew is O + 1.
		
%sp(diepte1/heur1, diepte2/heur2 ) heuristiek = (simpel/dynamisch)

sp(A,B,W) :-
	sp(A,B,W,_,_,_).

sp(D1/Heur1,D2/Heur2,Winnaar,Stateso,Statesx,Cputime) :-
	statistics(cputime,Cpu),
	
	retractall(statestotaal(_,_)),
	assert(statestotaal(x,0)),
	assert(statestotaal(o,0)),
		
	retractall(speelstijl(_,_,_)),
	assert(speelstijl(o,D1,Heur1)),
	assert(speelstijl(x,D2,Heur2)),
	beginstate(State),
	speel(State,10,Winnaar),
	
	retract(statestotaal(o,Stateso)),
	retract(statestotaal(x,Statesx)),
	write('states dit spel doorzocht door o / x :'),
	write(Stateso),
	write(' / '),			%weer geven hoeveel states er totaal bekeken zijn
	writeln(Statesx),
	write('dit heeft de cpu verspild aan dit spelletje: '),
	statistics(cputime,Cpu2),
	Cputime is Cpu2 - Cpu,
	writeln(Cputime).
	
speel(_,0,_) :-
	write('spel klaar? error... :/ ').

speel(S,_,Winnaar) :-
	aanzet(Aan),
	speelstijl(Aan,_,Heur),
	klaar(S,Val,0,Heur,Winnaar),
	write('het spel is al afgelopen! evaluatie: '),writeln(Val),!,
	show_result(S,Val,Winnaar).

	
speel([H|T],SpelD,Winnaar) :-
	retractall(aanzet(_)),
	assert(aanzet(H)),

	speelstijl(H,D,Heur),
	
	doezet([H|T],Zet,V,D,Heur),
	%	writeln('de zet die echt gemaakt wordt: '),
	write(Zet),tab(2),writeln(V),
	showbord(Zet),
	SpelDNew is SpelD -1,!,		%zodra een zet gedaan is niet meer terug gaan
	write('aantal states bekeken: '),
	retract(states(States)),
	writeln(States),writeln(' '),			%weer geven hoeveel states er bekeken zijn
	assert(states(0)),

	retract(statestotaal(H,Totaal)),
	TotaalState is Totaal + States,
	assert(statestotaal(H,TotaalState)),

	
	speel(Zet,SpelDNew,Winnaar).
	
show_result(S,_,gelijk) :-
	not(member(l,S)),
	writeln('het is gelijkspel').
	
show_result(_,_,Player) :-
	write('de winnaar is: '),writeln(Player).

doezet([H|T],Zet,V,D,Heur) :-
	
	my_minimax([H|T],ZetLijst,V,D,Heur),
	%write('member van '),writeln(ZetLijst),!,
	geef_beste(ZetLijst,Zet,_Vnu,Heur).
	%write('met diepte 0: '),writeln(Vnu).
	
geef_beste([A,B,C,D,E,F,G,H,I,J],[A,B,C,D,E,F,G,H,I,J],V,Heur) :- 
evalueer([A,B,C,D,E,F,G,H,I,J],V,Heur).

geef_beste([[A,B,C,D,E,F,G,H,I,J]],[A,B,C,D,E,F,G,H,I,J],V,Heur) :- 
evalueer([A,B,C,D,E,F,G,H,I,J],V,Heur).

geef_beste(List,Beste,Vnu,Heur) :-
	bestmove(List,BesteLijst,Vnu,0,Heur,list),
	%write('de nieuwe lijst: '),write(BesteLijst),
	de_member(Beste,BesteLijst).	

de_member([A,B,C,D,E,F,G,H,I,J],[A,B,C,D,E,F,G,H,I,J]).
de_member(Beste,BesteLijst) :-
	%random member?
	length(BesteLijst,Z),
	Z2 is Z+1,
	new_random(1,Z2,Rand),
	%write('random van 1 tot '),write(Z2),write(' geeft: '),writeln(Rand),
	memberx(BesteLijst,Rand,Beste).
	%writeln(Beste).

new_random(1,1,1).
new_random(1,Z,R) :-
	random(1,Z,R).

memberx([H|_],1,H).
memberx([_|T],R,H) :-
	Rnew is R - 1,
	memberx(T,Rnew,H).

my_minimax(S,S,VSout,D,Heur) :-
	klaar(S,VSout,D,Heur,_).		%vsout krijgt een waarde hier, afhankelijk van heuristiek
	
my_minimax(S,S,VSout,0,Heur) :-
	evalueer(S,VSout,Heur).		%vsout krijgt een waarde hier, afhankelijk van heuristiek
		
my_minimax(S,Sout,VSout,D,Heur) :-
	D \== 0,
	moves(S,OpvS),
	%	writeln(' de movez... '),
	D1 is D - 1,		%directe opvolgers
	bestmovelist(OpvS,Sout,VSout,D1,Heur).	
bestmovelist([],[],0,_,_) :-
	write('geen opvolgers of wat? :s ').
		
bestmovelist([[A,B,C,D,E,F,G,H,I,J]],[A,B,C,D,E,F,G,H,I,J],VSout,_,Heur) :-
	evalueer([A,B,C,D,E,F,G,H,I,J],VSout,Heur).


bestmovelist(OpvS,SoutList,VSout,D1,Heur) :-
	%OpvS is lijst van aantal states die beoordeeld moeten worden
	%Sout is beste daarvan
	%VSout is evaluatie van die state
	
	%Lijst is een lijst met alle die waarde VSout opleveren
	%en Sout daarvan moet de beste zijn op korte termijn
	%bijv. op diepte 3 levert iets 7 op, 10 mogelijkheden om dat te krijgen, deze 10 in Lijst,
	%dan op diepte 1 kijken welke van die 10 beste is
%		write(' lijst met opvlgs '),writeln(OpvS),
	bestmove3(OpvS,SoutList,VSout,D1,Heur).		%failure.. ergens :/
%		write(' lijst met goede mogelijkheden '),
	%	write(' met evaluatie '),write(VSout),write(' -> '),
%	writeln(SoutList).
		
	%bestmove3(SoutList,[Sout],V,0,Heur),
	%write('de zet wordt: '),write(Sout),write(' evaluatie nu: '),write(V),writeln(' '),!
	


bestmove3([S],S2,V,D,Heur) :-		%een item in lijst over
	my_minimax(S,S2,V,D,Heur).

bestmove3(S,[S],V,0,Heur) :-
	evalueer(S,V,Heur),write(' val: '),writeln(V).

bestmove3(S,SoutList,V,D,Heur) :-
	bestmove(S,SoutList,V,D,Heur,list).
	
bestmove([S],[S],V,D,Heur,_) :-
	my_minimax(S,S,V,D,Heur).

bestmove([H,H2|T],Eind,VEind,D,Heur,Aantal) :-		%is goed
	my_minimax(H,_,VH,D,Heur),
	my_minimax(H2,_,VH2,D,Heur),
	
	beste(H,VH,H2,VH2,BestMoveList,VBestMove,Aantal),
	bestmove2(T,BestMoveList,VBestMove,D,Heur,Eind,VEind,Aantal).

bestmove2([],S,V,_,_,S,V,_).
		
bestmove2([H|T],S,V,D,Heur,Eind,VEind,Aantal) :-
	my_minimax(H,_,VH,D,Heur),
	beste(H,VH,S,V,BestMoveList,VBestMove,Aantal),
	bestmove2(T,BestMoveList,VBestMove,D,Heur,Eind,VEind,Aantal).


samen_lijst(A,B,[A|[B]]) :-
	length(B,10),!.
samen_lijst(A,B,[A|B]).


beste(STail,VTail,H,VH,Lijst,VTail,list) :-
	VTail = VH,!,
	samen_lijst(STail,H,Lijst).
	
beste([H|STail],VTail,_,VH,[H|STail],VTail,_) :-
	VTail > VH,not(aanzet(H)),!.
	
beste([H|STail],VTail,_,VH,[H|STail],VTail,_) :-
	VTail < VH,aanzet(H),!.
	
beste(_,_,SH,VH,SH,VH,_).

showbord([_,A,B,C,D,E,F,G,H,I]) :-
	tab(1),write(A),write(' | '),write(B),write(' | '),writeln(C),writeln('---+---+---'),
	tab(1),write(D),write(' | '),write(E),write(' | '),writeln(F),writeln('---+---+---'),
	tab(1),write(G),write(' | '),write(H),write(' | '),writeln(I),nl.
	


/*

%  3 | 2 | 3
% ---+---+---
%  2 | 4 | 2
% ---+---+---
%  3 | 2 | 3
% deze methode neemt de standaard waarden van de velden, en telt extra punten per positie op aan de hand van aanliggende zetten
% door de huidige speler (beloning) en de andere speler (straf)
waardetotaalX(0,_,0).		
waardetotaalX(Nr,[P|S],VSout) :-
	NieuwNr is Nr -1,
	waardetotaalX(NieuwNr,[P|S],VStotaal),!,
        ander(P,P2),
	waardeX(Nr,P2,S,WaardeX),!,
%        write('WaardeX op '),write(Nr),write(' is '),writeln(WaardeX),
	VSout is VStotaal + WaardeX.

%waardeX(Positie,Player,State,Waarde)
waardeX(1,P,S,W) :-	%    S als [P,_,_,_,_,_,_,_,_],		% 123-159-147
    P \== l,    waarde(1,[P|S],W0),  controleer(2,P,S,W1),    controleer(3,P,S,W2),    controleer(4,P,S,W3),    controleer(5,P,S,W4),    controleer(7,P,S,W5),    controleer(9,P,S,W6),    W is W0+W1+W2+W3+W4+W5+W6.
waardeX(2,P,S,W) :-	%    S als [_,P,_,_,_,_,_,_,_],		% 123-258
    P \== l,    waarde(2,[P|S],W0),    controleer(1,P,S,W1),    controleer(3,P,S,W2),    controleer(5,P,S,W3),    controleer(8,P,S,W4),    W is W0+W1+W2+W3+W4.
waardeX(3,P,S,W) :-	%    S als [_,_,P,_,_,_,_,_,_],		% 123-357-369
    P \== l,    waarde(3,[P|S],W0),    controleer(1,P,S,W1),    controleer(2,P,S,W2),    controleer(5,P,S,W3),    controleer(6,P,S,W4),    controleer(7,P,S,W5),    controleer(9,P,S,W6),    W is W0+W1+W2+W3+W4+W5+W6.
waardeX(4,P,S,W) :-	%    S als [_,_,_,P,_,_,_,_,_],		% 147-456
    P \== l,    waarde(4,[P|S],W0),    controleer(1,P,S,W1),    controleer(5,P,S,W2),    controleer(6,P,S,W3),    controleer(7,P,S,W4),    W is W0+W1+W2+W3+W4.
waardeX(5,P,S,W) :-	%    S als [_,_,_,_,P,_,_,_,_],		% 159-258-357-456
    P \== l,    waarde(5,[P|S],W0),    controleer(1,P,S,W1),    controleer(2,P,S,W2),    controleer(3,P,S,W3),    controleer(4,P,S,W4),    controleer(6,P,S,W5),    controleer(7,P,S,W6),    controleer(8,P,S,W7),    controleer(9,P,S,W8),    W is W0+W1+W2+W3+W4+W5+W6+W7+W8.
waardeX(6,P,S,W) :-	%    S als [_,_,_,_,_,P,_,_,_],		% 369-456
    P \== l,    waarde(6,[P|S],W0),    controleer(3,P,S,W1),    controleer(4,P,S,W2),    controleer(5,P,S,W3),    controleer(9,P,S,W4),    W is W0+W1+W2+W3+W4.
waardeX(7,P,S,W) :-	%    S als [_,_,_,_,_,_,P,_,_],		% 147-357-789
    P \== l,    waarde(7,[P|S],W0),    controleer(1,P,S,W1),    controleer(3,P,S,W2),    controleer(4,P,S,W3),    controleer(5,P,S,W4),    controleer(8,P,S,W5),    controleer(9,P,S,W6),    W is W0+W1+W2+W3+W4+W5+W6.
waardeX(8,P,S,W) :-	%    S als [_,_,_,_,_,_,_,P,_],		% 258-789
    P \== l,    waarde(8,[P|S],W0),    controleer(2,P,S,W1),    controleer(5,P,S,W2),    controleer(7,P,S,W3),    controleer(9,P,S,W4),    W is W0+W1+W2+W3+W4.
waardeX(9,P,S,W) :-	%    S als [_,_,_,_,_,_,_,_,P],		% 159-369-789
    P \== l,    waarde(9,[P|S],W0),    controleer(1,P,S,W1),    controleer(3,P,S,W2),    controleer(5,P,S,W3),    controleer(6,P,S,W4),    controleer(7,P,S,W5),    controleer(8,P,S,W6),    W is W0+W1+W2+W3+W4+W5+W6.
waardeX(_,_,_,0).

*/

controleer(Nr,P,S,Waarde):-
    P\==l,    ander(P,P2),
    DM1 is 3, DM2 is 5,    %modifiers voor huidige positie aan de hand van gegeven zetten op bord van speler 1 (DM1) en speler 2 (DM2),
    ((controle(Nr,[P|S]),Waarde is DM1);(controle(Nr,[P2|S]),Waarde is DM2);(not(integer(Waarde)),Waarde is 0)).
	    %     write([P|S]),writeln(Waarde).
controle(1,[X,X,_,_,_,_,_,_,_,_]).
controle(2,[X,_,X,_,_,_,_,_,_,_]).
controle(3,[X,_,_,X,_,_,_,_,_,_]).
controle(4,[X,_,_,_,X,_,_,_,_,_]).
controle(5,[X,_,_,_,_,X,_,_,_,_]).
controle(6,[X,_,_,_,_,_,X,_,_,_]).
controle(7,[X,_,_,_,_,_,_,X,_,_]).
controle(8,[X,_,_,_,_,_,_,_,X,_]).
controle(9,[X,_,_,_,_,_,_,_,_,X]).
controle(_,_).



% ---------

staticval([_|S],Val) :-
        aanzet(Z),
        klaar2(S,X),
        ((X == Z,!,Val = 1);
        Val = -1).

staticval([_|S],0) :-
        full(S).

full([]).
full([H|T]) :-
        H \== l,
        full(T).

min_to_move([X| _]) :-
	ander(X, Y),
	aanzet(Y).

max_to_move([X| _]) :-
	aanzet(X).

moves(S,SLijst) :-		
	%vind alle moves en zet deze in de lijst SLijst
	setof(New,move(S,New),SLijst),!,
	retract(states(A)),
	length(SLijst,B),
	State is A + B,
	assert(states(State)).

move([AanZet|Srest],[Andere|Nieuw]) :-
	ander(AanZet,Andere),	%volgende speler aan de beurt
	move2(AanZet,Srest,Nieuw). %geef volgende zet

move2(AanZet,[l|T],[AanZet|T]).		
	%als er een l wordt gevonden.. vervangen door AanZet

move2(AanZet,[H|T],[H|Nieuw]) :-
	%een l in Srest vervangen door AanZet.
	move2(AanZet,T,Nieuw).

gomini([H|S]) :-
%[x,x,l,l,l,o,x,o,l,l]
        retractall(aanzet(_)),
        assert(aanzet(H)),
        retractall(speelstijl(_,_,_)),
        assert(speelstijl(o,9,simpel)),
        assert(speelstijl(x,9,simpel)),

        minimax([H|S],BesteZet,V),

        writeln('de zet die echt gemaakt wordt: '),
        write(BesteZet),tab(2),writeln(V),
        showbord(BesteZet).

goalpha([H|S]) :-
%[x,x,l,l,l,o,x,o,l,l]
        retractall(aanzet(_)),
        assert(aanzet(H)),
        retractall(speelstijl(_,_,_)),
        assert(speelstijl(o,9,simpel)),
        assert(speelstijl(x,9,simpel)),

        alphabeta([H|S],1,3,BesteZet,V),

        writeln('de zet die echt gemaakt wordt: '),
        write(BesteZet),tab(2),writeln(V),
        showbord(BesteZet).

klaar2([X,X,X,_,_,_,_,_,_],X).
klaar2([_,_,_,X,X,X,_,_,_],X).
klaar2([_,_,_,_,_,_,X,X,X],X).
klaar2([X,_,_,X,_,_,X,_,_],X).
klaar2([_,X,_,_,X,_,_,X,_],X).
klaar2([_,_,X,_,_,X,_,_,X],X).
klaar2([X,_,_,_,X,_,_,_,X],X).
klaar2([_,_,X,_,X,_,X,_,_],X).

evalueer(S,VSout,simpel) :-
	waardetotaal(9,S,VSout).
	
evalueer(S,VSout,dynamisch) :-
        waardetotaalX(9,S,VSout).

waardetotaal(0,_,0).		
waardetotaal(Nr,S,VSout) :-
	NieuwNr is Nr -1,
	waardetotaal(NieuwNr,S,VStotaal),!,
	waardeP(Nr,S,WaardeP),!,
	%waardeN(Nr,S,WaardeN),!,
	%VSout is VStotaal + WaardeP - WaardeN.
	VSout is VStotaal + WaardeP.
	
waardeP(Nr,[H|T],Waarde) :-
	ander(H,A),
	waarde(Nr,[A|T],Waarde).
	%waarde(Nr,[H|T],Waarde2),
	%Waarde is Waarde1 - Waarde2.

waardeN(Nr,[H|T],Waarde) :-
	waarde(Nr,[H|T],Waarde).
	
waarde(1,[X,X,_,_,_,_,_,_,_,_],3).
waarde(2,[X,_,X,_,_,_,_,_,_,_],2).
waarde(3,[X,_,_,X,_,_,_,_,_,_],3).
waarde(4,[X,_,_,_,X,_,_,_,_,_],2).
waarde(5,[X,_,_,_,_,X,_,_,_,_],4).
waarde(6,[X,_,_,_,_,_,X,_,_,_],2).
waarde(7,[X,_,_,_,_,_,_,X,_,_],3).
waarde(8,[X,_,_,_,_,_,_,_,X,_],2).
waarde(9,[X,_,_,_,_,_,_,_,_,X],3).
waarde(_,_,0).

ander(o, x).
ander(x, o).

