Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 26 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# NAIL agent
Navigate Acquire Interact Learn

NAIL is a general game-playing agent designed for parser-based interactive fiction games
NAIL is a general game-playing agent designed for parser-based interactive fiction games
([Hausknecht et al. 2019](https://arxiv.org/abs/1902.04259)).
NAIL employs a simple heuristic: examine the current location to identify relevant objects,
interact with the identified objects, navigate to a new location, and repeat.
Expand All @@ -16,36 +16,33 @@ where it was evaluated on a set of twenty unknown parser-based IF games.
* Python 3

## Installation
* Install basic build tools.
* sudo apt-get update
* sudo apt-get install build-essential
* sudo apt-get install python3-dev

* Install [fastText](https://github.com/facebookresearch/fastText#building-fasttext-for-python)
* pip3 install pybind11
* git clone https://github.com/facebookresearch/fastText.git
* cd fastText
* pip3 install .
* cd ..

* Install [Jericho](https://github.com/Microsoft/jericho)
* pip3 install jericho
* Clone this nail_agent repository to your Linux machine.
* Download the NAIL agent's language model to the nail_agent/agent/affordance_extractors/language_model directory:
* wget http://download.microsoft.com/download/B/8/8/B88DDDC1-F316-412A-94B3-025788436054/nail_agent_lm.zip
* unzip nail_agent_lm.zip
* The unzipped directory should contain 1028 files.

* pip3 install numpy
* pip3 install fuzzywuzzy
* pip3 install spacy
* python3 -m spacy download en
* pip3 install python-Levenshtein
Install basic build tools.

sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install python3-dev

> **Note:** We advise users to use virtual environments to avoid Python packages from different projects to interfere with each other. Popular choices are [Conda Environments](https://conda.io/projects/conda/en/latest/user-guide/getting-started.html) and [venv](https://docs.python.org/3/library/venv.html).

Clone this **nail_agent** repository to your Linux machine.

git clone https://github.com/microsoft/nail_agent.git

Download the NAIL agent's language model (8.1Gb, 1028 files) to the `nail_agent/agent/affordance_extractors/language_model` directory:

cd nail_agent/
wget http://download.microsoft.com/download/B/8/8/B88DDDC1-F316-412A-94B3-025788436054/nail_agent_lm.zip
unzip nail_agent_lm.zip -d agent/affordance_extractors/language_model/

Install dependencies:

pip install -r requirements.txt
python -m spacy download en_core_web_sm

## Usage
* Obtain a z-machine game (like zork1.z5)
* cd nail_agent
* python3 run_nail_agent.py <path_to_game>
Obtain a Z-Machine game (like `zork1.z5`). Then, within the `nail_agent/` folder, run the following command:

python run_nail_agent.py <path_to_game>

## Contributing

Expand Down
3 changes: 3 additions & 0 deletions agent/entity_detectors/spacy_entity_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ def __init__(self):


def detect(self, observation_text):
# Spacy has trouble detecting entities ending with \n.
# Ref: https://github.com/explosion/spaCy/issues/4792#issuecomment-614295948
observation_text = observation_text.replace("\n", " ")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some issues I found with the latest spacy version.

doc = gv.nlp(observation_text)
nouns = []
for chunk in doc.noun_chunks:
Expand Down
4 changes: 2 additions & 2 deletions agent/gv.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@

# Spacy NLP instance
try:
nlp = spacy.load('en')
nlp = spacy.load('en_core_web_sm')
except Exception as e:
print("Failed to load \'en\' with exception {}. Try: python -m spacy download en".format(e))
print("Failed to load \'en\' with exception {}. Try: python -m spacy download en_core_web_sm".format(e))
sys.exit(1)

# Global Action Definitions
Expand Down
2 changes: 1 addition & 1 deletion agent/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
def first_sentence(text):
""" Extracts the first sentence from text. """
tokens = gv.nlp(text)
return next(tokens.sents).merge().text
return next(tokens.sents).text


def tokenize(description):
Expand Down
10 changes: 7 additions & 3 deletions agent/valid_detectors/learned_valid_detector.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import os, sys
import fastText
import fasttext
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from valid_detector import ValidDetector
import gv
import util

# Monkey patch to remove warning message in fasttext 0.9.2.
# Ref: https://github.com/facebookresearch/fastText/issues/1067
fasttext.FastText.eprint = lambda x: None
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed until 0.9.3 comes out.


model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"valid_model.bin")

class LearnedValidDetector(ValidDetector):
"""
Uses a fastText classifier to predict the validity of the response text.
Uses a fasttext classifier to predict the validity of the response text.

"""
def __init__(self):
super().__init__()
self.model = fastText.load_model(model_path)
self.model = fasttext.load_model(model_path)

def action_valid(self, action, response_text):
if not util.action_recognized(action, response_text):
Expand Down
35 changes: 6 additions & 29 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,29 +1,6 @@
certifi==2018.10.15
chardet==3.0.4
cymem==2.0.2
cytoolz==0.9.0.1
dill==0.2.8.2
en-core-web-sm==2.0.0
fasttext==0.8.22
fuzzywuzzy==0.17.0
idna==2.7
jericho==1.1.1
msgpack==0.5.6
msgpack-numpy==0.4.3.2
murmurhash==1.0.1
numpy==1.15.3
pkg-resources==0.0.0
plac==0.9.6
preshed==2.0.1
pybind11==2.2.4
python-Levenshtein==0.12.0
regex==2018.1.10
requests==2.20.0
six==1.11.0
spacy==2.0.16
thinc==6.12.0
toolz==0.9.0
tqdm==4.28.1
ujson==1.35
urllib3==1.24
wrapt==1.10.11
spacy==3.1.1
fasttext==0.9.2
jericho==3.0.5
numpy==1.20.3
python-Levenshtein==0.12.2
fuzzywuzzy==0.18.0
16 changes: 11 additions & 5 deletions run_nail_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,37 @@ def main():
agent = NailAgent(seed=args.seed, env=env, rom_name=os.path.basename(args.game))

# Get the first observation from the environment.
obs = env.reset()
obs, info = env.reset()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reflects Jericho's API changes.


# Keep track of the maximum score achieved.
max_score = 0

# Run the agent on the environment for the specified number of steps.
for step_num in range(args.steps):
# Get one action from the agent.
action = agent.take_action(obs)

# Pass the action to the environment.
new_obs, score, done, info = env.step(action)
new_obs, reward, done, info = env.step(action)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The step method now returns the immediate reward instead of the current score.

max_score = max(info["score"], max_score)

# Update the agent.
agent.observe(obs, action, score, new_obs, done)
agent.observe(obs, action, reward, new_obs, done)
obs = new_obs

# Output this step.
print("Step {} Action [{}] Score {}\n{}".format(step_num, action, score, obs))
print("Step {} Action [{}] Score {}\n{}".format(step_num, action, info["score"], obs))

# Check for done (such as on death).
if done:
print("Environment returned done=True. So reset the environment.\n")
obs = env.reset()
obs, info = env.reset()

# Clean up the agent.
agent.finalize()

print(f"Max score achieved: {max_score}")


if __name__ == "__main__":
main()