%     CPSC 312 - Introduction to Functional and Logic Programming
%     Project 1: Adventures in Text
%     due: Tuesday, October 31st, 2006 @ 3:30pm
%
%     Byron Knoll - 81414047
%     George Stelle - 11994035
%     Michael Quan - 89751044
%     Jessica Best - 89055040
%
%
%     Amnesia -- This is a text-based adventure game.  The user plays the part of a man who wakes up not knowing who he is, where he is, or why he's there, and thus the objective is for him to find answers to these questions. Using Prolog's parsing capabilities, the user interacts with the program in natural language, maneuvering throughout the defined world and interacting with the environment in order to win by successfully opening the briefcase, which allows him to put together the pieces of the puzzle.

% main/0 -- This is the predicate called by the user to start the game.  It asserts all the dynamic relations in our game so that their initial states are set, prints out an opening to our story, and then begins the interpreter loop.

main :- 
% These statements define the relations associated with the cell.
    assertz(location(apple,drawer)),
    assertz(location(desk,cell)),
    assertz(location(chair,cell)),
    assertz(location(jacket,cell)),
    assertz(location(key,pocket)),
    assertz(location(pocket,jacket)),
    assertz(isClosed(drawer)),
    assertz(not_taken(key)),
    assertz(not_found(key)),
    assertz(locked(door(cell, lab))),

% These statements define the relations associated with the lab.
    assertz(not_found('security card')),
    assertz(not_taken('security card')),
    assertz(location(body, lab)),
    assertz(location('security card', hand)),
    assertz(location('brains in vats', lab)),
    assertz(location('operating table', lab)),

% These statements define the relations associated with the office.
    assertz(location(computer, office)),
    assertz(location(table, office)),
    assertz(location(garbage, office)),
    assertz(location(briefcase, office)),
    assertz(not_taken(briefcase)),
    assertz(not_opened(office)),

% These statements define the relations associated with the security room.
    assertz(no_briefcaseCode),
    assertz(not_entered('security room')),
    assertz(location(counter, 'security room')),
    assertz(location('tv monitors', 'security room')),
    assertz(location(vcr, 'security room')),

% These statements define the relations associated with the file room.
    assertz(not_found(password)),
    assertz(location('filing cabinet', 'file room')),
    assertz(location(painting, 'file room')),

% These statements define all other general relations that hold in the initial state of our world.
    assertz(not_finished),
    assertz(here(cell)),
    assertz(have('pocket fluff')),
    assertz(isClosed(door(lab, cell))),
    assertz(isClosed(door(cell, lab))),
    assertz(isClosed(door('file room', office))),
    assertz(isClosed(door(office, 'file room'))),
    assertz(isClosed(door(office, lab))),
    assertz(isClosed(door(lab, office))),
    assertz(isClosed(door(office, 'security room'))),
    assertz(isClosed(door('security room', office))),

% The next few clauses print to the screen the introduction to our game.
    write('***Amnesia***'),nl,
    write('Made by:'),nl,
    write('Byron Knoll - 81414047'),nl,
    write('George Stelle - 11994035'),nl,
    write('Michael Quan - 89751044'),nl,
    write('Jessica Best - 89055040'),nl,nl,
    write('You wake up in the corner of a small, dimly lit cell. Your head hurts, and you smell the faint stench of decaying flesh. You try to remember how you got here, but come to the realization that you can''t remember anything about your past. You slowly rise to your feet determined to unravel the mystery of how you got here, and what has happened to your memory. \n\nThe room contains a desk, a chair with a jacket hanging on it, and a door labeled: LAB.'),nl,
    write('(hint: try typing "look" or "help")'),nl, nl,

% The last goal in the main predicate initiates the command loop.
    command_loop.


% command_loop/0 -- This predicate will continually get input from the user and act on that input until either the user decides to quit, or has ended the game.

command_loop :-
    repeat,
    get_command(X), nl,
    do(X),
    (X == quit; end_condition). %If end_condition is true or X equals "quit" then the last line doesn't fail and the program ends.


% get_command/1 -- This predicate reads the input the user has provided and transforms it into a clause that Prolog can evaluate, or, if it is an illegal input in the scope of the world, asks for another command.

get_command(C) :-
    read_atomics(L),
    command(X,L,[]),
    C =.. X,!. % Note the new predicate here

get_command(_):-write('\nI don''t understand, try rephrasing the command or type "help"'),nl, nl,fail.

% end_condition/0 -- end_condition is true when the game has ended naturally (either completing the game, or dying).

end_condition :- \+ not_finished.


%% DO CLAUSES %%

% do/1 -- do/1 is called by the command_loop.  The goals are the functors we have defined and want Prolog to run through given the input the user has provided.  Each is followed by a cut, so that Prolog does not check for multiple actions to perform given any input; this is a green cut, because we have defined the desired response for every action we have deemed likely to be made by the user. Some rules ensure that the operation is valid first.  do(quit) is a special case, in which all the asserted dynamic relations are abolished.  This allows the user to quit the game and restart it in the same Prolog window without causing asserted statements from the previous run to interfere with the current run.

do(openDoor(Room)) :- here(Y), open(door(Y,Room)), !.
do(closeDoor(Room)) :- here(Y), closeObject(door(Y,Room)), !.
do(open(door)) :- openwhichdoor, !.
do(close(door)) :- closewhichdoor, !.
do(open(drawer)) :- open(drawer), !.
do(close(drawer)) :- closeObject(drawer), !.
do(close(X)) :- closeObject(X), !.
do(open(X)) :- open(X), !.
do(eat(X)) :- eat(X),!.
do(look) :- here(X), look(X),!.
do(look_at(X)) :- room(X), here(X), look(X),!.
do(look_at(X)) :- look_at(X),!.
do(help) :- help,!.
do(quit) :- abolish(location/2), abolish(have/1),
               abolish(isClosed/1), abolish(unlocked/1),
               abolish(here/1), abolish(no_briefcaseCode/0),
               abolish(not_found/1), abolish(not_taken/1),
               abolish(not_entered/1), abolish(locked/1), quit,!.
