diff --git a/__init__.py b/__init__.py
new file mode 100644
index 000000000..5f282702b
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/aibc_agents.elsa..ipynb b/aibc_agents.elsa..ipynb
new file mode 100644
index 000000000..5ef73cc40
--- /dev/null
+++ b/aibc_agents.elsa..ipynb
@@ -0,0 +1,314 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Constructing Agents"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## An agent is a thing inside your environnment\n",
+ "\n",
+ "The initial **Thing** class is really just a shell. We'll want to define whether the thing is *alive*, how to display our thing's state, and how to display maybe a picture of our thing, like if our thing were a function we could make a picture. The **Agent** class is a subclass that performs actions based on what it perceives in the environment. To keep things general, this agent class will take in a user-defined FUNCTION that turns perceptions into actions."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## The Environment class\n",
+ "Is a shell for all environments. It owns **things** and **agents**. It specifies:\n",
+ "* The thing classes it can hold. These things can be just things (like dirt) or agents (like vacuums that can do stuff)\n",
+ "* What it can perceive (percept classes -- like what sensors are on my robot?)\n",
+ "* What it can do. Like if a vacuum sucks up dirt, then it can change the amount of dirt in its environment.\n",
+ "* Specify a default location for new things, like where more dirt might go.\n",
+ "* Specify changes we won't allow (\"exogenous_change\")\n",
+ "* Tell us if all of the agents are dead\n",
+ "* Perform one time step in our environmental \"game\" definition.\n",
+ " * Each agent gets to perceive its state\n",
+ " * Each agent gets to perform an action\n",
+ "* Perform a bunch of steps\n",
+ "* List all the things at a location\n",
+ "* List some things at a location?\n",
+ "* Add a thing at a location (or default location)\n",
+ "* Delete a specified thing"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## The Direction Class\n",
+ "* Specify a heading (**R**ight, **L**eft, **U**p, **Do**wn)\n",
+ "* Move Forward"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Environments on a plane XYEnvironment(Environment)\n",
+ "* Rectangle with width and height\n",
+ " * also initialize a list of observers -- this might be a list provided to the GUI that tells us when things change\n",
+ "* Things near a location (based on perceptible_distance = 1 or specified radius)\n",
+ "* Return what things I can see\n",
+ "* Execute an action\n",
+ " * bump against an edge\n",
+ " * turn left or right\n",
+ " * move forward\n",
+ " * grab a thing\n",
+ " * release a thing\n",
+ "* Add observers who get to find out what's happened\n",
+ "* Move to where I say to move:\n",
+ " * If there's an *Obstacle* at my destination I bump. Obstacles are their own trivial class that can be extended into more \n",
+ " complicated obstacles that are sets of coordinates.\n",
+ " * Otherwise tell all observers the thing moved and remove the thing from the old destination and put it in the new one\n",
+ " * Return True/False for whether or not I moved the thing\n",
+ "* Add a thing to a location. Say what to do if there's a thing there.\n",
+ "* Check to see if the location some jerk specified is actually in my rectangle.\n",
+ "* Randomly choose a location in my rectangle, and maybe I'll list some patches that aren't allowed.\n",
+ "* Delete a thing from the environment. If that thing is an agent drop everything it's holding.\n",
+ "* Add walls so the vacuum doesn't fall down the stairs. A *Wall* is its own trivial class.\n",
+ "* Describe the new heading after a turn happens"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## GraphicEnvironment(XYEnvironment)\n",
+ "Handles the GUI"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Finally let's make a vacuum environment\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "\n",
+ "* Initialize Dirt as a Trivial thing\n",
+ "\n",
+ "* Extend the XYEnvironment to **VacuumEnvironment**:\n",
+ " * The things are Wall, Dirt, and Four agents (reflux, random, tabledriven, and modelbased) for vacuum behavior. We'll get to those.\n",
+ " * The environment knows if an agent (a.k.a. a vacuum) is standing in dirt and if it will bump into something if it moves forward.\n",
+ " * An agent can execute an action:\n",
+ " * If the action is suck it gets 100 points (**performance**) and it deletes the dirt, otherwise the performance is -1 and the action is exected according to the agent's logic."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The only fully-defined environment is for two grids:"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The ReflexVacuumAgent\n",
+ "\n",
+ "This is actually only for the trivial world with 2 grids.\n",
+ "\n",
+ "```python\n",
+ "class TrivialVacuumEnvironment(Environment):\n",
+ " \"\"\"This environment has two locations, A and B. Each can be Dirty\n",
+ " or Clean. The agent perceives its location and the location's\n",
+ " status. This serves as an example of how to implement a simple\n",
+ " Environment.\"\"\"\n",
+ "\n",
+ " def __init__(self):\n",
+ " super().__init__()\n",
+ " self.status = {loc_A: random.choice(['Clean', 'Dirty']),\n",
+ " loc_B: random.choice(['Clean', 'Dirty'])}\n",
+ "\n",
+ " def thing_classes(self):\n",
+ " return [Wall, Dirt, ReflexVacuumAgent, RandomVacuumAgent, TableDrivenVacuumAgent, ModelBasedVacuumAgent]\n",
+ "\n",
+ " def percept(self, agent):\n",
+ " \"\"\"Returns the agent's location, and the location status (Dirty/Clean).\"\"\"\n",
+ " return agent.location, self.status[agent.location]\n",
+ "\n",
+ " def execute_action(self, agent, action):\n",
+ " \"\"\"Change agent's location and/or location's status; track performance.\n",
+ " Score 10 for each dirt cleaned; -1 for each move.\"\"\"\n",
+ " if action == 'Right':\n",
+ " agent.location = loc_B\n",
+ " agent.performance -= 1\n",
+ " elif action == 'Left':\n",
+ " agent.location = loc_A\n",
+ " agent.performance -= 1\n",
+ " elif action == 'Suck':\n",
+ " if self.status[agent.location] == 'Dirty':\n",
+ " agent.performance += 10\n",
+ " self.status[agent.location] = 'Clean'\n",
+ "\n",
+ " def default_location(self, thing):\n",
+ " \"\"\"Agents start in either location at random.\"\"\"\n",
+ " return random.choice([loc_A, loc_B])\n",
+ "```\n",
+ "\n",
+ "\n",
+ "
\n",
+ "
\n",
+ "\n",
+ "Perceive the location and status\n",
+ "* If the status is dirty, suck\n",
+ "* Otherwise move to the other location"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But more generally it is\n",
+ "```python\n",
+ "def SimpleReflexAgentProgram(rules, interpret_input):\n",
+ " \"\"\"\n",
+ " [Figure 2.10]\n",
+ " This agent takes action based solely on the percept.\n",
+ " \"\"\"\n",
+ "\n",
+ " def program(percept):\n",
+ " state = interpret_input(percept)\n",
+ " rule = rule_match(state, rules)\n",
+ " action = rule.action\n",
+ " return action\n",
+ "\n",
+ " return program\n",
+ "\n",
+ "```\n",
+ "\n",
+ "So maybe we'd say\n",
+ "* if the location is dirty, suck (performance +100)\n",
+ "* Otherwise if no bump move forward (performance -1)\n",
+ "* Otherwise move heading to the left or right -- probably want to be random (performance -1)\n",
+ "\n",
+ "state = (location, heading, status)\n",
+ "Note that the examples don't have a heading as part of the state, but it would be necessary for the environment to track to know what happens when we say to move right or left. (We don't need to know to determine action, though. If there's a bump we need to change the heading.)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from agents import ReflexVacuumAgent, TrivialVacuumEnvironment"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{(0, 0): 'Dirty', (1, 0): 'Clean'}"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "agent = ReflexVacuumAgent()\n",
+ "environment = TrivialVacuumEnvironment()\n",
+ "environment.add_thing(agent)\n",
+ "environment.status\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{(0, 0): 'Clean', (1, 0): 'Clean'}"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "environment.run()\n",
+ "environment.status"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from agents import Agent"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The TraceAgent function reports on the agents perception-action movements."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from agents import TraceAgent"
+ ]
+ }
+ ],
+ "metadata": {
+ "interpreter": {
+ "hash": "d82299ac97702ffacc0d7b6e7812598354a240b0faec67019e9fec27ff7627a8"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.8.12 ('parsing')",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.12"
+ },
+ "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}