%%%%%%% HUGO %%%% alias %%%% EXPLOZE %%%%%%%%%%%%%%%%%%% main %%%%%%%%
g:-	game,!.
game:-	
	init(1,1,Pole),			% inicializece 
	uk(Pole),
	r_man(Xm,Ym,Pole),
	expand(Xm,Ym,man,Pole,NewPole),
	uk(NewPole),
	playpec(NewPole),!.
playpec(Pole):-
	a_pec(Xp,Yp,Pole),
       	expand(Xp,Yp,pec,Pole,NewPole),
	uk(NewPole),
	( najdi(_,_,_,man,_,NewPole), playman(NewPole),!;
	write('Congratulation to PECECKO!') ).
playman(Pole):-
	r_man(Xm,Ym,Pole),
	expand(Xm,Ym,man,Pole,NewPole),
	uk(NewPole),
	( najdi(_,_,_,pec,_,NewPole), playpec(NewPole),!;
	write('Congratulation to MAN!') ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% predikaty %%%%%%%%%%%%%%%%%%%%%%
najdi(_,_,_,_,_,[]):-
	fail.
najdi(X,Y,Kolik,Kdo,Max,[H|T]):-
	H = [X1,Y1,Kolik1,Kdo1,Max1],
	(X=X1, Y=Y1, Kolik=Kolik1, Kdo=Kdo1, Max=Max1;
	najdi(X,Y,Kolik,Kdo,Max,T)).	
% Struktura pole:
% [X, Y, Kolik punt'u, Kdo je vlastni, Maximum pro toto policko]
init(4,4,Seznam):-	% pole 1..4 x 1..4  % zde se meni velikost
	Seznam=[[4,4,0,nic,2]|[]].
init(X,Y,Seznam):-      % pole 1..4 x 1..4  % zde se meni velikost
	X1 is X+1,
	(X1<5, init(X1,Y,Sezn1); Y1 is Y+1, init(1,Y1,Sezn1) ),
	P1Max is 2,
	( (X>1, X<4, P2Max is P1Max+1); P2Max is P1Max ),
	( (Y>1, Y<4, Max is P2Max+1); Max is P2Max ),
	Seznam = [[X,Y,0,nic,Max]|Sezn1],!.
zmena(_,_,_,_,_,[],[]).
zmena(X,Y,Kolik,Kdo,Max,[H|T],[HOut|TOut]):-
	H = [X1,Y1,Kolik1,Kdo1,Max1],
	zmena(X,Y,Kolik,Kdo,Max,T,TOut),
	(X=X1, Y=Y1, HOut=[X,Y,Kolik,Kdo,Max],!;
	HOut = H).	
expand(0,_,_,S,S).   		      		% zde se meni velikost
expand(5,_,_,S,S).      			% zde se meni velikost
expand(_,0,_,S,S).       		      	% zde se meni velikost
expand(_,5,_,S,S).	         		% zde se meni velikost
expand(X,Y,Kdo,Seznam,Seznam1):-     		% zde se meni velikost
	X>0,X<5,Y>0,Y<5,
	najdi(X,Y,Kolik1,_,Max1,Seznam),
       	Kolik is Kolik1+1,
	(
	Kolik < Max1, zmena(X,Y,Kolik,Kdo,Max1,Seznam,Seznam1);
	Kolik =:= Max1, zmena(X,Y,0,nic,Max1,Seznam,Seznam5),
	najdi(_,_,_,man,_,Seznam),	% osetreni zacykleneho vitezstvi
	najdi(_,_,_,pec,_,Seznam),	% osetreni zacykleneho vitezstvi
	Xplus is X+1,
	Xminus is X-1,
	Yplus is Y+1,
	Yminus is Y-1,
	expand(Xplus,Y,Kdo,Seznam5,Seznam4),
	expand(X,Yminus,Kdo,Seznam4,Seznam3),
	expand(Xminus,Y,Kdo,Seznam3,Seznam2),
	expand(X,Yplus,Kdo,Seznam2,Seznam1);
	Seznam1=Seznam),!.	
man(X,Y,Seznam):-     			
	repeat,
	nl,
	write('Zadej, kam hraje MAN (tvar: "X/Y."): '),
	read(T),
	T=X/Y,
	najdi(X,Y,_,Kdo,_,Seznam), 
	(Kdo \= pec; 
	write('Zkus to jeste jednou, lepe a radostneji!'),fail ).
pec(X,Y,Seznam):-			
	repeat,
	nl,
	write('Zadej, kam hraje PECECKO (tvar: "X/Y."): '),
	read(T),
	T=X/Y, 
	najdi(X,Y,_,Kdo,_,Seznam), 
	(Kdo \= man; 
	write('Zkus to jeste jednou, lepe a radostneji!'),fail ).
write1(Kdo):-
	Kdo=man, write('o');
	Kdo=pec, write('x').
drawX(X,Y,Prah,Seznam):-     	      	% zde se meni velikost
	Prah1 is Prah+1,
	write(' '),		
	write(' '),
	write(' '),
	najdi(X,Y,Kolik,Kdo,_,Seznam),
	(Kolik>Prah, write1(Kdo); write('.')),
	write(' '),
	(Kolik>Prah1, write1(Kdo); write('.')),
	((X<4, X1 is X+1, drawX(X1,Y,Prah,Seznam)); true).
drawY(Y,Seznam):-			% zde se meni velikost
	drawX(1,Y,0,Seznam),
	nl,
	drawX(1,Y,2,Seznam),	
	nl,
	nl,
	((Y<4, Y1 is Y+1, !, drawY(Y1,Seznam)); true).
uk(Seznam):-     				
	nl,nl,
	write('HUGO alias EXPLOZE    verze 2.0      o...man x...pececko'),
	nl,nl,
	drawY(1,Seznam).

%%%%%%%%%%%%%%%%% alternativa zadavani random %%%%%%%%%%%%%%%%%%%
r_man(X,Y,Seznam):-			       % zde se meni velikost
	repeat,
	random(NahX),
	random(NahY),
	mod(NahX,4,Nah2X),
	mod(NahY,4,Nah2Y),
	X2 is Nah2X + 1,
	Y2 is Nah2Y +1,
	(najdi(X2,Y2,_,nic,_,Seznam);najdi(X2,Y2,_,man,_,Seznam)),
	X is X2,
	Y is Y2,!.
r_pec(X,Y,Seznam):-
	repeat,
	random(NahX),	    
	random(NahY),
	mod(NahX,4,Nah2X),
        mod(NahY,4,Nah2Y),
        X2 is Nah2X + 1,
        Y2 is Nah2Y +1,
	(najdi(X2,Y2,_,nic,_,Seznam);najdi(X2,Y2,_,man,_,Seznam)),
	X is X2,
	Y is Y2,!.

%%%%%%%%%%%%%%% predikaty pro minimax a alfa-beta proceduru %%%%%%%%
% struktura Pozice: 
% [Pro koho optimalizace, Uroven zanoreni, Kdo tahl, KamX, KamY, Situace]

m_pec(X,Y,Pole):-			% (minimax alternativa zadavani)
	write('Moment, myslim...'),
	minimax([pec,0,man,_,_,Pole],GoodPos,Val),
%	write(Val),
	GoodPos=[_,_,_,X,Y,_],!.
m_man(X,Y,Pole):-			% (minimax alternativa zadavani)
	write('Moment, myslim...'),
	minimax([man,0,pec,_,_,Pole],GoodPos,Val),
%	write(Val),
	GoodPos=[_,_,_,X,Y,_],!.
a_pec(X,Y,Pole):-			% (alfa-beta alternativa zadavani)
	write('Moment, myslim...'),
	alphabeta([pec,0,man,_,_,Pole],0,30000,GoodPos,Val),
%	write(Val),
	GoodPos=[_,_,_,X,Y,_],!.
a_man(X,Y,Pole):-			% (alfa-beta alternativa zadavani)
	write('Moment, myslim...'),
	alphabeta([man,0,pec,_,_,Pole],0,30000,GoodPos,Val),
%	write(Val),
	GoodPos=[_,_,_,X,Y,_],!.

appendlist([],List,List).
appendlist([Head|Tail],List,[Head|Newlist]):-
	appendlist(Tail,List,Newlist).
memberof(H,[H|_]).
memberof(Q,[_|T]):-
	memberof(Q,T).
moves([Pro,Uroven,Tahl,KamX,KamY,Seznam],PosList):- 	
%	write(Uroven),
	Uroven<3,	% hlida si hranici zanoreni
	(Tahl=man, Kdo=pec; Kdo=man),
	Uroven1 is Uroven+1,
	setof([Pro,Uroven1,Kdo,X,Y,Seznam1],
		(memberof([X,Y,_,Kdopak,2],Seznam),
		(Kdopak=nic; Kdopak=Kdo),
		expand(X,Y,Kdo,Seznam,Seznam1)	
		),		
		PosList2),
	setof([Pro,Uroven1,Kdo,X,Y,Seznam1],
		(memberof([X,Y,_,Kdopak,3],Seznam),
		(Kdopak=nic; Kdopak=Kdo),
		expand(X,Y,Kdo,Seznam,Seznam1)	
		),		
		PosList3),
	setof([Pro,Uroven1,Kdo,X,Y,Seznam1],
		(memberof([X,Y,_,Kdopak,4],Seznam),
		(Kdopak=nic; Kdopak=Kdo),
		expand(X,Y,Kdo,Seznam,Seznam1)	
		),		
		PosList4),
	appendlist(PosList3,PosList4,PosList1),
	appendlist(PosList2,PosList1,PosList),
	!.
min_to_move([Pro,_,Tahl,_,_,_]):- 	
	Pro=Tahl.
max_to_move([Pro,_,Tahl,_,_,_]):-
	Pro\=Tahl.

%%%%%%%%%%%%%%%%%%%%%%%%%% minimax %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
minimax(Pos,BestSucc,Val):-
	moves(Pos,PosList),!,best(PosList,BestSucc,Val);
	staticval(Pos,Val).
best([Pos],Pos,Val):-
	minimax(Pos,_,Val),!.
best([Pos1|PosList],BestPos,BestVal):-
	minimax(Pos1,_,Val1),
	best(PosList,Pos2,Val2),
	betterofmm(Pos1,Val1,Pos2,Val2,BestPos,BestVal).
betterofmm(Pos0,Val0,Pos1,Val1,Pos0,Val0):-
	min_to_move(Pos0),Val0>Val1,!;
	max_to_move(Pos0),Val0<Val1,!.
betterofmm(Pos0,Val0,Pos1,Val1,Pos1,Val1).

%%%%%%%%%%%%%%%%%%%%%%% alfa-beta procedura %%%%%%%%%%%%%%%%%%%%
alphabeta(Pos,Alpha,Beta,GoodPos,Val):-
%	Pos=[_,A,B,C,D|T],write('uroven'),writeln(A),writeln(B),write('kam: '),
%	write(C),write('-'),writeln(D),
	moves(Pos,PosList),!,
	boundedbest(PosList,Alpha,Beta,GoodPos,Val);
	staticval(Pos,Val).
boundedbest([Pos|PosList],Alpha,Beta,GoodPos,GoodVal):-
	alphabeta(Pos,Alpha,Beta,_,Val),
	goodenough(PosList,Alpha,Beta,Pos,Val,GoodPos,GoodVal).
goodenough([],_,_,Pos,Val,Pos,Val):-
	!. 		% no other candidate
goodenough(_,Alpha,Beta,Pos,Val,Pos,Val):-
	min_to_move(Pos),Val>Beta,!; 	% maximizer attained upper bound
	max_to_move(Pos),Val<Alpha,!.	% minimizer attained lower bound
goodenough(PosList,Alpha,Beta,Pos,Val,GoodPos,GoodVal):-
	newbounds(Alpha,Beta,Pos,Val,NewAlpha,NewBeta),	% refine bounds
	boundedbest(PosList,NewAlpha,NewBeta,Pos1,Val1),
	betterof(Pos,Val,Pos1,Val1,GoodPos,GoodVal).
newbounds(Alpha,Beta,Pos,Val,Val,Beta):-
	min_to_move(Pos),Val>Alpha,!. 	% maximizer increased lower bound
newbounds(Alpha,Beta,Pos,Val,Alpha,Val):-
	max_to_move(Pos),Val<Beta,!.	% minimizer decreaser upper bound
newbounds(Alpha,Beta,_,_,Alpha,Beta).
betterof(Pos,Val,Pos1,Val1,Pos,Val):-
	min_to_move(Pos),Val>Val1,!;
	max_to_move(Pos),Val<Val1,!.
betterof(_,_,Pos1,Val1,Pos1,Val1).

%%%%%%%%%%%%%%% heurisitcke funkce %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
vitez(Kdo,Seznam):-
	(Kdo=man, Druhy=pec; Druhy=man),
	not(najdi(_,_,_,Druhy,_,Seznam)).
porazen(Kdo,Seznam):-
	not(najdi(_,_,_,Kdo,_,Seznam)).
% pocet punt'u jedne barvy
secti(_,Suma,[]):-
	Suma is 0.
secti(Kdo,Suma,[H|T]):-
	secti(Kdo,Suma1,T),	
	H = [_,_,Kolik,KdoTo,_],
	(Kdo=KdoTo, Suma is Suma1+Kolik;
	Suma is Suma1).
% pocet punt'u ktery chybi vsem polickum aby se rozprskla
rozprsk(_,Suma,[]):-
	Suma is 0.
rozprsk(Kdo,Suma,[H|T]):-
	rozprsk(Kdo,Suma1,T),	
	H = [_,_,Kolik,KdoTo,Max],
	(Kdo=KdoTo, Suma is Suma1+(Max-Kolik);
	Suma is Suma1).
% pocet obsazenych poli (nikoliv jednotlivych punt'u)
obsazenych(_,Suma,[]):-
	Suma is 0.
obsazenych(Kdo,Suma,[H|T]):-
	obsazenych(Kdo,Suma1,T),	
	H = [_,_,_,KdoTo,_],
	(Kdo=KdoTo, Suma is Suma1+1;
	Suma is Suma1).
% pocet hran mezi poli stejneho hrace
sousednost(_,Suma,[],Seznam):-
	Suma is 0.
sousednost(Kdo,Suma,[H|T],Seznam):-
	sousednost(Kdo,Suma3,T,Seznam),	
	H = [X,Y,_,KdoTo,_],
	(Kdo=KdoTo,  
	X1 is X+1,
	Y2 is Y+1,	
	najdi(X1,Y,_,Kdo1,_,Seznam),
	najdi(X,Y2,_,Kdo2,_,Seznam),
	(Kdo1 = Kdo, Suma1 is 1; Suma1 is 0),
	(Kdo2 = Kdo, Suma2 is 1; Suma2 is 0),
	Suma is Suma1+Suma2+Suma3;
	Suma is Suma3).
% pocet obsazenych rohu
rohy(_,Suma,[]):-
	Suma is 0.
rohy(Kdo,Suma,[H|T]):-
	rohy(Kdo,Suma1,T),	
	H = [_,_,_,KdoTo,Max],
	(Kdo=KdoTo, Max =:= 2, Suma is Suma1+1;
	Suma is Suma1).
% pocet obsazenych boku
boky(_,Suma,[]):-
	Suma is 0.
boky(Kdo,Suma,[H|T]):-
	boky(Kdo,Suma1,T),	
	H = [_,_,_,KdoTo,Max],
	(Kdo=KdoTo, Max =:= 3, Suma is Suma1+1;
	Suma is Suma1).
% heuristicke ohodnoceni pozice	hodnotami z intervalu [0,30000]
% vyssi ohodnoceni lepsi, horsi pro protihrace
% nizsi ohodnoceni horsi, lepsi pro protihrace 
staticval([Pro,Uroven,Tahl,KamX,KamY,Seznam],Val):-
	write('.'),
	vitez(Pro,Seznam), Val is 30000;   %rekneme maximum
	porazen(Pro,Seznam), Val is 0;
% pocet punt'u jedne barvy
  	secti(Pro,Val1,Seznam),
% pocet punt'u ktery chybi vsem polickum aby se rozprskla
	rozprsk(Pro,Val2,Seznam),
% pocet obsazenych poli (nikoliv jednotlivych punt'u)
	obsazenych(Pro,Val3,Seznam),
% pocet hran mezi poli stejneho hrace
	sousednost(Pro,Val4,Seznam,Seznam),
% pocet obsazenych rohu
	rohy(Pro,Val5,Seznam),
% pocet obsazenych boku
	boky(Pro,Val6,Seznam),
	Val is (2*(3*Val1+Val5)+Val6)+0*Val4+0*Val2+0*Val3,!.

%%%%%%%%%%%%%%%%%%%%%%%%%% that's all folks %%%%%%%%%%%%%%%%%%%%%%%%%%%