do(go(Room)) :- go(Room), !.
do(take(X)) :- take(X), !.
do(drop(X)) :- drop(X), !.
do(inventory) :- inventory, !.
do(sit(X)) :- sit(X), !.
do(read(X)) :- my_read(X), !.
do(pull(X)) :- pull(X), !.
do(turn_on(X)) :- turn_on(X), !.


%% OPEN CLAUSES %%

% openwhichdoor/0 -- This is called when the user has not specified a particular door to open; this is necessary because some rooms contain multiple doors, and so this simply prints a message alerting the user that they must be more specific.

openwhichdoor :- write('Which door do you want to open?  When opening doors it is a good idea to be specific.'), nl, nl.

% open/1 -- This can be called to open a variety of items, from doors to the drawer or the briefcase.  It ensures that the user can only open items which are meant to be openable, are within reach of the user, and that they have not already been opened. Special cases require certain other aspects of the world to be true, such as opening the lab door for the first time: this requires the user to have (found and) picked up the key.  Many special cases also have lead to special messages that must be printed to the screen. open(briefcase) is our special, successful end game call.

open(door(cell, lab)) :- have(key),
                                retract(have(key)),
                                write('You fumble with the key and force it into the rusty old lock.'), nl,
                                write('A few slight jiggles, followed by a few rough ones, and you''ve opened the lock!'), nl,
                                write('The door opens into a white room...'), nl,
                                write('It reeks of formaldehyde, and something else you can''t quite place....'), nl,
                                write('You try to remove the key from the lock to take with you, but it''s stuck inside the old contraption.'),
                                write('Perhaps it''s better that way, now you can''t lock yourself out.'), nl,
                                write('Now the question is... do you stay here in the cell, or go to the lab?'), nl, nl,
                                retract(locked(door(cell, lab))),
                                retract(isClosed(door(lab, cell))),
                                retract(isClosed(door(cell, lab))).
open(door(lab,office)) :- isClosed(door(lab,office)), not_opened(office),
                                  retract(not_opened(office)), write('The door opens to reveal a large corner office,'),
                                  write(' with a thick layer of dust over all the surfaces.'),
                                  write(' You immediately notice a large cherrywood table with a huge mess on top, and papers scattered everywhere.'),
                                  write(' It looks like someone left in quite a hurry; should you enter, or stay here?'), nl, nl,
                                  retract(isClosed(door(lab,office))),
                                  retract(isClosed(door(office,lab))).

open(door(office, 'security room')) :- not_entered('security room'),
                                                  not_found(password),
                                                  have('security card'),
                                                  write('Hmmm... could you need a password sequence for entrance here?  '),
                                                  write('Well, you have the security guard''s key card.  Perhaps you won''t need it.  '),
                                                  write('You swipe the card, and the flashing red light turns from red to yellow.  '),
                                                  write('But the door is still locked.  What sequence should you try?  '),
                                                  write('You close your eyes and begin to randomly press buttons.  Nothing happens.  '),
                                                  write('A moment or so passes... Then you hear a slight high-pitched whistling noise... '),
                                                  write('You look up and see a white gas being poured into the room, billowing down '),
                                                  write('around you.  It becomes hard to breathe... you try to steady yourself, grabbing '),
                                                  write('onto the table for support.  But you already know it''s too late.  With one final '),
                                                  write('breath, you collapse onto the floor, still not knowing who you are, and as the '),
                                                  write('lights go out, one last thought crosses your mind: '),
                                                  write('Why didn''t I take heed of the warning?'), nl, nl,
                                                  do(quit), retract(not_finished).

open(door(office, 'security room')) :- not_entered('security room'),
                                                  not_found(password),
                                                  \+ have('security card'),
                                                  write('The door''s warning isn''t exactly inviting... and the red flashing light on the key '),
                                                  write('reader seems rather unfriendly.  But you decide to heck with it, there''s no one '),
                                                  write('else here, why would such high security be necessary?  It''s probably just a '),
                                                  write('warning for a security system that''s been disabled.  You decide to enter '),
                                                  write('numbers at random, and see what that does.  You try 3...1...2... and the light '),
                                                  write('stops flashing.  A good sign?  But the door is still locked. You start to look at '),
                                                  write('the room around you, but can''t think of anything.  Then you hear a slight '),
                                                  write('high-pitched whistling noise... You look up and see a white gas being poured '),
                                                  write('into the room, billowing down around you.  It becomes hard to breathe... '),
                                                  write('you try to steady yourself, grabbing onto the table for support.  But you '),
                                                  write('already know it''s too late.  With one final breath, you collapse onto the floor, '),
                                                  write('still not knowing who you are, and as the lights go out, one last thought '),
                                                  write('crosses your mind: Why didn''t I take heed of the warning?'), nl, nl,
                                                  do(quit), retract(not_finished).

open(door(office, 'security room')) :- not_entered('security room'),
                                                  have('security card'),
                                                  write('The danger sign doesn''t deter you... you have the security guard''s pass '),
                                                  write('card, and you have already obtained a password from the file room... with '),
                                                  write('any luck, these should allow you safe entrance.  You swipe the pass card, '),
                                                  write('and the flashing light turns from red to yellow.  Can you remember the '),
                                                  write('password...?  4-8-15-16-23-42...?  Yes, that must be it -- the light turns '),
                                                  write('green and you hear a beep, followed by the thud of the locks sliding open.  '),
                                                  write('The key reader also seems to have disabled itself -- the red light has stopped '),
                                                  write('flashing. \nThe door swings open slowly, revealing an empty surveilllance room.'),
                                                  write('  There is a large tv monitor and a VCR on the floor.  Are you brave enough to '),
                                                  write('enter?'),
                                                  retract(isClosed(door(office, 'security room'))),
                                                  retract(isClosed(door('security room', office))),
                                                  retract(not_entered('security room')), nl, nl.

