% vim:ft=prolog

%'@' - black
%'O' - white
%'_' - espaco
%'B' - Dama preta
%'P' - Dama branca

tabuleiro([
	  ['@','_','@','_','@','_','@','_'],
	  ['_','@','_','@','_','@','_','@'],
	  ['@','_','@','_','@','_','@','_'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','0','_','0','_','0','_','0'],
	  ['0','_','0','_','0','_','0','_'],
	  ['_','0','_','0','_','0','_','0']
	                                  ]).

tabuleiro2([
	  ['@','_','@','_','B','_','_','@'],
	  ['_','@','_','_','_','@','_','_'],
	  ['_','_','_','_','_','_','B','_'],
	  ['_','0','_','@','_','_','_','_'],
	  ['_','_','0','_','0','_','_','_'],
	  ['_','P','_','_','_','_','_','_'],
	  ['@','_','_','_','_','_','0','_'],
	  ['_','0','_','0','_','0','_','_']
	                                  ]).

tabuleiro3([
	  ['@','_','@','_','@','_','@','_'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','@','_'],
	  ['_','@','_','_','_','_','_','_'],
	  ['0','_','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','0','_'],
	  ['_','_','_','0','_','_','_','_']
	                                  ]).
tabuleiro4([
	  ['_','_','@','_','_','_','_','_'],
	  ['_','@','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','0','_','_','_','_','_','@'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','_','_'],
	  ['_','_','_','_','_','_','_','_']
	                                  ]).

tabuleiro5([
	  ['@','_','@','_','@','_','@','_'],
	  ['_','@','_','@','_','@','_','@'],
	  ['@','_','@','_','@','_','_','_'],
	  ['_','_','_','_','_','_','_','@'],
	  ['0','_','_','_','_','_','_','_'],
	  ['_','0','_','0','_','0','_','0'],
	  ['0','_','_','_','0','_','0','_'],
	  ['_','0','_','0','_','0','_','0']
	                                  ]).


/******************************************/
/*       Predicados de entrada de dados   */
/******************************************/

inicio :-
	retractall( computador( X, Y ) ), retractall( nivel( X ) ),
	write('1- Humano/Humano'),nl,
	write('2- Humano/Computador'),nl,
	write('3- Computador/Humano'),nl,
	write('4- Computador/Computador'),nl,
	read(Opcao),	
	(
		Opcao == 1, assert( computador( X, fail ) )
		;
		write( '1-Easy' ),nl,
		write( '2-Medium' ),nl,
		write( '3-Hard' ),nl,
		write( '4-Very Hard (nao recomendado)' ),nl,
		read( Dificuldade ), assert( nivel( Dificuldade ) ),
		(
		Opcao == 2, assert( computador( 'pretas', true ) )
		;
		Opcao == 3, assert( computador( 'brancas', true ) )
		;
		Opcao == 4, assert( computador( 'brancas', true ) ), assert( computador( 'pretas', true ) )
		)
		;
		true
	),
	tabuleiro(Board),
	joga('brancas', Board),
	retractall( computador( X, Y ) ), retractall( nivel( X ) ).

/******************************************/
/*        Predicado de de jogo            */
/******************************************/


joga(Jogador, Board) :-
	showBoard(Board),
	(
		computador( Jogador, Value ), Value,
		nivel( Dificuldade ), intelectual( Jogador, Dificuldade, Board, Mov-NewBoard ),write(Mov),nl
		;
		recebeJogada(Jogador, Board, NewBoard )
	),
	mudaJogador(Jogador, Jogador2),
	(acabou(Jogador2, NewBoard),nl,showBoard(NewBoard),assert(final(NewBoard)),nl,write('Jogo teminou. Ganharam as '), write(Jogador), nl
	;
	joga(Jogador2, NewBoard)).

mudaJogador('brancas', 'pretas').
mudaJogador('pretas', 'brancas').

/******************************************/
/*	    Predicados do tabuleiro       */
/******************************************/

showBoard(Board) :-
	nl,
	showLines( Board, 1 ),
	write('  A B C D E F G H'),nl.

showLines( [], _ ).
showLines( [Cabeca | Resto], N ) :-
	write(N),
	write(' '),
	N1 is N + 1,
	showLine(Cabeca),nl,
	showLines(Resto, N1).


showLine( [] ).
showLine( [Cabeca | Resto] ) :-
	write(Cabeca),
	write(' '),
	showLine(Resto).

/*******************************************/
/*           Predicados de jogada          */
/*******************************************/

recebeJogada(Jogador, Board, NewBoard) :-
	repeat,
	write('Jogada '),write(Jogador),write(' - '),
	read(X),name(X,Lista),modificaLista( Lista, ListaFinal),
	validaJogada( Jogador, ListaFinal, Board, NewBoard, TipoJogada ).

/******************************************/
/*       Predicado para validar jogada    */
/******************************************/

validaJogada( Jogador, [ColIni, LinIni, ColFim, LinFim], Board, NewBoard, TipoJogada ) :-!,
	(
	getPeca( ColFim, LinFim, Board, '_' ),
	getPeca( ColIni, LinIni, Board, Peca ), pecas( Jogador, Pc, PcDama ), (Peca == Pc ; Peca == PcDama ),
	obrigaComer( Jogador, Board, Lista ),!,
	length( Lista, N ),
		(
		( N == 0 )
		;
		member( [ColIni, LinIni, ColFim, LinFim], Lista )
		),
	(
	( Peca == 'B'; Peca == 'P' ),
		verificaJogadaDama( ColIni, LinIni, ColFim, LinFim ),
		mudaJogador( Jogador, Adv ),
		movimentoDama( Adv, Peca, ColIni, LinIni, ColFim, LinFim, Board, NB, TJ )
	;
	verificaLinha( Jogador, LinIni, LinFim ),
	verificaColuna( ColIni, ColFim ),
	TJ = 'simples',
	fazJogada( Peca, TJ, ColIni, LinIni, ColFim, LinFim, Board, NB )
	;
	determinaIntermedia( Jogador, ColIni, LinIni, ColFim, LinFim, X, Y ),
	member( [ColIni, LinIni, ColFim, LinFim], Lista ),
/*	verificaLinha( Jogador, LinIni, Y ),
	verificaLinha( Jogador, Y, LinFim ),
	verificaColuna( ColIni, X ),
	verificaColuna( X, ColFim ),

	getPeca( X, Y, Board, PecaIntermedia ),
	(( Jogador == 'brancas', (PecaIntermedia == '@'; PecaIntermedia == 'P') ) ; ( Jogador == 'pretas', (PecaIntermedia == '0'; PecaIntermedia == 'B'))),*/

	TJ = 'come',
	fazJogada( Peca, TJ, ColIni, LinIni, X, Y, ColFim, LinFim, Board, NB )
	)
	;
	fail), verificaSeFazDama( Peca, ColFim, LinFim, NB, NewBoard, TJ, TipoJogada ).
	
validaJogada( Jogador, [ColIni, LinIni, ColFim, LinFim | Resto ], Board, NewBoard, TipoJogada ) :-
	validaJogada( Jogador, [ColIni, LinIni, ColFim, LinFim], Board, NewBoard1, TipoJogada2 ),
	validaJogada( Jogador, [ColFim, LinFim | Resto ], NewBoard1, NewBoard, TipoJogada ).

/*****************************************************/
/*   Predicado para validar movimentos de uma dama   */
/*****************************************************/

movimentoDama( Adv, Peca, ColFim, LinFim, ColFim, LinFim, NewBoard, NewBoard, TipoJogada ) :- TipoJogada = 'damaSimples'.
movimentoDama( Adv, Peca, ColIni, LinIni, ColFim, LinFim, Board, NewBoard, TipoJogada ) :-
	((ColFim > ColIni) -> X is ColIni + 1, X1 is X + 1 ; X is ColIni - 1, X1 is X - 1),
	((LinFim > LinIni) -> Y is LinIni + 1, Y1 is Y + 1 ; Y is LinIni - 1, Y1 is Y - 1),
	(
	getPeca( X, Y, Board, '_' ),
	TipoJogada2 = 'damaSimples',
	fazJogada( Peca, 'simples', ColIni, LinIni, X, Y, Board, NB ),
	Z = X, Z1 = Y
	;
	getPeca( X1, Y1, Board, '_' ),
	TipoJogada2 = 'damaCome',
	fazJogada( Peca, 'come', ColIni, LinIni, X, Y, X1, Y1, Board, NB ),
	Z = X1, Z1 = Y1
	),
	movimentoDama( Adv, Peca, Z, Z1, ColFim, LinFim, NB, NewBoard, TipoJogada3 ),
	( TipoJogada3 == 'damaSimples' -> TipoJogada = TipoJogada2 ; TipoJogada = 'damaCome' ).


descobrePeca( 'brancas', '0' ).
descobrePeca( 'brancasDama', 'B' ).
descobrePeca( 'pretas', '@' ).
descobrePeca( 'pretasDama', 'P' ).

/*********************************************************/
/*   Predicado para descobrir qual a peca numa posicao   */
/*********************************************************/

getPeca( Col, Lin, Board, Peca ) :-
	getLinha( Linha, Lin, 1, Board ),
	getLinha( Peca, Col, 1, Linha ).

getLinha(Member, N, N, [Member | R]).
getLinha(Member, N, P, [Cabeca | R]) :-
	P1 is P + 1,
	getLinha(Member, N, P1, R).

/*********************************************************/
/*   Predicado para verificar se a jogada origina dama   */
/*********************************************************/
verificaSeFazDama( Peca, ColFim, LinFim, Board, NewBoard, TJ, TipoJogada ) :-
	mudaPeca( Peca, NovaPeca ),
	(Peca == '0', LinFim == 1 ; Peca == '@', LinFim == 8)
	,
	mudaBoard( NovaPeca, ColFim, LinFim, Board, NewBoard ), TipoJogada = 'fazDama'
	;
	NewBoard = Board, TipoJogada = TJ,
	true.

mudaPeca( '0', 'B' ).
mudaPeca( '@', 'P' ).

verificaLinha('brancas', LinIni, LinFim ) :-
	X is LinIni - 1,
	X == LinFim.
verificaLinha( 'pretas', LinIni, LinFim ) :-
	X is LinIni + 1,
	X == LinFim.

verificaColuna( 1, 2 ).
verificaColuna( 8, 7 ).
verificaColuna( ColIni, ColFim ) :-
	 Z is ColIni + 1, ColFim == Z
	 ;
	 Z is ColIni - 1, ColFim == Z .

/***************************************************/
/*   Verifica se a movimentacao da dama é valida   */
/***************************************************/

verificaJogadaDama( ColIni, LinIni, ColFim, LinFim ) :-
	(ColIni \= ColFim, LinIni \= LinFim),
	X is abs(ColIni - ColFim),
	Y is abs(LinIni - LinFim),
	X == Y.

determinaIntermedia( 'brancas', ColIni, LinIni, ColFim, LinFim, X, Y ) :-
	(ColFim > ColIni, X is ColFim - 1 ; X is ColIni - 1),

	(Y is LinIni - 1 ).

determinaIntermedia( 'pretas', ColIni, LinIni, ColFim, LinFim, X, Y ) :-
	(ColFim > ColIni, X is ColFim - 1 ; X is ColIni - 1),
	(Y is LinIni + 1 ).

/*********************************************/
/*       Predicado para efectuar jogada      */
/*********************************************/

fazJogada( Peca, 'simples', ColIni, LinIni, ColFim, LinFim, Board, NewBoard ) :-
	mudaBoard( '_', ColIni, LinIni, Board, NewBoard1 ),
	mudaBoard( Peca, ColFim, LinFim, NewBoard1, NewBoard ).

fazJogada( Peca, 'come', ColIni, LinIni,X, Y,  ColFim, LinFim, Board, NewBoard ) :-
	fazJogada( Peca, 'simples', ColIni, LinIni, X, Y, Board, NewBoard1 ),
	fazJogada( Peca, 'simples', X, Y, ColFim, LinFim, NewBoard1, NewBoard ).

mudaBoard( Novo, Col, Lin, Board, NewBoard ) :-
	mudaBoard2( 1, Novo, Col, Lin, Board, NewBoard ).

mudaBoard2( _,_,_,_, [],[] ).
mudaBoard2(Y, Novo, X, Y, [Lin|Resto], [NovLin|NovResto]) :-
	mudaLinha( 1, Novo, X, Lin, NovLin ),
	N2 is Y + 1,
	mudaBoard2( N2, Novo, X, Y, Resto, NovResto ).
mudaBoard2(N, Novo, X, Y, [Lin|Resto], [Lin|NovResto] ) :-
	Y\=N, N2 is N + 1,
	mudaBoard2( N2, Novo, X, Y, Resto, NovResto ).

mudaLinha( _, _, _, [], [] ).
mudaLinha( X, Novo, X, [Peca|Resto], [Novo|NovResto] ) :-
	N2 = X + 1,
	mudaLinha( N2, Novo, X, Resto, NovResto ).
mudaLinha( N, Novo, X, [Peca|Resto], [Peca|NovResto] ) :-
	N \= X, N2 is N + 1,
	mudaLinha( N2, Novo, X, Resto, NovResto ).


/***************************************************/
/*     Predicado para traduzir asciis em numeros   */
/***************************************************/

modificaLista( [], [] ).
modificaLista( [Col, Lin |Resto], [NovaCol, NovaLinha|NovoResto] ) :-
	modifica( Col, Lin, NovaCol, NovaLinha ),
	modificaLista( Resto, NovoResto ).

modifica( X, Y, Col, Lin ) :-
	Col is X - 96,
	Lin is Y - 48.


/***************************************************/
/*       Predicado para ver se o jogo acabou       */
/***************************************************/

acabou( Jogador, Board ) :-	
	pecas( Jogador, PecaNormal, PecaDama ),
	findall( PecaNormal, getPeca(_,_, Board, PecaNormal), ListaNormal ),
	findall( PecaDama, getPeca(_,_, Board, PecaDama), ListaDama ),
	append(ListaNormal, ListaDama, Lista),
	length( Lista, N),
	(
	(N == 0)
	;
	acabou( Jogador, 1, 1, Board, M ),!,( N == M )
	).

acabou( _, 1, 9, _, 0).
acabou( Jogador, Col, Lin, Board, N ) :-
	(
		(Col < 8) -> C is Col + 1, L is Lin ; C is 1, L is Lin + 1
	),
	acabou( Jogador, C, L, Board, N2 ),
	getPeca(Col, Lin, Board, Peca),
	(
	(Peca == '_'), N = N2
	;
	((Jogador == 'brancas', Peca == '0'),
		(
		(Col == 8, Lin == 2),getPeca(7,1,Board,P), (P == '@' ; P == 'P')
		;
		((Col == 1 ),(C1 is Col + 1, C2 is Col + 2, L1 is Lin - 1, L2 is Lin - 2),( L2 >= 1, L2 =< 8 )
		;
		(Col == 8),(C1 is Col - 1, C2 is Col - 2, L1 is Lin - 1, L2 is Lin - 2),( L2 >= 1, L2 =< 8 )),
		getPeca(C1,L1,Board, P1), getPeca(C2,L2,Board,P2), (P1 == '@' ; P1 == 'P'), (P2 == '@' ; P2 == 'P')
		), N is N2 + 1)
	;
	((Jogador == 'pretas', Peca == '@'),
		(
		(Col == 1, Lin == 7),getPeca(2,8,Board,P), (P == '0' ; P == 'B')
		;
		((Col == 1 ),(C1 is Col + 1, C2 is Col + 2, L1 is Lin + 1, L2 is Lin + 2),( L2 >= 1, L2 =< 8 )
		;
		( Col == 8 ), (C1 is Col - 1, C2 is Col - 2, L1 is Lin + 1, L2 is Lin + 2),( L2 >= 1, L2 =< 8 )),
		getPeca(C1,L1,Board, P1), getPeca(C2,L2,Board,P2), (P1 == '0' ; P1 == 'B'), (P2 == '0' ; P2 == 'B')
		), N is N2 + 1)
	;
	true, N = N2
	).
/******************************************************/
/*	Predicados uteis na manipulacao de listas     */
/******************************************************/
	
append( [], B, B ).
append( [A|B], C, [A|D] ) :- append( B, C, D ).

member( [A, B, C, D], [A, B, C, D | T] ).
member( X, [_|T] ):-member(X,T).

/************************************************************************************/
/*       Predicado para ver se existe alguma posicao em que e obrigado a comer      */
/************************************************************************************/

obrigaComer( Jogador, Board, Lista ) :- !,
	obrigaComer( Jogador, 1, 1, Board, [], Lista ).

obrigaComer( _, 1, 9, _, Lista, Lista).
obrigaComer( Jogador, Col, Lin, Board, Lst, Lista ) :- 
	getPeca( Col, Lin, Board, Peca ),
	(
	Peca == '_',
	List = Lst
	;
	((Col == 1) ; (Col == 2)),obrigaDireita( Jogador, Col, Lin, ColF, LinF, Board, Peca ),
	Move = [Col, Lin, ColF, LinF],
	append( Lst, Move, List)
	;
	((Col == 8) ; (Col == 7)),obrigaEsquerda( Jogador, Col, Lin, ColF, LinF, Board, Peca ),
	Move = [Col, Lin, ColF, LinF],
	append( Lst, Move, List)
	;
	obrigaDireita( Jogador, Col, Lin, ColF1, LinF1, Board, Peca ),
	Move1 = [Col, Lin, ColF1, LinF1],
	append( Lst, Move1, List1),
	obrigaEsquerda( Jogador, Col, Lin, ColF2, LinF2, Board, Peca ),
	Move2 = [Col, Lin, ColF2, LinF2],
	append( List1, Move2, List)
	;
	obrigaDireita( Jogador, Col, Lin, ColF, LinF, Board, Peca ),
	Move = [Col, Lin, ColF, LinF],
	append( Lst, Move, List)
	;
	obrigaEsquerda( Jogador, Col, Lin, ColF, LinF, Board, Peca ),
	Move = [Col, Lin, ColF, LinF],
	append( Lst, Move, List)
	;
	List = Lst,
	true
	),
	(
		(Col < 8) -> C is Col + 1, L is Lin ; C is 1, L is Lin + 1
	),
	obrigaComer(Jogador, C, L, Board, List, Lista).

obrigaDireita( Jogador, Col, Lin, ColF, LinF, Board, P ) :-
	(
	(Jogador == 'brancas', (P == '0' ; P == 'B')),
		(Lin \= 1), (Lin \= 2),
		C1 is Col + 1, L1 is Lin - 1, getPeca( C1, L1, Board, Peca), (Peca == '@' ; Peca == 'P'),
		C2 is Col + 2, L2 is Lin - 2, getPeca( C2, L2, Board, '_' ), ColF = C2, LinF= L2
		;
	(Jogador == 'pretas', (P == '@' ; P == 'P')),
		(Lin \= 7), (Lin \= 8),
		C1 is Col + 1, L1 is Lin + 1, getPeca( C1, L1, Board, Peca), (Peca == '0' ; Peca == 'B'),
		C2 is Col + 2, L2 is Lin + 2, getPeca( C2, L2, Board, '_' ), ColF = C2, LinF= L2).

obrigaEsquerda( Jogador, Col, Lin, ColF, LinF, Board, P ) :-
	(
	(Jogador == 'brancas', (P == '0' ; P == 'B')),
		(Lin \= 1), (Lin \= 2),
		C1 is Col - 1, L1 is Lin - 1, getPeca( C1, L1, Board, Peca), (Peca == '@' ; Peca == 'P'),
		C2 is Col - 2, L2 is Lin - 2, getPeca( C2, L2, Board, '_' ), ColF = C2, LinF= L2
		;
	(Jogador == 'pretas', (P == '@' ; P == 'P')),
		(Lin \= 7), (Lin \= 8),
		C1 is Col - 1, L1 is Lin + 1, getPeca( C1, L1, Board, Peca), (Peca == '0' ; Peca == 'B'),
		C2 is Col - 2, L2 is Lin + 2, getPeca( C2, L2, Board, '_' ), ColF = C2, LinF= L2).

obrigaDama( Jogador, ColI, LinI, Board, Lista ) :-
	findall( [ColI, LinI, ColF, LinF],
	(getPeca(X, Y, Board, Peca), (Peca == '@' ; Peca == 'P'),write(X),write(Y),nl,
		verificaJogadaDama( ColI, LinI, X, Y ),write('passei'),nl,
		livres( ColI, LinI, X, Y, ColF, LinF, Board )
	), Lst), Lista = Lst.

livres(ColIni, LinIni, W, Z, ColFim, LinFim, Board) :-
	((W > ColIni) -> X is W + 1 ; X is W - 1),
	((Z > LinIni) -> Y is Z + 1 ; Y is Z - 1),
	getPeca( X, Y, Board, '_' ),
	ColFim = X, LinFim = Y.


		
/************************************************/
/*                 MiniMax                      */
/************************************************/

intelectual( Jogador, MaxDepth, Board, BestMov) :-
	write( 'Computador a pensar........'), nl,
	intelectual( 0, MaxDepth, Jogador, Board, BestMov, Val, Jogador ), !.

intelectual(Depth, MaxDepth, Jogador, Board, BestMov, Val, JogActual ) :-
 	geraJogadas( Jogador, Board, Lista ),!,
	Depth2 is Depth + 1,
	best( Depth2, MaxDepth, Jogador, Lista, BestMov, Val, JogActual ).

best( Depth, MaxDepth, Jogador, [Mov-Board], Mov-Board, Val, JogActual ) :-
	minimax( Depth, MaxDepth, Jogador, Board, _, Val, JogActual ), !.
best( Depth, MaxDepth, Jogador, [Mov1-Board1|BoardList], Mov-BestMov, BestVal, JogActual ) :- !,
	minimax( Depth, MaxDepth, Jogador, Board1, _, Val1, JogActual ), !,
	best( Depth, MaxDepth, Jogador, BoardList, Mov2, Val2, JogActual ),
	betterof( Jogador, Mov1-Board1, Val1, Mov2, Val2, Mov-BestMov, BestVal, JogActual ).

minimax( Depth, MaxDepth, Jogador, Board, BestMov, Val, JogActual ) :-
%	write(Depth),write(' - '),write(Jogador),nl,
	Depth < MaxDepth,
	mudaJogador( Jogador, Adv),
	Depth2 is Depth + 1,
	geraJogadas( Adv, Board, Lista ),!,
	length( Lista, Len ), (Len \= 0,best( Depth2, MaxDepth, Adv, Lista, BestMov, Val, JogActual ) ; Val = 1000).
minimax( Depth, MaxDepth, Jogador, Board, BestMov, Val, JogActual ) :- !,
	evaluate_board( JogActual, Board, Val ).

betterof( Jogador, Mov0, Val0, Mov1, Val1, Mov0, Val0, JogActual ) :-	
	mudaJogador( JogActual, Adv ),
	(
	Jogador == JogActual, Val0 > Val1, !
	;
	Jogador == Adv, Val0 < Val1, !
	).
betterof( Jogador, Mov0, Val0, Mov1, Val1, Mov1, Val1, JogActual ) :-!.

evaluate_board( Jogador, Board, Val ) :-
	nivel(X), X =< 2 -> evaluate_board1( Jogador, Board, Val ) ; evaluate_board2( Jogador, Board, Val ).

evaluate_board1( Jogador, Board, Val ) :-
	pecas( Jogador, Peca, PecaDama ),
	findall( Peca, getPeca(_,_,Board,Peca), Bag), length( Bag, Z1 ),
	findall( PecaDama,getPeca(_,_,Board,PecaDama), Bag2), length( Bag2, Z2 ),
	mudaJogador( Jogador, Adv ), pecas( Adv, PecaAdv, PecaDamaAdv ),
	findall( PecaAdv, getPeca(_,_,Board,PecaAdv), Bag3), length( Bag3, Z3 ),
	findall( PecaDamaAdv, getPeca(_,_,Board,PecaDamaAdv), Bag4), length( Bag4, Z4 ),

	Pecas is Z1 * 10, Damas is Z2 * 15, ValorJogador is Pecas + Damas,
	PecasAdv is Z3 * 10, DamasAdv is Z4 * 15, ValorJogadorAdv is PecasAdv + DamasAdv,
	Val is ValorJogador - ValorJogadorAdv.

evaluate_board2( Jogador, Board, Val ) :-
	pecas( Jogador, Pc, PcDama ),
	findall( [X,Y,Peca], (getPeca( X, Y, Board, Peca), (Peca == Pc ; Peca == PcDama)), Bag ),
	processaLista( Bag, Valor ),
	mudaJogador( Jogador, Adv ),
	pecas( Adv, PcAdv, PcDamaAdv ),
	findall( [X2,Y2,Peca2], (getPeca( X2, Y2, Board, Peca2), (Peca2 == PcAdv ; Peca2 == PcDamaAdv)), BagAdv ),
	processaLista( BagAdv, ValorAdv ),
	Val is Valor - ValorAdv.

processaLista( Lista, Valor ) :-
	processaLista( Lista, Valor, 0 ).

processaLista( [], Valor, Valor ).
processaLista( [[X,Y,Peca]|Resto], Valor, Acumulador ) :-
	(	
		((Peca == 'B' ; Peca == 'P'), valorDama(X,Y,Z), X2 is Acumulador + 15, X3 is X2 + Z)
		;
		((Peca == '0' ; Peca == '@'), valorPeca(X,Y,Z), X2 is Acumulador + 10, X3 is X2 + Z)
	)
	,
	processaLista( Resto, Valor, X3 ).

% Dama no rio e muito valiosa
valorDama(X,X,10). 
% Dama noutra casa qualquer
valorDama(_,_,5).

% Peca na defesa, valor elevado
valorPeca(_,1,10).
valorPeca(_,8,10).
% Peca nas colunas 1 ou 8 tem poucos movimentos, valor minimo
valorPeca(1,_,0).
valorPeca(8,_,0).
% Peca em qualquer outra posicao
valorPeca(_,_,5).

/*******************************************************************************/
/*    Predicado para gerar todas as jogadas validas de um determinado jogador  */
/*******************************************************************************/

geraJogadas( Jogador, Board, Lista ) :-
	pecas( Jogador, Peca, PecaDama ),	
	findall( [Y,X], (getPeca( Y,X, Board, Pc), (Pc == Peca ; Pc == PecaDama )), Bag ),
	findall( [Y2,X2], (getPeca( Y2,X2, Board, '_'), casaPreta( Y2,X2 )), Bag2 ),
	geraJogadas( Jogador, Bag, Bag2, Board, Lista, [] ).
	
geraJogadas( _, [], _, _, Lista, Lista ) :- !.
geraJogadas( Jogador, [[ColIni, LinIni] | Resto ], Bag, Board, Lista, Lst ) :-
	geraJogadas2( Jogador, ColIni, LinIni, Bag, Board, Lst2, [] ),
	append( Lst, Lst2, Lista2 ),
	geraJogadas( Jogador, Resto, Bag, Board, Lista, Lista2 ).

geraJogadas2( Jogador, _, _, [], _, Lista, Lista ) :- !.
geraJogadas2( Jogador, ColIni, LinIni, [[ColFim, LinFim]| Resto ], Board, Lista, Lst ) :-
	pecas( Jogador, Peca, PecaDama ),
	( getPeca( ColIni, LinIni, Board, Pc ), (
						(( Pc == Peca, X is ColFim - ColIni, 2 >= abs(X), Y is LinFim - LinIni, 2 >= abs(Y)), (Jogador == 'brancas', LinIni > LinFim ; Jogador == 'pretas', LinFim > LinIni ) )
						; 
						Pc == PecaDama )
	),
	validaJogada( Jogador, [ ColIni, LinIni, ColFim, LinFim ], Board, NewBoard, _),
	append( Lst, [[ColIni, LinIni, ColFim, LinFim] - NewBoard], Lista2 ),
	geraJogadas2( Jogador, ColIni, LinIni, Resto, Board, Lista, Lista2 )
	;
	Lista2 = Lst,
	geraJogadas2( Jogador, ColIni, LinIni, Resto, Board, Lista, Lista2 ).

pecas( 'brancas', '0', 'B' ).
pecas( 'pretas', '@', 'P' ).

/*************************************************************************************/
/*  Predicado para verificar se uma determinada casa e uma casa valida de jogo       */
/*************************************************************************************/

casaPreta( X, Y ) :- odd(X),odd(Y) ; even(X),even(Y).
even( X ) :- Y is X mod 2, Y == 0.
odd( X ) :- Y is X mod 2, Y \= 0.

