% NANI SEARCH - A sample adventure game


main:- nani_search.       % predicado principal

nani_search:-
  init_dynamic_facts,     % 

  write('NANI SEARCH - Adventure Game'),nl,
  nl,
  write('Voce e uma pessoa que fica como oaventureiro e voce tem 3 anos'),nl,
  write('o Nani e sua seguranca, e tarde y voce esta cansado'),nl,
  write('mas voce nao pode dormir sem seu Nani.'),nl,
  write('Sua missao e achar o Nani.'),nl,
  nl,
  write('Voce controla o jogo usando comandos simples  em Ingles '),nl,
  write('expressando a acao que voce deseja levar. '),nl,
  write('Voce Pode : '),nl,
  write('           - Ir para outros quartos. '),nl,
  write('           - olhar seus ambientes. '),nl,
  write('           - olhar coisas. '),nl,
  write('           - levar coisas. '),nl,
  write('           - colocar coisas. '),nl,
  write('           - fazer inventario do que voce tem. '),nl,
  write('           - Ligar e desligar coisas. '),nl,
  nl,nl,
  write('Bata qualquer tecla para continuar.'),get0(_),
  nl,nl,
  write('***********************************************'),nl,
  write('Escreber "help" se voce precisa de mais ajuda.'),nl,
  write('Escreber "hint" se voce quer uma sugestao grande.'),nl,
  write('Escreber "quit" se voce se rende.'),nl,
  nl,nl,
  write('Desfrute de Adventure Game.'),nl,
  write('***********************************************'),nl,
  nl,nl,
  look,                   % give a look before starting the game
  asserta(have(.)),            % and add to your possessions
  asserta(turned_on(.)),
  command_loop.

% command_loop - repete ate o nani e achado ou ate  
% jogador deixaram


command_loop:-
  repeat,
  get_command(X),
  do(X),
  (nanifound; X == quit).

% do - Igualar os commandos de entrada com os predicados
%      
%     Os cortes previnem o fracaso forcado ao termino de "command_loop"
%     de regressar nos predicados de comando.

do(goto(X))	:- goto(X),!.
do(nshelp)	:- nshelp,!.
do(hint)	:- hint,!.
do(inventory)	:- inventory,!.
do(take(X))	:- take(X),!.
do(drop(X))	:- drop(X),!.
do(eat(X))	:- eat(X),!.
do(look)	:- look,!.
do(turn_on(X))	:- turn_on(X),!.
do(turn_off(X))	:- turn_off(X),!.
do(look_in(X))	:- look_in(X),!.
do(quit)	:- quit,!.

% Estes sao os predicados que controlam saida do jogo
% Se o jogador levou o Nani, entao a chamada para "have(nani)"va
% tenher sucesso e o command_loop completarao.caso contrario falha
% e command_loop repetirao.

nanifound:-
  have(nani),        
  write('Parabens, voce salvou o Nani.'),nl,
  write('Agora voce pode descansar seguro.'),nl,nl.

quit:-
  write('Se rendeo? Issta vai ser uma noite assustador'),nl,
  nl,nl.

% O comando de Ajuda

nshelp:-
  write('Use simple English sentences to enter commands.'),nl,
  write('The commands can cause you to:'),nl,
  nl,
  write('   go to a room          (ex. go to the office)'),nl,
  write('   look around           (ex. look)'),nl,
  write('   look in something     (ex. look in the desk)'),nl,
  write('   take something        (ex. take the apple)'),nl,
  write('   drop something        (ex. drop the apple)'),nl,
  write('   eat something         (ex. eat the apple)'),nl,
  write('   turn something on     (ex. turn on the light)'),nl,
  write('   inventory your things (ex. inventory)'),nl,
  nl,
  write('The examples are verbose, terser commands and synonyms'),nl,
  write('are usually accepted.'),nl,nl,
  write('Hit any key to continue.'),nl,
  get0(_),
  look.

hint:-
  write('You need to get to the cellar, and you can''t unless'),nl,
  write('you get some light.  You can''t turn on the cellar'),nl,
  write('light, but there is a flash light in the desk in the'),nl,
  write('office you might use.'),
  nl,nl,
  write('Bata qualquer tecla para continuar.'),get0(_),
  nl,nl,
  look.