open(door(office, 'security room')) :- not_entered('security room'),
                                                  \+ have('security card'),
                                                  write('You decide to try the key reader without the key card... maybe simply '),
                                                  write('entering the code you found earlier will suffice?  You fumble slightly as you '),
                                                  write('enter the sequence: 4-8-15-16-23-42.  The flashing red light... stops.  '),
                                                  write('For a moment, nothing happens.  Then you hear a slight high-pitched whistling '),
                                                  write('noise... You look up and see a white gas being poured into the room, billowing '),
                                                  write('down around you.  It becomes hard to breathe... you try to steady yourself, '),
                                                  write('grabbing onto the table for support.  But you already know it''s too late.  With '),
                                                  write('one final breath, you collapse onto the floor, still not knowing who you are, '),
                                                  write('and as the lights go out, one last thought crosses your mind: '),
                                                  write('Why didn''t I take heed of the warning?'), nl, nl,
                                                  do(quit), retract(not_finished).

open(door(Currentroom, Nextroom)) :- locked(door(Currentroom, Nextroom)),
                                                    write('Hmm... the door seems to be locked.  Maybe there''s a key around?'), nl, nl.

open(door(Currentroom, Currentroom)) :- write('You are already in the '),
                                                        write(Currentroom), write('.'), nl,
                                                        write('If you want to open a door, try specifying which door you want by the room it leads to.'), nl, nl.

open(door(Currentroom, Nextroom)) :- door(Currentroom, Nextroom),
                                isClosed(door(Currentroom, Nextroom)) ,
                                retract(isClosed(door(Currentroom, Nextroom))),
                                retract(isClosed(door(Nextroom, Currentroom))),
                                write('The door is now open!  The question is, do you want to enter the '), write(Nextroom), write('?'), nl, nl.

open(door(Currentroom, Nextroom)) :- door(Currentroom, Nextroom),
                                write('The door is already open!'), nl, nl.

open(door(_, Nextroom)) :- write('You cannot get to the '),
                                write(Nextroom),
                                write(' from here.'), nl, nl.

open(desk) :- write('You can''t open a desk, silly!  But maybe you could open the drawer?'), nl, nl.
open('filing cabinet') :- look_at('filing cabinet').
open(jacket) :- write('The jacket zipper seems to be caught... you can''t open it.  But is there something in the pocket?'), nl, nl.
open(pocket) :- look_at(pocket).
open(hand) :- look_at(hand).
open(painting) :- pull(painting).
open(drawer) :- here(cell), isClosed(drawer), retract(isClosed(drawer)), write('You have opened the drawer.'), nl,
asserta(location(apple, cell)), write('There is an apple in the drawer!'), nl, nl.
open(drawer) :- here(cell), write('The drawer is already open.'), nl, nl.
open('brains in vats') :- here(lab), write('You toy with the idea of playing with the brains... but the lids are sealed shut.  Too bad, you always wondered what a real brain would actually feel like.'), nl, nl.
open(briefcase) :- \+no_briefcaseCode, have(briefcase),
        write('You wonder what the code could be, when you remember the guard''s dying words from the security monitors.  You enter the code: 1-3-3-7.  The briefcase creaks open; there is a lone folder labeled: Observations for SUBJECT 2038: Geoffrey Knight.  The name looks familiar... you open the folder, and begin reading... '), nl, nl,
       write('March 15th, 2064: The experiments for genetic enhancement have begun, and so far are running smoothly.  The subject was put through vigorous genetic alteration methods. The initial results are encouraging: better problem solving; significant increase in physical abilities; only side effect is slight increase in temper.  I hope to god that the midazolam works though, because if the subject remembers any of this I worry for his sanity.'), nl, nl,
       write('A screaming pain runs down your neck as you get flashes of needles, drills, and scalpels.  You feel sick and want to put it down, but you can''t resist and continue reading.'), nl, nl,
       write('Press ENTER to continue...'),nl, nl, read_atomics(_), nl,
       write('April 2nd, 2064: The subject is psychologically deteriorating. We are cutting back his doses of gene X, but it does not seem to be helping. Constant fits of rage.  One scientist in critical condition after being thrown across the room. Requested the termination of the project, but General Kinsey refused.  If it gets much worse I am going to pull the plug, screw Kinsey.'), nl, nl,
       write('April 4th, 2064: The subject is out of control.  Got a call late last night from Smith saying that the subject had attacked Johnson and that he and the rest of the scientists had fled, leaving Johnson alone with the subject.  I came to the lab as soon as I could, but it was too late.  Luckily for me, Johnson''s training as a security guard had served him well, and he had managed to drug the subject before passing.  I promptly moved the subject into the cell, and locked the door.  I couldn''t find my key afterwards, but I didn''t care.  I plan on leaving the country, I''ll leave this mess for Kinsey to clean up.'), nl, nl,
       write('The next page has photos and descriptions of other subjects.  Kiera Knight, Johanna Knight.  You know them... Confused tears of rage fill your eyes as you read that these subjects have been terminated due to complications... You find confirmation of your fears as you turn the page and stare yourself in the face.  Geoffrey Knight, age 38.'), nl, nl,
       write('Press ENTER to continue...'), nl, nl, read_atomics(_), nl,
       write('New York Times: June 6th, 2064'), nl,
       write('Secret genetic experiments end in tragedy: General Kinsey was found dead in his bedroom this morning, along with documents giving in depth detail on genetic experiments funded by the military.'),nl, nl, do(quit), retract(not_finished).

open(briefcase) :- have(briefcase), write('You do not know the combination to open the briefcase...'),nl, nl.
open(briefcase) :- write('You do not have a briefcase.'),nl,nl.

open(X) :- write('You can''t open the '),write(X), write('.'), nl, nl.

% pull/1 -- This action is only performable on the painting; any other objects cannot be pulled.

pull(painting) :- here('file room'), write('The painting slowly swings open, revealing some numbers etched into the wall. The numbers say "4-8-15-16-23-42". You memorize the numbers in case they turn out to be important.'),nl, nl, asserta(found(password)), retract(not_found(password)).
pull(_) :- write('You can''t pull that.'),nl, nl.


%% CLOSE CLAUSES %%

% closewhichdoor/0 -- This is called when the user has not specified a particular door to close; this is necessary because some rooms contain multiple doors, and so this simply prints a message alerting the user that they must be more specific.

closewhichdoor :- write('which door do you want to close?'), nl, nl.

% closeObject/1 -- This is our close functor; as close/1 is a Prolog command that we cannot redefine, we simply named ourscloseObject/1. This predicate checks that the argument it is passed is reachable by the user, is not already closed, and that it can be closed. In certain cases, special messages are printed.

