/*======================================================================
  File			    :  mar_mastermind.P
  Author(s)		      :  Mauricio Ayala-Rincon 
  Last modification	:  October 7, 2003
========================================================================*/

/* 
*  DESCRICAO:
*
*    O programa especifica o jogo "mastemind" consistente no calculo de um
*    numero de quatro digitos diferentes, definido pelo jugador humano, por
*    meio de "chutes" de numeros de quatro digitos diferentes, para os quais
*    o jugador humano deve responder quantos digitos sao acertados na posi-
*    cao exata ("bulls") e quantos digitos sao acertados em posicao errada
*    ("cows").
*
*  
*
*  INSTRUCOES:
*
*       PARA JOGAR contra o programa voce deve pensar um numero de cuatro
*       digitos diferentes e RESPONDER CORRECTAMENTE aos chutes formulados
*       pelo programa.  Inicie com o objetivo:
*
*                | ?- mastermind(X).
*
*       As respostas ("bulls" e cows") devem ser fornecidas no formato:
*      
*                |: N,M.
*
*/



/*
*
*       "mastermind" e o predicado principal do programa.
*
*/

mastermind(Numero) :- iniciar, chute(Numero), verifique(Numero), anuncie_asserto.

/*   
*       "chute" e o predicado de selecao nao-determinista de um chute.
*/

chute(Numero) :- Numero = [X1,X2,X3,X4], seleciones(Numero, [1,2,3,4,5,6,7,8,9,0]).


/*
*    Verificacao dos chutes.  Quando um novo chute "nao e inconsistente" em relacao aos
*                             chutes anteriores, o programa "pergunta" ao jugador humano
*                             quantos Bulls e Cows tem o novo chute.  Caso contrario o
*                             chute sera modificado. 
*                             "Nao inconsistente" significa que se o chute fosse o numero
*                             definido pelo jugador humano, as respostas fornecidas pelo
*                             jugador humano aos chutes anteriores se manteriam. 
*/

verifique(Chute) :- nao_inconsistente(Chute), pergunte(Chute).



/*  
*   verifica-se a consistencia do chute selecionado, supondo este seja o numero correto
*   com relacao aos chutes anteriores.  "lista_resp_ant" e um predicado que vai se 
*   controindo no tempo de execucao do programa com os chutes e respostas ("bulls" e "cows")
*   fornecidas pelo jugador humano. Isto se realiza com o auxilio das funcoes pre-constroidas
*   de Prolog: 
*   "assert"   que inclue novas triplas no predicado;
*   "abolish"  que elimina o predicado.
*/

inconsistente(Chute) :- lista_resp_ant(Chts_anteriores, Bulls, Cows),
                        nao_casam(Chts_anteriores, Chute, Bulls, Cows).

nao_inconsistente(Chute) :- inconsistente(Chute), !, fail.
nao_inconsistente(_). 

casam(Chts_anteriores, Chute, Bulls, Cows) :- exato(Chts_anteriores, Chute, N1),
                                              Bulls =:= N1,
                                              membros_comuns(Chts_anteriores, Chute, N2),
                                              Cows =:= N2-Bulls.

nao_casam(Chts_anteriores,Chute, Bulls, Cows) :- casam(Chts_anteriores, Chute, Bulls, Cows),
                                                 !, fail.
nao_casam(_,_,_,_).

exato(Xs,Ys,N) :- tamanho_de(A, mesma_posicao(A,Xs,Ys), N).
membros_comuns(Xs,Ys,N) :- tamanho_de(A, (membro(A,Xs), membro(A,Ys)), N).

mesma_posicao(X,[X|Xs],[X|Ys]).
mesma_posicao(A,[X|Xs],[Y|Ys]) :- mesma_posicao(A,Xs,Ys).

/*
*   "pergunte" questiona ao jugador humano o numero de "bulls" e "cows" no novo chute.
*   Observe que "assert(lista_resp_ant(Chute,Bulls,Cows))" inclui a nova tripla na
*   relacao "lista_resp_ant".
*/

pergunte(Chute) :- repeat,
                   write(['Quantos "bulls" e "cows" temos em ', Chute,' ?']), nl,
                   read((Bulls,Cows)),
                   sensible(Bulls,Cows), !,
                   assert(lista_resp_ant(Chute,Bulls,Cows)),
                   Bulls =:= 4.

sensible(Bulls,Cows) :- integer(Bulls), integer(Cows), Bulls+Cows =< 4.

/*
*     "iniciar" e o predicado chave para a criacao e eliminacao do proprio predicado
*     "lista_resp_ant" e de suas triplas chute, "bulls" e "cows". 
*/

iniciar :- assert(  lista_resp_ant(_,_,_)), 
           abolish( lista_resp_ant,3), 
           assert(  lista_resp_ant([-1,-1,-1,-1],0,0) ).

anuncie_asserto :- tamanho_de(X, lista_resp_ant(X,A,B), N), N1 is N - 1,
                   write('  -------------------------------------------- '), nl,
                   write(' | Encontrei a resposta apos --> '), write(N1),
                   write(' <-- chutes |'), nl,
                   write('  -------------------------------------------- '), nl.

tamanho_de(X,G,N) :- findall(X,G,Xs), longitude(Xs,N).


longitude([X|Xs],N) :- longitude(Xs,N1), N is  N1 + 1.
longitude([],0).


seleciones([X|Xs],Ys) :- selecione(X,Ys,Ys1), seleciones(Xs,Ys1).
seleciones([],Ys).
selecione(X,[X|Ys],Ys).
selecione(X,[Y|Xs], [Y|Zs]) :- selecione(X,Xs,Zs).


membro( X, [_|Y] ) :- membro( X, Y ).
membro( X, [X|_] ).







