
% :- use_module(wumpus).

%% Movims, Pos_Ant, Mov_Ant, Limites, Visitadas, Objetos
despertar(
    c([],   p(0,-1), n, lim(_,_,_,_), _Visitadas, o([],[],[],[]))
     ).

robot(Cons, Perceps, Mov, NewCons) :-
    arg(1, Cons, Movs),
    actua(Movs, Cons, Perceps, Mov, NewCons).

% Percibiendo y deduciendo 
actua([], Cons, Perceps, Mov, NewCons) :-
    Cons = c(_, Sit0, Mov0, Lims, Vis, Obj0),
    NewCons = c(Movs, Sit, Mov, Lims, Vis, Obj),
    deduce(Perceps, Sit0, Mov0, Sit, Lims, Vis, Obj0, Obj,
           Tengo_oro),
    nuevos_movs(Tengo_oro, Sit, Vis, Obj, [Mov|Movs]).
% Siguiendo un camino precomputado
actua([M|Ms], Cons, _Per, M, NewCons) :-
    Cons = c(_, Sit0, M0, Lims, Vis, Obj),
    NewCons = c(Ms, Sit, M, Lims, Vis, Obj),
    cambia_coord(M0, Sit0, Sit).

% Deducir informacion a partir de las percepciones
deduce([golpe], Sit0, Mov0, Sit0, Lims, _Vis, Obj0, Obj,
       no_tengo_oro) :-
    !,
    cambia_limites(Mov0, Sit0, Lims, Val),
    depura_limite(Mov0, Val, Obj0, Obj).
deduce(Perceps, Sit0, Mov0, Sit, Lims, Vis, Obj0, Obj,
       Tengo_oro) :-
    cambia_coord(Mov0, Sit0, Sit),
    member(Sit, Vis),
    no_hay_nada(Sit, Obj0, Obj1),
    tengo_oro(Perceps, NewPerceps, Tengo_oro),
    renueva_conocimiento(NewPerceps, Sit, Lims, Obj1, Obj).

tengo_oro(Perceps, NewPerceps, tengo_oro):-
    select(brillo, Perceps, NewPerceps), !.
tengo_oro(Perceps, Perceps, no_tengo_oro).

% Objetos: Wn - lugares sin wumpus
%          Wp - lugares con wumpus (disyunciones)
%          An - lugares sin agujeros
%          Ap - lugares con agujeros (disyunciones)

no_hay_nada(P, o(Wn0, Wp0, An0, Ap0), o(Wn, Wp, An, Ap)):-
    posicion_neg(P, Wn0, Wp0, Wn, Wp), % no hay wumpus
    posicion_neg(P, An0, Ap0, An, Ap). % no hay agujeros

posicion_neg(P, In0, Ip0, In, Ip) :-
    annade(P, In0, In),
    elimina_pos(Ip0, P, Ip).

elimina_pos([], _P, []).
elimina_pos([Disj|Disjs], P, [NDisj|NDisjs]):-
    elimina(P, Disj, NDisj),
    disj_no_vacia(NDisj),
    elimina_pos(Disjs, P, NDisjs).

disj_no_vacia([_|_]) :- !.
disj_no_vacia([]) :-
    write('Disyuncion vacia! Error interno!'), nl, abort.


renueva_conocimiento(Perceps, Sit, Lims, Obj0, Obj) :-
    Obj0 = o(Wn0, Wp0, An0, Ap0),
    Obj  = o(Wn, Wp, An, Ap),
    % wumpus alrededor?
    comprueba_percepcion(hedor, Perceps, Sit, Lims,
                         Wn0, Wp0, Wn, Wp),
    % agujero alrededor?
    comprueba_percepcion(brisa, Perceps, Sit, Lims,
                         An0, Ap0, An, Ap).

comprueba_percepcion(P, Ps, Sit, Lims, In, Ip, In, [Disj|Ip]) :-
    member(P, Ps), !,
    objeto_alrededor(Sit, Lims, In, Disj),
    disj_no_vacia(Disj).
comprueba_percepcion(_, _, Sit, Lims, In0, Ip0, In, Ip) :-
    vacia_alrededor([n,s,e,o], Sit, Lims, In0, Ip0, In, Ip).

objeto_alrededor(Sit, Lims, Ineg, Disj) :-
    findall(SitAlr, (
                        member(M, [n,s,e,o]),
                        cambia_coord(M, Sit, SitAlr),
                        dentro_limites(SitAlr, Lims),
                        \+ member(SitAlr, Ineg)
                    ),
                    Disj).