closeObject(door(Currentroom, Currentroom)) :- write('You are in the '), write(Currentroom), write('.'), nl, write('If you want to close a door, you have to decide which one.'), nl, nl.
closeObject(door(Currentroom, Nextroom)) :- door(Currentroom, Nextroom),
    \+ isClosed(door(Currentroom, Nextroom)),
    \+ isClosed(door(Nextroom, Currentroom)),
    asserta(isClosed(door(Currentroom, Nextroom))),
    asserta(isClosed(door(Nextroom, Currentroom))),
    write('The '),
    write(Nextroom),
    write(' door is now closed!'), nl, nl.
closeObject(door(Currentroom, Nextroom)) :- door(Currentroom, Nextroom), write('The door is already closed.'), nl, nl.
closeObject(door(_, Nextroom)) :- write('You cannot get to the '), write(Nextroom), write(' from here.'), nl, nl.

closeObject(drawer) :- isClosed(drawer), write('The drawer is already closed.'), nl, nl.
closeObject(drawer) :- write('...hmmm, the drawer seems to be stuck open.'), nl, nl.

closeObject(hand) :- write('His fingers are too stiff.'),nl, nl.
closeObject(X) :- write('You can''t close the '), write(X), write('.'),nl, nl.

% isClosed/2 -- This clause is used for for determing if a certain door from between rooms X and Y is closed; it is a
%                   symmetric state.

isClosed(X, Y) :- isClosed(X, Y); isClosed(Y, X).


%% LOOK CLAUSES %%

% look/1 -- This is called when the user wants information about his current surroundings. Specific settings about thecurrent room are determined and displayed, and then list_things/1 is called so that the program lists all the visible objects in the room.

look(cell) :- here(cell), write('You are in a cell. The walls are a dirty brownish grey. You notice a few fingernails lodged in the cracks between the cinderblocks.  There is a big solid iron door labelled LAB, with a small slot to push food through in the bottom. You can see:'),nl,list_things(cell), nl, nl.
look(lab) :- here(lab), write('You are in a laboratory. The room feels eerily familiar, but you dismiss the feeling once you look around.  There is the body of a security guard hunched in the corner, with a blood trail leading up to it.  His body is mangled and broken.  You feel nausious... whoever did this is sick.... really sick. There is a door to the CELL, and a door labeled OFFICE. You can see:'), nl, list_things(lab), nl, nl.
look(office) :- here(office), write('You are in an office. The room is poorly lit, and smells of old cigarette smoke. There are doors labeled FILING ROOM, LAB and SECURITY. The door to the security room has a large warning sign written on it, probably worth reading. You can see:'),nl,list_things(office), nl, nl.
look('security room') :- here('security room'), write('You are in a security room.  It''s rather small, and gives you a slightly uneasy feeling.  You''ve never liked the idea of being watched. There is a door labeled OFFICE. You can see:'),nl,list_things('security room'), nl, nl.
look('file room') :- here('file room'), write('You are in a filing room.  It''s very poorly furnished, with only a portrait of an old man hanging on the wall to give the place any sense of mood, though, you suppose that filing rooms needn''t be too luxurious to serve their purpose.  No one really goes into a filing room to do anything more than file. There is a door labeled OFFICE. You can see:'),nl,list_things('file room'), nl, nl.

% look_at/1 -- This is different from look/1, which aims to describe the entire environment; rather, look_at/1 aims to describe a certain object that the user wishes to look at, whether it be a door, or an object in the room.  It ensures that an object is visible to the user before describing it, or it states that the user cannot see it. As each object is distinct, individual descriptions have been provided.

