base on Give a brain to your game's NPCs # AI Toolkit <center> ![tests](https://img.shields.io/github/actions/workflow/status/linkdd/aitoolkit/tests.yml?style=flat-square&logo=github&label=tests) ![docs](https://img.shields.io/github/actions/workflow/status/linkdd/aitoolkit/docs.yml?style=flat-square&logo=github&label=docs) ![license](https://img.shields.io/github/license/linkdd/aitoolkit?style=flat-square&color=blue) ![version](https://img.shields.io/github/v/release/linkdd/aitoolkit?style=flat-square&color=red) </center> **AI Toolkit** is a header-only C++ library which provides tools for building the brain of your game's NPCs. It provides: - Finite State Machines - Behavior Tree - Utility AI - Goal Oriented Action Planning Why this project? Well, I wrote about it [here](https://david-delassus.medium.com/ai-toolkit-give-a-brain-to-your-npcs-a-header-only-c-library-02a50ae9faed?sk=011cd1ed8e61d22f1be6b6430847f430). ## Installation Add the `include` folder of this repository to your include paths. Or add it as a submodule: ``` $ git submodule add https://github.com/linkdd/aitoolkit.git $ g++ -std=c++23 -Iaitoolkit/include main.cpp -o mygame ``` > **NB:** This library is compatible with C++20. Or using [Shipp](https://github.com/linkdd/shipp), add it to your dependencies: ```json { "name": "myproject", "version": "0.1.0", "dependencies": [ { "name": "aitoolkit", "url": "https://github.com/linkdd/aitoolkit.git", "version": "v0.5.1" } ] } ``` ## Usage ### Finite State Machine First, include the header: ```cpp #include <aitoolkit/fsm.hpp> using namespace aitoolkit::fsm; ``` Then, create your blackboard type: ```cpp struct blackboard_type { // ... }; ``` Then, create a state type for each of your states: ```cpp class state_dummy final : public state<blackboard_type> { public: virtual void enter(blackboard_type& blackboard) override { // ... } virtual void exit(blackboard_type& blackboard) override { // ... } virtual void pause(blackboard_type& blackboard) override { // ... } virtual void resume(blackboard_type& blackboard) override { // ... } virtual void update(blackboard_type& blackboard) override { // ... } }; ``` Create your simple state machine: ```cpp auto simple_bb = blackboard_type{}; auto simple_fsm = simple_machine<blackboard_type>(); simple_fsm.set_state(state_dummy{}, simple_bb); simple_fsm.pause(simple_bb); simple_fsm.resume(simple_bb); simple_fsm.update(simple_bb); ``` Or with a stack state machine: ```cpp auto stack_bb = blackboard_type{}; auto stack_fsm = stack_machine<blackboard_type>{}; stack_fsm.push_state(state_dummy{}, stack_bb); stack_fsm.push_state(state_dummy{}, stack_bb); stack_fsm.update(stack_bb); stack_fsm.pop_state(stack_bb); stack_fsm.pop_state(stack_bb); ``` ### Behavior Tree First, include the header: ```cpp #include <aitoolkit/behtree.hpp> using namespace aitoolkit::bt; ``` Then, create your blackboard type: ```cpp struct blackboard_type { // ... }; ``` Then, create your tree: ```cpp auto tree = seq<blackboard_type>( node_list<blackboard_type>( check<blackboard_type>([](const blackboard_type& bb) { // check some condition return true; }), task<blackboard_type>([](blackboard_type& bb) { // perform some action return execution_state::success; }) ) ); ``` Finally, evaluate it: ```cpp auto blackboard = blackboard_type{ // ... }; auto state = tree.evaluate(blackboard); ``` For more informations, consult the [documentation](https://linkdd.github.io/aitoolkit/group__behtree.html). ### Utility AI First, include the header file: ```cpp #include <aitoolkit/utility.hpp> using namespace aitoolkit::utility; ``` Then, create a blackboard type: ```cpp struct blackboard_type { int food{0}; int wood{0}; int stone{0}; int gold{0}; }; ``` Next, create a class for each action that you want to be able to perform: ```cpp class collect_food final : public action<blackboard_type> { public: virtual float score(const blackboard_type& blackboard) const override { return 50.0f; } virtual void apply(blackboard_type& blackboard) const override { blackboard.food += 1; } }; class collect_wood final : public action<blackboard_type> { public: virtual float score(const blackboard_type& blackboard) const override { return 150.0f; } virtual void apply(blackboard_type& blackboard) const override { blackboard.wood += 1; } }; class collect_stone final : public action<blackboard_type> { public: virtual float score(const blackboard_type& blackboard) const override { return -10.0f; } virtual void apply(blackboard_type& blackboard) const override { blackboard.stone += 1; } }; class collect_gold final : public action<blackboard_type> { public: virtual float score(const blackboard_type& blackboard) const override { return 75.0f; } virtual void apply(blackboard_type& blackboard) const override { blackboard.gold += 1; } }; ``` Finally, create an evaluator and run it: ```cpp auto evaluator = evaluator<blackboard_type>( action_list<blackboard_type>( collect_food{}, collect_wood{}, collect_stone{}, collect_gold{} ) ); auto blackboard = blackboard_type{}; evaluator.run(blackboard); ``` ### Goal Oriented Action Planning First, include the header file: ```cpp #include <aitoolkit/goap.hpp> using namespace aitoolkit::goap; ``` Then, create a blackboard class that will hold the state of the planner: ```cpp struct blackboard_type { bool has_axe{false}; int wood{0}; }; ``` > **NB:** The blackboard needs to be comparable (`a == b`) and hashable. Next, create a class for each action that you want to be able to perform: ```cpp class get_axe final : public action<blackboard_type> { public: virtual float cost(const blackboard_type& blackboard) const override { return 1.0f; } virtual bool check_preconditions(const blackboard_type& blackboard) const override { return !blackboard.has_axe; } virtual void apply_effects(blackboard_type& blackboard, bool dry_run) const override { blackboard.has_axe = true; } }; class chop_tree final : public action<blackboard_type> { public: virtual float cost(const blackboard_type& blackboard) const override { return 1.0f; } virtual bool check_preconditions(const blackboard_type& blackboard) const override { return blackboard.has_axe; } virtual void apply_effects(blackboard_type& blackboard, bool dry_run) const override { blackboard.wood += 1; } }; ``` Finally, create a plan and run it: ```cpp auto initial = blackboard_type{}; auto goal = blackboard_type{ .has_axe = true, .wood = 3 }; auto p = planner<blackboard_type>( action_list<blackboard_type>( get_axe{}, chop_tree{} ), initial, goal ); auto blackboard = initial; while (p) { p.run_next(blackboard); // will mutate the blackboard } ``` For more informations, consult the [documentation](https://linkdd.github.io/aitoolkit/group__goap.html). ## Documentation The documentation is available online [here](https://linkdd.github.io/aitoolkit). You can build it locally using [doxygen](https://www.doxygen.nl/): ``` $ make docs ``` ## License This library is released under the terms of the [MIT License](./LICENSE.txt). ", Assign "at most 3 tags" to the expected json: {"id":"6834","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"