vacia_alrededor([], _Sit, _Lims, In, Ip, In, Ip).
vacia_alrededor([M|Ms], Sit, Lims, In0, Ip0, In, Ip):-
    cambia_coord(M, Sit, SitAlr),
    dentro_limites(SitAlr, Lims), !,
    posicion_neg(SitAlr, In0, Ip0, In1, Ip1),
    vacia_alrededor(Ms, Sit, Lims, In1, Ip1, In, Ip).
vacia_alrededor([_|Ms], Sit, Lims, In0, Ip0, In, Ip):-
    vacia_alrededor(Ms, Sit, Lims, In0, Ip0, In, Ip).


nuevos_movs(tengo_oro, Sit, Vis, _Obj, Movs):-
    dame_camino(Sit, p(0,0), Vis, Movs0),
    append(Movs0, [q], Movs).
nuevos_movs(no_tengo_oro, Sit, Vis, Obj, Movs):-
    findall(D-P, (
                     posicion_vacia(P, Obj),
                     \+ member_ol(P, Vis),
                     d_manhattan(P, Sit, D)
                 ),
                 Objetivos),
    nueva_posicion(Objetivos, Sit, Vis, Obj, Movs).

nueva_posicion([], Sit, Vis, Obj, Movs):-  !,
    findall(D-P, (
                     posicion_insegura(P, Obj),
                     d_manhattan(P, Sit, D)
                 ),
                 Objetivos),
    arriesga(Objetivos, Sit, Vis, Movs).
nueva_posicion(Objetivos, Sit, Vis, _Obj, Movs):- 
    keysort(Objetivos, [_-P|_]),
    dame_camino(Sit, P, Vis, Movs).

arriesga([], _Sit, _Vis, _Movs):-
    write('No hay solucion'), nl, abort.
arriesga(Objetivos, Sit, Vis, Movs):-
    keysort(Objetivos, [_-P|_]),
    dame_camino(Sit, P, Vis, Movs).


posicion_vacia(P, o(Wn, _Wp, An, _Ap)) :-
    member(P, Wn),
    member(P, An).

posicion_insegura(P, Obj) :-
    Disj = [_,_|_],
    conjunto_inseguro(Disj, Obj),
    member(P, Disj),
    \+ conjunto_inseguro([P], Obj).

conjunto_inseguro(Disj, o(_Wn, Wp, _An, _Ap)) :-
    member(Disj, Wp).
conjunto_inseguro(Disj, o(_Wn, _Wp, _An, Ap)) :-
    member(Disj, Ap).

dame_camino(Po, Pf, Pos, C) :-
    dame_un_camino(Po, Pf, Pos, CC), !,
    acorta_camino(CC, C).

dame_un_camino(Pf, Pf, _Pos, []) :- !. % no necesario Pf en Pos
dame_un_camino(Po, Pf, Pos, [M|Ms]) :-
    select_ol(Po, Pos, NPos), !,
    sel_mov(Po, Pf, M),
    cambia_coord(M, Po, P1),
    dame_un_camino(P1, Pf, NPos, Ms).

sel_mov(p(Xo, Yo), p(Xf, Yf), M) :-
    Dx is Xf-Xo,
    Dy is Yf-Yo,
    pref_movs(Dx, Dy, Mlist),
    member(M, Mlist).

pref_movs(Dx, Dy, [n,e,o,s]) :- Dx >= 0, Dy > Dx, !.
pref_movs(Dx, Dy, [n,o,e,s]) :- Dx < 0,  Dy >= -Dx, !.
pref_movs(Dx, Dy, [o,n,s,e]) :- Dy >= 0, Dy < -Dx, !.
pref_movs(Dx, Dy, [o,s,n,e]) :- Dy < 0,  Dy >= Dx, !.
pref_movs(Dx, Dy, [s,o,e,n]) :- Dx =< 0, Dx > Dy, !.
pref_movs(Dx, Dy, [s,e,o,n]) :- Dx > 0,  Dx < -Dy, !.
pref_movs(Dx, Dy, [e,s,n,o]) :- Dy =< 0, Dx >= -Dy, !.
pref_movs(Dx, Dy, [e,n,s,o]) :- Dy > 0,  Dx >= Dy, !.

acorta_camino([], []) :- !.
acorta_camino(CC, [M|C]) :-
    acorta_principio(CC, 0, 0, 0, [M|CC_]), !,
    acorta_camino(CC_, C).