look_at(cell) :- \+here(cell), here(Y), door(Y, cell), write('The cell door seems cold and uninviting... there wasn''t much in there in the first place, why go back?'), nl, nl.
look_at(cell) :- \+here(cell), here(Y), \+door(Y, cell), write('You cannot see a door to the cell.  Have you lost your way back already?'), nl, nl.
look_at(cell).
look_at(office) :- \+ here(office), here(Y), door(Y, office), write('The office door is made of a strong cherry wood.  Very professional looking, and under other circumstances you might consider it warm.'), nl, nl.
look_at(office) :- \+ here(office), here(Y), \+door(Y, office), write('You cannot see an office door from here.'), nl, nl.
look_at(office).
look_at('file room') :- \+here('file room'), here(Y), door(Y, 'file room'), write('The file room door looks rather strong.  There''s a tiny window at the top, but it doesn''t let you see much of what''s inside, only a very dimly lit room that looks just as abandoned as the rest of this place.'), nl, nl.
look_at('file room') :- \+here('file room'), here(Y), \+door(Y, 'file room'), write('You cannot see a file room door from here.'), nl, nl.
look_at('file room').
look_at('security room') :- \+here('security room'), here(Y), door(Y, 'security room'), write('The security room door seems extremely intimidating -- stainless steel, very thick, and there''s an electronic keycard reader with a number pad flashing a red light.  Even more attention-catching is the big yellow sign in the middle of the door, right at eye-level to ensure visibility.  It reads:'), nl, nl, write('***WARNING: AUTHORIZED ACCESS ONLY. Lethal security measures are enforced.***'), nl, nl, write('...You question your initial curiosity to visit this room.  Do you dare try without the proper access information?'), nl, nl.
look_at('security room') :- \+ here('security room'), here(Y), \+ door(Y, 'security room'), write('You cannot see a door to a security room from here.'), nl, nl.
look_at('security room').
look_at(lab) :- here(cell), write('The door to the lab seems uncared for.  This is evident from the rusty lock that seems ready to fall apart.'), nl, nl.
look_at(lab) :- here(office), write('This door to the lab seems much nicer, as if to make up for appearances.  Just your simple, plain, run of the mill white laboratory door.'), nl, nl.
look_at(lab) :- \+here(lab), here(Y), \+ door(Y, lab), write('You cannot see a door to a laboratory from here.'), nl, nl.
look_at(lab).
look_at(apple) :- location(apple,here(_)), write('The apple looks very old, and is somewhat squishy.'),nl, nl.
look_at(apple) :- here(cell), location(apple, drawer), write('The apple looks very old, and is somewhat squishy.'), nl, nl.
look_at(apple) :- have(apple), write('The apple looks very old, and is somewhat squishy.'),nl, nl.
look_at(chair) :- here(cell), write('Looks comfortable.'),nl, nl.
look_at(desk) :- here(cell), write('The desk looks old, and probably wouldn''t make a very good writing surface. It has a single drawer.'),nl, nl.
look_at(drawer) :- here(cell), write('The desk drawer looks barely functional.'),nl, nl.
look_at('pocket fluff') :- have('pocket fluff'), write('The pocket fluff has a very interesting texture.'),nl, nl.
look_at('pocket fluff') :- location('pocket fluff',here(_)), write('The pocket fluff has a very interesting texture.'),nl, nl.
look_at(jacket) :- here(cell), write('The jacket is black and is in a decent condition. It has only one pocket.'),nl, nl.
look_at(pocket) :- not_taken(key), not_found(key), here(cell), write('You see a key!'),nl, nl, asserta(location(key,cell)),retract(not_found(key)).
look_at(pocket) :- not_taken(key), here(cell), write('You see a key!'),nl, nl.
look_at(pocket) :- here(cell), write('Its empty.'),nl, nl.
look_at(key) :- here(cell), \+not_found(key), write('It''s rusty.'),nl, nl.
look_at(body) :- here(lab), write('It''s a dead security guard. He doesn''t look like he''s had a good time. His hand is resting on the floor.'), nl, nl.
look_at(hand) :- not_found('security card'), not_taken('security card'), here(lab), write('After closer examination you notice a card in the guard''s hand.'), nl, nl, asserta(location('security card',lab)), retract(not_found('security card')).
look_at(hand) :- here(lab), location('security card', lab), not_taken('security card'), write('The security card is still held by the guard''s rigid fingers.'), nl, nl.
look_at(hand) :- here(lab), write('There''s nothing else to see but a bloodied, pale hand.'),nl, nl.
look_at('security card') :- not_taken('security card'), here(lab), write('It looks like a standard security card like something you''d see in a movie.'), nl, nl.
look_at('security card') :- have('security card'), write('A standard issue security card. It reads: "R. JOHNSON -- SECURITY ROOM ACCESS" in red lettering, and a picture of the dead guard below it.'), nl, nl.
look_at('brains in vats') :- here(lab), write('Placed high on a series of steel shelves, you are startled to find several vats with brains contained within. With a growing sense of disgust, you wonder whose heads those used to belong to.'), nl, nl.
look_at('operating table') :- here(lab), write('There are blood stains on the table, suggesting that it has been used multiple times in the past.'), nl, nl.
look_at(door) :- write('Which door do you want to look at?'), nl, nl.
look_at(computer) :- here(office), write('The monitor is blank. You try pressing the power button, but nothing happens. You are left wondering whether the computer ever ran Linux...'),nl, nl.
look_at(table) :- here(office), write('You rub your hand along its surface. The table is surprisingly smooth!'), nl, nl.
look_at(garbage) :- here(office), write('There are various items lying around the room. None of them seem to be of any importance.'), nl, nl.
look_at(painting) :- here('file room'), write('The painting is of an old man with glasses and a white beard. The man is wearing a lab coat. On closer inspection, you see small hinges on the side of the painting.'),nl, nl.
look_at('filing cabinet') :- here('file room'), write('The filing cabinet is a large metal rectangle. You browse through some of the files, but none of them are interesting (financial transactions).'),nl, nl.
look_at(warning) :- my_read(warning).
look_at(counter) :- here('security room'), write('The counter is built into the wall, and has various dials and displays on it. Directly above the counter are some tv monitors. There is also a VCR sitting on the ground beside the counter.'), nl, nl.
look_at(vcr) :- here('security room'), write('The VCR is sitting on the ground beside the counter. There is no power cord attached to it.'), nl, nl.
look_at('tv monitors') :- here('security room'), write('You see a short video sequence which seems to be caught in a loop. A man (who looks strangely familiar) is wrestling viciously with a security guard. The man is yelling at the guard, asking him for a "combination". With his last breath, the security guard utters "1337". The security guard slumps to the floor. The screen goes black, and then the sequence starts over again.'), retract(no_briefcaseCode), nl, nl.

look_at(briefcase) :- here(office), location(briefcase, office), not_taken(briefcase), write('A handsomely appointed dark brown leather briefcase is among the mess on the table. Whoever owned it must have been in a hurry to forget something like this. You notice it has a combination lock, it must contain something important.'), nl, nl.
look_at(briefcase) :- here(Y), location(briefcase,Y), write('It''s the mysterious briefcase you found in the office room. You stare at the combination lock with abject curiosity.'), nl, nl.
look_at(briefcase) :- have(briefcase), write('It has to be important. Why else would it have a combination lock?'), nl, nl.
look_at(_) :- write('You can''t see that.'),nl, nl.


%%TAKE CLAUSES

% take/1 -- This is how the user is able to pick up certain objects in the world.  It ensures that the object is visible and reachable by the user, that it is not too big, and that the user is not already holding it. Certain cases in which the user cannot pick up objects have been supplemented by entertaining details.

take(jacket) :- here(cell), write('You contemplate taking the jacket, but decide not to since its not really your style.'),nl, nl.
take(key) :- location(key,cell), retract(not_taken(key)), write('This key will probably be useful!'), nl, nl, asserta(have(key)), retract(location(key,cell)).
take('security card') :- location('security card', lab), retract(not_taken('security card')), asserta(have('security card')), write('You pry the card from the guard''s cold, dead hand. You''ll probably need this in the future.'), nl, nl, retract(location('security card',lab)).
take('brains in vats') :- here(lab), write('You toy with the idea of taking one of the brains... but the jar is too heavy to pick up and the lid is sealed shut. Such a shame -- you have always wondered what a brain actually feels like.'), nl, nl.
take(briefcase) :- retract(not_taken(briefcase)), here(Y), location(briefcase, Y), retract(location(briefcase,Y)), asserta(have(briefcase)), write('This briefcase looks too important not to take. Besides, who here is going to miss it?'), nl, nl.
take(briefcase) :- \+ not_taken(briefcase), here(Y), location(briefcase, Y), retract(location(briefcase,Y)), asserta(have(briefcase)), write('This briefcase looks too important not to take. Besides, who here is going to miss it?'), nl, nl.
take('tv monitors') :- here('security room'), write('You can''t take the tv''s, they''re too big!'), nl, nl.
take(garbage) :- here(office), write('You sift through some of the garbage cluttering the room, but none of it looks worth taking with you, so instead you throw everything back to the floor, leaving the office no more dishevelled looking than it was when you first entered.'), nl, nl.