% Fatos inicias que descrevem o mundo. Quartos e portas nao mudam,
% assim sao feitos

room(office).
room(kitchen).
room('dining room').
room(hall).
room(cellar).

door(office,hall).
door(hall,'dining room').
door('dining room',kitchen).
door(kitchen,cellar).
door(kitchen,office).

connect(X,Y):-
  door(X,Y).
connect(X,Y):-
  door(Y,X).

% Estes fatos sao tudo sujeito a mudanca o jogo.
% Este predicado e chamado quando nanisrch comeca

init_dynamic_facts:-
  assertz(location(desk,office)),
  assertz(location(apple,kitchen)),
  assertz(location(flashlight,desk)),
  assertz(location('washing machine',cellar)),
  assertz(location(nani,'washing machine')),
  assertz(location(table,kitchen)),
  assertz(location(crackers,desk)),
  assertz(location(broccoli,kitchen)),
  assertz(here(kitchen)),
  assertz(turned_off(flashlight)).

furniture(desk).
furniture('washing machine').
furniture(table).

edible(apple).
edible(crackers).

tastes_yuchy(broccoli).

%%%%%%%% COMMANDS %%%%%%%%%%%%%%%%%%%%%%%%%%

% goto moves the player from room to room.

goto(Room):-
  can_go(Room),                 % confira para movimento legal
  puzzle(goto(Room)),           % confira para condicoes especias
  moveto(Room),                 % vai la e fala para o jogador
  look.
goto(_):- look.

can_go(Room):-                  % se ha uma conexao fazer isto
  here(Here),                   % e um movimento legal
  connect(Here,Room),!.
can_go(Room):-
  respond(['You can''t get to ',Room,' from here']),fail.

moveto(Room):-                  % atualize o banco de dados com o
  retract(here(_)),             % quarto novo
  asserta(here(Room)).

% olhar lista de coisas em um quarto e as conexoes

look:-
  here(Here),
  nl,
  respond(['(*) You are in the ',Here]),
  write('(*) You can see the following things:'),nl,
  list_things(Here),
  write('(*) You can go to the following rooms:'),nl,
  list_connections(Here),
  nl.

list_things(Place):-
  location(X,Place),
  tab(2),write(X),nl,
  fail.
list_things(_).

list_connections(Place):-
  connect(Place,X),
  tab(2),write(X),nl,
  fail.
list_connections(_).

% look_in permite para o jogador olhar dentro de uma coisa que pode
% contenha outras coisas

look_in(Thing):-
  location(_,Thing),                  
  write('The '),write(Thing),write(' contains:'),nl,
  list_things(Thing).
look_in(Thing):-
  respond(['There is nothing in the ',Thing]).

% objeto pegado permite levar para o jogador.

take(Thing):-
  is_here(Thing),
  is_takable(Thing),
  move(Thing,have),
  respond(['You now have the ',Thing]).

is_here(Thing):-
  here(Here),
  contains(Thing,Here),!.             % nao fazer backtrack
is_here(Thing):-
  respond(['There is no ',Thing,' here']),
  fail.

contains(Thing,Here):-                % definicao recursiva para achar
  location(Thing,Here).               % coisas contiveram em coisas etc.
contains(Thing,Here):-
  location(Thing,X),
  contains(X,Here).

is_takable(Thing):-                   %voce nao pode levar a mobilia
  furniture(Thing),
  respond(['You can''t pick up a ',Thing]),
  !,fail.
is_takable(_).                       % nao mobilia, ok para levar.

move(Thing,have):-
  retract(location(Thing,_)),      % leve de seu lugar velho
  asserta(have(Thing)).            % e soma a suas possessoes

% drop - permite para o jogador transferir uma possessao para um quarto

drop(Thing):-
  have(Thing),          % voce tem que ter a coisa para colocar isto
  here(Here),           % onde e nos 
  retract(have(Thing)),
  asserta(location(Thing,Here)).
drop(Thing):-
  respond(['You don''t have the ',Thing]).

% eat, porque todo jogo de aventura lhe deixa comer materiais.

eat(Thing):-
  have(Thing),
  eat2(Thing).
