|
1 | | -# opsearch |
| 1 | +# Operator Search |
2 | 2 |
|
3 | | -A Clojure library designed to ... well, that part is up to you. |
| 3 | +Provides a simple, partially optimised implementation of a breadth-first search mechanism for applying simple |
| 4 | +STRIPS-style operators. For a more efficient engine, check out [the planner](https://github.com/cognesence/planner). |
4 | 5 |
|
5 | | -## Usage |
| 6 | +## Contents |
6 | 7 |
|
7 | | -FIXME |
| 8 | ++ [Installation](#installation) |
| 9 | ++ [Overview](#overview) |
| 10 | ++ [Operators](#operators) |
| 11 | ++ [Examples](#examples) |
| 12 | + - [Without a World](#without-a-world) |
| 13 | + - [Using a World](#using-a-world) |
| 14 | + - [Using Compound Goals](#using-compound-goals) |
8 | 15 |
|
| 16 | +## Installation |
| 17 | + |
| 18 | +This library is hosted on clojars. Get it by adding `org.clojars.cognesence/opsearch` to your dependencies in your |
| 19 | +Leiningen `project.clj` file. |
| 20 | + |
| 21 | +``` |
| 22 | +(defproject com.example/myproject "1.0.0" |
| 23 | + :description "My Leiningen project!" |
| 24 | + :url "http://example.com/projects/myproject" |
| 25 | + :license {:name "Eclipse Public License" |
| 26 | + :url "http://www.eclipse.org/legal/epl-v10.html"} |
| 27 | + :dependencies [[org.clojure/clojure "1.8.0"] |
| 28 | + [org.clojars.cognesence/opsearch "1.0.1"]]) |
| 29 | +
|
| 30 | +``` |
| 31 | + |
| 32 | +## Overview |
| 33 | + |
| 34 | +The `ops-search` method takes the following arguments: |
| 35 | + |
| 36 | ++ `start` - a start state |
| 37 | ++ `goal` - a minimally specified goal state (see below) |
| 38 | ++ `operators` - a collection of operators |
| 39 | ++ `:world` - a definition of unchanging world states |
| 40 | + |
| 41 | +A map is returned showing: |
| 42 | + |
| 43 | ++ the goal state it reached |
| 44 | ++ the path it took |
| 45 | ++ any commands to send to another subsystem |
| 46 | ++ a textual description of the path |
| 47 | + |
| 48 | +**Note:** it is possible to use ops-search without the pattern matcher but its use is then highly restricted. For this |
| 49 | +guide we assume the pattern matcher will be used. |
| 50 | + |
| 51 | +Goals are minimally specified so any state which is a superset of the goal is deemed a goal state. |
| 52 | + |
| 53 | +## Operators |
| 54 | + |
| 55 | +Operators for this search are specified in a map which should associate the operator name with its definition. Operator |
| 56 | +definitions describe preconditions and effects. Effects are specified in terms of deletions and additions. The |
| 57 | +operators used in the examples below are based on a tuple representation. |
| 58 | + |
| 59 | +For example, a move-agent operator could be written as: |
| 60 | + |
| 61 | +```clojure |
| 62 | + move {:pre ((agent ?agent) |
| 63 | + (at ?agent ?p1) |
| 64 | + (connects ?p1 ?p2) |
| 65 | + ) |
| 66 | + :add ((at ?agent ?p2)) |
| 67 | + :del ((at ?agent ?p1)) |
| 68 | +``` |
| 69 | + |
| 70 | +Given a state description like: |
| 71 | + |
| 72 | +```clojure |
| 73 | + '#{(at Ralf table) |
| 74 | + (connects table bench) |
| 75 | + (agent Ralf) |
| 76 | + }) |
| 77 | +``` |
| 78 | + |
| 79 | +The move operator could produce a successor state: |
| 80 | + |
| 81 | +```clojure |
| 82 | + '#{(at Ralf bench) |
| 83 | + (connects table bench) |
| 84 | + (agent Ralf) |
| 85 | + }) |
| 86 | +``` |
| 87 | + |
| 88 | +By deleting `(at Ralf table)` and adding `(at Ralf bench)`. |
| 89 | + |
| 90 | +Operators may (optionally) also specify: |
| 91 | + |
| 92 | ++ `:txt` - a textual description of the operator application to aid readibility |
| 93 | ++ `:cmd` - an encoded command for the operator, typically for the benefit of some other subsystem. |
| 94 | + |
| 95 | +A more complete (but still slightly toy) set of operators could be: |
| 96 | + |
| 97 | +```clojure |
| 98 | +(def ops |
| 99 | + '{pickup {:pre ( (agent ?agent) |
| 100 | + (manipulable ?obj) |
| 101 | + (at ?agent ?place) |
| 102 | + (on ?obj ?place) |
| 103 | + (holds ?agent nil) |
| 104 | + ) |
| 105 | + :add ((holds ?agent ?obj)) |
| 106 | + :del ((on ?obj ?place) |
| 107 | + (holds ?agent nil)) |
| 108 | + :txt (pickup ?obj from ?place) |
| 109 | + :cmd [grasp ?obj] |
| 110 | + } |
| 111 | + drop {:pre ( (at ?agent ?place) |
| 112 | + (holds ?agent ?obj) |
| 113 | + (:guard (? obj)) |
| 114 | + ) |
| 115 | + :add ( (holds ?agent nil) |
| 116 | + (on ?obj ?place)) |
| 117 | + :del ((holds ?agent ?obj)) |
| 118 | + :txt (drop ?obj at ?place) |
| 119 | + :cmd [drop ?obj] |
| 120 | + } |
| 121 | + move {:pre ( (agent ?agent) |
| 122 | + (at ?agent ?p1) |
| 123 | + (connects ?p1 ?p2) |
| 124 | + ) |
| 125 | + :add ((at ?agent ?p2)) |
| 126 | + :del ((at ?agent ?p1)) |
| 127 | + :txt (move ?p1 to ?p2) |
| 128 | + :cmd [move ?p2] |
| 129 | + } |
| 130 | + }) |
| 131 | +``` |
| 132 | + |
| 133 | +## Examples |
| 134 | + |
| 135 | +### Without a World |
| 136 | + |
| 137 | +All following examples use the pickup, drop and move operators defined above: |
| 138 | + |
| 139 | +```clojure |
| 140 | +(def state1 |
| 141 | + '#{(at R table) |
| 142 | + (on book table) |
| 143 | + (on spud table) |
| 144 | + (holds R nil) |
| 145 | + (connects table bench) |
| 146 | + (manipulable book) |
| 147 | + (manipulable spud) |
| 148 | + (agent R) |
| 149 | + }) |
| 150 | +``` |
| 151 | + |
| 152 | +Note (as stated above) goals are minimally specified so any state which is a superset of the goal is deemed a goal |
| 153 | +state. |
| 154 | + |
| 155 | +```clojure |
| 156 | +; user=> (ops-search state1 '((on book bench)) ops) |
| 157 | +{:state #{(agent R) (manipulable book) (on spud table) |
| 158 | + (on book bench) (holds R nil) (at R bench) |
| 159 | + (manipulable spud) (connects table bench)}, |
| 160 | +:path (#{(agent R) (manipulable book) (on spud table) |
| 161 | + (holds R nil) (manipulable spud) (connects table bench) |
| 162 | + (on book table) (at R table)} |
| 163 | + #{(agent R) (holds R book) (manipulable book) |
| 164 | + (on spud table) (manipulable spud) |
| 165 | + (connects table bench) (at R table)} |
| 166 | + #{(agent R) (holds R book) (manipulable book) |
| 167 | + (on spud table) (at R bench) |
| 168 | + (manipulable spud) (connects table bench)}), |
| 169 | +:cmds ([grasp book] [move bench] [drop book]), |
| 170 | +:txt ((pickup book from table) (move table to bench) (drop book at bench))} |
| 171 | +``` |
| 172 | + |
| 173 | +### Using a World |
| 174 | + |
| 175 | +`:world` definitions contain unchanging state relations. Separating static/world relations from those that may change |
| 176 | +improves the efficiency of the search. |
| 177 | + |
| 178 | +```clojure |
| 179 | +(def world |
| 180 | + '#{(connects table bench) |
| 181 | + (manipulable book) |
| 182 | + (manipulable spud) |
| 183 | + (agent R) |
| 184 | + }) |
| 185 | + |
| 186 | +(def state2 |
| 187 | + '#{(at R table) |
| 188 | + (on book table) |
| 189 | + (on spud table) |
| 190 | + (holds R nil) |
| 191 | + }) |
| 192 | + |
| 193 | +; user=> (ops-search state2 '((on book bench)) ops :world world) |
| 194 | +{:state #{(on spud table) (on book bench) (holds R nil) (at R bench)}, |
| 195 | +:path (#{(on spud table) (holds R nil) (on book table) (at R table)} |
| 196 | + #{(holds R book) (on spud table) (at R table)} |
| 197 | + #{(holds R book) (on spud table) (at R bench)}), |
| 198 | +:cmds ([grasp book] [move bench] [drop book]), |
| 199 | +:txt ((pickup book from table) (move table to bench) (drop book at bench))} |
| 200 | +``` |
| 201 | + |
| 202 | +### Using Compound Goals |
| 203 | + |
| 204 | +```clojure |
| 205 | +(def world2 |
| 206 | + '#{(connects table bench) |
| 207 | + (connects bench table) |
| 208 | + (connects bench sink) |
| 209 | + (connects sink bench) |
| 210 | + (manipulable book) |
| 211 | + (manipulable spud) |
| 212 | + (agent R) |
| 213 | + }) |
| 214 | + |
| 215 | +(def state3 |
| 216 | + '#{(at R table) |
| 217 | + (on book table) |
| 218 | + (on spud table) |
| 219 | + (holds R nil) |
| 220 | + }) |
| 221 | + |
| 222 | +; user=> (ops-search state3 '((on book bench)(on spud sink)) ops :world world2) |
| 223 | +{:state #{(at R sink) (on book bench) (holds R nil) (on spud sink)}, |
| 224 | + :path (#{(on spud table) (holds R nil) (on book table) (at R table)} |
| 225 | + #{(holds R book) (on spud table) (at R table)} |
| 226 | + #{(holds R book) (on spud table) (at R bench)} |
| 227 | + #{(on spud table) (on book bench) (holds R nil) (at R bench)} |
| 228 | + #{(on spud table) (on book bench) (holds R nil) (at R table)} |
| 229 | + #{(on book bench) (holds R spud) (at R table)} |
| 230 | + #{(on book bench) (at R bench) (holds R spud)} |
| 231 | + #{(at R sink) (on book bench) (holds R spud)}), |
| 232 | + :cmds ([grasp book] [move bench] [drop book] |
| 233 | + [move table] [grasp spud] [move bench] [move sink] [drop spud]), |
| 234 | + :txt ((pickup book from table) (move table to bench) (drop book at bench) |
| 235 | + (move bench to table) (pickup spud from table) (move table to bench) |
| 236 | + (move bench to sink) (drop spud at sink))} |
| 237 | +``` |
| 238 | + |
| 239 | +Note that goals can be specified as matcher patterns so the following is also a valid call: |
| 240 | + |
| 241 | +```clojure |
| 242 | +(ops-search state3 '((on ?x bench)(on ?y sink)) ops :world world2) |
| 243 | +``` |
| 244 | + |
9 | 245 | ## License |
10 | 246 |
|
11 | | -Copyright © 2017 FIXME |
| 247 | +Copyright © 2017 Simon Lynch |
12 | 248 |
|
13 | 249 | Distributed under the Eclipse Public License either version 1.0 or (at |
14 | 250 | your option) any later version. |
0 commit comments