acorta_camino([M|CC], [M|C]) :-
    acorta_camino(CC, C).

acorta_principio([M|Ms], Count, X0, Y0, C) :-
    delta_coord(M, Dx, Dy),
    X is X0+Dx,
    Y is Y0+Dy,
    acorta_principio(Ms, s(Count), X, Y, C).
acorta_principio(Ms, s(s(s(_))), Dx, Dy, [M|Ms]) :-
    delta_coord(M, Dx, Dy).
    
cambia_limites(n, p(_,Ymax), lim(_, _, _, Ymax), Ymax).
cambia_limites(s, p(_,Ymin), lim(_, Ymin, _, _), Ymin).
cambia_limites(e, p(Xmax,_), lim(_, _, Xmax, _), Xmax).
cambia_limites(o, p(Xmin,_), lim(Xmin, _, _, _), Xmin).

depura_limite(Mov, Val, o(Wn0, Wp0, An0, Ap0),
                    o(Wn, Wp, An, Ap)):-
    depura_limite_l(Wn0, Mov, Val, Wn),
    depura_limite_l(An0, Mov, Val, An),
    depura_limite_l_l(Wp0, Mov, Val, Wp),
    depura_limite_l_l(Ap0, Mov, Val, Ap).

depura_limite_l_l([], _, _, []).
depura_limite_l_l([L|Ls], Mov, Val, [N|Ns]) :-
    depura_limite_l(L, Mov, Val, N),
    disj_no_vacia(N),
    depura_limite_l_l(Ls, Mov, Val, Ns).

depura_limite_l([], _, _, []).
depura_limite_l([P|Ps], Mov, Val, NPs) :-
    fuera_limite(Mov, Val, P), !,
    depura_limite_l(Ps, Mov, Val, NPs).
depura_limite_l([P|Ps], Mov, Val, [P|NPs]) :-
    depura_limite_l(Ps, Mov, Val, NPs).

fuera_limite(n, Ymax, p(_,Y)) :- Y > Ymax.
fuera_limite(s, Ymin, p(_,Y)) :- Y < Ymin.
fuera_limite(e, Xmax, p(X,_)) :- X > Xmax.
fuera_limite(o, Xmin, p(X,_)) :- X < Xmin.


dentro_limites(p(X,Y), lim(Xmin, Ymin, Xmax, Ymax)) :-
    lim_inferior(Xmin, X),
    lim_inferior(Ymin, Y),
    lim_superior(Xmax, X),
    lim_superior(Ymax, Y).

lim_inferior(Min, _) :- var(Min), !.
lim_inferior(Min, X) :- Min =< X.

lim_superior(Max, _) :- var(Max), !.
lim_superior(Max, X) :- Max >= X.


cambia_coord(q, P, P):- !.
cambia_coord(M, p(X, Y), p(Nx, Ny)):-
    delta_coord(M, Dx, Dy),
    Nx is X + Dx,
    Ny is Y + Dy.


member_ol(E, L):- 
    nonvar(L),
    L = [X|Xs],
    member_ol(E, X, Xs).

member_ol(E, E, _).
member_ol(E, _, L):- member_ol(E, L).


select_ol(X, L, R) :-
    nonvar(L),
    L = [Y|Ys],
    select_ol(X, Y, Ys, R).

select_ol(X, X, R, R).
select_ol(X, Y, L, [Y|R]) :-
    select_ol(X, L, R).

 %% delta_coord(n,  0,  1).
 %% delta_coord(s,  0, -1).
 %% delta_coord(e,  1,  0).
 %% delta_coord(o, -1,  0).


d_manhattan(p(X1, Y1), p(X2, Y2), D):-
    d_pt(X1, X2, Dx),
    d_pt(Y1, Y2, Dy),
    D is Dx + Dy.

d_pt(X1, X2, D) :- X1 >= X2, !, D is X1-X2.
d_pt(X1, X2, D) :- D is X2-X1.

annade(E, L, L) :- member(E, L), !.
annade(E, L, [E|L]).

elimina(E, L, R) :- select(E, L, R), !.
elimina(_, L, L).

 %% append([], L, L).
 %% append([X|Xs], L, [X|Ys]) :-
 %%         append(Xs, L, Ys).
 %% 
 %% member(X, [X|_]).
 %% member(X, [_|Xs]) :- member(X, Xs).
