{"id":22946590,"url":"https://github.com/mmower/aido","last_synced_at":"2025-08-12T23:32:53.972Z","repository":{"id":62431253,"uuid":"106920764","full_name":"mmower/aido","owner":"mmower","description":"Clojure behaviour tree library","archived":false,"fork":false,"pushed_at":"2020-07-18T18:18:00.000Z","size":50,"stargazers_count":11,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-28T14:54:16.533Z","etag":null,"topics":["behaviour-tree","clojure"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mmower.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-14T11:02:22.000Z","updated_at":"2024-04-08T08:59:20.000Z","dependencies_parsed_at":"2022-11-01T20:46:24.279Z","dependency_job_id":null,"html_url":"https://github.com/mmower/aido","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmower%2Faido","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmower%2Faido/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmower%2Faido/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmower%2Faido/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mmower","download_url":"https://codeload.github.com/mmower/aido/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229639963,"owners_count":18102846,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["behaviour-tree","clojure"],"created_at":"2024-12-14T14:47:23.399Z","updated_at":"2024-12-14T14:47:23.968Z","avatar_url":"https://github.com/mmower.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# aido\n\n[![Clojars Project](https://img.shields.io/clojars/v/sandbags/aido.svg)](https://clojars.org/sandbags/aido)\n\n## Introduction\n\n`aido` stands for \"AI do\" and is a behaviour tree library suitable\nfor implementing AI behaviours into games or applications. It could be\nused to model the behaviour of a game character or for a chabot to\ncontrol the different responses it might employ.\n\nI won't introduce behaviour trees here (see\n[Google](https://www.google.co.uk/search?safe=off\u0026dcr=0\u0026source=hp\u0026q=introduction+to+behaviour+trees\u0026oq=introduction+to+behaviour+trees))\nbeyond the basics. A behaviour tree is a tree-like data structure that\nspecifies conditions we are interested in, actions that might be taken\nin responsento a given set of conditions, and some control flow mechanisms \nhat govern how the tree \"makes decisions\".\n\nThe heart of the control flow are behaviours such as `sequence` and `selector`\n and the notion that any element of the behaviour tree either results in\n **success** or **failure**. In particular, a sequence considers each of its\n children until one of them fails while a selector selects among its children\n until one succeeds. From such a simple premise quite complex behaviours can\n emerge.\n \n## Changelog\n\n* 15.07.20 - v0.4.0 - introduces the `:choose-each` node, implements 'node memory' for stateful node types, some expansion and tidying of documentation\n\n## Specifying a tree\n\n`aido` behaviour trees are implemented as Clojure data structures much in the style of Hiccup markup. As such they can be implemented in EDN notation.\n\nThe structure of a behaviour is\n\n    [:ns/keyword {options}* children*]\n    \nThe core `aido` behaviours do not have a namespace prefix. I recommend namespacing domain specific behaviours.\n\nThe author has developed a convention that behaviours that form conditions should\nhave a `?` suffix, e.g. `:time/after?` while behaviours that represent actions\nwith side-effects should have a `!` suffix, e.g. `:beverage/drink!`.\n\nA behaviour can have zero or more child behaviours. For example the `[:selector]`\nand `[:sequence]` behaviour expect at least one child although, in practice, both\nonly make sense with multiple children.\n\n## Example\n\n    [:selector\n      [:sequence\n        [:time/after? {:t :teatime}]\n        [:beverage/drink! {:beverage :tea}]\n      [:actor/say! {:message \"Oh, how I wish it was time for tea!\"}]\n      \nIn this example the `:selector` runs and ticks its first child, the `:sequence`.\nThe `:sequence` ticks each of its children in turn. If `:time/after?` fails then\nthe `:sequence` fails and the `:selector` goes on to tick `:actor/say!`. On the\nother hand if it succeeds then the `:beverage/drink!` child is ticked and, we\nassume, succeeds leading to the `:sequence` succeeding and the `:selector`\nsucceeding without ticking the `:actor/say!` child.\n      \n## Options\n\nIn the example above we see that `:time/after?`, `:beverage/drink!`, and `:actor/say!`\nall specify a map of options. These are assumed to be understood by the implementation\nof the behaviour in question. The `:selector` and `:sequence` do not have options although\nsome of the built-in behaviours, e.g. `loop` do. In the case of `:loop` it has a `count`\n option to specify how many times the loop should iterate.\n\nSometimes it is advantageous to be able to specify options that are dynamic. aido offers\ntwo approaches:\n\n1. Specify a function value, the syntax for which is:\n\n    `[:aido/fn function-id arg1 ... argN]`\n    \n    e.g.\n\n    `[:loop {:count [:aido/fn rand 5]} ...]`\n\nThe `function-id` should correspond to a function registered with the compiler (see below).\n\nThese functions are stand-alone and executed each time the beheaviour is ticked with\nthe return value of the function being passed into the options maps.\n\n2. Specify a database key-path\n\n     `[:aido/db path1 ... pathN]`\n     \n     e.g.\n     \n     `[:loop {:count [:aido/db :settings :loop-count]]}]`\n     \nWhen the behaviour is ticked the appropriate value in the database will be passed in instead,\nthis example would be conceptually equivalent to:\n\n      [:loop {:count (get-in db [:settings :loop-count])}]\n              \nFunctions are specified by passing an optional map to `compile` for example to satisfy the\ntrees above you might use:\n\n    (aido.compile/compile tree {:rand rand-int})\n    \n## Return values and flow control\n\nAny behaviour is expected to return one of four values:\n\n    SUCCESS\n    FAILURE\n    RUNNING\n    ERROR\n    \nMost commonly behaviours are going to return `SUCCESS` or `FAILURE`.\n\n`ERROR` is intended to be a severe form of `FAILURE` indicating a problem processing the behaviour tree.\nIn the current version of `aido` the two are interchangable and `ERROR` is essentially unused.\n\n`RUNNING` is intended to be an alternative to `SUCCESS` that indicates that a behaviour has neither succeeded\nnor failed but is in-progress. The main difference between `SUCCESS` and `RUNNING` is that a sequence will\nterminate with the `RUNNING` status if any of its children returns `RUNNING`.\n\nFlow control is primilarily implemented in terms of `:selector` and `:sequence` behaviours.\n\n## Usage\n\nIn basic usage you must compile a behaviour tree using `aido.compile/compile` and then to run it\nuse the `aido.core/run-tick` function. It is not recommend to call the `tick` function directly.\n\n    (ns 'aido.example\n      (:require\n        [aido.core :as ai]\n        [aido.compile :as ac]))\n    \n    (let [tree-fns {:coin-toss #(\u003c (rand) 0.5)}\n          tree     (ac/compile [:selector\n                                [:sequence\n                                 [:true? {:expr [:aido/fn coin-toss]}]\n                                 [:heads!]\n                                 [:tails!]]]\n                               tree-fns)\n          db       {:foo :bar}]\n      (let [{:keys [db* status]} (ai/run-tick db tree)]\n        (if (= ai/SUCCESS status)\n          ; extract from or use db*\n          ; otherwise...))\n\n## Memory\n\nSome nodes use a working memory that is reset between invocations of `run-tick`. Some stateful nodes\nsuch as `:choose-each` use storage that is persisted in the database between invocations of `run-tick`\nusing a namespaced key. It is important to preserve this key if the database is modified between\ninvocations of `run-tick`.\n\n## Built-ins\n\n### :selector\n\nThe `:selector` node executes its children in turn until one of them succeeds at\nwhich point execution stops and the `:selector` succeeds. If none of the children\nsuceeds the `:selector` fails.\n\n### :sequence\n\nThe `:sequence` node executes its children in turn. If a child fails execution\nstops and the `:sequence` fails. If all of the children succeed the `:sequence`\nsucceeds.\n\n### :selector-p\n\nThe `:selector-p` node iterates over its children in turn. For each child it does a probability\ncheck which, if it passes, selects that child to be ticked. The `:selector-p` succeeds or fails\nbased on whether the child succeeds or fails. If the probability test does not pass for any\nchild `:selector-p` fails.\n\n#### Parameters\n\n`:p` - probability of ticking any child (0…1)\n\n### :loop\n\nThe `:loop` node executes a single child a specified number of times. It is\nsuccessful if it completes the specified iterations. If the child fails\nthen the `:loop` fails.\n\n#### Parameters\n\n`:count` - number of times the loop should execute \n\n### :loop-until-success\n\nThe `:loop-until-success` node executes a child up to a specified number of times.\nIf the child succeeds then the `:loop-until-success` succeeds. Otherwise, after\nthe specified number of iterations the `:loop-until-success` fails.\n\n#### Parameters\n\n`:count` - number of times the loop should execute\n\n### :parallel\n\nThe `:parallel` node executes all of its children.\n\n#### Parameters\n\n`:mode` - When `:mode` is `:success` the `:how-many` parameter refers to how many children must succeed. When `:mode`\n is `:failure` the parameter refers to how many children must fail. \n`:how-many` - Number of children that must succeed or fail for the node to return success or failure\n\n### :randomly\n\nThe `:randomly` node operates in one of two modes depending on whether it has\none or two children.\n\nWith one child `:randomly` evaluates the child if the p test passes and succeeds\nor fails if the child succeeds or fails. If the `p` test fails `:randomly` fails.\n\nWith two children `:randomly` evaluates the first child if the p test passes or\nthe second child if it fails. `:randomly` succeeds or fails based on the selected\nchild succeeding or failing.\n\n#### Parameters\n\n`:p` - probability\n\n### :choose\n\nThe `:choose` node takes one or more children and, when evaluated, randomly\nselects one child and ticks it. `:choose` succeeds or fails if the child\nsucceeds or fails.\n\n### :choose-each\n\nThe `:choose-each` node takes one or more children and, when evaluated, randomly\nselects one child, ticks, and then marks it. Once a node has been marked it will\nnot be choosen again. `:choose-each` succeeeds or fails if the child\nsucceeds or fails. Subsequent ticks will always select from unmarked children.\n\n#### Parameters\n\n`:repeat` - if repeat is `true`, after all children have been ticked the node \"refills\" its\nchildren. If repeat is `false` after all children have been ticked the node will thereafter\nfail.\n\n### :invert\n\nThe `:invert` node takes a single child. When `:invert` is ticked it ticks its child and inverts\nthe success or failure of the child. So if the child returns failure, invert returns success\nand vice verca.\n\n### :always\n\nThe `:always` node expects one child that it ticks and then suceeds regardless of\nwhether the child succeeds.\n\n### :never\n\nThe `:never` node expects one child that it ticks and then fails regardless of\nwhether the child fails.\n\n## Not yet implemented\n\nThe following are extensions of the choice idea that provide for non-uniform behaviour. They are given\nas separate nodes but, in practice, could be implemented by extending the existing `:choice` node type\nwith additional options.\n\n### :weighted-choice\n\nThe `:weighted-choice` node randomly selects a child to tick based on some weighting algorithm.\n\n## Extending AIDO\n\nBehaviours in AIDO are defined by creating new tick node types. A node type is defined by implementing 3\nmultimethods: `tick`, `options`, and `children`. Implementing `tick` is required, `options` and `children`\noffer default behaviour ('no options' and 'no children' respectively).\n\nLet's define a new node type that is a variation on a selector but has a probability check to determine\n whether to try and tick child nodes. If the probability check fails it moves on to the next child. It\n succeeds if a child succeeds, otherwise fails.\n \nIt would look something like:\n\n    [:selector-p {:p 0.15} [child1] [child2] [child3]]\n    \nHere is how the node type would be defined:\n    \n    (defmethod options :selector-p [\u0026 _] [:p])\n    \n    (defmethod children :selector-p [\u0026 _] :some)\n    \n    (defmethod tick :selector-p [db [node-type {:keys [p]} \u0026 children]\n      ...)\n                \nSee `aido.core` source for definitions of the built in node-types.\n\n## Usage\n\n    (require '[aido.core :as aido]\n              [aido.compile :as ac])\n    \n    (let [tree (ac/compile ...)]\n      (aido/run-tick {} tree))\n\n## License\n\nCopyright 2017-2018 Matthew Mower \u003cself@mattmower.com\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmower%2Faido","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmmower%2Faido","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmower%2Faido/lists"}