diff --git a/README.md b/README.md
index 8bac287b6..d89a90bca 100644
--- a/README.md
+++ b/README.md
@@ -112,11 +112,11 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and
| 10.3 | Three-Block-Tower | `three_block_tower` | [`planning.py`][planning] | Done | Included |
| 10.7 | Cake-Problem | `have_cake_and_eat_cake_too` | [`planning.py`][planning] | Done | Included |
| 10.9 | Graphplan | `GraphPlan` | [`planning.py`][planning] | Done | Included |
-| 10.13 | Partial-Order-Planner | | | | |
-| 11.1 | Job-Shop-Problem-With-Resources | `job_shop_problem` | [`planning.py`][planning] | Done | |
+| 10.13 | Partial-Order-Planner | `PartialOrderPlanner` | [`planning.py`][planning] | Done | Included |
+| 11.1 | Job-Shop-Problem-With-Resources | `job_shop_problem` | [`planning.py`][planning] | Done | Included |
| 11.5 | Hierarchical-Search | `hierarchical_search` | [`planning.py`][planning] | | |
| 11.8 | Angelic-Search | | | | |
-| 11.10 | Doubles-tennis | `double_tennis_problem` | [`planning.py`][planning] | | |
+| 11.10 | Doubles-tennis | `double_tennis_problem` | [`planning.py`][planning] | Done | Included |
| 13 | Discrete Probability Distribution | `ProbDist` | [`probability.py`][probability] | Done | Included |
| 13.1 | DT-Agent | `DTAgent` | [`probability.py`][probability] | | |
| 14.9 | Enumeration-Ask | `enumeration_ask` | [`probability.py`][probability] | Done | Included |
diff --git a/images/pop.jpg b/images/pop.jpg
new file mode 100644
index 000000000..52b3e3756
Binary files /dev/null and b/images/pop.jpg differ
diff --git a/planning.ipynb b/planning.ipynb
index fd21a6e88..ca54bcde2 100644
--- a/planning.ipynb
+++ b/planning.ipynb
@@ -19,7 +19,7 @@
"This notebook uses implementations from the [planning.py](https://github.com/aimacode/aima-python/blob/master/planning.py) module. \n",
"See the [intro notebook](https://github.com/aimacode/aima-python/blob/master/intro.ipynb) for instructions.\n",
"\n",
- "We'll start by looking at `PDDL` and `Action` data types for defining problems and actions. \n",
+ "We'll start by looking at `PlanningProblem` and `Action` data types for defining problems and actions. \n",
"Then, we will see how to use them by trying to plan a trip from *Sibiu* to *Bucharest* across the familiar map of Romania, from [search.ipynb](https://github.com/aimacode/aima-python/blob/master/search.ipynb) \n",
"followed by some common planning problems and methods of solving them.\n",
"\n",
@@ -44,26 +44,41 @@
"source": [
"## CONTENTS\n",
"\n",
- "- PDDL\n",
+ "**Classical Planning**\n",
+ "- PlanningProblem\n",
"- Action\n",
"- Planning Problems\n",
" * Air cargo problem\n",
" * Spare tire problem\n",
" * Three block tower problem\n",
" * Shopping Problem\n",
+ " * Socks and shoes problem\n",
" * Cake problem\n",
"- Solving Planning Problems\n",
- " * GraphPlan"
+ " * GraphPlan\n",
+ " * Linearize\n",
+ " * PartialOrderPlanner\n",
+ "
\n",
+ "\n",
+ "**Planning in the real world**\n",
+ "- Problem\n",
+ "- HLA\n",
+ "- Planning Problems\n",
+ " * Job shop problem\n",
+ " * Double tennis problem\n",
+ "- Solving Planning Problems\n",
+ " * Hierarchical Search\n",
+ " * Angelic Search"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## PDDL\n",
+ "## PlanningProblem\n",
"\n",
"PDDL stands for Planning Domain Definition Language.\n",
- "The `PDDL` class is used to represent planning problems in this module. The following attributes are essential to be able to define a problem:\n",
+ "The `PlanningProblem` class is used to represent planning problems in this module. The following attributes are essential to be able to define a problem:\n",
"* an initial state\n",
"* a set of goals\n",
"* a set of viable actions that can be executed in the search space of the problem\n",
@@ -165,29 +180,41 @@
"
class PDDL:\n",
+ "class PlanningProblem:\n",
" """\n",
- " Planning Domain Definition Language (PDDL) used to define a search problem.\n",
+ " Planning Domain Definition Language (PlanningProblem) used to define a search problem.\n",
" It stores states in a knowledge base consisting of first order logic statements.\n",
" The conjunction of these logical statements completely defines a state.\n",
" """\n",
"\n",
" def __init__(self, init, goals, actions):\n",
" self.init = self.convert(init)\n",
- " self.goals = expr(goals)\n",
+ " self.goals = self.convert(goals)\n",
" self.actions = actions\n",
"\n",
- " def convert(self, init):\n",
+ " def convert(self, clauses):\n",
" """Converts strings into exprs"""\n",
+ " if not isinstance(clauses, Expr):\n",
+ " if len(clauses) > 0:\n",
+ " clauses = expr(clauses)\n",
+ " else:\n",
+ " clauses = []\n",
" try:\n",
- " init = conjuncts(expr(init))\n",
+ " clauses = conjuncts(clauses)\n",
" except AttributeError:\n",
- " init = expr(init)\n",
- " return init\n",
+ " clauses = clauses\n",
+ "\n",
+ " new_clauses = []\n",
+ " for clause in clauses:\n",
+ " if clause.op == '~':\n",
+ " new_clauses.append(expr('Not' + str(clause.args[0])))\n",
+ " else:\n",
+ " new_clauses.append(clause)\n",
+ " return new_clauses\n",
"\n",
" def goal_test(self):\n",
" """Checks if the goals have been reached"""\n",
- " return all(goal in self.init for goal in conjuncts(self.goals))\n",
+ " return all(goal in self.init for goal in self.goals)\n",
"\n",
" def act(self, action):\n",
" """\n",
@@ -215,7 +242,7 @@
}
],
"source": [
- "psource(PDDL)"
+ "psource(PlanningProblem)"
]
},
{
@@ -350,7 +377,7 @@
"class Action:\n",
" """\n",
" Defines an action schema using preconditions and effects.\n",
- " Use this to describe actions in PDDL.\n",
+ " Use this to describe actions in PlanningProblem.\n",
" action is an Expr where variables are given as arguments(args).\n",
" Precondition and effect are both lists with positive and negative literals.\n",
" Negative preconditions and effects are defined by adding a 'Not' before the name of the clause\n",
@@ -361,34 +388,38 @@
" """\n",
"\n",
" def __init__(self, action, precond, effect):\n",
- " action = expr(action)\n",
+ " if isinstance(action, str):\n",
+ " action = expr(action)\n",
" self.name = action.op\n",
" self.args = action.args\n",
- " self.precond, self.effect = self.convert(precond, effect)\n",
+ " self.precond = self.convert(precond)\n",
+ " self.effect = self.convert(effect)\n",
"\n",
" def __call__(self, kb, args):\n",
" return self.act(kb, args)\n",
"\n",
- " def convert(self, precond, effect):\n",
+ " def __repr__(self):\n",
+ " return '{}({})'.format(self.__class__.__name__, Expr(self.name, *self.args))\n",
+ "\n",
+ " def convert(self, clauses):\n",
" """Converts strings into Exprs"""\n",
+ " if isinstance(clauses, Expr):\n",
+ " clauses = conjuncts(clauses)\n",
+ " for i in range(len(clauses)):\n",
+ " if clauses[i].op == '~':\n",
+ " clauses[i] = expr('Not' + str(clauses[i].args[0]))\n",
"\n",
- " precond = precond.replace('~', 'Not')\n",
- " if len(precond) > 0:\n",
- " precond = expr(precond)\n",
- " effect = effect.replace('~', 'Not')\n",
- " if len(effect) > 0:\n",
- " effect = expr(effect)\n",
+ " elif isinstance(clauses, str):\n",
+ " clauses = clauses.replace('~', 'Not')\n",
+ " if len(clauses) > 0:\n",
+ " clauses = expr(clauses)\n",
"\n",
- " try:\n",
- " precond = conjuncts(precond)\n",
- " except AttributeError:\n",
- " pass\n",
- " try:\n",
- " effect = conjuncts(effect)\n",
- " except AttributeError:\n",
- " pass\n",
+ " try:\n",
+ " clauses = conjuncts(clauses)\n",
+ " except AttributeError:\n",
+ " pass\n",
"\n",
- " return precond, effect\n",
+ " return clauses\n",
"\n",
" def substitute(self, e, args):\n",
" """Replaces variables in expression with their respective Propositional symbol"""\n",
@@ -405,7 +436,6 @@
"\n",
" if isinstance(kb, list):\n",
" kb = FolKB(kb)\n",
- "\n",
" for clause in self.precond:\n",
" if self.substitute(clause, args) not in kb.clauses:\n",
" return False\n",
@@ -676,7 +706,7 @@
},
"outputs": [],
"source": [
- "prob = PDDL(knowledge_base, goals, [fly_s_b, fly_b_s, fly_s_c, fly_c_s, fly_b_c, fly_c_b, drive])"
+ "prob = PlanningProblem(knowledge_base, goals, [fly_s_b, fly_b_s, fly_s_c, fly_c_s, fly_b_c, fly_c_b, drive])"
]
},
{
@@ -793,12 +823,34 @@
"\n",
"\n",
"def air_cargo():\n",
- " """Air cargo problem"""\n",
+ " """\n",
+ " [Figure 10.1] AIR-CARGO-PROBLEM\n",
+ "\n",
+ " An air-cargo shipment problem for delivering cargo to different locations,\n",
+ " given the starting location and airplanes.\n",
+ "\n",
+ " Example:\n",
+ " >>> from planning import *\n",
+ " >>> ac = air_cargo()\n",
+ " >>> ac.goal_test()\n",
+ " False\n",
+ " >>> ac.act(expr('Load(C2, P2, JFK)'))\n",
+ " >>> ac.act(expr('Load(C1, P1, SFO)'))\n",
+ " >>> ac.act(expr('Fly(P1, SFO, JFK)'))\n",
+ " >>> ac.act(expr('Fly(P2, JFK, SFO)'))\n",
+ " >>> ac.act(expr('Unload(C2, P2, SFO)'))\n",
+ " >>> ac.goal_test()\n",
+ " False\n",
+ " >>> ac.act(expr('Unload(C1, P1, JFK)'))\n",
+ " >>> ac.goal_test()\n",
+ " True\n",
+ " >>>\n",
+ " """\n",
"\n",
- " return PDDL(init='At(C1, SFO) & At(C2, JFK) & At(P1, SFO) & At(P2, JFK) & Cargo(C1) & Cargo(C2) & Plane(P1) & Plane(P2) & Airport(SFO) & Airport(JFK)',\n",
- " goals='At(C1, JFK) & At(C2, SFO)', \n",
+ " return PlanningProblem(init='At(C1, SFO) & At(C2, JFK) & At(P1, SFO) & At(P2, JFK) & Cargo(C1) & Cargo(C2) & Plane(P1) & Plane(P2) & Airport(SFO) & Airport(JFK)', \n",
+ " goals='At(C1, JFK) & At(C2, SFO)',\n",
" actions=[Action('Load(c, p, a)', \n",
- " precond='At(c, a) & At(p, a) & Cargo(c) & Plane(p) & Airport(a)', \n",
+ " precond='At(c, a) & At(p, a) & Cargo(c) & Plane(p) & Airport(a)',\n",
" effect='In(c, p) & ~At(c, a)'),\n",
" Action('Unload(c, p, a)',\n",
" precond='In(c, p) & At(p, a) & Cargo(c) & Plane(p) & Airport(a)',\n",
@@ -886,7 +938,7 @@
"metadata": {},
"source": [
"It returns False because the goal state is not yet reached. Now, we define the sequence of actions that it should take in order to achieve the goal.\n",
- "The actions are then carried out on the `airCargo` PDDL.\n",
+ "The actions are then carried out on the `airCargo` PlanningProblem.\n",
"\n",
"The actions available to us are the following: Load, Unload, Fly\n",
"\n",
@@ -1060,9 +1112,27 @@
"\n",
"\n",
"def spare_tire():\n",
- " """Spare tire problem"""\n",
+ " """[Figure 10.2] SPARE-TIRE-PROBLEM\n",
+ "\n",
+ " A problem involving changing the flat tire of a car\n",
+ " with a spare tire from the trunk.\n",
+ "\n",
+ " Example:\n",
+ " >>> from planning import *\n",
+ " >>> st = spare_tire()\n",
+ " >>> st.goal_test()\n",
+ " False\n",
+ " >>> st.act(expr('Remove(Spare, Trunk)'))\n",
+ " >>> st.act(expr('Remove(Flat, Axle)'))\n",
+ " >>> st.goal_test()\n",
+ " False\n",
+ " >>> st.act(expr('PutOn(Spare, Axle)'))\n",
+ " >>> st.goal_test()\n",
+ " True\n",
+ " >>>\n",
+ " """\n",
"\n",
- " return PDDL(init='Tire(Flat) & Tire(Spare) & At(Flat, Axle) & At(Spare, Trunk)',\n",
+ " return PlanningProblem(init='Tire(Flat) & Tire(Spare) & At(Flat, Axle) & At(Spare, Trunk)',\n",
" goals='At(Spare, Axle) & At(Flat, Ground)',\n",
" actions=[Action('Remove(obj, loc)',\n",
" precond='At(obj, loc)',\n",
@@ -1144,7 +1214,7 @@
"source": [
"As we can see, it hasn't completed the goal. \n",
"We now define a possible solution that can help us reach the goal of having a spare tire mounted onto the car's axle. \n",
- "The actions are then carried out on the `spareTire` PDDL.\n",
+ "The actions are then carried out on the `spareTire` PlanningProblem.\n",
"\n",
"The actions available to us are the following: Remove, PutOn\n",
"\n",
@@ -1369,9 +1439,28 @@
"\n",
"\n",
"def three_block_tower():\n",
- " """Sussman Anomaly problem"""\n",
+ " """\n",
+ " [Figure 10.3] THREE-BLOCK-TOWER\n",
+ "\n",
+ " A blocks-world problem of stacking three blocks in a certain configuration,\n",
+ " also known as the Sussman Anomaly.\n",
"\n",
- " return PDDL(init='On(A, Table) & On(B, Table) & On(C, A) & Block(A) & Block(B) & Block(C) & Clear(B) & Clear(C)',\n",
+ " Example:\n",
+ " >>> from planning import *\n",
+ " >>> tbt = three_block_tower()\n",
+ " >>> tbt.goal_test()\n",
+ " False\n",
+ " >>> tbt.act(expr('MoveToTable(C, A)'))\n",
+ " >>> tbt.act(expr('Move(B, Table, C)'))\n",
+ " >>> tbt.goal_test()\n",
+ " False\n",
+ " >>> tbt.act(expr('Move(A, Table, B)'))\n",
+ " >>> tbt.goal_test()\n",
+ " True\n",
+ " >>>\n",
+ " """\n",
+ "\n",
+ " return PlanningProblem(init='On(A, Table) & On(B, Table) & On(C, A) & Block(A) & Block(B) & Block(C) & Clear(B) & Clear(C)',\n",
" goals='On(A, B) & On(B, C)',\n",
" actions=[Action('Move(b, x, y)',\n",
" precond='On(b, x) & Clear(b) & Clear(y) & Block(b) & Block(y)',\n",
@@ -1453,7 +1542,7 @@
"source": [
"As we can see, it hasn't completed the goal. \n",
"We now define a sequence of actions that can stack three blocks in the required order. \n",
- "The actions are then carried out on the `threeBlockTower` PDDL.\n",
+ "The actions are then carried out on the `threeBlockTower` PlanningProblem.\n",
"\n",
"The actions available to us are the following: MoveToTable, Move\n",
"\n",
@@ -1513,16 +1602,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Shopping Problem"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This problem requires us to acquire a carton of milk, a banana and a drill.\n",
- "Initially, we start from home and it is known to us that milk and bananas are available in the supermarket and the hardware store sells drills.\n",
- "Let's take a look at the definition of the `shopping_problem` in the module."
+ "The `three_block_tower` problem can also be defined in simpler terms using just two actions `ToTable(x, y)` and `FromTable(x, y)`.\n",
+ "The underlying problem remains the same however, stacking up three blocks in a certain configuration given a particular starting state.\n",
+ "Let's have a look at the alternative definition."
]
},
{
@@ -1619,17 +1701,35 @@
"\n",
"\n",
"\n",
- "def shopping_problem():\n",
- " """Shopping problem"""\n",
+ "def simple_blocks_world():\n",
+ " """\n",
+ " SIMPLE-BLOCKS-WORLD\n",
"\n",
- " return PDDL(init='At(Home) & Sells(SM, Milk) & Sells(SM, Banana) & Sells(HW, Drill)',\n",
- " goals='Have(Milk) & Have(Banana) & Have(Drill)', \n",
- " actions=[Action('Buy(x, store)',\n",
- " precond='At(store) & Sells(store, x)',\n",
- " effect='Have(x)'),\n",
- " Action('Go(x, y)',\n",
- " precond='At(x)',\n",
- " effect='At(y) & ~At(x)')])\n",
+ " A simplified definition of the Sussman Anomaly problem.\n",
+ "\n",
+ " Example:\n",
+ " >>> from planning import *\n",
+ " >>> sbw = simple_blocks_world()\n",
+ " >>> sbw.goal_test()\n",
+ " False\n",
+ " >>> sbw.act(expr('ToTable(A, B)'))\n",
+ " >>> sbw.act(expr('FromTable(B, A)'))\n",
+ " >>> sbw.goal_test()\n",
+ " False\n",
+ " >>> sbw.act(expr('FromTable(C, B)'))\n",
+ " >>> sbw.goal_test()\n",
+ " True\n",
+ " >>>\n",
+ " """\n",
+ "\n",
+ " return PlanningProblem(init='On(A, B) & Clear(A) & OnTable(B) & OnTable(C) & Clear(C)',\n",
+ " goals='On(B, A) & On(C, B)',\n",
+ " actions=[Action('ToTable(x, y)',\n",
+ " precond='On(x, y) & Clear(x)',\n",
+ " effect='~On(x, y) & Clear(y) & OnTable(x)'),\n",
+ " Action('FromTable(y, x)',\n",
+ " precond='OnTable(y) & Clear(y) & Clear(x)',\n",
+ " effect='~OnTable(y) & ~Clear(x) & On(y, x)')])\n",
"
\n",
"\n",
"\n"
@@ -1643,20 +1743,26 @@
}
],
"source": [
- "psource(shopping_problem)"
+ "psource(simple_blocks_world)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "**At(x):** Indicates that we are currently at **'x'** where **'x'** can be Home, SM (supermarket) or HW (Hardware store).\n",
+ "**On(x, y):** The block **'x'** is on **'y'**. Both **'x'** and **'y'** have to be blocks.\n",
"\n",
- "**~At(x):** Indicates that we are currently _not_ at **'x'**.\n",
+ "**~On(x, y):** The block **'x'** is _not_ on **'y'**. Both **'x'** and **'y'** have to be blocks.\n",
"\n",
- "**Sells(s, x):** Indicates that item **'x'** can be bought from store **'s'**.\n",
+ "**OnTable(x):** The block **'x'** is on the table.\n",
"\n",
- "**Have(x):** Indicates that we possess the item **'x'**."
+ "**~OnTable(x):** The block **'x'** is _not_ on the table.\n",
+ "\n",
+ "**Clear(x):** To indicate that there is nothing on **'x'** and it is free to be moved around.\n",
+ "\n",
+ "**~Clear(x):** To indicate that there is something on **'x'** and it cannot be moved.\n",
+ "\n",
+ "Let's now define a `simple_blocks_world` prolem."
]
},
{
@@ -1667,14 +1773,14 @@
},
"outputs": [],
"source": [
- "shoppingProblem = shopping_problem()"
+ "simpleBlocksWorld = simple_blocks_world()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Let's first check whether the goal state Have(Milk), Have(Banana), Have(Drill) is reached or not."
+ "Before taking any actions, we will see if `simple_bw` has reached its goal."
]
},
{
@@ -1683,34 +1789,33 @@
"metadata": {},
"outputs": [
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "False\n"
- ]
+ "data": {
+ "text/plain": [
+ "False"
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
}
],
"source": [
- "print(shoppingProblem.goal_test())"
+ "simpleBlocksWorld.goal_test()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Let's look at the possible actions\n",
+ "As we can see, it hasn't completed the goal. \n",
+ "We now define a sequence of actions that can stack three blocks in the required order. \n",
+ "The actions are then carried out on the `simple_bw` PlanningProblem.\n",
"\n",
- "**Buy(x, store):** Buy an item **'x'** from a **'store'** given that the **'store'** sells **'x'**.\n",
+ "The actions available to us are the following: MoveToTable, Move\n",
"\n",
- "**Go(x, y):** Go to destination **'y'** starting from source **'x'**."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We now define a valid solution that will help us reach the goal.\n",
- "The sequence of actions will then be carried out onto the `shoppingProblem` PDDL."
+ "**ToTable(x, y): ** Move box **'x'** stacked on **'y'** to the table, given that box **'y'** is clear.\n",
+ "\n",
+ "**FromTable(x, y): ** Move box **'x'** from wherever it is, to the top of **'y'**, given that both **'x'** and **'y'** are clear.\n"
]
},
{
@@ -1721,22 +1826,19 @@
},
"outputs": [],
"source": [
- "solution = [expr('Go(Home, SM)'),\n",
- " expr('Buy(Milk, SM)'),\n",
- " expr('Buy(Banana, SM)'),\n",
- " expr('Go(SM, HW)'),\n",
- " expr('Buy(Drill, HW)')]\n",
+ "solution = [expr('ToTable(A, B)'),\n",
+ " expr('FromTable(B, A)'),\n",
+ " expr('FromTable(C, B)')]\n",
"\n",
"for action in solution:\n",
- " shoppingProblem.act(action)"
+ " simpleBlocksWorld.act(action)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "We have taken the steps required to acquire all the stuff we need. \n",
- "Let's see if we have reached our goal."
+ "As the `three_block_tower` has taken all the steps it needed in order to achieve the goal, we can now check if it has acheived its goal."
]
},
{
@@ -1745,40 +1847,38 @@
"metadata": {},
"outputs": [
{
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 33,
- "metadata": {},
- "output_type": "execute_result"
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "True\n"
+ ]
}
],
"source": [
- "shoppingProblem.goal_test()"
+ "print(simpleBlocksWorld.goal_test())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "It has now successfully achieved the goal."
+ "It has now successfully achieved its goal i.e, to build a stack of three blocks in the specified order."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Have Cake and Eat Cake Too"
+ "## Shopping Problem"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "This problem requires us to reach the state of having a cake and having eaten a cake simlutaneously, given a single cake.\n",
- "Let's first take a look at the definition of the `have_cake_and_eat_cake_too` problem in the module."
+ "This problem requires us to acquire a carton of milk, a banana and a drill.\n",
+ "Initially, we start from home and it is known to us that milk and bananas are available in the supermarket and the hardware store sells drills.\n",
+ "Let's take a look at the definition of the `shopping_problem` in the module."
]
},
{
@@ -1875,17 +1975,37 @@
"\n",
"\n",
"\n",
- "def have_cake_and_eat_cake_too():\n",
- " """Cake problem"""\n",
+ "def shopping_problem():\n",
+ " """\n",
+ " SHOPPING-PROBLEM\n",
"\n",
- " return PDDL(init='Have(Cake)',\n",
- " goals='Have(Cake) & Eaten(Cake)',\n",
- " actions=[Action('Eat(Cake)',\n",
- " precond='Have(Cake)',\n",
- " effect='Eaten(Cake) & ~Have(Cake)'),\n",
- " Action('Bake(Cake)',\n",
- " precond='~Have(Cake)',\n",
- " effect='Have(Cake)')])\n",
+ " A problem of acquiring some items given their availability at certain stores.\n",
+ "\n",
+ " Example:\n",
+ " >>> from planning import *\n",
+ " >>> sp = shopping_problem()\n",
+ " >>> sp.goal_test()\n",
+ " False\n",
+ " >>> sp.act(expr('Go(Home, HW)'))\n",
+ " >>> sp.act(expr('Buy(Drill, HW)'))\n",
+ " >>> sp.act(expr('Go(HW, SM)'))\n",
+ " >>> sp.act(expr('Buy(Banana, SM)'))\n",
+ " >>> sp.goal_test()\n",
+ " False\n",
+ " >>> sp.act(expr('Buy(Milk, SM)'))\n",
+ " >>> sp.goal_test()\n",
+ " True\n",
+ " >>>\n",
+ " """\n",
+ "\n",
+ " return PlanningProblem(init='At(Home) & Sells(SM, Milk) & Sells(SM, Banana) & Sells(HW, Drill)',\n",
+ " goals='Have(Milk) & Have(Banana) & Have(Drill)', \n",
+ " actions=[Action('Buy(x, store)',\n",
+ " precond='At(store) & Sells(store, x)',\n",
+ " effect='Have(x)'),\n",
+ " Action('Go(x, y)',\n",
+ " precond='At(x)',\n",
+ " effect='At(y) & ~At(x)')])\n",
"
\n",
"\n",
"