eat(Thing):-
  respond(['You don''t have the ',Thing]).
  
eat2(Thing):-
  edible(Thing),
  retract(have(Thing)),
  respond(['That ',Thing,' was good']).
eat2(Thing):-
  tastes_yuchy(Thing),
  respond(['Three year olds don''t eat ',Thing]).
eat2(Thing):-
  respond(['You can''t eat a ',Thing]).

% lista de inventario de suas possesoes.

inventory:-
  have(X),        % que tenha certeza que voce tem uma coisa pelo menos
  write('You have: '),nl,
  list_possessions.
inventory:-
  write('You have nothing'),nl.

list_possessions:-
  have(X),
  tab(2),write(X),nl,
  fail.
list_possessions.

% turn_on reconhece dois casos. Se o jogador tenta a simplesmente trocar
% na luz, e assumido esta e a luz de quarto, e o mensagem de erro apropiada e
% emitida. Caso contrario turno_on tem se referida a um objeto que e turned_off

turn_on(light):-
  respond(['You can''t reach the switch and there''s nothing to stand on']).
turn_on(Thing):-
  have(Thing),
  turn_on2(Thing).
turn_on(Thing):-
  respond(['You don''t have the ',Thing]).

turn_on2(Thing):-
  turned_on(Thing),
  respond([Thing,' is already on']).
turn_on2(Thing):-
  turned_off(Thing),
  retract(turned_off(Thing)),
  asserta(turned_on(Thing)),
  respond([Thing,' turned on']).
turn_on2(Thing):-
  respond(['You can''t turn a ',Thing,' on']).

turn_off(Thing):-
  respond(['nao se pode virar a off']).

% O unico quebra-cabeca(Puzzle) em procura de Nani tem que ver com ir
% a cellar. Puzzle so e chamado por isto de goto.

puzzle(goto(cellar)):-
  have(flashlight),
  turned_on(flashlight),!.
puzzle(goto(cellar)):-
  write('You can''t go to the cellar because it''s dark in the'),nl,
  write('cellar, and you''re afraid of the dark.'),nl,
  !,fail.
puzzle(_).

% respond - predicado que escrebe uma mistura de literals e variaveis como parametros
 
respond([]):-
  write('.'),nl,nl.
respond([H|T]):-
  write(H),
  respond(T).

% Simple interprete de comandos em Ingles. Faz alguma verificacao semantica
% e permite varios sinonimos. Dentro de um subconjunto restringido de o Ingles
% um comando pode ser mencionado muitos modos.

% Alguma interpretação esta baseado na situacao.  Note que quando  
% o jogador diz volta na luz e ambiguo.  Poderia significar  
% a luz de quarto  ou o  
% luz de flash.  Se o jogador tem a luz de flash que e interpretado  
% caso contrario como luz de flash, e interpretado como luz de quarto.  

get_command(C):-
  readlist(L),        % le uma oracao e poe [it,in,list,form]
  command(X,L,[]),    % chame a gramatica para comando
  C =.. X,!.          
get_command(_):-
  respond(['I don''t understand, try again or type help']),fail.

% a gramatica nao tem que ser real ingles. Ha dois tipos de comandos em Nani
% esses com e sem um unico argumento. Um caso especial tambem e constituido o comando
% goto que pode ser atividado dando um nome de quarto simplesmente.


command([Pred,Arg]) --> verb(Type,Pred),nounphrase(Type,Arg).
command([Pred]) --> verb(intran,Pred).
command([goto,Arg]) --> noun(go_place,Arg).

% Reconhece tres tipos de verbos. Cada verbo corresponde a um comando,
% mas ha muitos  sinonimos permitidos. Por exemplo
% turn_on serao ativados por "turn on" ou "switch on".


verb(go_place,goto) --> go_verb.
verb(thing,V) --> tran_verb(V).
verb(intran,V) --> intran_verb(V).

go_verb --> [go].
go_verb --> [go,to].
go_verb --> [g].

