% Natural Language Hints for the Wumpus World
%
% nl_hint(S): S is a list of ascii codes representing an English language
%   hint about the wumpus world.  The hint is randomly selected from among
%   the different templates in nl_hint_templates(L).  Within each template,
%   the final string S is randomly selected from among several alternative
%   expressions of the same information (as generated by the grammars later
%   in this file.  The hints are returned as part of the percept after
%   each action execution (call to execute(Action,Percept)).
%
%   If you add another template, don't forget to add the template name to
%   the nl_hint_templates list.
%
% Loaded from: wumpus2.pl

nl_hint(S) :-
  nl_hint_templates(L),
  random_member(T,L),
  nl_hint_template(T,S).

nl_hint_templates([
  wumpus_location,
  stench_location,
  pit_location,
  breeze_location,
  gold_location
%  wall_location      % all wumpus worlds are 4x4 for now
  ]).


% nl_hint_template(Template,String): String is a randomly-generated
%   string related to Template and the current wumpus-world state.


%%% wumpus_location
%
% the wumpus is [at|in] [location|square|_] X Y
% the wumpus is to your [left|right]
% the wumpus is ahead of you
% the wumpus is behind you
% [location|square|_] X Y contains the wumpus

nl_hint_template(wumpus_location,S) :-
  wumpus_location(WX,WY),
  findall(Sent,wumpus_location_gmr(WX,WY,Sent,[]),L),
  random_member(SL,L),
  atoms_to_string(SL,S).


%%% stench_location
%
% [there is a stench|a stench is] [at|in] [location|square|_] X Y
% [there is a stench|a stench is] to your [left|right]
% [there is a stench|a stench is] ahead of you
% [there is a stench|a stench is] behind you
% [location|square|_] X Y contains a stench
% [location|square|_] X Y smells bad

nl_hint_template(stench_location,S) :-
  findall([X,Y],stench_location(X,Y),SLocs),
  random_member([SX,SY],SLocs),
  findall(Sent,stench_location_gmr(SX,SY,Sent,[]),L),
  random_member(SL,L),
  atoms_to_string(SL,S).


%%% pit_location
%
% [there is a pit|a pit is] [at|in] [location|square|_] X Y
% [there is a pit|a pit is] to your [left|right]
% [there is a pit|a pit is] ahead of you
% [there is a pit|a pit is] behind you
% [location|square|_] X Y contains a pit
% there are no pits

nl_hint_template(pit_location,S) :-
  not pit(_,_),
  !,
  atoms_to_string([there,are,no,pits],S).

nl_hint_template(pit_location,S) :-
  findall([X,Y],pit(X,Y),PLocs),
  random_member([PX,PY],PLocs),
  findall(Sent,pit_location_gmr(PX,PY,Sent,[]),L),
  random_member(SL,L),
  atoms_to_string(SL,S).


%%% breeze_location
%
% [there is a breeze|a breeze is] [at|in] [location|square|_] X Y
% [there is a breeze|a breeze is] to your [left|right]
% [there is a breeze|a breeze is] ahead of you
% [there is a breeze|a breeze is] behind you
% [location|square|_] X Y contains a breeze
% [location|square|_] X Y feels breezy
% there are no breezes

nl_hint_template(breeze_location,S) :-
  not pit(_,_),
  !,
  atoms_to_string([there,are,no,breezes],S).

nl_hint_template(breeze_location,S) :-
  findall([X,Y],breeze_location(X,Y),BLocs),
  random_member([BX,BY],BLocs),
  findall(Sent,breeze_location_gmr(BX,BY,Sent,[]),L),
  random_member(SL,L),
  atoms_to_string(SL,S).


%%% gold_location
%
% there is [gold|a glitter] [at|in] [location|square|_] X Y
% there is [gold|a glitter] to your [left|right]
% there is [gold|a glitter] ahead of you
% there is [gold|a glitter] behind you
% [location|square|_] contains [gold|a glitter]
% [location|square|_] looks glittery
% there is no gold

nl_hint_template(gold_location,S) :-
  not gold(_,_),
  !,
  atoms_to_string([there,is,no,gold],S).

nl_hint_template(gold_location,S) :-
  findall([X,Y],gold(X,Y),GLocs),
  random_member([GX,GY],GLocs),
  findall(Sent,gold_location_gmr(GX,GY,Sent,[]),L),
  random_member(SL,L),
  atoms_to_string(SL,S).


%%% wall_location
%
% [there is a wall|a wall is] next to [location|square|_] X Y
% [location|square|_] X Y is next to a wall

nl_hint_template(wall_location,S) :-
  wumpus_world_extent(E),
  E1 is E + 1,
  random(1,E1,C),  % C = random from 1 to E
  random_member([EX,EY],[[C,E],[E,C]]),
  findall(Sent,wall_location_gmr(EX,EY,Sent,[]),L),
  random_member(SL,L),
  atoms_to_string(SL,S).


%---------------------- NL Hint Utilities ---------------------

% stench_location(X,Y): (X,Y) is in the world and nextto or at the wumpus.

stench_location(X,Y) :-
  wumpus_location(WX,WY),
  new_location(WX,WY,_,X,Y),
  X > 0,
  Y > 0,
  wumpus_world_extent(E),
  X =< E,
  Y =< E.

stench_location(X,Y) :-
  wumpus_location(X,Y).


% breeze_location(X,Y): (X,Y) is in the world and nextto or at the location
%   of a pit.

breeze_location(X,Y) :-
  pit(PX,PY),
  new_location(PX,PY,_,X,Y),
  X > 0,
  Y > 0,
  wumpus_world_extent(E),
  X =< E,
  Y =< E.

breeze_location(X,Y) :-
  pit(X,Y).


% atoms_to_string(Atoms,String): String is a list of ascii characters
%   containing the atoms in Atoms, separated by spaces.

atoms_to_string([],[]) :- !.

atoms_to_string([Atom],String) :-
  !,
  name(Atom,String).

atoms_to_string([Atom|Atoms],String) :-
  name(Atom,L),
  atoms_to_string(Atoms,S1),
  conc(L,[32|S1],String).     % Space = 32


%------------------- NL Hint Grammars -------------------

wumpus_location_gmr(WX,WY) -->
  [the], [wumpus], [is], at_in, loc_sq_nil(WX,WY) |
  [the], [wumpus], [is], direction_gmr(WX,WY) |
  loc_sq_nil(WX,WY), [contains], [the], [wumpus].

pit_location_gmr(PX,PY) -->
  intro(pit), at_in, loc_sq_nil(PX,PY) |
  intro(pit), direction_gmr(PX,PY) |
  loc_sq_nil(PX,PY), [contains], [a], [pit].

gold_location_gmr(GX,GY) -->
  [there], [is], gold_glit, at_in, loc_sq_nil(GX,GY) |
  [there], [is], gold_glit, direction_gmr(GX,GY) |
  loc_sq_nil(GX,GY), [contains], gold_glit |
  loc_sq_nil(GX,GY), [looks], [glittery].

stench_location_gmr(SX,SY) -->
  intro(stench), at_in, loc_sq_nil(SX,SY) |
  intro(stench), direction_gmr(SX,SY) |
  loc_sq_nil(SX,SY), [contains], [a], [stench] |
  loc_sq_nil(SX,SY), [smells], [bad].

breeze_location_gmr(BX,BY) -->
  intro(breeze), at_in, loc_sq_nil(BX,BY) |
  intro(breeze), direction_gmr(BX,BY) |
  loc_sq_nil(BX,BY), [contains], [a], [breeze] |
  loc_sq_nil(BX,BY), [feels], [breezy].

wall_location_gmr(EX,EY) -->
  intro(wall), [next], [to], loc_sq_nil(EX,EY) |
  loc_sq_nil(EX,EY), [is], [next], [to], [a], [wall].

intro(Obj) --> [there], [is], [a], [Obj] |
               [a], [Obj], [is].

direction_gmr(X,Y) --> [to], [your], [left], {next_to_agent(X,Y,left)}.

direction_gmr(X,Y) --> [to], [your], [right], {next_to_agent(X,Y,right)}.

direction_gmr(X,Y) --> [ahead], [of], [you], {next_to_agent(X,Y,ahead)}.

direction_gmr(X,Y) --> [behind], [you], {next_to_agent(X,Y,behind)}.

gold_glit --> [gold] | [a], [glitter].

at_in --> [at] | [in].

loc_sq_nil(X,Y) --> [location], [X], [Y] |
                    [square], [X], [Y] |
                    [X], [Y].


%--------------------- Grammar Utilities --------------------

% next_to_agent(X,Y,Direction): True is location (X,Y) is adjacent and to
%   the Direction of the agent, where Direction is one of
%   {left,right,ahead,behind}.

next_to_agent(X,Y,Direction) :-
  agent_location(AX,AY),
  agent_orientation(AOrient),
  new_location(AX,AY,Orient,X,Y),
  next_to_agent_direction(AOrient,Orient,Direction).


% next_to_agent_direction(AOrient,Orient,Direction): Returns the Direction
%   {left,right,ahead,behind} describing where the object at Orient from the
%   agent is in relationship to the agent's orientation AOrient.

next_to_agent_direction(Orient,Orient,ahead).

next_to_agent_direction(AOrient,Orient,right) :-
  ( AOrient =:= 0, Orient =:= 270 ;
    90 is AOrient - Orient ).

next_to_agent_direction(AOrient,Orient,left) :-
  ( AOrient =:= 270, Orient =:= 0 ;
    90 is Orient - AOrient ).

next_to_agent_direction(AOrient,Orient,behind) :-
  ( 180 is Orient - AOrient ;
    180 is AOrient - Orient ).
