Crates.io | npc-engine-utils |
lib.rs | npc-engine-utils |
version | 0.1.0 |
source | src |
created_at | 2022-06-15 15:07:47.553452 |
updated_at | 2022-06-15 15:07:47.553452 |
description | The utility module of the NPC engine, providing re-usable support code |
homepage | https://github.com/ethz-gtc/npc-engine |
repository | https://github.com/ethz-gtc/npc-engine |
max_upload_size | |
id | 606566 |
size | 81,776 |
© 2020-2022 ETH Zurich and other contributors. See AUTHORS.txt for more details.
A customizable Monte Carlo Tree Search (MCTS) planner with advanced featured tailored to multi-agent simulations and emergent narratives, by the ETH Game Technology Center.
NPC engine provides the following features:
To get the NPC engine, clone this repository:
git clone https://github.com/ethz-gtc/npc-engine.git
Make sure that you have GIT LFS installed, you can read here how to install it. If not, the PNG files will not be properly fetched, and you will see PNG decoding errors.
We provide several examples that illustrate the various features of the engine.
To run the examples, you need to have Rust installed.
Make sure your console uses a font that supports unicode emojis, as we use emojis for visualization.
On recent Linux and MacOS machines, they are supported out of the box.
On Windows 10 and newer, you can install the recently-released Windows Terminal, and use it with wt
.
cargo run --release --example tic-tac-toe
A traditional tic-tac-toe turn-based game to play interactively against the computer.
To make a move, type X Y
where X
and Y
are the coordinates (0, 1, or 2) of your move.
Source directory: tic-tac-toe
cargo run --release --example capture
A simulation of a competitive battle between two agents in which each tries to capture locations.
Agents can secure locations, collect ammunition and medical kits, and shoot each others. They plan in turn. The simulation outputs the state of the world and the agents each time a task finishes:
This domain demonstrates actions of various durations, a world agent that respawns collectibles, disappearance of agents (upon death), and the use of the simple executor utility.
Source directory: capture
cargo run --release --example learn
A 1-D woodcutter simulation where the agent's performance improves over time due to self learning. The amount of wood collected is output after each run.
An agent must collect wood by cutting down trees in a 1-D world, while using a low number of visits for the MCTS. The state value function estimator in this example is not a standard roll-out simulation, but instead a feed-forward neural network with one hidden layer containing two neurons. The simulation is repeated over multiple epochs, each time using the MCTS-based state value estimation to train the neural network for the next epoch using back-propagation. This simulation shows that over the course of several hundreds epochs, the performance of the agent — the amount of wood collected during a certain duration — improves by more than 50 %.
With Python 3, scipy
and matplotlib
installed, the performance over epochs, averaged over 20 runs, can be seen with the following command:
npc-engine-core/examples/learn/plot.py
The curve should look like this:
Source directory: learn
cargo run --release --example ecosystem
A 2-D ecosystem simulation in which herbivores (🐄) and carnivores (🐅) eat and die.
The world consists of a tilemap where each tile can be empty (dark green), an obstacle (gray), or grass (green). A grass tile can provide 1-3 units of food, visualized with increasing saturation levels. By eating, a herbivore reduces the amount of food of the tile it's standing on by 1. Herbivores are born with 5 units of food, and can store up to 5 units of food. Carnivores can eat herbivores on an adjacent tile or one tile away. They can also jump to one tile away, including over other agents, but not over obstacles, at a cost of 1 additional unit of food. Carnivores are born with 10 units of food, and can store up to 10 units of food. When agents eat, their stored food are resplenished to their maximum amount. Every 10 frames, all agents consume one unit of food. If they do not have food any more, they die.
All agents plan in parallel in a multi-threaded way on their own partial world views. The planning lasts 3 frames, and other actions are instantaneous. Agents see the map up to a distance of 8 tiles and consider other agents up to a distance of 3, respectively 6 tiles for herbivores, respectively carnivores. When planning, agents only consider the 3 closest other agents, if there are more than 3. They aim at 1000 visits per planning, but if not enough computational power is available, planning might end earlier. In that case, the plan quality degrades. The simulation runs at 25 frames per second.
A world agent, with only one "step" action, manages the decrease of food per agent, every 10 frames; and the regrow of the grass, for tiles having 1 or 2 unit of food, every 30 frames.
Source directory: ecosystem
Directory scenario-lumberjacks
The research code used in the paper Leveraging efficient planning and lightweight agent definition: a novel path towards emergent narrative by Raymond et al, 2020. This domain features two lumberjacks whose value function is the total collected wood logs. There is no explicit communication between these two agents, but because they plan for each others, a theory of mind emerges. You can get an overview of this work in this video.
To see a basic run of this scenario, use the following command:
cargo run --release --bin lumberjacks scenario-lumberjacks/experiments/base.json
You can step the simulation by pressing "space". There are two agents, red and yellow, which execute their actions turn by turn. In this basic scenario, the red agent will collect all wood at the right because it reasons about the possible actions of the yellow agent and thus collects the trees in the optimal order.
The scenario can be run non-interactively by setting a configuration parameter with the -s
flag:
cargo run --release --bin lumberjacks -- -s display.interactive=false scenario-lumberjacks/experiments/base.json
Here are some additional interesting experiments from the paper:
The red agent collects the wood in an order that prevents the yellow agent from collecting any, leaving all for itself.
cargo run --release --bin lumberjacks scenario-lumberjacks/experiments/competition-basic/base.json
The red agent typically invests the first wood it collects to build a barrier to block the yellow agent from collecting any more wood, leaving more for itself. This shows the ability to reason about others and accept a short-term loss for a bigger, longer-term profit.
cargo run --release --bin lumberjacks scenario-lumberjacks/experiments/barrier/base.json
Now all adjacent agents to a tree being cut also receive one wood. Because of that, and implicitly through planning, the two agents synchronize together to cut the same tree.
cargo run --release --bin lumberjacks scenario-lumberjacks/experiments/teamwork-basic/base.json
A well is available for the agent to water a tree and let it regrow to full size. The agent does not cut the tree fully, but goes to fetch the water from the well when it is almost dead.
cargo run --release --bin lumberjacks scenario-lumberjacks/experiments/optimization/base.json
If we also allow the agent to plant a new tree (using one wood), the agent cuts the tree and replants one closer to the well:
cargo run --release --bin lumberjacks -- -s features.planting=true scenario-lumberjacks/experiments/optimization/base.json
The NPC engine is composed of two packages: npc-engine-core
and npc-engine-utils
.
The documentation can be generated and browsed interactively with the following command:
cargo doc --open -p npc-engine-core -p npc-engine-utils
Rust is heavily dependent on compiler optimizations.
Make sure that you include the --release
flag to your cargo call for efficient execution.
Some examples (ecosystem, capture, tic-tac-toe) use the the helper functions plot_tree_in_tmp_with_task_name
and plot_tree_in_tmp
to generate graphs of the search tree in the Graphviz's dot format in your temporary directory. Using the generated .dot
files, you can create PDF trees with the following command:
for file in $(ls *.dot); do dot -Tpdf $file -o `basename -s .dot $file`.pdf; done
If you use this software in an academic context, please cite our paper:
@inproceedings{raymond2020leveraging,
title={Leveraging efficient planning and lightweight agent definition: a novel path towards emergent narrative},
author={Raymond, Henry and Knobloch, Sven and Z{\"u}nd, Fabio and Sumner, Robert W and Magnenat, St{\'e}phane},
booktitle={12th Intelligent Narrative Technolgies Workshop, held with the AIIDE Conference (INT10 2020)},
doi={10.3929/ethz-b-000439084},
year={2020},
}
We would like to thank Patrick Eppensteiner, Nora Tommila, and Heinrich Grattenthaler for their contributions to this research project.
Some possible alternatives to this work, also in Rust, are the mcts, arbor and board-game frameworks.
NPC Engine is free and open source! All code in this repository is dual-licensed under either:
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.