tran_verb(take) --> [take].
tran_verb(take) --> [pick,up].
tran_verb(drop) --> [drop].
tran_verb(drop) --> [put].
tran_verb(drop) --> [put,down].
tran_verb(eat) --> [eat].
tran_verb(turn_on) --> [turn,on].
tran_verb(turn_on) --> [switch,on].
tran_verb(turn_off) --> [turn,off].
tran_verb(look_in) --> [look,in].
tran_verb(look_in) --> [look].
tran_verb(look_in) --> [open].

intran_verb(inventory) --> [inventory].
intran_verb(inventory) --> [i].
intran_verb(look) --> [look].
intran_verb(look) --> [look,around].
intran_verb(look) --> [l].
intran_verb(quit) --> [quit].
intran_verb(quit) --> [exit].
intran_verb(quit) --> [end].
intran_verb(quit) --> [bye].
intran_verb(nshelp) --> [help].
intran_verb(hint) --> [hint].



nounphrase(Type,Noun) --> det,noun(Type,Noun).
nounphrase(Type,Noun) --> noun(Type,Noun).

det --> [the].
det --> [a].


noun(go_place,R) --> [R], {room(R)}.
noun(go_place,'dining room') --> [dining,room].

noun(thing,T) --> [T], {location(T,_)}.
noun(thing,T) --> [T], {have(T)}.
noun(thing,flashlight) --> [flash,light].
noun(thing,'washing machine') --> [washing,machine].
noun(thing,'dirty clothes') --> [dirty,clothes].

% Se o jogador digitou "light", pode ser interpetado tres modos.
% Se um nome de quarto esta antes disto deve ser uam luz de quarto.
% Se o jogador tem a luz de lanterna, assuma a luz de flas. Caso contrario
% assuma e luz de quarto.

noun(thing,light) --> [X,light], {room(X)}.
noun(thing,flashlight) --> [light], {have(flashlight)}.
noun(thing,light) --> [light].

% readlist - leia uma lista de palavras.

readlist(L):-
  write('> '),
  read_word_list(L).

read_word_list([W|Ws]) :-
	get0(C),
	readword(C, W, C1),  	% leia palavra que comeca com C
	restsent(C1, Ws), !.    % caracter - use para adquirir resto de oracao

restsent(C,[]) :- lastword(C), !.  % Nada partiu se bateu marcador de ultima palavra
restsent(C,[W1|Ws]) :-
	readword(C,W1,C1),	   % do contrario leia proxima palavra e resto de oracao
	restsent(C1,Ws).

readword(C,W,C1) :-		% algumas palabras sao unico caracter
	single_char(C),	    	% i.e. pontuacao
	!, 
	name(W, [C]),	      	% adquira como um atomo
	get0(C1).
readword(C, W, C1) :-
	is_num(C),		% se nos temos um numero
	!,
	number_word(C, W, C1, _).  % converta para um numero genuino
readword(C,W,C2) :-			% caso contrario se caracter  nao faz
	in_word(C, NewC),	  	% fim de palavra - mantenha os
	get0(C1),		  	% acumulando ate 
	restword(C1,Cs,C2),		% nos temos toda a palavra
	name(W, [NewC|Cs]).		% entao faca isto um atomo
readword(C,W,C2) :-			% caso contrario
	get0(C1),       
	readword(C1,W,C2).	 	% comeco uma palavra nova

restword(C, [NewC|Cs], C2) :-
	in_word(C, NewC),
	get0(C1),
	restword(C1, Cs, C2).
restword(C, [], C).


single_char(44). % ,.
single_char(59). % ;.
single_char(58). % :.
single_char(63). % ?.
single_char(33). % !.
single_char(46). % ..


in_word(C, C) :- C >= 97, C =< 122.
in_word(C, L) :- C >= 65, C =< 90, L is C + 32.
in_word(39,39).
in_word(45,45).


number_word(C, W, C1, Pow10) :- 
	is_num(C),
	!,
	get0(C2),
	number_word(C2, W1, C1, P10),
	Pow10 is P10 * 10,
	W is integer(((C - 48) * Pow10) + W1).
number_word(C, 0, C, 0.1).


is_num(C) :-
	C =< 57,
	C >= 48.

% Estes simbolos delineiam fim de oracao

lastword(10).   % fim se nova linha e entrada
lastword(13).   % Enter
lastword(46).   %.
lastword(33).   %!
lastword(63).   %?