take(X) :- room(X), write('You can''t pick up a room!!'), nl, nl.
take(X) :- here(Y), \+location(X, Y), write('You cannot see a(n) '), write(X), write('.'), nl, nl.
take(X) :- have(X), write('You already have that.'), nl, nl.
take(X) :- tooBig(X), write('You can''t pick up the '), write(X), write(', it''s too big!'), nl, nl.

take(X) :- pickupable(X), asserta(have(X)), retract(location(X, _)), write('You have picked up the '), write(X), write('.'), nl, nl.
take(X) :- write('You can''t take the '),write(X),write('.'),nl, nl.


% drop/1 -- This is the action that lets the user put down any objects currently in his inventory.  Whichever room the user is in at the time of the drop is where the dropped object is left, and therefore can be picked up again later. If the user does not currently have the object, he cannot drop it.

drop(X) :- have(X),retract(have(X)), here(Y), asserta(location(X, Y)), write('You have put down the '), write(X), write(' in the '), write(Y), write('.'), nl, nl.
drop(X) :- write('You do not have a(n) '), write(X), write(' to drop.'), nl, nl.


% list_things/1 -- This action displays all the items visible to the user in the current room.

list_things(Place) :- location(X,Place),write('-'),write(X),nl,fail.
list_things(_) :- nl, fail.


% pickupable/1 -- This ensures that the object is not too big to be picked up, is not a room, and is currently visible to the user.

pickupable(X) :- \+ tooBig(X), \+room(X), here(Y), location(X, Y).

% tooBig/1 -- This simply defines all the objects in the world that are too big to be picked up by the user.

tooBig(drawer).
tooBig(desk).
tooBig(chair).
tooBig(body).
tooBig('operating table').
tooBig('large desk').
tooBig('filing cabinet').
tooBig('tv monitors').
tooBig(table).
tooBig(vcr).
tooBig(computer).
tooBig(painting).
tooBig(counter).


% inventory/0 -- This lists all the items that the user is currently holding by calling upon list_possessions, or prints 'You have nothing.' if that is the case.

inventory:-
  have(_),
  write('You have: '),nl,
  list_possessions.
inventory:-
  write('You have nothing.'),nl, nl.


% list_possessions/0 -- This lists all the items that the user is currently holding.

list_possessions:-
  have(X),
  write('-'),write(X),nl,
  fail.
list_possessions:- nl.


%% GO CLAUSES %%

% go/1 -- This is called to move the user either to a specific object in the room, or to another room in the world. If the user is wishing to move to an object, it ensures that the object is in the same room. If the user is wishing to move to another room, it ensures that there is an open door from the current room to the desired room, and that the user is not already in the specified room.

go(Object) :- \+room(Object), here(X), location(Object, X), write('You stand by the '), write(Object), write('.'), nl, nl.
go(Object) :- \+room(Object), write('You cannot see a(n) '), write(Object), write('.'), nl, nl.
go(Gotoroom) :- here(Gotoroom), write('You are already in the '), write(Gotoroom), nl, nl.
go(Gotoroom) :- here(Currentroom), !,
        isDoor(Currentroom, Gotoroom), !,
        isOpen(door(Currentroom, Gotoroom)), !,
        %\+ isClosed(door(Currentroom, Gotoroom)), !,
        %\+ isClosed(door(Gotoroom, Currentroom)), !,
        retract(here(Currentroom)),
        asserta(here(Gotoroom)),
        look(Gotoroom), nl, nl.
go(Gotoroom) :- here(Currentroom), \+ isDoor(Currentroom, Gotoroom), write('You can''t get to the '), write(Gotoroom), write(' from here.'), nl, nl.

% isDoor/2 -- This ensures that there is a door to the desired room from the current room, and prints an appropriate message if the user is already in that room or if there is no door to the destination.

isDoor(Currentroom, Currentroom) :-  write('You are already in the '), write(Currentroom), nl, nl, fail.
isDoor(Currentroom, Gotoroom) :- door(Currentroom,Gotoroom).
isDoor(Currentroom, _) :- write('There''s no door to that room from here...'), nl, write('You sit dumbfounded in the '), write(Currentroom), write('.'), nl, nl, fail.

% isOpen/1 :- This is used to determine if a given door is currently open, in which case it evaluates to true.

isOpen(door(Currentroom, Nextroom)) :- \+(isClosed(door(Currentroom,Nextroom))), \+(isClosed(door(Nextroom, Currentroom))).
isOpen(door(Currentroom, _)) :- write('The door is closed.'), nl, write('You sit dumbfounded in the '), write(Currentroom), nl, nl, fail.


%% MISCELLANEOUS CLAUSES %%

% help/0 -- This is the command called when the user requests help, or a hint.  It tells the user how to use the Prolog interface to move about in our game world, and lists a few key actions.

help :- write('Enter simple English phrases such as "open lab door", "look at painting" or "inventory". \nMany common actions include: picking up, putting down, looking at, opening, and closing things, and going to other rooms.\nA list of basic commands: (o)pen, take, (l)ook, go, (inv)entory, (q)uit.\n(parentheses denotes shortcut)'),nl, nl.

% quit/0 -- This is called only upon exiting the game.

quit :- write('Game Over.').


% sit/1 -- This allows the user to sit on a few items within the world.  However, the sit capability is not integral to the game, and so we have not defined separate similar actions, such as 'get up' or 'stand up'.  This is merely a secondary action meant to create some variability.

sit(chair):- here(cell), write('Wow, this chair is comfortable!'),nl, nl.
sit('operating table') :- here(lab), write('It''s dirty and bloodstained, but you feel the need to rest and so you hoist yourself up onto the operating table to have a sit.  It''s not that comfortable though, so you might as well continue to search for clues as to why you''re here.'), nl, nl.
sit(X):- write('You can''t sit on the '), write(X), write('.'), nl, nl.


