Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pacman tutorial website #51

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ gem "github-pages", "~> 232", group: :jekyll_plugins
# If you have any plugins, put them here!
group :jekyll_plugins do
gem "jekyll-feed", "~> 0.12"
gem 'jekyll-commonmark-ghpages', "~> 0.5.1"
end

# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,18 @@ In this scene,
</p>


We will shortly add a [tutorial](https://github.com/KIT-MRT/arbitration_graphs/pull/51) based on this demo – stay tuned!
## Tutorial

Follow our [Tutorial](./docs/Tutorial.md) and learn how to use the Arbitration Graphs library!
It's based on this demo and guides you through all important concepts:

0. [Introduction – start here!](./docs/Tutorial.md)
1. [Implement your first behavior component](./docs/tasks/1_implement_behavior_component.md)
2. [Extend the arbitration graph with that behavior](./docs/tasks/2_extend_arbitration_graph.md)
3. [Add even more behavior components](./docs/tasks/3_add_more_behaviors.md)
4. [Learn about nested arbitration graphs](./docs/tasks/4_nested_arbitrators.md)
5. [Arbitrate based on predicted utility](./docs/tasks/5_cost_arbitration.md)
6. [Verify commands and add a fallback strategy](./docs/tasks/6_verification.md)


## Installation
Expand Down
8 changes: 8 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ sass:
sass_dir: docs/assets/_sass


# Support collapsible details/summary sections
markdown: CommonMarkGhPages

commonmark:
options: ["UNSAFE", "SMART", "FOOTNOTES"]
extensions: ["strikethrough", "autolink", "table", "tagfilter"]


# Tell Jekyll to use README.md and docs/ only.
#
# For some reason exluding root and then including only these does not work,
Expand Down
14 changes: 7 additions & 7 deletions demo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ find_package(Yaml-cpp REQUIRED)
## Build ##
###########

add_library(${PROJECT_NAME} SHARED
add_library(${PROJECT_NAME}_lib SHARED
src/astar.cpp
src/avoid_ghost_behavior.cpp
src/change_dot_cluster_behavior.cpp
Expand All @@ -71,11 +71,11 @@ add_library(${PROJECT_NAME} SHARED
src/move_randomly_behavior.cpp
src/utils.cpp
)
target_include_directories(${PROJECT_NAME} PRIVATE
target_include_directories(${PROJECT_NAME}_lib PRIVATE
include
${SDL2_INCLUDE_DIR}
)
target_link_libraries(${PROJECT_NAME} PUBLIC
target_link_libraries(${PROJECT_NAME}_lib PUBLIC
arbitration_graphs

glog::glog
Expand All @@ -86,15 +86,15 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
${SDL2_LIBRARY}
)

add_executable(${PROJECT_NAME}_exe
add_executable(${PROJECT_NAME}
src/main.cpp
src/pacman_wrapper.cpp
)
target_include_directories(${PROJECT_NAME}_exe PRIVATE
target_include_directories(${PROJECT_NAME} PRIVATE
include
${SDL2_INCLUDE_DIR}
)
target_link_libraries(${PROJECT_NAME}_exe PRIVATE
target_link_libraries(${PROJECT_NAME} PRIVATE
${PROJECT_NAME}
)

Expand Down Expand Up @@ -133,7 +133,7 @@ endif()
## Install ##
#############

install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_exe
install(TARGETS ${PROJECT_NAME}_lib ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
COMPONENT demo
LIBRARY DESTINATION lib COMPONENT Runtime
Expand Down
2 changes: 1 addition & 1 deletion demo/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ WORKDIR /home/blinky/demo/build
RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \
cmake --build . -j8

CMD ["bash", "-c", "/home/blinky/.motd && /home/blinky/demo/build/arbitration_graphs_pacman_demo_exe"]
CMD ["bash", "-c", "/home/blinky/.motd && /home/blinky/demo/build/arbitration_graphs_pacman_demo"]

2 changes: 1 addition & 1 deletion demo/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ if(GTEST_FOUND)
target_link_libraries(${TEST_TARGET_NAME} PUBLIC
${GTEST_BOTH_LIBRARIES}
pthread
arbitration_graphs_pacman_demo
arbitration_graphs_pacman_demo_lib
arbitration_graphs
EnTT_Pacman
util_caching
Expand Down
107 changes: 107 additions & 0 deletions docs/Tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Arbitration Graphs Tutorial

Let's write an agent for the famous PacMan game using Arbitration Graphs 🕹️

**TL;DR:** Find links to the individual tasks at the bottom of this page.

## Introduction


### Goal

The goal of this tutorial is to help you understand how to use the Arbitration Graphs library.
To keep things interesting, we will re-implement some parts of our PacMan demo.

We'll start by looking into the implementation of a single behavior component
and then learn how to integrate it into an arbitration graph using a simple priority arbitrator.

Next, we'll start adding more and more behavior components to the graph and learn about other aspects of the library
such as cost arbitrators, nested structures and verification.

The tutorial is structured into several tasks that are meant to be completed in order.

### What to find where

Let's take a look at the structure and content of the `arbitration_graphs/demo/` directory.

```
demo
├── include
├── src
├── test
└── …
```

All header files can be found in the `include/demo/` directory with corresponding implementation files in the `src/` directory.
The entire demo is thoroughly tested using the unit tests you'll find in the `test/` directory.

Each behavior component is implemented in a separate `_behavior.hpp` file as a class inheriting from the abstract `Behavior` class.

Next, there is `environment_model.hpp`.
You guessed it, it contains the environment model for the arbitration graph.
In it, we store things like current positions of PacMan and the ghosts, the maze, several utility functions
and other things required by the behavior components.

The `cost_estimator.hpp` file will be relevant for a later task when we cover [cost arbitrators](./tasks/5_cost_arbitration.md).

Similarly, the `verifier.hpp` file will be used to add a verification layer to the arbitration graph [near the end](./tasks/6_verification.md) of the tutorial.

Finally, in `pacman_agent.hpp`, the behavior components are assembled into an arbitration graph.
It's also where you'll spend most of your time during this tutorial.

If you are interested in how the demo works in detail,
you'll find additional code not directly relevant to the tutorial in the `include/utils/` directory

### Development Environment

The easiest way to get started is to use the provided docker setup.

Start by checking out the `tutorial` branch where we have removed some parts
of the demo implementation for the purpose of this tutorial.
```bash
git clone --branch tutorial https://github.com/KIT-MRT/arbitration_graphs.git
```

To start an interactive shell in the docker container with all required dependencies installed
and the current directory mounted, run
```bash
cd arbitration_graphs/demo
docker compose run --rm tutorial
```

You can then create a build directory and run CMake to build the project.
You should enable the `BUILD_TESTS` option to build the unit tests as well.

```bash
cd /home/blinky/demo
mkdir build
cd build
cmake -DBUILD_TESTS=true ..
cmake --build . -j9
```

You can then run the demo with
```bash
./arbitration_graphs_pacman_demo
```

You'll also find the individual unit executables in this directory.
To execute them all at once, run
```bash
cmake --build . --target test
```

We'll leave the setup of your favorite IDE up to you
though most modern IDEs should support attaching to a running docker container.


## Tasks

With the basics out of the way, let's work through the tasks.

1. [Implement your first behavior component](./tasks/1_implement_behavior_component.md)
2. [Extend the arbitration graph with that behavior](./tasks/2_extend_arbitration_graph.md)
3. [Add even more behavior components](./tasks/3_add_more_behaviors.md)
4. [Learn about nested arbitration graphs](./tasks/4_nested_arbitrators.md)
5. [Arbitrate based on predicted utility](./tasks/5_cost_arbitration.md)
6. [Verify commands and add a fallback strategy](./tasks/6_verification.md)
12 changes: 12 additions & 0 deletions docs/assets/css/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@

// Add table of contents navbar
@import 'toc';

// Improve collapsible sections
details summary {
cursor: pointer;
display: list-item;
counter-increment: list-item 0;
list-style: disclosure-closed inside;
}

details[open] summary {
list-style-type: disclosure-open;
}
101 changes: 101 additions & 0 deletions docs/tasks/1_implement_behavior_component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
title: "Task 1: Implement a Behavior Component"
description: Implement your first checkInvocationCondition and getCommand function, such that the ChaseGhost behavior component passes its unit tests.
---

# Chase Ghost

## Context

Before we start building our arbitration graph, we want to take a closer look into behavior components.
Don't worry, most of the behavior components are already implemented for you
but we want to make sure you have an idea of how they work.

With the current state of the arbitration graph, PacMan will just move around randomly until a ghost gets too close.
That's great and all but if we ate a power pellet, we want to chase the ghosts to eat them for extra points.

To do this, we need to implement the `ChaseGhost` behavior component.
It essentially does the exact opposite of the `AvoidGhost` behavior component
but is only applicable when PacMan ate a power pellet.
We can ensure that's always the case using the behavior's invocation condition.

But wait - the current implementation of the invocation condition is not complete.
It should only be applicable if there is one of these tasty ghosts close by.

Once that's out of the way, we'll take a closer look at the `getCommand` function which is missing some core logic right now.

## Goal

Finish the implementation of the `checkInvocationCondition` and `getCommand` functions
of the `ChaseGhost` behavior component such that it passes its unit tests.

## Instructions

- Open the implementation of the `ChaseGhost` behavior component in `src/chase_ghost_behavior.cpp`.
- The `checkInvocationCondition` function is already implemented but does not check for the presence of a ghost.
- Implement the missing piece. Take a look at the implementation of `AvoidGhostBehavior::checkInvocationCondition` if you need inspiration.
- The `getCommand` function is partially implemented but the core logic is missing.
- Implement the missing piece. Take a look at the implementation of `AvoidGhostBehavior::getCommand` if you need inspiration.
- Compile and run the unit tests for the `ChaseGhost` behavior component to verify that your implementation is correct.

## Solution

<details>
<summary>Click here to expand the solution</summary>

Fix the invocation condition in `src/chase_ghost_behavior.cpp`:
```cpp
bool ChaseGhostBehavior::checkInvocationCondition(const Time& time) const {
return environmentModel_->closestScaredGhost(time).has_value() &&
environmentModel_->closestScaredGhost(time)->ghost.scaredCountdown > parameters_.minScaredTicksLeft &&
environmentModel_->closestScaredGhost(time)->distance < parameters_.invocationMinDistance; // Only applicable if a ghost is close by
}
```

Add the missing pice of the getCommand function in `src/chase_ghost_behavior.cpp`:
```cpp
Command ChaseGhostBehavior::getCommand(const Time& time) {
auto pacmanPosition = environmentModel_->pacmanPosition();

auto closestScaredGhost = environmentModel_->closestScaredGhost(time);
if (!closestScaredGhost) {
throw std::runtime_error("Can not compute command to chase ghost because there are no scared ghosts.");
}

auto ghostPosition = closestScaredGhost->ghost.position;

std::optional<Direction> direction;

// Add this part:
// Chose the direction moving pacman towards the closest scared ghost
double minDistance = std::numeric_limits<double>::max();
for (const auto& move : Move::possibleMoves()) {
auto nextPosition = environmentModel_->positionConsideringTunnel(pacmanPosition + move.deltaPosition);

if (environmentModel_->isWall(nextPosition)) {
continue;
}

// Chose the direction moving pacman towards the closest scared ghost (considering ghost movement)
auto nextDistance = environmentModel_->mazeDistance(nextPosition, ghostPosition);
if (nextDistance < minDistance) {
direction = move.direction;
minDistance = nextDistance;
}
}

if (!direction) {
throw std::runtime_error("Failed to compute direction to chase the closest ghost.");
}

return Command{direction.value()};
}

```
</details>


---
[Tutorial Home](../Tutorial.md)
|
[Next task →](2_extend_arbitration_graph.md)
Loading
Loading