% my_read/1 -- As Prolog has read/1 predefined, we created our own my_read/1 that allows the user to read the warning on the security door.  No other objects are readable.

my_read(warning) :- here(office), write('***WARNING: AUTHORIZED ACCESS ONLY. Lethal security measures are enforced.***'),nl, nl.
my_read(X) :- here(Y), \+location(X, Y), write('You cannot see a(n) '), write(X), write('.'), nl, nl.
my_read(_) :- write('You can''t read that.'),nl, nl.


% turn_on/1 -- This action is only applicable to the computer, and for all other objects it displays appropriate messages, such as the fact that the user cannot even see a certain object, or that it is not readable.

turn_on(computer) :- here(office), write('You try pressing the power button, but nothing happens.  You are left wondering whether the computer ever ran Linux...'), nl, nl.
turn_on(X) :- here(Y), location(X, Y), write('You can''t turn on the '), write(X), write('.'), nl, nl.
turn_on(X) :- write('You can''t see a(n) '), write(X), write(' to turn on.'), nl, nl.


% eat/1 -- This functor is merely for enjoyment.  Certain eating attempts will result in special messages, whilst the majority of the objects in our world are not edible and so an appropriate message will be displayed.  Eating the apple will remove it from the world.

eat('pocket fluff') :- write('Ick... you decide not to eat the pocket fluff.'), nl, nl.
eat(apple) :- have(apple), retract(have(apple)), write('Although the apple is rotten, you eat it anyway since you are very hungry.'),nl, nl.
eat(apple) :- write('You must pick up the apple before you can eat it!'), nl, nl.
eat(X) :- write('Sorry, the '), write(X), write(' isn''t very yummy.  You decide it would be best not to try to ingest it.'), nl, nl.


%% COMMAND CLAUSES %%

% command/3 -- This simply allows the user to begin their statements with 'Please'

command(X, [please | L], []) :- command(X, L, []).

% command/1 -- This ensures that a given list of words supplied by the user is defined in our world and can be operated upon by our defined functors.

command([openDoor, Arg1]) --> verb(room, open), nounphrase(room, Arg1).
command([closeDoor, Arg1]) --> verb(room, close), nounphrase(room, Arg1).
command([Pred,Arg]) --> verb(Type,Pred),nounphrase(Type,Arg).
command([Pred]) --> verb(intran,Pred).

%% VERBS %%

% verb/2 -- This determines which kind of verb the user has supplied, which affects how the rest of the user's input is interpreted.

verb(room, V) --> tran_verb(V).
verb(thing, V) --> tran_verb(V).
verb(intran,V) --> intran_verb(V).

% tran_verb/1 -- Is true when the user has inputted one of the allowed transitive verbs.

tran_verb(take) --> [take].
tran_verb(take) --> [pick,up].
tran_verb(take) --> [pickup].
tran_verb(take) --> [get].
tran_verb(drop) --> [drop].
tran_verb(drop) --> [put]. 
tran_verb(drop) --> [put,down].
tran_verb(eat) --> [eat]. 
tran_verb(look_at) --> [look,in].
tran_verb(look_at) --> [look,at].
tran_verb(look_at) --> [look].
tran_verb(look_at) --> [examine].
tran_verb(look_at) --> [l].
tran_verb(look_at) --> [watch].
tran_verb(open) --> [open].
tran_verb(open) --> [o].
tran_verb(close) --> [close].
tran_verb(go) --> [go].
tran_verb(go) --> [go, to].
tran_verb(go) --> [go, into].
tran_verb(go) --> [enter].
tran_verb(go) --> [goto].
tran_verb(sit) --> [sit].
tran_verb(sit) --> [sit, on].
tran_verb(pull) --> [pull].
tran_verb(pull) --> [tug].
tran_verb(pull) --> [look, behind].
tran_verb(read) --> [read].
tran_verb(turn_on) --> [turn, on].

% intran_verb/1 -- Is true when the user has inputted one of the allowed intransitive verbs.

intran_verb(inventory) --> [inventory].
intran_verb(inventory) --> [inv].
intran_verb(look) --> [look].
intran_verb(look) --> [l].
intran_verb(look) --> [look,around].
intran_verb(look) --> [here].
intran_verb(look) --> [look, here].
intran_verb(quit) --> [quit].
intran_verb(quit) --> [exit].
intran_verb(quit) --> [end].
intran_verb(quit) --> [bye].
intran_verb(quit) --> [halt].
intran_verb(quit) --> [q].
intran_verb(help) --> [help].
intran_verb(help) --> [hint].
intran_verb(help) --> [h].

% nounphrase/2 -- Determines if the user has provided a legal noun phrase as determined by our program.

nounphrase(Type,Noun) --> det,noun(Type,Noun).
nounphrase(Type,Noun) --> noun(Type,Noun).

%% NOUNS %%

% det/0 -- Is true if the user has supplied a legal determiner as defined by our program.

det --> [the].
det --> [a].
det --> [an].
det --> [my]. % This really isn't a determiner in natural language, but will make no difference in this world if interpreted as one.

% noun/2 -- This is true if the user has supplied a noun that has been defined by our program.  We have attempted to consider multiple ways in which a user can refer to various objects in the world.

noun(thing, briefcase) --> [case].
noun(thing, briefcase) --> [briefcase].
noun(thing, briefcase) --> [brief, case].
noun(thing, briefcase) --> [breifcase]. %just for byron
noun(thing, key) --> [key].
noun(thing, 'security card') --> [securitycard].
noun(thing, 'security card') --> [card].
noun(thing, 'security card') --> [security, card].
noun(thing, 'pocket fluff') --> [pocketfluff].
noun(thing, 'pocket fluff') --> [fluff].
noun(thing, 'pocket fluff') --> [lint].
noun(thing, 'pocket fluff') --> [pocket, fluff].
noun(thing, apple) --> [apple].
noun(thing, jacket) --> [jacket].
noun(thing, chair) --> [chair].
noun(thing, desk) --> [desk].
noun(thing, pocket) --> [pocket].
noun(thing, pocket) --> [jacket,pocket].
noun(thing, drawer) --> [desk, drawer].
noun(thing, drawer) --> [drawer].
noun(thing, hand) --> [hand].
noun(thing, hand) --> [fist].
noun(thing, body) --> [body].
noun(thing, body) --> [security, guard].
noun(thing, body) --> [securityguard].
noun(thing, body) --> [corpse].
noun(thing, 'brains in vats') --> [brains].
noun(thing, 'brains in vats') --> [brain].
noun(thing, 'brains in vats') --> [vat].
noun(thing, 'brains in vats') --> [vats].
noun(thing, 'brains in vats') --> [brains, in, vats].
noun(thing, 'brains in vats') --> [brain, in, vat].
noun(thing, 'operating table') --> [operating, table].
noun(thing, 'operating table') --> [operating].
noun(thing, computer) --> [computer].
noun(thing, table) --> [table].
noun(thing, 'filing cabinet') --> [filing, cabinet].
noun(thing, 'filing cabinet') --> [cabinet].
noun(thing, garbage) --> [garbage].
noun(thing, garbage) --> [garbage, bin].
noun(thing, garbage) --> [garbage, pail].
noun(thing, garbage) --> [waste, basket].
noun(thing, garbage) --> [rubbish].
noun(thing, counter) --> [counter].
noun(thing, vcr) --> [vcr].
noun(thing, 'tv monitors') --> [tv, monitors].
noun(thing, 'tv monitors') --> [tv].
noun(thing, 'tv monitors') --> [monitor].
noun(thing, 'tv monitors') --> [monitors].
noun(thing, painting) --> [painting].
noun(thing, warning) --> [warning].
noun(thing, warning) --> [warning, sign].
noun(thing, warning) --> [sign].
noun(thing, door) --> [door].
noun(room, lab) --> [lab].
noun(room, lab) --> [lab, door].
noun(room, lab) --> [laboratory].
noun(room, lab) --> [laboratory, door].
noun(room, cell) --> [cell].
noun(room, cell) --> [cell, door].
noun(room, office) --> [office].
noun(room, office) --> [office, door].
noun(room, 'file room') --> [file].
noun(room, 'file room') --> [file, room].
noun(room, 'file room') --> [file, room, door].
noun(room, 'file room') --> [file, door].
noun(room, 'file room') --> [fileroom].
noun(room, 'file room') --> [fileroom, door].
noun(room, 'file room') --> [filing, room].
noun(room, 'file room') --> [filing, room, door].
noun(room, 'security room') --> [security].
noun(room, 'security room') --> [security, room].
noun(room, 'security room') --> [security, room, door].
noun(room, 'security room') --> [security, door].


%%  FACTS that describe our laboratory. These don't change throughout the game. %%

% room/1 -- This defines the rooms in our world.

room(lab).
room(cell).
room(office).
room('file room').
room('security room').

% doors/2 -- This is true if there is a door connecting the first argument and the second argument.

doors(cell,lab).
doors(office,'file room').
doors(lab,office).
doors('security room',office).

% door/2 -- This is true if there is a door connecting the first argument and the second.  This ensures that if there is a door from X to Y, then Prolog will also recognize that there is a door from Y to X.

door(X,Y) :- doors(X, Y).
door(X,Y) :- doors(Y, X).


% ****************
% Tokenizer below:  This has been supplied for use, however there are additional lines that will allow the user to end his comment with a period or an exclamation mark and still have his input recognized as being valid.
% ****************

% read_atomics (-Atomics)
%   Reads a line of text, breaking it into a
%   a list of atomic terms: [this,is,an,example].

read_atomics(Atomics):-
    read_char(FirstC,FirstT),
    complete_line(FirstC,FirstT,Atomics).

% read_char(-Char,-Type)
%  Reads a character and runs it through char_type/1.

read_char(Char,Type):-
    get0(C), % Gets a character from the current input source (check via
         % seeing/1).
    char_type(C,Type,Char).

% complete_line(+FirstC,+FirstT,-Atomics)
%  Given FirstC (the first character) and FirstT (its type), reads
%  and tokenizes the rest of the line into atoms and numbers.

complete_line(_,end,[]):- !.        % stop at end

complete_line(_,blank,Atomics):-    % skip blanks
    !,
    read_atomics(Atomics).

complete_line(FirstC,special,[A|Atomics]):-     % special char
    !,
    name(A,[FirstC]),
    read_atomics(Atomics).

complete_line(FirstC,alpha,[A|Atomics]):-    % begin word
    complete_word(FirstC,alpha,Word,NextC,NextT),
    name(A,Word),    % may have some problems with numbers
    complete_line(NextC,NextT,Atomics).

% complete_word(+FirstC,+FirstT,-List,-FollC,-FollT)
%  Given FirstC (the first character) and FirstT (its type)
%  reads the rest of a word, putting its characters into List.

complete_word(FirstC,alpha,[FirstC|List],FollC,FollT):-
    !,
    read_char(NextC,NextT),
    complete_word(NextC,NextT,List,FollC,FollT).

complete_word(FirstC,FirstT,[],FirstC,FirstT).
    % where FirstT is not alpha

% char_type(+Code,?Type,-NewCode)
%  Given an ASCII Code classifies the character as 'end'
%  (of line/file), 'blank', 'alpha'(numeric), or 'special',
%  and changes it to a potentially different character (NewCode).

char_type(10,end,10):- !.    % UNIX eol mark
char_type(13,end,13):- !.    % DOS eol mark
char_type(-1,end,-1):- !.    % get0 end of file code

char_type(Code,blank,32):-    % blanks, other CTRL codes
    Code =< 32,
    !.

char_type(Code, blank, 46) :- Code = 46, !.     % periods are ignored

char_type(Code, blank, 33) :- Code = 33, !.     % exclamation marks are ignored

char_type(Code,alpha,Code):-    % digits
    48 =< Code, Code =< 57,
    !.

char_type(Code,alpha,Code):-    % lower-case letters
    97 =< Code, Code =< 122,
    !.

char_type(Code,alpha,NewCode):- % upper-case letters
    65 =< Code, Code >= 90,
    !, NewCode is Code + 32.    % (translate to lower case)

char_type(Code,special,Code).    % all others
