diff --git a/.github/workflows/_cleanup.yml b/.github/workflows/_cleanup.yml new file mode 100644 index 000000000..85113cf5c --- /dev/null +++ b/.github/workflows/_cleanup.yml @@ -0,0 +1,24 @@ +name: cleanup + +on: + workflow_call: + inputs: + when: + description: "When to clean up (before/after)" + required: false + default: "before" + type: string + +jobs: + free-disk: + runs-on: ubuntu-latest + steps: + - name: Free up disk space (${{ inputs.when }}) + run: | + echo "Before cleanup:" + df -h + sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/share/boost + sudo apt clean + sudo rm -rf ~/.cache/pip /tmp/* /var/tmp/* + echo "After cleanup:" + df -h diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index 8541dbee2..af1d9d8f1 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -5,22 +5,34 @@ on: pull_request: jobs: + pre-commit: + name: pre-commit + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + + - name: Run pre-commit on all files + uses: pre-commit/action@v3.0.1 + with: + extra_args: --all-files --show-diff-on-failure + test: name: test ${{ matrix.py }} - ${{ matrix.os }} - ${{ matrix.env }} + needs: pre-commit runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: - - ubuntu-latest - - macos-latest - py: - - "3.11" - - "3.10" - - "3.9" - env: - - pip - - conda + os: [ubuntu-latest, macos-latest] + py: ["3.11", "3.10"] + env: ["pip"] + steps: - name: Checkout code uses: actions/checkout@v3 @@ -40,8 +52,31 @@ jobs: with: python-version: ${{ matrix.py }} + - name: Check disk space (before cleanup) + if: runner.os == 'Linux' + run: df -h + + - name: Free up disk space (Linux only) + if: runner.os == 'Linux' + run: | + echo "Before cleanup:" + df -h + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + sudo apt-get clean + sudo rm -rf ~/.cache/pip /tmp/* /var/tmp/* + echo "After cleanup:" + df -h + - name: Upgrade pip run: python -m pip install -U pip + - name: Check disk space (before install) + if: runner.os == 'Linux' + run: df -h + - name: Install pufferlib run: pip install -e . + + - name: Check disk space (after install) + if: runner.os == 'Linux' + run: df -h diff --git a/.github/workflows/perf-ci.yml b/.github/workflows/perf-ci.yml new file mode 100644 index 000000000..fd6f76553 --- /dev/null +++ b/.github/workflows/perf-ci.yml @@ -0,0 +1,43 @@ +name: perf-ci + +on: + pull_request: + branches: [ main ] + +jobs: + test-performance: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + + - name: Check disk space (before cleanup) + run: df -h + + - name: Free up disk space + run: | + echo "Before cleanup:" + df -h + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + sudo apt-get clean + sudo rm -rf ~/.cache/pip /tmp/* /var/tmp/* + echo "After cleanup:" + df -h + - name: Install dependencies + run: | + sudo apt update && sudo apt install -y build-essential + python -m pip install -U pip + python -m pip install -e . + python setup.py build_ext --inplace --force + - name: Check disk space (after install) + run: df -h + + - name: Test simulator performance + run: python tests/test_simulator_perf.py + timeout-minutes: 20 diff --git a/.github/workflows/render-ci.yml b/.github/workflows/render-ci.yml new file mode 100644 index 000000000..9634af71d --- /dev/null +++ b/.github/workflows/render-ci.yml @@ -0,0 +1,59 @@ +name: render-ci + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + test-render: + name: test-render - ubuntu-latest + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + + - name: Check disk space (before cleanup) + run: df -h + + - name: Free up disk space + run: | + echo "Before cleanup:" + df -h + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + sudo apt-get clean + sudo rm -rf ~/.cache/pip /tmp/* /var/tmp/* + echo "After cleanup:" + df -h + + - name: Install system dependencies for headless rendering + run: | + sudo apt update + sudo apt install -y ffmpeg xvfb build-essential + + - name: Upgrade pip + run: python -m pip install -U pip + + - name: Install pufferlib + run: | + pip install -e .[cpu] --no-cache-dir + env: + TMPDIR: ${{ runner.temp }}/build + PIP_NO_CACHE_DIR: 1 + + - name: Compile C extensions + run: python setup.py build_ext --inplace --force + + - name: Check disk space (after install) + run: df -h + + - name: Run render test + run: python tests/test_drive_render.py + timeout-minutes: 15 diff --git a/.github/workflows/train-ci.yml b/.github/workflows/train-ci.yml new file mode 100644 index 000000000..bd5b37776 --- /dev/null +++ b/.github/workflows/train-ci.yml @@ -0,0 +1,48 @@ +name: train-ci + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + test-training: + name: test-training + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + + - name: Check disk space (before cleanup) + run: df -h + + - name: Free up disk space + run: | + echo "Before cleanup:" + df -h + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + sudo apt-get clean + sudo rm -rf ~/.cache/pip /tmp/* /var/tmp/* + echo "After cleanup:" + df -h + + - name: Install dependencies + run: | + sudo apt update && sudo apt install -y build-essential + python -m pip install -U pip + pip install -e . + python setup.py build_ext --inplace --force + + - name: Check disk space (after install) + run: df -h + + - name: Test training + run: python tests/test_drive_train.py + timeout-minutes: 20 diff --git a/.gitignore b/.gitignore index b579f3dd0..808b54b25 100644 --- a/.gitignore +++ b/.gitignore @@ -161,6 +161,7 @@ wandb/ .neptune/ raylib*/ box2d*/ +inih*/ # Temp Impulse Wars files pufferlib/ocean/impulse_wars/*-debug/ @@ -168,3 +169,27 @@ pufferlib/ocean/impulse_wars/*-release/ pufferlib/ocean/impulse_wars/debug-*/ pufferlib/ocean/impulse_wars/release-*/ pufferlib/ocean/impulse_wars/benchmark/ + + +# Ignore data files +data/ +pufferlib/resources/drive/binaries/ + +# But keep map_000.bin for the training test +!pufferlib/resources/drive/binaries/map_000.bin + +# Compiled drive binary in root +/drive +/visualize +# But allow drive directories +!*/drive/ +*.exe +*.out +*.app +artifacts/ +pufferlib/resources/drive/output_agent.gif pufferlib/resources/drive/output.gif pufferlib/resources/drive/output_topdown.gif + +# Local artifacts and outputs +artifacts/ +# Local drive renders +pufferlib/resources/drive/output*.gif diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..df89de29e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +# Pre-commit hooks for Python and Jupyter notebooks +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer +# Ruff for Python linting and formatting +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.12 + hooks: + # - id: ruff + # args: [ --fix ] + - id: ruff-format +# Cleaning Jupyter notebooks +- repo: https://github.com/srstevenson/nb-clean + rev: 4.0.1 + hooks: + - id: nb-clean + args: + - --remove-empty-cells diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..576130d05 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,7 @@ +# Agent Instructions + +## Git Workflow + +- **Never push directly to `main`, `2.0`, or `3.0` branches** +- Always create a feature branch and open a Pull Request for changes to these branches +- PRs allow for code review and CI checks before merging diff --git a/MANIFEST.in b/MANIFEST.in index 14b53eb75..7f6f85bab 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -13,7 +13,7 @@ include raylib-5.5_linux_amd64/lib/libraylib.a include raylib-5.5_macos/lib/libraylib.a include box2d-linux-amd64/libbox2d.a recursive-exclude box2d-web * -recursive-exclude raylib-5.5_webassembly * +recursive-exclude raylib-5.5_webassembly * recursive-exclude pufferlib/ocean/impulse_wars/debug* * recursive-exclude pufferlib/ocean/impulse_wars/release* * recursive-exclude box2d* * diff --git a/README.md b/README.md index dc1bfaa81..4cadf675a 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,137 @@ -![figure](https://pufferai.github.io/source/resource/header.png) +# PufferDrive -[![PyPI version](https://badge.fury.io/py/pufferlib.svg)](https://badge.fury.io/py/pufferlib) -![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pufferlib) -![Github Actions](https://github.com/PufferAI/PufferLib/actions/workflows/install.yml/badge.svg) -[![](https://dcbadge.vercel.app/api/server/spT4huaGYV?style=plastic)](https://discord.gg/spT4huaGYV) -[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40jsuarez5341)](https://twitter.com/jsuarez5341) -PufferLib is the reinforcement learning library I wish existed during my PhD. It started as a compatibility layer to make working with complex environments a breeze. Now, it's a high-performance toolkit for research and industry with optimized parallel simulation, environments that run and train at 1M+ steps/second, and tons of quality of life improvements for practitioners. All our tools are free and open source. We also offer priority service for companies, startups, and labs! +## Installation -![Trailer](https://github.com/PufferAI/puffer.ai/blob/main/docs/assets/puffer_2.gif?raw=true) +Clone the repo +```bash +https://github.com/Emerge-Lab/PufferDrive.git +``` -All of our documentation is hosted at [puffer.ai](https://puffer.ai "PufferLib Documentation"). @jsuarez5341 on [Discord](https://discord.gg/puffer) for support -- post here before opening issues. We're always looking for new contributors, too! +Make a venv (`uv venv`), activate the venv +``` +source .venv/bin/activate +``` -## Star to puff up the project! +Inside the venv, install the dependencies +``` +uv pip install -e . +``` - - - - - Star History Chart - - +Compile the C code +``` +python setup.py build_ext --inplace --force +``` + +To test your setup, you can run +``` +puffer train puffer_drive +``` +See also the [puffer docs](https://puffer.ai/docs.html). + + +## Quick start + +Start a training run +``` +puffer train puffer_drive +``` + +## Dataset + +
+Downloading and using data + +### Data preparation + +To train with PufferDrive, you need to convert JSON files to map binaries. Run the following command with the path to your data folder: + +```bash +python pufferlib/ocean/drive/drive.py +``` + +### Downloading Waymo Data + +You can download the WOMD data from Hugging Face in two versions: + +- **Mini Dataset**: [GPUDrive_mini](https://huggingface.co/datasets/EMERGE-lab/GPUDrive_mini) contains 1,000 training files and 300 test/validation files +- **Full Dataset**: [GPUDrive](https://huggingface.co/datasets/EMERGE-lab/GPUDrive) contains 100,000 unique scenes + +**Note**: Replace 'GPUDrive_mini' with 'GPUDrive' in your download commands if you want to use the full dataset. + +### Additional Data Sources + +For more training data compatible with PufferDrive, see [ScenarioMax](https://github.com/valeoai/ScenarioMax). The GPUDrive data format is fully compatible with PufferDrive. +
+ + +## Visualizer + +
+Dependencies and usage + +## Headless server setup + +Run the Raylib visualizer on a headless server and export as .mp4. This will rollout the pre-trained policy in the env. + +### Install dependencies + +```bash +sudo apt update +sudo apt install ffmpeg xvfb +``` + +For HPC (There are no root privileges), so install into the conda environment +```bash +conda install -c conda-forge xorg-x11-server-xvfb-cos6-x86_64 +conda install -c conda-forge ffmpeg +``` + +- `ffmpeg`: Video processing and conversion +- `xvfb`: Virtual display for headless environments + +### Build and run + +1. Build the application: +```bash +bash scripts/build_ocean.sh visualize local +``` + +2. Run with virtual display: +```bash +xvfb-run -s "-screen 0 1280x720x24" ./visualize +``` + +The `-s` flag sets up a virtual screen at 1280x720 resolution with 24-bit color depth. + +--- + +> To force a rebuild, you can delete the cached compiled executable binary using `rm ./visualize`. + +--- + +
+ + +## Benchmarks + +### Distributional realism + +We provide a PufferDrive implementation of the [Waymo Open Sim Agents Challenge (WOSAC)](https://waymo.com/open/challenges/2025/sim-agents/) for fast, easy evaluation of how well your trained agent matches distributional properties of human behavior. See details [here](https://github.com/Emerge-Lab/PufferDrive/main/pufferlib/ocean/benchmark). + +WOSAC evaluation with random policy: +```bash +puffer eval puffer_drive --eval.wosac-realism-eval True +``` + +WOSAC evaluation with your checkpoint (must be .pt file): +```bash +puffer eval puffer_drive --eval.wosac-realism-eval True --load-model-path .pt +``` + +### Human-compatibility + +You may be interested in how compatible your agent is with human partners. For this purpose, we support an eval where your policy only controls the self-driving car (SDC). The rest of the agents in the scene are stepped using the logs. While it is not a perfect eval since the human partners here are static, it will still give you a sense of how closely aligned your agent's behavior is to how people drive. You can run it like this: +```bash +puffer eval puffer_drive --eval.human-replay-eval True --load-model-path .pt +``` diff --git a/data_utils/carla/carla/Town01_0.json b/data_utils/carla/carla/Town01_0.json new file mode 100644 index 000000000..21b0fbec4 --- /dev/null +++ b/data_utils/carla/carla/Town01_0.json @@ -0,0 +1,107229 @@ +{ + "name": "Town01", + "scenario_id": "N0KHWCZZgZhX", + "objects": [ + { + "position": [ + { + "x": 92.37733991874609, + "y": -48.26687711506169, + "z": 1.0 + }, + { + "x": 92.37727862621773, + "y": -47.863670458684396, + "z": 1.0 + }, + { + "x": 92.37720201055727, + "y": -47.359662138212784, + "z": 1.0 + }, + { + "x": 92.37711007176472, + "y": -46.75485215364684, + "z": 1.0 + }, + { + "x": 92.37704877923635, + "y": -46.35164549726926, + "z": 1.0 + }, + { + "x": 92.37701813297217, + "y": -46.15004216908089, + "z": 1.0 + }, + { + "x": 92.37694183367158, + "y": -45.648114989838035, + "z": 1.0 + }, + { + "x": 92.37688083639642, + "y": -45.24685062665783, + "z": 1.0 + }, + { + "x": 92.3767893404837, + "y": -44.644954081888244, + "z": 1.0 + }, + { + "x": 92.37669784457097, + "y": -44.04305753711837, + "z": 1.0 + }, + { + "x": 92.37663684729581, + "y": -43.64179317393818, + "z": 1.0 + }, + { + "x": 92.37654535138309, + "y": -43.039896629168595, + "z": 1.0 + }, + { + "x": 92.37645385547036, + "y": -42.43800008439872, + "z": 1.0 + }, + { + "x": 92.3763928581952, + "y": -42.036735721218534, + "z": 1.0 + }, + { + "x": 92.37630136228248, + "y": -41.434839176448946, + "z": 1.0 + }, + { + "x": 92.37622511568853, + "y": -40.93325872247377, + "z": 1.0 + }, + { + "x": 92.37616411841337, + "y": -40.53199435929386, + "z": 1.0 + }, + { + "x": 92.37607262250064, + "y": -39.93009781452399, + "z": 1.0 + }, + { + "x": 92.3760116252255, + "y": -39.52883345134437, + "z": 1.0 + }, + { + "x": 92.37592012931276, + "y": -38.92693690657421, + "z": 1.0 + }, + { + "x": 92.37585913203762, + "y": -38.52567254339458, + "z": 1.0 + }, + { + "x": 92.37578288544367, + "y": -38.02409208941941, + "z": 1.0 + }, + { + "x": 92.37572188816851, + "y": -37.62282772623949, + "z": 1.0 + }, + { + "x": 92.37563039225579, + "y": -37.02093118146962, + "z": 1.0 + }, + { + "x": 92.37555414566185, + "y": -36.51935072749474, + "z": 1.0 + }, + { + "x": 92.3754931483867, + "y": -36.11808636431482, + "z": 1.0 + }, + { + "x": 92.37541690179276, + "y": -35.61650591034021, + "z": 1.0 + }, + { + "x": 92.37532540588003, + "y": -35.01460936557035, + "z": 1.0 + }, + { + "x": 92.37526440860488, + "y": -34.61334500239043, + "z": 1.0 + }, + { + "x": 92.37518816201093, + "y": -34.11176454841525, + "z": 1.0 + }, + { + "x": 92.37512716473579, + "y": -33.71050018523563, + "z": 1.0 + }, + { + "x": 92.37505091814184, + "y": -33.20891973126045, + "z": 1.0 + }, + { + "x": 92.37495942222911, + "y": -32.60702318649058, + "z": 1.0 + }, + { + "x": 92.37488317563518, + "y": -32.10544273251598, + "z": 1.0 + }, + { + "x": 92.37482217836002, + "y": -31.704178369336063, + "z": 1.0 + }, + { + "x": 92.37474593176607, + "y": -31.202597915360887, + "z": 1.0 + }, + { + "x": 92.37466968517214, + "y": -30.701017461385998, + "z": 1.0 + }, + { + "x": 92.37460868789698, + "y": -30.299753098206086, + "z": 1.0 + }, + { + "x": 92.37453244130305, + "y": -29.798172644231478, + "z": 1.0 + }, + { + "x": 92.37447144402789, + "y": -29.39690828105128, + "z": 1.0 + }, + { + "x": 92.37437994811516, + "y": -28.795011736281694, + "z": 1.0 + }, + { + "x": 92.37430370152121, + "y": -28.29343128230652, + "z": 1.0 + }, + { + "x": 92.37421220560849, + "y": -27.691534737536653, + "z": 1.0 + }, + { + "x": 92.37415120833333, + "y": -27.29027037435674, + "z": 1.0 + }, + { + "x": 92.3740749617394, + "y": -26.788689920381852, + "z": 1.0 + }, + { + "x": 92.37398346582667, + "y": -26.18679337561198, + "z": 1.0 + }, + { + "x": 92.37390721923272, + "y": -25.685212921637092, + "z": 1.0 + }, + { + "x": 92.37384622195758, + "y": -25.28394855845746, + "z": 1.0 + }, + { + "x": 92.37376997536363, + "y": -24.782368104482284, + "z": 1.0 + }, + { + "x": 92.37370897808847, + "y": -24.381103741302372, + "z": 1.0 + }, + { + "x": 92.37364798081333, + "y": -23.97983937812274, + "z": 1.0 + }, + { + "x": 92.37355648490059, + "y": -23.377942833352595, + "z": 1.0 + }, + { + "x": 92.37349548762545, + "y": -22.976678470172963, + "z": 1.0 + }, + { + "x": 92.37340399171272, + "y": -22.37478192540309, + "z": 1.0 + }, + { + "x": 92.37334299443756, + "y": -21.9735175622229, + "z": 1.0 + }, + { + "x": 92.37325149852484, + "y": -21.371621017453307, + "z": 1.0 + }, + { + "x": 92.37317525193089, + "y": -20.87004056347814, + "z": 1.0 + }, + { + "x": 92.37308375601816, + "y": -20.268144018708266, + "z": 1.0 + }, + { + "x": 92.37299226010543, + "y": -19.6662474739384, + "z": 1.0 + }, + { + "x": 92.3729007641927, + "y": -19.06435092916853, + "z": 1.0 + }, + { + "x": 92.37283976691755, + "y": -18.66308656598862, + "z": 1.0 + }, + { + "x": 92.37274827100482, + "y": -18.06119002121876, + "z": 1.0 + }, + { + "x": 92.37267202441087, + "y": -17.55960956724387, + "z": 1.0 + }, + { + "x": 92.37258052849815, + "y": -16.957713022474007, + "z": 1.0 + }, + { + "x": 92.372519531223, + "y": -16.556448659294382, + "z": 1.0 + }, + { + "x": 92.37244328462906, + "y": -16.054868205319213, + "z": 1.0 + }, + { + "x": 92.37236703803512, + "y": -15.553287751344609, + "z": 1.0 + }, + { + "x": 92.37230604075997, + "y": -15.152023388164416, + "z": 1.0 + }, + { + "x": 92.37224504348481, + "y": -14.750759024984504, + "z": 1.0 + }, + { + "x": 92.37218404620965, + "y": -14.349494661804595, + "z": 1.0 + }, + { + "x": 92.37210779961572, + "y": -13.847914207829707, + "z": 1.0 + }, + { + "x": 92.37204680234056, + "y": -13.446649844649794, + "z": 1.0 + }, + { + "x": 92.3719858050654, + "y": -13.045385481469882, + "z": 1.0 + }, + { + "x": 92.37189430915268, + "y": -12.443488936700017, + "z": 1.0 + }, + { + "x": 92.37180281323995, + "y": -11.841592391930153, + "z": 1.0 + }, + { + "x": 92.37172656664602, + "y": -11.340011937955548, + "z": 1.0 + }, + { + "x": 92.37171131732723, + "y": -11.239695847160288, + "z": 1.0 + }, + { + "x": 92.37165010369664, + "y": -10.837008211690133, + "z": 1.0 + }, + { + "x": 92.37157358665839, + "y": -10.333648667352328, + "z": 1.0 + }, + { + "x": 92.37149706962015, + "y": -9.830289123014808, + "z": 1.0 + }, + { + "x": 92.37142055258188, + "y": -9.326929578676726, + "z": 1.0 + }, + { + "x": 92.37135933895128, + "y": -8.924241943206486, + "z": 1.0 + }, + { + "x": 92.37128282191303, + "y": -8.420882398868397, + "z": 1.0 + }, + { + "x": 92.38387847983404, + "y": -7.926753944901536, + "z": 1.0 + }, + { + "x": 92.41037157599465, + "y": -7.620861084595008, + "z": 1.0 + }, + { + "x": 92.45147026774205, + "y": -7.316185300747591, + "z": 1.0 + }, + { + "x": 92.50702318936625, + "y": -7.013812440974701, + "z": 1.0 + }, + { + "x": 92.59659554589444, + "y": -6.640127905133789, + "z": 1.0 + }, + { + "x": 92.68416038829855, + "y": -6.345430981750049, + "z": 1.0 + }, + { + "x": 92.8415494737095, + "y": -5.9120333829588265, + "z": 1.0 + }, + { + "x": 93.02946380436408, + "y": -5.490972509787698, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 1, + "heading": [ + 1.5541722980230424, + 1.5709483394834673, + 1.570948339483483, + 1.5709483394834929, + 1.5709483394834955, + 1.5709483394834909, + 1.570948339483491, + 1.5709483394834989, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834896, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834896, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394835004, + 1.5709483394834896, + 1.5709483394834753, + 1.5709483394834896, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394835004, + 1.5709483394834896, + 1.5709483394834804, + 1.570948339483485, + 1.570948339483485, + 1.5709483394834804, + 1.5709483394834896, + 1.5709483394835004, + 1.570948339483485, + 1.570948339483485, + 1.5709483394834933, + 1.5709483394834804, + 1.570948339483485, + 1.5709483394835004, + 1.5709483394834896, + 1.570948339483485, + 1.570948339483485, + 1.570948339483485, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394834933, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834804, + 1.5709483394834933, + 1.570948339483485, + 1.570948339483485, + 1.5709483394835004, + 1.570948339483479, + 1.5709483394834896, + 1.5709483394834896, + 1.5709483394834753, + 1.5709483394834896, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394834933, + 1.570948339483485, + 1.570948339483485, + 1.5709483394834896, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394834933, + 1.5709483394834753, + 1.570948339483485, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834967, + 1.5709483394834967, + 1.570948339483485, + 1.570948339483485, + 1.5709483394834967, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834804, + 1.570948339483485, + 1.5709483394834682, + 1.570948339483472, + 1.5709483394834736, + 1.5709483394834878, + 1.5709483394835033, + 1.5709483394834878, + 1.5582463175408183, + 1.5219755106522177, + 1.4605422460017705, + 1.4129059797025108, + 1.3593412115275172, + 1.3117280718770892, + 1.2462600242857522, + 1.1867436228401171 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.00061292528357626, + "y": 4.032066563772929 + }, + { + "x": -0.0013790818881886935, + "y": 9.072149768489055 + }, + { + "x": -0.001685544530118932, + "y": 11.08818305037559 + }, + { + "x": -0.001532313209224867, + "y": 10.080166409435236 + }, + { + "x": -0.0009193879255064985, + "y": 6.048099845659465 + }, + { + "x": -0.001069455647666473, + "y": 7.035305074312248 + }, + { + "x": -0.00137296575744017, + "y": 9.03191542423059 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497912 + }, + { + "x": -0.0018299182545433723, + "y": 12.037930895394595 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079500612 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0018299182545433723, + "y": 12.037930895394595 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079500612 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987447675 + }, + { + "x": -0.0013724386910496378, + "y": 9.028448171550849 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0015249318786914046, + "y": 10.031609079494928 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497912 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.0013724386910496378, + "y": 9.028448171550849 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497912 + }, + { + "x": -0.0016774250666173884, + "y": 11.034769987447532 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171545307 + }, + { + "x": -0.0016774250666173884, + "y": 11.03476998744469 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0013724386910496378, + "y": 9.028448171550991 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987450517 + }, + { + "x": -0.0016774250666173884, + "y": 11.03476998744469 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171545165 + }, + { + "x": -0.0013724386910496378, + "y": 9.028448171550956 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079500647 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.0013724386909075292, + "y": 9.0284481715452 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548042 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949784 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987447604 + }, + { + "x": -0.001677425066759497, + "y": 11.03476998745041 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497805 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.0016774250666173884, + "y": 11.034769987447604 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987447604 + }, + { + "x": -0.0013724386909075292, + "y": 9.0284481715452 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548078 + }, + { + "x": -0.0013724386910496378, + "y": 9.028448171550885 + }, + { + "x": -0.0012199455029815454, + "y": 8.025287263595438 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0015249318786914046, + "y": 10.031609079495034 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079500647 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949784 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987447604 + }, + { + "x": -0.001677425066759497, + "y": 11.03476998745041 + }, + { + "x": -0.0018299182545433723, + "y": 12.037930895397366 + }, + { + "x": -0.0018299182545433723, + "y": 12.037930895397366 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497805 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497699 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987447497 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987447532 + }, + { + "x": -0.0015249318786914046, + "y": 10.031609079494892 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171547936 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497734 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171547971 + }, + { + "x": -0.001219945503123654, + "y": 8.02528726360105 + }, + { + "x": -0.001219945503123654, + "y": 8.025287263598209 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171547971 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.001219945503123654, + "y": 8.025287263598244 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0018299182545433723, + "y": 12.037930895397295 + }, + { + "x": -0.0016774250666173884, + "y": 11.03476998744469 + }, + { + "x": -0.0009149591272716862, + "y": 6.018965447698648 + }, + { + "x": -0.0007646294938012943, + "y": 5.0300372626541545 + }, + { + "x": -0.0013773066883970841, + "y": 9.060471798079597 + }, + { + "x": -0.0015303407649014389, + "y": 10.067190886753252 + }, + { + "x": -0.0015303407650435474, + "y": 10.067190886756023 + }, + { + "x": -0.0013773066886813012, + "y": 9.06047179808322 + }, + { + "x": -0.0013773066885391927, + "y": 9.060471798083292 + }, + { + "x": 0.1251914088275896, + "y": 9.974879983049494 + }, + { + "x": 0.3908875408161805, + "y": 8.000213142733887 + }, + { + "x": 0.6759178790801457, + "y": 6.105686441539451 + }, + { + "x": 0.9665161337160555, + "y": 6.070486436203071 + }, + { + "x": 1.4512527815239196, + "y": 6.760573956138023 + }, + { + "x": 1.7713719893230007, + "y": 6.683814592246522 + }, + { + "x": 2.44953927815061, + "y": 7.280945221749624 + }, + { + "x": 3.453034160655335, + "y": 8.544584719623511 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 93.02946380436408, + "y": -5.490972509787698, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 88.39124251873497, + "y": -139.72401945159578, + "z": 1.0 + }, + { + "x": 88.3913191036827, + "y": -140.22782573152114, + "z": 1.0 + }, + { + "x": 88.39141100561999, + "y": -140.83239326743148, + "z": 1.0 + }, + { + "x": 88.3914875905677, + "y": -141.33619954735684, + "z": 1.0 + }, + { + "x": 88.39156417551544, + "y": -141.84000582728214, + "z": 1.0 + }, + { + "x": 88.39156417551544, + "y": -141.84000582728214, + "z": 1.0 + }, + { + "x": 88.39162514892526, + "y": -142.2411131951493, + "z": 1.0 + }, + { + "x": 88.39168607725401, + "y": -142.6419240013292, + "z": 1.0 + }, + { + "x": 88.39176223766492, + "y": -143.14293750905404, + "z": 1.0 + }, + { + "x": 88.39185363015802, + "y": -143.74415371832356, + "z": 1.0 + }, + { + "x": 88.39191455848675, + "y": -144.14496452450373, + "z": 1.0 + }, + { + "x": 88.3919754868155, + "y": -144.54577533068306, + "z": 1.0 + }, + { + "x": 88.39203641514422, + "y": -144.94658613686346, + "z": 1.0 + }, + { + "x": 88.39209734347294, + "y": -145.34739694304335, + "z": 1.0 + }, + { + "x": 88.39217350388387, + "y": -145.84841045076791, + "z": 1.0 + }, + { + "x": 88.3922344322126, + "y": -146.2492212569481, + "z": 1.0 + }, + { + "x": 88.39229536054133, + "y": -146.65003206312798, + "z": 1.0 + }, + { + "x": 88.39235628887006, + "y": -147.05084286930781, + "z": 1.0 + }, + { + "x": 88.3924172171988, + "y": -147.4516536754877, + "z": 1.0 + }, + { + "x": 88.39249337760971, + "y": -147.95266718321255, + "z": 1.0 + }, + { + "x": 88.39258477010281, + "y": -148.55388339248236, + "z": 1.0 + }, + { + "x": 88.39266093051373, + "y": -149.0548969002072, + "z": 1.0 + }, + { + "x": 88.39273709092465, + "y": -149.55591040793178, + "z": 1.0 + }, + { + "x": 88.39281325133557, + "y": -150.05692391565663, + "z": 1.0 + }, + { + "x": 88.3928741796643, + "y": -150.45773472183646, + "z": 1.0 + }, + { + "x": 88.39293510799303, + "y": -150.85854552801663, + "z": 1.0 + }, + { + "x": 88.39299603632178, + "y": -151.25935633419624, + "z": 1.0 + }, + { + "x": 88.39308742881487, + "y": -151.86057254346605, + "z": 1.0 + }, + { + "x": 88.39316358922578, + "y": -152.36158605119118, + "z": 1.0 + }, + { + "x": 88.39322451755451, + "y": -152.76239685737107, + "z": 1.0 + }, + { + "x": 88.39328544588325, + "y": -153.16320766355062, + "z": 1.0 + }, + { + "x": 88.39337683837634, + "y": -153.76442387282077, + "z": 1.0 + }, + { + "x": 88.39346823086944, + "y": -154.36564008209058, + "z": 1.0 + }, + { + "x": 88.39352915919818, + "y": -154.7664508882704, + "z": 1.0 + }, + { + "x": 88.39362055169128, + "y": -155.36766709754022, + "z": 1.0 + }, + { + "x": 88.39371194418439, + "y": -155.9688833068098, + "z": 1.0 + }, + { + "x": 88.3937881045953, + "y": -156.4698968145346, + "z": 1.0 + }, + { + "x": 88.39386426500621, + "y": -156.97091032225944, + "z": 1.0 + }, + { + "x": 88.39392519333495, + "y": -157.37172112843933, + "z": 1.0 + }, + { + "x": 88.39401658582806, + "y": -157.97293733770914, + "z": 1.0 + }, + { + "x": 88.39409274623897, + "y": -158.47395084543427, + "z": 1.0 + }, + { + "x": 88.39418413873207, + "y": -159.0751670547038, + "z": 1.0 + }, + { + "x": 88.39426029914299, + "y": -159.57618056242865, + "z": 1.0 + }, + { + "x": 88.3943364595539, + "y": -160.0771940701535, + "z": 1.0 + }, + { + "x": 88.39439738788263, + "y": -160.4780048763334, + "z": 1.0 + }, + { + "x": 88.39445831621137, + "y": -160.87881568251328, + "z": 1.0 + }, + { + "x": 88.39454970870446, + "y": -161.48003189178337, + "z": 1.0 + }, + { + "x": 88.39462586911537, + "y": -161.98104539950816, + "z": 1.0 + }, + { + "x": 88.39471726160848, + "y": -162.58226160877803, + "z": 1.0 + }, + { + "x": 88.39480865410158, + "y": -163.18347781804755, + "z": 1.0 + }, + { + "x": 88.39490004659467, + "y": -163.78469402731764, + "z": 1.0 + }, + { + "x": 88.3949609749234, + "y": -164.18550483349753, + "z": 1.0 + }, + { + "x": 88.39502190325214, + "y": -164.58631563967737, + "z": 1.0 + }, + { + "x": 88.39509806366306, + "y": -165.08732914740222, + "z": 1.0 + }, + { + "x": 88.39517422407398, + "y": -165.58834265512678, + "z": 1.0 + }, + { + "x": 88.3952503844849, + "y": -166.0893561628519, + "z": 1.0 + }, + { + "x": 88.39532654489581, + "y": -166.59036967057676, + "z": 1.0 + }, + { + "x": 88.39540270530674, + "y": -167.09138317830133, + "z": 1.0 + }, + { + "x": 88.39547886571765, + "y": -167.59239668602618, + "z": 1.0 + }, + { + "x": 88.39555502612856, + "y": -168.09341019375103, + "z": 1.0 + }, + { + "x": 88.39563118653948, + "y": -168.59442370147616, + "z": 1.0 + }, + { + "x": 88.39570734695039, + "y": -169.09543720920095, + "z": 1.0 + }, + { + "x": 88.39579873944349, + "y": -169.69665341847082, + "z": 1.0 + }, + { + "x": 88.3958748998544, + "y": -170.19766692619567, + "z": 1.0 + }, + { + "x": 88.39595106026533, + "y": -170.69868043392017, + "z": 1.0 + }, + { + "x": 88.39602722067625, + "y": -171.1996939416453, + "z": 1.0 + }, + { + "x": 88.39611861316935, + "y": -171.80091015091483, + "z": 1.0 + }, + { + "x": 88.39619477358026, + "y": -172.30192365863996, + "z": 1.0 + }, + { + "x": 88.39625570190898, + "y": -172.70273446481986, + "z": 1.0 + }, + { + "x": 88.39634709440209, + "y": -173.30395067408966, + "z": 1.0 + }, + { + "x": 88.39643848689519, + "y": -173.9051668833592, + "z": 1.0 + }, + { + "x": 88.39651464730612, + "y": -174.40618039108404, + "z": 1.0 + }, + { + "x": 88.39657557563484, + "y": -174.80699119726415, + "z": 1.0 + }, + { + "x": 88.39665173604575, + "y": -175.308004704989, + "z": 1.0 + }, + { + "x": 88.39671266437449, + "y": -175.70881551116884, + "z": 1.0 + }, + { + "x": 88.3968040568676, + "y": -176.31003172043836, + "z": 1.0 + }, + { + "x": 88.39688021727851, + "y": -176.8110452281632, + "z": 1.0 + }, + { + "x": 88.39695637768943, + "y": -177.312058735888, + "z": 1.0 + }, + { + "x": 88.39701730601816, + "y": -177.71286954206818, + "z": 1.0 + }, + { + "x": 88.39710869851126, + "y": -178.3140857513377, + "z": 1.0 + }, + { + "x": 88.39720009100436, + "y": -178.91530196060745, + "z": 1.0 + }, + { + "x": 88.39729148349745, + "y": -179.51651816987754, + "z": 1.0 + }, + { + "x": 88.39738287599056, + "y": -180.11773437914735, + "z": 1.0 + }, + { + "x": 88.3974438043193, + "y": -180.5185451853269, + "z": 1.0 + }, + { + "x": 88.39750473264803, + "y": -180.9193559915068, + "z": 1.0 + }, + { + "x": 88.39756566097675, + "y": -181.3201667976869, + "z": 1.0 + }, + { + "x": 88.39764182138767, + "y": -181.82118030541176, + "z": 1.0 + }, + { + "x": 88.39770274971642, + "y": -182.2219911115913, + "z": 1.0 + }, + { + "x": 88.39776367804514, + "y": -182.62280191777148, + "z": 1.0 + }, + { + "x": 88.39782460637387, + "y": -183.02361272395103, + "z": 1.0 + }, + { + "x": 88.3979007667848, + "y": -183.5246262316756, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 2, + "heading": [ + -1.5706443141063071, + -1.5706443141062971, + -1.5706443141062971, + -1.57064431410631, + -1.5706443141063113, + -1.5706443141062971, + -1.5706443141064466, + -1.5706443141063546, + -1.5706443141062902, + -1.5706443141063045, + -1.5706443141063122, + -1.570644314106298, + -1.570644314106298, + -1.5706443141063335, + -1.570644314106306, + -1.5706443141062902, + -1.5706443141063158, + -1.5706443141063158, + -1.570644314106298, + -1.570644314106306, + -1.5706443141063045, + -1.5706443141063045, + -1.570644314106298, + -1.570644314106298, + -1.570644314106306, + -1.5706443141063158, + -1.570644314106298, + -1.570644314106298, + -1.5706443141063176, + -1.570644314106306, + -1.570644314106298, + -1.5706443141063122, + -1.57064431410631, + -1.570644314106298, + -1.570644314106298, + -1.570644314106298, + -1.5706443141063045, + -1.5706443141063122, + -1.570644314106306, + -1.570644314106298, + -1.5706443141063047, + -1.5706443141063045, + -1.5706443141063045, + -1.5706443141063122, + -1.570644314106306, + -1.570644314106298, + -1.5706443141063122, + -1.5706443141063176, + -1.5706443141063045, + -1.570644314106298, + -1.57064431410631, + -1.5706443141063122, + -1.570644314106298, + -1.570644314106306, + -1.570644314106298, + -1.570644314106298, + -1.5706443141063122, + -1.570644314106298, + -1.570644314106298, + -1.5706443141063122, + -1.5706443141063122, + -1.5706443141063122, + -1.5706443141063045, + -1.5706443141063045, + -1.570644314106298, + -1.570644314106298, + -1.5706443141063045, + -1.5706443141063045, + -1.5706443141063218, + -1.5706443141063122, + -1.570644314106298, + -1.5706443141062916, + -1.570644314106306, + -1.5706443141063218, + -1.570644314106306, + -1.570644314106298, + -1.5706443141063045, + -1.570644314106298, + -1.570644314106306, + -1.5706443141063122, + -1.570644314106298, + -1.57064431410631, + -1.57064431410631, + -1.570644314106298, + -1.570644314106298, + -1.5706443141063158, + -1.5706443141063218, + -1.5706443141062902, + -1.570644314106298, + -1.5706443141063158, + -1.57064431410629 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.0007658494773465918, + "y": -5.0380627992535665 + }, + { + "x": 0.001684868850162502, + "y": -11.08373815835705 + }, + { + "x": 0.0016848688500203934, + "y": -11.08373815835705 + }, + { + "x": 0.001531698954551075, + "y": -10.076125598506565 + }, + { + "x": 0.0007658494773465918, + "y": -5.038062799252998 + }, + { + "x": 0.0006097340981625621, + "y": -4.011073678671551 + }, + { + "x": 0.001219017385665211, + "y": -8.019181740470458 + }, + { + "x": 0.0013708873966322699, + "y": -9.0182431390474 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169943727 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154496984 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123594972 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123597246 + }, + { + "x": 0.0012185665744368634, + "y": -8.01621612360293 + }, + { + "x": 0.0013708873964901613, + "y": -9.018243139044557 + }, + { + "x": 0.0013708873966322699, + "y": -9.0182431390474 + }, + { + "x": 0.001218566574578972, + "y": -8.016216123600657 + }, + { + "x": 0.001218566574578972, + "y": -8.016216123597246 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123597246 + }, + { + "x": 0.0013708873964901613, + "y": -9.0182431390474 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169946569 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169946569 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154494142 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154494142 + }, + { + "x": 0.0013708873964901613, + "y": -9.018243139046831 + }, + { + "x": 0.001218566574578972, + "y": -8.016216123600088 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123597815 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154494142 + }, + { + "x": 0.001675529040028323, + "y": -11.022297169949411 + }, + { + "x": 0.0013708873964901613, + "y": -9.018243139050242 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123594404 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154496984 + }, + { + "x": 0.0018278498619395123, + "y": -12.024324185399564 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154496416 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154496416 + }, + { + "x": 0.0018278498620816208, + "y": -12.02432418539388 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169943727 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154496416 + }, + { + "x": 0.0013708873964901613, + "y": -9.0182431390474 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154496984 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169949411 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169946569 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169943727 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154496984 + }, + { + "x": 0.0013708873964901613, + "y": -9.0182431390474 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123597815 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154499826 + }, + { + "x": 0.001675529040028323, + "y": -11.022297169948843 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169946569 + }, + { + "x": 0.0018278498620816208, + "y": -12.02432418539388 + }, + { + "x": 0.0018278498619395123, + "y": -12.024324185396154 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154499826 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123597246 + }, + { + "x": 0.0013708873964901613, + "y": -9.018243139046831 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154494142 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154496984 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154499826 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154494142 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154494142 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154496984 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154499826 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154499258 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169946569 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169947137 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154493574 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154496416 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169946569 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169946569 + }, + { + "x": 0.0013708873963480528, + "y": -9.018243139050242 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154496984 + }, + { + "x": 0.0018278498620816208, + "y": -12.024324185393311 + }, + { + "x": 0.00167552904031254, + "y": -11.022297169943727 + }, + { + "x": 0.0013708873964901613, + "y": -9.018243139049673 + }, + { + "x": 0.0013708873963480528, + "y": -9.018243139049673 + }, + { + "x": 0.0013708873964901613, + "y": -9.018243139046831 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154493574 + }, + { + "x": 0.0016755290401704315, + "y": -11.022297169943727 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154496416 + }, + { + "x": 0.0013708873964901613, + "y": -9.018243139049673 + }, + { + "x": 0.0015232082182592421, + "y": -10.020270154496984 + }, + { + "x": 0.0018278498620816208, + "y": -12.024324185392743 + }, + { + "x": 0.0018278498619395123, + "y": -12.024324185398427 + }, + { + "x": 0.0018278498619395123, + "y": -12.024324185398996 + }, + { + "x": 0.0015232082184013507, + "y": -10.020270154493574 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123594404 + }, + { + "x": 0.001218566574578972, + "y": -8.016216123600088 + }, + { + "x": 0.0013708873963480528, + "y": -9.018243139049673 + }, + { + "x": 0.0013708873966322699, + "y": -9.018243139043989 + }, + { + "x": 0.0012185665747210805, + "y": -8.016216123597246 + }, + { + "x": 0.001218566574578972, + "y": -8.016216123597246 + }, + { + "x": 0.0013708873966322699, + "y": -9.018243139041147 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 88.3979007667848, + "y": -183.5246262316756, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 92.17738470449606, + "y": -5.543033931552639, + "z": 1.0 + }, + { + "x": 92.02893835203474, + "y": -4.9254451369259495, + "z": 1.0 + }, + { + "x": 91.83953272702882, + "y": -4.319165007001053, + "z": 1.0 + }, + { + "x": 91.55938507168747, + "y": -3.6103693432412705, + "z": 1.0 + }, + { + "x": 91.2232886141611, + "y": -2.92632261465836, + "z": 1.0 + }, + { + "x": 90.90160151268881, + "y": -2.3777725998696635, + "z": 1.0 + }, + { + "x": 90.46849895465499, + "y": -1.750514037920517, + "z": 1.0 + }, + { + "x": 89.98654266873672, + "y": -1.1600946235224388, + "z": 1.0 + }, + { + "x": 89.54979571361987, + "y": -0.6989172678760334, + "z": 1.0 + }, + { + "x": 89.08343174905256, + "y": -0.2681208996727509, + "z": 1.0 + }, + { + "x": 88.58937810804395, + "y": 0.13059820714422288, + "z": 1.0 + }, + { + "x": 87.96305420654113, + "y": 0.5642434078047397, + "z": 1.0 + }, + { + "x": 87.30318887392193, + "y": 0.9465681376052837, + "z": 1.0 + }, + { + "x": 86.73196755078408, + "y": 1.2236474556272232, + "z": 1.0 + }, + { + "x": 86.0240164078902, + "y": 1.5049320036980738, + "z": 1.0 + }, + { + "x": 85.41840054260388, + "y": 1.6954362312776485, + "z": 1.0 + }, + { + "x": 84.67690593435475, + "y": 1.8700932166469466, + "z": 1.0 + }, + { + "x": 83.923806202719, + "y": 1.9848547149246605, + "z": 1.0 + }, + { + "x": 83.41683773555708, + "y": 2.027617963707917, + "z": 1.0 + }, + { + "x": 82.90914004207372, + "y": 2.043181161697282, + "z": 1.0 + }, + { + "x": 82.48887153292773, + "y": 2.0432892540786893, + "z": 1.0 + }, + { + "x": 81.88708783985336, + "y": 2.043093126861388, + "z": 1.0 + }, + { + "x": 81.28530414677901, + "y": 2.0428969996440856, + "z": 1.0 + }, + { + "x": 80.88411501806277, + "y": 2.0427662481658846, + "z": 1.0 + }, + { + "x": 80.38262860716748, + "y": 2.042602808818133, + "z": 1.0 + }, + { + "x": 79.88114219627218, + "y": 2.0424393694703813, + "z": 1.0 + }, + { + "x": 79.37965578537688, + "y": 2.0422759301226296, + "z": 1.0 + }, + { + "x": 78.8789939970618, + "y": 2.0421127595274697, + "z": 1.0 + }, + { + "x": 78.37862787552477, + "y": 2.0419496852930257, + "z": 1.0 + }, + { + "x": 77.77818852968032, + "y": 2.0417539962116917, + "z": 1.0 + }, + { + "x": 77.2778224081433, + "y": 2.0415909219772477, + "z": 1.0 + }, + { + "x": 76.77745628660625, + "y": 2.0414278477428027, + "z": 1.0 + }, + { + "x": 76.27709016506921, + "y": 2.0412647735083587, + "z": 1.0 + }, + { + "x": 75.77672404353218, + "y": 2.0411016992739137, + "z": 1.0 + }, + { + "x": 75.37643114630255, + "y": 2.0409712398863586, + "z": 1.0 + }, + { + "x": 74.97613824907293, + "y": 2.0408407804988027, + "z": 1.0 + }, + { + "x": 74.47577212753589, + "y": 2.0406777062643586, + "z": 1.0 + }, + { + "x": 73.97540600599886, + "y": 2.0405146320299137, + "z": 1.0 + }, + { + "x": 73.57511310876923, + "y": 2.0403841726423577, + "z": 1.0 + }, + { + "x": 73.1748202115396, + "y": 2.0402537132548026, + "z": 1.0 + }, + { + "x": 72.67445409000257, + "y": 2.0400906390203577, + "z": 1.0 + }, + { + "x": 72.27416119277294, + "y": 2.0399601796328026, + "z": 1.0 + }, + { + "x": 71.7737950712359, + "y": 2.039797105398358, + "z": 1.0 + }, + { + "x": 71.37350217400628, + "y": 2.0396666460108026, + "z": 1.0 + }, + { + "x": 70.87313605246923, + "y": 2.0395035717763577, + "z": 1.0 + }, + { + "x": 70.37276993093221, + "y": 2.0393404975419136, + "z": 1.0 + }, + { + "x": 69.77233058508776, + "y": 2.0391448084605797, + "z": 1.0 + }, + { + "x": 69.17189123924332, + "y": 2.0389491193792466, + "z": 1.0 + }, + { + "x": 68.57145189339887, + "y": 2.0387534302979127, + "z": 1.0 + }, + { + "x": 67.97101254755444, + "y": 2.0385577412165796, + "z": 1.0 + }, + { + "x": 67.4706464260174, + "y": 2.0383946669821356, + "z": 1.0 + }, + { + "x": 67.07035352878778, + "y": 2.0382642075945796, + "z": 1.0 + }, + { + "x": 66.46991418294333, + "y": 2.0380685185132466, + "z": 1.0 + }, + { + "x": 65.9695480614063, + "y": 2.0379054442788016, + "z": 1.0 + }, + { + "x": 65.36910871556185, + "y": 2.0377097551974686, + "z": 1.0 + }, + { + "x": 64.96881581833222, + "y": 2.037579295809913, + "z": 1.0 + }, + { + "x": 64.36837647248778, + "y": 2.0373836067285795, + "z": 1.0 + }, + { + "x": 63.96808357525816, + "y": 2.0372531473410236, + "z": 1.0 + }, + { + "x": 63.36764422941371, + "y": 2.0370574582596905, + "z": 1.0 + }, + { + "x": 62.76720488356926, + "y": 2.0368617691783566, + "z": 1.0 + }, + { + "x": 62.36691198633963, + "y": 2.0367313097908015, + "z": 1.0 + }, + { + "x": 61.96661908911, + "y": 2.0366008504032456, + "z": 1.0 + }, + { + "x": 61.56632619188035, + "y": 2.0364703910156896, + "z": 1.0 + }, + { + "x": 61.16603329465072, + "y": 2.0363399316281345, + "z": 1.0 + }, + { + "x": 60.665667173113675, + "y": 2.0361768573936896, + "z": 1.0 + }, + { + "x": 60.065227827269226, + "y": 2.0359811683123565, + "z": 1.0 + }, + { + "x": 59.464788481424776, + "y": 2.0357854792310235, + "z": 1.0 + }, + { + "x": 58.964422359887735, + "y": 2.0356224049965785, + "z": 1.0 + }, + { + "x": 58.36398301404327, + "y": 2.0354267159152455, + "z": 1.0 + }, + { + "x": 57.76354366819882, + "y": 2.0352310268339115, + "z": 1.0 + }, + { + "x": 57.26317754666178, + "y": 2.0350679525994675, + "z": 1.0 + }, + { + "x": 56.66273820081733, + "y": 2.0348722635181336, + "z": 1.0 + }, + { + "x": 56.062298854972866, + "y": 2.0346765744368005, + "z": 1.0 + }, + { + "x": 55.66200595774323, + "y": 2.0345461150492445, + "z": 1.0 + }, + { + "x": 55.06156661189878, + "y": 2.0343504259679115, + "z": 1.0 + }, + { + "x": 54.56120049036174, + "y": 2.0341873517334665, + "z": 1.0 + }, + { + "x": 53.96076114451729, + "y": 2.0339916626521335, + "z": 1.0 + }, + { + "x": 53.46039502298024, + "y": 2.0338285884176894, + "z": 1.0 + }, + { + "x": 53.0601021257506, + "y": 2.0336981290301335, + "z": 1.0 + }, + { + "x": 52.65980922852097, + "y": 2.0335676696425775, + "z": 1.0 + }, + { + "x": 52.25951633129134, + "y": 2.0334372102550224, + "z": 1.0 + }, + { + "x": 51.859223434061704, + "y": 2.0333067508674665, + "z": 1.0 + }, + { + "x": 51.258784088217254, + "y": 2.0331110617861334, + "z": 1.0 + }, + { + "x": 50.65834474237279, + "y": 2.0329153727047995, + "z": 1.0 + }, + { + "x": 50.25805184514316, + "y": 2.0327849133172444, + "z": 1.0 + }, + { + "x": 49.65761249929871, + "y": 2.0325892242359105, + "z": 1.0 + }, + { + "x": 49.257319602069074, + "y": 2.0324587648483554, + "z": 1.0 + }, + { + "x": 48.85702670483944, + "y": 2.0323283054607995, + "z": 1.0 + }, + { + "x": 48.25658735899499, + "y": 2.0321326163794664, + "z": 1.0 + }, + { + "x": 47.65614801315053, + "y": 2.0319369272981325, + "z": 1.0 + }, + { + "x": 47.155781891613486, + "y": 2.0317738530636884, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 3, + "heading": [ + -1.8948221592538605, + 1.8066858210761172, + 1.8401406999963243, + 1.913741599788107, + 1.9873425501355715, + 2.0610054059259815, + 2.1414792235087416, + 2.215243017273065, + 2.2888481200291793, + 2.362398683948051, + 2.4291969436707634, + 2.5026304445853182, + 2.5762559620189145, + 2.649850799449807, + 2.730018525577581, + 2.7967754706062085, + 2.8768838068652123, + 2.950316514123116, + 3.017224983051775, + 3.0841724570389077, + 3.124706477370499, + -3.141506518228522, + -3.141266743777655, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776465, + -3.141266743777647, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776536, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.141266743777654, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776554, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.141266743777654, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776536, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776554, + -3.1412667437776545, + -3.1412667437776536, + -3.141266743777655, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -1.4844635246132043, + "y": 6.175887946266894 + }, + { + "x": -3.378519774672384, + "y": 12.23868924551586 + }, + { + "x": -4.695532803472702, + "y": 13.15075793684679 + }, + { + "x": -6.162441128677187, + "y": 13.928423923426928 + }, + { + "x": -6.5778355899865915, + "y": 12.32596743371607 + }, + { + "x": -7.547896595061161, + "y": 11.758085767378429 + }, + { + "x": -9.15058843952096, + "y": 12.176779763472247 + }, + { + "x": -9.18703241035118, + "y": 10.515967700444836 + }, + { + "x": -9.031109196841527, + "y": 8.919737238496879 + }, + { + "x": -9.604176055759268, + "y": 8.295154750202562 + }, + { + "x": -11.203775425114344, + "y": 8.323643074774905 + }, + { + "x": -12.861892341220198, + "y": 8.159699304610607 + }, + { + "x": -12.310866557570534, + "y": 6.5940404782248345 + }, + { + "x": -12.791724660317243, + "y": 5.5836386609279005 + }, + { + "x": -13.135670081802004, + "y": 4.717887756504253 + }, + { + "x": -13.471104735354515, + "y": 3.6516121294887283 + }, + { + "x": -14.945943398848698, + "y": 2.8941848364701195 + }, + { + "x": -12.600681987976685, + "y": 1.575247470609702 + }, + { + "x": -10.14666160645291, + "y": 0.5832644677262144 + }, + { + "x": -9.279662026293494, + "y": 0.15671290370772528 + }, + { + "x": -10.220522022203511, + "y": -0.0008803483589403527 + }, + { + "x": -12.035673861487197, + "y": -0.003922544346037782 + }, + { + "x": -10.029728217905927, + "y": -0.003268786955032965 + }, + { + "x": -9.026755396115362, + "y": -0.002941908259526116 + }, + { + "x": -10.029728217905927, + "y": -0.003268786955032965 + }, + { + "x": -10.029728217905927, + "y": -0.003268786955032965 + }, + { + "x": -10.021481992103816, + "y": -0.0032660994291155987 + }, + { + "x": -10.010279098521124, + "y": -0.0032624482960397216 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -8.00585794459252, + "y": -0.002609187751110298 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -9.006590187666603, + "y": -0.002935336220009077 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362199957544 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200046362 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916888852, + "y": -0.003913781626669888 + }, + { + "x": -12.008786916888852, + "y": -0.003913781626669888 + }, + { + "x": -12.008786916888852, + "y": -0.003913781626669888 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577711085 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688885652 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740544, + "y": -0.0032614846888945337 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -12.008786916888994, + "y": -0.003913781626669888 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -8.005857944592805, + "y": -0.0026091877511191797 + }, + { + "x": -8.005857944592805, + "y": -0.002609187751110298 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362200001953 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916888994, + "y": -0.003913781626661006 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673815053, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916889136, + "y": -0.003913781626669888 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916889136, + "y": -0.003913781626669888 + }, + { + "x": -10.00732243074097, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673815053, + "y": -0.0035876331577711085 + }, + { + "x": -9.006590187666887, + "y": -0.0029353362200001953 + }, + { + "x": -8.005857944592663, + "y": -0.0026091877511191797 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -12.008786916889136, + "y": -0.003913781626669888 + }, + { + "x": -10.00732243074097, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -12.008786916889136, + "y": -0.003913781626669888 + }, + { + "x": -11.008054673815053, + "y": -0.0035876331577799903 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 47.155781891613486, + "y": 2.0317738530636884, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 161.64683250352334, + "y": 2.0437492128140557, + "z": 1.0 + }, + { + "x": 161.1442238107338, + "y": 2.0438028558703465, + "z": 1.0 + }, + { + "x": 160.64161511794427, + "y": 2.0438564989266377, + "z": 1.0 + }, + { + "x": 160.23952816371266, + "y": 2.0438994133716704, + "z": 1.0 + }, + { + "x": 159.63639773236523, + "y": 2.0439637850392196, + "z": 1.0 + }, + { + "x": 159.13378903957567, + "y": 2.0440174280955103, + "z": 1.0 + }, + { + "x": 158.53065860822824, + "y": 2.0440817997630596, + "z": 1.0 + }, + { + "x": 157.92752817688083, + "y": 2.0441461714306093, + "z": 1.0 + }, + { + "x": 157.5254412226492, + "y": 2.0441890858756424, + "z": 1.0 + }, + { + "x": 157.12335426841756, + "y": 2.044232000320675, + "z": 1.0 + }, + { + "x": 156.72126731418595, + "y": 2.0442749147657078, + "z": 1.0 + }, + { + "x": 156.2186586213964, + "y": 2.0443285578219985, + "z": 1.0 + }, + { + "x": 155.8165716671648, + "y": 2.044371472267032, + "z": 1.0 + }, + { + "x": 155.41448471293316, + "y": 2.0444143867120648, + "z": 1.0 + }, + { + "x": 154.91187602014364, + "y": 2.044468029768356, + "z": 1.0 + }, + { + "x": 154.40926732735412, + "y": 2.0445216728246467, + "z": 1.0 + }, + { + "x": 153.90665863456456, + "y": 2.0445753158809374, + "z": 1.0 + }, + { + "x": 153.50457168033296, + "y": 2.044618230325971, + "z": 1.0 + }, + { + "x": 152.90144124898552, + "y": 2.0446826019935203, + "z": 1.0 + }, + { + "x": 152.29831081763808, + "y": 2.0447469736610695, + "z": 1.0 + }, + { + "x": 151.89622386340645, + "y": 2.044789888106102, + "z": 1.0 + }, + { + "x": 151.293093432059, + "y": 2.0448542597736514, + "z": 1.0 + }, + { + "x": 150.79048473926946, + "y": 2.0449079028299426, + "z": 1.0 + }, + { + "x": 150.18735430792202, + "y": 2.044972274497492, + "z": 1.0 + }, + { + "x": 149.6847456151325, + "y": 2.045025917553783, + "z": 1.0 + }, + { + "x": 149.28265866090086, + "y": 2.0450688319988157, + "z": 1.0 + }, + { + "x": 148.6795282295534, + "y": 2.0451332036663645, + "z": 1.0 + }, + { + "x": 148.2774412753218, + "y": 2.045176118111398, + "z": 1.0 + }, + { + "x": 147.87535432109016, + "y": 2.045219032556431, + "z": 1.0 + }, + { + "x": 147.2722238897427, + "y": 2.04528340422398, + "z": 1.0 + }, + { + "x": 146.87013693551108, + "y": 2.0453263186690127, + "z": 1.0 + }, + { + "x": 146.36752824272153, + "y": 2.045379961725304, + "z": 1.0 + }, + { + "x": 145.9654412884899, + "y": 2.0454228761703366, + "z": 1.0 + }, + { + "x": 145.46283259570038, + "y": 2.0454765192266278, + "z": 1.0 + }, + { + "x": 145.06074564146874, + "y": 2.045519433671661, + "z": 1.0 + }, + { + "x": 144.96022390291083, + "y": 2.045530162282919, + "z": 1.0 + }, + { + "x": 144.5595603980998, + "y": 2.0455729248042416, + "z": 1.0 + }, + { + "x": 144.05873101708605, + "y": 2.045626377955895, + "z": 1.0 + }, + { + "x": 143.55790163607227, + "y": 2.045679831107548, + "z": 1.0 + }, + { + "x": 143.15723813126127, + "y": 2.0457225936288705, + "z": 1.0 + }, + { + "x": 142.75657462645026, + "y": 2.045765356150193, + "z": 1.0 + }, + { + "x": 142.25574524543651, + "y": 2.0458188093018466, + "z": 1.0 + }, + { + "x": 141.8550817406255, + "y": 2.045861571823169, + "z": 1.0 + }, + { + "x": 141.4544182358145, + "y": 2.045904334344492, + "z": 1.0 + }, + { + "x": 140.95358885480073, + "y": 2.045957787496145, + "z": 1.0 + }, + { + "x": 140.35259359758422, + "y": 2.0460219312781294, + "z": 1.0 + }, + { + "x": 139.85176421657047, + "y": 2.0460753844297823, + "z": 1.0 + }, + { + "x": 139.45110071175947, + "y": 2.046118146951105, + "z": 1.0 + }, + { + "x": 138.85010545454296, + "y": 2.046182290733089, + "z": 1.0 + }, + { + "x": 138.44944194973195, + "y": 2.0462250532544117, + "z": 1.0 + }, + { + "x": 138.04877844492094, + "y": 2.0462678157757344, + "z": 1.0 + }, + { + "x": 137.44778318770443, + "y": 2.046331959557718, + "z": 1.0 + }, + { + "x": 136.9469538066907, + "y": 2.0463854127093715, + "z": 1.0 + }, + { + "x": 136.54629030187968, + "y": 2.046428175230694, + "z": 1.0 + }, + { + "x": 136.14562679706867, + "y": 2.0464709377520167, + "z": 1.0 + }, + { + "x": 135.74496329225767, + "y": 2.0465137002733393, + "z": 1.0 + }, + { + "x": 135.34429978744666, + "y": 2.046556462794662, + "z": 1.0 + }, + { + "x": 134.94363628263565, + "y": 2.0465992253159846, + "z": 1.0 + }, + { + "x": 134.34264102541917, + "y": 2.0466633690979688, + "z": 1.0 + }, + { + "x": 133.74164576820266, + "y": 2.0467275128799525, + "z": 1.0 + }, + { + "x": 133.34098226339165, + "y": 2.046770275401275, + "z": 1.0 + }, + { + "x": 132.8401528823779, + "y": 2.0468237285529285, + "z": 1.0 + }, + { + "x": 132.2391576251614, + "y": 2.0468878723349127, + "z": 1.0 + }, + { + "x": 131.73832824414762, + "y": 2.0469413254865656, + "z": 1.0 + }, + { + "x": 131.23749886313388, + "y": 2.046994778638219, + "z": 1.0 + }, + { + "x": 130.73666948212013, + "y": 2.0470482317898724, + "z": 1.0 + }, + { + "x": 130.33600597730913, + "y": 2.047090994311195, + "z": 1.0 + }, + { + "x": 129.83517659629538, + "y": 2.0471444474628484, + "z": 1.0 + }, + { + "x": 129.43451309148438, + "y": 2.047187209984171, + "z": 1.0 + }, + { + "x": 129.03384958667337, + "y": 2.0472299725054937, + "z": 1.0 + }, + { + "x": 128.5330202056596, + "y": 2.0472834256571466, + "z": 1.0 + }, + { + "x": 128.1323567008486, + "y": 2.0473261881784692, + "z": 1.0 + }, + { + "x": 127.53136144363208, + "y": 2.0473903319604534, + "z": 1.0 + }, + { + "x": 127.13069793882107, + "y": 2.047433094481776, + "z": 1.0 + }, + { + "x": 126.73003443401007, + "y": 2.0474758570030986, + "z": 1.0 + }, + { + "x": 126.2292050529963, + "y": 2.047529310154752, + "z": 1.0 + }, + { + "x": 125.82854154818529, + "y": 2.0475720726760747, + "z": 1.0 + }, + { + "x": 125.32771216717153, + "y": 2.047625525827728, + "z": 1.0 + }, + { + "x": 124.82688278615777, + "y": 2.047678978979381, + "z": 1.0 + }, + { + "x": 124.42621928134676, + "y": 2.0477217415007036, + "z": 1.0 + }, + { + "x": 123.82522402413024, + "y": 2.047785885282688, + "z": 1.0 + }, + { + "x": 123.22422876691373, + "y": 2.0478500290646715, + "z": 1.0 + }, + { + "x": 122.72339938589997, + "y": 2.047903482216325, + "z": 1.0 + }, + { + "x": 122.12240412868344, + "y": 2.047967625998309, + "z": 1.0 + }, + { + "x": 121.72174062387244, + "y": 2.0480103885196317, + "z": 1.0 + }, + { + "x": 121.22091124285868, + "y": 2.0480638416712846, + "z": 1.0 + }, + { + "x": 120.61991598564217, + "y": 2.048127985453269, + "z": 1.0 + }, + { + "x": 120.21925248083116, + "y": 2.0481707479745914, + "z": 1.0 + }, + { + "x": 119.61825722361463, + "y": 2.048234891756575, + "z": 1.0 + }, + { + "x": 119.11742784260088, + "y": 2.0482883449082285, + "z": 1.0 + }, + { + "x": 118.51643258538436, + "y": 2.0483524886902127, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 4, + "heading": [ + 3.1414859243253446, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.141485924325344, + 3.1414859243253432, + 3.141485924325343, + 3.1414859243253437, + 3.141485924325344, + 3.141485924325344, + 3.1414859243253432, + 3.141485924325343, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253432, + 3.141485924325343, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253432, + 3.141485924325343, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253432, + 3.1414859243253432, + 3.141485924325344, + 3.1414859243253437, + 3.141485924325344, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253432, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253432, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253432, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253437, + 3.1414859243253432 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -5.026086927895506, + "y": 0.0005364305629074195 + }, + { + "x": -10.052173855790727, + "y": 0.00107286112581928 + }, + { + "x": -9.046956470211285, + "y": 0.0009655750132386842 + }, + { + "x": -10.052173855790443, + "y": 0.00107286112581928 + }, + { + "x": -11.057391241369885, + "y": 0.0011801472383998757 + }, + { + "x": -11.057391241369885, + "y": 0.0011801472383998757 + }, + { + "x": -12.062608626948474, + "y": 0.0012874333509893532 + }, + { + "x": -10.052173855790443, + "y": 0.0010728611258281617 + }, + { + "x": -8.041739084632695, + "y": 0.0008582889006580885 + }, + { + "x": -8.041739084632411, + "y": 0.0008582889006536476 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132342433 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132431251 + }, + { + "x": -8.041739084632411, + "y": 0.0008582889006625294 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132386842 + }, + { + "x": -10.052173855790443, + "y": 0.00107286112581928 + }, + { + "x": -10.052173855790727, + "y": 0.001072861125814839 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132431251 + }, + { + "x": -10.052173855790443, + "y": 0.0010728611258281617 + }, + { + "x": -12.062608626948759, + "y": 0.0012874333509849123 + }, + { + "x": -10.052173855790727, + "y": 0.00107286112581928 + }, + { + "x": -10.052173855790727, + "y": 0.00107286112581928 + }, + { + "x": -11.057391241369885, + "y": 0.0011801472384043166 + }, + { + "x": -11.057391241369885, + "y": 0.0011801472384043166 + }, + { + "x": -11.0573912413696, + "y": 0.0011801472384043166 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132386842 + }, + { + "x": -10.052173855791011, + "y": 0.001072861125814839 + }, + { + "x": -10.052173855790727, + "y": 0.0010728611258237208 + }, + { + "x": -8.041739084632411, + "y": 0.0008582889006625294 + }, + { + "x": -10.052173855791011, + "y": 0.00107286112581928 + }, + { + "x": -10.052173855790727, + "y": 0.00107286112581928 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132386842 + }, + { + "x": -9.046956470211853, + "y": 0.0009655750132386842 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132386842 + }, + { + "x": -9.046956470211569, + "y": 0.0009655750132431251 + }, + { + "x": -5.026086927895506, + "y": 0.0005364305629118604 + }, + { + "x": -5.011852433689512, + "y": 0.0005349113258068527 + }, + { + "x": -9.0149288582478, + "y": 0.0009621567297601885 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033063422 + }, + { + "x": -9.0149288582478, + "y": 0.0009621567297557476 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297601885 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297601885 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -9.0149288582478, + "y": 0.0009621567297601885 + }, + { + "x": -11.018246382302834, + "y": 0.0011759693363755375 + }, + { + "x": -11.01824638230255, + "y": 0.0011759693363710966 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297557476 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033067863 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033067863 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033063422 + }, + { + "x": -11.01824638230255, + "y": 0.0011759693363710966 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297601885 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -10.01658762027489, + "y": 0.001069063033067863 + }, + { + "x": -12.019905144329925, + "y": 0.001282875639678771 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033063422 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297601885 + }, + { + "x": -11.01824638230255, + "y": 0.0011759693363755375 + }, + { + "x": -11.018246382302834, + "y": 0.0011759693363710966 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033063422 + }, + { + "x": -10.01658762027489, + "y": 0.001069063033067863 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297601885 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297601885 + }, + { + "x": -9.014928858247515, + "y": 0.0009621567297601885 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -9.0149288582478, + "y": 0.0009621567297557476 + }, + { + "x": -9.0149288582478, + "y": 0.0009621567297557476 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033067863 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033067863 + }, + { + "x": -8.01327009622014, + "y": 0.000855250426452514 + }, + { + "x": -9.014928858247657, + "y": 0.0009621567297601885 + }, + { + "x": -9.0149288582478, + "y": 0.0009621567297601885 + }, + { + "x": -9.0149288582478, + "y": 0.0009621567297601885 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033063422 + }, + { + "x": -9.014928858247657, + "y": 0.0009621567297557476 + }, + { + "x": -10.016587620275317, + "y": 0.001069063033067863 + }, + { + "x": -12.019905144330352, + "y": 0.001282875639678771 + }, + { + "x": -11.018246382302692, + "y": 0.0011759693363710966 + }, + { + "x": -11.018246382302834, + "y": 0.0011759693363755375 + }, + { + "x": -10.016587620275317, + "y": 0.001069063033067863 + }, + { + "x": -9.014928858247657, + "y": 0.0009621567297557476 + }, + { + "x": -11.018246382302692, + "y": 0.0011759693363710966 + }, + { + "x": -10.016587620275175, + "y": 0.001069063033067863 + }, + { + "x": -10.016587620275317, + "y": 0.001069063033063422 + }, + { + "x": -11.018246382302834, + "y": 0.0011759693363710966 + }, + { + "x": -11.018246382302692, + "y": 0.0011759693363755375 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 118.51643258538436, + "y": 2.0483524886902127, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 166.26698125656839, + "y": -59.490756629877396, + "z": 1.0 + }, + { + "x": 166.7687912075939, + "y": -59.49069548293693, + "z": 1.0 + }, + { + "x": 167.1702391684144, + "y": -59.49064656538455, + "z": 1.0 + }, + { + "x": 167.67052415520067, + "y": -59.490585604265235, + "z": 1.0 + }, + { + "x": 168.2708661393441, + "y": -59.49051245092204, + "z": 1.0 + }, + { + "x": 168.67109412877318, + "y": -59.490463682026586, + "z": 1.0 + }, + { + "x": 169.0713221182021, + "y": -59.49041491313112, + "z": 1.0 + }, + { + "x": 169.5716071049883, + "y": -59.49035395201179, + "z": 1.0 + }, + { + "x": 170.1719490891316, + "y": -59.49028079866859, + "z": 1.0 + }, + { + "x": 170.57217707856051, + "y": -59.49023202977314, + "z": 1.0 + }, + { + "x": 171.17251906270394, + "y": -59.490158876429945, + "z": 1.0 + }, + { + "x": 171.5727470521329, + "y": -59.49011010753449, + "z": 1.0 + }, + { + "x": 171.97297504156188, + "y": -59.49006133863902, + "z": 1.0 + }, + { + "x": 172.57331702570542, + "y": -59.48998818529584, + "z": 1.0 + }, + { + "x": 173.17365900984873, + "y": -59.48991503195265, + "z": 1.0 + }, + { + "x": 173.57388699927765, + "y": -59.48986626305718, + "z": 1.0 + }, + { + "x": 174.17422898342107, + "y": -59.489793109713986, + "z": 1.0 + }, + { + "x": 174.57445697285004, + "y": -59.48974434081853, + "z": 1.0 + }, + { + "x": 174.974684962279, + "y": -59.489695571923065, + "z": 1.0 + }, + { + "x": 175.57502694642244, + "y": -59.489622418579884, + "z": 1.0 + }, + { + "x": 176.07531193320875, + "y": -59.48956145746055, + "z": 1.0 + }, + { + "x": 176.67565391735207, + "y": -59.48948830411736, + "z": 1.0 + }, + { + "x": 177.1759389041382, + "y": -59.48942734299804, + "z": 1.0 + }, + { + "x": 177.57616689356718, + "y": -59.489378574102574, + "z": 1.0 + }, + { + "x": 178.1765088777106, + "y": -59.48930542075939, + "z": 1.0 + }, + { + "x": 178.57673686713957, + "y": -59.489256651863926, + "z": 1.0 + }, + { + "x": 178.9769648565685, + "y": -59.48920788296846, + "z": 1.0 + }, + { + "x": 179.47724984335468, + "y": -59.48914692184914, + "z": 1.0 + }, + { + "x": 180.07759182749828, + "y": -59.489073768505946, + "z": 1.0 + }, + { + "x": 180.5778768142843, + "y": -59.489012807386615, + "z": 1.0 + }, + { + "x": 181.0781618010705, + "y": -59.4889518462673, + "z": 1.0 + }, + { + "x": 181.67850378521393, + "y": -59.488878692924104, + "z": 1.0 + }, + { + "x": 182.27884576935747, + "y": -59.48880553958092, + "z": 1.0 + }, + { + "x": 182.8791877535008, + "y": -59.48873238623773, + "z": 1.0 + }, + { + "x": 183.3794727402871, + "y": -59.4886714251184, + "z": 1.0 + }, + { + "x": 183.97981472443053, + "y": -59.4885982717752, + "z": 1.0 + }, + { + "x": 184.38004271385938, + "y": -59.48854950287975, + "z": 1.0 + }, + { + "x": 184.9803846980028, + "y": -59.488476349536555, + "z": 1.0 + }, + { + "x": 185.58072668214623, + "y": -59.48840319619336, + "z": 1.0 + }, + { + "x": 186.08101166893238, + "y": -59.48834223507404, + "z": 1.0 + }, + { + "x": 186.48123965836135, + "y": -59.488293466178575, + "z": 1.0 + }, + { + "x": 187.08158164250494, + "y": -59.488220312835395, + "z": 1.0 + }, + { + "x": 187.6819236266482, + "y": -59.4881471594922, + "z": 1.0 + }, + { + "x": 188.08215161607728, + "y": -59.48809839059673, + "z": 1.0 + }, + { + "x": 188.6824936002206, + "y": -59.48802523725354, + "z": 1.0 + }, + { + "x": 189.0827215896495, + "y": -59.487976468358085, + "z": 1.0 + }, + { + "x": 189.48294957907848, + "y": -59.48792769946262, + "z": 1.0 + }, + { + "x": 190.0832915632219, + "y": -59.487854546119436, + "z": 1.0 + }, + { + "x": 190.68363354736533, + "y": -59.48778139277624, + "z": 1.0 + }, + { + "x": 191.18391853415153, + "y": -59.48772043165691, + "z": 1.0 + }, + { + "x": 191.78426051829496, + "y": -59.48764727831373, + "z": 1.0 + }, + { + "x": 192.18448850772393, + "y": -59.48759850941826, + "z": 1.0 + }, + { + "x": 192.68477349451007, + "y": -59.48753754829893, + "z": 1.0 + }, + { + "x": 193.28511547865367, + "y": -59.48746439495575, + "z": 1.0 + }, + { + "x": 193.7854004654397, + "y": -59.48740343383642, + "z": 1.0 + }, + { + "x": 194.2856854522259, + "y": -59.48734247271709, + "z": 1.0 + }, + { + "x": 194.68591344165486, + "y": -59.487293703821635, + "z": 1.0 + }, + { + "x": 195.2862554257983, + "y": -59.48722055047844, + "z": 1.0 + }, + { + "x": 195.7865404125845, + "y": -59.48715958935912, + "z": 1.0 + }, + { + "x": 196.3868823967279, + "y": -59.48708643601593, + "z": 1.0 + }, + { + "x": 196.88716738351405, + "y": -59.4870254748966, + "z": 1.0 + }, + { + "x": 197.48750936765748, + "y": -59.4869523215534, + "z": 1.0 + }, + { + "x": 197.88773735708645, + "y": -59.48690355265795, + "z": 1.0 + }, + { + "x": 198.38802234387265, + "y": -59.48684259153862, + "z": 1.0 + }, + { + "x": 198.78825033330162, + "y": -59.486793822643165, + "z": 1.0 + }, + { + "x": 199.38859231744516, + "y": -59.48672066929997, + "z": 1.0 + }, + { + "x": 199.98893430158847, + "y": -59.486647515956776, + "z": 1.0 + }, + { + "x": 200.5892762857319, + "y": -59.486574362613595, + "z": 1.0 + }, + { + "x": 200.9895042751608, + "y": -59.48652559371813, + "z": 1.0 + }, + { + "x": 201.38973226458978, + "y": -59.486476824822674, + "z": 1.0 + }, + { + "x": 201.78996025401875, + "y": -59.486428055927206, + "z": 1.0 + }, + { + "x": 202.29024524080495, + "y": -59.486367094807875, + "z": 1.0 + }, + { + "x": 202.89058722494838, + "y": -59.486293941464695, + "z": 1.0 + }, + { + "x": 203.39087221173452, + "y": -59.48623298034536, + "z": 1.0 + }, + { + "x": 203.89115719852072, + "y": -59.48617201922603, + "z": 1.0 + }, + { + "x": 204.2913851879497, + "y": -59.48612325033058, + "z": 1.0 + }, + { + "x": 204.79167017473588, + "y": -59.48606228921125, + "z": 1.0 + }, + { + "x": 205.1918981641648, + "y": -59.486013520315794, + "z": 1.0 + }, + { + "x": 205.692183150951, + "y": -59.48595255919646, + "z": 1.0 + }, + { + "x": 206.09241114037997, + "y": -59.48590379030101, + "z": 1.0 + }, + { + "x": 206.49263912980905, + "y": -59.48585502140554, + "z": 1.0 + }, + { + "x": 207.09298111395236, + "y": -59.48578186806235, + "z": 1.0 + }, + { + "x": 207.6933230980959, + "y": -59.485708714719166, + "z": 1.0 + }, + { + "x": 208.29366508223922, + "y": -59.48563556137597, + "z": 1.0 + }, + { + "x": 208.79395006902536, + "y": -59.48557460025664, + "z": 1.0 + }, + { + "x": 209.19417805845433, + "y": -59.48552583136119, + "z": 1.0 + }, + { + "x": 209.79452004259775, + "y": -59.48545267801799, + "z": 1.0 + }, + { + "x": 210.29480502938395, + "y": -59.485391716898675, + "z": 1.0 + }, + { + "x": 210.89514701352738, + "y": -59.48531856355548, + "z": 1.0 + }, + { + "x": 211.4954889976708, + "y": -59.485245410212286, + "z": 1.0 + }, + { + "x": 211.995773984457, + "y": -59.485184449092955, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 5, + "heading": [ + 0.00012185278518115317, + 0.0001218527851825946, + 0.00012185278518572584, + 0.00012185278517498467, + 0.00012185278517614517, + 0.00012185278517742751, + 0.00012185278518097994, + 0.00012185278519480644, + 0.00012185278518907564, + 0.00012185278517746212, + 0.00012185278517744829, + 0.00012185278517744136, + 0.00012185278518098858, + 0.00012185278517742751, + 0.00012185278517507654, + 0.00012185278519166489, + 0.00012185278519165104, + 0.00012185278517744136, + 0.00012185278518098858, + 0.00012185278517744136, + 0.00012185278517613887, + 0.00012185278518906306, + 0.00012185278517617034, + 0.0001218527851790256, + 0.00012185278517744136, + 0.00012185278517744136, + 0.00012185278519875069, + 0.0001218527851790256, + 0.00012185278517613258, + 0.00012185278518906306, + 0.00012185278517746212, + 0.00012185278517615146, + 0.000121852785175065, + 0.00012185278517507654, + 0.00012185278518906306, + 0.00012185278518905047, + 0.0001218527851774552, + 0.0001218527851774552, + 0.00012185278518691218, + 0.00012185278517615775, + 0.0001218527851790256, + 0.00012185278517742058, + 0.00012185278517507654, + 0.00012185278519165104, + 0.00012185278519164411, + 0.00012185278517746212, + 0.00012185278518099724, + 0.00012185278517744136, + 0.00012185278517507654, + 0.00012185278518906306, + 0.00012185278517615146, + 0.00012185278517744136, + 0.00012185278519480644, + 0.00012185278517613887, + 0.00012185278517615146, + 0.00012185278519166489, + 0.0001218527851790179, + 0.00012185278517744136, + 0.00012185278517615146, + 0.00012185278517615146, + 0.00012185278518906935, + 0.00012185278518906935, + 0.00012185278517744136, + 0.0001218527851790179, + 0.0001218527851790179, + 0.00012185278517742751, + 0.00012185278518691218, + 0.00012185278517508807, + 0.00012185278517744829, + 0.00012185278518099724, + 0.00012185278518098858, + 0.00012185278519479875, + 0.00012185278517615146, + 0.00012185278517615775, + 0.00012185278519165104, + 0.0001218527851790179, + 0.0001218527851790179, + 0.0001218527851790256, + 0.0001218527851790256, + 0.0001218527851790179, + 0.00012185278518097128, + 0.00012185278519164411, + 0.00012185278517507654, + 0.00012185278517507654, + 0.00012185278518908194, + 0.0001218527851790256, + 0.00012185278517744136, + 0.00012185278517615146, + 0.00012185278517615146, + 0.00012185278518691218, + 0.00012185278518906306 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 5.01809951025507, + "y": 0.0006114694046743807 + }, + { + "x": 9.032579118460262, + "y": 0.001100644928442307 + }, + { + "x": 9.017329476067744, + "y": 0.0010987867169376386 + }, + { + "x": 11.006269709296816, + "y": 0.0013411446251154757 + }, + { + "x": 10.005699735725102, + "y": 0.0012192223864815332 + }, + { + "x": 8.004559788579968, + "y": 0.0009753779092136483 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001479896993 + }, + { + "x": 11.006269709295111, + "y": 0.0013411446252575843 + }, + { + "x": 10.00569973572226, + "y": 0.0012192223864815332 + }, + { + "x": 10.005699735723397, + "y": 0.0012192223864815332 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 8.0045597885794, + "y": 0.0009753779092136483 + }, + { + "x": 10.005699735725102, + "y": 0.0012192223864815332 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668637494182 + }, + { + "x": 10.00569973572226, + "y": 0.0012192223866236418 + }, + { + "x": 10.005699735723397, + "y": 0.0012192223866236418 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 8.0045597885794, + "y": 0.0009753779092136483 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 11.006269709297385, + "y": 0.0013411446251154757 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446252575843 + }, + { + "x": 11.006269709294543, + "y": 0.0013411446251154757 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001478475908 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 8.004559788578831, + "y": 0.0009753779093557569 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001478475908 + }, + { + "x": 11.006269709297953, + "y": 0.0013411446251154757 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446252575843 + }, + { + "x": 10.00569973572226, + "y": 0.0012192223864815332 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 12.006839682869668, + "y": 0.0014630668637494182 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668637494182 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446252575843 + }, + { + "x": 11.006269709297385, + "y": 0.0013411446252575843 + }, + { + "x": 10.005699735722828, + "y": 0.0012192223864815332 + }, + { + "x": 10.005699735722828, + "y": 0.0012192223864815332 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668638915267 + }, + { + "x": 11.00626970929568, + "y": 0.0013411446251154757 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001478475908 + }, + { + "x": 10.00569973572567, + "y": 0.0012192223864815332 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668637494182 + }, + { + "x": 10.005699735723397, + "y": 0.0012192223866236418 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223866236418 + }, + { + "x": 10.00569973572226, + "y": 0.0012192223864815332 + }, + { + "x": 8.004559788578831, + "y": 0.0009753779092136483 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668637494182 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446252575843 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001479896993 + }, + { + "x": 11.006269709297385, + "y": 0.0013411446251154757 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 10.00569973572226, + "y": 0.0012192223866236418 + }, + { + "x": 9.005129762151682, + "y": 0.0010973001478475908 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 11.00626970929568, + "y": 0.0013411446252575843 + }, + { + "x": 11.00626970929568, + "y": 0.0013411446252575843 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 9.005129762151682, + "y": 0.0010973001478475908 + }, + { + "x": 9.005129762151682, + "y": 0.0010973001478475908 + }, + { + "x": 10.005699735725102, + "y": 0.0012192223864815332 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668638915267 + }, + { + "x": 12.006839682867394, + "y": 0.0014630668637494182 + }, + { + "x": 10.005699735723397, + "y": 0.0012192223864815332 + }, + { + "x": 8.004559788578831, + "y": 0.0009753779092136483 + }, + { + "x": 8.0045597885794, + "y": 0.0009753779092136483 + }, + { + "x": 9.005129762151682, + "y": 0.0010973001479896993 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 11.00626970929568, + "y": 0.0013411446251154757 + }, + { + "x": 10.005699735723397, + "y": 0.0012192223866236418 + }, + { + "x": 9.005129762151682, + "y": 0.0010973001478475908 + }, + { + "x": 9.005129762151682, + "y": 0.0010973001478475908 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001478475908 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001478475908 + }, + { + "x": 9.005129762151682, + "y": 0.0010973001478475908 + }, + { + "x": 8.004559788580536, + "y": 0.0009753779092136483 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223866236418 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668637494182 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668637494182 + }, + { + "x": 11.006269709294543, + "y": 0.0013411446252575843 + }, + { + "x": 9.005129762151114, + "y": 0.0010973001478475908 + }, + { + "x": 10.005699735723965, + "y": 0.0012192223864815332 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446251154757 + }, + { + "x": 12.00683968286853, + "y": 0.0014630668638915267 + }, + { + "x": 11.006269709296248, + "y": 0.0013411446252575843 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 211.995773984457, + "y": -59.485184449092955, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 336.87871837455714, + "y": -1.9946542982485806, + "z": 1.0 + }, + { + "x": 337.48138495618383, + "y": -1.9949744568755592, + "z": 1.0 + }, + { + "x": 338.08405153781047, + "y": -1.995294615502536, + "z": 1.0 + }, + { + "x": 338.58627368916603, + "y": -1.9955614143583502, + "z": 1.0 + }, + { + "x": 339.0884958405216, + "y": -1.995828213214164, + "z": 1.0 + }, + { + "x": 339.49027356160605, + "y": -1.9960416522988154, + "z": 1.0 + }, + { + "x": 339.8920512826905, + "y": -1.9962550913834662, + "z": 1.0 + }, + { + "x": 340.394273434046, + "y": -1.9965218902392805, + "z": 1.0 + }, + { + "x": 340.9969400156727, + "y": -1.9968420488662573, + "z": 1.0 + }, + { + "x": 341.4991621670282, + "y": -1.997108847722071, + "z": 1.0 + }, + { + "x": 341.90093988811265, + "y": -1.9973222868067224, + "z": 1.0 + }, + { + "x": 342.3027176091971, + "y": -1.9975357258913733, + "z": 1.0 + }, + { + "x": 342.70449533028153, + "y": -1.9977491649760246, + "z": 1.0 + }, + { + "x": 343.106273051366, + "y": -1.997962604060676, + "z": 1.0 + }, + { + "x": 343.5080507724504, + "y": -1.9981760431453273, + "z": 1.0 + }, + { + "x": 344.1107173540771, + "y": -1.998496201772304, + "z": 1.0 + }, + { + "x": 344.6129395054326, + "y": -1.9987630006281178, + "z": 1.0 + }, + { + "x": 345.01471722651706, + "y": -1.9989764397127692, + "z": 1.0 + }, + { + "x": 345.4164949476015, + "y": -1.99918987879742, + "z": 1.0 + }, + { + "x": 346.0191615292282, + "y": -1.9995100374243995, + "z": 1.0 + }, + { + "x": 346.42093925031264, + "y": -1.9997234765090504, + "z": 1.0 + }, + { + "x": 346.92316140166815, + "y": -1.9999902753648646, + "z": 1.0 + }, + { + "x": 347.3249391227526, + "y": -2.000203714449516, + "z": 1.0 + }, + { + "x": 347.72671684383704, + "y": -2.0004171535341677, + "z": 1.0 + }, + { + "x": 348.2289389951926, + "y": -2.000683952389981, + "z": 1.0 + }, + { + "x": 348.82993064592614, + "y": -2.0010032212321396, + "z": 1.0 + }, + { + "x": 349.3307570215374, + "y": -2.001269278600605, + "z": 1.0 + }, + { + "x": 349.9317486722709, + "y": -2.0015885474427635, + "z": 1.0 + }, + { + "x": 350.43257504788215, + "y": -2.001854604811229, + "z": 1.0 + }, + { + "x": 350.9334014234934, + "y": -2.0021206621796934, + "z": 1.0 + }, + { + "x": 351.53439307422695, + "y": -2.002439931021852, + "z": 1.0 + }, + { + "x": 352.13538472496043, + "y": -2.0027591998640104, + "z": 1.0 + }, + { + "x": 352.53604582544943, + "y": -2.0029720457587827, + "z": 1.0 + }, + { + "x": 353.13703747618297, + "y": -2.0032913146009403, + "z": 1.0 + }, + { + "x": 353.537698576672, + "y": -2.0035041604957127, + "z": 1.0 + }, + { + "x": 354.13869022740556, + "y": -2.003823429337871, + "z": 1.0 + }, + { + "x": 354.53935132789456, + "y": -2.0040362752326435, + "z": 1.0 + }, + { + "x": 355.1403429786281, + "y": -2.004355544074802, + "z": 1.0 + }, + { + "x": 355.74133462936163, + "y": -2.0046748129169596, + "z": 1.0 + }, + { + "x": 356.2421610049729, + "y": -2.004940870285425, + "z": 1.0 + }, + { + "x": 356.84315265570643, + "y": -2.0052601391275835, + "z": 1.0 + }, + { + "x": 357.3439790313177, + "y": -2.005526196496049, + "z": 1.0 + }, + { + "x": 357.74464013180676, + "y": -2.0057390423908212, + "z": 1.0 + }, + { + "x": 358.14530123229576, + "y": -2.0059518882855927, + "z": 1.0 + }, + { + "x": 358.7462928830293, + "y": -2.006271157127751, + "z": 1.0 + }, + { + "x": 359.1469539835183, + "y": -2.0064840030225235, + "z": 1.0 + }, + { + "x": 359.74794563425183, + "y": -2.006803271864682, + "z": 1.0 + }, + { + "x": 360.34893728498537, + "y": -2.0071225407068396, + "z": 1.0 + }, + { + "x": 360.8497636605967, + "y": -2.007388598075305, + "z": 1.0 + }, + { + "x": 361.4507553113302, + "y": -2.0077078669174635, + "z": 1.0 + }, + { + "x": 361.9515816869415, + "y": -2.007973924285929, + "z": 1.0 + }, + { + "x": 362.552573337675, + "y": -2.0082931931280865, + "z": 1.0 + }, + { + "x": 363.0533997132863, + "y": -2.008559250496552, + "z": 1.0 + }, + { + "x": 363.65439136401983, + "y": -2.0088785193387104, + "z": 1.0 + }, + { + "x": 364.1552177396311, + "y": -2.009144576707176, + "z": 1.0 + }, + { + "x": 364.65604411524237, + "y": -2.0094106340756412, + "z": 1.0 + }, + { + "x": 365.0567052157314, + "y": -2.0096234799704127, + "z": 1.0 + }, + { + "x": 365.45736631622043, + "y": -2.009836325865185, + "z": 1.0 + }, + { + "x": 365.9581926918317, + "y": -2.0101023832336504, + "z": 1.0 + }, + { + "x": 366.35885379232076, + "y": -2.0103152291284228, + "z": 1.0 + }, + { + "x": 366.9598454430543, + "y": -2.0106344979705812, + "z": 1.0 + }, + { + "x": 367.5608370937878, + "y": -2.010953766812739, + "z": 1.0 + }, + { + "x": 368.0616634693991, + "y": -2.0112198241812043, + "z": 1.0 + }, + { + "x": 368.6626551201326, + "y": -2.0115390930233628, + "z": 1.0 + }, + { + "x": 369.1634814957439, + "y": -2.011805150391828, + "z": 1.0 + }, + { + "x": 369.66430787135516, + "y": -2.0120712077602927, + "z": 1.0 + }, + { + "x": 370.16513424696643, + "y": -2.012337265128758, + "z": 1.0 + }, + { + "x": 370.6659606225777, + "y": -2.0126033224972235, + "z": 1.0 + }, + { + "x": 371.16678699818897, + "y": -2.012869379865689, + "z": 1.0 + }, + { + "x": 371.66761337380024, + "y": -2.0131354372341543, + "z": 1.0 + }, + { + "x": 372.1684397494115, + "y": -2.013401494602619, + "z": 1.0 + }, + { + "x": 372.6692661250228, + "y": -2.0136675519710843, + "z": 1.0 + }, + { + "x": 373.17009250063404, + "y": -2.0139336093395497, + "z": 1.0 + }, + { + "x": 373.6709188762453, + "y": -2.014199666708015, + "z": 1.0 + }, + { + "x": 374.1717452518566, + "y": -2.0144657240764805, + "z": 1.0 + }, + { + "x": 374.5724063523456, + "y": -2.014678569971252, + "z": 1.0 + }, + { + "x": 375.1733980030791, + "y": -2.0149978388134104, + "z": 1.0 + }, + { + "x": 375.77438965381265, + "y": -2.015317107655569, + "z": 1.0 + }, + { + "x": 376.3753813045462, + "y": -2.0156363764977274, + "z": 1.0 + }, + { + "x": 376.7760424050352, + "y": -2.015849222392499, + "z": 1.0 + }, + { + "x": 377.27686878064645, + "y": -2.0161152797609643, + "z": 1.0 + }, + { + "x": 377.7776951562577, + "y": -2.0163813371294297, + "z": 1.0 + }, + { + "x": 378.1783562567467, + "y": -2.016594183024202, + "z": 1.0 + }, + { + "x": 378.5790173572357, + "y": -2.0168070289189743, + "z": 1.0 + }, + { + "x": 379.079843732847, + "y": -2.017073086287439, + "z": 1.0 + }, + { + "x": 379.68083538358053, + "y": -2.0173923551295974, + "z": 1.0 + }, + { + "x": 380.28182703431406, + "y": -2.017711623971756, + "z": 1.0 + }, + { + "x": 380.8828186850476, + "y": -2.0180308928139143, + "z": 1.0 + }, + { + "x": 381.38364506065886, + "y": -2.018296950182379, + "z": 1.0 + }, + { + "x": 381.9846367113924, + "y": -2.0186162190245374, + "z": 1.0 + }, + { + "x": 382.3852978118814, + "y": -2.0188290649193097, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 6, + "heading": [ + -0.0005312366847937874, + -0.0005312366847964263, + -0.0005312366847949776, + -0.0005312366847937787, + -0.0005312366847936362, + -0.0005312366847934955, + -0.0005312366847933196, + -0.000531236684793529, + -0.0005312366847937787, + -0.0005312366847933768, + -0.000531236684793529, + -0.0005312366847933196, + -0.0005312366847933196, + -0.0005312366847938724, + -0.0005312366847938724, + -0.0005312366847936362, + -0.0005312366847933768, + -0.000531236684793529, + -0.0005312366847933196, + -0.0005312366847958469, + -0.0005312366847958469, + -0.000531236684793529, + -0.0005312366847940202, + -0.000531236684794425, + -0.0005312366847934955, + -0.0005312366847932849, + -0.0005312366847941043, + -0.0005312366847941316, + -0.0005312366847941316, + -0.000531236684793223, + -0.0005312366847932982, + -0.0005312366847941248, + -0.0005312366847941398, + -0.000531236684793223, + -0.0005312366847931929, + -0.0005312366847940795, + -0.0005312366847941097, + -0.0005312366847941097, + -0.0005312366847933607, + -0.0005312366847932982, + -0.0005312366847941043, + -0.0005312366847941043, + -0.0005312366847940829, + -0.0005312366847929788, + -0.000531236684793223, + -0.0005312366847941097, + -0.0005312366847941097, + -0.0005312366847933607, + -0.0005312366847932707, + -0.0005312366847940768, + -0.0005312366847941043, + -0.0005312366847932982, + -0.0005312366847932982, + -0.0005312366847941043, + -0.0005312366847941043, + -0.0005312366847941097, + -0.0005312366847930977, + -0.0005312366847929788, + -0.0005312366847941164, + -0.0005312366847940829, + -0.0005312366847940795, + -0.0005312366847933607, + -0.0005312366847932982, + -0.0005312366847941043, + -0.0005312366847941043, + -0.000531236684793223, + -0.000531236684793223, + -0.0005312366847941097, + -0.0005312366847941097, + -0.0005312366847941097, + -0.000531236684793223, + -0.000531236684793223, + -0.0005312366847941097, + -0.0005312366847941097, + -0.0005312366847941097, + -0.0005312366847931312, + -0.000531236684793223, + -0.0005312366847940997, + -0.0005312366847940997, + -0.000531236684793223, + -0.0005312366847931312, + -0.0005312366847941097, + -0.0005312366847941164, + -0.0005312366847941248, + -0.0005312366847931312, + -0.0005312366847932982, + -0.0005312366847940997, + -0.0005312366847940997, + -0.0005312366847932982, + -0.0005312366847932982, + -0.0005312366847941097 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 6.026665816266927, + "y": -0.0032015862697853947 + }, + { + "x": 12.053331632533286, + "y": -0.006403172539553026 + }, + { + "x": 11.048887329822037, + "y": -0.005869574827910284 + }, + { + "x": 10.044443027111356, + "y": -0.005335977116280866 + }, + { + "x": 9.039998724400107, + "y": -0.004802379404651447 + }, + { + "x": 8.035554421688857, + "y": -0.004268781693022028 + }, + { + "x": 9.039998724399538, + "y": -0.004802379404651447 + }, + { + "x": 11.048887329822037, + "y": -0.005869574827910284 + }, + { + "x": 11.048887329822037, + "y": -0.0058695748279058435 + }, + { + "x": 9.039998724399538, + "y": -0.004802379404651447 + }, + { + "x": 8.035554421688857, + "y": -0.004268781693022028 + }, + { + "x": 8.035554421688857, + "y": -0.004268781693022028 + }, + { + "x": 8.035554421688857, + "y": -0.004268781693026469 + }, + { + "x": 8.035554421688857, + "y": -0.004268781693026469 + }, + { + "x": 10.044443027111356, + "y": -0.005335977116280866 + }, + { + "x": 11.048887329822037, + "y": -0.0058695748279058435 + }, + { + "x": 9.039998724399538, + "y": -0.004802379404651447 + }, + { + "x": 8.035554421688857, + "y": -0.004268781693022028 + }, + { + "x": 10.044443027111356, + "y": -0.00533597711630307 + }, + { + "x": 10.044443027111356, + "y": -0.00533597711630307 + }, + { + "x": 9.039998724399538, + "y": -0.004802379404651447 + }, + { + "x": 9.039998724399538, + "y": -0.004802379404655888 + }, + { + "x": 8.035554421688857, + "y": -0.00426878169303091 + }, + { + "x": 9.039998724400107, + "y": -0.004802379404651447 + }, + { + "x": 11.032138020891011, + "y": -0.005860676979718704 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 11.018180263447448, + "y": -0.005853262106239043 + }, + { + "x": 11.018180263447448, + "y": -0.005853262106239043 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 12.019833014670098, + "y": -0.006385376843169865 + }, + { + "x": 10.016527512224798, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 10.016527512225935, + "y": -0.005321147369299339 + }, + { + "x": 10.016527512225935, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 12.019833014670667, + "y": -0.006385376843160984 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 9.014874761003284, + "y": -0.004789032632377399 + }, + { + "x": 8.013222009780634, + "y": -0.004256917895437695 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 12.019833014670667, + "y": -0.006385376843160984 + }, + { + "x": 11.018180263448585, + "y": -0.0058532621062301615 + }, + { + "x": 11.018180263448585, + "y": -0.005853262106239043 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 9.014874761003284, + "y": -0.004789032632368517 + }, + { + "x": 8.013222009780634, + "y": -0.004256917895437695 + }, + { + "x": 9.014874761002716, + "y": -0.004789032632377399 + }, + { + "x": 9.014874761003284, + "y": -0.004789032632377399 + }, + { + "x": 10.016527512225935, + "y": -0.005321147369308221 + }, + { + "x": 12.019833014670667, + "y": -0.006385376843160984 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 11.018180263448016, + "y": -0.005853262106239043 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 9.014874761002716, + "y": -0.004789032632368517 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 12.019833014670667, + "y": -0.006385376843169865 + }, + { + "x": 12.019833014670667, + "y": -0.006385376843169865 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369299339 + }, + { + "x": 9.014874761002716, + "y": -0.004789032632368517 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + }, + { + "x": 9.014874761002716, + "y": -0.004789032632377399 + }, + { + "x": 8.013222009780065, + "y": -0.004256917895446577 + }, + { + "x": 9.014874761002716, + "y": -0.004789032632368517 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 12.019833014670667, + "y": -0.006385376843169865 + }, + { + "x": 12.019833014670667, + "y": -0.006385376843169865 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 11.018180263448016, + "y": -0.0058532621062301615 + }, + { + "x": 10.016527512225366, + "y": -0.005321147369308221 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 382.3852978118814, + "y": -2.0188290649193097, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 396.3472344367284, + "y": -37.160345373104796, + "z": 1.0 + }, + { + "x": 396.34783460167466, + "y": -36.660199843302536, + "z": 1.0 + }, + { + "x": 396.3484347666208, + "y": -36.16005431350368, + "z": 1.0 + }, + { + "x": 396.3491549645562, + "y": -35.55987967774118, + "z": 1.0 + }, + { + "x": 396.3496350965132, + "y": -35.15976325390029, + "z": 1.0 + }, + { + "x": 396.3503552944486, + "y": -34.559588618138925, + "z": 1.0 + }, + { + "x": 396.351075492384, + "y": -33.959413982377555, + "z": 1.0 + }, + { + "x": 396.351555624341, + "y": -33.559297558536606, + "z": 1.0 + }, + { + "x": 396.35203575629794, + "y": -33.15918113469572, + "z": 1.0 + }, + { + "x": 396.35251588825486, + "y": -32.75906471085483, + "z": 1.0 + }, + { + "x": 396.3531160532011, + "y": -32.258919181052505, + "z": 1.0 + }, + { + "x": 396.35359618515804, + "y": -31.85880275721275, + "z": 1.0 + }, + { + "x": 396.3543163830934, + "y": -31.258628121451384, + "z": 1.0 + }, + { + "x": 396.3550365810288, + "y": -30.65845348569116, + "z": 1.0 + }, + { + "x": 396.35551671298583, + "y": -30.25833706184913, + "z": 1.0 + }, + { + "x": 396.35599684494275, + "y": -29.858220638008184, + "z": 1.0 + }, + { + "x": 396.35671704287813, + "y": -29.25804600224796, + "z": 1.0 + }, + { + "x": 396.3574372408136, + "y": -28.657871366485452, + "z": 1.0 + }, + { + "x": 396.358157438749, + "y": -28.05769673072409, + "z": 1.0 + }, + { + "x": 396.3588776366844, + "y": -27.457522094963863, + "z": 1.0 + }, + { + "x": 396.3593577686414, + "y": -27.057405671122968, + "z": 1.0 + }, + { + "x": 396.35983790059834, + "y": -26.65728924728094, + "z": 1.0 + }, + { + "x": 396.3605580985337, + "y": -26.057114611520714, + "z": 1.0 + }, + { + "x": 396.36115826348, + "y": -25.556969081718446, + "z": 1.0 + }, + { + "x": 396.3617584284261, + "y": -25.05682355191732, + "z": 1.0 + }, + { + "x": 396.3624786263615, + "y": -24.456648916155956, + "z": 1.0 + }, + { + "x": 396.36295875831854, + "y": -24.056532492315007, + "z": 1.0 + }, + { + "x": 396.3636789562539, + "y": -23.456357856552508, + "z": 1.0 + }, + { + "x": 396.3643991541893, + "y": -22.856183220793415, + "z": 1.0 + }, + { + "x": 396.36487928614633, + "y": -22.456066796951387, + "z": 1.0 + }, + { + "x": 396.36535941810325, + "y": -22.055950373110495, + "z": 1.0 + }, + { + "x": 396.36607961603875, + "y": -21.45577573734913, + "z": 1.0 + }, + { + "x": 396.36655974799555, + "y": -21.055659313509317, + "z": 1.0 + }, + { + "x": 396.3671599129418, + "y": -20.555513783707056, + "z": 1.0 + }, + { + "x": 396.3678801108772, + "y": -19.95533914794569, + "z": 1.0 + }, + { + "x": 396.3683602428342, + "y": -19.555222724104787, + "z": 1.0 + }, + { + "x": 396.36884037479115, + "y": -19.155106300262716, + "z": 1.0 + }, + { + "x": 396.36932050674807, + "y": -18.754989876424098, + "z": 1.0 + }, + { + "x": 396.36992067169433, + "y": -18.25484434662183, + "z": 1.0 + }, + { + "x": 396.3706408696297, + "y": -17.654669710860468, + "z": 1.0 + }, + { + "x": 396.37124103457586, + "y": -17.15452418105934, + "z": 1.0 + }, + { + "x": 396.37196123251124, + "y": -16.55434954529911, + "z": 1.0 + }, + { + "x": 396.37268143044673, + "y": -15.954174909536611, + "z": 1.0 + }, + { + "x": 396.37316156240365, + "y": -15.55405848569572, + "z": 1.0 + }, + { + "x": 396.3737617273499, + "y": -15.053912955894535, + "z": 1.0 + }, + { + "x": 396.3744819252853, + "y": -14.453738320133171, + "z": 1.0 + }, + { + "x": 396.3749620572422, + "y": -14.053621896292277, + "z": 1.0 + }, + { + "x": 396.3756822551776, + "y": -13.453447260530915, + "z": 1.0 + }, + { + "x": 396.3761623871346, + "y": -13.053330836690023, + "z": 1.0 + }, + { + "x": 396.37664251909155, + "y": -12.653214412849076, + "z": 1.0 + }, + { + "x": 396.3772426840377, + "y": -12.153068883047947, + "z": 1.0 + }, + { + "x": 396.37772281599473, + "y": -11.752952459207044, + "z": 1.0 + }, + { + "x": 396.37820294795165, + "y": -11.352836035366167, + "z": 1.0 + }, + { + "x": 396.37892314588703, + "y": -10.7526613996048, + "z": 1.0 + }, + { + "x": 396.3796433438224, + "y": -10.152486763844573, + "z": 1.0 + }, + { + "x": 396.3800034427902, + "y": -9.852399445962725, + "z": 1.0 + }, + { + "x": 396.38072947083594, + "y": -9.24736630647071, + "z": 1.0 + }, + { + "x": 396.38018884440146, + "y": -8.827697833519784, + "z": 1.0 + }, + { + "x": 396.3559542057511, + "y": -8.213198337464048, + "z": 1.0 + }, + { + "x": 396.31200879560265, + "y": -7.723158864023834, + "z": 1.0 + }, + { + "x": 396.20527536482757, + "y": -6.992592214704764, + "z": 1.0 + }, + { + "x": 396.0500718257525, + "y": -6.271164008187339, + "z": 1.0 + }, + { + "x": 395.8842368704714, + "y": -5.678967931305309, + "z": 1.0 + }, + { + "x": 395.6857622060334, + "y": -5.096899880584985, + "z": 1.0 + }, + { + "x": 395.455260748745, + "y": -4.526756868425108, + "z": 1.0 + }, + { + "x": 395.2482753165647, + "y": -4.0804127215268355, + "z": 1.0 + }, + { + "x": 394.9011210530243, + "y": -3.4292445540602023, + "z": 1.0 + }, + { + "x": 394.57919369521915, + "y": -2.905263665688727, + "z": 1.0 + }, + { + "x": 394.2286560290763, + "y": -2.3999741932397383, + "z": 1.0 + }, + { + "x": 393.85059020493986, + "y": -1.9149362194119146, + "z": 1.0 + }, + { + "x": 393.4461633242467, + "y": -1.4516473413939093, + "z": 1.0 + }, + { + "x": 393.10315414617816, + "y": -1.0964914090564002, + "z": 1.0 + }, + { + "x": 392.5556607414593, + "y": -0.5917147279677624, + "z": 1.0 + }, + { + "x": 391.97441258430604, + "y": -0.12667220556926106, + "z": 1.0 + }, + { + "x": 391.5694707284134, + "y": 0.16029339798120468, + "z": 1.0 + }, + { + "x": 391.1515965517793, + "y": 0.42807561936125904, + "z": 1.0 + }, + { + "x": 390.6124534574134, + "y": 0.7349446250181975, + "z": 1.0 + }, + { + "x": 389.9432649415583, + "y": 1.060967210825062, + "z": 1.0 + }, + { + "x": 389.3693148975709, + "y": 1.296387251222732, + "z": 1.0 + }, + { + "x": 388.90088974534075, + "y": 1.4604024328109124, + "z": 1.0 + }, + { + "x": 388.3054464689899, + "y": 1.6344358076569507, + "z": 1.0 + }, + { + "x": 387.82242371460416, + "y": 1.748505382053843, + "z": 1.0 + }, + { + "x": 387.3346097798446, + "y": 1.8399412798968666, + "z": 1.0 + }, + { + "x": 386.84306450454335, + "y": 1.9085448273143313, + "z": 1.0 + }, + { + "x": 386.2250132455845, + "y": 1.961968903319451, + "z": 1.0 + }, + { + "x": 385.4980846920273, + "y": 1.979517872767351, + "z": 1.0 + }, + { + "x": 384.8934007714826, + "y": 1.9798391030788676, + "z": 1.0 + }, + { + "x": 384.59105881121025, + "y": 1.979999718234626, + "z": 1.0 + }, + { + "x": 383.9900671604767, + "y": 1.9803189870767843, + "z": 1.0 + }, + { + "x": 383.48924078486544, + "y": 1.9805850444452497, + "z": 1.0 + }, + { + "x": 383.08857968437644, + "y": 1.980797890340022, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 7, + "heading": [ + 1.8979433016187948, + 1.5695963467442284, + 1.569596346744338, + 1.5695963467444518, + 1.5695963467443406, + 1.5695963467443392, + 1.569596346744453, + 1.5695963467443395, + 1.5695963467443108, + 1.569596346744453, + 1.5695963467443281, + 1.5695963467443268, + 1.5695963467444516, + 1.5695963467444518, + 1.5695963467443392, + 1.5695963467443126, + 1.5695963467444516, + 1.5695963467443583, + 1.5695963467443594, + 1.5695963467444518, + 1.569596346744338, + 1.5695963467443126, + 1.569596346744453, + 1.5695963467443497, + 1.5695963467443406, + 1.569596346744453, + 1.5695963467443395, + 1.5695963467443408, + 1.5695963467444518, + 1.569596346744338, + 1.5695963467443126, + 1.5695963467443392, + 1.5695963467444516, + 1.569596346744453, + 1.5695963467443508, + 1.5695963467443392, + 1.5695963467443126, + 1.5695963467444514, + 1.569596346744325, + 1.5695963467443508, + 1.569596346744453, + 1.5695963467444518, + 1.5695963467443583, + 1.5695963467443406, + 1.5695963467443268, + 1.5695963467443497, + 1.569596346744453, + 1.569596346744453, + 1.5695963467443392, + 1.569596346744311, + 1.569596346744453, + 1.5695963467443266, + 1.5695963467443108, + 1.569596346744453, + 1.5695963467444518, + 1.5695963467443268, + 1.569596346744325, + 1.5706153945049763, + 1.5947484581231608, + 1.6324452626433934, + 1.6936208548054112, + 1.7492747489820462, + 1.8104893934341382, + 1.8716255364061702, + 1.9272038296191953, + 1.9772242945495098, + 2.0383604066772216, + 2.0883808667884334, + 2.1495169833593275, + 2.2050952761025653, + 2.260673574203252, + 2.3108780558633866, + 2.373656925855639, + 2.4317794339744916, + 2.4901149063298034, + 2.548388391437845, + 2.600834497835706, + 2.659107941869012, + 2.7173814004965506, + 2.775654864224719, + 2.8339283163605002, + 2.880547077762562, + 2.932993183603905, + 2.9796119446485148, + 3.0320580554517327, + 3.0888726955741186, + 3.1281734888993147, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.141061416904999 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.006001649462632486, + "y": 5.001455298022606 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596011176 + }, + { + "x": 0.013203628815290358, + "y": 11.003201655613566 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596033914 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596022545 + }, + { + "x": 0.014403958707589481, + "y": 12.003492715227324 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596023185 + }, + { + "x": 0.009602639139529856, + "y": 8.002328476818334 + }, + { + "x": 0.009602639138392988, + "y": 8.002328476817766 + }, + { + "x": 0.01080296903182898, + "y": 9.002619536432164 + }, + { + "x": 0.01080296903182898, + "y": 9.002619536420795 + }, + { + "x": 0.012003298922991235, + "y": 10.002910596011212 + }, + { + "x": 0.014403958707589481, + "y": 12.003492715215884 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596022545 + }, + { + "x": 0.009602639139529856, + "y": 8.002328476829774 + }, + { + "x": 0.012003298922991235, + "y": 10.00291059601171 + }, + { + "x": 0.01440395870872635, + "y": 12.003492715227324 + }, + { + "x": 0.01440395870872635, + "y": 12.003492715238693 + }, + { + "x": 0.014403958707589481, + "y": 12.003492715215884 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596011212 + }, + { + "x": 0.009602639139529856, + "y": 8.002328476829241 + }, + { + "x": 0.012003298922991235, + "y": 10.002910596022545 + }, + { + "x": 0.013203628816427226, + "y": 11.003201655624935 + }, + { + "x": 0.012003298924128103, + "y": 10.00291059603395 + }, + { + "x": 0.013203628815290358, + "y": 11.003201655624899 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596023113 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596034482 + }, + { + "x": 0.014403958707589481, + "y": 12.00349271521592 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596011212 + }, + { + "x": 0.009602639139529856, + "y": 8.002328476829206 + }, + { + "x": 0.012003298924128103, + "y": 10.00291059602258 + }, + { + "x": 0.012003298922991235, + "y": 10.00291059601178 + }, + { + "x": 0.010802969030692111, + "y": 9.002619536420724 + }, + { + "x": 0.013203628816427226, + "y": 11.003201655636268 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596022687 + }, + { + "x": 0.009602639139529856, + "y": 8.002328476829739 + }, + { + "x": 0.009602639138392988, + "y": 8.002328476806895 + }, + { + "x": 0.01080296903182898, + "y": 9.002619536408858 + }, + { + "x": 0.013203628816427226, + "y": 11.003201655636303 + }, + { + "x": 0.013203628815290358, + "y": 11.003201655624899 + }, + { + "x": 0.013203628815290358, + "y": 11.003201655613566 + }, + { + "x": 0.01440395870872635, + "y": 12.003492715227289 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596033914 + }, + { + "x": 0.01080296903182898, + "y": 9.00261953642076 + }, + { + "x": 0.013203628816427226, + "y": 11.003201655625485 + }, + { + "x": 0.012003298922991235, + "y": 10.00291059602258 + }, + { + "x": 0.012003298922991235, + "y": 10.002910596022563 + }, + { + "x": 0.012003298924128103, + "y": 10.002910596022545 + }, + { + "x": 0.009602639139529856, + "y": 8.002328476818388 + }, + { + "x": 0.010802969030692111, + "y": 9.00261953642076 + }, + { + "x": 0.01080296903182898, + "y": 9.002619536420315 + }, + { + "x": 0.009602639139529856, + "y": 8.002328476817802 + }, + { + "x": 0.012003298922991235, + "y": 10.002910596022438 + }, + { + "x": 0.014403958707589481, + "y": 12.003492715215938 + }, + { + "x": 0.01080296903182898, + "y": 9.00261953642076 + }, + { + "x": 0.010861270135364975, + "y": 9.051204573738634 + }, + { + "x": 0.00185401611247471, + "y": 10.247016124429411 + }, + { + "x": -0.2477526508482697, + "y": 10.341679690066616 + }, + { + "x": -0.6818004879880846, + "y": 11.0453896949595 + }, + { + "x": -1.5067884092354689, + "y": 12.206061227592837 + }, + { + "x": -2.6193696985012593, + "y": 14.519948558364941 + }, + { + "x": -3.210384943561735, + "y": 13.13624283399455 + }, + { + "x": -3.6430961971910847, + "y": 11.742641276023544 + }, + { + "x": -4.289761217264072, + "y": 11.522110628802018 + }, + { + "x": -4.374868894687438, + "y": 10.164871590581495 + }, + { + "x": -5.5413969572066435, + "y": 10.975123143649052 + }, + { + "x": -6.690816213455264, + "y": 11.751490558381086 + }, + { + "x": -6.72465023948007, + "y": 10.29270360820464 + }, + { + "x": -7.286034902792835, + "y": 9.903274462768122 + }, + { + "x": -7.824927048296217, + "y": 9.483268518458289 + }, + { + "x": -7.4743605876170705, + "y": 8.184448103555143 + }, + { + "x": -8.905025827874056, + "y": 8.599326134261469 + }, + { + "x": -11.287415618721184, + "y": 9.69819203487139 + }, + { + "x": -9.861900130458707, + "y": 7.52008125948967 + }, + { + "x": -8.228160325267595, + "y": 5.5474782493052 + }, + { + "x": -9.570172710000406, + "y": 5.746512270369927 + }, + { + "x": -12.083316102209665, + "y": 6.32891591463803 + }, + { + "x": -12.43138559842464, + "y": 5.6144262620453445 + }, + { + "x": -10.42375196217563, + "y": 3.9943522198585035 + }, + { + "x": -10.638684285810314, + "y": 3.3804855643421883 + }, + { + "x": -10.784660307365925, + "y": 2.8810294924293056 + }, + { + "x": -9.70836689145301, + "y": 2.055054722399159 + }, + { + "x": -9.793592100608066, + "y": 1.600394452604883 + }, + { + "x": -11.095965342601062, + "y": 1.2202762342258433 + }, + { + "x": -13.449798125160441, + "y": 0.7097304545301975 + }, + { + "x": -13.316124741018598, + "y": 0.17870199759416616 + }, + { + "x": -9.070258808170593, + "y": 0.0048184546727503275 + }, + { + "x": -9.033336110059054, + "y": 0.004798839979167635 + }, + { + "x": -11.018180263448016, + "y": 0.005853262106236823 + }, + { + "x": -9.014874761002716, + "y": 0.004789032632377399 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 383.08857968437644, + "y": 1.980797890340022, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 92.38854030013255, + "y": -121.94744551989223, + "z": 1.0 + }, + { + "x": 92.38846402549676, + "y": -121.44568059549218, + "z": 1.0 + }, + { + "x": 92.38840300578813, + "y": -121.04426865597216, + "z": 1.0 + }, + { + "x": 92.38834198607951, + "y": -120.64285671645214, + "z": 1.0 + }, + { + "x": 92.38828096637087, + "y": -120.2414447769321, + "z": 1.0 + }, + { + "x": 92.38821994666225, + "y": -119.84003283741208, + "z": 1.0 + }, + { + "x": 92.38821994666225, + "y": -119.84003283741208, + "z": 1.0 + }, + { + "x": 92.38815898411413, + "y": -119.43899692261479, + "z": 1.0 + }, + { + "x": 92.38808284520091, + "y": -118.93812483537857, + "z": 1.0 + }, + { + "x": 92.38799147850507, + "y": -118.33707833069512, + "z": 1.0 + }, + { + "x": 92.38790011180922, + "y": -117.73603182601165, + "z": 1.0 + }, + { + "x": 92.38780874511338, + "y": -117.1349853213282, + "z": 1.0 + }, + { + "x": 92.3877478339828, + "y": -116.73428765153922, + "z": 1.0 + }, + { + "x": 92.38768692285224, + "y": -116.33358998175025, + "z": 1.0 + }, + { + "x": 92.3875955561564, + "y": -115.7325434770668, + "z": 1.0 + }, + { + "x": 92.38753464502582, + "y": -115.33184580727782, + "z": 1.0 + }, + { + "x": 92.38747373389526, + "y": -114.93114813748885, + "z": 1.0 + }, + { + "x": 92.38741282276469, + "y": -114.5304504676996, + "z": 1.0 + }, + { + "x": 92.38732145606885, + "y": -113.92940396301641, + "z": 1.0 + }, + { + "x": 92.38726054493829, + "y": -113.52870629322744, + "z": 1.0 + }, + { + "x": 92.38718440602507, + "y": -113.02783420599124, + "z": 1.0 + }, + { + "x": 92.38709303932922, + "y": -112.42678770130777, + "z": 1.0 + }, + { + "x": 92.38700167263336, + "y": -111.82574119662432, + "z": 1.0 + }, + { + "x": 92.38692553372016, + "y": -111.3248691093881, + "z": 1.0 + }, + { + "x": 92.3868341670243, + "y": -110.72382260470464, + "z": 1.0 + }, + { + "x": 92.38677325589374, + "y": -110.32312493491567, + "z": 1.0 + }, + { + "x": 92.38671234476318, + "y": -109.9224272651267, + "z": 1.0 + }, + { + "x": 92.38662097806733, + "y": -109.32138076044323, + "z": 1.0 + }, + { + "x": 92.38652961137149, + "y": -108.72033425575978, + "z": 1.0 + }, + { + "x": 92.38643824467563, + "y": -108.11928775107631, + "z": 1.0 + }, + { + "x": 92.38637733354507, + "y": -107.71859008128735, + "z": 1.0 + }, + { + "x": 92.38628596684921, + "y": -107.1175435766039, + "z": 1.0 + }, + { + "x": 92.38620982793601, + "y": -106.61667148936768, + "z": 1.0 + }, + { + "x": 92.3861336890228, + "y": -106.11579940213146, + "z": 1.0 + }, + { + "x": 92.38604232232696, + "y": -105.514752897448, + "z": 1.0 + }, + { + "x": 92.3859509556311, + "y": -104.91370639276454, + "z": 1.0 + }, + { + "x": 92.38589004450054, + "y": -104.51300872297557, + "z": 1.0 + }, + { + "x": 92.38579867780469, + "y": -103.91196221829183, + "z": 1.0 + }, + { + "x": 92.38573776667413, + "y": -103.51126454850314, + "z": 1.0 + }, + { + "x": 92.38566162776092, + "y": -103.01039246126692, + "z": 1.0 + }, + { + "x": 92.3855854888477, + "y": -102.50952037403071, + "z": 1.0 + }, + { + "x": 92.3855093499345, + "y": -102.0086482867945, + "z": 1.0 + }, + { + "x": 92.38544843880392, + "y": -101.60795061700523, + "z": 1.0 + }, + { + "x": 92.38538752767336, + "y": -101.20725294721655, + "z": 1.0 + }, + { + "x": 92.38531138876016, + "y": -100.70638085998033, + "z": 1.0 + }, + { + "x": 92.38523524984694, + "y": -100.20550877274411, + "z": 1.0 + }, + { + "x": 92.3851438831511, + "y": -99.60446226806066, + "z": 1.0 + }, + { + "x": 92.38508297202053, + "y": -99.2037645982714, + "z": 1.0 + }, + { + "x": 92.38502206088997, + "y": -98.80306692848272, + "z": 1.0 + }, + { + "x": 92.38493069419413, + "y": -98.20202042379925, + "z": 1.0 + }, + { + "x": 92.38486978306355, + "y": -97.80132275401029, + "z": 1.0 + }, + { + "x": 92.38479364415035, + "y": -97.30045066677407, + "z": 1.0 + }, + { + "x": 92.38471750523713, + "y": -96.79957857953785, + "z": 1.0 + }, + { + "x": 92.38465659410657, + "y": -96.39888090974888, + "z": 1.0 + }, + { + "x": 92.38456522741073, + "y": -95.79783440506542, + "z": 1.0 + }, + { + "x": 92.38450431628016, + "y": -95.39713673527645, + "z": 1.0 + }, + { + "x": 92.3844434051496, + "y": -94.99643906548748, + "z": 1.0 + }, + { + "x": 92.38438249401904, + "y": -94.59574139569851, + "z": 1.0 + }, + { + "x": 92.38432158288846, + "y": -94.19504372590953, + "z": 1.0 + }, + { + "x": 92.38423021619262, + "y": -93.59399722122608, + "z": 1.0 + }, + { + "x": 92.38416930506204, + "y": -93.19329955143711, + "z": 1.0 + }, + { + "x": 92.38407793836619, + "y": -92.59225304675364, + "z": 1.0 + }, + { + "x": 92.38401702723561, + "y": -92.19155537696439, + "z": 1.0 + }, + { + "x": 92.38394088832241, + "y": -91.69068328972845, + "z": 1.0 + }, + { + "x": 92.38387997719185, + "y": -91.28998561993949, + "z": 1.0 + }, + { + "x": 92.383788610496, + "y": -90.68893911525602, + "z": 1.0 + }, + { + "x": 92.3837124715828, + "y": -90.1880670280198, + "z": 1.0 + }, + { + "x": 92.38365156045222, + "y": -89.78736935823055, + "z": 1.0 + }, + { + "x": 92.38356019375638, + "y": -89.18632285354738, + "z": 1.0 + }, + { + "x": 92.38349928262582, + "y": -88.7856251837584, + "z": 1.0 + }, + { + "x": 92.38343837149525, + "y": -88.38492751396943, + "z": 1.0 + }, + { + "x": 92.38337746036468, + "y": -87.98422984418046, + "z": 1.0 + }, + { + "x": 92.38331654923412, + "y": -87.5835321743915, + "z": 1.0 + }, + { + "x": 92.38324041032091, + "y": -87.08266008715528, + "z": 1.0 + }, + { + "x": 92.38317949919035, + "y": -86.68196241736631, + "z": 1.0 + }, + { + "x": 92.38311858805977, + "y": -86.28126474757704, + "z": 1.0 + }, + { + "x": 92.38302722136393, + "y": -85.68021824289387, + "z": 1.0 + }, + { + "x": 92.38296631023337, + "y": -85.2795205731049, + "z": 1.0 + }, + { + "x": 92.38287494353752, + "y": -84.67847406842144, + "z": 1.0 + }, + { + "x": 92.38278357684167, + "y": -84.07742756373798, + "z": 1.0 + }, + { + "x": 92.3827226657111, + "y": -83.676729893949, + "z": 1.0 + }, + { + "x": 92.38263129901524, + "y": -83.07568338926527, + "z": 1.0 + }, + { + "x": 92.38255516010203, + "y": -82.57481130202933, + "z": 1.0 + }, + { + "x": 92.38246379340619, + "y": -81.97376479734588, + "z": 1.0 + }, + { + "x": 92.38240288227561, + "y": -81.57306712755661, + "z": 1.0 + }, + { + "x": 92.38232674336241, + "y": -81.07219504032068, + "z": 1.0 + }, + { + "x": 92.38225060444921, + "y": -80.57132295308448, + "z": 1.0 + }, + { + "x": 92.38218969331864, + "y": -80.1706252832955, + "z": 1.0 + }, + { + "x": 92.3820983266228, + "y": -79.56957877861204, + "z": 1.0 + }, + { + "x": 92.38203741549222, + "y": -79.16888110882307, + "z": 1.0 + }, + { + "x": 92.38194604879638, + "y": -78.5678346041396, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 8, + "heading": [ + 1.5709483394834889, + 1.5709483394834909, + 1.5709483394834847, + 1.5709483394834767, + 1.5709483394834944, + 1.5709483394834944, + 1.5709483394834767, + 1.5709483394835362, + 1.5709483394835178, + 1.5709483394834876, + 1.5709483394834864, + 1.5709483394834864, + 1.5709483394834889, + 1.5709483394834922, + 1.5709483394834747, + 1.5709483394834889, + 1.5709483394834922, + 1.5709483394834922, + 1.5709483394834889, + 1.5709483394834747, + 1.5709483394834904, + 1.5709483394835004, + 1.5709483394834982, + 1.5709483394834876, + 1.5709483394834876, + 1.5709483394834889, + 1.5709483394834747, + 1.5709483394834889, + 1.5709483394834864, + 1.5709483394834864, + 1.5709483394834889, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834864, + 1.5709483394834889, + 1.5709483394834887, + 1.5709483394834889, + 1.5709483394834747, + 1.5709483394835029, + 1.5709483394835029, + 1.5709483394834902, + 1.5709483394834922, + 1.5709483394834747, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834887, + 1.5709483394834922, + 1.5709483394834747, + 1.5709483394834889, + 1.5709483394834904, + 1.5709483394834889, + 1.5709483394834904, + 1.5709483394834747, + 1.5709483394834889, + 1.5709483394834922, + 1.5709483394834747, + 1.5709483394834922, + 1.5709483394834889, + 1.5709483394834889, + 1.5709483394835029, + 1.5709483394835029, + 1.5709483394834904, + 1.5709483394834747, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834902, + 1.5709483394834889, + 1.5709483394834747, + 1.5709483394834922, + 1.5709483394834922, + 1.5709483394834747, + 1.5709483394834904, + 1.5709483394834904, + 1.5709483394834922, + 1.5709483394834889, + 1.5709483394834747, + 1.5709483394834889, + 1.5709483394834864, + 1.5709483394834889, + 1.5709483394835029, + 1.5709483394835004, + 1.5709483394834876, + 1.5709483394834887, + 1.5709483394834904, + 1.5709483394834747, + 1.5709483394834904, + 1.5709483394834889, + 1.5709483394834889, + 1.5709483394834889 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.000762746357878541, + "y": 5.017649244000495 + }, + { + "x": -0.0013729434441245303, + "y": 9.031768639200664 + }, + { + "x": -0.0012203941724919787, + "y": 8.028238790400337 + }, + { + "x": -0.0012203941726340872, + "y": 8.028238790400621 + }, + { + "x": -0.0012203941726340872, + "y": 8.028238790400621 + }, + { + "x": -0.0006101970862459893, + "y": 4.014119395200169 + }, + { + "x": -0.0006096254811893687, + "y": 4.010359147972906 + }, + { + "x": -0.0013710146133405487, + "y": 9.019080020335082 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001218222611356623, + "y": 8.01395339577951 + }, + { + "x": -0.001522778264018143, + "y": 10.01744174472421 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001218222611356623, + "y": 8.01395339577951 + }, + { + "x": -0.001218222611356623, + "y": 8.01395339578221 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001522778264018143, + "y": 10.01744174472151 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570251717 + }, + { + "x": -0.0016750560907041745, + "y": 11.019185919196701 + }, + { + "x": -0.0018273339171059888, + "y": 12.020930093669193 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0012182226112145145, + "y": 8.013953395779367 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782641602516, + "y": 10.01744174472421 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744727051 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0013705004376163288, + "y": 9.01569757024916 + }, + { + "x": -0.0015227782643023602, + "y": 10.01744174472421 + }, + { + "x": -0.0015227782643023602, + "y": 10.01744174472421 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570254844 + }, + { + "x": -0.001218222611356623, + "y": 8.01395339577951 + }, + { + "x": -0.0013705004376163288, + "y": 9.015697570249017 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744727051 + }, + { + "x": -0.001218222611356623, + "y": 8.013953395779367 + }, + { + "x": -0.001522778264018143, + "y": 10.01744174472151 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.001522778264018143, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001218222611356623, + "y": 8.013953395779367 + }, + { + "x": -0.0012182226112145145, + "y": 8.013953395779367 + }, + { + "x": -0.001218222611356623, + "y": 8.01395339577951 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782641602516, + "y": 10.01744174472421 + }, + { + "x": -0.0015227782643023602, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782643023602, + "y": 10.017441744727194 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0013705004376163288, + "y": 9.015697570249017 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570254702 + }, + { + "x": -0.0015227782641602516, + "y": 10.01744174472421 + }, + { + "x": -0.001522778264018143, + "y": 10.01744174472151 + }, + { + "x": -0.001218222611356623, + "y": 8.01395339577951 + }, + { + "x": -0.001218222611356623, + "y": 8.013953395779367 + }, + { + "x": -0.0012182226112145145, + "y": 8.013953395779367 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.001218222611356623, + "y": 8.013953395782352 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001522778264018143, + "y": 10.017441744721367 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782643023602, + "y": 10.017441744727194 + }, + { + "x": -0.0016750560907041745, + "y": 11.019185919196701 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919193859 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744727194 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570252001 + }, + { + "x": -0.001522778264018143, + "y": 10.017441744721367 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782641602516, + "y": 10.01744174472421 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 92.38194604879638, + "y": -78.5678346041396, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 336.1489057852639, + "y": -325.05033162122857, + "z": 1.0 + }, + { + "x": 336.5229456022105, + "y": -325.67646946787795, + "z": 1.0 + }, + { + "x": 336.7939565516582, + "y": -326.0799650034013, + "z": 1.0 + }, + { + "x": 337.15633618498657, + "y": -326.5676097729596, + "z": 1.0 + }, + { + "x": 337.5440052274156, + "y": -327.03539979664663, + "z": 1.0 + }, + { + "x": 337.9622085454788, + "y": -327.4877505784889, + "z": 1.0 + }, + { + "x": 338.3183308810072, + "y": -327.8342538022563, + "z": 1.0 + }, + { + "x": 338.8825055067306, + "y": -328.3211543814014, + "z": 1.0 + }, + { + "x": 339.378296205565, + "y": -328.69520423560505, + "z": 1.0 + }, + { + "x": 340.0013780611089, + "y": -329.10403038474396, + "z": 1.0 + }, + { + "x": 340.5418874628223, + "y": -329.4099335158296, + "z": 1.0 + }, + { + "x": 340.9871585419557, + "y": -329.63124209683366, + "z": 1.0 + }, + { + "x": 341.5575350479918, + "y": -329.8773357090277, + "z": 1.0 + }, + { + "x": 342.141456730006, + "y": -330.0889067021541, + "z": 1.0 + }, + { + "x": 342.8572627989721, + "y": -330.29624980319954, + "z": 1.0 + }, + { + "x": 343.5860716295812, + "y": -330.45182618187255, + "z": 1.0 + }, + { + "x": 344.07732448992476, + "y": -330.52637956177955, + "z": 1.0 + }, + { + "x": 344.57157639307513, + "y": -330.5773955476963, + "z": 1.0 + }, + { + "x": 345.31253722029027, + "y": -330.6095350149743, + "z": 1.0 + }, + { + "x": 345.82291093398845, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 346.2243261579756, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 346.6257413819628, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 347.02715660595004, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 347.528925635934, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 347.7296332479276, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 348.1305367405361, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 348.73136320009144, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 349.23205191638755, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 349.73274063268366, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 350.1332916057205, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 350.6339803220166, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 351.0345312950535, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 351.5352200113496, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 351.93577098438647, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 352.33632195742337, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 352.8370106737195, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 353.2375616467564, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 353.6381126197932, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 354.23893907934854, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 354.8397655389039, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 355.24031651194076, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 355.8411429714961, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 356.4419694310514, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 357.0427958906067, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 357.4433468636436, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 357.8438978366805, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 358.44472429623585, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 358.94541301253196, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 359.3459639855688, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 359.9467904451242, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 360.44747916142023, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 360.94816787771634, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 361.44885659401245, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 361.94954531030857, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 362.4502340266047, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 362.9509227429008, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 363.4516114591969, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 364.0524379187522, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 364.45298889178906, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 365.0538153513444, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 365.5545040676405, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 366.0551927839366, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 366.5558815002327, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 367.0565702165288, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 367.65739667608415, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 368.15808539238026, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 368.65877410867637, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 369.1594628249725, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 369.5600137980093, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 369.9605647710462, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 370.3611157440831, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 370.76166671712, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 371.16221769015687, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 371.662906406453, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 372.1635951227491, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 372.6642838390452, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 373.1649725553413, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 373.6656612716374, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 374.26648773119274, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 374.76717644748885, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 375.2678651637849, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 375.768553880081, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 376.36938033963634, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 376.76993131267324, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 377.17048228571014, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 377.571033258747, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 378.17185971830236, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 378.7726861778577, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 379.1732371508945, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 379.5737881239314, + "y": -330.6099853515625, + "z": 1.0 + }, + { + "x": 380.17461458348674, + "y": -330.6099853515625, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 9, + "heading": [ + -0.11553553829427615, + -1.0323083606255987, + -1.0111292333148185, + -0.9528825726285378, + -0.9052479255653736, + -0.8515053875159617, + -0.8009884773025137, + -0.7358901996185394, + -0.6821623094798233, + -0.6105251199603642, + -0.5508274551632518, + -0.4911113655175353, + -0.4313047534661909, + -0.3774745492893383, + -0.3117961589325643, + -0.24612873318359238, + -0.18643105564388768, + -0.1267334041301747, + -0.067219329230323, + -0.026038151534696963, + -0.0004939043817662466, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 3.7403981694660615, + "y": -6.261378466493852 + }, + { + "x": 6.450507663943199, + "y": -10.29633382172733 + }, + { + "x": 6.333905827760873, + "y": -8.911403050816489 + }, + { + "x": 7.500486757573981, + "y": -9.554347932453311 + }, + { + "x": 8.058723604922307, + "y": -9.201408055292859 + }, + { + "x": 7.743256535916316, + "y": -7.988540056096554 + }, + { + "x": 9.20296961251779, + "y": -8.334038029125281 + }, + { + "x": 10.599653245577656, + "y": -8.609504333487621 + }, + { + "x": 11.188725543782994, + "y": -7.82876003342551 + }, + { + "x": 11.635912572572806, + "y": -7.147292802245602 + }, + { + "x": 9.857804808468131, + "y": -5.272117120896951 + }, + { + "x": 10.156475851695177, + "y": -4.674021931980974 + }, + { + "x": 11.542981880502907, + "y": -4.576646053204172 + }, + { + "x": 12.997277509803098, + "y": -4.189140941718392 + }, + { + "x": 14.446148995752424, + "y": -3.6291947971847094 + }, + { + "x": 12.200616909526616, + "y": -2.3012975858000573 + }, + { + "x": 9.85504763493907, + "y": -1.2556936582376466 + }, + { + "x": 12.352127303655038, + "y": -0.8315545319476314 + }, + { + "x": 12.513345409133194, + "y": -0.32589803866187594 + }, + { + "x": 9.11788937685344, + "y": -0.004503365881873833 + }, + { + "x": 8.028304479743724, + "y": 0.0 + }, + { + "x": 8.028304479744293, + "y": 0.0 + }, + { + "x": 9.03184253971176, + "y": 0.0 + }, + { + "x": 7.024766419775688, + "y": 0.0 + }, + { + "x": 6.0161110460211376, + "y": 0.0 + }, + { + "x": 10.017299521638279, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 9.012396893329537, + "y": 0.0 + }, + { + "x": 9.012396893329537, + "y": 0.0 + }, + { + "x": 9.012396893330106, + "y": 0.0 + }, + { + "x": 9.012396893330106, + "y": 0.0 + }, + { + "x": 9.012396893329537, + "y": 0.0 + }, + { + "x": 8.01101946073743, + "y": 0.0 + }, + { + "x": 9.012396893330106, + "y": 0.0 + }, + { + "x": 9.012396893330106, + "y": 0.0 + }, + { + "x": 8.01101946073743, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 12.016529191106997, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 12.016529191106429, + "y": 0.0 + }, + { + "x": 12.016529191106429, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 8.011019460737998, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 9.012396893329537, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 9.012396893329537, + "y": 0.0 + }, + { + "x": 8.01101946073743, + "y": 0.0 + }, + { + "x": 8.011019460737998, + "y": 0.0 + }, + { + "x": 8.011019460737998, + "y": 0.0 + }, + { + "x": 8.01101946073743, + "y": 0.0 + }, + { + "x": 9.012396893329537, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 11.015151758514321, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 8.011019460737998, + "y": 0.0 + }, + { + "x": 8.01101946073743, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + }, + { + "x": 12.016529191106997, + "y": 0.0 + }, + { + "x": 10.013774325921645, + "y": 0.0 + }, + { + "x": 8.01101946073743, + "y": 0.0 + }, + { + "x": 10.013774325922213, + "y": 0.0 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 380.17461458348674, + "y": -330.6099853515625, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 334.75466558785854, + "y": -240.4847832109267, + "z": 1.0 + }, + { + "x": 334.7543028798573, + "y": -241.0852821771714, + "z": 1.0 + }, + { + "x": 334.75406107452307, + "y": -241.48561482133636, + "z": 1.0 + }, + { + "x": 334.75381926918885, + "y": -241.88594746550024, + "z": 1.0 + }, + { + "x": 334.75357746385475, + "y": -242.28628010966406, + "z": 1.0 + }, + { + "x": 334.75333565852054, + "y": -242.68661275382675, + "z": 1.0 + }, + { + "x": 334.7530334018528, + "y": -243.18702855903274, + "z": 1.0 + }, + { + "x": 334.75273114518507, + "y": -243.68744436423754, + "z": 1.0 + }, + { + "x": 334.7523684371838, + "y": -244.28794333048216, + "z": 1.0 + }, + { + "x": 334.75206618051607, + "y": -244.7883591356881, + "z": 1.0 + }, + { + "x": 334.75182437518185, + "y": -245.18869177985084, + "z": 1.0 + }, + { + "x": 334.7514616671806, + "y": -245.78919074609774, + "z": 1.0 + }, + { + "x": 334.75109895917933, + "y": -246.3896897123435, + "z": 1.0 + }, + { + "x": 334.7508571538451, + "y": -246.7900223565062, + "z": 1.0 + }, + { + "x": 334.7505548971774, + "y": -247.2904381617122, + "z": 1.0 + }, + { + "x": 334.7501921891761, + "y": -247.89093712795682, + "z": 1.0 + }, + { + "x": 334.7499503838419, + "y": -248.29126977212064, + "z": 1.0 + }, + { + "x": 334.74958767584064, + "y": -248.89176873836755, + "z": 1.0 + }, + { + "x": 334.7492249678394, + "y": -249.49226770461217, + "z": 1.0 + }, + { + "x": 334.74898316250517, + "y": -249.89260034877606, + "z": 1.0 + }, + { + "x": 334.74868090583743, + "y": -250.39301615398085, + "z": 1.0 + }, + { + "x": 334.7483786491697, + "y": -250.89343195918678, + "z": 1.0 + }, + { + "x": 334.74801594116843, + "y": -251.4939309254314, + "z": 1.0 + }, + { + "x": 334.74765323316717, + "y": -252.09442989167837, + "z": 1.0 + }, + { + "x": 334.74735097649943, + "y": -252.59484569688203, + "z": 1.0 + }, + { + "x": 334.74698826849806, + "y": -253.19534466312894, + "z": 1.0 + }, + { + "x": 334.7466860118303, + "y": -253.69576046833373, + "z": 1.0 + }, + { + "x": 334.7464442064962, + "y": -254.0960931124976, + "z": 1.0 + }, + { + "x": 334.7461419498285, + "y": -254.5965089177024, + "z": 1.0 + }, + { + "x": 334.7457792418271, + "y": -255.19700788394817, + "z": 1.0 + }, + { + "x": 334.74541653382585, + "y": -255.7975068501928, + "z": 1.0 + }, + { + "x": 334.7451142771581, + "y": -256.2979226553988, + "z": 1.0 + }, + { + "x": 334.7448120204904, + "y": -256.79833846060353, + "z": 1.0 + }, + { + "x": 334.74450976382263, + "y": -257.2987542658084, + "z": 1.0 + }, + { + "x": 334.7442075071549, + "y": -257.7991700710121, + "z": 1.0 + }, + { + "x": 334.7439657018208, + "y": -258.199502715177, + "z": 1.0 + }, + { + "x": 334.74366344515306, + "y": -258.6999185203807, + "z": 1.0 + }, + { + "x": 334.7433007371517, + "y": -259.3004174866277, + "z": 1.0 + }, + { + "x": 334.7430589318175, + "y": -259.70075013079145, + "z": 1.0 + }, + { + "x": 334.7428171264834, + "y": -260.1010827749542, + "z": 1.0 + }, + { + "x": 334.74251486981564, + "y": -260.6014985801602, + "z": 1.0 + }, + { + "x": 334.7421521618144, + "y": -261.20199754640475, + "z": 1.0 + }, + { + "x": 334.74184990514664, + "y": -261.7024133516096, + "z": 1.0 + }, + { + "x": 334.7415476484789, + "y": -262.2028291568156, + "z": 1.0 + }, + { + "x": 334.74118494047764, + "y": -262.80332812306017, + "z": 1.0 + }, + { + "x": 334.7409431351434, + "y": -263.20366076722405, + "z": 1.0 + }, + { + "x": 334.74058042714205, + "y": -263.8041597334709, + "z": 1.0 + }, + { + "x": 334.7402781704744, + "y": -264.30457553867575, + "z": 1.0 + }, + { + "x": 334.7400363651402, + "y": -264.70490818283963, + "z": 1.0 + }, + { + "x": 334.739794559806, + "y": -265.10524082700226, + "z": 1.0 + }, + { + "x": 334.73943185180474, + "y": -265.7057397932481, + "z": 1.0 + }, + { + "x": 334.7390691438035, + "y": -266.30623875949505, + "z": 1.0 + }, + { + "x": 334.73876688713574, + "y": -266.8066545646998, + "z": 1.0 + }, + { + "x": 334.73840417913436, + "y": -267.4071535309456, + "z": 1.0 + }, + { + "x": 334.7380414711331, + "y": -268.0076524971913, + "z": 1.0 + }, + { + "x": 334.73767876313184, + "y": -268.60815146343714, + "z": 1.0 + }, + { + "x": 334.7373160551306, + "y": -269.20865042968285, + "z": 1.0 + }, + { + "x": 334.73701379846284, + "y": -269.7090662348877, + "z": 1.0 + }, + { + "x": 334.7367719931286, + "y": -270.1093988790516, + "z": 1.0 + }, + { + "x": 334.7364697364609, + "y": -270.6098146842563, + "z": 1.0 + }, + { + "x": 334.7361070284596, + "y": -271.210313650501, + "z": 1.0 + }, + { + "x": 334.7358047717919, + "y": -271.710729455707, + "z": 1.0 + }, + { + "x": 334.7355629664578, + "y": -272.1110620998696, + "z": 1.0 + }, + { + "x": 334.73526070978994, + "y": -272.6114779050756, + "z": 1.0 + }, + { + "x": 334.7349584531222, + "y": -273.11189371028047, + "z": 1.0 + }, + { + "x": 334.73459574512094, + "y": -273.7123926765262, + "z": 1.0 + }, + { + "x": 334.73435393978684, + "y": -274.1127253206889, + "z": 1.0 + }, + { + "x": 334.73411213445263, + "y": -274.5130579648528, + "z": 1.0 + }, + { + "x": 334.7338098777849, + "y": -275.01347377005754, + "z": 1.0 + }, + { + "x": 334.7335680724507, + "y": -275.4138064142214, + "z": 1.0 + }, + { + "x": 334.73332626711647, + "y": -275.81413905838644, + "z": 1.0 + }, + { + "x": 334.73308446178225, + "y": -276.2144717025502, + "z": 1.0 + }, + { + "x": 334.7327822051145, + "y": -276.71488750775507, + "z": 1.0 + }, + { + "x": 334.7324799484468, + "y": -277.2153033129599, + "z": 1.0 + }, + { + "x": 334.7321172404455, + "y": -277.8158022792056, + "z": 1.0 + }, + { + "x": 334.7318754351114, + "y": -278.2161349233684, + "z": 1.0 + }, + { + "x": 334.7316336297772, + "y": -278.61646756753225, + "z": 1.0 + }, + { + "x": 334.73133137310947, + "y": -279.116883372737, + "z": 1.0 + }, + { + "x": 334.7309686651081, + "y": -279.7173823389828, + "z": 1.0 + }, + { + "x": 334.73060595710683, + "y": -280.31788130522966, + "z": 1.0 + }, + { + "x": 334.73024324910557, + "y": -280.91838027147435, + "z": 1.0 + }, + { + "x": 334.72994099243783, + "y": -281.4187960766802, + "z": 1.0 + }, + { + "x": 334.7296387357701, + "y": -281.9192118818862, + "z": 1.0 + }, + { + "x": 334.72927602776883, + "y": -282.51971084812976, + "z": 1.0 + }, + { + "x": 334.72891331976757, + "y": -283.12020981437547, + "z": 1.0 + }, + { + "x": 334.72867151443336, + "y": -283.5205424585405, + "z": 1.0 + }, + { + "x": 334.72842970909915, + "y": -283.9208751027044, + "z": 1.0 + }, + { + "x": 334.7281274524314, + "y": -284.4212909079091, + "z": 1.0 + }, + { + "x": 334.7278856470972, + "y": -284.821623552073, + "z": 1.0 + }, + { + "x": 334.72752293909593, + "y": -285.4221225183177, + "z": 1.0 + }, + { + "x": 334.7272811337617, + "y": -285.8224551624826, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 10, + "heading": [ + -1.5714003377550492, + -1.5714003377550179, + -1.5714003377550545, + -1.5714003377551105, + -1.5714003377549692, + -1.5714003377549701, + -1.5714003377550798, + -1.5714003377550538, + -1.5714003377550345, + -1.5714003377550338, + -1.5714003377550798, + -1.5714003377550545, + -1.571400337755016, + -1.5714003377550552, + -1.5714003377550798, + -1.5714003377550338, + -1.5714003377550552, + -1.5714003377550538, + -1.5714003377550168, + -1.5714003377550552, + -1.5714003377550798, + -1.5714003377550538, + -1.5714003377550338, + -1.5714003377550168, + -1.5714003377550338, + -1.571400337755137, + -1.5714003377551364, + -1.5714003377549535, + -1.5714003377549535, + -1.571400337755137, + -1.571400337755112, + -1.5714003377550338, + -1.5714003377550538, + -1.5714003377550545, + -1.5714003377550552, + -1.5714003377549537, + -1.5714003377549537, + -1.571400337755137, + -1.5714003377551675, + -1.5714003377549701, + -1.5714003377549535, + -1.5714003377550338, + -1.5714003377550345, + -1.5714003377550538, + -1.5714003377550338, + -1.5714003377550552, + -1.5714003377551675, + -1.5714003377550332, + -1.5714003377549535, + -1.5714003377551122, + -1.5714003377550552, + -1.571400337755016, + -1.5714003377550332, + -1.571400337755137, + -1.5714003377551113, + -1.5714003377550168, + -1.5714003377550168, + -1.5714003377550338, + -1.5714003377550798, + -1.5714003377550798, + -1.5714003377550345, + -1.5714003377550338, + -1.5714003377549537, + -1.5714003377550798, + -1.5714003377551673, + -1.5714003377550338, + -1.5714003377549417, + -1.5714003377549701, + -1.5714003377550798, + -1.5714003377550798, + -1.5714003377551105, + -1.5714003377551105, + -1.5714003377550798, + -1.5714003377550545, + -1.5714003377550338, + -1.5714003377549417, + -1.5714003377549701, + -1.5714003377550798, + -1.571400337755137, + -1.5714003377551107, + -1.5714003377550168, + -1.5714003377550338, + -1.5714003377550532, + -1.5714003377550345, + -1.5714003377550179, + -1.5714003377550538, + -1.5714003377551105, + -1.5714003377550798, + -1.5714003377550798, + -1.5714003377550552, + -1.5714003377550545 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.003627080012620354, + "y": -6.00498966244686 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610409648 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883288439 + }, + { + "x": -0.004836106683114849, + "y": -8.00665288327707 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883265133 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104107848 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714494247 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610409648 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932492669 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506184 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610410728 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932491532 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104085111 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610410728 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932491589 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506184 + }, + { + "x": -0.006649646691130329, + "y": -11.009147714505616 + }, + { + "x": -0.006649646691130329, + "y": -11.009147714516985 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493686775 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493686775 + }, + { + "x": -0.006649646691130329, + "y": -11.009147714505616 + }, + { + "x": -0.007254160026377576, + "y": -12.009979324903952 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506184 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610410728 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104095911 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610408568 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493686206 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493686206 + }, + { + "x": -0.006649646691130329, + "y": -11.009147714506753 + }, + { + "x": -0.006045133355883081, + "y": -10.00831610410728 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883265133 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493687343 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714494247 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104108417 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.006045133355883081, + "y": -10.00831610410728 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714516985 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493687343 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883265133 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.007254160025240708, + "y": -12.009979324927826 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714516985 + }, + { + "x": -0.006649646691130329, + "y": -11.009147714505616 + }, + { + "x": -0.007254160026377576, + "y": -12.00997932491532 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932491532 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932491532 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493687343 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714494247 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506753 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493686206 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.006045133355883081, + "y": -10.008316104108417 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.006045133353609344, + "y": -10.008316104084543 + }, + { + "x": -0.004836106683114849, + "y": -8.00665288326627 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883289007 + }, + { + "x": -0.0048361066842517175, + "y": -8.00665288328787 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104097048 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.006045133353609344, + "y": -10.008316104084543 + }, + { + "x": -0.004836106683114849, + "y": -8.00665288326627 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.006649646691130329, + "y": -11.009147714505616 + }, + { + "x": -0.007254160026377576, + "y": -12.00997932492669 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932491532 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104118649 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714495384 + }, + { + "x": -0.007254160025240708, + "y": -12.009979324892583 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610410728 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883289007 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610408568 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104095911 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 334.7272811337617, + "y": -285.8224551624826, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 158.0791310994979, + "y": -7.179605138451363, + "z": 1.0 + }, + { + "x": 158.11097082453202, + "y": -6.804332927747964, + "z": 1.0 + }, + { + "x": 158.15356090357636, + "y": -6.506044877657019, + "z": 1.0 + }, + { + "x": 158.24571299169025, + "y": -6.06363223592093, + "z": 1.0 + }, + { + "x": 158.37129529979165, + "y": -5.629523240288274, + "z": 1.0 + }, + { + "x": 158.52957898435005, + "y": -5.206238236024314, + "z": 1.0 + }, + { + "x": 158.68580526464018, + "y": -4.863544334243314, + "z": 1.0 + }, + { + "x": 158.82599564588895, + "y": -4.597510743033778, + "z": 1.0 + }, + { + "x": 159.02040646680712, + "y": -4.274940885637076, + "z": 1.0 + }, + { + "x": 159.19043770232992, + "y": -4.026182441576452, + "z": 1.0 + }, + { + "x": 159.3728867448101, + "y": -3.7863852726082063, + "z": 1.0 + }, + { + "x": 159.66581715772864, + "y": -3.4485299588337934, + "z": 1.0 + }, + { + "x": 159.86664884961996, + "y": -3.248066137378774, + "z": 1.0 + }, + { + "x": 160.07906496134046, + "y": -3.059920311173475, + "z": 1.0 + }, + { + "x": 160.4177510798241, + "y": -2.8022456274618905, + "z": 1.0 + }, + { + "x": 160.77809586609953, + "y": -2.575834518513972, + "z": 1.0 + }, + { + "x": 161.09221276714152, + "y": -2.4128186939734793, + "z": 1.0 + }, + { + "x": 161.41831692152203, + "y": -2.273349780093421, + "z": 1.0 + }, + { + "x": 161.75389318218015, + "y": -2.1585414349510534, + "z": 1.0 + }, + { + "x": 162.16646585430743, + "y": -2.054208769202902, + "z": 1.0 + }, + { + "x": 162.5867055330603, + "y": -1.9871276453476843, + "z": 1.0 + }, + { + "x": 162.94034406743444, + "y": -1.960078107492641, + "z": 1.0 + }, + { + "x": 163.44525098561672, + "y": -1.9564427538507048, + "z": 1.0 + }, + { + "x": 163.94900597132929, + "y": -1.956496519249997, + "z": 1.0 + }, + { + "x": 164.45276095704185, + "y": -1.9565502846492888, + "z": 1.0 + }, + { + "x": 165.05726693989692, + "y": -1.9566148031284396, + "z": 1.0 + }, + { + "x": 165.66177292275202, + "y": -1.95667932160759, + "z": 1.0 + }, + { + "x": 166.06477691132207, + "y": -1.9567223339270237, + "z": 1.0 + }, + { + "x": 166.66928289417714, + "y": -1.956786852406174, + "z": 1.0 + }, + { + "x": 167.17303787988973, + "y": -1.9568406178054727, + "z": 1.0 + }, + { + "x": 167.7775438627448, + "y": -1.9569051362846233, + "z": 1.0 + }, + { + "x": 168.07979685417234, + "y": -1.9569373955241984, + "z": 1.0 + }, + { + "x": 168.480178046297, + "y": -1.9569801279145151, + "z": 1.0 + }, + { + "x": 169.080749834484, + "y": -1.9570442264999608, + "z": 1.0 + }, + { + "x": 169.58122632463983, + "y": -1.9570976419878323, + "z": 1.0 + }, + { + "x": 170.18179811282684, + "y": -1.957161740573278, + "z": 1.0 + }, + { + "x": 170.5821793049515, + "y": -1.957204472963575, + "z": 1.0 + }, + { + "x": 171.18275109313848, + "y": -1.957268571549021, + "z": 1.0 + }, + { + "x": 171.68322758329433, + "y": -1.9573219870368925, + "z": 1.0 + }, + { + "x": 172.08360877541898, + "y": -1.9573647194271895, + "z": 1.0 + }, + { + "x": 172.58408526557483, + "y": -1.957418134915061, + "z": 1.0 + }, + { + "x": 173.08456175573065, + "y": -1.9574715504029325, + "z": 1.0 + }, + { + "x": 173.58503824588647, + "y": -1.957524965890804, + "z": 1.0 + }, + { + "x": 174.0855147360423, + "y": -1.9575783813786751, + "z": 1.0 + }, + { + "x": 174.48589592816697, + "y": -1.9576211137689725, + "z": 1.0 + }, + { + "x": 174.9863724183228, + "y": -1.957674529256844, + "z": 1.0 + }, + { + "x": 175.5869442065098, + "y": -1.9577386278422897, + "z": 1.0 + }, + { + "x": 175.98732539863445, + "y": -1.9577813602325866, + "z": 1.0 + }, + { + "x": 176.58789718682146, + "y": -1.9578454588180327, + "z": 1.0 + }, + { + "x": 177.18846897500845, + "y": -1.9579095574034784, + "z": 1.0 + }, + { + "x": 177.6889454651643, + "y": -1.95796297289135, + "z": 1.0 + }, + { + "x": 178.28951725335128, + "y": -1.9580270714767956, + "z": 1.0 + }, + { + "x": 178.7899937435071, + "y": -1.958080486964667, + "z": 1.0 + }, + { + "x": 179.3905655316941, + "y": -1.9581445855501127, + "z": 1.0 + }, + { + "x": 179.9911373198811, + "y": -1.9582086841355584, + "z": 1.0 + }, + { + "x": 180.49161381003694, + "y": -1.95826209962343, + "z": 1.0 + }, + { + "x": 181.09218559822392, + "y": -1.9583261982088755, + "z": 1.0 + }, + { + "x": 181.69275738641093, + "y": -1.9583902967943212, + "z": 1.0 + }, + { + "x": 182.19323387656675, + "y": -1.9584437122821927, + "z": 1.0 + }, + { + "x": 182.59361506869143, + "y": -1.9584864446724901, + "z": 1.0 + }, + { + "x": 183.19418685687842, + "y": -1.9585505432579358, + "z": 1.0 + }, + { + "x": 183.7947586450654, + "y": -1.9586146418433814, + "z": 1.0 + }, + { + "x": 184.3953304332524, + "y": -1.958678740428827, + "z": 1.0 + }, + { + "x": 184.79571162537707, + "y": -1.9587214728191245, + "z": 1.0 + }, + { + "x": 185.2961881155329, + "y": -1.9587748883069955, + "z": 1.0 + }, + { + "x": 185.8967599037199, + "y": -1.9588389868924416, + "z": 1.0 + }, + { + "x": 186.39723639387574, + "y": -1.9588924023803127, + "z": 1.0 + }, + { + "x": 186.7976175860004, + "y": -1.95893513477061, + "z": 1.0 + }, + { + "x": 187.39818937418738, + "y": -1.9589992333560557, + "z": 1.0 + }, + { + "x": 187.9987611623744, + "y": -1.9590633319415014, + "z": 1.0 + }, + { + "x": 188.4992376525302, + "y": -1.959116747429373, + "z": 1.0 + }, + { + "x": 189.09980944071722, + "y": -1.9591808460148186, + "z": 1.0 + }, + { + "x": 189.50019063284188, + "y": -1.959223578405116, + "z": 1.0 + }, + { + "x": 190.0006671229977, + "y": -1.9592769938929875, + "z": 1.0 + }, + { + "x": 190.40104831512238, + "y": -1.9593197262832844, + "z": 1.0 + }, + { + "x": 190.9015248052782, + "y": -1.959373141771156, + "z": 1.0 + }, + { + "x": 191.30190599740286, + "y": -1.959415874161453, + "z": 1.0 + }, + { + "x": 191.8023824875587, + "y": -1.9594692896493244, + "z": 1.0 + }, + { + "x": 192.30285897771452, + "y": -1.959522705137196, + "z": 1.0 + }, + { + "x": 192.80333546787037, + "y": -1.9595761206250675, + "z": 1.0 + }, + { + "x": 193.3038119580262, + "y": -1.959629536112939, + "z": 1.0 + }, + { + "x": 193.90438374621317, + "y": -1.9596936346983846, + "z": 1.0 + }, + { + "x": 194.40486023636902, + "y": -1.9597470501862562, + "z": 1.0 + }, + { + "x": 194.90533672652484, + "y": -1.9598004656741277, + "z": 1.0 + }, + { + "x": 195.50590851471185, + "y": -1.9598645642595733, + "z": 1.0 + }, + { + "x": 195.9062897068365, + "y": -1.9599072966498703, + "z": 1.0 + }, + { + "x": 196.50686149502351, + "y": -1.9599713952353164, + "z": 1.0 + }, + { + "x": 197.00733798517933, + "y": -1.9600248107231875, + "z": 1.0 + }, + { + "x": 197.60790977336632, + "y": -1.9600889093086336, + "z": 1.0 + }, + { + "x": 198.00829096549103, + "y": -1.9601316416989305, + "z": 1.0 + }, + { + "x": 198.60886275367812, + "y": -1.9601957402843762, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 11, + "heading": [ + 0.19935498835074622, + 1.4861546924418993, + 1.4607407580935998, + 1.3908522439327338, + 1.3273172116869896, + 1.2510751549492403, + 1.1811866373806958, + 1.1176473528504982, + 1.053886187741048, + 1.0029846256276123, + 0.9458030512447957, + 0.8822169386900508, + 0.828543390212532, + 0.7546814735401104, + 0.6801814600203556, + 0.6056813147480384, + 0.5236280170446252, + 0.44138141729989416, + 0.3668904708916304, + 0.2849404521945748, + 0.20299041185484692, + 0.12104038438677348, + 0.035725349092794956, + 0.0035508163599969026, + -0.00010672926444951508, + -0.00010672926444963529, + -0.00010672926444973295, + -0.00010672926444951205, + -0.00010672926444951508, + -0.00010672926445544282, + -0.00010672926445564316, + -0.00010672926444961302, + -0.00010672926447730693, + -0.00010672926446900236, + -0.00010672926444954181, + -0.00010672926444954181, + -0.0001067292644492592, + -0.0001067292644497059, + -0.00010672926444994514, + -0.00010672926444940675, + -0.00010672926444940675, + -0.00010672926444970287, + -0.0001067292644497059, + -0.0001067292644492592, + -0.00010672926444940675, + -0.00010672926444990308, + -0.00010672926444954181, + -0.0001067292644492592, + -0.00010672926444970287, + -0.00010672926444977732, + -0.00010672926444954181, + -0.00010672926444954181, + -0.00010672926444954457, + -0.00010672926444954181, + -0.00010672926444940759, + -0.00010672926444954181, + -0.00010672926444954181, + -0.00010672926444940759, + -0.00010672926444954181, + -0.00010672926444989972, + -0.00010672926444970287, + -0.00010672926444941012, + -0.00010672926444940759, + -0.00010672926444970287, + -0.00010672926444940675, + -0.00010672926444954181, + -0.00010672926444954181, + -0.00010672926444940675, + -0.0001067292644497059, + -0.00010672926444940759, + -0.00010672926444954181, + -0.00010672926444954181, + -0.00010672926444970287, + -0.00010672926444990308, + -0.00010672926444940675, + -0.00010672926444940675, + -0.00010672926444941012, + -0.00010672926444940675, + -0.00010672926444970287, + -0.00010672926444970287, + -0.00010672926444970287, + -0.00010672926444954457, + -0.00010672926444954181, + -0.00010672926444970287, + -0.00010672926444954181, + -0.0001067292644492592, + -0.00010672926444970287, + -0.00010672926444954181, + -0.00010672926444954457, + -0.00010672926444969984, + -0.00010672926444924405 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.31839725034103594, + "y": 3.7527221070339856 + }, + { + "x": 0.7442980407844857, + "y": 6.735602607943436 + }, + { + "x": 1.3474216715823673, + "y": 7.407006918270342 + }, + { + "x": 2.177343962152918, + "y": 8.765216373687448 + }, + { + "x": 2.838659926597984, + "y": 8.573939998966162 + }, + { + "x": 3.1450996484852567, + "y": 7.659789060449604 + }, + { + "x": 2.96416661538899, + "y": 6.087274929905355 + }, + { + "x": 3.3460120216693667, + "y": 5.886034486062384 + }, + { + "x": 3.644420564409643, + "y": 5.7132830145732605 + }, + { + "x": 3.5248027800298587, + "y": 4.885556130288693 + }, + { + "x": 4.753794553987234, + "y": 5.776524827426588 + }, + { + "x": 4.937621048098606, + "y": 5.383191352294325 + }, + { + "x": 4.132478036118243, + "y": 3.8860964766031847 + }, + { + "x": 5.511022302041226, + "y": 4.458205099168833 + }, + { + "x": 6.990309047590699, + "y": 4.840857926595028 + }, + { + "x": 6.744616873174323, + "y": 3.894269334884113 + }, + { + "x": 6.402210554225007, + "y": 3.0248473842055112 + }, + { + "x": 6.616804150386315, + "y": 2.542772590224258 + }, + { + "x": 7.4814893278539785, + "y": 2.191410108905192 + }, + { + "x": 8.328123508801468, + "y": 1.7141378960336917 + }, + { + "x": 7.738782131270057, + "y": 0.9413066171026085 + }, + { + "x": 8.585454525564273, + "y": 0.30684891496979505 + }, + { + "x": 10.08661903894847, + "y": 0.03581588242643985 + }, + { + "x": 10.075099714251223, + "y": -0.0010753079858405457 + }, + { + "x": 11.082609685676346, + "y": -0.0011828387844259325 + }, + { + "x": 12.090119657101752, + "y": -0.0012903695830113193 + }, + { + "x": 10.075099714251508, + "y": -0.0010753079858405457 + }, + { + "x": 10.075099714251223, + "y": -0.0010753079858405457 + }, + { + "x": 11.08260968567663, + "y": -0.0011828387844903254 + }, + { + "x": 11.08260968567663, + "y": -0.0011828387844925459 + }, + { + "x": 9.067589742826101, + "y": -0.0009677771872573793 + }, + { + "x": 7.026341835521919, + "y": -0.0007499162989188513 + }, + { + "x": 10.009529803116664, + "y": -0.00106830975762362 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574260003 + }, + { + "x": 10.00952980311638, + "y": -0.0010683097574304412 + }, + { + "x": 11.010482783428301, + "y": -0.0011751407331761499 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 10.00952980311638, + "y": -0.0010683097574304412 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574260003 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 9.008576822804741, + "y": -0.0009614787816891734 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574260003 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 12.01143576373994, + "y": -0.0012819717089174176 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428017, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 12.01143576373994, + "y": -0.0012819717089129767 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 12.01143576373994, + "y": -0.0012819717089129767 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816891734 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 12.011435763739655, + "y": -0.0012819717089129767 + }, + { + "x": 12.01143576373994, + "y": -0.0012819717089129767 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 10.00952980311638, + "y": -0.0010683097574304412 + }, + { + "x": 12.01143576373994, + "y": -0.0012819717089129767 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 9.008576822804741, + "y": -0.0009614787816891734 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 9.008576822804741, + "y": -0.0009614787816847326 + }, + { + "x": 9.008576822805026, + "y": -0.0009614787816847326 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 11.010482783428017, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574260003 + }, + { + "x": 10.009529803116664, + "y": -0.0010683097574304412 + }, + { + "x": 11.010482783428301, + "y": -0.001175140733171709 + }, + { + "x": 11.010482783428017, + "y": -0.001175140733171709 + }, + { + "x": 10.009529803116948, + "y": -0.0010683097574304412 + }, + { + "x": 10.009529803118085, + "y": -0.0010683097574260003 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 198.60886275367812, + "y": -1.9601957402843762, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": -2.0249086705307424, + "y": -8.280471528793488, + "z": 1.0 + }, + { + "x": -2.029612009939554, + "y": -8.952394399315889, + "z": 1.0 + }, + { + "x": -2.0297671463414297, + "y": -9.355128488453571, + "z": 1.0 + }, + { + "x": -2.029922282743306, + "y": -9.757862577591254, + "z": 1.0 + }, + { + "x": -2.029999850944244, + "y": -9.959229622160095, + "z": 1.0 + }, + { + "x": -2.030231080992902, + "y": -10.5595028133723, + "z": 1.0 + }, + { + "x": -2.0304623110415596, + "y": -11.159776004584504, + "z": 1.0 + }, + { + "x": -2.0306164644073315, + "y": -11.559958132059307, + "z": 1.0 + }, + { + "x": -2.0308476944559883, + "y": -12.160231323271512, + "z": 1.0 + }, + { + "x": -2.0310403861632036, + "y": -12.660458982615017, + "z": 1.0 + }, + { + "x": -2.031233077870418, + "y": -13.16068664195852, + "z": 1.0 + }, + { + "x": -2.03138723123619, + "y": -13.560868769433325, + "z": 1.0 + }, + { + "x": -2.0316184612848476, + "y": -14.16114196064553, + "z": 1.0 + }, + { + "x": -2.0317726146506194, + "y": -14.561324088120333, + "z": 1.0 + }, + { + "x": -2.0319267680163913, + "y": -14.961506215595136, + "z": 1.0 + }, + { + "x": -2.032157998065049, + "y": -15.561779406807341, + "z": 1.0 + }, + { + "x": -2.032312151430821, + "y": -15.961961534282144, + "z": 1.0 + }, + { + "x": -2.032466304796592, + "y": -16.36214366175695, + "z": 1.0 + }, + { + "x": -2.0326975348452496, + "y": -16.962416852969152, + "z": 1.0 + }, + { + "x": -2.0329287648939074, + "y": -17.56269004418136, + "z": 1.0 + }, + { + "x": -2.033159994942565, + "y": -18.162963235393562, + "z": 1.0 + }, + { + "x": -2.0333526866497795, + "y": -18.66319089473706, + "z": 1.0 + }, + { + "x": -2.033545378356995, + "y": -19.16341855408056, + "z": 1.0 + }, + { + "x": -2.033738070064209, + "y": -19.66364621342406, + "z": 1.0 + }, + { + "x": -2.0339307617714244, + "y": -20.16387387276756, + "z": 1.0 + }, + { + "x": -2.0341619918200813, + "y": -20.76414706397976, + "z": 1.0 + }, + { + "x": -2.034393221868739, + "y": -21.36442025519196, + "z": 1.0 + }, + { + "x": -2.034624451917397, + "y": -21.96469344640416, + "z": 1.0 + }, + { + "x": -2.0348556819660546, + "y": -22.56496663761636, + "z": 1.0 + }, + { + "x": -2.0350869120147124, + "y": -23.16523982882856, + "z": 1.0 + }, + { + "x": -2.035241065380484, + "y": -23.56542195630336, + "z": 1.0 + }, + { + "x": -2.0354337570876986, + "y": -24.06564961564686, + "z": 1.0 + }, + { + "x": -2.035626448794913, + "y": -24.56587727499036, + "z": 1.0 + }, + { + "x": -2.035780602160685, + "y": -24.96605940246516, + "z": 1.0 + }, + { + "x": -2.0359732938679, + "y": -25.466287061808657, + "z": 1.0 + }, + { + "x": -2.036204523916558, + "y": -26.06656025302086, + "z": 1.0 + }, + { + "x": -2.0364357539652147, + "y": -26.666833444233074, + "z": 1.0 + }, + { + "x": -2.03662844567243, + "y": -27.167061103576582, + "z": 1.0 + }, + { + "x": -2.036782599038202, + "y": -27.567243231051386, + "z": 1.0 + }, + { + "x": -2.0369367524039736, + "y": -27.967425358526196, + "z": 1.0 + }, + { + "x": -2.037129444111188, + "y": -28.467653017869704, + "z": 1.0 + }, + { + "x": -2.03728359747696, + "y": -28.867835145344507, + "z": 1.0 + }, + { + "x": -2.0374377508427317, + "y": -29.268017272819318, + "z": 1.0 + }, + { + "x": -2.037630442549946, + "y": -29.768244932162826, + "z": 1.0 + }, + { + "x": -2.0378231342571613, + "y": -30.268472591506335, + "z": 1.0 + }, + { + "x": -2.0380158259643757, + "y": -30.768700250849843, + "z": 1.0 + }, + { + "x": -2.0381699793301475, + "y": -31.16888237832465, + "z": 1.0 + }, + { + "x": -2.0383241326959194, + "y": -31.569064505799457, + "z": 1.0 + }, + { + "x": -2.038555362744577, + "y": -32.16933769701166, + "z": 1.0 + }, + { + "x": -2.038709516110349, + "y": -32.56951982448648, + "z": 1.0 + }, + { + "x": -2.038940746159006, + "y": -33.16979301569869, + "z": 1.0 + }, + { + "x": -2.039133437866221, + "y": -33.670020675042196, + "z": 1.0 + }, + { + "x": -2.0393261295734355, + "y": -34.170248334385704, + "z": 1.0 + }, + { + "x": -2.0394802829392074, + "y": -34.57043046186051, + "z": 1.0 + }, + { + "x": -2.039634436304979, + "y": -34.97061258933532, + "z": 1.0 + }, + { + "x": -2.039865666353637, + "y": -35.57088578054753, + "z": 1.0 + }, + { + "x": -2.040019819719409, + "y": -35.971067908022334, + "z": 1.0 + }, + { + "x": -2.0401739730851807, + "y": -36.37125003549714, + "z": 1.0 + }, + { + "x": -2.0403281264509525, + "y": -36.77143216297195, + "z": 1.0 + }, + { + "x": -2.0405593564996094, + "y": -37.37170535418416, + "z": 1.0 + }, + { + "x": -2.0407520482068247, + "y": -37.871933013527666, + "z": 1.0 + }, + { + "x": -2.0409062015725965, + "y": -38.27211514100247, + "z": 1.0 + }, + { + "x": -2.041098893279811, + "y": -38.77234280034598, + "z": 1.0 + }, + { + "x": -2.0412915849870252, + "y": -39.27257045968949, + "z": 1.0 + }, + { + "x": -2.0414842766942405, + "y": -39.772798119033, + "z": 1.0 + }, + { + "x": -2.041676968401455, + "y": -40.27302577837651, + "z": 1.0 + }, + { + "x": -2.04186966010867, + "y": -40.773253437720015, + "z": 1.0 + }, + { + "x": -2.042100890157327, + "y": -41.373526628932225, + "z": 1.0 + }, + { + "x": -2.0422935818645422, + "y": -41.873754288275734, + "z": 1.0 + }, + { + "x": -2.042486273571757, + "y": -42.37398194761924, + "z": 1.0 + }, + { + "x": -2.042678965278972, + "y": -42.87420960696275, + "z": 1.0 + }, + { + "x": -2.0428331186447437, + "y": -43.27439173443756, + "z": 1.0 + }, + { + "x": -2.043025810351958, + "y": -43.774619393781066, + "z": 1.0 + }, + { + "x": -2.04317996371773, + "y": -44.17480152125587, + "z": 1.0 + }, + { + "x": -2.0434111937663877, + "y": -44.77507471246808, + "z": 1.0 + }, + { + "x": -2.0436424238150455, + "y": -45.37534790368029, + "z": 1.0 + }, + { + "x": -2.0438736538637023, + "y": -45.97562109489251, + "z": 1.0 + }, + { + "x": -2.0440278072294746, + "y": -46.37580322236731, + "z": 1.0 + }, + { + "x": -2.0441819605952465, + "y": -46.775985349842124, + "z": 1.0 + }, + { + "x": -2.044336113961018, + "y": -47.17616747731693, + "z": 1.0 + }, + { + "x": -2.0445288056682327, + "y": -47.67639513666044, + "z": 1.0 + }, + { + "x": -2.0447214973754475, + "y": -48.17662279600395, + "z": 1.0 + }, + { + "x": -2.0449527274241053, + "y": -48.77689598721615, + "z": 1.0 + }, + { + "x": -2.045183957472763, + "y": -49.37716917842837, + "z": 1.0 + }, + { + "x": -2.0453766491799774, + "y": -49.877396837771876, + "z": 1.0 + }, + { + "x": -2.045569340887192, + "y": -50.377624497115384, + "z": 1.0 + }, + { + "x": -2.045762032594407, + "y": -50.87785215645889, + "z": 1.0 + }, + { + "x": -2.045954724301622, + "y": -51.378079815802394, + "z": 1.0 + }, + { + "x": -2.0461474160088358, + "y": -51.87830747514591, + "z": 1.0 + }, + { + "x": -2.0463786460574935, + "y": -52.47858066635812, + "z": 1.0 + }, + { + "x": -2.0466098761061513, + "y": -53.07885385757033, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 12, + "heading": [ + -1.5713009816235572, + -1.577796032862566, + -1.5753172510457183, + -1.5711815347978622, + -1.5711815347978622, + -1.5711815347978622, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978615, + -1.571181534797862, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978613, + -1.5711815347978615, + -1.5711815347978624, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978617, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978615, + -1.571181534797862, + -1.5711815347978628, + -1.5711815347978628, + -1.5711815347978617, + -1.571181534797862, + -1.5711815347978628, + -1.5711815347978624, + -1.571181534797862, + -1.571181534797862, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978624, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978615, + -1.571181534797862, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978615, + -1.571181534797862, + -1.5711815347978628, + -1.571181534797862, + -1.5711815347978615, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.571181534797862, + -1.571181534797862, + -1.5711815347978628, + -1.5711815347978624, + -1.5711815347978624, + -1.571181534797862, + -1.571181534797862, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978617, + -1.571181534797862, + -1.5711815347978628, + -1.5711815347978617, + -1.571181534797862, + -1.5711815347978624, + -1.5711815347978624, + -1.5711815347978624, + -1.571181534797862, + -1.5711815347978615, + -1.5711815347978624, + -1.5711815347978628, + -1.5711815347978615, + -1.5711815347978615, + -1.5711815347978624 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.04703339408811402, + "y": -6.719228705224012 + }, + { + "x": -0.04858475810687324, + "y": -10.746569596600839 + }, + { + "x": -0.0031027280375228727, + "y": -8.054681782753654 + }, + { + "x": -0.0023270460281432648, + "y": -6.041011337065232 + }, + { + "x": -0.00308798249595732, + "y": -8.016402357810453 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244096 + }, + { + "x": -0.0038538341442961865, + "y": -10.00455318687008 + }, + { + "x": -0.0038538341442873048, + "y": -10.00455318687008 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557097 + }, + { + "x": -0.0038538341442961865, + "y": -10.00455318687008 + }, + { + "x": -0.003468450729862127, + "y": -9.00409786818308 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870098 + }, + { + "x": -0.0038538341442961865, + "y": -10.00455318687008 + }, + { + "x": -0.0030830673154369492, + "y": -8.003642549496064 + }, + { + "x": -0.0038538341442961865, + "y": -10.00455318687008 + }, + { + "x": -0.0038538341442961865, + "y": -10.00455318687008 + }, + { + "x": -0.0030830673154280674, + "y": -8.003642549496082 + }, + { + "x": -0.0038538341442873048, + "y": -10.00455318687008 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244096 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244096 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557008 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186869991 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186869991 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186869991 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557008 + }, + { + "x": -0.004624600973146542, + "y": -12.00546382424399 + }, + { + "x": -0.004624600973155424, + "y": -12.00546382424399 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244025 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244025 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186869991 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868182974 + }, + { + "x": -0.0038538341442873048, + "y": -10.004553186869991 + }, + { + "x": -0.003468450729862127, + "y": -9.00409786818301 + }, + { + "x": -0.0034684507298710088, + "y": -9.004097868182974 + }, + { + "x": -0.004239217558730246, + "y": -11.005008505557008 + }, + { + "x": -0.004624600973146542, + "y": -12.005463824244167 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557221 + }, + { + "x": -0.0034684507298710088, + "y": -9.004097868183116 + }, + { + "x": -0.0030830673154369492, + "y": -8.003642549496135 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183187 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183116 + }, + { + "x": -0.0030830673154369492, + "y": -8.003642549496135 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183187 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183152 + }, + { + "x": -0.0030830673154369492, + "y": -8.003642549496135 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870133 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870204 + }, + { + "x": -0.0038538341442873048, + "y": -10.00455318687024 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557186 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183152 + }, + { + "x": -0.0030830673154369492, + "y": -8.003642549496135 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.0030830673154369492, + "y": -8.003642549496135 + }, + { + "x": -0.0030830673154369492, + "y": -8.003642549496135 + }, + { + "x": -0.0038538341442873048, + "y": -10.004553186870169 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557186 + }, + { + "x": -0.0034684507298710088, + "y": -9.004097868183152 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183152 + }, + { + "x": -0.0038538341442873048, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557186 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557186 + }, + { + "x": -0.0038538341443006274, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.003468450729866568, + "y": -9.004097868183152 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183152 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183152 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244202 + }, + { + "x": -0.004624600973146542, + "y": -12.005463824244273 + }, + { + "x": -0.0038538341442917456, + "y": -10.004553186870169 + }, + { + "x": -0.00308306731544139, + "y": -8.003642549496135 + }, + { + "x": -0.0030830673154325083, + "y": -8.003642549496206 + }, + { + "x": -0.003468450729862127, + "y": -9.004097868183152 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.004239217558725805, + "y": -11.005008505557115 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244202 + }, + { + "x": -0.004239217558721364, + "y": -11.005008505557257 + }, + { + "x": -0.0038538341442873048, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341442961865, + "y": -10.004553186870169 + }, + { + "x": -0.0038538341443006274, + "y": -10.004553186870098 + }, + { + "x": -0.0038538341442873048, + "y": -10.004553186870169 + }, + { + "x": -0.004239217558716923, + "y": -11.005008505557257 + }, + { + "x": -0.004624600973155424, + "y": -12.005463824244202 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": -2.0466098761061513, + "y": -53.07885385757033, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 338.85713578381797, + "y": -70.83644799954263, + "z": 1.0 + }, + { + "x": 338.85743867061865, + "y": -70.33498894704724, + "z": 1.0 + }, + { + "x": 338.8578021347794, + "y": -69.73323808405277, + "z": 1.0 + }, + { + "x": 338.8580444442198, + "y": -69.33207084205532, + "z": 1.0 + }, + { + "x": 338.85822617630015, + "y": -69.03119541055808, + "z": 1.0 + }, + { + "x": 338.8584689618465, + "y": -68.62923992832572, + "z": 1.0 + }, + { + "x": 338.8587117473928, + "y": -68.22728444609271, + "z": 1.0 + }, + { + "x": 338.85895453293904, + "y": -67.82532896385973, + "z": 1.0 + }, + { + "x": 338.85931871125854, + "y": -67.22239574051028, + "z": 1.0 + }, + { + "x": 338.85956149680476, + "y": -66.82044025827727, + "z": 1.0 + }, + { + "x": 338.85992567512415, + "y": -66.21750703492668, + "z": 1.0 + }, + { + "x": 338.86028985344365, + "y": -65.61457381157831, + "z": 1.0 + }, + { + "x": 338.8605933353765, + "y": -65.11212945878708, + "z": 1.0 + }, + { + "x": 338.86089681730937, + "y": -64.60968510599582, + "z": 1.0 + }, + { + "x": 338.86126099562875, + "y": -64.00675188264634, + "z": 1.0 + }, + { + "x": 338.8615037811751, + "y": -63.604796400412226, + "z": 1.0 + }, + { + "x": 338.8617465667213, + "y": -63.202840918180364, + "z": 1.0 + }, + { + "x": 338.86205004865417, + "y": -62.70039656538912, + "z": 1.0 + }, + { + "x": 338.86235353058703, + "y": -62.197952212596746, + "z": 1.0 + }, + { + "x": 338.8626570125199, + "y": -61.69550785980665, + "z": 1.0 + }, + { + "x": 338.86296049445286, + "y": -61.19306350701542, + "z": 1.0 + }, + { + "x": 338.86332467277225, + "y": -60.59013028366594, + "z": 1.0 + }, + { + "x": 338.8636281547051, + "y": -60.087685930874706, + "z": 1.0 + }, + { + "x": 338.863931636638, + "y": -59.585241578083476, + "z": 1.0 + }, + { + "x": 338.86429581495736, + "y": -58.982308354733995, + "z": 1.0 + }, + { + "x": 338.8645992968902, + "y": -58.479864001942765, + "z": 1.0 + }, + { + "x": 338.86484208243655, + "y": -58.07790851970978, + "z": 1.0 + }, + { + "x": 338.8650848679828, + "y": -57.67595303747565, + "z": 1.0 + }, + { + "x": 338.86538834991563, + "y": -57.17350868468442, + "z": 1.0 + }, + { + "x": 338.86563113546197, + "y": -56.77155320245143, + "z": 1.0 + }, + { + "x": 338.86599531378135, + "y": -56.16861997910308, + "z": 1.0 + }, + { + "x": 338.8662987957142, + "y": -55.66617562631184, + "z": 1.0 + }, + { + "x": 338.86654158126055, + "y": -55.26422014407885, + "z": 1.0 + }, + { + "x": 338.86690575957994, + "y": -54.66128692072936, + "z": 1.0 + }, + { + "x": 338.86726993789944, + "y": -54.05835369737988, + "z": 1.0 + }, + { + "x": 338.86751272344566, + "y": -53.65639821514689, + "z": 1.0 + }, + { + "x": 338.867755508992, + "y": -53.25444273291391, + "z": 1.0 + }, + { + "x": 338.8681196873114, + "y": -52.65150950956443, + "z": 1.0 + }, + { + "x": 338.8683624728577, + "y": -52.24955402733144, + "z": 1.0 + }, + { + "x": 338.86860525840405, + "y": -51.84759854509846, + "z": 1.0 + }, + { + "x": 338.86896943672343, + "y": -51.244665321747846, + "z": 1.0 + }, + { + "x": 338.8692729186563, + "y": -50.74222096895887, + "z": 1.0 + }, + { + "x": 338.86957640058915, + "y": -50.23977661616764, + "z": 1.0 + }, + { + "x": 338.869879882522, + "y": -49.73733226337527, + "z": 1.0 + }, + { + "x": 338.8701833644549, + "y": -49.2348879105829, + "z": 1.0 + }, + { + "x": 338.8704261500012, + "y": -48.83293242835218, + "z": 1.0 + }, + { + "x": 338.8707903283206, + "y": -48.22999920500157, + "z": 1.0 + }, + { + "x": 338.87103311386693, + "y": -47.828043722768584, + "z": 1.0 + }, + { + "x": 338.8713365957998, + "y": -47.32559936997735, + "z": 1.0 + }, + { + "x": 338.871579381346, + "y": -46.92364388774436, + "z": 1.0 + }, + { + "x": 338.87182216689234, + "y": -46.52168840551025, + "z": 1.0 + }, + { + "x": 338.87188286327887, + "y": -46.42119953495314, + "z": 1.0 + }, + { + "x": 338.8722465969555, + "y": -45.81900246170348, + "z": 1.0 + }, + { + "x": 338.87261033063214, + "y": -45.21680538845446, + "z": 1.0 + }, + { + "x": 338.8729740643088, + "y": -44.61460831520546, + "z": 1.0 + }, + { + "x": 338.8733377979855, + "y": -44.01241124195758, + "z": 1.0 + }, + { + "x": 338.87364090938263, + "y": -43.51058034758226, + "z": 1.0 + }, + { + "x": 338.87394402077985, + "y": -43.0087494532081, + "z": 1.0 + }, + { + "x": 338.87418650989764, + "y": -42.607284737709875, + "z": 1.0 + }, + { + "x": 338.8745502435743, + "y": -42.00508766445971, + "z": 1.0 + }, + { + "x": 338.8747927326921, + "y": -41.603622948960364, + "z": 1.0 + }, + { + "x": 338.87503522180987, + "y": -41.202158233461006, + "z": 1.0 + }, + { + "x": 338.8753989554865, + "y": -40.599961160211976, + "z": 1.0 + }, + { + "x": 338.8757020668837, + "y": -40.09813026583778, + "z": 1.0 + }, + { + "x": 338.87600517828093, + "y": -39.59629937146359, + "z": 1.0 + }, + { + "x": 338.8762476673986, + "y": -39.19483465596423, + "z": 1.0 + }, + { + "x": 338.87661140107537, + "y": -38.592637582716335, + "z": 1.0 + }, + { + "x": 338.87685389019305, + "y": -38.191172867215855, + "z": 1.0 + }, + { + "x": 338.87715700159026, + "y": -37.68934197284166, + "z": 1.0 + }, + { + "x": 338.8775207352669, + "y": -37.08714489959263, + "z": 1.0 + }, + { + "x": 338.87788446894353, + "y": -36.4849478263436, + "z": 1.0 + }, + { + "x": 338.8782482026203, + "y": -35.8827507530957, + "z": 1.0 + }, + { + "x": 338.87849069173797, + "y": -35.48128603759521, + "z": 1.0 + }, + { + "x": 338.8788544254146, + "y": -34.87908896434618, + "z": 1.0 + }, + { + "x": 338.87921815909124, + "y": -34.27689189109715, + "z": 1.0 + }, + { + "x": 338.87952127048845, + "y": -33.77506099672296, + "z": 1.0 + }, + { + "x": 338.8798850041652, + "y": -33.172863923475056, + "z": 1.0 + }, + { + "x": 338.8801881155623, + "y": -32.67103302909973, + "z": 1.0 + }, + { + "x": 338.88055184923905, + "y": -32.06883595585183, + "z": 1.0 + }, + { + "x": 338.8809155829157, + "y": -31.466638882602805, + "z": 1.0 + }, + { + "x": 338.8812186943129, + "y": -30.96480798822861, + "z": 1.0 + }, + { + "x": 338.8815218057101, + "y": -30.46297709385442, + "z": 1.0 + }, + { + "x": 338.88188553938676, + "y": -29.860780020605386, + "z": 1.0 + }, + { + "x": 338.882188650784, + "y": -29.358949126231195, + "z": 1.0 + }, + { + "x": 338.8825523844606, + "y": -28.756752052982165, + "z": 1.0 + }, + { + "x": 338.88291611813725, + "y": -28.154554979731998, + "z": 1.0 + }, + { + "x": 338.8832798518139, + "y": -27.552357906482968, + "z": 1.0 + }, + { + "x": 338.8836435854905, + "y": -26.950160833233937, + "z": 1.0 + }, + { + "x": 338.88394669688773, + "y": -26.44832993885975, + "z": 1.0 + }, + { + "x": 338.88431043056437, + "y": -25.846132865611864, + "z": 1.0 + }, + { + "x": 338.8846135419616, + "y": -25.344301971236543, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 13, + "heading": [ + 1.570192315834742, + 1.5701923158345978, + 1.5701923158346802, + 1.5701923158348252, + 1.5701923158348579, + 1.5701923158346793, + 1.5701923158346276, + 1.5701923158347695, + 1.5701923158347413, + 1.5701923158347413, + 1.570192315834855, + 1.5701923158347224, + 1.5701923158346789, + 1.5701923158347413, + 1.5701923158347824, + 1.570192315834742, + 1.5701923158347695, + 1.5701923158348159, + 1.570192315834742, + 1.5701923158347413, + 1.5701923158346274, + 1.5701923158346796, + 1.5701923158347824, + 1.5701923158347413, + 1.5701923158347824, + 1.5701923158347824, + 1.5701923158346909, + 1.5701923158347704, + 1.5701923158348174, + 1.5701923158346909, + 1.5701923158347406, + 1.5701923158347817, + 1.5701923158346909, + 1.5701923158347413, + 1.5701923158347224, + 1.5701923158347413, + 1.5701923158347695, + 1.5701923158347413, + 1.5701923158347413, + 1.570192315834628, + 1.570192315834742, + 1.5701923158347817, + 1.57019231583474, + 1.570192315834742, + 1.5701923158347426, + 1.5701923158346902, + 1.5701923158347406, + 1.570192315834742, + 1.5701923158346909, + 1.5701923158348166, + 1.5701923158347704, + 1.5701923158347413, + 1.5701923158348248, + 1.570192315834764, + 1.5701923158347635, + 1.5701923158346687, + 1.5701923158347464, + 1.5701923158348399, + 1.5701923158347, + 1.5701923158347257, + 1.5701923158347266, + 1.5701923158346691, + 1.5701923158347257, + 1.5701923158347464, + 1.5701923158347257, + 1.5701923158348265, + 1.570192315834725, + 1.5701923158347257, + 1.5701923158348272, + 1.5701923158347464, + 1.5701923158347637, + 1.5701923158346687, + 1.5701923158347257, + 1.5701923158348399, + 1.5701923158347637, + 1.5701923158347464, + 1.570192315834643, + 1.5701923158347464, + 1.5701923158347464, + 1.5701923158346687, + 1.5701923158347464, + 1.5701923158347257, + 1.5701923158347464, + 1.5701923158347464, + 1.5701923158347464, + 1.5701923158347642, + 1.5701923158347642, + 1.5701923158347637, + 1.5701923158347464, + 1.5701923158347457, + 1.5701923158347464 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.0030288680068224494, + "y": 5.014590524953917 + }, + { + "x": 0.006663509614099894, + "y": 11.03209915489856 + }, + { + "x": 0.006057736011371162, + "y": 10.029181049919202 + }, + { + "x": 0.00424041520773244, + "y": 7.020426734946881 + }, + { + "x": 0.004245176266977069, + "y": 7.028309137295992 + }, + { + "x": 0.004855710926676693, + "y": 8.039109644653735 + }, + { + "x": 0.0048557109255398245, + "y": 8.039109644659845 + }, + { + "x": 0.006069638657208998, + "y": 10.04888705582431 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824594 + }, + { + "x": 0.006069638656072129, + "y": 10.048887055835962 + }, + { + "x": 0.007283566388878171, + "y": 12.058664466989626 + }, + { + "x": 0.0066766025236120186, + "y": 11.053775761396025 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824878 + }, + { + "x": 0.00667660252247515, + "y": 11.053775761407394 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055835962 + }, + { + "x": 0.0048557109255398245, + "y": 8.039109644659774 + }, + { + "x": 0.005462674790805977, + "y": 9.043998350231064 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055836175 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824665 + }, + { + "x": 0.006069638658345866, + "y": 10.048887055813296 + }, + { + "x": 0.0066766025236120186, + "y": 11.05377576140711 + }, + { + "x": 0.00667660252247515, + "y": 11.05377576140711 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824665 + }, + { + "x": 0.00667660252247515, + "y": 11.05377576140711 + }, + { + "x": 0.00667660252247515, + "y": 11.05377576140711 + }, + { + "x": 0.005462674791942845, + "y": 9.043998350242148 + }, + { + "x": 0.0048557109255398245, + "y": 8.039109644671143 + }, + { + "x": 0.005462674790805977, + "y": 9.043998350253588 + }, + { + "x": 0.005462674791942845, + "y": 9.04399835024222 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055813438 + }, + { + "x": 0.00667660252247515, + "y": 11.053775761395883 + }, + { + "x": 0.005462674791942845, + "y": 9.04399835024229 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824807 + }, + { + "x": 0.007283566388878171, + "y": 12.058664466989697 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824665 + }, + { + "x": 0.0048557109255398245, + "y": 8.039109644659703 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824665 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055824665 + }, + { + "x": 0.004855710926676693, + "y": 8.039109644659703 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055835962 + }, + { + "x": 0.00667660252247515, + "y": 11.053775761395883 + }, + { + "x": 0.006069638657208998, + "y": 10.04888705580207 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055835962 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055847402 + }, + { + "x": 0.005462674791942845, + "y": 9.043998350230922 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055813296 + }, + { + "x": 0.006069638657208998, + "y": 10.048887055835962 + }, + { + "x": 0.005462674791942845, + "y": 9.04399835024222 + }, + { + "x": 0.005462674790805977, + "y": 9.04399835024222 + }, + { + "x": 0.0048557109255398245, + "y": 8.039109644671 + }, + { + "x": 0.003034819328604499, + "y": 5.024443527912226 + }, + { + "x": 0.0042443006316261744, + "y": 7.026859438067703 + }, + { + "x": 0.007274673532720044, + "y": 12.043941464986787 + }, + { + "x": 0.007274673532720044, + "y": 12.043941464980179 + }, + { + "x": 0.007274673533856912, + "y": 12.04394146496881 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676231961 + }, + { + "x": 0.006062227943175458, + "y": 10.036617887494828 + }, + { + "x": 0.005456005150108467, + "y": 9.032956098723872 + }, + { + "x": 0.006062227944312326, + "y": 10.036617887483885 + }, + { + "x": 0.006062227944312326, + "y": 10.036617887495112 + }, + { + "x": 0.004849782355904608, + "y": 8.029294309987023 + }, + { + "x": 0.006062227944312326, + "y": 10.036617887483885 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232245 + }, + { + "x": 0.006062227944312326, + "y": 10.036617887483885 + }, + { + "x": 0.005456005148971599, + "y": 9.032956098735525 + }, + { + "x": 0.006062227944312326, + "y": 10.036617887472516 + }, + { + "x": 0.006062227944312326, + "y": 10.036617887483743 + }, + { + "x": 0.005456005148971599, + "y": 9.032956098746752 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232245 + }, + { + "x": 0.007274673532720044, + "y": 12.043941464980605 + }, + { + "x": 0.007274673533856912, + "y": 12.043941464969308 + }, + { + "x": 0.006062227944312326, + "y": 10.036617887483885 + }, + { + "x": 0.006062227943175458, + "y": 10.036617887495183 + }, + { + "x": 0.007274673532720044, + "y": 12.043941464980605 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232245 + }, + { + "x": 0.0066684507396530535, + "y": 11.040279676220948 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232245 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232245 + }, + { + "x": 0.007274673533856912, + "y": 12.043941464969272 + }, + { + "x": 0.006668450738516185, + "y": 11.04027967623221 + }, + { + "x": 0.006062227944312326, + "y": 10.03661788748385 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232245 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232245 + }, + { + "x": 0.006668450738516185, + "y": 11.04027967623221 + }, + { + "x": 0.007274673532720044, + "y": 12.043941464991974 + }, + { + "x": 0.007274673532720044, + "y": 12.043941464991974 + }, + { + "x": 0.007274673532720044, + "y": 12.043941464980605 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232174 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676220734 + }, + { + "x": 0.006668450738516185, + "y": 11.040279676232068 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 338.8846135419616, + "y": -25.344301971236543, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 393.3504725840923, + "y": -327.0485015111582, + "z": 1.0 + }, + { + "x": 393.69005141079685, + "y": -326.6858281969275, + "z": 1.0 + }, + { + "x": 394.16622838247656, + "y": -326.11265285187653, + "z": 1.0 + }, + { + "x": 394.46051050493213, + "y": -325.7123460897148, + "z": 1.0 + }, + { + "x": 394.8011293029479, + "y": -325.1930784805868, + "z": 1.0 + }, + { + "x": 395.1103756129993, + "y": -324.6545354059099, + "z": 1.0 + }, + { + "x": 395.438600971734, + "y": -323.9852775888519, + "z": 1.0 + }, + { + "x": 395.67509170223576, + "y": -323.4110529176839, + "z": 1.0 + }, + { + "x": 395.8395177444637, + "y": -322.9422111616245, + "z": 1.0 + }, + { + "x": 396.0440198204258, + "y": -322.2256531197213, + "z": 1.0 + }, + { + "x": 396.19705673814303, + "y": -321.4963701042684, + "z": 1.0 + }, + { + "x": 396.2847072711144, + "y": -320.88157522525205, + "z": 1.0 + }, + { + "x": 396.32849329031455, + "y": -320.38667352672815, + "z": 1.0 + }, + { + "x": 396.34876069791414, + "y": -319.89025260019343, + "z": 1.0 + }, + { + "x": 396.3502793922545, + "y": -319.2427121151802, + "z": 1.0 + }, + { + "x": 396.3500450152746, + "y": -318.6397023052805, + "z": 1.0 + }, + { + "x": 396.35000595244463, + "y": -318.53920067029856, + "z": 1.0 + }, + { + "x": 396.349811556479, + "y": -318.03905481818254, + "z": 1.0 + }, + { + "x": 396.3496171605133, + "y": -317.5389089660682, + "z": 1.0 + }, + { + "x": 396.34946164374077, + "y": -317.13879228437474, + "z": 1.0 + }, + { + "x": 396.3493061269682, + "y": -316.73867560268235, + "z": 1.0 + }, + { + "x": 396.3490728518094, + "y": -316.13850058014384, + "z": 1.0 + }, + { + "x": 396.3488784558437, + "y": -315.6383547280295, + "z": 1.0 + }, + { + "x": 396.34868405987805, + "y": -315.13820887591294, + "z": 1.0 + }, + { + "x": 396.34845078471926, + "y": -314.53803385337324, + "z": 1.0 + }, + { + "x": 396.3482952679467, + "y": -314.13791717168203, + "z": 1.0 + }, + { + "x": 396.3480619927879, + "y": -313.53774214914233, + "z": 1.0 + }, + { + "x": 396.34782871762906, + "y": -312.9375671266049, + "z": 1.0 + }, + { + "x": 396.3476732008565, + "y": -312.5374504449137, + "z": 1.0 + }, + { + "x": 396.34747880489084, + "y": -312.0373045927971, + "z": 1.0 + }, + { + "x": 396.34732328811833, + "y": -311.6371879111048, + "z": 1.0 + }, + { + "x": 396.3471288921526, + "y": -311.1370420589893, + "z": 1.0 + }, + { + "x": 396.34689561699383, + "y": -310.53686703645076, + "z": 1.0 + }, + { + "x": 396.3467012210281, + "y": -310.03672118433644, + "z": 1.0 + }, + { + "x": 396.3465457042556, + "y": -309.63660450264297, + "z": 1.0 + }, + { + "x": 396.3463513082899, + "y": -309.13645865052865, + "z": 1.0 + }, + { + "x": 396.3461957915174, + "y": -308.7363419688351, + "z": 1.0 + }, + { + "x": 396.3460402747449, + "y": -308.33622528714164, + "z": 1.0 + }, + { + "x": 396.34580699958605, + "y": -307.7360502646042, + "z": 1.0 + }, + { + "x": 396.34561260362034, + "y": -307.2359044124899, + "z": 1.0 + }, + { + "x": 396.34545708684783, + "y": -306.8357877307964, + "z": 1.0 + }, + { + "x": 396.34522381168904, + "y": -306.23561270825786, + "z": 1.0 + }, + { + "x": 396.3449905365302, + "y": -305.6354376857205, + "z": 1.0 + }, + { + "x": 396.3448350197577, + "y": -305.23532100402696, + "z": 1.0 + }, + { + "x": 396.3446017445989, + "y": -304.63514598148845, + "z": 1.0 + }, + { + "x": 396.3444073486332, + "y": -304.135000129373, + "z": 1.0 + }, + { + "x": 396.3441740734744, + "y": -303.53482510683443, + "z": 1.0 + }, + { + "x": 396.3440185567019, + "y": -303.1347084251421, + "z": 1.0 + }, + { + "x": 396.34378528154303, + "y": -302.53453340260353, + "z": 1.0 + }, + { + "x": 396.34362976477047, + "y": -302.1344167209112, + "z": 1.0 + }, + { + "x": 396.34347424799796, + "y": -301.7343000392188, + "z": 1.0 + }, + { + "x": 396.34324097283917, + "y": -301.13412501667915, + "z": 1.0 + }, + { + "x": 396.3430854560666, + "y": -300.7340083349879, + "z": 1.0 + }, + { + "x": 396.3429299392941, + "y": -300.33389165329555, + "z": 1.0 + }, + { + "x": 396.34277442252153, + "y": -299.93377497160316, + "z": 1.0 + }, + { + "x": 396.34261890574896, + "y": -299.5336582899108, + "z": 1.0 + }, + { + "x": 396.3424245097833, + "y": -299.0335124377953, + "z": 1.0 + }, + { + "x": 396.3422301138176, + "y": -298.533366585681, + "z": 1.0 + }, + { + "x": 396.3419968386588, + "y": -297.93319156314135, + "z": 1.0 + }, + { + "x": 396.34180244269317, + "y": -297.43304571102584, + "z": 1.0 + }, + { + "x": 396.3416469259206, + "y": -297.0329290293335, + "z": 1.0 + }, + { + "x": 396.3414136507618, + "y": -296.43275400679494, + "z": 1.0 + }, + { + "x": 396.34118037560296, + "y": -295.8325789842564, + "z": 1.0 + }, + { + "x": 396.34102485883045, + "y": -295.43246230256403, + "z": 1.0 + }, + { + "x": 396.34086934205794, + "y": -295.03234562087164, + "z": 1.0 + }, + { + "x": 396.3407138252854, + "y": -294.6322289391793, + "z": 1.0 + }, + { + "x": 396.34051942931967, + "y": -294.13208308706385, + "z": 1.0 + }, + { + "x": 396.340325033354, + "y": -293.6319372349484, + "z": 1.0 + }, + { + "x": 396.34016951658145, + "y": -293.23182055325714, + "z": 1.0 + }, + { + "x": 396.33993624142266, + "y": -292.63164553071744, + "z": 1.0 + }, + { + "x": 396.33970296626387, + "y": -292.0314705081789, + "z": 1.0 + }, + { + "x": 396.33950857029816, + "y": -291.5313246560635, + "z": 1.0 + }, + { + "x": 396.3392752951394, + "y": -290.9311496335249, + "z": 1.0 + }, + { + "x": 396.3391197783668, + "y": -290.5310329518325, + "z": 1.0 + }, + { + "x": 396.3389642615943, + "y": -290.1309162701402, + "z": 1.0 + }, + { + "x": 396.3387698656286, + "y": -289.6307704180247, + "z": 1.0 + }, + { + "x": 396.3385365904698, + "y": -289.03059539548616, + "z": 1.0 + }, + { + "x": 396.338303315311, + "y": -288.4304203729476, + "z": 1.0 + }, + { + "x": 396.3381089193453, + "y": -287.93027452083214, + "z": 1.0 + }, + { + "x": 396.3378756441865, + "y": -287.3300994982936, + "z": 1.0 + }, + { + "x": 396.33768124822086, + "y": -286.829953646177, + "z": 1.0 + }, + { + "x": 396.3375257314483, + "y": -286.4298369644858, + "z": 1.0 + }, + { + "x": 396.3373702146758, + "y": -286.0297202827934, + "z": 1.0 + }, + { + "x": 396.3372146979032, + "y": -285.62960360110105, + "z": 1.0 + }, + { + "x": 396.3369814227444, + "y": -285.0294285785636, + "z": 1.0 + }, + { + "x": 396.3367481475856, + "y": -284.4292535560239, + "z": 1.0 + }, + { + "x": 396.3365926308131, + "y": -284.0291368743316, + "z": 1.0 + }, + { + "x": 396.33639823484737, + "y": -283.5289910222172, + "z": 1.0 + }, + { + "x": 396.3361649596885, + "y": -282.9288159996787, + "z": 1.0 + }, + { + "x": 396.3359316845297, + "y": -282.32864097714014, + "z": 1.0 + }, + { + "x": 396.3357372885641, + "y": -281.82849512502355, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 14, + "heading": [ + 1.5100439818715332, + 0.8182726338896165, + 0.8538526685934247, + 0.9012926828937182, + 0.9665226973987117, + 1.019892727772154, + 1.085160506298573, + 1.1445042776719234, + 1.2038418448434116, + 1.2690718565066614, + 1.3283718560986324, + 1.3936018810754445, + 1.4529019040221436, + 1.5062719155068656, + 1.5517541909138082, + 1.5697693253906897, + 1.5711850053275338, + 1.5711850053274206, + 1.571185005327503, + 1.5711850053274965, + 1.571185005327488, + 1.5711850053275027, + 1.5711850053275083, + 1.5711850053275027, + 1.5711850053274554, + 1.5711850053275027, + 1.5711850053275027, + 1.5711850053275123, + 1.5711850053275604, + 1.5711850053274965, + 1.5711850053274328, + 1.5711850053274965, + 1.571185005327508, + 1.5711850053275083, + 1.5711850053274965, + 1.5711850053274965, + 1.5711850053274965, + 1.5711850053274163, + 1.5711850053275027, + 1.5711850053275604, + 1.5711850053274965, + 1.5711850053274454, + 1.5711850053275127, + 1.5711850053275027, + 1.5711850053274454, + 1.571185005327508, + 1.571185005327508, + 1.5711850053274459, + 1.5711850053275027, + 1.5711850053275596, + 1.5711850053274885, + 1.5711850053274454, + 1.5711850053275027, + 1.5711850053274892, + 1.5711850053274885, + 1.5711850053275596, + 1.5711850053274965, + 1.5711850053275032, + 1.571185005327508, + 1.5711850053274559, + 1.5711850053274965, + 1.5711850053275027, + 1.5711850053275123, + 1.5711850053275027, + 1.5711850053274174, + 1.5711850053274885, + 1.5711850053275596, + 1.5711850053275027, + 1.571185005327497, + 1.5711850053275027, + 1.5711850053274645, + 1.571185005327508, + 1.571185005327508, + 1.5711850053275027, + 1.5711850053274885, + 1.5711850053274965, + 1.571185005327508, + 1.571185005327465, + 1.571185005327508, + 1.571185005327508, + 1.5711850053274559, + 1.5711850053274965, + 1.5711850053274892, + 1.5711850053274885, + 1.57118500532756, + 1.5711850053275123, + 1.5711850053274454, + 1.571185005327497, + 1.57118500532756, + 1.5711850053275123, + 1.5711850053274559 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 3.395788267045532, + "y": 3.6267331423067617 + }, + { + "x": 8.157557983842594, + "y": 9.358486592816462 + }, + { + "x": 7.7045909413527625, + "y": 9.734821072127033 + }, + { + "x": 6.349009204713525, + "y": 9.19574371289741 + }, + { + "x": 6.498651080671607, + "y": 10.578106838049166 + }, + { + "x": 6.374716687860769, + "y": 12.078008917349052 + }, + { + "x": 5.647160892364695, + "y": 12.434824882259932 + }, + { + "x": 4.009167727297154, + "y": 10.430664272273589 + }, + { + "x": 3.689281181900128, + "y": 11.853997979625888 + }, + { + "x": 3.5753899367932718, + "y": 14.458410573561196 + }, + { + "x": 2.4068745068865383, + "y": 13.44077894469251 + }, + { + "x": 1.3143655217152173, + "y": 11.096965775402623 + }, + { + "x": 0.6405342679971682, + "y": 9.913226250586149 + }, + { + "x": 0.21786101939937907, + "y": 11.439614115479344 + }, + { + "x": 0.012843173604437652, + "y": 12.50550294912955 + }, + { + "x": -0.0027343980985961025, + "y": 7.035114448816557 + }, + { + "x": -0.0023345879560565663, + "y": 6.006474870979446 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042303352 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633858711 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.004276711244983744, + "y": 11.00320874652823 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.00427671124441531, + "y": 11.003208746562905 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.0046655031763975785, + "y": 12.003500450771298 + }, + { + "x": -0.003887919314138344, + "y": 10.002917042286299 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.003499127381587641, + "y": 9.002625338089274 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746540167 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746528799 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338078474 + }, + { + "x": -0.0031103354501738067, + "y": 8.00233363387008 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.004276711245552178, + "y": 11.00320874651743 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.0038879193130014755, + "y": 10.002917042320405 + }, + { + "x": -0.0046655031763975785, + "y": 12.003500450759361 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.0038879193130014755, + "y": 10.002917042320405 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746539599 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746540167 + }, + { + "x": -0.0038879193130014755, + "y": 10.002917042309036 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.003887919314138344, + "y": 10.002917042309036 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633847343 + }, + { + "x": -0.0038879193130014755, + "y": 10.002917042320405 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633835974 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633847343 + }, + { + "x": -0.003110335451310675, + "y": 8.002333633847343 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338078474 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042298236 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746539599 + }, + { + "x": -0.00427671124441531, + "y": 11.003208746551536 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338078474 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.0046655031763975785, + "y": 12.003500450771298 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.0031103354501738067, + "y": 8.002333633847343 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633847343 + }, + { + "x": -0.0034991273827245095, + "y": 9.002625338077905 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309036 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338067105 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309605 + }, + { + "x": -0.004665503175829144, + "y": 12.003500450782099 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746539599 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746540167 + }, + { + "x": -0.0038879193135699097, + "y": 10.002917042309605 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633847343 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746540167 + }, + { + "x": -0.004665503175829144, + "y": 12.003500450771298 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746540167 + }, + { + "x": -0.004276711244983744, + "y": 11.003208746540167 + }, + { + "x": -0.00427671124441531, + "y": 11.003208746551536 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338077905 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633835974 + }, + { + "x": -0.003110335450742241, + "y": 8.002333633847343 + }, + { + "x": -0.003887919314138344, + "y": 10.002917042297668 + }, + { + "x": -0.0046655031763975785, + "y": 12.003500450771298 + }, + { + "x": -0.0038879193130014755, + "y": 10.002917042320405 + }, + { + "x": -0.0034991273821560753, + "y": 9.002625338067105 + }, + { + "x": -0.004276711245552178, + "y": 11.003208746528799 + }, + { + "x": -0.0046655031763975785, + "y": 12.00350045077073 + }, + { + "x": -0.00427671124441531, + "y": 11.003208746551536 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 396.3357372885641, + "y": -281.82849512502355, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 83.3983254223986, + "y": 2.043585654035443, + "z": 1.0 + }, + { + "x": 82.89595475942362, + "y": 2.043421926501252, + "z": 1.0 + }, + { + "x": 82.29310996385367, + "y": 2.0432254534602228, + "z": 1.0 + }, + { + "x": 81.8912134334737, + "y": 2.0430944714328696, + "z": 1.0 + }, + { + "x": 81.48931690309372, + "y": 2.0429634894055173, + "z": 1.0 + }, + { + "x": 80.98694624011875, + "y": 2.0427997618713256, + "z": 1.0 + }, + { + "x": 80.48457557714379, + "y": 2.042636034337135, + "z": 1.0 + }, + { + "x": 80.0826790467638, + "y": 2.0425050523097816, + "z": 1.0 + }, + { + "x": 79.58030838378885, + "y": 2.042341324775591, + "z": 1.0 + }, + { + "x": 79.37936011859885, + "y": 2.0422758337619147, + "z": 1.0 + }, + { + "x": 78.77892077275439, + "y": 2.042080144680581, + "z": 1.0 + }, + { + "x": 78.27855465121736, + "y": 2.0419170704461367, + "z": 1.0 + }, + { + "x": 77.87826175398773, + "y": 2.0417866110585807, + "z": 1.0 + }, + { + "x": 77.3778956324507, + "y": 2.0416235368241367, + "z": 1.0 + }, + { + "x": 76.87752951091366, + "y": 2.0414604625896917, + "z": 1.0 + }, + { + "x": 76.27709016506921, + "y": 2.0412647735083587, + "z": 1.0 + }, + { + "x": 75.67665081922478, + "y": 2.0410690844270247, + "z": 1.0 + }, + { + "x": 75.17628469768773, + "y": 2.0409060101925807, + "z": 1.0 + }, + { + "x": 74.77599180045812, + "y": 2.0407755508050247, + "z": 1.0 + }, + { + "x": 74.17555245461367, + "y": 2.0405798617236917, + "z": 1.0 + }, + { + "x": 73.57511310876923, + "y": 2.0403841726423577, + "z": 1.0 + }, + { + "x": 73.07474698723219, + "y": 2.0402210984079137, + "z": 1.0 + }, + { + "x": 72.67445409000257, + "y": 2.0400906390203577, + "z": 1.0 + }, + { + "x": 72.17408796846553, + "y": 2.0399275647859136, + "z": 1.0 + }, + { + "x": 71.7737950712359, + "y": 2.039797105398358, + "z": 1.0 + }, + { + "x": 71.17335572539146, + "y": 2.0396014163170246, + "z": 1.0 + }, + { + "x": 70.77306282816183, + "y": 2.0394709569294687, + "z": 1.0 + }, + { + "x": 70.37276993093221, + "y": 2.0393404975419136, + "z": 1.0 + }, + { + "x": 69.77233058508776, + "y": 2.0391448084605797, + "z": 1.0 + }, + { + "x": 69.27196446355073, + "y": 2.0389817342261356, + "z": 1.0 + }, + { + "x": 68.8716715663211, + "y": 2.0388512748385796, + "z": 1.0 + }, + { + "x": 68.27123222047666, + "y": 2.0386555857572466, + "z": 1.0 + }, + { + "x": 67.77086609893962, + "y": 2.0384925115228016, + "z": 1.0 + }, + { + "x": 67.27049997740258, + "y": 2.0383294372883576, + "z": 1.0 + }, + { + "x": 66.87020708017296, + "y": 2.0381989779008016, + "z": 1.0 + }, + { + "x": 66.36984095863592, + "y": 2.0380359036663576, + "z": 1.0 + }, + { + "x": 65.9695480614063, + "y": 2.0379054442788016, + "z": 1.0 + }, + { + "x": 65.36910871556185, + "y": 2.0377097551974686, + "z": 1.0 + }, + { + "x": 64.86874259402482, + "y": 2.0375466809630236, + "z": 1.0 + }, + { + "x": 64.46844969679519, + "y": 2.0374162215754685, + "z": 1.0 + }, + { + "x": 63.96808357525816, + "y": 2.0372531473410236, + "z": 1.0 + }, + { + "x": 63.36764422941371, + "y": 2.0370574582596905, + "z": 1.0 + }, + { + "x": 62.96735133218408, + "y": 2.0369269988721346, + "z": 1.0 + }, + { + "x": 62.36691198633963, + "y": 2.0367313097908015, + "z": 1.0 + }, + { + "x": 61.86654586480259, + "y": 2.0365682355563566, + "z": 1.0 + }, + { + "x": 61.36617974326553, + "y": 2.0364051613219125, + "z": 1.0 + }, + { + "x": 60.765740397421084, + "y": 2.0362094722405786, + "z": 1.0 + }, + { + "x": 60.26537427588404, + "y": 2.0360463980061345, + "z": 1.0 + }, + { + "x": 59.66493493003959, + "y": 2.0358507089248006, + "z": 1.0 + }, + { + "x": 59.26464203280996, + "y": 2.0357202495372455, + "z": 1.0 + }, + { + "x": 58.764275911272904, + "y": 2.0355571753028006, + "z": 1.0 + }, + { + "x": 58.26390978973586, + "y": 2.0353941010683565, + "z": 1.0 + }, + { + "x": 57.86361689250623, + "y": 2.0352636416808005, + "z": 1.0 + }, + { + "x": 57.26317754666178, + "y": 2.0350679525994675, + "z": 1.0 + }, + { + "x": 56.66273820081733, + "y": 2.0348722635181336, + "z": 1.0 + }, + { + "x": 56.162372079280274, + "y": 2.0347091892836895, + "z": 1.0 + }, + { + "x": 55.66200595774323, + "y": 2.0345461150492445, + "z": 1.0 + }, + { + "x": 55.06156661189878, + "y": 2.0343504259679115, + "z": 1.0 + }, + { + "x": 54.46112726605433, + "y": 2.0341547368865784, + "z": 1.0 + }, + { + "x": 54.0608343688247, + "y": 2.0340242774990225, + "z": 1.0 + }, + { + "x": 53.46039502298024, + "y": 2.0338285884176894, + "z": 1.0 + }, + { + "x": 52.960028901443195, + "y": 2.0336655141832445, + "z": 1.0 + }, + { + "x": 52.359589555598745, + "y": 2.0334698251019114, + "z": 1.0 + }, + { + "x": 51.859223434061704, + "y": 2.0333067508674665, + "z": 1.0 + }, + { + "x": 51.45893053683207, + "y": 2.0331762914799114, + "z": 1.0 + }, + { + "x": 51.05863763960244, + "y": 2.0330458320923555, + "z": 1.0 + }, + { + "x": 50.55827151806538, + "y": 2.0328827578579105, + "z": 1.0 + }, + { + "x": 50.15797862083575, + "y": 2.0327522984703554, + "z": 1.0 + }, + { + "x": 49.757685723606116, + "y": 2.0326218390827995, + "z": 1.0 + }, + { + "x": 49.157246377761666, + "y": 2.0324261500014664, + "z": 1.0 + }, + { + "x": 48.656880256224625, + "y": 2.0322630757670215, + "z": 1.0 + }, + { + "x": 48.25658735899499, + "y": 2.0321326163794664, + "z": 1.0 + }, + { + "x": 47.756221237457936, + "y": 2.0319695421450215, + "z": 1.0 + }, + { + "x": 47.3559283402283, + "y": 2.0318390827574664, + "z": 1.0 + }, + { + "x": 46.85556221869126, + "y": 2.0316760085230214, + "z": 1.0 + }, + { + "x": 46.25512287284681, + "y": 2.0314803194416884, + "z": 1.0 + }, + { + "x": 45.65468352700236, + "y": 2.0312846303603544, + "z": 1.0 + }, + { + "x": 45.15431740546532, + "y": 2.0311215561259104, + "z": 1.0 + }, + { + "x": 44.75402450823567, + "y": 2.0309910967383544, + "z": 1.0 + }, + { + "x": 44.25365838669863, + "y": 2.0308280225039104, + "z": 1.0 + }, + { + "x": 43.75329226516159, + "y": 2.0306649482694654, + "z": 1.0 + }, + { + "x": 43.15285291931714, + "y": 2.0304692591881324, + "z": 1.0 + }, + { + "x": 42.75256002208751, + "y": 2.0303387998005764, + "z": 1.0 + }, + { + "x": 42.352267124857875, + "y": 2.0302083404130213, + "z": 1.0 + }, + { + "x": 41.95197422762823, + "y": 2.0300778810254654, + "z": 1.0 + }, + { + "x": 41.551681330398594, + "y": 2.0299474216379094, + "z": 1.0 + }, + { + "x": 41.15138843316896, + "y": 2.0298169622503544, + "z": 1.0 + }, + { + "x": 40.65102231163192, + "y": 2.0296538880159094, + "z": 1.0 + }, + { + "x": 40.05058296578747, + "y": 2.0294581989345764, + "z": 1.0 + }, + { + "x": 39.450143619943006, + "y": 2.0292625098532433, + "z": 1.0 + }, + { + "x": 38.94977749840598, + "y": 2.0290994356187984, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 15, + "heading": [ + -3.1412667437776554, + -3.141266743777655, + -3.141266743777655, + -3.141266743777654, + -3.141266743777655, + -3.1412667437776545, + -3.141266743777654, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776554, + -3.141266743777655, + -3.1412667437776545, + -3.141266743777654, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.141266743777654, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776536, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.141266743777655, + -3.1412667437776536, + -3.141266743777655, + -3.1412667437776545, + -3.1412667437776545, + -3.141266743777655, + -3.1412667437776545 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -5.0237066297498245, + "y": -0.001637275341908051 + }, + { + "x": -11.05215458544933, + "y": -0.003602005752201265 + }, + { + "x": -10.047413259499223, + "y": -0.003274550683824984 + }, + { + "x": -8.037930607599435, + "y": -0.002619640547054658 + }, + { + "x": -9.0426719335494, + "y": -0.002947095615439821 + }, + { + "x": -10.047413259499365, + "y": -0.003274550683824984 + }, + { + "x": -9.042671933549542, + "y": -0.002947095615439821 + }, + { + "x": -9.0426719335494, + "y": -0.002947095615439821 + }, + { + "x": -7.03318928164947, + "y": -0.002292185478669495 + }, + { + "x": -8.013876110344569, + "y": -0.002611800950096388 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200046362 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916888852, + "y": -0.003913781626669888 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -12.008786916888852, + "y": -0.003913781626669888 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362199957544 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740686, + "y": -0.0032614846888945337 + }, + { + "x": -8.00585794459252, + "y": -0.002609187751110298 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740686, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666603, + "y": -0.0029353362200001953 + }, + { + "x": -11.008054673814769, + "y": -0.0035876331577799903 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -10.00732243074097, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673815053, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -9.006590187666887, + "y": -0.0029353362200001953 + }, + { + "x": -10.00732243074097, + "y": -0.003261484688890093 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -12.008786916888994, + "y": -0.003913781626669888 + }, + { + "x": -11.008054673815053, + "y": -0.0035876331577799903 + }, + { + "x": -10.00732243074097, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916888994, + "y": -0.003913781626661006 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -10.00732243074097, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673815053, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362200001953 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -9.006590187666887, + "y": -0.002935336220009077 + }, + { + "x": -9.006590187666887, + "y": -0.0029353362200001953 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666887, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666887, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362200001953 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916888994, + "y": -0.003913781626669888 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -9.006590187666887, + "y": -0.0029353362200001953 + }, + { + "x": -9.006590187666887, + "y": -0.0029353362200001953 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -10.007322430740828, + "y": -0.003261484688890093 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -8.005857944592805, + "y": -0.002609187751110298 + }, + { + "x": -8.005857944592805, + "y": -0.0026091877511191797 + }, + { + "x": -8.005857944592663, + "y": -0.002609187751110298 + }, + { + "x": -9.006590187666745, + "y": -0.0029353362200001953 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + }, + { + "x": -12.008786916889136, + "y": -0.003913781626661006 + }, + { + "x": -11.008054673814911, + "y": -0.0035876331577799903 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 38.94977749840598, + "y": 2.0290994356187984, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 92.40138135506758, + "y": -206.4210177929624, + "z": 1.0 + }, + { + "x": 92.40132027234365, + "y": -206.01919131358682, + "z": 1.0 + }, + { + "x": 92.40125918961971, + "y": -205.61736483421123, + "z": 1.0 + }, + { + "x": 92.40119810689576, + "y": -205.21553835483564, + "z": 1.0 + }, + { + "x": 92.40113702417182, + "y": -204.81371187546003, + "z": 1.0 + }, + { + "x": 92.40107594144787, + "y": -204.41188539608444, + "z": 1.0 + }, + { + "x": 92.40101485872391, + "y": -204.01005891670857, + "z": 1.0 + }, + { + "x": 92.40093850531899, + "y": -203.50777581748966, + "z": 1.0 + }, + { + "x": 92.40087742259504, + "y": -203.10594933811376, + "z": 1.0 + }, + { + "x": 92.40081633987108, + "y": -202.7041228587379, + "z": 1.0 + }, + { + "x": 92.40075525714714, + "y": -202.3022963793626, + "z": 1.0 + }, + { + "x": 92.40067890374222, + "y": -201.8000132801434, + "z": 1.0 + }, + { + "x": 92.40061782101826, + "y": -201.39818680076752, + "z": 1.0 + }, + { + "x": 92.40055673829431, + "y": -200.99636032139193, + "z": 1.0 + }, + { + "x": 92.40046511420839, + "y": -200.39362060232827, + "z": 1.0 + }, + { + "x": 92.40040403148444, + "y": -199.99179412295297, + "z": 1.0 + }, + { + "x": 92.4003429487605, + "y": -199.58996764357738, + "z": 1.0 + }, + { + "x": 92.40025132467457, + "y": -198.98722792451397, + "z": 1.0 + }, + { + "x": 92.40015970058866, + "y": -198.38448820545088, + "z": 1.0 + }, + { + "x": 92.40006807650273, + "y": -197.78174848638722, + "z": 1.0 + }, + { + "x": 92.39997645241681, + "y": -197.17900876732415, + "z": 1.0 + }, + { + "x": 92.39988482833088, + "y": -196.57626904826049, + "z": 1.0 + }, + { + "x": 92.39980847492595, + "y": -196.073985949041, + "z": 1.0 + }, + { + "x": 92.39971685084004, + "y": -195.4712462299779, + "z": 1.0 + }, + { + "x": 92.39965576811608, + "y": -195.06941975060204, + "z": 1.0 + }, + { + "x": 92.39956414403017, + "y": -194.46668003153894, + "z": 1.0 + }, + { + "x": 92.39948779062522, + "y": -193.96439693231886, + "z": 1.0 + }, + { + "x": 92.3994114372203, + "y": -193.46211383309966, + "z": 1.0 + }, + { + "x": 92.39933508381536, + "y": -192.95983073388047, + "z": 1.0 + }, + { + "x": 92.39927400109141, + "y": -192.55800425450488, + "z": 1.0 + }, + { + "x": 92.39918237700549, + "y": -191.9552645354415, + "z": 1.0 + }, + { + "x": 92.39909075291956, + "y": -191.3525248163778, + "z": 1.0 + }, + { + "x": 92.39901439951464, + "y": -190.8502417171586, + "z": 1.0 + }, + { + "x": 92.39892277542872, + "y": -190.24750199809523, + "z": 1.0 + }, + { + "x": 92.39886169270477, + "y": -189.84567551871964, + "z": 1.0 + }, + { + "x": 92.39878533929982, + "y": -189.34339241949988, + "z": 1.0 + }, + { + "x": 92.39872425657587, + "y": -188.9415659401243, + "z": 1.0 + }, + { + "x": 92.39866317385194, + "y": -188.53973946074896, + "z": 1.0 + }, + { + "x": 92.39858682044698, + "y": -188.03745636152922, + "z": 1.0 + }, + { + "x": 92.39852573772303, + "y": -187.6356298821536, + "z": 1.0 + }, + { + "x": 92.39846465499909, + "y": -187.23380340277802, + "z": 1.0 + }, + { + "x": 92.39837303091316, + "y": -186.63106368371464, + "z": 1.0 + }, + { + "x": 92.39832721887021, + "y": -186.32969382418295, + "z": 1.0 + }, + { + "x": 92.39826629054149, + "y": -185.92888301800355, + "z": 1.0 + }, + { + "x": 92.39819013013054, + "y": -185.42786951027819, + "z": 1.0 + }, + { + "x": 92.39809873763745, + "y": -184.82665330100866, + "z": 1.0 + }, + { + "x": 92.39800734514435, + "y": -184.22543709173857, + "z": 1.0 + }, + { + "x": 92.39793118473344, + "y": -183.72442358401378, + "z": 1.0 + }, + { + "x": 92.39785502432252, + "y": -183.22341007628893, + "z": 1.0 + }, + { + "x": 92.39776363182942, + "y": -182.62219386701912, + "z": 1.0 + }, + { + "x": 92.39768747141851, + "y": -182.12118035929433, + "z": 1.0 + }, + { + "x": 92.39762654308977, + "y": -181.72036955311472, + "z": 1.0 + }, + { + "x": 92.39755038267886, + "y": -181.21935604538993, + "z": 1.0 + }, + { + "x": 92.39748945435012, + "y": -180.81854523920975, + "z": 1.0 + }, + { + "x": 92.3974285260214, + "y": -180.4177344330302, + "z": 1.0 + }, + { + "x": 92.3973371335283, + "y": -179.8165182237604, + "z": 1.0 + }, + { + "x": 92.39724574103519, + "y": -179.2153020144903, + "z": 1.0 + }, + { + "x": 92.39716958062426, + "y": -178.7142885067655, + "z": 1.0 + }, + { + "x": 92.39710865229554, + "y": -178.3134777005859, + "z": 1.0 + }, + { + "x": 92.3970477239668, + "y": -177.9126668944058, + "z": 1.0 + }, + { + "x": 92.39695633147372, + "y": -177.31145068513626, + "z": 1.0 + }, + { + "x": 92.39689540314497, + "y": -176.9106398789561, + "z": 1.0 + }, + { + "x": 92.39680401065188, + "y": -176.30942366968662, + "z": 1.0 + }, + { + "x": 92.39674308232314, + "y": -175.90861286350645, + "z": 1.0 + }, + { + "x": 92.39668215399442, + "y": -175.5078020573269, + "z": 1.0 + }, + { + "x": 92.39659076150132, + "y": -174.9065858480571, + "z": 1.0 + }, + { + "x": 92.39649936900821, + "y": -174.305369638787, + "z": 1.0 + }, + { + "x": 92.39643844067947, + "y": -173.90455883260745, + "z": 1.0 + }, + { + "x": 92.39636228026856, + "y": -173.4035453248826, + "z": 1.0 + }, + { + "x": 92.39628611985765, + "y": -172.90253181715775, + "z": 1.0 + }, + { + "x": 92.39622519152891, + "y": -172.50172101097786, + "z": 1.0 + }, + { + "x": 92.39616426320018, + "y": -172.10091020479797, + "z": 1.0 + }, + { + "x": 92.39607287070707, + "y": -171.49969399552788, + "z": 1.0 + }, + { + "x": 92.39598147821397, + "y": -170.89847778625807, + "z": 1.0 + }, + { + "x": 92.39592054988525, + "y": -170.49766698007852, + "z": 1.0 + }, + { + "x": 92.39584438947433, + "y": -169.99665347235367, + "z": 1.0 + }, + { + "x": 92.3957682290634, + "y": -169.49563996462882, + "z": 1.0 + }, + { + "x": 92.39567683657032, + "y": -168.894423755359, + "z": 1.0 + }, + { + "x": 92.39560067615939, + "y": -168.39341024763388, + "z": 1.0 + }, + { + "x": 92.39553974783067, + "y": -167.99259944145427, + "z": 1.0 + }, + { + "x": 92.39547881950193, + "y": -167.59178863527438, + "z": 1.0 + }, + { + "x": 92.39538742700883, + "y": -166.9905724260043, + "z": 1.0 + }, + { + "x": 92.39532649868009, + "y": -166.5897616198244, + "z": 1.0 + }, + { + "x": 92.39523510618699, + "y": -165.9885454105546, + "z": 1.0 + }, + { + "x": 92.39517417785827, + "y": -165.58773460437504, + "z": 1.0 + }, + { + "x": 92.39509801744734, + "y": -165.0867210966499, + "z": 1.0 + }, + { + "x": 92.39503708911862, + "y": -164.6859102904703, + "z": 1.0 + }, + { + "x": 92.39494569662551, + "y": -164.0846940812005, + "z": 1.0 + }, + { + "x": 92.39488476829678, + "y": -163.6838832750206, + "z": 1.0 + }, + { + "x": 92.39479337580369, + "y": -163.0826670657508, + "z": 1.0 + }, + { + "x": 92.39471721539276, + "y": -162.58165355802566, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 16, + "heading": [ + 1.5709483394834876, + 1.5709483394834476, + 1.5709483394834654, + 1.570948339483483, + 1.570948339483483, + 1.570948339483483, + 1.5709483394835007, + 1.5709483394834869, + 1.5709483394834711, + 1.5709483394835004, + 1.5709483394835007, + 1.5709483394834713, + 1.5709483394834869, + 1.5709483394835007, + 1.57094833948349, + 1.57094833948349, + 1.5709483394834831, + 1.57094833948349, + 1.570948339483483, + 1.570948339483483, + 1.5709483394834947, + 1.5709483394834947, + 1.5709483394834927, + 1.5709483394834798, + 1.57094833948349, + 1.57094833948349, + 1.5709483394834927, + 1.57094833948349, + 1.570948339483476, + 1.5709483394834869, + 1.57094833948349, + 1.5709483394834947, + 1.5709483394834798, + 1.5709483394834798, + 1.57094833948349, + 1.5709483394835027, + 1.5709483394835027, + 1.5709483394834654, + 1.5709483394835027, + 1.5709483394835182, + 1.570948339483483, + 1.57094833948349, + 1.5709483394834869, + 1.5709483394834647, + 1.570948339483503, + 1.5709483394835015, + 1.5709483394834833, + 1.5709483394834887, + 1.570948339483481, + 1.5709483394834887, + 1.5709483394834887, + 1.5709483394834873, + 1.5709483394834873, + 1.5709483394834873, + 1.5709483394834773, + 1.5709483394834811, + 1.570948339483495, + 1.5709483394835015, + 1.5709483394834873, + 1.5709483394834773, + 1.570948339483481, + 1.570948339483495, + 1.570948339483495, + 1.570948339483481, + 1.5709483394834773, + 1.5709483394834811, + 1.570948339483495, + 1.570948339483495, + 1.5709483394834873, + 1.570948339483481, + 1.5709483394834873, + 1.570948339483495, + 1.570948339483495, + 1.570948339483495, + 1.5709483394834811, + 1.5709483394834716, + 1.570948339483495, + 1.5709483394834887, + 1.5709483394834887, + 1.5709483394834873, + 1.5709483394834776, + 1.570948339483495, + 1.570948339483495, + 1.570948339483495, + 1.5709483394834811, + 1.5709483394834873, + 1.5709483394834873, + 1.5709483394834811, + 1.570948339483495, + 1.570948339483481, + 1.5709483394834887 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.0006108272393134939, + "y": 4.018264793755861 + }, + { + "x": -0.0012216544787690964, + "y": 8.036529587511723 + }, + { + "x": -0.001221654478911205, + "y": 8.036529587511723 + }, + { + "x": -0.001221654478911205, + "y": 8.036529587512007 + }, + { + "x": -0.001221654478911205, + "y": 8.036529587512007 + }, + { + "x": -0.0012216544790533135, + "y": 8.036529587514565 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785947846 + }, + { + "x": -0.0013743612886685241, + "y": 9.04109578594813 + }, + { + "x": -0.0012216544790533135, + "y": 8.036529587517691 + }, + { + "x": -0.0012216544790533135, + "y": 8.036529587511723 + }, + { + "x": -0.0013743612886685241, + "y": 9.041095785945004 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785950688 + }, + { + "x": -0.0012216544790533135, + "y": 8.036529587514565 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984392495 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.001221654478911205, + "y": 8.03652958750888 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389938 + }, + { + "x": -0.0018324817183668074, + "y": 12.054794381265026 + }, + { + "x": -0.0018324817183668074, + "y": 12.054794381267584 + }, + { + "x": -0.001832481718508916, + "y": 12.0547943812673 + }, + { + "x": -0.001832481718508916, + "y": 12.0547943812673 + }, + { + "x": -0.0016797749086094882, + "y": 11.05022818283146 + }, + { + "x": -0.0016797749084673796, + "y": 11.050228182825776 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.0016797749086094882, + "y": 11.050228182831745 + }, + { + "x": -0.0015270680987100604, + "y": 10.04566198439278 + }, + { + "x": -0.001527068098567952, + "y": 10.045661984383969 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785947846 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.001832481718508916, + "y": 12.05479438127071 + }, + { + "x": -0.0016797749084673796, + "y": 11.050228182828903 + }, + { + "x": -0.0016797749084673796, + "y": 11.050228182825776 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.0013743612889527412, + "y": 9.04109578595353 + }, + { + "x": -0.0013743612889527412, + "y": 9.04109578595353 + }, + { + "x": -0.0012216544787690964, + "y": 8.036529587509165 + }, + { + "x": -0.0013743612889527412, + "y": 9.041095785950688 + }, + { + "x": -0.0013743612890948498, + "y": 9.04109578595353 + }, + { + "x": -0.001221654478911205, + "y": 8.036529587512007 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785950688 + }, + { + "x": -0.0010674037167746064, + "y": 7.021806657110972 + }, + { + "x": -0.0013708873966322699, + "y": 9.018243139047684 + }, + { + "x": -0.00167552904031254, + "y": 11.022297169948843 + }, + { + "x": -0.0018278498619395123, + "y": 12.024324185396154 + }, + { + "x": -0.0016755290401704315, + "y": 11.022297169948843 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154496416 + }, + { + "x": -0.0016755290401704315, + "y": 11.022297169946569 + }, + { + "x": -0.0016755290401704315, + "y": 11.022297169946 + }, + { + "x": -0.0013708873964901613, + "y": 9.018243139043989 + }, + { + "x": -0.0013708873964901613, + "y": 9.018243139043989 + }, + { + "x": -0.0013708873964901613, + "y": 9.018243139049673 + }, + { + "x": -0.001218566574578972, + "y": 8.016216123597246 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154493574 + }, + { + "x": -0.0018278498620816208, + "y": 12.024324185398996 + }, + { + "x": -0.00167552904031254, + "y": 11.022297169948843 + }, + { + "x": -0.0013708873964901613, + "y": 9.018243139043989 + }, + { + "x": -0.001218566574578972, + "y": 8.016216123597246 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154496416 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154496984 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154496416 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154496416 + }, + { + "x": -0.001218566574578972, + "y": 8.016216123597246 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154493574 + }, + { + "x": -0.0018278498620816208, + "y": 12.024324185398996 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154496416 + }, + { + "x": -0.0013708873964901613, + "y": 9.018243139043989 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154496984 + }, + { + "x": -0.0013708873964901613, + "y": 9.0182431390474 + }, + { + "x": -0.0012185665747210805, + "y": 8.016216123597815 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154499826 + }, + { + "x": -0.0018278498620816208, + "y": 12.024324185398996 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154493574 + }, + { + "x": -0.0013708873963480528, + "y": 9.018243139043989 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154496984 + }, + { + "x": -0.0016755290401704315, + "y": 11.022297169946569 + }, + { + "x": -0.0016755290401704315, + "y": 11.022297169949411 + }, + { + "x": -0.0013708873964901613, + "y": 9.0182431390474 + }, + { + "x": -0.001218566574578972, + "y": 8.016216123594972 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154499826 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154499826 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154496984 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154493574 + }, + { + "x": -0.0013708873964901613, + "y": 9.018243139046831 + }, + { + "x": -0.0013708873964901613, + "y": 9.0182431390474 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154494142 + }, + { + "x": -0.0015232082184013507, + "y": 10.020270154496984 + }, + { + "x": -0.0015232082182592421, + "y": 10.020270154496984 + }, + { + "x": -0.0016755290401704315, + "y": 11.022297169949411 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 92.39471721539276, + "y": -162.58165355802566, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 98.51810306057145, + "y": -55.50484681194324, + "z": 1.0 + }, + { + "x": 97.90210763219395, + "y": -55.54275923928253, + "z": 1.0 + }, + { + "x": 97.1672865624749, + "y": -55.63466477552653, + "z": 1.0 + }, + { + "x": 96.68166077772335, + "y": -55.72386663030572, + "z": 1.0 + }, + { + "x": 96.20062722226538, + "y": -55.835202363034306, + "z": 1.0 + }, + { + "x": 95.72519384633145, + "y": -55.96843865514556, + "z": 1.0 + }, + { + "x": 95.25635687268348, + "y": -56.123296327664626, + "z": 1.0 + }, + { + "x": 94.56761440630069, + "y": -56.395400963932104, + "z": 1.0 + }, + { + "x": 94.00903059441187, + "y": -56.65783241733771, + "z": 1.0 + }, + { + "x": 93.35990744115566, + "y": -57.01427844825002, + "z": 1.0 + }, + { + "x": 92.73644077798586, + "y": -57.414744385709014, + "z": 1.0 + }, + { + "x": 92.33698122798639, + "y": -57.70496070084755, + "z": 1.0 + }, + { + "x": 91.85702245212593, + "y": -58.09293837455154, + "z": 1.0 + }, + { + "x": 91.39786416293066, + "y": -58.51010636110452, + "z": 1.0 + }, + { + "x": 91.04411377147096, + "y": -58.869432796682446, + "z": 1.0 + }, + { + "x": 90.70915520760263, + "y": -59.246337381510976, + "z": 1.0 + }, + { + "x": 90.24384526998537, + "y": -59.84249537269999, + "z": 1.0 + }, + { + "x": 89.89184851897855, + "y": -60.3653002596416, + "z": 1.0 + }, + { + "x": 89.63469918666868, + "y": -60.799042668986026, + "z": 1.0 + }, + { + "x": 89.2910951948812, + "y": -61.47329534779308, + "z": 1.0 + }, + { + "x": 89.00037913616302, + "y": -62.171598554025486, + "z": 1.0 + }, + { + "x": 88.83665481370848, + "y": -62.64851758812371, + "z": 1.0 + }, + { + "x": 88.69747394854923, + "y": -63.13316632318826, + "z": 1.0 + }, + { + "x": 88.53549611385694, + "y": -63.871868352028564, + "z": 1.0 + }, + { + "x": 88.44400352070429, + "y": -64.49544921231548, + "z": 1.0 + }, + { + "x": 88.39955449165987, + "y": -64.99772352931295, + "z": 1.0 + }, + { + "x": 88.37999549573044, + "y": -65.73662391198296, + "z": 1.0 + }, + { + "x": 88.38005663906475, + "y": -66.13884911037657, + "z": 1.0 + }, + { + "x": 88.3801483540662, + "y": -66.74218690796627, + "z": 1.0 + }, + { + "x": 88.38020949740049, + "y": -67.1444121063596, + "z": 1.0 + }, + { + "x": 88.38028592656836, + "y": -67.64719360435126, + "z": 1.0 + }, + { + "x": 88.38036235573624, + "y": -68.1499751023429, + "z": 1.0 + }, + { + "x": 88.38042331674794, + "y": -68.55100091016726, + "z": 1.0 + }, + { + "x": 88.38051468344379, + "y": -69.15204741485073, + "z": 1.0 + }, + { + "x": 88.380590822357, + "y": -69.65291950208695, + "z": 1.0 + }, + { + "x": 88.38065173348757, + "y": -70.05361717187591, + "z": 1.0 + }, + { + "x": 88.38072787240077, + "y": -70.55448925911213, + "z": 1.0 + }, + { + "x": 88.38081923909662, + "y": -71.15553576379558, + "z": 1.0 + }, + { + "x": 88.38091060579247, + "y": -71.75658226847905, + "z": 1.0 + }, + { + "x": 88.38098674470567, + "y": -72.25745435571554, + "z": 1.0 + }, + { + "x": 88.38107811140152, + "y": -72.85850086039872, + "z": 1.0 + }, + { + "x": 88.38115425031474, + "y": -73.35937294763522, + "z": 1.0 + }, + { + "x": 88.38121516144531, + "y": -73.7600706174239, + "z": 1.0 + }, + { + "x": 88.38130652814115, + "y": -74.36111712210736, + "z": 1.0 + }, + { + "x": 88.38139789483701, + "y": -74.96216362679083, + "z": 1.0 + }, + { + "x": 88.38148926153285, + "y": -75.56321013147428, + "z": 1.0 + }, + { + "x": 88.3815806282287, + "y": -76.16425663615775, + "z": 1.0 + }, + { + "x": 88.38164153935926, + "y": -76.56495430594671, + "z": 1.0 + }, + { + "x": 88.38171767827248, + "y": -77.06582639318293, + "z": 1.0 + }, + { + "x": 88.38177858940304, + "y": -77.4665240629719, + "z": 1.0 + }, + { + "x": 88.38185472831624, + "y": -77.96739615020812, + "z": 1.0 + }, + { + "x": 88.3819460950121, + "y": -78.56844265489157, + "z": 1.0 + }, + { + "x": 88.3820222339253, + "y": -79.06931474212779, + "z": 1.0 + }, + { + "x": 88.38208314505587, + "y": -79.47001241191676, + "z": 1.0 + }, + { + "x": 88.38214405618643, + "y": -79.87071008170574, + "z": 1.0 + }, + { + "x": 88.38222019509963, + "y": -80.37158216894196, + "z": 1.0 + }, + { + "x": 88.38231156179549, + "y": -80.97262867362541, + "z": 1.0 + }, + { + "x": 88.38237247292605, + "y": -81.37332634341438, + "z": 1.0 + }, + { + "x": 88.38243338405663, + "y": -81.77402401320336, + "z": 1.0 + }, + { + "x": 88.38252475075247, + "y": -82.37507051788681, + "z": 1.0 + }, + { + "x": 88.38260088966568, + "y": -82.87594260512303, + "z": 1.0 + }, + { + "x": 88.38269225636154, + "y": -83.47698910980648, + "z": 1.0 + }, + { + "x": 88.3827531674921, + "y": -83.87768677959545, + "z": 1.0 + }, + { + "x": 88.38282930640531, + "y": -84.37855886683167, + "z": 1.0 + }, + { + "x": 88.38292067310115, + "y": -84.97960537151513, + "z": 1.0 + }, + { + "x": 88.38301203979701, + "y": -85.58065187619859, + "z": 1.0 + }, + { + "x": 88.38310340649285, + "y": -86.18169838088205, + "z": 1.0 + }, + { + "x": 88.38316431762343, + "y": -86.58239605067102, + "z": 1.0 + }, + { + "x": 88.38324045653663, + "y": -87.08326813790724, + "z": 1.0 + }, + { + "x": 88.38331659544984, + "y": -87.58414022514346, + "z": 1.0 + }, + { + "x": 88.3833775065804, + "y": -87.98483789493243, + "z": 1.0 + }, + { + "x": 88.38346887327624, + "y": -88.58588439961588, + "z": 1.0 + }, + { + "x": 88.38354501218946, + "y": -89.0867564868521, + "z": 1.0 + }, + { + "x": 88.3836363788853, + "y": -89.68780299153555, + "z": 1.0 + }, + { + "x": 88.38372774558115, + "y": -90.28884949621902, + "z": 1.0 + }, + { + "x": 88.383819112277, + "y": -90.88989600090247, + "z": 1.0 + }, + { + "x": 88.38391047897285, + "y": -91.49094250558593, + "z": 1.0 + }, + { + "x": 88.38398661788605, + "y": -91.99181459282215, + "z": 1.0 + }, + { + "x": 88.38407798458191, + "y": -92.5928610975056, + "z": 1.0 + }, + { + "x": 88.38413889571248, + "y": -92.99355876729457, + "z": 1.0 + }, + { + "x": 88.38419980684304, + "y": -93.39425643708384, + "z": 1.0 + }, + { + "x": 88.38427594575626, + "y": -93.89512852431977, + "z": 1.0 + }, + { + "x": 88.38435208466946, + "y": -94.39600061155598, + "z": 1.0 + }, + { + "x": 88.38442822358267, + "y": -94.8968726987922, + "z": 1.0 + }, + { + "x": 88.38448913471323, + "y": -95.29757036858116, + "z": 1.0 + }, + { + "x": 88.38456527362645, + "y": -95.79844245581738, + "z": 1.0 + }, + { + "x": 88.38464141253965, + "y": -96.2993145430536, + "z": 1.0 + }, + { + "x": 88.38471755145285, + "y": -96.80018663028982, + "z": 1.0 + }, + { + "x": 88.3848089181487, + "y": -97.40123313497327, + "z": 1.0 + }, + { + "x": 88.38488505706191, + "y": -97.90210522220949, + "z": 1.0 + }, + { + "x": 88.38496119597512, + "y": -98.4029773094457, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 17, + "heading": [ + -1.8073716690550956, + -3.0801235849284487, + -3.045783560178179, + -2.9942734138791116, + -2.937039928828713, + -2.891253154713288, + -2.8454663805070655, + -2.7882328993815464, + -2.736722755012727, + -2.6680425608727507, + -2.605019650346343, + -2.547703940744266, + -2.484670601444463, + -2.4328508823942907, + -2.3791011330762006, + -2.322856624015015, + -2.259060740481147, + -2.2016444200165335, + -2.1378485409691073, + -2.0676302122575954, + -2.0037041380264404, + -1.9397715028023987, + -1.8759678803709683, + -1.81217201350278, + -1.7547557105582061, + -1.6909598213230572, + -1.6223212022660787, + -1.587881173364619, + -1.5706443141062976, + -1.5706443141063116, + -1.570644314106307, + -1.5706443141062976, + -1.5706443141063156, + -1.5706443141063124, + -1.5706443141063058, + -1.570644314106303, + -1.570644314106303, + -1.5706443141063058, + -1.570644314106307, + -1.5706443141063187, + -1.5706443141063058, + -1.570644314106293, + -1.5706443141062871, + -1.5706443141063045, + -1.570644314106307, + -1.570644314106307, + -1.570644314106307, + -1.5706443141063045, + -1.570644314106303, + -1.570644314106303, + -1.5706443141063187, + -1.5706443141063058, + -1.5706443141063058, + -1.570644314106303, + -1.570644314106301, + -1.5706443141063187, + -1.5706443141063058, + -1.5706443141063045, + -1.570644314106301, + -1.5706443141063045, + -1.5706443141063058, + -1.570644314106293, + -1.5706443141063045, + -1.570644314106303, + -1.5706443141063058, + -1.570644314106307, + -1.570644314106307, + -1.5706443141063045, + -1.570644314106303, + -1.5706443141063045, + -1.570644314106303, + -1.5706443141063187, + -1.5706443141063058, + -1.5706443141063058, + -1.570644314106307, + -1.570644314106307, + -1.570644314106307, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141062902, + -1.570644314106301, + -1.570644314106303, + -1.5706443141063045, + -1.5706443141063045, + -1.570644314106303, + -1.570644314106303, + -1.5706443141063045, + -1.5706443141063187, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141063045 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -6.159954283775022, + "y": -0.3791242733929323 + }, + { + "x": -13.50816498096549, + "y": -1.2981796358329234 + }, + { + "x": -12.204468544705946, + "y": -1.8110739102318973 + }, + { + "x": -9.666593402095174, + "y": -2.005375875077746 + }, + { + "x": -9.564669313919012, + "y": -2.445720248398402 + }, + { + "x": -9.44270349581899, + "y": -2.8809396463032044 + }, + { + "x": -11.575794400307586, + "y": -4.269623087865426 + }, + { + "x": -12.473262782716148, + "y": -5.345360896730824 + }, + { + "x": -12.077069651450358, + "y": -6.188774843179132 + }, + { + "x": -12.725898164260059, + "y": -7.569119683713055 + }, + { + "x": -10.229262131692707, + "y": -6.906822525975329 + }, + { + "x": -8.794183258599304, + "y": -6.781939888425228 + }, + { + "x": -9.391170650557257, + "y": -8.051456602569687 + }, + { + "x": -8.129086806549708, + "y": -7.764944221309094 + }, + { + "x": -6.887089553280248, + "y": -7.3623102040645705 + }, + { + "x": -8.0026850148559, + "y": -9.73062576017547 + }, + { + "x": -8.17306688624086, + "y": -11.18962878130624 + }, + { + "x": -6.091460833166877, + "y": -9.565472962860326 + }, + { + "x": -6.007533240973544, + "y": -11.079950881514833 + }, + { + "x": -6.343200505056643, + "y": -13.725558850394606 + }, + { + "x": -4.544403811727165, + "y": -11.752222403306263 + }, + { + "x": -3.029051876137885, + "y": -9.615677691627766 + }, + { + "x": -3.011586998515412, + "y": -12.23350763904854 + }, + { + "x": -2.5347042784494533, + "y": -13.62282889127215 + }, + { + "x": -1.3594162219706618, + "y": -11.258551772843859 + }, + { + "x": -0.6400802497384461, + "y": -12.411746996674822 + }, + { + "x": -0.19497852595122822, + "y": -11.411255810636192 + }, + { + "x": 0.0015285833575262586, + "y": -10.055629959833112 + }, + { + "x": 0.00152858335738415, + "y": -10.05562995983027 + }, + { + "x": 0.0013757250216883676, + "y": -9.050066963849872 + }, + { + "x": 0.0015285833575262586, + "y": -10.055629959833112 + }, + { + "x": 0.001373901795744814, + "y": -9.038073058160023 + }, + { + "x": 0.0015232770755346792, + "y": -10.020723125078206 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196843 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0016750560904199574, + "y": -11.019185919199543 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0016750560907041745, + "y": -11.019185919196843 + }, + { + "x": 0.0013705004379005459, + "y": -9.01569757025186 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744721367 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0013705004376163288, + "y": -9.01569757025186 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.001218222611356623, + "y": -8.01395339577951 + }, + { + "x": 0.0013705004376163288, + "y": -9.015697570252001 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.001218222611356623, + "y": -8.01395339577951 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0016750560907041745, + "y": -11.019185919196701 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196843 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.001522778264018143, + "y": -10.01744174472421 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196843 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0015227782643023602, + "y": -10.01744174472421 + }, + { + "x": 0.001218222611356623, + "y": -8.013953395782352 + }, + { + "x": 0.0013705004377584373, + "y": -9.015697570252001 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744721367 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.001522778264018143, + "y": -10.017441744724351 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 88.38496119597512, + "y": -98.4029773094457, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 155.20273178651496, + "y": -2.8951887416430546, + "z": 1.0 + }, + { + "x": 154.92579367058397, + "y": -3.46365707666486, + "z": 1.0 + }, + { + "x": 154.64273338351381, + "y": -4.167631816676954, + "z": 1.0 + }, + { + "x": 154.44890421003072, + "y": -4.770275261371781, + "z": 1.0 + }, + { + "x": 154.268611816407, + "y": -5.507298142270336, + "z": 1.0 + }, + { + "x": 154.162323368481, + "y": -6.130640444882252, + "z": 1.0 + }, + { + "x": 154.09647483718254, + "y": -6.759539423961707, + "z": 1.0 + }, + { + "x": 154.07136072828882, + "y": -7.38586739845515, + "z": 1.0 + }, + { + "x": 154.07045676264278, + "y": -7.796281509985351, + "z": 1.0 + }, + { + "x": 154.06984533725478, + "y": -8.299606630835797, + "z": 1.0 + }, + { + "x": 154.0693561969444, + "y": -8.702266727515596, + "z": 1.0 + }, + { + "x": 154.06862248647877, + "y": -9.306256872536707, + "z": 1.0 + }, + { + "x": 154.06788877601315, + "y": -9.910247017557246, + "z": 1.0 + }, + { + "x": 154.06715506554758, + "y": -10.514237162577224, + "z": 1.0 + }, + { + "x": 154.0666659252372, + "y": -10.916897259258146, + "z": 1.0 + }, + { + "x": 154.06654364015958, + "y": -11.01756228342824, + "z": 1.0 + }, + { + "x": 154.06605662413546, + "y": -11.418473668533366, + "z": 1.0 + }, + { + "x": 154.06532610009936, + "y": -12.019840746190324, + "z": 1.0 + }, + { + "x": 154.06483908407523, + "y": -12.42075213129591, + "z": 1.0 + }, + { + "x": 154.06423031404506, + "y": -12.92189136267718, + "z": 1.0 + }, + { + "x": 154.06349979000896, + "y": -13.52325844033357, + "z": 1.0 + }, + { + "x": 154.06301277398484, + "y": -13.924169825439153, + "z": 1.0 + }, + { + "x": 154.06240400395467, + "y": -14.425309056820423, + "z": 1.0 + }, + { + "x": 154.0616734799185, + "y": -15.026676134477947, + "z": 1.0 + }, + { + "x": 154.0610647098884, + "y": -15.527815365859787, + "z": 1.0 + }, + { + "x": 154.06045593985823, + "y": -16.02895459724106, + "z": 1.0 + }, + { + "x": 154.05984716982812, + "y": -16.530093828622334, + "z": 1.0 + }, + { + "x": 154.059360153804, + "y": -16.931005213727346, + "z": 1.0 + }, + { + "x": 154.05875138377388, + "y": -17.43214444510862, + "z": 1.0 + }, + { + "x": 154.0581426137437, + "y": -17.933283676489893, + "z": 1.0 + }, + { + "x": 154.0575338437136, + "y": -18.434422907871166, + "z": 1.0 + }, + { + "x": 154.05680331967744, + "y": -19.035789985528126, + "z": 1.0 + }, + { + "x": 154.05631630365332, + "y": -19.436701370633706, + "z": 1.0 + }, + { + "x": 154.05558577961716, + "y": -20.03806844829067, + "z": 1.0 + }, + { + "x": 154.054855255581, + "y": -20.639435525948198, + "z": 1.0 + }, + { + "x": 154.05436823955688, + "y": -21.04034691105378, + "z": 1.0 + }, + { + "x": 154.05363771552072, + "y": -21.64171398871131, + "z": 1.0 + }, + { + "x": 154.0530289454906, + "y": -22.142853220092015, + "z": 1.0 + }, + { + "x": 154.05254192946649, + "y": -22.543764605197595, + "z": 1.0 + }, + { + "x": 154.05205491344236, + "y": -22.944675990302613, + "z": 1.0 + }, + { + "x": 154.05132438940626, + "y": -23.546043067959577, + "z": 1.0 + }, + { + "x": 154.05083737338214, + "y": -23.94695445306516, + "z": 1.0 + }, + { + "x": 154.05022860335197, + "y": -24.448093684446434, + "z": 1.0 + }, + { + "x": 154.04961983332186, + "y": -24.94923291582714, + "z": 1.0 + }, + { + "x": 154.0488893092857, + "y": -25.550599993484663, + "z": 1.0 + }, + { + "x": 154.04828053925553, + "y": -26.051739224866502, + "z": 1.0 + }, + { + "x": 154.04779352323146, + "y": -26.45265060997152, + "z": 1.0 + }, + { + "x": 154.0470629991953, + "y": -27.05401768762905, + "z": 1.0 + }, + { + "x": 154.04657598317118, + "y": -27.454929072733492, + "z": 1.0 + }, + { + "x": 154.0460889671471, + "y": -27.855840457838504, + "z": 1.0 + }, + { + "x": 154.0453584431109, + "y": -28.457207535496586, + "z": 1.0 + }, + { + "x": 154.0447496730808, + "y": -28.958346766877852, + "z": 1.0 + }, + { + "x": 154.04426265705666, + "y": -29.359258151982296, + "z": 1.0 + }, + { + "x": 154.04377564103254, + "y": -29.760169537087876, + "z": 1.0 + }, + { + "x": 154.04316687100243, + "y": -30.26130876846857, + "z": 1.0 + }, + { + "x": 154.0426798549783, + "y": -30.662220153573582, + "z": 1.0 + }, + { + "x": 154.04219283895424, + "y": -31.06313153867916, + "z": 1.0 + }, + { + "x": 154.04146231491808, + "y": -31.664498616336104, + "z": 1.0 + }, + { + "x": 154.0408535448879, + "y": -32.16563784771737, + "z": 1.0 + }, + { + "x": 154.04036652886384, + "y": -32.56654923282238, + "z": 1.0 + }, + { + "x": 154.03987951283972, + "y": -32.96746061792796, + "z": 1.0 + }, + { + "x": 154.03914898880356, + "y": -33.56882769558491, + "z": 1.0 + }, + { + "x": 154.03866197277944, + "y": -33.96973908068992, + "z": 1.0 + }, + { + "x": 154.03793144874328, + "y": -34.571106158348, + "z": 1.0 + }, + { + "x": 154.0373226787131, + "y": -35.07224538972927, + "z": 1.0 + }, + { + "x": 154.036592154677, + "y": -35.673612467386214, + "z": 1.0 + }, + { + "x": 154.03610513865289, + "y": -36.074523852491794, + "z": 1.0 + }, + { + "x": 154.03549636862272, + "y": -36.57566308387306, + "z": 1.0 + }, + { + "x": 154.03476584458656, + "y": -37.177030161530006, + "z": 1.0 + }, + { + "x": 154.03415707455645, + "y": -37.67816939291127, + "z": 1.0 + }, + { + "x": 154.03354830452628, + "y": -38.1793086242931, + "z": 1.0 + }, + { + "x": 154.03281778049012, + "y": -38.78067570195062, + "z": 1.0 + }, + { + "x": 154.03220901046, + "y": -39.28181493333131, + "z": 1.0 + }, + { + "x": 154.03147848642385, + "y": -39.883182010988826, + "z": 1.0 + }, + { + "x": 154.03099147039973, + "y": -40.284093396094406, + "z": 1.0 + }, + { + "x": 154.03038270036961, + "y": -40.785232627475104, + "z": 1.0 + }, + { + "x": 154.0298956843455, + "y": -41.18614401258068, + "z": 1.0 + }, + { + "x": 154.02940866832137, + "y": -41.58705539768569, + "z": 1.0 + }, + { + "x": 154.02867814428527, + "y": -42.18842247534264, + "z": 1.0 + }, + { + "x": 154.02794762024905, + "y": -42.78978955300072, + "z": 1.0 + }, + { + "x": 154.02733885021894, + "y": -43.290928784382, + "z": 1.0 + }, + { + "x": 154.02660832618278, + "y": -43.89229586203953, + "z": 1.0 + }, + { + "x": 154.02587780214662, + "y": -44.493662939697074, + "z": 1.0 + }, + { + "x": 154.0253907861225, + "y": -44.8945743248021, + "z": 1.0 + }, + { + "x": 154.0247820160924, + "y": -45.395713556183374, + "z": 1.0 + }, + { + "x": 154.02405149205623, + "y": -45.99708063384035, + "z": 1.0 + }, + { + "x": 154.02380798404414, + "y": -46.197536326393426, + "z": 1.0 + }, + { + "x": 154.02331963718677, + "y": -46.59954325286995, + "z": 1.0 + }, + { + "x": 154.02258711690075, + "y": -47.20255364258411, + "z": 1.0 + }, + { + "x": 154.02185459661473, + "y": -47.80556403229883, + "z": 1.0 + }, + { + "x": 154.02136624975736, + "y": -48.20757095877531, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 18, + "heading": [ + -1.5933732308870605, + -2.024123654791085, + -1.9853844803775595, + -1.920753503210538, + -1.8431230927316367, + -1.7784250031504312, + -1.7074032821837042, + -1.6431369583595543, + -1.5958870555446687, + -1.5724547754977745, + -1.572011098444287, + -1.5720110984443112, + -1.5720110984443483, + -1.5720110984443023, + -1.5720110984442548, + -1.5720110984443099, + -1.5720110984443811, + -1.5720110984442577, + -1.5720110984442572, + -1.5720110984443574, + -1.572011098444279, + -1.572011098444258, + -1.5720110984443574, + -1.5720110984443294, + -1.5720110984442772, + -1.5720110984443132, + -1.5720110984443139, + -1.572011098444295, + -1.572011098444295, + -1.5720110984443139, + -1.5720110984443139, + -1.5720110984442786, + -1.5720110984443139, + -1.5720110984443139, + -1.5720110984442957, + -1.5720110984443132, + -1.5720110984443132, + -1.5720110984442786, + -1.572011098444295, + -1.5720110984443414, + -1.572011098444258, + -1.5720110984442572, + -1.5720110984443574, + -1.5720110984443145, + -1.5720110984442786, + -1.5720110984443287, + -1.5720110984442943, + -1.5720110984442572, + -1.5720110984443145, + -1.5720110984442723, + -1.5720110984443132, + -1.5720110984443287, + -1.5720110984442959, + -1.5720110984443423, + -1.572011098444295, + -1.5720110984442959, + -1.5720110984442706, + -1.5720110984442572, + -1.57201109844433, + -1.572011098444295, + -1.5720110984442706, + -1.5720110984443139, + -1.5720110984443147, + -1.5720110984443132, + -1.5720110984443287, + -1.5720110984442786, + -1.5720110984442572, + -1.5720110984443574, + -1.57201109844433, + -1.5720110984442786, + -1.5720110984443132, + -1.5720110984443287, + -1.5720110984442786, + -1.5720110984442786, + -1.5720110984443132, + -1.572011098444295, + -1.572011098444295, + -1.5720110984443414, + -1.572011098444258, + -1.572011098444295, + -1.5720110984443287, + -1.5720110984442779, + -1.572011098444295, + -1.5720110984443139, + -1.572011098444295, + -1.5720110984442786, + -1.5720110984443423, + -1.5720110984443705, + -1.5720110984442879, + -1.572011098444269, + -1.5720110984442872 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -2.769381159309887, + "y": -5.684683350218052 + }, + { + "x": -5.599984030011456, + "y": -12.72443075033899 + }, + { + "x": -4.768894605532523, + "y": -13.066181847069211 + }, + { + "x": -3.741215671068119, + "y": -13.396663255933827 + }, + { + "x": -2.865808415497213, + "y": -13.603651835104706 + }, + { + "x": -1.7213697922446158, + "y": -12.522412816913704 + }, + { + "x": -0.9096264019217415, + "y": -12.552269535728984 + }, + { + "x": -0.2601807453976335, + "y": -10.367420860236445 + }, + { + "x": -0.015153910340472976, + "y": -9.137392323806468 + }, + { + "x": -0.011005656983797962, + "y": -9.059852175302447 + }, + { + "x": -0.012228507760028151, + "y": -10.066502417009104 + }, + { + "x": -0.014674209312488529, + "y": -12.079802900416503 + }, + { + "x": -0.014674209311920094, + "y": -12.07980290040517 + }, + { + "x": -0.012228507759459717, + "y": -10.066502417008998 + }, + { + "x": -0.0061142538800140755, + "y": -5.0332512085101655 + }, + { + "x": -0.006093011017469507, + "y": -5.0157640927522 + }, + { + "x": -0.012175400602245645, + "y": -10.022784627620833 + }, + { + "x": -0.012175400602245645, + "y": -10.022784627625434 + }, + { + "x": -0.010957860542930575, + "y": -9.020506164868554 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090376612 + }, + { + "x": -0.012175400602245645, + "y": -10.022784627619732 + }, + { + "x": -0.010957860542930575, + "y": -9.020506164868518 + }, + { + "x": -0.013392940663266018, + "y": -11.025063090387945 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090393648 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627631136 + }, + { + "x": -0.01217540060281408, + "y": -10.02278462762547 + }, + { + "x": -0.010957860542362141, + "y": -9.020506164862852 + }, + { + "x": -0.010957860542362141, + "y": -9.020506164862852 + }, + { + "x": -0.01217540060281408, + "y": -10.02278462762547 + }, + { + "x": -0.01217540060281408, + "y": -10.02278462762547 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382332 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627625398 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627625434 + }, + { + "x": -0.014610480723149522, + "y": -12.027341553144915 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627631118 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627631118 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382332 + }, + { + "x": -0.010957860542362141, + "y": -9.020506164862852 + }, + { + "x": -0.009740320482478637, + "y": -8.018227702105989 + }, + { + "x": -0.012175400602245645, + "y": -10.02278462761982 + }, + { + "x": -0.012175400602245645, + "y": -10.02278462762547 + }, + { + "x": -0.010957860542930575, + "y": -9.020506164868571 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627619785 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382297 + }, + { + "x": -0.013392940663266018, + "y": -11.02506309039363 + }, + { + "x": -0.010957860542362141, + "y": -9.020506164868571 + }, + { + "x": -0.012175400602245645, + "y": -10.02278462762547 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627619714 + }, + { + "x": -0.009740320481910203, + "y": -8.01822770209455 + }, + { + "x": -0.01217540060281408, + "y": -10.02278462763094 + }, + { + "x": -0.013392940663266018, + "y": -11.025063090393488 + }, + { + "x": -0.010957860542362141, + "y": -9.020506164857096 + }, + { + "x": -0.009740320482478637, + "y": -8.018227702100234 + }, + { + "x": -0.010957860542362141, + "y": -9.020506164862745 + }, + { + "x": -0.010957860542362141, + "y": -9.02050616485706 + }, + { + "x": -0.009740320481910203, + "y": -8.018227702105882 + }, + { + "x": -0.012175400602245645, + "y": -10.02278462762522 + }, + { + "x": -0.013392940663266018, + "y": -11.025063090382119 + }, + { + "x": -0.010957860542362141, + "y": -9.02050616486278 + }, + { + "x": -0.009740320481910203, + "y": -8.018227702105918 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627625256 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627619572 + }, + { + "x": -0.01217540060281408, + "y": -10.02278462763094 + }, + { + "x": -0.013392940663266018, + "y": -11.025063090393488 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382119 + }, + { + "x": -0.012175400602245645, + "y": -10.022784627625256 + }, + { + "x": -0.010957860542930575, + "y": -9.020506164868465 + }, + { + "x": -0.013392940663266018, + "y": -11.025063090382119 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382119 + }, + { + "x": -0.01217540060281408, + "y": -10.02278462763094 + }, + { + "x": -0.013392940663266018, + "y": -11.025063090393488 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382119 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382048 + }, + { + "x": -0.01217540060281408, + "y": -10.02278462763094 + }, + { + "x": -0.010957860542362141, + "y": -9.02050616486278 + }, + { + "x": -0.010957860542362141, + "y": -9.02050616486271 + }, + { + "x": -0.009740320482478637, + "y": -8.018227702105847 + }, + { + "x": -0.012175400602245645, + "y": -10.022784627619643 + }, + { + "x": -0.014610480723149522, + "y": -12.02734155315035 + }, + { + "x": -0.013392940663266018, + "y": -11.025063090393559 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090388088 + }, + { + "x": -0.014610480723149522, + "y": -12.027341553150777 + }, + { + "x": -0.01217540060281408, + "y": -10.022784627625683 + }, + { + "x": -0.010957860542362141, + "y": -9.020506164862994 + }, + { + "x": -0.013392940662697583, + "y": -11.025063090382474 + }, + { + "x": -0.009740320482478637, + "y": -8.018227702100518 + }, + { + "x": -0.0073185486945703815, + "y": -6.0246261902960185 + }, + { + "x": -0.012208671433882046, + "y": -10.050173161906812 + }, + { + "x": -0.014650405720431081, + "y": -12.060207794288829 + }, + { + "x": -0.012208671433882046, + "y": -10.050173161912 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 154.02136624975736, + "y": -48.20757095877531, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 327.0808235050242, + "y": -195.15922625059818, + "z": 1.0 + }, + { + "x": 326.5771403058819, + "y": -195.1591853215736, + "z": 1.0 + }, + { + "x": 326.0734571067396, + "y": -195.15914439254902, + "z": 1.0 + }, + { + "x": 325.6705105474251, + "y": -195.1591116493294, + "z": 1.0 + }, + { + "x": 325.0697613586538, + "y": -195.1590628327749, + "z": 1.0 + }, + { + "x": 324.56942668828424, + "y": -195.15902217584997, + "z": 1.0 + }, + { + "x": 324.1691589519886, + "y": -195.15898965031002, + "z": 1.0 + }, + { + "x": 323.56875734754516, + "y": -195.1589408620001, + "z": 1.0 + }, + { + "x": 323.1684896112495, + "y": -195.15890833646017, + "z": 1.0 + }, + { + "x": 322.7682218749539, + "y": -195.15887581092022, + "z": 1.0 + }, + { + "x": 322.2678872045844, + "y": -195.15883515399528, + "z": 1.0 + }, + { + "x": 321.6674856001403, + "y": -195.1587863656854, + "z": 1.0 + }, + { + "x": 321.0670839956975, + "y": -195.15873757737546, + "z": 1.0 + }, + { + "x": 320.466682391254, + "y": -195.15868878906554, + "z": 1.0 + }, + { + "x": 320.06641465495835, + "y": -195.1586562635256, + "z": 1.0 + }, + { + "x": 319.6661469186627, + "y": -195.15862373798566, + "z": 1.0 + }, + { + "x": 319.1658122482932, + "y": -195.15858308106073, + "z": 1.0 + }, + { + "x": 318.7655445119976, + "y": -195.15855055552078, + "z": 1.0 + }, + { + "x": 318.36527677570194, + "y": -195.15851802998083, + "z": 1.0 + }, + { + "x": 317.86494210533243, + "y": -195.15847737305592, + "z": 1.0 + }, + { + "x": 317.4646743690368, + "y": -195.15844484751597, + "z": 1.0 + }, + { + "x": 316.96433969866723, + "y": -195.15840419059103, + "z": 1.0 + }, + { + "x": 316.3639380942238, + "y": -195.1583554022811, + "z": 1.0 + }, + { + "x": 315.8636034238543, + "y": -195.1583147453562, + "z": 1.0 + }, + { + "x": 315.3632687534847, + "y": -195.15827408843126, + "z": 1.0 + }, + { + "x": 314.76286714904126, + "y": -195.15822530012133, + "z": 1.0 + }, + { + "x": 314.3625994127456, + "y": -195.15819277458138, + "z": 1.0 + }, + { + "x": 313.96233167645, + "y": -195.15816024904146, + "z": 1.0 + }, + { + "x": 313.36193007200654, + "y": -195.15811146073153, + "z": 1.0 + }, + { + "x": 312.861595401637, + "y": -195.1580708038066, + "z": 1.0 + }, + { + "x": 312.36126073126746, + "y": -195.15803014688169, + "z": 1.0 + }, + { + "x": 311.76085912682396, + "y": -195.15798135857176, + "z": 1.0 + }, + { + "x": 311.16045752238057, + "y": -195.15793257026183, + "z": 1.0 + }, + { + "x": 310.660122852011, + "y": -195.1578919133369, + "z": 1.0 + }, + { + "x": 310.15978818164143, + "y": -195.157851256412, + "z": 1.0 + }, + { + "x": 309.55938657719804, + "y": -195.15780246810206, + "z": 1.0 + }, + { + "x": 308.95898497275454, + "y": -195.15775367979214, + "z": 1.0 + }, + { + "x": 308.458650302385, + "y": -195.15771302286723, + "z": 1.0 + }, + { + "x": 307.8582486979416, + "y": -195.1576642345573, + "z": 1.0 + }, + { + "x": 307.45798096164594, + "y": -195.15763170901735, + "z": 1.0 + }, + { + "x": 307.0577132253503, + "y": -195.1575991834774, + "z": 1.0 + }, + { + "x": 306.6574454890547, + "y": -195.15756665793748, + "z": 1.0 + }, + { + "x": 306.05704388461123, + "y": -195.15751786962755, + "z": 1.0 + }, + { + "x": 305.4566422801678, + "y": -195.15746908131763, + "z": 1.0 + }, + { + "x": 305.0563745438716, + "y": -195.1574365557777, + "z": 1.0 + }, + { + "x": 304.5560398735026, + "y": -195.15739589885277, + "z": 1.0 + }, + { + "x": 304.0557052031331, + "y": -195.15735524192783, + "z": 1.0 + }, + { + "x": 303.55537053276356, + "y": -195.1573145850029, + "z": 1.0 + }, + { + "x": 302.95496892832006, + "y": -195.157265796693, + "z": 1.0 + }, + { + "x": 302.5547011920244, + "y": -195.15723327115305, + "z": 1.0 + }, + { + "x": 302.1544334557288, + "y": -195.1572007456131, + "z": 1.0 + }, + { + "x": 301.5540318512854, + "y": -195.15715195730317, + "z": 1.0 + }, + { + "x": 301.05369718091583, + "y": -195.15711130037826, + "z": 1.0 + }, + { + "x": 300.4532955764724, + "y": -195.15706251206834, + "z": 1.0 + }, + { + "x": 300.05302784017675, + "y": -195.15702998652839, + "z": 1.0 + }, + { + "x": 299.45262623573325, + "y": -195.1569811982185, + "z": 1.0 + }, + { + "x": 298.85222463128986, + "y": -195.15693240990856, + "z": 1.0 + }, + { + "x": 298.25182302684635, + "y": -195.15688362159864, + "z": 1.0 + }, + { + "x": 297.6514214224024, + "y": -195.15683483328874, + "z": 1.0 + }, + { + "x": 297.1510867520334, + "y": -195.1567941763638, + "z": 1.0 + }, + { + "x": 296.75081901573776, + "y": -195.15676165082385, + "z": 1.0 + }, + { + "x": 296.2504843453682, + "y": -195.15672099389892, + "z": 1.0 + }, + { + "x": 295.7501496749987, + "y": -195.156680336974, + "z": 1.0 + }, + { + "x": 295.24981500462917, + "y": -195.15663968004907, + "z": 1.0 + }, + { + "x": 294.64941340018567, + "y": -195.15659089173914, + "z": 1.0 + }, + { + "x": 294.24914566389003, + "y": -195.1565583661992, + "z": 1.0 + }, + { + "x": 293.7488109935205, + "y": -195.15651770927428, + "z": 1.0 + }, + { + "x": 293.248476323151, + "y": -195.15647705234935, + "z": 1.0 + }, + { + "x": 292.8482085868554, + "y": -195.1564445268094, + "z": 1.0 + }, + { + "x": 292.2478069824119, + "y": -195.15639573849947, + "z": 1.0 + }, + { + "x": 291.6474053779685, + "y": -195.15634695018957, + "z": 1.0 + }, + { + "x": 291.047003773525, + "y": -195.15629816187965, + "z": 1.0 + }, + { + "x": 290.64673603722935, + "y": -195.1562656363397, + "z": 1.0 + }, + { + "x": 290.04633443278595, + "y": -195.1562168480298, + "z": 1.0 + }, + { + "x": 289.6460666964903, + "y": -195.15618432248985, + "z": 1.0 + }, + { + "x": 289.2457989601947, + "y": -195.1561517969499, + "z": 1.0 + }, + { + "x": 288.6453973557512, + "y": -195.15610300863997, + "z": 1.0 + }, + { + "x": 288.0449957513078, + "y": -195.15605422033008, + "z": 1.0 + }, + { + "x": 287.4445941468643, + "y": -195.15600543202015, + "z": 1.0 + }, + { + "x": 286.84419254242084, + "y": -195.15595664371023, + "z": 1.0 + }, + { + "x": 286.3438578720513, + "y": -195.15591598678532, + "z": 1.0 + }, + { + "x": 285.7434562676079, + "y": -195.1558671984754, + "z": 1.0 + }, + { + "x": 285.34318853131225, + "y": -195.15583467293544, + "z": 1.0 + }, + { + "x": 284.74278692686875, + "y": -195.15578588462554, + "z": 1.0 + }, + { + "x": 284.24245225649923, + "y": -195.1557452277006, + "z": 1.0 + }, + { + "x": 283.7421175861297, + "y": -195.15570457077567, + "z": 1.0 + }, + { + "x": 283.1417159816862, + "y": -195.15565578246574, + "z": 1.0 + }, + { + "x": 282.6413813113167, + "y": -195.15561512554083, + "z": 1.0 + }, + { + "x": 282.1410466409472, + "y": -195.1555744686159, + "z": 1.0 + }, + { + "x": 281.6407119705776, + "y": -195.15553381169096, + "z": 1.0 + }, + { + "x": 281.1403773002075, + "y": -195.15549315476605, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 19, + "heading": [ + 3.1415113941303776, + 3.141511394130359, + 3.141511394130359, + 3.1415113941303905, + 3.141511394130348, + 3.1415113941303248, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130399, + 3.1415113941303634, + 3.141511394130389, + 3.141511394130387, + 3.141511394130363, + 3.1415113941303634, + 3.141511394130399, + 3.141511394130395, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130395, + 3.141511394130395, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130389, + 3.141511394130392, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130399, + 3.141511394130392, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130389, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130389, + 3.1415113941303634, + 3.141511394130389, + 3.141511394130389, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130399, + 3.141511394130392, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130395, + 3.141511394130363, + 3.1415113941303634, + 3.141511394130389, + 3.141511394130392, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130389, + 3.141511394130389, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130387, + 3.1415113941303634, + 3.141511394130387, + 3.141511394130389, + 3.141511394130363, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130392, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130395, + 3.141511394130392, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130387, + 3.141511394130387, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130392, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130387, + 3.141511394130387, + 3.1415113941303634, + 3.141511394130389, + 3.141511394130389, + 3.1415113941303634, + 3.141511394130392, + 3.141511394130389, + 3.1415113941303634, + 3.1415113941303634, + 3.141511394130389, + 3.141511394130392, + 3.1415113941303634, + 3.141511394130392 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -5.036831991423014, + "y": 0.0004092902457841774 + }, + { + "x": -10.073663982846028, + "y": 0.0008185804915683548 + }, + { + "x": -9.06629758456802, + "y": 0.0007367224421273022 + }, + { + "x": -10.036957480858177, + "y": 0.0008155977411661297 + }, + { + "x": -11.010838591408856, + "y": 0.0008947347942012129 + }, + { + "x": -9.006024066652003, + "y": 0.0007318246488807745 + }, + { + "x": -10.006693407390799, + "y": 0.0008131384987564161 + }, + { + "x": -10.006693407390799, + "y": 0.000813138498472199 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107987209158 + }, + { + "x": -9.006024066651435, + "y": 0.0007318246488807745 + }, + { + "x": -11.007362748135847, + "y": 0.0008944523483478406 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661982234822 + }, + { + "x": -12.008032088863274, + "y": 0.0009757661985076993 + }, + { + "x": -10.006693407391367, + "y": 0.0008131384987564161 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107987209158 + }, + { + "x": -9.006024066651435, + "y": 0.0007318246485965574 + }, + { + "x": -9.006024066651435, + "y": 0.0007318246488807745 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107990051329 + }, + { + "x": -9.006024066651435, + "y": 0.0007318246485965574 + }, + { + "x": -9.006024066651435, + "y": 0.0007318246485965574 + }, + { + "x": -9.006024066652003, + "y": 0.0007318246488807745 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523486320577 + }, + { + "x": -11.007362748129594, + "y": 0.0008944523483478406 + }, + { + "x": -10.006693407390799, + "y": 0.000813138498472199 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523486320577 + }, + { + "x": -10.006693407390799, + "y": 0.0008131384987564161 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107987209158 + }, + { + "x": -10.006693407390799, + "y": 0.000813138498472199 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523486320577 + }, + { + "x": -10.006693407390799, + "y": 0.000813138498472199 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523483478406 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661985076993 + }, + { + "x": -11.007362748129594, + "y": 0.0008944523486320577 + }, + { + "x": -10.006693407391367, + "y": 0.000813138498472199 + }, + { + "x": -11.007362748129594, + "y": 0.0008944523483478406 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661985076993 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523483478406 + }, + { + "x": -11.007362748129594, + "y": 0.0008944523483478406 + }, + { + "x": -10.006693407390799, + "y": 0.0008131384987564161 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107990051329 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107987209158 + }, + { + "x": -10.006693407390799, + "y": 0.000813138498472199 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661985076993 + }, + { + "x": -10.006693407396483, + "y": 0.000813138498472199 + }, + { + "x": -9.006024066652003, + "y": 0.0007318246485965574 + }, + { + "x": -10.006693407385114, + "y": 0.0008131384987564161 + }, + { + "x": -10.00669340739023, + "y": 0.0008131384987564161 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523483478406 + }, + { + "x": -10.006693407391367, + "y": 0.000813138498472199 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107990051329 + }, + { + "x": -10.00669340739023, + "y": 0.0008131384987564161 + }, + { + "x": -11.007362748129594, + "y": 0.0008944523483478406 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523483478406 + }, + { + "x": -10.006693407390799, + "y": 0.0008131384987564161 + }, + { + "x": -10.006693407391367, + "y": 0.000813138498472199 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661982234822 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661985076993 + }, + { + "x": -12.008032088874643, + "y": 0.0009757661982234822 + }, + { + "x": -11.007362748129594, + "y": 0.0008944523483478406 + }, + { + "x": -9.006024066646319, + "y": 0.0007318246488807745 + }, + { + "x": -9.006024066652003, + "y": 0.0007318246488807745 + }, + { + "x": -10.006693407390799, + "y": 0.000813138498472199 + }, + { + "x": -10.00669340739023, + "y": 0.000813138498472199 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523486320577 + }, + { + "x": -10.006693407391367, + "y": 0.0008131384987564161 + }, + { + "x": -9.006024066651435, + "y": 0.0007318246485965574 + }, + { + "x": -10.00669340739023, + "y": 0.000813138498472199 + }, + { + "x": -9.006024066651435, + "y": 0.0007318246488807745 + }, + { + "x": -10.006693407391367, + "y": 0.0008131384987564161 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661982234822 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661982234822 + }, + { + "x": -10.006693407391367, + "y": 0.0008131384987564161 + }, + { + "x": -10.00669340739023, + "y": 0.000813138498472199 + }, + { + "x": -10.00669340739023, + "y": 0.000813138498472199 + }, + { + "x": -8.005354725912639, + "y": 0.0006505107990051329 + }, + { + "x": -10.006693407391367, + "y": 0.0008131384987564161 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661982234822 + }, + { + "x": -12.008032088868958, + "y": 0.0009757661982234822 + }, + { + "x": -12.008032088869527, + "y": 0.0009757661985076993 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523483478406 + }, + { + "x": -11.007362748129594, + "y": 0.0008944523483478406 + }, + { + "x": -10.00669340739023, + "y": 0.0008131384987564161 + }, + { + "x": -10.006693407391367, + "y": 0.000813138498472199 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523483478406 + }, + { + "x": -10.00669340739023, + "y": 0.0008131384987564161 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523486320577 + }, + { + "x": -11.007362748130163, + "y": 0.0008944523483478406 + }, + { + "x": -10.00669340739023, + "y": 0.000813138498472199 + }, + { + "x": -10.006693407390799, + "y": 0.0008131384987564161 + }, + { + "x": -10.006693407397051, + "y": 0.000813138498472199 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 281.1403773002075, + "y": -195.15549315476605, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 92.39029670785891, + "y": -133.50179489117374, + "z": 1.0 + }, + { + "x": 92.39023562513496, + "y": -133.09996841179816, + "z": 1.0 + }, + { + "x": 92.39017454241102, + "y": -132.69814193242286, + "z": 1.0 + }, + { + "x": 92.39008291832509, + "y": -132.0954022133592, + "z": 1.0 + }, + { + "x": 92.39000656492016, + "y": -131.5931191141397, + "z": 1.0 + }, + { + "x": 92.38991494083423, + "y": -130.99037939507633, + "z": 1.0 + }, + { + "x": 92.38985385811029, + "y": -130.58855291570075, + "z": 1.0 + }, + { + "x": 92.38979277538634, + "y": -130.18672643632513, + "z": 1.0 + }, + { + "x": 92.38970115130041, + "y": -129.58398671726175, + "z": 1.0 + }, + { + "x": 92.38960952721449, + "y": -128.98124699819837, + "z": 1.0 + }, + { + "x": 92.38953317380957, + "y": -128.47896389897917, + "z": 1.0 + }, + { + "x": 92.38947209108561, + "y": -128.0771374196033, + "z": 1.0 + }, + { + "x": 92.38939573768069, + "y": -127.5748543203841, + "z": 1.0 + }, + { + "x": 92.38931938427575, + "y": -127.0725712211646, + "z": 1.0 + }, + { + "x": 92.38925830155179, + "y": -126.67074474178872, + "z": 1.0 + }, + { + "x": 92.38916667746587, + "y": -126.06800502272534, + "z": 1.0 + }, + { + "x": 92.38909032406094, + "y": -125.56572192350586, + "z": 1.0 + }, + { + "x": 92.389029241337, + "y": -125.16389544413025, + "z": 1.0 + }, + { + "x": 92.38895288793206, + "y": -124.66161234491105, + "z": 1.0 + }, + { + "x": 92.38887653452713, + "y": -124.15932924569157, + "z": 1.0 + }, + { + "x": 92.38881545180318, + "y": -123.7575027663157, + "z": 1.0 + }, + { + "x": 92.38875436907924, + "y": -123.35567628694037, + "z": 1.0 + }, + { + "x": 92.38866274499331, + "y": -122.7529365678767, + "z": 1.0 + }, + { + "x": 92.38857112090739, + "y": -122.15019684881332, + "z": 1.0 + }, + { + "x": 92.38847949682147, + "y": -121.54745712975023, + "z": 1.0 + }, + { + "x": 92.38841841409752, + "y": -121.14563065037433, + "z": 1.0 + }, + { + "x": 92.38832679001159, + "y": -120.54289093131095, + "z": 1.0 + }, + { + "x": 92.38825043660665, + "y": -120.04060783209147, + "z": 1.0 + }, + { + "x": 92.38821989524469, + "y": -119.83969459240367, + "z": 1.0 + }, + { + "x": 92.38815898411413, + "y": -119.43899692261479, + "z": 1.0 + }, + { + "x": 92.38806761741827, + "y": -118.83795041793134, + "z": 1.0 + }, + { + "x": 92.38799147850507, + "y": -118.33707833069512, + "z": 1.0 + }, + { + "x": 92.3879305673745, + "y": -117.93638066090614, + "z": 1.0 + }, + { + "x": 92.3878544284613, + "y": -117.43550857366992, + "z": 1.0 + }, + { + "x": 92.3877782895481, + "y": -116.93463648643372, + "z": 1.0 + }, + { + "x": 92.38768692285224, + "y": -116.33358998175025, + "z": 1.0 + }, + { + "x": 92.38762601172168, + "y": -115.93289231196128, + "z": 1.0 + }, + { + "x": 92.3875651005911, + "y": -115.53219464217231, + "z": 1.0 + }, + { + "x": 92.38747373389526, + "y": -114.93114813748885, + "z": 1.0 + }, + { + "x": 92.38739759498205, + "y": -114.43027605025264, + "z": 1.0 + }, + { + "x": 92.3873062282862, + "y": -113.82922954556918, + "z": 1.0 + }, + { + "x": 92.38724531715565, + "y": -113.42853187578021, + "z": 1.0 + }, + { + "x": 92.38718440602507, + "y": -113.02783420599124, + "z": 1.0 + }, + { + "x": 92.38710826711186, + "y": -112.52696211875502, + "z": 1.0 + }, + { + "x": 92.38704735598128, + "y": -112.12626444896576, + "z": 1.0 + }, + { + "x": 92.38698644485072, + "y": -111.72556677917707, + "z": 1.0 + }, + { + "x": 92.38689507815488, + "y": -111.12452027449362, + "z": 1.0 + }, + { + "x": 92.38681893924166, + "y": -110.6236481872574, + "z": 1.0 + }, + { + "x": 92.3867580281111, + "y": -110.22295051746842, + "z": 1.0 + }, + { + "x": 92.38668188919789, + "y": -109.72207843023222, + "z": 1.0 + }, + { + "x": 92.38660575028469, + "y": -109.221206342996, + "z": 1.0 + }, + { + "x": 92.38651438358883, + "y": -108.62015983831225, + "z": 1.0 + }, + { + "x": 92.38643824467563, + "y": -108.11928775107631, + "z": 1.0 + }, + { + "x": 92.38636210576243, + "y": -107.61841566384011, + "z": 1.0 + }, + { + "x": 92.38630119463186, + "y": -107.21771799405114, + "z": 1.0 + }, + { + "x": 92.38620982793601, + "y": -106.61667148936768, + "z": 1.0 + }, + { + "x": 92.38611846124016, + "y": -106.01562498468421, + "z": 1.0 + }, + { + "x": 92.3860575501096, + "y": -105.61492731489524, + "z": 1.0 + }, + { + "x": 92.38596618341374, + "y": -105.01388081021179, + "z": 1.0 + }, + { + "x": 92.38589004450054, + "y": -104.51300872297557, + "z": 1.0 + }, + { + "x": 92.38582913336998, + "y": -104.1123110531866, + "z": 1.0 + }, + { + "x": 92.38575299445677, + "y": -103.61143896595038, + "z": 1.0 + }, + { + "x": 92.3856920833262, + "y": -103.21074129616142, + "z": 1.0 + }, + { + "x": 92.38563117219563, + "y": -102.81004362637215, + "z": 1.0 + }, + { + "x": 92.38557026106506, + "y": -102.40934595658346, + "z": 1.0 + }, + { + "x": 92.38547889436921, + "y": -101.80829945190001, + "z": 1.0 + }, + { + "x": 92.38538752767336, + "y": -101.20725294721655, + "z": 1.0 + }, + { + "x": 92.3853266165428, + "y": -100.80655527742758, + "z": 1.0 + }, + { + "x": 92.38525047762958, + "y": -100.30568319019136, + "z": 1.0 + }, + { + "x": 92.38517433871638, + "y": -99.80481110295514, + "z": 1.0 + }, + { + "x": 92.38511342758582, + "y": -99.40411343316617, + "z": 1.0 + }, + { + "x": 92.3850372886726, + "y": -98.90324134592996, + "z": 1.0 + }, + { + "x": 92.38494592197677, + "y": -98.3021948412465, + "z": 1.0 + }, + { + "x": 92.38485455528091, + "y": -97.70114833656304, + "z": 1.0 + }, + { + "x": 92.38477841636771, + "y": -97.20027624932682, + "z": 1.0 + }, + { + "x": 92.3847022774545, + "y": -96.6994041620906, + "z": 1.0 + }, + { + "x": 92.38464136632393, + "y": -96.29870649230163, + "z": 1.0 + }, + { + "x": 92.38454999962808, + "y": -95.6976599876179, + "z": 1.0 + }, + { + "x": 92.38447386071488, + "y": -95.19678790038196, + "z": 1.0 + }, + { + "x": 92.38438249401904, + "y": -94.59574139569851, + "z": 1.0 + }, + { + "x": 92.38430635510582, + "y": -94.0948693084623, + "z": 1.0 + }, + { + "x": 92.38421498840998, + "y": -93.49382280377883, + "z": 1.0 + }, + { + "x": 92.38413884949676, + "y": -92.99295071654261, + "z": 1.0 + }, + { + "x": 92.38406271058355, + "y": -92.4920786293064, + "z": 1.0 + }, + { + "x": 92.38400179945297, + "y": -92.09138095951742, + "z": 1.0 + }, + { + "x": 92.38391043275713, + "y": -91.49033445483397, + "z": 1.0 + }, + { + "x": 92.38383429384392, + "y": -90.98946236759747, + "z": 1.0 + }, + { + "x": 92.38374292714808, + "y": -90.38841586291429, + "z": 1.0 + }, + { + "x": 92.38366678823488, + "y": -89.88754377567808, + "z": 1.0 + }, + { + "x": 92.38357542153902, + "y": -89.28649727099462, + "z": 1.0 + }, + { + "x": 92.38349928262582, + "y": -88.7856251837584, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 20, + "heading": [ + 1.5709483394834882, + 1.570948339483483, + 1.5709483394834831, + 1.57094833948349, + 1.5709483394834927, + 1.5709483394834927, + 1.57094833948349, + 1.570948339483483, + 1.57094833948349, + 1.5709483394834947, + 1.5709483394834798, + 1.5709483394834869, + 1.5709483394834869, + 1.570948339483476, + 1.5709483394835027, + 1.5709483394835042, + 1.5709483394834798, + 1.5709483394834711, + 1.5709483394834869, + 1.57094833948349, + 1.5709483394834869, + 1.570948339483483, + 1.57094833948349, + 1.5709483394834947, + 1.570948339483483, + 1.57094833948349, + 1.5709483394835042, + 1.5709483394834927, + 1.570948339483478, + 1.5709483394834656, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834904, + 1.5709483394834904, + 1.5709483394834747, + 1.5709483394834876, + 1.5709483394834889, + 1.5709483394834922, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834876, + 1.5709483394834747, + 1.5709483394834922, + 1.5709483394835062, + 1.570948339483506, + 1.5709483394834922, + 1.5709483394834747, + 1.5709483394834876, + 1.5709483394834904, + 1.5709483394834904, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834876, + 1.5709483394834747, + 1.5709483394834904, + 1.5709483394834889, + 1.5709483394834864, + 1.5709483394834889, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834747, + 1.5709483394834904, + 1.5709483394834904, + 1.5709483394834922, + 1.57094833948351, + 1.5709483394834889, + 1.5709483394834864, + 1.5709483394834889, + 1.5709483394834904, + 1.5709483394834889, + 1.5709483394834747, + 1.5709483394834904, + 1.5709483394834876, + 1.5709483394834864, + 1.5709483394834876, + 1.5709483394834889, + 1.5709483394834904, + 1.5709483394834887, + 1.5709483394834876, + 1.5709483394834747, + 1.5709483394834876, + 1.5709483394834876, + 1.5709483394834876, + 1.5709483394835029, + 1.5709483394835062, + 1.5709483394834889, + 1.5709483394834876, + 1.5709483394834876, + 1.5709483394834747, + 1.5709483394834876, + 1.5709483394834876 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.0006108272394556025, + "y": 4.018264793755861 + }, + { + "x": -0.001221654478911205, + "y": 8.03652958750888 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.0016797749086094882, + "y": 11.05022818283146 + }, + { + "x": -0.0016797749086094882, + "y": 11.050228182828619 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389653 + }, + { + "x": -0.001221654478911205, + "y": 8.036529587512007 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389938 + }, + { + "x": -0.001832481718508916, + "y": 12.054794381267584 + }, + { + "x": -0.0016797749084673796, + "y": 11.050228182825776 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785950688 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785950688 + }, + { + "x": -0.001527068098567952, + "y": 10.045661984387095 + }, + { + "x": -0.0013743612889527412, + "y": 9.041095785953814 + }, + { + "x": -0.001527068098852169, + "y": 10.045661984392495 + }, + { + "x": -0.0016797749084673796, + "y": 11.050228182828619 + }, + { + "x": -0.0013743612886685241, + "y": 9.041095785950972 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578594813 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984386811 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595353 + }, + { + "x": -0.001221654478911205, + "y": 8.036529587512007 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389938 + }, + { + "x": -0.001832481718508916, + "y": 12.054794381270426 + }, + { + "x": -0.0018324817183668074, + "y": 12.054794381264742 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389938 + }, + { + "x": -0.001527068098852169, + "y": 10.04566198439278 + }, + { + "x": -0.0016797749086094882, + "y": 11.050228182828619 + }, + { + "x": -0.0010689476690117772, + "y": 7.031963389072757 + }, + { + "x": -0.0009145249252640042, + "y": 6.016109094766762 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744723357 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570252001 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570252001 + }, + { + "x": -0.001522778264018143, + "y": 10.01744174472421 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001218222611356623, + "y": 8.013953395779367 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.001522778264018143, + "y": 10.017441744724351 + }, + { + "x": -0.001218222611356623, + "y": 8.013953395779367 + }, + { + "x": -0.0013705004379005459, + "y": 9.01569757025186 + }, + { + "x": -0.0013705004379005459, + "y": 9.015697570254844 + }, + { + "x": -0.001218222611356623, + "y": 8.01395339577951 + }, + { + "x": -0.001522778264018143, + "y": 10.017441744721367 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570252001 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0015227782641602516, + "y": 10.01744174472421 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919199685 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + }, + { + "x": -0.001522778264018143, + "y": 10.017441744721367 + }, + { + "x": -0.0013705004377584373, + "y": 9.015697570251717 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669335 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0015227782641602516, + "y": 10.01744174472421 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0013705004376163288, + "y": 9.01569757025186 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.001218222611356623, + "y": 8.013953395782352 + }, + { + "x": -0.0012182226114987316, + "y": 8.01395339577951 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744721367 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0013705004376163288, + "y": 9.01569757025186 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0018273339169638803, + "y": 12.020930093669193 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.0013705004377584373, + "y": 9.01569757025186 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744727051 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.0016750560904199574, + "y": 11.019185919193859 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + }, + { + "x": -0.0015227782643023602, + "y": 10.01744174472421 + }, + { + "x": -0.0013705004379005459, + "y": 9.01569757025186 + }, + { + "x": -0.0015227782641602516, + "y": 10.017441744724351 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919199543 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + }, + { + "x": -0.0016750560904199574, + "y": 11.019185919193859 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196701 + }, + { + "x": -0.001675056090562066, + "y": 11.019185919196843 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 92.38349928262582, + "y": -88.7856251837584, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 88.40609795687595, + "y": -237.44900651847365, + "z": 1.0 + }, + { + "x": 88.40617401207619, + "y": -237.94932790846892, + "z": 1.0 + }, + { + "x": 88.40625006727643, + "y": -238.4496492984642, + "z": 1.0 + }, + { + "x": 88.40631091143662, + "y": -238.84990641046042, + "z": 1.0 + }, + { + "x": 88.40638696663686, + "y": -239.3502278004557, + "z": 1.0 + }, + { + "x": 88.40647823287715, + "y": -239.95061346845003, + "z": 1.0 + }, + { + "x": 88.40656949911744, + "y": -240.55099913644437, + "z": 1.0 + }, + { + "x": 88.40663034327763, + "y": -240.9512562484406, + "z": 1.0 + }, + { + "x": 88.40669118743781, + "y": -241.35151336043683, + "z": 1.0 + }, + { + "x": 88.4067824536781, + "y": -241.9518990284309, + "z": 1.0 + }, + { + "x": 88.40685850887834, + "y": -242.45222041842644, + "z": 1.0 + }, + { + "x": 88.40691935303853, + "y": -242.85247753042268, + "z": 1.0 + }, + { + "x": 88.40698019719873, + "y": -243.2527346424186, + "z": 1.0 + }, + { + "x": 88.40705625239896, + "y": -243.75305603241418, + "z": 1.0 + }, + { + "x": 88.40714751863925, + "y": -244.3534417004085, + "z": 1.0 + }, + { + "x": 88.40723878487954, + "y": -244.95382736840284, + "z": 1.0 + }, + { + "x": 88.40729962903973, + "y": -245.35408448039905, + "z": 1.0 + }, + { + "x": 88.40737568423997, + "y": -245.85440587039434, + "z": 1.0 + }, + { + "x": 88.4074517394402, + "y": -246.3547272603896, + "z": 1.0 + }, + { + "x": 88.4075125836004, + "y": -246.75498437238585, + "z": 1.0 + }, + { + "x": 88.40758863880063, + "y": -247.2553057623811, + "z": 1.0 + }, + { + "x": 88.40766469400087, + "y": -247.7556271523764, + "z": 1.0 + }, + { + "x": 88.40775596024116, + "y": -248.35601282037072, + "z": 1.0 + }, + { + "x": 88.4078320154414, + "y": -248.85633421036601, + "z": 1.0 + }, + { + "x": 88.40789285960159, + "y": -249.25659132236223, + "z": 1.0 + }, + { + "x": 88.40795370376178, + "y": -249.65684843435847, + "z": 1.0 + }, + { + "x": 88.40804497000208, + "y": -250.2572341023525, + "z": 1.0 + }, + { + "x": 88.40813623624236, + "y": -250.85761977034713, + "z": 1.0 + }, + { + "x": 88.40822750248265, + "y": -251.45800543834147, + "z": 1.0 + }, + { + "x": 88.40831876872294, + "y": -252.0583911063358, + "z": 1.0 + }, + { + "x": 88.40837961288312, + "y": -252.45864821833175, + "z": 1.0 + }, + { + "x": 88.40845566808336, + "y": -252.958969608327, + "z": 1.0 + }, + { + "x": 88.4085317232836, + "y": -253.4592909983223, + "z": 1.0 + }, + { + "x": 88.40862298952389, + "y": -254.05967666631662, + "z": 1.0 + }, + { + "x": 88.40868383368408, + "y": -254.45993377831314, + "z": 1.0 + }, + { + "x": 88.40874467784427, + "y": -254.86019089030938, + "z": 1.0 + }, + { + "x": 88.40880552200446, + "y": -255.2604480023056, + "z": 1.0 + }, + { + "x": 88.40889678824475, + "y": -255.8608336702999, + "z": 1.0 + }, + { + "x": 88.40897284344499, + "y": -256.36115506029523, + "z": 1.0 + }, + { + "x": 88.40904889864522, + "y": -256.86147645029047, + "z": 1.0 + }, + { + "x": 88.40912495384546, + "y": -257.36179784028576, + "z": 1.0 + }, + { + "x": 88.40921622008575, + "y": -257.9621835082798, + "z": 1.0 + }, + { + "x": 88.40929227528599, + "y": -258.4625048982751, + "z": 1.0 + }, + { + "x": 88.40936833048623, + "y": -258.96282628827066, + "z": 1.0 + }, + { + "x": 88.4094595967265, + "y": -259.563211956265, + "z": 1.0 + }, + { + "x": 88.40953565192675, + "y": -260.0635333462603, + "z": 1.0 + }, + { + "x": 88.40959649608695, + "y": -260.4637904582562, + "z": 1.0 + }, + { + "x": 88.40967255128719, + "y": -260.9641118482515, + "z": 1.0 + }, + { + "x": 88.40976381752746, + "y": -261.5644975162461, + "z": 1.0 + }, + { + "x": 88.40982466168767, + "y": -261.9647546282421, + "z": 1.0 + }, + { + "x": 88.40990071688789, + "y": -262.4650760182376, + "z": 1.0 + }, + { + "x": 88.4099919831282, + "y": -263.0654616862317, + "z": 1.0 + }, + { + "x": 88.41005282728837, + "y": -263.4657187982282, + "z": 1.0 + }, + { + "x": 88.41011367144857, + "y": -263.8659759102241, + "z": 1.0 + }, + { + "x": 88.41018972664881, + "y": -264.3662973002197, + "z": 1.0 + }, + { + "x": 88.410250570809, + "y": -264.7665544122159, + "z": 1.0 + }, + { + "x": 88.41031141496919, + "y": -265.1668115242121, + "z": 1.0 + }, + { + "x": 88.41037225912937, + "y": -265.5670686362084, + "z": 1.0 + }, + { + "x": 88.41044831432961, + "y": -266.0673900262036, + "z": 1.0 + }, + { + "x": 88.4105091584898, + "y": -266.4676471381999, + "z": 1.0 + }, + { + "x": 88.41060042473009, + "y": -267.0680328061942, + "z": 1.0 + }, + { + "x": 88.41067647993033, + "y": -267.5683541961895, + "z": 1.0 + }, + { + "x": 88.41075253513057, + "y": -268.0686755861847, + "z": 1.0 + }, + { + "x": 88.41081337929077, + "y": -268.4689326981807, + "z": 1.0 + }, + { + "x": 88.41090464553105, + "y": -269.0693183661753, + "z": 1.0 + }, + { + "x": 88.41096548969124, + "y": -269.4695754781715, + "z": 1.0 + }, + { + "x": 88.41102633385142, + "y": -269.86983259016773, + "z": 1.0 + }, + { + "x": 88.41111760009171, + "y": -270.4702182581621, + "z": 1.0 + }, + { + "x": 88.41119365529195, + "y": -270.97053964815734, + "z": 1.0 + }, + { + "x": 88.41126971049219, + "y": -271.47086103815263, + "z": 1.0 + }, + { + "x": 88.4113305546524, + "y": -271.87111815014856, + "z": 1.0 + }, + { + "x": 88.41140660985262, + "y": -272.37143954014414, + "z": 1.0 + }, + { + "x": 88.41148266505286, + "y": -272.8717609301394, + "z": 1.0 + }, + { + "x": 88.4115587202531, + "y": -273.37208232013495, + "z": 1.0 + }, + { + "x": 88.41163477545334, + "y": -273.8724037101299, + "z": 1.0 + }, + { + "x": 88.41171083065358, + "y": -274.37272510012514, + "z": 1.0 + }, + { + "x": 88.41178688585381, + "y": -274.8730464901204, + "z": 1.0 + }, + { + "x": 88.411847730014, + "y": -275.27330360211687, + "z": 1.0 + }, + { + "x": 88.4119085741742, + "y": -275.6735607141125, + "z": 1.0 + }, + { + "x": 88.41199984041448, + "y": -276.27394638210706, + "z": 1.0 + }, + { + "x": 88.41207589561472, + "y": -276.7742677721023, + "z": 1.0 + }, + { + "x": 88.41215195081497, + "y": -277.27458916209724, + "z": 1.0 + }, + { + "x": 88.41221279497516, + "y": -277.67484627409374, + "z": 1.0 + }, + { + "x": 88.41227363913535, + "y": -278.07510338608967, + "z": 1.0 + }, + { + "x": 88.41236490537563, + "y": -278.6754890540845, + "z": 1.0 + }, + { + "x": 88.41244096057588, + "y": -279.1758104440792, + "z": 1.0 + }, + { + "x": 88.41251701577612, + "y": -279.67613183407445, + "z": 1.0 + }, + { + "x": 88.41260828201641, + "y": -280.2765175020687, + "z": 1.0 + }, + { + "x": 88.4126691261766, + "y": -280.6767746140652, + "z": 1.0 + }, + { + "x": 88.41276039241689, + "y": -281.2771602820592, + "z": 1.0 + }, + { + "x": 88.41282123657707, + "y": -281.6774173940557, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 21, + "heading": [ + -1.5706443141063056, + -1.5706443141063047, + -1.5706443141063047, + -1.570644314106308, + -1.570644314106308, + -1.5706443141063022, + -1.5706443141063, + -1.5706443141063047, + -1.5706443141063118, + -1.5706443141063047, + -1.5706443141063022, + -1.570644314106308, + -1.570644314106294, + -1.570644314106308, + -1.570644314106315, + -1.5706443141063, + -1.5706443141063047, + -1.570644314106308, + -1.5706443141063047, + -1.570644314106308, + -1.570644314106308, + -1.5706443141063047, + -1.5706443141063022, + -1.5706443141063022, + -1.570644314106308, + -1.5706443141063118, + -1.5706443141062905, + -1.5706443141063, + -1.570644314106312, + -1.5706443141063, + -1.5706443141063047, + -1.5706443141063078, + -1.5706443141063047, + -1.5706443141063022, + -1.570644314106305, + -1.570644314106312, + -1.5706443141063118, + -1.5706443141063047, + -1.5706443141063022, + -1.5706443141063047, + -1.5706443141063047, + -1.5706443141063022, + -1.5706443141063022, + -1.570644314106305, + -1.570644314106315, + -1.570644314106315, + -1.570644314106292, + -1.570644314106292, + -1.570644314106315, + -1.570644314106305, + -1.570644314106308, + -1.5706443141063022, + -1.570644314106305, + -1.5706443141063118, + -1.5706443141062922, + -1.570644314106308, + -1.5706443141063118, + -1.570644314106312, + -1.570644314106308, + -1.570644314106308, + -1.570644314106305, + -1.5706443141063022, + -1.5706443141063047, + -1.570644314106292, + -1.570644314106305, + -1.570644314106319, + -1.5706443141063118, + -1.570644314106305, + -1.5706443141063022, + -1.5706443141063047, + -1.570644314106292, + -1.570644314106308, + -1.570644314106319, + -1.570644314106305, + -1.5706443141063047, + -1.5706443141063047, + -1.5706443141063047, + -1.570644314106308, + -1.570644314106294, + -1.5706443141063047, + -1.570644314106315, + -1.5706443141062905, + -1.5706443141062922, + -1.5706443141063118, + -1.570644314106319, + -1.5706443141063022, + -1.5706443141062905, + -1.5706443141063022, + -1.570644314106305, + -1.5706443141063047, + -1.5706443141063047 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.0007605520023901136, + "y": -5.003213899952641 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905566 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.001825324805793116, + "y": -12.007713359886623 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905566 + }, + { + "x": 0.0012168832037673383, + "y": -8.00514223992451 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799903008 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.0013689936042737827, + "y": -9.00578501991788 + }, + { + "x": 0.0012168832039094468, + "y": -8.005142239921668 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.001673214405144563, + "y": -11.007070579898937 + }, + { + "x": 0.001825324805793116, + "y": -12.007713359886623 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905566 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905566 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905566 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0012168832037673383, + "y": -8.00514223992451 + }, + { + "x": 0.0015211040049223357, + "y": -10.006427799902724 + }, + { + "x": 0.001825324805793116, + "y": -12.007713359886623 + }, + { + "x": 0.0018253248056510074, + "y": -12.007713359889749 + }, + { + "x": 0.001825324805793116, + "y": -12.007713359886623 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799902724 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019912196 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905566 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799908408 + }, + { + "x": 0.0012168832037673383, + "y": -8.005142239927636 + }, + { + "x": 0.0012168832037673383, + "y": -8.00514223992451 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905282 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896379 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905566 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905282 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579893252 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579893252 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799908693 + }, + { + "x": 0.001673214405144563, + "y": -11.007070579898937 + }, + { + "x": 0.001673214405144563, + "y": -11.007070579896094 + }, + { + "x": 0.0013689936044158912, + "y": -9.005785019912196 + }, + { + "x": 0.0013689936044158912, + "y": -9.005785019912196 + }, + { + "x": 0.001673214405144563, + "y": -11.007070579898937 + }, + { + "x": 0.0015211040047802271, + "y": -10.00642779990585 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.0015211040047802271, + "y": -10.00642779990585 + }, + { + "x": 0.0012168832037673383, + "y": -8.005142239924226 + }, + { + "x": 0.0013689936044158912, + "y": -9.005785019915038 + }, + { + "x": 0.0013689936042737827, + "y": -9.00578501991788 + }, + { + "x": 0.0012168832037673383, + "y": -8.005142239924226 + }, + { + "x": 0.0012168832037673383, + "y": -8.005142239924794 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0015211040047802271, + "y": -10.00642779990585 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905282 + }, + { + "x": 0.0013689936044158912, + "y": -9.005785019912196 + }, + { + "x": 0.0015211040047802271, + "y": -10.00642779990585 + }, + { + "x": 0.0015211040046381186, + "y": -10.006427799908124 + }, + { + "x": 0.0012168832037673383, + "y": -8.005142239924226 + }, + { + "x": 0.0015211040047802271, + "y": -10.00642779990585 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579896094 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905282 + }, + { + "x": 0.0013689936044158912, + "y": -9.005785019912196 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019915038 + }, + { + "x": 0.0015211040046381186, + "y": -10.006427799908124 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799908124 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799905282 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799901871 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799904714 + }, + { + "x": 0.0013689936042737827, + "y": -9.005785019917312 + }, + { + "x": 0.0012168832039094468, + "y": -8.005142239921383 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799901871 + }, + { + "x": 0.001673214405144563, + "y": -11.0070705798978 + }, + { + "x": 0.0015211040049223357, + "y": -10.006427799901871 + }, + { + "x": 0.0013689936044158912, + "y": -9.00578501991447 + }, + { + "x": 0.0012168832037673383, + "y": -8.005142239924226 + }, + { + "x": 0.0015211040046381186, + "y": -10.006427799907556 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579895526 + }, + { + "x": 0.0015211040049223357, + "y": -10.006427799899598 + }, + { + "x": 0.0016732144052866715, + "y": -11.007070579894958 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799907556 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799904714 + }, + { + "x": 0.0015211040047802271, + "y": -10.006427799904714 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 88.41282123657707, + "y": -281.6774173940557, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 92.38011802850588, + "y": -66.54238927212418, + "z": 1.0 + }, + { + "x": 92.38004167510093, + "y": -66.0401061729047, + "z": 1.0 + }, + { + "x": 92.37996532169599, + "y": -65.53782307368493, + "z": 1.0 + }, + { + "x": 92.37988896829106, + "y": -65.03553997446542, + "z": 1.0 + }, + { + "x": 92.37982788556711, + "y": -64.63371349509012, + "z": 1.0 + }, + { + "x": 92.37975153216217, + "y": -64.13143039587035, + "z": 1.0 + }, + { + "x": 92.37967517875724, + "y": -63.629147296650856, + "z": 1.0 + }, + { + "x": 92.3796140960333, + "y": -63.22732081727527, + "z": 1.0 + }, + { + "x": 92.37953774262836, + "y": -62.72503771805577, + "z": 1.0 + }, + { + "x": 92.37946138922342, + "y": -62.22275461883628, + "z": 1.0 + }, + { + "x": 92.3793697651375, + "y": -61.62001489977288, + "z": 1.0 + }, + { + "x": 92.37930868241357, + "y": -61.21818842039758, + "z": 1.0 + }, + { + "x": 92.37923232900862, + "y": -60.7159053211778, + "z": 1.0 + }, + { + "x": 92.37917124628467, + "y": -60.314078841802214, + "z": 1.0 + }, + { + "x": 92.37911016356072, + "y": -59.912252362426614, + "z": 1.0 + }, + { + "x": 92.3790185394748, + "y": -59.30951264336322, + "z": 1.0 + }, + { + "x": 92.37892691538889, + "y": -58.70677292430012, + "z": 1.0 + }, + { + "x": 92.37883529130296, + "y": -58.10403320523645, + "z": 1.0 + }, + { + "x": 92.37877420857902, + "y": -57.70220672586085, + "z": 1.0 + }, + { + "x": 92.37869785517408, + "y": -57.19992362664164, + "z": 1.0 + }, + { + "x": 92.37862150176916, + "y": -56.697640527421896, + "z": 1.0 + }, + { + "x": 92.37854514836424, + "y": -56.19535742820269, + "z": 1.0 + }, + { + "x": 92.3784687949593, + "y": -55.69307432898321, + "z": 1.0 + }, + { + "x": 92.37839244155437, + "y": -55.19079122976345, + "z": 1.0 + }, + { + "x": 92.37833135883041, + "y": -54.78896475038785, + "z": 1.0 + }, + { + "x": 92.37825500542549, + "y": -54.28668165116865, + "z": 1.0 + }, + { + "x": 92.37819392270154, + "y": -53.884855171793056, + "z": 1.0 + }, + { + "x": 92.37813283997758, + "y": -53.48302869241718, + "z": 1.0 + }, + { + "x": 92.37805648657265, + "y": -52.980745593197696, + "z": 1.0 + }, + { + "x": 92.37798013316771, + "y": -52.4784624939782, + "z": 1.0 + }, + { + "x": 92.37791905044377, + "y": -52.07663601460261, + "z": 1.0 + }, + { + "x": 92.37784269703883, + "y": -51.57435291538313, + "z": 1.0 + }, + { + "x": 92.37778161431488, + "y": -51.17252643600753, + "z": 1.0 + }, + { + "x": 92.37770526090996, + "y": -50.670243336788324, + "z": 1.0 + }, + { + "x": 92.37761363682404, + "y": -50.06750361772494, + "z": 1.0 + }, + { + "x": 92.37753728341909, + "y": -49.56522051850489, + "z": 1.0 + }, + { + "x": 92.37746093001417, + "y": -49.06293741928596, + "z": 1.0 + }, + { + "x": 92.37736930592824, + "y": -48.46019770022228, + "z": 1.0 + }, + { + "x": 92.37729295252328, + "y": -47.95791460100283, + "z": 1.0 + }, + { + "x": 92.37723186979933, + "y": -47.55608812162724, + "z": 1.0 + }, + { + "x": 92.3771555163944, + "y": -47.053805022407744, + "z": 1.0 + }, + { + "x": 92.37709443367045, + "y": -46.651978543032435, + "z": 1.0 + }, + { + "x": 92.37701808026551, + "y": -46.14969544381266, + "z": 1.0 + }, + { + "x": 92.37701808026551, + "y": -46.14969544381266, + "z": 1.0 + }, + { + "x": 92.37695708299036, + "y": -45.74843108063273, + "z": 1.0 + }, + { + "x": 92.37686558707763, + "y": -45.146534535862855, + "z": 1.0 + }, + { + "x": 92.37680458980248, + "y": -44.745270172682936, + "z": 1.0 + }, + { + "x": 92.37672834320854, + "y": -44.243689718708055, + "z": 1.0 + }, + { + "x": 92.3766520966146, + "y": -43.74210926473344, + "z": 1.0 + }, + { + "x": 92.37657585002066, + "y": -43.24052881075826, + "z": 1.0 + }, + { + "x": 92.37649960342672, + "y": -42.738948356783666, + "z": 1.0 + }, + { + "x": 92.376408107514, + "y": -42.137051812013794, + "z": 1.0 + }, + { + "x": 92.37631661160125, + "y": -41.53515526724364, + "z": 1.0 + }, + { + "x": 92.37624036500732, + "y": -41.03357481326876, + "z": 1.0 + }, + { + "x": 92.37616411841337, + "y": -40.53199435929386, + "z": 1.0 + }, + { + "x": 92.37607262250064, + "y": -39.93009781452399, + "z": 1.0 + }, + { + "x": 92.3760116252255, + "y": -39.52883345134437, + "z": 1.0 + }, + { + "x": 92.37592012931276, + "y": -38.92693690657421, + "z": 1.0 + }, + { + "x": 92.37584388271883, + "y": -38.42535645259932, + "z": 1.0 + }, + { + "x": 92.37576763612489, + "y": -37.923775998624706, + "z": 1.0 + }, + { + "x": 92.37569138953094, + "y": -37.42219554464954, + "z": 1.0 + }, + { + "x": 92.37561514293701, + "y": -36.92061509067493, + "z": 1.0 + }, + { + "x": 92.37552364702428, + "y": -36.31871854590506, + "z": 1.0 + }, + { + "x": 92.37543215111154, + "y": -35.716822001134915, + "z": 1.0 + }, + { + "x": 92.37534065519881, + "y": -35.11492545636504, + "z": 1.0 + }, + { + "x": 92.37526440860488, + "y": -34.61334500239043, + "z": 1.0 + }, + { + "x": 92.37517291269215, + "y": -34.01144845762056, + "z": 1.0 + }, + { + "x": 92.375111915417, + "y": -33.61018409444037, + "z": 1.0 + }, + { + "x": 92.37502041950427, + "y": -33.00828754967078, + "z": 1.0 + }, + { + "x": 92.37492892359154, + "y": -32.40639100490091, + "z": 1.0 + }, + { + "x": 92.37486792631638, + "y": -32.00512664172072, + "z": 1.0 + }, + { + "x": 92.37477643040366, + "y": -31.403230096951127, + "z": 1.0 + }, + { + "x": 92.37468493449093, + "y": -30.801333552181255, + "z": 1.0 + }, + { + "x": 92.37462393721577, + "y": -30.400069189001343, + "z": 1.0 + }, + { + "x": 92.37453244130305, + "y": -29.798172644231478, + "z": 1.0 + }, + { + "x": 92.3744561947091, + "y": -29.296592190256302, + "z": 1.0 + }, + { + "x": 92.37436469879637, + "y": -28.694695645486433, + "z": 1.0 + }, + { + "x": 92.37428845220244, + "y": -28.193115191511822, + "z": 1.0 + }, + { + "x": 92.37421220560849, + "y": -27.691534737536653, + "z": 1.0 + }, + { + "x": 92.37412070969576, + "y": -27.089638192766785, + "z": 1.0 + }, + { + "x": 92.3740597124206, + "y": -26.688373829586872, + "z": 1.0 + }, + { + "x": 92.37398346582667, + "y": -26.18679337561198, + "z": 1.0 + }, + { + "x": 92.37389196991394, + "y": -25.584896830842396, + "z": 1.0 + }, + { + "x": 92.37383097263879, + "y": -25.1836324676622, + "z": 1.0 + }, + { + "x": 92.37376997536363, + "y": -24.782368104482284, + "z": 1.0 + }, + { + "x": 92.37370897808847, + "y": -24.381103741302372, + "z": 1.0 + }, + { + "x": 92.37364798081333, + "y": -23.97983937812274, + "z": 1.0 + }, + { + "x": 92.37355648490059, + "y": -23.377942833352595, + "z": 1.0 + }, + { + "x": 92.37349548762545, + "y": -22.976678470172963, + "z": 1.0 + }, + { + "x": 92.3734192410315, + "y": -22.475098016197787, + "z": 1.0 + }, + { + "x": 92.37334299443756, + "y": -21.9735175622229, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 22, + "heading": [ + 1.5709483394834889, + 1.5709483394835184, + 1.5709483394835042, + 1.57094833948349, + 1.5709483394834869, + 1.5709483394834869, + 1.57094833948349, + 1.5709483394834869, + 1.5709483394834869, + 1.57094833948349, + 1.5709483394834927, + 1.570948339483476, + 1.5709483394834869, + 1.5709483394835027, + 1.570948339483483, + 1.57094833948349, + 1.570948339483483, + 1.570948339483483, + 1.57094833948349, + 1.5709483394834869, + 1.5709483394834758, + 1.5709483394834618, + 1.570948339483476, + 1.57094833948349, + 1.5709483394835027, + 1.5709483394834869, + 1.5709483394834711, + 1.5709483394835007, + 1.5709483394835027, + 1.57094833948349, + 1.5709483394834869, + 1.5709483394834869, + 1.5709483394834869, + 1.5709483394834711, + 1.5709483394834798, + 1.5709483394835053, + 1.57094833948349, + 1.5709483394834798, + 1.5709483394835182, + 1.5709483394835184, + 1.5709483394834869, + 1.5709483394834869, + 1.5709483394834869, + 1.57094833948349, + 1.5709483394834967, + 1.5709483394834896, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834753, + 1.5709483394834896, + 1.5709483394834896, + 1.5709483394834804, + 1.5709483394834967, + 1.5709483394834933, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394834753, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394834753, + 1.5709483394834896, + 1.5709483394834896, + 1.5709483394834804, + 1.5709483394834967, + 1.5709483394834967, + 1.5709483394834804, + 1.5709483394834804, + 1.5709483394834896, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834896, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834896, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394834933, + 1.5709483394834804, + 1.5709483394834896, + 1.5709483394834933, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834804, + 1.5709483394834896, + 1.5709483394834967, + 1.5709483394834967, + 1.570948339483479, + 1.5709483394834896, + 1.5709483394834896, + 1.570948339483485, + 1.5709483394834896 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.0007635340494971388, + "y": 5.022830992194827 + }, + { + "x": -0.001527068098852169, + "y": 10.045661984392495 + }, + { + "x": -0.0015270680987100604, + "y": 10.04566198439278 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578594813 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785950688 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984392638 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595083 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595083 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389938 + }, + { + "x": -0.0016797749086094882, + "y": 11.050228182828903 + }, + { + "x": -0.001527068098567952, + "y": 10.045661984386953 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595083 + }, + { + "x": -0.0013743612889527412, + "y": 9.041095785953672 + }, + { + "x": -0.001221654478911205, + "y": 8.036529587511865 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389938 + }, + { + "x": -0.0018324817183668074, + "y": 12.054794381264955 + }, + { + "x": -0.0018324817183668074, + "y": 12.054794381267726 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984392709 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785948059 + }, + { + "x": -0.001527068098567952, + "y": 10.045661984389511 + }, + { + "x": -0.0015270680984258433, + "y": 10.045661984389511 + }, + { + "x": -0.001527068098567952, + "y": 10.045661984386882 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984392424 + }, + { + "x": -0.0013743612889527412, + "y": 9.041095785953601 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785947988 + }, + { + "x": -0.0013743612886685241, + "y": 9.041095785947917 + }, + { + "x": -0.0012216544790533135, + "y": 8.036529587514707 + }, + { + "x": -0.0013743612889527412, + "y": 9.041095785953601 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389795 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595083 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785950688 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595083 + }, + { + "x": -0.0013743612886685241, + "y": 9.041095785948059 + }, + { + "x": -0.0016797749084673796, + "y": 11.050228182825919 + }, + { + "x": -0.0016797749087515967, + "y": 11.050228182834374 + }, + { + "x": -0.0015270680987100604, + "y": 10.045661984389795 + }, + { + "x": -0.0016797749084673796, + "y": 11.05022818282606 + }, + { + "x": -0.0016797749088937053, + "y": 11.050228182831319 + }, + { + "x": -0.0013743612890948498, + "y": 9.041095785950404 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595083 + }, + { + "x": -0.0013743612888106327, + "y": 9.041095785948059 + }, + { + "x": -0.0013743612888106327, + "y": 9.04109578595083 + }, + { + "x": -0.0007635340493550302, + "y": 5.02283099219774 + }, + { + "x": -0.000609972751561827, + "y": 4.012643631799335 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079498054 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497912 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548007 + }, + { + "x": -0.0015249318786914046, + "y": 10.031609079494928 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497912 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0016774250666173884, + "y": 11.03476998744469 + }, + { + "x": -0.0018299182546854809, + "y": 12.03793089540028 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987450375 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987447675 + }, + { + "x": -0.0015249318786914046, + "y": 10.031609079494928 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987450517 + }, + { + "x": -0.0015249318786914046, + "y": 10.03160907949507 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0016774250666173884, + "y": 11.034769987444832 + }, + { + "x": -0.0018299182546854809, + "y": 12.037930895400137 + }, + { + "x": -0.0018299182546854809, + "y": 12.037930895400137 + }, + { + "x": -0.0016774250666173884, + "y": 11.034769987444832 + }, + { + "x": -0.0016774250666173884, + "y": 11.034769987444832 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079500612 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0018299182545433723, + "y": 12.037930895394595 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079500612 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949784 + }, + { + "x": -0.0018299182545433723, + "y": 12.037930895394666 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949784 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.001677425066759497, + "y": 11.03476998745041 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987450446 + }, + { + "x": -0.0016774250666173884, + "y": 11.034769987444797 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497805 + }, + { + "x": -0.001677425066759497, + "y": 11.034769987450375 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497805 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548042 + }, + { + "x": -0.0016774250666173884, + "y": 11.034769987444761 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079497805 + }, + { + "x": -0.001219945503123654, + "y": 8.025287263601122 + }, + { + "x": -0.001219945503123654, + "y": 8.02528726359828 + }, + { + "x": -0.0012199455029815454, + "y": 8.025287263595438 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0015249318788335131, + "y": 10.03160907949777 + }, + { + "x": -0.0013724386909075292, + "y": 9.028448171548078 + }, + { + "x": -0.0015249318788335131, + "y": 10.031609079500647 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 92.37334299443756, + "y": -21.9735175622229, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 88.37158893513006, + "y": -10.434921050719414, + "z": 1.0 + }, + { + "x": 88.37165014933649, + "y": -10.837612474316, + "z": 1.0 + }, + { + "x": 88.37171136354294, + "y": -11.240303897912584, + "z": 1.0 + }, + { + "x": 88.3717723608181, + "y": -11.641568261092115, + "z": 1.0 + }, + { + "x": 88.37186385673083, + "y": -12.243464805862263, + "z": 1.0 + }, + { + "x": 88.37194010332476, + "y": -12.745045259837152, + "z": 1.0 + }, + { + "x": 88.37200110059992, + "y": -13.14630962301706, + "z": 1.0 + }, + { + "x": 88.37209259651264, + "y": -13.748206167786929, + "z": 1.0 + }, + { + "x": 88.3721535937878, + "y": -14.149470530966838, + "z": 1.0 + }, + { + "x": 88.37221459106296, + "y": -14.550734894146467, + "z": 1.0 + }, + { + "x": 88.37227558833811, + "y": -14.951999257326378, + "z": 1.0 + }, + { + "x": 88.37235183493205, + "y": -15.453579711301547, + "z": 1.0 + }, + { + "x": 88.37242808152598, + "y": -15.955160165276435, + "z": 1.0 + }, + { + "x": 88.37251957743872, + "y": -16.557056710046012, + "z": 1.0 + }, + { + "x": 88.37261107335145, + "y": -17.158953254815877, + "z": 1.0 + }, + { + "x": 88.37268731994538, + "y": -17.660533708791046, + "z": 1.0 + }, + { + "x": 88.37274831722054, + "y": -18.06179807197096, + "z": 1.0 + }, + { + "x": 88.37282456381448, + "y": -18.563378525945843, + "z": 1.0 + }, + { + "x": 88.37290081040842, + "y": -19.06495897992073, + "z": 1.0 + }, + { + "x": 88.37296180768358, + "y": -19.46622334310036, + "z": 1.0 + }, + { + "x": 88.37302280495872, + "y": -19.867487706280553, + "z": 1.0 + }, + { + "x": 88.37309905155267, + "y": -20.36906816025516, + "z": 1.0 + }, + { + "x": 88.3731752981466, + "y": -20.870648614230337, + "z": 1.0 + }, + { + "x": 88.37326679405933, + "y": -21.472545159000205, + "z": 1.0 + }, + { + "x": 88.37335828997207, + "y": -22.074441703769793, + "z": 1.0 + }, + { + "x": 88.37343453656601, + "y": -22.576022157744966, + "z": 1.0 + }, + { + "x": 88.37352603247874, + "y": -23.177918702514834, + "z": 1.0 + }, + { + "x": 88.37361752839146, + "y": -23.779815247284706, + "z": 1.0 + }, + { + "x": 88.37369377498541, + "y": -24.281395701259314, + "z": 1.0 + }, + { + "x": 88.37377002157935, + "y": -24.782976155234483, + "z": 1.0 + }, + { + "x": 88.3738462681733, + "y": -25.284556609209098, + "z": 1.0 + }, + { + "x": 88.37392251476723, + "y": -25.786137063184267, + "z": 1.0 + }, + { + "x": 88.37398351204239, + "y": -26.18740142636418, + "z": 1.0 + }, + { + "x": 88.37405975863632, + "y": -26.68898188033907, + "z": 1.0 + }, + { + "x": 88.37412075591148, + "y": -27.090246243518983, + "z": 1.0 + }, + { + "x": 88.3742122518242, + "y": -27.69214278828885, + "z": 1.0 + }, + { + "x": 88.37428849841815, + "y": -28.19372324226346, + "z": 1.0 + }, + { + "x": 88.37437999433088, + "y": -28.79561978703333, + "z": 1.0 + }, + { + "x": 88.37444099160604, + "y": -29.196884150213243, + "z": 1.0 + }, + { + "x": 88.37450198888118, + "y": -29.598148513393436, + "z": 1.0 + }, + { + "x": 88.3745934847939, + "y": -30.200045058163305, + "z": 1.0 + }, + { + "x": 88.37465448206906, + "y": -30.601309421343217, + "z": 1.0 + }, + { + "x": 88.37473072866301, + "y": -31.10288987531783, + "z": 1.0 + }, + { + "x": 88.37482222457574, + "y": -31.704786420087693, + "z": 1.0 + }, + { + "x": 88.3748832218509, + "y": -32.106050783267605, + "z": 1.0 + }, + { + "x": 88.37497471776362, + "y": -32.70794732803748, + "z": 1.0 + }, + { + "x": 88.37506621367635, + "y": -33.30984387280735, + "z": 1.0 + }, + { + "x": 88.3751272109515, + "y": -33.711108235987254, + "z": 1.0 + }, + { + "x": 88.37520345754544, + "y": -34.212688689962434, + "z": 1.0 + }, + { + "x": 88.37529495345817, + "y": -34.814585234732306, + "z": 1.0 + }, + { + "x": 88.37535595073332, + "y": -35.21584959791221, + "z": 1.0 + }, + { + "x": 88.37541694800848, + "y": -35.617113961091846, + "z": 1.0 + }, + { + "x": 88.3755084439212, + "y": -36.21901050586199, + "z": 1.0 + }, + { + "x": 88.37558469051514, + "y": -36.72059095983688, + "z": 1.0 + }, + { + "x": 88.3756456877903, + "y": -37.1218553230168, + "z": 1.0 + }, + { + "x": 88.37572193438423, + "y": -37.623435776991684, + "z": 1.0 + }, + { + "x": 88.37578293165939, + "y": -38.0247001401716, + "z": 1.0 + }, + { + "x": 88.37587442757211, + "y": -38.626596684941475, + "z": 1.0 + }, + { + "x": 88.37596592348486, + "y": -39.22849322971105, + "z": 1.0 + }, + { + "x": 88.37602692076, + "y": -39.62975759289125, + "z": 1.0 + }, + { + "x": 88.37611841667272, + "y": -40.231654137661124, + "z": 1.0 + }, + { + "x": 88.37620991258547, + "y": -40.83355068243071, + "z": 1.0 + }, + { + "x": 88.37627090986061, + "y": -41.2348150456109, + "z": 1.0 + }, + { + "x": 88.37634715645456, + "y": -41.73639549958551, + "z": 1.0 + }, + { + "x": 88.37640815372971, + "y": -42.13765986276542, + "z": 1.0 + }, + { + "x": 88.37648440032365, + "y": -42.6392403167406, + "z": 1.0 + }, + { + "x": 88.37657589623637, + "y": -43.24113686151047, + "z": 1.0 + }, + { + "x": 88.37663689351153, + "y": -43.642401224690374, + "z": 1.0 + }, + { + "x": 88.37672838942426, + "y": -44.244297769460246, + "z": 1.0 + }, + { + "x": 88.37678938669941, + "y": -44.64556213263988, + "z": 1.0 + }, + { + "x": 88.37686563329335, + "y": -45.14714258661505, + "z": 1.0 + }, + { + "x": 88.3769418798873, + "y": -45.64872304058966, + "z": 1.0 + }, + { + "x": 88.37701812648123, + "y": -46.15030349456484, + "z": 1.0 + }, + { + "x": 88.37707931482646, + "y": -46.55282479273959, + "z": 1.0 + }, + { + "x": 88.377155800258, + "y": -47.05597641545811, + "z": 1.0 + }, + { + "x": 88.37721698860321, + "y": -47.45849771363293, + "z": 1.0 + }, + { + "x": 88.37727817694844, + "y": -47.86101901180774, + "z": 1.0 + }, + { + "x": 88.37736995946628, + "y": -48.46480095906996, + "z": 1.0 + }, + { + "x": 88.37746869965171, + "y": -48.97708841609315, + "z": 1.0 + }, + { + "x": 88.40185746732712, + "y": -49.71365309840904, + "z": 1.0 + }, + { + "x": 88.44585825376632, + "y": -50.204616797586624, + "z": 1.0 + }, + { + "x": 88.55335565655965, + "y": -50.936077505571575, + "z": 1.0 + }, + { + "x": 88.71019196677977, + "y": -51.6585678626945, + "z": 1.0 + }, + { + "x": 88.91564576217036, + "y": -52.36876451225841, + "z": 1.0 + }, + { + "x": 89.12332049993944, + "y": -52.94884511931964, + "z": 1.0 + }, + { + "x": 89.41529380090776, + "y": -53.628069768414065, + "z": 1.0 + }, + { + "x": 89.63524235353502, + "y": -54.069212211922306, + "z": 1.0 + }, + { + "x": 89.87491205951238, + "y": -54.499960149336395, + "z": 1.0 + }, + { + "x": 90.27056376603892, + "y": -55.124948450891765, + "z": 1.0 + }, + { + "x": 90.63189274832027, + "y": -55.62401717672893, + "z": 1.0 + }, + { + "x": 90.94090186406459, + "y": -56.00807087257196, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 23, + "heading": [ + -1.5260968829987904, + -1.5706443141063537, + -1.5706443141063184, + -1.5706443141062898, + -1.5706443141063038, + -1.5706443141063127, + -1.5706443141063084, + -1.5706443141063038, + -1.5706443141063038, + -1.5706443141062967, + -1.5706443141062967, + -1.5706443141063084, + -1.570644314106318, + -1.5706443141062998, + -1.5706443141062967, + -1.5706443141063127, + -1.5706443141063084, + -1.5706443141063084, + -1.5706443141063038, + -1.5706443141062927, + -1.5706443141063142, + -1.5706443141063084, + -1.5706443141063038, + -1.5706443141063127, + -1.5706443141062967, + -1.5706443141062998, + -1.5706443141063127, + -1.5706443141063084, + -1.5706443141062998, + -1.5706443141063038, + -1.5706443141063038, + -1.5706443141063038, + -1.5706443141063084, + -1.5706443141063084, + -1.5706443141063084, + -1.5706443141063038, + -1.5706443141062998, + -1.5706443141062998, + -1.5706443141063038, + -1.5706443141063144, + -1.570644314106318, + -1.5706443141063038, + -1.5706443141062927, + -1.5706443141062998, + -1.5706443141063038, + -1.5706443141063038, + -1.5706443141063084, + -1.5706443141063038, + -1.5706443141063084, + -1.5706443141063127, + -1.5706443141063038, + -1.5706443141062967, + -1.5706443141063038, + -1.5706443141063127, + -1.5706443141063084, + -1.5706443141063084, + -1.5706443141063084, + -1.5706443141063038, + -1.5706443141062967, + -1.5706443141063038, + -1.570644314106318, + -1.5706443141062967, + -1.5706443141063038, + -1.5706443141063084, + -1.5706443141062927, + -1.5706443141063084, + -1.5706443141063127, + -1.5706443141063038, + -1.5706443141063038, + -1.5706443141063036, + -1.5706443141063084, + -1.5706443141063038, + -1.5706443141063038, + -1.5706443141063058, + -1.570644314106291, + -1.5706443141063064, + -1.5706443141063084, + -1.570644314106291, + -1.5706256181447933, + -1.5511908270386274, + -1.5151406473842302, + -1.4474924853507034, + -1.3909568256260652, + -1.3231140246525257, + -1.2609247954819858, + -1.1930819948683404, + -1.1421999017184103, + -1.0856642517891186, + -1.0290687118318207, + -0.978121757078758, + -0.9215254427402406 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.0006121420642557496, + "y": -4.026914235965862 + }, + { + "x": 0.0012242841287957162, + "y": -8.053828471931705 + }, + { + "x": 0.0012221148161017936, + "y": -8.039557867761147 + }, + { + "x": 0.0015249318788335131, + "y": -10.031609079496793 + }, + { + "x": 0.0016774250666173884, + "y": -11.034769987450375 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171547971 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.001219945503123654, + "y": -8.025287263595384 + }, + { + "x": 0.001219945503123654, + "y": -8.025287263595402 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171550796 + }, + { + "x": 0.0015249318786914046, + "y": -10.031609079500576 + }, + { + "x": 0.001677425066759497, + "y": -11.034769987444655 + }, + { + "x": 0.0018299182546854809, + "y": -12.037930895394418 + }, + { + "x": 0.0016774250666173884, + "y": -11.034769987450339 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171550814 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171547971 + }, + { + "x": 0.0015249318788335131, + "y": -10.031609079497699 + }, + { + "x": 0.0013724386910496378, + "y": -9.028448171545165 + }, + { + "x": 0.0012199455029815454, + "y": -8.025287263598244 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548007 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949784 + }, + { + "x": 0.0016774250666173884, + "y": -11.034769987450446 + }, + { + "x": 0.0018299182546854809, + "y": -12.03793089539456 + }, + { + "x": 0.001677425066759497, + "y": -11.034769987447604 + }, + { + "x": 0.0016774250666173884, + "y": -11.03476998745041 + }, + { + "x": 0.0018299182545433723, + "y": -12.037930895397402 + }, + { + "x": 0.001677425066759497, + "y": -11.034769987444797 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949784 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949784 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171550814 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548042 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548042 + }, + { + "x": 0.0015249318788335131, + "y": -10.031609079497805 + }, + { + "x": 0.001677425066759497, + "y": -11.034769987444761 + }, + { + "x": 0.001677425066759497, + "y": -11.034769987444797 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949784 + }, + { + "x": 0.0012199455029815454, + "y": -8.02528726360105 + }, + { + "x": 0.0015249318786914046, + "y": -10.031609079500612 + }, + { + "x": 0.0015249318788335131, + "y": -10.031609079497805 + }, + { + "x": 0.0013724386910496378, + "y": -9.028448171545236 + }, + { + "x": 0.001677425066759497, + "y": -11.034769987444761 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949784 + }, + { + "x": 0.0018299182545433723, + "y": -12.037930895397437 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171550849 + }, + { + "x": 0.0016774250666173884, + "y": -11.034769987450517 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.001219945503123654, + "y": -8.025287263595402 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0016774250666173884, + "y": -11.034769987450375 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548149 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548007 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548007 + }, + { + "x": 0.0015249318788335131, + "y": -10.031609079497912 + }, + { + "x": 0.0018299182546854809, + "y": -12.037930895394453 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0015249318786914046, + "y": -10.031609079500754 + }, + { + "x": 0.0018299182546854809, + "y": -12.037930895394595 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548007 + }, + { + "x": 0.0013724386910496378, + "y": -9.028448171545165 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171550849 + }, + { + "x": 0.0016774250666173884, + "y": -11.034769987450517 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949507 + }, + { + "x": 0.0013724386909075292, + "y": -9.028448171548007 + }, + { + "x": 0.0015249318788335131, + "y": -10.03160907949777 + }, + { + "x": 0.0015249318788335131, + "y": -10.031609079497912 + }, + { + "x": 0.0013743493916251737, + "y": -9.041017521499342 + }, + { + "x": 0.0013767377676288106, + "y": -9.056729208932737 + }, + { + "x": 0.001376737767486702, + "y": -9.056729208933376 + }, + { + "x": 0.0012237669044168342, + "y": -8.050425963496295 + }, + { + "x": 0.0015297086306986785, + "y": -10.063032454370315 + }, + { + "x": 0.0019052270327790666, + "y": -11.160694042854118 + }, + { + "x": 0.24487507860840196, + "y": -12.488521393390783 + }, + { + "x": 0.683895541146029, + "y": -12.27528381493471 + }, + { + "x": 1.5149818923252667, + "y": -12.22424407162535 + }, + { + "x": 2.6433371301345687, + "y": -14.539510651078729 + }, + { + "x": 3.6229010561071107, + "y": -14.326870066868338 + }, + { + "x": 4.131285331596644, + "y": -12.902772566251457 + }, + { + "x": 4.996480387374049, + "y": -12.593052561556561 + }, + { + "x": 5.11921853595581, + "y": -11.203670926026632 + }, + { + "x": 4.596182586046211, + "y": -8.718903809223306 + }, + { + "x": 6.353214125038988, + "y": -10.557362389694589 + }, + { + "x": 7.569806888078858, + "y": -11.240570273925314 + }, + { + "x": 6.703380980256668, + "y": -8.831224216801985 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 90.94090186406459, + "y": -56.00807087257196, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 334.3018481088766, + "y": -5.288249675787207, + "z": 1.0 + }, + { + "x": 334.483777617468, + "y": -5.696789019027442, + "z": 1.0 + }, + { + "x": 334.6335298766519, + "y": -6.1181936524140035, + "z": 1.0 + }, + { + "x": 334.7327699870293, + "y": -6.476662154719557, + "z": 1.0 + }, + { + "x": 334.8211665615416, + "y": -6.91505878669135, + "z": 1.0 + }, + { + "x": 334.86871357172106, + "y": -7.284721762788378, + "z": 1.0 + }, + { + "x": 334.8941339881858, + "y": -7.731212958309326, + "z": 1.0 + }, + { + "x": 334.89504189202273, + "y": -8.077929108123932, + "z": 1.0 + }, + { + "x": 334.89473870653876, + "y": -8.579882660367307, + "z": 1.0 + }, + { + "x": 334.8944961581516, + "y": -8.981445502159971, + "z": 1.0 + }, + { + "x": 334.89413233557093, + "y": -9.583789764850664, + "z": 1.0 + }, + { + "x": 334.8938291500871, + "y": -10.085743317094039, + "z": 1.0 + }, + { + "x": 334.8935259646031, + "y": -10.587696869336284, + "z": 1.0 + }, + { + "x": 334.8934046904095, + "y": -10.788478290233183, + "z": 1.0 + }, + { + "x": 334.8931013907418, + "y": -11.290620885066517, + "z": 1.0 + }, + { + "x": 334.89279827934456, + "y": -11.792451779440704, + "z": 1.0 + }, + { + "x": 334.8924345456679, + "y": -12.394648852689727, + "z": 1.0 + }, + { + "x": 334.8920708119913, + "y": -12.996845925937615, + "z": 1.0 + }, + { + "x": 334.8918283228735, + "y": -13.398310641438098, + "z": 1.0 + }, + { + "x": 334.89146458919686, + "y": -14.00050771468712, + "z": 1.0 + }, + { + "x": 334.8911008555202, + "y": -14.602704787936144, + "z": 1.0 + }, + { + "x": 334.8907371218436, + "y": -15.20490186118403, + "z": 1.0 + }, + { + "x": 334.89037338816695, + "y": -15.807098934433052, + "z": 1.0 + }, + { + "x": 334.89007027676973, + "y": -16.308929828807237, + "z": 1.0 + }, + { + "x": 334.8897065430931, + "y": -16.91112690205739, + "z": 1.0 + }, + { + "x": 334.88934280941646, + "y": -17.51332397530641, + "z": 1.0 + }, + { + "x": 334.88903969801925, + "y": -18.015154869680593, + "z": 1.0 + }, + { + "x": 334.8886759643426, + "y": -18.617351942929613, + "z": 1.0 + }, + { + "x": 334.8884334752248, + "y": -19.01881665842783, + "z": 1.0 + }, + { + "x": 334.888190986107, + "y": -19.420281373928308, + "z": 1.0 + }, + { + "x": 334.8878878747098, + "y": -19.922112268302495, + "z": 1.0 + }, + { + "x": 334.8876453855921, + "y": -20.32357698380184, + "z": 1.0 + }, + { + "x": 334.8874028964743, + "y": -20.725041699301187, + "z": 1.0 + }, + { + "x": 334.88716040735653, + "y": -21.126506414800534, + "z": 1.0 + }, + { + "x": 334.8868572959593, + "y": -21.628337309174718, + "z": 1.0 + }, + { + "x": 334.8865541845621, + "y": -22.1301682035489, + "z": 1.0 + }, + { + "x": 334.8862510731649, + "y": -22.631999097923085, + "z": 1.0 + }, + { + "x": 334.8860085840472, + "y": -23.033463813422433, + "z": 1.0 + }, + { + "x": 334.88564485037057, + "y": -23.63566088667032, + "z": 1.0 + }, + { + "x": 334.88534173897335, + "y": -24.137491781044503, + "z": 1.0 + }, + { + "x": 334.8849780052967, + "y": -24.739688854293526, + "z": 1.0 + }, + { + "x": 334.8846748938995, + "y": -25.241519748667706, + "z": 1.0 + }, + { + "x": 334.88431116022286, + "y": -25.84371682191673, + "z": 1.0 + }, + { + "x": 334.88400804882565, + "y": -26.34554771629091, + "z": 1.0 + }, + { + "x": 334.88370493742843, + "y": -26.847378610665096, + "z": 1.0 + }, + { + "x": 334.8833412037518, + "y": -27.449575683914127, + "z": 1.0 + }, + { + "x": 334.883098714634, + "y": -27.851040399414615, + "z": 1.0 + }, + { + "x": 334.8828562255162, + "y": -28.252505114912836, + "z": 1.0 + }, + { + "x": 334.88249249183957, + "y": -28.854702188163, + "z": 1.0 + }, + { + "x": 334.88212875816293, + "y": -29.456899261412033, + "z": 1.0 + }, + { + "x": 334.8817650244863, + "y": -30.059096334661064, + "z": 1.0 + }, + { + "x": 334.88140129080966, + "y": -30.66129340790896, + "z": 1.0 + }, + { + "x": 334.88109817941245, + "y": -31.163124302284288, + "z": 1.0 + }, + { + "x": 334.8807344457358, + "y": -31.765321375532185, + "z": 1.0 + }, + { + "x": 334.8804313343386, + "y": -32.267152269906376, + "z": 1.0 + }, + { + "x": 334.8801282229414, + "y": -32.76898316428171, + "z": 1.0 + }, + { + "x": 334.87982511154416, + "y": -33.27081405865589, + "z": 1.0 + }, + { + "x": 334.87952200014695, + "y": -33.77264495303008, + "z": 1.0 + }, + { + "x": 334.87921888874973, + "y": -34.274475847404275, + "z": 1.0 + }, + { + "x": 334.8789157773525, + "y": -34.77630674177734, + "z": 1.0 + }, + { + "x": 334.8786732882347, + "y": -35.17777145727783, + "z": 1.0 + }, + { + "x": 334.8783701768375, + "y": -35.67960235165202, + "z": 1.0 + }, + { + "x": 334.8780670654403, + "y": -36.18143324602622, + "z": 1.0 + }, + { + "x": 334.87770333176366, + "y": -36.78363031927525, + "z": 1.0 + }, + { + "x": 334.877460842646, + "y": -37.18509503477346, + "z": 1.0 + }, + { + "x": 334.87709710896934, + "y": -37.78729210802249, + "z": 1.0 + }, + { + "x": 334.8767939975721, + "y": -38.289123002396686, + "z": 1.0 + }, + { + "x": 334.8765515084543, + "y": -38.69058771789717, + "z": 1.0 + }, + { + "x": 334.87630901933653, + "y": -39.092052433395395, + "z": 1.0 + }, + { + "x": 334.8759452856599, + "y": -39.694249506644425, + "z": 1.0 + }, + { + "x": 334.87558155198326, + "y": -40.29644657989459, + "z": 1.0 + }, + { + "x": 334.87527844058604, + "y": -40.79827747426879, + "z": 1.0 + }, + { + "x": 334.8749147069094, + "y": -41.40047454751782, + "z": 1.0 + }, + { + "x": 334.8746722177916, + "y": -41.80193926301603, + "z": 1.0 + }, + { + "x": 334.8744297286738, + "y": -42.20340397851652, + "z": 1.0 + }, + { + "x": 334.8741266172766, + "y": -42.70523487289071, + "z": 1.0 + }, + { + "x": 334.87376288359997, + "y": -43.30743194613973, + "z": 1.0 + }, + { + "x": 334.8735203944823, + "y": -43.70889666163794, + "z": 1.0 + }, + { + "x": 334.87321728308507, + "y": -44.21072755601211, + "z": 1.0 + }, + { + "x": 334.8729747939673, + "y": -44.61219227151258, + "z": 1.0 + }, + { + "x": 334.87267168257006, + "y": -45.11402316588563, + "z": 1.0 + }, + { + "x": 334.8723079488934, + "y": -45.716220239135765, + "z": 1.0 + }, + { + "x": 334.8720048374962, + "y": -46.218051133509945, + "z": 1.0 + }, + { + "x": 334.87188359293737, + "y": -46.41878349125962, + "z": 1.0 + }, + { + "x": 334.8715801110045, + "y": -46.92122784405019, + "z": 1.0 + }, + { + "x": 334.87133732545817, + "y": -47.32318332628318, + "z": 1.0 + }, + { + "x": 334.8710338435253, + "y": -47.825627679074415, + "z": 1.0 + }, + { + "x": 334.8706696652059, + "y": -48.4285609024239, + "z": 1.0 + }, + { + "x": 334.87036618327295, + "y": -48.93100525521626, + "z": 1.0 + }, + { + "x": 334.8700627013401, + "y": -49.43344960800749, + "z": 1.0 + }, + { + "x": 334.8696985230207, + "y": -50.036382831355844, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 24, + "heading": [ + -1.5532445997494864, + -1.1518436168635509, + -1.1905972608595516, + -1.2617530685069873, + -1.3395403756927158, + -1.4041224818930387, + -1.4816294206080958, + -1.5376162787349619, + -1.5700837783804023, + -1.5714003377551682, + -1.5714003377550556, + -1.5714003377549617, + -1.5714003377550543, + -1.5714003377551682, + -1.5714003377551, + -1.57140033775507, + -1.5714003377550467, + -1.5714003377550303, + -1.5714003377550674, + -1.5714003377550667, + -1.5714003377550296, + -1.5714003377550303, + -1.5714003377550303, + -1.5714003377550467, + -1.5714003377550463, + -1.5714003377550292, + -1.5714003377550467, + -1.5714003377550467, + -1.571400337755068, + -1.571400337755124, + -1.5714003377550918, + -1.5714003377549668, + -1.5714003377549826, + -1.571400337755124, + -1.5714003377550925, + -1.5714003377550674, + -1.5714003377550674, + -1.5714003377549668, + -1.5714003377549548, + -1.5714003377550474, + -1.5714003377550467, + -1.5714003377550467, + -1.5714003377550467, + -1.5714003377550467, + -1.5714003377550674, + -1.5714003377550467, + -1.5714003377550667, + -1.571400337755124, + -1.5714003377550674, + -1.571400337755029, + -1.5714003377550296, + -1.5714003377550303, + -1.5714003377550467, + -1.5714003377550467, + -1.5714003377550474, + -1.5714003377550667, + -1.5714003377550667, + -1.5714003377550674, + -1.5714003377550674, + -1.571400337755068, + -1.5714003377550925, + -1.5714003377550918, + -1.5714003377550674, + -1.5714003377550467, + -1.5714003377549548, + -1.5714003377549548, + -1.5714003377550467, + -1.5714003377550918, + -1.571400337755124, + -1.571400337755068, + -1.571400337755029, + -1.5714003377550463, + -1.5714003377550467, + -1.571400337755068, + -1.571400337755124, + -1.5714003377550918, + -1.5714003377550467, + -1.5714003377549548, + -1.5714003377549675, + -1.5714003377550918, + -1.5714003377550927, + -1.5714003377550467, + -1.5714003377550463, + -1.5714003377550028, + -1.5714003377549923, + -1.5714003377551027, + -1.5714003377551022, + -1.571400337755011, + -1.5714003377551131, + -1.5714003377551644, + -1.5714003377550114 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 1.81929508591395, + "y": -4.085393432402347 + }, + { + "x": 3.316817677753079, + "y": -8.299439766267964 + }, + { + "x": 2.4899236956133564, + "y": -7.798731356921156 + }, + { + "x": 1.8763668488969643, + "y": -7.9686513427734695 + }, + { + "x": 1.3594358469174495, + "y": -8.080596080688203 + }, + { + "x": 0.7296742664419753, + "y": -8.161541716179759 + }, + { + "x": 0.2632832030167265, + "y": -7.932073453355546 + }, + { + "x": 0.006047183529744871, + "y": -8.486697020579808 + }, + { + "x": -0.005457338711494231, + "y": -9.035163940360391 + }, + { + "x": -0.006063709678301166, + "y": -10.03907104483357 + }, + { + "x": -0.006670080645108101, + "y": -11.042978149340676 + }, + { + "x": -0.006063709678301166, + "y": -10.0390710448562 + }, + { + "x": -0.004244596775606624, + "y": -7.027349731391439 + }, + { + "x": -0.004245738613235517, + "y": -7.0292401573023255 + }, + { + "x": -0.006064110649504073, + "y": -10.03973489207521 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232103 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464969112 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887483708 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887495058 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464980463 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464969095 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464969077 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232068 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676243383 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464991725 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232032 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232032 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887472374 + }, + { + "x": -0.004849782355904608, + "y": -8.029294309986952 + }, + { + "x": -0.005456005150108467, + "y": -9.032956098746645 + }, + { + "x": -0.005456005148971599, + "y": -9.032956098735312 + }, + { + "x": -0.00484978235476774, + "y": -8.029294309986916 + }, + { + "x": -0.004849782355904608, + "y": -8.029294309986952 + }, + { + "x": -0.005456005150108467, + "y": -9.032956098735312 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887483672 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887483672 + }, + { + "x": -0.005456005148971599, + "y": -9.032956098735312 + }, + { + "x": -0.006062227943175458, + "y": -10.036617887472339 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676220699 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232068 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232032 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232032 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232032 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887483672 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232174 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887495183 + }, + { + "x": -0.004849782355904608, + "y": -8.029294309987094 + }, + { + "x": -0.006062227944312326, + "y": -10.03661788748385 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464991974 + }, + { + "x": -0.007274673532720044, + "y": -12.04394146498064 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464969272 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232245 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232245 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676220877 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887495218 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887495112 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887483743 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887483885 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887472588 + }, + { + "x": -0.005456005150108467, + "y": -9.032956098735525 + }, + { + "x": -0.005456005150108467, + "y": -9.032956098746823 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887483885 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232245 + }, + { + "x": -0.006062227943175458, + "y": -10.036617887472445 + }, + { + "x": -0.006062227943175458, + "y": -10.036617887472445 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232245 + }, + { + "x": -0.005456005150108467, + "y": -9.032956098746823 + }, + { + "x": -0.004849782355904608, + "y": -8.029294309987094 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887472516 + }, + { + "x": -0.007274673532720044, + "y": -12.043941464991974 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676243614 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232245 + }, + { + "x": -0.006062227944312326, + "y": -10.036617887472445 + }, + { + "x": -0.004849782355904608, + "y": -8.029294309987023 + }, + { + "x": -0.005456005150108467, + "y": -9.032956098746823 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676232103 + }, + { + "x": -0.006062227943175458, + "y": -10.036617887472232 + }, + { + "x": -0.005456005148971599, + "y": -9.032956098723801 + }, + { + "x": -0.005456005150108467, + "y": -9.032956098746467 + }, + { + "x": -0.005456005150108467, + "y": -9.03295609873517 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676231819 + }, + { + "x": -0.006668450738516185, + "y": -11.040279676243188 + }, + { + "x": -0.004243559560563881, + "y": -7.025632521238521 + }, + { + "x": -0.004247264917012217, + "y": -7.03176710540248 + }, + { + "x": -0.005462674791942845, + "y": -9.043998350235611 + }, + { + "x": -0.005462674791942845, + "y": -9.04399835024222 + }, + { + "x": -0.00667660252247515, + "y": -11.05377576140718 + }, + { + "x": -0.0066766025236120186, + "y": -11.053775761418478 + }, + { + "x": -0.006069638658345866, + "y": -10.048887055835962 + }, + { + "x": -0.00667660252247515, + "y": -11.053775761395812 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 334.8696985230207, + "y": -50.036382831355844, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 335.72938655142246, + "y": -58.63575611077631, + "z": 1.0 + }, + { + "x": 335.26862518724147, + "y": -58.22155991552123, + "z": 1.0 + }, + { + "x": 334.698387263307, + "y": -57.76731713412676, + "z": 1.0 + }, + { + "x": 334.3028154287997, + "y": -57.48483981665477, + "z": 1.0 + }, + { + "x": 333.8957244634312, + "y": -57.219229528144716, + "z": 1.0 + }, + { + "x": 333.4774670769269, + "y": -56.970813165394034, + "z": 1.0 + }, + { + "x": 333.0494715041758, + "y": -56.740398386255336, + "z": 1.0 + }, + { + "x": 332.61217496998677, + "y": -56.52816815784608, + "z": 1.0 + }, + { + "x": 331.94048368733047, + "y": -56.244729219671626, + "z": 1.0 + }, + { + "x": 331.3680067648105, + "y": -56.04123961894434, + "z": 1.0 + }, + { + "x": 330.6681152139086, + "y": -55.83714288248695, + "z": 1.0 + }, + { + "x": 329.95675694016956, + "y": -55.67754008286519, + "z": 1.0 + }, + { + "x": 329.2367567288088, + "y": -55.56306505257391, + "z": 1.0 + }, + { + "x": 328.63221699389953, + "y": -55.502475293783945, + "z": 1.0 + }, + { + "x": 327.9038158309816, + "y": -55.471785085123855, + "z": 1.0 + }, + { + "x": 327.47163572658246, + "y": -55.47111336395268, + "z": 1.0 + }, + { + "x": 326.96905200000987, + "y": -55.471174605179854, + "z": 1.0 + }, + { + "x": 326.3659515281231, + "y": -55.471248094652466, + "z": 1.0 + }, + { + "x": 325.8633678015507, + "y": -55.47130933587964, + "z": 1.0 + }, + { + "x": 325.46130082029276, + "y": -55.471358328861356, + "z": 1.0 + }, + { + "x": 325.15975058434924, + "y": -55.47139507359766, + "z": 1.0 + }, + { + "x": 324.6594655975632, + "y": -55.47145603471698, + "z": 1.0 + }, + { + "x": 324.1591806107769, + "y": -55.47151699583631, + "z": 1.0 + }, + { + "x": 323.65889562399076, + "y": -55.47157795695564, + "z": 1.0 + }, + { + "x": 323.1586106372048, + "y": -55.47163891807496, + "z": 1.0 + }, + { + "x": 322.65832565041853, + "y": -55.47169987919429, + "z": 1.0 + }, + { + "x": 322.0579836662754, + "y": -55.471773032537484, + "z": 1.0 + }, + { + "x": 321.45764168213185, + "y": -55.47184618588068, + "z": 1.0 + }, + { + "x": 320.85729969798865, + "y": -55.47191933922386, + "z": 1.0 + }, + { + "x": 320.3570147112025, + "y": -55.47198030034319, + "z": 1.0 + }, + { + "x": 319.756672727059, + "y": -55.472053453686385, + "z": 1.0 + }, + { + "x": 319.35644473763034, + "y": -55.47210222258184, + "z": 1.0 + }, + { + "x": 318.9562167482014, + "y": -55.47215099147729, + "z": 1.0 + }, + { + "x": 318.35587476405806, + "y": -55.47222414482049, + "z": 1.0 + }, + { + "x": 317.7555327799147, + "y": -55.47229729816368, + "z": 1.0 + }, + { + "x": 317.3553047904859, + "y": -55.47234606705915, + "z": 1.0 + }, + { + "x": 316.7549628063425, + "y": -55.47241922040233, + "z": 1.0 + }, + { + "x": 316.2546778195562, + "y": -55.47248018152166, + "z": 1.0 + }, + { + "x": 315.7543928327701, + "y": -55.47254114264099, + "z": 1.0 + }, + { + "x": 315.3541648433412, + "y": -55.472589911536446, + "z": 1.0 + }, + { + "x": 314.9539368539124, + "y": -55.4726386804319, + "z": 1.0 + }, + { + "x": 314.35359486976915, + "y": -55.472711833775094, + "z": 1.0 + }, + { + "x": 313.95336688034024, + "y": -55.47276060267056, + "z": 1.0 + }, + { + "x": 313.55313889091116, + "y": -55.472809371566015, + "z": 1.0 + }, + { + "x": 313.1529109014824, + "y": -55.47285814046148, + "z": 1.0 + }, + { + "x": 312.55256891733916, + "y": -55.47293129380468, + "z": 1.0 + }, + { + "x": 312.15234092791025, + "y": -55.47298006270013, + "z": 1.0 + }, + { + "x": 311.75211293848133, + "y": -55.4730288315956, + "z": 1.0 + }, + { + "x": 311.15177095433796, + "y": -55.47310198493878, + "z": 1.0 + }, + { + "x": 310.75154296490905, + "y": -55.47315075383425, + "z": 1.0 + }, + { + "x": 310.2512579781229, + "y": -55.473211714953564, + "z": 1.0 + }, + { + "x": 309.6509159939795, + "y": -55.47328486829676, + "z": 1.0 + }, + { + "x": 309.15063100719334, + "y": -55.47334582941609, + "z": 1.0 + }, + { + "x": 308.65034602040726, + "y": -55.47340679053542, + "z": 1.0 + }, + { + "x": 308.25011803097834, + "y": -55.473455559430874, + "z": 1.0 + }, + { + "x": 307.6497760468351, + "y": -55.47352871277407, + "z": 1.0 + }, + { + "x": 307.2495480574063, + "y": -55.47357748166952, + "z": 1.0 + }, + { + "x": 306.6492060732629, + "y": -55.47365063501272, + "z": 1.0 + }, + { + "x": 306.248978083834, + "y": -55.47369940390817, + "z": 1.0 + }, + { + "x": 305.6486360996905, + "y": -55.473772557251365, + "z": 1.0 + }, + { + "x": 305.2484081102616, + "y": -55.47382132614683, + "z": 1.0 + }, + { + "x": 304.84818012083275, + "y": -55.47387009504229, + "z": 1.0 + }, + { + "x": 304.24783813668955, + "y": -55.47394324838548, + "z": 1.0 + }, + { + "x": 303.7475531499034, + "y": -55.47400420950481, + "z": 1.0 + }, + { + "x": 303.3473251604744, + "y": -55.474052978400266, + "z": 1.0 + }, + { + "x": 302.9470971710456, + "y": -55.474101747295734, + "z": 1.0 + }, + { + "x": 302.3467551869023, + "y": -55.474174900638914, + "z": 1.0 + }, + { + "x": 301.9465271974734, + "y": -55.47422366953438, + "z": 1.0 + }, + { + "x": 301.5462992080445, + "y": -55.474272438429836, + "z": 1.0 + }, + { + "x": 301.1460712186156, + "y": -55.4743212073253, + "z": 1.0 + }, + { + "x": 300.5457292344722, + "y": -55.474394360668484, + "z": 1.0 + }, + { + "x": 300.1455012450434, + "y": -55.47444312956395, + "z": 1.0 + }, + { + "x": 299.6452162582573, + "y": -55.47450409068327, + "z": 1.0 + }, + { + "x": 299.24498826882837, + "y": -55.474552859578736, + "z": 1.0 + }, + { + "x": 298.7447032820422, + "y": -55.47461382069807, + "z": 1.0 + }, + { + "x": 298.3444752926133, + "y": -55.47466258959352, + "z": 1.0 + }, + { + "x": 297.7441333084698, + "y": -55.474735742936716, + "z": 1.0 + }, + { + "x": 297.2438483216839, + "y": -55.47479670405605, + "z": 1.0 + }, + { + "x": 296.843620332255, + "y": -55.4748454729515, + "z": 1.0 + }, + { + "x": 296.24327834811163, + "y": -55.474918626294695, + "z": 1.0 + }, + { + "x": 295.8430503586827, + "y": -55.47496739519015, + "z": 1.0 + }, + { + "x": 295.24270837453935, + "y": -55.47504054853334, + "z": 1.0 + }, + { + "x": 294.7424233877531, + "y": -55.475101509652674, + "z": 1.0 + }, + { + "x": 294.3421953983243, + "y": -55.47515027854813, + "z": 1.0 + }, + { + "x": 293.7418534141807, + "y": -55.47522343189132, + "z": 1.0 + }, + { + "x": 293.14151143003744, + "y": -55.47529658523452, + "z": 1.0 + }, + { + "x": 292.6412264432512, + "y": -55.475357546353834, + "z": 1.0 + }, + { + "x": 292.2409984538223, + "y": -55.4754063152493, + "z": 1.0 + }, + { + "x": 291.74071346703613, + "y": -55.47546727636862, + "z": 1.0 + }, + { + "x": 291.3404854776071, + "y": -55.47551604526409, + "z": 1.0 + }, + { + "x": 290.84020049082096, + "y": -55.4755770063834, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 25, + "heading": [ + -2.0911486832018613, + 2.409364246695374, + 2.4415698129842616, + 2.4899494772842057, + 2.5424788063606654, + 2.584577189082867, + 2.6266809113227545, + 2.668743372007666, + 2.7212726952459185, + 2.76854909399482, + 2.8315843006733368, + 2.8893665716104087, + 2.952401781124309, + 3.010184052581499, + 3.0732192687883306, + 3.1145766226218736, + 3.14093956899561, + -3.141470800804608, + -3.141470800804608, + -3.141470800804635, + -3.1414708008046373, + -3.14147080080462, + -3.141470800804616, + -3.1414708008046017, + -3.141470800804616, + -3.141470800804616, + -3.141470800804604, + -3.141470800804606, + -3.141470800804618, + -3.1414708008046173, + -3.1414708008046044, + -3.141470800804616, + -3.14147080080463, + -3.141470800804616, + -3.141470800804606, + -3.1414708008046017, + -3.141470800804616, + -3.1414708008046173, + -3.1414708008046017, + -3.141470800804614, + -3.14147080080463, + -3.141470800804616, + -3.1414708008046017, + -3.1414708008046124, + -3.1414708008046124, + -3.1414708008046017, + -3.141470800804616, + -3.1414708008046124, + -3.141470800804616, + -3.141470800804616, + -3.141470800804614, + -3.1414708008046173, + -3.1414708008046044, + -3.1414708008046017, + -3.141470800804614, + -3.141470800804616, + -3.141470800804616, + -3.141470800804616, + -3.141470800804616, + -3.141470800804616, + -3.1414708008046017, + -3.1414708008046124, + -3.141470800804616, + -3.141470800804604, + -3.141470800804614, + -3.1414708008046124, + -3.141470800804616, + -3.141470800804616, + -3.1414708008046124, + -3.1414708008046124, + -3.141470800804616, + -3.141470800804616, + -3.141470800804614, + -3.141470800804614, + -3.1414708008045986, + -3.141470800804614, + -3.141470800804616, + -3.141470800804604, + -3.141470800804614, + -3.141470800804616, + -3.141470800804616, + -3.141470800804616, + -3.1414708008046044, + -3.141470800804614, + -3.141470800804616, + -3.141470800804606, + -3.1414708008046173, + -3.141470800804614, + -3.141470800804614, + -3.141470800804614, + -3.141470800804614 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -4.607613641809962, + "y": 4.141961952550801 + }, + { + "x": -10.309992881154812, + "y": 8.6843897664955 + }, + { + "x": -9.658097584417646, + "y": 7.367200988664635 + }, + { + "x": -8.026627998758045, + "y": 5.480876059820474 + }, + { + "x": -8.253483518727762, + "y": 5.140266512607354 + }, + { + "x": -8.462529592553665, + "y": 4.788311418893798 + }, + { + "x": -8.6529210694016, + "y": 4.426450075479522 + }, + { + "x": -11.089878168453424, + "y": 4.9566916658370985 + }, + { + "x": -12.441682051762655, + "y": 4.869285389017435 + }, + { + "x": -12.723684734218637, + "y": 4.075863371846751 + }, + { + "x": -14.112498246409473, + "y": 3.6369953607914596 + }, + { + "x": -14.313584850997927, + "y": 2.7407782991303975 + }, + { + "x": -13.24539946270022, + "y": 1.7506478908124734 + }, + { + "x": -13.329408978272, + "y": 0.9127996745005618 + }, + { + "x": -11.605812673170703, + "y": 0.3136192983126307 + }, + { + "x": -9.347638309717468, + "y": 0.006104799440009856 + }, + { + "x": -11.056841984593575, + "y": -0.001347306997843134 + }, + { + "x": -11.05684198459187, + "y": -0.001347306997843134 + }, + { + "x": -9.04650707830342, + "y": -0.0011023420888989222 + }, + { + "x": -7.036172172014403, + "y": -0.0008573771802389274 + }, + { + "x": -8.018352227295509, + "y": -0.0009770585562307588 + }, + { + "x": -10.005699735723397, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735724534, + "y": -0.0012192223866236418 + }, + { + "x": -10.005699735721123, + "y": -0.0012192223864815332 + }, + { + "x": -10.00569973572226, + "y": -0.0012192223864815332 + }, + { + "x": -11.006269709293974, + "y": -0.0013411446252575843 + }, + { + "x": -12.006839682866826, + "y": -0.0014630668638915267 + }, + { + "x": -12.006839682867394, + "y": -0.0014630668637494182 + }, + { + "x": -11.006269709293406, + "y": -0.0013411446251154757 + }, + { + "x": -11.006269709296248, + "y": -0.0013411446252575843 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223864815332 + }, + { + "x": -8.004559788575989, + "y": -0.0009753779090715398 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -12.006839682867394, + "y": -0.0014630668638915267 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223866236418 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223864815332 + }, + { + "x": -11.006269709296816, + "y": -0.0013411446251154757 + }, + { + "x": -10.005699735723965, + "y": -0.0012192223866236418 + }, + { + "x": -9.005129762149977, + "y": -0.0010973001478475908 + }, + { + "x": -8.004559788577126, + "y": -0.0009753779090715398 + }, + { + "x": -10.005699735720555, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223866236418 + }, + { + "x": -8.004559788579968, + "y": -0.0009753779092136483 + }, + { + "x": -8.004559788578263, + "y": -0.0009753779092136483 + }, + { + "x": -10.005699735719986, + "y": -0.0012192223866236418 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223864815332 + }, + { + "x": -8.004559788578263, + "y": -0.0009753779092136483 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -9.005129762150546, + "y": -0.0010973001478475908 + }, + { + "x": -11.00626970929568, + "y": -0.0013411446251154757 + }, + { + "x": -11.00626970929568, + "y": -0.0013411446252575843 + }, + { + "x": -10.00569973572226, + "y": -0.0012192223866236418 + }, + { + "x": -9.005129762149977, + "y": -0.0010973001478475908 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735720555, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735723965, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735723965, + "y": -0.0012192223866236418 + }, + { + "x": -8.004559788577694, + "y": -0.0009753779092136483 + }, + { + "x": -10.005699735720555, + "y": -0.0012192223864815332 + }, + { + "x": -11.006269709293406, + "y": -0.0013411446252575843 + }, + { + "x": -9.005129762151682, + "y": -0.0010973001478475908 + }, + { + "x": -8.004559788578263, + "y": -0.0009753779092136483 + }, + { + "x": -10.005699735720555, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223864815332 + }, + { + "x": -8.004559788578263, + "y": -0.0009753779092136483 + }, + { + "x": -8.004559788578263, + "y": -0.0009753779092136483 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735721691, + "y": -0.0012192223864815332 + }, + { + "x": -9.005129762149409, + "y": -0.0010973001478475908 + }, + { + "x": -9.005129762150546, + "y": -0.0010973001478475908 + }, + { + "x": -9.005129762150546, + "y": -0.0010973001479896993 + }, + { + "x": -9.005129762150546, + "y": -0.0010973001478475908 + }, + { + "x": -10.005699735723965, + "y": -0.0012192223864815332 + }, + { + "x": -11.006269709293974, + "y": -0.0013411446252575843 + }, + { + "x": -9.005129762148272, + "y": -0.0010973001478475908 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -10.005699735722828, + "y": -0.0012192223864815332 + }, + { + "x": -11.006269709296248, + "y": -0.0013411446252575843 + }, + { + "x": -9.005129762150546, + "y": -0.0010973001478475908 + }, + { + "x": -10.005699735723965, + "y": -0.0012192223864815332 + }, + { + "x": -12.00683968286853, + "y": -0.0014630668638915267 + }, + { + "x": -11.006269709295111, + "y": -0.0013411446251154757 + }, + { + "x": -9.005129762151682, + "y": -0.0010973001478475908 + }, + { + "x": -9.005129762150546, + "y": -0.0010973001478475908 + }, + { + "x": -9.005129762151682, + "y": -0.0010973001478475908 + }, + { + "x": -9.005129762151682, + "y": -0.0010973001478475908 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 290.84020049082096, + "y": -55.4755770063834, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 334.7845708455262, + "y": -190.97367165224483, + "z": 1.0 + }, + { + "x": 334.7842674203806, + "y": -191.47602198796034, + "z": 1.0 + }, + { + "x": 334.7839639952351, + "y": -191.97837232367357, + "z": 1.0 + }, + { + "x": 334.78359988506054, + "y": -192.5811927265308, + "z": 1.0 + }, + { + "x": 334.78335714494415, + "y": -192.98307299510117, + "z": 1.0 + }, + { + "x": 334.78299303476956, + "y": -193.58589339795952, + "z": 1.0 + }, + { + "x": 334.782628924595, + "y": -194.1887138008168, + "z": 1.0 + }, + { + "x": 334.7823861844786, + "y": -194.59059406938712, + "z": 1.0 + }, + { + "x": 334.7821434443622, + "y": -194.99247433795864, + "z": 1.0 + }, + { + "x": 334.7819007042457, + "y": -195.3943546065313, + "z": 1.0 + }, + { + "x": 334.7816579641293, + "y": -195.79623487510275, + "z": 1.0 + }, + { + "x": 334.7812938539547, + "y": -196.39905527795884, + "z": 1.0 + }, + { + "x": 334.7810511138383, + "y": -196.8009355465315, + "z": 1.0 + }, + { + "x": 334.78068700366373, + "y": -197.4037559493887, + "z": 1.0 + }, + { + "x": 334.78032289348914, + "y": -198.00657635224485, + "z": 1.0 + }, + { + "x": 334.78001946834354, + "y": -198.50892668796035, + "z": 1.0 + }, + { + "x": 334.77965535816895, + "y": -199.11174709081644, + "z": 1.0 + }, + { + "x": 334.77941261805256, + "y": -199.51362735938795, + "z": 1.0 + }, + { + "x": 334.7791091929071, + "y": -200.01597769510346, + "z": 1.0 + }, + { + "x": 334.7788057677616, + "y": -200.51832803081783, + "z": 1.0 + }, + { + "x": 334.7784416575869, + "y": -201.12114843367505, + "z": 1.0 + }, + { + "x": 334.7781382324414, + "y": -201.6234987693894, + "z": 1.0 + }, + { + "x": 334.7777741222668, + "y": -202.2263191722467, + "z": 1.0 + }, + { + "x": 334.7774100120922, + "y": -202.8291395751039, + "z": 1.0 + }, + { + "x": 334.77704590191763, + "y": -203.43195997796113, + "z": 1.0 + }, + { + "x": 334.77674247677214, + "y": -203.93431031367436, + "z": 1.0 + }, + { + "x": 334.77637836659744, + "y": -204.53713071653277, + "z": 1.0 + }, + { + "x": 334.77601425642285, + "y": -205.13995111939, + "z": 1.0 + }, + { + "x": 334.77571083127737, + "y": -205.64230145510322, + "z": 1.0 + }, + { + "x": 334.7754074061319, + "y": -206.14465179081873, + "z": 1.0 + }, + { + "x": 334.7751039809864, + "y": -206.6470021265331, + "z": 1.0 + }, + { + "x": 334.7747398708117, + "y": -207.24982252939145, + "z": 1.0 + }, + { + "x": 334.7744364456662, + "y": -207.75217286510355, + "z": 1.0 + }, + { + "x": 334.7740723354916, + "y": -208.35499326796304, + "z": 1.0 + }, + { + "x": 334.7737689103461, + "y": -208.85734360367633, + "z": 1.0 + }, + { + "x": 334.7735868552588, + "y": -209.15875380510494, + "z": 1.0 + }, + { + "x": 334.77322414725745, + "y": -209.7592527713511, + "z": 1.0 + }, + { + "x": 334.77298234192335, + "y": -210.1595854155138, + "z": 1.0 + }, + { + "x": 334.77274053658914, + "y": -210.55991805967767, + "z": 1.0 + }, + { + "x": 334.7724987312549, + "y": -210.96025070384263, + "z": 1.0 + }, + { + "x": 334.7722569259207, + "y": -211.36058334800651, + "z": 1.0 + }, + { + "x": 334.77189421791945, + "y": -211.96108231425114, + "z": 1.0 + }, + { + "x": 334.7715315099182, + "y": -212.56158128049805, + "z": 1.0 + }, + { + "x": 334.771289704584, + "y": -212.96191392466193, + "z": 1.0 + }, + { + "x": 334.77098744791624, + "y": -213.46232972986672, + "z": 1.0 + }, + { + "x": 334.770745642582, + "y": -213.8626623740306, + "z": 1.0 + }, + { + "x": 334.7704433859144, + "y": -214.3630781792354, + "z": 1.0 + }, + { + "x": 334.77014112924667, + "y": -214.86349398443912, + "z": 1.0 + }, + { + "x": 334.76983887257893, + "y": -215.3639097896439, + "z": 1.0 + }, + { + "x": 334.7695366159111, + "y": -215.8643255948499, + "z": 1.0 + }, + { + "x": 334.76917390790993, + "y": -216.46482456109453, + "z": 1.0 + }, + { + "x": 334.7689321025757, + "y": -216.86515720525836, + "z": 1.0 + }, + { + "x": 334.768629845908, + "y": -217.3655730104632, + "z": 1.0 + }, + { + "x": 334.76832758924013, + "y": -217.86598881566914, + "z": 1.0 + }, + { + "x": 334.76808578390603, + "y": -218.2663214598319, + "z": 1.0 + }, + { + "x": 334.76772307590477, + "y": -218.8668204260788, + "z": 1.0 + }, + { + "x": 334.76742081923703, + "y": -219.3672362312825, + "z": 1.0 + }, + { + "x": 334.7671790139028, + "y": -219.76756887544747, + "z": 1.0 + }, + { + "x": 334.7669372085686, + "y": -220.16790151961135, + "z": 1.0 + }, + { + "x": 334.76657450056734, + "y": -220.76840048585598, + "z": 1.0 + }, + { + "x": 334.76633269523313, + "y": -221.1687331300198, + "z": 1.0 + }, + { + "x": 334.7660908898989, + "y": -221.56906577418482, + "z": 1.0 + }, + { + "x": 334.7657886332312, + "y": -222.06948157938962, + "z": 1.0 + }, + { + "x": 334.7654259252299, + "y": -222.66998054563544, + "z": 1.0 + }, + { + "x": 334.76506321722866, + "y": -223.27047951188007, + "z": 1.0 + }, + { + "x": 334.7647609605609, + "y": -223.770895317086, + "z": 1.0 + }, + { + "x": 334.7645191552267, + "y": -224.17122796124988, + "z": 1.0 + }, + { + "x": 334.76415644722545, + "y": -224.7717269274945, + "z": 1.0 + }, + { + "x": 334.76391464189123, + "y": -225.17205957165953, + "z": 1.0 + }, + { + "x": 334.76355193388997, + "y": -225.77255853790416, + "z": 1.0 + }, + { + "x": 334.7633101285559, + "y": -226.17289118206799, + "z": 1.0 + }, + { + "x": 334.76306832322166, + "y": -226.573223826233, + "z": 1.0 + }, + { + "x": 334.7627660665539, + "y": -227.07363963143666, + "z": 1.0 + }, + { + "x": 334.7624638098862, + "y": -227.57405543664146, + "z": 1.0 + }, + { + "x": 334.7621011018849, + "y": -228.17455440288722, + "z": 1.0 + }, + { + "x": 334.76173839388355, + "y": -228.77505336913418, + "z": 1.0 + }, + { + "x": 334.7613756858823, + "y": -229.3755523353788, + "z": 1.0 + }, + { + "x": 334.7611338805481, + "y": -229.77588497954264, + "z": 1.0 + }, + { + "x": 334.760892075214, + "y": -230.17621762370766, + "z": 1.0 + }, + { + "x": 334.76065026987976, + "y": -230.57655026787148, + "z": 1.0 + }, + { + "x": 334.760348013212, + "y": -231.07696607307628, + "z": 1.0 + }, + { + "x": 334.7601062078778, + "y": -231.47729871724016, + "z": 1.0 + }, + { + "x": 334.7598644025436, + "y": -231.87763136140285, + "z": 1.0 + }, + { + "x": 334.7596225972095, + "y": -232.27796400556667, + "z": 1.0 + }, + { + "x": 334.7593807918753, + "y": -232.6782966497317, + "z": 1.0 + }, + { + "x": 334.7590180838739, + "y": -233.27879561597746, + "z": 1.0 + }, + { + "x": 334.7587762785398, + "y": -233.67912826014015, + "z": 1.0 + }, + { + "x": 334.7584740218721, + "y": -234.17954406534614, + "z": 1.0 + }, + { + "x": 334.7581113138708, + "y": -234.78004303159076, + "z": 1.0 + }, + { + "x": 334.75780905720296, + "y": -235.2804588367967, + "z": 1.0 + }, + { + "x": 334.75756725186886, + "y": -235.68079148095939, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 26, + "heading": [ + -1.5714003377550525, + -1.5714003377552381, + -1.5714003377551264, + -1.5714003377550139, + -1.5714003377550139, + -1.5714003377550132, + -1.5714003377550128, + -1.5714003377550139, + -1.571400337755014, + -1.5714003377551538, + -1.5714003377551538, + -1.5714003377550139, + -1.5714003377550132, + -1.5714003377550125, + -1.5714003377550139, + -1.571400337755116, + -1.5714003377551162, + -1.5714003377550139, + -1.5714003377550125, + -1.5714003377550125, + -1.5714003377551162, + -1.5714003377551162, + -1.5714003377550132, + -1.5714003377550132, + -1.5714003377550132, + -1.5714003377550139, + -1.571400337755116, + -1.571400337755107, + -1.5714003377550139, + -1.5714003377550132, + -1.5714003377550125, + -1.5714003377551156, + -1.5714003377551167, + -1.5714003377550132, + -1.5714003377550125, + -1.571400337755014, + -1.5714003377551413, + -1.571400337755055, + -1.5714003377549701, + -1.5714003377551105, + -1.5714003377551105, + -1.5714003377550552, + -1.5714003377550168, + -1.5714003377550538, + -1.5714003377550798, + -1.5714003377550798, + -1.5714003377549535, + -1.5714003377549417, + -1.5714003377550552, + -1.5714003377551675, + -1.5714003377550338, + -1.5714003377549417, + -1.5714003377550798, + -1.5714003377551675, + -1.5714003377550798, + -1.571400337754941, + -1.5714003377550338, + -1.5714003377550798, + -1.5714003377551105, + -1.5714003377550552, + -1.5714003377550552, + -1.5714003377551105, + -1.571400337755079, + -1.5714003377550338, + -1.5714003377550172, + -1.5714003377550338, + -1.571400337755079, + -1.5714003377550552, + -1.5714003377550545, + -1.5714003377550545, + -1.5714003377549417, + -1.5714003377549686, + -1.5714003377550798, + -1.5714003377550552, + -1.5714003377550338, + -1.5714003377551107, + -1.5714003377551113, + -1.5714003377550552, + -1.5714003377549686, + -1.5714003377549686, + -1.5714003377550798, + -1.5714003377550798, + -1.5714003377551122, + -1.5714003377549701, + -1.5714003377549686, + -1.5714003377551675, + -1.5714003377550552, + -1.5714003377549535, + -1.5714003377550338, + -1.571400337755137, + -1.5714003377550798 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.003034251456028869, + "y": -5.02350335715505 + }, + { + "x": -0.00606850291092087, + "y": -10.047006714287363 + }, + { + "x": -0.006675353200762402, + "y": -11.051707385704503 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275994 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714287363 + }, + { + "x": -0.007282203491740802, + "y": -12.056408057156318 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275994 + }, + { + "x": -0.004854802327827201, + "y": -8.037605371418408 + }, + { + "x": -0.00485480232896407, + "y": -8.037605371441714 + }, + { + "x": -0.00485480232896407, + "y": -8.037605371441146 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275426 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714287363 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714298732 + }, + { + "x": -0.007282203491740802, + "y": -12.05640805713358 + }, + { + "x": -0.00667535320189927, + "y": -11.05170738571644 + }, + { + "x": -0.00667535320189927, + "y": -11.051707385715872 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275994 + }, + { + "x": -0.005461652618805601, + "y": -9.042306042870223 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714298732 + }, + { + "x": -0.00667535320189927, + "y": -11.051707385715872 + }, + { + "x": -0.00667535320189927, + "y": -11.051707385715872 + }, + { + "x": -0.006675353200762402, + "y": -11.05170738571644 + }, + { + "x": -0.007282203491740802, + "y": -12.05640805714495 + }, + { + "x": -0.007282203491740802, + "y": -12.056408057144381 + }, + { + "x": -0.006675353200762402, + "y": -11.051707385704503 + }, + { + "x": -0.00667535320189927, + "y": -11.05170738571644 + }, + { + "x": -0.00728220349287767, + "y": -12.056408057156318 + }, + { + "x": -0.006675353200762402, + "y": -11.051707385704503 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714287363 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714298732 + }, + { + "x": -0.00667535320189927, + "y": -11.05170738572724 + }, + { + "x": -0.00667535320189927, + "y": -11.051707385704503 + }, + { + "x": -0.006675353200762402, + "y": -11.051707385715872 + }, + { + "x": -0.006675353200762402, + "y": -11.05170738572781 + }, + { + "x": -0.004854802327827201, + "y": -8.037605371418977 + }, + { + "x": -0.005447630886692423, + "y": -9.019091676747735 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104088522 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883265701 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883288439 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883288439 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104085111 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932491532 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104107848 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493686775 + }, + { + "x": -0.006045133353609344, + "y": -10.008316104085111 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104085111 + }, + { + "x": -0.006045133355883081, + "y": -10.008316104107848 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506184 + }, + { + "x": -0.006045133353609344, + "y": -10.008316104084543 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.006045133355883081, + "y": -10.008316104107848 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.006045133353609344, + "y": -10.00831610409648 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506184 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883288439 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104085111 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883288439 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493698144 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506184 + }, + { + "x": -0.007254160025240708, + "y": -12.00997932490452 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493698144 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104085111 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610409648 + }, + { + "x": -0.006045133354746213, + "y": -10.00831610409648 + }, + { + "x": -0.006045133353609344, + "y": -10.008316104084543 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883288439 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714505616 + }, + { + "x": -0.007254160026377576, + "y": -12.009979324927258 + }, + { + "x": -0.007254160026377576, + "y": -12.00997932491589 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883288439 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883288439 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686775 + }, + { + "x": -0.0048361066842517175, + "y": -8.006652883265701 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883265133 + }, + { + "x": -0.004836106683114849, + "y": -8.006652883288439 + }, + { + "x": -0.006045133355883081, + "y": -10.008316104107848 + }, + { + "x": -0.006045133354746213, + "y": -10.008316104084543 + }, + { + "x": -0.005440620018362097, + "y": -9.007484493686775 + }, + { + "x": -0.00664964668999346, + "y": -11.009147714506184 + }, + { + "x": -0.006649646691130329, + "y": -11.009147714505616 + }, + { + "x": -0.005440620019498965, + "y": -9.007484493686206 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 334.75756725186886, + "y": -235.68079148095939, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 329.263169991849, + "y": 1.9436457072073068, + "z": 1.0 + }, + { + "x": 328.7804214359283, + "y": 1.9852061222393989, + "z": 1.0 + }, + { + "x": 328.29636526433956, + "y": 2.0067022980379825, + "z": 1.0 + }, + { + "x": 327.63924552920065, + "y": 2.010254613561665, + "z": 1.0 + }, + { + "x": 327.2376770785852, + "y": 2.0104679414741553, + "z": 1.0 + }, + { + "x": 326.635324402662, + "y": 2.010787933342891, + "z": 1.0 + }, + { + "x": 326.03297172673877, + "y": 2.011107925211627, + "z": 1.0 + }, + { + "x": 325.6314032761233, + "y": 2.0113212531241174, + "z": 1.0 + }, + { + "x": 325.6314032761233, + "y": 2.0113212531241174, + "z": 1.0 + }, + { + "x": 325.2306859929232, + "y": 2.0115341288652, + "z": 1.0 + }, + { + "x": 324.63011428605995, + "y": 2.0118531746177477, + "z": 1.0 + }, + { + "x": 324.2297331481511, + "y": 2.012065871786113, + "z": 1.0 + }, + { + "x": 323.7292567257651, + "y": 2.0123317432465693, + "z": 1.0 + }, + { + "x": 323.22878030337904, + "y": 2.0125976147070257, + "z": 1.0 + }, + { + "x": 322.8283991654702, + "y": 2.0128103118753917, + "z": 1.0 + }, + { + "x": 322.227827458607, + "y": 2.0131293576279394, + "z": 1.0 + }, + { + "x": 321.8274463206982, + "y": 2.0133420547963046, + "z": 1.0 + }, + { + "x": 321.42706518278936, + "y": 2.0135547519646697, + "z": 1.0 + }, + { + "x": 320.9265887604033, + "y": 2.013820623425126, + "z": 1.0 + }, + { + "x": 320.32601705354006, + "y": 2.014139669177674, + "z": 1.0 + }, + { + "x": 319.825540631154, + "y": 2.0144055406381303, + "z": 1.0 + }, + { + "x": 319.4251594932452, + "y": 2.0146182378064954, + "z": 1.0 + }, + { + "x": 319.02477835533637, + "y": 2.0148309349748605, + "z": 1.0 + }, + { + "x": 318.5243019329503, + "y": 2.015096806435317, + "z": 1.0 + }, + { + "x": 318.1239207950415, + "y": 2.015309503603683, + "z": 1.0 + }, + { + "x": 317.62344437265546, + "y": 2.0155753750641394, + "z": 1.0 + }, + { + "x": 317.1229679502694, + "y": 2.015841246524596, + "z": 1.0 + }, + { + "x": 316.62249152788337, + "y": 2.0161071179850523, + "z": 1.0 + }, + { + "x": 316.1220151054973, + "y": 2.0163729894455087, + "z": 1.0 + }, + { + "x": 315.62153868311134, + "y": 2.016638860905965, + "z": 1.0 + }, + { + "x": 315.1210622607253, + "y": 2.0169047323664215, + "z": 1.0 + }, + { + "x": 314.62058583833925, + "y": 2.017170603826878, + "z": 1.0 + }, + { + "x": 314.020014131476, + "y": 2.0174896495794266, + "z": 1.0 + }, + { + "x": 313.51953770908995, + "y": 2.017755521039883, + "z": 1.0 + }, + { + "x": 313.01906128670396, + "y": 2.0180213925003394, + "z": 1.0 + }, + { + "x": 312.5185848643179, + "y": 2.018287263960796, + "z": 1.0 + }, + { + "x": 312.1182037264091, + "y": 2.018499961129161, + "z": 1.0 + }, + { + "x": 311.51763201954583, + "y": 2.0188190068817087, + "z": 1.0 + }, + { + "x": 311.117250881637, + "y": 2.019031704050074, + "z": 1.0 + }, + { + "x": 310.61677445925096, + "y": 2.0192975755105302, + "z": 1.0 + }, + { + "x": 310.1162980368649, + "y": 2.0195634469709867, + "z": 1.0 + }, + { + "x": 309.51572633000166, + "y": 2.0198824927235353, + "z": 1.0 + }, + { + "x": 309.11534519209283, + "y": 2.0200951898919004, + "z": 1.0 + }, + { + "x": 308.5147734852296, + "y": 2.020414235644448, + "z": 1.0 + }, + { + "x": 308.1143923473208, + "y": 2.0206269328128132, + "z": 1.0 + }, + { + "x": 307.7140112094119, + "y": 2.0208396299811784, + "z": 1.0 + }, + { + "x": 307.3136300715031, + "y": 2.0210523271495435, + "z": 1.0 + }, + { + "x": 306.7130583646399, + "y": 2.0213713729020912, + "z": 1.0 + }, + { + "x": 306.1124866577766, + "y": 2.021690418654639, + "z": 1.0 + }, + { + "x": 305.6120102353906, + "y": 2.0219562901150954, + "z": 1.0 + }, + { + "x": 305.11153381300454, + "y": 2.0222221615755527, + "z": 1.0 + }, + { + "x": 304.7111526750957, + "y": 2.022434858743918, + "z": 1.0 + }, + { + "x": 304.2106762527097, + "y": 2.0227007302043742, + "z": 1.0 + }, + { + "x": 303.7101998303237, + "y": 2.0229666016648307, + "z": 1.0 + }, + { + "x": 303.20972340793764, + "y": 2.023232473125287, + "z": 1.0 + }, + { + "x": 302.6091517010744, + "y": 2.023551518877835, + "z": 1.0 + }, + { + "x": 302.20877056316556, + "y": 2.0237642160462, + "z": 1.0 + }, + { + "x": 301.6081988563023, + "y": 2.0240832617987476, + "z": 1.0 + }, + { + "x": 301.10772243391625, + "y": 2.024349133259204, + "z": 1.0 + }, + { + "x": 300.7073412960074, + "y": 2.02456183042757, + "z": 1.0 + }, + { + "x": 300.1067695891442, + "y": 2.024880876180118, + "z": 1.0 + }, + { + "x": 299.6062931667582, + "y": 2.0251467476405742, + "z": 1.0 + }, + { + "x": 299.20591202884935, + "y": 2.0253594448089394, + "z": 1.0 + }, + { + "x": 298.7054356064633, + "y": 2.025625316269396, + "z": 1.0 + }, + { + "x": 298.10486389960005, + "y": 2.0259443620219435, + "z": 1.0 + }, + { + "x": 297.7044827616912, + "y": 2.0261570591903086, + "z": 1.0 + }, + { + "x": 297.2040063393052, + "y": 2.026422930650765, + "z": 1.0 + }, + { + "x": 296.80362520139636, + "y": 2.02663562781913, + "z": 1.0 + }, + { + "x": 296.3031487790103, + "y": 2.0269014992795875, + "z": 1.0 + }, + { + "x": 295.7025770721471, + "y": 2.0272205450321352, + "z": 1.0 + }, + { + "x": 295.10200536528384, + "y": 2.027539590784683, + "z": 1.0 + }, + { + "x": 294.5014336584206, + "y": 2.0278586365372306, + "z": 1.0 + }, + { + "x": 293.9008619515574, + "y": 2.0281776822897783, + "z": 1.0 + }, + { + "x": 293.50048081364855, + "y": 2.0283903794581435, + "z": 1.0 + }, + { + "x": 293.1000996757397, + "y": 2.0286030766265086, + "z": 1.0 + }, + { + "x": 292.6997185378309, + "y": 2.0288157737948738, + "z": 1.0 + }, + { + "x": 292.19924211544486, + "y": 2.02908164525533, + "z": 1.0 + }, + { + "x": 291.7988609775361, + "y": 2.0292943424236953, + "z": 1.0 + }, + { + "x": 291.19828927067283, + "y": 2.029613388176244, + "z": 1.0 + }, + { + "x": 290.797908132764, + "y": 2.029826085344609, + "z": 1.0 + }, + { + "x": 290.2965826672941, + "y": 2.030018519555301, + "z": 1.0 + }, + { + "x": 289.6960108791071, + "y": 2.0300826181407468, + "z": 1.0 + }, + { + "x": 289.2956296869824, + "y": 2.030125350531044, + "z": 1.0 + }, + { + "x": 288.69505789879537, + "y": 2.03018944911649, + "z": 1.0 + }, + { + "x": 288.09448611060833, + "y": 2.0302535477019354, + "z": 1.0 + }, + { + "x": 287.6941049184836, + "y": 2.030296280092233, + "z": 1.0 + }, + { + "x": 287.29372372635896, + "y": 2.0303390124825302, + "z": 1.0 + }, + { + "x": 286.6931519381719, + "y": 2.030403111067976, + "z": 1.0 + }, + { + "x": 286.0925801499849, + "y": 2.0304672096534215, + "z": 1.0 + }, + { + "x": 285.592103659829, + "y": 2.0305206251412926, + "z": 1.0 + }, + { + "x": 285.09162716967313, + "y": 2.0305740406291646, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 27, + "heading": [ + 3.1391704606677107, + 3.0557131807973996, + 3.0764632585844023, + 3.1196464578883, + 3.138035772908013, + 3.141061416905, + 3.141061416905, + 3.141061416905, + 3.141061416905, + 3.141061416905023, + 3.1410614169050093, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049986, + 3.1410614169049986, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049986, + 3.1410614169049986, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.141061416904999, + 3.141061416904999, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.141061416904999, + 3.1410614169049986, + 3.1410614169049995, + 3.1410614169049995, + 3.141061416905, + 3.141061416905, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049986, + 3.1410614169049986, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049986, + 3.1410614169049986, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049986, + 3.141061416904999, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049995, + 3.1410614169049986, + 3.1410614169049986, + 3.141143359606275, + 3.1413598435064105, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253437, + 3.1414859243253432, + 3.1414859243253437, + 3.1414859243253437, + 3.141485924325344, + 3.1414859243253437 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -4.827485559206934, + "y": 0.4156041503209207 + }, + { + "x": -9.668047275094409, + "y": 0.6305659083067572 + }, + { + "x": -11.411759067276535, + "y": 0.2504849132226594 + }, + { + "x": -10.58688185754363, + "y": 0.03765643436172805 + }, + { + "x": -10.039211265386712, + "y": 0.005333197812262824 + }, + { + "x": -12.047053518464281, + "y": 0.006399837374715389 + }, + { + "x": -10.039211265386712, + "y": 0.005333197812262824 + }, + { + "x": -4.015684506154571, + "y": 0.0021332791249051297 + }, + { + "x": -4.007172832000947, + "y": 0.0021287574108264096 + }, + { + "x": -10.012889900633581, + "y": 0.005319214936303496 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288224511 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209137359 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -8.007622758176467, + "y": 0.0042539433673027816 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130041325 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130041325 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -8.007622758176467, + "y": 0.0042539433673027816 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288224511 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288224511 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130050206 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130050206 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130050206 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209137359 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -8.007622758177035, + "y": 0.0042539433673027816 + }, + { + "x": -8.007622758177035, + "y": 0.0042539433673027816 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -12.0114341372647, + "y": 0.006380915050954172 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130041325 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209137359 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288224511 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130041325 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130041325 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288224511 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209137359 + }, + { + "x": -11.0104812924925, + "y": 0.005849172130041325 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -11.010481292493068, + "y": 0.005849172130041325 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209128477 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288224511 + }, + { + "x": -11.0104812924925, + "y": 0.005849172130050206 + }, + { + "x": -12.0114341372647, + "y": 0.006380915050954172 + }, + { + "x": -12.011434137265269, + "y": 0.006380915050954172 + }, + { + "x": -12.0114341372647, + "y": 0.006380915050954172 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209128477 + }, + { + "x": -8.007622758176467, + "y": 0.0042539433673027816 + }, + { + "x": -8.007622758176467, + "y": 0.0042539433673027816 + }, + { + "x": -9.008575602948667, + "y": 0.004785686288215629 + }, + { + "x": -9.008575602948099, + "y": 0.004785686288215629 + }, + { + "x": -10.0095284477203, + "y": 0.005317429209137359 + }, + { + "x": -10.009528447720868, + "y": 0.005317429209137359 + }, + { + "x": -9.017066033787273, + "y": 0.0040513137905717755 + }, + { + "x": -11.018972536568867, + "y": 0.002565327961376873 + }, + { + "x": -10.009529803116948, + "y": 0.0010683097574304412 + }, + { + "x": -10.009529803117516, + "y": 0.0010683097574304412 + }, + { + "x": -12.011435763740792, + "y": 0.0012819717089129767 + }, + { + "x": -10.009529803117516, + "y": 0.0010683097574304412 + }, + { + "x": -8.007623842493672, + "y": 0.0008546478059479057 + }, + { + "x": -10.009529803116948, + "y": 0.0010683097574304412 + }, + { + "x": -12.011435763740792, + "y": 0.0012819717089129767 + }, + { + "x": -11.010482783429438, + "y": 0.001175140733167268 + }, + { + "x": -10.009529803117516, + "y": 0.0010683097574304412 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 285.09162716967313, + "y": 2.0305740406291646, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 104.12054777946341, + "y": -133.416111777875, + "z": 1.0 + }, + { + "x": 104.62081546714708, + "y": -133.4163352943318, + "z": 1.0 + }, + { + "x": 105.22113669236818, + "y": -133.41660351407995, + "z": 1.0 + }, + { + "x": 105.62135084251558, + "y": -133.41678232724541, + "z": 1.0 + }, + { + "x": 106.22167206773668, + "y": -133.41705054699358, + "z": 1.0 + }, + { + "x": 106.82199329295777, + "y": -133.41731876674174, + "z": 1.0 + }, + { + "x": 107.22220744310516, + "y": -133.4174975799072, + "z": 1.0 + }, + { + "x": 107.62242159325257, + "y": -133.41767639307264, + "z": 1.0 + }, + { + "x": 108.02263574339996, + "y": -133.4178552062381, + "z": 1.0 + }, + { + "x": 108.52290343108422, + "y": -133.4180787226949, + "z": 1.0 + }, + { + "x": 109.12322465630531, + "y": -133.41834694244307, + "z": 1.0 + }, + { + "x": 109.5234388064527, + "y": -133.41852575560853, + "z": 1.0 + }, + { + "x": 110.02370649413696, + "y": -133.41874927206533, + "z": 1.0 + }, + { + "x": 110.52397418182177, + "y": -133.41897278852215, + "z": 1.0 + }, + { + "x": 111.02424186950545, + "y": -133.41919630497895, + "z": 1.0 + }, + { + "x": 111.42445601965285, + "y": -133.4193751181444, + "z": 1.0 + }, + { + "x": 111.9247237073371, + "y": -133.41959863460121, + "z": 1.0 + }, + { + "x": 112.52504493255819, + "y": -133.41986685434938, + "z": 1.0 + }, + { + "x": 113.1253661577793, + "y": -133.42013507409754, + "z": 1.0 + }, + { + "x": 113.52558030792669, + "y": -133.420313887263, + "z": 1.0 + }, + { + "x": 114.02584799561093, + "y": -133.4205374037198, + "z": 1.0 + }, + { + "x": 114.52611568329576, + "y": -133.42076092017663, + "z": 1.0 + }, + { + "x": 115.02638337097943, + "y": -133.42098443663343, + "z": 1.0 + }, + { + "x": 115.52665105866369, + "y": -133.42120795309023, + "z": 1.0 + }, + { + "x": 115.92686520881108, + "y": -133.4213867662557, + "z": 1.0 + }, + { + "x": 116.52718643403217, + "y": -133.42165498600386, + "z": 1.0 + }, + { + "x": 116.92740058418013, + "y": -133.42183379916932, + "z": 1.0 + }, + { + "x": 117.32761473432697, + "y": -133.42201261233475, + "z": 1.0 + }, + { + "x": 117.92793595954807, + "y": -133.42228083208292, + "z": 1.0 + }, + { + "x": 118.52825718476973, + "y": -133.4225490518311, + "z": 1.0 + }, + { + "x": 119.0285248724534, + "y": -133.4227725682879, + "z": 1.0 + }, + { + "x": 119.62884609767448, + "y": -133.42304078803608, + "z": 1.0 + }, + { + "x": 120.22916732289558, + "y": -133.42330900778424, + "z": 1.0 + }, + { + "x": 120.72943501057982, + "y": -133.42353252424107, + "z": 1.0 + }, + { + "x": 121.32975623580091, + "y": -133.42380074398923, + "z": 1.0 + }, + { + "x": 121.83002392348516, + "y": -133.42402426044603, + "z": 1.0 + }, + { + "x": 122.3302916111694, + "y": -133.42424777690286, + "z": 1.0 + }, + { + "x": 122.83055929885364, + "y": -133.42447129335966, + "z": 1.0 + }, + { + "x": 123.43088052407474, + "y": -133.42473951310782, + "z": 1.0 + }, + { + "x": 123.93114821175898, + "y": -133.42496302956465, + "z": 1.0 + }, + { + "x": 124.43141589944322, + "y": -133.42518654602145, + "z": 1.0 + }, + { + "x": 124.83163004959118, + "y": -133.4253653591869, + "z": 1.0 + }, + { + "x": 125.33189773727486, + "y": -133.4255888756437, + "z": 1.0 + }, + { + "x": 125.73211188742226, + "y": -133.42576768880915, + "z": 1.0 + }, + { + "x": 126.2323795751065, + "y": -133.42599120526597, + "z": 1.0 + }, + { + "x": 126.73264726279075, + "y": -133.42621472172277, + "z": 1.0 + }, + { + "x": 127.33296848801184, + "y": -133.42648294147094, + "z": 1.0 + }, + { + "x": 127.93328971323294, + "y": -133.42675116121913, + "z": 1.0 + }, + { + "x": 128.33350386338032, + "y": -133.42692997438456, + "z": 1.0 + }, + { + "x": 128.83377155106513, + "y": -133.4271534908414, + "z": 1.0 + }, + { + "x": 129.43409277628567, + "y": -133.42742171058956, + "z": 1.0 + }, + { + "x": 130.03441400150675, + "y": -133.42768993033772, + "z": 1.0 + }, + { + "x": 130.534681689191, + "y": -133.42791344679452, + "z": 1.0 + }, + { + "x": 131.1350029144121, + "y": -133.4281816665427, + "z": 1.0 + }, + { + "x": 131.53521706455948, + "y": -133.42836047970815, + "z": 1.0 + }, + { + "x": 132.13553828978058, + "y": -133.4286286994563, + "z": 1.0 + }, + { + "x": 132.73585951500166, + "y": -133.4288969192045, + "z": 1.0 + }, + { + "x": 133.2361272026859, + "y": -133.4291204356613, + "z": 1.0 + }, + { + "x": 133.83644842790702, + "y": -133.42938865540947, + "z": 1.0 + }, + { + "x": 134.23666257805442, + "y": -133.42956746857493, + "z": 1.0 + }, + { + "x": 134.7369302657387, + "y": -133.42979098503173, + "z": 1.0 + }, + { + "x": 135.3372514909598, + "y": -133.4300592047799, + "z": 1.0 + }, + { + "x": 135.7374656411072, + "y": -133.43023801794536, + "z": 1.0 + }, + { + "x": 136.33778686632832, + "y": -133.43050623769352, + "z": 1.0 + }, + { + "x": 136.8380545540126, + "y": -133.43072975415032, + "z": 1.0 + }, + { + "x": 137.33832224169683, + "y": -133.43095327060715, + "z": 1.0 + }, + { + "x": 137.8385899293811, + "y": -133.43117678706395, + "z": 1.0 + }, + { + "x": 138.2388040795285, + "y": -133.4313556002294, + "z": 1.0 + }, + { + "x": 138.73907176721278, + "y": -133.4315791166862, + "z": 1.0 + }, + { + "x": 139.13928591736075, + "y": -133.43175792985167, + "z": 1.0 + }, + { + "x": 139.63955360504445, + "y": -133.43198144630847, + "z": 1.0 + }, + { + "x": 140.03976775519186, + "y": -133.4321602594739, + "z": 1.0 + }, + { + "x": 140.43998190533927, + "y": -133.43233907263937, + "z": 1.0 + }, + { + "x": 140.94024959302354, + "y": -133.43256258909616, + "z": 1.0 + }, + { + "x": 141.34046374317094, + "y": -133.43274140226163, + "z": 1.0 + }, + { + "x": 141.74067789331835, + "y": -133.43292021542706, + "z": 1.0 + }, + { + "x": 142.2409455810026, + "y": -133.4331437318839, + "z": 1.0 + }, + { + "x": 142.64115973115, + "y": -133.43332254504932, + "z": 1.0 + }, + { + "x": 143.14142741883484, + "y": -133.43354606150615, + "z": 1.0 + }, + { + "x": 143.64169510651854, + "y": -133.43376957796295, + "z": 1.0 + }, + { + "x": 144.24201633173965, + "y": -133.4340377977111, + "z": 1.0 + }, + { + "x": 144.7422840194245, + "y": -133.43426131416794, + "z": 1.0 + }, + { + "x": 145.14249816957133, + "y": -133.43444012733337, + "z": 1.0 + }, + { + "x": 145.74281939479243, + "y": -133.43470834708154, + "z": 1.0 + }, + { + "x": 146.2430870824767, + "y": -133.43493186353837, + "z": 1.0 + }, + { + "x": 146.74335477016095, + "y": -133.43515537999517, + "z": 1.0 + }, + { + "x": 147.14356892030892, + "y": -133.43533419316063, + "z": 1.0 + }, + { + "x": 147.54378307045576, + "y": -133.43551300632606, + "z": 1.0 + }, + { + "x": 148.04405075814003, + "y": -133.43573652278286, + "z": 1.0 + }, + { + "x": 148.44426490828744, + "y": -133.43591533594832, + "z": 1.0 + }, + { + "x": 148.9445325959717, + "y": -133.43613885240512, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 28, + "heading": [ + -0.0004467936815410096, + -0.00044679368152074914, + -0.0004467936815256359, + -0.0004467936815486416, + -0.0004467936815486416, + -0.0004467936815297082, + -0.00044679368154864794, + -0.0004467936815415416, + -0.0004467936815415416, + -0.000446793681545486, + -0.00044679368152539934, + -0.00044679368154864794, + -0.000446793681545486, + -0.0004467936815483878, + -0.00044679368154864794, + -0.00044679368151420533, + -0.000446793681545486, + -0.0004467936815512292, + -0.0004467936815297029, + -0.0004467936815486416, + -0.00044679368154549307, + -0.0004467936815483878, + -0.0004467936815486416, + -0.00044679368152048893, + -0.000446793681545486, + -0.00044679368154864794, + -0.0004467936815483941, + -0.0004467936815415416, + -0.00044679368152048893, + -0.00044679368155316874, + -0.0004467936815512292, + -0.00044679368152564166, + -0.0004467936815297135, + -0.0004467936815512292, + -0.0004467936815512292, + -0.00044679368152540514, + -0.00044679368154864794, + -0.00044679368154864794, + -0.00044679368152540514, + -0.0004467936815512292, + -0.00044679368154864794, + -0.00044679368154521107, + -0.00044679368154549307, + -0.00044679368151420533, + -0.000446793681545486, + -0.00044679368154864794, + -0.00044679368152540514, + -0.00044679368155338026, + -0.0004467936815486543, + -0.0004467936815452181, + -0.00044679368155122346, + -0.0004467936815299197, + -0.0004467936815254109, + -0.00044679368155122346, + -0.00044679368154864794, + -0.00044679368152024146, + -0.00044679368155338026, + -0.000446793681551235, + -0.00044679368152539934, + -0.00044679368154863525, + -0.00044679368154547193, + -0.00044679368152538784, + -0.00044679368154863525, + -0.00044679368154863525, + -0.00044679368152538784, + -0.00044679368154863525, + -0.00044679368154863525, + -0.00044679368154547193, + -0.00044679368154547193, + -0.0004467936815451899, + -0.00044679368154547193, + -0.0004467936815141912, + -0.0004467936815415336, + -0.00044679368154547193, + -0.00044679368154547193, + -0.0004467936815415336, + -0.000446793681545486, + -0.000446793681545486, + -0.0004467936815451899, + -0.00044679368154862257, + -0.00044679368152561856, + -0.00044679368155098114, + -0.00044679368154547193, + -0.0004467936815204826, + -0.0004467936815512119, + -0.00044679368154863525, + -0.000446793681545204, + -0.0004467936815415336, + -0.0004467936815141912, + -0.00044679368154547193, + -0.00044679368154547193 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 5.002676876836745, + "y": -0.0022351645679918875 + }, + { + "x": 11.005889129047688, + "y": -0.004917362049638996 + }, + { + "x": 10.005353753685, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753685, + "y": -0.004470329136267992 + }, + { + "x": 12.006424504421886, + "y": -0.005364394963294217 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 8.004283002947972, + "y": -0.00357626330895755 + }, + { + "x": 8.004283002947972, + "y": -0.00357626330895755 + }, + { + "x": 9.004818378316486, + "y": -0.004023296222612771 + }, + { + "x": 11.005889129053514, + "y": -0.004917362049638996 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 9.004818378316486, + "y": -0.004023296222612771 + }, + { + "x": 10.005353753690684, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 9.004818378310802, + "y": -0.004023296222328554 + }, + { + "x": 9.004818378316486, + "y": -0.004023296222612771 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049923213 + }, + { + "x": 12.006424504422029, + "y": -0.005364394963294217 + }, + { + "x": 10.005353753685, + "y": -0.004470329136267992 + }, + { + "x": 9.004818378316344, + "y": -0.004023296222612771 + }, + { + "x": 10.005353753690684, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753685, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753679316, + "y": -0.004470329135983775 + }, + { + "x": 9.004818378316486, + "y": -0.004023296222612771 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753690542, + "y": -0.004470329136267992 + }, + { + "x": 8.004283002947972, + "y": -0.00357626330895755 + }, + { + "x": 10.005353753679316, + "y": -0.004470329135983775 + }, + { + "x": 12.00642450442757, + "y": -0.005364394963578434 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049923213 + }, + { + "x": 11.005889129047546, + "y": -0.004917362049638996 + }, + { + "x": 12.006424504421744, + "y": -0.005364394963294217 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049923213 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049923213 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049638996 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049638996 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049923213 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 9.004818378322028, + "y": -0.004023296222612771 + }, + { + "x": 9.004818378316344, + "y": -0.004023296222612771 + }, + { + "x": 9.004818378310802, + "y": -0.004023296222328554 + }, + { + "x": 9.004818378316486, + "y": -0.004023296222612771 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 11.005889129053372, + "y": -0.004917362049638996 + }, + { + "x": 12.006424504421886, + "y": -0.005364394963578434 + }, + { + "x": 10.005353753684716, + "y": -0.004470329136267992 + }, + { + "x": 9.004818378321886, + "y": -0.004023296222612771 + }, + { + "x": 11.005889129053514, + "y": -0.004917362049923213 + }, + { + "x": 12.006424504416202, + "y": -0.005364394963294217 + }, + { + "x": 11.00588912905323, + "y": -0.004917362049638996 + }, + { + "x": 11.005889129053514, + "y": -0.004917362049923213 + }, + { + "x": 10.005353753684858, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753684858, + "y": -0.004470329135983775 + }, + { + "x": 12.006424504421886, + "y": -0.005364394963578434 + }, + { + "x": 11.00588912905323, + "y": -0.004917362049923213 + }, + { + "x": 11.005889129053514, + "y": -0.004917362049638996 + }, + { + "x": 10.005353753685142, + "y": -0.004470329136267992 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 11.005889129053799, + "y": -0.004917362049638996 + }, + { + "x": 10.005353753685142, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753685142, + "y": -0.004470329136267992 + }, + { + "x": 11.005889129053799, + "y": -0.004917362049638996 + }, + { + "x": 10.005353753685142, + "y": -0.004470329136267992 + }, + { + "x": 10.005353753685142, + "y": -0.004470329136267992 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 9.004818378322454, + "y": -0.004023296222612771 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 9.004818378311086, + "y": -0.004023296222328554 + }, + { + "x": 8.004283002948114, + "y": -0.00357626330895755 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 8.004283002948114, + "y": -0.00357626330895755 + }, + { + "x": 9.004818378316486, + "y": -0.004023296222612771 + }, + { + "x": 9.004818378316486, + "y": -0.004023296222612771 + }, + { + "x": 9.004818378322454, + "y": -0.004023296222612771 + }, + { + "x": 10.005353753685426, + "y": -0.004470329136267992 + }, + { + "x": 11.005889129048114, + "y": -0.004917362049638996 + }, + { + "x": 11.005889129059483, + "y": -0.004917362049923213 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 10.005353753679458, + "y": -0.004470329135983775 + }, + { + "x": 11.005889129053799, + "y": -0.004917362049923213 + }, + { + "x": 10.005353753685142, + "y": -0.004470329136267992 + }, + { + "x": 9.00481837832217, + "y": -0.004023296222612771 + }, + { + "x": 8.004283002948114, + "y": -0.00357626330895755 + }, + { + "x": 9.004818378311086, + "y": -0.004023296222328554 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + }, + { + "x": 9.00481837831677, + "y": -0.004023296222612771 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 148.9445325959717, + "y": -133.43613885240512, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 88.3788047961567, + "y": -57.90372801630079, + "z": 1.0 + }, + { + "x": 88.37888114956165, + "y": -58.40601111552, + "z": 1.0 + }, + { + "x": 88.37894223228558, + "y": -58.80783759489587, + "z": 1.0 + }, + { + "x": 88.3790338563715, + "y": -59.410577313959266, + "z": 1.0 + }, + { + "x": 88.37909493909547, + "y": -59.81240379333458, + "z": 1.0 + }, + { + "x": 88.37917129250039, + "y": -60.31468689255435, + "z": 1.0 + }, + { + "x": 88.37924764590532, + "y": -60.816969991773846, + "z": 1.0 + }, + { + "x": 88.37932399931026, + "y": -61.31925309099334, + "z": 1.0 + }, + { + "x": 88.37941562339618, + "y": -61.92199281005672, + "z": 1.0 + }, + { + "x": 88.3794919768011, + "y": -62.42427590927622, + "z": 1.0 + }, + { + "x": 88.37955305952507, + "y": -62.82610238865182, + "z": 1.0 + }, + { + "x": 88.37964468361099, + "y": -63.4288421077152, + "z": 1.0 + }, + { + "x": 88.37972103701591, + "y": -63.931125206934695, + "z": 1.0 + }, + { + "x": 88.37979739042085, + "y": -64.4334083061542, + "z": 1.0 + }, + { + "x": 88.37988901450677, + "y": -65.03614802521759, + "z": 1.0 + }, + { + "x": 88.3799806385927, + "y": -65.63888774428096, + "z": 1.0 + }, + { + "x": 88.38004172131664, + "y": -66.0407142236563, + "z": 1.0 + }, + { + "x": 88.38013334540257, + "y": -66.64345394271993, + "z": 1.0 + }, + { + "x": 88.3802249694885, + "y": -67.24619366178331, + "z": 1.0 + }, + { + "x": 88.3803165935744, + "y": -67.84893338084669, + "z": 1.0 + }, + { + "x": 88.38036240561738, + "y": -68.15030324037838, + "z": 1.0 + }, + { + "x": 88.38043854453058, + "y": -68.6511753276148, + "z": 1.0 + }, + { + "x": 88.38052991122643, + "y": -69.25222183229796, + "z": 1.0 + }, + { + "x": 88.38062127792227, + "y": -69.85326833698171, + "z": 1.0 + }, + { + "x": 88.38068218905285, + "y": -70.2539660067704, + "z": 1.0 + }, + { + "x": 88.38077355574869, + "y": -70.85501251145385, + "z": 1.0 + }, + { + "x": 88.38083446687926, + "y": -71.25571018124283, + "z": 1.0 + }, + { + "x": 88.38091060579247, + "y": -71.75658226847905, + "z": 1.0 + }, + { + "x": 88.38097151692303, + "y": -72.15727993826802, + "z": 1.0 + }, + { + "x": 88.3810324280536, + "y": -72.55797760805699, + "z": 1.0 + }, + { + "x": 88.38112379474946, + "y": -73.15902411274045, + "z": 1.0 + }, + { + "x": 88.38118470588003, + "y": -73.55972178252942, + "z": 1.0 + }, + { + "x": 88.38126084479323, + "y": -74.06059386976564, + "z": 1.0 + }, + { + "x": 88.38133698370643, + "y": -74.56146595700213, + "z": 1.0 + }, + { + "x": 88.38139789483701, + "y": -74.96216362679083, + "z": 1.0 + }, + { + "x": 88.38145880596757, + "y": -75.3628612965798, + "z": 1.0 + }, + { + "x": 88.38155017266342, + "y": -75.96390780126325, + "z": 1.0 + }, + { + "x": 88.38161108379398, + "y": -76.36460547105223, + "z": 1.0 + }, + { + "x": 88.38170245048984, + "y": -76.96565197573568, + "z": 1.0 + }, + { + "x": 88.38179381718568, + "y": -77.56669848041915, + "z": 1.0 + }, + { + "x": 88.38185472831624, + "y": -77.96739615020812, + "z": 1.0 + }, + { + "x": 88.38193086722946, + "y": -78.46826823744433, + "z": 1.0 + }, + { + "x": 88.38200700614266, + "y": -78.96914032468055, + "z": 1.0 + }, + { + "x": 88.38209837283851, + "y": -79.570186829364, + "z": 1.0 + }, + { + "x": 88.38217451175171, + "y": -80.07105891660022, + "z": 1.0 + }, + { + "x": 88.38226587844757, + "y": -80.67210542128367, + "z": 1.0 + }, + { + "x": 88.38234201736077, + "y": -81.17297750851989, + "z": 1.0 + }, + { + "x": 88.38240292849133, + "y": -81.57367517830914, + "z": 1.0 + }, + { + "x": 88.3824638396219, + "y": -81.97437284809784, + "z": 1.0 + }, + { + "x": 88.38255520631775, + "y": -82.5754193527813, + "z": 1.0 + }, + { + "x": 88.38261611744832, + "y": -82.97611702257026, + "z": 1.0 + }, + { + "x": 88.38269225636154, + "y": -83.47698910980648, + "z": 1.0 + }, + { + "x": 88.38278362305739, + "y": -84.07803561448995, + "z": 1.0 + }, + { + "x": 88.38287498975323, + "y": -84.6790821191734, + "z": 1.0 + }, + { + "x": 88.3829359008838, + "y": -85.07977978896237, + "z": 1.0 + }, + { + "x": 88.38302726757965, + "y": -85.68082629364584, + "z": 1.0 + }, + { + "x": 88.38310340649285, + "y": -86.18169838088205, + "z": 1.0 + }, + { + "x": 88.3831947731887, + "y": -86.7827448855655, + "z": 1.0 + }, + { + "x": 88.38325568431927, + "y": -87.18344255535447, + "z": 1.0 + }, + { + "x": 88.38333182323248, + "y": -87.68431464259069, + "z": 1.0 + }, + { + "x": 88.38340796214568, + "y": -88.18518672982691, + "z": 1.0 + }, + { + "x": 88.38348410105888, + "y": -88.68605881706341, + "z": 1.0 + }, + { + "x": 88.38354501218946, + "y": -89.0867564868521, + "z": 1.0 + }, + { + "x": 88.3836363788853, + "y": -89.68780299153555, + "z": 1.0 + }, + { + "x": 88.38372774558115, + "y": -90.28884949621902, + "z": 1.0 + }, + { + "x": 88.383819112277, + "y": -90.88989600090247, + "z": 1.0 + }, + { + "x": 88.38388002340757, + "y": -91.29059367069145, + "z": 1.0 + }, + { + "x": 88.38395616232077, + "y": -91.79146575792767, + "z": 1.0 + }, + { + "x": 88.38401707345133, + "y": -92.19216342771692, + "z": 1.0 + }, + { + "x": 88.38409321236455, + "y": -92.69303551495285, + "z": 1.0 + }, + { + "x": 88.38416935127776, + "y": -93.19390760218907, + "z": 1.0 + }, + { + "x": 88.38426071797362, + "y": -93.79495410687252, + "z": 1.0 + }, + { + "x": 88.38433685688682, + "y": -94.29582619410874, + "z": 1.0 + }, + { + "x": 88.38442822358267, + "y": -94.8968726987922, + "z": 1.0 + }, + { + "x": 88.38450436249587, + "y": -95.39774478602841, + "z": 1.0 + }, + { + "x": 88.38456527362645, + "y": -95.79844245581738, + "z": 1.0 + }, + { + "x": 88.38464141253965, + "y": -96.2993145430536, + "z": 1.0 + }, + { + "x": 88.38473277923549, + "y": -96.90036104773735, + "z": 1.0 + }, + { + "x": 88.3848089181487, + "y": -97.40123313497327, + "z": 1.0 + }, + { + "x": 88.38490028484455, + "y": -98.00227963965673, + "z": 1.0 + }, + { + "x": 88.38496119597512, + "y": -98.4029773094457, + "z": 1.0 + }, + { + "x": 88.38505256267096, + "y": -99.00402381412917, + "z": 1.0 + }, + { + "x": 88.38514392936682, + "y": -99.60507031881262, + "z": 1.0 + }, + { + "x": 88.38520484049738, + "y": -100.00576798860159, + "z": 1.0 + }, + { + "x": 88.3852809794106, + "y": -100.50664007583781, + "z": 1.0 + }, + { + "x": 88.38537234610644, + "y": -101.10768658052127, + "z": 1.0 + }, + { + "x": 88.385433257237, + "y": -101.50838425031024, + "z": 1.0 + }, + { + "x": 88.38552462393285, + "y": -102.1094307549937, + "z": 1.0 + }, + { + "x": 88.3856159906287, + "y": -102.71047725967743, + "z": 1.0 + }, + { + "x": 88.38570735732456, + "y": -103.31152376436062, + "z": 1.0 + }, + { + "x": 88.38578349623776, + "y": -103.81239585159683, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 29, + "heading": [ + -1.5706443141063042, + -1.570644314106275, + -1.5706443141063064, + -1.5706443141063173, + -1.5706443141062891, + -1.5706443141063064, + -1.5706443141063173, + -1.5706443141063031, + -1.5706443141063007, + -1.5706443141063136, + -1.5706443141063064, + -1.5706443141062891, + -1.5706443141063136, + -1.5706443141063173, + -1.5706443141063007, + -1.5706443141062985, + -1.5706443141063031, + -1.5706443141063031, + -1.5706443141062985, + -1.5706443141063102, + -1.5706443141063064, + -1.5706443141063022, + -1.5706443141063058, + -1.570644314106307, + -1.5706443141063045, + -1.5706443141063045, + -1.5706443141063045, + -1.570644314106303, + -1.5706443141063187, + -1.570644314106301, + -1.5706443141062902, + -1.5706443141062902, + -1.570644314106303, + -1.5706443141063187, + -1.570644314106303, + -1.570644314106301, + -1.5706443141063045, + -1.5706443141063045, + -1.5706443141063045, + -1.570644314106307, + -1.5706443141063187, + -1.570644314106303, + -1.5706443141063045, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141063187, + -1.570644314106301, + -1.5706443141063045, + -1.5706443141063045, + -1.5706443141062871, + -1.570644314106293, + -1.570644314106307, + -1.5706443141063187, + -1.5706443141063045, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141063045, + -1.570644314106303, + -1.5706443141063045, + -1.5706443141063187, + -1.570644314106303, + -1.5706443141063045, + -1.570644314106307, + -1.570644314106307, + -1.5706443141063045, + -1.570644314106303, + -1.5706443141063187, + -1.570644314106303, + -1.5706443141062902, + -1.570644314106293, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141063058, + -1.570644314106303, + -1.570644314106303, + -1.5706443141063187, + -1.5706443141063058, + -1.5706443141063058, + -1.5706443141063045, + -1.5706443141063045, + -1.570644314106307, + -1.5706443141063045, + -1.570644314106303, + -1.5706443141063058, + -1.5706443141063187, + -1.5706443141063045, + -1.570644314106307, + -1.570644314106295, + -1.570644314106293 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.0007635340494971388, + "y": -5.022830992192127 + }, + { + "x": 0.0013743612888106327, + "y": -9.04109578595083 + }, + { + "x": 0.001527068098567952, + "y": -10.045661984392638 + }, + { + "x": 0.001527068098852169, + "y": -10.045661984387095 + }, + { + "x": 0.0013743612888106327, + "y": -9.04109578595083 + }, + { + "x": 0.001527068098567952, + "y": -10.045661984392638 + }, + { + "x": 0.0015270680987100604, + "y": -10.045661984389938 + }, + { + "x": 0.0016797749086094882, + "y": -11.05022818282876 + }, + { + "x": 0.0016797749084673796, + "y": -11.05022818282876 + }, + { + "x": 0.0013743612888106327, + "y": -9.041095785950972 + }, + { + "x": 0.001527068098852169, + "y": -10.045661984389795 + }, + { + "x": 0.0016797749084673796, + "y": -11.05022818282876 + }, + { + "x": 0.001527068098567952, + "y": -10.04566198439008 + }, + { + "x": 0.0016797749086094882, + "y": -11.050228182828903 + }, + { + "x": 0.001832481718508916, + "y": -12.054794381267584 + }, + { + "x": 0.0015270680987100604, + "y": -10.045661984387095 + }, + { + "x": 0.0015270680987100604, + "y": -10.045661984389653 + }, + { + "x": 0.001832481718508916, + "y": -12.054794381270142 + }, + { + "x": 0.0018324817183668074, + "y": -12.054794381267584 + }, + { + "x": 0.0013743612888106327, + "y": -9.041095785950688 + }, + { + "x": 0.0012195095617073548, + "y": -8.022419467681061 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919195849 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744721367 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0013705004377584373, + "y": -9.015697570252001 + }, + { + "x": 0.0013705004376163288, + "y": -9.01569757025186 + }, + { + "x": 0.001218222611356623, + "y": -8.013953395779367 + }, + { + "x": 0.0015227782643023602, + "y": -10.017441744724351 + }, + { + "x": 0.0015227782643023602, + "y": -10.017441744724351 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.001522778264018143, + "y": -10.017441744727051 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.001218222611356623, + "y": -8.013953395776667 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.001522778264018143, + "y": -10.017441744724351 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0013705004376163288, + "y": -9.015697570254702 + }, + { + "x": 0.001218222611356623, + "y": -8.01395339577951 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472151 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.0013705004379005459, + "y": -9.01569757025186 + }, + { + "x": 0.0016750560907041745, + "y": -11.019185919196843 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.001522778264018143, + "y": -10.01744174472421 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196843 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.001522778264018143, + "y": -10.017441744727194 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744721367 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0013705004377584373, + "y": -9.015697570252001 + }, + { + "x": 0.0013705004376163288, + "y": -9.015697570254702 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0015227782643023602, + "y": -10.01744174472151 + }, + { + "x": 0.0016750560907041745, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.0016750560904199574, + "y": -11.019185919199685 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196701 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919193859 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0015227782641602516, + "y": -10.017441744724351 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093669193 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.0013705004377584373, + "y": -9.01569757025186 + }, + { + "x": 0.001675056090562066, + "y": -11.019185919196843 + }, + { + "x": 0.001522778264018143, + "y": -10.017441744724351 + }, + { + "x": 0.0015227782641602516, + "y": -10.01744174472421 + }, + { + "x": 0.0018273339169638803, + "y": -12.020930093671893 + }, + { + "x": 0.0018273339171059888, + "y": -12.020930093669193 + }, + { + "x": 0.0016750560907041745, + "y": -11.019185919194001 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 88.38578349623776, + "y": -103.81239585159683, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 158.03439763122856, + "y": -37.48257278576148, + "z": 1.0 + }, + { + "x": 158.03488464725268, + "y": -37.08166140065703, + "z": 1.0 + }, + { + "x": 158.0354934172828, + "y": -36.580522169275206, + "z": 1.0 + }, + { + "x": 158.0361021873129, + "y": -36.07938293789394, + "z": 1.0 + }, + { + "x": 158.03658920333703, + "y": -35.678471552789496, + "z": 1.0 + }, + { + "x": 158.03719797336714, + "y": -35.17733232140766, + "z": 1.0 + }, + { + "x": 158.03768498939127, + "y": -34.77642093630322, + "z": 1.0 + }, + { + "x": 158.0381720054154, + "y": -34.37550955119821, + "z": 1.0 + }, + { + "x": 158.0386590214395, + "y": -33.9745981660932, + "z": 1.0 + }, + { + "x": 158.03914603746358, + "y": -33.57368678098819, + "z": 1.0 + }, + { + "x": 158.03987656149974, + "y": -32.97231970333011, + "z": 1.0 + }, + { + "x": 158.0406070855359, + "y": -32.370952625672594, + "z": 1.0 + }, + { + "x": 158.04121585556607, + "y": -31.869813394291327, + "z": 1.0 + }, + { + "x": 158.0417028715902, + "y": -31.468902009186884, + "z": 1.0 + }, + { + "x": 158.0424333956263, + "y": -30.8675349315288, + "z": 1.0 + }, + { + "x": 158.0431639196625, + "y": -30.266167853871853, + "z": 1.0 + }, + { + "x": 158.04377268969262, + "y": -29.76502862249002, + "z": 1.0 + }, + { + "x": 158.04438145972279, + "y": -29.263889391109323, + "z": 1.0 + }, + { + "x": 158.0449902297529, + "y": -28.762750159727496, + "z": 1.0 + }, + { + "x": 158.04547724577696, + "y": -28.361838774622484, + "z": 1.0 + }, + { + "x": 158.0459642618011, + "y": -27.96092738951804, + "z": 1.0 + }, + { + "x": 158.04669478583725, + "y": -27.35956031185996, + "z": 1.0 + }, + { + "x": 158.0474253098734, + "y": -26.758193234202437, + "z": 1.0 + }, + { + "x": 158.04803407990357, + "y": -26.257054002821164, + "z": 1.0 + }, + { + "x": 158.04864284993369, + "y": -25.75591477143989, + "z": 1.0 + }, + { + "x": 158.0492516199638, + "y": -25.254775540058617, + "z": 1.0 + }, + { + "x": 158.049982144, + "y": -24.653408462401657, + "z": 1.0 + }, + { + "x": 158.05059091403012, + "y": -24.152269231020387, + "z": 1.0 + }, + { + "x": 158.05132143806628, + "y": -23.55090215336286, + "z": 1.0 + }, + { + "x": 158.0518084540904, + "y": -23.149990768257275, + "z": 1.0 + }, + { + "x": 158.05253897812656, + "y": -22.548623690599747, + "z": 1.0 + }, + { + "x": 158.05326950216272, + "y": -21.94725661294222, + "z": 1.0 + }, + { + "x": 158.0537565181868, + "y": -21.5463452278372, + "z": 1.0 + }, + { + "x": 158.0542435342109, + "y": -21.14543384273219, + "z": 1.0 + }, + { + "x": 158.05497405824707, + "y": -20.54406676507466, + "z": 1.0 + }, + { + "x": 158.0554610742712, + "y": -20.14315537996964, + "z": 1.0 + }, + { + "x": 158.0560698443013, + "y": -19.642016148588368, + "z": 1.0 + }, + { + "x": 158.05680036833752, + "y": -19.04064907093141, + "z": 1.0 + }, + { + "x": 158.05753089237368, + "y": -18.439281993273312, + "z": 1.0 + }, + { + "x": 158.05801790839774, + "y": -18.0383706081683, + "z": 1.0 + }, + { + "x": 158.0586266784279, + "y": -17.537231376787027, + "z": 1.0 + }, + { + "x": 158.05923544845803, + "y": -17.036092145406318, + "z": 1.0 + }, + { + "x": 158.05972246448215, + "y": -16.635180760300734, + "z": 1.0 + }, + { + "x": 158.0604529885183, + "y": -16.033813682643206, + "z": 1.0 + }, + { + "x": 158.06094000454243, + "y": -15.632902297538756, + "z": 1.0 + }, + { + "x": 158.0614270205665, + "y": -15.231990912433172, + "z": 1.0 + }, + { + "x": 158.06203579059667, + "y": -14.730851681051899, + "z": 1.0 + }, + { + "x": 158.06276631463282, + "y": -14.129484603394374, + "z": 1.0 + }, + { + "x": 158.06337508466294, + "y": -13.628345372013104, + "z": 1.0 + }, + { + "x": 158.06386210068706, + "y": -13.227433986908089, + "z": 1.0 + }, + { + "x": 158.06459262472322, + "y": -12.626066909251133, + "z": 1.0 + }, + { + "x": 158.06532314875938, + "y": -12.024699831593606, + "z": 1.0 + }, + { + "x": 158.0658101647835, + "y": -11.62378844648859, + "z": 1.0 + }, + { + "x": 158.06629718080762, + "y": -11.222877061383572, + "z": 1.0 + }, + { + "x": 158.06654068881966, + "y": -11.022421368830496, + "z": 1.0 + }, + { + "x": 158.06702812465142, + "y": -10.621164398262831, + "z": 1.0 + }, + { + "x": 158.06775927839897, + "y": -10.01927894240989, + "z": 1.0 + }, + { + "x": 158.06824671423072, + "y": -9.618021971841639, + "z": 1.0 + }, + { + "x": 158.0688560090204, + "y": -9.11645075863133, + "z": 1.0 + }, + { + "x": 158.0693434448521, + "y": -8.715193788063083, + "z": 1.0 + }, + { + "x": 158.07007459859972, + "y": -8.113308332210707, + "z": 1.0 + }, + { + "x": 158.07056203443148, + "y": -7.712051361642459, + "z": 1.0 + }, + { + "x": 158.06648688551132, + "y": -7.1584986324351565, + "z": 1.0 + }, + { + "x": 158.02461504747117, + "y": -6.51829522095581, + "z": 1.0 + }, + { + "x": 157.91537219000054, + "y": -5.756265280126636, + "z": 1.0 + }, + { + "x": 157.80713622040798, + "y": -5.254505269284559, + "z": 1.0 + }, + { + "x": 157.59217082782527, + "y": -4.514683268550397, + "z": 1.0 + }, + { + "x": 157.3662201428967, + "y": -3.9142137585897396, + "z": 1.0 + }, + { + "x": 157.15553063700315, + "y": -3.4461523429930967, + "z": 1.0 + }, + { + "x": 156.79117762382486, + "y": -2.7680225145010073, + "z": 1.0 + }, + { + "x": 156.3711601304941, + "y": -2.1228895130661045, + "z": 1.0 + }, + { + "x": 156.0616732746556, + "y": -1.7133932466808826, + "z": 1.0 + }, + { + "x": 155.72972937947105, + "y": -1.3218789148370773, + "z": 1.0 + }, + { + "x": 155.3763707435337, + "y": -0.9495760774742261, + "z": 1.0 + }, + { + "x": 154.9083553058013, + "y": -0.5146331051925972, + "z": 1.0 + }, + { + "x": 154.42830485703843, + "y": -0.12187522067055712, + "z": 1.0 + }, + { + "x": 153.82225338896444, + "y": 0.31096146168839445, + "z": 1.0 + }, + { + "x": 153.18733284384513, + "y": 0.699261120180888, + "z": 1.0 + }, + { + "x": 152.74938521124585, + "y": 0.9325766110953226, + "z": 1.0 + }, + { + "x": 152.0729321872387, + "y": 1.242908208394323, + "z": 1.0 + }, + { + "x": 151.37610097632384, + "y": 1.5042882774523922, + "z": 1.0 + }, + { + "x": 150.9019679824224, + "y": 1.6506809930010826, + "z": 1.0 + }, + { + "x": 150.30040348196218, + "y": 1.8017426140988086, + "z": 1.0 + }, + { + "x": 149.5682777598086, + "y": 1.9355006328820132, + "z": 1.0 + }, + { + "x": 148.95215542328614, + "y": 2.0069078165924625, + "z": 1.0 + }, + { + "x": 148.20833740082458, + "y": 2.044065519194717, + "z": 1.0 + }, + { + "x": 147.66901799637068, + "y": 2.045241054680691, + "z": 1.0 + }, + { + "x": 147.26776073202618, + "y": 2.045283880573532, + "z": 1.0 + }, + { + "x": 146.6658748355094, + "y": 2.0453481194127936, + "z": 1.0 + }, + { + "x": 146.2646175711649, + "y": 2.045390945305635, + "z": 1.0 + }, + { + "x": 145.76304599073424, + "y": 2.0454444776716865, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 30, + "heading": [ + 1.85681491850764, + 1.5695815551454493, + 1.5695815551454981, + 1.5695815551455368, + 1.5695815551454975, + 1.5695815551454981, + 1.5695815551454981, + 1.5695815551454502, + 1.5695815551454508, + 1.569581555145522, + 1.5695815551455368, + 1.5695815551454988, + 1.5695815551454637, + 1.5695815551454344, + 1.569581555145536, + 1.5695815551454981, + 1.5695815551454637, + 1.5695815551454793, + 1.5695815551454793, + 1.5695815551455619, + 1.569581555145521, + 1.5695815551454793, + 1.5695815551454988, + 1.569581555145464, + 1.5695815551454793, + 1.569581555145536, + 1.5695815551454633, + 1.5695815551454633, + 1.5695815551455155, + 1.56958155514548, + 1.56958155514548, + 1.5695815551454981, + 1.569581555145536, + 1.569581555145522, + 1.5695815551454793, + 1.5695815551454793, + 1.5695815551454981, + 1.5695815551454633, + 1.569581555145451, + 1.5695815551455368, + 1.5695815551454981, + 1.5695815551454786, + 1.5695815551454981, + 1.56958155514548, + 1.5695815551454786, + 1.569581555145522, + 1.569581555145499, + 1.569581555145464, + 1.5695815551455155, + 1.5695815551454981, + 1.5695815551454786, + 1.5695815551454977, + 1.5695815551454793, + 1.569581555145451, + 1.5695815551454995, + 1.569581555145491, + 1.5695815551455252, + 1.5695815551455259, + 1.569581555145456, + 1.569581555145519, + 1.5695815551455252, + 1.5695815551454686, + 1.5745538254927947, + 1.6092667617277412, + 1.6781491685846572, + 1.7412118605974554, + 1.8254586180935752, + 1.8886150095974155, + 1.9587252808138194, + 2.0358018759865435, + 2.105871504850858, + 2.1759411589315487, + 2.246010784166259, + 2.3020664946610165, + 2.364869264322417, + 2.423872974200282, + 2.491630517482241, + 2.557044715117108, + 2.6164612050779623, + 2.687721824022901, + 2.74710565983511, + 2.8064895092109574, + 2.871811731469634, + 2.9311956033917057, + 2.990579471172711, + 3.0619309698677264, + 3.1117269203157245, + 3.14029731984635, + 3.1414859243253437, + 3.1414859243253432, + 3.1414859243253432 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": 0.0048701602412393186, + "y": 4.009113851044432 + }, + { + "x": 0.010957860542362141, + "y": 9.02050616486271 + }, + { + "x": 0.012175400602245645, + "y": 10.02278462763094 + }, + { + "x": 0.010957860542362141, + "y": 9.020506164857096 + }, + { + "x": 0.010957860542362141, + "y": 9.02050616486278 + }, + { + "x": 0.010957860542362141, + "y": 9.02050616486278 + }, + { + "x": 0.009740320482478637, + "y": 8.018227702094478 + }, + { + "x": 0.009740320482478637, + "y": 8.018227702100162 + }, + { + "x": 0.009740320481910203, + "y": 8.018227702100234 + }, + { + "x": 0.012175400602245645, + "y": 10.02278462763094 + }, + { + "x": 0.014610480723149522, + "y": 12.027341553155964 + }, + { + "x": 0.013392940663266018, + "y": 11.025063090387803 + }, + { + "x": 0.010957860542930575, + "y": 9.020506164857096 + }, + { + "x": 0.012175400602245645, + "y": 10.022784627625256 + }, + { + "x": 0.014610480723149522, + "y": 12.027341553150315 + }, + { + "x": 0.013392940663266018, + "y": 11.025063090387803 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627625292 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627625256 + }, + { + "x": 0.010957860541793707, + "y": 9.020506164868394 + }, + { + "x": 0.009740320481910203, + "y": 8.01822770209455 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627625256 + }, + { + "x": 0.014610480723149522, + "y": 12.027341553156035 + }, + { + "x": 0.013392940663266018, + "y": 11.025063090387945 + }, + { + "x": 0.01217540060281408, + "y": 10.02278462762547 + }, + { + "x": 0.012175400602245645, + "y": 10.02278462762547 + }, + { + "x": 0.013392940663266018, + "y": 11.025063090382332 + }, + { + "x": 0.013392940663266018, + "y": 11.025063090382297 + }, + { + "x": 0.013392940662697583, + "y": 11.025063090387981 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627631118 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627631118 + }, + { + "x": 0.014610480723149522, + "y": 12.027341553150563 + }, + { + "x": 0.012175400602245645, + "y": 10.02278462762547 + }, + { + "x": 0.009740320481910203, + "y": 8.018227702100305 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627625398 + }, + { + "x": 0.01217540060281408, + "y": 10.02278462762547 + }, + { + "x": 0.010957860542362141, + "y": 9.020506164862923 + }, + { + "x": 0.013392940663266018, + "y": 11.025063090382332 + }, + { + "x": 0.014610480723717956, + "y": 12.027341553150563 + }, + { + "x": 0.012175400602245645, + "y": 10.022784627631083 + }, + { + "x": 0.010957860542362141, + "y": 9.020506164862852 + }, + { + "x": 0.01217540060281408, + "y": 10.02278462761982 + }, + { + "x": 0.010957860542362141, + "y": 9.020506164862923 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627631118 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627619785 + }, + { + "x": 0.009740320481910203, + "y": 8.01822770210034 + }, + { + "x": 0.010957860542362141, + "y": 9.020506164868571 + }, + { + "x": 0.013392940663266018, + "y": 11.025063090387981 + }, + { + "x": 0.013392940662697583, + "y": 11.025063090387945 + }, + { + "x": 0.010957860542362141, + "y": 9.020506164862852 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627619714 + }, + { + "x": 0.014610480723149522, + "y": 12.027341553144826 + }, + { + "x": 0.01217540060281408, + "y": 10.022784627625434 + }, + { + "x": 0.009740320482478637, + "y": 8.01822770210034 + }, + { + "x": 0.007305240361574761, + "y": 6.0136707765809305 + }, + { + "x": 0.00730943843791465, + "y": 6.0171266312074145 + }, + { + "x": 0.01218589579309537, + "y": 10.03142426420606 + }, + { + "x": 0.01218589579309537, + "y": 10.031424264211921 + }, + { + "x": 0.01096730621441111, + "y": 9.028281837785599 + }, + { + "x": 0.010967306213842676, + "y": 9.028281837785563 + }, + { + "x": 0.01218589579309537, + "y": 10.031424264206237 + }, + { + "x": 0.012185895793663803, + "y": 10.031424264206237 + }, + { + "x": -0.035877130883932296, + "y": 9.548096997755504 + }, + { + "x": -0.4594698696030264, + "y": 11.937561406866486 + }, + { + "x": -1.5111469551078471, + "y": 14.022333523085209 + }, + { + "x": -2.1747882706318933, + "y": 12.637899516712512 + }, + { + "x": -3.232013621752685, + "y": 12.415820115762388 + }, + { + "x": -4.409160775112753, + "y": 13.402915106948194 + }, + { + "x": -4.366401908221178, + "y": 10.685309255573001 + }, + { + "x": -5.750425190718431, + "y": 11.461912440887323 + }, + { + "x": -7.843705065090489, + "y": 13.232628299269921 + }, + { + "x": -7.295043491692752, + "y": 10.546292678201247 + }, + { + "x": -6.414307510230515, + "y": 8.010105982290272 + }, + { + "x": -6.853025311218914, + "y": 7.638171692066564 + }, + { + "x": -8.213740736697446, + "y": 8.0724580964448 + }, + { + "x": -9.480658864952716, + "y": 8.27700856803669 + }, + { + "x": -10.86101916836867, + "y": 8.255945668809916 + }, + { + "x": -12.409720131933, + "y": 8.211363408514451 + }, + { + "x": -10.728681777185898, + "y": 6.2161514940692815 + }, + { + "x": -11.144006566064206, + "y": 5.4364708821343495 + }, + { + "x": -13.732842349220107, + "y": 5.717116663570696 + }, + { + "x": -11.709642048163005, + "y": 4.077727846067596 + }, + { + "x": -10.756974943616626, + "y": 2.974543366464164 + }, + { + "x": -13.336902226137965, + "y": 2.848196398809306 + }, + { + "x": -13.482480586760346, + "y": 2.051652024936539 + }, + { + "x": -13.599403589840335, + "y": 1.0856488631270356 + }, + { + "x": -12.831374269154594, + "y": 0.38333238088228416 + }, + { + "x": -9.405766687983999, + "y": 0.01218361378815036 + }, + { + "x": -10.031431608612706, + "y": 0.0010706473210264633 + }, + { + "x": -10.031431608612706, + "y": 0.0010706473210309042 + }, + { + "x": -9.02828844775172, + "y": 0.000963582588928702 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 145.76304599073424, + "y": 2.0454444776716865, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 334.8085442829706, + "y": -151.28327510229786, + "z": 1.0 + }, + { + "x": 334.80818118447087, + "y": -151.88442057736455, + "z": 1.0 + }, + { + "x": 334.80793911880437, + "y": -152.28518422740973, + "z": 1.0 + }, + { + "x": 334.80763653672125, + "y": -152.78613878996623, + "z": 1.0 + }, + { + "x": 334.8073339546381, + "y": -153.28709335252273, + "z": 1.0 + }, + { + "x": 334.807031372555, + "y": -153.78804791507923, + "z": 1.0 + }, + { + "x": 334.80666827405526, + "y": -154.389193390147, + "z": 1.0 + }, + { + "x": 334.80636569197213, + "y": -154.8901479527035, + "z": 1.0 + }, + { + "x": 334.80612362630563, + "y": -155.29091160274868, + "z": 1.0 + }, + { + "x": 334.80588156063914, + "y": -155.6916752527939, + "z": 1.0 + }, + { + "x": 334.805578978556, + "y": -156.19262981535041, + "z": 1.0 + }, + { + "x": 334.80521588005627, + "y": -156.79377529041818, + "z": 1.0 + }, + { + "x": 334.80491329797314, + "y": -157.29472985297468, + "z": 1.0 + }, + { + "x": 334.8045501994734, + "y": -157.89587532804245, + "z": 1.0 + }, + { + "x": 334.80418710097365, + "y": -158.49702080311027, + "z": 1.0 + }, + { + "x": 334.8038240024739, + "y": -159.09816627817918, + "z": 1.0 + }, + { + "x": 334.80346090397427, + "y": -159.69931175324473, + "z": 1.0 + }, + { + "x": 334.80315832189103, + "y": -160.2002663158035, + "z": 1.0 + }, + { + "x": 334.8028557398079, + "y": -160.70122087836, + "z": 1.0 + }, + { + "x": 334.8024926413083, + "y": -161.3023663534267, + "z": 1.0 + }, + { + "x": 334.80219005922504, + "y": -161.80332091598433, + "z": 1.0 + }, + { + "x": 334.80188747714203, + "y": -162.3042754785397, + "z": 1.0 + }, + { + "x": 334.80164541147553, + "y": -162.70503912858487, + "z": 1.0 + }, + { + "x": 334.8013428293924, + "y": -163.20599369114137, + "z": 1.0 + }, + { + "x": 334.8010402473093, + "y": -163.70694825369787, + "z": 1.0 + }, + { + "x": 334.8007981816428, + "y": -164.1077119037431, + "z": 1.0 + }, + { + "x": 334.80049559955967, + "y": -164.6086664662996, + "z": 1.0 + }, + { + "x": 334.80025353389317, + "y": -165.0094301163448, + "z": 1.0 + }, + { + "x": 334.80001146822667, + "y": -165.41019376639002, + "z": 1.0 + }, + { + "x": 334.79970888614355, + "y": -165.91114832894652, + "z": 1.0 + }, + { + "x": 334.7993457876438, + "y": -166.5122938040143, + "z": 1.0 + }, + { + "x": 334.79898268914405, + "y": -167.11343927908212, + "z": 1.0 + }, + { + "x": 334.79868010706093, + "y": -167.61439384163862, + "z": 1.0 + }, + { + "x": 334.7983170085612, + "y": -168.21553931670644, + "z": 1.0 + }, + { + "x": 334.7980749428947, + "y": -168.61630296675162, + "z": 1.0 + }, + { + "x": 334.79777236081156, + "y": -169.11725752930812, + "z": 1.0 + }, + { + "x": 334.7974092623118, + "y": -169.71840300437594, + "z": 1.0 + }, + { + "x": 334.79704616381207, + "y": -170.31954847944263, + "z": 1.0 + }, + { + "x": 334.79674358172895, + "y": -170.82050304199913, + "z": 1.0 + }, + { + "x": 334.79650151606245, + "y": -171.22126669204658, + "z": 1.0 + }, + { + "x": 334.7961384175627, + "y": -171.82241216711327, + "z": 1.0 + }, + { + "x": 334.7958358354796, + "y": -172.3233667296709, + "z": 1.0 + }, + { + "x": 334.79547273697983, + "y": -172.9245122047376, + "z": 1.0 + }, + { + "x": 334.7951701548967, + "y": -173.4254667672941, + "z": 1.0 + }, + { + "x": 334.7949280892302, + "y": -173.82623041733814, + "z": 1.0 + }, + { + "x": 334.79456499073046, + "y": -174.4273758924071, + "z": 1.0 + }, + { + "x": 334.79432292506397, + "y": -174.82813954245228, + "z": 1.0 + }, + { + "x": 334.7939598265642, + "y": -175.4292850175201, + "z": 1.0 + }, + { + "x": 334.7936572444811, + "y": -175.9302395800766, + "z": 1.0 + }, + { + "x": 334.793354662398, + "y": -176.4311941426331, + "z": 1.0 + }, + { + "x": 334.7931125967315, + "y": -176.83195779267828, + "z": 1.0 + }, + { + "x": 334.792870531065, + "y": -177.23272144272346, + "z": 1.0 + }, + { + "x": 334.79256794898185, + "y": -177.73367600527996, + "z": 1.0 + }, + { + "x": 334.79226536689873, + "y": -178.23463056783646, + "z": 1.0 + }, + { + "x": 334.7919627848156, + "y": -178.73558513039404, + "z": 1.0 + }, + { + "x": 334.7916602027325, + "y": -179.2365396929494, + "z": 1.0 + }, + { + "x": 334.7914181370661, + "y": -179.63730334299345, + "z": 1.0 + }, + { + "x": 334.7911760713995, + "y": -180.03806699303976, + "z": 1.0 + }, + { + "x": 334.7909340057331, + "y": -180.43883064308386, + "z": 1.0 + }, + { + "x": 334.79063142364987, + "y": -180.93978520564258, + "z": 1.0 + }, + { + "x": 334.7903893579834, + "y": -181.34054885568668, + "z": 1.0 + }, + { + "x": 334.7901472923169, + "y": -181.741312505733, + "z": 1.0 + }, + { + "x": 334.78978419381724, + "y": -182.34245798079849, + "z": 1.0 + }, + { + "x": 334.7894210953175, + "y": -182.94360345586625, + "z": 1.0 + }, + { + "x": 334.7891790296509, + "y": -183.34436710591376, + "z": 1.0 + }, + { + "x": 334.7889369639845, + "y": -183.74513075595667, + "z": 1.0 + }, + { + "x": 334.7886343819014, + "y": -184.2460853185143, + "z": 1.0 + }, + { + "x": 334.78839231623476, + "y": -184.64684896856062, + "z": 1.0 + }, + { + "x": 334.78808973415175, + "y": -185.14780353111593, + "z": 1.0 + }, + { + "x": 334.78778715206863, + "y": -185.64875809367243, + "z": 1.0 + }, + { + "x": 334.7875444119521, + "y": -186.050638362244, + "z": 1.0 + }, + { + "x": 334.78718030177754, + "y": -186.65345876510128, + "z": 1.0 + }, + { + "x": 334.78693756166115, + "y": -187.0553390336716, + "z": 1.0 + }, + { + "x": 334.78669482154476, + "y": -187.4572193022454, + "z": 1.0 + }, + { + "x": 334.78645208142837, + "y": -187.85909957081577, + "z": 1.0 + }, + { + "x": 334.7860879712538, + "y": -188.461919973673, + "z": 1.0 + }, + { + "x": 334.7858452311374, + "y": -188.86380024224337, + "z": 1.0 + }, + { + "x": 334.7856024910209, + "y": -189.2656805108171, + "z": 1.0 + }, + { + "x": 334.7852990658754, + "y": -189.7680308465292, + "z": 1.0 + }, + { + "x": 334.7849349557008, + "y": -190.37085124938875, + "z": 1.0 + }, + { + "x": 334.7845708455262, + "y": -190.97367165224483, + "z": 1.0 + }, + { + "x": 334.78420673535163, + "y": -191.57649205510205, + "z": 1.0 + }, + { + "x": 334.7839639952351, + "y": -191.97837232367357, + "z": 1.0 + }, + { + "x": 334.78372125511873, + "y": -192.38025259224509, + "z": 1.0 + }, + { + "x": 334.78335714494415, + "y": -192.98307299510117, + "z": 1.0 + }, + { + "x": 334.78311440482776, + "y": -193.38495326367382, + "z": 1.0 + }, + { + "x": 334.78275029465317, + "y": -193.98777366653104, + "z": 1.0 + }, + { + "x": 334.7824468695077, + "y": -194.4901240022454, + "z": 1.0 + }, + { + "x": 334.7822041293913, + "y": -194.89200427081693, + "z": 1.0 + }, + { + "x": 334.7819007042457, + "y": -195.3943546065313, + "z": 1.0 + }, + { + "x": 334.7816579641293, + "y": -195.79623487510275, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 31, + "heading": [ + -1.571400337755052, + -1.5714003377550565, + -1.571400337755056, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.571400337755055, + -1.5714003377549615, + -1.5714003377550554, + -1.5714003377551675, + -1.5714003377549528, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377549302, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.571400337755056, + -1.571400337755056, + -1.5714003377550538, + -1.5714003377550547, + -1.5714003377550554, + -1.5714003377550554, + -1.571400337755056, + -1.5714003377550563, + -1.5714003377550554, + -1.5714003377550547, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550547, + -1.5714003377550554, + -1.5714003377549308, + -1.5714003377550554, + -1.5714003377550554, + -1.5714003377550547, + -1.5714003377551808, + -1.5714003377550554, + -1.5714003377549426, + -1.5714003377549621, + -1.5714003377551675, + -1.5714003377550554, + -1.5714003377549302, + -1.57140033775518, + -1.5714003377550554, + -1.5714003377549426, + -1.5714003377551624, + -1.5714003377551262, + -1.5714003377550139, + -1.5714003377550123, + -1.5714003377550123, + -1.5714003377550139, + -1.5714003377550139, + -1.5714003377551538, + -1.571400337755139, + -1.5714003377550132, + -1.5714003377550128, + -1.5714003377550139, + -1.5714003377551264, + -1.5714003377551546, + -1.5714003377550139, + -1.5714003377550132, + -1.5714003377550125, + -1.5714003377550132, + -1.5714003377550132, + -1.5714003377551389, + -1.571400337755139 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -0.003630984997471387, + "y": -6.011454750666871 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251118653 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016792 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376242682 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376242682 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016792 + }, + { + "x": -0.004841313329961849, + "y": -8.01527300090413 + }, + { + "x": -0.0054464774962070805, + "y": -9.01718212601736 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376242682 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376242682 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376242682 + }, + { + "x": -0.007261969994942774, + "y": -12.022909501355912 + }, + { + "x": -0.007261969994942774, + "y": -12.02290950136728 + }, + { + "x": -0.007261969993805906, + "y": -12.022909501344543 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.00605164166358918, + "y": -10.019091251152759 + }, + { + "x": -0.0066568058275606745, + "y": -11.021000376231882 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.005446477495070212, + "y": -9.017182126005423 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016792 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.0054464774962070805, + "y": -9.01718212601736 + }, + { + "x": -0.0054464774962070805, + "y": -9.01718212601736 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016792 + }, + { + "x": -0.004841313329961849, + "y": -8.01527300090413 + }, + { + "x": -0.0054464774962070805, + "y": -9.01718212601736 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376242682 + }, + { + "x": -0.007261969994942774, + "y": -12.022909501355912 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016792 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.007261969994942774, + "y": -12.022909501345111 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376231882 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126039529 + }, + { + "x": -0.006051641662452312, + "y": -10.01909125114139 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.006656805828697543, + "y": -11.021000376231882 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126005423 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.006051641662452312, + "y": -10.01909125114139 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.006656805828697543, + "y": -11.02100037624325 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016792 + }, + { + "x": -0.004841313329961849, + "y": -8.015273000903562 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016792 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251130021 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251140821 + }, + { + "x": -0.006051641662452312, + "y": -10.019091251129453 + }, + { + "x": -0.005446477495070212, + "y": -9.017182125994054 + }, + { + "x": -0.004841313329961849, + "y": -8.015273000903562 + }, + { + "x": -0.004841313329961849, + "y": -8.01527300090413 + }, + { + "x": -0.0054464774962070805, + "y": -9.01718212602816 + }, + { + "x": -0.005446477497343949, + "y": -9.01718212602816 + }, + { + "x": -0.004841313329961849, + "y": -8.01527300090413 + }, + { + "x": -0.006051641661315443, + "y": -10.019091251118084 + }, + { + "x": -0.007261969993805906, + "y": -12.022909501332606 + }, + { + "x": -0.00605164166358918, + "y": -10.019091251152759 + }, + { + "x": -0.004841313329961849, + "y": -8.01527300090413 + }, + { + "x": -0.005446477495070212, + "y": -9.017182126005423 + }, + { + "x": -0.005446477497343949, + "y": -9.017182126039529 + }, + { + "x": -0.0054464774962070805, + "y": -9.017182126016223 + }, + { + "x": -0.006051641661315443, + "y": -10.019091251118084 + }, + { + "x": -0.005453221996276625, + "y": -9.028348311280752 + }, + { + "x": -0.00606850291092087, + "y": -10.0470067142885 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275994 + }, + { + "x": -0.004854802327827201, + "y": -8.037605371441146 + }, + { + "x": -0.004854802327827201, + "y": -8.037605371441714 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275994 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275994 + }, + { + "x": -0.00485480232896407, + "y": -8.037605371441146 + }, + { + "x": -0.00546165261994247, + "y": -9.042306042858286 + }, + { + "x": -0.006675353200762402, + "y": -11.05170738571644 + }, + { + "x": -0.007282203491740802, + "y": -12.056408057156318 + }, + { + "x": -0.007282203491740802, + "y": -12.056408057133012 + }, + { + "x": -0.00606850291092087, + "y": -10.047006714287363 + }, + { + "x": -0.00485480232896407, + "y": -8.037605371430345 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714275994 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714287363 + }, + { + "x": -0.0060685029097840015, + "y": -10.047006714298732 + }, + { + "x": -0.006675353200762402, + "y": -11.051707385715872 + }, + { + "x": -0.005461652618805601, + "y": -9.042306042858854 + }, + { + "x": -0.00546165261994247, + "y": -9.042306042858854 + }, + { + "x": -0.00546165261994247, + "y": -9.042306042858286 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 334.7816579641293, + "y": -195.79623487510275, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + }, + { + "position": [ + { + "x": 333.4145457505004, + "y": -126.94869021368251, + "z": 1.0 + }, + { + "x": 333.11065915516724, + "y": -127.28159127629539, + "z": 1.0 + }, + { + "x": 332.8388202990402, + "y": -127.5408461529031, + "z": 1.0 + }, + { + "x": 332.4740998353168, + "y": -127.84544927046008, + "z": 1.0 + }, + { + "x": 332.21851234968426, + "y": -128.0375613087877, + "z": 1.0 + }, + { + "x": 331.8883716074788, + "y": -128.26280080705058, + "z": 1.0 + }, + { + "x": 331.5471902743561, + "y": -128.47094126593532, + "z": 1.0 + }, + { + "x": 331.2668891448755, + "y": -128.62477820697123, + "z": 1.0 + }, + { + "x": 330.90852362645415, + "y": -128.8004960130125, + "z": 1.0 + }, + { + "x": 330.5411803754363, + "y": -128.95793338659394, + "z": 1.0 + }, + { + "x": 330.2418326926179, + "y": -129.07028837051308, + "z": 1.0 + }, + { + "x": 329.86162131658267, + "y": -129.19343286851142, + "z": 1.0 + }, + { + "x": 329.39783113296505, + "y": -129.31541954743648, + "z": 1.0 + }, + { + "x": 329.0062340233228, + "y": -129.39527707047642, + "z": 1.0 + }, + { + "x": 328.69034455550616, + "y": -129.44474248388758, + "z": 1.0 + }, + { + "x": 328.2930775110379, + "y": -129.4884137314665, + "z": 1.0 + }, + { + "x": 327.89463092744916, + "y": -129.51171443296764, + "z": 1.0 + }, + { + "x": 327.4468638830473, + "y": -129.51589217222232, + "z": 1.0 + }, + { + "x": 326.8449815717829, + "y": -129.51562325499071, + "z": 1.0 + }, + { + "x": 326.4437266976067, + "y": -129.5154439768363, + "z": 1.0 + }, + { + "x": 325.84184438634173, + "y": -129.5151750596047, + "z": 1.0 + }, + { + "x": 325.6412169492547, + "y": -129.5150854205275, + "z": 1.0 + }, + { + "x": 325.24065291057445, + "y": -129.51490645103408, + "z": 1.0 + }, + { + "x": 324.64033168535275, + "y": -129.51463823128591, + "z": 1.0 + }, + { + "x": 324.0400104601322, + "y": -129.51437001153772, + "z": 1.0 + }, + { + "x": 323.5397427724479, + "y": -129.51414649508092, + "z": 1.0 + }, + { + "x": 322.93942154722663, + "y": -129.51387827533276, + "z": 1.0 + }, + { + "x": 322.3391003220055, + "y": -129.51361005558456, + "z": 1.0 + }, + { + "x": 321.93888617185803, + "y": -129.51343124241913, + "z": 1.0 + }, + { + "x": 321.5386720217107, + "y": -129.51325242925367, + "z": 1.0 + }, + { + "x": 321.1384578715632, + "y": -129.51307361608823, + "z": 1.0 + }, + { + "x": 320.5381366463421, + "y": -129.51280539634007, + "z": 1.0 + }, + { + "x": 319.93781542112094, + "y": -129.51253717659188, + "z": 1.0 + }, + { + "x": 319.43754773343665, + "y": -129.51231366013508, + "z": 1.0 + }, + { + "x": 318.93728004575235, + "y": -129.51209014367828, + "z": 1.0 + }, + { + "x": 318.43701235806805, + "y": -129.51186662722145, + "z": 1.0 + }, + { + "x": 318.0367982079206, + "y": -129.51168781405602, + "z": 1.0 + }, + { + "x": 317.6365840577731, + "y": -129.51150900089056, + "z": 1.0 + }, + { + "x": 317.23636990762566, + "y": -129.51133018772512, + "z": 1.0 + }, + { + "x": 316.73610221994136, + "y": -129.5111066712683, + "z": 1.0 + }, + { + "x": 316.1357809947202, + "y": -129.51083845152013, + "z": 1.0 + }, + { + "x": 315.5354597694991, + "y": -129.51057023177196, + "z": 1.0 + }, + { + "x": 315.1352456193516, + "y": -129.5103914186065, + "z": 1.0 + }, + { + "x": 314.5349243941305, + "y": -129.51012319885834, + "z": 1.0 + }, + { + "x": 313.93460316890935, + "y": -129.50985497911017, + "z": 1.0 + }, + { + "x": 313.5343890187619, + "y": -129.5096761659447, + "z": 1.0 + }, + { + "x": 313.0341213310776, + "y": -129.5094526494879, + "z": 1.0 + }, + { + "x": 312.5338536433933, + "y": -129.5092291330311, + "z": 1.0 + }, + { + "x": 312.033585955709, + "y": -129.50900561657429, + "z": 1.0 + }, + { + "x": 311.6333718055615, + "y": -129.50882680340885, + "z": 1.0 + }, + { + "x": 311.0330505803404, + "y": -129.5085585836607, + "z": 1.0 + }, + { + "x": 310.43272935511925, + "y": -129.5082903639125, + "z": 1.0 + }, + { + "x": 309.8324081298981, + "y": -129.50802214416433, + "z": 1.0 + }, + { + "x": 309.43219397975065, + "y": -129.5078433309989, + "z": 1.0 + }, + { + "x": 308.93192629206635, + "y": -129.50761981454207, + "z": 1.0 + }, + { + "x": 308.5317121419189, + "y": -129.50744100137663, + "z": 1.0 + }, + { + "x": 308.13149799177154, + "y": -129.50726218821117, + "z": 1.0 + }, + { + "x": 307.7312838416241, + "y": -129.50708337504574, + "z": 1.0 + }, + { + "x": 307.13096261640237, + "y": -129.50681515529757, + "z": 1.0 + }, + { + "x": 306.5306413911818, + "y": -129.50654693554938, + "z": 1.0 + }, + { + "x": 305.93032016596055, + "y": -129.50627871580122, + "z": 1.0 + }, + { + "x": 305.43005247827625, + "y": -129.50605519934442, + "z": 1.0 + }, + { + "x": 304.8297312530551, + "y": -129.50578697959622, + "z": 1.0 + }, + { + "x": 304.229410027834, + "y": -129.50551875984806, + "z": 1.0 + }, + { + "x": 303.7291423401497, + "y": -129.50529524339126, + "z": 1.0 + }, + { + "x": 303.3289281900022, + "y": -129.5051164302258, + "z": 1.0 + }, + { + "x": 302.8286605023179, + "y": -129.504892913769, + "z": 1.0 + }, + { + "x": 302.2283392770968, + "y": -129.50462469402083, + "z": 1.0 + }, + { + "x": 301.7280715894125, + "y": -129.504401177564, + "z": 1.0 + }, + { + "x": 301.327857439265, + "y": -129.50422236439857, + "z": 1.0 + }, + { + "x": 300.7275362140433, + "y": -129.5039541446504, + "z": 1.0 + }, + { + "x": 300.3273220638964, + "y": -129.50377533148495, + "z": 1.0 + }, + { + "x": 299.8270543762121, + "y": -129.50355181502815, + "z": 1.0 + }, + { + "x": 299.3267866885278, + "y": -129.50332829857132, + "z": 1.0 + }, + { + "x": 298.92657253838047, + "y": -129.50314948540588, + "z": 1.0 + }, + { + "x": 298.32625131315876, + "y": -129.50288126565772, + "z": 1.0 + }, + { + "x": 297.82598362547503, + "y": -129.5026577492009, + "z": 1.0 + }, + { + "x": 297.2256624002538, + "y": -129.50238952945273, + "z": 1.0 + }, + { + "x": 296.7253947125689, + "y": -129.50216601299593, + "z": 1.0 + }, + { + "x": 296.12507348734835, + "y": -129.50189779324774, + "z": 1.0 + }, + { + "x": 295.7248593372009, + "y": -129.5017189800823, + "z": 1.0 + }, + { + "x": 295.1245381119792, + "y": -129.50145076033414, + "z": 1.0 + }, + { + "x": 294.7243239618324, + "y": -129.50127194716868, + "z": 1.0 + }, + { + "x": 294.32410981168437, + "y": -129.50109313400324, + "z": 1.0 + }, + { + "x": 293.82384212400063, + "y": -129.5008696175464, + "z": 1.0 + }, + { + "x": 293.4236279738532, + "y": -129.50069080438098, + "z": 1.0 + }, + { + "x": 292.9233602861689, + "y": -129.50046728792415, + "z": 1.0 + }, + { + "x": 292.32303906094774, + "y": -129.500199068176, + "z": 1.0 + }, + { + "x": 291.9228249108003, + "y": -129.50002025501055, + "z": 1.0 + }, + { + "x": 291.32250368557914, + "y": -129.49975203526236, + "z": 1.0 + }, + { + "x": 290.9222895354317, + "y": -129.49957322209693, + "z": 1.0 + } + ], + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": 32, + "heading": [ + 1.9659351147996624, + -2.3106621792270796, + -2.342126791184687, + -2.4166839819179406, + -2.4663910593611247, + -2.5225037539646777, + -2.5683436896219303, + -2.614183605833329, + -2.6652237205605016, + -2.7112171490512793, + -2.757063638376234, + -2.8079968773903605, + -2.858930107778461, + -2.9098633595080114, + -2.960796591246271, + -3.011729844024768, + -3.057624659728072, + -3.1091318330827113, + -3.1378687395670917, + 3.141145859908236, + 3.141145859908236, + 3.1411458599082356, + 3.1411458599082875, + 3.141145859908284, + 3.14114585990824, + 3.141145859908242, + 3.141145859908268, + 3.14114585990824, + 3.1411458599082445, + 3.1411458599082516, + 3.1411458599082516, + 3.141145859908273, + 3.14114585990824, + 3.1411458599082422, + 3.141145859908273, + 3.1411458599082445, + 3.141145859908248, + 3.1411458599082516, + 3.1411458599082516, + 3.141145859908248, + 3.1411458599082422, + 3.1411458599082636, + 3.1411458599082445, + 3.1411458599082445, + 3.1411458599082636, + 3.1411458599082445, + 3.141145859908248, + 3.141145859908273, + 3.1411458599082445, + 3.141145859908248, + 3.141145859908273, + 3.14114585990824, + 3.14114585990824, + 3.141145859908273, + 3.141145859908248, + 3.141145859908248, + 3.1411458599082516, + 3.1411458599082516, + 3.1411458599082733, + 3.14114585990824, + 3.1411458599082396, + 3.141145859908268, + 3.1411458599082422, + 3.14114585990824, + 3.141145859908268, + 3.141145859908248, + 3.141145859908248, + 3.141145859908268, + 3.1411458599082422, + 3.141145859908248, + 3.1411458599082733, + 3.1411458599082445, + 3.1411458599082476, + 3.1411458599082445, + 3.1411458599082476, + 3.1411458599082733, + 3.1411458599082422, + 3.141145859908242, + 3.141145859908268, + 3.1411458599082422, + 3.1411458599082445, + 3.1411458599082733, + 3.1411458599082445, + 3.1411458599082516, + 3.141145859908248, + 3.1411458599082476, + 3.141145859908248, + 3.1411458599082422, + 3.141145859908273, + 3.1411458599082445, + 3.1411458599082445 + ], + "velocity": [ + { + "x": 0.0, + "y": 0.0 + }, + { + "x": -3.0388659533315376, + "y": -3.3290106261287633 + }, + { + "x": -5.757254514601868, + "y": -5.921559392205893 + }, + { + "x": -6.3655931985044845, + "y": -5.638579941646924 + }, + { + "x": -6.203079493559471, + "y": -4.9671515588460124 + }, + { + "x": -5.857282278379898, + "y": -4.173515365905018 + }, + { + "x": -6.713220753281348, + "y": -4.33379957147622 + }, + { + "x": -6.214824626032964, + "y": -3.6197739992064726 + }, + { + "x": -6.386666479019709, + "y": -3.2955474707716803 + }, + { + "x": -7.257087694392226, + "y": -3.3315517962270746 + }, + { + "x": -6.666909338362643, + "y": -2.697923575005916 + }, + { + "x": -6.795590588536129, + "y": -2.3549948191748626 + }, + { + "x": -8.440015596528383, + "y": -2.4513117692339392 + }, + { + "x": -8.553872932598665, + "y": -2.018442019650024 + }, + { + "x": -7.074865774588943, + "y": -1.2932293645110349 + }, + { + "x": -7.131565122849111, + "y": -0.9313666099006923 + }, + { + "x": -7.957136280570012, + "y": -0.6697194908005599 + }, + { + "x": -8.462136279906076, + "y": -0.27478440755828615 + }, + { + "x": -10.496493556662472, + "y": -0.039088220230780735 + }, + { + "x": -10.031371854406075, + "y": 0.004481953860135945 + }, + { + "x": -10.03137185441176, + "y": 0.004481953860135945 + }, + { + "x": -8.025097483519517, + "y": 0.0035855630881087563 + }, + { + "x": -6.011914757672798, + "y": 0.0026860857062160903 + }, + { + "x": -10.008852639019779, + "y": 0.00447189241583601 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963578434 + }, + { + "x": -11.005889129048683, + "y": 0.004917362049923213 + }, + { + "x": -11.005889129055504, + "y": 0.004917362049638996 + }, + { + "x": -12.006424504423876, + "y": 0.005364394963578434 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -8.004283002948114, + "y": 0.00357626330895755 + }, + { + "x": -8.004283002948114, + "y": 0.00357626330895755 + }, + { + "x": -10.005353753685995, + "y": 0.004470329135983775 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963578434 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049923213 + }, + { + "x": -10.005353753685995, + "y": 0.004470329135983775 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -8.00428300294925, + "y": 0.00357626330895755 + }, + { + "x": -8.00428300294925, + "y": 0.00357626330895755 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049923213 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963294217 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963294217 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -10.005353753685995, + "y": 0.004470329135983775 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -10.005353753685995, + "y": 0.004470329135983775 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963578434 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963578434 + }, + { + "x": -10.005353753685995, + "y": 0.004470329135983775 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -8.004283002948114, + "y": 0.00357626330895755 + }, + { + "x": -8.004283002948114, + "y": 0.00357626330895755 + }, + { + "x": -10.00535375369168, + "y": 0.004470329135983775 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963578434 + }, + { + "x": -12.006424504418192, + "y": 0.005364394963578434 + }, + { + "x": -11.005889129055504, + "y": 0.004917362049638996 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049923213 + }, + { + "x": -12.00642450442274, + "y": 0.005364394963578434 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049638996 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049638996 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049923213 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -10.00535375369168, + "y": 0.004470329135983775 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -9.004818378311938, + "y": 0.004023296222612771 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -9.004818378316486, + "y": 0.004023296222612771 + }, + { + "x": -10.005353753690542, + "y": 0.004470329135983775 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049923213 + }, + { + "x": -11.00588912904982, + "y": 0.004917362049923213 + }, + { + "x": -11.005889129061188, + "y": 0.004917362049638996 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049923213 + }, + { + "x": -10.00535375368031, + "y": 0.004470329136267992 + }, + { + "x": -10.00535375369168, + "y": 0.004470329135983775 + }, + { + "x": -10.005353753684858, + "y": 0.004470329136267992 + }, + { + "x": -8.004283002948114, + "y": 0.00357626330895755 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -9.004818378311938, + "y": 0.004023296222612771 + }, + { + "x": -9.004818378317623, + "y": 0.004023296222612771 + }, + { + "x": -11.005889129054367, + "y": 0.004917362049923213 + }, + { + "x": -10.005353753685995, + "y": 0.004470329135983775 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + }, + { + "x": -10.005353753685995, + "y": 0.004470329136267992 + } + ], + "valid": [ + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "goalPosition": { + "x": 290.9222895354317, + "y": -129.49957322209693, + "z": 1.0 + }, + "type": "vehicle", + "mark_as_expert": false + } + ], + "roads": [ + { + "id": 0, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 348.227876521873, + "y": -4.0006836701775725, + "z": 0.0 + }, + { + "x": 349.26673351814094, + "y": -4.001235549176161, + "z": 0.0 + }, + { + "x": 350.3055905144089, + "y": -4.001787428174748, + "z": 0.0 + }, + { + "x": 351.34444751067684, + "y": -4.002339307173337, + "z": 0.0 + }, + { + "x": 352.3833045069448, + "y": -4.002891186171924, + "z": 0.0 + }, + { + "x": 353.42216150321275, + "y": -4.0034430651705115, + "z": 0.0 + }, + { + "x": 354.4610184994807, + "y": -4.0039949441691, + "z": 0.0 + }, + { + "x": 355.49987549574865, + "y": -4.004546823167687, + "z": 0.0 + }, + { + "x": 356.5387324920166, + "y": -4.005098702166276, + "z": 0.0 + }, + { + "x": 357.57758948828456, + "y": -4.005650581164863, + "z": 0.0 + }, + { + "x": 358.6164464845525, + "y": -4.006202460163451, + "z": 0.0 + }, + { + "x": 359.65530348082046, + "y": -4.006754339162039, + "z": 0.0 + }, + { + "x": 360.6941604770884, + "y": -4.007306218160627, + "z": 0.0 + }, + { + "x": 361.7330174733564, + "y": -4.007858097159215, + "z": 0.0 + }, + { + "x": 362.7718744696244, + "y": -4.008409976157803, + "z": 0.0 + }, + { + "x": 363.81073146589233, + "y": -4.00896185515639, + "z": 0.0 + }, + { + "x": 364.8495884621603, + "y": -4.009513734154979, + "z": 0.0 + }, + { + "x": 365.88844545842824, + "y": -4.010065613153566, + "z": 0.0 + }, + { + "x": 366.9273024546962, + "y": -4.0106174921521545, + "z": 0.0 + }, + { + "x": 367.96615945096414, + "y": -4.011169371150742, + "z": 0.0 + }, + { + "x": 369.0050164472321, + "y": -4.011721250149329, + "z": 0.0 + }, + { + "x": 370.04387344350005, + "y": -4.012273129147918, + "z": 0.0 + }, + { + "x": 371.082730439768, + "y": -4.012825008146505, + "z": 0.0 + }, + { + "x": 372.12158743603595, + "y": -4.013376887145093, + "z": 0.0 + }, + { + "x": 373.1604444323039, + "y": -4.013928766143681, + "z": 0.0 + }, + { + "x": 374.19930142857186, + "y": -4.014480645142269, + "z": 0.0 + }, + { + "x": 375.2381584248398, + "y": -4.015032524140857, + "z": 0.0 + }, + { + "x": 376.27701542110776, + "y": -4.015584403139445, + "z": 0.0 + }, + { + "x": 377.3158724173757, + "y": -4.016136282138032, + "z": 0.0 + }, + { + "x": 378.35472941364367, + "y": -4.016688161136621, + "z": 0.0 + }, + { + "x": 379.3935864099116, + "y": -4.017240040135208, + "z": 0.0 + }, + { + "x": 380.4324434061796, + "y": -4.0177919191337965, + "z": 0.0 + }, + { + "x": 381.4713004024475, + "y": -4.018343798132384, + "z": 0.0 + }, + { + "x": 382.5101573987155, + "y": -4.018895677130972, + "z": 0.0 + }, + { + "x": 383.54901439498343, + "y": -4.01944755612956, + "z": 0.0 + }, + { + "x": 384.5878713912514, + "y": -4.019999435128147, + "z": 0.0 + } + ] + }, + { + "id": 1, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 384.59212128452987, + "y": 3.9799994360222173, + "z": 0.0 + }, + { + "x": 383.5532642882619, + "y": 3.980551315020805, + "z": 0.0 + }, + { + "x": 382.51440729199396, + "y": 3.981103194019393, + "z": 0.0 + }, + { + "x": 381.475550295726, + "y": 3.981655073017981, + "z": 0.0 + }, + { + "x": 380.43669329945806, + "y": 3.982206952016569, + "z": 0.0 + }, + { + "x": 379.3978363031901, + "y": 3.9827588310151567, + "z": 0.0 + }, + { + "x": 378.35897930692215, + "y": 3.9833107100137446, + "z": 0.0 + }, + { + "x": 377.3201223106542, + "y": 3.9838625890123325, + "z": 0.0 + }, + { + "x": 376.28126531438625, + "y": 3.9844144680109204, + "z": 0.0 + }, + { + "x": 375.2424083181183, + "y": 3.984966347009508, + "z": 0.0 + }, + { + "x": 374.20355132185034, + "y": 3.9855182260080957, + "z": 0.0 + }, + { + "x": 373.1646943255824, + "y": 3.9860701050066836, + "z": 0.0 + }, + { + "x": 372.12583732931444, + "y": 3.9866219840052715, + "z": 0.0 + }, + { + "x": 371.0869803330465, + "y": 3.9871738630038593, + "z": 0.0 + }, + { + "x": 370.04812333677853, + "y": 3.9877257420024472, + "z": 0.0 + }, + { + "x": 369.0092663405106, + "y": 3.988277621001035, + "z": 0.0 + }, + { + "x": 367.9704093442426, + "y": 3.988829499999623, + "z": 0.0 + }, + { + "x": 366.9315523479747, + "y": 3.989381378998211, + "z": 0.0 + }, + { + "x": 365.8926953517067, + "y": 3.9899332579967988, + "z": 0.0 + }, + { + "x": 364.85383835543877, + "y": 3.9904851369953867, + "z": 0.0 + }, + { + "x": 363.8149813591708, + "y": 3.9910370159939745, + "z": 0.0 + }, + { + "x": 362.77612436290286, + "y": 3.9915888949925624, + "z": 0.0 + }, + { + "x": 361.7372673666349, + "y": 3.9921407739911503, + "z": 0.0 + }, + { + "x": 360.6984103703669, + "y": 3.992692652989738, + "z": 0.0 + }, + { + "x": 359.65955337409895, + "y": 3.9932445319883256, + "z": 0.0 + }, + { + "x": 358.620696377831, + "y": 3.9937964109869135, + "z": 0.0 + }, + { + "x": 357.58183938156304, + "y": 3.9943482899855014, + "z": 0.0 + }, + { + "x": 356.5429823852951, + "y": 3.9949001689840893, + "z": 0.0 + }, + { + "x": 355.50412538902714, + "y": 3.995452047982677, + "z": 0.0 + }, + { + "x": 354.4652683927592, + "y": 3.996003926981265, + "z": 0.0 + }, + { + "x": 353.42641139649123, + "y": 3.996555805979853, + "z": 0.0 + }, + { + "x": 352.3875544002233, + "y": 3.997107684978441, + "z": 0.0 + }, + { + "x": 351.3486974039553, + "y": 3.9976595639770287, + "z": 0.0 + }, + { + "x": 350.3098404076874, + "y": 3.9982114429756166, + "z": 0.0 + }, + { + "x": 349.2709834114194, + "y": 3.9987633219742045, + "z": 0.0 + }, + { + "x": 348.23212641515147, + "y": 3.9993152009727924, + "z": 0.0 + } + ] + }, + { + "id": 2, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 384.5899963378906, + "y": -0.019999999552965164, + "z": 0.0 + }, + { + "x": 383.5511393416227, + "y": -0.019448120554377307, + "z": 0.0 + }, + { + "x": 382.5122823453547, + "y": -0.01889624155578945, + "z": 0.0 + }, + { + "x": 381.47342534908677, + "y": -0.018344362557201593, + "z": 0.0 + }, + { + "x": 380.4345683528188, + "y": -0.017792483558613733, + "z": 0.0 + }, + { + "x": 379.39571135655086, + "y": -0.017240604560025876, + "z": 0.0 + }, + { + "x": 378.3568543602829, + "y": -0.01668872556143802, + "z": 0.0 + }, + { + "x": 377.31799736401496, + "y": -0.016136846562850162, + "z": 0.0 + }, + { + "x": 376.279140367747, + "y": -0.015584967564262307, + "z": 0.0 + }, + { + "x": 375.24028337147905, + "y": -0.01503308856567445, + "z": 0.0 + }, + { + "x": 374.2014263752111, + "y": -0.014481209567086591, + "z": 0.0 + }, + { + "x": 373.16256937894315, + "y": -0.013929330568498734, + "z": 0.0 + }, + { + "x": 372.1237123826752, + "y": -0.013377451569910877, + "z": 0.0 + }, + { + "x": 371.08485538640724, + "y": -0.01282557257132305, + "z": 0.0 + }, + { + "x": 370.0459983901393, + "y": -0.012273693572735195, + "z": 0.0 + }, + { + "x": 369.00714139387134, + "y": -0.011721814574147336, + "z": 0.0 + }, + { + "x": 367.9682843976034, + "y": -0.01116993557555948, + "z": 0.0 + }, + { + "x": 366.92942740133543, + "y": -0.01061805657697162, + "z": 0.0 + }, + { + "x": 365.8905704050675, + "y": -0.010066177578383764, + "z": 0.0 + }, + { + "x": 364.8517134087995, + "y": -0.009514298579795905, + "z": 0.0 + }, + { + "x": 363.8128564125316, + "y": -0.008962419581208046, + "z": 0.0 + }, + { + "x": 362.7739994162636, + "y": -0.00841054058262019, + "z": 0.0 + }, + { + "x": 361.73514241999567, + "y": -0.00785866158403233, + "z": 0.0 + }, + { + "x": 360.69628542372766, + "y": -0.007306782585444471, + "z": 0.0 + }, + { + "x": 359.6574284274597, + "y": -0.006754903586856614, + "z": 0.0 + }, + { + "x": 358.61857143119175, + "y": -0.0062030245882687554, + "z": 0.0 + }, + { + "x": 357.5797144349238, + "y": -0.005651145589680897, + "z": 0.0 + }, + { + "x": 356.54085743865585, + "y": -0.00509926659109304, + "z": 0.0 + }, + { + "x": 355.5020004423879, + "y": -0.004547387592505183, + "z": 0.0 + }, + { + "x": 354.46314344611994, + "y": -0.003995508593917322, + "z": 0.0 + }, + { + "x": 353.424286449852, + "y": -0.0034436295953294634, + "z": 0.0 + }, + { + "x": 352.38542945358404, + "y": -0.0028917505967416064, + "z": 0.0 + }, + { + "x": 351.3465724573161, + "y": -0.002339871598153753, + "z": 0.0 + }, + { + "x": 350.30771546104813, + "y": -0.001787992599565896, + "z": 0.0 + }, + { + "x": 349.2688584647802, + "y": -0.0012361136009780352, + "z": 0.0 + }, + { + "x": 348.2300014685122, + "y": -0.0006842346023901782, + "z": 0.0 + } + ] + }, + { + "id": 3, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 348.2289389951926, + "y": -2.0006839523899815, + "z": 0.0 + }, + { + "x": 349.26779599146056, + "y": -2.0012358313885694, + "z": 0.0 + }, + { + "x": 350.3066529877285, + "y": -2.0017877103871573, + "z": 0.0 + }, + { + "x": 351.34550998399646, + "y": -2.002339589385745, + "z": 0.0 + }, + { + "x": 352.3843669802644, + "y": -2.0028914683843326, + "z": 0.0 + }, + { + "x": 353.42322397653237, + "y": -2.0034433473829205, + "z": 0.0 + }, + { + "x": 354.4620809728003, + "y": -2.0039952263815084, + "z": 0.0 + }, + { + "x": 355.5009379690683, + "y": -2.0045471053800963, + "z": 0.0 + }, + { + "x": 356.5397949653362, + "y": -2.005098984378684, + "z": 0.0 + }, + { + "x": 357.5786519616042, + "y": -2.005650863377272, + "z": 0.0 + }, + { + "x": 358.61750895787213, + "y": -2.00620274237586, + "z": 0.0 + }, + { + "x": 359.6563659541401, + "y": -2.006754621374448, + "z": 0.0 + }, + { + "x": 360.69522295040804, + "y": -2.0073065003730357, + "z": 0.0 + }, + { + "x": 361.73407994667605, + "y": -2.0078583793716236, + "z": 0.0 + }, + { + "x": 362.772936942944, + "y": -2.0084102583702115, + "z": 0.0 + }, + { + "x": 363.81179393921195, + "y": -2.0089621373687994, + "z": 0.0 + }, + { + "x": 364.8506509354799, + "y": -2.0095140163673872, + "z": 0.0 + }, + { + "x": 365.88950793174786, + "y": -2.010065895365975, + "z": 0.0 + }, + { + "x": 366.9283649280158, + "y": -2.010617774364563, + "z": 0.0 + }, + { + "x": 367.96722192428376, + "y": -2.011169653363151, + "z": 0.0 + }, + { + "x": 369.0060789205517, + "y": -2.0117215323617383, + "z": 0.0 + }, + { + "x": 370.04493591681967, + "y": -2.0122734113603262, + "z": 0.0 + }, + { + "x": 371.0837929130876, + "y": -2.012825290358914, + "z": 0.0 + }, + { + "x": 372.1226499093556, + "y": -2.013377169357502, + "z": 0.0 + }, + { + "x": 373.1615069056235, + "y": -2.01392904835609, + "z": 0.0 + }, + { + "x": 374.2003639018915, + "y": -2.0144809273546778, + "z": 0.0 + }, + { + "x": 375.23922089815943, + "y": -2.0150328063532656, + "z": 0.0 + }, + { + "x": 376.2780778944274, + "y": -2.0155846853518535, + "z": 0.0 + }, + { + "x": 377.31693489069534, + "y": -2.0161365643504414, + "z": 0.0 + }, + { + "x": 378.3557918869633, + "y": -2.0166884433490293, + "z": 0.0 + }, + { + "x": 379.39464888323124, + "y": -2.017240322347617, + "z": 0.0 + }, + { + "x": 380.4335058794992, + "y": -2.017792201346205, + "z": 0.0 + }, + { + "x": 381.47236287576715, + "y": -2.018344080344793, + "z": 0.0 + }, + { + "x": 382.5112198720351, + "y": -2.018895959343381, + "z": 0.0 + }, + { + "x": 383.55007686830305, + "y": -2.0194478383419687, + "z": 0.0 + }, + { + "x": 384.588933864571, + "y": -2.019999717340556, + "z": 0.0 + } + ] + }, + { + "id": 4, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 384.59105881121025, + "y": 1.979999718234626, + "z": 0.0 + }, + { + "x": 383.5522018149423, + "y": 1.980551597233214, + "z": 0.0 + }, + { + "x": 382.51334481867434, + "y": 1.9811034762318018, + "z": 0.0 + }, + { + "x": 381.4744878224064, + "y": 1.9816553552303897, + "z": 0.0 + }, + { + "x": 380.43563082613844, + "y": 1.9822072342289776, + "z": 0.0 + }, + { + "x": 379.3967738298705, + "y": 1.9827591132275655, + "z": 0.0 + }, + { + "x": 378.35791683360253, + "y": 1.9833109922261534, + "z": 0.0 + }, + { + "x": 377.3190598373346, + "y": 1.9838628712247413, + "z": 0.0 + }, + { + "x": 376.2802028410666, + "y": 1.9844147502233291, + "z": 0.0 + }, + { + "x": 375.2413458447987, + "y": 1.9849666292219166, + "z": 0.0 + }, + { + "x": 374.2024888485307, + "y": 1.9855185082205045, + "z": 0.0 + }, + { + "x": 373.16363185226277, + "y": 1.9860703872190923, + "z": 0.0 + }, + { + "x": 372.1247748559948, + "y": 1.9866222662176802, + "z": 0.0 + }, + { + "x": 371.08591785972686, + "y": 1.9871741452162681, + "z": 0.0 + }, + { + "x": 370.0470608634589, + "y": 1.987726024214856, + "z": 0.0 + }, + { + "x": 369.00820386719096, + "y": 1.9882779032134439, + "z": 0.0 + }, + { + "x": 367.969346870923, + "y": 1.9888297822120318, + "z": 0.0 + }, + { + "x": 366.93048987465505, + "y": 1.9893816612106197, + "z": 0.0 + }, + { + "x": 365.8916328783871, + "y": 1.9899335402092075, + "z": 0.0 + }, + { + "x": 364.85277588211915, + "y": 1.9904854192077954, + "z": 0.0 + }, + { + "x": 363.8139188858512, + "y": 1.9910372982063833, + "z": 0.0 + }, + { + "x": 362.77506188958324, + "y": 1.9915891772049712, + "z": 0.0 + }, + { + "x": 361.7362048933153, + "y": 1.992141056203559, + "z": 0.0 + }, + { + "x": 360.6973478970473, + "y": 1.992692935202147, + "z": 0.0 + }, + { + "x": 359.6584909007793, + "y": 1.9932448142007344, + "z": 0.0 + }, + { + "x": 358.6196339045114, + "y": 1.9937966931993223, + "z": 0.0 + }, + { + "x": 357.5807769082434, + "y": 1.9943485721979102, + "z": 0.0 + }, + { + "x": 356.54191991197547, + "y": 1.994900451196498, + "z": 0.0 + }, + { + "x": 355.5030629157075, + "y": 1.995452330195086, + "z": 0.0 + }, + { + "x": 354.46420591943956, + "y": 1.9960042091936738, + "z": 0.0 + }, + { + "x": 353.4253489231716, + "y": 1.9965560881922617, + "z": 0.0 + }, + { + "x": 352.38649192690366, + "y": 1.9971079671908496, + "z": 0.0 + }, + { + "x": 351.3476349306357, + "y": 1.9976598461894375, + "z": 0.0 + }, + { + "x": 350.30877793436775, + "y": 1.9982117251880254, + "z": 0.0 + }, + { + "x": 349.2699209380998, + "y": 1.9987636041866133, + "z": 0.0 + }, + { + "x": 348.23106394183185, + "y": 1.9993154831852011, + "z": 0.0 + } + ] + }, + { + "id": 5, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 168.07958339564385, + "y": -3.956937384133082, + "z": 0.0 + }, + { + "x": 169.08308657463155, + "y": -3.957044487289655, + "z": 0.0 + }, + { + "x": 170.08658975361928, + "y": -3.957151590446228, + "z": 0.0 + }, + { + "x": 171.090092932607, + "y": -3.957258693602801, + "z": 0.0 + }, + { + "x": 172.0935961115947, + "y": -3.9573657967593734, + "z": 0.0 + }, + { + "x": 173.0970992905824, + "y": -3.9574728999159463, + "z": 0.0 + }, + { + "x": 174.10060246957013, + "y": -3.9575800030725192, + "z": 0.0 + }, + { + "x": 175.10410564855783, + "y": -3.957687106229092, + "z": 0.0 + }, + { + "x": 176.10760882754556, + "y": -3.957794209385665, + "z": 0.0 + }, + { + "x": 177.11111200653326, + "y": -3.9579013125422375, + "z": 0.0 + }, + { + "x": 178.114615185521, + "y": -3.9580084156988105, + "z": 0.0 + }, + { + "x": 179.1181183645087, + "y": -3.9581155188553834, + "z": 0.0 + }, + { + "x": 180.1216215434964, + "y": -3.9582226220119563, + "z": 0.0 + }, + { + "x": 181.12512472248412, + "y": -3.958329725168529, + "z": 0.0 + }, + { + "x": 182.12862790147184, + "y": -3.9584368283251017, + "z": 0.0 + }, + { + "x": 183.13213108045954, + "y": -3.9585439314816746, + "z": 0.0 + }, + { + "x": 184.13563425944724, + "y": -3.9586510346382475, + "z": 0.0 + }, + { + "x": 185.13913743843494, + "y": -3.9587581377948204, + "z": 0.0 + }, + { + "x": 186.1426406174227, + "y": -3.9588652409513934, + "z": 0.0 + }, + { + "x": 187.1461437964104, + "y": -3.958972344107966, + "z": 0.0 + }, + { + "x": 188.1496469753981, + "y": -3.9590794472645388, + "z": 0.0 + }, + { + "x": 189.1531501543858, + "y": -3.9591865504211117, + "z": 0.0 + }, + { + "x": 190.15665333337355, + "y": -3.9592936535776846, + "z": 0.0 + }, + { + "x": 191.16015651236125, + "y": -3.9594007567342575, + "z": 0.0 + }, + { + "x": 192.16365969134895, + "y": -3.95950785989083, + "z": 0.0 + }, + { + "x": 193.16716287033665, + "y": -3.959614963047403, + "z": 0.0 + }, + { + "x": 194.17066604932438, + "y": -3.959722066203976, + "z": 0.0 + }, + { + "x": 195.1741692283121, + "y": -3.9598291693605487, + "z": 0.0 + }, + { + "x": 196.1776724072998, + "y": -3.9599362725171217, + "z": 0.0 + }, + { + "x": 197.18117558628754, + "y": -3.960043375673694, + "z": 0.0 + }, + { + "x": 198.1846787652752, + "y": -3.960150478830267, + "z": 0.0 + }, + { + "x": 199.18818194426294, + "y": -3.96025758198684, + "z": 0.0 + }, + { + "x": 200.1916851232506, + "y": -3.960364685143413, + "z": 0.0 + }, + { + "x": 201.19518830223834, + "y": -3.9604717882999854, + "z": 0.0 + }, + { + "x": 202.198691481226, + "y": -3.9605788914565583, + "z": 0.0 + }, + { + "x": 203.20219466021373, + "y": -3.960685994613131, + "z": 0.0 + }, + { + "x": 204.2056978392014, + "y": -3.960793097769704, + "z": 0.0 + }, + { + "x": 205.2092010181891, + "y": -3.960900200926277, + "z": 0.0 + }, + { + "x": 206.2127041971768, + "y": -3.9610073040828495, + "z": 0.0 + }, + { + "x": 207.2162073761645, + "y": -3.9611144072394224, + "z": 0.0 + }, + { + "x": 208.2197105551522, + "y": -3.9612215103959953, + "z": 0.0 + }, + { + "x": 209.2232137341399, + "y": -3.9613286135525683, + "z": 0.0 + }, + { + "x": 210.2267169131276, + "y": -3.961435716709141, + "z": 0.0 + }, + { + "x": 211.2302200921153, + "y": -3.9615428198657137, + "z": 0.0 + }, + { + "x": 212.233723271103, + "y": -3.9616499230222866, + "z": 0.0 + }, + { + "x": 213.2372264500907, + "y": -3.9617570261788595, + "z": 0.0 + }, + { + "x": 214.2407296290784, + "y": -3.9618641293354324, + "z": 0.0 + }, + { + "x": 215.2442328080661, + "y": -3.9619712324920053, + "z": 0.0 + }, + { + "x": 216.2477359870538, + "y": -3.962078335648578, + "z": 0.0 + }, + { + "x": 217.2512391660415, + "y": -3.9621854388051507, + "z": 0.0 + }, + { + "x": 218.2547423450292, + "y": -3.9622925419617236, + "z": 0.0 + }, + { + "x": 219.2582455240169, + "y": -3.9623996451182966, + "z": 0.0 + }, + { + "x": 220.2617487030046, + "y": -3.962506748274869, + "z": 0.0 + }, + { + "x": 221.2652518819923, + "y": -3.962613851431442, + "z": 0.0 + }, + { + "x": 222.26875506098, + "y": -3.962720954588015, + "z": 0.0 + }, + { + "x": 223.2722582399677, + "y": -3.962828057744588, + "z": 0.0 + }, + { + "x": 224.2757614189554, + "y": -3.9629351609011607, + "z": 0.0 + }, + { + "x": 225.2792645979431, + "y": -3.963042264057733, + "z": 0.0 + }, + { + "x": 226.2827677769308, + "y": -3.963149367214306, + "z": 0.0 + }, + { + "x": 227.2862709559185, + "y": -3.963256470370879, + "z": 0.0 + }, + { + "x": 228.2897741349062, + "y": -3.963363573527452, + "z": 0.0 + }, + { + "x": 229.2932773138939, + "y": -3.963470676684025, + "z": 0.0 + }, + { + "x": 230.2967804928816, + "y": -3.9635777798405973, + "z": 0.0 + }, + { + "x": 231.3002836718693, + "y": -3.9636848829971703, + "z": 0.0 + }, + { + "x": 232.303786850857, + "y": -3.963791986153743, + "z": 0.0 + }, + { + "x": 233.3072900298447, + "y": -3.963899089310316, + "z": 0.0 + }, + { + "x": 234.3107932088324, + "y": -3.9640061924668886, + "z": 0.0 + }, + { + "x": 235.3142963878201, + "y": -3.9641132956234615, + "z": 0.0 + }, + { + "x": 236.31779956680776, + "y": -3.9642203987800344, + "z": 0.0 + }, + { + "x": 237.3213027457955, + "y": -3.9643275019366073, + "z": 0.0 + }, + { + "x": 238.32480592478316, + "y": -3.9644346050931802, + "z": 0.0 + }, + { + "x": 239.3283091037709, + "y": -3.9645417082497527, + "z": 0.0 + }, + { + "x": 240.33181228275856, + "y": -3.9646488114063256, + "z": 0.0 + }, + { + "x": 241.3353154617463, + "y": -3.9647559145628986, + "z": 0.0 + }, + { + "x": 242.33881864073396, + "y": -3.9648630177194715, + "z": 0.0 + }, + { + "x": 243.34232181972166, + "y": -3.9649701208760444, + "z": 0.0 + }, + { + "x": 244.34582499870936, + "y": -3.965077224032617, + "z": 0.0 + }, + { + "x": 245.34932817769706, + "y": -3.96518432718919, + "z": 0.0 + }, + { + "x": 246.35283135668476, + "y": -3.9652914303457627, + "z": 0.0 + }, + { + "x": 247.35633453567246, + "y": -3.9653985335023356, + "z": 0.0 + }, + { + "x": 248.35983771466016, + "y": -3.9655056366589085, + "z": 0.0 + }, + { + "x": 249.36334089364786, + "y": -3.965612739815481, + "z": 0.0 + }, + { + "x": 250.36684407263556, + "y": -3.965719842972054, + "z": 0.0 + }, + { + "x": 251.37034725162326, + "y": -3.965826946128627, + "z": 0.0 + }, + { + "x": 252.37385043061096, + "y": -3.9659340492851998, + "z": 0.0 + }, + { + "x": 253.37735360959866, + "y": -3.9660411524417722, + "z": 0.0 + }, + { + "x": 254.38085678858636, + "y": -3.966148255598345, + "z": 0.0 + }, + { + "x": 255.38435996757406, + "y": -3.966255358754918, + "z": 0.0 + }, + { + "x": 256.38786314656176, + "y": -3.966362461911491, + "z": 0.0 + }, + { + "x": 257.3913663255494, + "y": -3.966469565068064, + "z": 0.0 + }, + { + "x": 258.39486950453716, + "y": -3.9665766682246364, + "z": 0.0 + }, + { + "x": 259.3983726835248, + "y": -3.9666837713812093, + "z": 0.0 + }, + { + "x": 260.40187586251255, + "y": -3.9667908745377822, + "z": 0.0 + }, + { + "x": 261.4053790415002, + "y": -3.966897977694355, + "z": 0.0 + }, + { + "x": 262.40888222048795, + "y": -3.967005080850928, + "z": 0.0 + }, + { + "x": 263.4123853994757, + "y": -3.9671121840075005, + "z": 0.0 + }, + { + "x": 264.41588857846335, + "y": -3.9672192871640735, + "z": 0.0 + }, + { + "x": 265.4193917574511, + "y": -3.9673263903206464, + "z": 0.0 + }, + { + "x": 266.42289493643875, + "y": -3.9674334934772193, + "z": 0.0 + }, + { + "x": 267.4263981154265, + "y": -3.9675405966337918, + "z": 0.0 + }, + { + "x": 268.4299012944142, + "y": -3.9676476997903647, + "z": 0.0 + }, + { + "x": 269.4334044734019, + "y": -3.9677548029469376, + "z": 0.0 + }, + { + "x": 270.4369076523896, + "y": -3.9678619061035105, + "z": 0.0 + }, + { + "x": 271.4404108313773, + "y": -3.9679690092600834, + "z": 0.0 + }, + { + "x": 272.443914010365, + "y": -3.968076112416656, + "z": 0.0 + }, + { + "x": 273.44741718935273, + "y": -3.968183215573229, + "z": 0.0 + }, + { + "x": 274.4509203683404, + "y": -3.9682903187298018, + "z": 0.0 + }, + { + "x": 275.45442354732813, + "y": -3.9683974218863747, + "z": 0.0 + }, + { + "x": 276.45792672631586, + "y": -3.9685045250429476, + "z": 0.0 + }, + { + "x": 277.46142990530353, + "y": -3.96861162819952, + "z": 0.0 + }, + { + "x": 278.46493308429126, + "y": -3.968718731356093, + "z": 0.0 + }, + { + "x": 279.46843626327893, + "y": -3.968825834512666, + "z": 0.0 + }, + { + "x": 280.47193944226666, + "y": -3.968932937669239, + "z": 0.0 + }, + { + "x": 281.4754426212544, + "y": -3.9690400408258117, + "z": 0.0 + }, + { + "x": 282.47894580024206, + "y": -3.969147143982384, + "z": 0.0 + }, + { + "x": 283.4824489792298, + "y": -3.969254247138957, + "z": 0.0 + }, + { + "x": 284.48595215821746, + "y": -3.96936135029553, + "z": 0.0 + }, + { + "x": 285.4894553372052, + "y": -3.969468453452103, + "z": 0.0 + }, + { + "x": 286.4929585161929, + "y": -3.969575556608676, + "z": 0.0 + }, + { + "x": 287.4964616951806, + "y": -3.9696826597652484, + "z": 0.0 + }, + { + "x": 288.4999648741683, + "y": -3.9697897629218213, + "z": 0.0 + }, + { + "x": 289.50346805315604, + "y": -3.969896866078394, + "z": 0.0 + }, + { + "x": 290.50684979722337, + "y": -3.9700344178870632, + "z": 0.0 + }, + { + "x": 291.5088343943909, + "y": -3.970552431015961, + "z": 0.0 + }, + { + "x": 292.512279288484, + "y": -3.971085498227681, + "z": 0.0 + }, + { + "x": 293.51578233158676, + "y": -3.971618595907629, + "z": 0.0 + }, + { + "x": 294.51928537468945, + "y": -3.972151693587575, + "z": 0.0 + }, + { + "x": 295.5227884177921, + "y": -3.9726847912675227, + "z": 0.0 + }, + { + "x": 296.52629146089487, + "y": -3.9732178889474707, + "z": 0.0 + }, + { + "x": 297.52979450399755, + "y": -3.973750986627417, + "z": 0.0 + }, + { + "x": 298.53329754710023, + "y": -3.974284084307364, + "z": 0.0 + }, + { + "x": 299.536800590203, + "y": -3.974817181987312, + "z": 0.0 + }, + { + "x": 300.54030363330565, + "y": -3.9753502796672584, + "z": 0.0 + }, + { + "x": 301.54380667640834, + "y": -3.975883377347206, + "z": 0.0 + }, + { + "x": 302.5473097195111, + "y": -3.976416475027154, + "z": 0.0 + }, + { + "x": 303.55081276261376, + "y": -3.9769495727071003, + "z": 0.0 + }, + { + "x": 304.55431580571644, + "y": -3.9774826703870474, + "z": 0.0 + }, + { + "x": 305.5578188488192, + "y": -3.9780157680669954, + "z": 0.0 + }, + { + "x": 306.56132189192186, + "y": -3.978548865746942, + "z": 0.0 + }, + { + "x": 307.56482493502455, + "y": -3.9790819634268892, + "z": 0.0 + }, + { + "x": 308.5683279781273, + "y": -3.9796150611068373, + "z": 0.0 + }, + { + "x": 309.57183102122997, + "y": -3.9801481587867835, + "z": 0.0 + }, + { + "x": 310.57533406433265, + "y": -3.9806812564667307, + "z": 0.0 + }, + { + "x": 311.5788371074354, + "y": -3.981214354146679, + "z": 0.0 + }, + { + "x": 312.5823401505381, + "y": -3.9817474518266254, + "z": 0.0 + }, + { + "x": 313.58584319364076, + "y": -3.9822805495065725, + "z": 0.0 + }, + { + "x": 314.58934623674344, + "y": -3.9828136471865196, + "z": 0.0 + }, + { + "x": 315.5928492798462, + "y": -3.9833467448664677, + "z": 0.0 + }, + { + "x": 316.59635232294886, + "y": -3.9838798425464144, + "z": 0.0 + }, + { + "x": 317.59985536605154, + "y": -3.9844129402263615, + "z": 0.0 + }, + { + "x": 318.6033584091543, + "y": -3.9849460379063095, + "z": 0.0 + }, + { + "x": 319.60686145225696, + "y": -3.9854791355862558, + "z": 0.0 + }, + { + "x": 320.61036449535965, + "y": -3.986012233266203, + "z": 0.0 + }, + { + "x": 321.6138675384624, + "y": -3.986545330946151, + "z": 0.0 + }, + { + "x": 322.61737058156507, + "y": -3.9870784286260976, + "z": 0.0 + }, + { + "x": 323.62087362466775, + "y": -3.9876115263060448, + "z": 0.0 + }, + { + "x": 324.6243766677705, + "y": -3.988144623985993, + "z": 0.0 + }, + { + "x": 325.6278797108732, + "y": -3.988677721665939, + "z": 0.0 + } + ] + }, + { + "id": 6, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.63212960415166, + "y": 4.011321149484425, + "z": 0.0 + }, + { + "x": 324.628626561049, + "y": 4.011854247164374, + "z": 0.0 + }, + { + "x": 323.62512351794624, + "y": 4.012387344844321, + "z": 0.0 + }, + { + "x": 322.62162047484355, + "y": 4.012920442524267, + "z": 0.0 + }, + { + "x": 321.61811743174087, + "y": 4.013453540204216, + "z": 0.0 + }, + { + "x": 320.61461438863813, + "y": 4.0139866378841615, + "z": 0.0 + }, + { + "x": 319.61111134553545, + "y": 4.014519735564109, + "z": 0.0 + }, + { + "x": 318.60760830243277, + "y": 4.015052833244058, + "z": 0.0 + }, + { + "x": 317.60410525933, + "y": 4.015585930924003, + "z": 0.0 + }, + { + "x": 316.60060221622734, + "y": 4.016119028603951, + "z": 0.0 + }, + { + "x": 315.59709917312466, + "y": 4.016652126283899, + "z": 0.0 + }, + { + "x": 314.5935961300219, + "y": 4.017185223963845, + "z": 0.0 + }, + { + "x": 313.59009308691924, + "y": 4.017718321643793, + "z": 0.0 + }, + { + "x": 312.58659004381656, + "y": 4.0182514193237395, + "z": 0.0 + }, + { + "x": 311.5830870007139, + "y": 4.018784517003688, + "z": 0.0 + }, + { + "x": 310.57958395761113, + "y": 4.019317614683634, + "z": 0.0 + }, + { + "x": 309.57608091450845, + "y": 4.019850712363581, + "z": 0.0 + }, + { + "x": 308.57257787140577, + "y": 4.02038381004353, + "z": 0.0 + }, + { + "x": 307.56907482830303, + "y": 4.020916907723476, + "z": 0.0 + }, + { + "x": 306.56557178520035, + "y": 4.021450005403423, + "z": 0.0 + }, + { + "x": 305.56206874209767, + "y": 4.021983103083371, + "z": 0.0 + }, + { + "x": 304.5585656989949, + "y": 4.0225162007633175, + "z": 0.0 + }, + { + "x": 303.55506265589224, + "y": 4.023049298443264, + "z": 0.0 + }, + { + "x": 302.55155961278956, + "y": 4.023582396123213, + "z": 0.0 + }, + { + "x": 301.5480565696868, + "y": 4.024115493803159, + "z": 0.0 + }, + { + "x": 300.54455352658414, + "y": 4.024648591483106, + "z": 0.0 + }, + { + "x": 299.54105048348146, + "y": 4.0251816891630545, + "z": 0.0 + }, + { + "x": 298.5375474403787, + "y": 4.025714786843, + "z": 0.0 + }, + { + "x": 297.53404439727603, + "y": 4.026247884522948, + "z": 0.0 + }, + { + "x": 296.53054135417335, + "y": 4.026780982202896, + "z": 0.0 + }, + { + "x": 295.5270383110706, + "y": 4.027314079882842, + "z": 0.0 + }, + { + "x": 294.52353526796793, + "y": 4.02784717756279, + "z": 0.0 + }, + { + "x": 293.52003222486525, + "y": 4.028380275242737, + "z": 0.0 + }, + { + "x": 292.5165291817625, + "y": 4.028913372922684, + "z": 0.0 + }, + { + "x": 291.5129679896503, + "y": 4.029446501070859, + "z": 0.0 + }, + { + "x": 290.5079464882093, + "y": 4.029965506942243, + "z": 0.0 + }, + { + "x": 289.50432188727, + "y": 4.0301030883570625, + "z": 0.0 + }, + { + "x": 288.5008187082823, + "y": 4.0302101915136355, + "z": 0.0 + }, + { + "x": 287.49731552929455, + "y": 4.030317294670208, + "z": 0.0 + }, + { + "x": 286.4938123503069, + "y": 4.030424397826781, + "z": 0.0 + }, + { + "x": 285.49030917131915, + "y": 4.030531500983354, + "z": 0.0 + }, + { + "x": 284.4868059923314, + "y": 4.030638604139927, + "z": 0.0 + }, + { + "x": 283.48330281334376, + "y": 4.030745707296499, + "z": 0.0 + }, + { + "x": 282.479799634356, + "y": 4.030852810453072, + "z": 0.0 + }, + { + "x": 281.47629645536836, + "y": 4.030959913609645, + "z": 0.0 + }, + { + "x": 280.4727932763806, + "y": 4.031067016766218, + "z": 0.0 + }, + { + "x": 279.4692900973929, + "y": 4.031174119922791, + "z": 0.0 + }, + { + "x": 278.46578691840523, + "y": 4.031281223079364, + "z": 0.0 + }, + { + "x": 277.4622837394175, + "y": 4.031388326235937, + "z": 0.0 + }, + { + "x": 276.45878056042983, + "y": 4.03149542939251, + "z": 0.0 + }, + { + "x": 275.4552773814421, + "y": 4.0316025325490825, + "z": 0.0 + }, + { + "x": 274.4517742024544, + "y": 4.0317096357056545, + "z": 0.0 + }, + { + "x": 273.4482710234667, + "y": 4.0318167388622275, + "z": 0.0 + }, + { + "x": 272.444767844479, + "y": 4.0319238420188, + "z": 0.0 + }, + { + "x": 271.44126466549125, + "y": 4.032030945175373, + "z": 0.0 + }, + { + "x": 270.4377614865036, + "y": 4.032138048331946, + "z": 0.0 + }, + { + "x": 269.43425830751585, + "y": 4.032245151488519, + "z": 0.0 + }, + { + "x": 268.4307551285282, + "y": 4.032352254645092, + "z": 0.0 + }, + { + "x": 267.42725194954045, + "y": 4.032459357801665, + "z": 0.0 + }, + { + "x": 266.4237487705527, + "y": 4.032566460958238, + "z": 0.0 + }, + { + "x": 265.42024559156505, + "y": 4.032673564114811, + "z": 0.0 + }, + { + "x": 264.4167424125773, + "y": 4.032780667271383, + "z": 0.0 + }, + { + "x": 263.41323923358965, + "y": 4.032887770427956, + "z": 0.0 + }, + { + "x": 262.4097360546019, + "y": 4.032994873584529, + "z": 0.0 + }, + { + "x": 261.4062328756142, + "y": 4.033101976741102, + "z": 0.0 + }, + { + "x": 260.4027296966265, + "y": 4.0332090798976745, + "z": 0.0 + }, + { + "x": 259.3992265176388, + "y": 4.033316183054247, + "z": 0.0 + }, + { + "x": 258.3957233386511, + "y": 4.03342328621082, + "z": 0.0 + }, + { + "x": 257.3922201596634, + "y": 4.033530389367393, + "z": 0.0 + }, + { + "x": 256.3887169806757, + "y": 4.033637492523966, + "z": 0.0 + }, + { + "x": 255.38521380168802, + "y": 4.033744595680538, + "z": 0.0 + }, + { + "x": 254.38171062270033, + "y": 4.033851698837111, + "z": 0.0 + }, + { + "x": 253.37820744371263, + "y": 4.033958801993684, + "z": 0.0 + }, + { + "x": 252.37470426472493, + "y": 4.034065905150257, + "z": 0.0 + }, + { + "x": 251.37120108573723, + "y": 4.03417300830683, + "z": 0.0 + }, + { + "x": 250.36769790674953, + "y": 4.034280111463403, + "z": 0.0 + }, + { + "x": 249.36419472776183, + "y": 4.034387214619976, + "z": 0.0 + }, + { + "x": 248.36069154877413, + "y": 4.034494317776549, + "z": 0.0 + }, + { + "x": 247.35718836978643, + "y": 4.034601420933122, + "z": 0.0 + }, + { + "x": 246.35368519079873, + "y": 4.0347085240896945, + "z": 0.0 + }, + { + "x": 245.35018201181103, + "y": 4.0348156272462665, + "z": 0.0 + }, + { + "x": 244.34667883282333, + "y": 4.034922730402839, + "z": 0.0 + }, + { + "x": 243.34317565383563, + "y": 4.035029833559412, + "z": 0.0 + }, + { + "x": 242.33967247484793, + "y": 4.035136936715985, + "z": 0.0 + }, + { + "x": 241.33616929586026, + "y": 4.035244039872558, + "z": 0.0 + }, + { + "x": 240.33266611687253, + "y": 4.035351143029131, + "z": 0.0 + }, + { + "x": 239.32916293788486, + "y": 4.035458246185704, + "z": 0.0 + }, + { + "x": 238.32565975889713, + "y": 4.035565349342277, + "z": 0.0 + }, + { + "x": 237.32215657990946, + "y": 4.03567245249885, + "z": 0.0 + }, + { + "x": 236.31865340092173, + "y": 4.035779555655422, + "z": 0.0 + }, + { + "x": 235.31515022193406, + "y": 4.035886658811995, + "z": 0.0 + }, + { + "x": 234.31164704294636, + "y": 4.035993761968568, + "z": 0.0 + }, + { + "x": 233.30814386395866, + "y": 4.036100865125141, + "z": 0.0 + }, + { + "x": 232.30464068497096, + "y": 4.036207968281714, + "z": 0.0 + }, + { + "x": 231.30113750598326, + "y": 4.0363150714382865, + "z": 0.0 + }, + { + "x": 230.29763432699556, + "y": 4.036422174594859, + "z": 0.0 + }, + { + "x": 229.29413114800786, + "y": 4.036529277751432, + "z": 0.0 + }, + { + "x": 228.29062796902016, + "y": 4.036636380908005, + "z": 0.0 + }, + { + "x": 227.28712479003246, + "y": 4.036743484064578, + "z": 0.0 + }, + { + "x": 226.28362161104477, + "y": 4.03685058722115, + "z": 0.0 + }, + { + "x": 225.28011843205707, + "y": 4.036957690377723, + "z": 0.0 + }, + { + "x": 224.27661525306937, + "y": 4.037064793534296, + "z": 0.0 + }, + { + "x": 223.27311207408167, + "y": 4.037171896690869, + "z": 0.0 + }, + { + "x": 222.26960889509397, + "y": 4.037278999847442, + "z": 0.0 + }, + { + "x": 221.26610571610627, + "y": 4.037386103004015, + "z": 0.0 + }, + { + "x": 220.26260253711857, + "y": 4.037493206160588, + "z": 0.0 + }, + { + "x": 219.25909935813087, + "y": 4.037600309317161, + "z": 0.0 + }, + { + "x": 218.25559617914317, + "y": 4.0377074124737335, + "z": 0.0 + }, + { + "x": 217.25209300015547, + "y": 4.037814515630306, + "z": 0.0 + }, + { + "x": 216.24858982116777, + "y": 4.0379216187868785, + "z": 0.0 + }, + { + "x": 215.24508664218007, + "y": 4.038028721943451, + "z": 0.0 + }, + { + "x": 214.24158346319237, + "y": 4.038135825100024, + "z": 0.0 + }, + { + "x": 213.23808028420467, + "y": 4.038242928256597, + "z": 0.0 + }, + { + "x": 212.23457710521697, + "y": 4.03835003141317, + "z": 0.0 + }, + { + "x": 211.23107392622927, + "y": 4.038457134569743, + "z": 0.0 + }, + { + "x": 210.22757074724157, + "y": 4.038564237726316, + "z": 0.0 + }, + { + "x": 209.22406756825387, + "y": 4.038671340882889, + "z": 0.0 + }, + { + "x": 208.22056438926617, + "y": 4.038778444039462, + "z": 0.0 + }, + { + "x": 207.21706121027847, + "y": 4.038885547196034, + "z": 0.0 + }, + { + "x": 206.21355803129077, + "y": 4.038992650352607, + "z": 0.0 + }, + { + "x": 205.21005485230307, + "y": 4.03909975350918, + "z": 0.0 + }, + { + "x": 204.20655167331537, + "y": 4.039206856665753, + "z": 0.0 + }, + { + "x": 203.2030484943277, + "y": 4.0393139598223256, + "z": 0.0 + }, + { + "x": 202.19954531533998, + "y": 4.0394210629788985, + "z": 0.0 + }, + { + "x": 201.1960421363523, + "y": 4.039528166135471, + "z": 0.0 + }, + { + "x": 200.19253895736458, + "y": 4.039635269292044, + "z": 0.0 + }, + { + "x": 199.1890357783769, + "y": 4.039742372448617, + "z": 0.0 + }, + { + "x": 198.18553259938918, + "y": 4.039849475605189, + "z": 0.0 + }, + { + "x": 197.1820294204015, + "y": 4.039956578761762, + "z": 0.0 + }, + { + "x": 196.17852624141378, + "y": 4.040063681918335, + "z": 0.0 + }, + { + "x": 195.17502306242608, + "y": 4.040170785074908, + "z": 0.0 + }, + { + "x": 194.17151988343835, + "y": 4.040277888231481, + "z": 0.0 + }, + { + "x": 193.16801670445062, + "y": 4.040384991388054, + "z": 0.0 + }, + { + "x": 192.16451352546292, + "y": 4.040492094544627, + "z": 0.0 + }, + { + "x": 191.16101034647522, + "y": 4.0405991977012, + "z": 0.0 + }, + { + "x": 190.15750716748752, + "y": 4.040706300857773, + "z": 0.0 + }, + { + "x": 189.15400398849977, + "y": 4.0408134040143455, + "z": 0.0 + }, + { + "x": 188.15050080951207, + "y": 4.0409205071709176, + "z": 0.0 + }, + { + "x": 187.14699763052437, + "y": 4.0410276103274905, + "z": 0.0 + }, + { + "x": 186.14349445153667, + "y": 4.041134713484063, + "z": 0.0 + }, + { + "x": 185.1399912725489, + "y": 4.041241816640636, + "z": 0.0 + }, + { + "x": 184.1364880935612, + "y": 4.041348919797209, + "z": 0.0 + }, + { + "x": 183.1329849145735, + "y": 4.041456022953782, + "z": 0.0 + }, + { + "x": 182.1294817355858, + "y": 4.041563126110355, + "z": 0.0 + }, + { + "x": 181.12597855659808, + "y": 4.041670229266928, + "z": 0.0 + }, + { + "x": 180.12247537761036, + "y": 4.041777332423501, + "z": 0.0 + }, + { + "x": 179.11897219862266, + "y": 4.041884435580073, + "z": 0.0 + }, + { + "x": 178.11546901963496, + "y": 4.041991538736646, + "z": 0.0 + }, + { + "x": 177.11196584064723, + "y": 4.042098641893219, + "z": 0.0 + }, + { + "x": 176.10846266165953, + "y": 4.042205745049792, + "z": 0.0 + }, + { + "x": 175.1049594826718, + "y": 4.042312848206365, + "z": 0.0 + }, + { + "x": 174.1014563036841, + "y": 4.0424199513629375, + "z": 0.0 + }, + { + "x": 173.09795312469637, + "y": 4.04252705451951, + "z": 0.0 + }, + { + "x": 172.09444994570867, + "y": 4.042634157676083, + "z": 0.0 + }, + { + "x": 171.09094676672098, + "y": 4.042741260832656, + "z": 0.0 + }, + { + "x": 170.08744358773325, + "y": 4.042848363989229, + "z": 0.0 + }, + { + "x": 169.08394040874552, + "y": 4.042955467145801, + "z": 0.0 + }, + { + "x": 168.08043722975782, + "y": 4.043062570302374, + "z": 0.0 + } + ] + }, + { + "id": 7, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 325.6300046575124, + "y": 0.01132171390924333, + "z": 0.0 + }, + { + "x": 324.62650161440973, + "y": 0.01185481158919055, + "z": 0.0 + }, + { + "x": 323.622998571307, + "y": 0.012387909269137768, + "z": 0.0 + }, + { + "x": 322.6194955282043, + "y": 0.012921006949084988, + "z": 0.0 + }, + { + "x": 321.61599248510163, + "y": 0.013454104629032206, + "z": 0.0 + }, + { + "x": 320.6124894419989, + "y": 0.013987202308979426, + "z": 0.0 + }, + { + "x": 319.6089863988962, + "y": 0.014520299988926646, + "z": 0.0 + }, + { + "x": 318.6054833557935, + "y": 0.015053397668873866, + "z": 0.0 + }, + { + "x": 317.6019803126908, + "y": 0.015586495348821084, + "z": 0.0 + }, + { + "x": 316.5984772695881, + "y": 0.016119593028768302, + "z": 0.0 + }, + { + "x": 315.5949742264854, + "y": 0.016652690708715546, + "z": 0.0 + }, + { + "x": 314.5914711833827, + "y": 0.017185788388662773, + "z": 0.0 + }, + { + "x": 313.58796814028, + "y": 0.017718886068609992, + "z": 0.0 + }, + { + "x": 312.5844650971773, + "y": 0.018251983748557212, + "z": 0.0 + }, + { + "x": 311.58096205407463, + "y": 0.01878508142850443, + "z": 0.0 + }, + { + "x": 310.5774590109719, + "y": 0.019318179108451648, + "z": 0.0 + }, + { + "x": 309.5739559678692, + "y": 0.019851276788398868, + "z": 0.0 + }, + { + "x": 308.5704529247665, + "y": 0.020384374468346088, + "z": 0.0 + }, + { + "x": 307.5669498816638, + "y": 0.020917472148293304, + "z": 0.0 + }, + { + "x": 306.5634468385611, + "y": 0.021450569828240527, + "z": 0.0 + }, + { + "x": 305.5599437954584, + "y": 0.021983667508187747, + "z": 0.0 + }, + { + "x": 304.5564407523557, + "y": 0.022516765188134964, + "z": 0.0 + }, + { + "x": 303.552937709253, + "y": 0.023049862868082183, + "z": 0.0 + }, + { + "x": 302.5494346661503, + "y": 0.023582960548029403, + "z": 0.0 + }, + { + "x": 301.5459316230476, + "y": 0.024116058227976623, + "z": 0.0 + }, + { + "x": 300.5424285799449, + "y": 0.024649155907923843, + "z": 0.0 + }, + { + "x": 299.5389255368422, + "y": 0.025182253587871063, + "z": 0.0 + }, + { + "x": 298.5354224937395, + "y": 0.025715351267818282, + "z": 0.0 + }, + { + "x": 297.5319194506368, + "y": 0.0262484489477655, + "z": 0.0 + }, + { + "x": 296.5284164075341, + "y": 0.02678154662771272, + "z": 0.0 + }, + { + "x": 295.52491336443137, + "y": 0.027314644307659938, + "z": 0.0 + }, + { + "x": 294.5214103213287, + "y": 0.02784774198760716, + "z": 0.0 + }, + { + "x": 293.517907278226, + "y": 0.028380839667554406, + "z": 0.0 + }, + { + "x": 292.51440423512327, + "y": 0.02891393734750163, + "z": 0.0 + }, + { + "x": 291.5109011920206, + "y": 0.02944703502744885, + "z": 0.0 + }, + { + "x": 290.50739814271634, + "y": 0.029965544527589767, + "z": 0.0 + }, + { + "x": 289.503894970213, + "y": 0.03010311113933422, + "z": 0.0 + }, + { + "x": 288.5003917912253, + "y": 0.030210214295907045, + "z": 0.0 + }, + { + "x": 287.49688861223757, + "y": 0.03031731745247987, + "z": 0.0 + }, + { + "x": 286.4933854332499, + "y": 0.030424420609052698, + "z": 0.0 + }, + { + "x": 285.48988225426217, + "y": 0.030531523765625523, + "z": 0.0 + }, + { + "x": 284.48637907527444, + "y": 0.030638626922198348, + "z": 0.0 + }, + { + "x": 283.48287589628677, + "y": 0.030745730078771173, + "z": 0.0 + }, + { + "x": 282.47937271729904, + "y": 0.030852833235343997, + "z": 0.0 + }, + { + "x": 281.4758695383114, + "y": 0.030959936391916822, + "z": 0.0 + }, + { + "x": 280.47236635932364, + "y": 0.031067039548489647, + "z": 0.0 + }, + { + "x": 279.4688631803359, + "y": 0.031174142705062476, + "z": 0.0 + }, + { + "x": 278.46536000134824, + "y": 0.031281245861635304, + "z": 0.0 + }, + { + "x": 277.4618568223605, + "y": 0.031388349018208125, + "z": 0.0 + }, + { + "x": 276.45835364337285, + "y": 0.031495452174780954, + "z": 0.0 + }, + { + "x": 275.4548504643851, + "y": 0.031602555331353775, + "z": 0.0 + }, + { + "x": 274.4513472853974, + "y": 0.0317096584879266, + "z": 0.0 + }, + { + "x": 273.4478441064097, + "y": 0.03181676164449943, + "z": 0.0 + }, + { + "x": 272.444340927422, + "y": 0.03192386480107225, + "z": 0.0 + }, + { + "x": 271.44083774843426, + "y": 0.03203096795764508, + "z": 0.0 + }, + { + "x": 270.4373345694466, + "y": 0.0321380711142179, + "z": 0.0 + }, + { + "x": 269.43383139045886, + "y": 0.03224517427079073, + "z": 0.0 + }, + { + "x": 268.4303282114712, + "y": 0.03235227742736355, + "z": 0.0 + }, + { + "x": 267.42682503248346, + "y": 0.03245938058393638, + "z": 0.0 + }, + { + "x": 266.42332185349574, + "y": 0.0325664837405092, + "z": 0.0 + }, + { + "x": 265.41981867450806, + "y": 0.03267358689708203, + "z": 0.0 + }, + { + "x": 264.41631549552034, + "y": 0.03278069005365486, + "z": 0.0 + }, + { + "x": 263.41281231653267, + "y": 0.03288779321022768, + "z": 0.0 + }, + { + "x": 262.40930913754494, + "y": 0.03299489636680051, + "z": 0.0 + }, + { + "x": 261.4058059585572, + "y": 0.03310199952337333, + "z": 0.0 + }, + { + "x": 260.40230277956954, + "y": 0.03320910267994615, + "z": 0.0 + }, + { + "x": 259.3987996005818, + "y": 0.03331620583651898, + "z": 0.0 + }, + { + "x": 258.39529642159414, + "y": 0.03342330899309181, + "z": 0.0 + }, + { + "x": 257.3917932426064, + "y": 0.03353041214966463, + "z": 0.0 + }, + { + "x": 256.38829006361874, + "y": 0.03363751530623746, + "z": 0.0 + }, + { + "x": 255.38478688463104, + "y": 0.03374461846281028, + "z": 0.0 + }, + { + "x": 254.38128370564334, + "y": 0.0338517216193831, + "z": 0.0 + }, + { + "x": 253.37778052665564, + "y": 0.03395882477595593, + "z": 0.0 + }, + { + "x": 252.37427734766794, + "y": 0.03406592793252876, + "z": 0.0 + }, + { + "x": 251.37077416868024, + "y": 0.03417303108910158, + "z": 0.0 + }, + { + "x": 250.36727098969254, + "y": 0.03428013424567441, + "z": 0.0 + }, + { + "x": 249.36376781070484, + "y": 0.03438723740224723, + "z": 0.0 + }, + { + "x": 248.36026463171714, + "y": 0.03449434055882005, + "z": 0.0 + }, + { + "x": 247.35676145272944, + "y": 0.03460144371539288, + "z": 0.0 + }, + { + "x": 246.35325827374174, + "y": 0.0347085468719657, + "z": 0.0 + }, + { + "x": 245.34975509475404, + "y": 0.03481565002853853, + "z": 0.0 + }, + { + "x": 244.34625191576635, + "y": 0.03492275318511135, + "z": 0.0 + }, + { + "x": 243.34274873677865, + "y": 0.03502985634168418, + "z": 0.0 + }, + { + "x": 242.33924555779095, + "y": 0.035136959498257, + "z": 0.0 + }, + { + "x": 241.33574237880327, + "y": 0.03524406265482983, + "z": 0.0 + }, + { + "x": 240.33223919981555, + "y": 0.03535116581140265, + "z": 0.0 + }, + { + "x": 239.32873602082788, + "y": 0.03545826896797548, + "z": 0.0 + }, + { + "x": 238.32523284184015, + "y": 0.0355653721245483, + "z": 0.0 + }, + { + "x": 237.32172966285248, + "y": 0.03567247528112112, + "z": 0.0 + }, + { + "x": 236.31822648386475, + "y": 0.03577957843769394, + "z": 0.0 + }, + { + "x": 235.31472330487708, + "y": 0.03588668159426677, + "z": 0.0 + }, + { + "x": 234.31122012588938, + "y": 0.03599378475083959, + "z": 0.0 + }, + { + "x": 233.30771694690168, + "y": 0.03610088790741242, + "z": 0.0 + }, + { + "x": 232.30421376791398, + "y": 0.03620799106398524, + "z": 0.0 + }, + { + "x": 231.30071058892628, + "y": 0.03631509422055807, + "z": 0.0 + }, + { + "x": 230.29720740993858, + "y": 0.03642219737713089, + "z": 0.0 + }, + { + "x": 229.29370423095088, + "y": 0.03652930053370372, + "z": 0.0 + }, + { + "x": 228.29020105196318, + "y": 0.03663640369027654, + "z": 0.0 + }, + { + "x": 227.28669787297548, + "y": 0.036743506846849364, + "z": 0.0 + }, + { + "x": 226.28319469398778, + "y": 0.03685061000342219, + "z": 0.0 + }, + { + "x": 225.27969151500008, + "y": 0.03695771315999501, + "z": 0.0 + }, + { + "x": 224.27618833601238, + "y": 0.03706481631656784, + "z": 0.0 + }, + { + "x": 223.27268515702468, + "y": 0.03717191947314067, + "z": 0.0 + }, + { + "x": 222.26918197803698, + "y": 0.03727902262971349, + "z": 0.0 + }, + { + "x": 221.26567879904928, + "y": 0.03738612578628631, + "z": 0.0 + }, + { + "x": 220.26217562006158, + "y": 0.03749322894285914, + "z": 0.0 + }, + { + "x": 219.25867244107388, + "y": 0.03760033209943196, + "z": 0.0 + }, + { + "x": 218.25516926208618, + "y": 0.037707435256004784, + "z": 0.0 + }, + { + "x": 217.25166608309848, + "y": 0.03781453841257761, + "z": 0.0 + }, + { + "x": 216.24816290411079, + "y": 0.03792164156915044, + "z": 0.0 + }, + { + "x": 215.24465972512309, + "y": 0.03802874472572326, + "z": 0.0 + }, + { + "x": 214.2411565461354, + "y": 0.03813584788229609, + "z": 0.0 + }, + { + "x": 213.2376533671477, + "y": 0.03824295103886891, + "z": 0.0 + }, + { + "x": 212.23415018816, + "y": 0.038350054195441734, + "z": 0.0 + }, + { + "x": 211.2306470091723, + "y": 0.03845715735201456, + "z": 0.0 + }, + { + "x": 210.2271438301846, + "y": 0.03856426050858739, + "z": 0.0 + }, + { + "x": 209.2236406511969, + "y": 0.03867136366516021, + "z": 0.0 + }, + { + "x": 208.2201374722092, + "y": 0.03877846682173303, + "z": 0.0 + }, + { + "x": 207.2166342932215, + "y": 0.03888556997830586, + "z": 0.0 + }, + { + "x": 206.2131311142338, + "y": 0.03899267313487868, + "z": 0.0 + }, + { + "x": 205.2096279352461, + "y": 0.03909977629145151, + "z": 0.0 + }, + { + "x": 204.2061247562584, + "y": 0.03920687944802433, + "z": 0.0 + }, + { + "x": 203.20262157727072, + "y": 0.03931398260459716, + "z": 0.0 + }, + { + "x": 202.199118398283, + "y": 0.03942108576116999, + "z": 0.0 + }, + { + "x": 201.19561521929532, + "y": 0.03952818891774281, + "z": 0.0 + }, + { + "x": 200.1921120403076, + "y": 0.03963529207431563, + "z": 0.0 + }, + { + "x": 199.18860886131992, + "y": 0.03974239523088846, + "z": 0.0 + }, + { + "x": 198.1851056823322, + "y": 0.03984949838746128, + "z": 0.0 + }, + { + "x": 197.18160250334452, + "y": 0.03995660154403411, + "z": 0.0 + }, + { + "x": 196.1780993243568, + "y": 0.04006370470060694, + "z": 0.0 + }, + { + "x": 195.1745961453691, + "y": 0.04017080785717976, + "z": 0.0 + }, + { + "x": 194.17109296638137, + "y": 0.04027791101375259, + "z": 0.0 + }, + { + "x": 193.16758978739364, + "y": 0.04038501417032541, + "z": 0.0 + }, + { + "x": 192.16408660840594, + "y": 0.04049211732689824, + "z": 0.0 + }, + { + "x": 191.16058342941824, + "y": 0.04059922048347107, + "z": 0.0 + }, + { + "x": 190.15708025043054, + "y": 0.04070632364004389, + "z": 0.0 + }, + { + "x": 189.15357707144278, + "y": 0.04081342679661672, + "z": 0.0 + }, + { + "x": 188.15007389245508, + "y": 0.040920529953189545, + "z": 0.0 + }, + { + "x": 187.14657071346738, + "y": 0.041027633109762374, + "z": 0.0 + }, + { + "x": 186.14306753447968, + "y": 0.041134736266335195, + "z": 0.0 + }, + { + "x": 185.13956435549193, + "y": 0.04124183942290802, + "z": 0.0 + }, + { + "x": 184.13606117650423, + "y": 0.04134894257948085, + "z": 0.0 + }, + { + "x": 183.13255799751653, + "y": 0.04145604573605368, + "z": 0.0 + }, + { + "x": 182.12905481852883, + "y": 0.0415631488926265, + "z": 0.0 + }, + { + "x": 181.1255516395411, + "y": 0.04167025204919933, + "z": 0.0 + }, + { + "x": 180.12204846055337, + "y": 0.04177735520577216, + "z": 0.0 + }, + { + "x": 179.11854528156567, + "y": 0.04188445836234498, + "z": 0.0 + }, + { + "x": 178.11504210257797, + "y": 0.04199156151891781, + "z": 0.0 + }, + { + "x": 177.11153892359025, + "y": 0.04209866467549064, + "z": 0.0 + }, + { + "x": 176.10803574460255, + "y": 0.04220576783206346, + "z": 0.0 + }, + { + "x": 175.10453256561482, + "y": 0.042312870988636286, + "z": 0.0 + }, + { + "x": 174.10102938662712, + "y": 0.042419974145209115, + "z": 0.0 + }, + { + "x": 173.0975262076394, + "y": 0.042527077301781936, + "z": 0.0 + }, + { + "x": 172.0940230286517, + "y": 0.042634180458354765, + "z": 0.0 + }, + { + "x": 171.090519849664, + "y": 0.042741283614927586, + "z": 0.0 + }, + { + "x": 170.08701667067626, + "y": 0.042848386771500414, + "z": 0.0 + }, + { + "x": 169.08351349168854, + "y": 0.04295548992807324, + "z": 0.0 + }, + { + "x": 168.08001031270084, + "y": 0.043062593084646064, + "z": 0.0 + } + ] + }, + { + "id": 8, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 168.07979685417234, + "y": -1.956937395524218, + "z": 0.0 + }, + { + "x": 169.08330003316004, + "y": -1.957044498680791, + "z": 0.0 + }, + { + "x": 170.08680321214777, + "y": -1.9571516018373638, + "z": 0.0 + }, + { + "x": 171.0903063911355, + "y": -1.9572587049939367, + "z": 0.0 + }, + { + "x": 172.0938095701232, + "y": -1.9573658081505094, + "z": 0.0 + }, + { + "x": 173.0973127491109, + "y": -1.9574729113070821, + "z": 0.0 + }, + { + "x": 174.10081592809863, + "y": -1.957580014463655, + "z": 0.0 + }, + { + "x": 175.10431910708633, + "y": -1.957687117620228, + "z": 0.0 + }, + { + "x": 176.10782228607405, + "y": -1.9577942207768009, + "z": 0.0 + }, + { + "x": 177.11132546506175, + "y": -1.9579013239333733, + "z": 0.0 + }, + { + "x": 178.11482864404948, + "y": -1.9580084270899463, + "z": 0.0 + }, + { + "x": 179.11833182303718, + "y": -1.9581155302465192, + "z": 0.0 + }, + { + "x": 180.12183500202488, + "y": -1.958222633403092, + "z": 0.0 + }, + { + "x": 181.1253381810126, + "y": -1.958329736559665, + "z": 0.0 + }, + { + "x": 182.12884136000034, + "y": -1.9584368397162375, + "z": 0.0 + }, + { + "x": 183.13234453898804, + "y": -1.9585439428728104, + "z": 0.0 + }, + { + "x": 184.13584771797574, + "y": -1.9586510460293833, + "z": 0.0 + }, + { + "x": 185.13935089696344, + "y": -1.9587581491859563, + "z": 0.0 + }, + { + "x": 186.1428540759512, + "y": -1.9588652523425292, + "z": 0.0 + }, + { + "x": 187.1463572549389, + "y": -1.9589723554991016, + "z": 0.0 + }, + { + "x": 188.1498604339266, + "y": -1.9590794586556746, + "z": 0.0 + }, + { + "x": 189.1533636129143, + "y": -1.9591865618122475, + "z": 0.0 + }, + { + "x": 190.15686679190205, + "y": -1.9592936649688204, + "z": 0.0 + }, + { + "x": 191.16036997088975, + "y": -1.9594007681253933, + "z": 0.0 + }, + { + "x": 192.16387314987745, + "y": -1.9595078712819658, + "z": 0.0 + }, + { + "x": 193.16737632886515, + "y": -1.9596149744385387, + "z": 0.0 + }, + { + "x": 194.17087950785287, + "y": -1.9597220775951116, + "z": 0.0 + }, + { + "x": 195.1743826868406, + "y": -1.9598291807516846, + "z": 0.0 + }, + { + "x": 196.1778858658283, + "y": -1.9599362839082572, + "z": 0.0 + }, + { + "x": 197.18138904481603, + "y": -1.96004338706483, + "z": 0.0 + }, + { + "x": 198.1848922238037, + "y": -1.9601504902214029, + "z": 0.0 + }, + { + "x": 199.18839540279143, + "y": -1.9602575933779758, + "z": 0.0 + }, + { + "x": 200.1918985817791, + "y": -1.9603646965345487, + "z": 0.0 + }, + { + "x": 201.19540176076683, + "y": -1.9604717996911212, + "z": 0.0 + }, + { + "x": 202.1989049397545, + "y": -1.960578902847694, + "z": 0.0 + }, + { + "x": 203.20240811874223, + "y": -1.960686006004267, + "z": 0.0 + }, + { + "x": 204.2059112977299, + "y": -1.96079310916084, + "z": 0.0 + }, + { + "x": 205.2094144767176, + "y": -1.9609002123174128, + "z": 0.0 + }, + { + "x": 206.2129176557053, + "y": -1.9610073154739853, + "z": 0.0 + }, + { + "x": 207.216420834693, + "y": -1.9611144186305582, + "z": 0.0 + }, + { + "x": 208.2199240136807, + "y": -1.9612215217871312, + "z": 0.0 + }, + { + "x": 209.2234271926684, + "y": -1.961328624943704, + "z": 0.0 + }, + { + "x": 210.2269303716561, + "y": -1.961435728100277, + "z": 0.0 + }, + { + "x": 211.2304335506438, + "y": -1.9615428312568495, + "z": 0.0 + }, + { + "x": 212.2339367296315, + "y": -1.9616499344134224, + "z": 0.0 + }, + { + "x": 213.2374399086192, + "y": -1.9617570375699953, + "z": 0.0 + }, + { + "x": 214.2409430876069, + "y": -1.9618641407265682, + "z": 0.0 + }, + { + "x": 215.2444462665946, + "y": -1.961971243883141, + "z": 0.0 + }, + { + "x": 216.2479494455823, + "y": -1.9620783470397136, + "z": 0.0 + }, + { + "x": 217.25145262457, + "y": -1.9621854501962865, + "z": 0.0 + }, + { + "x": 218.2549558035577, + "y": -1.9622925533528595, + "z": 0.0 + }, + { + "x": 219.2584589825454, + "y": -1.9623996565094324, + "z": 0.0 + }, + { + "x": 220.2619621615331, + "y": -1.9625067596660049, + "z": 0.0 + }, + { + "x": 221.2654653405208, + "y": -1.9626138628225778, + "z": 0.0 + }, + { + "x": 222.2689685195085, + "y": -1.9627209659791507, + "z": 0.0 + }, + { + "x": 223.2724716984962, + "y": -1.9628280691357236, + "z": 0.0 + }, + { + "x": 224.2759748774839, + "y": -1.9629351722922965, + "z": 0.0 + }, + { + "x": 225.2794780564716, + "y": -1.963042275448869, + "z": 0.0 + }, + { + "x": 226.2829812354593, + "y": -1.963149378605442, + "z": 0.0 + }, + { + "x": 227.286484414447, + "y": -1.9632564817620148, + "z": 0.0 + }, + { + "x": 228.2899875934347, + "y": -1.9633635849185878, + "z": 0.0 + }, + { + "x": 229.2934907724224, + "y": -1.9634706880751607, + "z": 0.0 + }, + { + "x": 230.2969939514101, + "y": -1.9635777912317331, + "z": 0.0 + }, + { + "x": 231.3004971303978, + "y": -1.963684894388306, + "z": 0.0 + }, + { + "x": 232.3040003093855, + "y": -1.963791997544879, + "z": 0.0 + }, + { + "x": 233.3075034883732, + "y": -1.963899100701452, + "z": 0.0 + }, + { + "x": 234.31100666736089, + "y": -1.9640062038580244, + "z": 0.0 + }, + { + "x": 235.31450984634859, + "y": -1.9641133070145973, + "z": 0.0 + }, + { + "x": 236.31801302533626, + "y": -1.9642204101711702, + "z": 0.0 + }, + { + "x": 237.32151620432398, + "y": -1.9643275133277431, + "z": 0.0 + }, + { + "x": 238.32501938331166, + "y": -1.964434616484316, + "z": 0.0 + }, + { + "x": 239.32852256229938, + "y": -1.9645417196408885, + "z": 0.0 + }, + { + "x": 240.33202574128705, + "y": -1.9646488227974614, + "z": 0.0 + }, + { + "x": 241.33552892027478, + "y": -1.9647559259540344, + "z": 0.0 + }, + { + "x": 242.33903209926245, + "y": -1.9648630291106073, + "z": 0.0 + }, + { + "x": 243.34253527825015, + "y": -1.9649701322671802, + "z": 0.0 + }, + { + "x": 244.34603845723785, + "y": -1.9650772354237527, + "z": 0.0 + }, + { + "x": 245.34954163622555, + "y": -1.9651843385803256, + "z": 0.0 + }, + { + "x": 246.35304481521325, + "y": -1.9652914417368985, + "z": 0.0 + }, + { + "x": 247.35654799420095, + "y": -1.9653985448934714, + "z": 0.0 + }, + { + "x": 248.36005117318865, + "y": -1.9655056480500444, + "z": 0.0 + }, + { + "x": 249.36355435217635, + "y": -1.9656127512066168, + "z": 0.0 + }, + { + "x": 250.36705753116405, + "y": -1.9657198543631897, + "z": 0.0 + }, + { + "x": 251.37056071015175, + "y": -1.9658269575197627, + "z": 0.0 + }, + { + "x": 252.37406388913945, + "y": -1.9659340606763356, + "z": 0.0 + }, + { + "x": 253.37756706812715, + "y": -1.966041163832908, + "z": 0.0 + }, + { + "x": 254.38107024711485, + "y": -1.966148266989481, + "z": 0.0 + }, + { + "x": 255.38457342610255, + "y": -1.966255370146054, + "z": 0.0 + }, + { + "x": 256.38807660509025, + "y": -1.9663624733026268, + "z": 0.0 + }, + { + "x": 257.3915797840779, + "y": -1.9664695764591997, + "z": 0.0 + }, + { + "x": 258.39508296306565, + "y": -1.9665766796157722, + "z": 0.0 + }, + { + "x": 259.3985861420533, + "y": -1.9666837827723451, + "z": 0.0 + }, + { + "x": 260.40208932104105, + "y": -1.966790885928918, + "z": 0.0 + }, + { + "x": 261.4055925000287, + "y": -1.966897989085491, + "z": 0.0 + }, + { + "x": 262.40909567901645, + "y": -1.9670050922420639, + "z": 0.0 + }, + { + "x": 263.4125988580042, + "y": -1.9671121953986364, + "z": 0.0 + }, + { + "x": 264.41610203699184, + "y": -1.9672192985552093, + "z": 0.0 + }, + { + "x": 265.4196052159796, + "y": -1.9673264017117822, + "z": 0.0 + }, + { + "x": 266.42310839496724, + "y": -1.967433504868355, + "z": 0.0 + }, + { + "x": 267.426611573955, + "y": -1.9675406080249278, + "z": 0.0 + }, + { + "x": 268.4301147529427, + "y": -1.9676477111815005, + "z": 0.0 + }, + { + "x": 269.43361793193037, + "y": -1.9677548143380734, + "z": 0.0 + }, + { + "x": 270.4371211109181, + "y": -1.9678619174946463, + "z": 0.0 + }, + { + "x": 271.44062428990577, + "y": -1.9679690206512193, + "z": 0.0 + }, + { + "x": 272.4441274688935, + "y": -1.9680761238077917, + "z": 0.0 + }, + { + "x": 273.4476306478812, + "y": -1.9681832269643647, + "z": 0.0 + }, + { + "x": 274.4511338268689, + "y": -1.9682903301209376, + "z": 0.0 + }, + { + "x": 275.4546370058566, + "y": -1.9683974332775105, + "z": 0.0 + }, + { + "x": 276.45814018484435, + "y": -1.9685045364340834, + "z": 0.0 + }, + { + "x": 277.461643363832, + "y": -1.9686116395906559, + "z": 0.0 + }, + { + "x": 278.46514654281975, + "y": -1.9687187427472288, + "z": 0.0 + }, + { + "x": 279.4686497218074, + "y": -1.9688258459038017, + "z": 0.0 + }, + { + "x": 280.47215290079515, + "y": -1.9689329490603746, + "z": 0.0 + }, + { + "x": 281.4756560797829, + "y": -1.9690400522169476, + "z": 0.0 + }, + { + "x": 282.47915925877055, + "y": -1.96914715537352, + "z": 0.0 + }, + { + "x": 283.4826624377583, + "y": -1.969254258530093, + "z": 0.0 + }, + { + "x": 284.48616561674595, + "y": -1.9693613616866659, + "z": 0.0 + }, + { + "x": 285.4896687957337, + "y": -1.9694684648432388, + "z": 0.0 + }, + { + "x": 286.4931719747214, + "y": -1.9695755679998117, + "z": 0.0 + }, + { + "x": 287.4966751537091, + "y": -1.9696826711563842, + "z": 0.0 + }, + { + "x": 288.5001783326968, + "y": -1.969789774312957, + "z": 0.0 + }, + { + "x": 289.50368151168453, + "y": -1.96989687746953, + "z": 0.0 + }, + { + "x": 290.5071239699698, + "y": -1.9700344366797367, + "z": 0.0 + }, + { + "x": 291.50986779320573, + "y": -1.970552697994256, + "z": 0.0 + }, + { + "x": 292.51334176180364, + "y": -1.9710857804400896, + "z": 0.0 + }, + { + "x": 293.5168448049064, + "y": -1.9716188781200372, + "z": 0.0 + }, + { + "x": 294.52034784800907, + "y": -1.972151975799984, + "z": 0.0 + }, + { + "x": 295.52385089111175, + "y": -1.9726850734799315, + "z": 0.0 + }, + { + "x": 296.5273539342145, + "y": -1.973218171159879, + "z": 0.0 + }, + { + "x": 297.53085697731717, + "y": -1.9737512688398258, + "z": 0.0 + }, + { + "x": 298.53436002041985, + "y": -1.974284366519773, + "z": 0.0 + }, + { + "x": 299.5378630635226, + "y": -1.9748174641997205, + "z": 0.0 + }, + { + "x": 300.5413661066253, + "y": -1.9753505618796672, + "z": 0.0 + }, + { + "x": 301.54486914972796, + "y": -1.9758836595596148, + "z": 0.0 + }, + { + "x": 302.5483721928307, + "y": -1.9764167572395623, + "z": 0.0 + }, + { + "x": 303.5518752359334, + "y": -1.976949854919509, + "z": 0.0 + }, + { + "x": 304.55537827903606, + "y": -1.9774829525994562, + "z": 0.0 + }, + { + "x": 305.5588813221388, + "y": -1.9780160502794037, + "z": 0.0 + }, + { + "x": 306.5623843652415, + "y": -1.9785491479593509, + "z": 0.0 + }, + { + "x": 307.56588740834417, + "y": -1.979082245639298, + "z": 0.0 + }, + { + "x": 308.5693904514469, + "y": -1.9796153433192456, + "z": 0.0 + }, + { + "x": 309.5728934945496, + "y": -1.9801484409991923, + "z": 0.0 + }, + { + "x": 310.5763965376523, + "y": -1.9806815386791394, + "z": 0.0 + }, + { + "x": 311.579899580755, + "y": -1.9812146363590875, + "z": 0.0 + }, + { + "x": 312.5834026238577, + "y": -1.9817477340390341, + "z": 0.0 + }, + { + "x": 313.5869056669604, + "y": -1.9822808317189813, + "z": 0.0 + }, + { + "x": 314.59040871006306, + "y": -1.9828139293989284, + "z": 0.0 + }, + { + "x": 315.5939117531658, + "y": -1.983347027078876, + "z": 0.0 + }, + { + "x": 316.5974147962685, + "y": -1.9838801247588231, + "z": 0.0 + }, + { + "x": 317.60091783937116, + "y": -1.9844132224387703, + "z": 0.0 + }, + { + "x": 318.6044208824739, + "y": -1.9849463201187179, + "z": 0.0 + }, + { + "x": 319.6079239255766, + "y": -1.9854794177986645, + "z": 0.0 + }, + { + "x": 320.61142696867927, + "y": -1.9860125154786117, + "z": 0.0 + }, + { + "x": 321.614930011782, + "y": -1.9865456131585593, + "z": 0.0 + }, + { + "x": 322.6184330548847, + "y": -1.9870787108385064, + "z": 0.0 + }, + { + "x": 323.6219360979874, + "y": -1.9876118085184535, + "z": 0.0 + }, + { + "x": 324.6254391410901, + "y": -1.9881449061984011, + "z": 0.0 + }, + { + "x": 325.6289421841928, + "y": -1.9886780038783478, + "z": 0.0 + } + ] + }, + { + "id": 9, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.63106713083204, + "y": 2.0113214316968344, + "z": 0.0 + }, + { + "x": 324.62756408772935, + "y": 2.011854529376782, + "z": 0.0 + }, + { + "x": 323.6240610446266, + "y": 2.012387627056729, + "z": 0.0 + }, + { + "x": 322.62055800152393, + "y": 2.0129207247366763, + "z": 0.0 + }, + { + "x": 321.61705495842125, + "y": 2.013453822416624, + "z": 0.0 + }, + { + "x": 320.6135519153185, + "y": 2.0139869200965705, + "z": 0.0 + }, + { + "x": 319.6100488722158, + "y": 2.0145200177765177, + "z": 0.0 + }, + { + "x": 318.60654582911314, + "y": 2.0150531154564657, + "z": 0.0 + }, + { + "x": 317.6030427860104, + "y": 2.0155862131364124, + "z": 0.0 + }, + { + "x": 316.5995397429077, + "y": 2.0161193108163595, + "z": 0.0 + }, + { + "x": 315.59603669980504, + "y": 2.016652408496307, + "z": 0.0 + }, + { + "x": 314.5925336567023, + "y": 2.017185506176254, + "z": 0.0 + }, + { + "x": 313.5890306135996, + "y": 2.0177186038562014, + "z": 0.0 + }, + { + "x": 312.58552757049694, + "y": 2.0182517015361485, + "z": 0.0 + }, + { + "x": 311.58202452739425, + "y": 2.018784799216096, + "z": 0.0 + }, + { + "x": 310.5785214842915, + "y": 2.019317896896043, + "z": 0.0 + }, + { + "x": 309.57501844118883, + "y": 2.01985099457599, + "z": 0.0 + }, + { + "x": 308.57151539808615, + "y": 2.020384092255938, + "z": 0.0 + }, + { + "x": 307.5680123549834, + "y": 2.0209171899358847, + "z": 0.0 + }, + { + "x": 306.5645093118807, + "y": 2.021450287615832, + "z": 0.0 + }, + { + "x": 305.56100626877804, + "y": 2.0219833852957794, + "z": 0.0 + }, + { + "x": 304.5575032256753, + "y": 2.022516482975726, + "z": 0.0 + }, + { + "x": 303.5540001825726, + "y": 2.023049580655673, + "z": 0.0 + }, + { + "x": 302.55049713946994, + "y": 2.023582678335621, + "z": 0.0 + }, + { + "x": 301.5469940963672, + "y": 2.024115776015568, + "z": 0.0 + }, + { + "x": 300.5434910532645, + "y": 2.024648873695515, + "z": 0.0 + }, + { + "x": 299.53998801016184, + "y": 2.0251819713754626, + "z": 0.0 + }, + { + "x": 298.5364849670591, + "y": 2.0257150690554093, + "z": 0.0 + }, + { + "x": 297.5329819239564, + "y": 2.026248166735357, + "z": 0.0 + }, + { + "x": 296.52947888085373, + "y": 2.0267812644153045, + "z": 0.0 + }, + { + "x": 295.525975837751, + "y": 2.027314362095251, + "z": 0.0 + }, + { + "x": 294.5224727946483, + "y": 2.0278474597751983, + "z": 0.0 + }, + { + "x": 293.5189697515456, + "y": 2.028380557455146, + "z": 0.0 + }, + { + "x": 292.5154667084429, + "y": 2.028913655135093, + "z": 0.0 + }, + { + "x": 291.51193459083544, + "y": 2.0294467680491537, + "z": 0.0 + }, + { + "x": 290.50767231546286, + "y": 2.0299655257349163, + "z": 0.0 + }, + { + "x": 289.5041084287415, + "y": 2.0301030997481986, + "z": 0.0 + }, + { + "x": 288.5006052497538, + "y": 2.030210202904771, + "z": 0.0 + }, + { + "x": 287.49710207076606, + "y": 2.030317306061344, + "z": 0.0 + }, + { + "x": 286.4935988917784, + "y": 2.030424409217917, + "z": 0.0 + }, + { + "x": 285.49009571279066, + "y": 2.03053151237449, + "z": 0.0 + }, + { + "x": 284.48659253380293, + "y": 2.0306386155310627, + "z": 0.0 + }, + { + "x": 283.48308935481526, + "y": 2.030745718687635, + "z": 0.0 + }, + { + "x": 282.47958617582754, + "y": 2.030852821844208, + "z": 0.0 + }, + { + "x": 281.47608299683986, + "y": 2.030959925000781, + "z": 0.0 + }, + { + "x": 280.47257981785214, + "y": 2.031067028157354, + "z": 0.0 + }, + { + "x": 279.4690766388644, + "y": 2.031174131313927, + "z": 0.0 + }, + { + "x": 278.46557345987674, + "y": 2.0312812344704994, + "z": 0.0 + }, + { + "x": 277.462070280889, + "y": 2.0313883376270723, + "z": 0.0 + }, + { + "x": 276.45856710190134, + "y": 2.031495440783645, + "z": 0.0 + }, + { + "x": 275.4550639229136, + "y": 2.031602543940218, + "z": 0.0 + }, + { + "x": 274.4515607439259, + "y": 2.0317096470967906, + "z": 0.0 + }, + { + "x": 273.4480575649382, + "y": 2.0318167502533635, + "z": 0.0 + }, + { + "x": 272.4445543859505, + "y": 2.0319238534099364, + "z": 0.0 + }, + { + "x": 271.44105120696275, + "y": 2.0320309565665093, + "z": 0.0 + }, + { + "x": 270.4375480279751, + "y": 2.0321380597230823, + "z": 0.0 + }, + { + "x": 269.43404484898736, + "y": 2.0322451628796547, + "z": 0.0 + }, + { + "x": 268.4305416699997, + "y": 2.0323522660362277, + "z": 0.0 + }, + { + "x": 267.42703849101196, + "y": 2.0324593691928006, + "z": 0.0 + }, + { + "x": 266.4235353120242, + "y": 2.0325664723493735, + "z": 0.0 + }, + { + "x": 265.42003213303656, + "y": 2.0326735755059464, + "z": 0.0 + }, + { + "x": 264.41652895404883, + "y": 2.032780678662519, + "z": 0.0 + }, + { + "x": 263.41302577506116, + "y": 2.032887781819092, + "z": 0.0 + }, + { + "x": 262.40952259607343, + "y": 2.0329948849756647, + "z": 0.0 + }, + { + "x": 261.4060194170857, + "y": 2.0331019881322376, + "z": 0.0 + }, + { + "x": 260.40251623809803, + "y": 2.0332090912888106, + "z": 0.0 + }, + { + "x": 259.3990130591103, + "y": 2.033316194445383, + "z": 0.0 + }, + { + "x": 258.39550988012263, + "y": 2.033423297601956, + "z": 0.0 + }, + { + "x": 257.3920067011349, + "y": 2.033530400758529, + "z": 0.0 + }, + { + "x": 256.38850352214723, + "y": 2.033637503915102, + "z": 0.0 + }, + { + "x": 255.38500034315953, + "y": 2.0337446070716743, + "z": 0.0 + }, + { + "x": 254.38149716417183, + "y": 2.033851710228247, + "z": 0.0 + }, + { + "x": 253.37799398518413, + "y": 2.03395881338482, + "z": 0.0 + }, + { + "x": 252.37449080619643, + "y": 2.034065916541393, + "z": 0.0 + }, + { + "x": 251.37098762720873, + "y": 2.034173019697966, + "z": 0.0 + }, + { + "x": 250.36748444822103, + "y": 2.0342801228545384, + "z": 0.0 + }, + { + "x": 249.36398126923334, + "y": 2.0343872260111113, + "z": 0.0 + }, + { + "x": 248.36047809024564, + "y": 2.0344943291676842, + "z": 0.0 + }, + { + "x": 247.35697491125794, + "y": 2.034601432324257, + "z": 0.0 + }, + { + "x": 246.35347173227024, + "y": 2.03470853548083, + "z": 0.0 + }, + { + "x": 245.34996855328254, + "y": 2.0348156386374026, + "z": 0.0 + }, + { + "x": 244.34646537429484, + "y": 2.0349227417939755, + "z": 0.0 + }, + { + "x": 243.34296219530714, + "y": 2.0350298449505484, + "z": 0.0 + }, + { + "x": 242.33945901631944, + "y": 2.0351369481071213, + "z": 0.0 + }, + { + "x": 241.33595583733177, + "y": 2.0352440512636942, + "z": 0.0 + }, + { + "x": 240.33245265834404, + "y": 2.0353511544202667, + "z": 0.0 + }, + { + "x": 239.32894947935637, + "y": 2.0354582575768396, + "z": 0.0 + }, + { + "x": 238.32544630036864, + "y": 2.0355653607334125, + "z": 0.0 + }, + { + "x": 237.32194312138097, + "y": 2.0356724638899855, + "z": 0.0 + }, + { + "x": 236.31843994239324, + "y": 2.035779567046558, + "z": 0.0 + }, + { + "x": 235.31493676340557, + "y": 2.035886670203131, + "z": 0.0 + }, + { + "x": 234.31143358441787, + "y": 2.0359937733597038, + "z": 0.0 + }, + { + "x": 233.30793040543017, + "y": 2.0361008765162767, + "z": 0.0 + }, + { + "x": 232.30442722644247, + "y": 2.0362079796728496, + "z": 0.0 + }, + { + "x": 231.30092404745477, + "y": 2.036315082829422, + "z": 0.0 + }, + { + "x": 230.29742086846707, + "y": 2.036422185985995, + "z": 0.0 + }, + { + "x": 229.29391768947937, + "y": 2.036529289142568, + "z": 0.0 + }, + { + "x": 228.29041451049167, + "y": 2.036636392299141, + "z": 0.0 + }, + { + "x": 227.28691133150397, + "y": 2.0367434954557138, + "z": 0.0 + }, + { + "x": 226.28340815251627, + "y": 2.0368505986122862, + "z": 0.0 + }, + { + "x": 225.27990497352857, + "y": 2.036957701768859, + "z": 0.0 + }, + { + "x": 224.27640179454087, + "y": 2.037064804925432, + "z": 0.0 + }, + { + "x": 223.27289861555317, + "y": 2.037171908082005, + "z": 0.0 + }, + { + "x": 222.26939543656547, + "y": 2.0372790112385775, + "z": 0.0 + }, + { + "x": 221.26589225757778, + "y": 2.0373861143951504, + "z": 0.0 + }, + { + "x": 220.26238907859008, + "y": 2.0374932175517233, + "z": 0.0 + }, + { + "x": 219.25888589960238, + "y": 2.037600320708296, + "z": 0.0 + }, + { + "x": 218.25538272061468, + "y": 2.037707423864869, + "z": 0.0 + }, + { + "x": 217.25187954162698, + "y": 2.0378145270214416, + "z": 0.0 + }, + { + "x": 216.24837636263928, + "y": 2.0379216301780145, + "z": 0.0 + }, + { + "x": 215.24487318365158, + "y": 2.0380287333345875, + "z": 0.0 + }, + { + "x": 214.24137000466388, + "y": 2.0381358364911604, + "z": 0.0 + }, + { + "x": 213.23786682567618, + "y": 2.0382429396477333, + "z": 0.0 + }, + { + "x": 212.23436364668848, + "y": 2.0383500428043058, + "z": 0.0 + }, + { + "x": 211.23086046770078, + "y": 2.0384571459608787, + "z": 0.0 + }, + { + "x": 210.22735728871308, + "y": 2.0385642491174516, + "z": 0.0 + }, + { + "x": 209.22385410972538, + "y": 2.0386713522740245, + "z": 0.0 + }, + { + "x": 208.22035093073768, + "y": 2.0387784554305974, + "z": 0.0 + }, + { + "x": 207.21684775174998, + "y": 2.03888555858717, + "z": 0.0 + }, + { + "x": 206.21334457276228, + "y": 2.038992661743743, + "z": 0.0 + }, + { + "x": 205.20984139377458, + "y": 2.0390997649003157, + "z": 0.0 + }, + { + "x": 204.20633821478688, + "y": 2.0392068680568887, + "z": 0.0 + }, + { + "x": 203.2028350357992, + "y": 2.039313971213461, + "z": 0.0 + }, + { + "x": 202.19933185681148, + "y": 2.039421074370034, + "z": 0.0 + }, + { + "x": 201.1958286778238, + "y": 2.039528177526607, + "z": 0.0 + }, + { + "x": 200.19232549883608, + "y": 2.03963528068318, + "z": 0.0 + }, + { + "x": 199.1888223198484, + "y": 2.039742383839753, + "z": 0.0 + }, + { + "x": 198.18531914086068, + "y": 2.0398494869963253, + "z": 0.0 + }, + { + "x": 197.181815961873, + "y": 2.039956590152898, + "z": 0.0 + }, + { + "x": 196.17831278288529, + "y": 2.040063693309471, + "z": 0.0 + }, + { + "x": 195.1748096038976, + "y": 2.040170796466044, + "z": 0.0 + }, + { + "x": 194.17130642490986, + "y": 2.040277899622617, + "z": 0.0 + }, + { + "x": 193.16780324592213, + "y": 2.0403850027791894, + "z": 0.0 + }, + { + "x": 192.16430006693443, + "y": 2.0404921059357624, + "z": 0.0 + }, + { + "x": 191.16079688794673, + "y": 2.0405992090923353, + "z": 0.0 + }, + { + "x": 190.15729370895903, + "y": 2.040706312248908, + "z": 0.0 + }, + { + "x": 189.15379052997127, + "y": 2.040813415405481, + "z": 0.0 + }, + { + "x": 188.15028735098358, + "y": 2.0409205185620536, + "z": 0.0 + }, + { + "x": 187.14678417199588, + "y": 2.0410276217186265, + "z": 0.0 + }, + { + "x": 186.14328099300818, + "y": 2.0411347248751994, + "z": 0.0 + }, + { + "x": 185.13977781402042, + "y": 2.0412418280317723, + "z": 0.0 + }, + { + "x": 184.13627463503272, + "y": 2.0413489311883453, + "z": 0.0 + }, + { + "x": 183.13277145604502, + "y": 2.0414560343449177, + "z": 0.0 + }, + { + "x": 182.12926827705732, + "y": 2.0415631375014907, + "z": 0.0 + }, + { + "x": 181.1257650980696, + "y": 2.0416702406580636, + "z": 0.0 + }, + { + "x": 180.12226191908186, + "y": 2.0417773438146365, + "z": 0.0 + }, + { + "x": 179.11875874009417, + "y": 2.041884446971209, + "z": 0.0 + }, + { + "x": 178.11525556110647, + "y": 2.041991550127782, + "z": 0.0 + }, + { + "x": 177.11175238211874, + "y": 2.042098653284355, + "z": 0.0 + }, + { + "x": 176.10824920313104, + "y": 2.0422057564409277, + "z": 0.0 + }, + { + "x": 175.1047460241433, + "y": 2.0423128595975006, + "z": 0.0 + }, + { + "x": 174.1012428451556, + "y": 2.042419962754073, + "z": 0.0 + }, + { + "x": 173.09773966616788, + "y": 2.042527065910646, + "z": 0.0 + }, + { + "x": 172.09423648718018, + "y": 2.042634169067219, + "z": 0.0 + }, + { + "x": 171.09073330819248, + "y": 2.042741272223792, + "z": 0.0 + }, + { + "x": 170.08723012920476, + "y": 2.042848375380365, + "z": 0.0 + }, + { + "x": 169.08372695021703, + "y": 2.0429554785369373, + "z": 0.0 + }, + { + "x": 168.08022377122933, + "y": 2.04306258169351, + "z": 0.0 + } + ] + }, + { + "id": 10, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 102.69023839464042, + "y": -3.9501270959538224, + "z": 0.0 + }, + { + "x": 103.72121398505429, + "y": -3.9500684091140976, + "z": 0.0 + }, + { + "x": 104.75153497744992, + "y": -3.9501784280806986, + "z": 0.0 + }, + { + "x": 105.78251058129288, + "y": -3.95028846334898, + "z": 0.0 + }, + { + "x": 106.81348618513584, + "y": -3.9503984986172616, + "z": 0.0 + }, + { + "x": 107.8444617889788, + "y": -3.950508533885543, + "z": 0.0 + }, + { + "x": 108.87543739282175, + "y": -3.9506185691538245, + "z": 0.0 + }, + { + "x": 109.90641299666471, + "y": -3.950728604422106, + "z": 0.0 + }, + { + "x": 110.93738860050767, + "y": -3.9508386396903874, + "z": 0.0 + }, + { + "x": 111.96836420435064, + "y": -3.950948674958669, + "z": 0.0 + }, + { + "x": 112.9993398081936, + "y": -3.95105871022695, + "z": 0.0 + }, + { + "x": 114.03031541203654, + "y": -3.9511687454952313, + "z": 0.0 + }, + { + "x": 115.0612910158795, + "y": -3.951278780763513, + "z": 0.0 + }, + { + "x": 116.09226661972247, + "y": -3.9513888160317943, + "z": 0.0 + }, + { + "x": 117.12324222356543, + "y": -3.9514988513000757, + "z": 0.0 + }, + { + "x": 118.15421782740839, + "y": -3.951608886568357, + "z": 0.0 + }, + { + "x": 119.18519343125135, + "y": -3.9517189218366386, + "z": 0.0 + }, + { + "x": 120.2161690350943, + "y": -3.95182895710492, + "z": 0.0 + }, + { + "x": 121.24714463893727, + "y": -3.951938992373201, + "z": 0.0 + }, + { + "x": 122.27812024278022, + "y": -3.9520490276414826, + "z": 0.0 + }, + { + "x": 123.30909584662318, + "y": -3.952159062909764, + "z": 0.0 + }, + { + "x": 124.34007145046614, + "y": -3.9522690981780455, + "z": 0.0 + }, + { + "x": 125.3710470543091, + "y": -3.952379133446327, + "z": 0.0 + }, + { + "x": 126.40202265815206, + "y": -3.9524891687146084, + "z": 0.0 + }, + { + "x": 127.43299826199502, + "y": -3.95259920398289, + "z": 0.0 + }, + { + "x": 128.46397386583797, + "y": -3.9527092392511713, + "z": 0.0 + }, + { + "x": 129.49494946968093, + "y": -3.9528192745194524, + "z": 0.0 + }, + { + "x": 130.5259250735239, + "y": -3.952929309787734, + "z": 0.0 + }, + { + "x": 131.55690067736685, + "y": -3.9530393450560153, + "z": 0.0 + }, + { + "x": 132.58787628120982, + "y": -3.9531493803242967, + "z": 0.0 + }, + { + "x": 133.61885188505278, + "y": -3.953259415592578, + "z": 0.0 + }, + { + "x": 134.64982748889574, + "y": -3.9533694508608597, + "z": 0.0 + }, + { + "x": 135.6808030927387, + "y": -3.953479486129141, + "z": 0.0 + }, + { + "x": 136.71177869658166, + "y": -3.9535895213974226, + "z": 0.0 + }, + { + "x": 137.74275430042462, + "y": -3.9536995566657036, + "z": 0.0 + }, + { + "x": 138.77372990426755, + "y": -3.953809591933985, + "z": 0.0 + }, + { + "x": 139.80470550811052, + "y": -3.9539196272022665, + "z": 0.0 + }, + { + "x": 140.83568111195348, + "y": -3.954029662470548, + "z": 0.0 + }, + { + "x": 141.86665671579644, + "y": -3.9541396977388295, + "z": 0.0 + }, + { + "x": 142.8976323196394, + "y": -3.954249733007111, + "z": 0.0 + }, + { + "x": 143.92860792348236, + "y": -3.9543597682753924, + "z": 0.0 + }, + { + "x": 144.95958352732532, + "y": -3.954469803543674, + "z": 0.0 + } + ] + }, + { + "id": 11, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 144.9604373614393, + "y": 4.045530150891783, + "z": 0.0 + }, + { + "x": 143.92946175759633, + "y": 4.045640186160065, + "z": 0.0 + }, + { + "x": 142.89848615375337, + "y": 4.045750221428346, + "z": 0.0 + }, + { + "x": 141.8675105499104, + "y": 4.045860256696628, + "z": 0.0 + }, + { + "x": 140.83653494606745, + "y": 4.045970291964909, + "z": 0.0 + }, + { + "x": 139.80555934222448, + "y": 4.04608032723319, + "z": 0.0 + }, + { + "x": 138.77458373838152, + "y": 4.046190362501472, + "z": 0.0 + }, + { + "x": 137.7436081345386, + "y": 4.046300397769753, + "z": 0.0 + }, + { + "x": 136.71263253069563, + "y": 4.046410433038035, + "z": 0.0 + }, + { + "x": 135.68165692685267, + "y": 4.046520468306316, + "z": 0.0 + }, + { + "x": 134.6506813230097, + "y": 4.0466305035745975, + "z": 0.0 + }, + { + "x": 133.61970571916675, + "y": 4.0467405388428785, + "z": 0.0 + }, + { + "x": 132.58873011532378, + "y": 4.04685057411116, + "z": 0.0 + }, + { + "x": 131.55775451148082, + "y": 4.0469606093794415, + "z": 0.0 + }, + { + "x": 130.52677890763786, + "y": 4.0470706446477225, + "z": 0.0 + }, + { + "x": 129.4958033037949, + "y": 4.047180679916004, + "z": 0.0 + }, + { + "x": 128.46482769995194, + "y": 4.047290715184285, + "z": 0.0 + }, + { + "x": 127.43385209610899, + "y": 4.047400750452567, + "z": 0.0 + }, + { + "x": 126.40287649226603, + "y": 4.047510785720848, + "z": 0.0 + }, + { + "x": 125.37190088842307, + "y": 4.04762082098913, + "z": 0.0 + }, + { + "x": 124.34092528458011, + "y": 4.047730856257411, + "z": 0.0 + }, + { + "x": 123.30994968073715, + "y": 4.047840891525692, + "z": 0.0 + }, + { + "x": 122.27897407689419, + "y": 4.047950926793974, + "z": 0.0 + }, + { + "x": 121.24799847305124, + "y": 4.048060962062255, + "z": 0.0 + }, + { + "x": 120.21702286920826, + "y": 4.048170997330537, + "z": 0.0 + }, + { + "x": 119.18604726536532, + "y": 4.048281032598818, + "z": 0.0 + }, + { + "x": 118.15507166152236, + "y": 4.0483910678671, + "z": 0.0 + }, + { + "x": 117.1240960576794, + "y": 4.048501103135381, + "z": 0.0 + }, + { + "x": 116.09312045383643, + "y": 4.048611138403662, + "z": 0.0 + }, + { + "x": 115.06214484999347, + "y": 4.048721173671944, + "z": 0.0 + }, + { + "x": 114.03116924615051, + "y": 4.048831208940225, + "z": 0.0 + }, + { + "x": 113.00019364230756, + "y": 4.048941244208507, + "z": 0.0 + }, + { + "x": 111.9692180384646, + "y": 4.049051279476788, + "z": 0.0 + }, + { + "x": 110.93824243462164, + "y": 4.04916131474507, + "z": 0.0 + }, + { + "x": 109.90726683077868, + "y": 4.049271350013351, + "z": 0.0 + }, + { + "x": 108.87629122693572, + "y": 4.049381385281633, + "z": 0.0 + }, + { + "x": 107.84531562309277, + "y": 4.049491420549914, + "z": 0.0 + }, + { + "x": 106.81434001924981, + "y": 4.049601455818195, + "z": 0.0 + }, + { + "x": 105.78336441540685, + "y": 4.049711491086477, + "z": 0.0 + }, + { + "x": 104.75238881156389, + "y": 4.049821526354758, + "z": 0.0 + }, + { + "x": 103.7207585962736, + "y": 4.04993157792472, + "z": 0.0 + }, + { + "x": 102.68978300585972, + "y": 4.049872891084994, + "z": 0.0 + } + ] + }, + { + "id": 12, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 144.9600104443823, + "y": 0.04553017367405473, + "z": 0.0 + }, + { + "x": 143.92903484053934, + "y": 0.045640208942336134, + "z": 0.0 + }, + { + "x": 142.89805923669638, + "y": 0.04575024421061754, + "z": 0.0 + }, + { + "x": 141.86708363285342, + "y": 0.045860279478898945, + "z": 0.0 + }, + { + "x": 140.83610802901046, + "y": 0.04597031474718035, + "z": 0.0 + }, + { + "x": 139.8051324251675, + "y": 0.046080350015461756, + "z": 0.0 + }, + { + "x": 138.77415682132454, + "y": 0.04619038528374317, + "z": 0.0 + }, + { + "x": 137.7431812174816, + "y": 0.04630042055202457, + "z": 0.0 + }, + { + "x": 136.71220561363864, + "y": 0.04641045582030597, + "z": 0.0 + }, + { + "x": 135.68123000979568, + "y": 0.046520491088587385, + "z": 0.0 + }, + { + "x": 134.65025440595272, + "y": 0.04663052635686878, + "z": 0.0 + }, + { + "x": 133.61927880210976, + "y": 0.04674056162515019, + "z": 0.0 + }, + { + "x": 132.5883031982668, + "y": 0.0468505968934316, + "z": 0.0 + }, + { + "x": 131.55732759442384, + "y": 0.046960632161713, + "z": 0.0 + }, + { + "x": 130.52635199058088, + "y": 0.047070667429994405, + "z": 0.0 + }, + { + "x": 129.49537638673792, + "y": 0.04718070269827582, + "z": 0.0 + }, + { + "x": 128.46440078289496, + "y": 0.04729073796655722, + "z": 0.0 + }, + { + "x": 127.43342517905201, + "y": 0.04740077323483862, + "z": 0.0 + }, + { + "x": 126.40244957520905, + "y": 0.047510808503120026, + "z": 0.0 + }, + { + "x": 125.37147397136609, + "y": 0.04762084377140144, + "z": 0.0 + }, + { + "x": 124.34049836752313, + "y": 0.047730879039682844, + "z": 0.0 + }, + { + "x": 123.30952276368016, + "y": 0.04784091430796424, + "z": 0.0 + }, + { + "x": 122.2785471598372, + "y": 0.047950949576245655, + "z": 0.0 + }, + { + "x": 121.24757155599426, + "y": 0.04806098484452706, + "z": 0.0 + }, + { + "x": 120.21659595215128, + "y": 0.048171020112808466, + "z": 0.0 + }, + { + "x": 119.18562034830833, + "y": 0.04828105538108987, + "z": 0.0 + }, + { + "x": 118.15464474446537, + "y": 0.04839109064937128, + "z": 0.0 + }, + { + "x": 117.12366914062241, + "y": 0.04850112591765268, + "z": 0.0 + }, + { + "x": 116.09269353677945, + "y": 0.04861116118593409, + "z": 0.0 + }, + { + "x": 115.06171793293649, + "y": 0.04872119645421549, + "z": 0.0 + }, + { + "x": 114.03074232909353, + "y": 0.0488312317224969, + "z": 0.0 + }, + { + "x": 112.99976672525058, + "y": 0.048941266990778304, + "z": 0.0 + }, + { + "x": 111.96879112140762, + "y": 0.04905130225905971, + "z": 0.0 + }, + { + "x": 110.93781551756466, + "y": 0.049161337527341115, + "z": 0.0 + }, + { + "x": 109.9068399137217, + "y": 0.04927137279562252, + "z": 0.0 + }, + { + "x": 108.87586430987874, + "y": 0.049381408063903925, + "z": 0.0 + }, + { + "x": 107.84488870603579, + "y": 0.04949144333218533, + "z": 0.0 + }, + { + "x": 106.81391310219283, + "y": 0.049601478600466736, + "z": 0.0 + }, + { + "x": 105.78293749834987, + "y": 0.04971151386874814, + "z": 0.0 + }, + { + "x": 104.7519618945069, + "y": 0.04982154913702955, + "z": 0.0 + }, + { + "x": 103.72098629066394, + "y": 0.04993158440531095, + "z": 0.0 + }, + { + "x": 102.69001070025007, + "y": 0.04987289756558597, + "z": 0.0 + } + ] + }, + { + "id": 13, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 102.69012454744524, + "y": -1.9501270991941182, + "z": 0.0 + }, + { + "x": 103.72110013785911, + "y": -1.9500684123543934, + "z": 0.0 + }, + { + "x": 104.75174843597841, + "y": -1.9501784394718344, + "z": 0.0 + }, + { + "x": 105.78272403982137, + "y": -1.950288474740116, + "z": 0.0 + }, + { + "x": 106.81369964366434, + "y": -1.9503985100083974, + "z": 0.0 + }, + { + "x": 107.8446752475073, + "y": -1.9505085452766788, + "z": 0.0 + }, + { + "x": 108.87565085135024, + "y": -1.9506185805449603, + "z": 0.0 + }, + { + "x": 109.9066264551932, + "y": -1.9507286158132418, + "z": 0.0 + }, + { + "x": 110.93760205903617, + "y": -1.9508386510815232, + "z": 0.0 + }, + { + "x": 111.96857766287913, + "y": -1.9509486863498047, + "z": 0.0 + }, + { + "x": 112.99955326672209, + "y": -1.9510587216180857, + "z": 0.0 + }, + { + "x": 114.03052887056504, + "y": -1.9511687568863672, + "z": 0.0 + }, + { + "x": 115.061504474408, + "y": -1.9512787921546486, + "z": 0.0 + }, + { + "x": 116.09248007825096, + "y": -1.95138882742293, + "z": 0.0 + }, + { + "x": 117.12345568209392, + "y": -1.9514988626912115, + "z": 0.0 + }, + { + "x": 118.15443128593688, + "y": -1.951608897959493, + "z": 0.0 + }, + { + "x": 119.18540688977984, + "y": -1.9517189332277745, + "z": 0.0 + }, + { + "x": 120.21638249362279, + "y": -1.951828968496056, + "z": 0.0 + }, + { + "x": 121.24735809746576, + "y": -1.951939003764337, + "z": 0.0 + }, + { + "x": 122.27833370130871, + "y": -1.9520490390326184, + "z": 0.0 + }, + { + "x": 123.30930930515167, + "y": -1.9521590743008999, + "z": 0.0 + }, + { + "x": 124.34028490899463, + "y": -1.9522691095691813, + "z": 0.0 + }, + { + "x": 125.3712605128376, + "y": -1.9523791448374628, + "z": 0.0 + }, + { + "x": 126.40223611668056, + "y": -1.9524891801057442, + "z": 0.0 + }, + { + "x": 127.43321172052352, + "y": -1.9525992153740257, + "z": 0.0 + }, + { + "x": 128.46418732436646, + "y": -1.9527092506423072, + "z": 0.0 + }, + { + "x": 129.49516292820942, + "y": -1.9528192859105882, + "z": 0.0 + }, + { + "x": 130.52613853205239, + "y": -1.9529293211788696, + "z": 0.0 + }, + { + "x": 131.55711413589535, + "y": -1.953039356447151, + "z": 0.0 + }, + { + "x": 132.5880897397383, + "y": -1.9531493917154326, + "z": 0.0 + }, + { + "x": 133.61906534358127, + "y": -1.953259426983714, + "z": 0.0 + }, + { + "x": 134.65004094742423, + "y": -1.9533694622519955, + "z": 0.0 + }, + { + "x": 135.6810165512672, + "y": -1.953479497520277, + "z": 0.0 + }, + { + "x": 136.71199215511015, + "y": -1.9535895327885584, + "z": 0.0 + }, + { + "x": 137.7429677589531, + "y": -1.9536995680568394, + "z": 0.0 + }, + { + "x": 138.77394336279605, + "y": -1.9538096033251209, + "z": 0.0 + }, + { + "x": 139.804918966639, + "y": -1.9539196385934023, + "z": 0.0 + }, + { + "x": 140.83589457048197, + "y": -1.9540296738616838, + "z": 0.0 + }, + { + "x": 141.86687017432493, + "y": -1.9541397091299653, + "z": 0.0 + }, + { + "x": 142.8978457781679, + "y": -1.9542497443982467, + "z": 0.0 + }, + { + "x": 143.92882138201085, + "y": -1.9543597796665282, + "z": 0.0 + }, + { + "x": 144.9597969858538, + "y": -1.9544698149348096, + "z": 0.0 + } + ] + }, + { + "id": 14, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 144.9602239029108, + "y": 2.045530162282919, + "z": 0.0 + }, + { + "x": 143.92924829906784, + "y": 2.0456401975512004, + "z": 0.0 + }, + { + "x": 142.89827269522488, + "y": 2.045750232819482, + "z": 0.0 + }, + { + "x": 141.86729709138191, + "y": 2.0458602680877633, + "z": 0.0 + }, + { + "x": 140.83632148753895, + "y": 2.0459703033560444, + "z": 0.0 + }, + { + "x": 139.805345883696, + "y": 2.046080338624326, + "z": 0.0 + }, + { + "x": 138.77437027985303, + "y": 2.0461903738926073, + "z": 0.0 + }, + { + "x": 137.7433946760101, + "y": 2.0463004091608887, + "z": 0.0 + }, + { + "x": 136.71241907216714, + "y": 2.04641044442917, + "z": 0.0 + }, + { + "x": 135.68144346832418, + "y": 2.0465204796974517, + "z": 0.0 + }, + { + "x": 134.65046786448121, + "y": 2.046630514965733, + "z": 0.0 + }, + { + "x": 133.61949226063825, + "y": 2.0467405502340146, + "z": 0.0 + }, + { + "x": 132.5885166567953, + "y": 2.0468505855022956, + "z": 0.0 + }, + { + "x": 131.55754105295233, + "y": 2.046960620770577, + "z": 0.0 + }, + { + "x": 130.52656544910937, + "y": 2.0470706560388585, + "z": 0.0 + }, + { + "x": 129.4955898452664, + "y": 2.04718069130714, + "z": 0.0 + }, + { + "x": 128.46461424142345, + "y": 2.0472907265754214, + "z": 0.0 + }, + { + "x": 127.4336386375805, + "y": 2.047400761843703, + "z": 0.0 + }, + { + "x": 126.40266303373754, + "y": 2.0475107971119844, + "z": 0.0 + }, + { + "x": 125.37168742989458, + "y": 2.047620832380266, + "z": 0.0 + }, + { + "x": 124.34071182605162, + "y": 2.047730867648547, + "z": 0.0 + }, + { + "x": 123.30973622220866, + "y": 2.0478409029168283, + "z": 0.0 + }, + { + "x": 122.2787606183657, + "y": 2.0479509381851098, + "z": 0.0 + }, + { + "x": 121.24778501452275, + "y": 2.048060973453391, + "z": 0.0 + }, + { + "x": 120.21680941067977, + "y": 2.0481710087216727, + "z": 0.0 + }, + { + "x": 119.18583380683683, + "y": 2.048281043989954, + "z": 0.0 + }, + { + "x": 118.15485820299386, + "y": 2.0483910792582356, + "z": 0.0 + }, + { + "x": 117.1238825991509, + "y": 2.048501114526517, + "z": 0.0 + }, + { + "x": 116.09290699530794, + "y": 2.048611149794798, + "z": 0.0 + }, + { + "x": 115.06193139146498, + "y": 2.0487211850630795, + "z": 0.0 + }, + { + "x": 114.03095578762202, + "y": 2.048831220331361, + "z": 0.0 + }, + { + "x": 112.99998018377907, + "y": 2.0489412555996425, + "z": 0.0 + }, + { + "x": 111.96900457993611, + "y": 2.049051290867924, + "z": 0.0 + }, + { + "x": 110.93802897609315, + "y": 2.0491613261362054, + "z": 0.0 + }, + { + "x": 109.90705337225019, + "y": 2.049271361404487, + "z": 0.0 + }, + { + "x": 108.87607776840723, + "y": 2.0493813966727683, + "z": 0.0 + }, + { + "x": 107.84510216456428, + "y": 2.0494914319410493, + "z": 0.0 + }, + { + "x": 106.81412656072132, + "y": 2.049601467209331, + "z": 0.0 + }, + { + "x": 105.78315095687836, + "y": 2.0497115024776122, + "z": 0.0 + }, + { + "x": 104.7521753530354, + "y": 2.0498215377458937, + "z": 0.0 + }, + { + "x": 103.72087244346878, + "y": 2.049931581165015, + "z": 0.0 + }, + { + "x": 102.6898968530549, + "y": 2.04987289432529, + "z": 0.0 + } + ] + }, + { + "id": 15, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 11.031303375477442, + "y": -3.97999978801159, + "z": 0.0 + }, + { + "x": 12.051452811327586, + "y": -3.9796673112888272, + "z": 0.0 + }, + { + "x": 13.07160224717773, + "y": -3.979334834566064, + "z": 0.0 + }, + { + "x": 14.091751683027873, + "y": -3.979002357843301, + "z": 0.0 + }, + { + "x": 15.111901118878016, + "y": -3.9786698811205383, + "z": 0.0 + }, + { + "x": 16.132050554728153, + "y": -3.978337404397776, + "z": 0.0 + }, + { + "x": 17.152199990578296, + "y": -3.9780049276750122, + "z": 0.0 + }, + { + "x": 18.17234942642844, + "y": -3.9776724509522494, + "z": 0.0 + }, + { + "x": 19.192498862278583, + "y": -3.977339974229486, + "z": 0.0 + }, + { + "x": 20.212648298128727, + "y": -3.9770074975067233, + "z": 0.0 + }, + { + "x": 21.23279773397887, + "y": -3.9766750207839605, + "z": 0.0 + }, + { + "x": 22.252947169829014, + "y": -3.9763425440611972, + "z": 0.0 + }, + { + "x": 23.273096605679157, + "y": -3.9760100673384344, + "z": 0.0 + }, + { + "x": 24.2932460415293, + "y": -3.9756775906156716, + "z": 0.0 + }, + { + "x": 25.313395477379444, + "y": -3.9753451138929083, + "z": 0.0 + }, + { + "x": 26.333544913229588, + "y": -3.9750126371701455, + "z": 0.0 + }, + { + "x": 27.35369434907973, + "y": -3.9746801604473823, + "z": 0.0 + }, + { + "x": 28.373843784929875, + "y": -3.9743476837246194, + "z": 0.0 + }, + { + "x": 29.39399322078002, + "y": -3.9740152070018566, + "z": 0.0 + }, + { + "x": 30.414142656630162, + "y": -3.9736827302790934, + "z": 0.0 + }, + { + "x": 31.434292092480312, + "y": -3.9733502535563305, + "z": 0.0 + }, + { + "x": 32.454441528330456, + "y": -3.9730177768335677, + "z": 0.0 + }, + { + "x": 33.4745909641806, + "y": -3.9726853001108045, + "z": 0.0 + }, + { + "x": 34.49474040003074, + "y": -3.9723528233880416, + "z": 0.0 + }, + { + "x": 35.514889835880886, + "y": -3.972020346665279, + "z": 0.0 + }, + { + "x": 36.53503927173103, + "y": -3.9716878699425155, + "z": 0.0 + }, + { + "x": 37.55518870758117, + "y": -3.9713553932197527, + "z": 0.0 + }, + { + "x": 38.57533814343132, + "y": -3.97102291649699, + "z": 0.0 + }, + { + "x": 39.59548757928146, + "y": -3.9706904397742266, + "z": 0.0 + }, + { + "x": 40.615637015131604, + "y": -3.970357963051464, + "z": 0.0 + }, + { + "x": 41.63578645098175, + "y": -3.970025486328701, + "z": 0.0 + }, + { + "x": 42.65593588683189, + "y": -3.9696930096059377, + "z": 0.0 + }, + { + "x": 43.676085322682034, + "y": -3.969360532883175, + "z": 0.0 + }, + { + "x": 44.69623475853218, + "y": -3.969028056160412, + "z": 0.0 + }, + { + "x": 45.71638419438232, + "y": -3.968695579437649, + "z": 0.0 + }, + { + "x": 46.736533630232465, + "y": -3.968363102714886, + "z": 0.0 + }, + { + "x": 47.75668306608261, + "y": -3.968030625992123, + "z": 0.0 + }, + { + "x": 48.77683250193275, + "y": -3.96769814926936, + "z": 0.0 + }, + { + "x": 49.796981937782895, + "y": -3.967365672546597, + "z": 0.0 + }, + { + "x": 50.81713137363304, + "y": -3.967033195823834, + "z": 0.0 + }, + { + "x": 51.83728080948318, + "y": -3.966700719101071, + "z": 0.0 + }, + { + "x": 52.857430245333326, + "y": -3.966368242378308, + "z": 0.0 + }, + { + "x": 53.87757968118347, + "y": -3.966035765655545, + "z": 0.0 + }, + { + "x": 54.89772911703361, + "y": -3.965703288932782, + "z": 0.0 + }, + { + "x": 55.91787855288376, + "y": -3.9653708122100193, + "z": 0.0 + }, + { + "x": 56.9380279887339, + "y": -3.965038335487256, + "z": 0.0 + }, + { + "x": 57.95817742458404, + "y": -3.964705858764493, + "z": 0.0 + }, + { + "x": 58.97832686043419, + "y": -3.9643733820417304, + "z": 0.0 + }, + { + "x": 59.99847629628433, + "y": -3.964040905318967, + "z": 0.0 + }, + { + "x": 61.018625732134474, + "y": -3.9637084285962043, + "z": 0.0 + }, + { + "x": 62.03877516798462, + "y": -3.9633759518734415, + "z": 0.0 + }, + { + "x": 63.05892460383476, + "y": -3.9630434751506782, + "z": 0.0 + }, + { + "x": 64.07907403968491, + "y": -3.9627109984279154, + "z": 0.0 + }, + { + "x": 65.09922347553506, + "y": -3.9623785217051526, + "z": 0.0 + }, + { + "x": 66.1193729113852, + "y": -3.9620460449823893, + "z": 0.0 + }, + { + "x": 67.13952234723534, + "y": -3.9617135682596265, + "z": 0.0 + }, + { + "x": 68.15967178308549, + "y": -3.9613810915368637, + "z": 0.0 + }, + { + "x": 69.17982121893563, + "y": -3.9610486148141004, + "z": 0.0 + }, + { + "x": 70.19997065478577, + "y": -3.9607161380913376, + "z": 0.0 + }, + { + "x": 71.22012009063592, + "y": -3.9603836613685743, + "z": 0.0 + }, + { + "x": 72.24026952648606, + "y": -3.9600511846458115, + "z": 0.0 + }, + { + "x": 73.2604189623362, + "y": -3.9597187079230487, + "z": 0.0 + }, + { + "x": 74.28056839818635, + "y": -3.9593862312002854, + "z": 0.0 + }, + { + "x": 75.30071783403649, + "y": -3.9590537544775226, + "z": 0.0 + }, + { + "x": 76.32086726988663, + "y": -3.95872127775476, + "z": 0.0 + }, + { + "x": 77.34101670573678, + "y": -3.9583888010319965, + "z": 0.0 + }, + { + "x": 78.36116614158692, + "y": -3.9580563243092337, + "z": 0.0 + }, + { + "x": 79.38131557743706, + "y": -3.957723847586471, + "z": 0.0 + } + ] + }, + { + "id": 16, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 79.3787082989861, + "y": 4.042275727544709, + "z": 0.0 + }, + { + "x": 78.35855886313595, + "y": 4.041943250821946, + "z": 0.0 + }, + { + "x": 77.33840942728581, + "y": 4.041610774099183, + "z": 0.0 + }, + { + "x": 76.31825999143567, + "y": 4.0412782973764205, + "z": 0.0 + }, + { + "x": 75.29811055558552, + "y": 4.040945820653657, + "z": 0.0 + }, + { + "x": 74.27796111973538, + "y": 4.040613343930894, + "z": 0.0 + }, + { + "x": 73.25781168388524, + "y": 4.040280867208131, + "z": 0.0 + }, + { + "x": 72.2376622480351, + "y": 4.039948390485368, + "z": 0.0 + }, + { + "x": 71.21751281218495, + "y": 4.0396159137626055, + "z": 0.0 + }, + { + "x": 70.1973633763348, + "y": 4.039283437039843, + "z": 0.0 + }, + { + "x": 69.17721394048466, + "y": 4.038950960317079, + "z": 0.0 + }, + { + "x": 68.15706450463452, + "y": 4.038618483594316, + "z": 0.0 + }, + { + "x": 67.13691506878438, + "y": 4.038286006871553, + "z": 0.0 + }, + { + "x": 66.11676563293423, + "y": 4.0379535301487905, + "z": 0.0 + }, + { + "x": 65.09661619708409, + "y": 4.037621053426028, + "z": 0.0 + }, + { + "x": 64.07646676123395, + "y": 4.037288576703265, + "z": 0.0 + }, + { + "x": 63.05631732538381, + "y": 4.036956099980501, + "z": 0.0 + }, + { + "x": 62.036167889533665, + "y": 4.036623623257738, + "z": 0.0 + }, + { + "x": 61.01601845368352, + "y": 4.036291146534976, + "z": 0.0 + }, + { + "x": 59.99586901783338, + "y": 4.035958669812213, + "z": 0.0 + }, + { + "x": 58.975719581983235, + "y": 4.03562619308945, + "z": 0.0 + }, + { + "x": 57.95557014613309, + "y": 4.035293716366686, + "z": 0.0 + }, + { + "x": 56.93542071028295, + "y": 4.034961239643923, + "z": 0.0 + }, + { + "x": 55.915271274432804, + "y": 4.034628762921161, + "z": 0.0 + }, + { + "x": 54.89512183858266, + "y": 4.034296286198398, + "z": 0.0 + }, + { + "x": 53.87497240273252, + "y": 4.033963809475635, + "z": 0.0 + }, + { + "x": 52.854822966882374, + "y": 4.033631332752872, + "z": 0.0 + }, + { + "x": 51.83467353103223, + "y": 4.033298856030108, + "z": 0.0 + }, + { + "x": 50.81452409518209, + "y": 4.032966379307346, + "z": 0.0 + }, + { + "x": 49.79437465933194, + "y": 4.032633902584583, + "z": 0.0 + }, + { + "x": 48.7742252234818, + "y": 4.03230142586182, + "z": 0.0 + }, + { + "x": 47.754075787631656, + "y": 4.031968949139057, + "z": 0.0 + }, + { + "x": 46.73392635178151, + "y": 4.031636472416294, + "z": 0.0 + }, + { + "x": 45.71377691593137, + "y": 4.031303995693531, + "z": 0.0 + }, + { + "x": 44.693627480081226, + "y": 4.030971518970768, + "z": 0.0 + }, + { + "x": 43.67347804423108, + "y": 4.030639042248005, + "z": 0.0 + }, + { + "x": 42.65332860838094, + "y": 4.030306565525242, + "z": 0.0 + }, + { + "x": 41.633179172530795, + "y": 4.029974088802479, + "z": 0.0 + }, + { + "x": 40.61302973668065, + "y": 4.0296416120797165, + "z": 0.0 + }, + { + "x": 39.59288030083051, + "y": 4.029309135356953, + "z": 0.0 + }, + { + "x": 38.572730864980365, + "y": 4.02897665863419, + "z": 0.0 + }, + { + "x": 37.55258142913022, + "y": 4.028644181911427, + "z": 0.0 + }, + { + "x": 36.53243199328008, + "y": 4.028311705188664, + "z": 0.0 + }, + { + "x": 35.512282557429934, + "y": 4.0279792284659015, + "z": 0.0 + }, + { + "x": 34.49213312157979, + "y": 4.027646751743138, + "z": 0.0 + }, + { + "x": 33.47198368572965, + "y": 4.027314275020375, + "z": 0.0 + }, + { + "x": 32.451834249879504, + "y": 4.026981798297612, + "z": 0.0 + }, + { + "x": 31.43168481402936, + "y": 4.026649321574849, + "z": 0.0 + }, + { + "x": 30.41153537817921, + "y": 4.0263168448520865, + "z": 0.0 + }, + { + "x": 29.391385942329066, + "y": 4.025984368129324, + "z": 0.0 + }, + { + "x": 28.371236506478922, + "y": 4.02565189140656, + "z": 0.0 + }, + { + "x": 27.35108707062878, + "y": 4.025319414683797, + "z": 0.0 + }, + { + "x": 26.330937634778635, + "y": 4.024986937961034, + "z": 0.0 + }, + { + "x": 25.310788198928492, + "y": 4.0246544612382715, + "z": 0.0 + }, + { + "x": 24.29063876307835, + "y": 4.024321984515509, + "z": 0.0 + }, + { + "x": 23.270489327228205, + "y": 4.023989507792746, + "z": 0.0 + }, + { + "x": 22.25033989137806, + "y": 4.023657031069982, + "z": 0.0 + }, + { + "x": 21.230190455527918, + "y": 4.023324554347219, + "z": 0.0 + }, + { + "x": 20.210041019677774, + "y": 4.0229920776244565, + "z": 0.0 + }, + { + "x": 19.18989158382763, + "y": 4.022659600901694, + "z": 0.0 + }, + { + "x": 18.169742147977487, + "y": 4.022327124178931, + "z": 0.0 + }, + { + "x": 17.149592712127344, + "y": 4.021994647456168, + "z": 0.0 + }, + { + "x": 16.1294432762772, + "y": 4.021662170733405, + "z": 0.0 + }, + { + "x": 15.109293840427064, + "y": 4.0213296940106416, + "z": 0.0 + }, + { + "x": 14.08914440457692, + "y": 4.020997217287879, + "z": 0.0 + }, + { + "x": 13.068994968726777, + "y": 4.020664740565116, + "z": 0.0 + }, + { + "x": 12.048845532876634, + "y": 4.020332263842353, + "z": 0.0 + }, + { + "x": 11.02869609702649, + "y": 4.019999787119589, + "z": 0.0 + } + ] + }, + { + "id": 17, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 79.38001193821158, + "y": 0.04227593997911919, + "z": 0.0 + }, + { + "x": 78.35986250236144, + "y": 0.041943463256356214, + "z": 0.0 + }, + { + "x": 77.3397130665113, + "y": 0.041610986533593235, + "z": 0.0 + }, + { + "x": 76.31956363066115, + "y": 0.04127850981083026, + "z": 0.0 + }, + { + "x": 75.299414194811, + "y": 0.04094603308806729, + "z": 0.0 + }, + { + "x": 74.27926475896086, + "y": 0.04061355636530431, + "z": 0.0 + }, + { + "x": 73.25911532311072, + "y": 0.04028107964254134, + "z": 0.0 + }, + { + "x": 72.23896588726058, + "y": 0.039948602919778364, + "z": 0.0 + }, + { + "x": 71.21881645141043, + "y": 0.039616126197015385, + "z": 0.0 + }, + { + "x": 70.19866701556029, + "y": 0.03928364947425241, + "z": 0.0 + }, + { + "x": 69.17851757971015, + "y": 0.03895117275148943, + "z": 0.0 + }, + { + "x": 68.15836814386, + "y": 0.03861869602872646, + "z": 0.0 + }, + { + "x": 67.13821870800986, + "y": 0.03828621930596349, + "z": 0.0 + }, + { + "x": 66.11806927215972, + "y": 0.037953742583200514, + "z": 0.0 + }, + { + "x": 65.09791983630957, + "y": 0.037621265860437535, + "z": 0.0 + }, + { + "x": 64.07777040045943, + "y": 0.03728878913767456, + "z": 0.0 + }, + { + "x": 63.057620964609285, + "y": 0.03695631241491158, + "z": 0.0 + }, + { + "x": 62.03747152875914, + "y": 0.03662383569214861, + "z": 0.0 + }, + { + "x": 61.017322092909, + "y": 0.03629135896938564, + "z": 0.0 + }, + { + "x": 59.997172657058854, + "y": 0.03595888224662266, + "z": 0.0 + }, + { + "x": 58.97702322120871, + "y": 0.035626405523859685, + "z": 0.0 + }, + { + "x": 57.95687378535857, + "y": 0.03529392880109671, + "z": 0.0 + }, + { + "x": 56.936724349508424, + "y": 0.03496145207833373, + "z": 0.0 + }, + { + "x": 55.91657491365828, + "y": 0.03462897535557076, + "z": 0.0 + }, + { + "x": 54.89642547780814, + "y": 0.03429649863280779, + "z": 0.0 + }, + { + "x": 53.87627604195799, + "y": 0.03396402191004481, + "z": 0.0 + }, + { + "x": 52.85612660610785, + "y": 0.033631545187281835, + "z": 0.0 + }, + { + "x": 51.835977170257706, + "y": 0.03329906846451886, + "z": 0.0 + }, + { + "x": 50.81582773440756, + "y": 0.03296659174175588, + "z": 0.0 + }, + { + "x": 49.79567829855742, + "y": 0.03263411501899291, + "z": 0.0 + }, + { + "x": 48.775528862707276, + "y": 0.03230163829622994, + "z": 0.0 + }, + { + "x": 47.75537942685713, + "y": 0.03196916157346696, + "z": 0.0 + }, + { + "x": 46.73522999100699, + "y": 0.031636684850703985, + "z": 0.0 + }, + { + "x": 45.715080555156845, + "y": 0.031304208127941005, + "z": 0.0 + }, + { + "x": 44.6949311193067, + "y": 0.030971731405178032, + "z": 0.0 + }, + { + "x": 43.67478168345656, + "y": 0.03063925468241506, + "z": 0.0 + }, + { + "x": 42.654632247606415, + "y": 0.030306777959652087, + "z": 0.0 + }, + { + "x": 41.63448281175627, + "y": 0.029974301236889107, + "z": 0.0 + }, + { + "x": 40.61433337590613, + "y": 0.029641824514126138, + "z": 0.0 + }, + { + "x": 39.594183940055984, + "y": 0.029309347791363162, + "z": 0.0 + }, + { + "x": 38.57403450420584, + "y": 0.028976871068600186, + "z": 0.0 + }, + { + "x": 37.5538850683557, + "y": 0.028644394345837213, + "z": 0.0 + }, + { + "x": 36.533735632505554, + "y": 0.028311917623074237, + "z": 0.0 + }, + { + "x": 35.51358619665541, + "y": 0.027979440900311257, + "z": 0.0 + }, + { + "x": 34.49343676080527, + "y": 0.027646964177548285, + "z": 0.0 + }, + { + "x": 33.47328732495512, + "y": 0.02731448745478531, + "z": 0.0 + }, + { + "x": 32.45313788910498, + "y": 0.026982010732022332, + "z": 0.0 + }, + { + "x": 31.432988453254836, + "y": 0.02664953400925936, + "z": 0.0 + }, + { + "x": 30.412839017404686, + "y": 0.02631705728649638, + "z": 0.0 + }, + { + "x": 29.392689581554542, + "y": 0.025984580563733407, + "z": 0.0 + }, + { + "x": 28.3725401457044, + "y": 0.02565210384097043, + "z": 0.0 + }, + { + "x": 27.352390709854255, + "y": 0.02531962711820746, + "z": 0.0 + }, + { + "x": 26.33224127400411, + "y": 0.024987150395444482, + "z": 0.0 + }, + { + "x": 25.312091838153968, + "y": 0.024654673672681506, + "z": 0.0 + }, + { + "x": 24.291942402303825, + "y": 0.024322196949918534, + "z": 0.0 + }, + { + "x": 23.27179296645368, + "y": 0.023989720227155554, + "z": 0.0 + }, + { + "x": 22.251643530603538, + "y": 0.023657243504392578, + "z": 0.0 + }, + { + "x": 21.231494094753394, + "y": 0.023324766781629605, + "z": 0.0 + }, + { + "x": 20.21134465890325, + "y": 0.02299229005886663, + "z": 0.0 + }, + { + "x": 19.191195223053107, + "y": 0.022659813336103656, + "z": 0.0 + }, + { + "x": 18.171045787202964, + "y": 0.02232733661334068, + "z": 0.0 + }, + { + "x": 17.15089635135282, + "y": 0.021994859890577707, + "z": 0.0 + }, + { + "x": 16.130746915502677, + "y": 0.02166238316781473, + "z": 0.0 + }, + { + "x": 15.11059747965254, + "y": 0.021329906445051755, + "z": 0.0 + }, + { + "x": 14.090448043802397, + "y": 0.020997429722288782, + "z": 0.0 + }, + { + "x": 13.070298607952253, + "y": 0.020664952999525806, + "z": 0.0 + }, + { + "x": 12.05014917210211, + "y": 0.02033247627676283, + "z": 0.0 + }, + { + "x": 11.029999736251966, + "y": 0.019999999553999857, + "z": 0.0 + } + ] + }, + { + "id": 18, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 11.030651555864704, + "y": -1.979999894228795, + "z": 0.0 + }, + { + "x": 12.050800991714848, + "y": -1.9796674175060323, + "z": 0.0 + }, + { + "x": 13.070950427564991, + "y": -1.979334940783269, + "z": 0.0 + }, + { + "x": 14.091099863415135, + "y": -1.9790024640605062, + "z": 0.0 + }, + { + "x": 15.111249299265278, + "y": -1.9786699873377434, + "z": 0.0 + }, + { + "x": 16.131398735115415, + "y": -1.9783375106149805, + "z": 0.0 + }, + { + "x": 17.151548170965558, + "y": -1.9780050338922173, + "z": 0.0 + }, + { + "x": 18.1716976068157, + "y": -1.9776725571694544, + "z": 0.0 + }, + { + "x": 19.191847042665845, + "y": -1.9773400804466912, + "z": 0.0 + }, + { + "x": 20.21199647851599, + "y": -1.9770076037239284, + "z": 0.0 + }, + { + "x": 21.232145914366132, + "y": -1.9766751270011655, + "z": 0.0 + }, + { + "x": 22.252295350216276, + "y": -1.9763426502784023, + "z": 0.0 + }, + { + "x": 23.27244478606642, + "y": -1.9760101735556395, + "z": 0.0 + }, + { + "x": 24.292594221916563, + "y": -1.9756776968328766, + "z": 0.0 + }, + { + "x": 25.312743657766706, + "y": -1.9753452201101134, + "z": 0.0 + }, + { + "x": 26.33289309361685, + "y": -1.9750127433873506, + "z": 0.0 + }, + { + "x": 27.353042529466993, + "y": -1.9746802666645873, + "z": 0.0 + }, + { + "x": 28.373191965317137, + "y": -1.9743477899418245, + "z": 0.0 + }, + { + "x": 29.39334140116728, + "y": -1.9740153132190617, + "z": 0.0 + }, + { + "x": 30.413490837017424, + "y": -1.9736828364962984, + "z": 0.0 + }, + { + "x": 31.433640272867574, + "y": -1.9733503597735356, + "z": 0.0 + }, + { + "x": 32.45378970871772, + "y": -1.9730178830507727, + "z": 0.0 + }, + { + "x": 33.473939144567865, + "y": -1.9726854063280095, + "z": 0.0 + }, + { + "x": 34.49408858041801, + "y": -1.9723529296052467, + "z": 0.0 + }, + { + "x": 35.51423801626815, + "y": -1.9720204528824838, + "z": 0.0 + }, + { + "x": 36.534387452118295, + "y": -1.9716879761597206, + "z": 0.0 + }, + { + "x": 37.55453688796844, + "y": -1.9713554994369578, + "z": 0.0 + }, + { + "x": 38.57468632381858, + "y": -1.971023022714195, + "z": 0.0 + }, + { + "x": 39.594835759668726, + "y": -1.9706905459914317, + "z": 0.0 + }, + { + "x": 40.61498519551887, + "y": -1.9703580692686689, + "z": 0.0 + }, + { + "x": 41.63513463136901, + "y": -1.970025592545906, + "z": 0.0 + }, + { + "x": 42.655284067219156, + "y": -1.9696931158231428, + "z": 0.0 + }, + { + "x": 43.6754335030693, + "y": -1.96936063910038, + "z": 0.0 + }, + { + "x": 44.69558293891944, + "y": -1.9690281623776171, + "z": 0.0 + }, + { + "x": 45.71573237476959, + "y": -1.9686956856548539, + "z": 0.0 + }, + { + "x": 46.73588181061973, + "y": -1.968363208932091, + "z": 0.0 + }, + { + "x": 47.756031246469874, + "y": -1.9680307322093282, + "z": 0.0 + }, + { + "x": 48.77618068232002, + "y": -1.967698255486565, + "z": 0.0 + }, + { + "x": 49.79633011817016, + "y": -1.9673657787638021, + "z": 0.0 + }, + { + "x": 50.816479554020304, + "y": -1.9670333020410389, + "z": 0.0 + }, + { + "x": 51.83662898987045, + "y": -1.966700825318276, + "z": 0.0 + }, + { + "x": 52.85677842572059, + "y": -1.9663683485955132, + "z": 0.0 + }, + { + "x": 53.876927861570735, + "y": -1.96603587187275, + "z": 0.0 + }, + { + "x": 54.89707729742088, + "y": -1.9657033951499872, + "z": 0.0 + }, + { + "x": 55.91722673327102, + "y": -1.9653709184272243, + "z": 0.0 + }, + { + "x": 56.937376169121166, + "y": -1.965038441704461, + "z": 0.0 + }, + { + "x": 57.95752560497131, + "y": -1.9647059649816982, + "z": 0.0 + }, + { + "x": 58.97767504082145, + "y": -1.9643734882589354, + "z": 0.0 + }, + { + "x": 59.997824476671596, + "y": -1.9640410115361722, + "z": 0.0 + }, + { + "x": 61.01797391252174, + "y": -1.9637085348134093, + "z": 0.0 + }, + { + "x": 62.03812334837188, + "y": -1.9633760580906465, + "z": 0.0 + }, + { + "x": 63.05827278422203, + "y": -1.9630435813678833, + "z": 0.0 + }, + { + "x": 64.07842222007217, + "y": -1.9627111046451204, + "z": 0.0 + }, + { + "x": 65.09857165592231, + "y": -1.9623786279223576, + "z": 0.0 + }, + { + "x": 66.11872109177246, + "y": -1.9620461511995944, + "z": 0.0 + }, + { + "x": 67.1388705276226, + "y": -1.9617136744768315, + "z": 0.0 + }, + { + "x": 68.15901996347274, + "y": -1.9613811977540687, + "z": 0.0 + }, + { + "x": 69.17916939932289, + "y": -1.9610487210313055, + "z": 0.0 + }, + { + "x": 70.19931883517303, + "y": -1.9607162443085426, + "z": 0.0 + }, + { + "x": 71.21946827102317, + "y": -1.9603837675857794, + "z": 0.0 + }, + { + "x": 72.23961770687332, + "y": -1.9600512908630165, + "z": 0.0 + }, + { + "x": 73.25976714272346, + "y": -1.9597188141402537, + "z": 0.0 + }, + { + "x": 74.2799165785736, + "y": -1.9593863374174905, + "z": 0.0 + }, + { + "x": 75.30006601442375, + "y": -1.9590538606947276, + "z": 0.0 + }, + { + "x": 76.32021545027389, + "y": -1.9587213839719648, + "z": 0.0 + }, + { + "x": 77.34036488612404, + "y": -1.9583889072492016, + "z": 0.0 + }, + { + "x": 78.36051432197418, + "y": -1.9580564305264387, + "z": 0.0 + }, + { + "x": 79.38066375782432, + "y": -1.957723953803676, + "z": 0.0 + } + ] + }, + { + "id": 19, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 79.37936011859884, + "y": 2.0422758337619142, + "z": 0.0 + }, + { + "x": 78.3592106827487, + "y": 2.041943357039151, + "z": 0.0 + }, + { + "x": 77.33906124689855, + "y": 2.041610880316388, + "z": 0.0 + }, + { + "x": 76.31891181104841, + "y": 2.0412784035936253, + "z": 0.0 + }, + { + "x": 75.29876237519827, + "y": 2.040945926870862, + "z": 0.0 + }, + { + "x": 74.27861293934812, + "y": 2.0406134501480993, + "z": 0.0 + }, + { + "x": 73.25846350349798, + "y": 2.0402809734253364, + "z": 0.0 + }, + { + "x": 72.23831406764783, + "y": 2.039948496702573, + "z": 0.0 + }, + { + "x": 71.21816463179769, + "y": 2.0396160199798103, + "z": 0.0 + }, + { + "x": 70.19801519594755, + "y": 2.0392835432570475, + "z": 0.0 + }, + { + "x": 69.1778657600974, + "y": 2.0389510665342843, + "z": 0.0 + }, + { + "x": 68.15771632424726, + "y": 2.0386185898115214, + "z": 0.0 + }, + { + "x": 67.13756688839712, + "y": 2.0382861130887586, + "z": 0.0 + }, + { + "x": 66.11741745254697, + "y": 2.0379536363659954, + "z": 0.0 + }, + { + "x": 65.09726801669683, + "y": 2.0376211596432325, + "z": 0.0 + }, + { + "x": 64.07711858084669, + "y": 2.0372886829204697, + "z": 0.0 + }, + { + "x": 63.05696914499654, + "y": 2.0369562061977065, + "z": 0.0 + }, + { + "x": 62.0368197091464, + "y": 2.0366237294749436, + "z": 0.0 + }, + { + "x": 61.016670273296256, + "y": 2.036291252752181, + "z": 0.0 + }, + { + "x": 59.99652083744611, + "y": 2.0359587760294175, + "z": 0.0 + }, + { + "x": 58.97637140159597, + "y": 2.0356262993066547, + "z": 0.0 + }, + { + "x": 57.956221965745826, + "y": 2.0352938225838915, + "z": 0.0 + }, + { + "x": 56.93607252989568, + "y": 2.0349613458611286, + "z": 0.0 + }, + { + "x": 55.91592309404554, + "y": 2.034628869138366, + "z": 0.0 + }, + { + "x": 54.895773658195395, + "y": 2.0342963924156026, + "z": 0.0 + }, + { + "x": 53.87562422234525, + "y": 2.0339639156928397, + "z": 0.0 + }, + { + "x": 52.85547478649511, + "y": 2.033631438970077, + "z": 0.0 + }, + { + "x": 51.835325350644965, + "y": 2.0332989622473137, + "z": 0.0 + }, + { + "x": 50.81517591479482, + "y": 2.032966485524551, + "z": 0.0 + }, + { + "x": 49.79502647894468, + "y": 2.032634008801788, + "z": 0.0 + }, + { + "x": 48.774877043094534, + "y": 2.0323015320790248, + "z": 0.0 + }, + { + "x": 47.75472760724439, + "y": 2.031969055356262, + "z": 0.0 + }, + { + "x": 46.73457817139425, + "y": 2.031636578633499, + "z": 0.0 + }, + { + "x": 45.714428735544104, + "y": 2.031304101910736, + "z": 0.0 + }, + { + "x": 44.69427929969396, + "y": 2.030971625187973, + "z": 0.0 + }, + { + "x": 43.67412986384382, + "y": 2.03063914846521, + "z": 0.0 + }, + { + "x": 42.65398042799367, + "y": 2.030306671742447, + "z": 0.0 + }, + { + "x": 41.63383099214353, + "y": 2.029974195019684, + "z": 0.0 + }, + { + "x": 40.613681556293386, + "y": 2.0296417182969213, + "z": 0.0 + }, + { + "x": 39.59353212044324, + "y": 2.029309241574158, + "z": 0.0 + }, + { + "x": 38.5733826845931, + "y": 2.028976764851395, + "z": 0.0 + }, + { + "x": 37.553233248742956, + "y": 2.0286442881286324, + "z": 0.0 + }, + { + "x": 36.53308381289281, + "y": 2.028311811405869, + "z": 0.0 + }, + { + "x": 35.51293437704267, + "y": 2.0279793346831063, + "z": 0.0 + }, + { + "x": 34.492784941192525, + "y": 2.027646857960343, + "z": 0.0 + }, + { + "x": 33.47263550534238, + "y": 2.0273143812375802, + "z": 0.0 + }, + { + "x": 32.45248606949224, + "y": 2.0269819045148174, + "z": 0.0 + }, + { + "x": 31.432336633642098, + "y": 2.026649427792054, + "z": 0.0 + }, + { + "x": 30.412187197791948, + "y": 2.0263169510692913, + "z": 0.0 + }, + { + "x": 29.392037761941804, + "y": 2.0259844743465285, + "z": 0.0 + }, + { + "x": 28.37188832609166, + "y": 2.0256519976237652, + "z": 0.0 + }, + { + "x": 27.351738890241517, + "y": 2.0253195209010024, + "z": 0.0 + }, + { + "x": 26.331589454391374, + "y": 2.0249870441782396, + "z": 0.0 + }, + { + "x": 25.31144001854123, + "y": 2.0246545674554763, + "z": 0.0 + }, + { + "x": 24.291290582691087, + "y": 2.0243220907327135, + "z": 0.0 + }, + { + "x": 23.271141146840943, + "y": 2.0239896140099507, + "z": 0.0 + }, + { + "x": 22.2509917109908, + "y": 2.0236571372871874, + "z": 0.0 + }, + { + "x": 21.230842275140656, + "y": 2.0233246605644246, + "z": 0.0 + }, + { + "x": 20.210692839290513, + "y": 2.022992183841662, + "z": 0.0 + }, + { + "x": 19.19054340344037, + "y": 2.0226597071188985, + "z": 0.0 + }, + { + "x": 18.170393967590226, + "y": 2.0223272303961357, + "z": 0.0 + }, + { + "x": 17.150244531740082, + "y": 2.021994753673373, + "z": 0.0 + }, + { + "x": 16.13009509588994, + "y": 2.02166227695061, + "z": 0.0 + }, + { + "x": 15.109945660039802, + "y": 2.021329800227847, + "z": 0.0 + }, + { + "x": 14.089796224189659, + "y": 2.0209973235050835, + "z": 0.0 + }, + { + "x": 13.069646788339515, + "y": 2.0206648467823207, + "z": 0.0 + }, + { + "x": 12.049497352489372, + "y": 2.020332370059558, + "z": 0.0 + }, + { + "x": 11.029347916639228, + "y": 2.0199998933367946, + "z": 0.0 + } + ] + }, + { + "id": 20, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.6417606480552, + "y": -127.51508546382411, + "z": 0.0 + }, + { + "x": 324.6362898963956, + "y": -127.5146362258154, + "z": 0.0 + }, + { + "x": 323.63081914473594, + "y": -127.51418698780671, + "z": 0.0 + }, + { + "x": 322.6253483930765, + "y": -127.513737749798, + "z": 0.0 + }, + { + "x": 321.6198776414169, + "y": -127.51328851178928, + "z": 0.0 + }, + { + "x": 320.61440688975733, + "y": -127.51283927378057, + "z": 0.0 + }, + { + "x": 319.60893613809776, + "y": -127.51239003577186, + "z": 0.0 + }, + { + "x": 318.6034653864382, + "y": -127.51194079776315, + "z": 0.0 + }, + { + "x": 317.5979946347786, + "y": -127.51149155975443, + "z": 0.0 + }, + { + "x": 316.592523883119, + "y": -127.51104232174572, + "z": 0.0 + }, + { + "x": 315.5870531314593, + "y": -127.51059308373704, + "z": 0.0 + }, + { + "x": 314.58158237979984, + "y": -127.51014384572832, + "z": 0.0 + }, + { + "x": 313.57611162814027, + "y": -127.50969460771961, + "z": 0.0 + }, + { + "x": 312.5706408764807, + "y": -127.5092453697109, + "z": 0.0 + }, + { + "x": 311.5651701248211, + "y": -127.50879613170218, + "z": 0.0 + }, + { + "x": 310.55969937316155, + "y": -127.50834689369347, + "z": 0.0 + }, + { + "x": 309.554228621502, + "y": -127.50789765568476, + "z": 0.0 + }, + { + "x": 308.5487578698423, + "y": -127.50744841767607, + "z": 0.0 + }, + { + "x": 307.54328711818283, + "y": -127.50699917966736, + "z": 0.0 + }, + { + "x": 306.53781636652326, + "y": -127.50654994165865, + "z": 0.0 + }, + { + "x": 305.5323456148637, + "y": -127.50610070364993, + "z": 0.0 + }, + { + "x": 304.5268748632041, + "y": -127.50565146564122, + "z": 0.0 + }, + { + "x": 303.5214041115445, + "y": -127.50520222763251, + "z": 0.0 + }, + { + "x": 302.5159333598849, + "y": -127.5047529896238, + "z": 0.0 + }, + { + "x": 301.51046260822534, + "y": -127.50430375161508, + "z": 0.0 + }, + { + "x": 300.50499185656565, + "y": -127.5038545136064, + "z": 0.0 + }, + { + "x": 299.4995211049062, + "y": -127.50340527559769, + "z": 0.0 + }, + { + "x": 298.4940503532466, + "y": -127.50295603758897, + "z": 0.0 + }, + { + "x": 297.48857960158705, + "y": -127.50250679958026, + "z": 0.0 + }, + { + "x": 296.4831088499275, + "y": -127.50205756157155, + "z": 0.0 + }, + { + "x": 295.4776380982679, + "y": -127.50160832356283, + "z": 0.0 + }, + { + "x": 294.47216734660833, + "y": -127.50115908555412, + "z": 0.0 + }, + { + "x": 293.46669659494864, + "y": -127.50070984754544, + "z": 0.0 + }, + { + "x": 292.4612258432892, + "y": -127.50026060953672, + "z": 0.0 + }, + { + "x": 291.4557550916296, + "y": -127.49981137152801, + "z": 0.0 + }, + { + "x": 290.45028433997, + "y": -127.4993621335193, + "z": 0.0 + }, + { + "x": 289.4448135883104, + "y": -127.49891289551059, + "z": 0.0 + }, + { + "x": 288.43934283665084, + "y": -127.49846365750187, + "z": 0.0 + }, + { + "x": 287.43387208499126, + "y": -127.49801441949316, + "z": 0.0 + }, + { + "x": 286.4284013333317, + "y": -127.49756518148445, + "z": 0.0 + }, + { + "x": 285.422930581672, + "y": -127.49711594347576, + "z": 0.0 + }, + { + "x": 284.41745983001255, + "y": -127.49666670546705, + "z": 0.0 + }, + { + "x": 283.411989078353, + "y": -127.49621746745834, + "z": 0.0 + }, + { + "x": 282.4065183266934, + "y": -127.49576822944962, + "z": 0.0 + }, + { + "x": 281.40104757503383, + "y": -127.49531899144091, + "z": 0.0 + }, + { + "x": 280.39557682337426, + "y": -127.4948697534322, + "z": 0.0 + }, + { + "x": 279.3901060717147, + "y": -127.49442051542349, + "z": 0.0 + }, + { + "x": 278.384635320055, + "y": -127.4939712774148, + "z": 0.0 + }, + { + "x": 277.3791645683955, + "y": -127.49352203940609, + "z": 0.0 + }, + { + "x": 276.3736938167359, + "y": -127.49307280139737, + "z": 0.0 + }, + { + "x": 275.36822306507634, + "y": -127.49262356338866, + "z": 0.0 + }, + { + "x": 274.36275231341676, + "y": -127.49217432537995, + "z": 0.0 + }, + { + "x": 273.3572815617572, + "y": -127.49172508737124, + "z": 0.0 + }, + { + "x": 272.3518108100976, + "y": -127.49127584936252, + "z": 0.0 + }, + { + "x": 271.34634005843805, + "y": -127.49082661135381, + "z": 0.0 + }, + { + "x": 270.34086930677836, + "y": -127.49037737334513, + "z": 0.0 + }, + { + "x": 269.3353985551189, + "y": -127.48992813533641, + "z": 0.0 + }, + { + "x": 268.3299278034593, + "y": -127.4894788973277, + "z": 0.0 + }, + { + "x": 267.32445705179975, + "y": -127.48902965931899, + "z": 0.0 + }, + { + "x": 266.3189863001402, + "y": -127.48858042131027, + "z": 0.0 + }, + { + "x": 265.31351554848055, + "y": -127.48813118330156, + "z": 0.0 + }, + { + "x": 264.308044796821, + "y": -127.48768194529285, + "z": 0.0 + }, + { + "x": 263.3025740451613, + "y": -127.48723270728416, + "z": 0.0 + }, + { + "x": 262.29710329350183, + "y": -127.48678346927545, + "z": 0.0 + }, + { + "x": 261.29163254184226, + "y": -127.48633423126674, + "z": 0.0 + }, + { + "x": 260.2861617901827, + "y": -127.48588499325803, + "z": 0.0 + }, + { + "x": 259.2806910385231, + "y": -127.48543575524931, + "z": 0.0 + }, + { + "x": 258.27522028686354, + "y": -127.4849865172406, + "z": 0.0 + }, + { + "x": 257.26974953520397, + "y": -127.48453727923189, + "z": 0.0 + }, + { + "x": 256.2642787835444, + "y": -127.48408804122317, + "z": 0.0 + }, + { + "x": 255.2588080318847, + "y": -127.48363880321449, + "z": 0.0 + }, + { + "x": 254.25333728022525, + "y": -127.48318956520578, + "z": 0.0 + }, + { + "x": 253.24786652856568, + "y": -127.48274032719706, + "z": 0.0 + }, + { + "x": 252.24239577690608, + "y": -127.48229108918835, + "z": 0.0 + }, + { + "x": 251.2369250252465, + "y": -127.48184185117964, + "z": 0.0 + }, + { + "x": 250.23145427358693, + "y": -127.48139261317093, + "z": 0.0 + }, + { + "x": 249.22598352192736, + "y": -127.48094337516221, + "z": 0.0 + }, + { + "x": 248.22051277026767, + "y": -127.48049413715353, + "z": 0.0 + }, + { + "x": 247.21504201860822, + "y": -127.48004489914481, + "z": 0.0 + }, + { + "x": 246.20957126694861, + "y": -127.4795956611361, + "z": 0.0 + }, + { + "x": 245.20410051528904, + "y": -127.47914642312739, + "z": 0.0 + }, + { + "x": 244.19862976362947, + "y": -127.47869718511868, + "z": 0.0 + }, + { + "x": 243.1931590119699, + "y": -127.47824794710996, + "z": 0.0 + }, + { + "x": 242.18768826031032, + "y": -127.47779870910125, + "z": 0.0 + }, + { + "x": 241.18221750865075, + "y": -127.47734947109254, + "z": 0.0 + }, + { + "x": 240.17674675699107, + "y": -127.47690023308385, + "z": 0.0 + }, + { + "x": 239.17127600533158, + "y": -127.47645099507514, + "z": 0.0 + }, + { + "x": 238.165805253672, + "y": -127.47600175706643, + "z": 0.0 + }, + { + "x": 237.16033450201243, + "y": -127.47555251905771, + "z": 0.0 + }, + { + "x": 236.15486375035286, + "y": -127.475103281049, + "z": 0.0 + }, + { + "x": 235.1493929986933, + "y": -127.47465404304029, + "z": 0.0 + }, + { + "x": 234.14392224703371, + "y": -127.47420480503158, + "z": 0.0 + }, + { + "x": 233.138451495374, + "y": -127.47375556702289, + "z": 0.0 + }, + { + "x": 232.13298074371454, + "y": -127.47330632901418, + "z": 0.0 + }, + { + "x": 231.12750999205497, + "y": -127.47285709100547, + "z": 0.0 + }, + { + "x": 230.1220392403954, + "y": -127.47240785299675, + "z": 0.0 + }, + { + "x": 229.1165684887358, + "y": -127.47195861498804, + "z": 0.0 + }, + { + "x": 228.1110977370762, + "y": -127.47150937697933, + "z": 0.0 + }, + { + "x": 227.10562698541662, + "y": -127.47106013897061, + "z": 0.0 + }, + { + "x": 226.10015623375702, + "y": -127.4706109009619, + "z": 0.0 + }, + { + "x": 225.0946854820973, + "y": -127.47016166295322, + "z": 0.0 + }, + { + "x": 224.08921473043785, + "y": -127.4697124249445, + "z": 0.0 + }, + { + "x": 223.08374397877824, + "y": -127.46926318693579, + "z": 0.0 + }, + { + "x": 222.07827322711867, + "y": -127.46881394892708, + "z": 0.0 + }, + { + "x": 221.07280247545907, + "y": -127.46836471091837, + "z": 0.0 + }, + { + "x": 220.06733172379947, + "y": -127.46791547290965, + "z": 0.0 + }, + { + "x": 219.0618609721399, + "y": -127.46746623490094, + "z": 0.0 + }, + { + "x": 218.05639022048018, + "y": -127.46701699689226, + "z": 0.0 + }, + { + "x": 217.0509194688207, + "y": -127.46656775888354, + "z": 0.0 + }, + { + "x": 216.04544871716112, + "y": -127.46611852087483, + "z": 0.0 + }, + { + "x": 215.03997796550152, + "y": -127.46566928286612, + "z": 0.0 + }, + { + "x": 214.03450721384192, + "y": -127.4652200448574, + "z": 0.0 + }, + { + "x": 213.02903646218235, + "y": -127.46477080684869, + "z": 0.0 + }, + { + "x": 212.02356571052275, + "y": -127.46432156883998, + "z": 0.0 + }, + { + "x": 211.01809495886314, + "y": -127.46387233083126, + "z": 0.0 + }, + { + "x": 210.01262420720346, + "y": -127.46342309282258, + "z": 0.0 + }, + { + "x": 209.00715345554397, + "y": -127.46297385481387, + "z": 0.0 + }, + { + "x": 208.00168270388437, + "y": -127.46252461680515, + "z": 0.0 + }, + { + "x": 206.9962119522248, + "y": -127.46207537879644, + "z": 0.0 + }, + { + "x": 205.9907412005652, + "y": -127.46162614078773, + "z": 0.0 + }, + { + "x": 204.9852704489056, + "y": -127.46117690277902, + "z": 0.0 + }, + { + "x": 203.97979969724602, + "y": -127.4607276647703, + "z": 0.0 + }, + { + "x": 202.9743289455863, + "y": -127.46027842676162, + "z": 0.0 + }, + { + "x": 201.96885819392682, + "y": -127.4598291887529, + "z": 0.0 + }, + { + "x": 200.96338744226725, + "y": -127.4593799507442, + "z": 0.0 + }, + { + "x": 199.95791669060765, + "y": -127.45893071273548, + "z": 0.0 + }, + { + "x": 198.95244593894805, + "y": -127.45848147472677, + "z": 0.0 + }, + { + "x": 197.94697518728847, + "y": -127.45803223671805, + "z": 0.0 + }, + { + "x": 196.94150443562887, + "y": -127.45758299870934, + "z": 0.0 + }, + { + "x": 195.93603368396927, + "y": -127.45713376070063, + "z": 0.0 + }, + { + "x": 194.93056293230958, + "y": -127.45668452269194, + "z": 0.0 + }, + { + "x": 193.9250921806501, + "y": -127.45623528468323, + "z": 0.0 + }, + { + "x": 192.9196214289905, + "y": -127.45578604667452, + "z": 0.0 + }, + { + "x": 191.91415067733092, + "y": -127.4553368086658, + "z": 0.0 + }, + { + "x": 190.90867992567132, + "y": -127.45488757065709, + "z": 0.0 + }, + { + "x": 189.90320917401175, + "y": -127.45443833264838, + "z": 0.0 + }, + { + "x": 188.89773842235215, + "y": -127.45398909463967, + "z": 0.0 + }, + { + "x": 187.89226767069243, + "y": -127.45353985663098, + "z": 0.0 + }, + { + "x": 186.88679691903297, + "y": -127.45309061862227, + "z": 0.0 + }, + { + "x": 185.88132616737337, + "y": -127.45264138061356, + "z": 0.0 + }, + { + "x": 184.87585541571377, + "y": -127.45219214260484, + "z": 0.0 + }, + { + "x": 183.8703846640542, + "y": -127.45174290459613, + "z": 0.0 + }, + { + "x": 182.8649139123946, + "y": -127.45129366658742, + "z": 0.0 + }, + { + "x": 181.859443160735, + "y": -127.4508444285787, + "z": 0.0 + }, + { + "x": 180.85397240907542, + "y": -127.45039519056999, + "z": 0.0 + }, + { + "x": 179.8485016574157, + "y": -127.44994595256131, + "z": 0.0 + }, + { + "x": 178.84303090575622, + "y": -127.4494967145526, + "z": 0.0 + }, + { + "x": 177.83756015409665, + "y": -127.44904747654388, + "z": 0.0 + }, + { + "x": 176.83208940243705, + "y": -127.44859823853517, + "z": 0.0 + }, + { + "x": 175.82661865077745, + "y": -127.44814900052646, + "z": 0.0 + }, + { + "x": 174.82114789911788, + "y": -127.44769976251774, + "z": 0.0 + }, + { + "x": 173.81567714745827, + "y": -127.44725052450903, + "z": 0.0 + }, + { + "x": 172.81020639579856, + "y": -127.44680128650035, + "z": 0.0 + }, + { + "x": 171.8047356441391, + "y": -127.44635204849163, + "z": 0.0 + }, + { + "x": 170.7992648924795, + "y": -127.44590281048292, + "z": 0.0 + }, + { + "x": 169.7937941408199, + "y": -127.44545357247421, + "z": 0.0 + }, + { + "x": 168.78832338916033, + "y": -127.4450043344655, + "z": 0.0 + }, + { + "x": 167.78285263750072, + "y": -127.44455509645678, + "z": 0.0 + }, + { + "x": 166.77738188584112, + "y": -127.44410585844807, + "z": 0.0 + }, + { + "x": 165.77191113418155, + "y": -127.44365662043936, + "z": 0.0 + }, + { + "x": 164.76644038252184, + "y": -127.44320738243067, + "z": 0.0 + }, + { + "x": 163.76096963086238, + "y": -127.44275814442196, + "z": 0.0 + }, + { + "x": 162.75549887920278, + "y": -127.44230890641325, + "z": 0.0 + }, + { + "x": 161.7500281275432, + "y": -127.44185966840453, + "z": 0.0 + }, + { + "x": 160.74455737588363, + "y": -127.44141043039582, + "z": 0.0 + }, + { + "x": 159.73908662422403, + "y": -127.4409611923871, + "z": 0.0 + }, + { + "x": 158.73361587256446, + "y": -127.4405119543784, + "z": 0.0 + }, + { + "x": 157.72814512090474, + "y": -127.44006271636971, + "z": 0.0 + }, + { + "x": 156.72267436924528, + "y": -127.439613478361, + "z": 0.0 + }, + { + "x": 155.7172036175857, + "y": -127.43916424035228, + "z": 0.0 + }, + { + "x": 154.7117328659261, + "y": -127.43871500234357, + "z": 0.0 + }, + { + "x": 153.70626211426654, + "y": -127.43826576433486, + "z": 0.0 + }, + { + "x": 152.70079136260694, + "y": -127.43781652632615, + "z": 0.0 + }, + { + "x": 151.69532061094736, + "y": -127.43736728831743, + "z": 0.0 + }, + { + "x": 150.6898498592878, + "y": -127.43691805030872, + "z": 0.0 + }, + { + "x": 149.68437910762808, + "y": -127.43646881230003, + "z": 0.0 + }, + { + "x": 148.67890835596862, + "y": -127.43601957429132, + "z": 0.0 + }, + { + "x": 147.67343760430902, + "y": -127.43557033628261, + "z": 0.0 + }, + { + "x": 146.66796685264944, + "y": -127.4351210982739, + "z": 0.0 + }, + { + "x": 145.66249610098987, + "y": -127.43467186026518, + "z": 0.0 + }, + { + "x": 144.65702534933027, + "y": -127.43422262225647, + "z": 0.0 + }, + { + "x": 143.6515545976707, + "y": -127.43377338424776, + "z": 0.0 + }, + { + "x": 142.64608384601098, + "y": -127.43332414623907, + "z": 0.0 + }, + { + "x": 141.64061309435152, + "y": -127.43287490823036, + "z": 0.0 + }, + { + "x": 140.63514234269192, + "y": -127.43242567022165, + "z": 0.0 + }, + { + "x": 139.62967159103235, + "y": -127.43197643221293, + "z": 0.0 + }, + { + "x": 138.62420083937278, + "y": -127.43152719420422, + "z": 0.0 + }, + { + "x": 137.61873008771317, + "y": -127.43107795619551, + "z": 0.0 + }, + { + "x": 136.6132593360536, + "y": -127.4306287181868, + "z": 0.0 + }, + { + "x": 135.607788584394, + "y": -127.43017948017808, + "z": 0.0 + }, + { + "x": 134.60231783273431, + "y": -127.4297302421694, + "z": 0.0 + }, + { + "x": 133.59684708107486, + "y": -127.42928100416069, + "z": 0.0 + }, + { + "x": 132.59137632941525, + "y": -127.42883176615197, + "z": 0.0 + }, + { + "x": 131.58590557775568, + "y": -127.42838252814326, + "z": 0.0 + }, + { + "x": 130.58043482609608, + "y": -127.42793329013455, + "z": 0.0 + }, + { + "x": 129.5749640744365, + "y": -127.42748405212583, + "z": 0.0 + }, + { + "x": 128.5694933227769, + "y": -127.42703481411712, + "z": 0.0 + }, + { + "x": 127.56402257111719, + "y": -127.42658557610844, + "z": 0.0 + }, + { + "x": 126.55855181945772, + "y": -127.42613633809972, + "z": 0.0 + }, + { + "x": 125.55308106779813, + "y": -127.42568710009101, + "z": 0.0 + }, + { + "x": 124.54761031613855, + "y": -127.4252378620823, + "z": 0.0 + }, + { + "x": 123.54213956447896, + "y": -127.42478862407359, + "z": 0.0 + }, + { + "x": 122.53666881281937, + "y": -127.42433938606487, + "z": 0.0 + }, + { + "x": 121.53119806115978, + "y": -127.42389014805616, + "z": 0.0 + }, + { + "x": 120.5257273095002, + "y": -127.42344091004745, + "z": 0.0 + }, + { + "x": 119.52025655784048, + "y": -127.42299167203876, + "z": 0.0 + }, + { + "x": 118.51478580618101, + "y": -127.42254243403005, + "z": 0.0 + }, + { + "x": 117.50931505452142, + "y": -127.42209319602134, + "z": 0.0 + }, + { + "x": 116.50384430286184, + "y": -127.42164395801262, + "z": 0.0 + }, + { + "x": 115.49837355120225, + "y": -127.42119472000391, + "z": 0.0 + }, + { + "x": 114.49290279954266, + "y": -127.4207454819952, + "z": 0.0 + }, + { + "x": 113.48743204788308, + "y": -127.42029624398648, + "z": 0.0 + }, + { + "x": 112.48196129622337, + "y": -127.4198470059778, + "z": 0.0 + }, + { + "x": 111.4764905445639, + "y": -127.41939776796909, + "z": 0.0 + }, + { + "x": 110.47101979290431, + "y": -127.41894852996037, + "z": 0.0 + }, + { + "x": 109.46554904124473, + "y": -127.41849929195166, + "z": 0.0 + }, + { + "x": 108.46007828958514, + "y": -127.41805005394295, + "z": 0.0 + }, + { + "x": 107.45460753792555, + "y": -127.41760081593424, + "z": 0.0 + }, + { + "x": 106.44913678626597, + "y": -127.41715157792552, + "z": 0.0 + }, + { + "x": 105.44366603460638, + "y": -127.41670233991681, + "z": 0.0 + }, + { + "x": 104.43819528294668, + "y": -127.41625310190813, + "z": 0.0 + }, + { + "x": 103.4327245312872, + "y": -127.41580386389941, + "z": 0.0 + }, + { + "x": 102.42725377962762, + "y": -127.4153546258907, + "z": 0.0 + }, + { + "x": 101.42178302796803, + "y": -127.41490538788199, + "z": 0.0 + } + ] + }, + { + "id": 21, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.4182086786346, + "y": -135.4149045893836, + "z": 0.0 + }, + { + "x": 102.42367943029419, + "y": -135.41535382739232, + "z": 0.0 + }, + { + "x": 103.42915018195377, + "y": -135.41580306540104, + "z": 0.0 + }, + { + "x": 104.43462093361347, + "y": -135.41625230340975, + "z": 0.0 + }, + { + "x": 105.44009168527295, + "y": -135.41670154141843, + "z": 0.0 + }, + { + "x": 106.44556243693253, + "y": -135.41715077942715, + "z": 0.0 + }, + { + "x": 107.45103318859212, + "y": -135.41760001743586, + "z": 0.0 + }, + { + "x": 108.45650394025171, + "y": -135.41804925544457, + "z": 0.0 + }, + { + "x": 109.4619746919113, + "y": -135.41849849345328, + "z": 0.0 + }, + { + "x": 110.46744544357088, + "y": -135.418947731462, + "z": 0.0 + }, + { + "x": 111.47291619523047, + "y": -135.4193969694707, + "z": 0.0 + }, + { + "x": 112.47838694689017, + "y": -135.41984620747942, + "z": 0.0 + }, + { + "x": 113.48385769854964, + "y": -135.4202954454881, + "z": 0.0 + }, + { + "x": 114.48932845020923, + "y": -135.42074468349682, + "z": 0.0 + }, + { + "x": 115.49479920186882, + "y": -135.42119392150553, + "z": 0.0 + }, + { + "x": 116.5002699535284, + "y": -135.42164315951425, + "z": 0.0 + }, + { + "x": 117.50574070518799, + "y": -135.42209239752296, + "z": 0.0 + }, + { + "x": 118.51121145684758, + "y": -135.42254163553167, + "z": 0.0 + }, + { + "x": 119.51668220850728, + "y": -135.42299087354039, + "z": 0.0 + }, + { + "x": 120.52215296016676, + "y": -135.42344011154907, + "z": 0.0 + }, + { + "x": 121.52762371182635, + "y": -135.42388934955778, + "z": 0.0 + }, + { + "x": 122.53309446348594, + "y": -135.4243385875665, + "z": 0.0 + }, + { + "x": 123.53856521514552, + "y": -135.4247878255752, + "z": 0.0 + }, + { + "x": 124.54403596680511, + "y": -135.42523706358392, + "z": 0.0 + }, + { + "x": 125.5495067184647, + "y": -135.42568630159263, + "z": 0.0 + }, + { + "x": 126.55497747012429, + "y": -135.42613553960135, + "z": 0.0 + }, + { + "x": 127.56044822178399, + "y": -135.42658477761006, + "z": 0.0 + }, + { + "x": 128.56591897344344, + "y": -135.42703401561874, + "z": 0.0 + }, + { + "x": 129.57138972510305, + "y": -135.42748325362746, + "z": 0.0 + }, + { + "x": 130.57686047676262, + "y": -135.42793249163617, + "z": 0.0 + }, + { + "x": 131.58233122842222, + "y": -135.42838172964488, + "z": 0.0 + }, + { + "x": 132.5878019800818, + "y": -135.4288309676536, + "z": 0.0 + }, + { + "x": 133.5932727317414, + "y": -135.4292802056623, + "z": 0.0 + }, + { + "x": 134.59874348340108, + "y": -135.42972944367102, + "z": 0.0 + }, + { + "x": 135.60421423506054, + "y": -135.4301786816797, + "z": 0.0 + }, + { + "x": 136.60968498672014, + "y": -135.43062791968842, + "z": 0.0 + }, + { + "x": 137.6151557383797, + "y": -135.43107715769713, + "z": 0.0 + }, + { + "x": 138.6206264900393, + "y": -135.43152639570584, + "z": 0.0 + }, + { + "x": 139.6260972416989, + "y": -135.43197563371456, + "z": 0.0 + }, + { + "x": 140.63156799335846, + "y": -135.43242487172327, + "z": 0.0 + }, + { + "x": 141.63703874501806, + "y": -135.43287410973198, + "z": 0.0 + }, + { + "x": 142.64250949667775, + "y": -135.4333233477407, + "z": 0.0 + }, + { + "x": 143.64798024833723, + "y": -135.43377258574938, + "z": 0.0 + }, + { + "x": 144.6534509999968, + "y": -135.4342218237581, + "z": 0.0 + }, + { + "x": 145.6589217516564, + "y": -135.4346710617668, + "z": 0.0 + }, + { + "x": 146.66439250331598, + "y": -135.43512029977552, + "z": 0.0 + }, + { + "x": 147.66986325497555, + "y": -135.43556953778423, + "z": 0.0 + }, + { + "x": 148.67533400663515, + "y": -135.43601877579295, + "z": 0.0 + }, + { + "x": 149.68080475829484, + "y": -135.43646801380166, + "z": 0.0 + }, + { + "x": 150.68627550995433, + "y": -135.43691725181034, + "z": 0.0 + }, + { + "x": 151.6917462616139, + "y": -135.43736648981906, + "z": 0.0 + }, + { + "x": 152.69721701327347, + "y": -135.43781572782777, + "z": 0.0 + }, + { + "x": 153.70268776493307, + "y": -135.43826496583648, + "z": 0.0 + }, + { + "x": 154.70815851659265, + "y": -135.4387142038452, + "z": 0.0 + }, + { + "x": 155.71362926825225, + "y": -135.4391634418539, + "z": 0.0 + }, + { + "x": 156.71910001991182, + "y": -135.43961267986262, + "z": 0.0 + }, + { + "x": 157.7245707715715, + "y": -135.44006191787133, + "z": 0.0 + }, + { + "x": 158.730041523231, + "y": -135.44051115588002, + "z": 0.0 + }, + { + "x": 159.73551227489057, + "y": -135.44096039388873, + "z": 0.0 + }, + { + "x": 160.74098302655017, + "y": -135.44140963189744, + "z": 0.0 + }, + { + "x": 161.74645377820974, + "y": -135.44185886990616, + "z": 0.0 + }, + { + "x": 162.7519245298693, + "y": -135.44230810791487, + "z": 0.0 + }, + { + "x": 163.75739528152891, + "y": -135.44275734592358, + "z": 0.0 + }, + { + "x": 164.7628660331886, + "y": -135.4432065839323, + "z": 0.0 + }, + { + "x": 165.7683367848481, + "y": -135.44365582194098, + "z": 0.0 + }, + { + "x": 166.77380753650766, + "y": -135.4441050599497, + "z": 0.0 + }, + { + "x": 167.77927828816726, + "y": -135.4445542979584, + "z": 0.0 + }, + { + "x": 168.78474903982686, + "y": -135.44500353596712, + "z": 0.0 + }, + { + "x": 169.79021979148644, + "y": -135.44545277397583, + "z": 0.0 + }, + { + "x": 170.79569054314604, + "y": -135.44590201198454, + "z": 0.0 + }, + { + "x": 171.80116129480564, + "y": -135.44635124999326, + "z": 0.0 + }, + { + "x": 172.80663204646532, + "y": -135.44680048800197, + "z": 0.0 + }, + { + "x": 173.8121027981248, + "y": -135.44724972601065, + "z": 0.0 + }, + { + "x": 174.8175735497844, + "y": -135.44769896401937, + "z": 0.0 + }, + { + "x": 175.82304430144399, + "y": -135.44814820202808, + "z": 0.0 + }, + { + "x": 176.8285150531036, + "y": -135.4485974400368, + "z": 0.0 + }, + { + "x": 177.8339858047632, + "y": -135.4490466780455, + "z": 0.0 + }, + { + "x": 178.83945655642276, + "y": -135.44949591605422, + "z": 0.0 + }, + { + "x": 179.84492730808248, + "y": -135.44994515406293, + "z": 0.0 + }, + { + "x": 180.85039805974196, + "y": -135.45039439207162, + "z": 0.0 + }, + { + "x": 181.85586881140154, + "y": -135.45084363008033, + "z": 0.0 + }, + { + "x": 182.86133956306114, + "y": -135.45129286808904, + "z": 0.0 + }, + { + "x": 183.86681031472074, + "y": -135.45174210609775, + "z": 0.0 + }, + { + "x": 184.8722810663803, + "y": -135.45219134410647, + "z": 0.0 + }, + { + "x": 185.8777518180399, + "y": -135.45264058211518, + "z": 0.0 + }, + { + "x": 186.8832225696995, + "y": -135.4530898201239, + "z": 0.0 + }, + { + "x": 187.8886933213592, + "y": -135.4535390581326, + "z": 0.0 + }, + { + "x": 188.8941640730187, + "y": -135.4539882961413, + "z": 0.0 + }, + { + "x": 189.8996348246783, + "y": -135.45443753415, + "z": 0.0 + }, + { + "x": 190.90510557633786, + "y": -135.45488677215872, + "z": 0.0 + }, + { + "x": 191.91057632799746, + "y": -135.45533601016743, + "z": 0.0 + }, + { + "x": 192.91604707965703, + "y": -135.45578524817614, + "z": 0.0 + }, + { + "x": 193.92151783131663, + "y": -135.45623448618485, + "z": 0.0 + }, + { + "x": 194.92698858297635, + "y": -135.45668372419357, + "z": 0.0 + }, + { + "x": 195.9324593346358, + "y": -135.45713296220225, + "z": 0.0 + }, + { + "x": 196.9379300862954, + "y": -135.45758220021096, + "z": 0.0 + }, + { + "x": 197.943400837955, + "y": -135.45803143821968, + "z": 0.0 + }, + { + "x": 198.94887158961458, + "y": -135.4584806762284, + "z": 0.0 + }, + { + "x": 199.95434234127418, + "y": -135.4589299142371, + "z": 0.0 + }, + { + "x": 200.95981309293379, + "y": -135.45937915224582, + "z": 0.0 + }, + { + "x": 201.96528384459336, + "y": -135.45982839025453, + "z": 0.0 + }, + { + "x": 202.97075459625307, + "y": -135.46027762826324, + "z": 0.0 + }, + { + "x": 203.97622534791256, + "y": -135.46072686627193, + "z": 0.0 + }, + { + "x": 204.98169609957213, + "y": -135.46117610428064, + "z": 0.0 + }, + { + "x": 205.98716685123173, + "y": -135.46162534228935, + "z": 0.0 + }, + { + "x": 206.99263760289134, + "y": -135.46207458029807, + "z": 0.0 + }, + { + "x": 207.9981083545509, + "y": -135.46252381830678, + "z": 0.0 + }, + { + "x": 209.0035791062105, + "y": -135.4629730563155, + "z": 0.0 + }, + { + "x": 210.00904985787022, + "y": -135.4634222943242, + "z": 0.0 + }, + { + "x": 211.01452060952968, + "y": -135.4638715323329, + "z": 0.0 + }, + { + "x": 212.01999136118928, + "y": -135.4643207703416, + "z": 0.0 + }, + { + "x": 213.02546211284888, + "y": -135.4647700083503, + "z": 0.0 + }, + { + "x": 214.03093286450846, + "y": -135.46521924635903, + "z": 0.0 + }, + { + "x": 215.03640361616806, + "y": -135.46566848436774, + "z": 0.0 + }, + { + "x": 216.04187436782766, + "y": -135.46611772237645, + "z": 0.0 + }, + { + "x": 217.04734511948723, + "y": -135.46656696038517, + "z": 0.0 + }, + { + "x": 218.05281587114695, + "y": -135.46701619839388, + "z": 0.0 + }, + { + "x": 219.05828662280643, + "y": -135.46746543640256, + "z": 0.0 + }, + { + "x": 220.063757374466, + "y": -135.46791467441128, + "z": 0.0 + }, + { + "x": 221.0692281261256, + "y": -135.46836391242, + "z": 0.0 + }, + { + "x": 222.0746988777852, + "y": -135.4688131504287, + "z": 0.0 + }, + { + "x": 223.08016962944478, + "y": -135.46926238843741, + "z": 0.0 + }, + { + "x": 224.08564038110438, + "y": -135.46971162644613, + "z": 0.0 + }, + { + "x": 225.09111113276407, + "y": -135.47016086445484, + "z": 0.0 + }, + { + "x": 226.09658188442356, + "y": -135.47061010246352, + "z": 0.0 + }, + { + "x": 227.10205263608316, + "y": -135.47105934047224, + "z": 0.0 + }, + { + "x": 228.10752338774273, + "y": -135.47150857848095, + "z": 0.0 + }, + { + "x": 229.11299413940233, + "y": -135.47195781648966, + "z": 0.0 + }, + { + "x": 230.11846489106193, + "y": -135.47240705449838, + "z": 0.0 + }, + { + "x": 231.1239356427215, + "y": -135.4728562925071, + "z": 0.0 + }, + { + "x": 232.12940639438108, + "y": -135.4733055305158, + "z": 0.0 + }, + { + "x": 233.13487714604076, + "y": -135.47375476852451, + "z": 0.0 + }, + { + "x": 234.14034789770025, + "y": -135.4742040065332, + "z": 0.0 + }, + { + "x": 235.14581864935982, + "y": -135.4746532445419, + "z": 0.0 + }, + { + "x": 236.1512894010194, + "y": -135.47510248255062, + "z": 0.0 + }, + { + "x": 237.15676015267897, + "y": -135.47555172055934, + "z": 0.0 + }, + { + "x": 238.16223090433854, + "y": -135.47600095856805, + "z": 0.0 + }, + { + "x": 239.16770165599812, + "y": -135.47645019657676, + "z": 0.0 + }, + { + "x": 240.17317240765783, + "y": -135.47689943458548, + "z": 0.0 + }, + { + "x": 241.1786431593173, + "y": -135.47734867259416, + "z": 0.0 + }, + { + "x": 242.18411391097686, + "y": -135.47779791060287, + "z": 0.0 + }, + { + "x": 243.18958466263643, + "y": -135.4782471486116, + "z": 0.0 + }, + { + "x": 244.195055414296, + "y": -135.4786963866203, + "z": 0.0 + }, + { + "x": 245.20052616595558, + "y": -135.479145624629, + "z": 0.0 + }, + { + "x": 246.20599691761515, + "y": -135.47959486263773, + "z": 0.0 + }, + { + "x": 247.21146766927475, + "y": -135.48004410064644, + "z": 0.0 + }, + { + "x": 248.21693842093444, + "y": -135.48049333865515, + "z": 0.0 + }, + { + "x": 249.2224091725939, + "y": -135.48094257666384, + "z": 0.0 + }, + { + "x": 250.22787992425347, + "y": -135.48139181467255, + "z": 0.0 + }, + { + "x": 251.23335067591304, + "y": -135.48184105268126, + "z": 0.0 + }, + { + "x": 252.23882142757262, + "y": -135.48229029068997, + "z": 0.0 + }, + { + "x": 253.24429217923222, + "y": -135.4827395286987, + "z": 0.0 + }, + { + "x": 254.2497629308918, + "y": -135.4831887667074, + "z": 0.0 + }, + { + "x": 255.25523368255148, + "y": -135.4836380047161, + "z": 0.0 + }, + { + "x": 256.26070443421094, + "y": -135.4840872427248, + "z": 0.0 + }, + { + "x": 257.2661751858705, + "y": -135.4845364807335, + "z": 0.0 + }, + { + "x": 258.2716459375301, + "y": -135.48498571874222, + "z": 0.0 + }, + { + "x": 259.27711668918965, + "y": -135.48543495675094, + "z": 0.0 + }, + { + "x": 260.2825874408492, + "y": -135.48588419475965, + "z": 0.0 + }, + { + "x": 261.2880581925088, + "y": -135.48633343276836, + "z": 0.0 + }, + { + "x": 262.2935289441684, + "y": -135.48678267077707, + "z": 0.0 + }, + { + "x": 263.29899969582806, + "y": -135.4872319087858, + "z": 0.0 + }, + { + "x": 264.3044704474875, + "y": -135.48768114679447, + "z": 0.0 + }, + { + "x": 265.3099411991471, + "y": -135.48813038480318, + "z": 0.0 + }, + { + "x": 266.3154119508067, + "y": -135.4885796228119, + "z": 0.0 + }, + { + "x": 267.3208827024663, + "y": -135.4890288608206, + "z": 0.0 + }, + { + "x": 268.32635345412587, + "y": -135.48947809882932, + "z": 0.0 + }, + { + "x": 269.33182420578544, + "y": -135.48992733683804, + "z": 0.0 + }, + { + "x": 270.3372949574451, + "y": -135.49037657484675, + "z": 0.0 + }, + { + "x": 271.3427657091046, + "y": -135.49082581285543, + "z": 0.0 + }, + { + "x": 272.34823646076416, + "y": -135.49127505086415, + "z": 0.0 + }, + { + "x": 273.3537072124237, + "y": -135.49172428887286, + "z": 0.0 + }, + { + "x": 274.3591779640833, + "y": -135.49217352688157, + "z": 0.0 + }, + { + "x": 275.3646487157429, + "y": -135.49262276489029, + "z": 0.0 + }, + { + "x": 276.37011946740245, + "y": -135.493072002899, + "z": 0.0 + }, + { + "x": 277.375590219062, + "y": -135.4935212409077, + "z": 0.0 + }, + { + "x": 278.38106097072176, + "y": -135.49397047891642, + "z": 0.0 + }, + { + "x": 279.3865317223812, + "y": -135.4944197169251, + "z": 0.0 + }, + { + "x": 280.3920024740408, + "y": -135.49486895493382, + "z": 0.0 + }, + { + "x": 281.39747322570037, + "y": -135.49531819294253, + "z": 0.0 + }, + { + "x": 282.40294397735994, + "y": -135.49576743095125, + "z": 0.0 + }, + { + "x": 283.4084147290195, + "y": -135.49621666895996, + "z": 0.0 + }, + { + "x": 284.4138854806791, + "y": -135.49666590696867, + "z": 0.0 + }, + { + "x": 285.41935623233877, + "y": -135.49711514497739, + "z": 0.0 + }, + { + "x": 286.42482698399823, + "y": -135.49756438298607, + "z": 0.0 + }, + { + "x": 287.4302977356578, + "y": -135.49801362099478, + "z": 0.0 + }, + { + "x": 288.4357684873174, + "y": -135.4984628590035, + "z": 0.0 + }, + { + "x": 289.44123923897695, + "y": -135.4989120970122, + "z": 0.0 + }, + { + "x": 290.4467099906365, + "y": -135.49936133502092, + "z": 0.0 + }, + { + "x": 291.45218074229615, + "y": -135.49981057302963, + "z": 0.0 + }, + { + "x": 292.4576514939557, + "y": -135.50025981103835, + "z": 0.0 + }, + { + "x": 293.4631222456154, + "y": -135.50070904904706, + "z": 0.0 + }, + { + "x": 294.46859299727487, + "y": -135.50115828705574, + "z": 0.0 + }, + { + "x": 295.47406374893444, + "y": -135.50160752506446, + "z": 0.0 + }, + { + "x": 296.479534500594, + "y": -135.50205676307317, + "z": 0.0 + }, + { + "x": 297.4850052522536, + "y": -135.50250600108188, + "z": 0.0 + }, + { + "x": 298.49047600391316, + "y": -135.5029552390906, + "z": 0.0 + }, + { + "x": 299.49594675557273, + "y": -135.5034044770993, + "z": 0.0 + }, + { + "x": 300.5014175072324, + "y": -135.50385371510802, + "z": 0.0 + }, + { + "x": 301.5068882588919, + "y": -135.5043029531167, + "z": 0.0 + }, + { + "x": 302.51235901055145, + "y": -135.50475219112542, + "z": 0.0 + }, + { + "x": 303.517829762211, + "y": -135.50520142913413, + "z": 0.0 + }, + { + "x": 304.52330051387065, + "y": -135.50565066714285, + "z": 0.0 + }, + { + "x": 305.5287712655302, + "y": -135.50609990515156, + "z": 0.0 + }, + { + "x": 306.5342420171898, + "y": -135.50654914316027, + "z": 0.0 + }, + { + "x": 307.53971276884937, + "y": -135.50699838116898, + "z": 0.0 + }, + { + "x": 308.54518352050906, + "y": -135.5074476191777, + "z": 0.0 + }, + { + "x": 309.5506542721685, + "y": -135.50789685718638, + "z": 0.0 + }, + { + "x": 310.5561250238281, + "y": -135.5083460951951, + "z": 0.0 + }, + { + "x": 311.56159577548766, + "y": -135.5087953332038, + "z": 0.0 + }, + { + "x": 312.56706652714723, + "y": -135.50924457121252, + "z": 0.0 + }, + { + "x": 313.5725372788068, + "y": -135.50969380922123, + "z": 0.0 + }, + { + "x": 314.5780080304664, + "y": -135.51014304722995, + "z": 0.0 + }, + { + "x": 315.58347878212606, + "y": -135.51059228523866, + "z": 0.0 + }, + { + "x": 316.5889495337855, + "y": -135.51104152324734, + "z": 0.0 + }, + { + "x": 317.59442028544515, + "y": -135.51149076125606, + "z": 0.0 + }, + { + "x": 318.5998910371047, + "y": -135.51193999926477, + "z": 0.0 + }, + { + "x": 319.6053617887643, + "y": -135.51238923727348, + "z": 0.0 + }, + { + "x": 320.61083254042387, + "y": -135.5128384752822, + "z": 0.0 + }, + { + "x": 321.61630329208344, + "y": -135.5132877132909, + "z": 0.0 + }, + { + "x": 322.621774043743, + "y": -135.51373695129962, + "z": 0.0 + }, + { + "x": 323.6272447954027, + "y": -135.51418618930833, + "z": 0.0 + }, + { + "x": 324.63271554706216, + "y": -135.51463542731702, + "z": 0.0 + }, + { + "x": 325.63818629872173, + "y": -135.51508466532573, + "z": 0.0 + } + ] + }, + { + "id": 22, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 101.41999585330132, + "y": -131.4149049886328, + "z": 0.0 + }, + { + "x": 102.4254666049609, + "y": -131.4153542266415, + "z": 0.0 + }, + { + "x": 103.43093735662049, + "y": -131.41580346465022, + "z": 0.0 + }, + { + "x": 104.43640810828008, + "y": -131.41625270265894, + "z": 0.0 + }, + { + "x": 105.44187885993966, + "y": -131.41670194066762, + "z": 0.0 + }, + { + "x": 106.44734961159925, + "y": -131.41715117867633, + "z": 0.0 + }, + { + "x": 107.45282036325884, + "y": -131.41760041668505, + "z": 0.0 + }, + { + "x": 108.45829111491842, + "y": -131.41804965469376, + "z": 0.0 + }, + { + "x": 109.46376186657801, + "y": -131.41849889270247, + "z": 0.0 + }, + { + "x": 110.4692326182376, + "y": -131.4189481307112, + "z": 0.0 + }, + { + "x": 111.47470336989718, + "y": -131.4193973687199, + "z": 0.0 + }, + { + "x": 112.48017412155677, + "y": -131.4198466067286, + "z": 0.0 + }, + { + "x": 113.48564487321636, + "y": -131.4202958447373, + "z": 0.0 + }, + { + "x": 114.49111562487595, + "y": -131.420745082746, + "z": 0.0 + }, + { + "x": 115.49658637653553, + "y": -131.42119432075472, + "z": 0.0 + }, + { + "x": 116.50205712819512, + "y": -131.42164355876344, + "z": 0.0 + }, + { + "x": 117.5075278798547, + "y": -131.42209279677215, + "z": 0.0 + }, + { + "x": 118.5129986315143, + "y": -131.42254203478086, + "z": 0.0 + }, + { + "x": 119.51846938317388, + "y": -131.42299127278957, + "z": 0.0 + }, + { + "x": 120.52394013483348, + "y": -131.42344051079826, + "z": 0.0 + }, + { + "x": 121.52941088649307, + "y": -131.42388974880697, + "z": 0.0 + }, + { + "x": 122.53488163815265, + "y": -131.42433898681568, + "z": 0.0 + }, + { + "x": 123.54035238981224, + "y": -131.4247882248244, + "z": 0.0 + }, + { + "x": 124.54582314147183, + "y": -131.4252374628331, + "z": 0.0 + }, + { + "x": 125.55129389313142, + "y": -131.42568670084182, + "z": 0.0 + }, + { + "x": 126.556764644791, + "y": -131.42613593885054, + "z": 0.0 + }, + { + "x": 127.56223539645059, + "y": -131.42658517685925, + "z": 0.0 + }, + { + "x": 128.56770614811018, + "y": -131.42703441486793, + "z": 0.0 + }, + { + "x": 129.57317689976978, + "y": -131.42748365287665, + "z": 0.0 + }, + { + "x": 130.57864765142935, + "y": -131.42793289088536, + "z": 0.0 + }, + { + "x": 131.58411840308895, + "y": -131.42838212889407, + "z": 0.0 + }, + { + "x": 132.58958915474852, + "y": -131.42883136690278, + "z": 0.0 + }, + { + "x": 133.59505990640812, + "y": -131.4292806049115, + "z": 0.0 + }, + { + "x": 134.6005306580677, + "y": -131.4297298429202, + "z": 0.0 + }, + { + "x": 135.60600140972727, + "y": -131.4301790809289, + "z": 0.0 + }, + { + "x": 136.61147216138687, + "y": -131.4306283189376, + "z": 0.0 + }, + { + "x": 137.61694291304644, + "y": -131.43107755694632, + "z": 0.0 + }, + { + "x": 138.62241366470604, + "y": -131.43152679495503, + "z": 0.0 + }, + { + "x": 139.62788441636562, + "y": -131.43197603296375, + "z": 0.0 + }, + { + "x": 140.6333551680252, + "y": -131.43242527097246, + "z": 0.0 + }, + { + "x": 141.6388259196848, + "y": -131.43287450898117, + "z": 0.0 + }, + { + "x": 142.64429667134436, + "y": -131.43332374698988, + "z": 0.0 + }, + { + "x": 143.64976742300396, + "y": -131.43377298499857, + "z": 0.0 + }, + { + "x": 144.65523817466354, + "y": -131.43422222300728, + "z": 0.0 + }, + { + "x": 145.66070892632314, + "y": -131.434671461016, + "z": 0.0 + }, + { + "x": 146.6661796779827, + "y": -131.4351206990247, + "z": 0.0 + }, + { + "x": 147.67165042964228, + "y": -131.43556993703342, + "z": 0.0 + }, + { + "x": 148.67712118130189, + "y": -131.43601917504213, + "z": 0.0 + }, + { + "x": 149.68259193296146, + "y": -131.43646841305085, + "z": 0.0 + }, + { + "x": 150.68806268462106, + "y": -131.43691765105953, + "z": 0.0 + }, + { + "x": 151.69353343628063, + "y": -131.43736688906824, + "z": 0.0 + }, + { + "x": 152.6990041879402, + "y": -131.43781612707696, + "z": 0.0 + }, + { + "x": 153.7044749395998, + "y": -131.43826536508567, + "z": 0.0 + }, + { + "x": 154.70994569125938, + "y": -131.43871460309438, + "z": 0.0 + }, + { + "x": 155.71541644291898, + "y": -131.4391638411031, + "z": 0.0 + }, + { + "x": 156.72088719457855, + "y": -131.4396130791118, + "z": 0.0 + }, + { + "x": 157.72635794623812, + "y": -131.44006231712052, + "z": 0.0 + }, + { + "x": 158.73182869789773, + "y": -131.4405115551292, + "z": 0.0 + }, + { + "x": 159.7372994495573, + "y": -131.44096079313792, + "z": 0.0 + }, + { + "x": 160.7427702012169, + "y": -131.44141003114663, + "z": 0.0 + }, + { + "x": 161.74824095287647, + "y": -131.44185926915534, + "z": 0.0 + }, + { + "x": 162.75371170453604, + "y": -131.44230850716406, + "z": 0.0 + }, + { + "x": 163.75918245619565, + "y": -131.44275774517277, + "z": 0.0 + }, + { + "x": 164.76465320785522, + "y": -131.44320698318148, + "z": 0.0 + }, + { + "x": 165.77012395951482, + "y": -131.44365622119017, + "z": 0.0 + }, + { + "x": 166.7755947111744, + "y": -131.44410545919888, + "z": 0.0 + }, + { + "x": 167.781065462834, + "y": -131.4445546972076, + "z": 0.0 + }, + { + "x": 168.7865362144936, + "y": -131.4450039352163, + "z": 0.0 + }, + { + "x": 169.79200696615317, + "y": -131.44545317322502, + "z": 0.0 + }, + { + "x": 170.79747771781277, + "y": -131.44590241123373, + "z": 0.0 + }, + { + "x": 171.80294846947237, + "y": -131.44635164924244, + "z": 0.0 + }, + { + "x": 172.80841922113194, + "y": -131.44680088725116, + "z": 0.0 + }, + { + "x": 173.81388997279154, + "y": -131.44725012525984, + "z": 0.0 + }, + { + "x": 174.81936072445114, + "y": -131.44769936326855, + "z": 0.0 + }, + { + "x": 175.82483147611072, + "y": -131.44814860127727, + "z": 0.0 + }, + { + "x": 176.83030222777032, + "y": -131.44859783928598, + "z": 0.0 + }, + { + "x": 177.83577297942992, + "y": -131.4490470772947, + "z": 0.0 + }, + { + "x": 178.8412437310895, + "y": -131.4494963153034, + "z": 0.0 + }, + { + "x": 179.8467144827491, + "y": -131.44994555331212, + "z": 0.0 + }, + { + "x": 180.8521852344087, + "y": -131.4503947913208, + "z": 0.0 + }, + { + "x": 181.85765598606827, + "y": -131.45084402932952, + "z": 0.0 + }, + { + "x": 182.86312673772787, + "y": -131.45129326733823, + "z": 0.0 + }, + { + "x": 183.86859748938747, + "y": -131.45174250534694, + "z": 0.0 + }, + { + "x": 184.87406824104704, + "y": -131.45219174335566, + "z": 0.0 + }, + { + "x": 185.87953899270664, + "y": -131.45264098136437, + "z": 0.0 + }, + { + "x": 186.88500974436624, + "y": -131.45309021937308, + "z": 0.0 + }, + { + "x": 187.89048049602582, + "y": -131.4535394573818, + "z": 0.0 + }, + { + "x": 188.89595124768542, + "y": -131.45398869539048, + "z": 0.0 + }, + { + "x": 189.90142199934502, + "y": -131.4544379333992, + "z": 0.0 + }, + { + "x": 190.9068927510046, + "y": -131.4548871714079, + "z": 0.0 + }, + { + "x": 191.9123635026642, + "y": -131.45533640941662, + "z": 0.0 + }, + { + "x": 192.91783425432376, + "y": -131.45578564742533, + "z": 0.0 + }, + { + "x": 193.92330500598337, + "y": -131.45623488543404, + "z": 0.0 + }, + { + "x": 194.92877575764297, + "y": -131.45668412344276, + "z": 0.0 + }, + { + "x": 195.93424650930254, + "y": -131.45713336145144, + "z": 0.0 + }, + { + "x": 196.93971726096214, + "y": -131.45758259946015, + "z": 0.0 + }, + { + "x": 197.94518801262174, + "y": -131.45803183746887, + "z": 0.0 + }, + { + "x": 198.95065876428131, + "y": -131.45848107547758, + "z": 0.0 + }, + { + "x": 199.95612951594092, + "y": -131.4589303134863, + "z": 0.0 + }, + { + "x": 200.96160026760052, + "y": -131.459379551495, + "z": 0.0 + }, + { + "x": 201.9670710192601, + "y": -131.45982878950372, + "z": 0.0 + }, + { + "x": 202.9725417709197, + "y": -131.46027802751243, + "z": 0.0 + }, + { + "x": 203.9780125225793, + "y": -131.46072726552111, + "z": 0.0 + }, + { + "x": 204.98348327423886, + "y": -131.46117650352983, + "z": 0.0 + }, + { + "x": 205.98895402589847, + "y": -131.46162574153854, + "z": 0.0 + }, + { + "x": 206.99442477755807, + "y": -131.46207497954725, + "z": 0.0 + }, + { + "x": 207.99989552921764, + "y": -131.46252421755597, + "z": 0.0 + }, + { + "x": 209.00536628087724, + "y": -131.46297345556468, + "z": 0.0 + }, + { + "x": 210.01083703253684, + "y": -131.4634226935734, + "z": 0.0 + }, + { + "x": 211.0163077841964, + "y": -131.46387193158208, + "z": 0.0 + }, + { + "x": 212.02177853585601, + "y": -131.4643211695908, + "z": 0.0 + }, + { + "x": 213.02724928751562, + "y": -131.4647704075995, + "z": 0.0 + }, + { + "x": 214.0327200391752, + "y": -131.46521964560822, + "z": 0.0 + }, + { + "x": 215.0381907908348, + "y": -131.46566888361693, + "z": 0.0 + }, + { + "x": 216.0436615424944, + "y": -131.46611812162564, + "z": 0.0 + }, + { + "x": 217.04913229415396, + "y": -131.46656735963435, + "z": 0.0 + }, + { + "x": 218.05460304581356, + "y": -131.46701659764307, + "z": 0.0 + }, + { + "x": 219.06007379747317, + "y": -131.46746583565175, + "z": 0.0 + }, + { + "x": 220.06554454913274, + "y": -131.46791507366046, + "z": 0.0 + }, + { + "x": 221.07101530079234, + "y": -131.46836431166918, + "z": 0.0 + }, + { + "x": 222.07648605245194, + "y": -131.4688135496779, + "z": 0.0 + }, + { + "x": 223.0819568041115, + "y": -131.4692627876866, + "z": 0.0 + }, + { + "x": 224.08742755577111, + "y": -131.46971202569532, + "z": 0.0 + }, + { + "x": 225.0928983074307, + "y": -131.47016126370403, + "z": 0.0 + }, + { + "x": 226.0983690590903, + "y": -131.4706105017127, + "z": 0.0 + }, + { + "x": 227.1038398107499, + "y": -131.47105973972143, + "z": 0.0 + }, + { + "x": 228.10931056240946, + "y": -131.47150897773014, + "z": 0.0 + }, + { + "x": 229.11478131406906, + "y": -131.47195821573885, + "z": 0.0 + }, + { + "x": 230.12025206572866, + "y": -131.47240745374756, + "z": 0.0 + }, + { + "x": 231.12572281738824, + "y": -131.47285669175628, + "z": 0.0 + }, + { + "x": 232.1311935690478, + "y": -131.473305929765, + "z": 0.0 + }, + { + "x": 233.13666432070738, + "y": -131.4737551677737, + "z": 0.0 + }, + { + "x": 234.14213507236698, + "y": -131.4742044057824, + "z": 0.0 + }, + { + "x": 235.14760582402656, + "y": -131.4746536437911, + "z": 0.0 + }, + { + "x": 236.15307657568613, + "y": -131.4751028817998, + "z": 0.0 + }, + { + "x": 237.1585473273457, + "y": -131.47555211980853, + "z": 0.0 + }, + { + "x": 238.16401807900527, + "y": -131.47600135781724, + "z": 0.0 + }, + { + "x": 239.16948883066485, + "y": -131.47645059582595, + "z": 0.0 + }, + { + "x": 240.17495958232445, + "y": -131.47689983383466, + "z": 0.0 + }, + { + "x": 241.18043033398402, + "y": -131.47734907184335, + "z": 0.0 + }, + { + "x": 242.1859010856436, + "y": -131.47779830985206, + "z": 0.0 + }, + { + "x": 243.19137183730317, + "y": -131.47824754786078, + "z": 0.0 + }, + { + "x": 244.19684258896274, + "y": -131.4786967858695, + "z": 0.0 + }, + { + "x": 245.2023133406223, + "y": -131.4791460238782, + "z": 0.0 + }, + { + "x": 246.20778409228188, + "y": -131.4795952618869, + "z": 0.0 + }, + { + "x": 247.21325484394148, + "y": -131.48004449989563, + "z": 0.0 + }, + { + "x": 248.21872559560106, + "y": -131.48049373790434, + "z": 0.0 + }, + { + "x": 249.22419634726063, + "y": -131.48094297591302, + "z": 0.0 + }, + { + "x": 250.2296670989202, + "y": -131.48139221392174, + "z": 0.0 + }, + { + "x": 251.23513785057978, + "y": -131.48184145193045, + "z": 0.0 + }, + { + "x": 252.24060860223935, + "y": -131.48229068993916, + "z": 0.0 + }, + { + "x": 253.24607935389895, + "y": -131.48273992794788, + "z": 0.0 + }, + { + "x": 254.25155010555852, + "y": -131.4831891659566, + "z": 0.0 + }, + { + "x": 255.2570208572181, + "y": -131.4836384039653, + "z": 0.0 + }, + { + "x": 256.26249160887767, + "y": -131.48408764197399, + "z": 0.0 + }, + { + "x": 257.26796236053724, + "y": -131.4845368799827, + "z": 0.0 + }, + { + "x": 258.2734331121968, + "y": -131.4849861179914, + "z": 0.0 + }, + { + "x": 259.2789038638564, + "y": -131.48543535600012, + "z": 0.0 + }, + { + "x": 260.28437461551596, + "y": -131.48588459400884, + "z": 0.0 + }, + { + "x": 261.28984536717553, + "y": -131.48633383201755, + "z": 0.0 + }, + { + "x": 262.2953161188351, + "y": -131.48678307002626, + "z": 0.0 + }, + { + "x": 263.3007868704947, + "y": -131.48723230803498, + "z": 0.0 + }, + { + "x": 264.30625762215425, + "y": -131.48768154604366, + "z": 0.0 + }, + { + "x": 265.3117283738138, + "y": -131.48813078405237, + "z": 0.0 + }, + { + "x": 266.31719912547345, + "y": -131.4885800220611, + "z": 0.0 + }, + { + "x": 267.322669877133, + "y": -131.4890292600698, + "z": 0.0 + }, + { + "x": 268.3281406287926, + "y": -131.4894784980785, + "z": 0.0 + }, + { + "x": 269.33361138045217, + "y": -131.48992773608722, + "z": 0.0 + }, + { + "x": 270.33908213211174, + "y": -131.49037697409594, + "z": 0.0 + }, + { + "x": 271.3445528837713, + "y": -131.49082621210462, + "z": 0.0 + }, + { + "x": 272.3500236354309, + "y": -131.49127545011333, + "z": 0.0 + }, + { + "x": 273.35549438709046, + "y": -131.49172468812205, + "z": 0.0 + }, + { + "x": 274.36096513875003, + "y": -131.49217392613076, + "z": 0.0 + }, + { + "x": 275.3664358904096, + "y": -131.49262316413947, + "z": 0.0 + }, + { + "x": 276.3719066420692, + "y": -131.4930724021482, + "z": 0.0 + }, + { + "x": 277.37737739372875, + "y": -131.4935216401569, + "z": 0.0 + }, + { + "x": 278.3828481453884, + "y": -131.4939708781656, + "z": 0.0 + }, + { + "x": 279.38831889704795, + "y": -131.4944201161743, + "z": 0.0 + }, + { + "x": 280.3937896487075, + "y": -131.494869354183, + "z": 0.0 + }, + { + "x": 281.3992604003671, + "y": -131.49531859219172, + "z": 0.0 + }, + { + "x": 282.40473115202667, + "y": -131.49576783020044, + "z": 0.0 + }, + { + "x": 283.41020190368624, + "y": -131.49621706820915, + "z": 0.0 + }, + { + "x": 284.4156726553458, + "y": -131.49666630621786, + "z": 0.0 + }, + { + "x": 285.4211434070054, + "y": -131.49711554422657, + "z": 0.0 + }, + { + "x": 286.42661415866496, + "y": -131.49756478223526, + "z": 0.0 + }, + { + "x": 287.43208491032453, + "y": -131.49801402024397, + "z": 0.0 + }, + { + "x": 288.4375556619841, + "y": -131.49846325825268, + "z": 0.0 + }, + { + "x": 289.4430264136437, + "y": -131.4989124962614, + "z": 0.0 + }, + { + "x": 290.44849716530325, + "y": -131.4993617342701, + "z": 0.0 + }, + { + "x": 291.4539679169629, + "y": -131.49981097227882, + "z": 0.0 + }, + { + "x": 292.45943866862245, + "y": -131.50026021028754, + "z": 0.0 + }, + { + "x": 293.464909420282, + "y": -131.50070944829625, + "z": 0.0 + }, + { + "x": 294.4703801719416, + "y": -131.50115868630493, + "z": 0.0 + }, + { + "x": 295.4758509236012, + "y": -131.50160792431365, + "z": 0.0 + }, + { + "x": 296.48132167526074, + "y": -131.50205716232236, + "z": 0.0 + }, + { + "x": 297.4867924269203, + "y": -131.50250640033107, + "z": 0.0 + }, + { + "x": 298.4922631785799, + "y": -131.50295563833978, + "z": 0.0 + }, + { + "x": 299.49773393023946, + "y": -131.5034048763485, + "z": 0.0 + }, + { + "x": 300.50320468189904, + "y": -131.5038541143572, + "z": 0.0 + }, + { + "x": 301.5086754335586, + "y": -131.5043033523659, + "z": 0.0 + }, + { + "x": 302.5141461852182, + "y": -131.5047525903746, + "z": 0.0 + }, + { + "x": 303.51961693687775, + "y": -131.50520182838332, + "z": 0.0 + }, + { + "x": 304.5250876885374, + "y": -131.50565106639203, + "z": 0.0 + }, + { + "x": 305.53055844019696, + "y": -131.50610030440075, + "z": 0.0 + }, + { + "x": 306.5360291918565, + "y": -131.50654954240946, + "z": 0.0 + }, + { + "x": 307.5414999435161, + "y": -131.50699878041817, + "z": 0.0 + }, + { + "x": 308.5469706951757, + "y": -131.50744801842689, + "z": 0.0 + }, + { + "x": 309.55244144683525, + "y": -131.50789725643557, + "z": 0.0 + }, + { + "x": 310.5579121984948, + "y": -131.50834649444428, + "z": 0.0 + }, + { + "x": 311.5633829501544, + "y": -131.508795732453, + "z": 0.0 + }, + { + "x": 312.56885370181396, + "y": -131.5092449704617, + "z": 0.0 + }, + { + "x": 313.57432445347354, + "y": -131.50969420847042, + "z": 0.0 + }, + { + "x": 314.5797952051331, + "y": -131.51014344647913, + "z": 0.0 + }, + { + "x": 315.5852659567927, + "y": -131.51059268448785, + "z": 0.0 + }, + { + "x": 316.59073670845225, + "y": -131.51104192249653, + "z": 0.0 + }, + { + "x": 317.5962074601119, + "y": -131.51149116050524, + "z": 0.0 + }, + { + "x": 318.60167821177146, + "y": -131.51194039851396, + "z": 0.0 + }, + { + "x": 319.60714896343103, + "y": -131.51238963652267, + "z": 0.0 + }, + { + "x": 320.6126197150906, + "y": -131.51283887453138, + "z": 0.0 + }, + { + "x": 321.6180904667502, + "y": -131.5132881125401, + "z": 0.0 + }, + { + "x": 322.62356121840975, + "y": -131.5137373505488, + "z": 0.0 + }, + { + "x": 323.6290319700693, + "y": -131.51418658855752, + "z": 0.0 + }, + { + "x": 324.6345027217289, + "y": -131.5146358265662, + "z": 0.0 + }, + { + "x": 325.63997347338847, + "y": -131.51508506457492, + "z": 0.0 + } + ] + }, + { + "id": 23, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.6408670607218, + "y": -129.5150852641995, + "z": 0.0 + }, + { + "x": 324.63539630906223, + "y": -129.5146360261908, + "z": 0.0 + }, + { + "x": 323.62992555740266, + "y": -129.51418678818212, + "z": 0.0 + }, + { + "x": 322.6244548057431, + "y": -129.5137375501734, + "z": 0.0 + }, + { + "x": 321.6189840540835, + "y": -129.5132883121647, + "z": 0.0 + }, + { + "x": 320.61351330242394, + "y": -129.51283907415598, + "z": 0.0 + }, + { + "x": 319.60804255076437, + "y": -129.51238983614726, + "z": 0.0 + }, + { + "x": 318.6025717991048, + "y": -129.51194059813855, + "z": 0.0 + }, + { + "x": 317.5971010474452, + "y": -129.51149136012984, + "z": 0.0 + }, + { + "x": 316.59163029578565, + "y": -129.51104212212113, + "z": 0.0 + }, + { + "x": 315.58615954412596, + "y": -129.51059288411244, + "z": 0.0 + }, + { + "x": 314.5806887924665, + "y": -129.51014364610373, + "z": 0.0 + }, + { + "x": 313.57521804080693, + "y": -129.50969440809502, + "z": 0.0 + }, + { + "x": 312.56974728914736, + "y": -129.5092451700863, + "z": 0.0 + }, + { + "x": 311.5642765374878, + "y": -129.5087959320776, + "z": 0.0 + }, + { + "x": 310.5588057858282, + "y": -129.50834669406888, + "z": 0.0 + }, + { + "x": 309.55333503416864, + "y": -129.50789745606016, + "z": 0.0 + }, + { + "x": 308.54786428250895, + "y": -129.50744821805148, + "z": 0.0 + }, + { + "x": 307.5423935308495, + "y": -129.50699898004277, + "z": 0.0 + }, + { + "x": 306.5369227791899, + "y": -129.50654974203405, + "z": 0.0 + }, + { + "x": 305.53145202753035, + "y": -129.50610050402534, + "z": 0.0 + }, + { + "x": 304.5259812758708, + "y": -129.50565126601663, + "z": 0.0 + }, + { + "x": 303.5205105242111, + "y": -129.50520202800791, + "z": 0.0 + }, + { + "x": 302.5150397725515, + "y": -129.5047527899992, + "z": 0.0 + }, + { + "x": 301.50956902089195, + "y": -129.5043035519905, + "z": 0.0 + }, + { + "x": 300.5040982692324, + "y": -129.5038543139818, + "z": 0.0 + }, + { + "x": 299.4986275175728, + "y": -129.5034050759731, + "z": 0.0 + }, + { + "x": 298.4931567659132, + "y": -129.50295583796438, + "z": 0.0 + }, + { + "x": 297.48768601425365, + "y": -129.50250659995567, + "z": 0.0 + }, + { + "x": 296.4822152625941, + "y": -129.50205736194695, + "z": 0.0 + }, + { + "x": 295.4767445109345, + "y": -129.50160812393824, + "z": 0.0 + }, + { + "x": 294.47127375927494, + "y": -129.50115888592953, + "z": 0.0 + }, + { + "x": 293.46580300761536, + "y": -129.50070964792084, + "z": 0.0 + }, + { + "x": 292.4603322559558, + "y": -129.50026040991213, + "z": 0.0 + }, + { + "x": 291.4548615042962, + "y": -129.49981117190342, + "z": 0.0 + }, + { + "x": 290.44939075263665, + "y": -129.4993619338947, + "z": 0.0 + }, + { + "x": 289.4439200009771, + "y": -129.498912695886, + "z": 0.0 + }, + { + "x": 288.4384492493175, + "y": -129.49846345787728, + "z": 0.0 + }, + { + "x": 287.4329784976579, + "y": -129.49801421986857, + "z": 0.0 + }, + { + "x": 286.42750774599835, + "y": -129.49756498185985, + "z": 0.0 + }, + { + "x": 285.42203699433867, + "y": -129.49711574385117, + "z": 0.0 + }, + { + "x": 284.4165662426792, + "y": -129.49666650584246, + "z": 0.0 + }, + { + "x": 283.41109549101964, + "y": -129.49621726783374, + "z": 0.0 + }, + { + "x": 282.40562473936006, + "y": -129.49576802982503, + "z": 0.0 + }, + { + "x": 281.4001539877005, + "y": -129.49531879181632, + "z": 0.0 + }, + { + "x": 280.3946832360409, + "y": -129.4948695538076, + "z": 0.0 + }, + { + "x": 279.38921248438135, + "y": -129.4944203157989, + "z": 0.0 + }, + { + "x": 278.38374173272166, + "y": -129.4939710777902, + "z": 0.0 + }, + { + "x": 277.3782709810621, + "y": -129.4935218397815, + "z": 0.0 + }, + { + "x": 276.3728002294025, + "y": -129.49307260177278, + "z": 0.0 + }, + { + "x": 275.36732947774294, + "y": -129.49262336376407, + "z": 0.0 + }, + { + "x": 274.36185872608337, + "y": -129.49217412575535, + "z": 0.0 + }, + { + "x": 273.3563879744238, + "y": -129.49172488774664, + "z": 0.0 + }, + { + "x": 272.3509172227642, + "y": -129.49127564973793, + "z": 0.0 + }, + { + "x": 271.34544647110465, + "y": -129.49082641172922, + "z": 0.0 + }, + { + "x": 270.3399757194451, + "y": -129.49037717372053, + "z": 0.0 + }, + { + "x": 269.3345049677855, + "y": -129.48992793571182, + "z": 0.0 + }, + { + "x": 268.32903421612593, + "y": -129.4894786977031, + "z": 0.0 + }, + { + "x": 267.32356346446636, + "y": -129.4890294596944, + "z": 0.0 + }, + { + "x": 266.3180927128068, + "y": -129.48858022168568, + "z": 0.0 + }, + { + "x": 265.3126219611472, + "y": -129.48813098367697, + "z": 0.0 + }, + { + "x": 264.30715120948764, + "y": -129.48768174566825, + "z": 0.0 + }, + { + "x": 263.30168045782796, + "y": -129.48723250765957, + "z": 0.0 + }, + { + "x": 262.2962097061685, + "y": -129.48678326965086, + "z": 0.0 + }, + { + "x": 261.2907389545089, + "y": -129.48633403164214, + "z": 0.0 + }, + { + "x": 260.28526820284935, + "y": -129.48588479363343, + "z": 0.0 + }, + { + "x": 259.2797974511898, + "y": -129.48543555562472, + "z": 0.0 + }, + { + "x": 258.2743266995302, + "y": -129.484986317616, + "z": 0.0 + }, + { + "x": 257.26885594787063, + "y": -129.4845370796073, + "z": 0.0 + }, + { + "x": 256.26338519621106, + "y": -129.48408784159858, + "z": 0.0 + }, + { + "x": 255.2579144445514, + "y": -129.4836386035899, + "z": 0.0 + }, + { + "x": 254.2524436928919, + "y": -129.48318936558118, + "z": 0.0 + }, + { + "x": 253.24697294123231, + "y": -129.48274012757247, + "z": 0.0 + }, + { + "x": 252.2415021895727, + "y": -129.48229088956376, + "z": 0.0 + }, + { + "x": 251.23603143791314, + "y": -129.48184165155504, + "z": 0.0 + }, + { + "x": 250.23056068625357, + "y": -129.48139241354633, + "z": 0.0 + }, + { + "x": 249.225089934594, + "y": -129.48094317553762, + "z": 0.0 + }, + { + "x": 248.21961918293437, + "y": -129.48049393752893, + "z": 0.0 + }, + { + "x": 247.21414843127485, + "y": -129.48004469952022, + "z": 0.0 + }, + { + "x": 246.20867767961525, + "y": -129.4795954615115, + "z": 0.0 + }, + { + "x": 245.20320692795568, + "y": -129.4791462235028, + "z": 0.0 + }, + { + "x": 244.1977361762961, + "y": -129.47869698549408, + "z": 0.0 + }, + { + "x": 243.19226542463653, + "y": -129.47824774748537, + "z": 0.0 + }, + { + "x": 242.18679467297696, + "y": -129.47779850947666, + "z": 0.0 + }, + { + "x": 241.1813239213174, + "y": -129.47734927146794, + "z": 0.0 + }, + { + "x": 240.17585316965776, + "y": -129.47690003345926, + "z": 0.0 + }, + { + "x": 239.1703824179982, + "y": -129.47645079545055, + "z": 0.0 + }, + { + "x": 238.16491166633864, + "y": -129.47600155744183, + "z": 0.0 + }, + { + "x": 237.15944091467907, + "y": -129.47555231943312, + "z": 0.0 + }, + { + "x": 236.1539701630195, + "y": -129.4751030814244, + "z": 0.0 + }, + { + "x": 235.14849941135992, + "y": -129.4746538434157, + "z": 0.0 + }, + { + "x": 234.14302865970035, + "y": -129.47420460540698, + "z": 0.0 + }, + { + "x": 233.1375579080407, + "y": -129.4737553673983, + "z": 0.0 + }, + { + "x": 232.13208715638117, + "y": -129.47330612938958, + "z": 0.0 + }, + { + "x": 231.1266164047216, + "y": -129.47285689138087, + "z": 0.0 + }, + { + "x": 230.12114565306203, + "y": -129.47240765337216, + "z": 0.0 + }, + { + "x": 229.11567490140243, + "y": -129.47195841536345, + "z": 0.0 + }, + { + "x": 228.11020414974283, + "y": -129.47150917735473, + "z": 0.0 + }, + { + "x": 227.10473339808325, + "y": -129.47105993934602, + "z": 0.0 + }, + { + "x": 226.09926264642365, + "y": -129.4706107013373, + "z": 0.0 + }, + { + "x": 225.093791894764, + "y": -129.47016146332862, + "z": 0.0 + }, + { + "x": 224.08832114310448, + "y": -129.4697122253199, + "z": 0.0 + }, + { + "x": 223.08285039144488, + "y": -129.4692629873112, + "z": 0.0 + }, + { + "x": 222.0773796397853, + "y": -129.46881374930248, + "z": 0.0 + }, + { + "x": 221.0719088881257, + "y": -129.46836451129377, + "z": 0.0 + }, + { + "x": 220.0664381364661, + "y": -129.46791527328506, + "z": 0.0 + }, + { + "x": 219.06096738480653, + "y": -129.46746603527635, + "z": 0.0 + }, + { + "x": 218.05549663314687, + "y": -129.46701679726766, + "z": 0.0 + }, + { + "x": 217.05002588148733, + "y": -129.46656755925895, + "z": 0.0 + }, + { + "x": 216.04455512982776, + "y": -129.46611832125024, + "z": 0.0 + }, + { + "x": 215.03908437816816, + "y": -129.46566908324152, + "z": 0.0 + }, + { + "x": 214.03361362650855, + "y": -129.4652198452328, + "z": 0.0 + }, + { + "x": 213.02814287484898, + "y": -129.4647706072241, + "z": 0.0 + }, + { + "x": 212.02267212318938, + "y": -129.46432136921538, + "z": 0.0 + }, + { + "x": 211.01720137152978, + "y": -129.46387213120667, + "z": 0.0 + }, + { + "x": 210.01173061987015, + "y": -129.463422893198, + "z": 0.0 + }, + { + "x": 209.0062598682106, + "y": -129.46297365518927, + "z": 0.0 + }, + { + "x": 208.000789116551, + "y": -129.46252441718056, + "z": 0.0 + }, + { + "x": 206.99531836489143, + "y": -129.46207517917185, + "z": 0.0 + }, + { + "x": 205.98984761323183, + "y": -129.46162594116313, + "z": 0.0 + }, + { + "x": 204.98437686157223, + "y": -129.46117670315442, + "z": 0.0 + }, + { + "x": 203.97890610991266, + "y": -129.4607274651457, + "z": 0.0 + }, + { + "x": 202.973435358253, + "y": -129.46027822713702, + "z": 0.0 + }, + { + "x": 201.96796460659345, + "y": -129.4598289891283, + "z": 0.0 + }, + { + "x": 200.96249385493388, + "y": -129.4593797511196, + "z": 0.0 + }, + { + "x": 199.95702310327428, + "y": -129.4589305131109, + "z": 0.0 + }, + { + "x": 198.95155235161468, + "y": -129.45848127510217, + "z": 0.0 + }, + { + "x": 197.9460815999551, + "y": -129.45803203709346, + "z": 0.0 + }, + { + "x": 196.9406108482955, + "y": -129.45758279908475, + "z": 0.0 + }, + { + "x": 195.9351400966359, + "y": -129.45713356107603, + "z": 0.0 + }, + { + "x": 194.92966934497628, + "y": -129.45668432306735, + "z": 0.0 + }, + { + "x": 193.92419859331673, + "y": -129.45623508505864, + "z": 0.0 + }, + { + "x": 192.91872784165713, + "y": -129.45578584704992, + "z": 0.0 + }, + { + "x": 191.91325708999756, + "y": -129.4553366090412, + "z": 0.0 + }, + { + "x": 190.90778633833796, + "y": -129.4548873710325, + "z": 0.0 + }, + { + "x": 189.90231558667838, + "y": -129.45443813302379, + "z": 0.0 + }, + { + "x": 188.89684483501878, + "y": -129.45398889501507, + "z": 0.0 + }, + { + "x": 187.89137408335912, + "y": -129.4535396570064, + "z": 0.0 + }, + { + "x": 186.8859033316996, + "y": -129.45309041899768, + "z": 0.0 + }, + { + "x": 185.88043258004, + "y": -129.45264118098896, + "z": 0.0 + }, + { + "x": 184.8749618283804, + "y": -129.45219194298025, + "z": 0.0 + }, + { + "x": 183.86949107672083, + "y": -129.45174270497154, + "z": 0.0 + }, + { + "x": 182.86402032506123, + "y": -129.45129346696282, + "z": 0.0 + }, + { + "x": 181.85854957340163, + "y": -129.4508442289541, + "z": 0.0 + }, + { + "x": 180.85307882174206, + "y": -129.4503949909454, + "z": 0.0 + }, + { + "x": 179.8476080700824, + "y": -129.4499457529367, + "z": 0.0 + }, + { + "x": 178.84213731842286, + "y": -129.449496514928, + "z": 0.0 + }, + { + "x": 177.83666656676328, + "y": -129.4490472769193, + "z": 0.0 + }, + { + "x": 176.83119581510368, + "y": -129.44859803891057, + "z": 0.0 + }, + { + "x": 175.82572506344408, + "y": -129.44814880090186, + "z": 0.0 + }, + { + "x": 174.8202543117845, + "y": -129.44769956289315, + "z": 0.0 + }, + { + "x": 173.8147835601249, + "y": -129.44725032488444, + "z": 0.0 + }, + { + "x": 172.80931280846525, + "y": -129.44680108687575, + "z": 0.0 + }, + { + "x": 171.80384205680573, + "y": -129.44635184886704, + "z": 0.0 + }, + { + "x": 170.79837130514613, + "y": -129.44590261085833, + "z": 0.0 + }, + { + "x": 169.79290055348653, + "y": -129.4454533728496, + "z": 0.0 + }, + { + "x": 168.78742980182696, + "y": -129.4450041348409, + "z": 0.0 + }, + { + "x": 167.78195905016736, + "y": -129.4445548968322, + "z": 0.0 + }, + { + "x": 166.77648829850776, + "y": -129.44410565882347, + "z": 0.0 + }, + { + "x": 165.77101754684819, + "y": -129.44365642081476, + "z": 0.0 + }, + { + "x": 164.76554679518853, + "y": -129.44320718280608, + "z": 0.0 + }, + { + "x": 163.760076043529, + "y": -129.44275794479736, + "z": 0.0 + }, + { + "x": 162.7546052918694, + "y": -129.44230870678865, + "z": 0.0 + }, + { + "x": 161.74913454020984, + "y": -129.44185946877994, + "z": 0.0 + }, + { + "x": 160.74366378855026, + "y": -129.44141023077123, + "z": 0.0 + }, + { + "x": 159.73819303689066, + "y": -129.4409609927625, + "z": 0.0 + }, + { + "x": 158.7327222852311, + "y": -129.4405117547538, + "z": 0.0 + }, + { + "x": 157.72725153357143, + "y": -129.44006251674512, + "z": 0.0 + }, + { + "x": 156.72178078191192, + "y": -129.4396132787364, + "z": 0.0 + }, + { + "x": 155.71631003025234, + "y": -129.4391640407277, + "z": 0.0 + }, + { + "x": 154.71083927859274, + "y": -129.43871480271898, + "z": 0.0 + }, + { + "x": 153.70536852693317, + "y": -129.43826556471026, + "z": 0.0 + }, + { + "x": 152.69989777527357, + "y": -129.43781632670155, + "z": 0.0 + }, + { + "x": 151.694427023614, + "y": -129.43736708869284, + "z": 0.0 + }, + { + "x": 150.68895627195442, + "y": -129.43691785068413, + "z": 0.0 + }, + { + "x": 149.68348552029477, + "y": -129.43646861267544, + "z": 0.0 + }, + { + "x": 148.67801476863525, + "y": -129.43601937466673, + "z": 0.0 + }, + { + "x": 147.67254401697565, + "y": -129.43557013665801, + "z": 0.0 + }, + { + "x": 146.66707326531608, + "y": -129.4351208986493, + "z": 0.0 + }, + { + "x": 145.6616025136565, + "y": -129.4346716606406, + "z": 0.0 + }, + { + "x": 144.6561317619969, + "y": -129.43422242263188, + "z": 0.0 + }, + { + "x": 143.65066101033733, + "y": -129.43377318462316, + "z": 0.0 + }, + { + "x": 142.64519025867767, + "y": -129.43332394661448, + "z": 0.0 + }, + { + "x": 141.63971950701816, + "y": -129.43287470860577, + "z": 0.0 + }, + { + "x": 140.63424875535856, + "y": -129.43242547059705, + "z": 0.0 + }, + { + "x": 139.62877800369898, + "y": -129.43197623258834, + "z": 0.0 + }, + { + "x": 138.6233072520394, + "y": -129.43152699457963, + "z": 0.0 + }, + { + "x": 137.6178365003798, + "y": -129.43107775657091, + "z": 0.0 + }, + { + "x": 136.61236574872024, + "y": -129.4306285185622, + "z": 0.0 + }, + { + "x": 135.60689499706064, + "y": -129.4301792805535, + "z": 0.0 + }, + { + "x": 134.601424245401, + "y": -129.4297300425448, + "z": 0.0 + }, + { + "x": 133.5959534937415, + "y": -129.4292808045361, + "z": 0.0 + }, + { + "x": 132.5904827420819, + "y": -129.42883156652738, + "z": 0.0 + }, + { + "x": 131.58501199042232, + "y": -129.42838232851867, + "z": 0.0 + }, + { + "x": 130.57954123876272, + "y": -129.42793309050995, + "z": 0.0 + }, + { + "x": 129.57407048710314, + "y": -129.42748385250124, + "z": 0.0 + }, + { + "x": 128.56859973544354, + "y": -129.42703461449253, + "z": 0.0 + }, + { + "x": 127.56312898378388, + "y": -129.42658537648384, + "z": 0.0 + }, + { + "x": 126.55765823212437, + "y": -129.42613613847513, + "z": 0.0 + }, + { + "x": 125.55218748046477, + "y": -129.42568690046642, + "z": 0.0 + }, + { + "x": 124.5467167288052, + "y": -129.4252376624577, + "z": 0.0 + }, + { + "x": 123.54124597714559, + "y": -129.424788424449, + "z": 0.0 + }, + { + "x": 122.53577522548602, + "y": -129.42433918644028, + "z": 0.0 + }, + { + "x": 121.53030447382642, + "y": -129.42388994843157, + "z": 0.0 + }, + { + "x": 120.52483372216685, + "y": -129.42344071042285, + "z": 0.0 + }, + { + "x": 119.51936297050719, + "y": -129.42299147241417, + "z": 0.0 + }, + { + "x": 118.51389221884764, + "y": -129.42254223440546, + "z": 0.0 + }, + { + "x": 117.50842146718807, + "y": -129.42209299639674, + "z": 0.0 + }, + { + "x": 116.50295071552847, + "y": -129.42164375838803, + "z": 0.0 + }, + { + "x": 115.4974799638689, + "y": -129.42119452037932, + "z": 0.0 + }, + { + "x": 114.4920092122093, + "y": -129.4207452823706, + "z": 0.0 + }, + { + "x": 113.48653846054972, + "y": -129.4202960443619, + "z": 0.0 + }, + { + "x": 112.48106770889007, + "y": -129.4198468063532, + "z": 0.0 + }, + { + "x": 111.47559695723055, + "y": -129.4193975683445, + "z": 0.0 + }, + { + "x": 110.47012620557095, + "y": -129.41894833033578, + "z": 0.0 + }, + { + "x": 109.46465545391138, + "y": -129.41849909232707, + "z": 0.0 + }, + { + "x": 108.45918470225178, + "y": -129.41804985431835, + "z": 0.0 + }, + { + "x": 107.4537139505922, + "y": -129.41760061630964, + "z": 0.0 + }, + { + "x": 106.4482431989326, + "y": -129.41715137830093, + "z": 0.0 + }, + { + "x": 105.44277244727303, + "y": -129.41670214029222, + "z": 0.0 + }, + { + "x": 104.43730169561337, + "y": -129.41625290228353, + "z": 0.0 + }, + { + "x": 103.43183094395386, + "y": -129.41580366427482, + "z": 0.0 + }, + { + "x": 102.42636019229425, + "y": -129.4153544262661, + "z": 0.0 + }, + { + "x": 101.42088944063468, + "y": -129.4149051882574, + "z": 0.0 + } + ] + }, + { + "id": 24, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.41910226596795, + "y": -133.4149047890082, + "z": 0.0 + }, + { + "x": 102.42457301762755, + "y": -133.41535402701692, + "z": 0.0 + }, + { + "x": 103.43004376928712, + "y": -133.41580326502563, + "z": 0.0 + }, + { + "x": 104.43551452094678, + "y": -133.41625250303434, + "z": 0.0 + }, + { + "x": 105.4409852726063, + "y": -133.41670174104303, + "z": 0.0 + }, + { + "x": 106.4464560242659, + "y": -133.41715097905174, + "z": 0.0 + }, + { + "x": 107.45192677592547, + "y": -133.41760021706045, + "z": 0.0 + }, + { + "x": 108.45739752758507, + "y": -133.41804945506917, + "z": 0.0 + }, + { + "x": 109.46286827924465, + "y": -133.41849869307788, + "z": 0.0 + }, + { + "x": 110.46833903090425, + "y": -133.4189479310866, + "z": 0.0 + }, + { + "x": 111.47380978256382, + "y": -133.4193971690953, + "z": 0.0 + }, + { + "x": 112.47928053422348, + "y": -133.41984640710402, + "z": 0.0 + }, + { + "x": 113.484751285883, + "y": -133.4202956451127, + "z": 0.0 + }, + { + "x": 114.4902220375426, + "y": -133.42074488312142, + "z": 0.0 + }, + { + "x": 115.49569278920217, + "y": -133.42119412113013, + "z": 0.0 + }, + { + "x": 116.50116354086177, + "y": -133.42164335913884, + "z": 0.0 + }, + { + "x": 117.50663429252134, + "y": -133.42209259714755, + "z": 0.0 + }, + { + "x": 118.51210504418094, + "y": -133.42254183515627, + "z": 0.0 + }, + { + "x": 119.51757579584057, + "y": -133.42299107316498, + "z": 0.0 + }, + { + "x": 120.52304654750012, + "y": -133.42344031117366, + "z": 0.0 + }, + { + "x": 121.52851729915972, + "y": -133.42388954918238, + "z": 0.0 + }, + { + "x": 122.53398805081929, + "y": -133.4243387871911, + "z": 0.0 + }, + { + "x": 123.53945880247889, + "y": -133.4247880251998, + "z": 0.0 + }, + { + "x": 124.54492955413846, + "y": -133.42523726320852, + "z": 0.0 + }, + { + "x": 125.55040030579806, + "y": -133.42568650121723, + "z": 0.0 + }, + { + "x": 126.55587105745764, + "y": -133.42613573922594, + "z": 0.0 + }, + { + "x": 127.5613418091173, + "y": -133.42658497723465, + "z": 0.0 + }, + { + "x": 128.5668125607768, + "y": -133.42703421524334, + "z": 0.0 + }, + { + "x": 129.5722833124364, + "y": -133.42748345325205, + "z": 0.0 + }, + { + "x": 130.57775406409598, + "y": -133.42793269126076, + "z": 0.0 + }, + { + "x": 131.58322481575559, + "y": -133.42838192926948, + "z": 0.0 + }, + { + "x": 132.58869556741516, + "y": -133.4288311672782, + "z": 0.0 + }, + { + "x": 133.59416631907476, + "y": -133.4292804052869, + "z": 0.0 + }, + { + "x": 134.5996370707344, + "y": -133.42972964329562, + "z": 0.0 + }, + { + "x": 135.6051078223939, + "y": -133.4301788813043, + "z": 0.0 + }, + { + "x": 136.6105785740535, + "y": -133.430628119313, + "z": 0.0 + }, + { + "x": 137.61604932571308, + "y": -133.43107735732173, + "z": 0.0 + }, + { + "x": 138.62152007737268, + "y": -133.43152659533044, + "z": 0.0 + }, + { + "x": 139.62699082903225, + "y": -133.43197583333915, + "z": 0.0 + }, + { + "x": 140.63246158069182, + "y": -133.43242507134786, + "z": 0.0 + }, + { + "x": 141.63793233235143, + "y": -133.43287430935658, + "z": 0.0 + }, + { + "x": 142.64340308401106, + "y": -133.4333235473653, + "z": 0.0 + }, + { + "x": 143.6488738356706, + "y": -133.43377278537398, + "z": 0.0 + }, + { + "x": 144.65434458733017, + "y": -133.4342220233827, + "z": 0.0 + }, + { + "x": 145.65981533898977, + "y": -133.4346712613914, + "z": 0.0 + }, + { + "x": 146.66528609064935, + "y": -133.4351204994001, + "z": 0.0 + }, + { + "x": 147.67075684230892, + "y": -133.43556973740883, + "z": 0.0 + }, + { + "x": 148.67622759396852, + "y": -133.43601897541754, + "z": 0.0 + }, + { + "x": 149.68169834562815, + "y": -133.43646821342625, + "z": 0.0 + }, + { + "x": 150.6871690972877, + "y": -133.43691745143494, + "z": 0.0 + }, + { + "x": 151.69263984894727, + "y": -133.43736668944365, + "z": 0.0 + }, + { + "x": 152.69811060060684, + "y": -133.43781592745236, + "z": 0.0 + }, + { + "x": 153.70358135226644, + "y": -133.43826516546108, + "z": 0.0 + }, + { + "x": 154.709052103926, + "y": -133.4387144034698, + "z": 0.0 + }, + { + "x": 155.7145228555856, + "y": -133.4391636414785, + "z": 0.0 + }, + { + "x": 156.7199936072452, + "y": -133.4396128794872, + "z": 0.0 + }, + { + "x": 157.72546435890482, + "y": -133.44006211749593, + "z": 0.0 + }, + { + "x": 158.73093511056436, + "y": -133.4405113555046, + "z": 0.0 + }, + { + "x": 159.73640586222393, + "y": -133.44096059351332, + "z": 0.0 + }, + { + "x": 160.74187661388353, + "y": -133.44140983152204, + "z": 0.0 + }, + { + "x": 161.7473473655431, + "y": -133.44185906953075, + "z": 0.0 + }, + { + "x": 162.75281811720268, + "y": -133.44230830753946, + "z": 0.0 + }, + { + "x": 163.75828886886228, + "y": -133.44275754554818, + "z": 0.0 + }, + { + "x": 164.7637596205219, + "y": -133.4432067835569, + "z": 0.0 + }, + { + "x": 165.76923037218145, + "y": -133.44365602156557, + "z": 0.0 + }, + { + "x": 166.77470112384103, + "y": -133.4441052595743, + "z": 0.0 + }, + { + "x": 167.78017187550063, + "y": -133.444554497583, + "z": 0.0 + }, + { + "x": 168.78564262716023, + "y": -133.4450037355917, + "z": 0.0 + }, + { + "x": 169.7911133788198, + "y": -133.44545297360042, + "z": 0.0 + }, + { + "x": 170.7965841304794, + "y": -133.44590221160914, + "z": 0.0 + }, + { + "x": 171.802054882139, + "y": -133.44635144961785, + "z": 0.0 + }, + { + "x": 172.80752563379863, + "y": -133.44680068762656, + "z": 0.0 + }, + { + "x": 173.81299638545818, + "y": -133.44724992563525, + "z": 0.0 + }, + { + "x": 174.81846713711778, + "y": -133.44769916364396, + "z": 0.0 + }, + { + "x": 175.82393788877735, + "y": -133.44814840165267, + "z": 0.0 + }, + { + "x": 176.82940864043695, + "y": -133.4485976396614, + "z": 0.0 + }, + { + "x": 177.83487939209655, + "y": -133.4490468776701, + "z": 0.0 + }, + { + "x": 178.84035014375613, + "y": -133.4494961156788, + "z": 0.0 + }, + { + "x": 179.84582089541578, + "y": -133.44994535368753, + "z": 0.0 + }, + { + "x": 180.85129164707533, + "y": -133.4503945916962, + "z": 0.0 + }, + { + "x": 181.8567623987349, + "y": -133.45084382970492, + "z": 0.0 + }, + { + "x": 182.8622331503945, + "y": -133.45129306771364, + "z": 0.0 + }, + { + "x": 183.8677039020541, + "y": -133.45174230572235, + "z": 0.0 + }, + { + "x": 184.87317465371368, + "y": -133.45219154373106, + "z": 0.0 + }, + { + "x": 185.87864540537328, + "y": -133.45264078173977, + "z": 0.0 + }, + { + "x": 186.88411615703288, + "y": -133.4530900197485, + "z": 0.0 + }, + { + "x": 187.8895869086925, + "y": -133.4535392577572, + "z": 0.0 + }, + { + "x": 188.89505766035205, + "y": -133.45398849576588, + "z": 0.0 + }, + { + "x": 189.90052841201165, + "y": -133.4544377337746, + "z": 0.0 + }, + { + "x": 190.90599916367123, + "y": -133.4548869717833, + "z": 0.0 + }, + { + "x": 191.91146991533083, + "y": -133.45533620979202, + "z": 0.0 + }, + { + "x": 192.9169406669904, + "y": -133.45578544780074, + "z": 0.0 + }, + { + "x": 193.92241141865, + "y": -133.45623468580945, + "z": 0.0 + }, + { + "x": 194.92788217030966, + "y": -133.45668392381816, + "z": 0.0 + }, + { + "x": 195.93335292196917, + "y": -133.45713316182685, + "z": 0.0 + }, + { + "x": 196.93882367362878, + "y": -133.45758239983556, + "z": 0.0 + }, + { + "x": 197.94429442528838, + "y": -133.45803163784427, + "z": 0.0 + }, + { + "x": 198.94976517694795, + "y": -133.45848087585298, + "z": 0.0 + }, + { + "x": 199.95523592860755, + "y": -133.4589301138617, + "z": 0.0 + }, + { + "x": 200.96070668026715, + "y": -133.4593793518704, + "z": 0.0 + }, + { + "x": 201.96617743192672, + "y": -133.45982858987912, + "z": 0.0 + }, + { + "x": 202.97164818358638, + "y": -133.46027782788784, + "z": 0.0 + }, + { + "x": 203.97711893524593, + "y": -133.46072706589652, + "z": 0.0 + }, + { + "x": 204.9825896869055, + "y": -133.46117630390523, + "z": 0.0 + }, + { + "x": 205.9880604385651, + "y": -133.46162554191395, + "z": 0.0 + }, + { + "x": 206.9935311902247, + "y": -133.46207477992266, + "z": 0.0 + }, + { + "x": 207.99900194188427, + "y": -133.46252401793137, + "z": 0.0 + }, + { + "x": 209.00447269354387, + "y": -133.46297325594008, + "z": 0.0 + }, + { + "x": 210.00994344520353, + "y": -133.4634224939488, + "z": 0.0 + }, + { + "x": 211.01541419686305, + "y": -133.46387173195748, + "z": 0.0 + }, + { + "x": 212.02088494852265, + "y": -133.4643209699662, + "z": 0.0 + }, + { + "x": 213.02635570018225, + "y": -133.4647702079749, + "z": 0.0 + }, + { + "x": 214.03182645184182, + "y": -133.46521944598362, + "z": 0.0 + }, + { + "x": 215.03729720350142, + "y": -133.46566868399233, + "z": 0.0 + }, + { + "x": 216.04276795516103, + "y": -133.46611792200105, + "z": 0.0 + }, + { + "x": 217.0482387068206, + "y": -133.46656716000976, + "z": 0.0 + }, + { + "x": 218.05370945848026, + "y": -133.46701639801847, + "z": 0.0 + }, + { + "x": 219.0591802101398, + "y": -133.46746563602716, + "z": 0.0 + }, + { + "x": 220.06465096179937, + "y": -133.46791487403587, + "z": 0.0 + }, + { + "x": 221.07012171345897, + "y": -133.46836411204458, + "z": 0.0 + }, + { + "x": 222.07559246511858, + "y": -133.4688133500533, + "z": 0.0 + }, + { + "x": 223.08106321677815, + "y": -133.469262588062, + "z": 0.0 + }, + { + "x": 224.08653396843775, + "y": -133.46971182607072, + "z": 0.0 + }, + { + "x": 225.09200472009738, + "y": -133.47016106407943, + "z": 0.0 + }, + { + "x": 226.09747547175692, + "y": -133.47061030208812, + "z": 0.0 + }, + { + "x": 227.10294622341652, + "y": -133.47105954009683, + "z": 0.0 + }, + { + "x": 228.1084169750761, + "y": -133.47150877810554, + "z": 0.0 + }, + { + "x": 229.1138877267357, + "y": -133.47195801611426, + "z": 0.0 + }, + { + "x": 230.1193584783953, + "y": -133.47240725412297, + "z": 0.0 + }, + { + "x": 231.12482923005487, + "y": -133.47285649213168, + "z": 0.0 + }, + { + "x": 232.13029998171444, + "y": -133.4733057301404, + "z": 0.0 + }, + { + "x": 233.13577073337407, + "y": -133.4737549681491, + "z": 0.0 + }, + { + "x": 234.14124148503362, + "y": -133.4742042061578, + "z": 0.0 + }, + { + "x": 235.1467122366932, + "y": -133.4746534441665, + "z": 0.0 + }, + { + "x": 236.15218298835276, + "y": -133.47510268217522, + "z": 0.0 + }, + { + "x": 237.15765374001234, + "y": -133.47555192018393, + "z": 0.0 + }, + { + "x": 238.1631244916719, + "y": -133.47600115819264, + "z": 0.0 + }, + { + "x": 239.16859524333148, + "y": -133.47645039620136, + "z": 0.0 + }, + { + "x": 240.17406599499114, + "y": -133.47689963421007, + "z": 0.0 + }, + { + "x": 241.17953674665065, + "y": -133.47734887221876, + "z": 0.0 + }, + { + "x": 242.18500749831023, + "y": -133.47779811022747, + "z": 0.0 + }, + { + "x": 243.1904782499698, + "y": -133.47824734823618, + "z": 0.0 + }, + { + "x": 244.19594900162937, + "y": -133.4786965862449, + "z": 0.0 + }, + { + "x": 245.20141975328895, + "y": -133.4791458242536, + "z": 0.0 + }, + { + "x": 246.20689050494852, + "y": -133.47959506226232, + "z": 0.0 + }, + { + "x": 247.21236125660812, + "y": -133.48004430027103, + "z": 0.0 + }, + { + "x": 248.21783200826775, + "y": -133.48049353827975, + "z": 0.0 + }, + { + "x": 249.22330275992726, + "y": -133.48094277628843, + "z": 0.0 + }, + { + "x": 250.22877351158684, + "y": -133.48139201429714, + "z": 0.0 + }, + { + "x": 251.2342442632464, + "y": -133.48184125230586, + "z": 0.0 + }, + { + "x": 252.23971501490598, + "y": -133.48229049031457, + "z": 0.0 + }, + { + "x": 253.24518576656558, + "y": -133.48273972832328, + "z": 0.0 + }, + { + "x": 254.25065651822516, + "y": -133.483188966332, + "z": 0.0 + }, + { + "x": 255.2561272698848, + "y": -133.4836382043407, + "z": 0.0 + }, + { + "x": 256.2615980215443, + "y": -133.4840874423494, + "z": 0.0 + }, + { + "x": 257.26706877320385, + "y": -133.4845366803581, + "z": 0.0 + }, + { + "x": 258.2725395248634, + "y": -133.48498591836682, + "z": 0.0 + }, + { + "x": 259.278010276523, + "y": -133.48543515637553, + "z": 0.0 + }, + { + "x": 260.28348102818256, + "y": -133.48588439438424, + "z": 0.0 + }, + { + "x": 261.28895177984214, + "y": -133.48633363239296, + "z": 0.0 + }, + { + "x": 262.2944225315017, + "y": -133.48678287040167, + "z": 0.0 + }, + { + "x": 263.2998932831614, + "y": -133.48723210841038, + "z": 0.0 + }, + { + "x": 264.30536403482085, + "y": -133.48768134641907, + "z": 0.0 + }, + { + "x": 265.3108347864804, + "y": -133.48813058442778, + "z": 0.0 + }, + { + "x": 266.3163055381401, + "y": -133.4885798224365, + "z": 0.0 + }, + { + "x": 267.3217762897997, + "y": -133.4890290604452, + "z": 0.0 + }, + { + "x": 268.32724704145926, + "y": -133.48947829845392, + "z": 0.0 + }, + { + "x": 269.33271779311883, + "y": -133.48992753646263, + "z": 0.0 + }, + { + "x": 270.3381885447784, + "y": -133.49037677447134, + "z": 0.0 + }, + { + "x": 271.343659296438, + "y": -133.49082601248003, + "z": 0.0 + }, + { + "x": 272.34913004809755, + "y": -133.49127525048874, + "z": 0.0 + }, + { + "x": 273.3546007997571, + "y": -133.49172448849745, + "z": 0.0 + }, + { + "x": 274.3600715514167, + "y": -133.49217372650617, + "z": 0.0 + }, + { + "x": 275.36554230307627, + "y": -133.49262296451488, + "z": 0.0 + }, + { + "x": 276.37101305473584, + "y": -133.4930722025236, + "z": 0.0 + }, + { + "x": 277.3764838063954, + "y": -133.4935214405323, + "z": 0.0 + }, + { + "x": 278.3819545580551, + "y": -133.49397067854102, + "z": 0.0 + }, + { + "x": 279.38742530971456, + "y": -133.4944199165497, + "z": 0.0 + }, + { + "x": 280.39289606137413, + "y": -133.49486915455842, + "z": 0.0 + }, + { + "x": 281.3983668130337, + "y": -133.49531839256713, + "z": 0.0 + }, + { + "x": 282.4038375646933, + "y": -133.49576763057584, + "z": 0.0 + }, + { + "x": 283.40930831635285, + "y": -133.49621686858455, + "z": 0.0 + }, + { + "x": 284.4147790680124, + "y": -133.49666610659327, + "z": 0.0 + }, + { + "x": 285.4202498196721, + "y": -133.49711534460198, + "z": 0.0 + }, + { + "x": 286.42572057133157, + "y": -133.49756458261066, + "z": 0.0 + }, + { + "x": 287.43119132299114, + "y": -133.49801382061938, + "z": 0.0 + }, + { + "x": 288.4366620746507, + "y": -133.4984630586281, + "z": 0.0 + }, + { + "x": 289.4421328263103, + "y": -133.4989122966368, + "z": 0.0 + }, + { + "x": 290.44760357796986, + "y": -133.49936153464552, + "z": 0.0 + }, + { + "x": 291.45307432962954, + "y": -133.49981077265423, + "z": 0.0 + }, + { + "x": 292.4585450812891, + "y": -133.50026001066294, + "z": 0.0 + }, + { + "x": 293.4640158329487, + "y": -133.50070924867165, + "z": 0.0 + }, + { + "x": 294.46948658460826, + "y": -133.50115848668034, + "z": 0.0 + }, + { + "x": 295.47495733626783, + "y": -133.50160772468905, + "z": 0.0 + }, + { + "x": 296.4804280879274, + "y": -133.50205696269776, + "z": 0.0 + }, + { + "x": 297.485898839587, + "y": -133.50250620070648, + "z": 0.0 + }, + { + "x": 298.49136959124655, + "y": -133.5029554387152, + "z": 0.0 + }, + { + "x": 299.4968403429061, + "y": -133.5034046767239, + "z": 0.0 + }, + { + "x": 300.5023110945657, + "y": -133.50385391473262, + "z": 0.0 + }, + { + "x": 301.50778184622527, + "y": -133.5043031527413, + "z": 0.0 + }, + { + "x": 302.51325259788484, + "y": -133.50475239075, + "z": 0.0 + }, + { + "x": 303.5187233495444, + "y": -133.50520162875873, + "z": 0.0 + }, + { + "x": 304.524194101204, + "y": -133.50565086676744, + "z": 0.0 + }, + { + "x": 305.52966485286356, + "y": -133.50610010477615, + "z": 0.0 + }, + { + "x": 306.53513560452313, + "y": -133.50654934278487, + "z": 0.0 + }, + { + "x": 307.5406063561827, + "y": -133.50699858079358, + "z": 0.0 + }, + { + "x": 308.5460771078424, + "y": -133.5074478188023, + "z": 0.0 + }, + { + "x": 309.55154785950185, + "y": -133.50789705681098, + "z": 0.0 + }, + { + "x": 310.5570186111614, + "y": -133.5083462948197, + "z": 0.0 + }, + { + "x": 311.562489362821, + "y": -133.5087955328284, + "z": 0.0 + }, + { + "x": 312.56796011448057, + "y": -133.5092447708371, + "z": 0.0 + }, + { + "x": 313.57343086614014, + "y": -133.50969400884583, + "z": 0.0 + }, + { + "x": 314.5789016177997, + "y": -133.51014324685454, + "z": 0.0 + }, + { + "x": 315.5843723694594, + "y": -133.51059248486325, + "z": 0.0 + }, + { + "x": 316.58984312111886, + "y": -133.51104172287194, + "z": 0.0 + }, + { + "x": 317.59531387277855, + "y": -133.51149096088065, + "z": 0.0 + }, + { + "x": 318.6007846244381, + "y": -133.51194019888936, + "z": 0.0 + }, + { + "x": 319.6062553760977, + "y": -133.51238943689808, + "z": 0.0 + }, + { + "x": 320.61172612775727, + "y": -133.5128386749068, + "z": 0.0 + }, + { + "x": 321.61719687941684, + "y": -133.5132879129155, + "z": 0.0 + }, + { + "x": 322.6226676310764, + "y": -133.51373715092421, + "z": 0.0 + }, + { + "x": 323.628138382736, + "y": -133.51418638893293, + "z": 0.0 + }, + { + "x": 324.63360913439556, + "y": -133.5146356269416, + "z": 0.0 + }, + { + "x": 325.63907988605513, + "y": -133.51508486495032, + "z": 0.0 + } + ] + }, + { + "id": 25, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 79.64213303769317, + "y": -324.57727938928275, + "z": 0.0 + }, + { + "x": 78.6330027477079, + "y": -324.5767387981524, + "z": 0.0 + }, + { + "x": 77.62387245772217, + "y": -324.5761982070222, + "z": 0.0 + }, + { + "x": 76.61474216773736, + "y": -324.5756576158918, + "z": 0.0 + }, + { + "x": 75.60561187775163, + "y": -324.5751170247616, + "z": 0.0 + }, + { + "x": 74.59648158776682, + "y": -324.57457643363125, + "z": 0.0 + }, + { + "x": 73.58735129778131, + "y": -324.57403584250096, + "z": 0.0 + }, + { + "x": 72.57822100779582, + "y": -324.57349525137073, + "z": 0.0 + }, + { + "x": 71.569090717811, + "y": -324.5729546602404, + "z": 0.0 + }, + { + "x": 70.5599604278255, + "y": -324.5724140691101, + "z": 0.0 + }, + { + "x": 69.55083013784022, + "y": -324.5718734779798, + "z": 0.0 + }, + { + "x": 68.54169984785494, + "y": -324.5713328868495, + "z": 0.0 + }, + { + "x": 67.53256955786968, + "y": -324.57079229571923, + "z": 0.0 + }, + { + "x": 66.5234392678844, + "y": -324.57025170458894, + "z": 0.0 + }, + { + "x": 65.51430897789913, + "y": -324.56971111345865, + "z": 0.0 + }, + { + "x": 64.50517868791385, + "y": -324.56917052232836, + "z": 0.0 + }, + { + "x": 63.496048397928575, + "y": -324.5686299311981, + "z": 0.0 + }, + { + "x": 62.486918107943296, + "y": -324.5680893400678, + "z": 0.0 + }, + { + "x": 61.477787817958024, + "y": -324.5675487489375, + "z": 0.0 + }, + { + "x": 60.468657527972745, + "y": -324.5670081578072, + "z": 0.0 + }, + { + "x": 59.45952723798747, + "y": -324.5664675666769, + "z": 0.0 + }, + { + "x": 58.450396948002194, + "y": -324.56592697554663, + "z": 0.0 + }, + { + "x": 57.441266658016914, + "y": -324.56538638441634, + "z": 0.0 + }, + { + "x": 56.43213636803164, + "y": -324.56484579328605, + "z": 0.0 + }, + { + "x": 55.42300607804658, + "y": -324.5643052021557, + "z": 0.0 + }, + { + "x": 54.41387578806109, + "y": -324.5637646110254, + "z": 0.0 + }, + { + "x": 53.404745498075584, + "y": -324.5632240198952, + "z": 0.0 + }, + { + "x": 52.39561520809054, + "y": -324.5626834287649, + "z": 0.0 + }, + { + "x": 51.38648491810525, + "y": -324.5621428376346, + "z": 0.0 + }, + { + "x": 50.37735462812019, + "y": -324.56160224650426, + "z": 0.0 + }, + { + "x": 49.368224338134695, + "y": -324.561061655374, + "z": 0.0 + }, + { + "x": 48.35909404814919, + "y": -324.56052106424374, + "z": 0.0 + }, + { + "x": 47.349963758164144, + "y": -324.55998047311346, + "z": 0.0 + }, + { + "x": 46.340833468179085, + "y": -324.5594398819831, + "z": 0.0 + }, + { + "x": 45.33170317819359, + "y": -324.5588992908528, + "z": 0.0 + }, + { + "x": 44.32257288820831, + "y": -324.55835869972253, + "z": 0.0 + }, + { + "x": 43.31344259822304, + "y": -324.55781810859224, + "z": 0.0 + }, + { + "x": 42.30431230823776, + "y": -324.55727751746196, + "z": 0.0 + }, + { + "x": 41.29518201825249, + "y": -324.55673692633167, + "z": 0.0 + }, + { + "x": 40.28605172826721, + "y": -324.5561963352014, + "z": 0.0 + }, + { + "x": 39.27692143828194, + "y": -324.5556557440711, + "z": 0.0 + }, + { + "x": 38.26779114829666, + "y": -324.5551151529408, + "z": 0.0 + }, + { + "x": 37.25866085831138, + "y": -324.5545745618105, + "z": 0.0 + }, + { + "x": 36.24953056832611, + "y": -324.5540339706802, + "z": 0.0 + }, + { + "x": 35.24040027834083, + "y": -324.55349337954993, + "z": 0.0 + }, + { + "x": 34.23126998835556, + "y": -324.55295278841965, + "z": 0.0 + }, + { + "x": 33.22213969837028, + "y": -324.55241219728936, + "z": 0.0 + }, + { + "x": 32.213009408385, + "y": -324.55187160615907, + "z": 0.0 + }, + { + "x": 31.203879118399723, + "y": -324.5513310150288, + "z": 0.0 + }, + { + "x": 30.19474882841467, + "y": -324.55079042389843, + "z": 0.0 + }, + { + "x": 29.185618538428944, + "y": -324.5502498327682, + "z": 0.0 + }, + { + "x": 28.176488248443892, + "y": -324.5497092416379, + "z": 0.0 + }, + { + "x": 27.167357958458613, + "y": -324.5491686505076, + "z": 0.0 + }, + { + "x": 26.158227668473337, + "y": -324.54862805937734, + "z": 0.0 + }, + { + "x": 25.14909737848829, + "y": -324.548087468247, + "z": 0.0 + }, + { + "x": 24.139967088502562, + "y": -324.54754687711676, + "z": 0.0 + }, + { + "x": 23.130836798517738, + "y": -324.5470062859864, + "z": 0.0 + }, + { + "x": 22.121706508532235, + "y": -324.5464656948561, + "z": 0.0 + }, + { + "x": 21.11257621854673, + "y": -324.5459251037259, + "z": 0.0 + }, + { + "x": 20.103445928561907, + "y": -324.54538451259555, + "z": 0.0 + }, + { + "x": 19.094315638576404, + "y": -324.54484392146526, + "z": 0.0 + }, + { + "x": 18.08518534859113, + "y": -324.54430333033497, + "z": 0.0 + }, + { + "x": 17.076055058605853, + "y": -324.5437627392047, + "z": 0.0 + }, + { + "x": 16.06692476862035, + "y": -324.54322214807445, + "z": 0.0 + }, + { + "x": 15.057794478635525, + "y": -324.5426815569441, + "z": 0.0 + }, + { + "x": 14.048664188650024, + "y": -324.5421409658138, + "z": 0.0 + }, + { + "x": 13.039533898664747, + "y": -324.5416003746835, + "z": 0.0 + }, + { + "x": 12.030403608679471, + "y": -324.54105978355324, + "z": 0.0 + }, + { + "x": 11.021273318694194, + "y": -324.54051919242295, + "z": 0.0 + }, + { + "x": 10.012143028708918, + "y": -324.53997860129266, + "z": 0.0 + } + ] + }, + { + "id": 26, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 10.007857429054754, + "y": -332.53997745339484, + "z": 0.0 + }, + { + "x": 11.01698771904003, + "y": -332.54051804452513, + "z": 0.0 + }, + { + "x": 12.026118009025307, + "y": -332.5410586356554, + "z": 0.0 + }, + { + "x": 13.035248299010583, + "y": -332.5415992267857, + "z": 0.0 + }, + { + "x": 14.04437858899586, + "y": -332.542139817916, + "z": 0.0 + }, + { + "x": 15.05350887898091, + "y": -332.5426804090463, + "z": 0.0 + }, + { + "x": 16.062639168966637, + "y": -332.54322100017663, + "z": 0.0 + }, + { + "x": 17.071769458951692, + "y": -332.54376159130686, + "z": 0.0 + }, + { + "x": 18.080899748936968, + "y": -332.54430218243715, + "z": 0.0 + }, + { + "x": 19.090030038922244, + "y": -332.54484277356744, + "z": 0.0 + }, + { + "x": 20.099160328907292, + "y": -332.5453833646977, + "z": 0.0 + }, + { + "x": 21.10829061889302, + "y": -332.5459239558281, + "z": 0.0 + }, + { + "x": 22.117420908878074, + "y": -332.5464645469583, + "z": 0.0 + }, + { + "x": 23.126551198863122, + "y": -332.5470051380886, + "z": 0.0 + }, + { + "x": 24.13568148884885, + "y": -332.54754572921894, + "z": 0.0 + }, + { + "x": 25.144811778833674, + "y": -332.54808632034917, + "z": 0.0 + }, + { + "x": 26.153942068819177, + "y": -332.5486269114795, + "z": 0.0 + }, + { + "x": 27.163072358804452, + "y": -332.5491675026098, + "z": 0.0 + }, + { + "x": 28.17220264878973, + "y": -332.5497080937401, + "z": 0.0 + }, + { + "x": 29.18133293877523, + "y": -332.5502486848704, + "z": 0.0 + }, + { + "x": 30.190463228760056, + "y": -332.5507892760006, + "z": 0.0 + }, + { + "x": 31.199593518745562, + "y": -332.55132986713096, + "z": 0.0 + }, + { + "x": 32.20872380873083, + "y": -332.55187045826125, + "z": 0.0 + }, + { + "x": 33.21785409871611, + "y": -332.55241104939154, + "z": 0.0 + }, + { + "x": 34.22698438870139, + "y": -332.5529516405218, + "z": 0.0 + }, + { + "x": 35.23611467868666, + "y": -332.5534922316521, + "z": 0.0 + }, + { + "x": 36.24524496867194, + "y": -332.5540328227824, + "z": 0.0 + }, + { + "x": 37.25437525865721, + "y": -332.5545734139127, + "z": 0.0 + }, + { + "x": 38.26350554864249, + "y": -332.555114005043, + "z": 0.0 + }, + { + "x": 39.27263583862777, + "y": -332.55565459617327, + "z": 0.0 + }, + { + "x": 40.28176612861304, + "y": -332.55619518730356, + "z": 0.0 + }, + { + "x": 41.29089641859832, + "y": -332.55673577843385, + "z": 0.0 + }, + { + "x": 42.300026708583594, + "y": -332.55727636956414, + "z": 0.0 + }, + { + "x": 43.309156998568874, + "y": -332.5578169606944, + "z": 0.0 + }, + { + "x": 44.318287288554146, + "y": -332.5583575518247, + "z": 0.0 + }, + { + "x": 45.327417578539425, + "y": -332.558898142955, + "z": 0.0 + }, + { + "x": 46.33654786852448, + "y": -332.5594387340853, + "z": 0.0 + }, + { + "x": 47.345678158509976, + "y": -332.55997932521564, + "z": 0.0 + }, + { + "x": 48.354808448495476, + "y": -332.5605199163459, + "z": 0.0 + }, + { + "x": 49.36393873848053, + "y": -332.56106050747616, + "z": 0.0 + }, + { + "x": 50.37306902846558, + "y": -332.56160109860645, + "z": 0.0 + }, + { + "x": 51.382199318451086, + "y": -332.5621416897368, + "z": 0.0 + }, + { + "x": 52.39132960843637, + "y": -332.5626822808671, + "z": 0.0 + }, + { + "x": 53.40045989842187, + "y": -332.56322287199737, + "z": 0.0 + }, + { + "x": 54.40959018840692, + "y": -332.5637634631276, + "z": 0.0 + }, + { + "x": 55.418720478391975, + "y": -332.5643040542579, + "z": 0.0 + }, + { + "x": 56.427850768377475, + "y": -332.56484464538823, + "z": 0.0 + }, + { + "x": 57.43698105836275, + "y": -332.5653852365185, + "z": 0.0 + }, + { + "x": 58.446111348348026, + "y": -332.5659258276488, + "z": 0.0 + }, + { + "x": 59.455241638333305, + "y": -332.5664664187791, + "z": 0.0 + }, + { + "x": 60.46437192831858, + "y": -332.5670070099094, + "z": 0.0 + }, + { + "x": 61.47350221830386, + "y": -332.5675476010397, + "z": 0.0 + }, + { + "x": 62.48263250828913, + "y": -332.56808819216997, + "z": 0.0 + }, + { + "x": 63.49176279827441, + "y": -332.56862878330026, + "z": 0.0 + }, + { + "x": 64.50089308825969, + "y": -332.56916937443054, + "z": 0.0 + }, + { + "x": 65.51002337824497, + "y": -332.56970996556083, + "z": 0.0 + }, + { + "x": 66.51915366823023, + "y": -332.5702505566911, + "z": 0.0 + }, + { + "x": 67.52828395821551, + "y": -332.5707911478214, + "z": 0.0 + }, + { + "x": 68.53741424820078, + "y": -332.5713317389517, + "z": 0.0 + }, + { + "x": 69.54654453818605, + "y": -332.571872330082, + "z": 0.0 + }, + { + "x": 70.55567482817133, + "y": -332.5724129212123, + "z": 0.0 + }, + { + "x": 71.56480511815637, + "y": -332.57295351234256, + "z": 0.0 + }, + { + "x": 72.5739354081421, + "y": -332.5734941034729, + "z": 0.0 + }, + { + "x": 73.58306569812714, + "y": -332.57403469460314, + "z": 0.0 + }, + { + "x": 74.5921959881122, + "y": -332.57457528573343, + "z": 0.0 + }, + { + "x": 75.60132627809791, + "y": -332.5751158768638, + "z": 0.0 + }, + { + "x": 76.61045656808274, + "y": -332.575656467994, + "z": 0.0 + }, + { + "x": 77.61958685806846, + "y": -332.57619705912435, + "z": 0.0 + }, + { + "x": 78.62871714805328, + "y": -332.5767376502546, + "z": 0.0 + }, + { + "x": 79.63784743803855, + "y": -332.57727824138493, + "z": 0.0 + } + ] + }, + { + "id": 27, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 10.010000228881836, + "y": -328.53997802734375, + "z": 0.0 + }, + { + "x": 11.019130518867112, + "y": -328.54051861847404, + "z": 0.0 + }, + { + "x": 12.028260808852389, + "y": -328.5410592096043, + "z": 0.0 + }, + { + "x": 13.037391098837665, + "y": -328.5415998007346, + "z": 0.0 + }, + { + "x": 14.046521388822942, + "y": -328.5421403918649, + "z": 0.0 + }, + { + "x": 15.055651678808218, + "y": -328.5426809829952, + "z": 0.0 + }, + { + "x": 16.064781968793493, + "y": -328.54322157412554, + "z": 0.0 + }, + { + "x": 17.073912258778773, + "y": -328.54376216525577, + "z": 0.0 + }, + { + "x": 18.08304254876405, + "y": -328.54430275638606, + "z": 0.0 + }, + { + "x": 19.092172838749324, + "y": -328.54484334751635, + "z": 0.0 + }, + { + "x": 20.1013031287346, + "y": -328.54538393864664, + "z": 0.0 + }, + { + "x": 21.110433418719875, + "y": -328.545924529777, + "z": 0.0 + }, + { + "x": 22.119563708705154, + "y": -328.5464651209072, + "z": 0.0 + }, + { + "x": 23.12869399869043, + "y": -328.5470057120375, + "z": 0.0 + }, + { + "x": 24.137824288675706, + "y": -328.54754630316785, + "z": 0.0 + }, + { + "x": 25.14695457866098, + "y": -328.5480868942981, + "z": 0.0 + }, + { + "x": 26.156084868646257, + "y": -328.5486274854284, + "z": 0.0 + }, + { + "x": 27.165215158631533, + "y": -328.5491680765587, + "z": 0.0 + }, + { + "x": 28.174345448616812, + "y": -328.549708667689, + "z": 0.0 + }, + { + "x": 29.183475738602088, + "y": -328.5502492588193, + "z": 0.0 + }, + { + "x": 30.192606028587363, + "y": -328.5507898499495, + "z": 0.0 + }, + { + "x": 31.201736318572642, + "y": -328.55133044107987, + "z": 0.0 + }, + { + "x": 32.210866608557915, + "y": -328.55187103221016, + "z": 0.0 + }, + { + "x": 33.219996898543194, + "y": -328.55241162334045, + "z": 0.0 + }, + { + "x": 34.22912718852847, + "y": -328.55295221447074, + "z": 0.0 + }, + { + "x": 35.238257478513745, + "y": -328.553492805601, + "z": 0.0 + }, + { + "x": 36.247387768499024, + "y": -328.5540333967313, + "z": 0.0 + }, + { + "x": 37.256518058484296, + "y": -328.5545739878616, + "z": 0.0 + }, + { + "x": 38.265648348469576, + "y": -328.5551145789919, + "z": 0.0 + }, + { + "x": 39.274778638454855, + "y": -328.5556551701222, + "z": 0.0 + }, + { + "x": 40.28390892844013, + "y": -328.55619576125247, + "z": 0.0 + }, + { + "x": 41.293039218425406, + "y": -328.55673635238276, + "z": 0.0 + }, + { + "x": 42.30216950841068, + "y": -328.55727694351305, + "z": 0.0 + }, + { + "x": 43.31129979839596, + "y": -328.55781753464333, + "z": 0.0 + }, + { + "x": 44.32043008838123, + "y": -328.5583581257736, + "z": 0.0 + }, + { + "x": 45.32956037836651, + "y": -328.5588987169039, + "z": 0.0 + }, + { + "x": 46.33869066835178, + "y": -328.5594393080342, + "z": 0.0 + }, + { + "x": 47.34782095833706, + "y": -328.55997989916455, + "z": 0.0 + }, + { + "x": 48.35695124832233, + "y": -328.56052049029483, + "z": 0.0 + }, + { + "x": 49.36608153830761, + "y": -328.56106108142507, + "z": 0.0 + }, + { + "x": 50.37521182829288, + "y": -328.56160167255535, + "z": 0.0 + }, + { + "x": 51.38434211827817, + "y": -328.5621422636857, + "z": 0.0 + }, + { + "x": 52.393472408263456, + "y": -328.562682854816, + "z": 0.0 + }, + { + "x": 53.40260269824873, + "y": -328.5632234459463, + "z": 0.0 + }, + { + "x": 54.41173298823401, + "y": -328.5637640370765, + "z": 0.0 + }, + { + "x": 55.42086327821928, + "y": -328.5643046282068, + "z": 0.0 + }, + { + "x": 56.42999356820456, + "y": -328.56484521933714, + "z": 0.0 + }, + { + "x": 57.43912385818983, + "y": -328.56538581046743, + "z": 0.0 + }, + { + "x": 58.44825414817511, + "y": -328.5659264015977, + "z": 0.0 + }, + { + "x": 59.45738443816039, + "y": -328.566466992728, + "z": 0.0 + }, + { + "x": 60.46651472814566, + "y": -328.5670075838583, + "z": 0.0 + }, + { + "x": 61.47564501813094, + "y": -328.5675481749886, + "z": 0.0 + }, + { + "x": 62.48477530811621, + "y": -328.5680887661189, + "z": 0.0 + }, + { + "x": 63.49390559810149, + "y": -328.56862935724916, + "z": 0.0 + }, + { + "x": 64.50303588808677, + "y": -328.56916994837945, + "z": 0.0 + }, + { + "x": 65.51216617807205, + "y": -328.56971053950974, + "z": 0.0 + }, + { + "x": 66.52129646805732, + "y": -328.57025113064003, + "z": 0.0 + }, + { + "x": 67.5304267580426, + "y": -328.5707917217703, + "z": 0.0 + }, + { + "x": 68.53955704802786, + "y": -328.5713323129006, + "z": 0.0 + }, + { + "x": 69.54868733801314, + "y": -328.5718729040309, + "z": 0.0 + }, + { + "x": 70.55781762799842, + "y": -328.5724134951612, + "z": 0.0 + }, + { + "x": 71.56694791798368, + "y": -328.5729540862915, + "z": 0.0 + }, + { + "x": 72.57607820796896, + "y": -328.5734946774218, + "z": 0.0 + }, + { + "x": 73.58520849795423, + "y": -328.57403526855205, + "z": 0.0 + }, + { + "x": 74.5943387879395, + "y": -328.57457585968234, + "z": 0.0 + }, + { + "x": 75.60346907792477, + "y": -328.5751164508127, + "z": 0.0 + }, + { + "x": 76.61259936791005, + "y": -328.5756570419429, + "z": 0.0 + }, + { + "x": 77.62172965789532, + "y": -328.57619763307326, + "z": 0.0 + }, + { + "x": 78.6308599478806, + "y": -328.5767382242035, + "z": 0.0 + }, + { + "x": 79.63999023786586, + "y": -328.57727881533384, + "z": 0.0 + } + ] + }, + { + "id": 28, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 79.64106163777952, + "y": -326.5772791023083, + "z": 0.0 + }, + { + "x": 78.63193134779425, + "y": -326.57673851117795, + "z": 0.0 + }, + { + "x": 77.62280105780874, + "y": -326.5761979200477, + "z": 0.0 + }, + { + "x": 76.6136707678237, + "y": -326.5756573289174, + "z": 0.0 + }, + { + "x": 75.6045404778382, + "y": -326.57511673778714, + "z": 0.0 + }, + { + "x": 74.59541018785316, + "y": -326.5745761466568, + "z": 0.0 + }, + { + "x": 73.58627989786777, + "y": -326.5740355555265, + "z": 0.0 + }, + { + "x": 72.57714960788239, + "y": -326.5734949643963, + "z": 0.0 + }, + { + "x": 71.56801931789734, + "y": -326.57295437326593, + "z": 0.0 + }, + { + "x": 70.55888902791196, + "y": -326.57241378213564, + "z": 0.0 + }, + { + "x": 69.54975873792668, + "y": -326.57187319100535, + "z": 0.0 + }, + { + "x": 68.5406284479414, + "y": -326.57133259987506, + "z": 0.0 + }, + { + "x": 67.53149815795614, + "y": -326.5707920087448, + "z": 0.0 + }, + { + "x": 66.52236786797086, + "y": -326.5702514176145, + "z": 0.0 + }, + { + "x": 65.51323757798559, + "y": -326.5697108264842, + "z": 0.0 + }, + { + "x": 64.50410728800031, + "y": -326.5691702353539, + "z": 0.0 + }, + { + "x": 63.494976998015034, + "y": -326.5686296442236, + "z": 0.0 + }, + { + "x": 62.485846708029754, + "y": -326.56808905309333, + "z": 0.0 + }, + { + "x": 61.47671641804448, + "y": -326.56754846196304, + "z": 0.0 + }, + { + "x": 60.4675861280592, + "y": -326.56700787083275, + "z": 0.0 + }, + { + "x": 59.45845583807393, + "y": -326.56646727970247, + "z": 0.0 + }, + { + "x": 58.44932554808865, + "y": -326.5659266885722, + "z": 0.0 + }, + { + "x": 57.44019525810337, + "y": -326.5653860974419, + "z": 0.0 + }, + { + "x": 56.4310649681181, + "y": -326.5648455063116, + "z": 0.0 + }, + { + "x": 55.42193467813293, + "y": -326.56430491518125, + "z": 0.0 + }, + { + "x": 54.41280438814755, + "y": -326.56376432405096, + "z": 0.0 + }, + { + "x": 53.403674098162156, + "y": -326.56322373292073, + "z": 0.0 + }, + { + "x": 52.394543808177, + "y": -326.56268314179044, + "z": 0.0 + }, + { + "x": 51.38541351819171, + "y": -326.56214255066016, + "z": 0.0 + }, + { + "x": 50.37628322820653, + "y": -326.5616019595298, + "z": 0.0 + }, + { + "x": 49.36715293822115, + "y": -326.5610613683995, + "z": 0.0 + }, + { + "x": 48.35802264823576, + "y": -326.5605207772693, + "z": 0.0 + }, + { + "x": 47.3488923582506, + "y": -326.559980186139, + "z": 0.0 + }, + { + "x": 46.33976206826543, + "y": -326.55943959500866, + "z": 0.0 + }, + { + "x": 45.33063177828005, + "y": -326.55889900387837, + "z": 0.0 + }, + { + "x": 44.32150148829477, + "y": -326.5583584127481, + "z": 0.0 + }, + { + "x": 43.3123711983095, + "y": -326.5578178216178, + "z": 0.0 + }, + { + "x": 42.30324090832422, + "y": -326.5572772304875, + "z": 0.0 + }, + { + "x": 41.29411061833895, + "y": -326.5567366393572, + "z": 0.0 + }, + { + "x": 40.28498032835367, + "y": -326.5561960482269, + "z": 0.0 + }, + { + "x": 39.2758500383684, + "y": -326.55565545709663, + "z": 0.0 + }, + { + "x": 38.26671974838312, + "y": -326.55511486596635, + "z": 0.0 + }, + { + "x": 37.25758945839784, + "y": -326.55457427483606, + "z": 0.0 + }, + { + "x": 36.248459168412566, + "y": -326.55403368370577, + "z": 0.0 + }, + { + "x": 35.23932887842729, + "y": -326.5534930925755, + "z": 0.0 + }, + { + "x": 34.230198588442015, + "y": -326.5529525014452, + "z": 0.0 + }, + { + "x": 33.221068298456736, + "y": -326.5524119103149, + "z": 0.0 + }, + { + "x": 32.211938008471456, + "y": -326.5518713191846, + "z": 0.0 + }, + { + "x": 31.202807718486184, + "y": -326.5513307280543, + "z": 0.0 + }, + { + "x": 30.19367742850102, + "y": -326.550790136924, + "z": 0.0 + }, + { + "x": 29.184547138515516, + "y": -326.55024954579375, + "z": 0.0 + }, + { + "x": 28.175416848530354, + "y": -326.54970895466346, + "z": 0.0 + }, + { + "x": 27.166286558545075, + "y": -326.54916836353317, + "z": 0.0 + }, + { + "x": 26.157156268559795, + "y": -326.5486277724029, + "z": 0.0 + }, + { + "x": 25.148025978574637, + "y": -326.54808718127254, + "z": 0.0 + }, + { + "x": 24.138895688589134, + "y": -326.5475465901423, + "z": 0.0 + }, + { + "x": 23.129765398604086, + "y": -326.54700599901196, + "z": 0.0 + }, + { + "x": 22.120635108618693, + "y": -326.54646540788167, + "z": 0.0 + }, + { + "x": 21.111504818633303, + "y": -326.54592481675144, + "z": 0.0 + }, + { + "x": 20.102374528648255, + "y": -326.5453842256211, + "z": 0.0 + }, + { + "x": 19.093244238662862, + "y": -326.5448436344908, + "z": 0.0 + }, + { + "x": 18.08411394867759, + "y": -326.5443030433605, + "z": 0.0 + }, + { + "x": 17.07498365869231, + "y": -326.5437624522302, + "z": 0.0 + }, + { + "x": 16.06585336870692, + "y": -326.5432218611, + "z": 0.0 + }, + { + "x": 15.056723078721872, + "y": -326.54268126996965, + "z": 0.0 + }, + { + "x": 14.047592788736484, + "y": -326.54214067883936, + "z": 0.0 + }, + { + "x": 13.038462498751205, + "y": -326.54160008770907, + "z": 0.0 + }, + { + "x": 12.02933220876593, + "y": -326.5410594965788, + "z": 0.0 + }, + { + "x": 11.020201918780653, + "y": -326.5405189054485, + "z": 0.0 + }, + { + "x": 10.011071628795378, + "y": -326.5399783143182, + "z": 0.0 + } + ] + }, + { + "id": 29, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 10.008928828968294, + "y": -330.5399777403693, + "z": 0.0 + }, + { + "x": 11.01805911895357, + "y": -330.5405183314996, + "z": 0.0 + }, + { + "x": 12.027189408938849, + "y": -330.5410589226299, + "z": 0.0 + }, + { + "x": 13.036319698924125, + "y": -330.54159951376016, + "z": 0.0 + }, + { + "x": 14.0454499889094, + "y": -330.54214010489045, + "z": 0.0 + }, + { + "x": 15.054580278894564, + "y": -330.54268069602074, + "z": 0.0 + }, + { + "x": 16.063710568880065, + "y": -330.5432212871511, + "z": 0.0 + }, + { + "x": 17.072840858865234, + "y": -330.5437618782813, + "z": 0.0 + }, + { + "x": 18.081971148850506, + "y": -330.5443024694116, + "z": 0.0 + }, + { + "x": 19.091101438835786, + "y": -330.5448430605419, + "z": 0.0 + }, + { + "x": 20.100231728820944, + "y": -330.5453836516722, + "z": 0.0 + }, + { + "x": 21.109362018806447, + "y": -330.5459242428025, + "z": 0.0 + }, + { + "x": 22.118492308791616, + "y": -330.54646483393276, + "z": 0.0 + }, + { + "x": 23.127622598776775, + "y": -330.54700542506305, + "z": 0.0 + }, + { + "x": 24.136752888762278, + "y": -330.5475460161934, + "z": 0.0 + }, + { + "x": 25.145883178747326, + "y": -330.5480866073236, + "z": 0.0 + }, + { + "x": 26.15501346873272, + "y": -330.54862719845397, + "z": 0.0 + }, + { + "x": 27.16414375871799, + "y": -330.54916778958426, + "z": 0.0 + }, + { + "x": 28.17327404870327, + "y": -330.54970838071455, + "z": 0.0 + }, + { + "x": 29.18240433868866, + "y": -330.55024897184484, + "z": 0.0 + }, + { + "x": 30.191534628673708, + "y": -330.55078956297507, + "z": 0.0 + }, + { + "x": 31.2006649186591, + "y": -330.5513301541054, + "z": 0.0 + }, + { + "x": 32.20979520864437, + "y": -330.5518707452357, + "z": 0.0 + }, + { + "x": 33.21892549862965, + "y": -330.552411336366, + "z": 0.0 + }, + { + "x": 34.22805578861493, + "y": -330.5529519274963, + "z": 0.0 + }, + { + "x": 35.2371860786002, + "y": -330.55349251862657, + "z": 0.0 + }, + { + "x": 36.24631636858548, + "y": -330.55403310975686, + "z": 0.0 + }, + { + "x": 37.255446658570754, + "y": -330.55457370088715, + "z": 0.0 + }, + { + "x": 38.264576948556034, + "y": -330.55511429201744, + "z": 0.0 + }, + { + "x": 39.27370723854131, + "y": -330.5556548831477, + "z": 0.0 + }, + { + "x": 40.282837528526585, + "y": -330.556195474278, + "z": 0.0 + }, + { + "x": 41.291967818511864, + "y": -330.5567360654083, + "z": 0.0 + }, + { + "x": 42.301098108497136, + "y": -330.5572766565386, + "z": 0.0 + }, + { + "x": 43.310228398482415, + "y": -330.5578172476689, + "z": 0.0 + }, + { + "x": 44.31935868846769, + "y": -330.55835783879917, + "z": 0.0 + }, + { + "x": 45.32848897845297, + "y": -330.55889842992946, + "z": 0.0 + }, + { + "x": 46.33761926843813, + "y": -330.55943902105975, + "z": 0.0 + }, + { + "x": 47.34674955842352, + "y": -330.5599796121901, + "z": 0.0 + }, + { + "x": 48.355879848408904, + "y": -330.5605202033204, + "z": 0.0 + }, + { + "x": 49.36501013839407, + "y": -330.5610607944506, + "z": 0.0 + }, + { + "x": 50.374140428379235, + "y": -330.5616013855809, + "z": 0.0 + }, + { + "x": 51.38327071836463, + "y": -330.56214197671125, + "z": 0.0 + }, + { + "x": 52.392401008349914, + "y": -330.56268256784153, + "z": 0.0 + }, + { + "x": 53.4015312983353, + "y": -330.5632231589718, + "z": 0.0 + }, + { + "x": 54.410661588320465, + "y": -330.56376375010205, + "z": 0.0 + }, + { + "x": 55.41979187830563, + "y": -330.56430434123234, + "z": 0.0 + }, + { + "x": 56.42892216829102, + "y": -330.5648449323627, + "z": 0.0 + }, + { + "x": 57.43805245827629, + "y": -330.565385523493, + "z": 0.0 + }, + { + "x": 58.44718274826157, + "y": -330.56592611462327, + "z": 0.0 + }, + { + "x": 59.45631303824685, + "y": -330.56646670575356, + "z": 0.0 + }, + { + "x": 60.46544332823212, + "y": -330.56700729688384, + "z": 0.0 + }, + { + "x": 61.4745736182174, + "y": -330.56754788801413, + "z": 0.0 + }, + { + "x": 62.48370390820267, + "y": -330.5680884791444, + "z": 0.0 + }, + { + "x": 63.49283419818795, + "y": -330.5686290702747, + "z": 0.0 + }, + { + "x": 64.50196448817323, + "y": -330.569169661405, + "z": 0.0 + }, + { + "x": 65.51109477815851, + "y": -330.5697102525353, + "z": 0.0 + }, + { + "x": 66.52022506814377, + "y": -330.5702508436656, + "z": 0.0 + }, + { + "x": 67.52935535812905, + "y": -330.57079143479586, + "z": 0.0 + }, + { + "x": 68.53848564811432, + "y": -330.57133202592615, + "z": 0.0 + }, + { + "x": 69.5476159380996, + "y": -330.57187261705644, + "z": 0.0 + }, + { + "x": 70.55674622808488, + "y": -330.57241320818673, + "z": 0.0 + }, + { + "x": 71.56587651807003, + "y": -330.572953799317, + "z": 0.0 + }, + { + "x": 72.57500680805553, + "y": -330.57349439044737, + "z": 0.0 + }, + { + "x": 73.58413709804069, + "y": -330.5740349815776, + "z": 0.0 + }, + { + "x": 74.59326738802585, + "y": -330.5745755727079, + "z": 0.0 + }, + { + "x": 75.60239767801134, + "y": -330.57511616383823, + "z": 0.0 + }, + { + "x": 76.6115279679964, + "y": -330.57565675496846, + "z": 0.0 + }, + { + "x": 77.62065825798189, + "y": -330.5761973460988, + "z": 0.0 + }, + { + "x": 78.62978854796694, + "y": -330.57673793722904, + "z": 0.0 + }, + { + "x": 79.6389188379522, + "y": -330.5772785283594, + "z": 0.0 + } + ] + }, + { + "id": 30, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.71998576749917, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 324.7150530320806, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 323.7101202966621, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 322.70518756124363, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 321.7002548258251, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 320.69532209040653, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 319.69038935498804, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 318.6854566195695, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 317.68052388415094, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 316.67559114873245, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 315.67065841331396, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 314.6657256778954, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 313.66079294247686, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 312.65586020705837, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 311.6509274716398, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 310.64599473622127, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 309.6410620008028, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 308.6361292653843, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 307.63119652996573, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 306.6262637945472, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 305.6213310591287, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 304.61639832371014, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 303.6114655882916, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 302.6065328528731, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 301.6016001174546, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 300.59666738203606, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 299.5917346466175, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 298.586801911199, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 297.58186917578047, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 296.5769364403619, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 295.5720037049434, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 294.56707096952493, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 293.5621382341064, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 292.55720549868784, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 291.55227276326934, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 290.54734839213904, + "y": -324.6099832501986, + "z": 0.0 + }, + { + "x": 289.54283147550484, + "y": -324.6098767052554, + "z": 0.0 + }, + { + "x": 288.53790172978063, + "y": -324.60976938847693, + "z": 0.0 + }, + { + "x": 287.5329690000928, + "y": -324.6096620713808, + "z": 0.0 + }, + { + "x": 286.528036270404, + "y": -324.6095547542848, + "z": 0.0 + }, + { + "x": 285.52310354071574, + "y": -324.60944743718875, + "z": 0.0 + }, + { + "x": 284.5181708110277, + "y": -324.60934012009267, + "z": 0.0 + }, + { + "x": 283.51323808133935, + "y": -324.6092328029966, + "z": 0.0 + }, + { + "x": 282.5083053516508, + "y": -324.60912548590056, + "z": 0.0 + }, + { + "x": 281.50337262196274, + "y": -324.6090181688045, + "z": 0.0 + }, + { + "x": 280.49843989227446, + "y": -324.6089108517084, + "z": 0.0 + }, + { + "x": 279.4935071625859, + "y": -324.6088035346124, + "z": 0.0 + }, + { + "x": 278.48857443289785, + "y": -324.6086962175163, + "z": 0.0 + }, + { + "x": 277.48364170320957, + "y": -324.6085889004202, + "z": 0.0 + }, + { + "x": 276.478708973521, + "y": -324.6084815833242, + "z": 0.0 + }, + { + "x": 275.4737762438329, + "y": -324.6083742662281, + "z": 0.0 + }, + { + "x": 274.4688435141444, + "y": -324.6082669491321, + "z": 0.0 + }, + { + "x": 273.46391078445635, + "y": -324.608159632036, + "z": 0.0 + }, + { + "x": 272.4589780547678, + "y": -324.60805231494, + "z": 0.0 + }, + { + "x": 271.45404532507973, + "y": -324.6079449978439, + "z": 0.0 + }, + { + "x": 270.44911259539145, + "y": -324.6078376807478, + "z": 0.0 + }, + { + "x": 269.4441798657029, + "y": -324.6077303636518, + "z": 0.0 + }, + { + "x": 268.4392471360148, + "y": -324.6076230465557, + "z": 0.0 + }, + { + "x": 267.4343144063265, + "y": -324.60751572945964, + "z": 0.0 + }, + { + "x": 266.429381676638, + "y": -324.6074084123636, + "z": 0.0 + }, + { + "x": 265.4244489469499, + "y": -324.60730109526753, + "z": 0.0 + }, + { + "x": 264.4195162172616, + "y": -324.60719377817145, + "z": 0.0 + }, + { + "x": 263.4145834875731, + "y": -324.6070864610754, + "z": 0.0 + }, + { + "x": 262.409650757885, + "y": -324.60697914397934, + "z": 0.0 + }, + { + "x": 261.40471802819644, + "y": -324.6068718268833, + "z": 0.0 + }, + { + "x": 260.3997852985084, + "y": -324.60676450978724, + "z": 0.0 + }, + { + "x": 259.3948525688201, + "y": -324.60665719269116, + "z": 0.0 + }, + { + "x": 258.38991983913155, + "y": -324.60654987559514, + "z": 0.0 + }, + { + "x": 257.3849871094435, + "y": -324.60644255849905, + "z": 0.0 + }, + { + "x": 256.380054379755, + "y": -324.60633524140303, + "z": 0.0 + }, + { + "x": 255.37512165006692, + "y": -324.60622792430695, + "z": 0.0 + }, + { + "x": 254.37018892037858, + "y": -324.60612060721087, + "z": 0.0 + }, + { + "x": 253.36525619069005, + "y": -324.60601329011484, + "z": 0.0 + }, + { + "x": 252.360323461002, + "y": -324.60590597301876, + "z": 0.0 + }, + { + "x": 251.35539073131346, + "y": -324.60579865592274, + "z": 0.0 + }, + { + "x": 250.35045800162538, + "y": -324.60569133882666, + "z": 0.0 + }, + { + "x": 249.3455252719371, + "y": -324.6055840217306, + "z": 0.0 + }, + { + "x": 248.34059254224857, + "y": -324.60547670463455, + "z": 0.0 + }, + { + "x": 247.33565981256046, + "y": -324.6053693875385, + "z": 0.0 + }, + { + "x": 246.3307270828722, + "y": -324.6052620704424, + "z": 0.0 + }, + { + "x": 245.32579435318365, + "y": -324.60515475334637, + "z": 0.0 + }, + { + "x": 244.32086162349557, + "y": -324.6050474362503, + "z": 0.0 + }, + { + "x": 243.31592889380727, + "y": -324.6049401191542, + "z": 0.0 + }, + { + "x": 242.31099616411876, + "y": -324.6048328020582, + "z": 0.0 + }, + { + "x": 241.30606343443046, + "y": -324.60472548496216, + "z": 0.0 + }, + { + "x": 240.30113070474235, + "y": -324.6046181678661, + "z": 0.0 + }, + { + "x": 239.29619797505407, + "y": -324.60451085077, + "z": 0.0 + }, + { + "x": 238.29126524536554, + "y": -324.604403533674, + "z": 0.0 + }, + { + "x": 237.28633251567746, + "y": -324.6042962165779, + "z": 0.0 + }, + { + "x": 236.28139978598918, + "y": -324.6041888994818, + "z": 0.0 + }, + { + "x": 235.27646705630065, + "y": -324.6040815823858, + "z": 0.0 + }, + { + "x": 234.27153432661257, + "y": -324.6039742652897, + "z": 0.0 + }, + { + "x": 233.26660159692423, + "y": -324.6038669481936, + "z": 0.0 + }, + { + "x": 232.26166886723573, + "y": -324.6037596310976, + "z": 0.0 + }, + { + "x": 231.25673613754742, + "y": -324.6036523140016, + "z": 0.0 + }, + { + "x": 230.25180340785957, + "y": -324.60354499690544, + "z": 0.0 + }, + { + "x": 229.24687067817084, + "y": -324.6034376798094, + "z": 0.0 + }, + { + "x": 228.2419379484825, + "y": -324.6033303627134, + "z": 0.0 + }, + { + "x": 227.23700521879465, + "y": -324.60322304561726, + "z": 0.0 + }, + { + "x": 226.2320724891059, + "y": -324.60311572852123, + "z": 0.0 + }, + { + "x": 225.22713975941755, + "y": -324.6030084114252, + "z": 0.0 + }, + { + "x": 224.22220702972947, + "y": -324.6029010943291, + "z": 0.0 + }, + { + "x": 223.21727430004114, + "y": -324.60279377723305, + "z": 0.0 + }, + { + "x": 222.2123415703526, + "y": -324.602686460137, + "z": 0.0 + }, + { + "x": 221.20740884066453, + "y": -324.60257914304094, + "z": 0.0 + }, + { + "x": 220.2024761109762, + "y": -324.60247182594486, + "z": 0.0 + }, + { + "x": 219.19754338128766, + "y": -324.60236450884884, + "z": 0.0 + }, + { + "x": 218.19261065159935, + "y": -324.6022571917528, + "z": 0.0 + }, + { + "x": 217.18767792191147, + "y": -324.6021498746567, + "z": 0.0 + }, + { + "x": 216.1827451922227, + "y": -324.60204255756065, + "z": 0.0 + }, + { + "x": 215.1778124625344, + "y": -324.6019352404646, + "z": 0.0 + }, + { + "x": 214.17287973284655, + "y": -324.6018279233685, + "z": 0.0 + }, + { + "x": 213.16794700315776, + "y": -324.60172060627247, + "z": 0.0 + }, + { + "x": 212.16301427346946, + "y": -324.60161328917644, + "z": 0.0 + }, + { + "x": 211.15808154378138, + "y": -324.60150597208036, + "z": 0.0 + }, + { + "x": 210.15314881409304, + "y": -324.6013986549843, + "z": 0.0 + }, + { + "x": 209.1482160844045, + "y": -324.60129133788826, + "z": 0.0 + }, + { + "x": 208.1432833547162, + "y": -324.60118402079223, + "z": 0.0 + }, + { + "x": 207.13835062502832, + "y": -324.6010767036961, + "z": 0.0 + }, + { + "x": 206.13341789533956, + "y": -324.60096938660007, + "z": 0.0 + }, + { + "x": 205.12848516565123, + "y": -324.60086206950405, + "z": 0.0 + }, + { + "x": 204.12355243596338, + "y": -324.6007547524079, + "z": 0.0 + }, + { + "x": 203.11861970627461, + "y": -324.6006474353119, + "z": 0.0 + }, + { + "x": 202.11368697658628, + "y": -324.60054011821586, + "z": 0.0 + }, + { + "x": 201.1087542468982, + "y": -324.6004328011198, + "z": 0.0 + }, + { + "x": 200.1038215172099, + "y": -324.6003254840237, + "z": 0.0 + }, + { + "x": 199.09888878752133, + "y": -324.6002181669277, + "z": 0.0 + }, + { + "x": 198.09395605783325, + "y": -324.6001108498316, + "z": 0.0 + }, + { + "x": 197.08902332814495, + "y": -324.6000035327355, + "z": 0.0 + }, + { + "x": 196.08409059845638, + "y": -324.5998962156395, + "z": 0.0 + }, + { + "x": 195.0791578687683, + "y": -324.5997888985434, + "z": 0.0 + }, + { + "x": 194.07422513908, + "y": -324.5996815814473, + "z": 0.0 + }, + { + "x": 193.06929240939147, + "y": -324.5995742643513, + "z": 0.0 + }, + { + "x": 192.06435967970313, + "y": -324.5994669472553, + "z": 0.0 + }, + { + "x": 191.05942695001505, + "y": -324.5993596301592, + "z": 0.0 + }, + { + "x": 190.05449422032672, + "y": -324.5992523130631, + "z": 0.0 + }, + { + "x": 189.04956149063818, + "y": -324.5991449959671, + "z": 0.0 + }, + { + "x": 188.0446287609501, + "y": -324.599037678871, + "z": 0.0 + }, + { + "x": 187.03969603126177, + "y": -324.59893036177493, + "z": 0.0 + }, + { + "x": 186.03476330157324, + "y": -324.5988230446789, + "z": 0.0 + }, + { + "x": 185.02983057188513, + "y": -324.5987157275828, + "z": 0.0 + }, + { + "x": 184.02489784219682, + "y": -324.59860841048675, + "z": 0.0 + }, + { + "x": 183.0199651125083, + "y": -324.5985010933907, + "z": 0.0 + }, + { + "x": 182.01503238282018, + "y": -324.59839377629464, + "z": 0.0 + }, + { + "x": 181.01009965313187, + "y": -324.59828645919856, + "z": 0.0 + }, + { + "x": 180.00516692344334, + "y": -324.59817914210254, + "z": 0.0 + }, + { + "x": 179.00023419375526, + "y": -324.59807182500646, + "z": 0.0 + }, + { + "x": 177.99530146406693, + "y": -324.5979645079104, + "z": 0.0 + }, + { + "x": 176.9903687343784, + "y": -324.59785719081435, + "z": 0.0 + }, + { + "x": 175.9854360046901, + "y": -324.5977498737183, + "z": 0.0 + }, + { + "x": 174.98050327500198, + "y": -324.59764255662225, + "z": 0.0 + }, + { + "x": 173.97557054531367, + "y": -324.59753523952617, + "z": 0.0 + }, + { + "x": 172.97063781562514, + "y": -324.59742792243014, + "z": 0.0 + }, + { + "x": 171.96570508593703, + "y": -324.59732060533406, + "z": 0.0 + }, + { + "x": 170.96077235624873, + "y": -324.597213288238, + "z": 0.0 + }, + { + "x": 169.95583962656016, + "y": -324.59710597114196, + "z": 0.0 + }, + { + "x": 168.95090689687208, + "y": -324.5969986540459, + "z": 0.0 + }, + { + "x": 167.94597416718378, + "y": -324.5968913369498, + "z": 0.0 + }, + { + "x": 166.94104143749522, + "y": -324.59678401985377, + "z": 0.0 + }, + { + "x": 165.93610870780714, + "y": -324.5966767027577, + "z": 0.0 + }, + { + "x": 164.9311759781186, + "y": -324.59656938566167, + "z": 0.0 + }, + { + "x": 163.92624324843052, + "y": -324.5964620685656, + "z": 0.0 + }, + { + "x": 162.92131051874222, + "y": -324.5963547514695, + "z": 0.0 + }, + { + "x": 161.91637778905368, + "y": -324.5962474343735, + "z": 0.0 + }, + { + "x": 160.9114450593656, + "y": -324.5961401172774, + "z": 0.0 + }, + { + "x": 159.90651232967707, + "y": -324.5960328001814, + "z": 0.0 + }, + { + "x": 158.901579599989, + "y": -324.5959254830853, + "z": 0.0 + }, + { + "x": 157.89664687030069, + "y": -324.5958181659892, + "z": 0.0 + }, + { + "x": 156.89171414061212, + "y": -324.5957108488932, + "z": 0.0 + }, + { + "x": 155.88678141092404, + "y": -324.5956035317971, + "z": 0.0 + }, + { + "x": 154.8818486812355, + "y": -324.5954962147011, + "z": 0.0 + }, + { + "x": 153.87691595154743, + "y": -324.595388897605, + "z": 0.0 + }, + { + "x": 152.87198322185913, + "y": -324.5952815805089, + "z": 0.0 + }, + { + "x": 151.8670504921706, + "y": -324.5951742634129, + "z": 0.0 + }, + { + "x": 150.8621177624825, + "y": -324.5950669463168, + "z": 0.0 + }, + { + "x": 149.8571850327942, + "y": -324.59495962922074, + "z": 0.0 + }, + { + "x": 148.85225230310567, + "y": -324.5948523121247, + "z": 0.0 + }, + { + "x": 147.8473195734176, + "y": -324.59474499502863, + "z": 0.0 + }, + { + "x": 146.84238684372926, + "y": -324.59463767793255, + "z": 0.0 + }, + { + "x": 145.83745411404072, + "y": -324.59453036083653, + "z": 0.0 + }, + { + "x": 144.83252138435242, + "y": -324.5944230437405, + "z": 0.0 + }, + { + "x": 143.82758865466434, + "y": -324.5943157266444, + "z": 0.0 + }, + { + "x": 142.82265592497603, + "y": -324.59420840954834, + "z": 0.0 + }, + { + "x": 141.8177231952875, + "y": -324.5941010924523, + "z": 0.0 + }, + { + "x": 140.81279046559942, + "y": -324.59399377535624, + "z": 0.0 + }, + { + "x": 139.8078577359111, + "y": -324.59388645826016, + "z": 0.0 + }, + { + "x": 138.80292500622258, + "y": -324.59377914116413, + "z": 0.0 + }, + { + "x": 137.7979922765345, + "y": -324.59367182406805, + "z": 0.0 + }, + { + "x": 136.7930595468462, + "y": -324.593564506972, + "z": 0.0 + }, + { + "x": 135.78812681715763, + "y": -324.59345718987595, + "z": 0.0 + }, + { + "x": 134.78319408746958, + "y": -324.59334987277987, + "z": 0.0 + }, + { + "x": 133.77826135778125, + "y": -324.5932425556838, + "z": 0.0 + }, + { + "x": 132.7733286280927, + "y": -324.59313523858776, + "z": 0.0 + }, + { + "x": 131.7683958984044, + "y": -324.59302792149174, + "z": 0.0 + }, + { + "x": 130.76346316871656, + "y": -324.5929206043956, + "z": 0.0 + }, + { + "x": 129.7585304390278, + "y": -324.5928132872996, + "z": 0.0 + }, + { + "x": 128.75359770933946, + "y": -324.59270597020355, + "z": 0.0 + }, + { + "x": 127.74866497965138, + "y": -324.5925986531075, + "z": 0.0 + }, + { + "x": 126.74373224996307, + "y": -324.5924913360114, + "z": 0.0 + }, + { + "x": 125.73879952027453, + "y": -324.59238401891537, + "z": 0.0 + }, + { + "x": 124.73386679058645, + "y": -324.5922767018193, + "z": 0.0 + }, + { + "x": 123.72893406089814, + "y": -324.5921693847232, + "z": 0.0 + }, + { + "x": 122.72400133120959, + "y": -324.5920620676272, + "z": 0.0 + }, + { + "x": 121.71906860152127, + "y": -324.59195475053116, + "z": 0.0 + }, + { + "x": 120.71413587183342, + "y": -324.591847433435, + "z": 0.0 + }, + { + "x": 119.70920314214466, + "y": -324.591740116339, + "z": 0.0 + }, + { + "x": 118.70427041245634, + "y": -324.591632799243, + "z": 0.0 + }, + { + "x": 117.69933768276849, + "y": -324.59152548214684, + "z": 0.0 + }, + { + "x": 116.69440495307973, + "y": -324.5914181650508, + "z": 0.0 + }, + { + "x": 115.6894722233914, + "y": -324.5913108479548, + "z": 0.0 + }, + { + "x": 114.68453949370333, + "y": -324.5912035308587, + "z": 0.0 + }, + { + "x": 113.67960676401502, + "y": -324.5910962137626, + "z": 0.0 + }, + { + "x": 112.67467403432649, + "y": -324.5909888966666, + "z": 0.0 + }, + { + "x": 111.66974130463817, + "y": -324.5908815795706, + "z": 0.0 + }, + { + "x": 110.66480857495031, + "y": -324.59077426247444, + "z": 0.0 + }, + { + "x": 109.65987584526155, + "y": -324.5906669453784, + "z": 0.0 + }, + { + "x": 108.65494311557323, + "y": -324.5905596282824, + "z": 0.0 + }, + { + "x": 107.65001038588538, + "y": -324.59045231118625, + "z": 0.0 + }, + { + "x": 106.64507765619662, + "y": -324.59034499409023, + "z": 0.0 + }, + { + "x": 105.6401449265083, + "y": -324.5902376769942, + "z": 0.0 + }, + { + "x": 104.63521219682022, + "y": -324.5901303598981, + "z": 0.0 + }, + { + "x": 103.63027946713191, + "y": -324.59002304280204, + "z": 0.0 + }, + { + "x": 102.62663591823683, + "y": -324.58959221099286, + "z": 0.0 + }, + { + "x": 101.62212988384367, + "y": -324.5890540743326, + "z": 0.0 + } + ] + }, + { + "id": 31, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.61784428418933, + "y": -332.58905292643476, + "z": 0.0 + }, + { + "x": 102.62320343224319, + "y": -332.58959147462036, + "z": 0.0 + }, + { + "x": 103.62942514451072, + "y": -332.5900229971853, + "z": 0.0 + }, + { + "x": 104.63435787419903, + "y": -332.5901303142814, + "z": 0.0 + }, + { + "x": 105.63929060388756, + "y": -332.5902376313775, + "z": 0.0 + }, + { + "x": 106.64422333357588, + "y": -332.5903449484735, + "z": 0.0 + }, + { + "x": 107.64915606326373, + "y": -332.5904522655695, + "z": 0.0 + }, + { + "x": 108.65408879295249, + "y": -332.59055958266566, + "z": 0.0 + }, + { + "x": 109.65902152264081, + "y": -332.5906668997617, + "z": 0.0 + }, + { + "x": 110.66395425232867, + "y": -332.5907742168577, + "z": 0.0 + }, + { + "x": 111.66888698201743, + "y": -332.59088153395385, + "z": 0.0 + }, + { + "x": 112.67381971170575, + "y": -332.5909888510499, + "z": 0.0 + }, + { + "x": 113.67875244139383, + "y": -332.5910961681459, + "z": 0.0 + }, + { + "x": 114.68368517108213, + "y": -332.591203485242, + "z": 0.0 + }, + { + "x": 115.68861790077067, + "y": -332.59131080233806, + "z": 0.0 + }, + { + "x": 116.69355063045899, + "y": -332.5914181194341, + "z": 0.0 + }, + { + "x": 117.69848336014684, + "y": -332.5915254365301, + "z": 0.0 + }, + { + "x": 118.7034160898356, + "y": -332.59163275362624, + "z": 0.0 + }, + { + "x": 119.70834881952392, + "y": -332.59174007072227, + "z": 0.0 + }, + { + "x": 120.71328154921177, + "y": -332.5918473878183, + "z": 0.0 + }, + { + "x": 121.71821427890053, + "y": -332.59195470491443, + "z": 0.0 + }, + { + "x": 122.72314700858885, + "y": -332.59206202201045, + "z": 0.0 + }, + { + "x": 123.72807973827695, + "y": -332.5921693391065, + "z": 0.0 + }, + { + "x": 124.73301246796525, + "y": -332.59227665620256, + "z": 0.0 + }, + { + "x": 125.73794519765379, + "y": -332.59238397329864, + "z": 0.0 + }, + { + "x": 126.74287792734188, + "y": -332.59249129039466, + "z": 0.0 + }, + { + "x": 127.74781065703019, + "y": -332.59259860749074, + "z": 0.0 + }, + { + "x": 128.75274338671872, + "y": -332.5927059245868, + "z": 0.0 + }, + { + "x": 129.75767611640705, + "y": -332.59281324168285, + "z": 0.0 + }, + { + "x": 130.7626088460949, + "y": -332.59292055877887, + "z": 0.0 + }, + { + "x": 131.76754157578367, + "y": -332.593027875875, + "z": 0.0 + }, + { + "x": 132.77247430547197, + "y": -332.59313519297103, + "z": 0.0 + }, + { + "x": 133.77740703516005, + "y": -332.59324251006706, + "z": 0.0 + }, + { + "x": 134.7823397648484, + "y": -332.59334982716314, + "z": 0.0 + }, + { + "x": 135.7872724945369, + "y": -332.5934571442592, + "z": 0.0 + }, + { + "x": 136.792205224225, + "y": -332.59356446135524, + "z": 0.0 + }, + { + "x": 137.7971379539133, + "y": -332.5936717784513, + "z": 0.0 + }, + { + "x": 138.80207068360184, + "y": -332.5937790955474, + "z": 0.0 + }, + { + "x": 139.80700341328992, + "y": -332.5938864126434, + "z": 0.0 + }, + { + "x": 140.81193614297823, + "y": -332.5939937297395, + "z": 0.0 + }, + { + "x": 141.81686887266676, + "y": -332.5941010468356, + "z": 0.0 + }, + { + "x": 142.82180160235484, + "y": -332.5942083639316, + "z": 0.0 + }, + { + "x": 143.82673433204315, + "y": -332.5943156810277, + "z": 0.0 + }, + { + "x": 144.83166706173168, + "y": -332.5944229981238, + "z": 0.0 + }, + { + "x": 145.83659979141999, + "y": -332.5945303152198, + "z": 0.0 + }, + { + "x": 146.84153252110806, + "y": -332.5946376323158, + "z": 0.0 + }, + { + "x": 147.8464652507964, + "y": -332.5947449494119, + "z": 0.0 + }, + { + "x": 148.85139798048493, + "y": -332.594852266508, + "z": 0.0 + }, + { + "x": 149.856330710173, + "y": -332.594959583604, + "z": 0.0 + }, + { + "x": 150.86126343986132, + "y": -332.5950669007001, + "z": 0.0 + }, + { + "x": 151.86619616954985, + "y": -332.59517421779617, + "z": 0.0 + }, + { + "x": 152.87112889923793, + "y": -332.5952815348922, + "z": 0.0 + }, + { + "x": 153.87606162892624, + "y": -332.5953888519883, + "z": 0.0 + }, + { + "x": 154.88099435861477, + "y": -332.59549616908436, + "z": 0.0 + }, + { + "x": 155.88592708830285, + "y": -332.5956034861804, + "z": 0.0 + }, + { + "x": 156.89085981799138, + "y": -332.59571080327646, + "z": 0.0 + }, + { + "x": 157.8957925476795, + "y": -332.5958181203725, + "z": 0.0 + }, + { + "x": 158.9007252773678, + "y": -332.59592543746857, + "z": 0.0 + }, + { + "x": 159.90565800705633, + "y": -332.59603275456465, + "z": 0.0 + }, + { + "x": 160.9105907367444, + "y": -332.59614007166067, + "z": 0.0 + }, + { + "x": 161.91552346643294, + "y": -332.59624738875675, + "z": 0.0 + }, + { + "x": 162.92045619612102, + "y": -332.5963547058528, + "z": 0.0 + }, + { + "x": 163.92538892580933, + "y": -332.59646202294886, + "z": 0.0 + }, + { + "x": 164.93032165549786, + "y": -332.59656934004494, + "z": 0.0 + }, + { + "x": 165.93525438518594, + "y": -332.59667665714096, + "z": 0.0 + }, + { + "x": 166.94018711487448, + "y": -332.59678397423704, + "z": 0.0 + }, + { + "x": 167.94511984456258, + "y": -332.59689129133307, + "z": 0.0 + }, + { + "x": 168.9500525742509, + "y": -332.59699860842915, + "z": 0.0 + }, + { + "x": 169.95498530393942, + "y": -332.5971059255252, + "z": 0.0 + }, + { + "x": 170.95991803362753, + "y": -332.59721324262125, + "z": 0.0 + }, + { + "x": 171.96485076331584, + "y": -332.59732055971733, + "z": 0.0 + }, + { + "x": 172.9697834930044, + "y": -332.5974278768134, + "z": 0.0 + }, + { + "x": 173.97471622269248, + "y": -332.59753519390944, + "z": 0.0 + }, + { + "x": 174.97964895238078, + "y": -332.5976425110055, + "z": 0.0 + }, + { + "x": 175.98458168206935, + "y": -332.5977498281016, + "z": 0.0 + }, + { + "x": 176.98951441175765, + "y": -332.5978571451976, + "z": 0.0 + }, + { + "x": 177.99444714144573, + "y": -332.59796446229365, + "z": 0.0 + }, + { + "x": 178.99937987113407, + "y": -332.5980717793897, + "z": 0.0 + }, + { + "x": 180.0043126008226, + "y": -332.5981790964858, + "z": 0.0 + }, + { + "x": 181.00924533051068, + "y": -332.59828641358183, + "z": 0.0 + }, + { + "x": 182.014178060199, + "y": -332.5983937306779, + "z": 0.0 + }, + { + "x": 183.01911078988755, + "y": -332.598501047774, + "z": 0.0 + }, + { + "x": 184.02404351957563, + "y": -332.59860836487, + "z": 0.0 + }, + { + "x": 185.02897624926393, + "y": -332.5987156819661, + "z": 0.0 + }, + { + "x": 186.0339089789525, + "y": -332.5988229990622, + "z": 0.0 + }, + { + "x": 187.03884170864058, + "y": -332.5989303161582, + "z": 0.0 + }, + { + "x": 188.0437744383289, + "y": -332.5990376332543, + "z": 0.0 + }, + { + "x": 189.04870716801744, + "y": -332.59914495035036, + "z": 0.0 + }, + { + "x": 190.05363989770552, + "y": -332.5992522674464, + "z": 0.0 + }, + { + "x": 191.05857262739386, + "y": -332.59935958454247, + "z": 0.0 + }, + { + "x": 192.0635053570824, + "y": -332.59946690163855, + "z": 0.0 + }, + { + "x": 193.06843808677073, + "y": -332.5995742187346, + "z": 0.0 + }, + { + "x": 194.0733708164588, + "y": -332.5996815358306, + "z": 0.0 + }, + { + "x": 195.0783035461471, + "y": -332.5997888529267, + "z": 0.0 + }, + { + "x": 196.08323627583565, + "y": -332.59989617002276, + "z": 0.0 + }, + { + "x": 197.08816900552375, + "y": -332.6000034871188, + "z": 0.0 + }, + { + "x": 198.09310173521206, + "y": -332.60011080421486, + "z": 0.0 + }, + { + "x": 199.0980344649006, + "y": -332.60021812131095, + "z": 0.0 + }, + { + "x": 200.1029671945887, + "y": -332.60032543840697, + "z": 0.0 + }, + { + "x": 201.107899924277, + "y": -332.60043275550305, + "z": 0.0 + }, + { + "x": 202.11283265396554, + "y": -332.60054007259913, + "z": 0.0 + }, + { + "x": 203.11776538365388, + "y": -332.60064738969515, + "z": 0.0 + }, + { + "x": 204.12269811334173, + "y": -332.6007547067912, + "z": 0.0 + }, + { + "x": 205.1276308430305, + "y": -332.6008620238873, + "z": 0.0 + }, + { + "x": 206.13256357271882, + "y": -332.60096934098334, + "z": 0.0 + }, + { + "x": 207.13749630240667, + "y": -332.60107665807936, + "z": 0.0 + }, + { + "x": 208.14242903209546, + "y": -332.6011839751755, + "z": 0.0 + }, + { + "x": 209.14736176178377, + "y": -332.6012912922715, + "z": 0.0 + }, + { + "x": 210.15229449147185, + "y": -332.60139860936755, + "z": 0.0 + }, + { + "x": 211.15722722116018, + "y": -332.60150592646363, + "z": 0.0 + }, + { + "x": 212.16215995084872, + "y": -332.6016132435597, + "z": 0.0 + }, + { + "x": 213.16709268053702, + "y": -332.60172056065574, + "z": 0.0 + }, + { + "x": 214.1720254102249, + "y": -332.60182787775176, + "z": 0.0 + }, + { + "x": 215.17695813991367, + "y": -332.6019351948479, + "z": 0.0 + }, + { + "x": 216.18189086960197, + "y": -332.6020425119439, + "z": 0.0 + }, + { + "x": 217.18682359928982, + "y": -332.60214982903994, + "z": 0.0 + }, + { + "x": 218.1917563289786, + "y": -332.6022571461361, + "z": 0.0 + }, + { + "x": 219.19668905866692, + "y": -332.6023644632321, + "z": 0.0 + }, + { + "x": 220.201621788355, + "y": -332.60247178032813, + "z": 0.0 + }, + { + "x": 221.20655451804333, + "y": -332.6025790974242, + "z": 0.0 + }, + { + "x": 222.21148724773187, + "y": -332.6026864145203, + "z": 0.0 + }, + { + "x": 223.21641997741995, + "y": -332.6027937316163, + "z": 0.0 + }, + { + "x": 224.22135270710828, + "y": -332.6029010487124, + "z": 0.0 + }, + { + "x": 225.22628543679681, + "y": -332.6030083658085, + "z": 0.0 + }, + { + "x": 226.23121816648515, + "y": -332.6031156829045, + "z": 0.0 + }, + { + "x": 227.236150896173, + "y": -332.6032230000005, + "z": 0.0 + }, + { + "x": 228.24108362586176, + "y": -332.60333031709666, + "z": 0.0 + }, + { + "x": 229.2460163555501, + "y": -332.6034376341927, + "z": 0.0 + }, + { + "x": 230.25094908523792, + "y": -332.6035449512887, + "z": 0.0 + }, + { + "x": 231.25588181492668, + "y": -332.60365226838485, + "z": 0.0 + }, + { + "x": 232.260814544615, + "y": -332.6037595854809, + "z": 0.0 + }, + { + "x": 233.26574727430304, + "y": -332.6038669025769, + "z": 0.0 + }, + { + "x": 234.27068000399137, + "y": -332.603974219673, + "z": 0.0 + }, + { + "x": 235.2756127336799, + "y": -332.60408153676906, + "z": 0.0 + }, + { + "x": 236.280545463368, + "y": -332.6041888538651, + "z": 0.0 + }, + { + "x": 237.28547819305626, + "y": -332.60429617096116, + "z": 0.0 + }, + { + "x": 238.2904109227448, + "y": -332.60440348805724, + "z": 0.0 + }, + { + "x": 239.29534365243288, + "y": -332.60451080515327, + "z": 0.0 + }, + { + "x": 240.30027638212115, + "y": -332.60461812224935, + "z": 0.0 + }, + { + "x": 241.30520911180972, + "y": -332.60472543934543, + "z": 0.0 + }, + { + "x": 242.31014184149802, + "y": -332.60483275644145, + "z": 0.0 + }, + { + "x": 243.31507457118607, + "y": -332.6049400735375, + "z": 0.0 + }, + { + "x": 244.32000730087438, + "y": -332.60504739063356, + "z": 0.0 + }, + { + "x": 245.3249400305629, + "y": -332.60515470772964, + "z": 0.0 + }, + { + "x": 246.329872760251, + "y": -332.60526202482566, + "z": 0.0 + }, + { + "x": 247.33480548993927, + "y": -332.60536934192174, + "z": 0.0 + }, + { + "x": 248.33973821962783, + "y": -332.6054766590178, + "z": 0.0 + }, + { + "x": 249.3446709493159, + "y": -332.60558397611385, + "z": 0.0 + }, + { + "x": 250.3496036790042, + "y": -332.60569129320993, + "z": 0.0 + }, + { + "x": 251.35453640869272, + "y": -332.605798610306, + "z": 0.0 + }, + { + "x": 252.3594691383808, + "y": -332.60590592740203, + "z": 0.0 + }, + { + "x": 253.3644018680693, + "y": -332.6060132444981, + "z": 0.0 + }, + { + "x": 254.3693345977574, + "y": -332.60612056159414, + "z": 0.0 + }, + { + "x": 255.37426732744572, + "y": -332.6062278786902, + "z": 0.0 + }, + { + "x": 256.37920005713426, + "y": -332.6063351957863, + "z": 0.0 + }, + { + "x": 257.3841327868223, + "y": -332.6064425128823, + "z": 0.0 + }, + { + "x": 258.3890655165108, + "y": -332.6065498299784, + "z": 0.0 + }, + { + "x": 259.3939982461989, + "y": -332.60665714707443, + "z": 0.0 + }, + { + "x": 260.3989309758872, + "y": -332.6067644641705, + "z": 0.0 + }, + { + "x": 261.4038637055757, + "y": -332.6068717812666, + "z": 0.0 + }, + { + "x": 262.4087964352638, + "y": -332.6069790983626, + "z": 0.0 + }, + { + "x": 263.4137291649524, + "y": -332.6070864154587, + "z": 0.0 + }, + { + "x": 264.4186618946404, + "y": -332.6071937325547, + "z": 0.0 + }, + { + "x": 265.4235946243287, + "y": -332.6073010496508, + "z": 0.0 + }, + { + "x": 266.42852735401726, + "y": -332.6074083667469, + "z": 0.0 + }, + { + "x": 267.4334600837053, + "y": -332.6075156838429, + "z": 0.0 + }, + { + "x": 268.4383928133936, + "y": -332.607623000939, + "z": 0.0 + }, + { + "x": 269.44332554308215, + "y": -332.60773031803507, + "z": 0.0 + }, + { + "x": 270.44825827277026, + "y": -332.6078376351311, + "z": 0.0 + }, + { + "x": 271.45319100245854, + "y": -332.6079449522272, + "z": 0.0 + }, + { + "x": 272.45812373214704, + "y": -332.60805226932325, + "z": 0.0 + }, + { + "x": 273.46305646183515, + "y": -332.6081595864193, + "z": 0.0 + }, + { + "x": 274.46798919152366, + "y": -332.60826690351536, + "z": 0.0 + }, + { + "x": 275.4729219212117, + "y": -332.6083742206114, + "z": 0.0 + }, + { + "x": 276.47785465090027, + "y": -332.60848153770746, + "z": 0.0 + }, + { + "x": 277.4827873805884, + "y": -332.6085888548035, + "z": 0.0 + }, + { + "x": 278.48772011027665, + "y": -332.60869617189957, + "z": 0.0 + }, + { + "x": 279.49265283996516, + "y": -332.60880348899565, + "z": 0.0 + }, + { + "x": 280.49758556965327, + "y": -332.60891080609167, + "z": 0.0 + }, + { + "x": 281.50251829934155, + "y": -332.60901812318775, + "z": 0.0 + }, + { + "x": 282.50745102903005, + "y": -332.60912544028383, + "z": 0.0 + }, + { + "x": 283.51238375871816, + "y": -332.60923275737986, + "z": 0.0 + }, + { + "x": 284.5173164884065, + "y": -332.60934007447594, + "z": 0.0 + }, + { + "x": 285.522249218095, + "y": -332.609447391572, + "z": 0.0 + }, + { + "x": 286.5271819477833, + "y": -332.60955470866804, + "z": 0.0 + }, + { + "x": 287.53211467747116, + "y": -332.60966202576407, + "z": 0.0 + }, + { + "x": 288.5370474071599, + "y": -332.6097693428602, + "z": 0.0 + }, + { + "x": 289.54198312081223, + "y": -332.6098766602738, + "z": 0.0 + }, + { + "x": 290.54733166367475, + "y": -332.6099832501811, + "z": 0.0 + }, + { + "x": 291.55227276326934, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 292.55720549868784, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 293.5621382341064, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 294.56707096952493, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 295.5720037049434, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 296.5769364403619, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 297.58186917578047, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 298.586801911199, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 299.5917346466175, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 300.59666738203606, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 301.6016001174546, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 302.6065328528731, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 303.6114655882916, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 304.61639832371014, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 305.6213310591287, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 306.6262637945472, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 307.63119652996573, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 308.6361292653843, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 309.6410620008028, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 310.64599473622127, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 311.6509274716398, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 312.65586020705837, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 313.66079294247686, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 314.6657256778954, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 315.67065841331396, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 316.67559114873245, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 317.68052388415094, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 318.6854566195695, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 319.69038935498804, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 320.69532209040653, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 321.7002548258251, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 322.70518756124363, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 323.7101202966621, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 324.7150530320806, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 325.71998576749917, + "y": -332.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 32, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 101.6199870840165, + "y": -328.58905350038367, + "z": 0.0 + }, + { + "x": 102.62491967524001, + "y": -328.5895918428066, + "z": 0.0 + }, + { + "x": 103.62985230582132, + "y": -328.5900230199937, + "z": 0.0 + }, + { + "x": 104.63478503550962, + "y": -328.59013033708976, + "z": 0.0 + }, + { + "x": 105.63971776519793, + "y": -328.59023765418584, + "z": 0.0 + }, + { + "x": 106.64465049488625, + "y": -328.59034497128187, + "z": 0.0 + }, + { + "x": 107.64958322457456, + "y": -328.5904522883779, + "z": 0.0 + }, + { + "x": 108.65451595426286, + "y": -328.590559605474, + "z": 0.0 + }, + { + "x": 109.65944868395118, + "y": -328.59066692257005, + "z": 0.0 + }, + { + "x": 110.66438141363949, + "y": -328.5907742396661, + "z": 0.0 + }, + { + "x": 111.6693141433278, + "y": -328.5908815567622, + "z": 0.0 + }, + { + "x": 112.67424687301612, + "y": -328.59098887385824, + "z": 0.0 + }, + { + "x": 113.67917960270442, + "y": -328.59109619095426, + "z": 0.0 + }, + { + "x": 114.68411233239273, + "y": -328.59120350805034, + "z": 0.0 + }, + { + "x": 115.68904506208104, + "y": -328.5913108251464, + "z": 0.0 + }, + { + "x": 116.69397779176936, + "y": -328.59141814224245, + "z": 0.0 + }, + { + "x": 117.69891052145766, + "y": -328.59152545933847, + "z": 0.0 + }, + { + "x": 118.70384325114597, + "y": -328.5916327764346, + "z": 0.0 + }, + { + "x": 119.70877598083429, + "y": -328.59174009353063, + "z": 0.0 + }, + { + "x": 120.7137087105226, + "y": -328.59184741062666, + "z": 0.0 + }, + { + "x": 121.7186414402109, + "y": -328.5919547277228, + "z": 0.0 + }, + { + "x": 122.72357416989922, + "y": -328.5920620448188, + "z": 0.0 + }, + { + "x": 123.72850689958754, + "y": -328.59216936191484, + "z": 0.0 + }, + { + "x": 124.73343962927585, + "y": -328.5922766790109, + "z": 0.0 + }, + { + "x": 125.73837235896416, + "y": -328.592383996107, + "z": 0.0 + }, + { + "x": 126.74330508865248, + "y": -328.592491313203, + "z": 0.0 + }, + { + "x": 127.74823781834078, + "y": -328.5925986302991, + "z": 0.0 + }, + { + "x": 128.7531705480291, + "y": -328.5927059473952, + "z": 0.0 + }, + { + "x": 129.75810327771742, + "y": -328.5928132644912, + "z": 0.0 + }, + { + "x": 130.76303600740573, + "y": -328.59292058158724, + "z": 0.0 + }, + { + "x": 131.76796873709404, + "y": -328.5930278986834, + "z": 0.0 + }, + { + "x": 132.77290146678234, + "y": -328.5931352157794, + "z": 0.0 + }, + { + "x": 133.77783419647065, + "y": -328.5932425328754, + "z": 0.0 + }, + { + "x": 134.78276692615898, + "y": -328.5933498499715, + "z": 0.0 + }, + { + "x": 135.78769965584726, + "y": -328.5934571670676, + "z": 0.0 + }, + { + "x": 136.7926323855356, + "y": -328.5935644841636, + "z": 0.0 + }, + { + "x": 137.7975651152239, + "y": -328.5936718012597, + "z": 0.0 + }, + { + "x": 138.8024978449122, + "y": -328.59377911835577, + "z": 0.0 + }, + { + "x": 139.80743057460052, + "y": -328.5938864354518, + "z": 0.0 + }, + { + "x": 140.81236330428882, + "y": -328.5939937525479, + "z": 0.0 + }, + { + "x": 141.81729603397713, + "y": -328.59410106964395, + "z": 0.0 + }, + { + "x": 142.82222876366544, + "y": -328.59420838674, + "z": 0.0 + }, + { + "x": 143.82716149335374, + "y": -328.59431570383606, + "z": 0.0 + }, + { + "x": 144.83209422304205, + "y": -328.59442302093214, + "z": 0.0 + }, + { + "x": 145.83702695273035, + "y": -328.59453033802816, + "z": 0.0 + }, + { + "x": 146.84195968241866, + "y": -328.5946376551242, + "z": 0.0 + }, + { + "x": 147.846892412107, + "y": -328.59474497222027, + "z": 0.0 + }, + { + "x": 148.8518251417953, + "y": -328.59485228931635, + "z": 0.0 + }, + { + "x": 149.8567578714836, + "y": -328.5949596064124, + "z": 0.0 + }, + { + "x": 150.86169060117192, + "y": -328.59506692350845, + "z": 0.0 + }, + { + "x": 151.86662333086022, + "y": -328.59517424060454, + "z": 0.0 + }, + { + "x": 152.87155606054853, + "y": -328.59528155770056, + "z": 0.0 + }, + { + "x": 153.87648879023683, + "y": -328.59538887479664, + "z": 0.0 + }, + { + "x": 154.88142151992514, + "y": -328.5954961918927, + "z": 0.0 + }, + { + "x": 155.88635424961345, + "y": -328.59560350898875, + "z": 0.0 + }, + { + "x": 156.89128697930175, + "y": -328.5957108260848, + "z": 0.0 + }, + { + "x": 157.8962197089901, + "y": -328.59581814318085, + "z": 0.0 + }, + { + "x": 158.9011524386784, + "y": -328.59592546027693, + "z": 0.0 + }, + { + "x": 159.9060851683667, + "y": -328.596032777373, + "z": 0.0 + }, + { + "x": 160.911017898055, + "y": -328.59614009446904, + "z": 0.0 + }, + { + "x": 161.9159506277433, + "y": -328.5962474115651, + "z": 0.0 + }, + { + "x": 162.92088335743162, + "y": -328.59635472866114, + "z": 0.0 + }, + { + "x": 163.92581608711993, + "y": -328.5964620457572, + "z": 0.0 + }, + { + "x": 164.93074881680823, + "y": -328.5965693628533, + "z": 0.0 + }, + { + "x": 165.93568154649654, + "y": -328.5966766799493, + "z": 0.0 + }, + { + "x": 166.94061427618485, + "y": -328.5967839970454, + "z": 0.0 + }, + { + "x": 167.94554700587318, + "y": -328.59689131414143, + "z": 0.0 + }, + { + "x": 168.9504797355615, + "y": -328.5969986312375, + "z": 0.0 + }, + { + "x": 169.9554124652498, + "y": -328.5971059483336, + "z": 0.0 + }, + { + "x": 170.96034519493813, + "y": -328.5972132654296, + "z": 0.0 + }, + { + "x": 171.96527792462643, + "y": -328.5973205825257, + "z": 0.0 + }, + { + "x": 172.97021065431477, + "y": -328.5974278996218, + "z": 0.0 + }, + { + "x": 173.97514338400308, + "y": -328.5975352167178, + "z": 0.0 + }, + { + "x": 174.98007611369138, + "y": -328.5976425338139, + "z": 0.0 + }, + { + "x": 175.98500884337972, + "y": -328.59774985090996, + "z": 0.0 + }, + { + "x": 176.98994157306802, + "y": -328.597857168006, + "z": 0.0 + }, + { + "x": 177.99487430275633, + "y": -328.597964485102, + "z": 0.0 + }, + { + "x": 178.99980703244466, + "y": -328.5980718021981, + "z": 0.0 + }, + { + "x": 180.00473976213297, + "y": -328.5981791192942, + "z": 0.0 + }, + { + "x": 181.00967249182128, + "y": -328.5982864363902, + "z": 0.0 + }, + { + "x": 182.01460522150958, + "y": -328.5983937534863, + "z": 0.0 + }, + { + "x": 183.01953795119792, + "y": -328.59850107058236, + "z": 0.0 + }, + { + "x": 184.02447068088622, + "y": -328.5986083876784, + "z": 0.0 + }, + { + "x": 185.02940341057453, + "y": -328.59871570477446, + "z": 0.0 + }, + { + "x": 186.03433614026287, + "y": -328.59882302187054, + "z": 0.0 + }, + { + "x": 187.03926886995117, + "y": -328.59893033896657, + "z": 0.0 + }, + { + "x": 188.0442015996395, + "y": -328.59903765606265, + "z": 0.0 + }, + { + "x": 189.0491343293278, + "y": -328.59914497315873, + "z": 0.0 + }, + { + "x": 190.05406705901612, + "y": -328.59925229025475, + "z": 0.0 + }, + { + "x": 191.05899978870445, + "y": -328.59935960735083, + "z": 0.0 + }, + { + "x": 192.06393251839276, + "y": -328.5994669244469, + "z": 0.0 + }, + { + "x": 193.0688652480811, + "y": -328.59957424154294, + "z": 0.0 + }, + { + "x": 194.0737979777694, + "y": -328.59968155863896, + "z": 0.0 + }, + { + "x": 195.0787307074577, + "y": -328.59978887573504, + "z": 0.0 + }, + { + "x": 196.08366343714601, + "y": -328.5998961928311, + "z": 0.0 + }, + { + "x": 197.08859616683435, + "y": -328.60000350992715, + "z": 0.0 + }, + { + "x": 198.09352889652266, + "y": -328.60011082702323, + "z": 0.0 + }, + { + "x": 199.09846162621096, + "y": -328.6002181441193, + "z": 0.0 + }, + { + "x": 200.1033943558993, + "y": -328.60032546121533, + "z": 0.0 + }, + { + "x": 201.1083270855876, + "y": -328.6004327783114, + "z": 0.0 + }, + { + "x": 202.1132598152759, + "y": -328.6005400954075, + "z": 0.0 + }, + { + "x": 203.11819254496424, + "y": -328.6006474125035, + "z": 0.0 + }, + { + "x": 204.12312527465255, + "y": -328.60075472959954, + "z": 0.0 + }, + { + "x": 205.12805800434086, + "y": -328.6008620466957, + "z": 0.0 + }, + { + "x": 206.1329907340292, + "y": -328.6009693637917, + "z": 0.0 + }, + { + "x": 207.1379234637175, + "y": -328.60107668088773, + "z": 0.0 + }, + { + "x": 208.14285619340583, + "y": -328.60118399798387, + "z": 0.0 + }, + { + "x": 209.14778892309414, + "y": -328.6012913150799, + "z": 0.0 + }, + { + "x": 210.15272165278245, + "y": -328.6013986321759, + "z": 0.0 + }, + { + "x": 211.15765438247078, + "y": -328.601505949272, + "z": 0.0 + }, + { + "x": 212.1625871121591, + "y": -328.6016132663681, + "z": 0.0 + }, + { + "x": 213.1675198418474, + "y": -328.6017205834641, + "z": 0.0 + }, + { + "x": 214.17245257153573, + "y": -328.6018279005601, + "z": 0.0 + }, + { + "x": 215.17738530122404, + "y": -328.60193521765626, + "z": 0.0 + }, + { + "x": 216.18231803091234, + "y": -328.6020425347523, + "z": 0.0 + }, + { + "x": 217.18725076060065, + "y": -328.6021498518483, + "z": 0.0 + }, + { + "x": 218.19218349028898, + "y": -328.60225716894445, + "z": 0.0 + }, + { + "x": 219.1971162199773, + "y": -328.60236448604047, + "z": 0.0 + }, + { + "x": 220.2020489496656, + "y": -328.6024718031365, + "z": 0.0 + }, + { + "x": 221.20698167935393, + "y": -328.6025791202326, + "z": 0.0 + }, + { + "x": 222.21191440904224, + "y": -328.60268643732866, + "z": 0.0 + }, + { + "x": 223.21684713873054, + "y": -328.6027937544247, + "z": 0.0 + }, + { + "x": 224.22177986841888, + "y": -328.60290107152076, + "z": 0.0 + }, + { + "x": 225.22671259810718, + "y": -328.60300838861684, + "z": 0.0 + }, + { + "x": 226.23164532779552, + "y": -328.60311570571287, + "z": 0.0 + }, + { + "x": 227.23657805748383, + "y": -328.6032230228089, + "z": 0.0 + }, + { + "x": 228.24151078717213, + "y": -328.603330339905, + "z": 0.0 + }, + { + "x": 229.24644351686047, + "y": -328.60343765700105, + "z": 0.0 + }, + { + "x": 230.25137624654874, + "y": -328.6035449740971, + "z": 0.0 + }, + { + "x": 231.25630897623705, + "y": -328.6036522911932, + "z": 0.0 + }, + { + "x": 232.26124170592536, + "y": -328.60375960828924, + "z": 0.0 + }, + { + "x": 233.26617443561364, + "y": -328.60386692538526, + "z": 0.0 + }, + { + "x": 234.27110716530197, + "y": -328.60397424248134, + "z": 0.0 + }, + { + "x": 235.27603989499028, + "y": -328.6040815595774, + "z": 0.0 + }, + { + "x": 236.28097262467858, + "y": -328.60418887667345, + "z": 0.0 + }, + { + "x": 237.28590535436686, + "y": -328.6042961937695, + "z": 0.0 + }, + { + "x": 238.29083808405517, + "y": -328.6044035108656, + "z": 0.0 + }, + { + "x": 239.29577081374347, + "y": -328.60451082796163, + "z": 0.0 + }, + { + "x": 240.30070354343175, + "y": -328.6046181450577, + "z": 0.0 + }, + { + "x": 241.3056362731201, + "y": -328.6047254621538, + "z": 0.0 + }, + { + "x": 242.3105690028084, + "y": -328.6048327792498, + "z": 0.0 + }, + { + "x": 243.31550173249667, + "y": -328.60494009634584, + "z": 0.0 + }, + { + "x": 244.32043446218498, + "y": -328.6050474134419, + "z": 0.0 + }, + { + "x": 245.32536719187328, + "y": -328.605154730538, + "z": 0.0 + }, + { + "x": 246.3302999215616, + "y": -328.605262047634, + "z": 0.0 + }, + { + "x": 247.33523265124987, + "y": -328.6053693647301, + "z": 0.0 + }, + { + "x": 248.3401653809382, + "y": -328.6054766818262, + "z": 0.0 + }, + { + "x": 249.3450981106265, + "y": -328.6055839989222, + "z": 0.0 + }, + { + "x": 250.3500308403148, + "y": -328.6056913160183, + "z": 0.0 + }, + { + "x": 251.3549635700031, + "y": -328.6057986331144, + "z": 0.0 + }, + { + "x": 252.3598962996914, + "y": -328.6059059502104, + "z": 0.0 + }, + { + "x": 253.36482902937968, + "y": -328.6060132673065, + "z": 0.0 + }, + { + "x": 254.36976175906798, + "y": -328.6061205844025, + "z": 0.0 + }, + { + "x": 255.37469448875632, + "y": -328.6062279014986, + "z": 0.0 + }, + { + "x": 256.3796272184446, + "y": -328.60633521859467, + "z": 0.0 + }, + { + "x": 257.3845599481329, + "y": -328.6064425356907, + "z": 0.0 + }, + { + "x": 258.3894926778212, + "y": -328.60654985278677, + "z": 0.0 + }, + { + "x": 259.3944254075095, + "y": -328.6066571698828, + "z": 0.0 + }, + { + "x": 260.3993581371978, + "y": -328.6067644869789, + "z": 0.0 + }, + { + "x": 261.4042908668861, + "y": -328.60687180407496, + "z": 0.0 + }, + { + "x": 262.4092235965744, + "y": -328.606979121171, + "z": 0.0 + }, + { + "x": 263.41415632626274, + "y": -328.60708643826706, + "z": 0.0 + }, + { + "x": 264.419089055951, + "y": -328.6071937553631, + "z": 0.0 + }, + { + "x": 265.4240217856393, + "y": -328.60730107245917, + "z": 0.0 + }, + { + "x": 266.42895451532763, + "y": -328.60740838955525, + "z": 0.0 + }, + { + "x": 267.4338872450159, + "y": -328.60751570665127, + "z": 0.0 + }, + { + "x": 268.4388199747042, + "y": -328.60762302374735, + "z": 0.0 + }, + { + "x": 269.4437527043925, + "y": -328.60773034084343, + "z": 0.0 + }, + { + "x": 270.44868543408086, + "y": -328.60783765793946, + "z": 0.0 + }, + { + "x": 271.45361816376914, + "y": -328.60794497503554, + "z": 0.0 + }, + { + "x": 272.4585508934574, + "y": -328.6080522921316, + "z": 0.0 + }, + { + "x": 273.46348362314575, + "y": -328.60815960922764, + "z": 0.0 + }, + { + "x": 274.468416352834, + "y": -328.6082669263237, + "z": 0.0 + }, + { + "x": 275.4733490825223, + "y": -328.60837424341975, + "z": 0.0 + }, + { + "x": 276.47828181221064, + "y": -328.6084815605158, + "z": 0.0 + }, + { + "x": 277.483214541899, + "y": -328.60858887761185, + "z": 0.0 + }, + { + "x": 278.48814727158725, + "y": -328.60869619470793, + "z": 0.0 + }, + { + "x": 279.49308000127553, + "y": -328.608803511804, + "z": 0.0 + }, + { + "x": 280.49801273096386, + "y": -328.60891082890004, + "z": 0.0 + }, + { + "x": 281.50294546065214, + "y": -328.6090181459961, + "z": 0.0 + }, + { + "x": 282.5078781903404, + "y": -328.6091254630922, + "z": 0.0 + }, + { + "x": 283.51281092002876, + "y": -328.6092327801882, + "z": 0.0 + }, + { + "x": 284.5177436497171, + "y": -328.6093400972843, + "z": 0.0 + }, + { + "x": 285.52267637940537, + "y": -328.6094474143804, + "z": 0.0 + }, + { + "x": 286.52760910909365, + "y": -328.6095547314764, + "z": 0.0 + }, + { + "x": 287.532541838782, + "y": -328.60966204857243, + "z": 0.0 + }, + { + "x": 288.53747456847026, + "y": -328.60976936566857, + "z": 0.0 + }, + { + "x": 289.54240729815854, + "y": -328.6098766827646, + "z": 0.0 + }, + { + "x": 290.5473400279069, + "y": -328.6099832501898, + "z": 0.0 + }, + { + "x": 291.55227276326934, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 292.55720549868784, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 293.5621382341064, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 294.56707096952493, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 295.5720037049434, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 296.5769364403619, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 297.58186917578047, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 298.586801911199, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 299.5917346466175, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 300.59666738203606, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 301.6016001174546, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 302.6065328528731, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 303.6114655882916, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 304.61639832371014, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 305.6213310591287, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 306.6262637945472, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 307.63119652996573, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 308.6361292653843, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 309.6410620008028, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 310.64599473622127, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 311.6509274716398, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 312.65586020705837, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 313.66079294247686, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 314.6657256778954, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 315.67065841331396, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 316.67559114873245, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 317.68052388415094, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 318.6854566195695, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 319.69038935498804, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 320.69532209040653, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 321.7002548258251, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 322.70518756124363, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 323.7101202966621, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 324.7150530320806, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 325.71998576749917, + "y": -328.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 33, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.71998576749917, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 324.7150530320806, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 323.7101202966621, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 322.70518756124363, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 321.7002548258251, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 320.69532209040653, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 319.69038935498804, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 318.6854566195695, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 317.68052388415094, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 316.67559114873245, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 315.67065841331396, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 314.6657256778954, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 313.66079294247686, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 312.65586020705837, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 311.6509274716398, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 310.64599473622127, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 309.6410620008028, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 308.6361292653843, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 307.63119652996573, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 306.6262637945472, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 305.6213310591287, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 304.61639832371014, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 303.6114655882916, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 302.6065328528731, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 301.6016001174546, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 300.59666738203606, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 299.5917346466175, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 298.586801911199, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 297.58186917578047, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 296.5769364403619, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 295.5720037049434, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 294.56707096952493, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 293.5621382341064, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 292.55720549868784, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 291.55227276326934, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 290.54734421002297, + "y": -326.6099832501942, + "z": 0.0 + }, + { + "x": 289.5426193868317, + "y": -326.60987669401, + "z": 0.0 + }, + { + "x": 288.5376881491254, + "y": -326.6097693770728, + "z": 0.0 + }, + { + "x": 287.5327554194374, + "y": -326.6096620599766, + "z": 0.0 + }, + { + "x": 286.52782268974886, + "y": -326.6095547428806, + "z": 0.0 + }, + { + "x": 285.5228899600605, + "y": -326.60944742578454, + "z": 0.0 + }, + { + "x": 284.5179572303724, + "y": -326.60934010868846, + "z": 0.0 + }, + { + "x": 283.5130245006841, + "y": -326.6092327915924, + "z": 0.0 + }, + { + "x": 282.50809177099563, + "y": -326.6091254744964, + "z": 0.0 + }, + { + "x": 281.5031590413074, + "y": -326.6090181574003, + "z": 0.0 + }, + { + "x": 280.4982263116192, + "y": -326.60891084030425, + "z": 0.0 + }, + { + "x": 279.49329358193074, + "y": -326.60880352320817, + "z": 0.0 + }, + { + "x": 278.4883608522425, + "y": -326.6086962061121, + "z": 0.0 + }, + { + "x": 277.4834281225543, + "y": -326.608588889016, + "z": 0.0 + }, + { + "x": 276.47849539286585, + "y": -326.60848157192004, + "z": 0.0 + }, + { + "x": 275.47356266317763, + "y": -326.60837425482396, + "z": 0.0 + }, + { + "x": 274.4686299334892, + "y": -326.6082669377279, + "z": 0.0 + }, + { + "x": 273.4636972038011, + "y": -326.6081596206318, + "z": 0.0 + }, + { + "x": 272.4587644741126, + "y": -326.6080523035358, + "z": 0.0 + }, + { + "x": 271.4538317444244, + "y": -326.60794498643975, + "z": 0.0 + }, + { + "x": 270.4488990147362, + "y": -326.60783766934367, + "z": 0.0 + }, + { + "x": 269.44396628504774, + "y": -326.6077303522476, + "z": 0.0 + }, + { + "x": 268.4390335553595, + "y": -326.6076230351515, + "z": 0.0 + }, + { + "x": 267.4341008256712, + "y": -326.6075157180554, + "z": 0.0 + }, + { + "x": 266.42916809598285, + "y": -326.60740840095946, + "z": 0.0 + }, + { + "x": 265.4242353662946, + "y": -326.6073010838634, + "z": 0.0 + }, + { + "x": 264.4193026366063, + "y": -326.6071937667673, + "z": 0.0 + }, + { + "x": 263.41436990691795, + "y": -326.6070864496712, + "z": 0.0 + }, + { + "x": 262.40943717722973, + "y": -326.60697913257513, + "z": 0.0 + }, + { + "x": 261.4045044475413, + "y": -326.60687181547917, + "z": 0.0 + }, + { + "x": 260.39957171785306, + "y": -326.6067644983831, + "z": 0.0 + }, + { + "x": 259.39463898816484, + "y": -326.606657181287, + "z": 0.0 + }, + { + "x": 258.3897062584764, + "y": -326.6065498641909, + "z": 0.0 + }, + { + "x": 257.3847735287882, + "y": -326.60644254709484, + "z": 0.0 + }, + { + "x": 256.37984079909984, + "y": -326.6063352299989, + "z": 0.0 + }, + { + "x": 255.37490806941162, + "y": -326.6062279129028, + "z": 0.0 + }, + { + "x": 254.36997533972328, + "y": -326.6061205958067, + "z": 0.0 + }, + { + "x": 253.36504261003486, + "y": -326.60601327871063, + "z": 0.0 + }, + { + "x": 252.3601098803467, + "y": -326.60590596161455, + "z": 0.0 + }, + { + "x": 251.35517715065828, + "y": -326.6057986445186, + "z": 0.0 + }, + { + "x": 250.35024442097009, + "y": -326.6056913274225, + "z": 0.0 + }, + { + "x": 249.3453116912818, + "y": -326.6055840103264, + "z": 0.0 + }, + { + "x": 248.3403789615934, + "y": -326.60547669323034, + "z": 0.0 + }, + { + "x": 247.33544623190517, + "y": -326.60536937613426, + "z": 0.0 + }, + { + "x": 246.3305135022169, + "y": -326.6052620590382, + "z": 0.0 + }, + { + "x": 245.32558077252847, + "y": -326.6051547419422, + "z": 0.0 + }, + { + "x": 244.32064804284028, + "y": -326.60504742484613, + "z": 0.0 + }, + { + "x": 243.31571531315197, + "y": -326.60494010775005, + "z": 0.0 + }, + { + "x": 242.31078258346358, + "y": -326.604832790654, + "z": 0.0 + }, + { + "x": 241.30584985377527, + "y": -326.604725473558, + "z": 0.0 + }, + { + "x": 240.30091712408705, + "y": -326.6046181564619, + "z": 0.0 + }, + { + "x": 239.29598439439877, + "y": -326.60451083936584, + "z": 0.0 + }, + { + "x": 238.29105166471035, + "y": -326.60440352226976, + "z": 0.0 + }, + { + "x": 237.28611893502216, + "y": -326.6042962051737, + "z": 0.0 + }, + { + "x": 236.28118620533388, + "y": -326.6041888880776, + "z": 0.0 + }, + { + "x": 235.27625347564546, + "y": -326.60408157098163, + "z": 0.0 + }, + { + "x": 234.27132074595727, + "y": -326.60397425388555, + "z": 0.0 + }, + { + "x": 233.26638801626893, + "y": -326.6038669367895, + "z": 0.0 + }, + { + "x": 232.26145528658054, + "y": -326.6037596196934, + "z": 0.0 + }, + { + "x": 231.25652255689224, + "y": -326.6036523025974, + "z": 0.0 + }, + { + "x": 230.25158982720416, + "y": -326.60354498550123, + "z": 0.0 + }, + { + "x": 229.24665709751565, + "y": -326.60343766840526, + "z": 0.0 + }, + { + "x": 228.24172436782732, + "y": -326.6033303513092, + "z": 0.0 + }, + { + "x": 227.23679163813924, + "y": -326.6032230342131, + "z": 0.0 + }, + { + "x": 226.2318589084507, + "y": -326.603115717117, + "z": 0.0 + }, + { + "x": 225.22692617876237, + "y": -326.60300840002105, + "z": 0.0 + }, + { + "x": 224.22199344907418, + "y": -326.602901082925, + "z": 0.0 + }, + { + "x": 223.21706071938584, + "y": -326.6027937658289, + "z": 0.0 + }, + { + "x": 222.21212798969742, + "y": -326.6026864487328, + "z": 0.0 + }, + { + "x": 221.20719526000923, + "y": -326.60257913163673, + "z": 0.0 + }, + { + "x": 220.2022625303209, + "y": -326.60247181454065, + "z": 0.0 + }, + { + "x": 219.19732980063247, + "y": -326.6023644974447, + "z": 0.0 + }, + { + "x": 218.19239707094417, + "y": -326.6022571803486, + "z": 0.0 + }, + { + "x": 217.18746434125606, + "y": -326.6021498632525, + "z": 0.0 + }, + { + "x": 216.18253161156753, + "y": -326.60204254615644, + "z": 0.0 + }, + { + "x": 215.17759888187922, + "y": -326.6019352290605, + "z": 0.0 + }, + { + "x": 214.17266615219114, + "y": -326.6018279119643, + "z": 0.0 + }, + { + "x": 213.16773342250258, + "y": -326.6017205948683, + "z": 0.0 + }, + { + "x": 212.16280069281427, + "y": -326.60161327777223, + "z": 0.0 + }, + { + "x": 211.15786796312608, + "y": -326.60150596067615, + "z": 0.0 + }, + { + "x": 210.15293523343774, + "y": -326.60139864358007, + "z": 0.0 + }, + { + "x": 209.14800250374932, + "y": -326.6012913264841, + "z": 0.0 + }, + { + "x": 208.14306977406102, + "y": -326.601184009388, + "z": 0.0 + }, + { + "x": 207.1381370443729, + "y": -326.60107669229194, + "z": 0.0 + }, + { + "x": 206.13320431468438, + "y": -326.60096937519586, + "z": 0.0 + }, + { + "x": 205.12827158499604, + "y": -326.6008620580999, + "z": 0.0 + }, + { + "x": 204.12333885530796, + "y": -326.6007547410037, + "z": 0.0 + }, + { + "x": 203.11840612561943, + "y": -326.60064742390773, + "z": 0.0 + }, + { + "x": 202.1134733959311, + "y": -326.60054010681165, + "z": 0.0 + }, + { + "x": 201.1085406662429, + "y": -326.60043278971557, + "z": 0.0 + }, + { + "x": 200.1036079365546, + "y": -326.6003254726195, + "z": 0.0 + }, + { + "x": 199.09867520686615, + "y": -326.6002181555235, + "z": 0.0 + }, + { + "x": 198.09374247717795, + "y": -326.60011083842744, + "z": 0.0 + }, + { + "x": 197.08880974748965, + "y": -326.60000352133136, + "z": 0.0 + }, + { + "x": 196.0838770178012, + "y": -326.5998962042353, + "z": 0.0 + }, + { + "x": 195.078944288113, + "y": -326.5997888871392, + "z": 0.0 + }, + { + "x": 194.0740115584247, + "y": -326.5996815700431, + "z": 0.0 + }, + { + "x": 193.06907882873628, + "y": -326.59957425294715, + "z": 0.0 + }, + { + "x": 192.06414609904795, + "y": -326.59946693585107, + "z": 0.0 + }, + { + "x": 191.05921336935975, + "y": -326.599359618755, + "z": 0.0 + }, + { + "x": 190.05428063967142, + "y": -326.5992523016589, + "z": 0.0 + }, + { + "x": 189.049347909983, + "y": -326.59914498456294, + "z": 0.0 + }, + { + "x": 188.0444151802948, + "y": -326.59903766746686, + "z": 0.0 + }, + { + "x": 187.03948245060647, + "y": -326.5989303503708, + "z": 0.0 + }, + { + "x": 186.03454972091805, + "y": -326.5988230332747, + "z": 0.0 + }, + { + "x": 185.02961699122983, + "y": -326.5987157161786, + "z": 0.0 + }, + { + "x": 184.02468426154152, + "y": -326.59860839908254, + "z": 0.0 + }, + { + "x": 183.0197515318531, + "y": -326.59850108198657, + "z": 0.0 + }, + { + "x": 182.01481880216488, + "y": -326.5983937648905, + "z": 0.0 + }, + { + "x": 181.00988607247658, + "y": -326.5982864477944, + "z": 0.0 + }, + { + "x": 180.00495334278816, + "y": -326.5981791306983, + "z": 0.0 + }, + { + "x": 179.00002061309996, + "y": -326.59807181360225, + "z": 0.0 + }, + { + "x": 177.99508788341163, + "y": -326.59796449650617, + "z": 0.0 + }, + { + "x": 176.9901551537232, + "y": -326.5978571794102, + "z": 0.0 + }, + { + "x": 175.9852224240349, + "y": -326.5977498623141, + "z": 0.0 + }, + { + "x": 174.98028969434668, + "y": -326.59764254521804, + "z": 0.0 + }, + { + "x": 173.97535696465837, + "y": -326.59753522812196, + "z": 0.0 + }, + { + "x": 172.97042423496995, + "y": -326.597427911026, + "z": 0.0 + }, + { + "x": 171.96549150528173, + "y": -326.5973205939299, + "z": 0.0 + }, + { + "x": 170.96055877559343, + "y": -326.5972132768338, + "z": 0.0 + }, + { + "x": 169.95562604590498, + "y": -326.59710595973775, + "z": 0.0 + }, + { + "x": 168.95069331621679, + "y": -326.59699864264167, + "z": 0.0 + }, + { + "x": 167.94576058652848, + "y": -326.5968913255456, + "z": 0.0 + }, + { + "x": 166.94082785684003, + "y": -326.5967840084496, + "z": 0.0 + }, + { + "x": 165.93589512715184, + "y": -326.59667669135354, + "z": 0.0 + }, + { + "x": 164.93096239746342, + "y": -326.59656937425746, + "z": 0.0 + }, + { + "x": 163.92602966777523, + "y": -326.5964620571614, + "z": 0.0 + }, + { + "x": 162.92109693808692, + "y": -326.5963547400653, + "z": 0.0 + }, + { + "x": 161.9161642083985, + "y": -326.5962474229693, + "z": 0.0 + }, + { + "x": 160.9112314787103, + "y": -326.59614010587325, + "z": 0.0 + }, + { + "x": 159.9062987490219, + "y": -326.59603278877717, + "z": 0.0 + }, + { + "x": 158.9013660193337, + "y": -326.5959254716811, + "z": 0.0 + }, + { + "x": 157.8964332896454, + "y": -326.595818154585, + "z": 0.0 + }, + { + "x": 156.89150055995694, + "y": -326.59571083748904, + "z": 0.0 + }, + { + "x": 155.88656783026875, + "y": -326.59560352039296, + "z": 0.0 + }, + { + "x": 154.88163510058033, + "y": -326.5954962032969, + "z": 0.0 + }, + { + "x": 153.87670237089213, + "y": -326.5953888862008, + "z": 0.0 + }, + { + "x": 152.87176964120383, + "y": -326.5952815691047, + "z": 0.0 + }, + { + "x": 151.8668369115154, + "y": -326.59517425200875, + "z": 0.0 + }, + { + "x": 150.8619041818272, + "y": -326.59506693491267, + "z": 0.0 + }, + { + "x": 149.8569714521389, + "y": -326.5949596178166, + "z": 0.0 + }, + { + "x": 148.8520387224505, + "y": -326.5948523007205, + "z": 0.0 + }, + { + "x": 147.8471059927623, + "y": -326.5947449836244, + "z": 0.0 + }, + { + "x": 146.84217326307396, + "y": -326.59463766652834, + "z": 0.0 + }, + { + "x": 145.83724053338554, + "y": -326.5945303494324, + "z": 0.0 + }, + { + "x": 144.83230780369723, + "y": -326.5944230323363, + "z": 0.0 + }, + { + "x": 143.82737507400904, + "y": -326.5943157152402, + "z": 0.0 + }, + { + "x": 142.82244234432073, + "y": -326.59420839814413, + "z": 0.0 + }, + { + "x": 141.8175096146323, + "y": -326.59410108104817, + "z": 0.0 + }, + { + "x": 140.81257688494412, + "y": -326.5939937639521, + "z": 0.0 + }, + { + "x": 139.80764415525582, + "y": -326.593886446856, + "z": 0.0 + }, + { + "x": 138.8027114255674, + "y": -326.5937791297599, + "z": 0.0 + }, + { + "x": 137.7977786958792, + "y": -326.59367181266384, + "z": 0.0 + }, + { + "x": 136.7928459661909, + "y": -326.59356449556776, + "z": 0.0 + }, + { + "x": 135.78791323650245, + "y": -326.5934571784718, + "z": 0.0 + }, + { + "x": 134.78298050681428, + "y": -326.5933498613757, + "z": 0.0 + }, + { + "x": 133.77804777712595, + "y": -326.59324254427963, + "z": 0.0 + }, + { + "x": 132.77311504743753, + "y": -326.59313522718355, + "z": 0.0 + }, + { + "x": 131.76818231774922, + "y": -326.5930279100876, + "z": 0.0 + }, + { + "x": 130.76324958806114, + "y": -326.5929205929914, + "z": 0.0 + }, + { + "x": 129.7583168583726, + "y": -326.5928132758954, + "z": 0.0 + }, + { + "x": 128.75338412868427, + "y": -326.59270595879934, + "z": 0.0 + }, + { + "x": 127.74845139899608, + "y": -326.59259864170326, + "z": 0.0 + }, + { + "x": 126.74351866930778, + "y": -326.5924913246072, + "z": 0.0 + }, + { + "x": 125.73858593961934, + "y": -326.5923840075112, + "z": 0.0 + }, + { + "x": 124.73365320993115, + "y": -326.59227669041513, + "z": 0.0 + }, + { + "x": 123.72872048024284, + "y": -326.59216937331905, + "z": 0.0 + }, + { + "x": 122.72378775055441, + "y": -326.59206205622297, + "z": 0.0 + }, + { + "x": 121.71885502086609, + "y": -326.591954739127, + "z": 0.0 + }, + { + "x": 120.71392229117801, + "y": -326.5918474220308, + "z": 0.0 + }, + { + "x": 119.70898956148947, + "y": -326.59174010493484, + "z": 0.0 + }, + { + "x": 118.70405683180115, + "y": -326.59163278783876, + "z": 0.0 + }, + { + "x": 117.69912410211307, + "y": -326.5915254707427, + "z": 0.0 + }, + { + "x": 116.69419137242454, + "y": -326.5914181536466, + "z": 0.0 + }, + { + "x": 115.68925864273622, + "y": -326.59131083655063, + "z": 0.0 + }, + { + "x": 114.68432591304803, + "y": -326.59120351945455, + "z": 0.0 + }, + { + "x": 113.67939318335972, + "y": -326.5910962023585, + "z": 0.0 + }, + { + "x": 112.6744604536713, + "y": -326.5909888852624, + "z": 0.0 + }, + { + "x": 111.66952772398298, + "y": -326.5908815681664, + "z": 0.0 + }, + { + "x": 110.6645949942949, + "y": -326.59077425107023, + "z": 0.0 + }, + { + "x": 109.65966226460637, + "y": -326.59066693397426, + "z": 0.0 + }, + { + "x": 108.65472953491805, + "y": -326.5905596168782, + "z": 0.0 + }, + { + "x": 107.64979680522997, + "y": -326.5904522997821, + "z": 0.0 + }, + { + "x": 106.64486407554143, + "y": -326.590344982686, + "z": 0.0 + }, + { + "x": 105.63993134585311, + "y": -326.59023766559005, + "z": 0.0 + }, + { + "x": 104.63499861616492, + "y": -326.590130348494, + "z": 0.0 + }, + { + "x": 103.63006588647661, + "y": -326.5900230313979, + "z": 0.0 + }, + { + "x": 102.62577779673842, + "y": -326.5895920268997, + "z": 0.0 + }, + { + "x": 101.62105848393009, + "y": -326.5890537873581, + "z": 0.0 + } + ] + }, + { + "id": 34, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.61891568410292, + "y": -330.5890532134092, + "z": 0.0 + }, + { + "x": 102.6240615537416, + "y": -330.5895916587135, + "z": 0.0 + }, + { + "x": 103.62963872516602, + "y": -330.59002300858947, + "z": 0.0 + }, + { + "x": 104.63457145485432, + "y": -330.59013032568555, + "z": 0.0 + }, + { + "x": 105.63950418454274, + "y": -330.59023764278163, + "z": 0.0 + }, + { + "x": 106.64443691423106, + "y": -330.5903449598777, + "z": 0.0 + }, + { + "x": 107.64936964391914, + "y": -330.5904522769737, + "z": 0.0 + }, + { + "x": 108.65430237360768, + "y": -330.5905595940699, + "z": 0.0 + }, + { + "x": 109.659235103296, + "y": -330.59066691116584, + "z": 0.0 + }, + { + "x": 110.66416783298408, + "y": -330.5907742282619, + "z": 0.0 + }, + { + "x": 111.66910056267261, + "y": -330.590881545358, + "z": 0.0 + }, + { + "x": 112.67403329236093, + "y": -330.5909888624541, + "z": 0.0 + }, + { + "x": 113.67896602204912, + "y": -330.59109617955005, + "z": 0.0 + }, + { + "x": 114.68389875173743, + "y": -330.59120349664613, + "z": 0.0 + }, + { + "x": 115.68883148142585, + "y": -330.5913108137422, + "z": 0.0 + }, + { + "x": 116.69376421111417, + "y": -330.5914181308383, + "z": 0.0 + }, + { + "x": 117.69869694080225, + "y": -330.59152544793426, + "z": 0.0 + }, + { + "x": 118.70362967049078, + "y": -330.59163276503045, + "z": 0.0 + }, + { + "x": 119.7085624001791, + "y": -330.5917400821264, + "z": 0.0 + }, + { + "x": 120.71349512986718, + "y": -330.5918473992225, + "z": 0.0 + }, + { + "x": 121.71842785955572, + "y": -330.5919547163186, + "z": 0.0 + }, + { + "x": 122.72336058924404, + "y": -330.59206203341466, + "z": 0.0 + }, + { + "x": 123.72829331893224, + "y": -330.59216935051063, + "z": 0.0 + }, + { + "x": 124.73322604862055, + "y": -330.5922766676067, + "z": 0.0 + }, + { + "x": 125.73815877830897, + "y": -330.5923839847028, + "z": 0.0 + }, + { + "x": 126.74309150799718, + "y": -330.5924913017989, + "z": 0.0 + }, + { + "x": 127.74802423768548, + "y": -330.59259861889495, + "z": 0.0 + }, + { + "x": 128.7529569673739, + "y": -330.59270593599103, + "z": 0.0 + }, + { + "x": 129.75788969706224, + "y": -330.592813253087, + "z": 0.0 + }, + { + "x": 130.76282242675032, + "y": -330.5929205701831, + "z": 0.0 + }, + { + "x": 131.76775515643885, + "y": -330.59302788727916, + "z": 0.0 + }, + { + "x": 132.77268788612716, + "y": -330.59313520437524, + "z": 0.0 + }, + { + "x": 133.77762061581535, + "y": -330.5932425214712, + "z": 0.0 + }, + { + "x": 134.7825533455037, + "y": -330.5933498385673, + "z": 0.0 + }, + { + "x": 135.78748607519208, + "y": -330.5934571556634, + "z": 0.0 + }, + { + "x": 136.7924188048803, + "y": -330.59356447275945, + "z": 0.0 + }, + { + "x": 137.7973515345686, + "y": -330.59367178985553, + "z": 0.0 + }, + { + "x": 138.80228426425703, + "y": -330.5937791069516, + "z": 0.0 + }, + { + "x": 139.80721699394522, + "y": -330.5938864240476, + "z": 0.0 + }, + { + "x": 140.81214972363352, + "y": -330.59399374114366, + "z": 0.0 + }, + { + "x": 141.81708245332194, + "y": -330.59410105823974, + "z": 0.0 + }, + { + "x": 142.82201518301014, + "y": -330.5942083753358, + "z": 0.0 + }, + { + "x": 143.82694791269844, + "y": -330.5943156924319, + "z": 0.0 + }, + { + "x": 144.83188064238686, + "y": -330.594423009528, + "z": 0.0 + }, + { + "x": 145.83681337207517, + "y": -330.59453032662395, + "z": 0.0 + }, + { + "x": 146.84174610176336, + "y": -330.59463764372003, + "z": 0.0 + }, + { + "x": 147.8466788314517, + "y": -330.5947449608161, + "z": 0.0 + }, + { + "x": 148.85161156114012, + "y": -330.5948522779122, + "z": 0.0 + }, + { + "x": 149.8565442908283, + "y": -330.59495959500816, + "z": 0.0 + }, + { + "x": 150.86147702051662, + "y": -330.59506691210424, + "z": 0.0 + }, + { + "x": 151.86640975020504, + "y": -330.5951742292003, + "z": 0.0 + }, + { + "x": 152.87134247989323, + "y": -330.5952815462964, + "z": 0.0 + }, + { + "x": 153.87627520958154, + "y": -330.5953888633925, + "z": 0.0 + }, + { + "x": 154.88120793926996, + "y": -330.59549618048857, + "z": 0.0 + }, + { + "x": 155.88614066895815, + "y": -330.59560349758453, + "z": 0.0 + }, + { + "x": 156.89107339864657, + "y": -330.5957108146806, + "z": 0.0 + }, + { + "x": 157.8960061283348, + "y": -330.5958181317767, + "z": 0.0 + }, + { + "x": 158.9009388580231, + "y": -330.5959254488728, + "z": 0.0 + }, + { + "x": 159.90587158771152, + "y": -330.59603276596886, + "z": 0.0 + }, + { + "x": 160.9108043173997, + "y": -330.5961400830648, + "z": 0.0 + }, + { + "x": 161.91573704708813, + "y": -330.5962474001609, + "z": 0.0 + }, + { + "x": 162.92066977677632, + "y": -330.596354717257, + "z": 0.0 + }, + { + "x": 163.92560250646463, + "y": -330.59646203435307, + "z": 0.0 + }, + { + "x": 164.93053523615305, + "y": -330.59656935144915, + "z": 0.0 + }, + { + "x": 165.93546796584124, + "y": -330.5966766685451, + "z": 0.0 + }, + { + "x": 166.94040069552966, + "y": -330.5967839856412, + "z": 0.0 + }, + { + "x": 167.94533342521788, + "y": -330.5968913027373, + "z": 0.0 + }, + { + "x": 168.9502661549062, + "y": -330.59699861983336, + "z": 0.0 + }, + { + "x": 169.9551988845946, + "y": -330.59710593692944, + "z": 0.0 + }, + { + "x": 170.96013161428283, + "y": -330.5972132540254, + "z": 0.0 + }, + { + "x": 171.96506434397114, + "y": -330.5973205711215, + "z": 0.0 + }, + { + "x": 172.96999707365958, + "y": -330.59742788821757, + "z": 0.0 + }, + { + "x": 173.97492980334778, + "y": -330.59753520531365, + "z": 0.0 + }, + { + "x": 174.97986253303608, + "y": -330.5976425224097, + "z": 0.0 + }, + { + "x": 175.98479526272453, + "y": -330.5977498395058, + "z": 0.0 + }, + { + "x": 176.98972799241284, + "y": -330.5978571566018, + "z": 0.0 + }, + { + "x": 177.99466072210103, + "y": -330.59796447369786, + "z": 0.0 + }, + { + "x": 178.99959345178937, + "y": -330.59807179079394, + "z": 0.0 + }, + { + "x": 180.0045261814778, + "y": -330.59817910789, + "z": 0.0 + }, + { + "x": 181.00945891116598, + "y": -330.598286424986, + "z": 0.0 + }, + { + "x": 182.01439164085429, + "y": -330.59839374208207, + "z": 0.0 + }, + { + "x": 183.01932437054273, + "y": -330.59850105917815, + "z": 0.0 + }, + { + "x": 184.02425710023093, + "y": -330.5986083762742, + "z": 0.0 + }, + { + "x": 185.02918982991923, + "y": -330.5987156933703, + "z": 0.0 + }, + { + "x": 186.03412255960768, + "y": -330.5988230104664, + "z": 0.0 + }, + { + "x": 187.03905528929587, + "y": -330.59893032756236, + "z": 0.0 + }, + { + "x": 188.0439880189842, + "y": -330.59903764465844, + "z": 0.0 + }, + { + "x": 189.04892074867263, + "y": -330.5991449617545, + "z": 0.0 + }, + { + "x": 190.05385347836082, + "y": -330.5992522788506, + "z": 0.0 + }, + { + "x": 191.05878620804916, + "y": -330.5993595959467, + "z": 0.0 + }, + { + "x": 192.06371893773758, + "y": -330.59946691304276, + "z": 0.0 + }, + { + "x": 193.0686516674259, + "y": -330.5995742301387, + "z": 0.0 + }, + { + "x": 194.0735843971141, + "y": -330.5996815472348, + "z": 0.0 + }, + { + "x": 195.0785171268024, + "y": -330.5997888643309, + "z": 0.0 + }, + { + "x": 196.08344985649083, + "y": -330.59989618142697, + "z": 0.0 + }, + { + "x": 197.08838258617905, + "y": -330.60000349852294, + "z": 0.0 + }, + { + "x": 198.09331531586736, + "y": -330.600110815619, + "z": 0.0 + }, + { + "x": 199.09824804555578, + "y": -330.6002181327151, + "z": 0.0 + }, + { + "x": 200.103180775244, + "y": -330.6003254498112, + "z": 0.0 + }, + { + "x": 201.1081135049323, + "y": -330.60043276690726, + "z": 0.0 + }, + { + "x": 202.11304623462073, + "y": -330.60054008400334, + "z": 0.0 + }, + { + "x": 203.11797896430906, + "y": -330.6006474010993, + "z": 0.0 + }, + { + "x": 204.12291169399714, + "y": -330.6007547181954, + "z": 0.0 + }, + { + "x": 205.12784442368567, + "y": -330.60086203529147, + "z": 0.0 + }, + { + "x": 206.132777153374, + "y": -330.60096935238755, + "z": 0.0 + }, + { + "x": 207.1377098830621, + "y": -330.6010766694835, + "z": 0.0 + }, + { + "x": 208.14264261275065, + "y": -330.6011839865797, + "z": 0.0 + }, + { + "x": 209.14757534243896, + "y": -330.6012913036757, + "z": 0.0 + }, + { + "x": 210.15250807212715, + "y": -330.60139862077176, + "z": 0.0 + }, + { + "x": 211.15744080181548, + "y": -330.60150593786784, + "z": 0.0 + }, + { + "x": 212.1623735315039, + "y": -330.6016132549639, + "z": 0.0 + }, + { + "x": 213.1673062611922, + "y": -330.6017205720599, + "z": 0.0 + }, + { + "x": 214.17223899088032, + "y": -330.60182788915597, + "z": 0.0 + }, + { + "x": 215.17717172056885, + "y": -330.60193520625205, + "z": 0.0 + }, + { + "x": 216.18210445025716, + "y": -330.60204252334813, + "z": 0.0 + }, + { + "x": 217.18703717994524, + "y": -330.6021498404441, + "z": 0.0 + }, + { + "x": 218.1919699096338, + "y": -330.6022571575403, + "z": 0.0 + }, + { + "x": 219.1969026393221, + "y": -330.60236447463626, + "z": 0.0 + }, + { + "x": 220.2018353690103, + "y": -330.60247179173234, + "z": 0.0 + }, + { + "x": 221.20676809869863, + "y": -330.6025791088284, + "z": 0.0 + }, + { + "x": 222.21170082838705, + "y": -330.6026864259245, + "z": 0.0 + }, + { + "x": 223.21663355807524, + "y": -330.60279374302047, + "z": 0.0 + }, + { + "x": 224.22156628776358, + "y": -330.60290106011655, + "z": 0.0 + }, + { + "x": 225.226499017452, + "y": -330.60300837721263, + "z": 0.0 + }, + { + "x": 226.23143174714033, + "y": -330.6031156943087, + "z": 0.0 + }, + { + "x": 227.2363644768284, + "y": -330.6032230114047, + "z": 0.0 + }, + { + "x": 228.24129720651695, + "y": -330.6033303285009, + "z": 0.0 + }, + { + "x": 229.24622993620528, + "y": -330.60343764559684, + "z": 0.0 + }, + { + "x": 230.25116266589333, + "y": -330.6035449626929, + "z": 0.0 + }, + { + "x": 231.25609539558187, + "y": -330.603652279789, + "z": 0.0 + }, + { + "x": 232.26102812527017, + "y": -330.6037595968851, + "z": 0.0 + }, + { + "x": 233.26596085495834, + "y": -330.60386691398105, + "z": 0.0 + }, + { + "x": 234.27089358464667, + "y": -330.60397423107713, + "z": 0.0 + }, + { + "x": 235.2758263143351, + "y": -330.6040815481732, + "z": 0.0 + }, + { + "x": 236.28075904402328, + "y": -330.6041888652693, + "z": 0.0 + }, + { + "x": 237.28569177371156, + "y": -330.6042961823654, + "z": 0.0 + }, + { + "x": 238.29062450339998, + "y": -330.60440349946145, + "z": 0.0 + }, + { + "x": 239.29555723308818, + "y": -330.6045108165574, + "z": 0.0 + }, + { + "x": 240.30048996277645, + "y": -330.6046181336535, + "z": 0.0 + }, + { + "x": 241.3054226924649, + "y": -330.6047254507496, + "z": 0.0 + }, + { + "x": 242.3103554221532, + "y": -330.60483276784566, + "z": 0.0 + }, + { + "x": 243.31528815184137, + "y": -330.60494008494163, + "z": 0.0 + }, + { + "x": 244.32022088152968, + "y": -330.6050474020377, + "z": 0.0 + }, + { + "x": 245.3251536112181, + "y": -330.6051547191338, + "z": 0.0 + }, + { + "x": 246.3300863409063, + "y": -330.6052620362299, + "z": 0.0 + }, + { + "x": 247.33501907059457, + "y": -330.60536935332595, + "z": 0.0 + }, + { + "x": 248.33995180028302, + "y": -330.60547667042204, + "z": 0.0 + }, + { + "x": 249.3448845299712, + "y": -330.605583987518, + "z": 0.0 + }, + { + "x": 250.3498172596595, + "y": -330.6056913046141, + "z": 0.0 + }, + { + "x": 251.3547499893479, + "y": -330.60579862171016, + "z": 0.0 + }, + { + "x": 252.3596827190361, + "y": -330.60590593880625, + "z": 0.0 + }, + { + "x": 253.3646154487245, + "y": -330.6060132559023, + "z": 0.0 + }, + { + "x": 254.36954817841269, + "y": -330.6061205729983, + "z": 0.0 + }, + { + "x": 255.37448090810102, + "y": -330.6062278900944, + "z": 0.0 + }, + { + "x": 256.3794136377894, + "y": -330.60633520719045, + "z": 0.0 + }, + { + "x": 257.38434636747763, + "y": -330.60644252428654, + "z": 0.0 + }, + { + "x": 258.38927909716597, + "y": -330.6065498413826, + "z": 0.0 + }, + { + "x": 259.3942118268542, + "y": -330.6066571584786, + "z": 0.0 + }, + { + "x": 260.3991445565425, + "y": -330.60676447557466, + "z": 0.0 + }, + { + "x": 261.40407728623086, + "y": -330.60687179267074, + "z": 0.0 + }, + { + "x": 262.4090100159191, + "y": -330.6069791097668, + "z": 0.0 + }, + { + "x": 263.4139427456075, + "y": -330.6070864268629, + "z": 0.0 + }, + { + "x": 264.41887547529575, + "y": -330.6071937439589, + "z": 0.0 + }, + { + "x": 265.42380820498397, + "y": -330.60730106105495, + "z": 0.0 + }, + { + "x": 266.4287409346724, + "y": -330.60740837815104, + "z": 0.0 + }, + { + "x": 267.43367366436064, + "y": -330.6075156952471, + "z": 0.0 + }, + { + "x": 268.43860639404886, + "y": -330.6076230123432, + "z": 0.0 + }, + { + "x": 269.4435391237373, + "y": -330.6077303294393, + "z": 0.0 + }, + { + "x": 270.44847185342553, + "y": -330.60783764653524, + "z": 0.0 + }, + { + "x": 271.45340458311387, + "y": -330.6079449636313, + "z": 0.0 + }, + { + "x": 272.4583373128022, + "y": -330.6080522807274, + "z": 0.0 + }, + { + "x": 273.4632700424904, + "y": -330.6081595978235, + "z": 0.0 + }, + { + "x": 274.46820277217887, + "y": -330.60826691491957, + "z": 0.0 + }, + { + "x": 275.473135501867, + "y": -330.60837423201554, + "z": 0.0 + }, + { + "x": 276.4780682315554, + "y": -330.6084815491116, + "z": 0.0 + }, + { + "x": 277.48300096124365, + "y": -330.6085888662077, + "z": 0.0 + }, + { + "x": 278.487933690932, + "y": -330.6086961833038, + "z": 0.0 + }, + { + "x": 279.4928664206203, + "y": -330.60880350039986, + "z": 0.0 + }, + { + "x": 280.49779915030854, + "y": -330.6089108174958, + "z": 0.0 + }, + { + "x": 281.5027318799969, + "y": -330.6090181345919, + "z": 0.0 + }, + { + "x": 282.5076646096852, + "y": -330.609125451688, + "z": 0.0 + }, + { + "x": 283.51259733937343, + "y": -330.60923276878407, + "z": 0.0 + }, + { + "x": 284.51753006906176, + "y": -330.60934008588015, + "z": 0.0 + }, + { + "x": 285.5224627987502, + "y": -330.60944740297623, + "z": 0.0 + }, + { + "x": 286.52739552843843, + "y": -330.6095547200722, + "z": 0.0 + }, + { + "x": 287.53232825812654, + "y": -330.6096620371683, + "z": 0.0 + }, + { + "x": 288.5372609878151, + "y": -330.60976935426436, + "z": 0.0 + }, + { + "x": 289.5421952094854, + "y": -330.6098766715192, + "z": 0.0 + }, + { + "x": 290.5473358457908, + "y": -330.60998325018545, + "z": 0.0 + }, + { + "x": 291.55227276326934, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 292.55720549868784, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 293.5621382341064, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 294.56707096952493, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 295.5720037049434, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 296.5769364403619, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 297.58186917578047, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 298.586801911199, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 299.5917346466175, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 300.59666738203606, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 301.6016001174546, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 302.6065328528731, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 303.6114655882916, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 304.61639832371014, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 305.6213310591287, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 306.6262637945472, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 307.63119652996573, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 308.6361292653843, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 309.6410620008028, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 310.64599473622127, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 311.6509274716398, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 312.65586020705837, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 313.66079294247686, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 314.6657256778954, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 315.67065841331396, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 316.67559114873245, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 317.68052388415094, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 318.6854566195695, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 319.69038935498804, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 320.69532209040653, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 321.7002548258251, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 322.70518756124363, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 323.7101202966621, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 324.7150530320806, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 325.71998576749917, + "y": -330.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 35, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 384.07998657059636, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 383.0414151190793, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 382.0028436675622, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 380.9642722160452, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 379.92570076452813, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 378.88712931301103, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 377.848557861494, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 376.80998640997694, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 375.77141495845984, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 374.7328435069428, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 373.69427205542576, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 372.6557006039087, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 371.6171291523916, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 370.57855770087457, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 369.5399862493575, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 368.5014147978404, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 367.4628433463234, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 366.42427189480634, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 365.38570044328924, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 364.3471289917722, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 363.30855754025515, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 362.26998608873805, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 361.231414637221, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 360.19284318570396, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 359.15427173418686, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 358.1157002826698, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 357.0771288311528, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 356.0385573796357, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 354.99998592811863, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 353.9614144766016, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 352.92284302508455, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 351.88427157356745, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 350.8457001220504, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 349.80712867053336, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 348.76855721901626, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 347.7299857674992, + "y": -324.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 36, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 347.7299857674992, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 348.76855721901626, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 349.80712867053336, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 350.8457001220504, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 351.88427157356745, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 352.92284302508455, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 353.9614144766016, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 354.99998592811863, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 356.0385573796357, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 357.0771288311528, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 358.1157002826698, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 359.15427173418686, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 360.19284318570396, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 361.231414637221, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 362.26998608873805, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 363.30855754025515, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 364.3471289917722, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 365.38570044328924, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 366.42427189480634, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 367.4628433463234, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 368.5014147978404, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 369.5399862493575, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 370.57855770087457, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 371.6171291523916, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 372.6557006039087, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 373.69427205542576, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 374.7328435069428, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 375.77141495845984, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 376.80998640997694, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 377.848557861494, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 378.88712931301103, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 379.92570076452813, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 380.9642722160452, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 382.0028436675622, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 383.0414151190793, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 384.07998657059636, + "y": -332.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 37, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 347.7299857674992, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 348.76855721901626, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 349.80712867053336, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 350.8457001220504, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 351.88427157356745, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 352.92284302508455, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 353.9614144766016, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 354.99998592811863, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 356.0385573796357, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 357.0771288311528, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 358.1157002826698, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 359.15427173418686, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 360.19284318570396, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 361.231414637221, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 362.26998608873805, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 363.30855754025515, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 364.3471289917722, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 365.38570044328924, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 366.42427189480634, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 367.4628433463234, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 368.5014147978404, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 369.5399862493575, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 370.57855770087457, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 371.6171291523916, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 372.6557006039087, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 373.69427205542576, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 374.7328435069428, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 375.77141495845984, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 376.80998640997694, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 377.848557861494, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 378.88712931301103, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 379.92570076452813, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 380.9642722160452, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 382.0028436675622, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 383.0414151190793, + "y": -328.6099853515625, + "z": 0.0 + }, + { + "x": 384.07998657059636, + "y": -328.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 38, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 384.07998657059636, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 383.0414151190793, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 382.0028436675622, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 380.9642722160452, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 379.92570076452813, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 378.88712931301103, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 377.848557861494, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 376.80998640997694, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 375.77141495845984, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 374.7328435069428, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 373.69427205542576, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 372.6557006039087, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 371.6171291523916, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 370.57855770087457, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 369.5399862493575, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 368.5014147978404, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 367.4628433463234, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 366.42427189480634, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 365.38570044328924, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 364.3471289917722, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 363.30855754025515, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 362.26998608873805, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 361.231414637221, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 360.19284318570396, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 359.15427173418686, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 358.1157002826698, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 357.0771288311528, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 356.0385573796357, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 354.99998592811863, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 353.9614144766016, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 352.92284302508455, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 351.88427157356745, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 350.8457001220504, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 349.80712867053336, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 348.76855721901626, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 347.7299857674992, + "y": -326.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 39, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 347.7299857674992, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 348.76855721901626, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 349.80712867053336, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 350.8457001220504, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 351.88427157356745, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 352.92284302508455, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 353.9614144766016, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 354.99998592811863, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 356.0385573796357, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 357.0771288311528, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 358.1157002826698, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 359.15427173418686, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 360.19284318570396, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 361.231414637221, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 362.26998608873805, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 363.30855754025515, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 364.3471289917722, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 365.38570044328924, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 366.42427189480634, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 367.4628433463234, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 368.5014147978404, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 369.5399862493575, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 370.57855770087457, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 371.6171291523916, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 372.6557006039087, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 373.69427205542576, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 374.7328435069428, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 375.77141495845984, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 376.80998640997694, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 377.848557861494, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 378.88712931301103, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 379.92570076452813, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 380.9642722160452, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 382.0028436675622, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 383.0414151190793, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 384.07998657059636, + "y": -330.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 40, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 390.380007762646, + "y": -9.845199567387848, + "z": 0.0 + }, + { + "x": 390.3788050944489, + "y": -10.847439245752769, + "z": 0.0 + }, + { + "x": 390.3776024262517, + "y": -11.849678924117207, + "z": 0.0 + }, + { + "x": 390.3763997580546, + "y": -12.85191860248258, + "z": 0.0 + }, + { + "x": 390.3751970898575, + "y": -13.854158280847502, + "z": 0.0 + }, + { + "x": 390.3739944216603, + "y": -14.856397959211929, + "z": 0.0 + }, + { + "x": 390.3727917534632, + "y": -15.8586376375773, + "z": 0.0 + }, + { + "x": 390.37158908526607, + "y": -16.860877315941995, + "z": 0.0 + }, + { + "x": 390.3703864170689, + "y": -17.86311699430688, + "z": 0.0 + }, + { + "x": 390.3691837488718, + "y": -18.865356672671986, + "z": 0.0 + }, + { + "x": 390.36798108067467, + "y": -19.86759635103667, + "z": 0.0 + }, + { + "x": 390.3667784124775, + "y": -20.86983602940159, + "z": 0.0 + }, + { + "x": 390.36557574428036, + "y": -21.872075707766566, + "z": 0.0 + }, + { + "x": 390.36437307608327, + "y": -22.87431538613169, + "z": 0.0 + }, + { + "x": 390.3631704078861, + "y": -23.876555064496372, + "z": 0.0 + }, + { + "x": 390.36196773968896, + "y": -24.87879474286126, + "z": 0.0 + }, + { + "x": 390.36076507149187, + "y": -25.88103442122636, + "z": 0.0 + }, + { + "x": 390.3595624032947, + "y": -26.883274099591002, + "z": 0.0 + }, + { + "x": 390.35835973509757, + "y": -27.885513777955907, + "z": 0.0 + }, + { + "x": 390.35715706690047, + "y": -28.887753456321025, + "z": 0.0 + }, + { + "x": 390.3559543987033, + "y": -29.889993134685668, + "z": 0.0 + }, + { + "x": 390.35475173050617, + "y": -30.89223281305054, + "z": 0.0 + }, + { + "x": 390.35354906230907, + "y": -31.89447249141564, + "z": 0.0 + }, + { + "x": 390.3523463941119, + "y": -32.89671216978028, + "z": 0.0 + }, + { + "x": 390.35114372591477, + "y": -33.89895184814515, + "z": 0.0 + }, + { + "x": 390.3499410577176, + "y": -34.90119152651005, + "z": 0.0 + }, + { + "x": 390.34873838952046, + "y": -35.90343120487495, + "z": 0.0 + }, + { + "x": 390.34753572132337, + "y": -36.90567088324007, + "z": 0.0 + }, + { + "x": 390.3463330531262, + "y": -37.907910561604744, + "z": 0.0 + }, + { + "x": 390.34513038492906, + "y": -38.91015023996968, + "z": 0.0 + }, + { + "x": 390.34392771673197, + "y": -39.912389918334775, + "z": 0.0 + }, + { + "x": 390.3427250485348, + "y": -40.91462959669942, + "z": 0.0 + }, + { + "x": 390.34152238033766, + "y": -41.91686927506429, + "z": 0.0 + }, + { + "x": 390.34031971214057, + "y": -42.919108953429394, + "z": 0.0 + }, + { + "x": 390.3391170439434, + "y": -43.92134863179405, + "z": 0.0 + }, + { + "x": 390.33791437574627, + "y": -44.923588310158955, + "z": 0.0 + }, + { + "x": 390.33671170754917, + "y": -45.92582798852408, + "z": 0.0 + }, + { + "x": 390.335509039352, + "y": -46.92806766688876, + "z": 0.0 + }, + { + "x": 390.33430637115487, + "y": -47.93030734525364, + "z": 0.0 + }, + { + "x": 390.3331037029577, + "y": -48.93254702361856, + "z": 0.0 + }, + { + "x": 390.3319010347606, + "y": -49.934786701983654, + "z": 0.0 + }, + { + "x": 390.33069836656347, + "y": -50.93702638034831, + "z": 0.0 + }, + { + "x": 390.3294956983663, + "y": -51.93926605871323, + "z": 0.0 + }, + { + "x": 390.3282930301692, + "y": -52.94150573707833, + "z": 0.0 + }, + { + "x": 390.327090361972, + "y": -53.94374541544286, + "z": 0.0 + }, + { + "x": 390.3258876937749, + "y": -54.94598509380819, + "z": 0.0 + }, + { + "x": 390.3246850255778, + "y": -55.948224772173106, + "z": 0.0 + }, + { + "x": 390.3234823573806, + "y": -56.95046445053753, + "z": 0.0 + }, + { + "x": 390.3222796891835, + "y": -57.95270412890285, + "z": 0.0 + }, + { + "x": 390.32107702098637, + "y": -58.954943807267554, + "z": 0.0 + }, + { + "x": 390.3198743527892, + "y": -59.95718348563243, + "z": 0.0 + }, + { + "x": 390.3186716845921, + "y": -60.95942316399757, + "z": 0.0 + }, + { + "x": 390.31746901639497, + "y": -61.961662842362216, + "z": 0.0 + }, + { + "x": 390.3162663481978, + "y": -62.96390252072709, + "z": 0.0 + }, + { + "x": 390.3150636800007, + "y": -63.96614219909221, + "z": 0.0 + }, + { + "x": 390.31386101180357, + "y": -64.96838187745689, + "z": 0.0 + }, + { + "x": 390.3126583436064, + "y": -65.9706215558218, + "z": 0.0 + }, + { + "x": 390.31145567540926, + "y": -66.97286123418672, + "z": 0.0 + }, + { + "x": 390.31025300721217, + "y": -67.97510091255184, + "z": 0.0 + }, + { + "x": 390.309050339015, + "y": -68.97734059091651, + "z": 0.0 + }, + { + "x": 390.30784767081786, + "y": -69.97958026928141, + "z": 0.0 + }, + { + "x": 390.30664500262077, + "y": -70.98181994764653, + "z": 0.0 + }, + { + "x": 390.3054423344236, + "y": -71.98405962601122, + "z": 0.0 + }, + { + "x": 390.30423966622647, + "y": -72.9862993043761, + "z": 0.0 + }, + { + "x": 390.30303699802937, + "y": -73.98853898274126, + "z": 0.0 + }, + { + "x": 390.3018343298322, + "y": -74.99077866110594, + "z": 0.0 + }, + { + "x": 390.30063166163507, + "y": -75.99301833947084, + "z": 0.0 + }, + { + "x": 390.2994289934379, + "y": -76.99525801783574, + "z": 0.0 + }, + { + "x": 390.29822632524076, + "y": -77.99749769620063, + "z": 0.0 + }, + { + "x": 390.29702365704367, + "y": -78.99973737456575, + "z": 0.0 + }, + { + "x": 390.2958209888465, + "y": -80.00197705293043, + "z": 0.0 + }, + { + "x": 390.29461832064936, + "y": -81.00421673129533, + "z": 0.0 + }, + { + "x": 390.29341565245227, + "y": -82.00645640966046, + "z": 0.0 + }, + { + "x": 390.2922129842551, + "y": -83.00869608802516, + "z": 0.0 + }, + { + "x": 390.29101031605796, + "y": -84.01093576639005, + "z": 0.0 + }, + { + "x": 390.28980764786087, + "y": -85.01317544475518, + "z": 0.0 + }, + { + "x": 390.2886049796637, + "y": -86.01541512311985, + "z": 0.0 + }, + { + "x": 390.2876623789813, + "y": -87.01869741734455, + "z": 0.0 + }, + { + "x": 390.2876355630676, + "y": -88.0245953007731, + "z": 0.0 + }, + { + "x": 390.28784715222, + "y": -89.02777977775638, + "z": 0.0 + }, + { + "x": 390.28805865355594, + "y": -90.03002015539369, + "z": 0.0 + }, + { + "x": 390.28827015489185, + "y": -91.03226053303119, + "z": 0.0 + }, + { + "x": 390.28848165622776, + "y": -92.0345009106687, + "z": 0.0 + }, + { + "x": 390.2886931575636, + "y": -93.03674128830598, + "z": 0.0 + }, + { + "x": 390.2889046588996, + "y": -94.03898166594394, + "z": 0.0 + }, + { + "x": 390.2891161602355, + "y": -95.04122204358121, + "z": 0.0 + }, + { + "x": 390.2893276615714, + "y": -96.04346242121872, + "z": 0.0 + }, + { + "x": 390.28953916290726, + "y": -97.045702798856, + "z": 0.0 + }, + { + "x": 390.28975066424323, + "y": -98.04794317649396, + "z": 0.0 + }, + { + "x": 390.28996216557914, + "y": -99.05018355413127, + "z": 0.0 + }, + { + "x": 390.29017366691505, + "y": -100.05242393176877, + "z": 0.0 + }, + { + "x": 390.29038516825096, + "y": -101.05466430940628, + "z": 0.0 + }, + { + "x": 390.2905966695868, + "y": -102.05690468704356, + "z": 0.0 + }, + { + "x": 390.2908081709228, + "y": -103.05914506468152, + "z": 0.0 + }, + { + "x": 390.2910196722587, + "y": -104.06138544231878, + "z": 0.0 + }, + { + "x": 390.2912311735946, + "y": -105.06362581995629, + "z": 0.0 + }, + { + "x": 390.29144267493047, + "y": -106.06586619759359, + "z": 0.0 + }, + { + "x": 390.2916541762664, + "y": -107.06810657523133, + "z": 0.0 + }, + { + "x": 390.2918656776023, + "y": -108.07034695286883, + "z": 0.0 + }, + { + "x": 390.29207717893826, + "y": -109.07258733050656, + "z": 0.0 + }, + { + "x": 390.2922886802741, + "y": -110.07482770814362, + "z": 0.0 + }, + { + "x": 390.29250018161, + "y": -111.07706808578135, + "z": 0.0 + }, + { + "x": 390.29271168294594, + "y": -112.07930846341885, + "z": 0.0 + }, + { + "x": 390.2929231842819, + "y": -113.08154884105659, + "z": 0.0 + }, + { + "x": 390.2931346856178, + "y": -114.08378921869385, + "z": 0.0 + }, + { + "x": 390.29334618695367, + "y": -115.08602959633114, + "z": 0.0 + }, + { + "x": 390.2935576882896, + "y": -116.08826997396886, + "z": 0.0 + }, + { + "x": 390.2937691896255, + "y": -117.09051035160635, + "z": 0.0 + }, + { + "x": 390.29398069096146, + "y": -118.09275072924407, + "z": 0.0 + }, + { + "x": 390.2941921922973, + "y": -119.09499110688111, + "z": 0.0 + }, + { + "x": 390.2944036936332, + "y": -120.09723148451884, + "z": 0.0 + }, + { + "x": 390.29461519496914, + "y": -121.09947186215635, + "z": 0.0 + }, + { + "x": 390.29482669630505, + "y": -122.10171223979387, + "z": 0.0 + }, + { + "x": 390.295038197641, + "y": -123.1039526174316, + "z": 0.0 + }, + { + "x": 390.2952496989769, + "y": -124.10619299506864, + "z": 0.0 + }, + { + "x": 390.2954612003128, + "y": -125.10843337270637, + "z": 0.0 + }, + { + "x": 390.2956727016487, + "y": -126.11067375034386, + "z": 0.0 + }, + { + "x": 390.29588420298467, + "y": -127.11291412798158, + "z": 0.0 + }, + { + "x": 390.2960957043205, + "y": -128.11515450561862, + "z": 0.0 + }, + { + "x": 390.29630720565643, + "y": -129.11739488325634, + "z": 0.0 + }, + { + "x": 390.29651870699234, + "y": -130.11963526089386, + "z": 0.0 + }, + { + "x": 390.29673020832826, + "y": -131.12187563853138, + "z": 0.0 + }, + { + "x": 390.29694170966417, + "y": -132.1241160161689, + "z": 0.0 + }, + { + "x": 390.2971532110001, + "y": -133.1263563938064, + "z": 0.0 + }, + { + "x": 390.297364712336, + "y": -134.1285967714439, + "z": 0.0 + }, + { + "x": 390.2975762136719, + "y": -135.1308371490814, + "z": 0.0 + }, + { + "x": 390.29778771500787, + "y": -136.13307752671915, + "z": 0.0 + }, + { + "x": 390.2979992163437, + "y": -137.13531790435619, + "z": 0.0 + }, + { + "x": 390.29821071767964, + "y": -138.13755828199396, + "z": 0.0 + }, + { + "x": 390.29842221901555, + "y": -139.13979865963145, + "z": 0.0 + }, + { + "x": 390.29863372035146, + "y": -140.14203903726897, + "z": 0.0 + }, + { + "x": 390.2988452216874, + "y": -141.14427941490646, + "z": 0.0 + }, + { + "x": 390.2990567230233, + "y": -142.14651979254398, + "z": 0.0 + }, + { + "x": 390.2992682243592, + "y": -143.14876017018148, + "z": 0.0 + }, + { + "x": 390.2994797256951, + "y": -144.151000547819, + "z": 0.0 + }, + { + "x": 390.299691227031, + "y": -145.1532409254565, + "z": 0.0 + }, + { + "x": 390.29990272836693, + "y": -146.155481303094, + "z": 0.0 + }, + { + "x": 390.30011422970284, + "y": -147.15772168073153, + "z": 0.0 + }, + { + "x": 390.30032573103875, + "y": -148.15996205836905, + "z": 0.0 + }, + { + "x": 390.30053723237467, + "y": -149.16220243600654, + "z": 0.0 + }, + { + "x": 390.3007487337106, + "y": -150.16444281364403, + "z": 0.0 + }, + { + "x": 390.3009602350465, + "y": -151.16668319128155, + "z": 0.0 + }, + { + "x": 390.3011717363824, + "y": -152.16892356891904, + "z": 0.0 + }, + { + "x": 390.3013832377183, + "y": -153.17116394655656, + "z": 0.0 + }, + { + "x": 390.30159473905417, + "y": -154.17340432419385, + "z": 0.0 + }, + { + "x": 390.30180624039014, + "y": -155.17564470183183, + "z": 0.0 + }, + { + "x": 390.30201774172605, + "y": -156.1778850794691, + "z": 0.0 + }, + { + "x": 390.30222924306196, + "y": -157.1801254571066, + "z": 0.0 + }, + { + "x": 390.30244074439787, + "y": -158.1823658347441, + "z": 0.0 + }, + { + "x": 390.3026522457337, + "y": -159.1846062123814, + "z": 0.0 + }, + { + "x": 390.3028637470697, + "y": -160.18684659001934, + "z": 0.0 + }, + { + "x": 390.3030752484056, + "y": -161.18908696765664, + "z": 0.0 + }, + { + "x": 390.3032867497415, + "y": -162.19132734529413, + "z": 0.0 + }, + { + "x": 390.3034982510774, + "y": -163.19356772293145, + "z": 0.0 + }, + { + "x": 390.30370975241334, + "y": -164.1958081005694, + "z": 0.0 + }, + { + "x": 390.30392125374925, + "y": -165.1980484782067, + "z": 0.0 + }, + { + "x": 390.30413275508516, + "y": -166.20028885584418, + "z": 0.0 + }, + { + "x": 390.3043442564211, + "y": -167.2025292334817, + "z": 0.0 + }, + { + "x": 390.30455575775693, + "y": -168.20476961111896, + "z": 0.0 + }, + { + "x": 390.3047672590929, + "y": -169.20700998875694, + "z": 0.0 + }, + { + "x": 390.3049787604288, + "y": -170.20925036639423, + "z": 0.0 + }, + { + "x": 390.3051902617647, + "y": -171.21149074403175, + "z": 0.0 + }, + { + "x": 390.3054017631006, + "y": -172.213731121669, + "z": 0.0 + }, + { + "x": 390.30561326443654, + "y": -173.215971499307, + "z": 0.0 + }, + { + "x": 390.30582476577246, + "y": -174.21821187694425, + "z": 0.0 + }, + { + "x": 390.30603626710837, + "y": -175.22045225458177, + "z": 0.0 + }, + { + "x": 390.3062477684443, + "y": -176.22269263221926, + "z": 0.0 + }, + { + "x": 390.30645926978013, + "y": -177.22493300985656, + "z": 0.0 + }, + { + "x": 390.3066707711161, + "y": -178.2271733874945, + "z": 0.0 + }, + { + "x": 390.306882272452, + "y": -179.22941376513182, + "z": 0.0 + }, + { + "x": 390.3070937737879, + "y": -180.23165414276932, + "z": 0.0 + }, + { + "x": 390.3073052751238, + "y": -181.23389452040658, + "z": 0.0 + }, + { + "x": 390.3075167764597, + "y": -182.23613489804433, + "z": 0.0 + }, + { + "x": 390.30772827779566, + "y": -183.23837527568205, + "z": 0.0 + }, + { + "x": 390.3079397791316, + "y": -184.24061565331934, + "z": 0.0 + }, + { + "x": 390.3081512804674, + "y": -185.2428560309566, + "z": 0.0 + }, + { + "x": 390.30836278180334, + "y": -186.24509640859438, + "z": 0.0 + }, + { + "x": 390.3085742831393, + "y": -187.2473367862321, + "z": 0.0 + }, + { + "x": 390.3087857844752, + "y": -188.2495771638694, + "z": 0.0 + }, + { + "x": 390.30899728581113, + "y": -189.25181754150688, + "z": 0.0 + }, + { + "x": 390.309208787147, + "y": -190.25405791914417, + "z": 0.0 + }, + { + "x": 390.3094202884829, + "y": -191.2562982967819, + "z": 0.0 + }, + { + "x": 390.3096317898188, + "y": -192.25853867441938, + "z": 0.0 + }, + { + "x": 390.3098432911548, + "y": -193.2607790520571, + "z": 0.0 + }, + { + "x": 390.31005479249063, + "y": -194.26301942969414, + "z": 0.0 + }, + { + "x": 390.31026629382654, + "y": -195.26525980733186, + "z": 0.0 + }, + { + "x": 390.31047779516246, + "y": -196.26750018496938, + "z": 0.0 + }, + { + "x": 390.31068929649837, + "y": -197.26974056260687, + "z": 0.0 + }, + { + "x": 390.31090079783434, + "y": -198.2719809402446, + "z": 0.0 + }, + { + "x": 390.3111122991702, + "y": -199.27422131788163, + "z": 0.0 + }, + { + "x": 390.3113238005061, + "y": -200.27646169551934, + "z": 0.0 + }, + { + "x": 390.311535301842, + "y": -201.27870207315684, + "z": 0.0 + }, + { + "x": 390.311746803178, + "y": -202.28094245079456, + "z": 0.0 + }, + { + "x": 390.31195830451384, + "y": -203.2831828284316, + "z": 0.0 + }, + { + "x": 390.31216980584975, + "y": -204.28542320606934, + "z": 0.0 + }, + { + "x": 390.31238130718566, + "y": -205.28766358370683, + "z": 0.0 + }, + { + "x": 390.3125928085216, + "y": -206.28990396134432, + "z": 0.0 + }, + { + "x": 390.3128043098575, + "y": -207.2921443389818, + "z": 0.0 + }, + { + "x": 390.3130158111934, + "y": -208.2943847166193, + "z": 0.0 + }, + { + "x": 390.3132273125293, + "y": -209.2966250942568, + "z": 0.0 + }, + { + "x": 390.3134388138652, + "y": -210.2988654718943, + "z": 0.0 + }, + { + "x": 390.3136503152012, + "y": -211.301105849532, + "z": 0.0 + }, + { + "x": 390.31386181653704, + "y": -212.30334622716907, + "z": 0.0 + }, + { + "x": 390.31407331787295, + "y": -213.3055866048068, + "z": 0.0 + }, + { + "x": 390.31428481920886, + "y": -214.30782698244428, + "z": 0.0 + }, + { + "x": 390.3144963205448, + "y": -215.31006736008177, + "z": 0.0 + }, + { + "x": 390.3147078218807, + "y": -216.31230773771927, + "z": 0.0 + }, + { + "x": 390.3149193232166, + "y": -217.31454811535676, + "z": 0.0 + }, + { + "x": 390.3151308245525, + "y": -218.31678849299425, + "z": 0.0 + }, + { + "x": 390.3153423258884, + "y": -219.31902887063174, + "z": 0.0 + }, + { + "x": 390.31555382722433, + "y": -220.32126924826926, + "z": 0.0 + }, + { + "x": 390.31576532856025, + "y": -221.32350962590675, + "z": 0.0 + }, + { + "x": 390.31597682989616, + "y": -222.32575000354424, + "z": 0.0 + }, + { + "x": 390.31618833123207, + "y": -223.32799038118173, + "z": 0.0 + }, + { + "x": 390.316399832568, + "y": -224.33023075881923, + "z": 0.0 + }, + { + "x": 390.3166113339039, + "y": -225.33247113645672, + "z": 0.0 + }, + { + "x": 390.3168228352398, + "y": -226.3347115140942, + "z": 0.0 + }, + { + "x": 390.3170343365757, + "y": -227.3369518917317, + "z": 0.0 + }, + { + "x": 390.3172458379116, + "y": -228.33919226936922, + "z": 0.0 + }, + { + "x": 390.31745733924754, + "y": -229.3414326470067, + "z": 0.0 + }, + { + "x": 390.31766884058345, + "y": -230.3436730246442, + "z": 0.0 + }, + { + "x": 390.31788034191936, + "y": -231.3459134022817, + "z": 0.0 + }, + { + "x": 390.3180918432553, + "y": -232.3481537799192, + "z": 0.0 + }, + { + "x": 390.3183033445912, + "y": -233.35039415755668, + "z": 0.0 + }, + { + "x": 390.3185148459271, + "y": -234.35263453519417, + "z": 0.0 + }, + { + "x": 390.318726347263, + "y": -235.35487491283166, + "z": 0.0 + }, + { + "x": 390.3189378485989, + "y": -236.35711529046915, + "z": 0.0 + }, + { + "x": 390.31914934993483, + "y": -237.35935566810664, + "z": 0.0 + }, + { + "x": 390.31936085127074, + "y": -238.36159604574414, + "z": 0.0 + }, + { + "x": 390.31957235260666, + "y": -239.36383642338163, + "z": 0.0 + }, + { + "x": 390.31978385394257, + "y": -240.36607680101912, + "z": 0.0 + }, + { + "x": 390.3200109086524, + "y": -241.36837919461823, + "z": 0.0 + }, + { + "x": 390.32040065747765, + "y": -242.3712680984976, + "z": 0.0 + }, + { + "x": 390.3207902067958, + "y": -243.37350842274657, + "z": 0.0 + }, + { + "x": 390.3211797561139, + "y": -244.37574874699575, + "z": 0.0 + }, + { + "x": 390.3215693054321, + "y": -245.37798907124514, + "z": 0.0 + }, + { + "x": 390.3219588547503, + "y": -246.38022939549433, + "z": 0.0 + }, + { + "x": 390.3223484040684, + "y": -247.38246971974326, + "z": 0.0 + }, + { + "x": 390.3227379533866, + "y": -248.38471004399267, + "z": 0.0 + }, + { + "x": 390.3231275027047, + "y": -249.38695036824163, + "z": 0.0 + }, + { + "x": 390.32351705202285, + "y": -250.38919069249079, + "z": 0.0 + }, + { + "x": 390.32390660134104, + "y": -251.3914310167402, + "z": 0.0 + }, + { + "x": 390.3242961506592, + "y": -252.39367134098939, + "z": 0.0 + }, + { + "x": 390.32468569997735, + "y": -253.39591166523832, + "z": 0.0 + }, + { + "x": 390.32507524929554, + "y": -254.39815198948773, + "z": 0.0 + }, + { + "x": 390.32546479861367, + "y": -255.40039231373666, + "z": 0.0 + }, + { + "x": 390.3258543479318, + "y": -256.40263263798585, + "z": 0.0 + }, + { + "x": 390.32624389725, + "y": -257.4048729622353, + "z": 0.0 + }, + { + "x": 390.32663344656817, + "y": -258.40711328648445, + "z": 0.0 + }, + { + "x": 390.3270229958863, + "y": -259.4093536107334, + "z": 0.0 + }, + { + "x": 390.3274125452044, + "y": -260.41159393498253, + "z": 0.0 + }, + { + "x": 390.3278020945226, + "y": -261.413834259232, + "z": 0.0 + }, + { + "x": 390.3281916438408, + "y": -262.41607458348113, + "z": 0.0 + }, + { + "x": 390.3285811931589, + "y": -263.41831490773006, + "z": 0.0 + }, + { + "x": 390.3289707424771, + "y": -264.4205552319795, + "z": 0.0 + }, + { + "x": 390.3293602917953, + "y": -265.42279555622866, + "z": 0.0 + }, + { + "x": 390.3297498411134, + "y": -266.4250358804776, + "z": 0.0 + }, + { + "x": 390.33013939043155, + "y": -267.4272762047268, + "z": 0.0 + }, + { + "x": 390.33052893974974, + "y": -268.4295165289762, + "z": 0.0 + }, + { + "x": 390.33091848906787, + "y": -269.4317568532251, + "z": 0.0 + }, + { + "x": 390.33130803838606, + "y": -270.43399717747457, + "z": 0.0 + }, + { + "x": 390.3316975877042, + "y": -271.4362375017235, + "z": 0.0 + }, + { + "x": 390.3320871370223, + "y": -272.43847782597265, + "z": 0.0 + }, + { + "x": 390.3324766863405, + "y": -273.4407181502221, + "z": 0.0 + }, + { + "x": 390.3328662356587, + "y": -274.44295847447125, + "z": 0.0 + }, + { + "x": 390.3332557849768, + "y": -275.4451987987202, + "z": 0.0 + }, + { + "x": 390.333645334295, + "y": -276.4474391229696, + "z": 0.0 + }, + { + "x": 390.3340348836131, + "y": -277.44967944721856, + "z": 0.0 + }, + { + "x": 390.3344244329313, + "y": -278.45191977146794, + "z": 0.0 + }, + { + "x": 390.33481398224944, + "y": -279.4541600957169, + "z": 0.0 + }, + { + "x": 390.33520353156763, + "y": -280.4564004199663, + "z": 0.0 + }, + { + "x": 390.3355930808858, + "y": -281.4586407442155, + "z": 0.0 + }, + { + "x": 390.33598263020394, + "y": -282.46088106846446, + "z": 0.0 + }, + { + "x": 390.3363721795221, + "y": -283.4631213927136, + "z": 0.0 + }, + { + "x": 390.33676172884026, + "y": -284.465361716963, + "z": 0.0 + }, + { + "x": 390.3371512781584, + "y": -285.467602041212, + "z": 0.0 + }, + { + "x": 390.3375408274766, + "y": -286.4698423654614, + "z": 0.0 + }, + { + "x": 390.33793037679476, + "y": -287.47208268971053, + "z": 0.0 + }, + { + "x": 390.3383199261129, + "y": -288.4743230139595, + "z": 0.0 + }, + { + "x": 390.338709475431, + "y": -289.4765633382087, + "z": 0.0 + }, + { + "x": 390.3390990247492, + "y": -290.47880366245806, + "z": 0.0 + }, + { + "x": 390.33948857406733, + "y": -291.48104398670705, + "z": 0.0 + }, + { + "x": 390.3398781233855, + "y": -292.48328431095644, + "z": 0.0 + }, + { + "x": 390.34026767270365, + "y": -293.4855246352054, + "z": 0.0 + }, + { + "x": 390.34065722202183, + "y": -294.4877649594548, + "z": 0.0 + }, + { + "x": 390.34104677133996, + "y": -295.49000528370374, + "z": 0.0 + }, + { + "x": 390.34143632065815, + "y": -296.4922456079532, + "z": 0.0 + }, + { + "x": 390.34182586997633, + "y": -297.49448593220234, + "z": 0.0 + }, + { + "x": 390.34221541929446, + "y": -298.49672625645127, + "z": 0.0 + }, + { + "x": 390.3426049686126, + "y": -299.4989665807005, + "z": 0.0 + }, + { + "x": 390.3429945179308, + "y": -300.50120690494987, + "z": 0.0 + }, + { + "x": 390.3433840672489, + "y": -301.50344722919885, + "z": 0.0 + }, + { + "x": 390.3437736165671, + "y": -302.50568755344824, + "z": 0.0 + }, + { + "x": 390.3441631658853, + "y": -303.5079278776974, + "z": 0.0 + }, + { + "x": 390.3445527152034, + "y": -304.5101682019464, + "z": 0.0 + }, + { + "x": 390.34494226452153, + "y": -305.51240852619554, + "z": 0.0 + }, + { + "x": 390.3453318138397, + "y": -306.5146488504449, + "z": 0.0 + }, + { + "x": 390.34572136315785, + "y": -307.5168891746939, + "z": 0.0 + }, + { + "x": 390.34611091247604, + "y": -308.5191294989433, + "z": 0.0 + }, + { + "x": 390.3465004617942, + "y": -309.5213698231925, + "z": 0.0 + }, + { + "x": 390.34689001111235, + "y": -310.52361014744145, + "z": 0.0 + }, + { + "x": 390.3472795604305, + "y": -311.5258504716906, + "z": 0.0 + }, + { + "x": 390.34766910974867, + "y": -312.52809079594005, + "z": 0.0 + }, + { + "x": 390.34805865906685, + "y": -313.5303311201892, + "z": 0.0 + }, + { + "x": 390.348448208385, + "y": -314.5325714444382, + "z": 0.0 + }, + { + "x": 390.34883775770317, + "y": -315.5348117686876, + "z": 0.0 + }, + { + "x": 390.3492273070213, + "y": -316.5370520929365, + "z": 0.0 + }, + { + "x": 390.3496168563394, + "y": -317.5392924171857, + "z": 0.0 + }, + { + "x": 390.3500064056576, + "y": -318.5415327414351, + "z": 0.0 + } + ] + }, + { + "id": 41, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 398.35000580137364, + "y": -318.5384233132524, + "z": 0.0 + }, + { + "x": 398.34961625205545, + "y": -317.53618298900346, + "z": 0.0 + }, + { + "x": 398.3492267027373, + "y": -316.53394266475425, + "z": 0.0 + }, + { + "x": 398.3488371534192, + "y": -315.53170234050486, + "z": 0.0 + }, + { + "x": 398.348447604101, + "y": -314.52946201625593, + "z": 0.0 + }, + { + "x": 398.3480580547829, + "y": -313.5272216920065, + "z": 0.0 + }, + { + "x": 398.3476685054647, + "y": -312.52498136775733, + "z": 0.0 + }, + { + "x": 398.3472789561465, + "y": -311.52274104350835, + "z": 0.0 + }, + { + "x": 398.3468894068284, + "y": -310.5205007192592, + "z": 0.0 + }, + { + "x": 398.34649985751025, + "y": -309.5182603950098, + "z": 0.0 + }, + { + "x": 398.34611030819207, + "y": -308.5160200707606, + "z": 0.0 + }, + { + "x": 398.3457207588739, + "y": -307.51377974651166, + "z": 0.0 + }, + { + "x": 398.34533120955575, + "y": -306.5115394222622, + "z": 0.0 + }, + { + "x": 398.34494166023757, + "y": -305.5092990980133, + "z": 0.0 + }, + { + "x": 398.34455211091944, + "y": -304.50705877376413, + "z": 0.0 + }, + { + "x": 398.3441625616013, + "y": -303.5048184495147, + "z": 0.0 + }, + { + "x": 398.3437730122831, + "y": -302.50257812526553, + "z": 0.0 + }, + { + "x": 398.34338346296494, + "y": -301.5003378010166, + "z": 0.0 + }, + { + "x": 398.3429939136468, + "y": -300.49809747676716, + "z": 0.0 + }, + { + "x": 398.3426043643286, + "y": -299.4958571525182, + "z": 0.0 + }, + { + "x": 398.3422148150105, + "y": -298.493616828269, + "z": 0.0 + }, + { + "x": 398.34182526569236, + "y": -297.4913765040196, + "z": 0.0 + }, + { + "x": 398.3414357163742, + "y": -296.48913617977047, + "z": 0.0 + }, + { + "x": 398.341046167056, + "y": -295.4868958555215, + "z": 0.0 + }, + { + "x": 398.34065661773786, + "y": -294.4846555312721, + "z": 0.0 + }, + { + "x": 398.3402670684197, + "y": -293.48241520702317, + "z": 0.0 + }, + { + "x": 398.33987751910155, + "y": -292.4801748827737, + "z": 0.0 + }, + { + "x": 398.33948796978336, + "y": -291.4779345585248, + "z": 0.0 + }, + { + "x": 398.33909842046523, + "y": -290.47569423427535, + "z": 0.0 + }, + { + "x": 398.33870887114705, + "y": -289.4734539100264, + "z": 0.0 + }, + { + "x": 398.3383193218289, + "y": -288.47121358577726, + "z": 0.0 + }, + { + "x": 398.3379297725108, + "y": -287.4689732615278, + "z": 0.0 + }, + { + "x": 398.3375402231926, + "y": -286.46673293727866, + "z": 0.0 + }, + { + "x": 398.3371506738744, + "y": -285.46449261302973, + "z": 0.0 + }, + { + "x": 398.3367611245563, + "y": -284.4622522887803, + "z": 0.0 + }, + { + "x": 398.3363715752381, + "y": -283.46001196453136, + "z": 0.0 + }, + { + "x": 398.33598202592, + "y": -282.4577716402822, + "z": 0.0 + }, + { + "x": 398.33559247660185, + "y": -281.45553131603276, + "z": 0.0 + }, + { + "x": 398.33520292728366, + "y": -280.4532909917836, + "z": 0.0 + }, + { + "x": 398.3348133779655, + "y": -279.4510506675346, + "z": 0.0 + }, + { + "x": 398.33442382864735, + "y": -278.44881034328523, + "z": 0.0 + }, + { + "x": 398.33403427932916, + "y": -277.4465700190363, + "z": 0.0 + }, + { + "x": 398.33364473001103, + "y": -276.4443296947869, + "z": 0.0 + }, + { + "x": 398.33325518069284, + "y": -275.44208937053793, + "z": 0.0 + }, + { + "x": 398.3328656313747, + "y": -274.43984904628854, + "z": 0.0 + }, + { + "x": 398.33247608205653, + "y": -273.4376087220394, + "z": 0.0 + }, + { + "x": 398.33208653273834, + "y": -272.4353683977904, + "z": 0.0 + }, + { + "x": 398.3316969834202, + "y": -271.43312807354124, + "z": 0.0 + }, + { + "x": 398.3313074341021, + "y": -270.43088774929186, + "z": 0.0 + }, + { + "x": 398.3309178847839, + "y": -269.42864742504287, + "z": 0.0 + }, + { + "x": 398.3305283354658, + "y": -268.4264071007935, + "z": 0.0 + }, + { + "x": 398.3301387861476, + "y": -267.42416677654455, + "z": 0.0 + }, + { + "x": 398.32974923682946, + "y": -266.42192645229534, + "z": 0.0 + }, + { + "x": 398.3293596875113, + "y": -265.41968612804595, + "z": 0.0 + }, + { + "x": 398.32897013819314, + "y": -264.4174458037968, + "z": 0.0 + }, + { + "x": 398.32858058887496, + "y": -263.4152054795478, + "z": 0.0 + }, + { + "x": 398.3281910395568, + "y": -262.4129651552984, + "z": 0.0 + }, + { + "x": 398.32780149023864, + "y": -261.41072483104927, + "z": 0.0 + }, + { + "x": 398.32741194092046, + "y": -260.4084845068003, + "z": 0.0 + }, + { + "x": 398.3270223916023, + "y": -259.4062441825511, + "z": 0.0 + }, + { + "x": 398.3266328422842, + "y": -258.40400385830173, + "z": 0.0 + }, + { + "x": 398.326243292966, + "y": -257.4017635340526, + "z": 0.0 + }, + { + "x": 398.3258537436478, + "y": -256.3995232098036, + "z": 0.0 + }, + { + "x": 398.3254641943297, + "y": -255.3972828855544, + "z": 0.0 + }, + { + "x": 398.32507464501157, + "y": -254.39504256130502, + "z": 0.0 + }, + { + "x": 398.3246850956934, + "y": -253.39280223705606, + "z": 0.0 + }, + { + "x": 398.32429554637525, + "y": -252.39056191280667, + "z": 0.0 + }, + { + "x": 398.32390599705707, + "y": -251.3883215885575, + "z": 0.0 + }, + { + "x": 398.3235164477389, + "y": -250.38608126430853, + "z": 0.0 + }, + { + "x": 398.32312689842075, + "y": -249.38384094005937, + "z": 0.0 + }, + { + "x": 398.3227373491026, + "y": -248.38160061580996, + "z": 0.0 + }, + { + "x": 398.32234779978444, + "y": -247.379360291561, + "z": 0.0 + }, + { + "x": 398.3219582504663, + "y": -246.37711996731161, + "z": 0.0 + }, + { + "x": 398.3215687011481, + "y": -245.37487964306243, + "z": 0.0 + }, + { + "x": 398.32117915182994, + "y": -244.3726393188135, + "z": 0.0 + }, + { + "x": 398.3207896025118, + "y": -243.3703989945643, + "z": 0.0 + }, + { + "x": 398.3204000531937, + "y": -242.3681586703149, + "z": 0.0 + }, + { + "x": 398.32001070338254, + "y": -241.3665669256959, + "z": 0.0 + }, + { + "x": 398.3197836758104, + "y": -240.36438857263863, + "z": 0.0 + }, + { + "x": 398.3195721744745, + "y": -239.36214819500114, + "z": 0.0 + }, + { + "x": 398.3193606731386, + "y": -238.35990781736365, + "z": 0.0 + }, + { + "x": 398.3191491718027, + "y": -237.35766743972616, + "z": 0.0 + }, + { + "x": 398.31893767046677, + "y": -236.35542706208867, + "z": 0.0 + }, + { + "x": 398.31872616913085, + "y": -235.35318668445117, + "z": 0.0 + }, + { + "x": 398.31851466779494, + "y": -234.35094630681368, + "z": 0.0 + }, + { + "x": 398.31830316645903, + "y": -233.3487059291762, + "z": 0.0 + }, + { + "x": 398.3180916651231, + "y": -232.3464655515387, + "z": 0.0 + }, + { + "x": 398.3178801637872, + "y": -231.3442251739012, + "z": 0.0 + }, + { + "x": 398.3176686624513, + "y": -230.34198479626372, + "z": 0.0 + }, + { + "x": 398.3174571611154, + "y": -229.33974441862622, + "z": 0.0 + }, + { + "x": 398.3172456597795, + "y": -228.33750404098873, + "z": 0.0 + }, + { + "x": 398.31703415844356, + "y": -227.3352636633512, + "z": 0.0 + }, + { + "x": 398.31682265710765, + "y": -226.33302328571372, + "z": 0.0 + }, + { + "x": 398.31661115577174, + "y": -225.33078290807623, + "z": 0.0 + }, + { + "x": 398.3163996544358, + "y": -224.32854253043874, + "z": 0.0 + }, + { + "x": 398.3161881530999, + "y": -223.32630215280125, + "z": 0.0 + }, + { + "x": 398.315976651764, + "y": -222.32406177516376, + "z": 0.0 + }, + { + "x": 398.3157651504281, + "y": -221.32182139752626, + "z": 0.0 + }, + { + "x": 398.3155536490922, + "y": -220.31958101988877, + "z": 0.0 + }, + { + "x": 398.31534214775627, + "y": -219.31734064225125, + "z": 0.0 + }, + { + "x": 398.31513064642036, + "y": -218.31510026461376, + "z": 0.0 + }, + { + "x": 398.31491914508445, + "y": -217.31285988697627, + "z": 0.0 + }, + { + "x": 398.31470764374853, + "y": -216.31061950933878, + "z": 0.0 + }, + { + "x": 398.3144961424126, + "y": -215.3083791317013, + "z": 0.0 + }, + { + "x": 398.3142846410767, + "y": -214.3061387540638, + "z": 0.0 + }, + { + "x": 398.3140731397408, + "y": -213.3038983764263, + "z": 0.0 + }, + { + "x": 398.3138616384049, + "y": -212.30165799878904, + "z": 0.0 + }, + { + "x": 398.31365013706903, + "y": -211.29941762115106, + "z": 0.0 + }, + { + "x": 398.31343863573306, + "y": -210.2971772435138, + "z": 0.0 + }, + { + "x": 398.31322713439715, + "y": -209.2949368658763, + "z": 0.0 + }, + { + "x": 398.31301563306124, + "y": -208.29269648823882, + "z": 0.0 + }, + { + "x": 398.31280413172533, + "y": -207.29045611060133, + "z": 0.0 + }, + { + "x": 398.3125926303894, + "y": -206.28821573296383, + "z": 0.0 + }, + { + "x": 398.3123811290535, + "y": -205.28597535532634, + "z": 0.0 + }, + { + "x": 398.3121696277176, + "y": -204.28373497768885, + "z": 0.0 + }, + { + "x": 398.3119581263817, + "y": -203.28149460005156, + "z": 0.0 + }, + { + "x": 398.3117466250458, + "y": -202.2792542224136, + "z": 0.0 + }, + { + "x": 398.31153512370986, + "y": -201.27701384477635, + "z": 0.0 + }, + { + "x": 398.31132362237395, + "y": -200.27477346713886, + "z": 0.0 + }, + { + "x": 398.31111212103804, + "y": -199.2725330895016, + "z": 0.0 + }, + { + "x": 398.3109006197022, + "y": -198.27029271186365, + "z": 0.0 + }, + { + "x": 398.3106891183662, + "y": -197.26805233422638, + "z": 0.0 + }, + { + "x": 398.3104776170303, + "y": -196.2658119565889, + "z": 0.0 + }, + { + "x": 398.3102661156944, + "y": -195.26357157895137, + "z": 0.0 + }, + { + "x": 398.3100546143585, + "y": -194.2613312013141, + "z": 0.0 + }, + { + "x": 398.3098431130226, + "y": -193.25909082367616, + "z": 0.0 + }, + { + "x": 398.30963161168665, + "y": -192.2568504460389, + "z": 0.0 + }, + { + "x": 398.30942011035074, + "y": -191.2546100684014, + "z": 0.0 + }, + { + "x": 398.30920860901483, + "y": -190.25236969076414, + "z": 0.0 + }, + { + "x": 398.308997107679, + "y": -189.2501293131264, + "z": 0.0 + }, + { + "x": 398.30878560634306, + "y": -188.2478889354889, + "z": 0.0 + }, + { + "x": 398.30857410500715, + "y": -187.24564855785115, + "z": 0.0 + }, + { + "x": 398.3083626036712, + "y": -186.2434081802139, + "z": 0.0 + }, + { + "x": 398.3081511023353, + "y": -185.24116780257657, + "z": 0.0 + }, + { + "x": 398.3079396009994, + "y": -184.23892742493885, + "z": 0.0 + }, + { + "x": 398.3077280996635, + "y": -183.2366870473011, + "z": 0.0 + }, + { + "x": 398.30751659832754, + "y": -182.23444666966384, + "z": 0.0 + }, + { + "x": 398.3073050969916, + "y": -181.23220629202655, + "z": 0.0 + }, + { + "x": 398.3070935956558, + "y": -180.22996591438883, + "z": 0.0 + }, + { + "x": 398.30688209431986, + "y": -179.22772553675134, + "z": 0.0 + }, + { + "x": 398.30667059298395, + "y": -178.22548515911356, + "z": 0.0 + }, + { + "x": 398.306459091648, + "y": -177.22324478147652, + "z": 0.0 + }, + { + "x": 398.3062475903121, + "y": -176.22100440383878, + "z": 0.0 + }, + { + "x": 398.3060360889762, + "y": -175.21876402620128, + "z": 0.0 + }, + { + "x": 398.3058245876403, + "y": -174.21652364856376, + "z": 0.0 + }, + { + "x": 398.3056130863044, + "y": -173.21428327092605, + "z": 0.0 + }, + { + "x": 398.3054015849684, + "y": -172.21204289328898, + "z": 0.0 + }, + { + "x": 398.30519008363257, + "y": -171.20980251565126, + "z": 0.0 + }, + { + "x": 398.30497858229666, + "y": -170.20756213801374, + "z": 0.0 + }, + { + "x": 398.30476708096074, + "y": -169.205321760376, + "z": 0.0 + }, + { + "x": 398.3045555796248, + "y": -168.20308138273893, + "z": 0.0 + }, + { + "x": 398.3043440782889, + "y": -167.2008410051012, + "z": 0.0 + }, + { + "x": 398.304132576953, + "y": -166.1986006274637, + "z": 0.0 + }, + { + "x": 398.3039210756171, + "y": -165.1963602498262, + "z": 0.0 + }, + { + "x": 398.3037095742812, + "y": -164.19411987218845, + "z": 0.0 + }, + { + "x": 398.3034980729452, + "y": -163.19187949455142, + "z": 0.0 + }, + { + "x": 398.30328657160936, + "y": -162.18963911691364, + "z": 0.0 + }, + { + "x": 398.30307507027345, + "y": -161.18739873927615, + "z": 0.0 + }, + { + "x": 398.30286356893754, + "y": -160.1851583616384, + "z": 0.0 + }, + { + "x": 398.30265206760157, + "y": -159.18291798400136, + "z": 0.0 + }, + { + "x": 398.3024405662657, + "y": -158.18067760636362, + "z": 0.0 + }, + { + "x": 398.3022290649298, + "y": -157.17843722872612, + "z": 0.0 + }, + { + "x": 398.3020175635939, + "y": -156.1761968510886, + "z": 0.0 + }, + { + "x": 398.301806062258, + "y": -155.1739564734509, + "z": 0.0 + }, + { + "x": 398.301594560922, + "y": -154.17171609581382, + "z": 0.0 + }, + { + "x": 398.30138305958616, + "y": -153.16947571817607, + "z": 0.0 + }, + { + "x": 398.30117155825025, + "y": -152.16723534053855, + "z": 0.0 + }, + { + "x": 398.30096005691433, + "y": -151.16499496290106, + "z": 0.0 + }, + { + "x": 398.3007485555784, + "y": -150.16275458526354, + "z": 0.0 + }, + { + "x": 398.3005370542425, + "y": -149.16051420762605, + "z": 0.0 + }, + { + "x": 398.3003255529066, + "y": -148.15827382998856, + "z": 0.0 + }, + { + "x": 398.3001140515707, + "y": -147.15603345235104, + "z": 0.0 + }, + { + "x": 398.2999025502348, + "y": -146.15379307471352, + "z": 0.0 + }, + { + "x": 398.29969104889886, + "y": -145.151552697076, + "z": 0.0 + }, + { + "x": 398.29947954756295, + "y": -144.1493123194385, + "z": 0.0 + }, + { + "x": 398.29926804622704, + "y": -143.147071941801, + "z": 0.0 + }, + { + "x": 398.29905654489113, + "y": -142.1448315641635, + "z": 0.0 + }, + { + "x": 398.2988450435552, + "y": -141.14259118652598, + "z": 0.0 + }, + { + "x": 398.2986335422193, + "y": -140.14035080888848, + "z": 0.0 + }, + { + "x": 398.2984220408834, + "y": -139.13811043125096, + "z": 0.0 + }, + { + "x": 398.2982105395475, + "y": -138.13587005361347, + "z": 0.0 + }, + { + "x": 398.2979990382116, + "y": -137.13362967597615, + "z": 0.0 + }, + { + "x": 398.2977875368757, + "y": -136.1313892983382, + "z": 0.0 + }, + { + "x": 398.29757603553975, + "y": -135.1291489207009, + "z": 0.0 + }, + { + "x": 398.29736453420384, + "y": -134.12690854306342, + "z": 0.0 + }, + { + "x": 398.2971530328679, + "y": -133.1246681654259, + "z": 0.0 + }, + { + "x": 398.296941531532, + "y": -132.1224277877884, + "z": 0.0 + }, + { + "x": 398.2967300301961, + "y": -131.1201874101509, + "z": 0.0 + }, + { + "x": 398.2965185288602, + "y": -130.11794703251337, + "z": 0.0 + }, + { + "x": 398.2963070275243, + "y": -129.11570665487585, + "z": 0.0 + }, + { + "x": 398.29609552618837, + "y": -128.1134662772386, + "z": 0.0 + }, + { + "x": 398.2958840248525, + "y": -127.11122589960061, + "z": 0.0 + }, + { + "x": 398.29567252351654, + "y": -126.10898552196335, + "z": 0.0 + }, + { + "x": 398.29546102218063, + "y": -125.10674514432586, + "z": 0.0 + }, + { + "x": 398.2952495208447, + "y": -124.10450476668858, + "z": 0.0 + }, + { + "x": 398.29503801950887, + "y": -123.10226438905063, + "z": 0.0 + }, + { + "x": 398.2948265181729, + "y": -122.10002401141335, + "z": 0.0 + }, + { + "x": 398.294615016837, + "y": -121.09778363377583, + "z": 0.0 + }, + { + "x": 398.2944035155011, + "y": -120.09554325613833, + "z": 0.0 + }, + { + "x": 398.29419201416516, + "y": -119.09330287850105, + "z": 0.0 + }, + { + "x": 398.2939805128293, + "y": -118.0910625008631, + "z": 0.0 + }, + { + "x": 398.29376901149334, + "y": -117.08882212322584, + "z": 0.0 + }, + { + "x": 398.2935575101574, + "y": -116.08658174558835, + "z": 0.0 + }, + { + "x": 398.2933460088215, + "y": -115.08434136795108, + "z": 0.0 + }, + { + "x": 398.29313450748566, + "y": -114.08210099031334, + "z": 0.0 + }, + { + "x": 398.29292300614975, + "y": -113.07986061267562, + "z": 0.0 + }, + { + "x": 398.2927115048138, + "y": -112.07762023503834, + "z": 0.0 + }, + { + "x": 398.29250000347787, + "y": -111.07537985740083, + "z": 0.0 + }, + { + "x": 398.29228850214196, + "y": -110.07313947976355, + "z": 0.0 + }, + { + "x": 398.2920770008061, + "y": -109.0708991021256, + "z": 0.0 + }, + { + "x": 398.29186549947013, + "y": -108.06865872448832, + "z": 0.0 + }, + { + "x": 398.2916539981342, + "y": -107.06641834685081, + "z": 0.0 + }, + { + "x": 398.2914424967983, + "y": -106.06417796921353, + "z": 0.0 + }, + { + "x": 398.29123099546246, + "y": -105.06193759157577, + "z": 0.0 + }, + { + "x": 398.29101949412654, + "y": -104.05969721393826, + "z": 0.0 + }, + { + "x": 398.29080799279063, + "y": -103.05745683630055, + "z": 0.0 + }, + { + "x": 398.29059649145466, + "y": -102.0552164586635, + "z": 0.0 + }, + { + "x": 398.2903849901188, + "y": -101.05297608102576, + "z": 0.0 + }, + { + "x": 398.2901734887829, + "y": -100.05073570338826, + "z": 0.0 + }, + { + "x": 398.289961987447, + "y": -99.04849532575075, + "z": 0.0 + }, + { + "x": 398.2897504861111, + "y": -98.04625494811299, + "z": 0.0 + }, + { + "x": 398.2895389847751, + "y": -97.04401457047594, + "z": 0.0 + }, + { + "x": 398.28932748343925, + "y": -96.0417741928382, + "z": 0.0 + }, + { + "x": 398.28911598210334, + "y": -95.0395338152007, + "z": 0.0 + }, + { + "x": 398.2889044807674, + "y": -94.03729343756297, + "z": 0.0 + }, + { + "x": 398.28869297943146, + "y": -93.03505305992591, + "z": 0.0 + }, + { + "x": 398.2884814780956, + "y": -92.03281268228818, + "z": 0.0 + }, + { + "x": 398.2882699767597, + "y": -91.03057230465068, + "z": 0.0 + }, + { + "x": 398.2880584754238, + "y": -90.02833192701317, + "z": 0.0 + }, + { + "x": 398.28784697408787, + "y": -89.02609154937541, + "z": 0.0 + }, + { + "x": 398.28763556056833, + "y": -88.02479527108368, + "z": 0.0 + }, + { + "x": 398.2876588492329, + "y": -87.02621246628772, + "z": 0.0 + }, + { + "x": 398.28859921985594, + "y": -86.02501496122022, + "z": 0.0 + }, + { + "x": 398.2898018880531, + "y": -85.0227752828551, + "z": 0.0 + }, + { + "x": 398.2910045562502, + "y": -84.02053560449042, + "z": 0.0 + }, + { + "x": 398.29220722444734, + "y": -83.01829592612553, + "z": 0.0 + }, + { + "x": 398.2934098926445, + "y": -82.01605624776037, + "z": 0.0 + }, + { + "x": 398.2946125608416, + "y": -81.0138165693957, + "z": 0.0 + }, + { + "x": 398.29581522903874, + "y": -80.0115768910308, + "z": 0.0 + }, + { + "x": 398.2970178972359, + "y": -79.00933721266567, + "z": 0.0 + }, + { + "x": 398.298220565433, + "y": -78.007097534301, + "z": 0.0 + }, + { + "x": 398.29942323363014, + "y": -77.00485785593611, + "z": 0.0 + }, + { + "x": 398.3006259018273, + "y": -76.00261817757121, + "z": 0.0 + }, + { + "x": 398.30182857002444, + "y": -75.0003784992063, + "z": 0.0 + }, + { + "x": 398.3030312382216, + "y": -73.99813882084118, + "z": 0.0 + }, + { + "x": 398.3042339064187, + "y": -72.99589914247647, + "z": 0.0 + }, + { + "x": 398.30543657461584, + "y": -71.99365946411159, + "z": 0.0 + }, + { + "x": 398.306639242813, + "y": -70.99141978574644, + "z": 0.0 + }, + { + "x": 398.3078419110101, + "y": -69.98918010738178, + "z": 0.0 + }, + { + "x": 398.30904457920724, + "y": -68.98694042901688, + "z": 0.0 + }, + { + "x": 398.3102472474044, + "y": -67.98470075065175, + "z": 0.0 + }, + { + "x": 398.3114499156015, + "y": -66.9824610722871, + "z": 0.0 + }, + { + "x": 398.31265258379864, + "y": -65.98022139392216, + "z": 0.0 + }, + { + "x": 398.3138552519958, + "y": -64.97798171555726, + "z": 0.0 + }, + { + "x": 398.31505792019294, + "y": -63.97574203719214, + "z": 0.0 + }, + { + "x": 398.31626058839004, + "y": -62.973502358827474, + "z": 0.0 + }, + { + "x": 398.3174632565872, + "y": -61.9712626804626, + "z": 0.0 + }, + { + "x": 398.31866592478434, + "y": -60.9690230020975, + "z": 0.0 + }, + { + "x": 398.31986859298144, + "y": -59.96678332373281, + "z": 0.0 + }, + { + "x": 398.3210712611786, + "y": -58.964543645367925, + "z": 0.0 + }, + { + "x": 398.32227392937574, + "y": -57.96230396700278, + "z": 0.0 + }, + { + "x": 398.32347659757284, + "y": -56.96006428863836, + "z": 0.0 + }, + { + "x": 398.32467926577004, + "y": -55.957824610273036, + "z": 0.0 + }, + { + "x": 398.32588193396714, + "y": -54.95558493190812, + "z": 0.0 + }, + { + "x": 398.32708460216423, + "y": -53.95334525354369, + "z": 0.0 + }, + { + "x": 398.32828727036144, + "y": -52.95110557517826, + "z": 0.0 + }, + { + "x": 398.32948993855854, + "y": -51.9488658968136, + "z": 0.0 + }, + { + "x": 398.3306926067557, + "y": -50.94662621844869, + "z": 0.0 + }, + { + "x": 398.33189527495284, + "y": -49.944386540083585, + "z": 0.0 + }, + { + "x": 398.33309794314994, + "y": -48.94214686171893, + "z": 0.0 + }, + { + "x": 398.3343006113471, + "y": -47.93990718335402, + "z": 0.0 + }, + { + "x": 398.33550327954424, + "y": -46.93766750498914, + "z": 0.0 + }, + { + "x": 398.3367059477414, + "y": -45.93542782662401, + "z": 0.0 + }, + { + "x": 398.3379086159385, + "y": -44.93318814825934, + "z": 0.0 + }, + { + "x": 398.33911128413564, + "y": -43.93094846989443, + "z": 0.0 + }, + { + "x": 398.3403139523328, + "y": -42.928708791529324, + "z": 0.0 + }, + { + "x": 398.3415166205299, + "y": -41.92646911316468, + "z": 0.0 + }, + { + "x": 398.34271928872704, + "y": -40.924229434799805, + "z": 0.0 + }, + { + "x": 398.3439219569242, + "y": -39.921989756434705, + "z": 0.0 + }, + { + "x": 398.3451246251213, + "y": -38.91975007807005, + "z": 0.0 + }, + { + "x": 398.34632729331844, + "y": -37.91751039970513, + "z": 0.0 + }, + { + "x": 398.3475299615156, + "y": -36.91527072134, + "z": 0.0 + }, + { + "x": 398.3487326297127, + "y": -35.913031042975334, + "z": 0.0 + }, + { + "x": 398.34993529790984, + "y": -34.91079136461043, + "z": 0.0 + }, + { + "x": 398.351137966107, + "y": -33.90855168624554, + "z": 0.0 + }, + { + "x": 398.35234063430414, + "y": -32.906312007880665, + "z": 0.0 + }, + { + "x": 398.3535433025013, + "y": -31.904072329515564, + "z": 0.0 + }, + { + "x": 398.3547459706984, + "y": -30.90183265115092, + "z": 0.0 + }, + { + "x": 398.35594863889554, + "y": -29.899592972786046, + "z": 0.0 + }, + { + "x": 398.3571513070927, + "y": -28.89735329442095, + "z": 0.0 + }, + { + "x": 398.3583539752898, + "y": -27.895113616056285, + "z": 0.0 + }, + { + "x": 398.35955664348694, + "y": -26.89287393769138, + "z": 0.0 + }, + { + "x": 398.3607593116841, + "y": -25.890634259326283, + "z": 0.0 + }, + { + "x": 398.3619619798812, + "y": -24.888394580961638, + "z": 0.0 + }, + { + "x": 398.36316464807834, + "y": -23.88615490259675, + "z": 0.0 + }, + { + "x": 398.3643673162755, + "y": -22.883915224231615, + "z": 0.0 + }, + { + "x": 398.3655699844726, + "y": -21.881675545866944, + "z": 0.0 + }, + { + "x": 398.36677265266974, + "y": -20.879435867501968, + "z": 0.0 + }, + { + "x": 398.3679753208669, + "y": -19.87719618913705, + "z": 0.0 + }, + { + "x": 398.36917798906404, + "y": -18.87495651077191, + "z": 0.0 + }, + { + "x": 398.37038065726114, + "y": -17.872716832407257, + "z": 0.0 + }, + { + "x": 398.3715833254583, + "y": -16.870477154042373, + "z": 0.0 + }, + { + "x": 398.37278599365544, + "y": -15.868237475677223, + "z": 0.0 + }, + { + "x": 398.37398866185254, + "y": -14.865997797312762, + "z": 0.0 + }, + { + "x": 398.37519133004974, + "y": -13.863758118947425, + "z": 0.0 + }, + { + "x": 398.37639399824684, + "y": -12.861518440582504, + "z": 0.0 + }, + { + "x": 398.37759666644394, + "y": -11.85927876221804, + "z": 0.0 + }, + { + "x": 398.37879933464114, + "y": -10.857039083852692, + "z": 0.0 + }, + { + "x": 398.38000200283824, + "y": -9.854799405487771, + "z": 0.0 + } + ] + }, + { + "id": 42, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 394.3500061035156, + "y": -318.53997802734375, + "z": 0.0 + }, + { + "x": 394.34961655419744, + "y": -317.5377377030946, + "z": 0.0 + }, + { + "x": 394.3492270048793, + "y": -316.5354973788454, + "z": 0.0 + }, + { + "x": 394.3488374555612, + "y": -315.5332570545962, + "z": 0.0 + }, + { + "x": 394.348447906243, + "y": -314.53101673034706, + "z": 0.0 + }, + { + "x": 394.34805835692487, + "y": -313.52877640609785, + "z": 0.0 + }, + { + "x": 394.3476688076067, + "y": -312.5265360818487, + "z": 0.0 + }, + { + "x": 394.3472792582885, + "y": -311.5242957575995, + "z": 0.0 + }, + { + "x": 394.34688970897037, + "y": -310.5220554333503, + "z": 0.0 + }, + { + "x": 394.34650015965224, + "y": -309.51981510910116, + "z": 0.0 + }, + { + "x": 394.34611061033405, + "y": -308.51757478485194, + "z": 0.0 + }, + { + "x": 394.34572106101587, + "y": -307.5153344606028, + "z": 0.0 + }, + { + "x": 394.34533151169774, + "y": -306.5130941363536, + "z": 0.0 + }, + { + "x": 394.34494196237955, + "y": -305.5108538121044, + "z": 0.0 + }, + { + "x": 394.3445524130614, + "y": -304.50861348785526, + "z": 0.0 + }, + { + "x": 394.3441628637433, + "y": -303.50637316360604, + "z": 0.0 + }, + { + "x": 394.3437733144251, + "y": -302.5041328393569, + "z": 0.0 + }, + { + "x": 394.3433837651069, + "y": -301.5018925151077, + "z": 0.0 + }, + { + "x": 394.3429942157888, + "y": -300.4996521908585, + "z": 0.0 + }, + { + "x": 394.3426046664706, + "y": -299.49741186660935, + "z": 0.0 + }, + { + "x": 394.3422151171525, + "y": -298.49517154236014, + "z": 0.0 + }, + { + "x": 394.34182556783435, + "y": -297.492931218111, + "z": 0.0 + }, + { + "x": 394.34143601851616, + "y": -296.4906908938618, + "z": 0.0 + }, + { + "x": 394.341046469198, + "y": -295.4884505696126, + "z": 0.0 + }, + { + "x": 394.34065691987985, + "y": -294.48621024536345, + "z": 0.0 + }, + { + "x": 394.34026737056166, + "y": -293.4839699211143, + "z": 0.0 + }, + { + "x": 394.33987782124353, + "y": -292.4817295968651, + "z": 0.0 + }, + { + "x": 394.33948827192535, + "y": -291.4794892726159, + "z": 0.0 + }, + { + "x": 394.3390987226072, + "y": -290.4772489483667, + "z": 0.0 + }, + { + "x": 394.33870917328903, + "y": -289.47500862411755, + "z": 0.0 + }, + { + "x": 394.3383196239709, + "y": -288.4727682998684, + "z": 0.0 + }, + { + "x": 394.3379300746528, + "y": -287.4705279756192, + "z": 0.0 + }, + { + "x": 394.3375405253346, + "y": -286.46828765137, + "z": 0.0 + }, + { + "x": 394.3371509760164, + "y": -285.46604732712086, + "z": 0.0 + }, + { + "x": 394.3367614266983, + "y": -284.46380700287165, + "z": 0.0 + }, + { + "x": 394.3363718773801, + "y": -283.4615666786225, + "z": 0.0 + }, + { + "x": 394.33598232806196, + "y": -282.45932635437333, + "z": 0.0 + }, + { + "x": 394.33559277874383, + "y": -281.4570860301241, + "z": 0.0 + }, + { + "x": 394.33520322942564, + "y": -280.45484570587496, + "z": 0.0 + }, + { + "x": 394.33481368010746, + "y": -279.45260538162574, + "z": 0.0 + }, + { + "x": 394.33442413078933, + "y": -278.4503650573766, + "z": 0.0 + }, + { + "x": 394.33403458147114, + "y": -277.44812473312743, + "z": 0.0 + }, + { + "x": 394.333645032153, + "y": -276.44588440887827, + "z": 0.0 + }, + { + "x": 394.33325548283483, + "y": -275.44364408462906, + "z": 0.0 + }, + { + "x": 394.3328659335167, + "y": -274.4414037603799, + "z": 0.0 + }, + { + "x": 394.3324763841985, + "y": -273.43916343613074, + "z": 0.0 + }, + { + "x": 394.33208683488033, + "y": -272.4369231118815, + "z": 0.0 + }, + { + "x": 394.3316972855622, + "y": -271.43468278763237, + "z": 0.0 + }, + { + "x": 394.33130773624407, + "y": -270.4324424633832, + "z": 0.0 + }, + { + "x": 394.3309181869259, + "y": -269.430202139134, + "z": 0.0 + }, + { + "x": 394.33052863760776, + "y": -268.42796181488484, + "z": 0.0 + }, + { + "x": 394.33013908828957, + "y": -267.4257214906357, + "z": 0.0 + }, + { + "x": 394.32974953897144, + "y": -266.42348116638647, + "z": 0.0 + }, + { + "x": 394.3293599896533, + "y": -265.4212408421373, + "z": 0.0 + }, + { + "x": 394.3289704403351, + "y": -264.41900051788815, + "z": 0.0 + }, + { + "x": 394.32858089101694, + "y": -263.41676019363894, + "z": 0.0 + }, + { + "x": 394.3281913416988, + "y": -262.4145198693898, + "z": 0.0 + }, + { + "x": 394.3278017923806, + "y": -261.4122795451406, + "z": 0.0 + }, + { + "x": 394.32741224306244, + "y": -260.4100392208914, + "z": 0.0 + }, + { + "x": 394.3270226937443, + "y": -259.40779889664225, + "z": 0.0 + }, + { + "x": 394.3266331444262, + "y": -258.4055585723931, + "z": 0.0 + }, + { + "x": 394.326243595108, + "y": -257.40331824814393, + "z": 0.0 + }, + { + "x": 394.3258540457898, + "y": -256.4010779238947, + "z": 0.0 + }, + { + "x": 394.3254644964717, + "y": -255.39883759964553, + "z": 0.0 + }, + { + "x": 394.32507494715355, + "y": -254.39659727539637, + "z": 0.0 + }, + { + "x": 394.32468539783537, + "y": -253.3943569511472, + "z": 0.0 + }, + { + "x": 394.32429584851724, + "y": -252.39211662689803, + "z": 0.0 + }, + { + "x": 394.32390629919905, + "y": -251.38987630264884, + "z": 0.0 + }, + { + "x": 394.32351674988087, + "y": -250.38763597839966, + "z": 0.0 + }, + { + "x": 394.32312720056274, + "y": -249.3853956541505, + "z": 0.0 + }, + { + "x": 394.3227376512446, + "y": -248.3831553299013, + "z": 0.0 + }, + { + "x": 394.3223481019264, + "y": -247.38091500565213, + "z": 0.0 + }, + { + "x": 394.3219585526083, + "y": -246.37867468140297, + "z": 0.0 + }, + { + "x": 394.3215690032901, + "y": -245.37643435715378, + "z": 0.0 + }, + { + "x": 394.3211794539719, + "y": -244.37419403290463, + "z": 0.0 + }, + { + "x": 394.3207899046538, + "y": -243.37195370865544, + "z": 0.0 + }, + { + "x": 394.32040035533566, + "y": -242.36971338440625, + "z": 0.0 + }, + { + "x": 394.3200108060175, + "y": -241.36747306015707, + "z": 0.0 + }, + { + "x": 394.3197837648765, + "y": -240.36523268682888, + "z": 0.0 + }, + { + "x": 394.3195722635406, + "y": -239.36299230919138, + "z": 0.0 + }, + { + "x": 394.31936076220467, + "y": -238.3607519315539, + "z": 0.0 + }, + { + "x": 394.31914926086876, + "y": -237.3585115539164, + "z": 0.0 + }, + { + "x": 394.31893775953284, + "y": -236.3562711762789, + "z": 0.0 + }, + { + "x": 394.31872625819693, + "y": -235.35403079864142, + "z": 0.0 + }, + { + "x": 394.318514756861, + "y": -234.35179042100393, + "z": 0.0 + }, + { + "x": 394.3183032555251, + "y": -233.34955004336643, + "z": 0.0 + }, + { + "x": 394.3180917541892, + "y": -232.34730966572894, + "z": 0.0 + }, + { + "x": 394.3178802528533, + "y": -231.34506928809145, + "z": 0.0 + }, + { + "x": 394.3176687515174, + "y": -230.34282891045396, + "z": 0.0 + }, + { + "x": 394.31745725018146, + "y": -229.34058853281647, + "z": 0.0 + }, + { + "x": 394.31724574884555, + "y": -228.33834815517898, + "z": 0.0 + }, + { + "x": 394.31703424750964, + "y": -227.33610777754146, + "z": 0.0 + }, + { + "x": 394.3168227461737, + "y": -226.33386739990397, + "z": 0.0 + }, + { + "x": 394.3166112448378, + "y": -225.33162702226647, + "z": 0.0 + }, + { + "x": 394.3163997435019, + "y": -224.32938664462898, + "z": 0.0 + }, + { + "x": 394.316188242166, + "y": -223.3271462669915, + "z": 0.0 + }, + { + "x": 394.3159767408301, + "y": -222.324905889354, + "z": 0.0 + }, + { + "x": 394.31576523949417, + "y": -221.3226655117165, + "z": 0.0 + }, + { + "x": 394.31555373815826, + "y": -220.32042513407902, + "z": 0.0 + }, + { + "x": 394.31534223682235, + "y": -219.3181847564415, + "z": 0.0 + }, + { + "x": 394.31513073548643, + "y": -218.315944378804, + "z": 0.0 + }, + { + "x": 394.3149192341505, + "y": -217.3137040011665, + "z": 0.0 + }, + { + "x": 394.3147077328146, + "y": -216.31146362352902, + "z": 0.0 + }, + { + "x": 394.3144962314787, + "y": -215.30922324589153, + "z": 0.0 + }, + { + "x": 394.3142847301428, + "y": -214.30698286825404, + "z": 0.0 + }, + { + "x": 394.3140732288069, + "y": -213.30474249061655, + "z": 0.0 + }, + { + "x": 394.31386172747096, + "y": -212.30250211297906, + "z": 0.0 + }, + { + "x": 394.3136502261351, + "y": -211.30026173534154, + "z": 0.0 + }, + { + "x": 394.31343872479914, + "y": -210.29802135770404, + "z": 0.0 + }, + { + "x": 394.31322722346323, + "y": -209.29578098006655, + "z": 0.0 + }, + { + "x": 394.3130157221273, + "y": -208.29354060242906, + "z": 0.0 + }, + { + "x": 394.3128042207914, + "y": -207.29130022479157, + "z": 0.0 + }, + { + "x": 394.3125927194555, + "y": -206.28905984715408, + "z": 0.0 + }, + { + "x": 394.3123812181196, + "y": -205.2868194695166, + "z": 0.0 + }, + { + "x": 394.31216971678367, + "y": -204.2845790918791, + "z": 0.0 + }, + { + "x": 394.31195821544776, + "y": -203.28233871424158, + "z": 0.0 + }, + { + "x": 394.3117467141119, + "y": -202.28009833660408, + "z": 0.0 + }, + { + "x": 394.31153521277594, + "y": -201.2778579589666, + "z": 0.0 + }, + { + "x": 394.31132371144, + "y": -200.2756175813291, + "z": 0.0 + }, + { + "x": 394.3111122101041, + "y": -199.2733772036916, + "z": 0.0 + }, + { + "x": 394.31090070876826, + "y": -198.27113682605412, + "z": 0.0 + }, + { + "x": 394.3106892074323, + "y": -197.26889644841663, + "z": 0.0 + }, + { + "x": 394.3104777060964, + "y": -196.26665607077913, + "z": 0.0 + }, + { + "x": 394.31026620476047, + "y": -195.26441569314161, + "z": 0.0 + }, + { + "x": 394.31005470342456, + "y": -194.26217531550412, + "z": 0.0 + }, + { + "x": 394.3098432020887, + "y": -193.25993493786663, + "z": 0.0 + }, + { + "x": 394.30963170075273, + "y": -192.25769456022914, + "z": 0.0 + }, + { + "x": 394.3094201994168, + "y": -191.25545418259165, + "z": 0.0 + }, + { + "x": 394.3092086980809, + "y": -190.25321380495416, + "z": 0.0 + }, + { + "x": 394.30899719674505, + "y": -189.25097342731664, + "z": 0.0 + }, + { + "x": 394.30878569540914, + "y": -188.24873304967915, + "z": 0.0 + }, + { + "x": 394.30857419407323, + "y": -187.24649267204163, + "z": 0.0 + }, + { + "x": 394.30836269273726, + "y": -186.24425229440413, + "z": 0.0 + }, + { + "x": 394.30815119140135, + "y": -185.2420119167666, + "z": 0.0 + }, + { + "x": 394.3079396900655, + "y": -184.2397715391291, + "z": 0.0 + }, + { + "x": 394.3077281887296, + "y": -183.23753116149157, + "z": 0.0 + }, + { + "x": 394.3075166873936, + "y": -182.23529078385408, + "z": 0.0 + }, + { + "x": 394.3073051860577, + "y": -181.23305040621656, + "z": 0.0 + }, + { + "x": 394.30709368472185, + "y": -180.23081002857907, + "z": 0.0 + }, + { + "x": 394.30688218338594, + "y": -179.22856965094158, + "z": 0.0 + }, + { + "x": 394.30667068205, + "y": -178.22632927330403, + "z": 0.0 + }, + { + "x": 394.30645918071406, + "y": -177.22408889566654, + "z": 0.0 + }, + { + "x": 394.3062476793782, + "y": -176.22184851802902, + "z": 0.0 + }, + { + "x": 394.3060361780423, + "y": -175.21960814039153, + "z": 0.0 + }, + { + "x": 394.3058246767064, + "y": -174.217367762754, + "z": 0.0 + }, + { + "x": 394.30561317537047, + "y": -173.21512738511652, + "z": 0.0 + }, + { + "x": 394.3054016740345, + "y": -172.212887007479, + "z": 0.0 + }, + { + "x": 394.30519017269864, + "y": -171.2106466298415, + "z": 0.0 + }, + { + "x": 394.30497867136273, + "y": -170.20840625220399, + "z": 0.0 + }, + { + "x": 394.3047671700268, + "y": -169.20616587456647, + "z": 0.0 + }, + { + "x": 394.30455566869085, + "y": -168.20392549692895, + "z": 0.0 + }, + { + "x": 394.304344167355, + "y": -167.20168511929145, + "z": 0.0 + }, + { + "x": 394.3041326660191, + "y": -166.19944474165393, + "z": 0.0 + }, + { + "x": 394.3039211646832, + "y": -165.19720436401644, + "z": 0.0 + }, + { + "x": 394.30370966334726, + "y": -164.19496398637892, + "z": 0.0 + }, + { + "x": 394.3034981620113, + "y": -163.19272360874143, + "z": 0.0 + }, + { + "x": 394.30328666067544, + "y": -162.19048323110388, + "z": 0.0 + }, + { + "x": 394.3030751593395, + "y": -161.1882428534664, + "z": 0.0 + }, + { + "x": 394.3028636580036, + "y": -160.18600247582887, + "z": 0.0 + }, + { + "x": 394.30265215666765, + "y": -159.18376209819138, + "z": 0.0 + }, + { + "x": 394.3024406553318, + "y": -158.18152172055386, + "z": 0.0 + }, + { + "x": 394.3022291539959, + "y": -157.17928134291637, + "z": 0.0 + }, + { + "x": 394.30201765265997, + "y": -156.17704096527885, + "z": 0.0 + }, + { + "x": 394.30180615132406, + "y": -155.17480058764136, + "z": 0.0 + }, + { + "x": 394.3015946499881, + "y": -154.17256021000384, + "z": 0.0 + }, + { + "x": 394.30138314865223, + "y": -153.17031983236632, + "z": 0.0 + }, + { + "x": 394.3011716473163, + "y": -152.1680794547288, + "z": 0.0 + }, + { + "x": 394.3009601459804, + "y": -151.1658390770913, + "z": 0.0 + }, + { + "x": 394.3007486446445, + "y": -150.1635986994538, + "z": 0.0 + }, + { + "x": 394.3005371433086, + "y": -149.1613583218163, + "z": 0.0 + }, + { + "x": 394.3003256419727, + "y": -148.1591179441788, + "z": 0.0 + }, + { + "x": 394.30011414063677, + "y": -147.15687756654128, + "z": 0.0 + }, + { + "x": 394.29990263930085, + "y": -146.15463718890376, + "z": 0.0 + }, + { + "x": 394.29969113796494, + "y": -145.15239681126624, + "z": 0.0 + }, + { + "x": 394.29947963662903, + "y": -144.15015643362875, + "z": 0.0 + }, + { + "x": 394.2992681352931, + "y": -143.14791605599123, + "z": 0.0 + }, + { + "x": 394.2990566339572, + "y": -142.14567567835374, + "z": 0.0 + }, + { + "x": 394.2988451326213, + "y": -141.14343530071622, + "z": 0.0 + }, + { + "x": 394.2986336312854, + "y": -140.14119492307873, + "z": 0.0 + }, + { + "x": 394.2984221299495, + "y": -139.1389545454412, + "z": 0.0 + }, + { + "x": 394.29821062861356, + "y": -138.13671416780372, + "z": 0.0 + }, + { + "x": 394.29799912727765, + "y": -137.13447379016617, + "z": 0.0 + }, + { + "x": 394.2977876259418, + "y": -136.13223341252868, + "z": 0.0 + }, + { + "x": 394.2975761246058, + "y": -135.12999303489116, + "z": 0.0 + }, + { + "x": 394.2973646232699, + "y": -134.12775265725367, + "z": 0.0 + }, + { + "x": 394.297153121934, + "y": -133.12551227961615, + "z": 0.0 + }, + { + "x": 394.2969416205981, + "y": -132.12327190197865, + "z": 0.0 + }, + { + "x": 394.2967301192622, + "y": -131.12103152434113, + "z": 0.0 + }, + { + "x": 394.29651861792627, + "y": -130.11879114670361, + "z": 0.0 + }, + { + "x": 394.29630711659036, + "y": -129.1165507690661, + "z": 0.0 + }, + { + "x": 394.29609561525444, + "y": -128.1143103914286, + "z": 0.0 + }, + { + "x": 394.2958841139186, + "y": -127.1120700137911, + "z": 0.0 + }, + { + "x": 394.2956726125826, + "y": -126.1098296361536, + "z": 0.0 + }, + { + "x": 394.2954611112467, + "y": -125.10758925851611, + "z": 0.0 + }, + { + "x": 394.2952496099108, + "y": -124.10534888087861, + "z": 0.0 + }, + { + "x": 394.29503810857494, + "y": -123.10310850324112, + "z": 0.0 + }, + { + "x": 394.294826607239, + "y": -122.10086812560361, + "z": 0.0 + }, + { + "x": 394.29461510590306, + "y": -121.09862774796609, + "z": 0.0 + }, + { + "x": 394.29440360456715, + "y": -120.09638737032859, + "z": 0.0 + }, + { + "x": 394.29419210323124, + "y": -119.09414699269108, + "z": 0.0 + }, + { + "x": 394.2939806018954, + "y": -118.09190661505359, + "z": 0.0 + }, + { + "x": 394.2937691005594, + "y": -117.0896662374161, + "z": 0.0 + }, + { + "x": 394.2935575992235, + "y": -116.0874258597786, + "z": 0.0 + }, + { + "x": 394.2933460978876, + "y": -115.08518548214111, + "z": 0.0 + }, + { + "x": 394.29313459655174, + "y": -114.0829451045036, + "z": 0.0 + }, + { + "x": 394.2929230952158, + "y": -113.0807047268661, + "z": 0.0 + }, + { + "x": 394.29271159387986, + "y": -112.0784643492286, + "z": 0.0 + }, + { + "x": 394.29250009254395, + "y": -111.07622397159109, + "z": 0.0 + }, + { + "x": 394.29228859120803, + "y": -110.07398359395359, + "z": 0.0 + }, + { + "x": 394.2920770898722, + "y": -109.07174321631608, + "z": 0.0 + }, + { + "x": 394.2918655885362, + "y": -108.06950283867857, + "z": 0.0 + }, + { + "x": 394.2916540872003, + "y": -107.06726246104107, + "z": 0.0 + }, + { + "x": 394.2914425858644, + "y": -106.06502208340356, + "z": 0.0 + }, + { + "x": 394.29123108452853, + "y": -105.06278170576603, + "z": 0.0 + }, + { + "x": 394.2910195831926, + "y": -104.06054132812852, + "z": 0.0 + }, + { + "x": 394.2908080818567, + "y": -103.05830095049103, + "z": 0.0 + }, + { + "x": 394.29059658052074, + "y": -102.05606057285353, + "z": 0.0 + }, + { + "x": 394.2903850791849, + "y": -101.05382019521602, + "z": 0.0 + }, + { + "x": 394.290173577849, + "y": -100.05157981757851, + "z": 0.0 + }, + { + "x": 394.28996207651306, + "y": -99.04933943994101, + "z": 0.0 + }, + { + "x": 394.28975057517715, + "y": -98.04709906230347, + "z": 0.0 + }, + { + "x": 394.2895390738412, + "y": -97.04485868466597, + "z": 0.0 + }, + { + "x": 394.28932757250533, + "y": -96.04261830702846, + "z": 0.0 + }, + { + "x": 394.2891160711694, + "y": -95.04037792939096, + "z": 0.0 + }, + { + "x": 394.2889045698335, + "y": -94.03813755175345, + "z": 0.0 + }, + { + "x": 394.28869306849754, + "y": -93.03589717411595, + "z": 0.0 + }, + { + "x": 394.2884815671617, + "y": -92.03365679647844, + "z": 0.0 + }, + { + "x": 394.28827006582577, + "y": -91.03141641884093, + "z": 0.0 + }, + { + "x": 394.28805856448986, + "y": -90.02917604120343, + "z": 0.0 + }, + { + "x": 394.28784706315395, + "y": -89.0269356635659, + "z": 0.0 + }, + { + "x": 394.287635561818, + "y": -88.02469528592839, + "z": 0.0 + }, + { + "x": 394.2876606141071, + "y": -87.02245494181614, + "z": 0.0 + }, + { + "x": 394.2886020997598, + "y": -86.02021504217004, + "z": 0.0 + }, + { + "x": 394.289804767957, + "y": -85.01797536380514, + "z": 0.0 + }, + { + "x": 394.2910074361541, + "y": -84.01573568544023, + "z": 0.0 + }, + { + "x": 394.2922101043512, + "y": -83.01349600707535, + "z": 0.0 + }, + { + "x": 394.2934127725484, + "y": -82.01125632871042, + "z": 0.0 + }, + { + "x": 394.2946154407455, + "y": -81.00901665034552, + "z": 0.0 + }, + { + "x": 394.2958181089426, + "y": -80.00677697198061, + "z": 0.0 + }, + { + "x": 394.2970207771398, + "y": -79.00453729361571, + "z": 0.0 + }, + { + "x": 394.2982234453369, + "y": -78.00229761525081, + "z": 0.0 + }, + { + "x": 394.299426113534, + "y": -77.00005793688592, + "z": 0.0 + }, + { + "x": 394.3006287817312, + "y": -75.99781825852102, + "z": 0.0 + }, + { + "x": 394.30183144992833, + "y": -74.99557858015612, + "z": 0.0 + }, + { + "x": 394.3030341181255, + "y": -73.99333890179122, + "z": 0.0 + }, + { + "x": 394.3042367863226, + "y": -72.99109922342629, + "z": 0.0 + }, + { + "x": 394.3054394545197, + "y": -71.9888595450614, + "z": 0.0 + }, + { + "x": 394.3066421227169, + "y": -70.98661986669649, + "z": 0.0 + }, + { + "x": 394.307844790914, + "y": -69.9843801883316, + "z": 0.0 + }, + { + "x": 394.3090474591111, + "y": -68.9821405099667, + "z": 0.0 + }, + { + "x": 394.3102501273083, + "y": -67.9799008316018, + "z": 0.0 + }, + { + "x": 394.3114527955054, + "y": -66.97766115323691, + "z": 0.0 + }, + { + "x": 394.3126554637025, + "y": -65.97542147487198, + "z": 0.0 + }, + { + "x": 394.3138581318997, + "y": -64.97318179650708, + "z": 0.0 + }, + { + "x": 394.31506080009683, + "y": -63.970942118142176, + "z": 0.0 + }, + { + "x": 394.3162634682939, + "y": -62.96870243977728, + "z": 0.0 + }, + { + "x": 394.3174661364911, + "y": -61.96646276141241, + "z": 0.0 + }, + { + "x": 394.31866880468823, + "y": -60.964223083047536, + "z": 0.0 + }, + { + "x": 394.3198714728853, + "y": -59.96198340468262, + "z": 0.0 + }, + { + "x": 394.3210741410825, + "y": -58.95974372631774, + "z": 0.0 + }, + { + "x": 394.32227680927963, + "y": -57.95750404795282, + "z": 0.0 + }, + { + "x": 394.3234794774767, + "y": -56.955264369587944, + "z": 0.0 + }, + { + "x": 394.32468214567393, + "y": -55.95302469122307, + "z": 0.0 + }, + { + "x": 394.325884813871, + "y": -54.950785012858155, + "z": 0.0 + }, + { + "x": 394.3270874820681, + "y": -53.948545334493275, + "z": 0.0 + }, + { + "x": 394.32829015026533, + "y": -52.946305656128295, + "z": 0.0 + }, + { + "x": 394.3294928184624, + "y": -51.944065977763415, + "z": 0.0 + }, + { + "x": 394.3306954866596, + "y": -50.9418262993985, + "z": 0.0 + }, + { + "x": 394.33189815485673, + "y": -49.93958662103362, + "z": 0.0 + }, + { + "x": 394.3331008230538, + "y": -48.937346942668746, + "z": 0.0 + }, + { + "x": 394.334303491251, + "y": -47.93510726430383, + "z": 0.0 + }, + { + "x": 394.33550615944813, + "y": -46.93286758593895, + "z": 0.0 + }, + { + "x": 394.3367088276453, + "y": -45.93062790757404, + "z": 0.0 + }, + { + "x": 394.3379114958424, + "y": -44.92838822920915, + "z": 0.0 + }, + { + "x": 394.33911416403953, + "y": -43.92614855084424, + "z": 0.0 + }, + { + "x": 394.3403168322367, + "y": -42.92390887247936, + "z": 0.0 + }, + { + "x": 394.3415195004338, + "y": -41.921669194114486, + "z": 0.0 + }, + { + "x": 394.3427221686309, + "y": -40.91942951574961, + "z": 0.0 + }, + { + "x": 394.3439248368281, + "y": -39.91718983738474, + "z": 0.0 + }, + { + "x": 394.3451275050252, + "y": -38.91495015901987, + "z": 0.0 + }, + { + "x": 394.3463301732223, + "y": -37.91271048065494, + "z": 0.0 + }, + { + "x": 394.3475328414195, + "y": -36.910470802290035, + "z": 0.0 + }, + { + "x": 394.3487355096166, + "y": -35.90823112392514, + "z": 0.0 + }, + { + "x": 394.3499381778137, + "y": -34.90599144556024, + "z": 0.0 + }, + { + "x": 394.3511408460109, + "y": -33.903751767195345, + "z": 0.0 + }, + { + "x": 394.35234351420803, + "y": -32.90151208883047, + "z": 0.0 + }, + { + "x": 394.3535461824052, + "y": -31.899272410465603, + "z": 0.0 + }, + { + "x": 394.3547488506023, + "y": -30.89703273210073, + "z": 0.0 + }, + { + "x": 394.35595151879943, + "y": -29.894793053735857, + "z": 0.0 + }, + { + "x": 394.3571541869966, + "y": -28.892553375370987, + "z": 0.0 + }, + { + "x": 394.3583568551937, + "y": -27.890313697006096, + "z": 0.0 + }, + { + "x": 394.3595595233908, + "y": -26.88807401864119, + "z": 0.0 + }, + { + "x": 394.360762191588, + "y": -25.885834340276322, + "z": 0.0 + }, + { + "x": 394.3619648597851, + "y": -24.88359466191145, + "z": 0.0 + }, + { + "x": 394.3631675279822, + "y": -23.88135498354656, + "z": 0.0 + }, + { + "x": 394.3643701961794, + "y": -22.879115305181653, + "z": 0.0 + }, + { + "x": 394.3655728643765, + "y": -21.876875626816755, + "z": 0.0 + }, + { + "x": 394.3667755325736, + "y": -20.87463594845178, + "z": 0.0 + }, + { + "x": 394.3679782007708, + "y": -19.87239627008686, + "z": 0.0 + }, + { + "x": 394.36918086896793, + "y": -18.870156591721948, + "z": 0.0 + }, + { + "x": 394.370383537165, + "y": -17.867916913357067, + "z": 0.0 + }, + { + "x": 394.3715862053622, + "y": -16.865677234992184, + "z": 0.0 + }, + { + "x": 394.37278887355933, + "y": -15.863437556627261, + "z": 0.0 + }, + { + "x": 394.3739915417564, + "y": -14.861197878262345, + "z": 0.0 + }, + { + "x": 394.37519420995363, + "y": -13.858958199897463, + "z": 0.0 + }, + { + "x": 394.37639687815073, + "y": -12.856718521532542, + "z": 0.0 + }, + { + "x": 394.3775995463478, + "y": -11.854478843167623, + "z": 0.0 + }, + { + "x": 394.37880221454503, + "y": -10.85223916480273, + "z": 0.0 + }, + { + "x": 394.3800048827421, + "y": -9.84999948643781, + "z": 0.0 + } + ] + }, + { + "id": 43, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 392.38000632269404, + "y": -9.847599526912829, + "z": 0.0 + }, + { + "x": 392.378803654497, + "y": -10.84983920527775, + "z": 0.0 + }, + { + "x": 392.37760098629974, + "y": -11.852078883642415, + "z": 0.0 + }, + { + "x": 392.3763983181027, + "y": -12.854318562007562, + "z": 0.0 + }, + { + "x": 392.37519564990555, + "y": -13.856558240372483, + "z": 0.0 + }, + { + "x": 392.3739929817084, + "y": -14.858797918737137, + "z": 0.0 + }, + { + "x": 392.37279031351125, + "y": -15.86103759710228, + "z": 0.0 + }, + { + "x": 392.3715876453141, + "y": -16.86327727546709, + "z": 0.0 + }, + { + "x": 392.37038497711694, + "y": -17.86551695383197, + "z": 0.0 + }, + { + "x": 392.3691823089199, + "y": -18.867756632196965, + "z": 0.0 + }, + { + "x": 392.36797964072275, + "y": -19.869996310561767, + "z": 0.0 + }, + { + "x": 392.3667769725256, + "y": -20.872235988926683, + "z": 0.0 + }, + { + "x": 392.36557430432845, + "y": -21.874475667291662, + "z": 0.0 + }, + { + "x": 392.3643716361313, + "y": -22.87671534565667, + "z": 0.0 + }, + { + "x": 392.36316896793414, + "y": -23.878955024021465, + "z": 0.0 + }, + { + "x": 392.361966299737, + "y": -24.881194702386352, + "z": 0.0 + }, + { + "x": 392.36076363153995, + "y": -25.88343438075134, + "z": 0.0 + }, + { + "x": 392.3595609633428, + "y": -26.8856740591161, + "z": 0.0 + }, + { + "x": 392.35835829514565, + "y": -27.887913737481, + "z": 0.0 + }, + { + "x": 392.3571556269485, + "y": -28.890153415846008, + "z": 0.0 + }, + { + "x": 392.35595295875135, + "y": -29.89239309421076, + "z": 0.0 + }, + { + "x": 392.3547502905542, + "y": -30.894632772575633, + "z": 0.0 + }, + { + "x": 392.35354762235715, + "y": -31.89687245094062, + "z": 0.0 + }, + { + "x": 392.35234495416, + "y": -32.89911212930538, + "z": 0.0 + }, + { + "x": 392.35114228596285, + "y": -33.90135180767025, + "z": 0.0 + }, + { + "x": 392.3499396177657, + "y": -34.90359148603514, + "z": 0.0 + }, + { + "x": 392.34873694956855, + "y": -35.90583116440004, + "z": 0.0 + }, + { + "x": 392.3475342813714, + "y": -36.908070842765056, + "z": 0.0 + }, + { + "x": 392.34633161317424, + "y": -37.910310521129844, + "z": 0.0 + }, + { + "x": 392.3451289449771, + "y": -38.912550199494774, + "z": 0.0 + }, + { + "x": 392.34392627678005, + "y": -39.91478987785976, + "z": 0.0 + }, + { + "x": 392.3427236085829, + "y": -40.91702955622452, + "z": 0.0 + }, + { + "x": 392.34152094038575, + "y": -41.91926923458939, + "z": 0.0 + }, + { + "x": 392.3403182721886, + "y": -42.92150891295438, + "z": 0.0 + }, + { + "x": 392.33911560399144, + "y": -43.92374859131914, + "z": 0.0 + }, + { + "x": 392.3379129357943, + "y": -44.925988269684055, + "z": 0.0 + }, + { + "x": 392.33671026759725, + "y": -45.928227948049056, + "z": 0.0 + }, + { + "x": 392.3355075994001, + "y": -46.93046762641386, + "z": 0.0 + }, + { + "x": 392.33430493120295, + "y": -47.93270730477873, + "z": 0.0 + }, + { + "x": 392.3331022630058, + "y": -48.934946983143654, + "z": 0.0 + }, + { + "x": 392.33189959480865, + "y": -49.93718666150863, + "z": 0.0 + }, + { + "x": 392.3306969266115, + "y": -50.93942633987341, + "z": 0.0 + }, + { + "x": 392.32949425841434, + "y": -51.94166601823832, + "z": 0.0 + }, + { + "x": 392.3282915902173, + "y": -52.94390569660331, + "z": 0.0 + }, + { + "x": 392.32708892202004, + "y": -53.94614537496807, + "z": 0.0 + }, + { + "x": 392.325886253823, + "y": -54.94838505333317, + "z": 0.0 + }, + { + "x": 392.32468358562585, + "y": -55.950624731698085, + "z": 0.0 + }, + { + "x": 392.3234809174287, + "y": -56.95286441006274, + "z": 0.0 + }, + { + "x": 392.32227824923154, + "y": -57.95510408842783, + "z": 0.0 + }, + { + "x": 392.3210755810344, + "y": -58.95734376679265, + "z": 0.0 + }, + { + "x": 392.31987291283724, + "y": -59.95958344515752, + "z": 0.0 + }, + { + "x": 392.3186702446402, + "y": -60.96182312352255, + "z": 0.0 + }, + { + "x": 392.31746757644305, + "y": -61.96406280188731, + "z": 0.0 + }, + { + "x": 392.3162649082459, + "y": -62.96630248025218, + "z": 0.0 + }, + { + "x": 392.31506224004875, + "y": -63.9685421586172, + "z": 0.0 + }, + { + "x": 392.3138595718516, + "y": -64.97078183698198, + "z": 0.0 + }, + { + "x": 392.31265690365444, + "y": -65.97302151534689, + "z": 0.0 + }, + { + "x": 392.3114542354573, + "y": -66.97526119371182, + "z": 0.0 + }, + { + "x": 392.31025156726025, + "y": -67.97750087207682, + "z": 0.0 + }, + { + "x": 392.3090488990631, + "y": -68.9797405504416, + "z": 0.0 + }, + { + "x": 392.30784623086595, + "y": -69.9819802288065, + "z": 0.0 + }, + { + "x": 392.3066435626688, + "y": -70.98421990717151, + "z": 0.0 + }, + { + "x": 392.30544089447164, + "y": -71.98645958553631, + "z": 0.0 + }, + { + "x": 392.3042382262745, + "y": -72.9886992639012, + "z": 0.0 + }, + { + "x": 392.30303555807745, + "y": -73.99093894226624, + "z": 0.0 + }, + { + "x": 392.3018328898803, + "y": -74.99317862063103, + "z": 0.0 + }, + { + "x": 392.30063022168315, + "y": -75.99541829899593, + "z": 0.0 + }, + { + "x": 392.299427553486, + "y": -76.99765797736083, + "z": 0.0 + }, + { + "x": 392.29822488528885, + "y": -77.99989765572572, + "z": 0.0 + }, + { + "x": 392.2970222170917, + "y": -79.00213733409073, + "z": 0.0 + }, + { + "x": 392.29581954889454, + "y": -80.00437701245552, + "z": 0.0 + }, + { + "x": 392.2946168806974, + "y": -81.00661669082042, + "z": 0.0 + }, + { + "x": 392.29341421250035, + "y": -82.00885636918544, + "z": 0.0 + }, + { + "x": 392.2922115443032, + "y": -83.01109604755025, + "z": 0.0 + }, + { + "x": 392.29100887610605, + "y": -84.01333572591514, + "z": 0.0 + }, + { + "x": 392.2898062079089, + "y": -85.01557540428016, + "z": 0.0 + }, + { + "x": 392.28860353971174, + "y": -86.01781508264494, + "z": 0.0 + }, + { + "x": 392.28766149654416, + "y": -87.02057617958035, + "z": 0.0 + }, + { + "x": 392.2876355624428, + "y": -88.02464529335074, + "z": 0.0 + }, + { + "x": 392.28784710768696, + "y": -89.02735772066114, + "z": 0.0 + }, + { + "x": 392.28805860902287, + "y": -90.02959809829855, + "z": 0.0 + }, + { + "x": 392.2882701103588, + "y": -91.03183847593607, + "z": 0.0 + }, + { + "x": 392.2884816116947, + "y": -92.03407885357356, + "z": 0.0 + }, + { + "x": 392.2886931130306, + "y": -93.03631923121097, + "z": 0.0 + }, + { + "x": 392.2889046143665, + "y": -94.03855960884869, + "z": 0.0 + }, + { + "x": 392.2891161157024, + "y": -95.04079998648609, + "z": 0.0 + }, + { + "x": 392.28932761703834, + "y": -96.04304036412358, + "z": 0.0 + }, + { + "x": 392.28953911837425, + "y": -97.04528074176099, + "z": 0.0 + }, + { + "x": 392.28975061971016, + "y": -98.04752111939871, + "z": 0.0 + }, + { + "x": 392.2899621210461, + "y": -99.04976149703614, + "z": 0.0 + }, + { + "x": 392.290173622382, + "y": -100.05200187467364, + "z": 0.0 + }, + { + "x": 392.2903851237179, + "y": -101.05424225231116, + "z": 0.0 + }, + { + "x": 392.2905966250538, + "y": -102.05648262994853, + "z": 0.0 + }, + { + "x": 392.2908081263897, + "y": -103.05872300758628, + "z": 0.0 + }, + { + "x": 392.29101962772563, + "y": -104.06096338522366, + "z": 0.0 + }, + { + "x": 392.29123112906154, + "y": -105.06320376286115, + "z": 0.0 + }, + { + "x": 392.29144263039746, + "y": -106.06544414049858, + "z": 0.0 + }, + { + "x": 392.29165413173337, + "y": -107.06768451813619, + "z": 0.0 + }, + { + "x": 392.2918656330693, + "y": -108.06992489577371, + "z": 0.0 + }, + { + "x": 392.2920771344052, + "y": -109.07216527341131, + "z": 0.0 + }, + { + "x": 392.2922886357411, + "y": -110.07440565104861, + "z": 0.0 + }, + { + "x": 392.292500137077, + "y": -111.07664602868621, + "z": 0.0 + }, + { + "x": 392.2927116384129, + "y": -112.07888640632373, + "z": 0.0 + }, + { + "x": 392.29292313974884, + "y": -113.08112678396134, + "z": 0.0 + }, + { + "x": 392.29313464108475, + "y": -114.08336716159872, + "z": 0.0 + }, + { + "x": 392.29334614242066, + "y": -115.08560753923612, + "z": 0.0 + }, + { + "x": 392.29355764375657, + "y": -116.08784791687373, + "z": 0.0 + }, + { + "x": 392.2937691450925, + "y": -117.09008829451122, + "z": 0.0 + }, + { + "x": 392.2939806464284, + "y": -118.09232867214882, + "z": 0.0 + }, + { + "x": 392.2941921477643, + "y": -119.09456904978609, + "z": 0.0 + }, + { + "x": 392.2944036491002, + "y": -120.09680942742372, + "z": 0.0 + }, + { + "x": 392.29461515043613, + "y": -121.09904980506121, + "z": 0.0 + }, + { + "x": 392.29482665177204, + "y": -122.10129018269873, + "z": 0.0 + }, + { + "x": 392.29503815310795, + "y": -123.10353056033637, + "z": 0.0 + }, + { + "x": 392.29524965444386, + "y": -124.10577093797363, + "z": 0.0 + }, + { + "x": 392.2954611557798, + "y": -125.10801131561124, + "z": 0.0 + }, + { + "x": 392.2956726571157, + "y": -126.11025169324873, + "z": 0.0 + }, + { + "x": 392.2958841584516, + "y": -127.11249207088633, + "z": 0.0 + }, + { + "x": 392.2960956597875, + "y": -128.11473244852363, + "z": 0.0 + }, + { + "x": 392.2963071611234, + "y": -129.1169728261612, + "z": 0.0 + }, + { + "x": 392.29651866245933, + "y": -130.11921320379872, + "z": 0.0 + }, + { + "x": 392.29673016379525, + "y": -131.12145358143624, + "z": 0.0 + }, + { + "x": 392.29694166513116, + "y": -132.12369395907376, + "z": 0.0 + }, + { + "x": 392.29715316646707, + "y": -133.12593433671128, + "z": 0.0 + }, + { + "x": 392.297364667803, + "y": -134.1281747143488, + "z": 0.0 + }, + { + "x": 392.2975761691389, + "y": -135.13041509198626, + "z": 0.0 + }, + { + "x": 392.2977876704748, + "y": -136.1326554696239, + "z": 0.0 + }, + { + "x": 392.2979991718107, + "y": -137.1348958472612, + "z": 0.0 + }, + { + "x": 392.2982106731466, + "y": -138.13713622489882, + "z": 0.0 + }, + { + "x": 392.29842217448254, + "y": -139.13937660253634, + "z": 0.0 + }, + { + "x": 392.29863367581845, + "y": -140.14161698017386, + "z": 0.0 + }, + { + "x": 392.29884517715436, + "y": -141.14385735781133, + "z": 0.0 + }, + { + "x": 392.2990566784903, + "y": -142.14609773544885, + "z": 0.0 + }, + { + "x": 392.2992681798262, + "y": -143.14833811308637, + "z": 0.0 + }, + { + "x": 392.2994796811621, + "y": -144.1505784907239, + "z": 0.0 + }, + { + "x": 392.299691182498, + "y": -145.15281886836135, + "z": 0.0 + }, + { + "x": 392.2999026838339, + "y": -146.15505924599887, + "z": 0.0 + }, + { + "x": 392.30011418516983, + "y": -147.1572996236364, + "z": 0.0 + }, + { + "x": 392.30032568650574, + "y": -148.1595400012739, + "z": 0.0 + }, + { + "x": 392.30053718784166, + "y": -149.16178037891143, + "z": 0.0 + }, + { + "x": 392.30074868917757, + "y": -150.1640207565489, + "z": 0.0 + }, + { + "x": 392.3009601905135, + "y": -151.1662611341864, + "z": 0.0 + }, + { + "x": 392.3011716918494, + "y": -152.16850151182393, + "z": 0.0 + }, + { + "x": 392.3013831931853, + "y": -153.17074188946145, + "z": 0.0 + }, + { + "x": 392.3015946945211, + "y": -154.17298226709886, + "z": 0.0 + }, + { + "x": 392.3018061958571, + "y": -155.1752226447366, + "z": 0.0 + }, + { + "x": 392.30201769719304, + "y": -156.17746302237396, + "z": 0.0 + }, + { + "x": 392.30222919852895, + "y": -157.17970340001148, + "z": 0.0 + }, + { + "x": 392.30244069986486, + "y": -158.181943777649, + "z": 0.0 + }, + { + "x": 392.30265220120066, + "y": -159.1841841552864, + "z": 0.0 + }, + { + "x": 392.3028637025367, + "y": -160.1864245329241, + "z": 0.0 + }, + { + "x": 392.3030752038726, + "y": -161.1886649105615, + "z": 0.0 + }, + { + "x": 392.3032867052085, + "y": -162.19090528819902, + "z": 0.0 + }, + { + "x": 392.3034982065443, + "y": -163.19314566583643, + "z": 0.0 + }, + { + "x": 392.30370970788033, + "y": -164.19538604347417, + "z": 0.0 + }, + { + "x": 392.30392120921624, + "y": -165.19762642111158, + "z": 0.0 + }, + { + "x": 392.30413271055215, + "y": -166.19986679874904, + "z": 0.0 + }, + { + "x": 392.30434421188806, + "y": -167.20210717638656, + "z": 0.0 + }, + { + "x": 392.30455571322386, + "y": -168.20434755402397, + "z": 0.0 + }, + { + "x": 392.3047672145599, + "y": -169.20658793166172, + "z": 0.0 + }, + { + "x": 392.3049787158958, + "y": -170.20882830929912, + "z": 0.0 + }, + { + "x": 392.3051902172317, + "y": -171.21106868693664, + "z": 0.0 + }, + { + "x": 392.3054017185675, + "y": -172.213309064574, + "z": 0.0 + }, + { + "x": 392.30561321990353, + "y": -173.21554944221174, + "z": 0.0 + }, + { + "x": 392.30582472123945, + "y": -174.21778981984914, + "z": 0.0 + }, + { + "x": 392.30603622257536, + "y": -175.22003019748666, + "z": 0.0 + }, + { + "x": 392.30624772391127, + "y": -176.22227057512413, + "z": 0.0 + }, + { + "x": 392.30645922524707, + "y": -177.22451095276153, + "z": 0.0 + }, + { + "x": 392.3066707265831, + "y": -178.22675133039928, + "z": 0.0 + }, + { + "x": 392.306882227919, + "y": -179.2289917080367, + "z": 0.0 + }, + { + "x": 392.3070937292549, + "y": -180.2312320856742, + "z": 0.0 + }, + { + "x": 392.3073052305907, + "y": -181.23347246331156, + "z": 0.0 + }, + { + "x": 392.3075167319266, + "y": -182.2357128409492, + "z": 0.0 + }, + { + "x": 392.30772823326265, + "y": -183.23795321858682, + "z": 0.0 + }, + { + "x": 392.30793973459856, + "y": -184.24019359622423, + "z": 0.0 + }, + { + "x": 392.30815123593436, + "y": -185.24243397386158, + "z": 0.0 + }, + { + "x": 392.3083627372703, + "y": -186.24467435149927, + "z": 0.0 + }, + { + "x": 392.3085742386063, + "y": -187.24691472913685, + "z": 0.0 + }, + { + "x": 392.3087857399422, + "y": -188.24915510677425, + "z": 0.0 + }, + { + "x": 392.3089972412781, + "y": -189.25139548441177, + "z": 0.0 + }, + { + "x": 392.3092087426139, + "y": -190.25363586204918, + "z": 0.0 + }, + { + "x": 392.30942024394983, + "y": -191.25587623968676, + "z": 0.0 + }, + { + "x": 392.30963174528574, + "y": -192.25811661732428, + "z": 0.0 + }, + { + "x": 392.30984324662177, + "y": -193.26035699496185, + "z": 0.0 + }, + { + "x": 392.31005474795757, + "y": -194.26259737259915, + "z": 0.0 + }, + { + "x": 392.3102662492935, + "y": -195.26483775023672, + "z": 0.0 + }, + { + "x": 392.3104777506294, + "y": -196.26707812787424, + "z": 0.0 + }, + { + "x": 392.3106892519653, + "y": -197.26931850551176, + "z": 0.0 + }, + { + "x": 392.3109007533013, + "y": -198.27155888314934, + "z": 0.0 + }, + { + "x": 392.3111122546371, + "y": -199.27379926078663, + "z": 0.0 + }, + { + "x": 392.31132375597304, + "y": -200.2760396384242, + "z": 0.0 + }, + { + "x": 392.31153525730895, + "y": -201.27828001606173, + "z": 0.0 + }, + { + "x": 392.311746758645, + "y": -202.2805203936993, + "z": 0.0 + }, + { + "x": 392.31195825998077, + "y": -203.2827607713366, + "z": 0.0 + }, + { + "x": 392.3121697613167, + "y": -204.28500114897423, + "z": 0.0 + }, + { + "x": 392.3123812626526, + "y": -205.2872415266117, + "z": 0.0 + }, + { + "x": 392.3125927639885, + "y": -206.28948190424921, + "z": 0.0 + }, + { + "x": 392.3128042653244, + "y": -207.29172228188668, + "z": 0.0 + }, + { + "x": 392.3130157666603, + "y": -208.2939626595242, + "z": 0.0 + }, + { + "x": 392.31322726799624, + "y": -209.29620303716166, + "z": 0.0 + }, + { + "x": 392.31343876933215, + "y": -210.29844341479918, + "z": 0.0 + }, + { + "x": 392.3136502706682, + "y": -211.30068379243676, + "z": 0.0 + }, + { + "x": 392.313861772004, + "y": -212.30292417007405, + "z": 0.0 + }, + { + "x": 392.3140732733399, + "y": -213.30516454771168, + "z": 0.0 + }, + { + "x": 392.3142847746758, + "y": -214.30740492534915, + "z": 0.0 + }, + { + "x": 392.3144962760117, + "y": -215.30964530298667, + "z": 0.0 + }, + { + "x": 392.3147077773476, + "y": -216.31188568062413, + "z": 0.0 + }, + { + "x": 392.31491927868353, + "y": -217.31412605826165, + "z": 0.0 + }, + { + "x": 392.31513078001944, + "y": -218.3163664358991, + "z": 0.0 + }, + { + "x": 392.31534228135536, + "y": -219.31860681353663, + "z": 0.0 + }, + { + "x": 392.31555378269127, + "y": -220.32084719117415, + "z": 0.0 + }, + { + "x": 392.3157652840272, + "y": -221.32308756881162, + "z": 0.0 + }, + { + "x": 392.3159767853631, + "y": -222.32532794644914, + "z": 0.0 + }, + { + "x": 392.316188286699, + "y": -223.3275683240866, + "z": 0.0 + }, + { + "x": 392.3163997880349, + "y": -224.32980870172412, + "z": 0.0 + }, + { + "x": 392.3166112893708, + "y": -225.33204907936158, + "z": 0.0 + }, + { + "x": 392.31682279070674, + "y": -226.3342894569991, + "z": 0.0 + }, + { + "x": 392.31703429204265, + "y": -227.33652983463656, + "z": 0.0 + }, + { + "x": 392.31724579337856, + "y": -228.33877021227408, + "z": 0.0 + }, + { + "x": 392.3174572947145, + "y": -229.3410105899116, + "z": 0.0 + }, + { + "x": 392.3176687960504, + "y": -230.34325096754907, + "z": 0.0 + }, + { + "x": 392.3178802973863, + "y": -231.3454913451866, + "z": 0.0 + }, + { + "x": 392.3180917987222, + "y": -232.34773172282405, + "z": 0.0 + }, + { + "x": 392.3183033000581, + "y": -233.34997210046157, + "z": 0.0 + }, + { + "x": 392.31851480139403, + "y": -234.35221247809903, + "z": 0.0 + }, + { + "x": 392.31872630272994, + "y": -235.35445285573655, + "z": 0.0 + }, + { + "x": 392.31893780406585, + "y": -236.35669323337402, + "z": 0.0 + }, + { + "x": 392.31914930540177, + "y": -237.35893361101154, + "z": 0.0 + }, + { + "x": 392.3193608067377, + "y": -238.361173988649, + "z": 0.0 + }, + { + "x": 392.3195723080736, + "y": -239.36341436628652, + "z": 0.0 + }, + { + "x": 392.3197838094095, + "y": -240.36565474392398, + "z": 0.0 + }, + { + "x": 392.320010857335, + "y": -241.36792612738765, + "z": 0.0 + }, + { + "x": 392.32040050640666, + "y": -242.37049074145193, + "z": 0.0 + }, + { + "x": 392.3207900557248, + "y": -243.372731065701, + "z": 0.0 + }, + { + "x": 392.3211796050429, + "y": -244.3749713899502, + "z": 0.0 + }, + { + "x": 392.3215691543611, + "y": -245.37721171419946, + "z": 0.0 + }, + { + "x": 392.3219587036793, + "y": -246.37945203844865, + "z": 0.0 + }, + { + "x": 392.3223482529974, + "y": -247.3816923626977, + "z": 0.0 + }, + { + "x": 392.3227378023156, + "y": -248.383932686947, + "z": 0.0 + }, + { + "x": 392.32312735163373, + "y": -249.38617301119606, + "z": 0.0 + }, + { + "x": 392.32351690095186, + "y": -250.38841333544522, + "z": 0.0 + }, + { + "x": 392.32390645027004, + "y": -251.39065365969452, + "z": 0.0 + }, + { + "x": 392.32429599958823, + "y": -252.3928939839437, + "z": 0.0 + }, + { + "x": 392.32468554890636, + "y": -253.39513430819275, + "z": 0.0 + }, + { + "x": 392.32507509822454, + "y": -254.39737463244205, + "z": 0.0 + }, + { + "x": 392.3254646475427, + "y": -255.3996149566911, + "z": 0.0 + }, + { + "x": 392.3258541968608, + "y": -256.40185528094025, + "z": 0.0 + }, + { + "x": 392.326243746179, + "y": -257.4040956051896, + "z": 0.0 + }, + { + "x": 392.3266332954972, + "y": -258.4063359294388, + "z": 0.0 + }, + { + "x": 392.3270228448153, + "y": -259.4085762536878, + "z": 0.0 + }, + { + "x": 392.32741239413343, + "y": -260.410816577937, + "z": 0.0 + }, + { + "x": 392.3278019434516, + "y": -261.4130569021863, + "z": 0.0 + }, + { + "x": 392.3281914927698, + "y": -262.4152972264354, + "z": 0.0 + }, + { + "x": 392.32858104208793, + "y": -263.41753755068453, + "z": 0.0 + }, + { + "x": 392.3289705914061, + "y": -264.41977787493386, + "z": 0.0 + }, + { + "x": 392.3293601407243, + "y": -265.42201819918296, + "z": 0.0 + }, + { + "x": 392.32974969004243, + "y": -266.42425852343206, + "z": 0.0 + }, + { + "x": 392.33013923936056, + "y": -267.4264988476813, + "z": 0.0 + }, + { + "x": 392.33052878867875, + "y": -268.4287391719305, + "z": 0.0 + }, + { + "x": 392.3309183379969, + "y": -269.4309794961796, + "z": 0.0 + }, + { + "x": 392.33130788731506, + "y": -270.4332198204289, + "z": 0.0 + }, + { + "x": 392.3316974366332, + "y": -271.4354601446779, + "z": 0.0 + }, + { + "x": 392.3320869859513, + "y": -272.4377004689271, + "z": 0.0 + }, + { + "x": 392.3324765352695, + "y": -273.43994079317645, + "z": 0.0 + }, + { + "x": 392.3328660845877, + "y": -274.44218111742555, + "z": 0.0 + }, + { + "x": 392.3332556339058, + "y": -275.44442144167465, + "z": 0.0 + }, + { + "x": 392.333645183224, + "y": -276.446661765924, + "z": 0.0 + }, + { + "x": 392.33403473254214, + "y": -277.44890209017296, + "z": 0.0 + }, + { + "x": 392.3344242818603, + "y": -278.4511424144223, + "z": 0.0 + }, + { + "x": 392.33481383117845, + "y": -279.4533827386713, + "z": 0.0 + }, + { + "x": 392.33520338049664, + "y": -280.4556230629206, + "z": 0.0 + }, + { + "x": 392.3355929298148, + "y": -281.4578633871698, + "z": 0.0 + }, + { + "x": 392.33598247913295, + "y": -282.4601037114189, + "z": 0.0 + }, + { + "x": 392.3363720284511, + "y": -283.462344035668, + "z": 0.0 + }, + { + "x": 392.33676157776927, + "y": -284.46458435991735, + "z": 0.0 + }, + { + "x": 392.3371511270874, + "y": -285.46682468416645, + "z": 0.0 + }, + { + "x": 392.3375406764056, + "y": -286.46906500841567, + "z": 0.0 + }, + { + "x": 392.33793022572377, + "y": -287.4713053326649, + "z": 0.0 + }, + { + "x": 392.3383197750419, + "y": -288.473545656914, + "z": 0.0 + }, + { + "x": 392.33870932436, + "y": -289.4757859811631, + "z": 0.0 + }, + { + "x": 392.3390988736782, + "y": -290.4780263054124, + "z": 0.0 + }, + { + "x": 392.33948842299634, + "y": -291.4802666296615, + "z": 0.0 + }, + { + "x": 392.3398779723145, + "y": -292.48250695391073, + "z": 0.0 + }, + { + "x": 392.34026752163265, + "y": -293.48474727815983, + "z": 0.0 + }, + { + "x": 392.34065707095084, + "y": -294.48698760240916, + "z": 0.0 + }, + { + "x": 392.34104662026897, + "y": -295.48922792665815, + "z": 0.0 + }, + { + "x": 392.34143616958715, + "y": -296.4914682509075, + "z": 0.0 + }, + { + "x": 392.34182571890534, + "y": -297.4937085751567, + "z": 0.0 + }, + { + "x": 392.34221526822347, + "y": -298.4959488994057, + "z": 0.0 + }, + { + "x": 392.3426048175416, + "y": -299.4981892236549, + "z": 0.0 + }, + { + "x": 392.3429943668598, + "y": -300.5004295479042, + "z": 0.0 + }, + { + "x": 392.3433839161779, + "y": -301.5026698721533, + "z": 0.0 + }, + { + "x": 392.3437734654961, + "y": -302.50491019640253, + "z": 0.0 + }, + { + "x": 392.3441630148143, + "y": -303.50715052065175, + "z": 0.0 + }, + { + "x": 392.3445525641324, + "y": -304.50939084490085, + "z": 0.0 + }, + { + "x": 392.34494211345054, + "y": -305.51163116914995, + "z": 0.0 + }, + { + "x": 392.34533166276873, + "y": -306.5138714933993, + "z": 0.0 + }, + { + "x": 392.34572121208686, + "y": -307.5161118176484, + "z": 0.0 + }, + { + "x": 392.34611076140504, + "y": -308.5183521418976, + "z": 0.0 + }, + { + "x": 392.34650031072323, + "y": -309.5205924661468, + "z": 0.0 + }, + { + "x": 392.34688986004136, + "y": -310.5228327903959, + "z": 0.0 + }, + { + "x": 392.3472794093595, + "y": -311.525073114645, + "z": 0.0 + }, + { + "x": 392.3476689586777, + "y": -312.52731343889434, + "z": 0.0 + }, + { + "x": 392.34805850799586, + "y": -313.52955376314355, + "z": 0.0 + }, + { + "x": 392.348448057314, + "y": -314.53179408739265, + "z": 0.0 + }, + { + "x": 392.3488376066322, + "y": -315.53403441164187, + "z": 0.0 + }, + { + "x": 392.3492271559503, + "y": -316.53627473589097, + "z": 0.0 + }, + { + "x": 392.34961670526843, + "y": -317.5385150601402, + "z": 0.0 + }, + { + "x": 392.3500062545866, + "y": -318.5407553843894, + "z": 0.0 + } + ] + }, + { + "id": 44, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 396.35000595244463, + "y": -318.5392006702981, + "z": 0.0 + }, + { + "x": 396.34961640312645, + "y": -317.536960346049, + "z": 0.0 + }, + { + "x": 396.3492268538083, + "y": -316.5347200217998, + "z": 0.0 + }, + { + "x": 396.3488373044902, + "y": -315.53247969755057, + "z": 0.0 + }, + { + "x": 396.348447755172, + "y": -314.53023937330147, + "z": 0.0 + }, + { + "x": 396.3480582058539, + "y": -313.52799904905214, + "z": 0.0 + }, + { + "x": 396.3476686565357, + "y": -312.52575872480304, + "z": 0.0 + }, + { + "x": 396.3472791072175, + "y": -311.52351840055394, + "z": 0.0 + }, + { + "x": 396.3468895578994, + "y": -310.5212780763047, + "z": 0.0 + }, + { + "x": 396.34650000858124, + "y": -309.5190377520555, + "z": 0.0 + }, + { + "x": 396.34611045926306, + "y": -308.5167974278063, + "z": 0.0 + }, + { + "x": 396.3457209099449, + "y": -307.5145571035572, + "z": 0.0 + }, + { + "x": 396.34533136062674, + "y": -306.51231677930787, + "z": 0.0 + }, + { + "x": 396.34494181130856, + "y": -305.5100764550589, + "z": 0.0 + }, + { + "x": 396.34455226199043, + "y": -304.50783613080966, + "z": 0.0 + }, + { + "x": 396.3441627126723, + "y": -303.50559580656034, + "z": 0.0 + }, + { + "x": 396.3437731633541, + "y": -302.50335548231124, + "z": 0.0 + }, + { + "x": 396.34338361403593, + "y": -301.50111515806213, + "z": 0.0 + }, + { + "x": 396.3429940647178, + "y": -300.4988748338128, + "z": 0.0 + }, + { + "x": 396.3426045153996, + "y": -299.4966345095638, + "z": 0.0 + }, + { + "x": 396.3422149660815, + "y": -298.4943941853146, + "z": 0.0 + }, + { + "x": 396.34182541676336, + "y": -297.4921538610653, + "z": 0.0 + }, + { + "x": 396.34143586744517, + "y": -296.4899135368162, + "z": 0.0 + }, + { + "x": 396.341046318127, + "y": -295.4876732125671, + "z": 0.0 + }, + { + "x": 396.34065676880886, + "y": -294.48543288831775, + "z": 0.0 + }, + { + "x": 396.34026721949067, + "y": -293.48319256406876, + "z": 0.0 + }, + { + "x": 396.33987767017254, + "y": -292.48095223981943, + "z": 0.0 + }, + { + "x": 396.33948812085436, + "y": -291.47871191557033, + "z": 0.0 + }, + { + "x": 396.3390985715362, + "y": -290.476471591321, + "z": 0.0 + }, + { + "x": 396.33870902221804, + "y": -289.474231267072, + "z": 0.0 + }, + { + "x": 396.3383194728999, + "y": -288.4719909428228, + "z": 0.0 + }, + { + "x": 396.3379299235818, + "y": -287.46975061857347, + "z": 0.0 + }, + { + "x": 396.3375403742636, + "y": -286.46751029432437, + "z": 0.0 + }, + { + "x": 396.3371508249454, + "y": -285.46526997007527, + "z": 0.0 + }, + { + "x": 396.3367612756273, + "y": -284.46302964582594, + "z": 0.0 + }, + { + "x": 396.3363717263091, + "y": -283.46078932157695, + "z": 0.0 + }, + { + "x": 396.33598217699097, + "y": -282.45854899732774, + "z": 0.0 + }, + { + "x": 396.33559262767284, + "y": -281.4563086730784, + "z": 0.0 + }, + { + "x": 396.33520307835465, + "y": -280.4540683488293, + "z": 0.0 + }, + { + "x": 396.33481352903647, + "y": -279.4518280245802, + "z": 0.0 + }, + { + "x": 396.33442397971834, + "y": -278.4495877003309, + "z": 0.0 + }, + { + "x": 396.33403443040015, + "y": -277.4473473760819, + "z": 0.0 + }, + { + "x": 396.333644881082, + "y": -276.44510705183257, + "z": 0.0 + }, + { + "x": 396.33325533176384, + "y": -275.44286672758346, + "z": 0.0 + }, + { + "x": 396.3328657824457, + "y": -274.44062640333425, + "z": 0.0 + }, + { + "x": 396.3324762331275, + "y": -273.43838607908503, + "z": 0.0 + }, + { + "x": 396.33208668380934, + "y": -272.43614575483593, + "z": 0.0 + }, + { + "x": 396.3316971344912, + "y": -271.43390543058683, + "z": 0.0 + }, + { + "x": 396.3313075851731, + "y": -270.4316651063375, + "z": 0.0 + }, + { + "x": 396.3309180358549, + "y": -269.4294247820884, + "z": 0.0 + }, + { + "x": 396.33052848653676, + "y": -268.4271844578392, + "z": 0.0 + }, + { + "x": 396.3301389372186, + "y": -267.4249441335901, + "z": 0.0 + }, + { + "x": 396.32974938790045, + "y": -266.4227038093409, + "z": 0.0 + }, + { + "x": 396.3293598385823, + "y": -265.42046348509166, + "z": 0.0 + }, + { + "x": 396.32897028926413, + "y": -264.41822316084244, + "z": 0.0 + }, + { + "x": 396.32858073994595, + "y": -263.41598283659334, + "z": 0.0 + }, + { + "x": 396.3281911906278, + "y": -262.41374251234413, + "z": 0.0 + }, + { + "x": 396.32780164130963, + "y": -261.4115021880949, + "z": 0.0 + }, + { + "x": 396.32741209199145, + "y": -260.4092618638458, + "z": 0.0 + }, + { + "x": 396.3270225426733, + "y": -259.4070215395967, + "z": 0.0 + }, + { + "x": 396.3266329933552, + "y": -258.4047812153474, + "z": 0.0 + }, + { + "x": 396.326243444037, + "y": -257.4025408910983, + "z": 0.0 + }, + { + "x": 396.3258538947188, + "y": -256.4003005668492, + "z": 0.0 + }, + { + "x": 396.3254643454007, + "y": -255.39806024259997, + "z": 0.0 + }, + { + "x": 396.32507479608256, + "y": -254.3958199183507, + "z": 0.0 + }, + { + "x": 396.3246852467644, + "y": -253.39357959410162, + "z": 0.0 + }, + { + "x": 396.32429569744625, + "y": -252.39133926985235, + "z": 0.0 + }, + { + "x": 396.32390614812806, + "y": -251.38909894560317, + "z": 0.0 + }, + { + "x": 396.3235165988099, + "y": -250.3868586213541, + "z": 0.0 + }, + { + "x": 396.32312704949175, + "y": -249.38461829710494, + "z": 0.0 + }, + { + "x": 396.3227375001736, + "y": -248.38237797285564, + "z": 0.0 + }, + { + "x": 396.32234795085543, + "y": -247.38013764860656, + "z": 0.0 + }, + { + "x": 396.3219584015373, + "y": -246.3778973243573, + "z": 0.0 + }, + { + "x": 396.3215688522191, + "y": -245.3756570001081, + "z": 0.0 + }, + { + "x": 396.32117930290093, + "y": -244.37341667585906, + "z": 0.0 + }, + { + "x": 396.3207897535828, + "y": -243.37117635160988, + "z": 0.0 + }, + { + "x": 396.3204002042647, + "y": -242.36893602736058, + "z": 0.0 + }, + { + "x": 396.3200107547, + "y": -241.3670199929265, + "z": 0.0 + }, + { + "x": 396.3197837203435, + "y": -240.36481062973377, + "z": 0.0 + }, + { + "x": 396.31957221900757, + "y": -239.36257025209625, + "z": 0.0 + }, + { + "x": 396.31936071767166, + "y": -238.36032987445878, + "z": 0.0 + }, + { + "x": 396.31914921633575, + "y": -237.35808949682126, + "z": 0.0 + }, + { + "x": 396.31893771499983, + "y": -236.3558491191838, + "z": 0.0 + }, + { + "x": 396.3187262136639, + "y": -235.35360874154628, + "z": 0.0 + }, + { + "x": 396.318514712328, + "y": -234.35136836390882, + "z": 0.0 + }, + { + "x": 396.3183032109921, + "y": -233.3491279862713, + "z": 0.0 + }, + { + "x": 396.3180917096562, + "y": -232.34688760863384, + "z": 0.0 + }, + { + "x": 396.3178802083203, + "y": -231.34464723099632, + "z": 0.0 + }, + { + "x": 396.31766870698436, + "y": -230.34240685335885, + "z": 0.0 + }, + { + "x": 396.31745720564845, + "y": -229.34016647572133, + "z": 0.0 + }, + { + "x": 396.31724570431254, + "y": -228.33792609808387, + "z": 0.0 + }, + { + "x": 396.31703420297663, + "y": -227.33568572044635, + "z": 0.0 + }, + { + "x": 396.3168227016407, + "y": -226.33344534280883, + "z": 0.0 + }, + { + "x": 396.3166112003048, + "y": -225.33120496517137, + "z": 0.0 + }, + { + "x": 396.3163996989689, + "y": -224.32896458753385, + "z": 0.0 + }, + { + "x": 396.316188197633, + "y": -223.32672420989638, + "z": 0.0 + }, + { + "x": 396.31597669629707, + "y": -222.32448383225886, + "z": 0.0 + }, + { + "x": 396.31576519496116, + "y": -221.3222434546214, + "z": 0.0 + }, + { + "x": 396.31555369362525, + "y": -220.32000307698388, + "z": 0.0 + }, + { + "x": 396.31534219228934, + "y": -219.31776269934636, + "z": 0.0 + }, + { + "x": 396.3151306909534, + "y": -218.3155223217089, + "z": 0.0 + }, + { + "x": 396.3149191896175, + "y": -217.31328194407138, + "z": 0.0 + }, + { + "x": 396.3147076882816, + "y": -216.31104156643391, + "z": 0.0 + }, + { + "x": 396.3144961869457, + "y": -215.3088011887964, + "z": 0.0 + }, + { + "x": 396.3142846856098, + "y": -214.30656081115893, + "z": 0.0 + }, + { + "x": 396.31407318427387, + "y": -213.3043204335214, + "z": 0.0 + }, + { + "x": 396.31386168293795, + "y": -212.30208005588406, + "z": 0.0 + }, + { + "x": 396.31365018160204, + "y": -211.29983967824631, + "z": 0.0 + }, + { + "x": 396.31343868026613, + "y": -210.2975993006089, + "z": 0.0 + }, + { + "x": 396.3132271789302, + "y": -209.29535892297145, + "z": 0.0 + }, + { + "x": 396.3130156775943, + "y": -208.29311854533393, + "z": 0.0 + }, + { + "x": 396.3128041762584, + "y": -207.29087816769646, + "z": 0.0 + }, + { + "x": 396.3125926749225, + "y": -206.28863779005894, + "z": 0.0 + }, + { + "x": 396.3123811735866, + "y": -205.28639741242148, + "z": 0.0 + }, + { + "x": 396.31216967225066, + "y": -204.28415703478396, + "z": 0.0 + }, + { + "x": 396.31195817091475, + "y": -203.28191665714655, + "z": 0.0 + }, + { + "x": 396.31174666957884, + "y": -202.27967627950886, + "z": 0.0 + }, + { + "x": 396.3115351682429, + "y": -201.27743590187146, + "z": 0.0 + }, + { + "x": 396.311323666907, + "y": -200.275195524234, + "z": 0.0 + }, + { + "x": 396.3111121655711, + "y": -199.2729551465966, + "z": 0.0 + }, + { + "x": 396.3109006642352, + "y": -198.2707147689589, + "z": 0.0 + }, + { + "x": 396.3106891628993, + "y": -197.2684743913215, + "z": 0.0 + }, + { + "x": 396.31047766156337, + "y": -196.26623401368403, + "z": 0.0 + }, + { + "x": 396.31026616022746, + "y": -195.2639936360465, + "z": 0.0 + }, + { + "x": 396.31005465889154, + "y": -194.2617532584091, + "z": 0.0 + }, + { + "x": 396.30984315755563, + "y": -193.2595128807714, + "z": 0.0 + }, + { + "x": 396.3096316562197, + "y": -192.257272503134, + "z": 0.0 + }, + { + "x": 396.3094201548838, + "y": -191.25503212549654, + "z": 0.0 + }, + { + "x": 396.3092086535479, + "y": -190.25279174785913, + "z": 0.0 + }, + { + "x": 396.308997152212, + "y": -189.2505513702215, + "z": 0.0 + }, + { + "x": 396.3087856508761, + "y": -188.24831099258404, + "z": 0.0 + }, + { + "x": 396.30857414954016, + "y": -187.2460706149464, + "z": 0.0 + }, + { + "x": 396.30836264820425, + "y": -186.243830237309, + "z": 0.0 + }, + { + "x": 396.30815114686834, + "y": -185.2415898596716, + "z": 0.0 + }, + { + "x": 396.30793964553243, + "y": -184.23934948203396, + "z": 0.0 + }, + { + "x": 396.3077281441965, + "y": -183.23710910439632, + "z": 0.0 + }, + { + "x": 396.3075166428606, + "y": -182.23486872675898, + "z": 0.0 + }, + { + "x": 396.3073051415247, + "y": -181.23262834912157, + "z": 0.0 + }, + { + "x": 396.3070936401888, + "y": -180.23038797148394, + "z": 0.0 + }, + { + "x": 396.30688213885287, + "y": -179.22814759384647, + "z": 0.0 + }, + { + "x": 396.30667063751696, + "y": -178.22590721620878, + "z": 0.0 + }, + { + "x": 396.30645913618105, + "y": -177.22366683857155, + "z": 0.0 + }, + { + "x": 396.30624763484514, + "y": -176.2214264609339, + "z": 0.0 + }, + { + "x": 396.3060361335092, + "y": -175.2191860832964, + "z": 0.0 + }, + { + "x": 396.3058246321733, + "y": -174.21694570565887, + "z": 0.0 + }, + { + "x": 396.3056131308374, + "y": -173.2147053280213, + "z": 0.0 + }, + { + "x": 396.3054016295015, + "y": -172.212464950384, + "z": 0.0 + }, + { + "x": 396.3051901281656, + "y": -171.21022457274637, + "z": 0.0 + }, + { + "x": 396.30497862682967, + "y": -170.20798419510885, + "z": 0.0 + }, + { + "x": 396.30476712549375, + "y": -169.20574381747122, + "z": 0.0 + }, + { + "x": 396.30455562415784, + "y": -168.20350343983392, + "z": 0.0 + }, + { + "x": 396.30434412282193, + "y": -167.20126306219635, + "z": 0.0 + }, + { + "x": 396.304132621486, + "y": -166.19902268455883, + "z": 0.0 + }, + { + "x": 396.3039211201501, + "y": -165.1967823069213, + "z": 0.0 + }, + { + "x": 396.3037096188142, + "y": -164.19454192928367, + "z": 0.0 + }, + { + "x": 396.3034981174783, + "y": -163.19230155164644, + "z": 0.0 + }, + { + "x": 396.3032866161424, + "y": -162.19006117400875, + "z": 0.0 + }, + { + "x": 396.30307511480646, + "y": -161.18782079637128, + "z": 0.0 + }, + { + "x": 396.30286361347055, + "y": -160.18558041873365, + "z": 0.0 + }, + { + "x": 396.30265211213464, + "y": -159.18334004109636, + "z": 0.0 + }, + { + "x": 396.3024406107987, + "y": -158.18109966345872, + "z": 0.0 + }, + { + "x": 396.3022291094628, + "y": -157.17885928582126, + "z": 0.0 + }, + { + "x": 396.3020176081269, + "y": -156.17661890818374, + "z": 0.0 + }, + { + "x": 396.301806106791, + "y": -155.1743785305461, + "z": 0.0 + }, + { + "x": 396.3015946054551, + "y": -154.17213815290881, + "z": 0.0 + }, + { + "x": 396.30138310411917, + "y": -153.16989777527118, + "z": 0.0 + }, + { + "x": 396.30117160278326, + "y": -152.16765739763366, + "z": 0.0 + }, + { + "x": 396.30096010144734, + "y": -151.1654170199962, + "z": 0.0 + }, + { + "x": 396.30074860011143, + "y": -150.16317664235868, + "z": 0.0 + }, + { + "x": 396.3005370987755, + "y": -149.16093626472116, + "z": 0.0 + }, + { + "x": 396.3003255974396, + "y": -148.1586958870837, + "z": 0.0 + }, + { + "x": 396.3001140961037, + "y": -147.15645550944618, + "z": 0.0 + }, + { + "x": 396.2999025947678, + "y": -146.15421513180866, + "z": 0.0 + }, + { + "x": 396.2996910934319, + "y": -145.15197475417114, + "z": 0.0 + }, + { + "x": 396.29947959209596, + "y": -144.14973437653362, + "z": 0.0 + }, + { + "x": 396.29926809076005, + "y": -143.1474939988961, + "z": 0.0 + }, + { + "x": 396.29905658942414, + "y": -142.14525362125863, + "z": 0.0 + }, + { + "x": 396.2988450880882, + "y": -141.1430132436211, + "z": 0.0 + }, + { + "x": 396.2986335867523, + "y": -140.1407728659836, + "z": 0.0 + }, + { + "x": 396.2984220854164, + "y": -139.13853248834607, + "z": 0.0 + }, + { + "x": 396.2982105840805, + "y": -138.1362921107086, + "z": 0.0 + }, + { + "x": 396.2979990827446, + "y": -137.13405173307115, + "z": 0.0 + }, + { + "x": 396.2977875814088, + "y": -136.13181135543346, + "z": 0.0 + }, + { + "x": 396.29757608007276, + "y": -135.12957097779605, + "z": 0.0 + }, + { + "x": 396.29736457873685, + "y": -134.12733060015853, + "z": 0.0 + }, + { + "x": 396.29715307740094, + "y": -133.125090222521, + "z": 0.0 + }, + { + "x": 396.296941576065, + "y": -132.12284984488355, + "z": 0.0 + }, + { + "x": 396.2967300747291, + "y": -131.12060946724603, + "z": 0.0 + }, + { + "x": 396.2965185733932, + "y": -130.1183690896085, + "z": 0.0 + }, + { + "x": 396.2963070720573, + "y": -129.116128711971, + "z": 0.0 + }, + { + "x": 396.2960955707214, + "y": -128.11388833433358, + "z": 0.0 + }, + { + "x": 396.2958840693856, + "y": -127.11164795669586, + "z": 0.0 + }, + { + "x": 396.29567256804955, + "y": -126.10940757905848, + "z": 0.0 + }, + { + "x": 396.29546106671364, + "y": -125.10716720142099, + "z": 0.0 + }, + { + "x": 396.29524956537773, + "y": -124.10492682378359, + "z": 0.0 + }, + { + "x": 396.29503806404193, + "y": -123.10268644614587, + "z": 0.0 + }, + { + "x": 396.2948265627059, + "y": -122.10044606850849, + "z": 0.0 + }, + { + "x": 396.29461506137, + "y": -121.09820569087097, + "z": 0.0 + }, + { + "x": 396.2944035600341, + "y": -120.09596531323345, + "z": 0.0 + }, + { + "x": 396.2941920586982, + "y": -119.09372493559607, + "z": 0.0 + }, + { + "x": 396.2939805573624, + "y": -118.09148455795835, + "z": 0.0 + }, + { + "x": 396.29376905602635, + "y": -117.08924418032097, + "z": 0.0 + }, + { + "x": 396.29355755469044, + "y": -116.08700380268348, + "z": 0.0 + }, + { + "x": 396.2933460533545, + "y": -115.0847634250461, + "z": 0.0 + }, + { + "x": 396.2931345520187, + "y": -114.08252304740847, + "z": 0.0 + }, + { + "x": 396.2929230506828, + "y": -113.08028266977087, + "z": 0.0 + }, + { + "x": 396.2927115493468, + "y": -112.07804229213346, + "z": 0.0 + }, + { + "x": 396.2925000480109, + "y": -111.07580191449597, + "z": 0.0 + }, + { + "x": 396.29228854667497, + "y": -110.07356153685856, + "z": 0.0 + }, + { + "x": 396.29207704533917, + "y": -109.07132115922084, + "z": 0.0 + }, + { + "x": 396.29186554400314, + "y": -108.06908078158344, + "z": 0.0 + }, + { + "x": 396.29165404266723, + "y": -107.06684040394595, + "z": 0.0 + }, + { + "x": 396.2914425413313, + "y": -106.06460002630854, + "z": 0.0 + }, + { + "x": 396.2912310399955, + "y": -105.0623596486709, + "z": 0.0 + }, + { + "x": 396.2910195386596, + "y": -104.06011927103339, + "z": 0.0 + }, + { + "x": 396.2908080373237, + "y": -103.05787889339578, + "z": 0.0 + }, + { + "x": 396.2905965359877, + "y": -102.05563851575852, + "z": 0.0 + }, + { + "x": 396.2903850346519, + "y": -101.05339813812088, + "z": 0.0 + }, + { + "x": 396.29017353331596, + "y": -100.05115776048339, + "z": 0.0 + }, + { + "x": 396.28996203198005, + "y": -99.04891738284587, + "z": 0.0 + }, + { + "x": 396.28975053064414, + "y": -98.04667700520824, + "z": 0.0 + }, + { + "x": 396.2895390293081, + "y": -97.04443662757095, + "z": 0.0 + }, + { + "x": 396.2893275279723, + "y": -96.04219624993334, + "z": 0.0 + }, + { + "x": 396.2891160266364, + "y": -95.03995587229582, + "z": 0.0 + }, + { + "x": 396.2889045253005, + "y": -94.03771549465822, + "z": 0.0 + }, + { + "x": 396.28869302396447, + "y": -93.03547511702092, + "z": 0.0 + }, + { + "x": 396.2884815226287, + "y": -92.03323473938332, + "z": 0.0 + }, + { + "x": 396.28827002129276, + "y": -91.0309943617458, + "z": 0.0 + }, + { + "x": 396.28805851995685, + "y": -90.0287539841083, + "z": 0.0 + }, + { + "x": 396.28784701862094, + "y": -89.02651360647064, + "z": 0.0 + }, + { + "x": 396.28763556119316, + "y": -88.02474527850603, + "z": 0.0 + }, + { + "x": 396.28765973167003, + "y": -87.02433370405193, + "z": 0.0 + }, + { + "x": 396.2886006598079, + "y": -86.02261500169513, + "z": 0.0 + }, + { + "x": 396.28980332800506, + "y": -85.02037532333011, + "z": 0.0 + }, + { + "x": 396.2910059962021, + "y": -84.01813564496533, + "z": 0.0 + }, + { + "x": 396.29220866439925, + "y": -83.01589596660044, + "z": 0.0 + }, + { + "x": 396.2934113325964, + "y": -82.0136562882354, + "z": 0.0 + }, + { + "x": 396.29461400079356, + "y": -81.01141660987061, + "z": 0.0 + }, + { + "x": 396.2958166689907, + "y": -80.0091769315057, + "z": 0.0 + }, + { + "x": 396.29701933718786, + "y": -79.00693725314069, + "z": 0.0 + }, + { + "x": 396.2982220053849, + "y": -78.0046975747759, + "z": 0.0 + }, + { + "x": 396.29942467358205, + "y": -77.00245789641102, + "z": 0.0 + }, + { + "x": 396.3006273417792, + "y": -76.00021821804611, + "z": 0.0 + }, + { + "x": 396.30183000997636, + "y": -74.99797853968121, + "z": 0.0 + }, + { + "x": 396.3030326781735, + "y": -73.9957388613162, + "z": 0.0 + }, + { + "x": 396.30423534637066, + "y": -72.99349918295138, + "z": 0.0 + }, + { + "x": 396.3054380145678, + "y": -71.9912595045865, + "z": 0.0 + }, + { + "x": 396.30664068276496, + "y": -70.98901982622147, + "z": 0.0 + }, + { + "x": 396.307843350962, + "y": -69.98678014785669, + "z": 0.0 + }, + { + "x": 396.30904601915915, + "y": -68.98454046949179, + "z": 0.0 + }, + { + "x": 396.3102486873563, + "y": -67.98230079112678, + "z": 0.0 + }, + { + "x": 396.31145135555346, + "y": -66.980061112762, + "z": 0.0 + }, + { + "x": 396.3126540237506, + "y": -65.97782143439707, + "z": 0.0 + }, + { + "x": 396.31385669194776, + "y": -64.97558175603217, + "z": 0.0 + }, + { + "x": 396.3150593601449, + "y": -63.973342077667155, + "z": 0.0 + }, + { + "x": 396.31626202834195, + "y": -62.97110239930238, + "z": 0.0 + }, + { + "x": 396.3174646965391, + "y": -61.96886272093751, + "z": 0.0 + }, + { + "x": 396.31866736473626, + "y": -60.96662304257252, + "z": 0.0 + }, + { + "x": 396.3198700329334, + "y": -59.96438336420772, + "z": 0.0 + }, + { + "x": 396.32107270113056, + "y": -58.96214368584283, + "z": 0.0 + }, + { + "x": 396.3222753693277, + "y": -57.9599040074778, + "z": 0.0 + }, + { + "x": 396.32347803752475, + "y": -56.95766432911315, + "z": 0.0 + }, + { + "x": 396.324680705722, + "y": -55.95542465074806, + "z": 0.0 + }, + { + "x": 396.32588337391905, + "y": -54.95318497238314, + "z": 0.0 + }, + { + "x": 396.3270860421162, + "y": -53.95094529401848, + "z": 0.0 + }, + { + "x": 396.32828871031336, + "y": -52.94870561565328, + "z": 0.0 + }, + { + "x": 396.3294913785105, + "y": -51.94646593728851, + "z": 0.0 + }, + { + "x": 396.33069404670766, + "y": -50.94422625892359, + "z": 0.0 + }, + { + "x": 396.3318967149048, + "y": -49.941986580558606, + "z": 0.0 + }, + { + "x": 396.33309938310185, + "y": -48.93974690219384, + "z": 0.0 + }, + { + "x": 396.334302051299, + "y": -47.93750722382893, + "z": 0.0 + }, + { + "x": 396.33550471949616, + "y": -46.93526754546404, + "z": 0.0 + }, + { + "x": 396.3367073876933, + "y": -45.93302786709903, + "z": 0.0 + }, + { + "x": 396.33791005589046, + "y": -44.93078818873424, + "z": 0.0 + }, + { + "x": 396.3391127240876, + "y": -43.92854851036934, + "z": 0.0 + }, + { + "x": 396.34031539228477, + "y": -42.92630883200434, + "z": 0.0 + }, + { + "x": 396.3415180604818, + "y": -41.92406915363958, + "z": 0.0 + }, + { + "x": 396.34272072867896, + "y": -40.921829475274706, + "z": 0.0 + }, + { + "x": 396.3439233968761, + "y": -39.91958979690972, + "z": 0.0 + }, + { + "x": 396.34512606507326, + "y": -38.91735011854496, + "z": 0.0 + }, + { + "x": 396.3463287332704, + "y": -37.91511044018003, + "z": 0.0 + }, + { + "x": 396.34753140146756, + "y": -36.912870761815014, + "z": 0.0 + }, + { + "x": 396.3487340696646, + "y": -35.91063108345024, + "z": 0.0 + }, + { + "x": 396.34993673786175, + "y": -34.90839140508534, + "z": 0.0 + }, + { + "x": 396.3511394060589, + "y": -33.90615172672044, + "z": 0.0 + }, + { + "x": 396.35234207425606, + "y": -32.903912048355565, + "z": 0.0 + }, + { + "x": 396.3535447424532, + "y": -31.901672369990585, + "z": 0.0 + }, + { + "x": 396.35474741065036, + "y": -30.899432691625826, + "z": 0.0 + }, + { + "x": 396.3559500788475, + "y": -29.897193013260953, + "z": 0.0 + }, + { + "x": 396.35715274704467, + "y": -28.894953334895966, + "z": 0.0 + }, + { + "x": 396.3583554152417, + "y": -27.892713656531193, + "z": 0.0 + }, + { + "x": 396.35955808343886, + "y": -26.890473978166284, + "z": 0.0 + }, + { + "x": 396.360760751636, + "y": -25.888234299801304, + "z": 0.0 + }, + { + "x": 396.36196341983316, + "y": -24.885994621436545, + "z": 0.0 + }, + { + "x": 396.3631660880303, + "y": -23.883754943071658, + "z": 0.0 + }, + { + "x": 396.36436875622746, + "y": -22.881515264706636, + "z": 0.0 + }, + { + "x": 396.3655714244245, + "y": -21.879275586341848, + "z": 0.0 + }, + { + "x": 396.36677409262165, + "y": -20.877035907976875, + "z": 0.0 + }, + { + "x": 396.3679767608188, + "y": -19.874796229611952, + "z": 0.0 + }, + { + "x": 396.36917942901596, + "y": -18.87255655124693, + "z": 0.0 + }, + { + "x": 396.3703820972131, + "y": -17.870316872882164, + "z": 0.0 + }, + { + "x": 396.37158476541026, + "y": -16.868077194517276, + "z": 0.0 + }, + { + "x": 396.3727874336074, + "y": -15.865837516152242, + "z": 0.0 + }, + { + "x": 396.37399010180445, + "y": -14.863597837787554, + "z": 0.0 + }, + { + "x": 396.3751927700017, + "y": -13.861358159422444, + "z": 0.0 + }, + { + "x": 396.37639543819876, + "y": -12.859118481057523, + "z": 0.0 + }, + { + "x": 396.3775981063959, + "y": -11.856878802692831, + "z": 0.0 + }, + { + "x": 396.37880077459306, + "y": -10.854639124327711, + "z": 0.0 + }, + { + "x": 396.3800034427902, + "y": -9.85239944596279, + "z": 0.0 + } + ] + }, + { + "id": 45, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 144.9895082163707, + "y": -53.49334930469749, + "z": 0.0 + }, + { + "x": 143.97555473552626, + "y": -53.49347285775378, + "z": 0.0 + }, + { + "x": 142.96160125468174, + "y": -53.493596410810085, + "z": 0.0 + }, + { + "x": 141.94764777383736, + "y": -53.493719963866376, + "z": 0.0 + }, + { + "x": 140.93369429299287, + "y": -53.493843516922674, + "z": 0.0 + }, + { + "x": 139.9197408121484, + "y": -53.49396706997897, + "z": 0.0 + }, + { + "x": 138.90578733130397, + "y": -53.49409062303527, + "z": 0.0 + }, + { + "x": 137.89183385045953, + "y": -53.49421417609156, + "z": 0.0 + }, + { + "x": 136.87788036961507, + "y": -53.49433772914786, + "z": 0.0 + }, + { + "x": 135.8639268887706, + "y": -53.494461282204156, + "z": 0.0 + }, + { + "x": 134.8499734079262, + "y": -53.49458483526045, + "z": 0.0 + }, + { + "x": 133.83601992708168, + "y": -53.49470838831675, + "z": 0.0 + }, + { + "x": 132.8220664462373, + "y": -53.49483194137304, + "z": 0.0 + }, + { + "x": 131.8081129653928, + "y": -53.49495549442934, + "z": 0.0 + }, + { + "x": 130.79415948454835, + "y": -53.49507904748564, + "z": 0.0 + }, + { + "x": 129.7802060037039, + "y": -53.49520260054194, + "z": 0.0 + }, + { + "x": 128.76625252285947, + "y": -53.49532615359823, + "z": 0.0 + }, + { + "x": 127.752299042015, + "y": -53.495449706654526, + "z": 0.0 + }, + { + "x": 126.73834556117055, + "y": -53.495573259710824, + "z": 0.0 + }, + { + "x": 125.72439208032611, + "y": -53.495696812767115, + "z": 0.0 + }, + { + "x": 124.71043859948163, + "y": -53.49582036582341, + "z": 0.0 + }, + { + "x": 123.69648511863718, + "y": -53.49594391887971, + "z": 0.0 + }, + { + "x": 122.68253163779274, + "y": -53.49606747193601, + "z": 0.0 + }, + { + "x": 121.66857815694829, + "y": -53.49619102499231, + "z": 0.0 + }, + { + "x": 120.65462467610384, + "y": -53.496314578048604, + "z": 0.0 + }, + { + "x": 119.64067119525942, + "y": -53.496438131104895, + "z": 0.0 + }, + { + "x": 118.62671771441494, + "y": -53.49656168416119, + "z": 0.0 + }, + { + "x": 117.61276423357049, + "y": -53.49668523721749, + "z": 0.0 + }, + { + "x": 116.59881075272605, + "y": -53.49680879027378, + "z": 0.0 + }, + { + "x": 115.58485727188157, + "y": -53.49693234333008, + "z": 0.0 + }, + { + "x": 114.57090379103713, + "y": -53.49705589638638, + "z": 0.0 + }, + { + "x": 113.55695031019268, + "y": -53.497179449442676, + "z": 0.0 + }, + { + "x": 112.54299682934824, + "y": -53.49730300249897, + "z": 0.0 + }, + { + "x": 111.52904334850375, + "y": -53.49742655555527, + "z": 0.0 + }, + { + "x": 110.51508986765934, + "y": -53.49755010861156, + "z": 0.0 + }, + { + "x": 109.50113638681486, + "y": -53.49767366166786, + "z": 0.0 + }, + { + "x": 108.48718290597041, + "y": -53.49779721472416, + "z": 0.0 + }, + { + "x": 107.47322942512598, + "y": -53.49792076778045, + "z": 0.0 + }, + { + "x": 106.4592759442815, + "y": -53.49804432083675, + "z": 0.0 + }, + { + "x": 105.44532246343705, + "y": -53.498167873893046, + "z": 0.0 + }, + { + "x": 104.4313689825926, + "y": -53.49829142694934, + "z": 0.0 + }, + { + "x": 103.41741550174817, + "y": -53.498414980005634, + "z": 0.0 + }, + { + "x": 102.40346202090367, + "y": -53.49853853306194, + "z": 0.0 + }, + { + "x": 101.38950854005927, + "y": -53.49866208611823, + "z": 0.0 + } + ] + }, + { + "id": 46, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.39048336233827, + "y": -61.498662026725825, + "z": 0.0 + }, + { + "x": 102.40443684318276, + "y": -61.498538473669534, + "z": 0.0 + }, + { + "x": 103.41839032402717, + "y": -61.49841492061323, + "z": 0.0 + }, + { + "x": 104.43234380487166, + "y": -61.49829136755694, + "z": 0.0 + }, + { + "x": 105.44629728571611, + "y": -61.49816781450064, + "z": 0.0 + }, + { + "x": 106.46025076656056, + "y": -61.49804426144434, + "z": 0.0 + }, + { + "x": 107.47420424740498, + "y": -61.497920708388044, + "z": 0.0 + }, + { + "x": 108.48815772824948, + "y": -61.49779715533175, + "z": 0.0 + }, + { + "x": 109.50211120909393, + "y": -61.497673602275455, + "z": 0.0 + }, + { + "x": 110.51606468993835, + "y": -61.49755004921916, + "z": 0.0 + }, + { + "x": 111.53001817078284, + "y": -61.497426496162866, + "z": 0.0 + }, + { + "x": 112.54397165162725, + "y": -61.49730294310656, + "z": 0.0 + }, + { + "x": 113.55792513247174, + "y": -61.49717939005027, + "z": 0.0 + }, + { + "x": 114.57187861331619, + "y": -61.49705583699397, + "z": 0.0 + }, + { + "x": 115.58583209416064, + "y": -61.496932283937674, + "z": 0.0 + }, + { + "x": 116.59978557500506, + "y": -61.49680873088138, + "z": 0.0 + }, + { + "x": 117.61373905584955, + "y": -61.496685177825086, + "z": 0.0 + }, + { + "x": 118.627692536694, + "y": -61.49656162476879, + "z": 0.0 + }, + { + "x": 119.64164601753842, + "y": -61.49643807171249, + "z": 0.0 + }, + { + "x": 120.6555994983829, + "y": -61.4963145186562, + "z": 0.0 + }, + { + "x": 121.66955297922735, + "y": -61.4961909655999, + "z": 0.0 + }, + { + "x": 122.6835064600718, + "y": -61.4960674125436, + "z": 0.0 + }, + { + "x": 123.69745994091625, + "y": -61.495943859487305, + "z": 0.0 + }, + { + "x": 124.7114134217607, + "y": -61.49582030643101, + "z": 0.0 + }, + { + "x": 125.72536690260512, + "y": -61.49569675337471, + "z": 0.0 + }, + { + "x": 126.73932038344961, + "y": -61.49557320031842, + "z": 0.0 + }, + { + "x": 127.75327386429406, + "y": -61.49544964726212, + "z": 0.0 + }, + { + "x": 128.76722734513845, + "y": -61.49532609420582, + "z": 0.0 + }, + { + "x": 129.78118082598294, + "y": -61.49520254114953, + "z": 0.0 + }, + { + "x": 130.79513430682738, + "y": -61.49507898809323, + "z": 0.0 + }, + { + "x": 131.80908778767184, + "y": -61.494955435036935, + "z": 0.0 + }, + { + "x": 132.82304126851628, + "y": -61.49483188198064, + "z": 0.0 + }, + { + "x": 133.83699474936077, + "y": -61.49470832892435, + "z": 0.0 + }, + { + "x": 134.85094823020518, + "y": -61.49458477586804, + "z": 0.0 + }, + { + "x": 135.86490171104964, + "y": -61.49446122281175, + "z": 0.0 + }, + { + "x": 136.8788551918941, + "y": -61.49433766975545, + "z": 0.0 + }, + { + "x": 137.8928086727385, + "y": -61.494214116699155, + "z": 0.0 + }, + { + "x": 138.906762153583, + "y": -61.494090563642864, + "z": 0.0 + }, + { + "x": 139.92071563442744, + "y": -61.493967010586566, + "z": 0.0 + }, + { + "x": 140.9346691152719, + "y": -61.49384345753027, + "z": 0.0 + }, + { + "x": 141.94862259611634, + "y": -61.49371990447397, + "z": 0.0 + }, + { + "x": 142.96257607696083, + "y": -61.49359635141768, + "z": 0.0 + }, + { + "x": 143.97652955780524, + "y": -61.493472798361374, + "z": 0.0 + }, + { + "x": 144.99048303864967, + "y": -61.49334924530508, + "z": 0.0 + } + ] + }, + { + "id": 47, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 101.38999595119877, + "y": -57.49866205642203, + "z": 0.0 + }, + { + "x": 102.40394943204322, + "y": -57.49853850336574, + "z": 0.0 + }, + { + "x": 103.41790291288767, + "y": -57.49841495030943, + "z": 0.0 + }, + { + "x": 104.43185639373213, + "y": -57.49829139725314, + "z": 0.0 + }, + { + "x": 105.44580987457658, + "y": -57.49816784419684, + "z": 0.0 + }, + { + "x": 106.45976335542103, + "y": -57.498044291140545, + "z": 0.0 + }, + { + "x": 107.47371683626548, + "y": -57.49792073808425, + "z": 0.0 + }, + { + "x": 108.48767031710995, + "y": -57.497797185027956, + "z": 0.0 + }, + { + "x": 109.5016237979544, + "y": -57.49767363197166, + "z": 0.0 + }, + { + "x": 110.51557727879884, + "y": -57.49755007891536, + "z": 0.0 + }, + { + "x": 111.5295307596433, + "y": -57.49742652585907, + "z": 0.0 + }, + { + "x": 112.54348424048774, + "y": -57.497302972802764, + "z": 0.0 + }, + { + "x": 113.5574377213322, + "y": -57.49717941974647, + "z": 0.0 + }, + { + "x": 114.57139120217666, + "y": -57.497055866690175, + "z": 0.0 + }, + { + "x": 115.5853446830211, + "y": -57.49693231363388, + "z": 0.0 + }, + { + "x": 116.59929816386556, + "y": -57.49680876057758, + "z": 0.0 + }, + { + "x": 117.61325164471002, + "y": -57.49668520752129, + "z": 0.0 + }, + { + "x": 118.62720512555447, + "y": -57.49656165446499, + "z": 0.0 + }, + { + "x": 119.64115860639892, + "y": -57.49643810140869, + "z": 0.0 + }, + { + "x": 120.65511208724337, + "y": -57.4963145483524, + "z": 0.0 + }, + { + "x": 121.66906556808782, + "y": -57.496190995296104, + "z": 0.0 + }, + { + "x": 122.68301904893227, + "y": -57.496067442239806, + "z": 0.0 + }, + { + "x": 123.69697252977672, + "y": -57.49594388918351, + "z": 0.0 + }, + { + "x": 124.71092601062117, + "y": -57.49582033612721, + "z": 0.0 + }, + { + "x": 125.72487949146561, + "y": -57.49569678307091, + "z": 0.0 + }, + { + "x": 126.73883297231008, + "y": -57.49557323001462, + "z": 0.0 + }, + { + "x": 127.75278645315453, + "y": -57.49544967695832, + "z": 0.0 + }, + { + "x": 128.76673993399896, + "y": -57.495326123902025, + "z": 0.0 + }, + { + "x": 129.78069341484343, + "y": -57.495202570845734, + "z": 0.0 + }, + { + "x": 130.79464689568786, + "y": -57.495079017789436, + "z": 0.0 + }, + { + "x": 131.80860037653233, + "y": -57.49495546473314, + "z": 0.0 + }, + { + "x": 132.8225538573768, + "y": -57.49483191167684, + "z": 0.0 + }, + { + "x": 133.83650733822122, + "y": -57.49470835862055, + "z": 0.0 + }, + { + "x": 134.8504608190657, + "y": -57.494584805564244, + "z": 0.0 + }, + { + "x": 135.86441429991012, + "y": -57.49446125250795, + "z": 0.0 + }, + { + "x": 136.8783677807546, + "y": -57.494337699451656, + "z": 0.0 + }, + { + "x": 137.89232126159902, + "y": -57.49421414639536, + "z": 0.0 + }, + { + "x": 138.9062747424435, + "y": -57.49409059333907, + "z": 0.0 + }, + { + "x": 139.92022822328792, + "y": -57.49396704028277, + "z": 0.0 + }, + { + "x": 140.93418170413238, + "y": -57.49384348722647, + "z": 0.0 + }, + { + "x": 141.94813518497685, + "y": -57.49371993417017, + "z": 0.0 + }, + { + "x": 142.96208866582128, + "y": -57.49359638111388, + "z": 0.0 + }, + { + "x": 143.97604214666575, + "y": -57.49347282805758, + "z": 0.0 + }, + { + "x": 144.98999562751018, + "y": -57.493349275001286, + "z": 0.0 + } + ] + }, + { + "id": 48, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 144.98975192194044, + "y": -55.49334928984939, + "z": 0.0 + }, + { + "x": 143.975798441096, + "y": -55.493472842905675, + "z": 0.0 + }, + { + "x": 142.9618449602515, + "y": -55.49359639596199, + "z": 0.0 + }, + { + "x": 141.9478914794071, + "y": -55.49371994901827, + "z": 0.0 + }, + { + "x": 140.9339379985626, + "y": -55.49384350207457, + "z": 0.0 + }, + { + "x": 139.91998451771815, + "y": -55.49396705513087, + "z": 0.0 + }, + { + "x": 138.90603103687374, + "y": -55.494090608187165, + "z": 0.0 + }, + { + "x": 137.89207755602928, + "y": -55.49421416124346, + "z": 0.0 + }, + { + "x": 136.87812407518481, + "y": -55.49433771429976, + "z": 0.0 + }, + { + "x": 135.86417059434035, + "y": -55.49446126735606, + "z": 0.0 + }, + { + "x": 134.85021711349594, + "y": -55.49458482041234, + "z": 0.0 + }, + { + "x": 133.83626363265145, + "y": -55.494708373468654, + "z": 0.0 + }, + { + "x": 132.82231015180705, + "y": -55.49483192652494, + "z": 0.0 + }, + { + "x": 131.80835667096255, + "y": -55.494955479581236, + "z": 0.0 + }, + { + "x": 130.7944031901181, + "y": -55.495079032637534, + "z": 0.0 + }, + { + "x": 129.78044970927368, + "y": -55.49520258569383, + "z": 0.0 + }, + { + "x": 128.76649622842922, + "y": -55.49532613875013, + "z": 0.0 + }, + { + "x": 127.75254274758476, + "y": -55.49544969180643, + "z": 0.0 + }, + { + "x": 126.73858926674032, + "y": -55.495573244862726, + "z": 0.0 + }, + { + "x": 125.72463578589586, + "y": -55.49569679791901, + "z": 0.0 + }, + { + "x": 124.71068230505139, + "y": -55.49582035097531, + "z": 0.0 + }, + { + "x": 123.69672882420696, + "y": -55.495943904031606, + "z": 0.0 + }, + { + "x": 122.6827753433625, + "y": -55.496067457087904, + "z": 0.0 + }, + { + "x": 121.66882186251806, + "y": -55.4961910101442, + "z": 0.0 + }, + { + "x": 120.6548683816736, + "y": -55.4963145632005, + "z": 0.0 + }, + { + "x": 119.64091490082916, + "y": -55.4964381162568, + "z": 0.0 + }, + { + "x": 118.6269614199847, + "y": -55.496561669313095, + "z": 0.0 + }, + { + "x": 117.61300793914026, + "y": -55.49668522236939, + "z": 0.0 + }, + { + "x": 116.5990544582958, + "y": -55.49680877542568, + "z": 0.0 + }, + { + "x": 115.58510097745133, + "y": -55.496932328481975, + "z": 0.0 + }, + { + "x": 114.5711474966069, + "y": -55.49705588153827, + "z": 0.0 + }, + { + "x": 113.55719401576243, + "y": -55.49717943459457, + "z": 0.0 + }, + { + "x": 112.543240534918, + "y": -55.49730298765087, + "z": 0.0 + }, + { + "x": 111.52928705407352, + "y": -55.49742654070717, + "z": 0.0 + }, + { + "x": 110.5153335732291, + "y": -55.497550093763465, + "z": 0.0 + }, + { + "x": 109.50138009238464, + "y": -55.49767364681976, + "z": 0.0 + }, + { + "x": 108.48742661154017, + "y": -55.49779719987606, + "z": 0.0 + }, + { + "x": 107.47347313069574, + "y": -55.497920752932345, + "z": 0.0 + }, + { + "x": 106.45951964985127, + "y": -55.49804430598864, + "z": 0.0 + }, + { + "x": 105.44556616900681, + "y": -55.49816785904494, + "z": 0.0 + }, + { + "x": 104.43161268816237, + "y": -55.49829141210124, + "z": 0.0 + }, + { + "x": 103.41765920731791, + "y": -55.49841496515754, + "z": 0.0 + }, + { + "x": 102.40370572647345, + "y": -55.498538518213834, + "z": 0.0 + }, + { + "x": 101.38975224562901, + "y": -55.49866207127013, + "z": 0.0 + } + ] + }, + { + "id": 49, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.39023965676853, + "y": -59.49866204157392, + "z": 0.0 + }, + { + "x": 102.40419313761299, + "y": -59.49853848851764, + "z": 0.0 + }, + { + "x": 103.41814661845743, + "y": -59.49841493546133, + "z": 0.0 + }, + { + "x": 104.43210009930189, + "y": -59.49829138240504, + "z": 0.0 + }, + { + "x": 105.44605358014635, + "y": -59.498167829348745, + "z": 0.0 + }, + { + "x": 106.46000706099079, + "y": -59.49804427629245, + "z": 0.0 + }, + { + "x": 107.47396054183523, + "y": -59.49792072323615, + "z": 0.0 + }, + { + "x": 108.48791402267972, + "y": -59.49779717017985, + "z": 0.0 + }, + { + "x": 109.50186750352415, + "y": -59.49767361712355, + "z": 0.0 + }, + { + "x": 110.51582098436859, + "y": -59.497550064067255, + "z": 0.0 + }, + { + "x": 111.52977446521307, + "y": -59.49742651101097, + "z": 0.0 + }, + { + "x": 112.54372794605749, + "y": -59.49730295795466, + "z": 0.0 + }, + { + "x": 113.55768142690198, + "y": -59.497179404898375, + "z": 0.0 + }, + { + "x": 114.57163490774641, + "y": -59.49705585184208, + "z": 0.0 + }, + { + "x": 115.58558838859088, + "y": -59.49693229878578, + "z": 0.0 + }, + { + "x": 116.59954186943531, + "y": -59.49680874572948, + "z": 0.0 + }, + { + "x": 117.61349535027978, + "y": -59.49668519267318, + "z": 0.0 + }, + { + "x": 118.62744883112424, + "y": -59.496561639616885, + "z": 0.0 + }, + { + "x": 119.64140231196868, + "y": -59.49643808656059, + "z": 0.0 + }, + { + "x": 120.65535579281314, + "y": -59.496314533504304, + "z": 0.0 + }, + { + "x": 121.66930927365757, + "y": -59.496190980448006, + "z": 0.0 + }, + { + "x": 122.68326275450204, + "y": -59.49606742739171, + "z": 0.0 + }, + { + "x": 123.69721623534647, + "y": -59.49594387433541, + "z": 0.0 + }, + { + "x": 124.71116971619094, + "y": -59.49582032127911, + "z": 0.0 + }, + { + "x": 125.72512319703537, + "y": -59.495696768222814, + "z": 0.0 + }, + { + "x": 126.73907667787984, + "y": -59.495573215166516, + "z": 0.0 + }, + { + "x": 127.7530301587243, + "y": -59.49544966211022, + "z": 0.0 + }, + { + "x": 128.7669836395687, + "y": -59.49532610905392, + "z": 0.0 + }, + { + "x": 129.78093712041317, + "y": -59.495202555997636, + "z": 0.0 + }, + { + "x": 130.79489060125763, + "y": -59.49507900294134, + "z": 0.0 + }, + { + "x": 131.8088440821021, + "y": -59.49495544988504, + "z": 0.0 + }, + { + "x": 132.82279756294653, + "y": -59.49483189682874, + "z": 0.0 + }, + { + "x": 133.836751043791, + "y": -59.494708343772444, + "z": 0.0 + }, + { + "x": 134.85070452463543, + "y": -59.49458479071615, + "z": 0.0 + }, + { + "x": 135.8646580054799, + "y": -59.49446123765985, + "z": 0.0 + }, + { + "x": 136.87861148632436, + "y": -59.49433768460355, + "z": 0.0 + }, + { + "x": 137.89256496716877, + "y": -59.49421413154725, + "z": 0.0 + }, + { + "x": 138.90651844801323, + "y": -59.49409057849097, + "z": 0.0 + }, + { + "x": 139.9204719288577, + "y": -59.49396702543467, + "z": 0.0 + }, + { + "x": 140.93442540970216, + "y": -59.49384347237837, + "z": 0.0 + }, + { + "x": 141.9483788905466, + "y": -59.493719919322075, + "z": 0.0 + }, + { + "x": 142.96233237139106, + "y": -59.49359636626578, + "z": 0.0 + }, + { + "x": 143.9762858522355, + "y": -59.49347281320948, + "z": 0.0 + }, + { + "x": 144.99023933307993, + "y": -59.49334926015318, + "z": 0.0 + } + ] + }, + { + "id": 50, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.15950687877944, + "y": -53.47139508844576, + "z": 0.0 + }, + { + "x": 324.15320115376625, + "y": -53.471517709601706, + "z": 0.0 + }, + { + "x": 323.1468954287531, + "y": -53.471640330757644, + "z": 0.0 + }, + { + "x": 322.14058970373986, + "y": -53.47176295191359, + "z": 0.0 + }, + { + "x": 321.13428397872667, + "y": -53.471885573069535, + "z": 0.0 + }, + { + "x": 320.1279782537135, + "y": -53.472008194225474, + "z": 0.0 + }, + { + "x": 319.12167252870023, + "y": -53.47213081538142, + "z": 0.0 + }, + { + "x": 318.11536680368704, + "y": -53.472253436537365, + "z": 0.0 + }, + { + "x": 317.10906107867385, + "y": -53.47237605769331, + "z": 0.0 + }, + { + "x": 316.10275535366065, + "y": -53.472498678849256, + "z": 0.0 + }, + { + "x": 315.09644962864746, + "y": -53.472621300005194, + "z": 0.0 + }, + { + "x": 314.0901439036342, + "y": -53.47274392116114, + "z": 0.0 + }, + { + "x": 313.083838178621, + "y": -53.472866542317085, + "z": 0.0 + }, + { + "x": 312.0775324536079, + "y": -53.472989163473024, + "z": 0.0 + }, + { + "x": 311.07122672859464, + "y": -53.47311178462897, + "z": 0.0 + }, + { + "x": 310.0649210035814, + "y": -53.473234405784915, + "z": 0.0 + }, + { + "x": 309.05861527856825, + "y": -53.47335702694085, + "z": 0.0 + }, + { + "x": 308.052309553555, + "y": -53.4734796480968, + "z": 0.0 + }, + { + "x": 307.0460038285418, + "y": -53.473602269252744, + "z": 0.0 + }, + { + "x": 306.0396981035287, + "y": -53.47372489040868, + "z": 0.0 + }, + { + "x": 305.03339237851543, + "y": -53.47384751156463, + "z": 0.0 + }, + { + "x": 304.0270866535022, + "y": -53.473970132720574, + "z": 0.0 + }, + { + "x": 303.02078092848905, + "y": -53.47409275387651, + "z": 0.0 + }, + { + "x": 302.0144752034758, + "y": -53.47421537503246, + "z": 0.0 + }, + { + "x": 301.0081694784626, + "y": -53.4743379961884, + "z": 0.0 + }, + { + "x": 300.00186375344947, + "y": -53.47446061734434, + "z": 0.0 + }, + { + "x": 298.99555802843616, + "y": -53.47458323850029, + "z": 0.0 + }, + { + "x": 297.989252303423, + "y": -53.47470585965623, + "z": 0.0 + }, + { + "x": 296.98294657840984, + "y": -53.47482848081217, + "z": 0.0 + }, + { + "x": 295.9766408533966, + "y": -53.47495110196812, + "z": 0.0 + }, + { + "x": 294.9703351283834, + "y": -53.475073723124055, + "z": 0.0 + }, + { + "x": 293.96402940337015, + "y": -53.47519634428, + "z": 0.0 + }, + { + "x": 292.95772367835696, + "y": -53.475318965435946, + "z": 0.0 + }, + { + "x": 291.95141795334376, + "y": -53.475441586591884, + "z": 0.0 + }, + { + "x": 290.9451122283305, + "y": -53.47556420774783, + "z": 0.0 + }, + { + "x": 289.93880650331727, + "y": -53.47568682890378, + "z": 0.0 + }, + { + "x": 288.93250077830413, + "y": -53.47580945005972, + "z": 0.0 + }, + { + "x": 287.9261950532908, + "y": -53.47593207121567, + "z": 0.0 + }, + { + "x": 286.91988932827763, + "y": -53.47605469237161, + "z": 0.0 + }, + { + "x": 285.91358360326444, + "y": -53.47617731352755, + "z": 0.0 + }, + { + "x": 284.9072778782512, + "y": -53.476299934683496, + "z": 0.0 + }, + { + "x": 283.900972153238, + "y": -53.47642255583944, + "z": 0.0 + }, + { + "x": 282.8946664282248, + "y": -53.47654517699538, + "z": 0.0 + }, + { + "x": 281.88836070321156, + "y": -53.476667798151325, + "z": 0.0 + }, + { + "x": 280.8820549781983, + "y": -53.47679041930727, + "z": 0.0 + }, + { + "x": 279.8757492531852, + "y": -53.47691304046321, + "z": 0.0 + }, + { + "x": 278.86944352817187, + "y": -53.477035661619155, + "z": 0.0 + }, + { + "x": 277.8631378031587, + "y": -53.4771582827751, + "z": 0.0 + }, + { + "x": 276.8568320781455, + "y": -53.47728090393104, + "z": 0.0 + }, + { + "x": 275.85052635313224, + "y": -53.477403525086984, + "z": 0.0 + }, + { + "x": 274.8442206281191, + "y": -53.47752614624292, + "z": 0.0 + }, + { + "x": 273.8379149031058, + "y": -53.47764876739887, + "z": 0.0 + }, + { + "x": 272.8316091780926, + "y": -53.477771388554814, + "z": 0.0 + }, + { + "x": 271.8253034530794, + "y": -53.47789400971075, + "z": 0.0 + }, + { + "x": 270.81899772806617, + "y": -53.4780166308667, + "z": 0.0 + }, + { + "x": 269.8126920030529, + "y": -53.47813925202264, + "z": 0.0 + }, + { + "x": 268.8063862780398, + "y": -53.47826187317858, + "z": 0.0 + }, + { + "x": 267.8000805530265, + "y": -53.47838449433453, + "z": 0.0 + }, + { + "x": 266.7937748280133, + "y": -53.47850711549047, + "z": 0.0 + }, + { + "x": 265.78746910300015, + "y": -53.47862973664641, + "z": 0.0 + }, + { + "x": 264.78116337798684, + "y": -53.47875235780236, + "z": 0.0 + }, + { + "x": 263.77485765297365, + "y": -53.4788749789583, + "z": 0.0 + }, + { + "x": 262.7685519279604, + "y": -53.47899760011425, + "z": 0.0 + }, + { + "x": 261.7622462029472, + "y": -53.47912022127019, + "z": 0.0 + }, + { + "x": 260.755940477934, + "y": -53.47924284242614, + "z": 0.0 + }, + { + "x": 259.7496347529208, + "y": -53.47936546358208, + "z": 0.0 + }, + { + "x": 258.7433290279076, + "y": -53.47948808473802, + "z": 0.0 + }, + { + "x": 257.73702330289433, + "y": -53.47961070589397, + "z": 0.0 + }, + { + "x": 256.7307175778812, + "y": -53.47973332704991, + "z": 0.0 + }, + { + "x": 255.72441185286795, + "y": -53.47985594820585, + "z": 0.0 + }, + { + "x": 254.71810612785475, + "y": -53.47997856936179, + "z": 0.0 + }, + { + "x": 253.7118004028415, + "y": -53.480101190517736, + "z": 0.0 + }, + { + "x": 252.70549467782828, + "y": -53.48022381167368, + "z": 0.0 + }, + { + "x": 251.6991889528151, + "y": -53.48034643282962, + "z": 0.0 + }, + { + "x": 250.69288322780184, + "y": -53.480469053985566, + "z": 0.0 + }, + { + "x": 249.68657750278862, + "y": -53.48059167514151, + "z": 0.0 + }, + { + "x": 248.68027177777543, + "y": -53.48071429629745, + "z": 0.0 + }, + { + "x": 247.67396605276218, + "y": -53.480836917453395, + "z": 0.0 + }, + { + "x": 246.667660327749, + "y": -53.48095953860934, + "z": 0.0 + }, + { + "x": 245.6613546027358, + "y": -53.48108215976528, + "z": 0.0 + }, + { + "x": 244.65504887772255, + "y": -53.481204780921225, + "z": 0.0 + }, + { + "x": 243.64874315270933, + "y": -53.48132740207717, + "z": 0.0 + }, + { + "x": 242.64243742769614, + "y": -53.48145002323311, + "z": 0.0 + }, + { + "x": 241.6361317026829, + "y": -53.481572644389054, + "z": 0.0 + }, + { + "x": 240.62982597766967, + "y": -53.481695265545, + "z": 0.0 + }, + { + "x": 239.62352025265648, + "y": -53.48181788670094, + "z": 0.0 + }, + { + "x": 238.61721452764323, + "y": -53.48194050785688, + "z": 0.0 + }, + { + "x": 237.61090880263004, + "y": -53.48206312901283, + "z": 0.0 + }, + { + "x": 236.60460307761684, + "y": -53.48218575016877, + "z": 0.0 + }, + { + "x": 235.5982973526036, + "y": -53.48230837132471, + "z": 0.0 + }, + { + "x": 234.59199162759037, + "y": -53.48243099248066, + "z": 0.0 + }, + { + "x": 233.58568590257715, + "y": -53.482553613636604, + "z": 0.0 + }, + { + "x": 232.57938017756393, + "y": -53.48267623479255, + "z": 0.0 + }, + { + "x": 231.57307445255074, + "y": -53.48279885594849, + "z": 0.0 + }, + { + "x": 230.56676872753752, + "y": -53.48292147710443, + "z": 0.0 + }, + { + "x": 229.5604630025243, + "y": -53.48304409826038, + "z": 0.0 + }, + { + "x": 228.5541572775111, + "y": -53.48316671941632, + "z": 0.0 + }, + { + "x": 227.5478515524979, + "y": -53.48328934057226, + "z": 0.0 + }, + { + "x": 226.54154582748467, + "y": -53.48341196172821, + "z": 0.0 + }, + { + "x": 225.5352401024715, + "y": -53.48353458288415, + "z": 0.0 + }, + { + "x": 224.52893437745826, + "y": -53.48365720404009, + "z": 0.0 + }, + { + "x": 223.52262865244504, + "y": -53.48377982519604, + "z": 0.0 + }, + { + "x": 222.51632292743187, + "y": -53.483902446351976, + "z": 0.0 + }, + { + "x": 221.51001720241862, + "y": -53.48402506750792, + "z": 0.0 + }, + { + "x": 220.5037114774054, + "y": -53.48414768866387, + "z": 0.0 + }, + { + "x": 219.49740575239224, + "y": -53.484270309819806, + "z": 0.0 + }, + { + "x": 218.491100027379, + "y": -53.48439293097575, + "z": 0.0 + }, + { + "x": 217.4847943023658, + "y": -53.4845155521317, + "z": 0.0 + }, + { + "x": 216.4784885773526, + "y": -53.484638173287635, + "z": 0.0 + }, + { + "x": 215.47218285233936, + "y": -53.48476079444358, + "z": 0.0 + }, + { + "x": 214.4658771273262, + "y": -53.48488341559952, + "z": 0.0 + }, + { + "x": 213.45957140231295, + "y": -53.485006036755465, + "z": 0.0 + }, + { + "x": 212.45326567729973, + "y": -53.48512865791141, + "z": 0.0 + }, + { + "x": 211.44695995228656, + "y": -53.48525127906735, + "z": 0.0 + }, + { + "x": 210.4406542272733, + "y": -53.485373900223294, + "z": 0.0 + }, + { + "x": 209.43434850226012, + "y": -53.48549652137924, + "z": 0.0 + }, + { + "x": 208.4280427772469, + "y": -53.485619142535185, + "z": 0.0 + }, + { + "x": 207.42173705223368, + "y": -53.48574176369113, + "z": 0.0 + }, + { + "x": 206.4154313272205, + "y": -53.485864384847076, + "z": 0.0 + }, + { + "x": 205.4091256022073, + "y": -53.485987006003015, + "z": 0.0 + }, + { + "x": 204.40281987719405, + "y": -53.48610962715896, + "z": 0.0 + }, + { + "x": 203.39651415218086, + "y": -53.486232248314906, + "z": 0.0 + }, + { + "x": 202.39020842716766, + "y": -53.486354869470844, + "z": 0.0 + }, + { + "x": 201.38390270215444, + "y": -53.48647749062679, + "z": 0.0 + }, + { + "x": 200.37759697714122, + "y": -53.486600111782735, + "z": 0.0 + }, + { + "x": 199.37129125212803, + "y": -53.486722732938674, + "z": 0.0 + }, + { + "x": 198.3649855271148, + "y": -53.48684535409462, + "z": 0.0 + }, + { + "x": 197.3586798021016, + "y": -53.486967975250565, + "z": 0.0 + }, + { + "x": 196.3523740770884, + "y": -53.4870905964065, + "z": 0.0 + }, + { + "x": 195.34606835207515, + "y": -53.48721321756245, + "z": 0.0 + }, + { + "x": 194.33976262706196, + "y": -53.48733583871839, + "z": 0.0 + }, + { + "x": 193.33345690204874, + "y": -53.48745845987433, + "z": 0.0 + }, + { + "x": 192.32715117703552, + "y": -53.48758108103028, + "z": 0.0 + }, + { + "x": 191.32084545202233, + "y": -53.48770370218622, + "z": 0.0 + }, + { + "x": 190.3145397270091, + "y": -53.48782632334216, + "z": 0.0 + }, + { + "x": 189.30823400199588, + "y": -53.48794894449811, + "z": 0.0 + }, + { + "x": 188.3019282769827, + "y": -53.488071565654046, + "z": 0.0 + }, + { + "x": 187.29562255196944, + "y": -53.48819418680999, + "z": 0.0 + }, + { + "x": 186.28931682695622, + "y": -53.48831680796594, + "z": 0.0 + }, + { + "x": 185.28301110194306, + "y": -53.488439429121875, + "z": 0.0 + }, + { + "x": 184.2767053769298, + "y": -53.48856205027782, + "z": 0.0 + }, + { + "x": 183.2703996519166, + "y": -53.48868467143377, + "z": 0.0 + }, + { + "x": 182.26409392690343, + "y": -53.488807292589705, + "z": 0.0 + }, + { + "x": 181.25778820189018, + "y": -53.48892991374565, + "z": 0.0 + }, + { + "x": 180.25148247687693, + "y": -53.4890525349016, + "z": 0.0 + }, + { + "x": 179.24517675186377, + "y": -53.48917515605754, + "z": 0.0 + }, + { + "x": 178.23887102685055, + "y": -53.48929777721349, + "z": 0.0 + }, + { + "x": 177.23256530183733, + "y": -53.48942039836943, + "z": 0.0 + }, + { + "x": 176.22625957682413, + "y": -53.48954301952537, + "z": 0.0 + }, + { + "x": 175.2199538518109, + "y": -53.48966564068132, + "z": 0.0 + }, + { + "x": 174.21364812679772, + "y": -53.489788261837255, + "z": 0.0 + }, + { + "x": 173.20734240178447, + "y": -53.4899108829932, + "z": 0.0 + }, + { + "x": 172.20103667677128, + "y": -53.490033504149146, + "z": 0.0 + }, + { + "x": 171.1947309517581, + "y": -53.490156125305084, + "z": 0.0 + }, + { + "x": 170.18842522674484, + "y": -53.49027874646103, + "z": 0.0 + }, + { + "x": 169.18211950173162, + "y": -53.490401367616975, + "z": 0.0 + }, + { + "x": 168.17581377671846, + "y": -53.490523988772914, + "z": 0.0 + }, + { + "x": 167.1695080517052, + "y": -53.49064660992886, + "z": 0.0 + } + ] + }, + { + "id": 51, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 167.17048287398424, + "y": -61.490646550536454, + "z": 0.0 + }, + { + "x": 168.17678859899743, + "y": -61.49052392938051, + "z": 0.0 + }, + { + "x": 169.18309432401065, + "y": -61.49040130822457, + "z": 0.0 + }, + { + "x": 170.18940004902387, + "y": -61.490278687068624, + "z": 0.0 + }, + { + "x": 171.19570577403707, + "y": -61.49015606591268, + "z": 0.0 + }, + { + "x": 172.20201149905031, + "y": -61.49003344475674, + "z": 0.0 + }, + { + "x": 173.2083172240635, + "y": -61.489910823600795, + "z": 0.0 + }, + { + "x": 174.2146229490767, + "y": -61.48978820244485, + "z": 0.0 + }, + { + "x": 175.22092867408995, + "y": -61.48966558128891, + "z": 0.0 + }, + { + "x": 176.2272343991031, + "y": -61.489542960132965, + "z": 0.0 + }, + { + "x": 177.23354012411636, + "y": -61.48942033897703, + "z": 0.0 + }, + { + "x": 178.23984584912958, + "y": -61.48929771782108, + "z": 0.0 + }, + { + "x": 179.24615157414274, + "y": -61.489175096665136, + "z": 0.0 + }, + { + "x": 180.25245729915602, + "y": -61.4890524755092, + "z": 0.0 + }, + { + "x": 181.2587630241692, + "y": -61.488929854353245, + "z": 0.0 + }, + { + "x": 182.2650687491824, + "y": -61.4888072331973, + "z": 0.0 + }, + { + "x": 183.27137447419562, + "y": -61.48868461204136, + "z": 0.0 + }, + { + "x": 184.27768019920885, + "y": -61.488561990885415, + "z": 0.0 + }, + { + "x": 185.28398592422204, + "y": -61.48843936972947, + "z": 0.0 + }, + { + "x": 186.29029164923526, + "y": -61.48831674857353, + "z": 0.0 + }, + { + "x": 187.29659737424848, + "y": -61.488194127417586, + "z": 0.0 + }, + { + "x": 188.30290309926167, + "y": -61.48807150626164, + "z": 0.0 + }, + { + "x": 189.30920882427492, + "y": -61.4879488851057, + "z": 0.0 + }, + { + "x": 190.31551454928814, + "y": -61.487826263949756, + "z": 0.0 + }, + { + "x": 191.3218202743013, + "y": -61.48770364279381, + "z": 0.0 + }, + { + "x": 192.32812599931455, + "y": -61.48758102163787, + "z": 0.0 + }, + { + "x": 193.33443172432777, + "y": -61.48745840048193, + "z": 0.0 + }, + { + "x": 194.34073744934093, + "y": -61.48733577932598, + "z": 0.0 + }, + { + "x": 195.34704317435418, + "y": -61.48721315817004, + "z": 0.0 + }, + { + "x": 196.35334889936738, + "y": -61.4870905370141, + "z": 0.0 + }, + { + "x": 197.35965462438062, + "y": -61.48696791585816, + "z": 0.0 + }, + { + "x": 198.36596034939384, + "y": -61.48684529470221, + "z": 0.0 + }, + { + "x": 199.372266074407, + "y": -61.48672267354627, + "z": 0.0 + }, + { + "x": 200.37857179942026, + "y": -61.48660005239033, + "z": 0.0 + }, + { + "x": 201.38487752443348, + "y": -61.486477431234384, + "z": 0.0 + }, + { + "x": 202.39118324944664, + "y": -61.48635481007844, + "z": 0.0 + }, + { + "x": 203.3974889744599, + "y": -61.4862321889225, + "z": 0.0 + }, + { + "x": 204.40379469947308, + "y": -61.486109567766555, + "z": 0.0 + }, + { + "x": 205.41010042448627, + "y": -61.48598694661061, + "z": 0.0 + }, + { + "x": 206.41640614949952, + "y": -61.48586432545467, + "z": 0.0 + }, + { + "x": 207.42271187451271, + "y": -61.485741704298725, + "z": 0.0 + }, + { + "x": 208.42901759952593, + "y": -61.48561908314278, + "z": 0.0 + }, + { + "x": 209.43532332453916, + "y": -61.485496461986834, + "z": 0.0 + }, + { + "x": 210.44162904955235, + "y": -61.48537384083089, + "z": 0.0 + }, + { + "x": 211.44793477456554, + "y": -61.48525121967494, + "z": 0.0 + }, + { + "x": 212.45424049957876, + "y": -61.485128598519005, + "z": 0.0 + }, + { + "x": 213.46054622459198, + "y": -61.48500597736306, + "z": 0.0 + }, + { + "x": 214.46685194960517, + "y": -61.48488335620711, + "z": 0.0 + }, + { + "x": 215.4731576746184, + "y": -61.484760735051175, + "z": 0.0 + }, + { + "x": 216.47946339963158, + "y": -61.48463811389523, + "z": 0.0 + }, + { + "x": 217.48576912464483, + "y": -61.48451549273929, + "z": 0.0 + }, + { + "x": 218.49207484965802, + "y": -61.484392871583346, + "z": 0.0 + }, + { + "x": 219.49838057467122, + "y": -61.4842702504274, + "z": 0.0 + }, + { + "x": 220.50468629968444, + "y": -61.48414762927146, + "z": 0.0 + }, + { + "x": 221.51099202469766, + "y": -61.484025008115516, + "z": 0.0 + }, + { + "x": 222.51729774971085, + "y": -61.48390238695957, + "z": 0.0 + }, + { + "x": 223.52360347472407, + "y": -61.48377976580363, + "z": 0.0 + }, + { + "x": 224.5299091997373, + "y": -61.48365714464769, + "z": 0.0 + }, + { + "x": 225.53621492475048, + "y": -61.48353452349174, + "z": 0.0 + }, + { + "x": 226.5425206497637, + "y": -61.4834119023358, + "z": 0.0 + }, + { + "x": 227.54882637477692, + "y": -61.48328928117986, + "z": 0.0 + }, + { + "x": 228.5551320997901, + "y": -61.48316666002391, + "z": 0.0 + }, + { + "x": 229.56143782480333, + "y": -61.48304403886797, + "z": 0.0 + }, + { + "x": 230.56774354981656, + "y": -61.48292141771203, + "z": 0.0 + }, + { + "x": 231.57404927482972, + "y": -61.48279879655608, + "z": 0.0 + }, + { + "x": 232.58035499984297, + "y": -61.482676175400144, + "z": 0.0 + }, + { + "x": 233.5866607248562, + "y": -61.4825535542442, + "z": 0.0 + }, + { + "x": 234.5929664498694, + "y": -61.48243093308825, + "z": 0.0 + }, + { + "x": 235.59927217488263, + "y": -61.48230831193231, + "z": 0.0 + }, + { + "x": 236.60557789989582, + "y": -61.48218569077636, + "z": 0.0 + }, + { + "x": 237.61188362490907, + "y": -61.48206306962042, + "z": 0.0 + }, + { + "x": 238.61818934992226, + "y": -61.48194044846448, + "z": 0.0 + }, + { + "x": 239.62449507493545, + "y": -61.48181782730853, + "z": 0.0 + }, + { + "x": 240.6308007999487, + "y": -61.481695206152594, + "z": 0.0 + }, + { + "x": 241.63710652496192, + "y": -61.48157258499665, + "z": 0.0 + }, + { + "x": 242.6434122499751, + "y": -61.4814499638407, + "z": 0.0 + }, + { + "x": 243.64971797498836, + "y": -61.481327342684764, + "z": 0.0 + }, + { + "x": 244.65602370000158, + "y": -61.48120472152882, + "z": 0.0 + }, + { + "x": 245.66232942501478, + "y": -61.48108210037287, + "z": 0.0 + }, + { + "x": 246.66863515002802, + "y": -61.480959479216935, + "z": 0.0 + }, + { + "x": 247.67494087504122, + "y": -61.48083685806099, + "z": 0.0 + }, + { + "x": 248.6812466000544, + "y": -61.480714236905044, + "z": 0.0 + }, + { + "x": 249.68755232506766, + "y": -61.480591615749105, + "z": 0.0 + }, + { + "x": 250.69385805008088, + "y": -61.48046899459316, + "z": 0.0 + }, + { + "x": 251.70016377509407, + "y": -61.480346373437214, + "z": 0.0 + }, + { + "x": 252.70646950010732, + "y": -61.480223752281276, + "z": 0.0 + }, + { + "x": 253.71277522512054, + "y": -61.48010113112533, + "z": 0.0 + }, + { + "x": 254.71908095013373, + "y": -61.479978509969385, + "z": 0.0 + }, + { + "x": 255.72538667514698, + "y": -61.47985588881345, + "z": 0.0 + }, + { + "x": 256.73169240016017, + "y": -61.4797332676575, + "z": 0.0 + }, + { + "x": 257.7379981251734, + "y": -61.47961064650156, + "z": 0.0 + }, + { + "x": 258.74430385018667, + "y": -61.47948802534562, + "z": 0.0 + }, + { + "x": 259.7506095751998, + "y": -61.47936540418967, + "z": 0.0 + }, + { + "x": 260.7569153002131, + "y": -61.47924278303373, + "z": 0.0 + }, + { + "x": 261.7632210252263, + "y": -61.47912016187779, + "z": 0.0 + }, + { + "x": 262.7695267502395, + "y": -61.47899754072184, + "z": 0.0 + }, + { + "x": 263.77583247525274, + "y": -61.4788749195659, + "z": 0.0 + }, + { + "x": 264.78213820026593, + "y": -61.47875229840995, + "z": 0.0 + }, + { + "x": 265.7884439252791, + "y": -61.478629677254006, + "z": 0.0 + }, + { + "x": 266.7947496502924, + "y": -61.47850705609807, + "z": 0.0 + }, + { + "x": 267.80105537530557, + "y": -61.47838443494212, + "z": 0.0 + }, + { + "x": 268.80736110031876, + "y": -61.478261813786176, + "z": 0.0 + }, + { + "x": 269.813666825332, + "y": -61.47813919263024, + "z": 0.0 + }, + { + "x": 270.81997255034526, + "y": -61.47801657147429, + "z": 0.0 + }, + { + "x": 271.8262782753584, + "y": -61.47789395031835, + "z": 0.0 + }, + { + "x": 272.8325840003717, + "y": -61.47777132916241, + "z": 0.0 + }, + { + "x": 273.8388897253849, + "y": -61.47764870800646, + "z": 0.0 + }, + { + "x": 274.8451954503981, + "y": -61.47752608685052, + "z": 0.0 + }, + { + "x": 275.85150117541133, + "y": -61.47740346569458, + "z": 0.0 + }, + { + "x": 276.85780690042446, + "y": -61.47728084453863, + "z": 0.0 + }, + { + "x": 277.86411262543777, + "y": -61.477158223382695, + "z": 0.0 + }, + { + "x": 278.87041835045096, + "y": -61.47703560222675, + "z": 0.0 + }, + { + "x": 279.87672407546415, + "y": -61.476912981070804, + "z": 0.0 + }, + { + "x": 280.8830298004774, + "y": -61.476790359914865, + "z": 0.0 + }, + { + "x": 281.88933552549065, + "y": -61.47666773875892, + "z": 0.0 + }, + { + "x": 282.8956412505038, + "y": -61.476545117602974, + "z": 0.0 + }, + { + "x": 283.9019469755171, + "y": -61.476422496447036, + "z": 0.0 + }, + { + "x": 284.9082527005303, + "y": -61.47629987529109, + "z": 0.0 + }, + { + "x": 285.9145584255434, + "y": -61.476177254135145, + "z": 0.0 + }, + { + "x": 286.9208641505567, + "y": -61.476054632979206, + "z": 0.0 + }, + { + "x": 287.9271698755699, + "y": -61.47593201182326, + "z": 0.0 + }, + { + "x": 288.9334756005831, + "y": -61.475809390667315, + "z": 0.0 + }, + { + "x": 289.93978132559636, + "y": -61.47568676951138, + "z": 0.0 + }, + { + "x": 290.9460870506096, + "y": -61.475564148355424, + "z": 0.0 + }, + { + "x": 291.95239277562274, + "y": -61.47544152719948, + "z": 0.0 + }, + { + "x": 292.95869850063605, + "y": -61.47531890604354, + "z": 0.0 + }, + { + "x": 293.96500422564924, + "y": -61.475196284887595, + "z": 0.0 + }, + { + "x": 294.9713099506624, + "y": -61.47507366373165, + "z": 0.0 + }, + { + "x": 295.9776156756757, + "y": -61.47495104257571, + "z": 0.0 + }, + { + "x": 296.9839214006888, + "y": -61.474828421419765, + "z": 0.0 + }, + { + "x": 297.99022712570206, + "y": -61.47470580026383, + "z": 0.0 + }, + { + "x": 298.99653285071525, + "y": -61.47458317910788, + "z": 0.0 + }, + { + "x": 300.00283857572845, + "y": -61.474460557951936, + "z": 0.0 + }, + { + "x": 301.0091443007417, + "y": -61.474337936796, + "z": 0.0 + }, + { + "x": 302.0154500257549, + "y": -61.47421531564005, + "z": 0.0 + }, + { + "x": 303.021755750768, + "y": -61.474092694484106, + "z": 0.0 + }, + { + "x": 304.02806147578127, + "y": -61.47397007332817, + "z": 0.0 + }, + { + "x": 305.0343672007945, + "y": -61.47384745217222, + "z": 0.0 + }, + { + "x": 306.04067292580766, + "y": -61.47372483101628, + "z": 0.0 + }, + { + "x": 307.0469786508209, + "y": -61.47360220986034, + "z": 0.0 + }, + { + "x": 308.0532843758341, + "y": -61.47347958870439, + "z": 0.0 + }, + { + "x": 309.05959010084723, + "y": -61.47335696754845, + "z": 0.0 + }, + { + "x": 310.0658958258605, + "y": -61.47323434639251, + "z": 0.0 + }, + { + "x": 311.07220155087373, + "y": -61.47311172523656, + "z": 0.0 + }, + { + "x": 312.07850727588686, + "y": -61.47298910408062, + "z": 0.0 + }, + { + "x": 313.0848130009001, + "y": -61.47286648292468, + "z": 0.0 + }, + { + "x": 314.0911187259133, + "y": -61.472743861768734, + "z": 0.0 + }, + { + "x": 315.09742445092644, + "y": -61.47262124061279, + "z": 0.0 + }, + { + "x": 316.10373017593975, + "y": -61.47249861945685, + "z": 0.0 + }, + { + "x": 317.11003590095294, + "y": -61.472375998300905, + "z": 0.0 + }, + { + "x": 318.11634162596613, + "y": -61.47225337714496, + "z": 0.0 + }, + { + "x": 319.1226473509793, + "y": -61.47213075598901, + "z": 0.0 + }, + { + "x": 320.12895307599246, + "y": -61.47200813483307, + "z": 0.0 + }, + { + "x": 321.13525880100576, + "y": -61.47188551367713, + "z": 0.0 + }, + { + "x": 322.14156452601895, + "y": -61.471762892521184, + "z": 0.0 + }, + { + "x": 323.1478702510321, + "y": -61.47164027136524, + "z": 0.0 + }, + { + "x": 324.15417597604534, + "y": -61.4715176502093, + "z": 0.0 + }, + { + "x": 325.16048170105853, + "y": -61.471395029053355, + "z": 0.0 + } + ] + }, + { + "id": 52, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 167.16999546284472, + "y": -57.49064658023266, + "z": 0.0 + }, + { + "x": 168.17630118785794, + "y": -57.49052395907671, + "z": 0.0 + }, + { + "x": 169.18260691287114, + "y": -57.49040133792077, + "z": 0.0 + }, + { + "x": 170.18891263788436, + "y": -57.49027871676483, + "z": 0.0 + }, + { + "x": 171.19521836289758, + "y": -57.49015609560888, + "z": 0.0 + }, + { + "x": 172.2015240879108, + "y": -57.49003347445294, + "z": 0.0 + }, + { + "x": 173.207829812924, + "y": -57.489910853297, + "z": 0.0 + }, + { + "x": 174.2141355379372, + "y": -57.48978823214105, + "z": 0.0 + }, + { + "x": 175.22044126295043, + "y": -57.489665610985114, + "z": 0.0 + }, + { + "x": 176.22674698796362, + "y": -57.48954298982917, + "z": 0.0 + }, + { + "x": 177.23305271297684, + "y": -57.48942036867323, + "z": 0.0 + }, + { + "x": 178.23935843799006, + "y": -57.489297747517284, + "z": 0.0 + }, + { + "x": 179.24566416300325, + "y": -57.48917512636134, + "z": 0.0 + }, + { + "x": 180.25196988801648, + "y": -57.4890525052054, + "z": 0.0 + }, + { + "x": 181.2582756130297, + "y": -57.48892988404945, + "z": 0.0 + }, + { + "x": 182.26458133804292, + "y": -57.4888072628935, + "z": 0.0 + }, + { + "x": 183.2708870630561, + "y": -57.488684641737564, + "z": 0.0 + }, + { + "x": 184.27719278806933, + "y": -57.48856202058162, + "z": 0.0 + }, + { + "x": 185.28349851308255, + "y": -57.48843939942567, + "z": 0.0 + }, + { + "x": 186.28980423809574, + "y": -57.488316778269734, + "z": 0.0 + }, + { + "x": 187.29610996310896, + "y": -57.48819415711379, + "z": 0.0 + }, + { + "x": 188.30241568812218, + "y": -57.48807153595784, + "z": 0.0 + }, + { + "x": 189.3087214131354, + "y": -57.487948914801905, + "z": 0.0 + }, + { + "x": 190.31502713814862, + "y": -57.48782629364596, + "z": 0.0 + }, + { + "x": 191.3213328631618, + "y": -57.487703672490014, + "z": 0.0 + }, + { + "x": 192.32763858817503, + "y": -57.487581051334075, + "z": 0.0 + }, + { + "x": 193.33394431318825, + "y": -57.48745843017813, + "z": 0.0 + }, + { + "x": 194.34025003820145, + "y": -57.487335809022184, + "z": 0.0 + }, + { + "x": 195.34655576321467, + "y": -57.487213187866246, + "z": 0.0 + }, + { + "x": 196.3528614882279, + "y": -57.4870905667103, + "z": 0.0 + }, + { + "x": 197.3591672132411, + "y": -57.48696794555436, + "z": 0.0 + }, + { + "x": 198.36547293825433, + "y": -57.486845324398416, + "z": 0.0 + }, + { + "x": 199.37177866326752, + "y": -57.48672270324247, + "z": 0.0 + }, + { + "x": 200.37808438828074, + "y": -57.48660008208653, + "z": 0.0 + }, + { + "x": 201.38439011329396, + "y": -57.48647746093059, + "z": 0.0 + }, + { + "x": 202.39069583830715, + "y": -57.48635483977464, + "z": 0.0 + }, + { + "x": 203.39700156332037, + "y": -57.4862322186187, + "z": 0.0 + }, + { + "x": 204.40330728833356, + "y": -57.48610959746276, + "z": 0.0 + }, + { + "x": 205.40961301334679, + "y": -57.48598697630681, + "z": 0.0 + }, + { + "x": 206.41591873836, + "y": -57.48586435515087, + "z": 0.0 + }, + { + "x": 207.4222244633732, + "y": -57.48574173399493, + "z": 0.0 + }, + { + "x": 208.42853018838642, + "y": -57.48561911283898, + "z": 0.0 + }, + { + "x": 209.43483591339964, + "y": -57.48549649168304, + "z": 0.0 + }, + { + "x": 210.44114163841283, + "y": -57.48537387052709, + "z": 0.0 + }, + { + "x": 211.44744736342605, + "y": -57.485251249371146, + "z": 0.0 + }, + { + "x": 212.45375308843924, + "y": -57.48512862821521, + "z": 0.0 + }, + { + "x": 213.46005881345246, + "y": -57.48500600705926, + "z": 0.0 + }, + { + "x": 214.46636453846568, + "y": -57.484883385903316, + "z": 0.0 + }, + { + "x": 215.47267026347888, + "y": -57.48476076474738, + "z": 0.0 + }, + { + "x": 216.4789759884921, + "y": -57.48463814359143, + "z": 0.0 + }, + { + "x": 217.48528171350532, + "y": -57.484515522435494, + "z": 0.0 + }, + { + "x": 218.4915874385185, + "y": -57.48439290127955, + "z": 0.0 + }, + { + "x": 219.49789316353173, + "y": -57.4842702801236, + "z": 0.0 + }, + { + "x": 220.50419888854492, + "y": -57.484147658967665, + "z": 0.0 + }, + { + "x": 221.51050461355814, + "y": -57.48402503781172, + "z": 0.0 + }, + { + "x": 222.51681033857136, + "y": -57.48390241665577, + "z": 0.0 + }, + { + "x": 223.52311606358455, + "y": -57.483779795499835, + "z": 0.0 + }, + { + "x": 224.52942178859777, + "y": -57.48365717434389, + "z": 0.0 + }, + { + "x": 225.535727513611, + "y": -57.483534553187944, + "z": 0.0 + }, + { + "x": 226.54203323862419, + "y": -57.483411932032006, + "z": 0.0 + }, + { + "x": 227.5483389636374, + "y": -57.48328931087606, + "z": 0.0 + }, + { + "x": 228.5546446886506, + "y": -57.483166689720115, + "z": 0.0 + }, + { + "x": 229.56095041366382, + "y": -57.483044068564176, + "z": 0.0 + }, + { + "x": 230.56725613867704, + "y": -57.48292144740823, + "z": 0.0 + }, + { + "x": 231.57356186369023, + "y": -57.482798826252285, + "z": 0.0 + }, + { + "x": 232.57986758870345, + "y": -57.48267620509635, + "z": 0.0 + }, + { + "x": 233.58617331371667, + "y": -57.4825535839404, + "z": 0.0 + }, + { + "x": 234.5924790387299, + "y": -57.482430962784456, + "z": 0.0 + }, + { + "x": 235.5987847637431, + "y": -57.48230834162851, + "z": 0.0 + }, + { + "x": 236.60509048875633, + "y": -57.482185720472565, + "z": 0.0 + }, + { + "x": 237.61139621376955, + "y": -57.482063099316626, + "z": 0.0 + }, + { + "x": 238.61770193878274, + "y": -57.48194047816068, + "z": 0.0 + }, + { + "x": 239.62400766379596, + "y": -57.481817857004735, + "z": 0.0 + }, + { + "x": 240.63031338880918, + "y": -57.4816952358488, + "z": 0.0 + }, + { + "x": 241.6366191138224, + "y": -57.48157261469285, + "z": 0.0 + }, + { + "x": 242.64292483883563, + "y": -57.481449993536906, + "z": 0.0 + }, + { + "x": 243.64923056384885, + "y": -57.48132737238097, + "z": 0.0 + }, + { + "x": 244.65553628886207, + "y": -57.48120475122502, + "z": 0.0 + }, + { + "x": 245.6618420138753, + "y": -57.481082130069076, + "z": 0.0 + }, + { + "x": 246.6681477388885, + "y": -57.48095950891314, + "z": 0.0 + }, + { + "x": 247.6744534639017, + "y": -57.48083688775719, + "z": 0.0 + }, + { + "x": 248.68075918891492, + "y": -57.48071426660125, + "z": 0.0 + }, + { + "x": 249.68706491392814, + "y": -57.48059164544531, + "z": 0.0 + }, + { + "x": 250.69337063894136, + "y": -57.48046902428936, + "z": 0.0 + }, + { + "x": 251.69967636395458, + "y": -57.48034640313342, + "z": 0.0 + }, + { + "x": 252.7059820889678, + "y": -57.48022378197748, + "z": 0.0 + }, + { + "x": 253.71228781398102, + "y": -57.48010116082153, + "z": 0.0 + }, + { + "x": 254.71859353899424, + "y": -57.47997853966559, + "z": 0.0 + }, + { + "x": 255.72489926400746, + "y": -57.47985591850965, + "z": 0.0 + }, + { + "x": 256.7312049890207, + "y": -57.479733297353704, + "z": 0.0 + }, + { + "x": 257.7375107140339, + "y": -57.479610676197765, + "z": 0.0 + }, + { + "x": 258.7438164390471, + "y": -57.47948805504182, + "z": 0.0 + }, + { + "x": 259.7501221640603, + "y": -57.479365433885874, + "z": 0.0 + }, + { + "x": 260.75642788907356, + "y": -57.479242812729936, + "z": 0.0 + }, + { + "x": 261.76273361408676, + "y": -57.47912019157399, + "z": 0.0 + }, + { + "x": 262.76903933909995, + "y": -57.478997570418045, + "z": 0.0 + }, + { + "x": 263.7753450641132, + "y": -57.4788749492621, + "z": 0.0 + }, + { + "x": 264.7816507891264, + "y": -57.478752328106154, + "z": 0.0 + }, + { + "x": 265.78795651413964, + "y": -57.47862970695021, + "z": 0.0 + }, + { + "x": 266.79426223915283, + "y": -57.47850708579427, + "z": 0.0 + }, + { + "x": 267.800567964166, + "y": -57.478384464638324, + "z": 0.0 + }, + { + "x": 268.80687368917927, + "y": -57.47826184348238, + "z": 0.0 + }, + { + "x": 269.81317941419246, + "y": -57.47813922232644, + "z": 0.0 + }, + { + "x": 270.8194851392057, + "y": -57.478016601170495, + "z": 0.0 + }, + { + "x": 271.8257908642189, + "y": -57.47789398001455, + "z": 0.0 + }, + { + "x": 272.83209658923215, + "y": -57.47777135885861, + "z": 0.0 + }, + { + "x": 273.83840231424534, + "y": -57.477648737702665, + "z": 0.0 + }, + { + "x": 274.8447080392586, + "y": -57.47752611654672, + "z": 0.0 + }, + { + "x": 275.8510137642718, + "y": -57.47740349539078, + "z": 0.0 + }, + { + "x": 276.857319489285, + "y": -57.477280874234836, + "z": 0.0 + }, + { + "x": 277.8636252142982, + "y": -57.4771582530789, + "z": 0.0 + }, + { + "x": 278.8699309393114, + "y": -57.47703563192295, + "z": 0.0 + }, + { + "x": 279.87623666432467, + "y": -57.47691301076701, + "z": 0.0 + }, + { + "x": 280.88254238933786, + "y": -57.47679038961107, + "z": 0.0 + }, + { + "x": 281.8888481143511, + "y": -57.47666776845512, + "z": 0.0 + }, + { + "x": 282.8951538393643, + "y": -57.47654514729918, + "z": 0.0 + }, + { + "x": 283.90145956437755, + "y": -57.47642252614324, + "z": 0.0 + }, + { + "x": 284.90776528939074, + "y": -57.47629990498729, + "z": 0.0 + }, + { + "x": 285.91407101440393, + "y": -57.47617728383135, + "z": 0.0 + }, + { + "x": 286.9203767394172, + "y": -57.47605466267541, + "z": 0.0 + }, + { + "x": 287.92668246443037, + "y": -57.475932041519464, + "z": 0.0 + }, + { + "x": 288.9329881894436, + "y": -57.47580942036352, + "z": 0.0 + }, + { + "x": 289.9392939144568, + "y": -57.47568679920758, + "z": 0.0 + }, + { + "x": 290.94559963947006, + "y": -57.47556417805163, + "z": 0.0 + }, + { + "x": 291.95190536448325, + "y": -57.47544155689568, + "z": 0.0 + }, + { + "x": 292.9582110894965, + "y": -57.47531893573974, + "z": 0.0 + }, + { + "x": 293.9645168145097, + "y": -57.4751963145838, + "z": 0.0 + }, + { + "x": 294.9708225395229, + "y": -57.47507369342785, + "z": 0.0 + }, + { + "x": 295.97712826453613, + "y": -57.474951072271914, + "z": 0.0 + }, + { + "x": 296.9834339895493, + "y": -57.47482845111597, + "z": 0.0 + }, + { + "x": 297.9897397145625, + "y": -57.47470582996003, + "z": 0.0 + }, + { + "x": 298.9960454395757, + "y": -57.474583208804084, + "z": 0.0 + }, + { + "x": 300.00235116458896, + "y": -57.47446058764814, + "z": 0.0 + }, + { + "x": 301.00865688960215, + "y": -57.4743379664922, + "z": 0.0 + }, + { + "x": 302.01496261461534, + "y": -57.474215345336255, + "z": 0.0 + }, + { + "x": 303.02126833962853, + "y": -57.47409272418031, + "z": 0.0 + }, + { + "x": 304.0275740646417, + "y": -57.47397010302437, + "z": 0.0 + }, + { + "x": 305.033879789655, + "y": -57.473847481868425, + "z": 0.0 + }, + { + "x": 306.04018551466817, + "y": -57.47372486071248, + "z": 0.0 + }, + { + "x": 307.04649123968136, + "y": -57.47360223955654, + "z": 0.0 + }, + { + "x": 308.05279696469455, + "y": -57.473479618400596, + "z": 0.0 + }, + { + "x": 309.05910268970774, + "y": -57.47335699724465, + "z": 0.0 + }, + { + "x": 310.06540841472093, + "y": -57.47323437608871, + "z": 0.0 + }, + { + "x": 311.0717141397342, + "y": -57.473111754932766, + "z": 0.0 + }, + { + "x": 312.0780198647474, + "y": -57.47298913377682, + "z": 0.0 + }, + { + "x": 313.08432558976057, + "y": -57.47286651262088, + "z": 0.0 + }, + { + "x": 314.09063131477376, + "y": -57.47274389146494, + "z": 0.0 + }, + { + "x": 315.09693703978695, + "y": -57.47262127030899, + "z": 0.0 + }, + { + "x": 316.1032427648002, + "y": -57.47249864915305, + "z": 0.0 + }, + { + "x": 317.1095484898134, + "y": -57.47237602799711, + "z": 0.0 + }, + { + "x": 318.1158542148266, + "y": -57.47225340684116, + "z": 0.0 + }, + { + "x": 319.1221599398398, + "y": -57.472130785685216, + "z": 0.0 + }, + { + "x": 320.12846566485297, + "y": -57.47200816452927, + "z": 0.0 + }, + { + "x": 321.1347713898662, + "y": -57.47188554337333, + "z": 0.0 + }, + { + "x": 322.1410771148794, + "y": -57.47176292221739, + "z": 0.0 + }, + { + "x": 323.1473828398926, + "y": -57.47164030106144, + "z": 0.0 + }, + { + "x": 324.1536885649058, + "y": -57.4715176799055, + "z": 0.0 + }, + { + "x": 325.159994289919, + "y": -57.47139505874956, + "z": 0.0 + } + ] + }, + { + "id": 53, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.15975058434924, + "y": -55.47139507359766, + "z": 0.0 + }, + { + "x": 324.153444859336, + "y": -55.4715176947536, + "z": 0.0 + }, + { + "x": 323.14713913432286, + "y": -55.47164031590954, + "z": 0.0 + }, + { + "x": 322.1408334093096, + "y": -55.47176293706549, + "z": 0.0 + }, + { + "x": 321.1345276842965, + "y": -55.47188555822143, + "z": 0.0 + }, + { + "x": 320.1282219592832, + "y": -55.47200817937737, + "z": 0.0 + }, + { + "x": 319.12191623427, + "y": -55.47213080053332, + "z": 0.0 + }, + { + "x": 318.11561050925684, + "y": -55.47225342168926, + "z": 0.0 + }, + { + "x": 317.1093047842436, + "y": -55.47237604284521, + "z": 0.0 + }, + { + "x": 316.10299905923046, + "y": -55.47249866400115, + "z": 0.0 + }, + { + "x": 315.0966933342172, + "y": -55.47262128515709, + "z": 0.0 + }, + { + "x": 314.09038760920396, + "y": -55.47274390631304, + "z": 0.0 + }, + { + "x": 313.0840818841908, + "y": -55.47286652746898, + "z": 0.0 + }, + { + "x": 312.07777615917763, + "y": -55.47298914862492, + "z": 0.0 + }, + { + "x": 311.07147043416444, + "y": -55.47311176978087, + "z": 0.0 + }, + { + "x": 310.0651647091512, + "y": -55.47323439093681, + "z": 0.0 + }, + { + "x": 309.058858984138, + "y": -55.47335701209275, + "z": 0.0 + }, + { + "x": 308.0525532591248, + "y": -55.4734796332487, + "z": 0.0 + }, + { + "x": 307.04624753411156, + "y": -55.47360225440464, + "z": 0.0 + }, + { + "x": 306.0399418090984, + "y": -55.47372487556058, + "z": 0.0 + }, + { + "x": 305.0336360840852, + "y": -55.47384749671653, + "z": 0.0 + }, + { + "x": 304.0273303590719, + "y": -55.47397011787247, + "z": 0.0 + }, + { + "x": 303.0210246340588, + "y": -55.47409273902841, + "z": 0.0 + }, + { + "x": 302.01471890904554, + "y": -55.47421536018436, + "z": 0.0 + }, + { + "x": 301.0084131840324, + "y": -55.4743379813403, + "z": 0.0 + }, + { + "x": 300.0021074590192, + "y": -55.47446060249624, + "z": 0.0 + }, + { + "x": 298.9958017340059, + "y": -55.47458322365219, + "z": 0.0 + }, + { + "x": 297.9894960089928, + "y": -55.47470584480813, + "z": 0.0 + }, + { + "x": 296.9831902839796, + "y": -55.474828465964066, + "z": 0.0 + }, + { + "x": 295.9768845589664, + "y": -55.47495108712002, + "z": 0.0 + }, + { + "x": 294.97057883395314, + "y": -55.47507370827596, + "z": 0.0 + }, + { + "x": 293.9642731089399, + "y": -55.475196329431895, + "z": 0.0 + }, + { + "x": 292.95796738392676, + "y": -55.47531895058785, + "z": 0.0 + }, + { + "x": 291.9516616589135, + "y": -55.47544157174379, + "z": 0.0 + }, + { + "x": 290.94535593390026, + "y": -55.475564192899725, + "z": 0.0 + }, + { + "x": 289.939050208887, + "y": -55.47568681405568, + "z": 0.0 + }, + { + "x": 288.9327444838739, + "y": -55.475809435211616, + "z": 0.0 + }, + { + "x": 287.9264387588606, + "y": -55.47593205636757, + "z": 0.0 + }, + { + "x": 286.9201330338474, + "y": -55.47605467752351, + "z": 0.0 + }, + { + "x": 285.9138273088342, + "y": -55.476177298679445, + "z": 0.0 + }, + { + "x": 284.907521583821, + "y": -55.4762999198354, + "z": 0.0 + }, + { + "x": 283.90121585880775, + "y": -55.47642254099134, + "z": 0.0 + }, + { + "x": 282.89491013379455, + "y": -55.476545162147275, + "z": 0.0 + }, + { + "x": 281.88860440878136, + "y": -55.47666778330323, + "z": 0.0 + }, + { + "x": 280.8822986837681, + "y": -55.476790404459166, + "z": 0.0 + }, + { + "x": 279.8759929587549, + "y": -55.476913025615104, + "z": 0.0 + }, + { + "x": 278.8696872337416, + "y": -55.47703564677106, + "z": 0.0 + }, + { + "x": 277.8633815087285, + "y": -55.477158267926995, + "z": 0.0 + }, + { + "x": 276.85707578371523, + "y": -55.477280889082934, + "z": 0.0 + }, + { + "x": 275.850770058702, + "y": -55.47740351023889, + "z": 0.0 + }, + { + "x": 274.84446433368885, + "y": -55.477526131394825, + "z": 0.0 + }, + { + "x": 273.8381586086756, + "y": -55.47764875255076, + "z": 0.0 + }, + { + "x": 272.83185288366235, + "y": -55.477771373706716, + "z": 0.0 + }, + { + "x": 271.82554715864916, + "y": -55.477893994862654, + "z": 0.0 + }, + { + "x": 270.81924143363597, + "y": -55.47801661601859, + "z": 0.0 + }, + { + "x": 269.8129357086227, + "y": -55.478139237174545, + "z": 0.0 + }, + { + "x": 268.8066299836095, + "y": -55.478261858330484, + "z": 0.0 + }, + { + "x": 267.8003242585962, + "y": -55.47838447948642, + "z": 0.0 + }, + { + "x": 266.7940185335831, + "y": -55.478507100642375, + "z": 0.0 + }, + { + "x": 265.7877128085699, + "y": -55.47862972179831, + "z": 0.0 + }, + { + "x": 264.7814070835566, + "y": -55.47875234295425, + "z": 0.0 + }, + { + "x": 263.77510135854345, + "y": -55.478874964110204, + "z": 0.0 + }, + { + "x": 262.7687956335302, + "y": -55.47899758526614, + "z": 0.0 + }, + { + "x": 261.76248990851695, + "y": -55.479120206422095, + "z": 0.0 + }, + { + "x": 260.7561841835038, + "y": -55.479242827578034, + "z": 0.0 + }, + { + "x": 259.74987845849057, + "y": -55.47936544873397, + "z": 0.0 + }, + { + "x": 258.7435727334773, + "y": -55.479488069889925, + "z": 0.0 + }, + { + "x": 257.7372670084641, + "y": -55.47961069104586, + "z": 0.0 + }, + { + "x": 256.73096128345094, + "y": -55.4797333122018, + "z": 0.0 + }, + { + "x": 255.7246555584377, + "y": -55.479855933357754, + "z": 0.0 + }, + { + "x": 254.7183498334245, + "y": -55.47997855451369, + "z": 0.0 + }, + { + "x": 253.71204410841125, + "y": -55.48010117566963, + "z": 0.0 + }, + { + "x": 252.70573838339806, + "y": -55.480223796825584, + "z": 0.0 + }, + { + "x": 251.69943265838484, + "y": -55.48034641798152, + "z": 0.0 + }, + { + "x": 250.69312693337162, + "y": -55.48046903913746, + "z": 0.0 + }, + { + "x": 249.68682120835837, + "y": -55.48059166029341, + "z": 0.0 + }, + { + "x": 248.68051548334518, + "y": -55.48071428144935, + "z": 0.0 + }, + { + "x": 247.67420975833193, + "y": -55.48083690260529, + "z": 0.0 + }, + { + "x": 246.66790403331873, + "y": -55.48095952376124, + "z": 0.0 + }, + { + "x": 245.66159830830554, + "y": -55.48108214491718, + "z": 0.0 + }, + { + "x": 244.6552925832923, + "y": -55.48120476607312, + "z": 0.0 + }, + { + "x": 243.6489868582791, + "y": -55.48132738722907, + "z": 0.0 + }, + { + "x": 242.64268113326588, + "y": -55.48145000838501, + "z": 0.0 + }, + { + "x": 241.63637540825266, + "y": -55.48157262954095, + "z": 0.0 + }, + { + "x": 240.6300696832394, + "y": -55.4816952506969, + "z": 0.0 + }, + { + "x": 239.62376395822622, + "y": -55.48181787185284, + "z": 0.0 + }, + { + "x": 238.61745823321297, + "y": -55.48194049300878, + "z": 0.0 + }, + { + "x": 237.61115250819978, + "y": -55.48206311416473, + "z": 0.0 + }, + { + "x": 236.6048467831866, + "y": -55.48218573532067, + "z": 0.0 + }, + { + "x": 235.59854105817334, + "y": -55.48230835647661, + "z": 0.0 + }, + { + "x": 234.59223533316015, + "y": -55.48243097763256, + "z": 0.0 + }, + { + "x": 233.5859296081469, + "y": -55.4825535987885, + "z": 0.0 + }, + { + "x": 232.5796238831337, + "y": -55.48267621994445, + "z": 0.0 + }, + { + "x": 231.5733181581205, + "y": -55.48279884110039, + "z": 0.0 + }, + { + "x": 230.56701243310727, + "y": -55.48292146225633, + "z": 0.0 + }, + { + "x": 229.56070670809407, + "y": -55.48304408341228, + "z": 0.0 + }, + { + "x": 228.55440098308085, + "y": -55.48316670456822, + "z": 0.0 + }, + { + "x": 227.54809525806763, + "y": -55.48328932572416, + "z": 0.0 + }, + { + "x": 226.54178953305444, + "y": -55.48341194688011, + "z": 0.0 + }, + { + "x": 225.53548380804125, + "y": -55.48353456803605, + "z": 0.0 + }, + { + "x": 224.529178083028, + "y": -55.48365718919199, + "z": 0.0 + }, + { + "x": 223.5228723580148, + "y": -55.48377981034794, + "z": 0.0 + }, + { + "x": 222.51656663300162, + "y": -55.48390243150388, + "z": 0.0 + }, + { + "x": 221.51026090798837, + "y": -55.48402505265982, + "z": 0.0 + }, + { + "x": 220.50395518297518, + "y": -55.48414767381577, + "z": 0.0 + }, + { + "x": 219.49764945796198, + "y": -55.48427029497171, + "z": 0.0 + }, + { + "x": 218.49134373294874, + "y": -55.484392916127646, + "z": 0.0 + }, + { + "x": 217.48503800793554, + "y": -55.4845155372836, + "z": 0.0 + }, + { + "x": 216.47873228292235, + "y": -55.48463815843954, + "z": 0.0 + }, + { + "x": 215.4724265579091, + "y": -55.484760779595476, + "z": 0.0 + }, + { + "x": 214.46612083289594, + "y": -55.484883400751414, + "z": 0.0 + }, + { + "x": 213.45981510788272, + "y": -55.48500602190737, + "z": 0.0 + }, + { + "x": 212.45350938286947, + "y": -55.485128643063305, + "z": 0.0 + }, + { + "x": 211.4472036578563, + "y": -55.485251264219244, + "z": 0.0 + }, + { + "x": 210.4408979328431, + "y": -55.485373885375196, + "z": 0.0 + }, + { + "x": 209.4345922078299, + "y": -55.485496506531135, + "z": 0.0 + }, + { + "x": 208.42828648281665, + "y": -55.48561912768709, + "z": 0.0 + }, + { + "x": 207.42198075780345, + "y": -55.485741748843026, + "z": 0.0 + }, + { + "x": 206.41567503279026, + "y": -55.48586436999898, + "z": 0.0 + }, + { + "x": 205.40936930777704, + "y": -55.48598699115492, + "z": 0.0 + }, + { + "x": 204.40306358276382, + "y": -55.486109612310855, + "z": 0.0 + }, + { + "x": 203.39675785775063, + "y": -55.48623223346681, + "z": 0.0 + }, + { + "x": 202.3904521327374, + "y": -55.486354854622746, + "z": 0.0 + }, + { + "x": 201.3841464077242, + "y": -55.486477475778685, + "z": 0.0 + }, + { + "x": 200.377840682711, + "y": -55.48660009693464, + "z": 0.0 + }, + { + "x": 199.37153495769778, + "y": -55.486722718090576, + "z": 0.0 + }, + { + "x": 198.36522923268456, + "y": -55.486845339246514, + "z": 0.0 + }, + { + "x": 197.35892350767136, + "y": -55.48696796040247, + "z": 0.0 + }, + { + "x": 196.35261778265814, + "y": -55.487090581558405, + "z": 0.0 + }, + { + "x": 195.34631205764492, + "y": -55.487213202714344, + "z": 0.0 + }, + { + "x": 194.3400063326317, + "y": -55.48733582387028, + "z": 0.0 + }, + { + "x": 193.33370060761848, + "y": -55.487458445026235, + "z": 0.0 + }, + { + "x": 192.3273948826053, + "y": -55.48758106618217, + "z": 0.0 + }, + { + "x": 191.32108915759207, + "y": -55.48770368733811, + "z": 0.0 + }, + { + "x": 190.31478343257885, + "y": -55.487826308494064, + "z": 0.0 + }, + { + "x": 189.30847770756566, + "y": -55.48794892965, + "z": 0.0 + }, + { + "x": 188.30217198255244, + "y": -55.48807155080594, + "z": 0.0 + }, + { + "x": 187.29586625753922, + "y": -55.488194171961894, + "z": 0.0 + }, + { + "x": 186.28956053252597, + "y": -55.48831679311783, + "z": 0.0 + }, + { + "x": 185.2832548075128, + "y": -55.48843941427377, + "z": 0.0 + }, + { + "x": 184.27694908249958, + "y": -55.48856203542972, + "z": 0.0 + }, + { + "x": 183.27064335748634, + "y": -55.48868465658566, + "z": 0.0 + }, + { + "x": 182.26433763247317, + "y": -55.4888072777416, + "z": 0.0 + }, + { + "x": 181.25803190745995, + "y": -55.48892989889755, + "z": 0.0 + }, + { + "x": 180.2517261824467, + "y": -55.489052520053505, + "z": 0.0 + }, + { + "x": 179.2454204574335, + "y": -55.489175141209444, + "z": 0.0 + }, + { + "x": 178.23911473242032, + "y": -55.48929776236538, + "z": 0.0 + }, + { + "x": 177.23280900740707, + "y": -55.489420383521335, + "z": 0.0 + }, + { + "x": 176.22650328239388, + "y": -55.48954300467727, + "z": 0.0 + }, + { + "x": 175.2201975573807, + "y": -55.48966562583321, + "z": 0.0 + }, + { + "x": 174.21389183236747, + "y": -55.48978824698915, + "z": 0.0 + }, + { + "x": 173.20758610735425, + "y": -55.4899108681451, + "z": 0.0 + }, + { + "x": 172.20128038234105, + "y": -55.49003348930104, + "z": 0.0 + }, + { + "x": 171.19497465732783, + "y": -55.49015611045698, + "z": 0.0 + }, + { + "x": 170.1886689323146, + "y": -55.49027873161293, + "z": 0.0 + }, + { + "x": 169.18236320730136, + "y": -55.49040135276887, + "z": 0.0 + }, + { + "x": 168.1760574822882, + "y": -55.49052397392481, + "z": 0.0 + }, + { + "x": 167.16975175727498, + "y": -55.49064659508076, + "z": 0.0 + } + ] + }, + { + "id": 54, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 167.17023916841447, + "y": -59.49064656538455, + "z": 0.0 + }, + { + "x": 168.1765448934277, + "y": -59.49052394422861, + "z": 0.0 + }, + { + "x": 169.1828506184409, + "y": -59.490401323072675, + "z": 0.0 + }, + { + "x": 170.1891563434541, + "y": -59.49027870191672, + "z": 0.0 + }, + { + "x": 171.19546206846732, + "y": -59.490156080760784, + "z": 0.0 + }, + { + "x": 172.20176779348054, + "y": -59.490033459604845, + "z": 0.0 + }, + { + "x": 173.20807351849373, + "y": -59.48991083844889, + "z": 0.0 + }, + { + "x": 174.21437924350695, + "y": -59.489788217292954, + "z": 0.0 + }, + { + "x": 175.22068496852017, + "y": -59.489665596137016, + "z": 0.0 + }, + { + "x": 176.22699069353337, + "y": -59.48954297498106, + "z": 0.0 + }, + { + "x": 177.23329641854662, + "y": -59.489420353825125, + "z": 0.0 + }, + { + "x": 178.2396021435598, + "y": -59.489297732669186, + "z": 0.0 + }, + { + "x": 179.245907868573, + "y": -59.489175111513234, + "z": 0.0 + }, + { + "x": 180.25221359358625, + "y": -59.489052490357295, + "z": 0.0 + }, + { + "x": 181.25851931859944, + "y": -59.48892986920134, + "z": 0.0 + }, + { + "x": 182.26482504361266, + "y": -59.488807248045404, + "z": 0.0 + }, + { + "x": 183.27113076862588, + "y": -59.488684626889466, + "z": 0.0 + }, + { + "x": 184.27743649363907, + "y": -59.48856200573351, + "z": 0.0 + }, + { + "x": 185.2837422186523, + "y": -59.488439384577575, + "z": 0.0 + }, + { + "x": 186.2900479436655, + "y": -59.488316763421636, + "z": 0.0 + }, + { + "x": 187.2963536686787, + "y": -59.488194142265684, + "z": 0.0 + }, + { + "x": 188.30265939369193, + "y": -59.488071521109745, + "z": 0.0 + }, + { + "x": 189.30896511870515, + "y": -59.48794889995381, + "z": 0.0 + }, + { + "x": 190.3152708437184, + "y": -59.487826278797854, + "z": 0.0 + }, + { + "x": 191.32157656873156, + "y": -59.487703657641916, + "z": 0.0 + }, + { + "x": 192.32788229374478, + "y": -59.48758103648598, + "z": 0.0 + }, + { + "x": 193.33418801875803, + "y": -59.487458415330025, + "z": 0.0 + }, + { + "x": 194.3404937437712, + "y": -59.487335794174086, + "z": 0.0 + }, + { + "x": 195.3467994687844, + "y": -59.48721317301815, + "z": 0.0 + }, + { + "x": 196.35310519379763, + "y": -59.487090551862195, + "z": 0.0 + }, + { + "x": 197.35941091881085, + "y": -59.48696793070626, + "z": 0.0 + }, + { + "x": 198.3657166438241, + "y": -59.48684530955032, + "z": 0.0 + }, + { + "x": 199.37202236883726, + "y": -59.486722688394366, + "z": 0.0 + }, + { + "x": 200.37832809385048, + "y": -59.48660006723843, + "z": 0.0 + }, + { + "x": 201.38463381886373, + "y": -59.48647744608249, + "z": 0.0 + }, + { + "x": 202.3909395438769, + "y": -59.486354824926536, + "z": 0.0 + }, + { + "x": 203.39724526889012, + "y": -59.4862322037706, + "z": 0.0 + }, + { + "x": 204.4035509939033, + "y": -59.48610958261466, + "z": 0.0 + }, + { + "x": 205.40985671891653, + "y": -59.48598696145871, + "z": 0.0 + }, + { + "x": 206.41616244392975, + "y": -59.48586434030277, + "z": 0.0 + }, + { + "x": 207.42246816894294, + "y": -59.48574171914683, + "z": 0.0 + }, + { + "x": 208.4287738939562, + "y": -59.48561909799088, + "z": 0.0 + }, + { + "x": 209.43507961896938, + "y": -59.48549647683494, + "z": 0.0 + }, + { + "x": 210.44138534398257, + "y": -59.485373855678986, + "z": 0.0 + }, + { + "x": 211.4476910689958, + "y": -59.48525123452305, + "z": 0.0 + }, + { + "x": 212.45399679400902, + "y": -59.48512861336711, + "z": 0.0 + }, + { + "x": 213.4603025190222, + "y": -59.48500599221116, + "z": 0.0 + }, + { + "x": 214.46660824403543, + "y": -59.48488337105522, + "z": 0.0 + }, + { + "x": 215.47291396904865, + "y": -59.48476074989928, + "z": 0.0 + }, + { + "x": 216.47921969406184, + "y": -59.48463812874333, + "z": 0.0 + }, + { + "x": 217.4855254190751, + "y": -59.48451550758739, + "z": 0.0 + }, + { + "x": 218.49183114408828, + "y": -59.48439288643145, + "z": 0.0 + }, + { + "x": 219.49813686910147, + "y": -59.4842702652755, + "z": 0.0 + }, + { + "x": 220.50444259411466, + "y": -59.48414764411956, + "z": 0.0 + }, + { + "x": 221.5107483191279, + "y": -59.48402502296362, + "z": 0.0 + }, + { + "x": 222.5170540441411, + "y": -59.48390240180767, + "z": 0.0 + }, + { + "x": 223.5233597691543, + "y": -59.48377978065173, + "z": 0.0 + }, + { + "x": 224.52966549416755, + "y": -59.48365715949579, + "z": 0.0 + }, + { + "x": 225.53597121918074, + "y": -59.48353453833984, + "z": 0.0 + }, + { + "x": 226.54227694419393, + "y": -59.4834119171839, + "z": 0.0 + }, + { + "x": 227.54858266920718, + "y": -59.48328929602796, + "z": 0.0 + }, + { + "x": 228.55488839422034, + "y": -59.48316667487201, + "z": 0.0 + }, + { + "x": 229.56119411923356, + "y": -59.48304405371607, + "z": 0.0 + }, + { + "x": 230.5674998442468, + "y": -59.48292143256013, + "z": 0.0 + }, + { + "x": 231.57380556925997, + "y": -59.48279881140418, + "z": 0.0 + }, + { + "x": 232.5801112942732, + "y": -59.48267619024824, + "z": 0.0 + }, + { + "x": 233.58641701928644, + "y": -59.4825535690923, + "z": 0.0 + }, + { + "x": 234.59272274429964, + "y": -59.48243094793635, + "z": 0.0 + }, + { + "x": 235.59902846931288, + "y": -59.48230832678041, + "z": 0.0 + }, + { + "x": 236.60533419432608, + "y": -59.48218570562446, + "z": 0.0 + }, + { + "x": 237.61163991933932, + "y": -59.48206308446852, + "z": 0.0 + }, + { + "x": 238.61794564435252, + "y": -59.48194046331258, + "z": 0.0 + }, + { + "x": 239.6242513693657, + "y": -59.48181784215663, + "z": 0.0 + }, + { + "x": 240.63055709437896, + "y": -59.48169522100069, + "z": 0.0 + }, + { + "x": 241.63686281939215, + "y": -59.48157259984475, + "z": 0.0 + }, + { + "x": 242.64316854440537, + "y": -59.4814499786888, + "z": 0.0 + }, + { + "x": 243.6494742694186, + "y": -59.48132735753286, + "z": 0.0 + }, + { + "x": 244.65577999443184, + "y": -59.481204736376924, + "z": 0.0 + }, + { + "x": 245.66208571944503, + "y": -59.48108211522097, + "z": 0.0 + }, + { + "x": 246.66839144445828, + "y": -59.48095949406503, + "z": 0.0 + }, + { + "x": 247.67469716947147, + "y": -59.480836872909094, + "z": 0.0 + }, + { + "x": 248.68100289448466, + "y": -59.48071425175314, + "z": 0.0 + }, + { + "x": 249.6873086194979, + "y": -59.4805916305972, + "z": 0.0 + }, + { + "x": 250.6936143445111, + "y": -59.480469009441265, + "z": 0.0 + }, + { + "x": 251.69992006952432, + "y": -59.48034638828531, + "z": 0.0 + }, + { + "x": 252.70622579453754, + "y": -59.480223767129374, + "z": 0.0 + }, + { + "x": 253.7125315195508, + "y": -59.480101145973435, + "z": 0.0 + }, + { + "x": 254.71883724456399, + "y": -59.47997852481748, + "z": 0.0 + }, + { + "x": 255.72514296957723, + "y": -59.479855903661544, + "z": 0.0 + }, + { + "x": 256.7314486945904, + "y": -59.479733282505606, + "z": 0.0 + }, + { + "x": 257.7377544196037, + "y": -59.47961066134967, + "z": 0.0 + }, + { + "x": 258.7440601446169, + "y": -59.479488040193715, + "z": 0.0 + }, + { + "x": 259.75036586963006, + "y": -59.47936541903778, + "z": 0.0 + }, + { + "x": 260.7566715946433, + "y": -59.47924279788184, + "z": 0.0 + }, + { + "x": 261.76297731965656, + "y": -59.479120176725885, + "z": 0.0 + }, + { + "x": 262.7692830446697, + "y": -59.47899755556995, + "z": 0.0 + }, + { + "x": 263.77558876968294, + "y": -59.478874934413994, + "z": 0.0 + }, + { + "x": 264.7818944946962, + "y": -59.478752313258056, + "z": 0.0 + }, + { + "x": 265.7882002197094, + "y": -59.4786296921021, + "z": 0.0 + }, + { + "x": 266.7945059447226, + "y": -59.478507070946165, + "z": 0.0 + }, + { + "x": 267.8008116697358, + "y": -59.47838444979023, + "z": 0.0 + }, + { + "x": 268.807117394749, + "y": -59.478261828634274, + "z": 0.0 + }, + { + "x": 269.8134231197622, + "y": -59.478139207478335, + "z": 0.0 + }, + { + "x": 270.81972884477545, + "y": -59.4780165863224, + "z": 0.0 + }, + { + "x": 271.82603456978865, + "y": -59.477893965166444, + "z": 0.0 + }, + { + "x": 272.83234029480195, + "y": -59.477771344010506, + "z": 0.0 + }, + { + "x": 273.8386460198151, + "y": -59.47764872285457, + "z": 0.0 + }, + { + "x": 274.84495174482834, + "y": -59.477526101698615, + "z": 0.0 + }, + { + "x": 275.8512574698416, + "y": -59.47740348054268, + "z": 0.0 + }, + { + "x": 276.8575631948547, + "y": -59.47728085938674, + "z": 0.0 + }, + { + "x": 277.86386891986797, + "y": -59.4771582382308, + "z": 0.0 + }, + { + "x": 278.8701746448812, + "y": -59.47703561707485, + "z": 0.0 + }, + { + "x": 279.8764803698944, + "y": -59.47691299591891, + "z": 0.0 + }, + { + "x": 280.8827860949076, + "y": -59.47679037476297, + "z": 0.0 + }, + { + "x": 281.88909181992085, + "y": -59.47666775360702, + "z": 0.0 + }, + { + "x": 282.89539754493404, + "y": -59.47654513245108, + "z": 0.0 + }, + { + "x": 283.90170326994735, + "y": -59.47642251129514, + "z": 0.0 + }, + { + "x": 284.9080089949605, + "y": -59.47629989013919, + "z": 0.0 + }, + { + "x": 285.9143147199737, + "y": -59.47617726898325, + "z": 0.0 + }, + { + "x": 286.920620444987, + "y": -59.47605464782731, + "z": 0.0 + }, + { + "x": 287.9269261700001, + "y": -59.47593202667136, + "z": 0.0 + }, + { + "x": 288.93323189501336, + "y": -59.47580940551542, + "z": 0.0 + }, + { + "x": 289.9395376200266, + "y": -59.47568678435948, + "z": 0.0 + }, + { + "x": 290.94584334503986, + "y": -59.47556416320353, + "z": 0.0 + }, + { + "x": 291.952149070053, + "y": -59.47544154204758, + "z": 0.0 + }, + { + "x": 292.95845479506625, + "y": -59.47531892089164, + "z": 0.0 + }, + { + "x": 293.9647605200795, + "y": -59.4751962997357, + "z": 0.0 + }, + { + "x": 294.97106624509263, + "y": -59.47507367857975, + "z": 0.0 + }, + { + "x": 295.9773719701059, + "y": -59.47495105742381, + "z": 0.0 + }, + { + "x": 296.98367769511907, + "y": -59.47482843626787, + "z": 0.0 + }, + { + "x": 297.98998342013226, + "y": -59.47470581511193, + "z": 0.0 + }, + { + "x": 298.9962891451455, + "y": -59.47458319395598, + "z": 0.0 + }, + { + "x": 300.0025948701587, + "y": -59.47446057280004, + "z": 0.0 + }, + { + "x": 301.0089005951719, + "y": -59.4743379516441, + "z": 0.0 + }, + { + "x": 302.01520632018514, + "y": -59.47421533048815, + "z": 0.0 + }, + { + "x": 303.0215120451983, + "y": -59.47409270933221, + "z": 0.0 + }, + { + "x": 304.0278177702115, + "y": -59.47397008817627, + "z": 0.0 + }, + { + "x": 305.0341234952248, + "y": -59.47384746702032, + "z": 0.0 + }, + { + "x": 306.0404292202379, + "y": -59.47372484586438, + "z": 0.0 + }, + { + "x": 307.04673494525116, + "y": -59.47360222470844, + "z": 0.0 + }, + { + "x": 308.0530406702643, + "y": -59.47347960355249, + "z": 0.0 + }, + { + "x": 309.0593463952775, + "y": -59.47335698239655, + "z": 0.0 + }, + { + "x": 310.0656521202907, + "y": -59.473234361240614, + "z": 0.0 + }, + { + "x": 311.0719578453039, + "y": -59.47311174008466, + "z": 0.0 + }, + { + "x": 312.0782635703171, + "y": -59.47298911892872, + "z": 0.0 + }, + { + "x": 313.0845692953303, + "y": -59.472866497772785, + "z": 0.0 + }, + { + "x": 314.09087502034356, + "y": -59.47274387661683, + "z": 0.0 + }, + { + "x": 315.0971807453567, + "y": -59.47262125546089, + "z": 0.0 + }, + { + "x": 316.10348647036994, + "y": -59.472498634304955, + "z": 0.0 + }, + { + "x": 317.1097921953832, + "y": -59.472376013149, + "z": 0.0 + }, + { + "x": 318.1160979203963, + "y": -59.472253391993064, + "z": 0.0 + }, + { + "x": 319.1224036454096, + "y": -59.47213077083711, + "z": 0.0 + }, + { + "x": 320.1287093704227, + "y": -59.47200814968117, + "z": 0.0 + }, + { + "x": 321.13501509543596, + "y": -59.471885528525235, + "z": 0.0 + }, + { + "x": 322.1413208204492, + "y": -59.47176290736928, + "z": 0.0 + }, + { + "x": 323.14762654546234, + "y": -59.47164028621334, + "z": 0.0 + }, + { + "x": 324.1539322704756, + "y": -59.471517665057405, + "z": 0.0 + }, + { + "x": 325.1602379954887, + "y": -59.47139504390145, + "z": 0.0 + } + ] + }, + { + "id": 55, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 398.37974812672553, + "y": -9.804292031687147, + "z": 0.0 + }, + { + "x": 398.36780240926834, + "y": -8.75089642316067, + "z": 0.0 + }, + { + "x": 398.2768958325799, + "y": -7.442858360963409, + "z": 0.0 + }, + { + "x": 397.9900120694231, + "y": -5.900146342483256, + "z": 0.0 + }, + { + "x": 397.5637979654414, + "y": -4.495994891100093, + "z": 0.0 + }, + { + "x": 396.99328823353665, + "y": -3.1428683346251147, + "z": 0.0 + }, + { + "x": 396.2283922371167, + "y": -1.799105281797945, + "z": 0.0 + }, + { + "x": 395.2664453280994, + "y": -0.5316745673262111, + "z": 0.0 + }, + { + "x": 394.24725845204534, + "y": 0.534257017414824, + "z": 0.0 + }, + { + "x": 393.11216551907125, + "y": 1.4903391899191767, + "z": 0.0 + }, + { + "x": 391.8245853286279, + "y": 2.3367176074276426, + "z": 0.0 + }, + { + "x": 390.3361931478448, + "y": 3.0382856252769592, + "z": 0.0 + }, + { + "x": 388.92224985890766, + "y": 3.5068677477098023, + "z": 0.0 + }, + { + "x": 387.4654183079017, + "y": 3.817648145243075, + "z": 0.0 + }, + { + "x": 385.96655987486594, + "y": 3.964678690223338, + "z": 0.0 + }, + { + "x": 384.598648710693, + "y": 3.9799906424916998, + "z": 0.0 + } + ] + }, + { + "id": 56, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 384.58134396508825, + "y": -4.01999064159763, + "z": 0.0 + }, + { + "x": 385.3210700215233, + "y": -4.009237715736265, + "z": 0.0 + }, + { + "x": 385.92311669882133, + "y": -4.032275787294617, + "z": 0.0 + }, + { + "x": 386.5289887266621, + "y": -4.1267618708191725, + "z": 0.0 + }, + { + "x": 387.12013661858225, + "y": -4.286806892950213, + "z": 0.0 + }, + { + "x": 387.55998559978843, + "y": -4.4318264865989745, + "z": 0.0 + }, + { + "x": 388.0557284532175, + "y": -4.709051441349425, + "z": 0.0 + }, + { + "x": 388.5501652261215, + "y": -5.082069965154249, + "z": 0.0 + }, + { + "x": 389.0062153063385, + "y": -5.5125901533047585, + "z": 0.0 + }, + { + "x": 389.3549299813356, + "y": -5.892455574449731, + "z": 0.0 + }, + { + "x": 389.66851789550583, + "y": -6.359658581921938, + "z": 0.0 + }, + { + "x": 389.9439489261116, + "y": -6.932775680958885, + "z": 0.0 + }, + { + "x": 390.1578642512467, + "y": -7.530318282566346, + "z": 0.0 + }, + { + "x": 390.2998091030685, + "y": -8.047909854519496, + "z": 0.0 + }, + { + "x": 390.36831676025355, + "y": -8.841612194702273, + "z": 0.0 + }, + { + "x": 390.38026247771074, + "y": -9.89500780322875, + "z": 0.0 + } + ] + }, + { + "id": 57, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 384.5899963378906, + "y": -0.019999999552965164, + "z": 0.0 + }, + { + "x": 385.64381494819463, + "y": -0.022279512756463074, + "z": 0.0 + }, + { + "x": 386.6942675033615, + "y": -0.10731382102577092, + "z": 0.0 + }, + { + "x": 387.72561929278487, + "y": -0.3099470615546849, + "z": 0.0 + }, + { + "x": 388.72816488321354, + "y": -0.624260633836627, + "z": 0.0 + }, + { + "x": 389.69228546420817, + "y": -1.0475544395856662, + "z": 0.0 + }, + { + "x": 390.5839469861444, + "y": -1.6093561257151243, + "z": 0.0 + }, + { + "x": 391.39871183908343, + "y": -2.2739064738697126, + "z": 0.0 + }, + { + "x": 392.13633031721895, + "y": -3.022132360315485, + "z": 0.0 + }, + { + "x": 392.79166110922614, + "y": -3.845780428123838, + "z": 0.0 + }, + { + "x": 393.33090306452124, + "y": -4.7512634582735265, + "z": 0.0 + }, + { + "x": 393.7538734457765, + "y": -5.714385286029489, + "z": 0.0 + }, + { + "x": 394.0739381603349, + "y": -6.715232312524801, + "z": 0.0 + }, + { + "x": 394.2883524678242, + "y": -7.745384107741452, + "z": 0.0 + }, + { + "x": 394.36805958476094, + "y": -8.796254308931472, + "z": 0.0 + }, + { + "x": 394.38000530221814, + "y": -9.849649917457949, + "z": 0.0 + } + ] + }, + { + "id": 58, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 396.37987671447183, + "y": -9.826970974572548, + "z": 0.0 + }, + { + "x": 396.36793099701464, + "y": -8.773575366046071, + "z": 0.0 + }, + { + "x": 396.2826241502021, + "y": -7.59412123435243, + "z": 0.0 + }, + { + "x": 396.03197511487895, + "y": -6.307689327504029, + "z": 0.0 + }, + { + "x": 395.6588357056089, + "y": -5.105190088564791, + "z": 0.0 + }, + { + "x": 395.16209564902897, + "y": -3.9470658964493204, + "z": 0.0 + }, + { + "x": 394.5100266731714, + "y": -2.8224428549608915, + "z": 0.0 + }, + { + "x": 393.70138782265917, + "y": -1.776903463820848, + "z": 0.0 + }, + { + "x": 392.8229851455644, + "y": -0.8698247282274443, + "z": 0.0 + }, + { + "x": 391.8480562526078, + "y": -0.059508467897973816, + "z": 0.0 + }, + { + "x": 390.758435396418, + "y": 0.6445815839209882, + "z": 0.0 + }, + { + "x": 389.5321790155292, + "y": 1.207012495720166, + "z": 0.0 + }, + { + "x": 388.32393457584624, + "y": 1.5984603430775586, + "z": 0.0 + }, + { + "x": 387.0798429056316, + "y": 1.855167162108652, + "z": 0.0 + }, + { + "x": 385.80518741153026, + "y": 1.9711995887334375, + "z": 0.0 + }, + { + "x": 384.5943225242918, + "y": 1.9799953214693673, + "z": 0.0 + } + ] + }, + { + "id": 59, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 384.58567015148947, + "y": -2.0199953205752976, + "z": 0.0 + }, + { + "x": 385.482442484859, + "y": -2.015758614246364, + "z": 0.0 + }, + { + "x": 386.3086921010914, + "y": -2.0697948041601943, + "z": 0.0 + }, + { + "x": 387.1273040097235, + "y": -2.2183544661869288, + "z": 0.0 + }, + { + "x": 387.92415075089787, + "y": -2.45553376339342, + "z": 0.0 + }, + { + "x": 388.62613553199833, + "y": -2.7396904630923204, + "z": 0.0 + }, + { + "x": 389.31983771968095, + "y": -3.159203783532275, + "z": 0.0 + }, + { + "x": 389.97443853260245, + "y": -3.6779882195119806, + "z": 0.0 + }, + { + "x": 390.5712728117787, + "y": -4.267361256810122, + "z": 0.0 + }, + { + "x": 391.07329554528087, + "y": -4.869118001286784, + "z": 0.0 + }, + { + "x": 391.4997104800135, + "y": -5.555461020097733, + "z": 0.0 + }, + { + "x": 391.8489111859441, + "y": -6.323580483494187, + "z": 0.0 + }, + { + "x": 392.1159012057908, + "y": -7.122775297545573, + "z": 0.0 + }, + { + "x": 392.29408078544634, + "y": -7.896646981130473, + "z": 0.0 + }, + { + "x": 392.36818817250725, + "y": -8.818933251816873, + "z": 0.0 + }, + { + "x": 392.38013388996444, + "y": -9.87232886034335, + "z": 0.0 + } + ] + }, + { + "id": 60, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.6703254820158, + "y": -193.1591116276879, + "z": 0.0 + }, + { + "x": 324.66472010416993, + "y": -193.15902991273836, + "z": 0.0 + }, + { + "x": 323.6591147263242, + "y": -193.1589481977888, + "z": 0.0 + }, + { + "x": 322.65350934847834, + "y": -193.15886648283922, + "z": 0.0 + }, + { + "x": 321.64790397063246, + "y": -193.15878476788964, + "z": 0.0 + }, + { + "x": 320.6422985927866, + "y": -193.15870305294007, + "z": 0.0 + }, + { + "x": 319.6366932149407, + "y": -193.1586213379905, + "z": 0.0 + }, + { + "x": 318.63108783709475, + "y": -193.15853962304095, + "z": 0.0 + }, + { + "x": 317.62548245924904, + "y": -193.15845790809138, + "z": 0.0 + }, + { + "x": 316.61987708140316, + "y": -193.1583761931418, + "z": 0.0 + }, + { + "x": 315.6142717035573, + "y": -193.15829447819223, + "z": 0.0 + }, + { + "x": 314.6086663257114, + "y": -193.15821276324266, + "z": 0.0 + }, + { + "x": 313.6030609478655, + "y": -193.1581310482931, + "z": 0.0 + }, + { + "x": 312.5974555700196, + "y": -193.15804933334354, + "z": 0.0 + }, + { + "x": 311.5918501921738, + "y": -193.15796761839397, + "z": 0.0 + }, + { + "x": 310.586244814328, + "y": -193.1578859034444, + "z": 0.0 + }, + { + "x": 309.5806394364821, + "y": -193.15780418849482, + "z": 0.0 + }, + { + "x": 308.5750340586362, + "y": -193.15772247354525, + "z": 0.0 + }, + { + "x": 307.5694286807902, + "y": -193.1576407585957, + "z": 0.0 + }, + { + "x": 306.5638233029445, + "y": -193.15755904364613, + "z": 0.0 + }, + { + "x": 305.55821792509863, + "y": -193.15747732869656, + "z": 0.0 + }, + { + "x": 304.5526125472528, + "y": -193.15739561374698, + "z": 0.0 + }, + { + "x": 303.5470071694069, + "y": -193.1573138987974, + "z": 0.0 + }, + { + "x": 302.54140179156104, + "y": -193.15723218384784, + "z": 0.0 + }, + { + "x": 301.53579641371505, + "y": -193.1571504688983, + "z": 0.0 + }, + { + "x": 300.53019103586934, + "y": -193.15706875394872, + "z": 0.0 + }, + { + "x": 299.52458565802345, + "y": -193.15698703899915, + "z": 0.0 + }, + { + "x": 298.51898028017763, + "y": -193.15690532404957, + "z": 0.0 + }, + { + "x": 297.51337490233175, + "y": -193.1568236091, + "z": 0.0 + }, + { + "x": 296.50776952448575, + "y": -193.15674189415046, + "z": 0.0 + }, + { + "x": 295.50216414664, + "y": -193.15666017920088, + "z": 0.0 + }, + { + "x": 294.49655876879416, + "y": -193.1565784642513, + "z": 0.0 + }, + { + "x": 293.4909533909483, + "y": -193.15649674930174, + "z": 0.0 + }, + { + "x": 292.48534801310245, + "y": -193.15641503435216, + "z": 0.0 + }, + { + "x": 291.47974263525657, + "y": -193.1563333194026, + "z": 0.0 + }, + { + "x": 290.4741372574106, + "y": -193.15625160445305, + "z": 0.0 + }, + { + "x": 289.4685318795648, + "y": -193.15616988950347, + "z": 0.0 + }, + { + "x": 288.4629265017189, + "y": -193.1560881745539, + "z": 0.0 + }, + { + "x": 287.4573211238731, + "y": -193.15600645960433, + "z": 0.0 + }, + { + "x": 286.4517157460273, + "y": -193.15592474465475, + "z": 0.0 + }, + { + "x": 285.4461103681814, + "y": -193.15584302970518, + "z": 0.0 + }, + { + "x": 284.4405049903354, + "y": -193.15576131475564, + "z": 0.0 + }, + { + "x": 283.43489961248963, + "y": -193.15567959980606, + "z": 0.0 + }, + { + "x": 282.42929423464375, + "y": -193.1555978848565, + "z": 0.0 + }, + { + "x": 281.4236888567979, + "y": -193.15551616990692, + "z": 0.0 + }, + { + "x": 280.4180834789521, + "y": -193.15543445495734, + "z": 0.0 + }, + { + "x": 279.4124781011061, + "y": -193.1553527400078, + "z": 0.0 + }, + { + "x": 278.40687272326034, + "y": -193.15527102505823, + "z": 0.0 + }, + { + "x": 277.40126734541445, + "y": -193.15518931010865, + "z": 0.0 + }, + { + "x": 276.39566196756857, + "y": -193.15510759515908, + "z": 0.0 + }, + { + "x": 275.39005658972275, + "y": -193.1550258802095, + "z": 0.0 + }, + { + "x": 274.38445121187686, + "y": -193.15494416525993, + "z": 0.0 + }, + { + "x": 273.3788458340309, + "y": -193.1548624503104, + "z": 0.0 + }, + { + "x": 272.37324045618516, + "y": -193.15478073536082, + "z": 0.0 + }, + { + "x": 271.3676350783393, + "y": -193.15469902041124, + "z": 0.0 + }, + { + "x": 270.3620297004934, + "y": -193.15461730546167, + "z": 0.0 + }, + { + "x": 269.35642432264757, + "y": -193.1545355905121, + "z": 0.0 + }, + { + "x": 268.3508189448017, + "y": -193.15445387556252, + "z": 0.0 + }, + { + "x": 267.34521356695575, + "y": -193.15437216061298, + "z": 0.0 + }, + { + "x": 266.33960818911, + "y": -193.1542904456634, + "z": 0.0 + }, + { + "x": 265.3340028112641, + "y": -193.15420873071383, + "z": 0.0 + }, + { + "x": 264.3283974334182, + "y": -193.15412701576426, + "z": 0.0 + }, + { + "x": 263.3227920555724, + "y": -193.1540453008147, + "z": 0.0 + }, + { + "x": 262.3171866777264, + "y": -193.15396358586514, + "z": 0.0 + }, + { + "x": 261.3115812998807, + "y": -193.15388187091557, + "z": 0.0 + }, + { + "x": 260.3059759220348, + "y": -193.153800155966, + "z": 0.0 + }, + { + "x": 259.3003705441889, + "y": -193.15371844101642, + "z": 0.0 + }, + { + "x": 258.29476516634304, + "y": -193.15363672606685, + "z": 0.0 + }, + { + "x": 257.28915978849716, + "y": -193.15355501111728, + "z": 0.0 + }, + { + "x": 256.2835544106512, + "y": -193.15347329616773, + "z": 0.0 + }, + { + "x": 255.27794903280545, + "y": -193.15339158121816, + "z": 0.0 + }, + { + "x": 254.27234365495957, + "y": -193.1533098662686, + "z": 0.0 + }, + { + "x": 253.26673827711372, + "y": -193.153228151319, + "z": 0.0 + }, + { + "x": 252.26113289926786, + "y": -193.15314643636944, + "z": 0.0 + }, + { + "x": 251.25552752142187, + "y": -193.1530647214199, + "z": 0.0 + }, + { + "x": 250.24992214357613, + "y": -193.15298300647032, + "z": 0.0 + }, + { + "x": 249.24431676573028, + "y": -193.15290129152075, + "z": 0.0 + }, + { + "x": 248.2387113878844, + "y": -193.15281957657118, + "z": 0.0 + }, + { + "x": 247.23310601003854, + "y": -193.1527378616216, + "z": 0.0 + }, + { + "x": 246.2275006321927, + "y": -193.15265614667203, + "z": 0.0 + }, + { + "x": 245.2218952543467, + "y": -193.15257443172248, + "z": 0.0 + }, + { + "x": 244.21628987650095, + "y": -193.1524927167729, + "z": 0.0 + }, + { + "x": 243.2106844986551, + "y": -193.15241100182334, + "z": 0.0 + }, + { + "x": 242.20507912080922, + "y": -193.15232928687377, + "z": 0.0 + }, + { + "x": 241.19947374296336, + "y": -193.1522475719242, + "z": 0.0 + }, + { + "x": 240.1938683651175, + "y": -193.15216585697462, + "z": 0.0 + }, + { + "x": 239.18826298727151, + "y": -193.15208414202507, + "z": 0.0 + }, + { + "x": 238.18265760942577, + "y": -193.1520024270755, + "z": 0.0 + }, + { + "x": 237.1770522315799, + "y": -193.15192071212593, + "z": 0.0 + }, + { + "x": 236.17144685373404, + "y": -193.15183899717636, + "z": 0.0 + }, + { + "x": 235.1658414758882, + "y": -193.15175728222678, + "z": 0.0 + }, + { + "x": 234.1602360980422, + "y": -193.15167556727724, + "z": 0.0 + }, + { + "x": 233.15463072019645, + "y": -193.15159385232766, + "z": 0.0 + }, + { + "x": 232.1490253423506, + "y": -193.1515121373781, + "z": 0.0 + }, + { + "x": 231.14341996450472, + "y": -193.15143042242852, + "z": 0.0 + }, + { + "x": 230.13781458665886, + "y": -193.15134870747895, + "z": 0.0 + }, + { + "x": 229.132209208813, + "y": -193.15126699252937, + "z": 0.0 + }, + { + "x": 228.126603830967, + "y": -193.15118527757983, + "z": 0.0 + }, + { + "x": 227.12099845312127, + "y": -193.15110356263025, + "z": 0.0 + }, + { + "x": 226.11539307527542, + "y": -193.15102184768068, + "z": 0.0 + }, + { + "x": 225.10978769742954, + "y": -193.1509401327311, + "z": 0.0 + }, + { + "x": 224.10418231958369, + "y": -193.15085841778154, + "z": 0.0 + }, + { + "x": 223.09857694173783, + "y": -193.15077670283196, + "z": 0.0 + }, + { + "x": 222.09297156389184, + "y": -193.15069498788242, + "z": 0.0 + }, + { + "x": 221.0873661860461, + "y": -193.15061327293284, + "z": 0.0 + }, + { + "x": 220.08176080820024, + "y": -193.15053155798327, + "z": 0.0 + }, + { + "x": 219.07615543035436, + "y": -193.1504498430337, + "z": 0.0 + }, + { + "x": 218.0705500525085, + "y": -193.15036812808412, + "z": 0.0 + }, + { + "x": 217.06494467466254, + "y": -193.15028641313458, + "z": 0.0 + }, + { + "x": 216.05933929681677, + "y": -193.150204698185, + "z": 0.0 + }, + { + "x": 215.0537339189709, + "y": -193.15012298323543, + "z": 0.0 + }, + { + "x": 214.04812854112507, + "y": -193.15004126828586, + "z": 0.0 + }, + { + "x": 213.04252316327918, + "y": -193.1499595533363, + "z": 0.0 + }, + { + "x": 212.0369177854333, + "y": -193.14987783838671, + "z": 0.0 + }, + { + "x": 211.03131240758736, + "y": -193.14979612343717, + "z": 0.0 + }, + { + "x": 210.0257070297416, + "y": -193.1497144084876, + "z": 0.0 + }, + { + "x": 209.02010165189571, + "y": -193.14963269353802, + "z": 0.0 + }, + { + "x": 208.0144962740499, + "y": -193.14955097858845, + "z": 0.0 + }, + { + "x": 207.008890896204, + "y": -193.14946926363888, + "z": 0.0 + }, + { + "x": 206.003285518358, + "y": -193.14938754868933, + "z": 0.0 + }, + { + "x": 204.99768014051227, + "y": -193.14930583373976, + "z": 0.0 + }, + { + "x": 203.99207476266642, + "y": -193.1492241187902, + "z": 0.0 + }, + { + "x": 202.98646938482054, + "y": -193.1491424038406, + "z": 0.0 + }, + { + "x": 201.98086400697468, + "y": -193.14906068889104, + "z": 0.0 + }, + { + "x": 200.97525862912883, + "y": -193.14897897394147, + "z": 0.0 + }, + { + "x": 199.96965325128284, + "y": -193.14889725899192, + "z": 0.0 + }, + { + "x": 198.9640478734371, + "y": -193.14881554404235, + "z": 0.0 + }, + { + "x": 197.95844249559124, + "y": -193.14873382909278, + "z": 0.0 + }, + { + "x": 196.95283711774536, + "y": -193.1486521141432, + "z": 0.0 + }, + { + "x": 195.9472317398995, + "y": -193.14857039919363, + "z": 0.0 + }, + { + "x": 194.94162636205365, + "y": -193.14848868424406, + "z": 0.0 + }, + { + "x": 193.93602098420766, + "y": -193.1484069692945, + "z": 0.0 + }, + { + "x": 192.93041560636192, + "y": -193.14832525434494, + "z": 0.0 + }, + { + "x": 191.92481022851607, + "y": -193.14824353939537, + "z": 0.0 + }, + { + "x": 190.91920485067018, + "y": -193.1481618244458, + "z": 0.0 + }, + { + "x": 189.91359947282433, + "y": -193.14808010949622, + "z": 0.0 + }, + { + "x": 188.90799409497836, + "y": -193.14799839454668, + "z": 0.0 + }, + { + "x": 187.9023887171326, + "y": -193.1479166795971, + "z": 0.0 + }, + { + "x": 186.89678333928674, + "y": -193.14783496464753, + "z": 0.0 + }, + { + "x": 185.8911779614409, + "y": -193.14775324969796, + "z": 0.0 + }, + { + "x": 184.885572583595, + "y": -193.14767153474838, + "z": 0.0 + }, + { + "x": 183.87996720574915, + "y": -193.1475898197988, + "z": 0.0 + }, + { + "x": 182.8743618279032, + "y": -193.14750810484927, + "z": 0.0 + }, + { + "x": 181.86875645005742, + "y": -193.1474263898997, + "z": 0.0 + }, + { + "x": 180.86315107221156, + "y": -193.14734467495012, + "z": 0.0 + }, + { + "x": 179.8575456943657, + "y": -193.14726296000055, + "z": 0.0 + }, + { + "x": 178.85194031651983, + "y": -193.14718124505097, + "z": 0.0 + }, + { + "x": 177.84633493867395, + "y": -193.1470995301014, + "z": 0.0 + }, + { + "x": 176.840729560828, + "y": -193.14701781515186, + "z": 0.0 + }, + { + "x": 175.83512418298224, + "y": -193.14693610020228, + "z": 0.0 + }, + { + "x": 174.82951880513636, + "y": -193.1468543852527, + "z": 0.0 + }, + { + "x": 173.82391342729053, + "y": -193.14677267030314, + "z": 0.0 + }, + { + "x": 172.81830804944465, + "y": -193.14669095535356, + "z": 0.0 + }, + { + "x": 171.81270267159866, + "y": -193.14660924040402, + "z": 0.0 + }, + { + "x": 170.80709729375295, + "y": -193.14652752545445, + "z": 0.0 + }, + { + "x": 169.80149191590706, + "y": -193.14644581050487, + "z": 0.0 + }, + { + "x": 168.79588653806118, + "y": -193.1463640955553, + "z": 0.0 + }, + { + "x": 167.79028116021533, + "y": -193.14628238060573, + "z": 0.0 + }, + { + "x": 166.78467578236948, + "y": -193.14620066565615, + "z": 0.0 + }, + { + "x": 165.77907040452348, + "y": -193.1461189507066, + "z": 0.0 + }, + { + "x": 164.77346502667777, + "y": -193.14603723575703, + "z": 0.0 + }, + { + "x": 163.7678596488319, + "y": -193.14595552080746, + "z": 0.0 + }, + { + "x": 162.76225427098603, + "y": -193.1458738058579, + "z": 0.0 + }, + { + "x": 161.75664889314015, + "y": -193.14579209090832, + "z": 0.0 + }, + { + "x": 160.75104351529419, + "y": -193.14571037595877, + "z": 0.0 + }, + { + "x": 159.74543813744845, + "y": -193.1456286610092, + "z": 0.0 + }, + { + "x": 158.73983275960256, + "y": -193.14554694605962, + "z": 0.0 + }, + { + "x": 157.7342273817567, + "y": -193.14546523111005, + "z": 0.0 + }, + { + "x": 156.72862200391083, + "y": -193.14538351616048, + "z": 0.0 + }, + { + "x": 155.72301662606498, + "y": -193.1453018012109, + "z": 0.0 + }, + { + "x": 154.717411248219, + "y": -193.14522008626136, + "z": 0.0 + }, + { + "x": 153.71180587037324, + "y": -193.1451383713118, + "z": 0.0 + }, + { + "x": 152.7062004925274, + "y": -193.14505665636221, + "z": 0.0 + }, + { + "x": 151.70059511468153, + "y": -193.14497494141264, + "z": 0.0 + }, + { + "x": 150.69498973683565, + "y": -193.14489322646307, + "z": 0.0 + }, + { + "x": 149.6893843589898, + "y": -193.1448115115135, + "z": 0.0 + }, + { + "x": 148.68377898114383, + "y": -193.14472979656395, + "z": 0.0 + }, + { + "x": 147.67817360329806, + "y": -193.14464808161438, + "z": 0.0 + }, + { + "x": 146.6725682254522, + "y": -193.1445663666648, + "z": 0.0 + }, + { + "x": 145.66696284760636, + "y": -193.14448465171523, + "z": 0.0 + }, + { + "x": 144.66135746976047, + "y": -193.14440293676566, + "z": 0.0 + }, + { + "x": 143.6557520919145, + "y": -193.1443212218161, + "z": 0.0 + }, + { + "x": 142.65014671406877, + "y": -193.14423950686654, + "z": 0.0 + }, + { + "x": 141.6445413362229, + "y": -193.14415779191697, + "z": 0.0 + }, + { + "x": 140.63893595837703, + "y": -193.1440760769674, + "z": 0.0 + }, + { + "x": 139.63333058053118, + "y": -193.14399436201782, + "z": 0.0 + }, + { + "x": 138.6277252026853, + "y": -193.14391264706825, + "z": 0.0 + }, + { + "x": 137.62211982483933, + "y": -193.1438309321187, + "z": 0.0 + }, + { + "x": 136.6165144469936, + "y": -193.14374921716913, + "z": 0.0 + }, + { + "x": 135.6109090691477, + "y": -193.14366750221956, + "z": 0.0 + }, + { + "x": 134.60530369130186, + "y": -193.14358578726998, + "z": 0.0 + }, + { + "x": 133.599698313456, + "y": -193.1435040723204, + "z": 0.0 + }, + { + "x": 132.59409293561012, + "y": -193.14342235737084, + "z": 0.0 + }, + { + "x": 131.58848755776415, + "y": -193.1433406424213, + "z": 0.0 + }, + { + "x": 130.5828821799184, + "y": -193.14325892747172, + "z": 0.0 + }, + { + "x": 129.57727680207256, + "y": -193.14317721252215, + "z": 0.0 + }, + { + "x": 128.57167142422668, + "y": -193.14309549757257, + "z": 0.0 + }, + { + "x": 127.56606604638083, + "y": -193.143013782623, + "z": 0.0 + }, + { + "x": 126.56046066853487, + "y": -193.14293206767346, + "z": 0.0 + }, + { + "x": 125.55485529068912, + "y": -193.14285035272388, + "z": 0.0 + }, + { + "x": 124.54924991284325, + "y": -193.1427686377743, + "z": 0.0 + }, + { + "x": 123.54364453499738, + "y": -193.14268692282474, + "z": 0.0 + }, + { + "x": 122.53803915715153, + "y": -193.14260520787516, + "z": 0.0 + }, + { + "x": 121.53243377930568, + "y": -193.1425234929256, + "z": 0.0 + }, + { + "x": 120.52682840145971, + "y": -193.14244177797605, + "z": 0.0 + }, + { + "x": 119.52122302361396, + "y": -193.14236006302647, + "z": 0.0 + }, + { + "x": 118.51561764576809, + "y": -193.1422783480769, + "z": 0.0 + }, + { + "x": 117.51001226792224, + "y": -193.14219663312733, + "z": 0.0 + }, + { + "x": 116.50440689007637, + "y": -193.14211491817775, + "z": 0.0 + }, + { + "x": 115.49880151223041, + "y": -193.1420332032282, + "z": 0.0 + }, + { + "x": 114.49319613438465, + "y": -193.14195148827864, + "z": 0.0 + }, + { + "x": 113.4875907565388, + "y": -193.14186977332906, + "z": 0.0 + }, + { + "x": 112.48198537869294, + "y": -193.1417880583795, + "z": 0.0 + }, + { + "x": 111.47638000084707, + "y": -193.14170634342992, + "z": 0.0 + }, + { + "x": 110.47077462300122, + "y": -193.14162462848034, + "z": 0.0 + }, + { + "x": 109.46516924515525, + "y": -193.1415429135308, + "z": 0.0 + }, + { + "x": 108.4595638673095, + "y": -193.14146119858123, + "z": 0.0 + }, + { + "x": 107.45395848946363, + "y": -193.14137948363165, + "z": 0.0 + }, + { + "x": 106.44835311161778, + "y": -193.14129776868208, + "z": 0.0 + }, + { + "x": 105.44274773377191, + "y": -193.1412160537325, + "z": 0.0 + }, + { + "x": 104.43714235592606, + "y": -193.14113433878293, + "z": 0.0 + }, + { + "x": 103.43153697808009, + "y": -193.1410526238334, + "z": 0.0 + }, + { + "x": 102.42593160023434, + "y": -193.14097090888382, + "z": 0.0 + }, + { + "x": 101.42032622238847, + "y": -193.14088919393424, + "z": 0.0 + } + ] + }, + { + "id": 61, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.41967614671384, + "y": -201.14088916752183, + "z": 0.0 + }, + { + "x": 102.42528152455971, + "y": -201.1409708824714, + "z": 0.0 + }, + { + "x": 103.43088690240566, + "y": -201.14105259742098, + "z": 0.0 + }, + { + "x": 104.43649228025143, + "y": -201.14113431237053, + "z": 0.0 + }, + { + "x": 105.44209765809728, + "y": -201.1412160273201, + "z": 0.0 + }, + { + "x": 106.44770303594315, + "y": -201.14129774226967, + "z": 0.0 + }, + { + "x": 107.453308413789, + "y": -201.14137945721924, + "z": 0.0 + }, + { + "x": 108.45891379163487, + "y": -201.14146117216882, + "z": 0.0 + }, + { + "x": 109.46451916948082, + "y": -201.1415428871184, + "z": 0.0 + }, + { + "x": 110.47012454732659, + "y": -201.14162460206794, + "z": 0.0 + }, + { + "x": 111.47572992517244, + "y": -201.1417063170175, + "z": 0.0 + }, + { + "x": 112.48133530301831, + "y": -201.14178803196708, + "z": 0.0 + }, + { + "x": 113.48694068086417, + "y": -201.14186974691665, + "z": 0.0 + }, + { + "x": 114.49254605871002, + "y": -201.14195146186623, + "z": 0.0 + }, + { + "x": 115.49815143655599, + "y": -201.1420331768158, + "z": 0.0 + }, + { + "x": 116.50375681440174, + "y": -201.14211489176535, + "z": 0.0 + }, + { + "x": 117.50936219224761, + "y": -201.14219660671492, + "z": 0.0 + }, + { + "x": 118.51496757009346, + "y": -201.1422783216645, + "z": 0.0 + }, + { + "x": 119.52057294793933, + "y": -201.14236003661406, + "z": 0.0 + }, + { + "x": 120.52617832578528, + "y": -201.14244175156364, + "z": 0.0 + }, + { + "x": 121.53178370363105, + "y": -201.14252346651318, + "z": 0.0 + }, + { + "x": 122.5373890814769, + "y": -201.14260518146276, + "z": 0.0 + }, + { + "x": 123.54299445932276, + "y": -201.14268689641233, + "z": 0.0 + }, + { + "x": 124.54859983716862, + "y": -201.1427686113619, + "z": 0.0 + }, + { + "x": 125.55420521501449, + "y": -201.14285032631147, + "z": 0.0 + }, + { + "x": 126.55981059286044, + "y": -201.14293204126105, + "z": 0.0 + }, + { + "x": 127.5654159707062, + "y": -201.1430137562106, + "z": 0.0 + }, + { + "x": 128.57102134855205, + "y": -201.14309547116017, + "z": 0.0 + }, + { + "x": 129.57662672639793, + "y": -201.14317718610974, + "z": 0.0 + }, + { + "x": 130.5822321042438, + "y": -201.1432589010593, + "z": 0.0 + }, + { + "x": 131.58783748208975, + "y": -201.14334061600889, + "z": 0.0 + }, + { + "x": 132.5934428599355, + "y": -201.14342233095843, + "z": 0.0 + }, + { + "x": 133.59904823778137, + "y": -201.143504045908, + "z": 0.0 + }, + { + "x": 134.60465361562723, + "y": -201.14358576085758, + "z": 0.0 + }, + { + "x": 135.61025899347308, + "y": -201.14366747580715, + "z": 0.0 + }, + { + "x": 136.61586437131896, + "y": -201.14374919075672, + "z": 0.0 + }, + { + "x": 137.62146974916493, + "y": -201.1438309057063, + "z": 0.0 + }, + { + "x": 138.62707512701067, + "y": -201.14391262065584, + "z": 0.0 + }, + { + "x": 139.63268050485655, + "y": -201.1439943356054, + "z": 0.0 + }, + { + "x": 140.6382858827024, + "y": -201.144076050555, + "z": 0.0 + }, + { + "x": 141.64389126054826, + "y": -201.14415776550456, + "z": 0.0 + }, + { + "x": 142.64949663839414, + "y": -201.14423948045413, + "z": 0.0 + }, + { + "x": 143.6551020162401, + "y": -201.1443211954037, + "z": 0.0 + }, + { + "x": 144.66070739408585, + "y": -201.14440291035325, + "z": 0.0 + }, + { + "x": 145.66631277193173, + "y": -201.14448462530282, + "z": 0.0 + }, + { + "x": 146.67191814977758, + "y": -201.1445663402524, + "z": 0.0 + }, + { + "x": 147.67752352762344, + "y": -201.14464805520197, + "z": 0.0 + }, + { + "x": 148.68312890546943, + "y": -201.14472977015154, + "z": 0.0 + }, + { + "x": 149.68873428331517, + "y": -201.1448114851011, + "z": 0.0 + }, + { + "x": 150.69433966116102, + "y": -201.14489320005066, + "z": 0.0 + }, + { + "x": 151.6999450390069, + "y": -201.14497491500023, + "z": 0.0 + }, + { + "x": 152.70555041685276, + "y": -201.1450566299498, + "z": 0.0 + }, + { + "x": 153.7111557946986, + "y": -201.14513834489938, + "z": 0.0 + }, + { + "x": 154.7167611725446, + "y": -201.14522005984895, + "z": 0.0 + }, + { + "x": 155.72236655039035, + "y": -201.1453017747985, + "z": 0.0 + }, + { + "x": 156.7279719282362, + "y": -201.14538348974807, + "z": 0.0 + }, + { + "x": 157.73357730608208, + "y": -201.14546520469764, + "z": 0.0 + }, + { + "x": 158.73918268392794, + "y": -201.14554691964722, + "z": 0.0 + }, + { + "x": 159.74478806177382, + "y": -201.1456286345968, + "z": 0.0 + }, + { + "x": 160.75039343961978, + "y": -201.14571034954636, + "z": 0.0 + }, + { + "x": 161.75599881746552, + "y": -201.1457920644959, + "z": 0.0 + }, + { + "x": 162.7616041953114, + "y": -201.14587377944548, + "z": 0.0 + }, + { + "x": 163.76720957315726, + "y": -201.14595549439505, + "z": 0.0 + }, + { + "x": 164.77281495100314, + "y": -201.14603720934463, + "z": 0.0 + }, + { + "x": 165.77842032884908, + "y": -201.1461189242942, + "z": 0.0 + }, + { + "x": 166.78402570669485, + "y": -201.14620063924374, + "z": 0.0 + }, + { + "x": 167.7896310845407, + "y": -201.14628235419332, + "z": 0.0 + }, + { + "x": 168.79523646238655, + "y": -201.1463640691429, + "z": 0.0 + }, + { + "x": 169.80084184023244, + "y": -201.14644578409246, + "z": 0.0 + }, + { + "x": 170.80644721807832, + "y": -201.14652749904204, + "z": 0.0 + }, + { + "x": 171.81205259592426, + "y": -201.1466092139916, + "z": 0.0 + }, + { + "x": 172.81765797377003, + "y": -201.14669092894115, + "z": 0.0 + }, + { + "x": 173.8232633516159, + "y": -201.14677264389073, + "z": 0.0 + }, + { + "x": 174.82886872946173, + "y": -201.1468543588403, + "z": 0.0 + }, + { + "x": 175.8344741073076, + "y": -201.14693607378987, + "z": 0.0 + }, + { + "x": 176.8400794851536, + "y": -201.14701778873945, + "z": 0.0 + }, + { + "x": 177.84568486299932, + "y": -201.147099503689, + "z": 0.0 + }, + { + "x": 178.8512902408452, + "y": -201.14718121863856, + "z": 0.0 + }, + { + "x": 179.85689561869108, + "y": -201.14726293358814, + "z": 0.0 + }, + { + "x": 180.86250099653694, + "y": -201.1473446485377, + "z": 0.0 + }, + { + "x": 181.8681063743828, + "y": -201.14742636348728, + "z": 0.0 + }, + { + "x": 182.8737117522288, + "y": -201.14750807843686, + "z": 0.0 + }, + { + "x": 183.87931713007453, + "y": -201.1475897933864, + "z": 0.0 + }, + { + "x": 184.88492250792038, + "y": -201.14767150833597, + "z": 0.0 + }, + { + "x": 185.89052788576626, + "y": -201.14775322328555, + "z": 0.0 + }, + { + "x": 186.8961332636121, + "y": -201.14783493823512, + "z": 0.0 + }, + { + "x": 187.90173864145797, + "y": -201.1479166531847, + "z": 0.0 + }, + { + "x": 188.90734401930396, + "y": -201.14799836813427, + "z": 0.0 + }, + { + "x": 189.9129493971497, + "y": -201.1480800830838, + "z": 0.0 + }, + { + "x": 190.91855477499556, + "y": -201.14816179803339, + "z": 0.0 + }, + { + "x": 191.92416015284144, + "y": -201.14824351298296, + "z": 0.0 + }, + { + "x": 192.9297655306873, + "y": -201.14832522793253, + "z": 0.0 + }, + { + "x": 193.93537090853326, + "y": -201.1484069428821, + "z": 0.0 + }, + { + "x": 194.94097628637903, + "y": -201.14848865783165, + "z": 0.0 + }, + { + "x": 195.94658166422488, + "y": -201.14857037278122, + "z": 0.0 + }, + { + "x": 196.95218704207073, + "y": -201.1486520877308, + "z": 0.0 + }, + { + "x": 197.95779241991661, + "y": -201.14873380268037, + "z": 0.0 + }, + { + "x": 198.96339779776247, + "y": -201.14881551762994, + "z": 0.0 + }, + { + "x": 199.96900317560844, + "y": -201.14889723257951, + "z": 0.0 + }, + { + "x": 200.9746085534542, + "y": -201.14897894752906, + "z": 0.0 + }, + { + "x": 201.98021393130006, + "y": -201.14906066247863, + "z": 0.0 + }, + { + "x": 202.9858193091459, + "y": -201.1491423774282, + "z": 0.0 + }, + { + "x": 203.9914246869918, + "y": -201.14922409237778, + "z": 0.0 + }, + { + "x": 204.99703006483765, + "y": -201.14930580732735, + "z": 0.0 + }, + { + "x": 206.0026354426836, + "y": -201.14938752227692, + "z": 0.0 + }, + { + "x": 207.00824082052938, + "y": -201.14946923722647, + "z": 0.0 + }, + { + "x": 208.01384619837526, + "y": -201.14955095217604, + "z": 0.0 + }, + { + "x": 209.0194515762211, + "y": -201.14963266712562, + "z": 0.0 + }, + { + "x": 210.02505695406697, + "y": -201.1497143820752, + "z": 0.0 + }, + { + "x": 211.03066233191296, + "y": -201.14979609702476, + "z": 0.0 + }, + { + "x": 212.03626770975868, + "y": -201.1498778119743, + "z": 0.0 + }, + { + "x": 213.04187308760456, + "y": -201.14995952692388, + "z": 0.0 + }, + { + "x": 214.04747846545044, + "y": -201.15004124187345, + "z": 0.0 + }, + { + "x": 215.05308384329626, + "y": -201.15012295682303, + "z": 0.0 + }, + { + "x": 216.05868922114215, + "y": -201.1502046717726, + "z": 0.0 + }, + { + "x": 217.06429459898814, + "y": -201.15028638672217, + "z": 0.0 + }, + { + "x": 218.06989997683388, + "y": -201.15036810167172, + "z": 0.0 + }, + { + "x": 219.07550535467973, + "y": -201.1504498166213, + "z": 0.0 + }, + { + "x": 220.08111073252562, + "y": -201.15053153157086, + "z": 0.0 + }, + { + "x": 221.08671611037147, + "y": -201.15061324652044, + "z": 0.0 + }, + { + "x": 222.09232148821744, + "y": -201.15069496147, + "z": 0.0 + }, + { + "x": 223.0979268660632, + "y": -201.15077667641955, + "z": 0.0 + }, + { + "x": 224.10353224390906, + "y": -201.15085839136913, + "z": 0.0 + }, + { + "x": 225.1091376217549, + "y": -201.1509401063187, + "z": 0.0 + }, + { + "x": 226.1147429996008, + "y": -201.15102182126827, + "z": 0.0 + }, + { + "x": 227.12034837744665, + "y": -201.15110353621785, + "z": 0.0 + }, + { + "x": 228.1259537552926, + "y": -201.15118525116742, + "z": 0.0 + }, + { + "x": 229.13155913313838, + "y": -201.15126696611696, + "z": 0.0 + }, + { + "x": 230.13716451098423, + "y": -201.15134868106654, + "z": 0.0 + }, + { + "x": 231.1427698888301, + "y": -201.1514303960161, + "z": 0.0 + }, + { + "x": 232.14837526667597, + "y": -201.15151211096568, + "z": 0.0 + }, + { + "x": 233.15398064452182, + "y": -201.15159382591526, + "z": 0.0 + }, + { + "x": 234.1595860223678, + "y": -201.15167554086483, + "z": 0.0 + }, + { + "x": 235.16519140021356, + "y": -201.15175725581437, + "z": 0.0 + }, + { + "x": 236.1707967780594, + "y": -201.15183897076395, + "z": 0.0 + }, + { + "x": 237.17640215590527, + "y": -201.15192068571352, + "z": 0.0 + }, + { + "x": 238.18200753375115, + "y": -201.1520024006631, + "z": 0.0 + }, + { + "x": 239.1876129115971, + "y": -201.15208411561267, + "z": 0.0 + }, + { + "x": 240.19321828944288, + "y": -201.1521658305622, + "z": 0.0 + }, + { + "x": 241.19882366728874, + "y": -201.15224754551178, + "z": 0.0 + }, + { + "x": 242.2044290451346, + "y": -201.15232926046136, + "z": 0.0 + }, + { + "x": 243.21003442298047, + "y": -201.15241097541093, + "z": 0.0 + }, + { + "x": 244.21563980082632, + "y": -201.1524926903605, + "z": 0.0 + }, + { + "x": 245.2212451786723, + "y": -201.15257440531008, + "z": 0.0 + }, + { + "x": 246.22685055651806, + "y": -201.15265612025962, + "z": 0.0 + }, + { + "x": 247.2324559343639, + "y": -201.1527378352092, + "z": 0.0 + }, + { + "x": 248.23806131220977, + "y": -201.15281955015877, + "z": 0.0 + }, + { + "x": 249.24366669005565, + "y": -201.15290126510834, + "z": 0.0 + }, + { + "x": 250.2492720679015, + "y": -201.1529829800579, + "z": 0.0 + }, + { + "x": 251.25487744574747, + "y": -201.1530646950075, + "z": 0.0 + }, + { + "x": 252.26048282359324, + "y": -201.15314640995703, + "z": 0.0 + }, + { + "x": 253.2660882014391, + "y": -201.1532281249066, + "z": 0.0 + }, + { + "x": 254.27169357928494, + "y": -201.15330983985618, + "z": 0.0 + }, + { + "x": 255.27729895713082, + "y": -201.15339155480575, + "z": 0.0 + }, + { + "x": 256.28290433497676, + "y": -201.15347326975532, + "z": 0.0 + }, + { + "x": 257.2885097128225, + "y": -201.15355498470487, + "z": 0.0 + }, + { + "x": 258.29411509066836, + "y": -201.15363669965444, + "z": 0.0 + }, + { + "x": 259.29972046851424, + "y": -201.15371841460401, + "z": 0.0 + }, + { + "x": 260.3053258463601, + "y": -201.1538001295536, + "z": 0.0 + }, + { + "x": 261.310931224206, + "y": -201.15388184450316, + "z": 0.0 + }, + { + "x": 262.31653660205194, + "y": -201.15396355945273, + "z": 0.0 + }, + { + "x": 263.3221419798977, + "y": -201.15404527440228, + "z": 0.0 + }, + { + "x": 264.32774735774353, + "y": -201.15412698935185, + "z": 0.0 + }, + { + "x": 265.3333527355894, + "y": -201.15420870430142, + "z": 0.0 + }, + { + "x": 266.3389581134353, + "y": -201.154290419251, + "z": 0.0 + }, + { + "x": 267.3445634912813, + "y": -201.15437213420057, + "z": 0.0 + }, + { + "x": 268.350168869127, + "y": -201.15445384915012, + "z": 0.0 + }, + { + "x": 269.3557742469729, + "y": -201.1545355640997, + "z": 0.0 + }, + { + "x": 270.3613796248187, + "y": -201.15461727904926, + "z": 0.0 + }, + { + "x": 271.3669850026646, + "y": -201.15469899399884, + "z": 0.0 + }, + { + "x": 272.3725903805105, + "y": -201.1547807089484, + "z": 0.0 + }, + { + "x": 273.37819575835647, + "y": -201.15486242389798, + "z": 0.0 + }, + { + "x": 274.3838011362022, + "y": -201.15494413884753, + "z": 0.0 + }, + { + "x": 275.38940651404806, + "y": -201.1550258537971, + "z": 0.0 + }, + { + "x": 276.3950118918939, + "y": -201.15510756874667, + "z": 0.0 + }, + { + "x": 277.40061726973977, + "y": -201.15518928369625, + "z": 0.0 + }, + { + "x": 278.40622264758565, + "y": -201.15527099864582, + "z": 0.0 + }, + { + "x": 279.41182802543165, + "y": -201.1553527135954, + "z": 0.0 + }, + { + "x": 280.4174334032774, + "y": -201.15543442854494, + "z": 0.0 + }, + { + "x": 281.42303878112324, + "y": -201.1555161434945, + "z": 0.0 + }, + { + "x": 282.42864415896906, + "y": -201.15559785844408, + "z": 0.0 + }, + { + "x": 283.43424953681495, + "y": -201.15567957339366, + "z": 0.0 + }, + { + "x": 284.43985491466094, + "y": -201.15576128834323, + "z": 0.0 + }, + { + "x": 285.4454602925067, + "y": -201.15584300329277, + "z": 0.0 + }, + { + "x": 286.4510656703526, + "y": -201.15592471824235, + "z": 0.0 + }, + { + "x": 287.4566710481984, + "y": -201.15600643319192, + "z": 0.0 + }, + { + "x": 288.46227642604424, + "y": -201.1560881481415, + "z": 0.0 + }, + { + "x": 289.4678818038901, + "y": -201.15616986309107, + "z": 0.0 + }, + { + "x": 290.4734871817361, + "y": -201.15625157804064, + "z": 0.0 + }, + { + "x": 291.4790925595819, + "y": -201.15633329299018, + "z": 0.0 + }, + { + "x": 292.48469793742777, + "y": -201.15641500793976, + "z": 0.0 + }, + { + "x": 293.4903033152736, + "y": -201.15649672288933, + "z": 0.0 + }, + { + "x": 294.4959086931195, + "y": -201.1565784378389, + "z": 0.0 + }, + { + "x": 295.5015140709653, + "y": -201.15666015278848, + "z": 0.0 + }, + { + "x": 296.5071194488113, + "y": -201.15674186773805, + "z": 0.0 + }, + { + "x": 297.51272482665706, + "y": -201.1568235826876, + "z": 0.0 + }, + { + "x": 298.51833020450294, + "y": -201.15690529763717, + "z": 0.0 + }, + { + "x": 299.52393558234877, + "y": -201.15698701258674, + "z": 0.0 + }, + { + "x": 300.52954096019465, + "y": -201.1570687275363, + "z": 0.0 + }, + { + "x": 301.5351463380406, + "y": -201.1571504424859, + "z": 0.0 + }, + { + "x": 302.54075171588636, + "y": -201.15723215743543, + "z": 0.0 + }, + { + "x": 303.54635709373224, + "y": -201.157313872385, + "z": 0.0 + }, + { + "x": 304.5519624715781, + "y": -201.15739558733458, + "z": 0.0 + }, + { + "x": 305.55756784942395, + "y": -201.15747730228415, + "z": 0.0 + }, + { + "x": 306.56317322726983, + "y": -201.15755901723372, + "z": 0.0 + }, + { + "x": 307.56877860511577, + "y": -201.1576407321833, + "z": 0.0 + }, + { + "x": 308.57438398296154, + "y": -201.15772244713284, + "z": 0.0 + }, + { + "x": 309.5799893608074, + "y": -201.1578041620824, + "z": 0.0 + }, + { + "x": 310.5855947386533, + "y": -201.157885877032, + "z": 0.0 + }, + { + "x": 311.5912001164991, + "y": -201.15796759198156, + "z": 0.0 + }, + { + "x": 312.5968054943451, + "y": -201.15804930693113, + "z": 0.0 + }, + { + "x": 313.60241087219083, + "y": -201.15813102188068, + "z": 0.0 + }, + { + "x": 314.6080162500367, + "y": -201.15821273683025, + "z": 0.0 + }, + { + "x": 315.6136216278826, + "y": -201.15829445177982, + "z": 0.0 + }, + { + "x": 316.6192270057285, + "y": -201.1583761667294, + "z": 0.0 + }, + { + "x": 317.62483238357436, + "y": -201.15845788167897, + "z": 0.0 + }, + { + "x": 318.6304377614203, + "y": -201.15853959662854, + "z": 0.0 + }, + { + "x": 319.636043139266, + "y": -201.1586213115781, + "z": 0.0 + }, + { + "x": 320.6416485171119, + "y": -201.15870302652766, + "z": 0.0 + }, + { + "x": 321.64725389495777, + "y": -201.15878474147723, + "z": 0.0 + }, + { + "x": 322.65285927280365, + "y": -201.1588664564268, + "z": 0.0 + }, + { + "x": 323.65846465064953, + "y": -201.15894817137638, + "z": 0.0 + }, + { + "x": 324.6640700284955, + "y": -201.15902988632595, + "z": 0.0 + }, + { + "x": 325.66967540634136, + "y": -201.1591116012755, + "z": 0.0 + } + ] + }, + { + "id": 62, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 101.42000118455115, + "y": -197.14088918072804, + "z": 0.0 + }, + { + "x": 102.42560656239702, + "y": -197.1409708956776, + "z": 0.0 + }, + { + "x": 103.43121194024287, + "y": -197.14105261062718, + "z": 0.0 + }, + { + "x": 104.43681731808874, + "y": -197.14113432557673, + "z": 0.0 + }, + { + "x": 105.4424226959346, + "y": -197.1412160405263, + "z": 0.0 + }, + { + "x": 106.44802807378046, + "y": -197.14129775547588, + "z": 0.0 + }, + { + "x": 107.45363345162632, + "y": -197.14137947042545, + "z": 0.0 + }, + { + "x": 108.45923882947218, + "y": -197.14146118537502, + "z": 0.0 + }, + { + "x": 109.46484420731804, + "y": -197.1415429003246, + "z": 0.0 + }, + { + "x": 110.4704495851639, + "y": -197.14162461527414, + "z": 0.0 + }, + { + "x": 111.47605496300976, + "y": -197.1417063302237, + "z": 0.0 + }, + { + "x": 112.48166034085563, + "y": -197.14178804517329, + "z": 0.0 + }, + { + "x": 113.48726571870148, + "y": -197.14186976012286, + "z": 0.0 + }, + { + "x": 114.49287109654733, + "y": -197.14195147507243, + "z": 0.0 + }, + { + "x": 115.4984764743932, + "y": -197.142033190022, + "z": 0.0 + }, + { + "x": 116.50408185223905, + "y": -197.14211490497155, + "z": 0.0 + }, + { + "x": 117.50968723008492, + "y": -197.14219661992112, + "z": 0.0 + }, + { + "x": 118.51529260793077, + "y": -197.1422783348707, + "z": 0.0 + }, + { + "x": 119.52089798577664, + "y": -197.14236004982027, + "z": 0.0 + }, + { + "x": 120.5265033636225, + "y": -197.14244176476984, + "z": 0.0 + }, + { + "x": 121.53210874146836, + "y": -197.1425234797194, + "z": 0.0 + }, + { + "x": 122.53771411931422, + "y": -197.14260519466896, + "z": 0.0 + }, + { + "x": 123.54331949716007, + "y": -197.14268690961853, + "z": 0.0 + }, + { + "x": 124.54892487500594, + "y": -197.1427686245681, + "z": 0.0 + }, + { + "x": 125.5545302528518, + "y": -197.14285033951768, + "z": 0.0 + }, + { + "x": 126.56013563069766, + "y": -197.14293205446725, + "z": 0.0 + }, + { + "x": 127.56574100854351, + "y": -197.1430137694168, + "z": 0.0 + }, + { + "x": 128.57134638638937, + "y": -197.14309548436637, + "z": 0.0 + }, + { + "x": 129.57695176423525, + "y": -197.14317719931594, + "z": 0.0 + }, + { + "x": 130.5825571420811, + "y": -197.14325891426552, + "z": 0.0 + }, + { + "x": 131.58816251992695, + "y": -197.1433406292151, + "z": 0.0 + }, + { + "x": 132.5937678977728, + "y": -197.14342234416463, + "z": 0.0 + }, + { + "x": 133.5993732756187, + "y": -197.1435040591142, + "z": 0.0 + }, + { + "x": 134.60497865346454, + "y": -197.14358577406378, + "z": 0.0 + }, + { + "x": 135.6105840313104, + "y": -197.14366748901335, + "z": 0.0 + }, + { + "x": 136.61618940915628, + "y": -197.14374920396293, + "z": 0.0 + }, + { + "x": 137.62179478700213, + "y": -197.1438309189125, + "z": 0.0 + }, + { + "x": 138.62740016484798, + "y": -197.14391263386204, + "z": 0.0 + }, + { + "x": 139.63300554269387, + "y": -197.14399434881162, + "z": 0.0 + }, + { + "x": 140.63861092053972, + "y": -197.1440760637612, + "z": 0.0 + }, + { + "x": 141.64421629838557, + "y": -197.14415777871076, + "z": 0.0 + }, + { + "x": 142.64982167623145, + "y": -197.14423949366034, + "z": 0.0 + }, + { + "x": 143.6554270540773, + "y": -197.1443212086099, + "z": 0.0 + }, + { + "x": 144.66103243192316, + "y": -197.14440292355945, + "z": 0.0 + }, + { + "x": 145.66663780976904, + "y": -197.14448463850903, + "z": 0.0 + }, + { + "x": 146.6722431876149, + "y": -197.1445663534586, + "z": 0.0 + }, + { + "x": 147.67784856546075, + "y": -197.14464806840817, + "z": 0.0 + }, + { + "x": 148.68345394330663, + "y": -197.14472978335775, + "z": 0.0 + }, + { + "x": 149.68905932115248, + "y": -197.1448114983073, + "z": 0.0 + }, + { + "x": 150.69466469899834, + "y": -197.14489321325686, + "z": 0.0 + }, + { + "x": 151.70027007684422, + "y": -197.14497492820644, + "z": 0.0 + }, + { + "x": 152.70587545469007, + "y": -197.145056643156, + "z": 0.0 + }, + { + "x": 153.71148083253593, + "y": -197.14513835810558, + "z": 0.0 + }, + { + "x": 154.7170862103818, + "y": -197.14522007305516, + "z": 0.0 + }, + { + "x": 155.72269158822766, + "y": -197.1453017880047, + "z": 0.0 + }, + { + "x": 156.72829696607351, + "y": -197.14538350295427, + "z": 0.0 + }, + { + "x": 157.7339023439194, + "y": -197.14546521790385, + "z": 0.0 + }, + { + "x": 158.73950772176525, + "y": -197.14554693285342, + "z": 0.0 + }, + { + "x": 159.74511309961113, + "y": -197.145628647803, + "z": 0.0 + }, + { + "x": 160.75071847745699, + "y": -197.14571036275257, + "z": 0.0 + }, + { + "x": 161.75632385530284, + "y": -197.1457920777021, + "z": 0.0 + }, + { + "x": 162.76192923314872, + "y": -197.14587379265168, + "z": 0.0 + }, + { + "x": 163.76753461099457, + "y": -197.14595550760126, + "z": 0.0 + }, + { + "x": 164.77313998884046, + "y": -197.14603722255083, + "z": 0.0 + }, + { + "x": 165.77874536668628, + "y": -197.1461189375004, + "z": 0.0 + }, + { + "x": 166.78435074453216, + "y": -197.14620065244995, + "z": 0.0 + }, + { + "x": 167.78995612237802, + "y": -197.14628236739952, + "z": 0.0 + }, + { + "x": 168.79556150022387, + "y": -197.1463640823491, + "z": 0.0 + }, + { + "x": 169.80116687806975, + "y": -197.14644579729867, + "z": 0.0 + }, + { + "x": 170.80677225591563, + "y": -197.14652751224824, + "z": 0.0 + }, + { + "x": 171.81237763376146, + "y": -197.1466092271978, + "z": 0.0 + }, + { + "x": 172.81798301160734, + "y": -197.14669094214736, + "z": 0.0 + }, + { + "x": 173.82358838945322, + "y": -197.14677265709693, + "z": 0.0 + }, + { + "x": 174.82919376729905, + "y": -197.1468543720465, + "z": 0.0 + }, + { + "x": 175.83479914514493, + "y": -197.14693608699608, + "z": 0.0 + }, + { + "x": 176.8404045229908, + "y": -197.14701780194565, + "z": 0.0 + }, + { + "x": 177.84600990083663, + "y": -197.1470995168952, + "z": 0.0 + }, + { + "x": 178.85161527868252, + "y": -197.14718123184477, + "z": 0.0 + }, + { + "x": 179.8572206565284, + "y": -197.14726294679434, + "z": 0.0 + }, + { + "x": 180.86282603437425, + "y": -197.14734466174392, + "z": 0.0 + }, + { + "x": 181.8684314122201, + "y": -197.1474263766935, + "z": 0.0 + }, + { + "x": 182.874036790066, + "y": -197.14750809164306, + "z": 0.0 + }, + { + "x": 183.87964216791184, + "y": -197.1475898065926, + "z": 0.0 + }, + { + "x": 184.8852475457577, + "y": -197.14767152154218, + "z": 0.0 + }, + { + "x": 185.89085292360357, + "y": -197.14775323649175, + "z": 0.0 + }, + { + "x": 186.89645830144943, + "y": -197.14783495144133, + "z": 0.0 + }, + { + "x": 187.90206367929528, + "y": -197.1479166663909, + "z": 0.0 + }, + { + "x": 188.90766905714116, + "y": -197.14799838134047, + "z": 0.0 + }, + { + "x": 189.91327443498702, + "y": -197.14808009629002, + "z": 0.0 + }, + { + "x": 190.91887981283287, + "y": -197.1481618112396, + "z": 0.0 + }, + { + "x": 191.92448519067875, + "y": -197.14824352618916, + "z": 0.0 + }, + { + "x": 192.9300905685246, + "y": -197.14832524113874, + "z": 0.0 + }, + { + "x": 193.93569594637046, + "y": -197.1484069560883, + "z": 0.0 + }, + { + "x": 194.94130132421634, + "y": -197.14848867103785, + "z": 0.0 + }, + { + "x": 195.9469067020622, + "y": -197.14857038598743, + "z": 0.0 + }, + { + "x": 196.95251207990805, + "y": -197.148652100937, + "z": 0.0 + }, + { + "x": 197.95811745775393, + "y": -197.14873381588657, + "z": 0.0 + }, + { + "x": 198.96372283559978, + "y": -197.14881553083615, + "z": 0.0 + }, + { + "x": 199.96932821344564, + "y": -197.14889724578572, + "z": 0.0 + }, + { + "x": 200.97493359129152, + "y": -197.14897896073526, + "z": 0.0 + }, + { + "x": 201.98053896913737, + "y": -197.14906067568484, + "z": 0.0 + }, + { + "x": 202.98614434698322, + "y": -197.1491423906344, + "z": 0.0 + }, + { + "x": 203.9917497248291, + "y": -197.14922410558398, + "z": 0.0 + }, + { + "x": 204.99735510267496, + "y": -197.14930582053356, + "z": 0.0 + }, + { + "x": 206.0029604805208, + "y": -197.14938753548313, + "z": 0.0 + }, + { + "x": 207.0085658583667, + "y": -197.14946925043267, + "z": 0.0 + }, + { + "x": 208.01417123621258, + "y": -197.14955096538225, + "z": 0.0 + }, + { + "x": 209.0197766140584, + "y": -197.14963268033182, + "z": 0.0 + }, + { + "x": 210.02538199190428, + "y": -197.1497143952814, + "z": 0.0 + }, + { + "x": 211.03098736975016, + "y": -197.14979611023097, + "z": 0.0 + }, + { + "x": 212.036592747596, + "y": -197.1498778251805, + "z": 0.0 + }, + { + "x": 213.04219812544187, + "y": -197.14995954013008, + "z": 0.0 + }, + { + "x": 214.04780350328775, + "y": -197.15004125507966, + "z": 0.0 + }, + { + "x": 215.05340888113358, + "y": -197.15012297002923, + "z": 0.0 + }, + { + "x": 216.05901425897946, + "y": -197.1502046849788, + "z": 0.0 + }, + { + "x": 217.06461963682534, + "y": -197.15028639992838, + "z": 0.0 + }, + { + "x": 218.0702250146712, + "y": -197.15036811487792, + "z": 0.0 + }, + { + "x": 219.07583039251705, + "y": -197.1504498298275, + "z": 0.0 + }, + { + "x": 220.08143577036293, + "y": -197.15053154477707, + "z": 0.0 + }, + { + "x": 221.08704114820878, + "y": -197.15061325972664, + "z": 0.0 + }, + { + "x": 222.09264652605464, + "y": -197.1506949746762, + "z": 0.0 + }, + { + "x": 223.09825190390052, + "y": -197.15077668962576, + "z": 0.0 + }, + { + "x": 224.10385728174637, + "y": -197.15085840457533, + "z": 0.0 + }, + { + "x": 225.10946265959223, + "y": -197.1509401195249, + "z": 0.0 + }, + { + "x": 226.1150680374381, + "y": -197.15102183447448, + "z": 0.0 + }, + { + "x": 227.12067341528396, + "y": -197.15110354942405, + "z": 0.0 + }, + { + "x": 228.1262787931298, + "y": -197.15118526437362, + "z": 0.0 + }, + { + "x": 229.1318841709757, + "y": -197.15126697932317, + "z": 0.0 + }, + { + "x": 230.13748954882155, + "y": -197.15134869427274, + "z": 0.0 + }, + { + "x": 231.1430949266674, + "y": -197.1514304092223, + "z": 0.0 + }, + { + "x": 232.14870030451328, + "y": -197.1515121241719, + "z": 0.0 + }, + { + "x": 233.15430568235914, + "y": -197.15159383912146, + "z": 0.0 + }, + { + "x": 234.159911060205, + "y": -197.15167555407103, + "z": 0.0 + }, + { + "x": 235.16551643805087, + "y": -197.15175726902058, + "z": 0.0 + }, + { + "x": 236.17112181589673, + "y": -197.15183898397015, + "z": 0.0 + }, + { + "x": 237.17672719374258, + "y": -197.15192069891972, + "z": 0.0 + }, + { + "x": 238.18233257158846, + "y": -197.1520024138693, + "z": 0.0 + }, + { + "x": 239.1879379494343, + "y": -197.15208412881887, + "z": 0.0 + }, + { + "x": 240.1935433272802, + "y": -197.15216584376842, + "z": 0.0 + }, + { + "x": 241.19914870512605, + "y": -197.152247558718, + "z": 0.0 + }, + { + "x": 242.2047540829719, + "y": -197.15232927366756, + "z": 0.0 + }, + { + "x": 243.21035946081778, + "y": -197.15241098861713, + "z": 0.0 + }, + { + "x": 244.21596483866364, + "y": -197.1524927035667, + "z": 0.0 + }, + { + "x": 245.2215702165095, + "y": -197.15257441851628, + "z": 0.0 + }, + { + "x": 246.22717559435537, + "y": -197.15265613346583, + "z": 0.0 + }, + { + "x": 247.23278097220123, + "y": -197.1527378484154, + "z": 0.0 + }, + { + "x": 248.23838635004708, + "y": -197.15281956336497, + "z": 0.0 + }, + { + "x": 249.24399172789296, + "y": -197.15290127831454, + "z": 0.0 + }, + { + "x": 250.24959710573881, + "y": -197.15298299326412, + "z": 0.0 + }, + { + "x": 251.25520248358467, + "y": -197.1530647082137, + "z": 0.0 + }, + { + "x": 252.26080786143055, + "y": -197.15314642316324, + "z": 0.0 + }, + { + "x": 253.2664132392764, + "y": -197.1532281381128, + "z": 0.0 + }, + { + "x": 254.27201861712226, + "y": -197.15330985306238, + "z": 0.0 + }, + { + "x": 255.27762399496814, + "y": -197.15339156801195, + "z": 0.0 + }, + { + "x": 256.283229372814, + "y": -197.15347328296153, + "z": 0.0 + }, + { + "x": 257.2888347506598, + "y": -197.15355499791107, + "z": 0.0 + }, + { + "x": 258.2944401285057, + "y": -197.15363671286065, + "z": 0.0 + }, + { + "x": 259.3000455063516, + "y": -197.15371842781022, + "z": 0.0 + }, + { + "x": 260.30565088419746, + "y": -197.1538001427598, + "z": 0.0 + }, + { + "x": 261.31125626204334, + "y": -197.15388185770937, + "z": 0.0 + }, + { + "x": 262.31686163988917, + "y": -197.15396357265894, + "z": 0.0 + }, + { + "x": 263.32246701773505, + "y": -197.15404528760848, + "z": 0.0 + }, + { + "x": 264.3280723955809, + "y": -197.15412700255806, + "z": 0.0 + }, + { + "x": 265.33367777342676, + "y": -197.15420871750763, + "z": 0.0 + }, + { + "x": 266.33928315127264, + "y": -197.1542904324572, + "z": 0.0 + }, + { + "x": 267.3448885291185, + "y": -197.15437214740678, + "z": 0.0 + }, + { + "x": 268.35049390696435, + "y": -197.15445386235632, + "z": 0.0 + }, + { + "x": 269.3560992848102, + "y": -197.1545355773059, + "z": 0.0 + }, + { + "x": 270.36170466265605, + "y": -197.15461729225547, + "z": 0.0 + }, + { + "x": 271.36731004050193, + "y": -197.15469900720504, + "z": 0.0 + }, + { + "x": 272.3729154183478, + "y": -197.1547807221546, + "z": 0.0 + }, + { + "x": 273.3785207961937, + "y": -197.15486243710419, + "z": 0.0 + }, + { + "x": 274.3841261740395, + "y": -197.15494415205373, + "z": 0.0 + }, + { + "x": 275.3897315518854, + "y": -197.1550258670033, + "z": 0.0 + }, + { + "x": 276.39533692973123, + "y": -197.15510758195288, + "z": 0.0 + }, + { + "x": 277.4009423075771, + "y": -197.15518929690245, + "z": 0.0 + }, + { + "x": 278.406547685423, + "y": -197.15527101185202, + "z": 0.0 + }, + { + "x": 279.4121530632689, + "y": -197.1553527268016, + "z": 0.0 + }, + { + "x": 280.41775844111476, + "y": -197.15543444175114, + "z": 0.0 + }, + { + "x": 281.4233638189606, + "y": -197.1555161567007, + "z": 0.0 + }, + { + "x": 282.4289691968064, + "y": -197.1555978716503, + "z": 0.0 + }, + { + "x": 283.4345745746523, + "y": -197.15567958659986, + "z": 0.0 + }, + { + "x": 284.44017995249817, + "y": -197.15576130154943, + "z": 0.0 + }, + { + "x": 285.44578533034405, + "y": -197.15584301649898, + "z": 0.0 + }, + { + "x": 286.45139070818993, + "y": -197.15592473144855, + "z": 0.0 + }, + { + "x": 287.45699608603576, + "y": -197.15600644639812, + "z": 0.0 + }, + { + "x": 288.4626014638816, + "y": -197.1560881613477, + "z": 0.0 + }, + { + "x": 289.46820684172746, + "y": -197.15616987629727, + "z": 0.0 + }, + { + "x": 290.47381221957335, + "y": -197.15625159124684, + "z": 0.0 + }, + { + "x": 291.47941759741923, + "y": -197.1563333061964, + "z": 0.0 + }, + { + "x": 292.4850229752651, + "y": -197.15641502114596, + "z": 0.0 + }, + { + "x": 293.49062835311094, + "y": -197.15649673609553, + "z": 0.0 + }, + { + "x": 294.4962337309568, + "y": -197.1565784510451, + "z": 0.0 + }, + { + "x": 295.50183910880264, + "y": -197.15666016599468, + "z": 0.0 + }, + { + "x": 296.5074444866485, + "y": -197.15674188094425, + "z": 0.0 + }, + { + "x": 297.5130498644944, + "y": -197.1568235958938, + "z": 0.0 + }, + { + "x": 298.5186552423403, + "y": -197.15690531084337, + "z": 0.0 + }, + { + "x": 299.5242606201861, + "y": -197.15698702579294, + "z": 0.0 + }, + { + "x": 300.529865998032, + "y": -197.15706874074252, + "z": 0.0 + }, + { + "x": 301.5354713758778, + "y": -197.1571504556921, + "z": 0.0 + }, + { + "x": 302.5410767537237, + "y": -197.15723217064163, + "z": 0.0 + }, + { + "x": 303.5466821315696, + "y": -197.1573138855912, + "z": 0.0 + }, + { + "x": 304.55228750941546, + "y": -197.15739560054078, + "z": 0.0 + }, + { + "x": 305.5578928872613, + "y": -197.15747731549035, + "z": 0.0 + }, + { + "x": 306.56349826510717, + "y": -197.15755903043993, + "z": 0.0 + }, + { + "x": 307.569103642953, + "y": -197.1576407453895, + "z": 0.0 + }, + { + "x": 308.5747090207989, + "y": -197.15772246033904, + "z": 0.0 + }, + { + "x": 309.58031439864476, + "y": -197.15780417528862, + "z": 0.0 + }, + { + "x": 310.58591977649064, + "y": -197.1578858902382, + "z": 0.0 + }, + { + "x": 311.59152515433647, + "y": -197.15796760518776, + "z": 0.0 + }, + { + "x": 312.59713053218235, + "y": -197.15804932013734, + "z": 0.0 + }, + { + "x": 313.6027359100282, + "y": -197.15813103508688, + "z": 0.0 + }, + { + "x": 314.60834128787405, + "y": -197.15821275003645, + "z": 0.0 + }, + { + "x": 315.61394666571994, + "y": -197.15829446498603, + "z": 0.0 + }, + { + "x": 316.6195520435658, + "y": -197.1583761799356, + "z": 0.0 + }, + { + "x": 317.6251574214117, + "y": -197.15845789488517, + "z": 0.0 + }, + { + "x": 318.6307627992575, + "y": -197.15853960983475, + "z": 0.0 + }, + { + "x": 319.63636817710335, + "y": -197.1586213247843, + "z": 0.0 + }, + { + "x": 320.64197355494923, + "y": -197.15870303973387, + "z": 0.0 + }, + { + "x": 321.6475789327951, + "y": -197.15878475468344, + "z": 0.0 + }, + { + "x": 322.653184310641, + "y": -197.158866469633, + "z": 0.0 + }, + { + "x": 323.6587896884869, + "y": -197.15894818458258, + "z": 0.0 + }, + { + "x": 324.6643950663327, + "y": -197.15902989953216, + "z": 0.0 + }, + { + "x": 325.6700004441786, + "y": -197.1591116144817, + "z": 0.0 + } + ] + }, + { + "id": 63, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.6701629630972, + "y": -195.1591116210848, + "z": 0.0 + }, + { + "x": 324.6645575852513, + "y": -195.15902990613526, + "z": 0.0 + }, + { + "x": 323.65895220740555, + "y": -195.1589481911857, + "z": 0.0 + }, + { + "x": 322.65334682955967, + "y": -195.1588664762361, + "z": 0.0 + }, + { + "x": 321.6477414517138, + "y": -195.15878476128654, + "z": 0.0 + }, + { + "x": 320.6421360738679, + "y": -195.15870304633697, + "z": 0.0 + }, + { + "x": 319.636530696022, + "y": -195.1586213313874, + "z": 0.0 + }, + { + "x": 318.63092531817614, + "y": -195.15853961643785, + "z": 0.0 + }, + { + "x": 317.62531994033037, + "y": -195.15845790148828, + "z": 0.0 + }, + { + "x": 316.6197145624845, + "y": -195.1583761865387, + "z": 0.0 + }, + { + "x": 315.6141091846386, + "y": -195.15829447158913, + "z": 0.0 + }, + { + "x": 314.6085038067927, + "y": -195.15821275663956, + "z": 0.0 + }, + { + "x": 313.60289842894684, + "y": -195.15813104168998, + "z": 0.0 + }, + { + "x": 312.59729305110096, + "y": -195.15804932674044, + "z": 0.0 + }, + { + "x": 311.59168767325514, + "y": -195.15796761179087, + "z": 0.0 + }, + { + "x": 310.5860822954093, + "y": -195.1578858968413, + "z": 0.0 + }, + { + "x": 309.58047691756343, + "y": -195.15780418189172, + "z": 0.0 + }, + { + "x": 308.57487153971755, + "y": -195.15772246694215, + "z": 0.0 + }, + { + "x": 307.5692661618716, + "y": -195.1576407519926, + "z": 0.0 + }, + { + "x": 306.56366078402584, + "y": -195.15755903704303, + "z": 0.0 + }, + { + "x": 305.55805540617996, + "y": -195.15747732209346, + "z": 0.0 + }, + { + "x": 304.55245002833414, + "y": -195.15739560714388, + "z": 0.0 + }, + { + "x": 303.54684465048825, + "y": -195.1573138921943, + "z": 0.0 + }, + { + "x": 302.5412392726424, + "y": -195.15723217724474, + "z": 0.0 + }, + { + "x": 301.53563389479643, + "y": -195.1571504622952, + "z": 0.0 + }, + { + "x": 300.53002851695067, + "y": -195.15706874734562, + "z": 0.0 + }, + { + "x": 299.5244231391048, + "y": -195.15698703239605, + "z": 0.0 + }, + { + "x": 298.51881776125896, + "y": -195.15690531744647, + "z": 0.0 + }, + { + "x": 297.5132123834131, + "y": -195.1568236024969, + "z": 0.0 + }, + { + "x": 296.50760700556714, + "y": -195.15674188754735, + "z": 0.0 + }, + { + "x": 295.5020016277213, + "y": -195.15666017259778, + "z": 0.0 + }, + { + "x": 294.4963962498755, + "y": -195.1565784576482, + "z": 0.0 + }, + { + "x": 293.4907908720296, + "y": -195.15649674269864, + "z": 0.0 + }, + { + "x": 292.4851854941838, + "y": -195.15641502774906, + "z": 0.0 + }, + { + "x": 291.4795801163379, + "y": -195.1563333127995, + "z": 0.0 + }, + { + "x": 290.47397473849196, + "y": -195.15625159784994, + "z": 0.0 + }, + { + "x": 289.46836936064614, + "y": -195.15616988290037, + "z": 0.0 + }, + { + "x": 288.46276398280025, + "y": -195.1560881679508, + "z": 0.0 + }, + { + "x": 287.45715860495443, + "y": -195.15600645300123, + "z": 0.0 + }, + { + "x": 286.4515532271086, + "y": -195.15592473805165, + "z": 0.0 + }, + { + "x": 285.4459478492627, + "y": -195.15584302310208, + "z": 0.0 + }, + { + "x": 284.4403424714168, + "y": -195.15576130815253, + "z": 0.0 + }, + { + "x": 283.43473709357096, + "y": -195.15567959320296, + "z": 0.0 + }, + { + "x": 282.4291317157251, + "y": -195.1555978782534, + "z": 0.0 + }, + { + "x": 281.42352633787925, + "y": -195.15551616330382, + "z": 0.0 + }, + { + "x": 280.4179209600334, + "y": -195.15543444835424, + "z": 0.0 + }, + { + "x": 279.4123155821875, + "y": -195.1553527334047, + "z": 0.0 + }, + { + "x": 278.40671020434166, + "y": -195.15527101845512, + "z": 0.0 + }, + { + "x": 277.4011048264958, + "y": -195.15518930350555, + "z": 0.0 + }, + { + "x": 276.3954994486499, + "y": -195.15510758855598, + "z": 0.0 + }, + { + "x": 275.3898940708041, + "y": -195.1550258736064, + "z": 0.0 + }, + { + "x": 274.3842886929582, + "y": -195.15494415865683, + "z": 0.0 + }, + { + "x": 273.3786833151123, + "y": -195.1548624437073, + "z": 0.0 + }, + { + "x": 272.3730779372665, + "y": -195.15478072875771, + "z": 0.0 + }, + { + "x": 271.3674725594206, + "y": -195.15469901380814, + "z": 0.0 + }, + { + "x": 270.3618671815747, + "y": -195.15461729885857, + "z": 0.0 + }, + { + "x": 269.3562618037289, + "y": -195.154535583909, + "z": 0.0 + }, + { + "x": 268.350656425883, + "y": -195.15445386895942, + "z": 0.0 + }, + { + "x": 267.34505104803713, + "y": -195.15437215400988, + "z": 0.0 + }, + { + "x": 266.3394456701913, + "y": -195.1542904390603, + "z": 0.0 + }, + { + "x": 265.3338402923454, + "y": -195.15420872411073, + "z": 0.0 + }, + { + "x": 264.32823491449955, + "y": -195.15412700916116, + "z": 0.0 + }, + { + "x": 263.3226295366537, + "y": -195.15404529421158, + "z": 0.0 + }, + { + "x": 262.3170241588078, + "y": -195.15396357926204, + "z": 0.0 + }, + { + "x": 261.311418780962, + "y": -195.15388186431247, + "z": 0.0 + }, + { + "x": 260.30581340311613, + "y": -195.1538001493629, + "z": 0.0 + }, + { + "x": 259.30020802527025, + "y": -195.15371843441332, + "z": 0.0 + }, + { + "x": 258.29460264742437, + "y": -195.15363671946375, + "z": 0.0 + }, + { + "x": 257.2889972695785, + "y": -195.15355500451417, + "z": 0.0 + }, + { + "x": 256.2833918917326, + "y": -195.15347328956463, + "z": 0.0 + }, + { + "x": 255.27778651388678, + "y": -195.15339157461506, + "z": 0.0 + }, + { + "x": 254.2721811360409, + "y": -195.15330985966548, + "z": 0.0 + }, + { + "x": 253.26657575819507, + "y": -195.1532281447159, + "z": 0.0 + }, + { + "x": 252.2609703803492, + "y": -195.15314642976634, + "z": 0.0 + }, + { + "x": 251.25536500250325, + "y": -195.1530647148168, + "z": 0.0 + }, + { + "x": 250.2497596246575, + "y": -195.15298299986722, + "z": 0.0 + }, + { + "x": 249.2441542468116, + "y": -195.15290128491765, + "z": 0.0 + }, + { + "x": 248.23854886896572, + "y": -195.15281956996807, + "z": 0.0 + }, + { + "x": 247.2329434911199, + "y": -195.1527378550185, + "z": 0.0 + }, + { + "x": 246.22733811327402, + "y": -195.15265614006893, + "z": 0.0 + }, + { + "x": 245.22173273542808, + "y": -195.15257442511938, + "z": 0.0 + }, + { + "x": 244.2161273575823, + "y": -195.1524927101698, + "z": 0.0 + }, + { + "x": 243.21052197973643, + "y": -195.15241099522024, + "z": 0.0 + }, + { + "x": 242.20491660189055, + "y": -195.15232928027066, + "z": 0.0 + }, + { + "x": 241.19931122404472, + "y": -195.1522475653211, + "z": 0.0 + }, + { + "x": 240.19370584619884, + "y": -195.15216585037152, + "z": 0.0 + }, + { + "x": 239.1881004683529, + "y": -195.15208413542197, + "z": 0.0 + }, + { + "x": 238.18249509050713, + "y": -195.1520024204724, + "z": 0.0 + }, + { + "x": 237.17688971266125, + "y": -195.15192070552283, + "z": 0.0 + }, + { + "x": 236.17128433481537, + "y": -195.15183899057325, + "z": 0.0 + }, + { + "x": 235.16567895696954, + "y": -195.15175727562368, + "z": 0.0 + }, + { + "x": 234.1600735791236, + "y": -195.15167556067414, + "z": 0.0 + }, + { + "x": 233.15446820127778, + "y": -195.15159384572456, + "z": 0.0 + }, + { + "x": 232.14886282343195, + "y": -195.151512130775, + "z": 0.0 + }, + { + "x": 231.14325744558607, + "y": -195.15143041582542, + "z": 0.0 + }, + { + "x": 230.1376520677402, + "y": -195.15134870087584, + "z": 0.0 + }, + { + "x": 229.13204668989437, + "y": -195.15126698592627, + "z": 0.0 + }, + { + "x": 228.12644131204843, + "y": -195.15118527097673, + "z": 0.0 + }, + { + "x": 227.1208359342026, + "y": -195.15110355602715, + "z": 0.0 + }, + { + "x": 226.11523055635678, + "y": -195.15102184107758, + "z": 0.0 + }, + { + "x": 225.1096251785109, + "y": -195.150940126128, + "z": 0.0 + }, + { + "x": 224.10401980066501, + "y": -195.15085841117843, + "z": 0.0 + }, + { + "x": 223.0984144228192, + "y": -195.15077669622886, + "z": 0.0 + }, + { + "x": 222.09280904497325, + "y": -195.15069498127932, + "z": 0.0 + }, + { + "x": 221.08720366712743, + "y": -195.15061326632974, + "z": 0.0 + }, + { + "x": 220.0815982892816, + "y": -195.15053155138017, + "z": 0.0 + }, + { + "x": 219.07599291143572, + "y": -195.1504498364306, + "z": 0.0 + }, + { + "x": 218.07038753358984, + "y": -195.15036812148102, + "z": 0.0 + }, + { + "x": 217.06478215574396, + "y": -195.15028640653148, + "z": 0.0 + }, + { + "x": 216.05917677789813, + "y": -195.1502046915819, + "z": 0.0 + }, + { + "x": 215.05357140005225, + "y": -195.15012297663233, + "z": 0.0 + }, + { + "x": 214.04796602220642, + "y": -195.15004126168276, + "z": 0.0 + }, + { + "x": 213.04236064436054, + "y": -195.14995954673319, + "z": 0.0 + }, + { + "x": 212.03675526651466, + "y": -195.1498778317836, + "z": 0.0 + }, + { + "x": 211.03114988866878, + "y": -195.14979611683407, + "z": 0.0 + }, + { + "x": 210.02554451082295, + "y": -195.1497144018845, + "z": 0.0 + }, + { + "x": 209.01993913297707, + "y": -195.14963268693492, + "z": 0.0 + }, + { + "x": 208.01433375513125, + "y": -195.14955097198535, + "z": 0.0 + }, + { + "x": 207.00872837728537, + "y": -195.14946925703578, + "z": 0.0 + }, + { + "x": 206.00312299943943, + "y": -195.14938754208623, + "z": 0.0 + }, + { + "x": 204.9975176215936, + "y": -195.14930582713666, + "z": 0.0 + }, + { + "x": 203.99191224374778, + "y": -195.14922411218708, + "z": 0.0 + }, + { + "x": 202.9863068659019, + "y": -195.1491423972375, + "z": 0.0 + }, + { + "x": 201.980701488056, + "y": -195.14906068228794, + "z": 0.0 + }, + { + "x": 200.9750961102102, + "y": -195.14897896733837, + "z": 0.0 + }, + { + "x": 199.96949073236425, + "y": -195.14889725238882, + "z": 0.0 + }, + { + "x": 198.96388535451842, + "y": -195.14881553743925, + "z": 0.0 + }, + { + "x": 197.9582799766726, + "y": -195.14873382248967, + "z": 0.0 + }, + { + "x": 196.95267459882672, + "y": -195.1486521075401, + "z": 0.0 + }, + { + "x": 195.94706922098084, + "y": -195.14857039259053, + "z": 0.0 + }, + { + "x": 194.941463843135, + "y": -195.14848867764096, + "z": 0.0 + }, + { + "x": 193.93585846528907, + "y": -195.1484069626914, + "z": 0.0 + }, + { + "x": 192.93025308744325, + "y": -195.14832524774184, + "z": 0.0 + }, + { + "x": 191.92464770959742, + "y": -195.14824353279226, + "z": 0.0 + }, + { + "x": 190.91904233175154, + "y": -195.1481618178427, + "z": 0.0 + }, + { + "x": 189.91343695390566, + "y": -195.14808010289312, + "z": 0.0 + }, + { + "x": 188.90783157605978, + "y": -195.14799838794357, + "z": 0.0 + }, + { + "x": 187.90222619821395, + "y": -195.147916672994, + "z": 0.0 + }, + { + "x": 186.89662082036807, + "y": -195.14783495804443, + "z": 0.0 + }, + { + "x": 185.89101544252225, + "y": -195.14775324309485, + "z": 0.0 + }, + { + "x": 184.88541006467636, + "y": -195.14767152814528, + "z": 0.0 + }, + { + "x": 183.87980468683048, + "y": -195.1475898131957, + "z": 0.0 + }, + { + "x": 182.8741993089846, + "y": -195.14750809824616, + "z": 0.0 + }, + { + "x": 181.86859393113878, + "y": -195.1474263832966, + "z": 0.0 + }, + { + "x": 180.8629885532929, + "y": -195.14734466834702, + "z": 0.0 + }, + { + "x": 179.85738317544707, + "y": -195.14726295339744, + "z": 0.0 + }, + { + "x": 178.8517777976012, + "y": -195.14718123844787, + "z": 0.0 + }, + { + "x": 177.8461724197553, + "y": -195.1470995234983, + "z": 0.0 + }, + { + "x": 176.84056704190942, + "y": -195.14701780854875, + "z": 0.0 + }, + { + "x": 175.8349616640636, + "y": -195.14693609359918, + "z": 0.0 + }, + { + "x": 174.82935628621772, + "y": -195.1468543786496, + "z": 0.0 + }, + { + "x": 173.8237509083719, + "y": -195.14677266370003, + "z": 0.0 + }, + { + "x": 172.818145530526, + "y": -195.14669094875046, + "z": 0.0 + }, + { + "x": 171.81254015268007, + "y": -195.14660923380092, + "z": 0.0 + }, + { + "x": 170.8069347748343, + "y": -195.14652751885134, + "z": 0.0 + }, + { + "x": 169.80132939698842, + "y": -195.14644580390177, + "z": 0.0 + }, + { + "x": 168.79572401914254, + "y": -195.1463640889522, + "z": 0.0 + }, + { + "x": 167.79011864129666, + "y": -195.14628237400262, + "z": 0.0 + }, + { + "x": 166.78451326345083, + "y": -195.14620065905305, + "z": 0.0 + }, + { + "x": 165.7789078856049, + "y": -195.1461189441035, + "z": 0.0 + }, + { + "x": 164.77330250775913, + "y": -195.14603722915393, + "z": 0.0 + }, + { + "x": 163.76769712991324, + "y": -195.14595551420436, + "z": 0.0 + }, + { + "x": 162.76209175206736, + "y": -195.1458737992548, + "z": 0.0 + }, + { + "x": 161.75648637422148, + "y": -195.1457920843052, + "z": 0.0 + }, + { + "x": 160.7508809963756, + "y": -195.14571036935567, + "z": 0.0 + }, + { + "x": 159.74527561852977, + "y": -195.1456286544061, + "z": 0.0 + }, + { + "x": 158.7396702406839, + "y": -195.14554693945652, + "z": 0.0 + }, + { + "x": 157.73406486283807, + "y": -195.14546522450695, + "z": 0.0 + }, + { + "x": 156.7284594849922, + "y": -195.14538350955738, + "z": 0.0 + }, + { + "x": 155.7228541071463, + "y": -195.1453017946078, + "z": 0.0 + }, + { + "x": 154.71724872930042, + "y": -195.14522007965826, + "z": 0.0 + }, + { + "x": 153.7116433514546, + "y": -195.14513836470869, + "z": 0.0 + }, + { + "x": 152.70603797360872, + "y": -195.1450566497591, + "z": 0.0 + }, + { + "x": 151.7004325957629, + "y": -195.14497493480954, + "z": 0.0 + }, + { + "x": 150.694827217917, + "y": -195.14489321985997, + "z": 0.0 + }, + { + "x": 149.68922184007113, + "y": -195.1448115049104, + "z": 0.0 + }, + { + "x": 148.68361646222525, + "y": -195.14472978996085, + "z": 0.0 + }, + { + "x": 147.67801108437942, + "y": -195.14464807501128, + "z": 0.0 + }, + { + "x": 146.67240570653354, + "y": -195.1445663600617, + "z": 0.0 + }, + { + "x": 145.6668003286877, + "y": -195.14448464511213, + "z": 0.0 + }, + { + "x": 144.66119495084183, + "y": -195.14440293016256, + "z": 0.0 + }, + { + "x": 143.6555895729959, + "y": -195.144321215213, + "z": 0.0 + }, + { + "x": 142.64998419515013, + "y": -195.14423950026344, + "z": 0.0 + }, + { + "x": 141.64437881730424, + "y": -195.14415778531387, + "z": 0.0 + }, + { + "x": 140.63877343945836, + "y": -195.1440760703643, + "z": 0.0 + }, + { + "x": 139.63316806161254, + "y": -195.14399435541472, + "z": 0.0 + }, + { + "x": 138.62756268376666, + "y": -195.14391264046515, + "z": 0.0 + }, + { + "x": 137.62195730592072, + "y": -195.1438309255156, + "z": 0.0 + }, + { + "x": 136.61635192807495, + "y": -195.14374921056603, + "z": 0.0 + }, + { + "x": 135.61074655022907, + "y": -195.14366749561646, + "z": 0.0 + }, + { + "x": 134.60514117238318, + "y": -195.14358578066688, + "z": 0.0 + }, + { + "x": 133.59953579453736, + "y": -195.1435040657173, + "z": 0.0 + }, + { + "x": 132.59393041669148, + "y": -195.14342235076774, + "z": 0.0 + }, + { + "x": 131.58832503884554, + "y": -195.1433406358182, + "z": 0.0 + }, + { + "x": 130.58271966099977, + "y": -195.14325892086862, + "z": 0.0 + }, + { + "x": 129.5771142831539, + "y": -195.14317720591905, + "z": 0.0 + }, + { + "x": 128.571508905308, + "y": -195.14309549096947, + "z": 0.0 + }, + { + "x": 127.56590352746217, + "y": -195.1430137760199, + "z": 0.0 + }, + { + "x": 126.56029814961627, + "y": -195.14293206107035, + "z": 0.0 + }, + { + "x": 125.55469277177046, + "y": -195.14285034612078, + "z": 0.0 + }, + { + "x": 124.5490873939246, + "y": -195.1427686311712, + "z": 0.0 + }, + { + "x": 123.54348201607873, + "y": -195.14268691622163, + "z": 0.0 + }, + { + "x": 122.53787663823287, + "y": -195.14260520127206, + "z": 0.0 + }, + { + "x": 121.53227126038702, + "y": -195.1425234863225, + "z": 0.0 + }, + { + "x": 120.5266658825411, + "y": -195.14244177137294, + "z": 0.0 + }, + { + "x": 119.5210605046953, + "y": -195.14236005642337, + "z": 0.0 + }, + { + "x": 118.51545512684943, + "y": -195.1422783414738, + "z": 0.0 + }, + { + "x": 117.50984974900358, + "y": -195.14219662652422, + "z": 0.0 + }, + { + "x": 116.50424437115771, + "y": -195.14211491157465, + "z": 0.0 + }, + { + "x": 115.4986389933118, + "y": -195.1420331966251, + "z": 0.0 + }, + { + "x": 114.49303361546599, + "y": -195.14195148167553, + "z": 0.0 + }, + { + "x": 113.48742823762014, + "y": -195.14186976672596, + "z": 0.0 + }, + { + "x": 112.48182285977428, + "y": -195.1417880517764, + "z": 0.0 + }, + { + "x": 111.47621748192842, + "y": -195.14170633682681, + "z": 0.0 + }, + { + "x": 110.47061210408256, + "y": -195.14162462187724, + "z": 0.0 + }, + { + "x": 109.46500672623665, + "y": -195.1415429069277, + "z": 0.0 + }, + { + "x": 108.45940134839084, + "y": -195.14146119197812, + "z": 0.0 + }, + { + "x": 107.45379597054497, + "y": -195.14137947702855, + "z": 0.0 + }, + { + "x": 106.44819059269912, + "y": -195.14129776207898, + "z": 0.0 + }, + { + "x": 105.44258521485325, + "y": -195.1412160471294, + "z": 0.0 + }, + { + "x": 104.4369798370074, + "y": -195.14113433217983, + "z": 0.0 + }, + { + "x": 103.43137445916147, + "y": -195.1410526172303, + "z": 0.0 + }, + { + "x": 102.42576908131568, + "y": -195.1409709022807, + "z": 0.0 + }, + { + "x": 101.42016370346981, + "y": -195.14088918733114, + "z": 0.0 + } + ] + }, + { + "id": 64, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.4198386656325, + "y": -199.14088917412494, + "z": 0.0 + }, + { + "x": 102.42544404347836, + "y": -199.1409708890745, + "z": 0.0 + }, + { + "x": 103.43104942132427, + "y": -199.14105260402408, + "z": 0.0 + }, + { + "x": 104.43665479917009, + "y": -199.14113431897363, + "z": 0.0 + }, + { + "x": 105.44226017701594, + "y": -199.1412160339232, + "z": 0.0 + }, + { + "x": 106.4478655548618, + "y": -199.14129774887277, + "z": 0.0 + }, + { + "x": 107.45347093270766, + "y": -199.14137946382235, + "z": 0.0 + }, + { + "x": 108.45907631055353, + "y": -199.14146117877192, + "z": 0.0 + }, + { + "x": 109.46468168839942, + "y": -199.1415428937215, + "z": 0.0 + }, + { + "x": 110.47028706624525, + "y": -199.14162460867104, + "z": 0.0 + }, + { + "x": 111.4758924440911, + "y": -199.1417063236206, + "z": 0.0 + }, + { + "x": 112.48149782193697, + "y": -199.14178803857018, + "z": 0.0 + }, + { + "x": 113.48710319978282, + "y": -199.14186975351976, + "z": 0.0 + }, + { + "x": 114.49270857762868, + "y": -199.14195146846933, + "z": 0.0 + }, + { + "x": 115.4983139554746, + "y": -199.1420331834189, + "z": 0.0 + }, + { + "x": 116.5039193333204, + "y": -199.14211489836845, + "z": 0.0 + }, + { + "x": 117.50952471116626, + "y": -199.14219661331802, + "z": 0.0 + }, + { + "x": 118.51513008901212, + "y": -199.1422783282676, + "z": 0.0 + }, + { + "x": 119.52073546685799, + "y": -199.14236004321717, + "z": 0.0 + }, + { + "x": 120.5263408447039, + "y": -199.14244175816674, + "z": 0.0 + }, + { + "x": 121.5319462225497, + "y": -199.14252347311628, + "z": 0.0 + }, + { + "x": 122.53755160039556, + "y": -199.14260518806586, + "z": 0.0 + }, + { + "x": 123.54315697824141, + "y": -199.14268690301543, + "z": 0.0 + }, + { + "x": 124.54876235608728, + "y": -199.142768617965, + "z": 0.0 + }, + { + "x": 125.55436773393315, + "y": -199.14285033291458, + "z": 0.0 + }, + { + "x": 126.55997311177904, + "y": -199.14293204786415, + "z": 0.0 + }, + { + "x": 127.56557848962485, + "y": -199.1430137628137, + "z": 0.0 + }, + { + "x": 128.57118386747072, + "y": -199.14309547776327, + "z": 0.0 + }, + { + "x": 129.5767892453166, + "y": -199.14317719271284, + "z": 0.0 + }, + { + "x": 130.58239462316243, + "y": -199.1432589076624, + "z": 0.0 + }, + { + "x": 131.58800000100837, + "y": -199.143340622612, + "z": 0.0 + }, + { + "x": 132.59360537885414, + "y": -199.14342233756153, + "z": 0.0 + }, + { + "x": 133.59921075670002, + "y": -199.1435040525111, + "z": 0.0 + }, + { + "x": 134.6048161345459, + "y": -199.14358576746068, + "z": 0.0 + }, + { + "x": 135.61042151239172, + "y": -199.14366748241025, + "z": 0.0 + }, + { + "x": 136.6160268902376, + "y": -199.14374919735982, + "z": 0.0 + }, + { + "x": 137.62163226808354, + "y": -199.1438309123094, + "z": 0.0 + }, + { + "x": 138.6272376459293, + "y": -199.14391262725894, + "z": 0.0 + }, + { + "x": 139.6328430237752, + "y": -199.14399434220852, + "z": 0.0 + }, + { + "x": 140.63844840162108, + "y": -199.1440760571581, + "z": 0.0 + }, + { + "x": 141.6440537794669, + "y": -199.14415777210766, + "z": 0.0 + }, + { + "x": 142.64965915731278, + "y": -199.14423948705723, + "z": 0.0 + }, + { + "x": 143.65526453515872, + "y": -199.1443212020068, + "z": 0.0 + }, + { + "x": 144.6608699130045, + "y": -199.14440291695635, + "z": 0.0 + }, + { + "x": 145.66647529085037, + "y": -199.14448463190593, + "z": 0.0 + }, + { + "x": 146.67208066869625, + "y": -199.1445663468555, + "z": 0.0 + }, + { + "x": 147.67768604654208, + "y": -199.14464806180507, + "z": 0.0 + }, + { + "x": 148.68329142438802, + "y": -199.14472977675464, + "z": 0.0 + }, + { + "x": 149.68889680223384, + "y": -199.1448114917042, + "z": 0.0 + }, + { + "x": 150.69450218007967, + "y": -199.14489320665376, + "z": 0.0 + }, + { + "x": 151.70010755792555, + "y": -199.14497492160334, + "z": 0.0 + }, + { + "x": 152.70571293577143, + "y": -199.1450566365529, + "z": 0.0 + }, + { + "x": 153.71131831361726, + "y": -199.14513835150248, + "z": 0.0 + }, + { + "x": 154.7169236914632, + "y": -199.14522006645205, + "z": 0.0 + }, + { + "x": 155.72252906930902, + "y": -199.1453017814016, + "z": 0.0 + }, + { + "x": 156.72813444715484, + "y": -199.14538349635117, + "z": 0.0 + }, + { + "x": 157.73373982500073, + "y": -199.14546521130075, + "z": 0.0 + }, + { + "x": 158.7393452028466, + "y": -199.14554692625032, + "z": 0.0 + }, + { + "x": 159.7449505806925, + "y": -199.1456286411999, + "z": 0.0 + }, + { + "x": 160.75055595853837, + "y": -199.14571035614946, + "z": 0.0 + }, + { + "x": 161.7561613363842, + "y": -199.145792071099, + "z": 0.0 + }, + { + "x": 162.76176671423008, + "y": -199.14587378604858, + "z": 0.0 + }, + { + "x": 163.7673720920759, + "y": -199.14595550099816, + "z": 0.0 + }, + { + "x": 164.77297746992178, + "y": -199.14603721594773, + "z": 0.0 + }, + { + "x": 165.77858284776767, + "y": -199.1461189308973, + "z": 0.0 + }, + { + "x": 166.7841882256135, + "y": -199.14620064584685, + "z": 0.0 + }, + { + "x": 167.78979360345937, + "y": -199.14628236079642, + "z": 0.0 + }, + { + "x": 168.7953989813052, + "y": -199.146364075746, + "z": 0.0 + }, + { + "x": 169.80100435915108, + "y": -199.14644579069557, + "z": 0.0 + }, + { + "x": 170.80660973699696, + "y": -199.14652750564514, + "z": 0.0 + }, + { + "x": 171.81221511484284, + "y": -199.1466092205947, + "z": 0.0 + }, + { + "x": 172.81782049268867, + "y": -199.14669093554426, + "z": 0.0 + }, + { + "x": 173.82342587053455, + "y": -199.14677265049383, + "z": 0.0 + }, + { + "x": 174.82903124838037, + "y": -199.1468543654434, + "z": 0.0 + }, + { + "x": 175.83463662622626, + "y": -199.14693608039298, + "z": 0.0 + }, + { + "x": 176.8402420040722, + "y": -199.14701779534255, + "z": 0.0 + }, + { + "x": 177.84584738191796, + "y": -199.1470995102921, + "z": 0.0 + }, + { + "x": 178.85145275976384, + "y": -199.14718122524167, + "z": 0.0 + }, + { + "x": 179.85705813760973, + "y": -199.14726294019124, + "z": 0.0 + }, + { + "x": 180.8626635154556, + "y": -199.1473446551408, + "z": 0.0 + }, + { + "x": 181.86826889330143, + "y": -199.1474263700904, + "z": 0.0 + }, + { + "x": 182.87387427114737, + "y": -199.14750808503996, + "z": 0.0 + }, + { + "x": 183.8794796489932, + "y": -199.1475897999895, + "z": 0.0 + }, + { + "x": 184.88508502683902, + "y": -199.14767151493908, + "z": 0.0 + }, + { + "x": 185.8906904046849, + "y": -199.14775322988865, + "z": 0.0 + }, + { + "x": 186.89629578253079, + "y": -199.14783494483822, + "z": 0.0 + }, + { + "x": 187.9019011603766, + "y": -199.1479166597878, + "z": 0.0 + }, + { + "x": 188.90750653822255, + "y": -199.14799837473737, + "z": 0.0 + }, + { + "x": 189.91311191606837, + "y": -199.1480800896869, + "z": 0.0 + }, + { + "x": 190.9187172939142, + "y": -199.1481618046365, + "z": 0.0 + }, + { + "x": 191.92432267176008, + "y": -199.14824351958606, + "z": 0.0 + }, + { + "x": 192.92992804960596, + "y": -199.14832523453563, + "z": 0.0 + }, + { + "x": 193.93553342745184, + "y": -199.1484069494852, + "z": 0.0 + }, + { + "x": 194.94113880529767, + "y": -199.14848866443475, + "z": 0.0 + }, + { + "x": 195.94674418314355, + "y": -199.14857037938432, + "z": 0.0 + }, + { + "x": 196.95234956098938, + "y": -199.1486520943339, + "z": 0.0 + }, + { + "x": 197.95795493883526, + "y": -199.14873380928347, + "z": 0.0 + }, + { + "x": 198.96356031668114, + "y": -199.14881552423304, + "z": 0.0 + }, + { + "x": 199.96916569452702, + "y": -199.14889723918262, + "z": 0.0 + }, + { + "x": 200.97477107237285, + "y": -199.14897895413216, + "z": 0.0 + }, + { + "x": 201.98037645021873, + "y": -199.14906066908173, + "z": 0.0 + }, + { + "x": 202.98598182806455, + "y": -199.1491423840313, + "z": 0.0 + }, + { + "x": 203.99158720591043, + "y": -199.14922409898088, + "z": 0.0 + }, + { + "x": 204.99719258375632, + "y": -199.14930581393045, + "z": 0.0 + }, + { + "x": 206.0027979616022, + "y": -199.14938752888003, + "z": 0.0 + }, + { + "x": 207.00840333944802, + "y": -199.14946924382957, + "z": 0.0 + }, + { + "x": 208.0140087172939, + "y": -199.14955095877914, + "z": 0.0 + }, + { + "x": 209.01961409513973, + "y": -199.14963267372872, + "z": 0.0 + }, + { + "x": 210.0252194729856, + "y": -199.1497143886783, + "z": 0.0 + }, + { + "x": 211.03082485083155, + "y": -199.14979610362786, + "z": 0.0 + }, + { + "x": 212.03643022867732, + "y": -199.1498778185774, + "z": 0.0 + }, + { + "x": 213.0420356065232, + "y": -199.14995953352698, + "z": 0.0 + }, + { + "x": 214.04764098436908, + "y": -199.15004124847655, + "z": 0.0 + }, + { + "x": 215.0532463622149, + "y": -199.15012296342613, + "z": 0.0 + }, + { + "x": 216.0588517400608, + "y": -199.1502046783757, + "z": 0.0 + }, + { + "x": 217.06445711790673, + "y": -199.15028639332527, + "z": 0.0 + }, + { + "x": 218.07006249575255, + "y": -199.15036810827482, + "z": 0.0 + }, + { + "x": 219.07566787359838, + "y": -199.1504498232244, + "z": 0.0 + }, + { + "x": 220.08127325144426, + "y": -199.15053153817396, + "z": 0.0 + }, + { + "x": 221.08687862929014, + "y": -199.15061325312354, + "z": 0.0 + }, + { + "x": 222.09248400713602, + "y": -199.1506949680731, + "z": 0.0 + }, + { + "x": 223.09808938498185, + "y": -199.15077668302266, + "z": 0.0 + }, + { + "x": 224.10369476282773, + "y": -199.15085839797223, + "z": 0.0 + }, + { + "x": 225.10930014067355, + "y": -199.1509401129218, + "z": 0.0 + }, + { + "x": 226.11490551851944, + "y": -199.15102182787138, + "z": 0.0 + }, + { + "x": 227.12051089636532, + "y": -199.15110354282095, + "z": 0.0 + }, + { + "x": 228.1261162742112, + "y": -199.15118525777052, + "z": 0.0 + }, + { + "x": 229.13172165205702, + "y": -199.15126697272007, + "z": 0.0 + }, + { + "x": 230.1373270299029, + "y": -199.15134868766964, + "z": 0.0 + }, + { + "x": 231.14293240774873, + "y": -199.1514304026192, + "z": 0.0 + }, + { + "x": 232.1485377855946, + "y": -199.15151211756879, + "z": 0.0 + }, + { + "x": 233.1541431634405, + "y": -199.15159383251836, + "z": 0.0 + }, + { + "x": 234.15974854128638, + "y": -199.15167554746793, + "z": 0.0 + }, + { + "x": 235.1653539191322, + "y": -199.15175726241748, + "z": 0.0 + }, + { + "x": 236.17095929697808, + "y": -199.15183897736705, + "z": 0.0 + }, + { + "x": 237.1765646748239, + "y": -199.15192069231662, + "z": 0.0 + }, + { + "x": 238.1821700526698, + "y": -199.1520024072662, + "z": 0.0 + }, + { + "x": 239.18777543051573, + "y": -199.15208412221577, + "z": 0.0 + }, + { + "x": 240.19338080836155, + "y": -199.1521658371653, + "z": 0.0 + }, + { + "x": 241.19898618620738, + "y": -199.1522475521149, + "z": 0.0 + }, + { + "x": 242.20459156405326, + "y": -199.15232926706446, + "z": 0.0 + }, + { + "x": 243.21019694189914, + "y": -199.15241098201403, + "z": 0.0 + }, + { + "x": 244.21580231974497, + "y": -199.1524926969636, + "z": 0.0 + }, + { + "x": 245.2214076975909, + "y": -199.15257441191318, + "z": 0.0 + }, + { + "x": 246.22701307543673, + "y": -199.15265612686272, + "z": 0.0 + }, + { + "x": 247.23261845328256, + "y": -199.1527378418123, + "z": 0.0 + }, + { + "x": 248.23822383112844, + "y": -199.15281955676187, + "z": 0.0 + }, + { + "x": 249.24382920897432, + "y": -199.15290127171144, + "z": 0.0 + }, + { + "x": 250.24943458682014, + "y": -199.15298298666102, + "z": 0.0 + }, + { + "x": 251.25503996466608, + "y": -199.1530647016106, + "z": 0.0 + }, + { + "x": 252.2606453425119, + "y": -199.15314641656013, + "z": 0.0 + }, + { + "x": 253.26625072035773, + "y": -199.1532281315097, + "z": 0.0 + }, + { + "x": 254.2718560982036, + "y": -199.15330984645928, + "z": 0.0 + }, + { + "x": 255.2774614760495, + "y": -199.15339156140885, + "z": 0.0 + }, + { + "x": 256.2830668538954, + "y": -199.15347327635843, + "z": 0.0 + }, + { + "x": 257.28867223174115, + "y": -199.15355499130797, + "z": 0.0 + }, + { + "x": 258.294277609587, + "y": -199.15363670625754, + "z": 0.0 + }, + { + "x": 259.2998829874329, + "y": -199.15371842120712, + "z": 0.0 + }, + { + "x": 260.3054883652788, + "y": -199.1538001361567, + "z": 0.0 + }, + { + "x": 261.3110937431247, + "y": -199.15388185110626, + "z": 0.0 + }, + { + "x": 262.31669912097055, + "y": -199.15396356605584, + "z": 0.0 + }, + { + "x": 263.3223044988164, + "y": -199.15404528100538, + "z": 0.0 + }, + { + "x": 264.3279098766622, + "y": -199.15412699595495, + "z": 0.0 + }, + { + "x": 265.3335152545081, + "y": -199.15420871090453, + "z": 0.0 + }, + { + "x": 266.33912063235397, + "y": -199.1542904258541, + "z": 0.0 + }, + { + "x": 267.3447260101999, + "y": -199.15437214080367, + "z": 0.0 + }, + { + "x": 268.3503313880457, + "y": -199.15445385575322, + "z": 0.0 + }, + { + "x": 269.35593676589156, + "y": -199.1545355707028, + "z": 0.0 + }, + { + "x": 270.3615421437374, + "y": -199.15461728565236, + "z": 0.0 + }, + { + "x": 271.36714752158326, + "y": -199.15469900060194, + "z": 0.0 + }, + { + "x": 272.37275289942914, + "y": -199.1547807155515, + "z": 0.0 + }, + { + "x": 273.3783582772751, + "y": -199.15486243050108, + "z": 0.0 + }, + { + "x": 274.38396365512085, + "y": -199.15494414545063, + "z": 0.0 + }, + { + "x": 275.38956903296673, + "y": -199.1550258604002, + "z": 0.0 + }, + { + "x": 276.39517441081256, + "y": -199.15510757534977, + "z": 0.0 + }, + { + "x": 277.40077978865844, + "y": -199.15518929029935, + "z": 0.0 + }, + { + "x": 278.4063851665043, + "y": -199.15527100524892, + "z": 0.0 + }, + { + "x": 279.41199054435026, + "y": -199.1553527201985, + "z": 0.0 + }, + { + "x": 280.4175959221961, + "y": -199.15543443514804, + "z": 0.0 + }, + { + "x": 281.4232013000419, + "y": -199.1555161500976, + "z": 0.0 + }, + { + "x": 282.42880667788774, + "y": -199.15559786504718, + "z": 0.0 + }, + { + "x": 283.4344120557336, + "y": -199.15567957999676, + "z": 0.0 + }, + { + "x": 284.44001743357956, + "y": -199.15576129494633, + "z": 0.0 + }, + { + "x": 285.4456228114254, + "y": -199.15584300989588, + "z": 0.0 + }, + { + "x": 286.45122818927126, + "y": -199.15592472484545, + "z": 0.0 + }, + { + "x": 287.4568335671171, + "y": -199.15600643979502, + "z": 0.0 + }, + { + "x": 288.4624389449629, + "y": -199.1560881547446, + "z": 0.0 + }, + { + "x": 289.4680443228088, + "y": -199.15616986969417, + "z": 0.0 + }, + { + "x": 290.47364970065473, + "y": -199.15625158464374, + "z": 0.0 + }, + { + "x": 291.47925507850056, + "y": -199.15633329959329, + "z": 0.0 + }, + { + "x": 292.48486045634644, + "y": -199.15641501454286, + "z": 0.0 + }, + { + "x": 293.49046583419226, + "y": -199.15649672949243, + "z": 0.0 + }, + { + "x": 294.49607121203815, + "y": -199.156578444442, + "z": 0.0 + }, + { + "x": 295.50167658988397, + "y": -199.15666015939158, + "z": 0.0 + }, + { + "x": 296.5072819677299, + "y": -199.15674187434115, + "z": 0.0 + }, + { + "x": 297.51288734557573, + "y": -199.1568235892907, + "z": 0.0 + }, + { + "x": 298.5184927234216, + "y": -199.15690530424027, + "z": 0.0 + }, + { + "x": 299.52409810126744, + "y": -199.15698701918984, + "z": 0.0 + }, + { + "x": 300.5297034791133, + "y": -199.15706873413941, + "z": 0.0 + }, + { + "x": 301.5353088569592, + "y": -199.157150449089, + "z": 0.0 + }, + { + "x": 302.54091423480503, + "y": -199.15723216403853, + "z": 0.0 + }, + { + "x": 303.5465196126509, + "y": -199.1573138789881, + "z": 0.0 + }, + { + "x": 304.5521249904968, + "y": -199.15739559393768, + "z": 0.0 + }, + { + "x": 305.5577303683426, + "y": -199.15747730888725, + "z": 0.0 + }, + { + "x": 306.5633357461885, + "y": -199.15755902383682, + "z": 0.0 + }, + { + "x": 307.5689411240344, + "y": -199.1576407387864, + "z": 0.0 + }, + { + "x": 308.5745465018802, + "y": -199.15772245373594, + "z": 0.0 + }, + { + "x": 309.5801518797261, + "y": -199.15780416868552, + "z": 0.0 + }, + { + "x": 310.58575725757197, + "y": -199.1578858836351, + "z": 0.0 + }, + { + "x": 311.5913626354178, + "y": -199.15796759858466, + "z": 0.0 + }, + { + "x": 312.59696801326373, + "y": -199.15804931353424, + "z": 0.0 + }, + { + "x": 313.6025733911095, + "y": -199.15813102848378, + "z": 0.0 + }, + { + "x": 314.6081787689554, + "y": -199.15821274343335, + "z": 0.0 + }, + { + "x": 315.61378414680127, + "y": -199.15829445838293, + "z": 0.0 + }, + { + "x": 316.61938952464715, + "y": -199.1583761733325, + "z": 0.0 + }, + { + "x": 317.62499490249303, + "y": -199.15845788828207, + "z": 0.0 + }, + { + "x": 318.6306002803389, + "y": -199.15853960323165, + "z": 0.0 + }, + { + "x": 319.6362056581847, + "y": -199.1586213181812, + "z": 0.0 + }, + { + "x": 320.64181103603056, + "y": -199.15870303313076, + "z": 0.0 + }, + { + "x": 321.64741641387644, + "y": -199.15878474808034, + "z": 0.0 + }, + { + "x": 322.6530217917223, + "y": -199.1588664630299, + "z": 0.0 + }, + { + "x": 323.6586271695682, + "y": -199.15894817797948, + "z": 0.0 + }, + { + "x": 324.6642325474141, + "y": -199.15902989292906, + "z": 0.0 + }, + { + "x": 325.66983792525997, + "y": -199.1591116078786, + "z": 0.0 + } + ] + }, + { + "id": 65, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 11.028342463475031, + "y": 4.019999671866976, + "z": 0.0 + }, + { + "x": 9.953070070668474, + "y": 4.019649230031031, + "z": 0.0 + }, + { + "x": 8.800612790358642, + "y": 3.9976050577194115, + "z": 0.0 + }, + { + "x": 7.459285666338035, + "y": 3.8888366999628543, + "z": 0.0 + }, + { + "x": 5.97521383867693, + "y": 3.6174579798610065, + "z": 0.0 + }, + { + "x": 4.528719668379331, + "y": 3.186406110896411, + "z": 0.0 + }, + { + "x": 3.113186224320456, + "y": 2.5812645029885717, + "z": 0.0 + }, + { + "x": 1.7875954122404156, + "y": 1.8263279175440637, + "z": 0.0 + }, + { + "x": 0.5663901916985239, + "y": 0.9406644934610671, + "z": 0.0 + }, + { + "x": -0.590244979393741, + "y": -0.13645072900332167, + "z": 0.0 + }, + { + "x": -1.5775183083072448, + "y": -1.299702319737953, + "z": 0.0 + }, + { + "x": -2.4352046366311417, + "y": -2.593825751303942, + "z": 0.0 + }, + { + "x": -3.148498396979441, + "y": -4.074152675533871, + "z": 0.0 + }, + { + "x": -3.6444600059072725, + "y": -5.6604724247747775, + "z": 0.0 + }, + { + "x": -3.9150557372144608, + "y": -7.187614391929258, + "z": 0.0 + }, + { + "x": -4.022155975422126, + "y": -8.641040654230935, + "z": 0.0 + }, + { + "x": -4.02999970255904, + "y": -9.958459206173217, + "z": 0.0 + } + ] + }, + { + "id": 66, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 3.969999703900145, + "y": -9.961540870120729, + "z": 0.0 + }, + { + "x": 3.962984383848893, + "y": -9.128414681791984, + "z": 0.0 + }, + { + "x": 3.9868029716624225, + "y": -8.436865745719414, + "z": 0.0 + }, + { + "x": 4.050850939410168, + "y": -7.847294195033525, + "z": 0.0 + }, + { + "x": 4.141788106296348, + "y": -7.368347979421437, + "z": 0.0 + }, + { + "x": 4.314033709673633, + "y": -6.8889138464155195, + "z": 0.0 + }, + { + "x": 4.6087026679026915, + "y": -6.37224310651337, + "z": 0.0 + }, + { + "x": 4.980163347792317, + "y": -5.878449602950986, + "z": 0.0 + }, + { + "x": 5.364000502351587, + "y": -5.46112707664768, + "z": 0.0 + }, + { + "x": 5.8620495302655184, + "y": -5.058352441057074, + "z": 0.0 + }, + { + "x": 6.386402842845939, + "y": -4.718465147362091, + "z": 0.0 + }, + { + "x": 6.933168944995435, + "y": -4.443706842145653, + "z": 0.0 + }, + { + "x": 7.537298805651212, + "y": -4.228553144012891, + "z": 0.0 + }, + { + "x": 8.160644969253037, + "y": -4.0803599426238115, + "z": 0.0 + }, + { + "x": 8.958862123970693, + "y": -4.000829610914904, + "z": 0.0 + }, + { + "x": 9.955677349119426, + "y": -3.980350345100149, + "z": 0.0 + }, + { + "x": 11.030949741925983, + "y": -3.9799999032642037, + "z": 0.0 + } + ] + }, + { + "id": 67, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": -0.029999999329447746, + "y": -9.960000038146973, + "z": 0.0 + }, + { + "x": -0.029585795786616468, + "y": -8.88472766801146, + "z": 0.0 + }, + { + "x": 0.035873617223980664, + "y": -7.8122400688243365, + "z": 0.0 + }, + { + "x": 0.2031954667514479, + "y": -6.753883309904151, + "z": 0.0 + }, + { + "x": 0.4966448546584533, + "y": -5.721250327477654, + "z": 0.0 + }, + { + "x": 0.9394145365212455, + "y": -4.741369798859731, + "z": 0.0 + }, + { + "x": 1.5155921797977234, + "y": -3.8359727131256616, + "z": 0.0 + }, + { + "x": 2.1949591841992877, + "y": -3.0074501659771538, + "z": 0.0 + }, + { + "x": 2.9651953470250554, + "y": -2.2602312915933065, + "z": 0.0 + }, + { + "x": 3.824822471252967, + "y": -1.6160122617565051, + "z": 0.0 + }, + { + "x": 4.749794533583198, + "y": -1.0686003221867595, + "z": 0.0 + }, + { + "x": 5.730944306687383, + "y": -0.6286503656246212, + "z": 0.0 + }, + { + "x": 6.756256322164071, + "y": -0.3055475820759422, + "z": 0.0 + }, + { + "x": 7.809965317795536, + "y": -0.09576162133047841, + "z": 0.0 + }, + { + "x": 8.879737457164667, + "y": -0.0016122765977463205, + "z": 0.0 + }, + { + "x": 9.95437370989395, + "y": 0.01964944246544118, + "z": 0.0 + }, + { + "x": 11.029646102700507, + "y": 0.019999884301386054, + "z": 0.0 + } + ] + }, + { + "id": 68, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 11.02899428308777, + "y": 2.019999778084181, + "z": 0.0 + }, + { + "x": 9.953721890281212, + "y": 2.019649336248236, + "z": 0.0 + }, + { + "x": 8.840175123761654, + "y": 1.9979963905608327, + "z": 0.0 + }, + { + "x": 7.634625492066785, + "y": 1.8965375393161878, + "z": 0.0 + }, + { + "x": 6.3657350804205, + "y": 1.655955198892532, + "z": 0.0 + }, + { + "x": 5.129831987533358, + "y": 1.278877872635895, + "z": 0.0 + }, + { + "x": 3.931490378951827, + "y": 0.7563320904009061, + "z": 0.0 + }, + { + "x": 2.806208941746691, + "y": 0.10515782789377925, + "z": 0.0 + }, + { + "x": 1.7657927693617896, + "y": -0.6597833990661197, + "z": 0.0 + }, + { + "x": 0.8023571024027734, + "y": -1.5719504474902377, + "z": 0.0 + }, + { + "x": -0.030963064254760697, + "y": -2.5678375164318075, + "z": 0.0 + }, + { + "x": -0.7478950500549482, + "y": -3.6675977750818367, + "z": 0.0 + }, + { + "x": -1.3259267711604938, + "y": -4.897701501505763, + "z": 0.0 + }, + { + "x": -1.7206322695779124, + "y": -6.207177867339464, + "z": 0.0 + }, + { + "x": -1.93959105999524, + "y": -7.499927230376797, + "z": 0.0 + }, + { + "x": -2.025870885604371, + "y": -8.762884161121196, + "z": 0.0 + }, + { + "x": -2.029999850944244, + "y": -9.959229622160095, + "z": 0.0 + } + ] + }, + { + "id": 69, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 1.9699998522853486, + "y": -9.96077045413385, + "z": 0.0 + }, + { + "x": 1.9666992940311383, + "y": -9.006571174901723, + "z": 0.0 + }, + { + "x": 2.0113382944432017, + "y": -8.124552907271875, + "z": 0.0 + }, + { + "x": 2.127023203080808, + "y": -7.300588752468839, + "z": 0.0 + }, + { + "x": 2.3192164804774005, + "y": -6.544799153449546, + "z": 0.0 + }, + { + "x": 2.626724123097439, + "y": -5.815141822637625, + "z": 0.0 + }, + { + "x": 3.0621474238502073, + "y": -5.104107909819516, + "z": 0.0 + }, + { + "x": 3.5875612659958023, + "y": -4.44294988446407, + "z": 0.0 + }, + { + "x": 4.164597924688321, + "y": -3.8606791841204933, + "z": 0.0 + }, + { + "x": 4.843436000759243, + "y": -3.3371823514067893, + "z": 0.0 + }, + { + "x": 5.568098688214569, + "y": -2.893532734774425, + "z": 0.0 + }, + { + "x": 6.332056625841409, + "y": -2.536178603885137, + "z": 0.0 + }, + { + "x": 7.146777563907642, + "y": -2.2670503630444165, + "z": 0.0 + }, + { + "x": 7.985305143524286, + "y": -2.088060781977145, + "z": 0.0 + }, + { + "x": 8.919299790567681, + "y": -2.001220943756325, + "z": 0.0 + }, + { + "x": 9.955025529506688, + "y": -1.980350451317354, + "z": 0.0 + }, + { + "x": 11.030297922313245, + "y": -1.9800000094814088, + "z": 0.0 + } + ] + }, + { + "id": 70, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 384.1736658134914, + "y": -332.60889681208965, + "z": 0.0 + }, + { + "x": 385.2643279115696, + "y": -332.58344691864033, + "z": 0.0 + }, + { + "x": 386.62477890205395, + "y": -332.46711385713115, + "z": 0.0 + }, + { + "x": 388.11199738649975, + "y": -332.1915900435458, + "z": 0.0 + }, + { + "x": 389.56266099323636, + "y": -331.7604566938843, + "z": 0.0 + }, + { + "x": 390.99555684282876, + "y": -331.1497265885555, + "z": 0.0 + }, + { + "x": 392.32475767360006, + "y": -330.39944230918036, + "z": 0.0 + }, + { + "x": 393.55519954086054, + "y": -329.5203187020522, + "z": 0.0 + }, + { + "x": 394.7013785390999, + "y": -328.49147913981506, + "z": 0.0 + }, + { + "x": 395.7435380994426, + "y": -327.29875989133785, + "z": 0.0 + }, + { + "x": 396.6346132755301, + "y": -325.97213713418074, + "z": 0.0 + }, + { + "x": 397.34968153032514, + "y": -324.5427275788562, + "z": 0.0 + }, + { + "x": 397.8771365105912, + "y": -323.0335278027845, + "z": 0.0 + }, + { + "x": 398.2083233275286, + "y": -321.4688127703625, + "z": 0.0 + }, + { + "x": 398.34184677749806, + "y": -319.89331026646045, + "z": 0.0 + }, + { + "x": 398.35000580137364, + "y": -318.5384233132525, + "z": 0.0 + } + ] + }, + { + "id": 71, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 390.3500064056576, + "y": -318.541532741435, + "z": 0.0 + }, + { + "x": 390.35901385763646, + "y": -319.36949868433095, + "z": 0.0 + }, + { + "x": 390.3496352343571, + "y": -319.97180861063424, + "z": 0.0 + }, + { + "x": 390.27293211969754, + "y": -320.54835666551554, + "z": 0.0 + }, + { + "x": 390.123631148747, + "y": -321.1099159203329, + "z": 0.0 + }, + { + "x": 389.9042235497951, + "y": -321.64757309375756, + "z": 0.0 + }, + { + "x": 389.61826346121734, + "y": -322.15278956434275, + "z": 0.0 + }, + { + "x": 389.259771012167, + "y": -322.62727352244224, + "z": 0.0 + }, + { + "x": 388.8089051340767, + "y": -323.0803894008771, + "z": 0.0 + }, + { + "x": 388.2842881070022, + "y": -323.4947623251842, + "z": 0.0 + }, + { + "x": 387.7306371533386, + "y": -323.84628223529455, + "z": 0.0 + }, + { + "x": 387.1707397701482, + "y": -324.1264071222257, + "z": 0.0 + }, + { + "x": 386.53881093627876, + "y": -324.34779731344116, + "z": 0.0 + }, + { + "x": 385.8877456964572, + "y": -324.5011373285537, + "z": 0.0 + }, + { + "x": 385.0777038952716, + "y": -324.585623997586, + "z": 0.0 + }, + { + "x": 383.98704179719346, + "y": -324.61107389103535, + "z": 0.0 + } + ] + }, + { + "id": 72, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 394.3500061035156, + "y": -318.53997802734375, + "z": 0.0 + }, + { + "x": 394.35043031756726, + "y": -319.6314044753957, + "z": 0.0 + }, + { + "x": 394.27897928094285, + "y": -320.72031069049837, + "z": 0.0 + }, + { + "x": 394.07503431514436, + "y": -321.79094223415, + "z": 0.0 + }, + { + "x": 393.73665633953607, + "y": -322.82632174959457, + "z": 0.0 + }, + { + "x": 393.2694184126626, + "y": -323.80985511396915, + "z": 0.0 + }, + { + "x": 392.68090078033, + "y": -324.7257747278403, + "z": 0.0 + }, + { + "x": 391.98057477563344, + "y": -325.55937633112865, + "z": 0.0 + }, + { + "x": 391.18205233746863, + "y": -326.30035405146464, + "z": 0.0 + }, + { + "x": 390.30452289030114, + "y": -326.94710231718227, + "z": 0.0 + }, + { + "x": 389.3630969980837, + "y": -327.498004411925, + "z": 0.0 + }, + { + "x": 388.3667003816923, + "y": -327.943431908055, + "z": 0.0 + }, + { + "x": 387.32540416138926, + "y": -328.26969367849347, + "z": 0.0 + }, + { + "x": 386.2562622992556, + "y": -328.4841255928424, + "z": 0.0 + }, + { + "x": 385.1710159034206, + "y": -328.5845354581132, + "z": 0.0 + }, + { + "x": 384.08035380534244, + "y": -328.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 73, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 384.1270098094169, + "y": -330.60944108182605, + "z": 0.0 + }, + { + "x": 385.21767190749506, + "y": -330.5839911883768, + "z": 0.0 + }, + { + "x": 386.44052060065474, + "y": -330.4756197249868, + "z": 0.0 + }, + { + "x": 387.71870077394453, + "y": -330.23064186101965, + "z": 0.0 + }, + { + "x": 388.96468068746435, + "y": -329.85194430096965, + "z": 0.0 + }, + { + "x": 390.1793269204562, + "y": -329.32386550024023, + "z": 0.0 + }, + { + "x": 391.3146402819506, + "y": -328.6732723131813, + "z": 0.0 + }, + { + "x": 392.3686259391646, + "y": -327.91033637675844, + "z": 0.0 + }, + { + "x": 393.3409766573667, + "y": -327.02542773547185, + "z": 0.0 + }, + { + "x": 394.2122194398863, + "y": -326.0122673095891, + "z": 0.0 + }, + { + "x": 394.95201584409637, + "y": -324.89099612407495, + "z": 0.0 + }, + { + "x": 395.5431689349306, + "y": -323.68452466422536, + "z": 0.0 + }, + { + "x": 395.97608541286775, + "y": -322.41223501846724, + "z": 0.0 + }, + { + "x": 396.24365130423575, + "y": -321.0945617304304, + "z": 0.0 + }, + { + "x": 396.34613854753263, + "y": -319.76235737092804, + "z": 0.0 + }, + { + "x": 396.35000595244463, + "y": -318.5392006702981, + "z": 0.0 + } + ] + }, + { + "id": 74, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 392.3500062545866, + "y": -318.5407553843894, + "z": 0.0 + }, + { + "x": 392.3547220876019, + "y": -319.50045157986335, + "z": 0.0 + }, + { + "x": 392.31430725764994, + "y": -320.3460596505663, + "z": 0.0 + }, + { + "x": 392.173983217421, + "y": -321.1696494498328, + "z": 0.0 + }, + { + "x": 391.93014374414156, + "y": -321.9681188349638, + "z": 0.0 + }, + { + "x": 391.58682098122887, + "y": -322.72871410386335, + "z": 0.0 + }, + { + "x": 391.14958212077363, + "y": -323.4392821460915, + "z": 0.0 + }, + { + "x": 390.6201728939002, + "y": -324.09332492678544, + "z": 0.0 + }, + { + "x": 389.9954787357727, + "y": -324.69037172617084, + "z": 0.0 + }, + { + "x": 389.2944054986517, + "y": -325.2209323211832, + "z": 0.0 + }, + { + "x": 388.54686707571113, + "y": -325.6721433236098, + "z": 0.0 + }, + { + "x": 387.7687200759202, + "y": -326.03491951514036, + "z": 0.0 + }, + { + "x": 386.932107548834, + "y": -326.3087454959673, + "z": 0.0 + }, + { + "x": 386.0720039978564, + "y": -326.492631460698, + "z": 0.0 + }, + { + "x": 385.12435989934613, + "y": -326.5850797278496, + "z": 0.0 + }, + { + "x": 384.033697801268, + "y": -326.61052962129895, + "z": 0.0 + } + ] + }, + { + "id": 75, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 4.009999965684669, + "y": -317.6005283434444, + "z": 0.0 + }, + { + "x": 4.010130803211286, + "y": -316.59844355295434, + "z": 0.0 + }, + { + "x": 4.0102616407379035, + "y": -315.59635876246426, + "z": 0.0 + }, + { + "x": 4.010392478264521, + "y": -314.5942739719742, + "z": 0.0 + }, + { + "x": 4.010523315791139, + "y": -313.5921891814841, + "z": 0.0 + }, + { + "x": 4.010654153317756, + "y": -312.59010439099404, + "z": 0.0 + }, + { + "x": 4.010784990844374, + "y": -311.58801960050397, + "z": 0.0 + }, + { + "x": 4.010915828370991, + "y": -310.5859348100139, + "z": 0.0 + }, + { + "x": 4.0110466658976085, + "y": -309.5838500195239, + "z": 0.0 + }, + { + "x": 4.011177503424226, + "y": -308.5817652290338, + "z": 0.0 + }, + { + "x": 4.011308340950843, + "y": -307.57968043854373, + "z": 0.0 + }, + { + "x": 4.0114391784774615, + "y": -306.57759564805366, + "z": 0.0 + }, + { + "x": 4.011570016004079, + "y": -305.5755108575636, + "z": 0.0 + }, + { + "x": 4.011700853530696, + "y": -304.5734260670735, + "z": 0.0 + }, + { + "x": 4.0118316910573135, + "y": -303.57134127658344, + "z": 0.0 + }, + { + "x": 4.011962528583931, + "y": -302.5692564860934, + "z": 0.0 + }, + { + "x": 4.012093366110548, + "y": -301.56717169560335, + "z": 0.0 + }, + { + "x": 4.012224203637166, + "y": -300.5650869051133, + "z": 0.0 + }, + { + "x": 4.012355041163783, + "y": -299.5630021146232, + "z": 0.0 + }, + { + "x": 4.012485878690401, + "y": -298.56091732413313, + "z": 0.0 + }, + { + "x": 4.0126167162170185, + "y": -297.55883253364306, + "z": 0.0 + }, + { + "x": 4.012747553743636, + "y": -296.556747743153, + "z": 0.0 + }, + { + "x": 4.012878391270253, + "y": -295.5546629526629, + "z": 0.0 + }, + { + "x": 4.013009228796871, + "y": -294.55257816217284, + "z": 0.0 + }, + { + "x": 4.013140066323488, + "y": -293.5504933716828, + "z": 0.0 + }, + { + "x": 4.013270903850105, + "y": -292.54840858119275, + "z": 0.0 + }, + { + "x": 4.013401741376724, + "y": -291.5463237907027, + "z": 0.0 + }, + { + "x": 4.013532578903341, + "y": -290.5442390002126, + "z": 0.0 + }, + { + "x": 4.013663416429958, + "y": -289.5421542097225, + "z": 0.0 + }, + { + "x": 4.013794253956576, + "y": -288.54006941923245, + "z": 0.0 + }, + { + "x": 4.013925091483193, + "y": -287.5379846287424, + "z": 0.0 + }, + { + "x": 4.01405592900981, + "y": -286.5358998382523, + "z": 0.0 + }, + { + "x": 4.014186766536428, + "y": -285.53381504776223, + "z": 0.0 + }, + { + "x": 4.014317604063046, + "y": -284.53173025727216, + "z": 0.0 + }, + { + "x": 4.014448441589663, + "y": -283.5296454667821, + "z": 0.0 + }, + { + "x": 4.014579279116281, + "y": -282.527560676292, + "z": 0.0 + }, + { + "x": 4.014710116642898, + "y": -281.52547588580194, + "z": 0.0 + }, + { + "x": 4.014840954169515, + "y": -280.52339109531187, + "z": 0.0 + }, + { + "x": 4.014971791696133, + "y": -279.5213063048218, + "z": 0.0 + }, + { + "x": 4.01510262922275, + "y": -278.5192215143317, + "z": 0.0 + }, + { + "x": 4.015233466749367, + "y": -277.5171367238417, + "z": 0.0 + }, + { + "x": 4.015364304275986, + "y": -276.51505193335163, + "z": 0.0 + }, + { + "x": 4.015495141802603, + "y": -275.51296714286156, + "z": 0.0 + }, + { + "x": 4.01562597932922, + "y": -274.5108823523715, + "z": 0.0 + }, + { + "x": 4.015756816855838, + "y": -273.5087975618814, + "z": 0.0 + }, + { + "x": 4.015887654382455, + "y": -272.50671277139134, + "z": 0.0 + }, + { + "x": 4.0160184919090725, + "y": -271.50462798090126, + "z": 0.0 + }, + { + "x": 4.01614932943569, + "y": -270.50254319041125, + "z": 0.0 + }, + { + "x": 4.016280166962308, + "y": -269.5004583999212, + "z": 0.0 + }, + { + "x": 4.016411004488925, + "y": -268.4983736094311, + "z": 0.0 + }, + { + "x": 4.016541842015543, + "y": -267.496288818941, + "z": 0.0 + }, + { + "x": 4.01667267954216, + "y": -266.49420402845095, + "z": 0.0 + }, + { + "x": 4.0168035170687775, + "y": -265.4921192379609, + "z": 0.0 + }, + { + "x": 4.016934354595395, + "y": -264.49003444747086, + "z": 0.0 + }, + { + "x": 4.017065192122012, + "y": -263.48794965698085, + "z": 0.0 + }, + { + "x": 4.0171960296486295, + "y": -262.4858648664908, + "z": 0.0 + }, + { + "x": 4.017326867175248, + "y": -261.48378007600076, + "z": 0.0 + }, + { + "x": 4.017457704701865, + "y": -260.48169528551074, + "z": 0.0 + }, + { + "x": 4.0175885422284825, + "y": -259.47961049502067, + "z": 0.0 + }, + { + "x": 4.0177193797551, + "y": -258.47752570453065, + "z": 0.0 + }, + { + "x": 4.017850217281717, + "y": -257.4754409140406, + "z": 0.0 + }, + { + "x": 4.017981054808335, + "y": -256.4733561235505, + "z": 0.0 + }, + { + "x": 4.018111892334951, + "y": -255.4712713330605, + "z": 0.0 + }, + { + "x": 4.018242729861569, + "y": -254.46918654257047, + "z": 0.0 + }, + { + "x": 4.018373567388187, + "y": -253.46710175208042, + "z": 0.0 + }, + { + "x": 4.018504404914805, + "y": -252.4650169615904, + "z": 0.0 + }, + { + "x": 4.018635242441421, + "y": -251.46293217110036, + "z": 0.0 + }, + { + "x": 4.018766079968039, + "y": -250.46084738061032, + "z": 0.0 + }, + { + "x": 4.018896917494656, + "y": -249.45876259012027, + "z": 0.0 + }, + { + "x": 4.019027755021273, + "y": -248.45667779963023, + "z": 0.0 + }, + { + "x": 4.019158592547892, + "y": -247.4545930091402, + "z": 0.0 + }, + { + "x": 4.01928943007451, + "y": -246.45250821865014, + "z": 0.0 + }, + { + "x": 4.019420267601126, + "y": -245.4504234281601, + "z": 0.0 + }, + { + "x": 4.019551105127745, + "y": -244.44833863767008, + "z": 0.0 + }, + { + "x": 4.019681942654361, + "y": -243.44625384718003, + "z": 0.0 + }, + { + "x": 4.019812780180978, + "y": -242.44416905669, + "z": 0.0 + }, + { + "x": 4.019943617707596, + "y": -241.44208426619994, + "z": 0.0 + }, + { + "x": 4.0197019046914795, + "y": -240.4385127574555, + "z": 0.0 + }, + { + "x": 4.019178091099026, + "y": -239.43530342597631, + "z": 0.0 + }, + { + "x": 4.018654706806054, + "y": -238.43321876362546, + "z": 0.0 + }, + { + "x": 4.018131322513082, + "y": -237.4311341012746, + "z": 0.0 + }, + { + "x": 4.0176079382201095, + "y": -236.42904943892378, + "z": 0.0 + }, + { + "x": 4.017084553927138, + "y": -235.42696477657293, + "z": 0.0 + }, + { + "x": 4.016561169634166, + "y": -234.42488011422208, + "z": 0.0 + }, + { + "x": 4.016037785341194, + "y": -233.42279545187122, + "z": 0.0 + }, + { + "x": 4.015514401048222, + "y": -232.42071078952037, + "z": 0.0 + }, + { + "x": 4.01499101675525, + "y": -231.41862612716957, + "z": 0.0 + }, + { + "x": 4.014467632462277, + "y": -230.41654146481872, + "z": 0.0 + }, + { + "x": 4.013944248169305, + "y": -229.41445680246787, + "z": 0.0 + }, + { + "x": 4.013420863876333, + "y": -228.41237214011704, + "z": 0.0 + }, + { + "x": 4.012897479583361, + "y": -227.4102874777662, + "z": 0.0 + }, + { + "x": 4.012374095290389, + "y": -226.40820281541534, + "z": 0.0 + }, + { + "x": 4.011850710997417, + "y": -225.40611815306448, + "z": 0.0 + }, + { + "x": 4.0113273267044445, + "y": -224.40403349071363, + "z": 0.0 + }, + { + "x": 4.0108039424114725, + "y": -223.4019488283628, + "z": 0.0 + }, + { + "x": 4.010280558118501, + "y": -222.39986416601195, + "z": 0.0 + }, + { + "x": 4.009757173825529, + "y": -221.3977795036611, + "z": 0.0 + }, + { + "x": 4.009233789532557, + "y": -220.39569484131027, + "z": 0.0 + }, + { + "x": 4.008710405239585, + "y": -219.39361017895945, + "z": 0.0 + }, + { + "x": 4.008187020946612, + "y": -218.3915255166086, + "z": 0.0 + }, + { + "x": 4.00766363665364, + "y": -217.38944085425774, + "z": 0.0 + }, + { + "x": 4.007140252360668, + "y": -216.3873561919069, + "z": 0.0 + }, + { + "x": 4.006616868067696, + "y": -215.38527152955606, + "z": 0.0 + }, + { + "x": 4.006093483774724, + "y": -214.3831868672052, + "z": 0.0 + }, + { + "x": 4.005570099481752, + "y": -213.38110220485436, + "z": 0.0 + }, + { + "x": 4.005046715188779, + "y": -212.37901754250353, + "z": 0.0 + }, + { + "x": 4.0045233308958075, + "y": -211.37693288015268, + "z": 0.0 + }, + { + "x": 4.0039999466028355, + "y": -210.37484821780183, + "z": 0.0 + }, + { + "x": 4.003476562309864, + "y": -209.37276355545097, + "z": 0.0 + }, + { + "x": 4.002953178016892, + "y": -208.37067889310012, + "z": 0.0 + }, + { + "x": 4.002429793723919, + "y": -207.3685942307493, + "z": 0.0 + }, + { + "x": 4.001906409430947, + "y": -206.36650956839847, + "z": 0.0 + }, + { + "x": 4.001383025137975, + "y": -205.36442490604762, + "z": 0.0 + }, + { + "x": 4.000859640845003, + "y": -204.3623402436968, + "z": 0.0 + }, + { + "x": 4.000336256552031, + "y": -203.36025558134594, + "z": 0.0 + }, + { + "x": 3.9998128722590587, + "y": -202.35817091899509, + "z": 0.0 + }, + { + "x": 3.9992894879660867, + "y": -201.35608625664423, + "z": 0.0 + }, + { + "x": 3.998766103673115, + "y": -200.35400159429338, + "z": 0.0 + }, + { + "x": 3.9982427193801424, + "y": -199.35191693194255, + "z": 0.0 + }, + { + "x": 3.9977193350871705, + "y": -198.3498322695917, + "z": 0.0 + }, + { + "x": 3.9971959507941985, + "y": -197.34774760724085, + "z": 0.0 + }, + { + "x": 3.996672566501226, + "y": -196.34566294489002, + "z": 0.0 + }, + { + "x": 3.996149182208254, + "y": -195.34357828253917, + "z": 0.0 + }, + { + "x": 3.9956257979152823, + "y": -194.34149362018834, + "z": 0.0 + }, + { + "x": 3.99510241362231, + "y": -193.3394089578375, + "z": 0.0 + }, + { + "x": 3.994579029329338, + "y": -192.33732429548664, + "z": 0.0 + }, + { + "x": 3.994055645036366, + "y": -191.3352396331358, + "z": 0.0 + }, + { + "x": 3.9935322607433936, + "y": -190.33315497078496, + "z": 0.0 + }, + { + "x": 3.9930088764504217, + "y": -189.3310703084341, + "z": 0.0 + }, + { + "x": 3.9924854921574493, + "y": -188.32898564608328, + "z": 0.0 + }, + { + "x": 3.9919621078644774, + "y": -187.32690098373243, + "z": 0.0 + }, + { + "x": 3.9914387235715054, + "y": -186.32481632138158, + "z": 0.0 + }, + { + "x": 3.990915339278533, + "y": -185.32273165903072, + "z": 0.0 + }, + { + "x": 3.990391954985561, + "y": -184.32064699667987, + "z": 0.0 + }, + { + "x": 3.989868570692589, + "y": -183.31856233432904, + "z": 0.0 + }, + { + "x": 3.9893451863996168, + "y": -182.3164776719782, + "z": 0.0 + }, + { + "x": 3.988821802106645, + "y": -181.31439300962737, + "z": 0.0 + }, + { + "x": 3.988298417813673, + "y": -180.31230834727654, + "z": 0.0 + }, + { + "x": 3.9877750335207005, + "y": -179.3102236849257, + "z": 0.0 + }, + { + "x": 3.9872516492277286, + "y": -178.30813902257484, + "z": 0.0 + }, + { + "x": 3.9867282649347566, + "y": -177.30605436022398, + "z": 0.0 + }, + { + "x": 3.9862048806417842, + "y": -176.30396969787313, + "z": 0.0 + }, + { + "x": 3.9856814963488123, + "y": -175.3018850355223, + "z": 0.0 + }, + { + "x": 3.9851581120558404, + "y": -174.29980037317145, + "z": 0.0 + }, + { + "x": 3.984634727762868, + "y": -173.2977157108206, + "z": 0.0 + }, + { + "x": 3.984111343469896, + "y": -172.29563104846977, + "z": 0.0 + }, + { + "x": 3.983587959176924, + "y": -171.29354638611892, + "z": 0.0 + }, + { + "x": 3.9830645748839517, + "y": -170.29146172376807, + "z": 0.0 + }, + { + "x": 3.9825411905909798, + "y": -169.28937706141724, + "z": 0.0 + }, + { + "x": 3.982017806298008, + "y": -168.2872923990664, + "z": 0.0 + }, + { + "x": 3.9814944220050354, + "y": -167.28520773671556, + "z": 0.0 + }, + { + "x": 3.9809710377120635, + "y": -166.2831230743647, + "z": 0.0 + }, + { + "x": 3.9804476534190916, + "y": -165.28103841201386, + "z": 0.0 + }, + { + "x": 3.979924269126119, + "y": -164.27895374966303, + "z": 0.0 + }, + { + "x": 3.9794008848331472, + "y": -163.27686908731218, + "z": 0.0 + }, + { + "x": 3.9788775005401753, + "y": -162.27478442496133, + "z": 0.0 + }, + { + "x": 3.978354116247203, + "y": -161.27269976261047, + "z": 0.0 + }, + { + "x": 3.977830731954231, + "y": -160.27061510025962, + "z": 0.0 + }, + { + "x": 3.977307347661259, + "y": -159.2685304379088, + "z": 0.0 + }, + { + "x": 3.9767839633682867, + "y": -158.26644577555794, + "z": 0.0 + }, + { + "x": 3.9762605790753147, + "y": -157.2643611132071, + "z": 0.0 + }, + { + "x": 3.9757371947823428, + "y": -156.2622764508563, + "z": 0.0 + }, + { + "x": 3.9752138104893704, + "y": -155.26019178850544, + "z": 0.0 + }, + { + "x": 3.9746904261963985, + "y": -154.25810712615458, + "z": 0.0 + }, + { + "x": 3.9741670419034265, + "y": -153.25602246380373, + "z": 0.0 + }, + { + "x": 3.973643657610454, + "y": -152.25393780145288, + "z": 0.0 + }, + { + "x": 3.973120273317482, + "y": -151.25185313910205, + "z": 0.0 + }, + { + "x": 3.9725968890245102, + "y": -150.2497684767512, + "z": 0.0 + }, + { + "x": 3.972073504731538, + "y": -149.24768381440035, + "z": 0.0 + }, + { + "x": 3.971550120438566, + "y": -148.24559915204952, + "z": 0.0 + }, + { + "x": 3.9710267361455935, + "y": -147.24351448969867, + "z": 0.0 + }, + { + "x": 3.9705033518526216, + "y": -146.24142982734782, + "z": 0.0 + }, + { + "x": 3.9699799675596497, + "y": -145.23934516499696, + "z": 0.0 + }, + { + "x": 3.9694565832666773, + "y": -144.23726050264614, + "z": 0.0 + }, + { + "x": 3.9689331989737053, + "y": -143.2351758402953, + "z": 0.0 + }, + { + "x": 3.9684098146807334, + "y": -142.23309117794446, + "z": 0.0 + }, + { + "x": 3.967886430387761, + "y": -141.2310065155936, + "z": 0.0 + }, + { + "x": 3.967363046094789, + "y": -140.22892185324278, + "z": 0.0 + }, + { + "x": 3.966839661801817, + "y": -139.22683719089193, + "z": 0.0 + }, + { + "x": 3.9663162775088447, + "y": -138.22475252854107, + "z": 0.0 + }, + { + "x": 3.965792893215873, + "y": -137.22266786619022, + "z": 0.0 + }, + { + "x": 3.965269508922901, + "y": -136.22058320383937, + "z": 0.0 + }, + { + "x": 3.9647461246299285, + "y": -135.21849854148851, + "z": 0.0 + }, + { + "x": 3.9642227403369565, + "y": -134.21641387913766, + "z": 0.0 + }, + { + "x": 3.9636993560439846, + "y": -133.21432921678678, + "z": 0.0 + }, + { + "x": 3.963175971751012, + "y": -132.21224455443593, + "z": 0.0 + }, + { + "x": 3.9626525874580403, + "y": -131.21015989208507, + "z": 0.0 + }, + { + "x": 3.9621292031650683, + "y": -130.20807522973422, + "z": 0.0 + }, + { + "x": 3.961605818872096, + "y": -129.20599056738337, + "z": 0.0 + }, + { + "x": 3.961082434579124, + "y": -128.2039059050325, + "z": 0.0 + }, + { + "x": 3.9605590502861516, + "y": -127.20182124268165, + "z": 0.0 + }, + { + "x": 3.9600356659931797, + "y": -126.19973658033079, + "z": 0.0 + }, + { + "x": 3.9595122817002077, + "y": -125.19765191797993, + "z": 0.0 + }, + { + "x": 3.9589888974072354, + "y": -124.19556725562907, + "z": 0.0 + }, + { + "x": 3.9584655131142634, + "y": -123.19348259327822, + "z": 0.0 + }, + { + "x": 3.9579421288212915, + "y": -122.19139793092735, + "z": 0.0 + }, + { + "x": 3.957418744528319, + "y": -121.18931326857648, + "z": 0.0 + }, + { + "x": 3.956895360235347, + "y": -120.18722860622564, + "z": 0.0 + }, + { + "x": 3.956371975942375, + "y": -119.18514394387479, + "z": 0.0 + }, + { + "x": 3.955848591649403, + "y": -118.18305928152392, + "z": 0.0 + }, + { + "x": 3.955325207356431, + "y": -117.18097461917306, + "z": 0.0 + }, + { + "x": 3.954801823063459, + "y": -116.1788899568222, + "z": 0.0 + }, + { + "x": 3.9542784387704866, + "y": -115.17680529447135, + "z": 0.0 + }, + { + "x": 3.9537550544775146, + "y": -114.1747206321205, + "z": 0.0 + }, + { + "x": 3.9532316701845422, + "y": -113.17263596976963, + "z": 0.0 + }, + { + "x": 3.9527082858915703, + "y": -112.17055130741878, + "z": 0.0 + }, + { + "x": 3.9521849015985984, + "y": -111.16846664506792, + "z": 0.0 + }, + { + "x": 3.951661517305626, + "y": -110.16638198271706, + "z": 0.0 + }, + { + "x": 3.951138133012654, + "y": -109.16429732036619, + "z": 0.0 + }, + { + "x": 3.950614748719682, + "y": -108.16221265801535, + "z": 0.0 + }, + { + "x": 3.9500913644267097, + "y": -107.1601279956645, + "z": 0.0 + }, + { + "x": 3.9495679801337378, + "y": -106.15804333331363, + "z": 0.0 + }, + { + "x": 3.949044595840766, + "y": -105.15595867096276, + "z": 0.0 + }, + { + "x": 3.9485212115477935, + "y": -104.15387400861191, + "z": 0.0 + }, + { + "x": 3.9479978272548215, + "y": -103.15178934626105, + "z": 0.0 + }, + { + "x": 3.9474744429618496, + "y": -102.1497046839102, + "z": 0.0 + }, + { + "x": 3.946951058668877, + "y": -101.14762002155933, + "z": 0.0 + }, + { + "x": 3.9464276743759052, + "y": -100.14553535920848, + "z": 0.0 + }, + { + "x": 3.945904290082933, + "y": -99.14345069685763, + "z": 0.0 + }, + { + "x": 3.945380905789961, + "y": -98.14136603450676, + "z": 0.0 + }, + { + "x": 3.944857521496989, + "y": -97.13928137215589, + "z": 0.0 + }, + { + "x": 3.9443341372040166, + "y": -96.13719670980505, + "z": 0.0 + }, + { + "x": 3.9438107529110447, + "y": -95.1351120474542, + "z": 0.0 + }, + { + "x": 3.9432873686180727, + "y": -94.13302738510333, + "z": 0.0 + }, + { + "x": 3.9427639843251003, + "y": -93.13094272275247, + "z": 0.0 + }, + { + "x": 3.9422406000321284, + "y": -92.12885806040161, + "z": 0.0 + }, + { + "x": 3.9417172157391565, + "y": -91.12677339805077, + "z": 0.0 + }, + { + "x": 3.941193831446184, + "y": -90.1246887356999, + "z": 0.0 + }, + { + "x": 3.940670447153212, + "y": -89.12260407334904, + "z": 0.0 + }, + { + "x": 3.94014706286024, + "y": -88.12051941099818, + "z": 0.0 + }, + { + "x": 3.9402771144979183, + "y": -87.12104091873772, + "z": 0.0 + }, + { + "x": 3.9406628622236557, + "y": -86.11997994611951, + "z": 0.0 + }, + { + "x": 3.9410488732983464, + "y": -85.1178952214353, + "z": 0.0 + }, + { + "x": 3.941434884373037, + "y": -84.11581049675112, + "z": 0.0 + }, + { + "x": 3.941820895447728, + "y": -83.11372577206693, + "z": 0.0 + }, + { + "x": 3.9422069065224186, + "y": -82.11164104738273, + "z": 0.0 + }, + { + "x": 3.942592917597109, + "y": -81.10955632269852, + "z": 0.0 + }, + { + "x": 3.9429789286717996, + "y": -80.10747159801434, + "z": 0.0 + }, + { + "x": 3.9433649397464903, + "y": -79.10538687333015, + "z": 0.0 + }, + { + "x": 3.943750950821181, + "y": -78.10330214864595, + "z": 0.0 + }, + { + "x": 3.9441369618958717, + "y": -77.10121742396174, + "z": 0.0 + }, + { + "x": 3.9445229729705624, + "y": -76.09913269927756, + "z": 0.0 + }, + { + "x": 3.9449089840452527, + "y": -75.09704797459337, + "z": 0.0 + }, + { + "x": 3.9452949951199434, + "y": -74.09496324990917, + "z": 0.0 + }, + { + "x": 3.945681006194634, + "y": -73.09287852522498, + "z": 0.0 + }, + { + "x": 3.946067017269325, + "y": -72.09079380054078, + "z": 0.0 + }, + { + "x": 3.9464530283440156, + "y": -71.08870907585658, + "z": 0.0 + }, + { + "x": 3.9468390394187063, + "y": -70.08662435117239, + "z": 0.0 + }, + { + "x": 3.9472250504933966, + "y": -69.0845396264882, + "z": 0.0 + }, + { + "x": 3.9476110615680873, + "y": -68.082454901804, + "z": 0.0 + }, + { + "x": 3.947997072642778, + "y": -67.0803701771198, + "z": 0.0 + }, + { + "x": 3.9483830837174687, + "y": -66.07828545243561, + "z": 0.0 + }, + { + "x": 3.9487690947921594, + "y": -65.07620072775141, + "z": 0.0 + }, + { + "x": 3.94915510586685, + "y": -64.07411600306722, + "z": 0.0 + }, + { + "x": 3.949541116941541, + "y": -63.072031278383015, + "z": 0.0 + }, + { + "x": 3.949927128016231, + "y": -62.06994655369882, + "z": 0.0 + }, + { + "x": 3.950313139090922, + "y": -61.067861829014625, + "z": 0.0 + }, + { + "x": 3.9506991501656126, + "y": -60.06577710433043, + "z": 0.0 + }, + { + "x": 3.9510851612403033, + "y": -59.063692379646234, + "z": 0.0 + }, + { + "x": 3.951471172314994, + "y": -58.06160765496204, + "z": 0.0 + }, + { + "x": 3.9518571833896847, + "y": -57.05952293027784, + "z": 0.0 + }, + { + "x": 3.952243194464375, + "y": -56.05743820559365, + "z": 0.0 + }, + { + "x": 3.9526292055390657, + "y": -55.055353480909446, + "z": 0.0 + }, + { + "x": 3.9530152166137564, + "y": -54.05326875622525, + "z": 0.0 + }, + { + "x": 3.953401227688447, + "y": -53.051184031541055, + "z": 0.0 + }, + { + "x": 3.953787238763138, + "y": -52.04909930685686, + "z": 0.0 + }, + { + "x": 3.9541732498378286, + "y": -51.047014582172665, + "z": 0.0 + }, + { + "x": 3.954559260912519, + "y": -50.04492985748847, + "z": 0.0 + }, + { + "x": 3.9549452719872096, + "y": -49.042845132804274, + "z": 0.0 + }, + { + "x": 3.9553312830619003, + "y": -48.04076040812008, + "z": 0.0 + }, + { + "x": 3.955717294136591, + "y": -47.03867568343588, + "z": 0.0 + }, + { + "x": 3.9561033052112817, + "y": -46.03659095875169, + "z": 0.0 + }, + { + "x": 3.9564893162859724, + "y": -45.03450623406749, + "z": 0.0 + }, + { + "x": 3.956875327360663, + "y": -44.0324215093833, + "z": 0.0 + }, + { + "x": 3.9572613384353534, + "y": -43.0303367846991, + "z": 0.0 + }, + { + "x": 3.957647349510044, + "y": -42.02825206001491, + "z": 0.0 + }, + { + "x": 3.958033360584735, + "y": -41.02616733533071, + "z": 0.0 + }, + { + "x": 3.9584193716594256, + "y": -40.02408261064652, + "z": 0.0 + }, + { + "x": 3.9588053827341163, + "y": -39.02199788596233, + "z": 0.0 + }, + { + "x": 3.959191393808807, + "y": -38.01991316127813, + "z": 0.0 + }, + { + "x": 3.9595774048834973, + "y": -37.017828436593945, + "z": 0.0 + }, + { + "x": 3.959963415958188, + "y": -36.01574371190975, + "z": 0.0 + }, + { + "x": 3.9603494270328787, + "y": -35.01365898722556, + "z": 0.0 + }, + { + "x": 3.9607354381075695, + "y": -34.011574262541366, + "z": 0.0 + }, + { + "x": 3.96112144918226, + "y": -33.00948953785717, + "z": 0.0 + }, + { + "x": 3.961507460256951, + "y": -32.00740481317298, + "z": 0.0 + }, + { + "x": 3.961893471331641, + "y": -31.00532008848879, + "z": 0.0 + }, + { + "x": 3.962279482406332, + "y": -30.003235363804595, + "z": 0.0 + }, + { + "x": 3.9626654934810226, + "y": -29.001150639120404, + "z": 0.0 + }, + { + "x": 3.9630515045557133, + "y": -27.999065914436212, + "z": 0.0 + }, + { + "x": 3.963437515630404, + "y": -26.99698118975202, + "z": 0.0 + }, + { + "x": 3.9638235267050947, + "y": -25.99489646506783, + "z": 0.0 + }, + { + "x": 3.964209537779785, + "y": -24.992811740383633, + "z": 0.0 + }, + { + "x": 3.9645955488544757, + "y": -23.99072701569944, + "z": 0.0 + }, + { + "x": 3.9649815599291665, + "y": -22.988642291015246, + "z": 0.0 + }, + { + "x": 3.965367571003857, + "y": -21.98655756633105, + "z": 0.0 + }, + { + "x": 3.965753582078548, + "y": -20.98447284164686, + "z": 0.0 + }, + { + "x": 3.9661395931532386, + "y": -19.982388116962664, + "z": 0.0 + }, + { + "x": 3.966525604227929, + "y": -18.98030339227847, + "z": 0.0 + }, + { + "x": 3.9669116153026196, + "y": -17.978218667594277, + "z": 0.0 + }, + { + "x": 3.9672976263773103, + "y": -16.97613394291008, + "z": 0.0 + }, + { + "x": 3.967683637452001, + "y": -15.97404921822589, + "z": 0.0 + }, + { + "x": 3.9680696485266918, + "y": -14.971964493541696, + "z": 0.0 + }, + { + "x": 3.9684556596013825, + "y": -13.969879768857503, + "z": 0.0 + }, + { + "x": 3.9688416706760727, + "y": -12.96779504417331, + "z": 0.0 + }, + { + "x": 3.9692276817507635, + "y": -11.965710319489116, + "z": 0.0 + }, + { + "x": 3.969613692825454, + "y": -10.963625594804922, + "z": 0.0 + }, + { + "x": 3.969999703900145, + "y": -9.961540870120729, + "z": 0.0 + } + ] + }, + { + "id": 76, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": -4.02999970255904, + "y": -9.958459206173217, + "z": 0.0 + }, + { + "x": -4.030385713633731, + "y": -10.96054393085741, + "z": 0.0 + }, + { + "x": -4.030771724708422, + "y": -11.962628655541604, + "z": 0.0 + }, + { + "x": -4.031157735783112, + "y": -12.964713380225797, + "z": 0.0 + }, + { + "x": -4.031543746857803, + "y": -13.96679810490999, + "z": 0.0 + }, + { + "x": -4.0319297579324935, + "y": -14.968882829594184, + "z": 0.0 + }, + { + "x": -4.032315769007185, + "y": -15.970967554278378, + "z": 0.0 + }, + { + "x": -4.032701780081875, + "y": -16.97305227896257, + "z": 0.0 + }, + { + "x": -4.033087791156565, + "y": -17.975137003646765, + "z": 0.0 + }, + { + "x": -4.033473802231256, + "y": -18.977221728330957, + "z": 0.0 + }, + { + "x": -4.033859813305947, + "y": -19.979306453015152, + "z": 0.0 + }, + { + "x": -4.034245824380638, + "y": -20.981391177699347, + "z": 0.0 + }, + { + "x": -4.034631835455328, + "y": -21.98347590238354, + "z": 0.0 + }, + { + "x": -4.035017846530018, + "y": -22.985560627067734, + "z": 0.0 + }, + { + "x": -4.0354038576047095, + "y": -23.98764535175193, + "z": 0.0 + }, + { + "x": -4.0357898686794, + "y": -24.98973007643612, + "z": 0.0 + }, + { + "x": -4.036175879754091, + "y": -25.991814801120317, + "z": 0.0 + }, + { + "x": -4.036561890828781, + "y": -26.99389952580451, + "z": 0.0 + }, + { + "x": -4.036947901903472, + "y": -27.9959842504887, + "z": 0.0 + }, + { + "x": -4.037333912978163, + "y": -28.99806897517289, + "z": 0.0 + }, + { + "x": -4.037719924052853, + "y": -30.000153699857083, + "z": 0.0 + }, + { + "x": -4.038105935127544, + "y": -31.00223842454128, + "z": 0.0 + }, + { + "x": -4.038491946202234, + "y": -32.00432314922547, + "z": 0.0 + }, + { + "x": -4.038877957276926, + "y": -33.00640787390966, + "z": 0.0 + }, + { + "x": -4.039263968351616, + "y": -34.008492598593854, + "z": 0.0 + }, + { + "x": -4.039649979426306, + "y": -35.01057732327805, + "z": 0.0 + }, + { + "x": -4.040035990500997, + "y": -36.01266204796224, + "z": 0.0 + }, + { + "x": -4.0404220015756875, + "y": -37.01474677264643, + "z": 0.0 + }, + { + "x": -4.040808012650379, + "y": -38.01683149733062, + "z": 0.0 + }, + { + "x": -4.041194023725069, + "y": -39.018916222014816, + "z": 0.0 + }, + { + "x": -4.04158003479976, + "y": -40.02100094669901, + "z": 0.0 + }, + { + "x": -4.04196604587445, + "y": -41.0230856713832, + "z": 0.0 + }, + { + "x": -4.042352056949141, + "y": -42.025170396067395, + "z": 0.0 + }, + { + "x": -4.042738068023832, + "y": -43.02725512075159, + "z": 0.0 + }, + { + "x": -4.043124079098522, + "y": -44.029339845435786, + "z": 0.0 + }, + { + "x": -4.043510090173213, + "y": -45.03142457011998, + "z": 0.0 + }, + { + "x": -4.0438961012479036, + "y": -46.033509294804176, + "z": 0.0 + }, + { + "x": -4.044282112322595, + "y": -47.03559401948837, + "z": 0.0 + }, + { + "x": -4.044668123397285, + "y": -48.03767874417257, + "z": 0.0 + }, + { + "x": -4.045054134471975, + "y": -49.03976346885676, + "z": 0.0 + }, + { + "x": -4.045440145546666, + "y": -50.04184819354096, + "z": 0.0 + }, + { + "x": -4.045826156621357, + "y": -51.04393291822515, + "z": 0.0 + }, + { + "x": -4.046212167696048, + "y": -52.04601764290935, + "z": 0.0 + }, + { + "x": -4.046598178770738, + "y": -53.04810236759354, + "z": 0.0 + }, + { + "x": -4.046984189845428, + "y": -54.05018709227774, + "z": 0.0 + }, + { + "x": -4.04737020092012, + "y": -55.052271816961934, + "z": 0.0 + }, + { + "x": -4.04775621199481, + "y": -56.054356541646136, + "z": 0.0 + }, + { + "x": -4.048142223069501, + "y": -57.05644126633033, + "z": 0.0 + }, + { + "x": -4.048528234144191, + "y": -58.05852599101453, + "z": 0.0 + }, + { + "x": -4.048914245218882, + "y": -59.06061071569872, + "z": 0.0 + }, + { + "x": -4.049300256293573, + "y": -60.06269544038292, + "z": 0.0 + }, + { + "x": -4.049686267368263, + "y": -61.06478016506711, + "z": 0.0 + }, + { + "x": -4.050072278442954, + "y": -62.06686488975131, + "z": 0.0 + }, + { + "x": -4.050458289517644, + "y": -63.0689496144355, + "z": 0.0 + }, + { + "x": -4.050844300592336, + "y": -64.0710343391197, + "z": 0.0 + }, + { + "x": -4.051230311667026, + "y": -65.0731190638039, + "z": 0.0 + }, + { + "x": -4.051616322741716, + "y": -66.0752037884881, + "z": 0.0 + }, + { + "x": -4.052002333816407, + "y": -67.07728851317229, + "z": 0.0 + }, + { + "x": -4.052388344891098, + "y": -68.07937323785649, + "z": 0.0 + }, + { + "x": -4.052774355965789, + "y": -69.08145796254068, + "z": 0.0 + }, + { + "x": -4.053160367040479, + "y": -70.08354268722488, + "z": 0.0 + }, + { + "x": -4.05354637811517, + "y": -71.08562741190907, + "z": 0.0 + }, + { + "x": -4.05393238918986, + "y": -72.08771213659327, + "z": 0.0 + }, + { + "x": -4.054318400264551, + "y": -73.08979686127746, + "z": 0.0 + }, + { + "x": -4.054704411339242, + "y": -74.09188158596166, + "z": 0.0 + }, + { + "x": -4.055090422413932, + "y": -75.09396631064585, + "z": 0.0 + }, + { + "x": -4.055476433488623, + "y": -76.09605103533005, + "z": 0.0 + }, + { + "x": -4.055862444563314, + "y": -77.09813576001423, + "z": 0.0 + }, + { + "x": -4.056248455638005, + "y": -78.10022048469844, + "z": 0.0 + }, + { + "x": -4.056634466712695, + "y": -79.10230520938264, + "z": 0.0 + }, + { + "x": -4.057020477787385, + "y": -80.10438993406683, + "z": 0.0 + }, + { + "x": -4.057406488862076, + "y": -81.10647465875101, + "z": 0.0 + }, + { + "x": -4.057792499936767, + "y": -82.10855938343522, + "z": 0.0 + }, + { + "x": -4.058178511011458, + "y": -83.11064410811942, + "z": 0.0 + }, + { + "x": -4.058564522086148, + "y": -84.11272883280361, + "z": 0.0 + }, + { + "x": -4.058950533160838, + "y": -85.11481355748779, + "z": 0.0 + }, + { + "x": -4.05933654423553, + "y": -86.116898282172, + "z": 0.0 + }, + { + "x": -4.059722818659173, + "y": -87.12000675892219, + "z": 0.0 + }, + { + "x": -4.059851845969695, + "y": -88.12469777429422, + "z": 0.0 + }, + { + "x": -4.059328461676723, + "y": -89.12678243664507, + "z": 0.0 + }, + { + "x": -4.058805077383751, + "y": -90.12886709899594, + "z": 0.0 + }, + { + "x": -4.0582816930907795, + "y": -91.13095176134681, + "z": 0.0 + }, + { + "x": -4.0577583087978075, + "y": -92.13303642369765, + "z": 0.0 + }, + { + "x": -4.057234924504835, + "y": -93.1351210860485, + "z": 0.0 + }, + { + "x": -4.056711540211863, + "y": -94.13720574839937, + "z": 0.0 + }, + { + "x": -4.056188155918891, + "y": -95.13929041075023, + "z": 0.0 + }, + { + "x": -4.055664771625919, + "y": -96.14137507310109, + "z": 0.0 + }, + { + "x": -4.055141387332947, + "y": -97.14345973545193, + "z": 0.0 + }, + { + "x": -4.054618003039974, + "y": -98.1455443978028, + "z": 0.0 + }, + { + "x": -4.054094618747002, + "y": -99.14762906015366, + "z": 0.0 + }, + { + "x": -4.05357123445403, + "y": -100.14971372250452, + "z": 0.0 + }, + { + "x": -4.053047850161058, + "y": -101.15179838485537, + "z": 0.0 + }, + { + "x": -4.052524465868086, + "y": -102.15388304720624, + "z": 0.0 + }, + { + "x": -4.052001081575114, + "y": -103.15596770955709, + "z": 0.0 + }, + { + "x": -4.051477697282142, + "y": -104.15805237190794, + "z": 0.0 + }, + { + "x": -4.05095431298917, + "y": -105.1601370342588, + "z": 0.0 + }, + { + "x": -4.050430928696198, + "y": -106.16222169660966, + "z": 0.0 + }, + { + "x": -4.049907544403226, + "y": -107.16430635896053, + "z": 0.0 + }, + { + "x": -4.049384160110254, + "y": -108.16639102131138, + "z": 0.0 + }, + { + "x": -4.048860775817281, + "y": -109.16847568366222, + "z": 0.0 + }, + { + "x": -4.048337391524309, + "y": -110.17056034601309, + "z": 0.0 + }, + { + "x": -4.047814007231337, + "y": -111.17264500836396, + "z": 0.0 + }, + { + "x": -4.047290622938365, + "y": -112.17472967071481, + "z": 0.0 + }, + { + "x": -4.046767238645393, + "y": -113.17681433306566, + "z": 0.0 + }, + { + "x": -4.046243854352421, + "y": -114.17889899541653, + "z": 0.0 + }, + { + "x": -4.0457204700594485, + "y": -115.18098365776738, + "z": 0.0 + }, + { + "x": -4.0451970857664765, + "y": -116.18306832011824, + "z": 0.0 + }, + { + "x": -4.044673701473505, + "y": -117.18515298246909, + "z": 0.0 + }, + { + "x": -4.044150317180533, + "y": -118.18723764481996, + "z": 0.0 + }, + { + "x": -4.043626932887561, + "y": -119.18932230717083, + "z": 0.0 + }, + { + "x": -4.043103548594589, + "y": -120.19140696952168, + "z": 0.0 + }, + { + "x": -4.042580164301616, + "y": -121.19349163187252, + "z": 0.0 + }, + { + "x": -4.042056780008644, + "y": -122.19557629422339, + "z": 0.0 + }, + { + "x": -4.041533395715672, + "y": -123.19766095657425, + "z": 0.0 + }, + { + "x": -4.0410100114227, + "y": -124.1997456189251, + "z": 0.0 + }, + { + "x": -4.040486627129728, + "y": -125.20183028127596, + "z": 0.0 + }, + { + "x": -4.039963242836755, + "y": -126.20391494362683, + "z": 0.0 + }, + { + "x": -4.039439858543783, + "y": -127.20599960597768, + "z": 0.0 + }, + { + "x": -4.0389164742508115, + "y": -128.20808426832855, + "z": 0.0 + }, + { + "x": -4.0383930899578395, + "y": -129.2101689306794, + "z": 0.0 + }, + { + "x": -4.037869705664868, + "y": -130.21225359303025, + "z": 0.0 + }, + { + "x": -4.037346321371896, + "y": -131.2143382553811, + "z": 0.0 + }, + { + "x": -4.036822937078923, + "y": -132.21642291773196, + "z": 0.0 + }, + { + "x": -4.036299552785951, + "y": -133.21850758008281, + "z": 0.0 + }, + { + "x": -4.035776168492979, + "y": -134.2205922424337, + "z": 0.0 + }, + { + "x": -4.035252784200007, + "y": -135.22267690478455, + "z": 0.0 + }, + { + "x": -4.034729399907035, + "y": -136.2247615671354, + "z": 0.0 + }, + { + "x": -4.034206015614062, + "y": -137.22684622948626, + "z": 0.0 + }, + { + "x": -4.03368263132109, + "y": -138.2289308918371, + "z": 0.0 + }, + { + "x": -4.033159247028118, + "y": -139.23101555418796, + "z": 0.0 + }, + { + "x": -4.032635862735146, + "y": -140.23310021653882, + "z": 0.0 + }, + { + "x": -4.0321124784421745, + "y": -141.23518487888964, + "z": 0.0 + }, + { + "x": -4.0315890941492025, + "y": -142.2372695412405, + "z": 0.0 + }, + { + "x": -4.03106570985623, + "y": -143.23935420359135, + "z": 0.0 + }, + { + "x": -4.030542325563258, + "y": -144.24143886594217, + "z": 0.0 + }, + { + "x": -4.030018941270286, + "y": -145.243523528293, + "z": 0.0 + }, + { + "x": -4.029495556977314, + "y": -146.24560819064385, + "z": 0.0 + }, + { + "x": -4.028972172684342, + "y": -147.2476928529947, + "z": 0.0 + }, + { + "x": -4.02844878839137, + "y": -148.24977751534556, + "z": 0.0 + }, + { + "x": -4.027925404098397, + "y": -149.25186217769638, + "z": 0.0 + }, + { + "x": -4.027402019805425, + "y": -150.25394684004723, + "z": 0.0 + }, + { + "x": -4.026878635512453, + "y": -151.2560315023981, + "z": 0.0 + }, + { + "x": -4.026355251219481, + "y": -152.2581161647489, + "z": 0.0 + }, + { + "x": -4.025831866926509, + "y": -153.26020082709977, + "z": 0.0 + }, + { + "x": -4.0253084826335375, + "y": -154.26228548945062, + "z": 0.0 + }, + { + "x": -4.024785098340565, + "y": -155.26437015180147, + "z": 0.0 + }, + { + "x": -4.024261714047593, + "y": -156.26645481415233, + "z": 0.0 + }, + { + "x": -4.023738329754621, + "y": -157.26853947650312, + "z": 0.0 + }, + { + "x": -4.023214945461649, + "y": -158.27062413885398, + "z": 0.0 + }, + { + "x": -4.022691561168677, + "y": -159.27270880120483, + "z": 0.0 + }, + { + "x": -4.022168176875705, + "y": -160.27479346355565, + "z": 0.0 + }, + { + "x": -4.021644792582732, + "y": -161.2768781259065, + "z": 0.0 + }, + { + "x": -4.02112140828976, + "y": -162.27896278825736, + "z": 0.0 + }, + { + "x": -4.020598023996788, + "y": -163.2810474506082, + "z": 0.0 + }, + { + "x": -4.020074639703816, + "y": -164.28313211295907, + "z": 0.0 + }, + { + "x": -4.019551255410844, + "y": -165.2852167753099, + "z": 0.0 + }, + { + "x": -4.019027871117872, + "y": -166.28730143766074, + "z": 0.0 + }, + { + "x": -4.0185044868249, + "y": -167.2893861000116, + "z": 0.0 + }, + { + "x": -4.017981102531928, + "y": -168.29147076236242, + "z": 0.0 + }, + { + "x": -4.017457718238956, + "y": -169.29355542471328, + "z": 0.0 + }, + { + "x": -4.016934333945984, + "y": -170.2956400870641, + "z": 0.0 + }, + { + "x": -4.016410949653012, + "y": -171.29772474941495, + "z": 0.0 + }, + { + "x": -4.015887565360039, + "y": -172.2998094117658, + "z": 0.0 + }, + { + "x": -4.015364181067067, + "y": -173.30189407411663, + "z": 0.0 + }, + { + "x": -4.014840796774095, + "y": -174.30397873646749, + "z": 0.0 + }, + { + "x": -4.014317412481123, + "y": -175.30606339881834, + "z": 0.0 + }, + { + "x": -4.013794028188151, + "y": -176.30814806116916, + "z": 0.0 + }, + { + "x": -4.013270643895179, + "y": -177.31023272352002, + "z": 0.0 + }, + { + "x": -4.0127472596022065, + "y": -178.31231738587087, + "z": 0.0 + }, + { + "x": -4.0122238753092345, + "y": -179.31440204822172, + "z": 0.0 + }, + { + "x": -4.011700491016263, + "y": -180.31648671057258, + "z": 0.0 + }, + { + "x": -4.011177106723291, + "y": -181.3185713729234, + "z": 0.0 + }, + { + "x": -4.010653722430319, + "y": -182.32065603527423, + "z": 0.0 + }, + { + "x": -4.010130338137347, + "y": -183.32274069762508, + "z": 0.0 + }, + { + "x": -4.009606953844374, + "y": -184.3248253599759, + "z": 0.0 + }, + { + "x": -4.009083569551402, + "y": -185.32691002232676, + "z": 0.0 + }, + { + "x": -4.00856018525843, + "y": -186.3289946846776, + "z": 0.0 + }, + { + "x": -4.008036800965458, + "y": -187.33107934702846, + "z": 0.0 + }, + { + "x": -4.007513416672486, + "y": -188.33316400937932, + "z": 0.0 + }, + { + "x": -4.006990032379514, + "y": -189.33524867173014, + "z": 0.0 + }, + { + "x": -4.006466648086541, + "y": -190.337333334081, + "z": 0.0 + }, + { + "x": -4.0059432637935695, + "y": -191.33941799643185, + "z": 0.0 + }, + { + "x": -4.0054198795005975, + "y": -192.34150265878267, + "z": 0.0 + }, + { + "x": -4.004896495207626, + "y": -193.34358732113353, + "z": 0.0 + }, + { + "x": -4.004373110914654, + "y": -194.34567198348438, + "z": 0.0 + }, + { + "x": -4.003849726621682, + "y": -195.3477566458352, + "z": 0.0 + }, + { + "x": -4.003326342328709, + "y": -196.34984130818606, + "z": 0.0 + }, + { + "x": -4.002802958035737, + "y": -197.35192597053688, + "z": 0.0 + }, + { + "x": -4.002279573742765, + "y": -198.35401063288774, + "z": 0.0 + }, + { + "x": -4.001756189449793, + "y": -199.3560952952386, + "z": 0.0 + }, + { + "x": -4.001232805156821, + "y": -200.3581799575894, + "z": 0.0 + }, + { + "x": -4.000709420863849, + "y": -201.36026461994027, + "z": 0.0 + }, + { + "x": -4.000186036570876, + "y": -202.36234928229112, + "z": 0.0 + }, + { + "x": -3.9996626522779044, + "y": -203.36443394464197, + "z": 0.0 + }, + { + "x": -3.9991392679849325, + "y": -204.36651860699283, + "z": 0.0 + }, + { + "x": -3.9986158836919605, + "y": -205.36860326934365, + "z": 0.0 + }, + { + "x": -3.998092499398988, + "y": -206.3706879316945, + "z": 0.0 + }, + { + "x": -3.997569115106016, + "y": -207.37277259404533, + "z": 0.0 + }, + { + "x": -3.9970457308130443, + "y": -208.37485725639615, + "z": 0.0 + }, + { + "x": -3.996522346520072, + "y": -209.376941918747, + "z": 0.0 + }, + { + "x": -3.9959989622271, + "y": -210.37902658109786, + "z": 0.0 + }, + { + "x": -3.995475577934128, + "y": -211.38111124344871, + "z": 0.0 + }, + { + "x": -3.9949521936411556, + "y": -212.38319590579957, + "z": 0.0 + }, + { + "x": -3.9944288093481837, + "y": -213.3852805681504, + "z": 0.0 + }, + { + "x": -3.9939054250552117, + "y": -214.38736523050125, + "z": 0.0 + }, + { + "x": -3.9933820407622393, + "y": -215.3894498928521, + "z": 0.0 + }, + { + "x": -3.9928586564692674, + "y": -216.39153455520292, + "z": 0.0 + }, + { + "x": -3.9923352721762955, + "y": -217.39361921755378, + "z": 0.0 + }, + { + "x": -3.991811887883323, + "y": -218.39570387990463, + "z": 0.0 + }, + { + "x": -3.991288503590351, + "y": -219.39778854225548, + "z": 0.0 + }, + { + "x": -3.990765119297379, + "y": -220.3998732046063, + "z": 0.0 + }, + { + "x": -3.990241735004407, + "y": -221.40195786695713, + "z": 0.0 + }, + { + "x": -3.989718350711435, + "y": -222.404042529308, + "z": 0.0 + }, + { + "x": -3.989194966418463, + "y": -223.40612719165884, + "z": 0.0 + }, + { + "x": -3.9886715821254906, + "y": -224.40821185400966, + "z": 0.0 + }, + { + "x": -3.9881481978325186, + "y": -225.41029651636052, + "z": 0.0 + }, + { + "x": -3.9876248135395467, + "y": -226.41238117871137, + "z": 0.0 + }, + { + "x": -3.9871014292465743, + "y": -227.41446584106222, + "z": 0.0 + }, + { + "x": -3.9865780449536024, + "y": -228.41655050341308, + "z": 0.0 + }, + { + "x": -3.9860546606606304, + "y": -229.4186351657639, + "z": 0.0 + }, + { + "x": -3.985531276367658, + "y": -230.42071982811476, + "z": 0.0 + }, + { + "x": -3.985007892074686, + "y": -231.4228044904656, + "z": 0.0 + }, + { + "x": -3.984484507781714, + "y": -232.4248891528164, + "z": 0.0 + }, + { + "x": -3.9839611234887418, + "y": -233.42697381516726, + "z": 0.0 + }, + { + "x": -3.98343773919577, + "y": -234.4290584775181, + "z": 0.0 + }, + { + "x": -3.9829143549027974, + "y": -235.43114313986896, + "z": 0.0 + }, + { + "x": -3.9823909706098255, + "y": -236.43322780221982, + "z": 0.0 + }, + { + "x": -3.9818675863168536, + "y": -237.43531246457064, + "z": 0.0 + }, + { + "x": -3.981344202023881, + "y": -238.4373971269215, + "z": 0.0 + }, + { + "x": -3.9808208177309092, + "y": -239.43948178927235, + "z": 0.0 + }, + { + "x": -3.980297862737418, + "y": -240.44044178249482, + "z": 0.0 + }, + { + "x": -3.9800563141031873, + "y": -241.4410397436067, + "z": 0.0 + }, + { + "x": -3.9801871516298046, + "y": -242.44312453409674, + "z": 0.0 + }, + { + "x": -3.980317989156422, + "y": -243.44520932458678, + "z": 0.0 + }, + { + "x": -3.9804488266830402, + "y": -244.44729411507683, + "z": 0.0 + }, + { + "x": -3.980579664209657, + "y": -245.44937890556685, + "z": 0.0 + }, + { + "x": -3.9807105017362754, + "y": -246.4514636960569, + "z": 0.0 + }, + { + "x": -3.9808413392628927, + "y": -247.45354848654696, + "z": 0.0 + }, + { + "x": -3.980972176789509, + "y": -248.45563327703698, + "z": 0.0 + }, + { + "x": -3.981103014316127, + "y": -249.45771806752703, + "z": 0.0 + }, + { + "x": -3.9812338518427444, + "y": -250.45980285801707, + "z": 0.0 + }, + { + "x": -3.9813646893693617, + "y": -251.46188764850712, + "z": 0.0 + }, + { + "x": -3.98149552689598, + "y": -252.46397243899716, + "z": 0.0 + }, + { + "x": -3.981626364422597, + "y": -253.46605722948718, + "z": 0.0 + }, + { + "x": -3.9817572019492142, + "y": -254.46814201997722, + "z": 0.0 + }, + { + "x": -3.9818880394758316, + "y": -255.47022681046727, + "z": 0.0 + }, + { + "x": -3.98201887700245, + "y": -256.47231160095725, + "z": 0.0 + }, + { + "x": -3.9821497145290676, + "y": -257.4743963914473, + "z": 0.0 + }, + { + "x": -3.982280552055685, + "y": -258.4764811819374, + "z": 0.0 + }, + { + "x": -3.9824113895823023, + "y": -259.4785659724274, + "z": 0.0 + }, + { + "x": -3.98254222710892, + "y": -260.4806507629175, + "z": 0.0 + }, + { + "x": -3.9826730646355375, + "y": -261.4827355534075, + "z": 0.0 + }, + { + "x": -3.982803902162155, + "y": -262.4848203438975, + "z": 0.0 + }, + { + "x": -3.982934739688772, + "y": -263.4869051343876, + "z": 0.0 + }, + { + "x": -3.98306557721539, + "y": -264.4889899248776, + "z": 0.0 + }, + { + "x": -3.9831964147420074, + "y": -265.49107471536763, + "z": 0.0 + }, + { + "x": -3.9833272522686247, + "y": -266.4931595058577, + "z": 0.0 + }, + { + "x": -3.983458089795242, + "y": -267.4952442963478, + "z": 0.0 + }, + { + "x": -3.98358892732186, + "y": -268.49732908683785, + "z": 0.0 + }, + { + "x": -3.9837197648484772, + "y": -269.4994138773279, + "z": 0.0 + }, + { + "x": -3.9838506023750946, + "y": -270.501498667818, + "z": 0.0 + }, + { + "x": -3.983981439901712, + "y": -271.503583458308, + "z": 0.0 + }, + { + "x": -3.9841122774283297, + "y": -272.5056682487981, + "z": 0.0 + }, + { + "x": -3.984243114954947, + "y": -273.50775303928816, + "z": 0.0 + }, + { + "x": -3.9843739524815645, + "y": -274.50983782977823, + "z": 0.0 + }, + { + "x": -3.9845047900081823, + "y": -275.5119226202683, + "z": 0.0 + }, + { + "x": -3.9846356275347996, + "y": -276.5140074107584, + "z": 0.0 + }, + { + "x": -3.984766465061417, + "y": -277.51609220124845, + "z": 0.0 + }, + { + "x": -3.9848973025880343, + "y": -278.51817699173847, + "z": 0.0 + }, + { + "x": -3.985028140114652, + "y": -279.52026178222854, + "z": 0.0 + }, + { + "x": -3.9851589776412695, + "y": -280.5223465727186, + "z": 0.0 + }, + { + "x": -3.985289815167887, + "y": -281.5244313632087, + "z": 0.0 + }, + { + "x": -3.985420652694504, + "y": -282.52651615369876, + "z": 0.0 + }, + { + "x": -3.985551490221122, + "y": -283.52860094418884, + "z": 0.0 + }, + { + "x": -3.9856823277477393, + "y": -284.5306857346789, + "z": 0.0 + }, + { + "x": -3.9858131652743567, + "y": -285.532770525169, + "z": 0.0 + }, + { + "x": -3.9859440028009745, + "y": -286.53485531565906, + "z": 0.0 + }, + { + "x": -3.986074840327592, + "y": -287.53694010614913, + "z": 0.0 + }, + { + "x": -3.986205677854209, + "y": -288.5390248966392, + "z": 0.0 + }, + { + "x": -3.9863365153808266, + "y": -289.5411096871293, + "z": 0.0 + }, + { + "x": -3.9864673529074444, + "y": -290.54319447761935, + "z": 0.0 + }, + { + "x": -3.9865981904340617, + "y": -291.5452792681094, + "z": 0.0 + }, + { + "x": -3.986729027960679, + "y": -292.5473640585995, + "z": 0.0 + }, + { + "x": -3.9868598654872964, + "y": -293.5494488490896, + "z": 0.0 + }, + { + "x": -3.9869907030139142, + "y": -294.5515336395796, + "z": 0.0 + }, + { + "x": -3.9871215405405316, + "y": -295.55361843006966, + "z": 0.0 + }, + { + "x": -3.987252378067149, + "y": -296.55570322055974, + "z": 0.0 + }, + { + "x": -3.9873832155937663, + "y": -297.5577880110498, + "z": 0.0 + }, + { + "x": -3.987514053120384, + "y": -298.5598728015399, + "z": 0.0 + }, + { + "x": -3.9876448906470014, + "y": -299.56195759202996, + "z": 0.0 + }, + { + "x": -3.987775728173619, + "y": -300.56404238252003, + "z": 0.0 + }, + { + "x": -3.9879065657002366, + "y": -301.5661271730101, + "z": 0.0 + }, + { + "x": -3.988037403226854, + "y": -302.5682119635002, + "z": 0.0 + }, + { + "x": -3.9881682407534713, + "y": -303.5702967539902, + "z": 0.0 + }, + { + "x": -3.9882990782800887, + "y": -304.57238154448027, + "z": 0.0 + }, + { + "x": -3.9884299158067065, + "y": -305.57446633497034, + "z": 0.0 + }, + { + "x": -3.988560753333324, + "y": -306.5765511254604, + "z": 0.0 + }, + { + "x": -3.988691590859941, + "y": -307.5786359159505, + "z": 0.0 + }, + { + "x": -3.9888224283865585, + "y": -308.58072070644056, + "z": 0.0 + }, + { + "x": -3.9889532659131763, + "y": -309.58280549693063, + "z": 0.0 + }, + { + "x": -3.9890841034397937, + "y": -310.58489028742065, + "z": 0.0 + }, + { + "x": -3.989214940966411, + "y": -311.5869750779107, + "z": 0.0 + }, + { + "x": -3.989345778493029, + "y": -312.5890598684008, + "z": 0.0 + }, + { + "x": -3.989476616019646, + "y": -313.59114465889087, + "z": 0.0 + }, + { + "x": -3.9896074535462636, + "y": -314.59322944938094, + "z": 0.0 + }, + { + "x": -3.989738291072881, + "y": -315.595314239871, + "z": 0.0 + }, + { + "x": -3.9898691285994987, + "y": -316.5973990303611, + "z": 0.0 + }, + { + "x": -3.989999966126116, + "y": -317.59948382085116, + "z": 0.0 + } + ] + }, + { + "id": 77, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": -0.029999999329447746, + "y": -9.960000038146973, + "z": 0.0 + }, + { + "x": -0.030386010404138394, + "y": -10.962084762831166, + "z": 0.0 + }, + { + "x": -0.030772021478829036, + "y": -11.96416948751536, + "z": 0.0 + }, + { + "x": -0.031158032553519684, + "y": -12.966254212199553, + "z": 0.0 + }, + { + "x": -0.031544043628210325, + "y": -13.968338936883747, + "z": 0.0 + }, + { + "x": -0.03193005470290097, + "y": -14.97042366156794, + "z": 0.0 + }, + { + "x": -0.03231606577759162, + "y": -15.972508386252134, + "z": 0.0 + }, + { + "x": -0.03270207685228226, + "y": -16.974593110936326, + "z": 0.0 + }, + { + "x": -0.03308808792697291, + "y": -17.97667783562052, + "z": 0.0 + }, + { + "x": -0.03347409900166356, + "y": -18.978762560304713, + "z": 0.0 + }, + { + "x": -0.033860110076354206, + "y": -19.980847284988908, + "z": 0.0 + }, + { + "x": -0.03424612115104485, + "y": -20.982932009673103, + "z": 0.0 + }, + { + "x": -0.034632132225735496, + "y": -21.985016734357295, + "z": 0.0 + }, + { + "x": -0.035018143300426144, + "y": -22.98710145904149, + "z": 0.0 + }, + { + "x": -0.035404154375116785, + "y": -23.989186183725685, + "z": 0.0 + }, + { + "x": -0.03579016544980743, + "y": -24.991270908409877, + "z": 0.0 + }, + { + "x": -0.03617617652449808, + "y": -25.993355633094072, + "z": 0.0 + }, + { + "x": -0.03656218759918872, + "y": -26.995440357778264, + "z": 0.0 + }, + { + "x": -0.03694819867387937, + "y": -27.997525082462456, + "z": 0.0 + }, + { + "x": -0.03733420974857001, + "y": -28.999609807146648, + "z": 0.0 + }, + { + "x": -0.03772022082326066, + "y": -30.00169453183084, + "z": 0.0 + }, + { + "x": -0.0381062318979513, + "y": -31.003779256515035, + "z": 0.0 + }, + { + "x": -0.03849224297264195, + "y": -32.00586398119923, + "z": 0.0 + }, + { + "x": -0.03887825404733259, + "y": -33.007948705883415, + "z": 0.0 + }, + { + "x": -0.03926426512202324, + "y": -34.01003343056761, + "z": 0.0 + }, + { + "x": -0.039650276196713886, + "y": -35.012118155251805, + "z": 0.0 + }, + { + "x": -0.04003628727140453, + "y": -36.01420287993599, + "z": 0.0 + }, + { + "x": -0.040422298346095176, + "y": -37.01628760462019, + "z": 0.0 + }, + { + "x": -0.040808309420785824, + "y": -38.01837232930438, + "z": 0.0 + }, + { + "x": -0.041194320495476465, + "y": -39.02045705398857, + "z": 0.0 + }, + { + "x": -0.04158033157016711, + "y": -40.02254177867277, + "z": 0.0 + }, + { + "x": -0.041966342644857754, + "y": -41.024626503356956, + "z": 0.0 + }, + { + "x": -0.0423523537195484, + "y": -42.02671122804115, + "z": 0.0 + }, + { + "x": -0.04273836479423905, + "y": -43.028795952725346, + "z": 0.0 + }, + { + "x": -0.04312437586892969, + "y": -44.03088067740954, + "z": 0.0 + }, + { + "x": -0.04351038694362034, + "y": -45.03296540209374, + "z": 0.0 + }, + { + "x": -0.04389639801831098, + "y": -46.03505012677793, + "z": 0.0 + }, + { + "x": -0.04428240909300163, + "y": -47.03713485146213, + "z": 0.0 + }, + { + "x": -0.04466842016769228, + "y": -48.03921957614632, + "z": 0.0 + }, + { + "x": -0.045054431242382925, + "y": -49.04130430083052, + "z": 0.0 + }, + { + "x": -0.04544044231707357, + "y": -50.04338902551471, + "z": 0.0 + }, + { + "x": -0.04582645339176422, + "y": -51.04547375019891, + "z": 0.0 + }, + { + "x": -0.04621246446645486, + "y": -52.047558474883104, + "z": 0.0 + }, + { + "x": -0.04659847554114551, + "y": -53.0496431995673, + "z": 0.0 + }, + { + "x": -0.04698448661583616, + "y": -54.051727924251495, + "z": 0.0 + }, + { + "x": -0.0473704976905268, + "y": -55.05381264893569, + "z": 0.0 + }, + { + "x": -0.04775650876521745, + "y": -56.05589737361989, + "z": 0.0 + }, + { + "x": -0.0481425198399081, + "y": -57.05798209830409, + "z": 0.0 + }, + { + "x": -0.048528530914598744, + "y": -58.06006682298828, + "z": 0.0 + }, + { + "x": -0.04891454198928939, + "y": -59.06215154767248, + "z": 0.0 + }, + { + "x": -0.04930055306398004, + "y": -60.06423627235667, + "z": 0.0 + }, + { + "x": -0.04968656413867068, + "y": -61.06632099704087, + "z": 0.0 + }, + { + "x": -0.05007257521336133, + "y": -62.068405721725064, + "z": 0.0 + }, + { + "x": -0.05045858628805198, + "y": -63.07049044640926, + "z": 0.0 + }, + { + "x": -0.050844597362742626, + "y": -64.07257517109346, + "z": 0.0 + }, + { + "x": -0.051230608437433274, + "y": -65.07465989577766, + "z": 0.0 + }, + { + "x": -0.05161661951212392, + "y": -66.07674462046185, + "z": 0.0 + }, + { + "x": -0.05200263058681456, + "y": -67.07882934514605, + "z": 0.0 + }, + { + "x": -0.05238864166150521, + "y": -68.08091406983024, + "z": 0.0 + }, + { + "x": -0.05277465273619586, + "y": -69.08299879451444, + "z": 0.0 + }, + { + "x": -0.0531606638108865, + "y": -70.08508351919863, + "z": 0.0 + }, + { + "x": -0.05354667488557715, + "y": -71.08716824388283, + "z": 0.0 + }, + { + "x": -0.0539326859602678, + "y": -72.08925296856702, + "z": 0.0 + }, + { + "x": -0.05431869703495844, + "y": -73.09133769325122, + "z": 0.0 + }, + { + "x": -0.054704708109649086, + "y": -74.09342241793541, + "z": 0.0 + }, + { + "x": -0.055090719184339734, + "y": -75.09550714261961, + "z": 0.0 + }, + { + "x": -0.05547673025903038, + "y": -76.0975918673038, + "z": 0.0 + }, + { + "x": -0.05586274133372103, + "y": -77.09967659198799, + "z": 0.0 + }, + { + "x": -0.05624875240841167, + "y": -78.1017613166722, + "z": 0.0 + }, + { + "x": -0.05663476348310232, + "y": -79.10384604135639, + "z": 0.0 + }, + { + "x": -0.05702077455779297, + "y": -80.10593076604059, + "z": 0.0 + }, + { + "x": -0.05740678563248361, + "y": -81.10801549072477, + "z": 0.0 + }, + { + "x": -0.05779279670717426, + "y": -82.11010021540898, + "z": 0.0 + }, + { + "x": -0.05817880778186491, + "y": -83.11218494009317, + "z": 0.0 + }, + { + "x": -0.05856481885655555, + "y": -84.11426966477737, + "z": 0.0 + }, + { + "x": -0.058950829931246194, + "y": -85.11635438946155, + "z": 0.0 + }, + { + "x": -0.05933684100593684, + "y": -86.11843911414576, + "z": 0.0 + }, + { + "x": -0.05972285208062749, + "y": -87.12052383882995, + "z": 0.0 + }, + { + "x": -0.059852391554727755, + "y": -88.1226085926462, + "z": 0.0 + }, + { + "x": -0.05932900726175566, + "y": -89.12469325499706, + "z": 0.0 + }, + { + "x": -0.058805622968783555, + "y": -90.12677791734792, + "z": 0.0 + }, + { + "x": -0.058282238675811456, + "y": -91.12886257969879, + "z": 0.0 + }, + { + "x": -0.05775885438283937, + "y": -92.13094724204963, + "z": 0.0 + }, + { + "x": -0.05723547008986727, + "y": -93.13303190440048, + "z": 0.0 + }, + { + "x": -0.05671208579689516, + "y": -94.13511656675135, + "z": 0.0 + }, + { + "x": -0.05618870150392306, + "y": -95.13720122910222, + "z": 0.0 + }, + { + "x": -0.055665317210950964, + "y": -96.13928589145307, + "z": 0.0 + }, + { + "x": -0.05514193291797887, + "y": -97.14137055380391, + "z": 0.0 + }, + { + "x": -0.054618548625006764, + "y": -98.14345521615478, + "z": 0.0 + }, + { + "x": -0.05409516433203466, + "y": -99.14553987850564, + "z": 0.0 + }, + { + "x": -0.053571780039062565, + "y": -100.1476245408565, + "z": 0.0 + }, + { + "x": -0.05304839574609047, + "y": -101.14970920320735, + "z": 0.0 + }, + { + "x": -0.05252501145311837, + "y": -102.15179386555822, + "z": 0.0 + }, + { + "x": -0.05200162716014627, + "y": -103.15387852790907, + "z": 0.0 + }, + { + "x": -0.05147824286717417, + "y": -104.15596319025992, + "z": 0.0 + }, + { + "x": -0.05095485857420208, + "y": -105.15804785261078, + "z": 0.0 + }, + { + "x": -0.050431474281229974, + "y": -106.16013251496165, + "z": 0.0 + }, + { + "x": -0.04990808998825787, + "y": -107.16221717731251, + "z": 0.0 + }, + { + "x": -0.04938470569528578, + "y": -108.16430183966337, + "z": 0.0 + }, + { + "x": -0.04886132140231368, + "y": -109.1663865020142, + "z": 0.0 + }, + { + "x": -0.04833793710934158, + "y": -110.16847116436507, + "z": 0.0 + }, + { + "x": -0.04781455281636948, + "y": -111.17055582671594, + "z": 0.0 + }, + { + "x": -0.04729116852339738, + "y": -112.1726404890668, + "z": 0.0 + }, + { + "x": -0.04676778423042528, + "y": -113.17472515141765, + "z": 0.0 + }, + { + "x": -0.04624439993745318, + "y": -114.17680981376851, + "z": 0.0 + }, + { + "x": -0.04572101564448108, + "y": -115.17889447611937, + "z": 0.0 + }, + { + "x": -0.045197631351508984, + "y": -116.18097913847022, + "z": 0.0 + }, + { + "x": -0.04467424705853689, + "y": -117.18306380082107, + "z": 0.0 + }, + { + "x": -0.044150862765564784, + "y": -118.18514846317194, + "z": 0.0 + }, + { + "x": -0.043627478472592685, + "y": -119.18723312552281, + "z": 0.0 + }, + { + "x": -0.043104094179620585, + "y": -120.18931778787366, + "z": 0.0 + }, + { + "x": -0.0425807098866485, + "y": -121.1914024502245, + "z": 0.0 + }, + { + "x": -0.0420573255936764, + "y": -122.19348711257537, + "z": 0.0 + }, + { + "x": -0.041533941300704286, + "y": -123.19557177492624, + "z": 0.0 + }, + { + "x": -0.04101055700773219, + "y": -124.19765643727709, + "z": 0.0 + }, + { + "x": -0.0404871727147601, + "y": -125.19974109962794, + "z": 0.0 + }, + { + "x": -0.039963788421787994, + "y": -126.20182576197881, + "z": 0.0 + }, + { + "x": -0.039440404128815894, + "y": -127.20391042432966, + "z": 0.0 + }, + { + "x": -0.0389170198358438, + "y": -128.20599508668053, + "z": 0.0 + }, + { + "x": -0.0383936355428717, + "y": -129.20807974903138, + "z": 0.0 + }, + { + "x": -0.0378702512498996, + "y": -130.21016441138224, + "z": 0.0 + }, + { + "x": -0.0373468669569275, + "y": -131.2122490737331, + "z": 0.0 + }, + { + "x": -0.0368234826639554, + "y": -132.21433373608394, + "z": 0.0 + }, + { + "x": -0.03630009837098331, + "y": -133.2164183984348, + "z": 0.0 + }, + { + "x": -0.0357767140780112, + "y": -134.21850306078568, + "z": 0.0 + }, + { + "x": -0.0352533297850391, + "y": -135.22058772313653, + "z": 0.0 + }, + { + "x": -0.034729945492067, + "y": -136.22267238548739, + "z": 0.0 + }, + { + "x": -0.03420656119909491, + "y": -137.22475704783824, + "z": 0.0 + }, + { + "x": -0.03368317690612281, + "y": -138.2268417101891, + "z": 0.0 + }, + { + "x": -0.03315979261315071, + "y": -139.22892637253995, + "z": 0.0 + }, + { + "x": -0.032636408320178625, + "y": -140.2310110348908, + "z": 0.0 + }, + { + "x": -0.03211302402720654, + "y": -141.23309569724162, + "z": 0.0 + }, + { + "x": -0.03158963973423444, + "y": -142.23518035959248, + "z": 0.0 + }, + { + "x": -0.03106625544126235, + "y": -143.23726502194333, + "z": 0.0 + }, + { + "x": -0.03054287114829027, + "y": -144.23934968429415, + "z": 0.0 + }, + { + "x": -0.03001948685531817, + "y": -145.24143434664498, + "z": 0.0 + }, + { + "x": -0.029496102562346072, + "y": -146.24351900899583, + "z": 0.0 + }, + { + "x": -0.028972718269373976, + "y": -147.2456036713467, + "z": 0.0 + }, + { + "x": -0.028449333976401887, + "y": -148.24768833369754, + "z": 0.0 + }, + { + "x": -0.0279259496834298, + "y": -149.24977299604836, + "z": 0.0 + }, + { + "x": -0.027402565390457705, + "y": -150.25185765839922, + "z": 0.0 + }, + { + "x": -0.026879181097485615, + "y": -151.25394232075007, + "z": 0.0 + }, + { + "x": -0.026355796804513533, + "y": -152.2560269831009, + "z": 0.0 + }, + { + "x": -0.025832412511541437, + "y": -153.25811164545175, + "z": 0.0 + }, + { + "x": -0.02530902821856935, + "y": -154.2601963078026, + "z": 0.0 + }, + { + "x": -0.024785643925597255, + "y": -155.26228097015345, + "z": 0.0 + }, + { + "x": -0.024262259632625155, + "y": -156.2643656325043, + "z": 0.0 + }, + { + "x": -0.02373887533965308, + "y": -157.2664502948551, + "z": 0.0 + }, + { + "x": -0.023215491046680976, + "y": -158.26853495720596, + "z": 0.0 + }, + { + "x": -0.022692106753708877, + "y": -159.2706196195568, + "z": 0.0 + }, + { + "x": -0.022168722460736805, + "y": -160.27270428190764, + "z": 0.0 + }, + { + "x": -0.02164533816776471, + "y": -161.2747889442585, + "z": 0.0 + }, + { + "x": -0.02112195387479261, + "y": -162.27687360660934, + "z": 0.0 + }, + { + "x": -0.02059856958182052, + "y": -163.2789582689602, + "z": 0.0 + }, + { + "x": -0.020075185288848416, + "y": -164.28104293131105, + "z": 0.0 + }, + { + "x": -0.019551800995876334, + "y": -165.28312759366187, + "z": 0.0 + }, + { + "x": -0.019028416702904248, + "y": -166.28521225601273, + "z": 0.0 + }, + { + "x": -0.018505032409932155, + "y": -167.28729691836358, + "z": 0.0 + }, + { + "x": -0.01798164811696007, + "y": -168.2893815807144, + "z": 0.0 + }, + { + "x": -0.01745826382398798, + "y": -169.29146624306526, + "z": 0.0 + }, + { + "x": -0.016934879531015877, + "y": -170.29355090541608, + "z": 0.0 + }, + { + "x": -0.01641149523804378, + "y": -171.29563556776694, + "z": 0.0 + }, + { + "x": -0.01588811094507169, + "y": -172.2977202301178, + "z": 0.0 + }, + { + "x": -0.015364726652099606, + "y": -173.29980489246861, + "z": 0.0 + }, + { + "x": -0.014841342359127515, + "y": -174.30188955481947, + "z": 0.0 + }, + { + "x": -0.014317958066155425, + "y": -175.30397421717032, + "z": 0.0 + }, + { + "x": -0.013794573773183338, + "y": -176.30605887952115, + "z": 0.0 + }, + { + "x": -0.01327118948021124, + "y": -177.308143541872, + "z": 0.0 + }, + { + "x": -0.01274780518723915, + "y": -178.31022820422285, + "z": 0.0 + }, + { + "x": -0.012224420894267046, + "y": -179.3123128665737, + "z": 0.0 + }, + { + "x": -0.011701036601294953, + "y": -180.31439752892456, + "z": 0.0 + }, + { + "x": -0.011177652308322879, + "y": -181.31648219127538, + "z": 0.0 + }, + { + "x": -0.010654268015350778, + "y": -182.3185668536262, + "z": 0.0 + }, + { + "x": -0.010130883722378678, + "y": -183.32065151597706, + "z": 0.0 + }, + { + "x": -0.009607499429406606, + "y": -184.3227361783279, + "z": 0.0 + }, + { + "x": -0.009084115136434505, + "y": -185.32482084067874, + "z": 0.0 + }, + { + "x": -0.008560730843462412, + "y": -186.3269055030296, + "z": 0.0 + }, + { + "x": -0.008037346550490324, + "y": -187.32899016538045, + "z": 0.0 + }, + { + "x": -0.007513962257518224, + "y": -188.3310748277313, + "z": 0.0 + }, + { + "x": -0.006990577964546139, + "y": -189.33315949008212, + "z": 0.0 + }, + { + "x": -0.00646719367157405, + "y": -190.33524415243298, + "z": 0.0 + }, + { + "x": -0.005943809378601944, + "y": -191.33732881478383, + "z": 0.0 + }, + { + "x": -0.005420425085629872, + "y": -192.33941347713466, + "z": 0.0 + }, + { + "x": -0.004897040792657784, + "y": -193.3414981394855, + "z": 0.0 + }, + { + "x": -0.00437365649968567, + "y": -194.34358280183636, + "z": 0.0 + }, + { + "x": -0.003850272206713584, + "y": -195.3456674641872, + "z": 0.0 + }, + { + "x": -0.003326887913741497, + "y": -196.34775212653804, + "z": 0.0 + }, + { + "x": -0.0028035036207694035, + "y": -197.34983678888887, + "z": 0.0 + }, + { + "x": -0.0022801193277973172, + "y": -198.35192145123972, + "z": 0.0 + }, + { + "x": -0.001756735034825231, + "y": -199.35400611359057, + "z": 0.0 + }, + { + "x": -0.0012333507418531237, + "y": -200.3560907759414, + "z": 0.0 + }, + { + "x": -0.0007099664488810368, + "y": -201.35817543829225, + "z": 0.0 + }, + { + "x": -0.0001865821559089505, + "y": -202.3602601006431, + "z": 0.0 + }, + { + "x": 0.00033680213706315724, + "y": -203.36234476299396, + "z": 0.0 + }, + { + "x": 0.0008601864300352446, + "y": -204.3644294253448, + "z": 0.0 + }, + { + "x": 0.00138357072300733, + "y": -205.36651408769563, + "z": 0.0 + }, + { + "x": 0.0019069550159794308, + "y": -206.3685987500465, + "z": 0.0 + }, + { + "x": 0.002430339308951518, + "y": -207.3706834123973, + "z": 0.0 + }, + { + "x": 0.002953723601923583, + "y": -208.37276807474814, + "z": 0.0 + }, + { + "x": 0.003477107894895689, + "y": -209.374852737099, + "z": 0.0 + }, + { + "x": 0.004000492187867783, + "y": -210.37693739944984, + "z": 0.0 + }, + { + "x": 0.00452387648083989, + "y": -211.3790220618007, + "z": 0.0 + }, + { + "x": 0.0050472607738119854, + "y": -212.38110672415155, + "z": 0.0 + }, + { + "x": 0.005570645066784064, + "y": -213.38319138650238, + "z": 0.0 + }, + { + "x": 0.006094029359756146, + "y": -214.38527604885323, + "z": 0.0 + }, + { + "x": 0.00661741365272825, + "y": -215.38736071120408, + "z": 0.0 + }, + { + "x": 0.0071407979457003305, + "y": -216.3894453735549, + "z": 0.0 + }, + { + "x": 0.007664182238672424, + "y": -217.39153003590576, + "z": 0.0 + }, + { + "x": 0.008187566531644517, + "y": -218.3936146982566, + "z": 0.0 + }, + { + "x": 0.008710950824616611, + "y": -219.39569936060747, + "z": 0.0 + }, + { + "x": 0.009234335117588716, + "y": -220.3977840229583, + "z": 0.0 + }, + { + "x": 0.009757719410560787, + "y": -221.39986868530912, + "z": 0.0 + }, + { + "x": 0.010281103703532888, + "y": -222.40195334765997, + "z": 0.0 + }, + { + "x": 0.010804487996504976, + "y": -223.40403801001082, + "z": 0.0 + }, + { + "x": 0.011327872289477061, + "y": -224.40612267236165, + "z": 0.0 + }, + { + "x": 0.01185125658244916, + "y": -225.4082073347125, + "z": 0.0 + }, + { + "x": 0.012374640875421252, + "y": -226.41029199706335, + "z": 0.0 + }, + { + "x": 0.012898025168393347, + "y": -227.4123766594142, + "z": 0.0 + }, + { + "x": 0.01342140946136544, + "y": -228.41446132176506, + "z": 0.0 + }, + { + "x": 0.013944793754337529, + "y": -229.41654598411588, + "z": 0.0 + }, + { + "x": 0.014468178047309627, + "y": -230.41863064646674, + "z": 0.0 + }, + { + "x": 0.01499156234028172, + "y": -231.4207153088176, + "z": 0.0 + }, + { + "x": 0.015514946633253798, + "y": -232.4227999711684, + "z": 0.0 + }, + { + "x": 0.01603833092622589, + "y": -233.42488463351924, + "z": 0.0 + }, + { + "x": 0.016561715219197987, + "y": -234.4269692958701, + "z": 0.0 + }, + { + "x": 0.01708509951217009, + "y": -235.42905395822095, + "z": 0.0 + }, + { + "x": 0.01760848380514218, + "y": -236.4311386205718, + "z": 0.0 + }, + { + "x": 0.018131868098114262, + "y": -237.43322328292263, + "z": 0.0 + }, + { + "x": 0.01865525239108635, + "y": -238.43530794527348, + "z": 0.0 + }, + { + "x": 0.019178636684058448, + "y": -239.43739260762433, + "z": 0.0 + }, + { + "x": 0.019702020977030534, + "y": -240.43947726997516, + "z": 0.0 + }, + { + "x": 0.01994365180220445, + "y": -241.44156200490332, + "z": 0.0 + }, + { + "x": 0.019812814275586975, + "y": -242.44364679539336, + "z": 0.0 + }, + { + "x": 0.0196819767489695, + "y": -243.4457315858834, + "z": 0.0 + }, + { + "x": 0.01955113922235203, + "y": -244.44781637637345, + "z": 0.0 + }, + { + "x": 0.01942030169573456, + "y": -245.44990116686347, + "z": 0.0 + }, + { + "x": 0.019289464169117088, + "y": -246.45198595735351, + "z": 0.0 + }, + { + "x": 0.019158626642499614, + "y": -247.4540707478436, + "z": 0.0 + }, + { + "x": 0.019027789115882147, + "y": -248.4561555383336, + "z": 0.0 + }, + { + "x": 0.018896951589264674, + "y": -249.45824032882365, + "z": 0.0 + }, + { + "x": 0.0187661140626472, + "y": -250.4603251193137, + "z": 0.0 + }, + { + "x": 0.018635276536029727, + "y": -251.46240990980374, + "z": 0.0 + }, + { + "x": 0.018504439009412253, + "y": -252.46449470029378, + "z": 0.0 + }, + { + "x": 0.01837360148279479, + "y": -253.4665794907838, + "z": 0.0 + }, + { + "x": 0.018242763956177317, + "y": -254.46866428127385, + "z": 0.0 + }, + { + "x": 0.018111926429559843, + "y": -255.4707490717639, + "z": 0.0 + }, + { + "x": 0.017981088902942376, + "y": -256.4728338622539, + "z": 0.0 + }, + { + "x": 0.0178502513763249, + "y": -257.47491865274395, + "z": 0.0 + }, + { + "x": 0.017719413849707426, + "y": -258.477003443234, + "z": 0.0 + }, + { + "x": 0.017588576323089952, + "y": -259.47908823372404, + "z": 0.0 + }, + { + "x": 0.017457738796472482, + "y": -260.4811730242141, + "z": 0.0 + }, + { + "x": 0.017326901269855012, + "y": -261.48325781470413, + "z": 0.0 + }, + { + "x": 0.01719606374323754, + "y": -262.48534260519415, + "z": 0.0 + }, + { + "x": 0.017065226216620065, + "y": -263.4874273956842, + "z": 0.0 + }, + { + "x": 0.016934388690002602, + "y": -264.48951218617424, + "z": 0.0 + }, + { + "x": 0.016803551163385128, + "y": -265.49159697666425, + "z": 0.0 + }, + { + "x": 0.01667271363676765, + "y": -266.4936817671543, + "z": 0.0 + }, + { + "x": 0.016541876110150174, + "y": -267.4957665576444, + "z": 0.0 + }, + { + "x": 0.016411038583532694, + "y": -268.4978513481345, + "z": 0.0 + }, + { + "x": 0.01628020105691522, + "y": -269.49993613862455, + "z": 0.0 + }, + { + "x": 0.016149363530297743, + "y": -270.5020209291146, + "z": 0.0 + }, + { + "x": 0.016018526003680273, + "y": -271.50410571960464, + "z": 0.0 + }, + { + "x": 0.015887688477062796, + "y": -272.5061905100947, + "z": 0.0 + }, + { + "x": 0.015756850950445322, + "y": -273.5082753005848, + "z": 0.0 + }, + { + "x": 0.01562601342382785, + "y": -274.51036009107486, + "z": 0.0 + }, + { + "x": 0.01549517589721037, + "y": -275.51244488156493, + "z": 0.0 + }, + { + "x": 0.015364338370592895, + "y": -276.514529672055, + "z": 0.0 + }, + { + "x": 0.015233500843975418, + "y": -277.5166144625451, + "z": 0.0 + }, + { + "x": 0.015102663317357948, + "y": -278.5186992530351, + "z": 0.0 + }, + { + "x": 0.01497182579074047, + "y": -279.52078404352517, + "z": 0.0 + }, + { + "x": 0.014840988264122999, + "y": -280.52286883401524, + "z": 0.0 + }, + { + "x": 0.014710150737505518, + "y": -281.5249536245053, + "z": 0.0 + }, + { + "x": 0.014579313210888043, + "y": -282.5270384149954, + "z": 0.0 + }, + { + "x": 0.01444847568427057, + "y": -283.52912320548546, + "z": 0.0 + }, + { + "x": 0.01431763815765309, + "y": -284.53120799597554, + "z": 0.0 + }, + { + "x": 0.014186800631035616, + "y": -285.5332927864656, + "z": 0.0 + }, + { + "x": 0.014055963104418139, + "y": -286.5353775769557, + "z": 0.0 + }, + { + "x": 0.013925125577800668, + "y": -287.53746236744576, + "z": 0.0 + }, + { + "x": 0.013794288051183191, + "y": -288.53954715793583, + "z": 0.0 + }, + { + "x": 0.013663450524565718, + "y": -289.5416319484259, + "z": 0.0 + }, + { + "x": 0.01353261299794824, + "y": -290.543716738916, + "z": 0.0 + }, + { + "x": 0.013401775471330764, + "y": -291.54580152940605, + "z": 0.0 + }, + { + "x": 0.01327093794471329, + "y": -292.5478863198961, + "z": 0.0 + }, + { + "x": 0.013140100418095813, + "y": -293.5499711103862, + "z": 0.0 + }, + { + "x": 0.013009262891478343, + "y": -294.5520559008762, + "z": 0.0 + }, + { + "x": 0.012878425364860866, + "y": -295.5541406913663, + "z": 0.0 + }, + { + "x": 0.012747587838243389, + "y": -296.55622548185636, + "z": 0.0 + }, + { + "x": 0.01261675031162591, + "y": -297.55831027234643, + "z": 0.0 + }, + { + "x": 0.012485912785008433, + "y": -298.5603950628365, + "z": 0.0 + }, + { + "x": 0.01235507525839096, + "y": -299.5624798533266, + "z": 0.0 + }, + { + "x": 0.012224237731773488, + "y": -300.56456464381665, + "z": 0.0 + }, + { + "x": 0.012093400205156011, + "y": -301.5666494343067, + "z": 0.0 + }, + { + "x": 0.011962562678538534, + "y": -302.5687342247968, + "z": 0.0 + }, + { + "x": 0.011831725151921064, + "y": -303.5708190152868, + "z": 0.0 + }, + { + "x": 0.011700887625303587, + "y": -304.5729038057769, + "z": 0.0 + }, + { + "x": 0.01157005009868611, + "y": -305.57498859626696, + "z": 0.0 + }, + { + "x": 0.011439212572068633, + "y": -306.57707338675704, + "z": 0.0 + }, + { + "x": 0.011308375045451156, + "y": -307.5791581772471, + "z": 0.0 + }, + { + "x": 0.011177537518833684, + "y": -308.5812429677372, + "z": 0.0 + }, + { + "x": 0.011046699992216202, + "y": -309.58332775822726, + "z": 0.0 + }, + { + "x": 0.010915862465598737, + "y": -310.5854125487173, + "z": 0.0 + }, + { + "x": 0.010785024938981261, + "y": -311.58749733920735, + "z": 0.0 + }, + { + "x": 0.010654187412363784, + "y": -312.5895821296974, + "z": 0.0 + }, + { + "x": 0.010523349885746306, + "y": -313.5916669201875, + "z": 0.0 + }, + { + "x": 0.010392512359128829, + "y": -314.59375171067757, + "z": 0.0 + }, + { + "x": 0.010261674832511353, + "y": -315.59583650116764, + "z": 0.0 + }, + { + "x": 0.010130837305893875, + "y": -316.5979212916577, + "z": 0.0 + }, + { + "x": 0.0099999997792764, + "y": -317.6000060821478, + "z": 0.0 + } + ] + }, + { + "id": 78, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 2.009999982731973, + "y": -317.6002672127961, + "z": 0.0 + }, + { + "x": 2.01013082025859, + "y": -316.598182422306, + "z": 0.0 + }, + { + "x": 2.0102616577852075, + "y": -315.59609763181595, + "z": 0.0 + }, + { + "x": 2.010392495311825, + "y": -314.5940128413259, + "z": 0.0 + }, + { + "x": 2.0105233328384426, + "y": -313.5919280508358, + "z": 0.0 + }, + { + "x": 2.01065417036506, + "y": -312.58984326034573, + "z": 0.0 + }, + { + "x": 2.0107850078916774, + "y": -311.58775846985566, + "z": 0.0 + }, + { + "x": 2.010915845418295, + "y": -310.5856736793656, + "z": 0.0 + }, + { + "x": 2.0110466829449125, + "y": -309.58358888887557, + "z": 0.0 + }, + { + "x": 2.01117752047153, + "y": -308.5815040983855, + "z": 0.0 + }, + { + "x": 2.0113083579981472, + "y": -307.5794193078954, + "z": 0.0 + }, + { + "x": 2.011439195524765, + "y": -306.57733451740535, + "z": 0.0 + }, + { + "x": 2.0115700330513824, + "y": -305.5752497269153, + "z": 0.0 + }, + { + "x": 2.0117008705779997, + "y": -304.5731649364252, + "z": 0.0 + }, + { + "x": 2.011831708104617, + "y": -303.5710801459351, + "z": 0.0 + }, + { + "x": 2.011962545631235, + "y": -302.5689953554451, + "z": 0.0 + }, + { + "x": 2.0120933831578522, + "y": -301.56691056495504, + "z": 0.0 + }, + { + "x": 2.0122242206844696, + "y": -300.56482577446496, + "z": 0.0 + }, + { + "x": 2.012355058211087, + "y": -299.5627409839749, + "z": 0.0 + }, + { + "x": 2.0124858957377048, + "y": -298.5606561934848, + "z": 0.0 + }, + { + "x": 2.012616733264322, + "y": -297.55857140299474, + "z": 0.0 + }, + { + "x": 2.0127475707909395, + "y": -296.55648661250467, + "z": 0.0 + }, + { + "x": 2.0128784083175573, + "y": -295.5544018220146, + "z": 0.0 + }, + { + "x": 2.0130092458441746, + "y": -294.5523170315245, + "z": 0.0 + }, + { + "x": 2.013140083370792, + "y": -293.5502322410345, + "z": 0.0 + }, + { + "x": 2.0132709208974093, + "y": -292.54814745054443, + "z": 0.0 + }, + { + "x": 2.013401758424027, + "y": -291.54606266005436, + "z": 0.0 + }, + { + "x": 2.0135325959506445, + "y": -290.5439778695643, + "z": 0.0 + }, + { + "x": 2.013663433477262, + "y": -289.5418930790742, + "z": 0.0 + }, + { + "x": 2.013794271003879, + "y": -288.53980828858414, + "z": 0.0 + }, + { + "x": 2.013925108530497, + "y": -287.53772349809407, + "z": 0.0 + }, + { + "x": 2.0140559460571144, + "y": -286.535638707604, + "z": 0.0 + }, + { + "x": 2.0141867835837317, + "y": -285.5335539171139, + "z": 0.0 + }, + { + "x": 2.0143176211103495, + "y": -284.53146912662385, + "z": 0.0 + }, + { + "x": 2.014448458636967, + "y": -283.5293843361338, + "z": 0.0 + }, + { + "x": 2.014579296163584, + "y": -282.5272995456437, + "z": 0.0 + }, + { + "x": 2.0147101336902016, + "y": -281.5252147551536, + "z": 0.0 + }, + { + "x": 2.0148409712168194, + "y": -280.52312996466355, + "z": 0.0 + }, + { + "x": 2.0149718087434367, + "y": -279.5210451741735, + "z": 0.0 + }, + { + "x": 2.015102646270054, + "y": -278.5189603836834, + "z": 0.0 + }, + { + "x": 2.0152334837966714, + "y": -277.5168755931934, + "z": 0.0 + }, + { + "x": 2.0153643213232892, + "y": -276.5147908027033, + "z": 0.0 + }, + { + "x": 2.0154951588499066, + "y": -275.51270601221324, + "z": 0.0 + }, + { + "x": 2.015625996376524, + "y": -274.51062122172317, + "z": 0.0 + }, + { + "x": 2.0157568339031418, + "y": -273.5085364312331, + "z": 0.0 + }, + { + "x": 2.015887671429759, + "y": -272.506451640743, + "z": 0.0 + }, + { + "x": 2.0160185089563765, + "y": -271.50436685025295, + "z": 0.0 + }, + { + "x": 2.016149346482994, + "y": -270.50228205976293, + "z": 0.0 + }, + { + "x": 2.0162801840096116, + "y": -269.50019726927286, + "z": 0.0 + }, + { + "x": 2.016411021536229, + "y": -268.4981124787828, + "z": 0.0 + }, + { + "x": 2.0165418590628463, + "y": -267.4960276882927, + "z": 0.0 + }, + { + "x": 2.0166726965894637, + "y": -266.49394289780264, + "z": 0.0 + }, + { + "x": 2.0168035341160815, + "y": -265.49185810731257, + "z": 0.0 + }, + { + "x": 2.016934371642699, + "y": -264.48977331682255, + "z": 0.0 + }, + { + "x": 2.017065209169316, + "y": -263.48768852633253, + "z": 0.0 + }, + { + "x": 2.0171960466959336, + "y": -262.48560373584246, + "z": 0.0 + }, + { + "x": 2.0173268842225514, + "y": -261.48351894535244, + "z": 0.0 + }, + { + "x": 2.0174577217491687, + "y": -260.4814341548624, + "z": 0.0 + }, + { + "x": 2.017588559275786, + "y": -259.47934936437235, + "z": 0.0 + }, + { + "x": 2.0177193968024034, + "y": -258.47726457388234, + "z": 0.0 + }, + { + "x": 2.017850234329021, + "y": -257.47517978339226, + "z": 0.0 + }, + { + "x": 2.0179810718556386, + "y": -256.4730949929022, + "z": 0.0 + }, + { + "x": 2.0181119093822555, + "y": -255.4710102024122, + "z": 0.0 + }, + { + "x": 2.0182427469088733, + "y": -254.46892541192216, + "z": 0.0 + }, + { + "x": 2.0183735844354906, + "y": -253.4668406214321, + "z": 0.0 + }, + { + "x": 2.0185044219621084, + "y": -252.4647558309421, + "z": 0.0 + }, + { + "x": 2.0186352594887254, + "y": -251.46267104045205, + "z": 0.0 + }, + { + "x": 2.018766097015343, + "y": -250.460586249962, + "z": 0.0 + }, + { + "x": 2.0188969345419605, + "y": -249.45850145947196, + "z": 0.0 + }, + { + "x": 2.019027772068578, + "y": -248.45641666898192, + "z": 0.0 + }, + { + "x": 2.0191586095951957, + "y": -247.4543318784919, + "z": 0.0 + }, + { + "x": 2.0192894471218135, + "y": -246.45224708800183, + "z": 0.0 + }, + { + "x": 2.0194202846484304, + "y": -245.45016229751178, + "z": 0.0 + }, + { + "x": 2.019551122175048, + "y": -244.44807750702176, + "z": 0.0 + }, + { + "x": 2.019681959701665, + "y": -243.44599271653172, + "z": 0.0 + }, + { + "x": 2.019812797228283, + "y": -242.44390792604167, + "z": 0.0 + }, + { + "x": 2.0199436347549002, + "y": -241.44182313555163, + "z": 0.0 + }, + { + "x": 2.019701962834255, + "y": -240.43899501371533, + "z": 0.0 + }, + { + "x": 2.0191783638915424, + "y": -239.4363480168003, + "z": 0.0 + }, + { + "x": 2.0186549795985704, + "y": -238.43426335444946, + "z": 0.0 + }, + { + "x": 2.0181315953055985, + "y": -237.4321786920986, + "z": 0.0 + }, + { + "x": 2.0176082110126257, + "y": -236.4300940297478, + "z": 0.0 + }, + { + "x": 2.0170848267196537, + "y": -235.42800936739695, + "z": 0.0 + }, + { + "x": 2.016561442426682, + "y": -234.4259247050461, + "z": 0.0 + }, + { + "x": 2.01603805813371, + "y": -233.42384004269525, + "z": 0.0 + }, + { + "x": 2.015514673840738, + "y": -232.4217553803444, + "z": 0.0 + }, + { + "x": 2.014991289547766, + "y": -231.4196707179936, + "z": 0.0 + }, + { + "x": 2.014467905254793, + "y": -230.41758605564274, + "z": 0.0 + }, + { + "x": 2.013944520961821, + "y": -229.4155013932919, + "z": 0.0 + }, + { + "x": 2.0134211366688493, + "y": -228.41341673094104, + "z": 0.0 + }, + { + "x": 2.0128977523758773, + "y": -227.41133206859018, + "z": 0.0 + }, + { + "x": 2.0123743680829054, + "y": -226.40924740623933, + "z": 0.0 + }, + { + "x": 2.0118509837899334, + "y": -225.40716274388848, + "z": 0.0 + }, + { + "x": 2.0113275994969606, + "y": -224.40507808153762, + "z": 0.0 + }, + { + "x": 2.0108042152039887, + "y": -223.40299341918683, + "z": 0.0 + }, + { + "x": 2.0102808309110167, + "y": -222.40090875683597, + "z": 0.0 + }, + { + "x": 2.009757446618045, + "y": -221.39882409448512, + "z": 0.0 + }, + { + "x": 2.009234062325073, + "y": -220.39673943213427, + "z": 0.0 + }, + { + "x": 2.008710678032101, + "y": -219.39465476978347, + "z": 0.0 + }, + { + "x": 2.008187293739128, + "y": -218.39257010743262, + "z": 0.0 + }, + { + "x": 2.007663909446156, + "y": -217.39048544508177, + "z": 0.0 + }, + { + "x": 2.007140525153184, + "y": -216.3884007827309, + "z": 0.0 + }, + { + "x": 2.0066171408602123, + "y": -215.38631612038006, + "z": 0.0 + }, + { + "x": 2.0060937565672403, + "y": -214.3842314580292, + "z": 0.0 + }, + { + "x": 2.0055703722742684, + "y": -213.38214679567835, + "z": 0.0 + }, + { + "x": 2.0050469879812955, + "y": -212.38006213332756, + "z": 0.0 + }, + { + "x": 2.0045236036883236, + "y": -211.3779774709767, + "z": 0.0 + }, + { + "x": 2.0040002193953517, + "y": -210.37589280862585, + "z": 0.0 + }, + { + "x": 2.0034768351023797, + "y": -209.373808146275, + "z": 0.0 + }, + { + "x": 2.002953450809408, + "y": -208.37172348392414, + "z": 0.0 + }, + { + "x": 2.002430066516435, + "y": -207.3696388215733, + "z": 0.0 + }, + { + "x": 2.001906682223463, + "y": -206.3675541592225, + "z": 0.0 + }, + { + "x": 2.001383297930491, + "y": -205.36546949687164, + "z": 0.0 + }, + { + "x": 2.000859913637519, + "y": -204.3633848345208, + "z": 0.0 + }, + { + "x": 2.000336529344547, + "y": -203.36130017216993, + "z": 0.0 + }, + { + "x": 1.9998131450515748, + "y": -202.35921550981908, + "z": 0.0 + }, + { + "x": 1.9992897607586029, + "y": -201.35713084746823, + "z": 0.0 + }, + { + "x": 1.998766376465631, + "y": -200.35504618511737, + "z": 0.0 + }, + { + "x": 1.9982429921726585, + "y": -199.35296152276658, + "z": 0.0 + }, + { + "x": 1.9977196078796866, + "y": -198.35087686041572, + "z": 0.0 + }, + { + "x": 1.9971962235867147, + "y": -197.34879219806487, + "z": 0.0 + }, + { + "x": 1.9966728392937423, + "y": -196.34670753571402, + "z": 0.0 + }, + { + "x": 1.9961494550007703, + "y": -195.34462287336316, + "z": 0.0 + }, + { + "x": 1.9956260707077984, + "y": -194.34253821101237, + "z": 0.0 + }, + { + "x": 1.995102686414826, + "y": -193.34045354866151, + "z": 0.0 + }, + { + "x": 1.994579302121854, + "y": -192.33836888631066, + "z": 0.0 + }, + { + "x": 1.9940559178288821, + "y": -191.3362842239598, + "z": 0.0 + }, + { + "x": 1.9935325335359098, + "y": -190.33419956160895, + "z": 0.0 + }, + { + "x": 1.9930091492429378, + "y": -189.3321148992581, + "z": 0.0 + }, + { + "x": 1.9924857649499654, + "y": -188.3300302369073, + "z": 0.0 + }, + { + "x": 1.9919623806569935, + "y": -187.32794557455645, + "z": 0.0 + }, + { + "x": 1.9914389963640216, + "y": -186.3258609122056, + "z": 0.0 + }, + { + "x": 1.9909156120710492, + "y": -185.32377624985475, + "z": 0.0 + }, + { + "x": 1.9903922277780772, + "y": -184.3216915875039, + "z": 0.0 + }, + { + "x": 1.9898688434851053, + "y": -183.31960692515304, + "z": 0.0 + }, + { + "x": 1.989345459192133, + "y": -182.31752226280219, + "z": 0.0 + }, + { + "x": 1.988822074899161, + "y": -181.3154376004514, + "z": 0.0 + }, + { + "x": 1.988298690606189, + "y": -180.31335293810054, + "z": 0.0 + }, + { + "x": 1.9877753063132166, + "y": -179.31126827574968, + "z": 0.0 + }, + { + "x": 1.9872519220202447, + "y": -178.30918361339883, + "z": 0.0 + }, + { + "x": 1.9867285377272728, + "y": -177.30709895104798, + "z": 0.0 + }, + { + "x": 1.9862051534343004, + "y": -176.30501428869712, + "z": 0.0 + }, + { + "x": 1.9856817691413284, + "y": -175.30292962634633, + "z": 0.0 + }, + { + "x": 1.9851583848483565, + "y": -174.30084496399547, + "z": 0.0 + }, + { + "x": 1.984635000555384, + "y": -173.29876030164462, + "z": 0.0 + }, + { + "x": 1.9841116162624122, + "y": -172.29667563929377, + "z": 0.0 + }, + { + "x": 1.9835882319694402, + "y": -171.2945909769429, + "z": 0.0 + }, + { + "x": 1.9830648476764678, + "y": -170.29250631459206, + "z": 0.0 + }, + { + "x": 1.982541463383496, + "y": -169.29042165224126, + "z": 0.0 + }, + { + "x": 1.982018079090524, + "y": -168.2883369898904, + "z": 0.0 + }, + { + "x": 1.9814946947975516, + "y": -167.28625232753956, + "z": 0.0 + }, + { + "x": 1.9809713105045796, + "y": -166.2841676651887, + "z": 0.0 + }, + { + "x": 1.9804479262116077, + "y": -165.28208300283785, + "z": 0.0 + }, + { + "x": 1.9799245419186353, + "y": -164.27999834048705, + "z": 0.0 + }, + { + "x": 1.9794011576256634, + "y": -163.2779136781362, + "z": 0.0 + }, + { + "x": 1.9788777733326914, + "y": -162.27582901578535, + "z": 0.0 + }, + { + "x": 1.978354389039719, + "y": -161.2737443534345, + "z": 0.0 + }, + { + "x": 1.9778310047467471, + "y": -160.27165969108364, + "z": 0.0 + }, + { + "x": 1.9773076204537752, + "y": -159.2695750287328, + "z": 0.0 + }, + { + "x": 1.9767842361608028, + "y": -158.26749036638194, + "z": 0.0 + }, + { + "x": 1.9762608518678308, + "y": -157.26540570403108, + "z": 0.0 + }, + { + "x": 1.975737467574859, + "y": -156.26332104168029, + "z": 0.0 + }, + { + "x": 1.9752140832818865, + "y": -155.26123637932943, + "z": 0.0 + }, + { + "x": 1.9746906989889146, + "y": -154.25915171697858, + "z": 0.0 + }, + { + "x": 1.9741673146959426, + "y": -153.25706705462773, + "z": 0.0 + }, + { + "x": 1.9736439304029703, + "y": -152.25498239227687, + "z": 0.0 + }, + { + "x": 1.9731205461099983, + "y": -151.25289772992608, + "z": 0.0 + }, + { + "x": 1.9725971618170264, + "y": -150.25081306757522, + "z": 0.0 + }, + { + "x": 1.972073777524054, + "y": -149.24872840522437, + "z": 0.0 + }, + { + "x": 1.971550393231082, + "y": -148.24664374287352, + "z": 0.0 + }, + { + "x": 1.9710270089381097, + "y": -147.24455908052266, + "z": 0.0 + }, + { + "x": 1.9705036246451377, + "y": -146.2424744181718, + "z": 0.0 + }, + { + "x": 1.9699802403521658, + "y": -145.24038975582096, + "z": 0.0 + }, + { + "x": 1.9694568560591934, + "y": -144.23830509347016, + "z": 0.0 + }, + { + "x": 1.9689334717662215, + "y": -143.2362204311193, + "z": 0.0 + }, + { + "x": 1.9684100874732495, + "y": -142.23413576876845, + "z": 0.0 + }, + { + "x": 1.9678867031802771, + "y": -141.2320511064176, + "z": 0.0 + }, + { + "x": 1.9673633188873052, + "y": -140.2299664440668, + "z": 0.0 + }, + { + "x": 1.9668399345943333, + "y": -139.22788178171595, + "z": 0.0 + }, + { + "x": 1.9663165503013609, + "y": -138.2257971193651, + "z": 0.0 + }, + { + "x": 1.965793166008389, + "y": -137.22371245701424, + "z": 0.0 + }, + { + "x": 1.965269781715417, + "y": -136.2216277946634, + "z": 0.0 + }, + { + "x": 1.9647463974224446, + "y": -135.21954313231254, + "z": 0.0 + }, + { + "x": 1.9642230131294727, + "y": -134.21745846996168, + "z": 0.0 + }, + { + "x": 1.9636996288365007, + "y": -133.21537380761077, + "z": 0.0 + }, + { + "x": 1.9631762445435283, + "y": -132.21328914525992, + "z": 0.0 + }, + { + "x": 1.9626528602505564, + "y": -131.21120448290907, + "z": 0.0 + }, + { + "x": 1.9621294759575845, + "y": -130.20911982055821, + "z": 0.0 + }, + { + "x": 1.961606091664612, + "y": -129.20703515820736, + "z": 0.0 + }, + { + "x": 1.9610827073716401, + "y": -128.2049504958565, + "z": 0.0 + }, + { + "x": 1.9605593230786678, + "y": -127.20286583350565, + "z": 0.0 + }, + { + "x": 1.9600359387856958, + "y": -126.2007811711548, + "z": 0.0 + }, + { + "x": 1.9595125544927239, + "y": -125.19869650880393, + "z": 0.0 + }, + { + "x": 1.9589891701997515, + "y": -124.19661184645308, + "z": 0.0 + }, + { + "x": 1.9584657859067796, + "y": -123.19452718410223, + "z": 0.0 + }, + { + "x": 1.9579424016138076, + "y": -122.19244252175136, + "z": 0.0 + }, + { + "x": 1.9574190173208352, + "y": -121.19035785940049, + "z": 0.0 + }, + { + "x": 1.9568956330278633, + "y": -120.18827319704965, + "z": 0.0 + }, + { + "x": 1.9563722487348914, + "y": -119.1861885346988, + "z": 0.0 + }, + { + "x": 1.955848864441919, + "y": -118.18410387234793, + "z": 0.0 + }, + { + "x": 1.955325480148947, + "y": -117.18201920999707, + "z": 0.0 + }, + { + "x": 1.954802095855975, + "y": -116.17993454764621, + "z": 0.0 + }, + { + "x": 1.9542787115630027, + "y": -115.17784988529536, + "z": 0.0 + }, + { + "x": 1.9537553272700308, + "y": -114.1757652229445, + "z": 0.0 + }, + { + "x": 1.9532319429770584, + "y": -113.17368056059364, + "z": 0.0 + }, + { + "x": 1.9527085586840864, + "y": -112.17159589824278, + "z": 0.0 + }, + { + "x": 1.9521851743911145, + "y": -111.16951123589193, + "z": 0.0 + }, + { + "x": 1.9516617900981421, + "y": -110.16742657354106, + "z": 0.0 + }, + { + "x": 1.9511384058051702, + "y": -109.1653419111902, + "z": 0.0 + }, + { + "x": 1.9506150215121982, + "y": -108.16325724883936, + "z": 0.0 + }, + { + "x": 1.9500916372192258, + "y": -107.1611725864885, + "z": 0.0 + }, + { + "x": 1.949568252926254, + "y": -106.15908792413764, + "z": 0.0 + }, + { + "x": 1.949044868633282, + "y": -105.15700326178677, + "z": 0.0 + }, + { + "x": 1.9485214843403096, + "y": -104.15491859943592, + "z": 0.0 + }, + { + "x": 1.9479981000473376, + "y": -103.15283393708506, + "z": 0.0 + }, + { + "x": 1.9474747157543657, + "y": -102.15074927473421, + "z": 0.0 + }, + { + "x": 1.9469513314613933, + "y": -101.14866461238334, + "z": 0.0 + }, + { + "x": 1.9464279471684214, + "y": -100.14657995003249, + "z": 0.0 + }, + { + "x": 1.945904562875449, + "y": -99.14449528768164, + "z": 0.0 + }, + { + "x": 1.945381178582477, + "y": -98.14241062533077, + "z": 0.0 + }, + { + "x": 1.9448577942895051, + "y": -97.1403259629799, + "z": 0.0 + }, + { + "x": 1.9443344099965327, + "y": -96.13824130062906, + "z": 0.0 + }, + { + "x": 1.9438110257035608, + "y": -95.13615663827821, + "z": 0.0 + }, + { + "x": 1.9432876414105889, + "y": -94.13407197592734, + "z": 0.0 + }, + { + "x": 1.9427642571176165, + "y": -93.13198731357647, + "z": 0.0 + }, + { + "x": 1.9422408728246445, + "y": -92.12990265122562, + "z": 0.0 + }, + { + "x": 1.9417174885316726, + "y": -91.12781798887478, + "z": 0.0 + }, + { + "x": 1.9411941042387002, + "y": -90.12573332652391, + "z": 0.0 + }, + { + "x": 1.9406707199457283, + "y": -89.12364866417305, + "z": 0.0 + }, + { + "x": 1.9401473356527563, + "y": -88.1215640018222, + "z": 0.0 + }, + { + "x": 1.9402771312086453, + "y": -87.12078237878384, + "z": 0.0 + }, + { + "x": 1.9406630106088594, + "y": -86.11920953013264, + "z": 0.0 + }, + { + "x": 1.94104902168355, + "y": -85.11712480544843, + "z": 0.0 + }, + { + "x": 1.9414350327582408, + "y": -84.11504008076425, + "z": 0.0 + }, + { + "x": 1.9418210438329315, + "y": -83.11295535608005, + "z": 0.0 + }, + { + "x": 1.9422070549076222, + "y": -82.11087063139585, + "z": 0.0 + }, + { + "x": 1.9425930659823125, + "y": -81.10878590671165, + "z": 0.0 + }, + { + "x": 1.9429790770570032, + "y": -80.10670118202746, + "z": 0.0 + }, + { + "x": 1.943365088131694, + "y": -79.10461645734327, + "z": 0.0 + }, + { + "x": 1.9437510992063847, + "y": -78.10253173265907, + "z": 0.0 + }, + { + "x": 1.9441371102810754, + "y": -77.10044700797486, + "z": 0.0 + }, + { + "x": 1.944523121355766, + "y": -76.09836228329068, + "z": 0.0 + }, + { + "x": 1.9449091324304564, + "y": -75.09627755860649, + "z": 0.0 + }, + { + "x": 1.945295143505147, + "y": -74.09419283392229, + "z": 0.0 + }, + { + "x": 1.9456811545798378, + "y": -73.0921081092381, + "z": 0.0 + }, + { + "x": 1.9460671656545285, + "y": -72.0900233845539, + "z": 0.0 + }, + { + "x": 1.9464531767292192, + "y": -71.0879386598697, + "z": 0.0 + }, + { + "x": 1.94683918780391, + "y": -70.08585393518551, + "z": 0.0 + }, + { + "x": 1.9472251988786002, + "y": -69.08376921050132, + "z": 0.0 + }, + { + "x": 1.947611209953291, + "y": -68.08168448581712, + "z": 0.0 + }, + { + "x": 1.9479972210279817, + "y": -67.07959976113293, + "z": 0.0 + }, + { + "x": 1.9483832321026724, + "y": -66.07751503644873, + "z": 0.0 + }, + { + "x": 1.948769243177363, + "y": -65.07543031176453, + "z": 0.0 + }, + { + "x": 1.9491552542520538, + "y": -64.07334558708034, + "z": 0.0 + }, + { + "x": 1.9495412653267445, + "y": -63.07126086239614, + "z": 0.0 + }, + { + "x": 1.9499272764014348, + "y": -62.06917613771194, + "z": 0.0 + }, + { + "x": 1.9503132874761255, + "y": -61.06709141302775, + "z": 0.0 + }, + { + "x": 1.9506992985508163, + "y": -60.06500668834355, + "z": 0.0 + }, + { + "x": 1.951085309625507, + "y": -59.062921963659356, + "z": 0.0 + }, + { + "x": 1.9514713207001977, + "y": -58.06083723897516, + "z": 0.0 + }, + { + "x": 1.9518573317748884, + "y": -57.058752514290966, + "z": 0.0 + }, + { + "x": 1.9522433428495787, + "y": -56.05666778960677, + "z": 0.0 + }, + { + "x": 1.9526293539242694, + "y": -55.05458306492257, + "z": 0.0 + }, + { + "x": 1.9530153649989601, + "y": -54.05249834023837, + "z": 0.0 + }, + { + "x": 1.9534013760736508, + "y": -53.05041361555418, + "z": 0.0 + }, + { + "x": 1.9537873871483415, + "y": -52.04832889086998, + "z": 0.0 + }, + { + "x": 1.9541733982230323, + "y": -51.04624416618579, + "z": 0.0 + }, + { + "x": 1.9545594092977225, + "y": -50.04415944150159, + "z": 0.0 + }, + { + "x": 1.9549454203724133, + "y": -49.042074716817396, + "z": 0.0 + }, + { + "x": 1.955331431447104, + "y": -48.0399899921332, + "z": 0.0 + }, + { + "x": 1.9557174425217947, + "y": -47.037905267449005, + "z": 0.0 + }, + { + "x": 1.9561034535964854, + "y": -46.03582054276481, + "z": 0.0 + }, + { + "x": 1.9564894646711761, + "y": -45.033735818080615, + "z": 0.0 + }, + { + "x": 1.9568754757458668, + "y": -44.03165109339642, + "z": 0.0 + }, + { + "x": 1.9572614868205571, + "y": -43.029566368712224, + "z": 0.0 + }, + { + "x": 1.9576474978952478, + "y": -42.02748164402803, + "z": 0.0 + }, + { + "x": 1.9580335089699386, + "y": -41.025396919343834, + "z": 0.0 + }, + { + "x": 1.9584195200446293, + "y": -40.023312194659646, + "z": 0.0 + }, + { + "x": 1.95880553111932, + "y": -39.02122746997545, + "z": 0.0 + }, + { + "x": 1.9591915421940107, + "y": -38.019142745291255, + "z": 0.0 + }, + { + "x": 1.959577553268701, + "y": -37.01705802060707, + "z": 0.0 + }, + { + "x": 1.9599635643433917, + "y": -36.01497329592287, + "z": 0.0 + }, + { + "x": 1.9603495754180824, + "y": -35.01288857123868, + "z": 0.0 + }, + { + "x": 1.9607355864927731, + "y": -34.01080384655449, + "z": 0.0 + }, + { + "x": 1.9611215975674638, + "y": -33.00871912187029, + "z": 0.0 + }, + { + "x": 1.9615076086421546, + "y": -32.006634397186104, + "z": 0.0 + }, + { + "x": 1.9618936197168448, + "y": -31.004549672501913, + "z": 0.0 + }, + { + "x": 1.9622796307915356, + "y": -30.002464947817717, + "z": 0.0 + }, + { + "x": 1.9626656418662263, + "y": -29.000380223133526, + "z": 0.0 + }, + { + "x": 1.963051652940917, + "y": -27.998295498449334, + "z": 0.0 + }, + { + "x": 1.9634376640156077, + "y": -26.996210773765142, + "z": 0.0 + }, + { + "x": 1.9638236750902984, + "y": -25.99412604908095, + "z": 0.0 + }, + { + "x": 1.9642096861649887, + "y": -24.992041324396755, + "z": 0.0 + }, + { + "x": 1.9645956972396794, + "y": -23.989956599712563, + "z": 0.0 + }, + { + "x": 1.9649817083143701, + "y": -22.987871875028368, + "z": 0.0 + }, + { + "x": 1.9653677193890609, + "y": -21.985787150344173, + "z": 0.0 + }, + { + "x": 1.9657537304637516, + "y": -20.98370242565998, + "z": 0.0 + }, + { + "x": 1.9661397415384423, + "y": -19.981617700975786, + "z": 0.0 + }, + { + "x": 1.9665257526131326, + "y": -18.97953297629159, + "z": 0.0 + }, + { + "x": 1.9669117636878233, + "y": -17.9774482516074, + "z": 0.0 + }, + { + "x": 1.967297774762514, + "y": -16.975363526923203, + "z": 0.0 + }, + { + "x": 1.9676837858372047, + "y": -15.973278802239012, + "z": 0.0 + }, + { + "x": 1.9680697969118954, + "y": -14.971194077554818, + "z": 0.0 + }, + { + "x": 1.9684558079865861, + "y": -13.969109352870625, + "z": 0.0 + }, + { + "x": 1.9688418190612764, + "y": -12.967024628186431, + "z": 0.0 + }, + { + "x": 1.9692278301359671, + "y": -11.964939903502238, + "z": 0.0 + }, + { + "x": 1.9696138412106579, + "y": -10.962855178818044, + "z": 0.0 + }, + { + "x": 1.9699998522853486, + "y": -9.96077045413385, + "z": 0.0 + } + ] + }, + { + "id": 79, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": -2.029999850944244, + "y": -9.959229622160095, + "z": 0.0 + }, + { + "x": -2.0303858620189343, + "y": -10.961314346844288, + "z": 0.0 + }, + { + "x": -2.0307718730936255, + "y": -11.963399071528482, + "z": 0.0 + }, + { + "x": -2.0311578841683158, + "y": -12.965483796212675, + "z": 0.0 + }, + { + "x": -2.031543895243007, + "y": -13.967568520896869, + "z": 0.0 + }, + { + "x": -2.031929906317697, + "y": -14.969653245581062, + "z": 0.0 + }, + { + "x": -2.0323159173923884, + "y": -15.971737970265256, + "z": 0.0 + }, + { + "x": -2.0327019284670786, + "y": -16.973822694949448, + "z": 0.0 + }, + { + "x": -2.033087939541769, + "y": -17.975907419633643, + "z": 0.0 + }, + { + "x": -2.03347395061646, + "y": -18.977992144317835, + "z": 0.0 + }, + { + "x": -2.0338599616911504, + "y": -19.98007686900203, + "z": 0.0 + }, + { + "x": -2.0342459727658415, + "y": -20.982161593686225, + "z": 0.0 + }, + { + "x": -2.034631983840532, + "y": -21.984246318370417, + "z": 0.0 + }, + { + "x": -2.035017994915222, + "y": -22.986331043054612, + "z": 0.0 + }, + { + "x": -2.0354040059899132, + "y": -23.988415767738807, + "z": 0.0 + }, + { + "x": -2.0357900170646035, + "y": -24.990500492423, + "z": 0.0 + }, + { + "x": -2.0361760281392947, + "y": -25.992585217107194, + "z": 0.0 + }, + { + "x": -2.036562039213985, + "y": -26.994669941791386, + "z": 0.0 + }, + { + "x": -2.036948050288676, + "y": -27.996754666475578, + "z": 0.0 + }, + { + "x": -2.0373340613633664, + "y": -28.99883939115977, + "z": 0.0 + }, + { + "x": -2.0377200724380566, + "y": -30.00092411584396, + "z": 0.0 + }, + { + "x": -2.038106083512748, + "y": -31.003008840528157, + "z": 0.0 + }, + { + "x": -2.038492094587438, + "y": -32.00509356521235, + "z": 0.0 + }, + { + "x": -2.0388781056621292, + "y": -33.00717828989654, + "z": 0.0 + }, + { + "x": -2.0392641167368195, + "y": -34.00926301458073, + "z": 0.0 + }, + { + "x": -2.03965012781151, + "y": -35.01134773926493, + "z": 0.0 + }, + { + "x": -2.040036138886201, + "y": -36.013432463949115, + "z": 0.0 + }, + { + "x": -2.040422149960891, + "y": -37.01551718863331, + "z": 0.0 + }, + { + "x": -2.0408081610355824, + "y": -38.0176019133175, + "z": 0.0 + }, + { + "x": -2.0411941721102727, + "y": -39.019686638001694, + "z": 0.0 + }, + { + "x": -2.041580183184964, + "y": -40.02177136268589, + "z": 0.0 + }, + { + "x": -2.041966194259654, + "y": -41.02385608737008, + "z": 0.0 + }, + { + "x": -2.0423522053343444, + "y": -42.02594081205427, + "z": 0.0 + }, + { + "x": -2.0427382164090355, + "y": -43.02802553673847, + "z": 0.0 + }, + { + "x": -2.043124227483726, + "y": -44.030110261422664, + "z": 0.0 + }, + { + "x": -2.043510238558417, + "y": -45.03219498610686, + "z": 0.0 + }, + { + "x": -2.0438962496331072, + "y": -46.034279710791054, + "z": 0.0 + }, + { + "x": -2.0442822607077984, + "y": -47.03636443547525, + "z": 0.0 + }, + { + "x": -2.0446682717824887, + "y": -48.038449160159445, + "z": 0.0 + }, + { + "x": -2.045054282857179, + "y": -49.04053388484364, + "z": 0.0 + }, + { + "x": -2.04544029393187, + "y": -50.042618609527835, + "z": 0.0 + }, + { + "x": -2.0458263050065604, + "y": -51.04470333421203, + "z": 0.0 + }, + { + "x": -2.0462123160812515, + "y": -52.046788058896226, + "z": 0.0 + }, + { + "x": -2.046598327155942, + "y": -53.04887278358042, + "z": 0.0 + }, + { + "x": -2.046984338230632, + "y": -54.05095750826462, + "z": 0.0 + }, + { + "x": -2.0473703493053232, + "y": -55.05304223294881, + "z": 0.0 + }, + { + "x": -2.0477563603800135, + "y": -56.055126957633014, + "z": 0.0 + }, + { + "x": -2.0481423714547047, + "y": -57.05721168231721, + "z": 0.0 + }, + { + "x": -2.048528382529395, + "y": -58.059296407001405, + "z": 0.0 + }, + { + "x": -2.048914393604086, + "y": -59.0613811316856, + "z": 0.0 + }, + { + "x": -2.0493004046787764, + "y": -60.063465856369795, + "z": 0.0 + }, + { + "x": -2.0496864157534667, + "y": -61.06555058105399, + "z": 0.0 + }, + { + "x": -2.050072426828158, + "y": -62.067635305738186, + "z": 0.0 + }, + { + "x": -2.050458437902848, + "y": -63.06972003042238, + "z": 0.0 + }, + { + "x": -2.0508444489775393, + "y": -64.07180475510658, + "z": 0.0 + }, + { + "x": -2.0512304600522295, + "y": -65.07388947979078, + "z": 0.0 + }, + { + "x": -2.05161647112692, + "y": -66.07597420447497, + "z": 0.0 + }, + { + "x": -2.052002482201611, + "y": -67.07805892915917, + "z": 0.0 + }, + { + "x": -2.0523884932763012, + "y": -68.08014365384336, + "z": 0.0 + }, + { + "x": -2.0527745043509924, + "y": -69.08222837852756, + "z": 0.0 + }, + { + "x": -2.0531605154256827, + "y": -70.08431310321176, + "z": 0.0 + }, + { + "x": -2.053546526500374, + "y": -71.08639782789595, + "z": 0.0 + }, + { + "x": -2.053932537575064, + "y": -72.08848255258015, + "z": 0.0 + }, + { + "x": -2.0543185486497544, + "y": -73.09056727726434, + "z": 0.0 + }, + { + "x": -2.0547045597244455, + "y": -74.09265200194854, + "z": 0.0 + }, + { + "x": -2.055090570799136, + "y": -75.09473672663273, + "z": 0.0 + }, + { + "x": -2.055476581873827, + "y": -76.09682145131693, + "z": 0.0 + }, + { + "x": -2.0558625929485173, + "y": -77.09890617600111, + "z": 0.0 + }, + { + "x": -2.0562486040232084, + "y": -78.10099090068532, + "z": 0.0 + }, + { + "x": -2.0566346150978987, + "y": -79.10307562536951, + "z": 0.0 + }, + { + "x": -2.057020626172589, + "y": -80.10516035005371, + "z": 0.0 + }, + { + "x": -2.05740663724728, + "y": -81.10724507473789, + "z": 0.0 + }, + { + "x": -2.0577926483219704, + "y": -82.1093297994221, + "z": 0.0 + }, + { + "x": -2.0581786593966616, + "y": -83.1114145241063, + "z": 0.0 + }, + { + "x": -2.058564670471352, + "y": -84.11349924879049, + "z": 0.0 + }, + { + "x": -2.058950681546042, + "y": -85.11558397347467, + "z": 0.0 + }, + { + "x": -2.0593366926207333, + "y": -86.11766869815888, + "z": 0.0 + }, + { + "x": -2.0597228353699, + "y": -87.12026529887606, + "z": 0.0 + }, + { + "x": -2.0598521187622114, + "y": -88.12365318347021, + "z": 0.0 + }, + { + "x": -2.0593287344692395, + "y": -89.12573784582106, + "z": 0.0 + }, + { + "x": -2.0588053501762675, + "y": -90.12782250817193, + "z": 0.0 + }, + { + "x": -2.0582819658832956, + "y": -91.1299071705228, + "z": 0.0 + }, + { + "x": -2.0577585815903237, + "y": -92.13199183287364, + "z": 0.0 + }, + { + "x": -2.057235197297351, + "y": -93.13407649522449, + "z": 0.0 + }, + { + "x": -2.056711813004379, + "y": -94.13616115757536, + "z": 0.0 + }, + { + "x": -2.056188428711407, + "y": -95.13824581992623, + "z": 0.0 + }, + { + "x": -2.055665044418435, + "y": -96.14033048227708, + "z": 0.0 + }, + { + "x": -2.055141660125463, + "y": -97.14241514462792, + "z": 0.0 + }, + { + "x": -2.0546182758324902, + "y": -98.14449980697879, + "z": 0.0 + }, + { + "x": -2.0540948915395183, + "y": -99.14658446932965, + "z": 0.0 + }, + { + "x": -2.0535715072465464, + "y": -100.1486691316805, + "z": 0.0 + }, + { + "x": -2.0530481229535744, + "y": -101.15075379403136, + "z": 0.0 + }, + { + "x": -2.0525247386606025, + "y": -102.15283845638223, + "z": 0.0 + }, + { + "x": -2.0520013543676305, + "y": -103.15492311873308, + "z": 0.0 + }, + { + "x": -2.0514779700746577, + "y": -104.15700778108393, + "z": 0.0 + }, + { + "x": -2.0509545857816858, + "y": -105.15909244343479, + "z": 0.0 + }, + { + "x": -2.050431201488714, + "y": -106.16117710578565, + "z": 0.0 + }, + { + "x": -2.049907817195742, + "y": -107.16326176813652, + "z": 0.0 + }, + { + "x": -2.04938443290277, + "y": -108.16534643048738, + "z": 0.0 + }, + { + "x": -2.048861048609797, + "y": -109.16743109283821, + "z": 0.0 + }, + { + "x": -2.048337664316825, + "y": -110.16951575518908, + "z": 0.0 + }, + { + "x": -2.0478142800238532, + "y": -111.17160041753995, + "z": 0.0 + }, + { + "x": -2.0472908957308813, + "y": -112.1736850798908, + "z": 0.0 + }, + { + "x": -2.0467675114379094, + "y": -113.17576974224166, + "z": 0.0 + }, + { + "x": -2.0462441271449374, + "y": -114.17785440459252, + "z": 0.0 + }, + { + "x": -2.0457207428519646, + "y": -115.17993906694338, + "z": 0.0 + }, + { + "x": -2.0451973585589927, + "y": -116.18202372929423, + "z": 0.0 + }, + { + "x": -2.0446739742660207, + "y": -117.18410839164508, + "z": 0.0 + }, + { + "x": -2.0441505899730488, + "y": -118.18619305399595, + "z": 0.0 + }, + { + "x": -2.043627205680077, + "y": -119.18827771634682, + "z": 0.0 + }, + { + "x": -2.043103821387105, + "y": -120.19036237869767, + "z": 0.0 + }, + { + "x": -2.042580437094132, + "y": -121.19244704104851, + "z": 0.0 + }, + { + "x": -2.04205705280116, + "y": -122.19453170339938, + "z": 0.0 + }, + { + "x": -2.041533668508188, + "y": -123.19661636575024, + "z": 0.0 + }, + { + "x": -2.0410102842152162, + "y": -124.1987010281011, + "z": 0.0 + }, + { + "x": -2.0404868999222443, + "y": -125.20078569045195, + "z": 0.0 + }, + { + "x": -2.0399635156292715, + "y": -126.20287035280282, + "z": 0.0 + }, + { + "x": -2.0394401313362995, + "y": -127.20495501515367, + "z": 0.0 + }, + { + "x": -2.0389167470433276, + "y": -128.20703967750455, + "z": 0.0 + }, + { + "x": -2.0383933627503557, + "y": -129.2091243398554, + "z": 0.0 + }, + { + "x": -2.0378699784573837, + "y": -130.21120900220626, + "z": 0.0 + }, + { + "x": -2.0373465941644118, + "y": -131.2132936645571, + "z": 0.0 + }, + { + "x": -2.036823209871439, + "y": -132.21537832690797, + "z": 0.0 + }, + { + "x": -2.036299825578467, + "y": -133.21746298925882, + "z": 0.0 + }, + { + "x": -2.035776441285495, + "y": -134.21954765160967, + "z": 0.0 + }, + { + "x": -2.035253056992523, + "y": -135.22163231396053, + "z": 0.0 + }, + { + "x": -2.034729672699551, + "y": -136.22371697631138, + "z": 0.0 + }, + { + "x": -2.0342062884065784, + "y": -137.22580163866223, + "z": 0.0 + }, + { + "x": -2.0336829041136064, + "y": -138.2278863010131, + "z": 0.0 + }, + { + "x": -2.0331595198206345, + "y": -139.22997096336394, + "z": 0.0 + }, + { + "x": -2.0326361355276625, + "y": -140.2320556257148, + "z": 0.0 + }, + { + "x": -2.0321127512346906, + "y": -141.23414028806565, + "z": 0.0 + }, + { + "x": -2.0315893669417187, + "y": -142.2362249504165, + "z": 0.0 + }, + { + "x": -2.031065982648746, + "y": -143.23830961276735, + "z": 0.0 + }, + { + "x": -2.030542598355774, + "y": -144.24039427511815, + "z": 0.0 + }, + { + "x": -2.030019214062802, + "y": -145.242478937469, + "z": 0.0 + }, + { + "x": -2.02949582976983, + "y": -146.24456359981986, + "z": 0.0 + }, + { + "x": -2.028972445476858, + "y": -147.2466482621707, + "z": 0.0 + }, + { + "x": -2.028449061183886, + "y": -148.24873292452156, + "z": 0.0 + }, + { + "x": -2.0279256768909133, + "y": -149.25081758687236, + "z": 0.0 + }, + { + "x": -2.0274022925979414, + "y": -150.2529022492232, + "z": 0.0 + }, + { + "x": -2.0268789083049694, + "y": -151.25498691157406, + "z": 0.0 + }, + { + "x": -2.0263555240119975, + "y": -152.25707157392492, + "z": 0.0 + }, + { + "x": -2.0258321397190255, + "y": -153.25915623627577, + "z": 0.0 + }, + { + "x": -2.0253087554260536, + "y": -154.26124089862662, + "z": 0.0 + }, + { + "x": -2.0247853711330808, + "y": -155.26332556097748, + "z": 0.0 + }, + { + "x": -2.024261986840109, + "y": -156.26541022332833, + "z": 0.0 + }, + { + "x": -2.023738602547137, + "y": -157.26749488567913, + "z": 0.0 + }, + { + "x": -2.023215218254165, + "y": -158.26957954802998, + "z": 0.0 + }, + { + "x": -2.022691833961193, + "y": -159.27166421038083, + "z": 0.0 + }, + { + "x": -2.022168449668221, + "y": -160.27374887273163, + "z": 0.0 + }, + { + "x": -2.0216450653752482, + "y": -161.27583353508248, + "z": 0.0 + }, + { + "x": -2.0211216810822763, + "y": -162.27791819743334, + "z": 0.0 + }, + { + "x": -2.0205982967893044, + "y": -163.2800028597842, + "z": 0.0 + }, + { + "x": -2.0200749124963324, + "y": -164.28208752213504, + "z": 0.0 + }, + { + "x": -2.0195515282033605, + "y": -165.2841721844859, + "z": 0.0 + }, + { + "x": -2.0190281439103885, + "y": -166.28625684683675, + "z": 0.0 + }, + { + "x": -2.0185047596174157, + "y": -167.2883415091876, + "z": 0.0 + }, + { + "x": -2.0179813753244438, + "y": -168.2904261715384, + "z": 0.0 + }, + { + "x": -2.017457991031472, + "y": -169.29251083388925, + "z": 0.0 + }, + { + "x": -2.0169346067385, + "y": -170.2945954962401, + "z": 0.0 + }, + { + "x": -2.016411222445528, + "y": -171.29668015859096, + "z": 0.0 + }, + { + "x": -2.015887838152555, + "y": -172.2987648209418, + "z": 0.0 + }, + { + "x": -2.015364453859583, + "y": -173.3008494832926, + "z": 0.0 + }, + { + "x": -2.0148410695666112, + "y": -174.30293414564346, + "z": 0.0 + }, + { + "x": -2.0143176852736393, + "y": -175.30501880799432, + "z": 0.0 + }, + { + "x": -2.0137943009806674, + "y": -176.30710347034517, + "z": 0.0 + }, + { + "x": -2.0132709166876954, + "y": -177.30918813269602, + "z": 0.0 + }, + { + "x": -2.0127475323947226, + "y": -178.31127279504688, + "z": 0.0 + }, + { + "x": -2.0122241481017507, + "y": -179.31335745739773, + "z": 0.0 + }, + { + "x": -2.0117007638087787, + "y": -180.31544211974858, + "z": 0.0 + }, + { + "x": -2.0111773795158068, + "y": -181.31752678209938, + "z": 0.0 + }, + { + "x": -2.010653995222835, + "y": -182.31961144445023, + "z": 0.0 + }, + { + "x": -2.010130610929863, + "y": -183.32169610680108, + "z": 0.0 + }, + { + "x": -2.00960722663689, + "y": -184.32378076915188, + "z": 0.0 + }, + { + "x": -2.009083842343918, + "y": -185.32586543150273, + "z": 0.0 + }, + { + "x": -2.008560458050946, + "y": -186.3279500938536, + "z": 0.0 + }, + { + "x": -2.0080370737579742, + "y": -187.33003475620444, + "z": 0.0 + }, + { + "x": -2.0075136894650023, + "y": -188.3321194185553, + "z": 0.0 + }, + { + "x": -2.0069903051720304, + "y": -189.33420408090615, + "z": 0.0 + }, + { + "x": -2.0064669208790575, + "y": -190.336288743257, + "z": 0.0 + }, + { + "x": -2.0059435365860856, + "y": -191.33837340560785, + "z": 0.0 + }, + { + "x": -2.0054201522931137, + "y": -192.34045806795865, + "z": 0.0 + }, + { + "x": -2.0048967680001417, + "y": -193.3425427303095, + "z": 0.0 + }, + { + "x": -2.0043733837071698, + "y": -194.34462739266036, + "z": 0.0 + }, + { + "x": -2.003849999414198, + "y": -195.3467120550112, + "z": 0.0 + }, + { + "x": -2.003326615121225, + "y": -196.34879671736206, + "z": 0.0 + }, + { + "x": -2.002803230828253, + "y": -197.35088137971286, + "z": 0.0 + }, + { + "x": -2.002279846535281, + "y": -198.3529660420637, + "z": 0.0 + }, + { + "x": -2.001756462242309, + "y": -199.35505070441457, + "z": 0.0 + }, + { + "x": -2.0012330779493372, + "y": -200.35713536676542, + "z": 0.0 + }, + { + "x": -2.0007096936563653, + "y": -201.35922002911627, + "z": 0.0 + }, + { + "x": -2.0001863093633925, + "y": -202.36130469146713, + "z": 0.0 + }, + { + "x": -1.9996629250704205, + "y": -203.36338935381798, + "z": 0.0 + }, + { + "x": -1.9991395407774486, + "y": -204.36547401616883, + "z": 0.0 + }, + { + "x": -1.9986161564844767, + "y": -205.36755867851963, + "z": 0.0 + }, + { + "x": -1.9980927721915043, + "y": -206.36964334087048, + "z": 0.0 + }, + { + "x": -1.9975693878985323, + "y": -207.37172800322134, + "z": 0.0 + }, + { + "x": -1.9970460036055604, + "y": -208.37381266557213, + "z": 0.0 + }, + { + "x": -1.996522619312588, + "y": -209.37589732792298, + "z": 0.0 + }, + { + "x": -1.995999235019616, + "y": -210.37798199027384, + "z": 0.0 + }, + { + "x": -1.9954758507266441, + "y": -211.3800666526247, + "z": 0.0 + }, + { + "x": -1.9949524664336717, + "y": -212.38215131497554, + "z": 0.0 + }, + { + "x": -1.9944290821406998, + "y": -213.3842359773264, + "z": 0.0 + }, + { + "x": -1.9939056978477279, + "y": -214.38632063967725, + "z": 0.0 + }, + { + "x": -1.9933823135547555, + "y": -215.3884053020281, + "z": 0.0 + }, + { + "x": -1.9928589292617835, + "y": -216.3904899643789, + "z": 0.0 + }, + { + "x": -1.9923355449688116, + "y": -217.39257462672975, + "z": 0.0 + }, + { + "x": -1.9918121606758392, + "y": -218.3946592890806, + "z": 0.0 + }, + { + "x": -1.9912887763828673, + "y": -219.39674395143146, + "z": 0.0 + }, + { + "x": -1.9907653920898953, + "y": -220.3988286137823, + "z": 0.0 + }, + { + "x": -1.990242007796923, + "y": -221.4009132761331, + "z": 0.0 + }, + { + "x": -1.989718623503951, + "y": -222.40299793848396, + "z": 0.0 + }, + { + "x": -1.989195239210979, + "y": -223.40508260083482, + "z": 0.0 + }, + { + "x": -1.9886718549180067, + "y": -224.40716726318567, + "z": 0.0 + }, + { + "x": -1.9881484706250347, + "y": -225.40925192553652, + "z": 0.0 + }, + { + "x": -1.9876250863320628, + "y": -226.41133658788738, + "z": 0.0 + }, + { + "x": -1.9871017020390904, + "y": -227.41342125023823, + "z": 0.0 + }, + { + "x": -1.9865783177461185, + "y": -228.41550591258908, + "z": 0.0 + }, + { + "x": -1.9860549334531465, + "y": -229.41759057493988, + "z": 0.0 + }, + { + "x": -1.9855315491601742, + "y": -230.41967523729073, + "z": 0.0 + }, + { + "x": -1.9850081648672022, + "y": -231.4217598996416, + "z": 0.0 + }, + { + "x": -1.9844847805742303, + "y": -232.42384456199238, + "z": 0.0 + }, + { + "x": -1.983961396281258, + "y": -233.42592922434324, + "z": 0.0 + }, + { + "x": -1.983438011988286, + "y": -234.4280138866941, + "z": 0.0 + }, + { + "x": -1.9829146276953136, + "y": -235.43009854904494, + "z": 0.0 + }, + { + "x": -1.9823912434023416, + "y": -236.4321832113958, + "z": 0.0 + }, + { + "x": -1.9818678591093697, + "y": -237.43426787374665, + "z": 0.0 + }, + { + "x": -1.9813444748163973, + "y": -238.4363525360975, + "z": 0.0 + }, + { + "x": -1.9808210905234254, + "y": -239.43843719844836, + "z": 0.0 + }, + { + "x": -1.9802979208801939, + "y": -240.439959526235, + "z": 0.0 + }, + { + "x": -1.9800563311504915, + "y": -241.441300874255, + "z": 0.0 + }, + { + "x": -1.9801871686771089, + "y": -242.44338566474505, + "z": 0.0 + }, + { + "x": -1.9803180062037262, + "y": -243.4454704552351, + "z": 0.0 + }, + { + "x": -1.980448843730344, + "y": -244.44755524572514, + "z": 0.0 + }, + { + "x": -1.9805796812569614, + "y": -245.44964003621516, + "z": 0.0 + }, + { + "x": -1.9807105187835792, + "y": -246.4517248267052, + "z": 0.0 + }, + { + "x": -1.9808413563101965, + "y": -247.45380961719528, + "z": 0.0 + }, + { + "x": -1.9809721938368134, + "y": -248.4558944076853, + "z": 0.0 + }, + { + "x": -1.9811030313634312, + "y": -249.45797919817534, + "z": 0.0 + }, + { + "x": -1.9812338688900486, + "y": -250.46006398866538, + "z": 0.0 + }, + { + "x": -1.981364706416666, + "y": -251.46214877915543, + "z": 0.0 + }, + { + "x": -1.9814955439432838, + "y": -252.46423356964547, + "z": 0.0 + }, + { + "x": -1.981626381469901, + "y": -253.4663183601355, + "z": 0.0 + }, + { + "x": -1.9817572189965185, + "y": -254.46840315062553, + "z": 0.0 + }, + { + "x": -1.9818880565231358, + "y": -255.47048794111558, + "z": 0.0 + }, + { + "x": -1.9820188940497536, + "y": -256.47257273160557, + "z": 0.0 + }, + { + "x": -1.9821497315763714, + "y": -257.47465752209564, + "z": 0.0 + }, + { + "x": -1.9822805691029888, + "y": -258.4767423125857, + "z": 0.0 + }, + { + "x": -1.9824114066296061, + "y": -259.47882710307573, + "z": 0.0 + }, + { + "x": -1.982542244156224, + "y": -260.4809118935658, + "z": 0.0 + }, + { + "x": -1.9826730816828413, + "y": -261.4829966840558, + "z": 0.0 + }, + { + "x": -1.9828039192094586, + "y": -262.48508147454584, + "z": 0.0 + }, + { + "x": -1.982934756736076, + "y": -263.4871662650359, + "z": 0.0 + }, + { + "x": -1.9830655942626938, + "y": -264.4892510555259, + "z": 0.0 + }, + { + "x": -1.9831964317893112, + "y": -265.49133584601594, + "z": 0.0 + }, + { + "x": -1.9833272693159285, + "y": -266.493420636506, + "z": 0.0 + }, + { + "x": -1.9834581068425459, + "y": -267.4955054269961, + "z": 0.0 + }, + { + "x": -1.9835889443691637, + "y": -268.49759021748616, + "z": 0.0 + }, + { + "x": -1.983719781895781, + "y": -269.49967500797624, + "z": 0.0 + }, + { + "x": -1.9838506194223984, + "y": -270.5017597984663, + "z": 0.0 + }, + { + "x": -1.9839814569490157, + "y": -271.5038445889563, + "z": 0.0 + }, + { + "x": -1.9841122944756335, + "y": -272.5059293794464, + "z": 0.0 + }, + { + "x": -1.9842431320022509, + "y": -273.5080141699365, + "z": 0.0 + }, + { + "x": -1.9843739695288682, + "y": -274.51009896042655, + "z": 0.0 + }, + { + "x": -1.984504807055486, + "y": -275.5121837509166, + "z": 0.0 + }, + { + "x": -1.9846356445821034, + "y": -276.5142685414067, + "z": 0.0 + }, + { + "x": -1.9847664821087208, + "y": -277.51635333189677, + "z": 0.0 + }, + { + "x": -1.984897319635338, + "y": -278.5184381223868, + "z": 0.0 + }, + { + "x": -1.985028157161956, + "y": -279.52052291287686, + "z": 0.0 + }, + { + "x": -1.9851589946885733, + "y": -280.52260770336693, + "z": 0.0 + }, + { + "x": -1.9852898322151906, + "y": -281.524692493857, + "z": 0.0 + }, + { + "x": -1.985420669741808, + "y": -282.5267772843471, + "z": 0.0 + }, + { + "x": -1.9855515072684258, + "y": -283.52886207483715, + "z": 0.0 + }, + { + "x": -1.9856823447950431, + "y": -284.5309468653272, + "z": 0.0 + }, + { + "x": -1.9858131823216605, + "y": -285.5330316558173, + "z": 0.0 + }, + { + "x": -1.9859440198482783, + "y": -286.53511644630737, + "z": 0.0 + }, + { + "x": -1.9860748573748956, + "y": -287.53720123679744, + "z": 0.0 + }, + { + "x": -1.986205694901513, + "y": -288.5392860272875, + "z": 0.0 + }, + { + "x": -1.9863365324281304, + "y": -289.5413708177776, + "z": 0.0 + }, + { + "x": -1.9864673699547482, + "y": -290.54345560826766, + "z": 0.0 + }, + { + "x": -1.9865982074813655, + "y": -291.54554039875774, + "z": 0.0 + }, + { + "x": -1.9867290450079829, + "y": -292.5476251892478, + "z": 0.0 + }, + { + "x": -1.9868598825346002, + "y": -293.5497099797379, + "z": 0.0 + }, + { + "x": -1.986990720061218, + "y": -294.5517947702279, + "z": 0.0 + }, + { + "x": -1.9871215575878354, + "y": -295.553879560718, + "z": 0.0 + }, + { + "x": -1.9872523951144527, + "y": -296.55596435120805, + "z": 0.0 + }, + { + "x": -1.98738323264107, + "y": -297.5580491416981, + "z": 0.0 + }, + { + "x": -1.9875140701676879, + "y": -298.5601339321882, + "z": 0.0 + }, + { + "x": -1.9876449076943052, + "y": -299.56221872267827, + "z": 0.0 + }, + { + "x": -1.9877757452209226, + "y": -300.56430351316834, + "z": 0.0 + }, + { + "x": -1.9879065827475404, + "y": -301.5663883036584, + "z": 0.0 + }, + { + "x": -1.9880374202741578, + "y": -302.5684730941485, + "z": 0.0 + }, + { + "x": -1.988168257800775, + "y": -303.5705578846385, + "z": 0.0 + }, + { + "x": -1.9882990953273925, + "y": -304.5726426751286, + "z": 0.0 + }, + { + "x": -1.9884299328540103, + "y": -305.57472746561865, + "z": 0.0 + }, + { + "x": -1.9885607703806276, + "y": -306.5768122561087, + "z": 0.0 + }, + { + "x": -1.988691607907245, + "y": -307.5788970465988, + "z": 0.0 + }, + { + "x": -1.9888224454338623, + "y": -308.58098183708887, + "z": 0.0 + }, + { + "x": -1.9889532829604801, + "y": -309.58306662757894, + "z": 0.0 + }, + { + "x": -1.9890841204870975, + "y": -310.58515141806896, + "z": 0.0 + }, + { + "x": -1.9892149580137148, + "y": -311.58723620855903, + "z": 0.0 + }, + { + "x": -1.9893457955403326, + "y": -312.5893209990491, + "z": 0.0 + }, + { + "x": -1.98947663306695, + "y": -313.5914057895392, + "z": 0.0 + }, + { + "x": -1.9896074705935674, + "y": -314.59349058002925, + "z": 0.0 + }, + { + "x": -1.9897383081201847, + "y": -315.5955753705193, + "z": 0.0 + }, + { + "x": -1.9898691456468025, + "y": -316.5976601610094, + "z": 0.0 + }, + { + "x": -1.9899999831734199, + "y": -317.5997449514995, + "z": 0.0 + } + ] + }, + { + "id": 80, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.8718824984497, + "y": -46.422407556799655, + "z": 0.0 + }, + { + "x": 340.8724973815697, + "y": -45.40440774249774, + "z": 0.0 + }, + { + "x": 340.8731122646897, + "y": -44.38640792819581, + "z": 0.0 + }, + { + "x": 340.87372714780986, + "y": -43.36840811389433, + "z": 0.0 + }, + { + "x": 340.8743420309299, + "y": -42.35040829959196, + "z": 0.0 + }, + { + "x": 340.8749569140499, + "y": -41.33240848529005, + "z": 0.0 + }, + { + "x": 340.8755717971699, + "y": -40.314408670988115, + "z": 0.0 + }, + { + "x": 340.87618668029, + "y": -39.29640885668642, + "z": 0.0 + }, + { + "x": 340.8768015634101, + "y": -38.2784090423845, + "z": 0.0 + }, + { + "x": 340.8774164465301, + "y": -37.26040922808235, + "z": 0.0 + }, + { + "x": 340.8780313296501, + "y": -36.24240941378043, + "z": 0.0 + }, + { + "x": 340.8786462127702, + "y": -35.22440959947873, + "z": 0.0 + }, + { + "x": 340.87926109589023, + "y": -34.20640978517658, + "z": 0.0 + }, + { + "x": 340.8798759790103, + "y": -33.188409970874886, + "z": 0.0 + }, + { + "x": 340.88049086213033, + "y": -32.170410156572736, + "z": 0.0 + }, + { + "x": 340.8811057452504, + "y": -31.15241034227104, + "z": 0.0 + }, + { + "x": 340.88172062837043, + "y": -30.134410527968893, + "z": 0.0 + }, + { + "x": 340.88233551149045, + "y": -29.11641071366697, + "z": 0.0 + }, + { + "x": 340.88295039461053, + "y": -28.098410899365273, + "z": 0.0 + }, + { + "x": 340.8835652777306, + "y": -27.08041108506335, + "z": 0.0 + }, + { + "x": 340.88418016085063, + "y": -26.062411270761203, + "z": 0.0 + }, + { + "x": 340.88479504397066, + "y": -25.04441145645928, + "z": 0.0 + }, + { + "x": 340.88540992709073, + "y": -24.02641164215758, + "z": 0.0 + }, + { + "x": 340.8860248102108, + "y": -23.008411827855657, + "z": 0.0 + }, + { + "x": 340.88663969333084, + "y": -21.99041201355351, + "z": 0.0 + }, + { + "x": 340.88725457645086, + "y": -20.97241219925159, + "z": 0.0 + }, + { + "x": 340.88786945957094, + "y": -19.95441238494989, + "z": 0.0 + }, + { + "x": 340.88848434269096, + "y": -18.936412570647743, + "z": 0.0 + }, + { + "x": 340.88909922581104, + "y": -17.918412756346047, + "z": 0.0 + }, + { + "x": 340.88971410893106, + "y": -16.9004129420439, + "z": 0.0 + }, + { + "x": 340.89032899205114, + "y": -15.882413127742202, + "z": 0.0 + }, + { + "x": 340.89094387517116, + "y": -14.864413313440057, + "z": 0.0 + }, + { + "x": 340.8915587582912, + "y": -13.846413499138135, + "z": 0.0 + }, + { + "x": 340.89217364141126, + "y": -12.828413684836438, + "z": 0.0 + }, + { + "x": 340.89278852453134, + "y": -11.810413870534514, + "z": 0.0 + }, + { + "x": 340.89340340765136, + "y": -10.792414056232369, + "z": 0.0 + } + ] + }, + { + "id": 81, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.89340486696824, + "y": -10.787581968845162, + "z": 0.0 + }, + { + "x": 332.8927899838482, + "y": -11.80558178314686, + "z": 0.0 + }, + { + "x": 332.89217510072814, + "y": -12.823581597448783, + "z": 0.0 + }, + { + "x": 332.89156021760806, + "y": -13.841581411750928, + "z": 0.0 + }, + { + "x": 332.89094533448804, + "y": -14.85958122605285, + "z": 0.0 + }, + { + "x": 332.890330451368, + "y": -15.877581040354547, + "z": 0.0 + }, + { + "x": 332.88971556824794, + "y": -16.895580854656693, + "z": 0.0 + }, + { + "x": 332.8891006851279, + "y": -17.913580668958392, + "z": 0.0 + }, + { + "x": 332.88848580200784, + "y": -18.931580483260536, + "z": 0.0 + }, + { + "x": 332.8878709188878, + "y": -19.949580297562235, + "z": 0.0 + }, + { + "x": 332.88725603576773, + "y": -20.967580111864383, + "z": 0.0 + }, + { + "x": 332.8866411526477, + "y": -21.985579926166302, + "z": 0.0 + }, + { + "x": 332.8860262695277, + "y": -23.003579740468002, + "z": 0.0 + }, + { + "x": 332.8854113864076, + "y": -24.021579554769925, + "z": 0.0 + }, + { + "x": 332.88479650328753, + "y": -25.039579369072072, + "z": 0.0 + }, + { + "x": 332.8841816201675, + "y": -26.057579183373996, + "z": 0.0 + }, + { + "x": 332.8835667370475, + "y": -27.075578997675695, + "z": 0.0 + }, + { + "x": 332.8829518539274, + "y": -28.09357881197762, + "z": 0.0 + }, + { + "x": 332.88233697080733, + "y": -29.111578626279762, + "z": 0.0 + }, + { + "x": 332.8817220876873, + "y": -30.129578440581685, + "z": 0.0 + }, + { + "x": 332.8811072045673, + "y": -31.147578254883385, + "z": 0.0 + }, + { + "x": 332.8804923214472, + "y": -32.165578069185536, + "z": 0.0 + }, + { + "x": 332.8798774383272, + "y": -33.18357788348723, + "z": 0.0 + }, + { + "x": 332.8792625552071, + "y": -34.20157769778938, + "z": 0.0 + }, + { + "x": 332.8786476720871, + "y": -35.21957751209108, + "z": 0.0 + }, + { + "x": 332.878032788967, + "y": -36.23757732639323, + "z": 0.0 + }, + { + "x": 332.877417905847, + "y": -37.25557714069515, + "z": 0.0 + }, + { + "x": 332.87680302272696, + "y": -38.27357695499685, + "z": 0.0 + }, + { + "x": 332.8761881396069, + "y": -39.291576769298764, + "z": 0.0 + }, + { + "x": 332.8755732564868, + "y": -40.309576583600915, + "z": 0.0 + }, + { + "x": 332.8749583733668, + "y": -41.32757639790284, + "z": 0.0 + }, + { + "x": 332.87434349024676, + "y": -42.34557621220476, + "z": 0.0 + }, + { + "x": 332.87372860712674, + "y": -43.36357602650624, + "z": 0.0 + }, + { + "x": 332.8731137240066, + "y": -44.38157584080861, + "z": 0.0 + }, + { + "x": 332.8724988408866, + "y": -45.399575655110525, + "z": 0.0 + }, + { + "x": 332.87188395776656, + "y": -46.41757546941244, + "z": 0.0 + } + ] + }, + { + "id": 82, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 336.8934041373098, + "y": -10.789998012538765, + "z": 0.0 + }, + { + "x": 336.8927892541898, + "y": -11.807997826840687, + "z": 0.0 + }, + { + "x": 336.8921743710697, + "y": -12.82599764114261, + "z": 0.0 + }, + { + "x": 336.8915594879496, + "y": -13.843997455444532, + "z": 0.0 + }, + { + "x": 336.8909446048296, + "y": -14.861997269746453, + "z": 0.0 + }, + { + "x": 336.8903297217096, + "y": -15.879997084048375, + "z": 0.0 + }, + { + "x": 336.8897148385895, + "y": -16.897996898350296, + "z": 0.0 + }, + { + "x": 336.8890999554695, + "y": -17.91599671265222, + "z": 0.0 + }, + { + "x": 336.8884850723494, + "y": -18.93399652695414, + "z": 0.0 + }, + { + "x": 336.8878701892294, + "y": -19.951996341256063, + "z": 0.0 + }, + { + "x": 336.8872553061093, + "y": -20.969996155557986, + "z": 0.0 + }, + { + "x": 336.8866404229893, + "y": -21.987995969859906, + "z": 0.0 + }, + { + "x": 336.88602553986925, + "y": -23.00599578416183, + "z": 0.0 + }, + { + "x": 336.8854106567492, + "y": -24.023995598463753, + "z": 0.0 + }, + { + "x": 336.8847957736291, + "y": -25.041995412765676, + "z": 0.0 + }, + { + "x": 336.88418089050907, + "y": -26.0599952270676, + "z": 0.0 + }, + { + "x": 336.88356600738905, + "y": -27.077995041369523, + "z": 0.0 + }, + { + "x": 336.88295112426897, + "y": -28.095994855671446, + "z": 0.0 + }, + { + "x": 336.8823362411489, + "y": -29.113994669973366, + "z": 0.0 + }, + { + "x": 336.88172135802887, + "y": -30.13199448427529, + "z": 0.0 + }, + { + "x": 336.88110647490885, + "y": -31.149994298577212, + "z": 0.0 + }, + { + "x": 336.88049159178877, + "y": -32.167994112879136, + "z": 0.0 + }, + { + "x": 336.87987670866875, + "y": -33.18599392718106, + "z": 0.0 + }, + { + "x": 336.87926182554867, + "y": -34.20399374148298, + "z": 0.0 + }, + { + "x": 336.87864694242865, + "y": -35.221993555784906, + "z": 0.0 + }, + { + "x": 336.87803205930857, + "y": -36.23999337008683, + "z": 0.0 + }, + { + "x": 336.87741717618854, + "y": -37.25799318438875, + "z": 0.0 + }, + { + "x": 336.8768022930685, + "y": -38.275992998690676, + "z": 0.0 + }, + { + "x": 336.87618740994844, + "y": -39.29399281299259, + "z": 0.0 + }, + { + "x": 336.87557252682836, + "y": -40.311992627294515, + "z": 0.0 + }, + { + "x": 336.87495764370834, + "y": -41.329992441596445, + "z": 0.0 + }, + { + "x": 336.8743427605883, + "y": -42.34799225589836, + "z": 0.0 + }, + { + "x": 336.8737278774683, + "y": -43.365992070200285, + "z": 0.0 + }, + { + "x": 336.87311299434816, + "y": -44.38399188450221, + "z": 0.0 + }, + { + "x": 336.87249811122814, + "y": -45.40199169880413, + "z": 0.0 + }, + { + "x": 336.8718832281081, + "y": -46.41999151310605, + "z": 0.0 + } + ] + }, + { + "id": 83, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.87188286327887, + "y": -46.42119953495285, + "z": 0.0 + }, + { + "x": 338.87249774639895, + "y": -45.40319972065093, + "z": 0.0 + }, + { + "x": 338.8731126295189, + "y": -44.38519990634901, + "z": 0.0 + }, + { + "x": 338.8737275126391, + "y": -43.36720009204731, + "z": 0.0 + }, + { + "x": 338.8743423957591, + "y": -42.34920027774516, + "z": 0.0 + }, + { + "x": 338.87495727887915, + "y": -41.33120046344325, + "z": 0.0 + }, + { + "x": 338.8755721619991, + "y": -40.313200649141315, + "z": 0.0 + }, + { + "x": 338.8761870451192, + "y": -39.295200834839505, + "z": 0.0 + }, + { + "x": 338.8768019282393, + "y": -38.27720102053759, + "z": 0.0 + }, + { + "x": 338.87741681135935, + "y": -37.25920120623555, + "z": 0.0 + }, + { + "x": 338.8780316944793, + "y": -36.24120139193363, + "z": 0.0 + }, + { + "x": 338.8786465775994, + "y": -35.22320157763182, + "z": 0.0 + }, + { + "x": 338.8792614607195, + "y": -34.20520176332978, + "z": 0.0 + }, + { + "x": 338.87987634383956, + "y": -33.18720194902797, + "z": 0.0 + }, + { + "x": 338.8804912269595, + "y": -32.169202134725936, + "z": 0.0 + }, + { + "x": 338.8811061100796, + "y": -31.151202320424126, + "z": 0.0 + }, + { + "x": 338.8817209931997, + "y": -30.13320250612209, + "z": 0.0 + }, + { + "x": 338.88233587631964, + "y": -29.115202691820166, + "z": 0.0 + }, + { + "x": 338.8829507594397, + "y": -28.09720287751836, + "z": 0.0 + }, + { + "x": 338.8835656425598, + "y": -27.079203063216436, + "z": 0.0 + }, + { + "x": 338.8841805256799, + "y": -26.061203248914403, + "z": 0.0 + }, + { + "x": 338.88479540879985, + "y": -25.04320343461248, + "z": 0.0 + }, + { + "x": 338.8854102919199, + "y": -24.025203620310666, + "z": 0.0 + }, + { + "x": 338.88602517504, + "y": -23.007203806008743, + "z": 0.0 + }, + { + "x": 338.8866400581601, + "y": -21.98920399170671, + "z": 0.0 + }, + { + "x": 338.88725494128005, + "y": -20.971204177404786, + "z": 0.0 + }, + { + "x": 338.8878698244001, + "y": -19.953204363102977, + "z": 0.0 + }, + { + "x": 338.8884847075202, + "y": -18.93520454880094, + "z": 0.0 + }, + { + "x": 338.8890995906403, + "y": -17.917204734499133, + "z": 0.0 + }, + { + "x": 338.88971447376025, + "y": -16.8992049201971, + "z": 0.0 + }, + { + "x": 338.89032935688033, + "y": -15.881205105895289, + "z": 0.0 + }, + { + "x": 338.8909442400004, + "y": -14.863205291593255, + "z": 0.0 + }, + { + "x": 338.8915591231204, + "y": -13.845205477291334, + "z": 0.0 + }, + { + "x": 338.89217400624045, + "y": -12.827205662989524, + "z": 0.0 + }, + { + "x": 338.89278888936053, + "y": -11.8092058486876, + "z": 0.0 + }, + { + "x": 338.8934037724806, + "y": -10.791206034385567, + "z": 0.0 + } + ] + }, + { + "id": 84, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.893404502139, + "y": -10.788789990691964, + "z": 0.0 + }, + { + "x": 334.892789619019, + "y": -11.806789804993773, + "z": 0.0 + }, + { + "x": 334.89217473589895, + "y": -12.824789619295696, + "z": 0.0 + }, + { + "x": 334.89155985277887, + "y": -13.84278943359773, + "z": 0.0 + }, + { + "x": 334.8909449696588, + "y": -14.860789247899651, + "z": 0.0 + }, + { + "x": 334.8903300865388, + "y": -15.878789062201461, + "z": 0.0 + }, + { + "x": 334.88971520341875, + "y": -16.896788876503493, + "z": 0.0 + }, + { + "x": 334.88910032029867, + "y": -17.914788690805306, + "z": 0.0 + }, + { + "x": 334.8884854371786, + "y": -18.93278850510734, + "z": 0.0 + }, + { + "x": 334.8878705540586, + "y": -19.95078831940915, + "z": 0.0 + }, + { + "x": 334.88725567093854, + "y": -20.968788133711186, + "z": 0.0 + }, + { + "x": 334.88664078781846, + "y": -21.986787948013102, + "z": 0.0 + }, + { + "x": 334.8860259046985, + "y": -23.004787762314916, + "z": 0.0 + }, + { + "x": 334.8854110215784, + "y": -24.02278757661684, + "z": 0.0 + }, + { + "x": 334.88479613845834, + "y": -25.040787390918872, + "z": 0.0 + }, + { + "x": 334.88418125533826, + "y": -26.058787205220796, + "z": 0.0 + }, + { + "x": 334.8835663722183, + "y": -27.07678701952261, + "z": 0.0 + }, + { + "x": 334.8829514890982, + "y": -28.094786833824532, + "z": 0.0 + }, + { + "x": 334.88233660597814, + "y": -29.112786648126566, + "z": 0.0 + }, + { + "x": 334.88172172285806, + "y": -30.13078646242849, + "z": 0.0 + }, + { + "x": 334.8811068397381, + "y": -31.1487862767303, + "z": 0.0 + }, + { + "x": 334.880491956618, + "y": -32.166786091032336, + "z": 0.0 + }, + { + "x": 334.87987707349794, + "y": -33.184785905334145, + "z": 0.0 + }, + { + "x": 334.87926219037786, + "y": -34.20278571963618, + "z": 0.0 + }, + { + "x": 334.8786473072579, + "y": -35.22078553393799, + "z": 0.0 + }, + { + "x": 334.8780324241378, + "y": -36.23878534824003, + "z": 0.0 + }, + { + "x": 334.87741754101773, + "y": -37.25678516254195, + "z": 0.0 + }, + { + "x": 334.87680265789777, + "y": -38.27478497684376, + "z": 0.0 + }, + { + "x": 334.8761877747777, + "y": -39.29278479114568, + "z": 0.0 + }, + { + "x": 334.8755728916576, + "y": -40.310784605447715, + "z": 0.0 + }, + { + "x": 334.87495800853753, + "y": -41.32878441974964, + "z": 0.0 + }, + { + "x": 334.87434312541757, + "y": -42.34678423405156, + "z": 0.0 + }, + { + "x": 334.8737282422975, + "y": -43.36478404835326, + "z": 0.0 + }, + { + "x": 334.8731133591774, + "y": -44.38278386265541, + "z": 0.0 + }, + { + "x": 334.87249847605733, + "y": -45.40078367695733, + "z": 0.0 + }, + { + "x": 334.87188359293737, + "y": -46.41878349125925, + "z": 0.0 + } + ] + }, + { + "id": 85, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.8270890483683, + "y": -120.582394028932, + "z": 0.0 + }, + { + "x": 340.827699573135, + "y": -119.57160989958834, + "z": 0.0 + }, + { + "x": 340.82831009790175, + "y": -118.56082577024468, + "z": 0.0 + }, + { + "x": 340.8289206226685, + "y": -117.55004164090101, + "z": 0.0 + }, + { + "x": 340.8295311474352, + "y": -116.53925751155735, + "z": 0.0 + }, + { + "x": 340.8301416722019, + "y": -115.52847338221346, + "z": 0.0 + }, + { + "x": 340.8307521969686, + "y": -114.51768925287003, + "z": 0.0 + }, + { + "x": 340.83136272173533, + "y": -113.50690512352637, + "z": 0.0 + }, + { + "x": 340.83197324650206, + "y": -112.4961209941827, + "z": 0.0 + }, + { + "x": 340.8325837712688, + "y": -111.48533686483904, + "z": 0.0 + }, + { + "x": 340.8331942960355, + "y": -110.47455273549538, + "z": 0.0 + }, + { + "x": 340.8338048208022, + "y": -109.46376860615149, + "z": 0.0 + }, + { + "x": 340.8344153455689, + "y": -108.45298447680806, + "z": 0.0 + }, + { + "x": 340.83502587033564, + "y": -107.4422003474644, + "z": 0.0 + }, + { + "x": 340.83563639510237, + "y": -106.43141621812073, + "z": 0.0 + }, + { + "x": 340.8362469198691, + "y": -105.42063208877707, + "z": 0.0 + }, + { + "x": 340.83685744463577, + "y": -104.40984795943318, + "z": 0.0 + }, + { + "x": 340.8374679694025, + "y": -103.39906383008974, + "z": 0.0 + }, + { + "x": 340.8380784941692, + "y": -102.38827970074608, + "z": 0.0 + }, + { + "x": 340.83868901893595, + "y": -101.37749557140242, + "z": 0.0 + }, + { + "x": 340.8392995437027, + "y": -100.36671144205877, + "z": 0.0 + }, + { + "x": 340.8399100684694, + "y": -99.35592731271511, + "z": 0.0 + }, + { + "x": 340.84052059323614, + "y": -98.34514318337145, + "z": 0.0 + }, + { + "x": 340.84113111800286, + "y": -97.3343590540278, + "z": 0.0 + }, + { + "x": 340.84174164276953, + "y": -96.32357492468391, + "z": 0.0 + }, + { + "x": 340.84235216753626, + "y": -95.31279079534048, + "z": 0.0 + }, + { + "x": 340.842962692303, + "y": -94.30200666599681, + "z": 0.0 + }, + { + "x": 340.8435732170697, + "y": -93.29122253665317, + "z": 0.0 + }, + { + "x": 340.84418374183645, + "y": -92.2804384073095, + "z": 0.0 + }, + { + "x": 340.8447942666031, + "y": -91.26965427796561, + "z": 0.0 + }, + { + "x": 340.84540479136984, + "y": -90.25887014862218, + "z": 0.0 + }, + { + "x": 340.8460153161366, + "y": -89.24808601927853, + "z": 0.0 + }, + { + "x": 340.8466258409033, + "y": -88.23730188993487, + "z": 0.0 + }, + { + "x": 340.84723636567, + "y": -87.22651776059121, + "z": 0.0 + }, + { + "x": 340.84784689043676, + "y": -86.21573363124755, + "z": 0.0 + }, + { + "x": 340.8484574152035, + "y": -85.2049495019039, + "z": 0.0 + }, + { + "x": 340.84906793997015, + "y": -84.19416537256001, + "z": 0.0 + }, + { + "x": 340.84967846473694, + "y": -83.1833812432168, + "z": 0.0 + }, + { + "x": 340.8502889895036, + "y": -82.1725971138727, + "z": 0.0 + }, + { + "x": 340.85089951427034, + "y": -81.16181298452926, + "z": 0.0 + }, + { + "x": 340.85151003903707, + "y": -80.1510288551856, + "z": 0.0 + }, + { + "x": 340.85212056380374, + "y": -79.14024472584171, + "z": 0.0 + }, + { + "x": 340.85273108857047, + "y": -78.12946059649829, + "z": 0.0 + }, + { + "x": 340.8533416133372, + "y": -77.11867646715463, + "z": 0.0 + }, + { + "x": 340.8539521381039, + "y": -76.10789233781097, + "z": 0.0 + }, + { + "x": 340.85456266287065, + "y": -75.0971082084673, + "z": 0.0 + }, + { + "x": 340.8551731876374, + "y": -74.08632407912366, + "z": 0.0 + }, + { + "x": 340.8557837124041, + "y": -73.07553994978, + "z": 0.0 + }, + { + "x": 340.85639423717083, + "y": -72.06475582043633, + "z": 0.0 + }, + { + "x": 340.8570047619375, + "y": -71.05397169109244, + "z": 0.0 + }, + { + "x": 340.8576152867043, + "y": -70.04318756174925, + "z": 0.0 + }, + { + "x": 340.85822581147096, + "y": -69.03240343240513, + "z": 0.0 + } + ] + }, + { + "id": 86, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.85822727078784, + "y": -69.0275713450181, + "z": 0.0 + }, + { + "x": 332.85761674602116, + "y": -70.03835547436131, + "z": 0.0 + }, + { + "x": 332.8570062212544, + "y": -71.04913960370541, + "z": 0.0 + }, + { + "x": 332.8563956964877, + "y": -72.05992373304885, + "z": 0.0 + }, + { + "x": 332.855785171721, + "y": -73.07070786239251, + "z": 0.0 + }, + { + "x": 332.85517464695425, + "y": -74.08149199173617, + "z": 0.0 + }, + { + "x": 332.8545641221875, + "y": -75.09227612107982, + "z": 0.0 + }, + { + "x": 332.8539535974208, + "y": -76.10306025042348, + "z": 0.0 + }, + { + "x": 332.85334307265407, + "y": -77.11384437976714, + "z": 0.0 + }, + { + "x": 332.85273254788734, + "y": -78.1246285091108, + "z": 0.0 + }, + { + "x": 332.8521220231206, + "y": -79.13541263845468, + "z": 0.0 + }, + { + "x": 332.85151149835394, + "y": -80.14619676779812, + "z": 0.0 + }, + { + "x": 332.8509009735872, + "y": -81.15698089714178, + "z": 0.0 + }, + { + "x": 332.8502904488205, + "y": -82.16776502648567, + "z": 0.0 + }, + { + "x": 332.8496799240538, + "y": -83.17854915582886, + "z": 0.0 + }, + { + "x": 332.84906939928703, + "y": -84.18933328517298, + "z": 0.0 + }, + { + "x": 332.84845887452036, + "y": -85.20011741451641, + "z": 0.0 + }, + { + "x": 332.84784834975363, + "y": -86.21090154386006, + "z": 0.0 + }, + { + "x": 332.8472378249869, + "y": -87.22168567320372, + "z": 0.0 + }, + { + "x": 332.8466273002202, + "y": -88.23246980254739, + "z": 0.0 + }, + { + "x": 332.84601677545345, + "y": -89.24325393189105, + "z": 0.0 + }, + { + "x": 332.8454062506867, + "y": -90.2540380612347, + "z": 0.0 + }, + { + "x": 332.84479572592, + "y": -91.26482219057858, + "z": 0.0 + }, + { + "x": 332.8441852011533, + "y": -92.27560631992202, + "z": 0.0 + }, + { + "x": 332.8435746763866, + "y": -93.28639044926568, + "z": 0.0 + }, + { + "x": 332.84296415161987, + "y": -94.29717457860933, + "z": 0.0 + }, + { + "x": 332.84235362685314, + "y": -95.30795870795299, + "z": 0.0 + }, + { + "x": 332.8417431020864, + "y": -96.31874283729688, + "z": 0.0 + }, + { + "x": 332.84113257731974, + "y": -97.32952696664032, + "z": 0.0 + }, + { + "x": 332.840522052553, + "y": -98.34031109598396, + "z": 0.0 + }, + { + "x": 332.8399115277863, + "y": -99.35109522532763, + "z": 0.0 + }, + { + "x": 332.83930100301956, + "y": -100.36187935467129, + "z": 0.0 + }, + { + "x": 332.83869047825283, + "y": -101.37266348401494, + "z": 0.0 + }, + { + "x": 332.8380799534861, + "y": -102.3834476133586, + "z": 0.0 + }, + { + "x": 332.8374694287194, + "y": -103.39423174270226, + "z": 0.0 + }, + { + "x": 332.83685890395265, + "y": -104.40501587204615, + "z": 0.0 + }, + { + "x": 332.836248379186, + "y": -105.41580000138958, + "z": 0.0 + }, + { + "x": 332.83563785441925, + "y": -106.42658413073325, + "z": 0.0 + }, + { + "x": 332.8350273296525, + "y": -107.43736826007691, + "z": 0.0 + }, + { + "x": 332.8344168048858, + "y": -108.44815238942057, + "z": 0.0 + }, + { + "x": 332.83380628011906, + "y": -109.45893651876446, + "z": 0.0 + }, + { + "x": 332.8331957553524, + "y": -110.4697206481079, + "z": 0.0 + }, + { + "x": 332.83258523058566, + "y": -111.48050477745156, + "z": 0.0 + }, + { + "x": 332.83197470581894, + "y": -112.49128890679522, + "z": 0.0 + }, + { + "x": 332.8313641810522, + "y": -113.50207303613888, + "z": 0.0 + }, + { + "x": 332.8307536562855, + "y": -114.51285716548254, + "z": 0.0 + }, + { + "x": 332.83014313151875, + "y": -115.52364129482643, + "z": 0.0 + }, + { + "x": 332.8295326067521, + "y": -116.53442542416987, + "z": 0.0 + }, + { + "x": 332.82892208198535, + "y": -117.54520955351353, + "z": 0.0 + }, + { + "x": 332.8283115572186, + "y": -118.55599368285719, + "z": 0.0 + }, + { + "x": 332.8277010324519, + "y": -119.56677781220085, + "z": 0.0 + }, + { + "x": 332.82709050768517, + "y": -120.57756194154452, + "z": 0.0 + } + ] + }, + { + "id": 87, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 336.8582265411294, + "y": -69.02998738871162, + "z": 0.0 + }, + { + "x": 336.8576160163627, + "y": -70.04077151805528, + "z": 0.0 + }, + { + "x": 336.85700549159594, + "y": -71.05155564739893, + "z": 0.0 + }, + { + "x": 336.85639496682927, + "y": -72.06233977674259, + "z": 0.0 + }, + { + "x": 336.85578444206254, + "y": -73.07312390608625, + "z": 0.0 + }, + { + "x": 336.8551739172958, + "y": -74.08390803542991, + "z": 0.0 + }, + { + "x": 336.8545633925291, + "y": -75.09469216477356, + "z": 0.0 + }, + { + "x": 336.85395286776236, + "y": -76.10547629411722, + "z": 0.0 + }, + { + "x": 336.85334234299563, + "y": -77.11626042346089, + "z": 0.0 + }, + { + "x": 336.8527318182289, + "y": -78.12704455280455, + "z": 0.0 + }, + { + "x": 336.8521212934622, + "y": -79.1378286821482, + "z": 0.0 + }, + { + "x": 336.8515107686955, + "y": -80.14861281149186, + "z": 0.0 + }, + { + "x": 336.8509002439288, + "y": -81.15939694083552, + "z": 0.0 + }, + { + "x": 336.85028971916205, + "y": -82.17018107017918, + "z": 0.0 + }, + { + "x": 336.8496791943954, + "y": -83.18096519952283, + "z": 0.0 + }, + { + "x": 336.8490686696286, + "y": -84.19174932886649, + "z": 0.0 + }, + { + "x": 336.8484581448619, + "y": -85.20253345821016, + "z": 0.0 + }, + { + "x": 336.8478476200952, + "y": -86.2133175875538, + "z": 0.0 + }, + { + "x": 336.84723709532847, + "y": -87.22410171689747, + "z": 0.0 + }, + { + "x": 336.84662657056174, + "y": -88.23488584624113, + "z": 0.0 + }, + { + "x": 336.846016045795, + "y": -89.24566997558479, + "z": 0.0 + }, + { + "x": 336.8454055210283, + "y": -90.25645410492844, + "z": 0.0 + }, + { + "x": 336.84479499626156, + "y": -91.2672382342721, + "z": 0.0 + }, + { + "x": 336.8441844714949, + "y": -92.27802236361576, + "z": 0.0 + }, + { + "x": 336.84357394672816, + "y": -93.28880649295942, + "z": 0.0 + }, + { + "x": 336.84296342196143, + "y": -94.29959062230307, + "z": 0.0 + }, + { + "x": 336.8423528971947, + "y": -95.31037475164673, + "z": 0.0 + }, + { + "x": 336.841742372428, + "y": -96.3211588809904, + "z": 0.0 + }, + { + "x": 336.8411318476613, + "y": -97.33194301033406, + "z": 0.0 + }, + { + "x": 336.8405213228946, + "y": -98.3427271396777, + "z": 0.0 + }, + { + "x": 336.83991079812785, + "y": -99.35351126902137, + "z": 0.0 + }, + { + "x": 336.8393002733611, + "y": -100.36429539836503, + "z": 0.0 + }, + { + "x": 336.8386897485944, + "y": -101.37507952770868, + "z": 0.0 + }, + { + "x": 336.83807922382766, + "y": -102.38586365705234, + "z": 0.0 + }, + { + "x": 336.83746869906093, + "y": -103.396647786396, + "z": 0.0 + }, + { + "x": 336.8368581742942, + "y": -104.40743191573966, + "z": 0.0 + }, + { + "x": 336.83624764952754, + "y": -105.41821604508333, + "z": 0.0 + }, + { + "x": 336.8356371247608, + "y": -106.42900017442699, + "z": 0.0 + }, + { + "x": 336.8350265999941, + "y": -107.43978430377065, + "z": 0.0 + }, + { + "x": 336.83441607522735, + "y": -108.45056843311431, + "z": 0.0 + }, + { + "x": 336.8338055504606, + "y": -109.46135256245798, + "z": 0.0 + }, + { + "x": 336.83319502569395, + "y": -110.47213669180164, + "z": 0.0 + }, + { + "x": 336.8325845009272, + "y": -111.4829208211453, + "z": 0.0 + }, + { + "x": 336.8319739761605, + "y": -112.49370495048896, + "z": 0.0 + }, + { + "x": 336.83136345139377, + "y": -113.50448907983262, + "z": 0.0 + }, + { + "x": 336.83075292662704, + "y": -114.51527320917629, + "z": 0.0 + }, + { + "x": 336.8301424018603, + "y": -115.52605733851995, + "z": 0.0 + }, + { + "x": 336.82953187709364, + "y": -116.53684146786361, + "z": 0.0 + }, + { + "x": 336.8289213523269, + "y": -117.54762559720727, + "z": 0.0 + }, + { + "x": 336.8283108275602, + "y": -118.55840972655093, + "z": 0.0 + }, + { + "x": 336.82770030279346, + "y": -119.5691938558946, + "z": 0.0 + }, + { + "x": 336.82708977802673, + "y": -120.57997798523826, + "z": 0.0 + } + ] + }, + { + "id": 88, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.82708941319754, + "y": -120.58118600708514, + "z": 0.0 + }, + { + "x": 338.8276999379642, + "y": -119.57040187774146, + "z": 0.0 + }, + { + "x": 338.828310462731, + "y": -118.55961774839781, + "z": 0.0 + }, + { + "x": 338.82892098749767, + "y": -117.54883361905414, + "z": 0.0 + }, + { + "x": 338.82953151226445, + "y": -116.53804948971049, + "z": 0.0 + }, + { + "x": 338.8301420370311, + "y": -115.5272653603667, + "z": 0.0 + }, + { + "x": 338.8307525617978, + "y": -114.51648123102316, + "z": 0.0 + }, + { + "x": 338.8313630865646, + "y": -113.50569710167949, + "z": 0.0 + }, + { + "x": 338.83197361133125, + "y": -112.49491297233584, + "z": 0.0 + }, + { + "x": 338.83258413609803, + "y": -111.48412884299216, + "z": 0.0 + }, + { + "x": 338.8331946608647, + "y": -110.47334471364852, + "z": 0.0 + }, + { + "x": 338.8338051856314, + "y": -109.46256058430473, + "z": 0.0 + }, + { + "x": 338.83441571039816, + "y": -108.45177645496119, + "z": 0.0 + }, + { + "x": 338.83502623516483, + "y": -107.44099232561751, + "z": 0.0 + }, + { + "x": 338.8356367599316, + "y": -106.43020819627387, + "z": 0.0 + }, + { + "x": 338.8362472846983, + "y": -105.41942406693019, + "z": 0.0 + }, + { + "x": 338.83685780946496, + "y": -104.40863993758643, + "z": 0.0 + }, + { + "x": 338.83746833423174, + "y": -103.39785580824287, + "z": 0.0 + }, + { + "x": 338.8380788589984, + "y": -102.38707167889922, + "z": 0.0 + }, + { + "x": 338.8386893837652, + "y": -101.37628754955554, + "z": 0.0 + }, + { + "x": 338.83929990853187, + "y": -100.3655034202119, + "z": 0.0 + }, + { + "x": 338.83991043329866, + "y": -99.35471929086825, + "z": 0.0 + }, + { + "x": 338.8405209580653, + "y": -98.34393516152457, + "z": 0.0 + }, + { + "x": 338.8411314828321, + "y": -97.33315103218092, + "z": 0.0 + }, + { + "x": 338.8417420075988, + "y": -96.32236690283716, + "z": 0.0 + }, + { + "x": 338.84235253236545, + "y": -95.3115827734936, + "z": 0.0 + }, + { + "x": 338.84296305713224, + "y": -94.30079864414995, + "z": 0.0 + }, + { + "x": 338.8435735818989, + "y": -93.2900145148063, + "z": 0.0 + }, + { + "x": 338.8441841066657, + "y": -92.27923038546263, + "z": 0.0 + }, + { + "x": 338.84479463143236, + "y": -91.26844625611886, + "z": 0.0 + }, + { + "x": 338.84540515619904, + "y": -90.2576621267753, + "z": 0.0 + }, + { + "x": 338.8460156809658, + "y": -89.24687799743165, + "z": 0.0 + }, + { + "x": 338.8466262057325, + "y": -88.236093868088, + "z": 0.0 + }, + { + "x": 338.8472367304993, + "y": -87.22530973874433, + "z": 0.0 + }, + { + "x": 338.84784725526595, + "y": -86.21452560940068, + "z": 0.0 + }, + { + "x": 338.84845778003273, + "y": -85.20374148005703, + "z": 0.0 + }, + { + "x": 338.8490683047994, + "y": -84.19295735071324, + "z": 0.0 + }, + { + "x": 338.8496788295662, + "y": -83.18217322136982, + "z": 0.0 + }, + { + "x": 338.85028935433286, + "y": -82.17138909202595, + "z": 0.0 + }, + { + "x": 338.85089987909953, + "y": -81.16060496268238, + "z": 0.0 + }, + { + "x": 338.8515104038663, + "y": -80.14982083333874, + "z": 0.0 + }, + { + "x": 338.852120928633, + "y": -79.13903670399495, + "z": 0.0 + }, + { + "x": 338.85273145339966, + "y": -78.12825257465141, + "z": 0.0 + }, + { + "x": 338.85334197816644, + "y": -77.11746844530776, + "z": 0.0 + }, + { + "x": 338.8539525029331, + "y": -76.10668431596409, + "z": 0.0 + }, + { + "x": 338.8545630276999, + "y": -75.09590018662044, + "z": 0.0 + }, + { + "x": 338.85517355246657, + "y": -74.08511605727679, + "z": 0.0 + }, + { + "x": 338.85578407723335, + "y": -73.07433192793312, + "z": 0.0 + }, + { + "x": 338.856394602, + "y": -72.06354779858947, + "z": 0.0 + }, + { + "x": 338.8570051267667, + "y": -71.05276366924568, + "z": 0.0 + }, + { + "x": 338.8576156515335, + "y": -70.04197953990226, + "z": 0.0 + }, + { + "x": 338.85822617630015, + "y": -69.03119541055838, + "z": 0.0 + } + ] + }, + { + "id": 89, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.85822690595865, + "y": -69.02877936686485, + "z": 0.0 + }, + { + "x": 334.857616381192, + "y": -70.0395634962083, + "z": 0.0 + }, + { + "x": 334.8570058564252, + "y": -71.05034762555218, + "z": 0.0 + }, + { + "x": 334.8563953316585, + "y": -72.06113175489571, + "z": 0.0 + }, + { + "x": 334.85578480689173, + "y": -73.07191588423939, + "z": 0.0 + }, + { + "x": 334.85517428212506, + "y": -74.08270001358304, + "z": 0.0 + }, + { + "x": 334.8545637573583, + "y": -75.09348414292668, + "z": 0.0 + }, + { + "x": 334.8539532325916, + "y": -76.10426827227036, + "z": 0.0 + }, + { + "x": 334.8533427078248, + "y": -77.11505240161401, + "z": 0.0 + }, + { + "x": 334.85273218305815, + "y": -78.12583653095768, + "z": 0.0 + }, + { + "x": 334.85212165829137, + "y": -79.13662066030145, + "z": 0.0 + }, + { + "x": 334.8515111335247, + "y": -80.14740478964498, + "z": 0.0 + }, + { + "x": 334.850900608758, + "y": -81.15818891898866, + "z": 0.0 + }, + { + "x": 334.85029008399124, + "y": -82.16897304833242, + "z": 0.0 + }, + { + "x": 334.84967955922457, + "y": -83.17975717767584, + "z": 0.0 + }, + { + "x": 334.8490690344578, + "y": -84.19054130701974, + "z": 0.0 + }, + { + "x": 334.8484585096911, + "y": -85.20132543636328, + "z": 0.0 + }, + { + "x": 334.84784798492444, + "y": -86.21210956570692, + "z": 0.0 + }, + { + "x": 334.84723746015766, + "y": -87.2228936950506, + "z": 0.0 + }, + { + "x": 334.846626935391, + "y": -88.23367782439425, + "z": 0.0 + }, + { + "x": 334.8460164106242, + "y": -89.24446195373793, + "z": 0.0 + }, + { + "x": 334.84540588585753, + "y": -90.25524608308157, + "z": 0.0 + }, + { + "x": 334.84479536109075, + "y": -91.26603021242533, + "z": 0.0 + }, + { + "x": 334.8441848363241, + "y": -92.2768143417689, + "z": 0.0 + }, + { + "x": 334.8435743115574, + "y": -93.28759847111255, + "z": 0.0 + }, + { + "x": 334.8429637867906, + "y": -94.2983826004562, + "z": 0.0 + }, + { + "x": 334.84235326202395, + "y": -95.30916672979987, + "z": 0.0 + }, + { + "x": 334.84174273725716, + "y": -96.31995085914363, + "z": 0.0 + }, + { + "x": 334.8411322124905, + "y": -97.3307349884872, + "z": 0.0 + }, + { + "x": 334.8405216877238, + "y": -98.34151911783084, + "z": 0.0 + }, + { + "x": 334.83991116295704, + "y": -99.35230324717449, + "z": 0.0 + }, + { + "x": 334.83930063819037, + "y": -100.36308737651817, + "z": 0.0 + }, + { + "x": 334.8386901134236, + "y": -101.37387150586181, + "z": 0.0 + }, + { + "x": 334.8380795886569, + "y": -102.38465563520546, + "z": 0.0 + }, + { + "x": 334.8374690638901, + "y": -103.39543976454914, + "z": 0.0 + }, + { + "x": 334.83685853912345, + "y": -104.4062238938929, + "z": 0.0 + }, + { + "x": 334.8362480143568, + "y": -105.41700802323646, + "z": 0.0 + }, + { + "x": 334.83563748959, + "y": -106.42779215258011, + "z": 0.0 + }, + { + "x": 334.8350269648233, + "y": -107.43857628192379, + "z": 0.0 + }, + { + "x": 334.83441644005654, + "y": -108.44936041126743, + "z": 0.0 + }, + { + "x": 334.8338059152899, + "y": -109.46014454061122, + "z": 0.0 + }, + { + "x": 334.8331953905232, + "y": -110.47092866995476, + "z": 0.0 + }, + { + "x": 334.8325848657564, + "y": -111.48171279929844, + "z": 0.0 + }, + { + "x": 334.83197434098975, + "y": -112.49249692864208, + "z": 0.0 + }, + { + "x": 334.83136381622296, + "y": -113.50328105798576, + "z": 0.0 + }, + { + "x": 334.8307532914563, + "y": -114.51406518732941, + "z": 0.0 + }, + { + "x": 334.8301427666895, + "y": -115.5248493166732, + "z": 0.0 + }, + { + "x": 334.82953224192283, + "y": -116.53563344601673, + "z": 0.0 + }, + { + "x": 334.82892171715616, + "y": -117.54641757536041, + "z": 0.0 + }, + { + "x": 334.8283111923894, + "y": -118.55720170470406, + "z": 0.0 + }, + { + "x": 334.8277006676227, + "y": -119.56798583404773, + "z": 0.0 + }, + { + "x": 334.8270901428559, + "y": -120.57876996339138, + "z": 0.0 + } + ] + }, + { + "id": 90, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.78778605758094, + "y": -185.65238215921298, + "z": 0.0 + }, + { + "x": 340.7884045058386, + "y": -184.62847990696335, + "z": 0.0 + }, + { + "x": 340.78902295409637, + "y": -183.60457765471395, + "z": 0.0 + }, + { + "x": 340.789641402354, + "y": -182.5806754024639, + "z": 0.0 + }, + { + "x": 340.7902598506117, + "y": -181.5567731502145, + "z": 0.0 + }, + { + "x": 340.7908782988694, + "y": -180.5328708979651, + "z": 0.0 + }, + { + "x": 340.7914967471271, + "y": -179.50896864571524, + "z": 0.0 + }, + { + "x": 340.7921151953848, + "y": -178.48506639346562, + "z": 0.0 + }, + { + "x": 340.79273364364246, + "y": -177.461164141216, + "z": 0.0 + }, + { + "x": 340.79335209190015, + "y": -176.4372618889664, + "z": 0.0 + }, + { + "x": 340.7939705401579, + "y": -175.413359636717, + "z": 0.0 + }, + { + "x": 340.79458898841557, + "y": -174.38945738446714, + "z": 0.0 + }, + { + "x": 340.79520743667325, + "y": -173.3655551322175, + "z": 0.0 + }, + { + "x": 340.79582588493093, + "y": -172.34165287996788, + "z": 0.0 + }, + { + "x": 340.7964443331886, + "y": -171.31775062771825, + "z": 0.0 + }, + { + "x": 340.79706278144636, + "y": -170.29384837546888, + "z": 0.0 + }, + { + "x": 340.79768122970404, + "y": -169.26994612321903, + "z": 0.0 + }, + { + "x": 340.7982996779617, + "y": -168.2460438709694, + "z": 0.0 + }, + { + "x": 340.7989181262194, + "y": -167.22214161871977, + "z": 0.0 + }, + { + "x": 340.7995365744771, + "y": -166.19823936647015, + "z": 0.0 + }, + { + "x": 340.8001550227348, + "y": -165.17433711422075, + "z": 0.0 + }, + { + "x": 340.8007734709925, + "y": -164.1504348619709, + "z": 0.0 + }, + { + "x": 340.8013919192502, + "y": -163.1265326097213, + "z": 0.0 + }, + { + "x": 340.80201036750793, + "y": -162.1026303574719, + "z": 0.0 + }, + { + "x": 340.8026288157656, + "y": -161.07872810522204, + "z": 0.0 + }, + { + "x": 340.8032472640233, + "y": -160.0548258529724, + "z": 0.0 + }, + { + "x": 340.803865712281, + "y": -159.0309236007228, + "z": 0.0 + }, + { + "x": 340.80448416053866, + "y": -158.0070213484732, + "z": 0.0 + }, + { + "x": 340.8051026087964, + "y": -156.9831190962238, + "z": 0.0 + }, + { + "x": 340.8057210570541, + "y": -155.95921684397393, + "z": 0.0 + }, + { + "x": 340.80633950531177, + "y": -154.9353145917243, + "z": 0.0 + }, + { + "x": 340.80695795356945, + "y": -153.91141233947468, + "z": 0.0 + }, + { + "x": 340.80757640182713, + "y": -152.88751008722505, + "z": 0.0 + }, + { + "x": 340.8081948500849, + "y": -151.86360783497565, + "z": 0.0 + }, + { + "x": 340.80881329834256, + "y": -150.83970558272583, + "z": 0.0 + }, + { + "x": 340.80943174660024, + "y": -149.8158033304762, + "z": 0.0 + }, + { + "x": 340.8100501948579, + "y": -148.79190107822657, + "z": 0.0 + }, + { + "x": 340.8106686431156, + "y": -147.76799882597695, + "z": 0.0 + }, + { + "x": 340.81128709137334, + "y": -146.74409657372755, + "z": 0.0 + }, + { + "x": 340.81190553963097, + "y": -145.7201943214775, + "z": 0.0 + }, + { + "x": 340.8125239878887, + "y": -144.69629206922832, + "z": 0.0 + }, + { + "x": 340.8131424361464, + "y": -143.67238981697847, + "z": 0.0 + } + ] + }, + { + "id": 91, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.81314389546327, + "y": -143.66755772959112, + "z": 0.0 + }, + { + "x": 332.8125254472056, + "y": -144.69145998184052, + "z": 0.0 + }, + { + "x": 332.81190699894785, + "y": -145.7153622340906, + "z": 0.0 + }, + { + "x": 332.8112885506902, + "y": -146.73926448633975, + "z": 0.0 + }, + { + "x": 332.8106701024325, + "y": -147.7631667385896, + "z": 0.0 + }, + { + "x": 332.8100516541748, + "y": -148.78706899083923, + "z": 0.0 + }, + { + "x": 332.8094332059171, + "y": -149.81097124308886, + "z": 0.0 + }, + { + "x": 332.80881475765943, + "y": -150.83487349533848, + "z": 0.0 + }, + { + "x": 332.80819630940175, + "y": -151.85877574758786, + "z": 0.0 + }, + { + "x": 332.807577861144, + "y": -152.8826779998377, + "z": 0.0 + }, + { + "x": 332.80695941288633, + "y": -153.90658025208734, + "z": 0.0 + }, + { + "x": 332.80634096462865, + "y": -154.93048250433696, + "z": 0.0 + }, + { + "x": 332.80572251637096, + "y": -155.9543847565866, + "z": 0.0 + }, + { + "x": 332.8051040681133, + "y": -156.978287008836, + "z": 0.0 + }, + { + "x": 332.80448561985554, + "y": -158.00218926108585, + "z": 0.0 + }, + { + "x": 332.80386717159786, + "y": -159.02609151333544, + "z": 0.0 + }, + { + "x": 332.8032487233402, + "y": -160.04999376558507, + "z": 0.0 + }, + { + "x": 332.8026302750825, + "y": -161.0738960178347, + "z": 0.0 + }, + { + "x": 332.8020118268248, + "y": -162.0977982700841, + "z": 0.0 + }, + { + "x": 332.80139337856707, + "y": -163.12170052233395, + "z": 0.0 + }, + { + "x": 332.8007749303094, + "y": -164.14560277458355, + "z": 0.0 + }, + { + "x": 332.8001564820517, + "y": -165.16950502683295, + "z": 0.0 + }, + { + "x": 332.79953803379397, + "y": -166.1934072790828, + "z": 0.0 + }, + { + "x": 332.7989195855363, + "y": -167.21730953133243, + "z": 0.0 + }, + { + "x": 332.7983011372786, + "y": -168.24121178358206, + "z": 0.0 + }, + { + "x": 332.7976826890209, + "y": -169.2651140358317, + "z": 0.0 + }, + { + "x": 332.79706424076323, + "y": -170.2890162880811, + "z": 0.0 + }, + { + "x": 332.7964457925055, + "y": -171.3129185403309, + "z": 0.0 + }, + { + "x": 332.7958273442478, + "y": -172.33682079258054, + "z": 0.0 + }, + { + "x": 332.79520889599013, + "y": -173.36072304483017, + "z": 0.0 + }, + { + "x": 332.79459044773245, + "y": -174.3846252970798, + "z": 0.0 + }, + { + "x": 332.79397199947476, + "y": -175.4085275493292, + "z": 0.0 + }, + { + "x": 332.793353551217, + "y": -176.43242980157905, + "z": 0.0 + }, + { + "x": 332.79273510295934, + "y": -177.45633205382865, + "z": 0.0 + }, + { + "x": 332.79211665470166, + "y": -178.48023430607827, + "z": 0.0 + }, + { + "x": 332.791498206444, + "y": -179.5041365583279, + "z": 0.0 + }, + { + "x": 332.7908797581863, + "y": -180.5280388105773, + "z": 0.0 + }, + { + "x": 332.79026130992855, + "y": -181.55194106282715, + "z": 0.0 + }, + { + "x": 332.78964286167087, + "y": -182.575843315077, + "z": 0.0 + }, + { + "x": 332.78902441341324, + "y": -183.59974556732615, + "z": 0.0 + }, + { + "x": 332.7884059651555, + "y": -184.623647819576, + "z": 0.0 + }, + { + "x": 332.7877875168978, + "y": -185.64755007182563, + "z": 0.0 + } + ] + }, + { + "id": 92, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 336.81314316580483, + "y": -143.6699737732848, + "z": 0.0 + }, + { + "x": 336.81252471754715, + "y": -144.69387602553442, + "z": 0.0 + }, + { + "x": 336.8119062692894, + "y": -145.71777827778405, + "z": 0.0 + }, + { + "x": 336.8112878210318, + "y": -146.74168053003365, + "z": 0.0 + }, + { + "x": 336.81066937277404, + "y": -147.76558278228327, + "z": 0.0 + }, + { + "x": 336.81005092451636, + "y": -148.7894850345329, + "z": 0.0 + }, + { + "x": 336.8094324762587, + "y": -149.81338728678253, + "z": 0.0 + }, + { + "x": 336.808814028001, + "y": -150.83728953903216, + "z": 0.0 + }, + { + "x": 336.8081955797433, + "y": -151.86119179128175, + "z": 0.0 + }, + { + "x": 336.8075771314856, + "y": -152.88509404353138, + "z": 0.0 + }, + { + "x": 336.8069586832279, + "y": -153.908996295781, + "z": 0.0 + }, + { + "x": 336.8063402349702, + "y": -154.93289854803064, + "z": 0.0 + }, + { + "x": 336.8057217867125, + "y": -155.95680080028026, + "z": 0.0 + }, + { + "x": 336.80510333845484, + "y": -156.9807030525299, + "z": 0.0 + }, + { + "x": 336.8044848901971, + "y": -158.00460530477952, + "z": 0.0 + }, + { + "x": 336.8038664419394, + "y": -159.02850755702912, + "z": 0.0 + }, + { + "x": 336.80324799368174, + "y": -160.05240980927874, + "z": 0.0 + }, + { + "x": 336.80262954542405, + "y": -161.07631206152837, + "z": 0.0 + }, + { + "x": 336.80201109716637, + "y": -162.100214313778, + "z": 0.0 + }, + { + "x": 336.80139264890863, + "y": -163.12411656602762, + "z": 0.0 + }, + { + "x": 336.80077420065095, + "y": -164.14801881827722, + "z": 0.0 + }, + { + "x": 336.80015575239327, + "y": -165.17192107052685, + "z": 0.0 + }, + { + "x": 336.7995373041355, + "y": -166.19582332277648, + "z": 0.0 + }, + { + "x": 336.79891885587784, + "y": -167.2197255750261, + "z": 0.0 + }, + { + "x": 336.79830040762016, + "y": -168.24362782727573, + "z": 0.0 + }, + { + "x": 336.7976819593625, + "y": -169.26753007952536, + "z": 0.0 + }, + { + "x": 336.7970635111048, + "y": -170.29143233177498, + "z": 0.0 + }, + { + "x": 336.79644506284706, + "y": -171.31533458402458, + "z": 0.0 + }, + { + "x": 336.7958266145894, + "y": -172.3392368362742, + "z": 0.0 + }, + { + "x": 336.7952081663317, + "y": -173.36313908852384, + "z": 0.0 + }, + { + "x": 336.794589718074, + "y": -174.38704134077346, + "z": 0.0 + }, + { + "x": 336.7939712698163, + "y": -175.4109435930231, + "z": 0.0 + }, + { + "x": 336.7933528215586, + "y": -176.43484584527272, + "z": 0.0 + }, + { + "x": 336.7927343733009, + "y": -177.45874809752232, + "z": 0.0 + }, + { + "x": 336.7921159250432, + "y": -178.48265034977194, + "z": 0.0 + }, + { + "x": 336.79149747678554, + "y": -179.50655260202157, + "z": 0.0 + }, + { + "x": 336.79087902852785, + "y": -180.5304548542712, + "z": 0.0 + }, + { + "x": 336.7902605802701, + "y": -181.55435710652083, + "z": 0.0 + }, + { + "x": 336.78964213201243, + "y": -182.57825935877045, + "z": 0.0 + }, + { + "x": 336.7890236837548, + "y": -183.60216161102005, + "z": 0.0 + }, + { + "x": 336.78840523549707, + "y": -184.62606386326968, + "z": 0.0 + }, + { + "x": 336.7877867872394, + "y": -185.6499661155193, + "z": 0.0 + } + ] + }, + { + "id": 93, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.78778642241014, + "y": -185.65117413736613, + "z": 0.0 + }, + { + "x": 338.7884048706678, + "y": -184.6272718851165, + "z": 0.0 + }, + { + "x": 338.7890233189256, + "y": -183.603369632867, + "z": 0.0 + }, + { + "x": 338.7896417671832, + "y": -182.5794673806172, + "z": 0.0 + }, + { + "x": 338.79026021544087, + "y": -181.55556512836768, + "z": 0.0 + }, + { + "x": 338.79087866369866, + "y": -180.53166287611816, + "z": 0.0 + }, + { + "x": 338.79149711195635, + "y": -179.50776062386842, + "z": 0.0 + }, + { + "x": 338.79211556021403, + "y": -178.4838583716188, + "z": 0.0 + }, + { + "x": 338.7927340084717, + "y": -177.45995611936917, + "z": 0.0 + }, + { + "x": 338.7933524567294, + "y": -176.43605386711954, + "z": 0.0 + }, + { + "x": 338.7939709049871, + "y": -175.41215161487003, + "z": 0.0 + }, + { + "x": 338.79458935324476, + "y": -174.3882493626203, + "z": 0.0 + }, + { + "x": 338.79520780150244, + "y": -173.36434711037066, + "z": 0.0 + }, + { + "x": 338.7958262497601, + "y": -172.34044485812103, + "z": 0.0 + }, + { + "x": 338.7964446980178, + "y": -171.3165426058714, + "z": 0.0 + }, + { + "x": 338.7970631462756, + "y": -170.29264035362195, + "z": 0.0 + }, + { + "x": 338.7976815945333, + "y": -169.2687381013722, + "z": 0.0 + }, + { + "x": 338.79830004279097, + "y": -168.24483584912258, + "z": 0.0 + }, + { + "x": 338.79891849104865, + "y": -167.22093359687295, + "z": 0.0 + }, + { + "x": 338.79953693930634, + "y": -166.19703134462333, + "z": 0.0 + }, + { + "x": 338.800155387564, + "y": -165.1731290923738, + "z": 0.0 + }, + { + "x": 338.8007738358217, + "y": -164.14922684012407, + "z": 0.0 + }, + { + "x": 338.8013922840794, + "y": -163.12532458787445, + "z": 0.0 + }, + { + "x": 338.8020107323372, + "y": -162.10142233562493, + "z": 0.0 + }, + { + "x": 338.80262918059486, + "y": -161.0775200833752, + "z": 0.0 + }, + { + "x": 338.80324762885255, + "y": -160.05361783112556, + "z": 0.0 + }, + { + "x": 338.80386607711023, + "y": -159.02971557887594, + "z": 0.0 + }, + { + "x": 338.8044845253679, + "y": -158.00581332662637, + "z": 0.0 + }, + { + "x": 338.8051029736256, + "y": -156.98191107437685, + "z": 0.0 + }, + { + "x": 338.8057214218833, + "y": -155.9580088221271, + "z": 0.0 + }, + { + "x": 338.80633987014096, + "y": -154.93410656987749, + "z": 0.0 + }, + { + "x": 338.80695831839864, + "y": -153.91020431762786, + "z": 0.0 + }, + { + "x": 338.8075767666563, + "y": -152.88630206537823, + "z": 0.0 + }, + { + "x": 338.8081952149141, + "y": -151.86239981312872, + "z": 0.0 + }, + { + "x": 338.8088136631718, + "y": -150.83849756087898, + "z": 0.0 + }, + { + "x": 338.8094321114295, + "y": -149.81459530862935, + "z": 0.0 + }, + { + "x": 338.81005055968717, + "y": -148.79069305637972, + "z": 0.0 + }, + { + "x": 338.81066900794485, + "y": -147.7667908041301, + "z": 0.0 + }, + { + "x": 338.81128745620254, + "y": -146.74288855188058, + "z": 0.0 + }, + { + "x": 338.8119059044602, + "y": -145.71898629963079, + "z": 0.0 + }, + { + "x": 338.8125243527179, + "y": -144.69508404738139, + "z": 0.0 + }, + { + "x": 338.8131428009756, + "y": -143.67118179513164, + "z": 0.0 + } + ] + }, + { + "id": 94, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.8131435306341, + "y": -143.66876575143795, + "z": 0.0 + }, + { + "x": 334.8125250823764, + "y": -144.69266800368746, + "z": 0.0 + }, + { + "x": 334.8119066341186, + "y": -145.7165702559373, + "z": 0.0 + }, + { + "x": 334.81128818586103, + "y": -146.7404725081867, + "z": 0.0 + }, + { + "x": 334.81066973760323, + "y": -147.76437476043645, + "z": 0.0 + }, + { + "x": 334.81005128934555, + "y": -148.78827701268608, + "z": 0.0 + }, + { + "x": 334.80943284108787, + "y": -149.8121792649357, + "z": 0.0 + }, + { + "x": 334.8088143928302, + "y": -150.83608151718533, + "z": 0.0 + }, + { + "x": 334.8081959445725, + "y": -151.8599837694348, + "z": 0.0 + }, + { + "x": 334.8075774963148, + "y": -152.88388602168453, + "z": 0.0 + }, + { + "x": 334.80695904805714, + "y": -153.90778827393416, + "z": 0.0 + }, + { + "x": 334.80634059979946, + "y": -154.9316905261838, + "z": 0.0 + }, + { + "x": 334.8057221515418, + "y": -155.9555927784334, + "z": 0.0 + }, + { + "x": 334.8051037032841, + "y": -156.97949503068293, + "z": 0.0 + }, + { + "x": 334.8044852550263, + "y": -158.00339728293267, + "z": 0.0 + }, + { + "x": 334.8038668067686, + "y": -159.0272995351823, + "z": 0.0 + }, + { + "x": 334.8032483585109, + "y": -160.05120178743192, + "z": 0.0 + }, + { + "x": 334.80262991025324, + "y": -161.07510403968155, + "z": 0.0 + }, + { + "x": 334.80201146199556, + "y": -162.09900629193106, + "z": 0.0 + }, + { + "x": 334.8013930137379, + "y": -163.1229085441808, + "z": 0.0 + }, + { + "x": 334.8007745654802, + "y": -164.14681079643037, + "z": 0.0 + }, + { + "x": 334.8001561172225, + "y": -165.1707130486799, + "z": 0.0 + }, + { + "x": 334.7995376689647, + "y": -166.19461530092963, + "z": 0.0 + }, + { + "x": 334.79891922070703, + "y": -167.21851755317925, + "z": 0.0 + }, + { + "x": 334.79830077244935, + "y": -168.24241980542888, + "z": 0.0 + }, + { + "x": 334.79768232419167, + "y": -169.2663220576785, + "z": 0.0 + }, + { + "x": 334.797063875934, + "y": -170.29022430992802, + "z": 0.0 + }, + { + "x": 334.7964454276763, + "y": -171.31412656217776, + "z": 0.0 + }, + { + "x": 334.7958269794186, + "y": -172.3380288144274, + "z": 0.0 + }, + { + "x": 334.79520853116094, + "y": -173.36193106667702, + "z": 0.0 + }, + { + "x": 334.79459008290326, + "y": -174.38583331892664, + "z": 0.0 + }, + { + "x": 334.7939716346456, + "y": -175.40973557117616, + "z": 0.0 + }, + { + "x": 334.7933531863878, + "y": -176.4336378234259, + "z": 0.0 + }, + { + "x": 334.7927347381301, + "y": -177.45754007567547, + "z": 0.0 + }, + { + "x": 334.7921162898724, + "y": -178.4814423279251, + "z": 0.0 + }, + { + "x": 334.7914978416147, + "y": -179.50534458017472, + "z": 0.0 + }, + { + "x": 334.79087939335705, + "y": -180.52924683242423, + "z": 0.0 + }, + { + "x": 334.79026094509936, + "y": -181.55314908467398, + "z": 0.0 + }, + { + "x": 334.7896424968417, + "y": -182.57705133692372, + "z": 0.0 + }, + { + "x": 334.789024048584, + "y": -183.60095358917312, + "z": 0.0 + }, + { + "x": 334.7884056003263, + "y": -184.62485584142286, + "z": 0.0 + }, + { + "x": 334.78778715206863, + "y": -185.64875809367248, + "z": 0.0 + } + ] + }, + { + "id": 95, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.70817741787323, + "y": -317.4523581169669, + "z": 0.0 + }, + { + "x": 340.70878871079753, + "y": -316.44030222681437, + "z": 0.0 + }, + { + "x": 340.7094000037218, + "y": -315.4282463366616, + "z": 0.0 + }, + { + "x": 340.7100112966461, + "y": -314.4161904465093, + "z": 0.0 + }, + { + "x": 340.7106225895704, + "y": -313.4041345563567, + "z": 0.0 + }, + { + "x": 340.71123388249464, + "y": -312.392078666204, + "z": 0.0 + }, + { + "x": 340.71184517541894, + "y": -311.3800227760517, + "z": 0.0 + }, + { + "x": 340.7124564683432, + "y": -310.3679668858989, + "z": 0.0 + }, + { + "x": 340.7130677612675, + "y": -309.3559109957466, + "z": 0.0 + }, + { + "x": 340.71367905419174, + "y": -308.34385510559383, + "z": 0.0 + }, + { + "x": 340.71429034711605, + "y": -307.33179921544155, + "z": 0.0 + }, + { + "x": 340.7149016400403, + "y": -306.3197433252888, + "z": 0.0 + }, + { + "x": 340.7155129329646, + "y": -305.3076874351365, + "z": 0.0 + }, + { + "x": 340.7161242258889, + "y": -304.29563154498396, + "z": 0.0 + }, + { + "x": 340.71673551881315, + "y": -303.28357565483117, + "z": 0.0 + }, + { + "x": 340.71734681173746, + "y": -302.2715197646789, + "z": 0.0 + }, + { + "x": 340.7179581046617, + "y": -301.25946387452615, + "z": 0.0 + }, + { + "x": 340.718569397586, + "y": -300.2474079843738, + "z": 0.0 + }, + { + "x": 340.71918069051026, + "y": -299.23535209422107, + "z": 0.0 + }, + { + "x": 340.71979198343456, + "y": -298.22329620406873, + "z": 0.0 + }, + { + "x": 340.7204032763588, + "y": -297.211240313916, + "z": 0.0 + }, + { + "x": 340.7210145692831, + "y": -296.1991844237637, + "z": 0.0 + }, + { + "x": 340.7216258622074, + "y": -295.18712853361114, + "z": 0.0 + }, + { + "x": 340.72223715513167, + "y": -294.1750726434584, + "z": 0.0 + }, + { + "x": 340.72284844805597, + "y": -293.16301675330607, + "z": 0.0 + }, + { + "x": 340.7234597409802, + "y": -292.15096086315333, + "z": 0.0 + }, + { + "x": 340.7240710339045, + "y": -291.13890497300105, + "z": 0.0 + }, + { + "x": 340.72468232682877, + "y": -290.12684908284825, + "z": 0.0 + }, + { + "x": 340.7252936197531, + "y": -289.11479319269597, + "z": 0.0 + }, + { + "x": 340.7259049126773, + "y": -288.10273730254323, + "z": 0.0 + }, + { + "x": 340.7265162056016, + "y": -287.0906814123909, + "z": 0.0 + }, + { + "x": 340.72712749852593, + "y": -286.0786255222384, + "z": 0.0 + }, + { + "x": 340.7277387914502, + "y": -285.0665696320856, + "z": 0.0 + }, + { + "x": 340.7283500843745, + "y": -284.0545137419333, + "z": 0.0 + }, + { + "x": 340.72896137729873, + "y": -283.04245785178057, + "z": 0.0 + }, + { + "x": 340.72957267022304, + "y": -282.03040196162823, + "z": 0.0 + }, + { + "x": 340.7301839631473, + "y": -281.0183460714755, + "z": 0.0 + }, + { + "x": 340.7307952560716, + "y": -280.00629018132315, + "z": 0.0 + }, + { + "x": 340.73140654899584, + "y": -278.9942342911704, + "z": 0.0 + }, + { + "x": 340.73201784192014, + "y": -277.98217840101813, + "z": 0.0 + }, + { + "x": 340.73262913484444, + "y": -276.97012251086556, + "z": 0.0 + }, + { + "x": 340.7332404277687, + "y": -275.9580666207128, + "z": 0.0 + }, + { + "x": 340.733851720693, + "y": -274.94601073056054, + "z": 0.0 + }, + { + "x": 340.73446301361724, + "y": -273.93395484040775, + "z": 0.0 + }, + { + "x": 340.73507430654155, + "y": -272.92189895025547, + "z": 0.0 + }, + { + "x": 340.7356855994658, + "y": -271.9098430601027, + "z": 0.0 + }, + { + "x": 340.7362968923901, + "y": -270.8977871699504, + "z": 0.0 + }, + { + "x": 340.7369081853144, + "y": -269.8857312797978, + "z": 0.0 + }, + { + "x": 340.73751947823865, + "y": -268.8736753896451, + "z": 0.0 + }, + { + "x": 340.73813077116296, + "y": -267.86161949949275, + "z": 0.0 + }, + { + "x": 340.7387420640872, + "y": -266.84956360934, + "z": 0.0 + }, + { + "x": 340.7393533570115, + "y": -265.83750771918767, + "z": 0.0 + }, + { + "x": 340.73996464993576, + "y": -264.82545182903493, + "z": 0.0 + }, + { + "x": 340.74057594286006, + "y": -263.8133959388826, + "z": 0.0 + }, + { + "x": 340.7411872357843, + "y": -262.80134004872986, + "z": 0.0 + }, + { + "x": 340.7417985287086, + "y": -261.7892841585775, + "z": 0.0 + }, + { + "x": 340.7424098216329, + "y": -260.777228268425, + "z": 0.0 + }, + { + "x": 340.74302111455717, + "y": -259.7651723782722, + "z": 0.0 + }, + { + "x": 340.74363240748147, + "y": -258.75311648811993, + "z": 0.0 + }, + { + "x": 340.7442437004057, + "y": -257.74106059796713, + "z": 0.0 + }, + { + "x": 340.74485499333, + "y": -256.72900470781485, + "z": 0.0 + }, + { + "x": 340.74546628625427, + "y": -255.71694881766206, + "z": 0.0 + }, + { + "x": 340.7460775791786, + "y": -254.70489292750975, + "z": 0.0 + }, + { + "x": 340.7466888721028, + "y": -253.69283703735698, + "z": 0.0 + }, + { + "x": 340.7473001650271, + "y": -252.68078114720467, + "z": 0.0 + }, + { + "x": 340.74791145795143, + "y": -251.66872525705213, + "z": 0.0 + }, + { + "x": 340.7485227508757, + "y": -250.65666936689937, + "z": 0.0 + }, + { + "x": 340.7491340438, + "y": -249.64461347674705, + "z": 0.0 + }, + { + "x": 340.74974533672423, + "y": -248.6325575865943, + "z": 0.0 + }, + { + "x": 340.75035662964854, + "y": -247.62050169644198, + "z": 0.0 + }, + { + "x": 340.7509679225728, + "y": -246.6084458062892, + "z": 0.0 + }, + { + "x": 340.7515792154971, + "y": -245.5963899161369, + "z": 0.0 + }, + { + "x": 340.75219050842134, + "y": -244.58433402598413, + "z": 0.0 + }, + { + "x": 340.75280180134564, + "y": -243.57227813583182, + "z": 0.0 + }, + { + "x": 340.75341309426994, + "y": -242.56022224567928, + "z": 0.0 + }, + { + "x": 340.7540243871942, + "y": -241.54816635552652, + "z": 0.0 + }, + { + "x": 340.7546356801185, + "y": -240.5361104653742, + "z": 0.0 + }, + { + "x": 340.75524697304274, + "y": -239.52405457522144, + "z": 0.0 + }, + { + "x": 340.75585826596705, + "y": -238.51199868506913, + "z": 0.0 + }, + { + "x": 340.7564695588913, + "y": -237.49994279491636, + "z": 0.0 + }, + { + "x": 340.7570808518156, + "y": -236.48788690476405, + "z": 0.0 + }, + { + "x": 340.75769214473985, + "y": -235.4758310146113, + "z": 0.0 + }, + { + "x": 340.75830343766415, + "y": -234.46377512445898, + "z": 0.0 + }, + { + "x": 340.75891473058846, + "y": -233.45171923430644, + "z": 0.0 + }, + { + "x": 340.7595260235127, + "y": -232.43966334415367, + "z": 0.0 + }, + { + "x": 340.760137316437, + "y": -231.42760745400136, + "z": 0.0 + }, + { + "x": 340.76074860936126, + "y": -230.4155515638486, + "z": 0.0 + }, + { + "x": 340.76135990228556, + "y": -229.40349567369628, + "z": 0.0 + }, + { + "x": 340.7619711952098, + "y": -228.39143978354352, + "z": 0.0 + }, + { + "x": 340.7625824881341, + "y": -227.3793838933912, + "z": 0.0 + }, + { + "x": 340.7631937810584, + "y": -226.36732800323867, + "z": 0.0 + }, + { + "x": 340.76380507398267, + "y": -225.3552721130859, + "z": 0.0 + }, + { + "x": 340.76441636690697, + "y": -224.3432162229336, + "z": 0.0 + }, + { + "x": 340.7650276598312, + "y": -223.33116033278083, + "z": 0.0 + }, + { + "x": 340.7656389527555, + "y": -222.31910444262851, + "z": 0.0 + }, + { + "x": 340.76625024567977, + "y": -221.30704855247575, + "z": 0.0 + }, + { + "x": 340.7668615386041, + "y": -220.29499266232344, + "z": 0.0 + }, + { + "x": 340.7674728315283, + "y": -219.28293677217067, + "z": 0.0 + }, + { + "x": 340.7680841244526, + "y": -218.27088088201836, + "z": 0.0 + }, + { + "x": 340.76869541737693, + "y": -217.25882499186582, + "z": 0.0 + }, + { + "x": 340.7693067103012, + "y": -216.24676910171306, + "z": 0.0 + }, + { + "x": 340.7699180032255, + "y": -215.23471321156075, + "z": 0.0 + }, + { + "x": 340.77052929614973, + "y": -214.22265732140798, + "z": 0.0 + }, + { + "x": 340.77114058907404, + "y": -213.21060143125567, + "z": 0.0 + }, + { + "x": 340.7717518819983, + "y": -212.1985455411029, + "z": 0.0 + }, + { + "x": 340.7723631749226, + "y": -211.1864896509506, + "z": 0.0 + }, + { + "x": 340.77297446784684, + "y": -210.17443376079783, + "z": 0.0 + }, + { + "x": 340.77358576077114, + "y": -209.1623778706455, + "z": 0.0 + } + ] + }, + { + "id": 96, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.773587220088, + "y": -209.1575457832579, + "z": 0.0 + }, + { + "x": 332.7729759271637, + "y": -210.16960167341065, + "z": 0.0 + }, + { + "x": 332.77236463423947, + "y": -211.18165756356296, + "z": 0.0 + }, + { + "x": 332.77175334131516, + "y": -212.19371345371573, + "z": 0.0 + }, + { + "x": 332.7711420483909, + "y": -213.20576934386804, + "z": 0.0 + }, + { + "x": 332.7705307554666, + "y": -214.2178252340208, + "z": 0.0 + }, + { + "x": 332.76991946254236, + "y": -215.22988112417312, + "z": 0.0 + }, + { + "x": 332.76930816961806, + "y": -216.24193701432588, + "z": 0.0 + }, + { + "x": 332.7686968766938, + "y": -217.2539929044782, + "z": 0.0 + }, + { + "x": 332.7680855837695, + "y": -218.26604879463073, + "z": 0.0 + }, + { + "x": 332.7674742908452, + "y": -219.2781046847835, + "z": 0.0 + }, + { + "x": 332.76686299792095, + "y": -220.2901605749358, + "z": 0.0 + }, + { + "x": 332.76625170499665, + "y": -221.30221646508858, + "z": 0.0 + }, + { + "x": 332.7656404120724, + "y": -222.3142723552409, + "z": 0.0 + }, + { + "x": 332.7650291191481, + "y": -223.32632824539365, + "z": 0.0 + }, + { + "x": 332.76441782622385, + "y": -224.33838413554597, + "z": 0.0 + }, + { + "x": 332.76380653329954, + "y": -225.35044002569873, + "z": 0.0 + }, + { + "x": 332.7631952403753, + "y": -226.36249591585104, + "z": 0.0 + }, + { + "x": 332.762583947451, + "y": -227.37455180600358, + "z": 0.0 + }, + { + "x": 332.7619726545267, + "y": -228.38660769615635, + "z": 0.0 + }, + { + "x": 332.76136136160244, + "y": -229.39866358630866, + "z": 0.0 + }, + { + "x": 332.76075006867813, + "y": -230.41071947646142, + "z": 0.0 + }, + { + "x": 332.7601387757539, + "y": -231.42277536661373, + "z": 0.0 + }, + { + "x": 332.7595274828296, + "y": -232.4348312567665, + "z": 0.0 + }, + { + "x": 332.75891618990534, + "y": -233.4468871469188, + "z": 0.0 + }, + { + "x": 332.75830489698103, + "y": -234.45894303707135, + "z": 0.0 + }, + { + "x": 332.7576936040567, + "y": -235.47099892722412, + "z": 0.0 + }, + { + "x": 332.7570823111325, + "y": -236.48305481737643, + "z": 0.0 + }, + { + "x": 332.7564710182082, + "y": -237.4951107075292, + "z": 0.0 + }, + { + "x": 332.7558597252839, + "y": -238.5071665976815, + "z": 0.0 + }, + { + "x": 332.7552484323596, + "y": -239.51922248783427, + "z": 0.0 + }, + { + "x": 332.7546371394354, + "y": -240.53127837798658, + "z": 0.0 + }, + { + "x": 332.75402584651107, + "y": -241.54333426813935, + "z": 0.0 + }, + { + "x": 332.7534145535868, + "y": -242.55539015829166, + "z": 0.0 + }, + { + "x": 332.7528032606625, + "y": -243.5674460484442, + "z": 0.0 + }, + { + "x": 332.7521919677382, + "y": -244.57950193859696, + "z": 0.0 + }, + { + "x": 332.75158067481397, + "y": -245.59155782874927, + "z": 0.0 + }, + { + "x": 332.75096938188966, + "y": -246.60361371890204, + "z": 0.0 + }, + { + "x": 332.7503580889654, + "y": -247.61566960905435, + "z": 0.0 + }, + { + "x": 332.7497467960411, + "y": -248.62772549920712, + "z": 0.0 + }, + { + "x": 332.74913550311686, + "y": -249.63978138935943, + "z": 0.0 + }, + { + "x": 332.74852421019256, + "y": -250.6518372795122, + "z": 0.0 + }, + { + "x": 332.7479129172683, + "y": -251.6638931696645, + "z": 0.0 + }, + { + "x": 332.747301624344, + "y": -252.67594905981704, + "z": 0.0 + }, + { + "x": 332.7466903314197, + "y": -253.6880049499698, + "z": 0.0 + }, + { + "x": 332.74607903849545, + "y": -254.70006084012212, + "z": 0.0 + }, + { + "x": 332.74546774557115, + "y": -255.7121167302749, + "z": 0.0 + }, + { + "x": 332.7448564526469, + "y": -256.7241726204272, + "z": 0.0 + }, + { + "x": 332.7442451597226, + "y": -257.73622851057996, + "z": 0.0 + }, + { + "x": 332.74363386679835, + "y": -258.7482844007323, + "z": 0.0 + }, + { + "x": 332.74302257387404, + "y": -259.76034029088504, + "z": 0.0 + }, + { + "x": 332.7424112809498, + "y": -260.7723961810374, + "z": 0.0 + }, + { + "x": 332.7417999880255, + "y": -261.7844520711899, + "z": 0.0 + }, + { + "x": 332.7411886951012, + "y": -262.7965079613427, + "z": 0.0 + }, + { + "x": 332.74057740217694, + "y": -263.80856385149497, + "z": 0.0 + }, + { + "x": 332.73996610925263, + "y": -264.82061974164776, + "z": 0.0 + }, + { + "x": 332.7393548163284, + "y": -265.83267563180004, + "z": 0.0 + }, + { + "x": 332.7387435234041, + "y": -266.84473152195284, + "z": 0.0 + }, + { + "x": 332.73813223047983, + "y": -267.8567874121051, + "z": 0.0 + }, + { + "x": 332.73752093755553, + "y": -268.8688433022579, + "z": 0.0 + }, + { + "x": 332.7369096446313, + "y": -269.8808991924102, + "z": 0.0 + }, + { + "x": 332.736298351707, + "y": -270.89295508256276, + "z": 0.0 + }, + { + "x": 332.7356870587827, + "y": -271.9050109727155, + "z": 0.0 + }, + { + "x": 332.7350757658584, + "y": -272.91706686286784, + "z": 0.0 + }, + { + "x": 332.7344644729341, + "y": -273.9291227530206, + "z": 0.0 + }, + { + "x": 332.7338531800099, + "y": -274.9411786431729, + "z": 0.0 + }, + { + "x": 332.73324188708557, + "y": -275.95323453332566, + "z": 0.0 + }, + { + "x": 332.7326305941613, + "y": -276.96529042347794, + "z": 0.0 + }, + { + "x": 332.732019301237, + "y": -277.9773463136305, + "z": 0.0 + }, + { + "x": 332.7314080083127, + "y": -278.98940220378324, + "z": 0.0 + }, + { + "x": 332.73079671538846, + "y": -280.0014580939355, + "z": 0.0 + }, + { + "x": 332.73018542246416, + "y": -281.0135139840883, + "z": 0.0 + }, + { + "x": 332.7295741295399, + "y": -282.0255698742406, + "z": 0.0 + }, + { + "x": 332.7289628366156, + "y": -283.0376257643934, + "z": 0.0 + }, + { + "x": 332.72835154369136, + "y": -284.0496816545457, + "z": 0.0 + }, + { + "x": 332.72774025076706, + "y": -285.0617375446984, + "z": 0.0 + }, + { + "x": 332.7271289578428, + "y": -286.07379343485076, + "z": 0.0 + }, + { + "x": 332.7265176649185, + "y": -287.08584932500327, + "z": 0.0 + }, + { + "x": 332.7259063719942, + "y": -288.09790521515606, + "z": 0.0 + }, + { + "x": 332.72529507906995, + "y": -289.10996110530834, + "z": 0.0 + }, + { + "x": 332.72468378614565, + "y": -290.1220169954611, + "z": 0.0 + }, + { + "x": 332.7240724932214, + "y": -291.1340728856134, + "z": 0.0 + }, + { + "x": 332.7234612002971, + "y": -292.14612877576616, + "z": 0.0 + }, + { + "x": 332.72284990737285, + "y": -293.15818466591844, + "z": 0.0 + }, + { + "x": 332.72223861444854, + "y": -294.17024055607124, + "z": 0.0 + }, + { + "x": 332.7216273215243, + "y": -295.1822964462235, + "z": 0.0 + }, + { + "x": 332.7210160286, + "y": -296.1943523363761, + "z": 0.0 + }, + { + "x": 332.7204047356757, + "y": -297.2064082265288, + "z": 0.0 + }, + { + "x": 332.71979344275144, + "y": -298.2184641166811, + "z": 0.0 + }, + { + "x": 332.71918214982713, + "y": -299.2305200068339, + "z": 0.0 + }, + { + "x": 332.7185708569029, + "y": -300.2425758969862, + "z": 0.0 + }, + { + "x": 332.7179595639786, + "y": -301.254631787139, + "z": 0.0 + }, + { + "x": 332.71734827105433, + "y": -302.26668767729126, + "z": 0.0 + }, + { + "x": 332.71673697813003, + "y": -303.278743567444, + "z": 0.0 + }, + { + "x": 332.7161256852058, + "y": -304.29079945759634, + "z": 0.0 + }, + { + "x": 332.7155143922815, + "y": -305.30285534774885, + "z": 0.0 + }, + { + "x": 332.7149030993572, + "y": -306.31491123790164, + "z": 0.0 + }, + { + "x": 332.7142918064329, + "y": -307.3269671280539, + "z": 0.0 + }, + { + "x": 332.7136805135086, + "y": -308.33902301820666, + "z": 0.0 + }, + { + "x": 332.7130692205844, + "y": -309.351078908359, + "z": 0.0 + }, + { + "x": 332.71245792766007, + "y": -310.36313479851174, + "z": 0.0 + }, + { + "x": 332.7118466347358, + "y": -311.3751906886641, + "z": 0.0 + }, + { + "x": 332.7112353418115, + "y": -312.3872465788168, + "z": 0.0 + }, + { + "x": 332.71062404888727, + "y": -313.3993024689691, + "z": 0.0 + }, + { + "x": 332.71001275596296, + "y": -314.41135835912166, + "z": 0.0 + }, + { + "x": 332.70940146303866, + "y": -315.4234142492744, + "z": 0.0 + }, + { + "x": 332.7087901701144, + "y": -316.43547013942674, + "z": 0.0 + }, + { + "x": 332.7081788771901, + "y": -317.44752602957925, + "z": 0.0 + } + ] + }, + { + "id": 97, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 336.7735864904296, + "y": -209.1599618269517, + "z": 0.0 + }, + { + "x": 336.7729751975053, + "y": -210.17201771710424, + "z": 0.0 + }, + { + "x": 336.772363904581, + "y": -211.18407360725678, + "z": 0.0 + }, + { + "x": 336.7717526116567, + "y": -212.19612949740932, + "z": 0.0 + }, + { + "x": 336.7711413187325, + "y": -213.20818538756185, + "z": 0.0 + }, + { + "x": 336.77053002580817, + "y": -214.2202412777144, + "z": 0.0 + }, + { + "x": 336.7699187328839, + "y": -215.23229716786693, + "z": 0.0 + }, + { + "x": 336.7693074399596, + "y": -216.24435305801947, + "z": 0.0 + }, + { + "x": 336.76869614703537, + "y": -217.256408948172, + "z": 0.0 + }, + { + "x": 336.76808485411107, + "y": -218.26846483832455, + "z": 0.0 + }, + { + "x": 336.76747356118676, + "y": -219.2805207284771, + "z": 0.0 + }, + { + "x": 336.7668622682625, + "y": -220.29257661862962, + "z": 0.0 + }, + { + "x": 336.7662509753382, + "y": -221.30463250878216, + "z": 0.0 + }, + { + "x": 336.76563968241396, + "y": -222.3166883989347, + "z": 0.0 + }, + { + "x": 336.76502838948966, + "y": -223.32874428908724, + "z": 0.0 + }, + { + "x": 336.7644170965654, + "y": -224.34080017923978, + "z": 0.0 + }, + { + "x": 336.7638058036411, + "y": -225.35285606939232, + "z": 0.0 + }, + { + "x": 336.76319451071686, + "y": -226.36491195954486, + "z": 0.0 + }, + { + "x": 336.76258321779255, + "y": -227.3769678496974, + "z": 0.0 + }, + { + "x": 336.76197192486825, + "y": -228.38902373984993, + "z": 0.0 + }, + { + "x": 336.761360631944, + "y": -229.40107963000247, + "z": 0.0 + }, + { + "x": 336.7607493390197, + "y": -230.413135520155, + "z": 0.0 + }, + { + "x": 336.76013804609545, + "y": -231.42519141030755, + "z": 0.0 + }, + { + "x": 336.75952675317114, + "y": -232.4372473004601, + "z": 0.0 + }, + { + "x": 336.7589154602469, + "y": -233.44930319061262, + "z": 0.0 + }, + { + "x": 336.7583041673226, + "y": -234.46135908076516, + "z": 0.0 + }, + { + "x": 336.7576928743983, + "y": -235.4734149709177, + "z": 0.0 + }, + { + "x": 336.75708158147404, + "y": -236.48547086107024, + "z": 0.0 + }, + { + "x": 336.75647028854974, + "y": -237.49752675122278, + "z": 0.0 + }, + { + "x": 336.7558589956255, + "y": -238.50958264137532, + "z": 0.0 + }, + { + "x": 336.7552477027012, + "y": -239.52163853152786, + "z": 0.0 + }, + { + "x": 336.75463640977694, + "y": -240.5336944216804, + "z": 0.0 + }, + { + "x": 336.75402511685263, + "y": -241.54575031183293, + "z": 0.0 + }, + { + "x": 336.7534138239284, + "y": -242.55780620198547, + "z": 0.0 + }, + { + "x": 336.7528025310041, + "y": -243.569862092138, + "z": 0.0 + }, + { + "x": 336.7521912380798, + "y": -244.58191798229055, + "z": 0.0 + }, + { + "x": 336.7515799451555, + "y": -245.5939738724431, + "z": 0.0 + }, + { + "x": 336.7509686522312, + "y": -246.60602976259563, + "z": 0.0 + }, + { + "x": 336.750357359307, + "y": -247.61808565274816, + "z": 0.0 + }, + { + "x": 336.74974606638267, + "y": -248.6301415429007, + "z": 0.0 + }, + { + "x": 336.7491347734584, + "y": -249.64219743305324, + "z": 0.0 + }, + { + "x": 336.7485234805341, + "y": -250.65425332320578, + "z": 0.0 + }, + { + "x": 336.74791218760987, + "y": -251.66630921335832, + "z": 0.0 + }, + { + "x": 336.74730089468557, + "y": -252.67836510351086, + "z": 0.0 + }, + { + "x": 336.74668960176126, + "y": -253.6904209936634, + "z": 0.0 + }, + { + "x": 336.746078308837, + "y": -254.70247688381593, + "z": 0.0 + }, + { + "x": 336.7454670159127, + "y": -255.71453277396847, + "z": 0.0 + }, + { + "x": 336.74485572298846, + "y": -256.72658866412104, + "z": 0.0 + }, + { + "x": 336.74424443006416, + "y": -257.73864455427355, + "z": 0.0 + }, + { + "x": 336.7436331371399, + "y": -258.7507004444261, + "z": 0.0 + }, + { + "x": 336.7430218442156, + "y": -259.7627563345786, + "z": 0.0 + }, + { + "x": 336.74241055129136, + "y": -260.7748122247312, + "z": 0.0 + }, + { + "x": 336.74179925836705, + "y": -261.7868681148837, + "z": 0.0 + }, + { + "x": 336.74118796544275, + "y": -262.79892400503627, + "z": 0.0 + }, + { + "x": 336.7405766725185, + "y": -263.8109798951888, + "z": 0.0 + }, + { + "x": 336.7399653795942, + "y": -264.82303578534135, + "z": 0.0 + }, + { + "x": 336.73935408666995, + "y": -265.83509167549386, + "z": 0.0 + }, + { + "x": 336.73874279374564, + "y": -266.8471475656464, + "z": 0.0 + }, + { + "x": 336.7381315008214, + "y": -267.85920345579893, + "z": 0.0 + }, + { + "x": 336.7375202078971, + "y": -268.8712593459515, + "z": 0.0 + }, + { + "x": 336.73690891497284, + "y": -269.883315236104, + "z": 0.0 + }, + { + "x": 336.73629762204854, + "y": -270.8953711262566, + "z": 0.0 + }, + { + "x": 336.73568632912423, + "y": -271.9074270164091, + "z": 0.0 + }, + { + "x": 336.7350750362, + "y": -272.91948290656165, + "z": 0.0 + }, + { + "x": 336.7344637432757, + "y": -273.93153879671416, + "z": 0.0 + }, + { + "x": 336.73385245035143, + "y": -274.94359468686673, + "z": 0.0 + }, + { + "x": 336.73324115742713, + "y": -275.95565057701924, + "z": 0.0 + }, + { + "x": 336.7326298645029, + "y": -276.96770646717175, + "z": 0.0 + }, + { + "x": 336.7320185715786, + "y": -277.9797623573243, + "z": 0.0 + }, + { + "x": 336.7314072786543, + "y": -278.99181824747683, + "z": 0.0 + }, + { + "x": 336.73079598573, + "y": -280.00387413762934, + "z": 0.0 + }, + { + "x": 336.7301846928057, + "y": -281.0159300277819, + "z": 0.0 + }, + { + "x": 336.7295733998815, + "y": -282.0279859179344, + "z": 0.0 + }, + { + "x": 336.72896210695717, + "y": -283.040041808087, + "z": 0.0 + }, + { + "x": 336.7283508140329, + "y": -284.0520976982395, + "z": 0.0 + }, + { + "x": 336.7277395211086, + "y": -285.064153588392, + "z": 0.0 + }, + { + "x": 336.72712822818437, + "y": -286.07620947854457, + "z": 0.0 + }, + { + "x": 336.72651693526007, + "y": -287.0882653686971, + "z": 0.0 + }, + { + "x": 336.72590564233576, + "y": -288.10032125884965, + "z": 0.0 + }, + { + "x": 336.7252943494115, + "y": -289.11237714900216, + "z": 0.0 + }, + { + "x": 336.7246830564872, + "y": -290.12443303915467, + "z": 0.0 + }, + { + "x": 336.72407176356296, + "y": -291.13648892930723, + "z": 0.0 + }, + { + "x": 336.72346047063866, + "y": -292.14854481945974, + "z": 0.0 + }, + { + "x": 336.7228491777144, + "y": -293.16060070961225, + "z": 0.0 + }, + { + "x": 336.7222378847901, + "y": -294.1726565997648, + "z": 0.0 + }, + { + "x": 336.72162659186586, + "y": -295.18471248991733, + "z": 0.0 + }, + { + "x": 336.72101529894155, + "y": -296.1967683800699, + "z": 0.0 + }, + { + "x": 336.72040400601725, + "y": -297.2088242702224, + "z": 0.0 + }, + { + "x": 336.719792713093, + "y": -298.2208801603749, + "z": 0.0 + }, + { + "x": 336.7191814201687, + "y": -299.2329360505275, + "z": 0.0 + }, + { + "x": 336.71857012724445, + "y": -300.24499194068, + "z": 0.0 + }, + { + "x": 336.71795883432014, + "y": -301.25704783083256, + "z": 0.0 + }, + { + "x": 336.7173475413959, + "y": -302.2691037209851, + "z": 0.0 + }, + { + "x": 336.7167362484716, + "y": -303.2811596111376, + "z": 0.0 + }, + { + "x": 336.71612495554734, + "y": -304.29321550129015, + "z": 0.0 + }, + { + "x": 336.71551366262304, + "y": -305.30527139144266, + "z": 0.0 + }, + { + "x": 336.71490236969873, + "y": -306.3173272815952, + "z": 0.0 + }, + { + "x": 336.7142910767745, + "y": -307.32938317174774, + "z": 0.0 + }, + { + "x": 336.7136797838502, + "y": -308.34143906190025, + "z": 0.0 + }, + { + "x": 336.71306849092593, + "y": -309.3534949520528, + "z": 0.0 + }, + { + "x": 336.71245719800163, + "y": -310.3655508422053, + "z": 0.0 + }, + { + "x": 336.7118459050774, + "y": -311.3776067323579, + "z": 0.0 + }, + { + "x": 336.7112346121531, + "y": -312.3896626225104, + "z": 0.0 + }, + { + "x": 336.71062331922883, + "y": -313.4017185126629, + "z": 0.0 + }, + { + "x": 336.7100120263045, + "y": -314.4137744028155, + "z": 0.0 + }, + { + "x": 336.7094007333802, + "y": -315.425830292968, + "z": 0.0 + }, + { + "x": 336.708789440456, + "y": -316.43788618312055, + "z": 0.0 + }, + { + "x": 336.70817814753167, + "y": -317.44994207327306, + "z": 0.0 + } + ] + }, + { + "id": 98, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.7081777827025, + "y": -317.45115009511994, + "z": 0.0 + }, + { + "x": 338.7087890756268, + "y": -316.43909420496743, + "z": 0.0 + }, + { + "x": 338.709400368551, + "y": -315.4270383148148, + "z": 0.0 + }, + { + "x": 338.7100116614753, + "y": -314.4149824246624, + "z": 0.0 + }, + { + "x": 338.7106229543996, + "y": -313.4029265345098, + "z": 0.0 + }, + { + "x": 338.7112342473239, + "y": -312.39087064435716, + "z": 0.0 + }, + { + "x": 338.7118455402482, + "y": -311.37881475420477, + "z": 0.0 + }, + { + "x": 338.7124568331724, + "y": -310.36675886405214, + "z": 0.0 + }, + { + "x": 338.7130681260967, + "y": -309.35470297389975, + "z": 0.0 + }, + { + "x": 338.713679419021, + "y": -308.342647083747, + "z": 0.0 + }, + { + "x": 338.7142907119453, + "y": -307.3305911935946, + "z": 0.0 + }, + { + "x": 338.7149020048695, + "y": -306.318535303442, + "z": 0.0 + }, + { + "x": 338.7155132977938, + "y": -305.3064794132896, + "z": 0.0 + }, + { + "x": 338.7161245907181, + "y": -304.2944235231371, + "z": 0.0 + }, + { + "x": 338.7167358836424, + "y": -303.28236763298435, + "z": 0.0 + }, + { + "x": 338.7173471765667, + "y": -302.27031174283195, + "z": 0.0 + }, + { + "x": 338.7179584694909, + "y": -301.2582558526793, + "z": 0.0 + }, + { + "x": 338.7185697624152, + "y": -300.24619996252693, + "z": 0.0 + }, + { + "x": 338.7191810553395, + "y": -299.2341440723743, + "z": 0.0 + }, + { + "x": 338.7197923482638, + "y": -298.2220881822218, + "z": 0.0 + }, + { + "x": 338.720403641188, + "y": -297.2100322920692, + "z": 0.0 + }, + { + "x": 338.7210149341123, + "y": -296.1979764019168, + "z": 0.0 + }, + { + "x": 338.7216262270366, + "y": -295.18592051176427, + "z": 0.0 + }, + { + "x": 338.7222375199609, + "y": -294.17386462161164, + "z": 0.0 + }, + { + "x": 338.7228488128852, + "y": -293.16180873145913, + "z": 0.0 + }, + { + "x": 338.7234601058094, + "y": -292.1497528413065, + "z": 0.0 + }, + { + "x": 338.7240713987337, + "y": -291.1376969511541, + "z": 0.0 + }, + { + "x": 338.724682691658, + "y": -290.1256410610015, + "z": 0.0 + }, + { + "x": 338.7252939845823, + "y": -289.1135851708491, + "z": 0.0 + }, + { + "x": 338.7259052775065, + "y": -288.10152928069647, + "z": 0.0 + }, + { + "x": 338.7265165704308, + "y": -287.08947339054396, + "z": 0.0 + }, + { + "x": 338.7271278633551, + "y": -286.07741750039145, + "z": 0.0 + }, + { + "x": 338.7277391562794, + "y": -285.0653616102388, + "z": 0.0 + }, + { + "x": 338.72835044920373, + "y": -284.0533057200864, + "z": 0.0 + }, + { + "x": 338.7289617421279, + "y": -283.0412498299338, + "z": 0.0 + }, + { + "x": 338.7295730350522, + "y": -282.0291939397813, + "z": 0.0 + }, + { + "x": 338.73018432797653, + "y": -281.01713804962867, + "z": 0.0 + }, + { + "x": 338.73079562090084, + "y": -280.0050821594763, + "z": 0.0 + }, + { + "x": 338.731406913825, + "y": -278.99302626932365, + "z": 0.0 + }, + { + "x": 338.73201820674933, + "y": -277.98097037917125, + "z": 0.0 + }, + { + "x": 338.73262949967364, + "y": -276.96891448901863, + "z": 0.0 + }, + { + "x": 338.73324079259794, + "y": -275.956858598866, + "z": 0.0 + }, + { + "x": 338.73385208552224, + "y": -274.9448027087136, + "z": 0.0 + }, + { + "x": 338.73446337844643, + "y": -273.932746818561, + "z": 0.0 + }, + { + "x": 338.73507467137074, + "y": -272.9206909284086, + "z": 0.0 + }, + { + "x": 338.73568596429504, + "y": -271.90863503825585, + "z": 0.0 + }, + { + "x": 338.73629725721935, + "y": -270.89657914810346, + "z": 0.0 + }, + { + "x": 338.73690855014365, + "y": -269.88452325795095, + "z": 0.0 + }, + { + "x": 338.73751984306784, + "y": -268.8724673677983, + "z": 0.0 + }, + { + "x": 338.73813113599215, + "y": -267.8604114776458, + "z": 0.0 + }, + { + "x": 338.73874242891645, + "y": -266.8483555874932, + "z": 0.0 + }, + { + "x": 338.73935372184076, + "y": -265.8362996973408, + "z": 0.0 + }, + { + "x": 338.73996501476495, + "y": -264.82424380718817, + "z": 0.0 + }, + { + "x": 338.74057630768925, + "y": -263.81218791703566, + "z": 0.0 + }, + { + "x": 338.74118760061356, + "y": -262.80013202688303, + "z": 0.0 + }, + { + "x": 338.74179889353786, + "y": -261.78807613673064, + "z": 0.0 + }, + { + "x": 338.74241018646217, + "y": -260.7760202465781, + "z": 0.0 + }, + { + "x": 338.74302147938636, + "y": -259.7639643564254, + "z": 0.0 + }, + { + "x": 338.74363277231066, + "y": -258.751908466273, + "z": 0.0 + }, + { + "x": 338.74424406523497, + "y": -257.73985257612037, + "z": 0.0 + }, + { + "x": 338.74485535815927, + "y": -256.727796685968, + "z": 0.0 + }, + { + "x": 338.74546665108346, + "y": -255.71574079581526, + "z": 0.0 + }, + { + "x": 338.74607794400777, + "y": -254.70368490566284, + "z": 0.0 + }, + { + "x": 338.74668923693207, + "y": -253.6916290155102, + "z": 0.0 + }, + { + "x": 338.7473005298564, + "y": -252.67957312535776, + "z": 0.0 + }, + { + "x": 338.7479118227807, + "y": -251.66751723520522, + "z": 0.0 + }, + { + "x": 338.74852311570487, + "y": -250.65546134505257, + "z": 0.0 + }, + { + "x": 338.7491344086292, + "y": -249.64340545490015, + "z": 0.0 + }, + { + "x": 338.7497457015535, + "y": -248.6313495647475, + "z": 0.0 + }, + { + "x": 338.7503569944778, + "y": -247.61929367459507, + "z": 0.0 + }, + { + "x": 338.750968287402, + "y": -246.60723778444242, + "z": 0.0 + }, + { + "x": 338.7515795803263, + "y": -245.59518189429, + "z": 0.0 + }, + { + "x": 338.7521908732506, + "y": -244.58312600413734, + "z": 0.0 + }, + { + "x": 338.7528021661749, + "y": -243.57107011398492, + "z": 0.0 + }, + { + "x": 338.7534134590992, + "y": -242.55901422383238, + "z": 0.0 + }, + { + "x": 338.7540247520234, + "y": -241.54695833367973, + "z": 0.0 + }, + { + "x": 338.7546360449477, + "y": -240.5349024435273, + "z": 0.0 + }, + { + "x": 338.755247337872, + "y": -239.52284655337465, + "z": 0.0 + }, + { + "x": 338.7558586307963, + "y": -238.51079066322222, + "z": 0.0 + }, + { + "x": 338.7564699237205, + "y": -237.49873477306957, + "z": 0.0 + }, + { + "x": 338.7570812166448, + "y": -236.48667888291715, + "z": 0.0 + }, + { + "x": 338.7576925095691, + "y": -235.4746229927645, + "z": 0.0 + }, + { + "x": 338.7583038024934, + "y": -234.46256710261207, + "z": 0.0 + }, + { + "x": 338.7589150954177, + "y": -233.45051121245953, + "z": 0.0 + }, + { + "x": 338.7595263883419, + "y": -232.43845532230688, + "z": 0.0 + }, + { + "x": 338.7601376812662, + "y": -231.42639943215445, + "z": 0.0 + }, + { + "x": 338.7607489741905, + "y": -230.4143435420018, + "z": 0.0 + }, + { + "x": 338.7613602671148, + "y": -229.40228765184938, + "z": 0.0 + }, + { + "x": 338.761971560039, + "y": -228.39023176169673, + "z": 0.0 + }, + { + "x": 338.7625828529633, + "y": -227.3781758715443, + "z": 0.0 + }, + { + "x": 338.7631941458876, + "y": -226.36611998139176, + "z": 0.0 + }, + { + "x": 338.7638054388119, + "y": -225.3540640912391, + "z": 0.0 + }, + { + "x": 338.7644167317362, + "y": -224.34200820108668, + "z": 0.0 + }, + { + "x": 338.7650280246604, + "y": -223.32995231093403, + "z": 0.0 + }, + { + "x": 338.7656393175847, + "y": -222.3178964207816, + "z": 0.0 + }, + { + "x": 338.766250610509, + "y": -221.30584053062896, + "z": 0.0 + }, + { + "x": 338.7668619034333, + "y": -220.29378464047653, + "z": 0.0 + }, + { + "x": 338.7674731963575, + "y": -219.28172875032388, + "z": 0.0 + }, + { + "x": 338.7680844892818, + "y": -218.26967286017145, + "z": 0.0 + }, + { + "x": 338.7686957822061, + "y": -217.25761697001892, + "z": 0.0 + }, + { + "x": 338.7693070751304, + "y": -216.24556107986626, + "z": 0.0 + }, + { + "x": 338.76991836805473, + "y": -215.23350518971384, + "z": 0.0 + }, + { + "x": 338.7705296609789, + "y": -214.2214492995612, + "z": 0.0 + }, + { + "x": 338.7711409539032, + "y": -213.20939340940876, + "z": 0.0 + }, + { + "x": 338.77175224682753, + "y": -212.1973375192561, + "z": 0.0 + }, + { + "x": 338.77236353975184, + "y": -211.18528162910368, + "z": 0.0 + }, + { + "x": 338.772974832676, + "y": -210.17322573895103, + "z": 0.0 + }, + { + "x": 338.77358612560033, + "y": -209.1611698487986, + "z": 0.0 + } + ] + }, + { + "id": 99, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.7735868552588, + "y": -209.1587538051048, + "z": 0.0 + }, + { + "x": 334.7729755623345, + "y": -210.17080969525745, + "z": 0.0 + }, + { + "x": 334.7723642694102, + "y": -211.18286558540987, + "z": 0.0 + }, + { + "x": 334.7717529764859, + "y": -212.19492147556252, + "z": 0.0 + }, + { + "x": 334.7711416835617, + "y": -213.20697736571495, + "z": 0.0 + }, + { + "x": 334.7705303906374, + "y": -214.2190332558676, + "z": 0.0 + }, + { + "x": 334.7699190977131, + "y": -215.23108914602003, + "z": 0.0 + }, + { + "x": 334.7693078047888, + "y": -216.24314503617268, + "z": 0.0 + }, + { + "x": 334.7686965118646, + "y": -217.2552009263251, + "z": 0.0 + }, + { + "x": 334.7680852189403, + "y": -218.26725681647764, + "z": 0.0 + }, + { + "x": 334.767473926016, + "y": -219.2793127066303, + "z": 0.0 + }, + { + "x": 334.7668626330917, + "y": -220.29136859678272, + "z": 0.0 + }, + { + "x": 334.7662513401674, + "y": -221.30342448693537, + "z": 0.0 + }, + { + "x": 334.7656400472432, + "y": -222.3154803770878, + "z": 0.0 + }, + { + "x": 334.7650287543189, + "y": -223.32753626724045, + "z": 0.0 + }, + { + "x": 334.7644174613946, + "y": -224.33959215739287, + "z": 0.0 + }, + { + "x": 334.7638061684703, + "y": -225.35164804754552, + "z": 0.0 + }, + { + "x": 334.7631948755461, + "y": -226.36370393769795, + "z": 0.0 + }, + { + "x": 334.7625835826218, + "y": -227.3757598278505, + "z": 0.0 + }, + { + "x": 334.7619722896975, + "y": -228.38781571800314, + "z": 0.0 + }, + { + "x": 334.7613609967732, + "y": -229.39987160815556, + "z": 0.0 + }, + { + "x": 334.7607497038489, + "y": -230.41192749830822, + "z": 0.0 + }, + { + "x": 334.7601384109247, + "y": -231.42398338846064, + "z": 0.0 + }, + { + "x": 334.7595271180004, + "y": -232.4360392786133, + "z": 0.0 + }, + { + "x": 334.7589158250761, + "y": -233.44809516876572, + "z": 0.0 + }, + { + "x": 334.7583045321518, + "y": -234.46015105891826, + "z": 0.0 + }, + { + "x": 334.7576932392275, + "y": -235.4722069490709, + "z": 0.0 + }, + { + "x": 334.7570819463033, + "y": -236.48426283922333, + "z": 0.0 + }, + { + "x": 334.756470653379, + "y": -237.496318729376, + "z": 0.0 + }, + { + "x": 334.7558593604547, + "y": -238.5083746195284, + "z": 0.0 + }, + { + "x": 334.7552480675304, + "y": -239.52043050968106, + "z": 0.0 + }, + { + "x": 334.7546367746062, + "y": -240.5324863998335, + "z": 0.0 + }, + { + "x": 334.7540254816819, + "y": -241.54454228998614, + "z": 0.0 + }, + { + "x": 334.7534141887576, + "y": -242.55659818013856, + "z": 0.0 + }, + { + "x": 334.75280289583327, + "y": -243.5686540702911, + "z": 0.0 + }, + { + "x": 334.75219160290897, + "y": -244.58070996044376, + "z": 0.0 + }, + { + "x": 334.7515803099848, + "y": -245.59276585059618, + "z": 0.0 + }, + { + "x": 334.75096901706047, + "y": -246.60482174074883, + "z": 0.0 + }, + { + "x": 334.75035772413617, + "y": -247.61687763090126, + "z": 0.0 + }, + { + "x": 334.74974643121186, + "y": -248.6289335210539, + "z": 0.0 + }, + { + "x": 334.74913513828767, + "y": -249.64098941120633, + "z": 0.0 + }, + { + "x": 334.74852384536337, + "y": -250.653045301359, + "z": 0.0 + }, + { + "x": 334.74791255243906, + "y": -251.6651011915114, + "z": 0.0 + }, + { + "x": 334.74730125951476, + "y": -252.67715708166395, + "z": 0.0 + }, + { + "x": 334.74668996659045, + "y": -253.6892129718166, + "z": 0.0 + }, + { + "x": 334.74607867366626, + "y": -254.70126886196903, + "z": 0.0 + }, + { + "x": 334.74546738074196, + "y": -255.71332475212168, + "z": 0.0 + }, + { + "x": 334.74485608781765, + "y": -256.7253806422741, + "z": 0.0 + }, + { + "x": 334.74424479489335, + "y": -257.7374365324267, + "z": 0.0 + }, + { + "x": 334.74363350196916, + "y": -258.74949242257924, + "z": 0.0 + }, + { + "x": 334.74302220904485, + "y": -259.76154831273186, + "z": 0.0 + }, + { + "x": 334.74241091612055, + "y": -260.77360420288426, + "z": 0.0 + }, + { + "x": 334.74179962319624, + "y": -261.78566009303677, + "z": 0.0 + }, + { + "x": 334.74118833027194, + "y": -262.7977159831895, + "z": 0.0 + }, + { + "x": 334.74057703734775, + "y": -263.8097718733419, + "z": 0.0 + }, + { + "x": 334.73996574442344, + "y": -264.8218277634945, + "z": 0.0 + }, + { + "x": 334.73935445149914, + "y": -265.8338836536469, + "z": 0.0 + }, + { + "x": 334.73874315857483, + "y": -266.84593954379966, + "z": 0.0 + }, + { + "x": 334.73813186565064, + "y": -267.85799543395206, + "z": 0.0 + }, + { + "x": 334.73752057272634, + "y": -268.8700513241047, + "z": 0.0 + }, + { + "x": 334.73690927980203, + "y": -269.8821072142571, + "z": 0.0 + }, + { + "x": 334.73629798687773, + "y": -270.8941631044097, + "z": 0.0 + }, + { + "x": 334.7356866939534, + "y": -271.9062189945623, + "z": 0.0 + }, + { + "x": 334.73507540102923, + "y": -272.9182748847147, + "z": 0.0 + }, + { + "x": 334.73446410810493, + "y": -273.93033077486734, + "z": 0.0 + }, + { + "x": 334.7338528151806, + "y": -274.94238666501985, + "z": 0.0 + }, + { + "x": 334.7332415222563, + "y": -275.9544425551725, + "z": 0.0 + }, + { + "x": 334.73263022933213, + "y": -276.9664984453249, + "z": 0.0 + }, + { + "x": 334.7320189364078, + "y": -277.9785543354774, + "z": 0.0 + }, + { + "x": 334.7314076434835, + "y": -278.99061022563, + "z": 0.0 + }, + { + "x": 334.7307963505592, + "y": -280.0026661157824, + "z": 0.0 + }, + { + "x": 334.7301850576349, + "y": -281.01472200593514, + "z": 0.0 + }, + { + "x": 334.7295737647107, + "y": -282.02677789608754, + "z": 0.0 + }, + { + "x": 334.7289624717864, + "y": -283.03883378624016, + "z": 0.0 + }, + { + "x": 334.7283511788621, + "y": -284.05088967639256, + "z": 0.0 + }, + { + "x": 334.7277398859378, + "y": -285.0629455665452, + "z": 0.0 + }, + { + "x": 334.7271285930136, + "y": -286.0750014566977, + "z": 0.0 + }, + { + "x": 334.7265173000893, + "y": -287.0870573468502, + "z": 0.0 + }, + { + "x": 334.725906007165, + "y": -288.0991132370028, + "z": 0.0 + }, + { + "x": 334.7252947142407, + "y": -289.1111691271552, + "z": 0.0 + }, + { + "x": 334.7246834213164, + "y": -290.12322501730785, + "z": 0.0 + }, + { + "x": 334.7240721283922, + "y": -291.13528090746036, + "z": 0.0 + }, + { + "x": 334.7234608354679, + "y": -292.147336797613, + "z": 0.0 + }, + { + "x": 334.7228495425436, + "y": -293.1593926877654, + "z": 0.0 + }, + { + "x": 334.7222382496193, + "y": -294.171448577918, + "z": 0.0 + }, + { + "x": 334.7216269566951, + "y": -295.1835044680704, + "z": 0.0 + }, + { + "x": 334.7210156637708, + "y": -296.195560358223, + "z": 0.0 + }, + { + "x": 334.7204043708465, + "y": -297.20761624837564, + "z": 0.0 + }, + { + "x": 334.7197930779222, + "y": -298.21967213852804, + "z": 0.0 + }, + { + "x": 334.7191817849979, + "y": -299.23172802868066, + "z": 0.0 + }, + { + "x": 334.7185704920737, + "y": -300.24378391883306, + "z": 0.0 + }, + { + "x": 334.7179591991494, + "y": -301.2558398089858, + "z": 0.0 + }, + { + "x": 334.7173479062251, + "y": -302.2678956991382, + "z": 0.0 + }, + { + "x": 334.7167366133008, + "y": -303.2799515892908, + "z": 0.0 + }, + { + "x": 334.7161253203766, + "y": -304.2920074794432, + "z": 0.0 + }, + { + "x": 334.7155140274523, + "y": -305.3040633695957, + "z": 0.0 + }, + { + "x": 334.714902734528, + "y": -306.31611925974846, + "z": 0.0 + }, + { + "x": 334.7142914416037, + "y": -307.32817514990086, + "z": 0.0 + }, + { + "x": 334.7136801486794, + "y": -308.3402310400535, + "z": 0.0 + }, + { + "x": 334.7130688557552, + "y": -309.3522869302059, + "z": 0.0 + }, + { + "x": 334.7124575628309, + "y": -310.3643428203585, + "z": 0.0 + }, + { + "x": 334.7118462699066, + "y": -311.376398710511, + "z": 0.0 + }, + { + "x": 334.71123497698227, + "y": -312.38845460066364, + "z": 0.0 + }, + { + "x": 334.7106236840581, + "y": -313.40051049081603, + "z": 0.0 + }, + { + "x": 334.7100123911338, + "y": -314.41256638096854, + "z": 0.0 + }, + { + "x": 334.70940109820947, + "y": -315.42462227112117, + "z": 0.0 + }, + { + "x": 334.70878980528516, + "y": -316.4366781612737, + "z": 0.0 + }, + { + "x": 334.70817851236086, + "y": -317.4487340514262, + "z": 0.0 + } + ] + }, + { + "id": 100, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": -3.987604267005838, + "y": -317.73882523260744, + "z": 0.0 + }, + { + "x": -3.9515143703865285, + "y": -318.7810983745968, + "z": 0.0 + }, + { + "x": -3.8357237138442493, + "y": -320.0628235375496, + "z": 0.0 + }, + { + "x": -3.6035435046796116, + "y": -321.42014727038304, + "z": 0.0 + }, + { + "x": -3.2131749323592027, + "y": -322.83156756042615, + "z": 0.0 + }, + { + "x": -2.7092233918898647, + "y": -324.14949690005676, + "z": 0.0 + }, + { + "x": -2.1098992234032172, + "y": -325.3878357724527, + "z": 0.0 + }, + { + "x": -1.409105747859908, + "y": -326.5720532697058, + "z": 0.0 + }, + { + "x": -0.6105033054651816, + "y": -327.6951555653618, + "z": 0.0 + }, + { + "x": 0.43807983625093394, + "y": -328.8516649130274, + "z": 0.0 + }, + { + "x": 1.5705790610352346, + "y": -329.83579929197316, + "z": 0.0 + }, + { + "x": 2.80235614833182, + "y": -330.68122629595655, + "z": 0.0 + }, + { + "x": 4.122257124910212, + "y": -331.3817673703651, + "z": 0.0 + }, + { + "x": 5.514762444971803, + "y": -331.9288192605436, + "z": 0.0 + }, + { + "x": 7.049720839454694, + "y": -332.31271128481416, + "z": 0.0 + }, + { + "x": 8.585458950714365, + "y": -332.4921951397266, + "z": 0.0 + }, + { + "x": 9.896942070360003, + "y": -332.53837993970717, + "z": 0.0 + } + ] + }, + { + "id": 101, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 10.123058387403669, + "y": -324.54157611498033, + "z": 0.0 + }, + { + "x": 9.350105318731895, + "y": -324.5288217382166, + "z": 0.0 + }, + { + "x": 8.811596495545004, + "y": -324.5091355549019, + "z": 0.0 + }, + { + "x": 8.311184096648525, + "y": -324.43348564915357, + "z": 0.0 + }, + { + "x": 7.750386387471255, + "y": -324.25178293460726, + "z": 0.0 + }, + { + "x": 7.2149908856266585, + "y": -324.00824685081966, + "z": 0.0 + }, + { + "x": 6.710985586909846, + "y": -323.7058546960779, + "z": 0.0 + }, + { + "x": 6.247647921620716, + "y": -323.3517632174102, + "z": 0.0 + }, + { + "x": 5.861716980926192, + "y": -322.9929896214138, + "z": 0.0 + }, + { + "x": 5.4339661146776255, + "y": -322.42809773372966, + "z": 0.0 + }, + { + "x": 5.055313250704519, + "y": -321.82978431120443, + "z": 0.0 + }, + { + "x": 4.728104996227859, + "y": -321.2022699920468, + "z": 0.0 + }, + { + "x": 4.464117028182713, + "y": -320.58230364692054, + "z": 0.0 + }, + { + "x": 4.267820431277832, + "y": -319.99129115876485, + "z": 0.0 + }, + { + "x": 4.127488182559836, + "y": -319.29649704514026, + "z": 0.0 + }, + { + "x": 4.0436940593728075, + "y": -318.5042551613782, + "z": 0.0 + }, + { + "x": 4.007604162753498, + "y": -317.4619820193888, + "z": 0.0 + } + ] + }, + { + "id": 102, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 10.010000228881836, + "y": -328.53997802734375, + "z": 0.0 + }, + { + "x": 8.96778213472313, + "y": -328.5105084389716, + "z": 0.0 + }, + { + "x": 7.9306586674998485, + "y": -328.410923419858, + "z": 0.0 + }, + { + "x": 6.912973270810164, + "y": -328.1811524548486, + "z": 0.0 + }, + { + "x": 5.9363217561907335, + "y": -327.8167751524862, + "z": 0.0 + }, + { + "x": 5.008673516979239, + "y": -327.3447365733881, + "z": 0.0 + }, + { + "x": 4.14078232397254, + "y": -326.7708269940255, + "z": 0.0 + }, + { + "x": 3.3428638789358254, + "y": -326.1017140652188, + "z": 0.0 + }, + { + "x": 2.6256068377305053, + "y": -325.3440725933878, + "z": 0.0 + }, + { + "x": 2.012430183408859, + "y": -324.50007550171773, + "z": 0.0 + }, + { + "x": 1.4727070136506513, + "y": -323.6088100418286, + "z": 0.0 + }, + { + "x": 1.0094408021689976, + "y": -322.6758834460518, + "z": 0.0 + }, + { + "x": 0.6254710479117551, + "y": -321.70693560367334, + "z": 0.0 + }, + { + "x": 0.3321384632991102, + "y": -320.70571921457395, + "z": 0.0 + }, + { + "x": 0.14588223435779368, + "y": -319.67966029134493, + "z": 0.0 + }, + { + "x": 0.04608984449313969, + "y": -318.6426767679875, + "z": 0.0 + }, + { + "x": 0.009999947873830324, + "y": -317.6004036259981, + "z": 0.0 + } + ] + }, + { + "id": 103, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": -1.988802159566004, + "y": -317.6696144293028, + "z": 0.0 + }, + { + "x": -1.9527122629466944, + "y": -318.71188757129215, + "z": 0.0 + }, + { + "x": -1.8449207397432277, + "y": -319.8712419144473, + "z": 0.0 + }, + { + "x": -1.6357025206902507, + "y": -321.0629332424785, + "z": 0.0 + }, + { + "x": -1.2938519422237238, + "y": -322.2692515820497, + "z": 0.0 + }, + { + "x": -0.8498912948604336, + "y": -323.4126901730543, + "z": 0.0 + }, + { + "x": -0.318596104876283, + "y": -324.4983229071406, + "z": 0.0 + }, + { + "x": 0.30166221777447544, + "y": -325.53606438571177, + "z": 0.0 + }, + { + "x": 1.0075517661326618, + "y": -326.51961407937483, + "z": 0.0 + }, + { + "x": 1.8904718575933797, + "y": -327.4766894891231, + "z": 0.0 + }, + { + "x": 2.8556806925038876, + "y": -328.30331314299934, + "z": 0.0 + }, + { + "x": 3.9055148326555296, + "y": -329.0129814346723, + "z": 0.0 + }, + { + "x": 5.029289440550473, + "y": -329.59927126142566, + "z": 0.0 + }, + { + "x": 6.213867857890984, + "y": -330.0549858576961, + "z": 0.0 + }, + { + "x": 7.490189753477271, + "y": -330.36181735233606, + "z": 0.0 + }, + { + "x": 8.776620542718748, + "y": -330.5013517893491, + "z": 0.0 + }, + { + "x": 9.95347114962092, + "y": -330.53917898352546, + "z": 0.0 + } + ] + }, + { + "id": 104, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 10.066529308142751, + "y": -326.54077707116204, + "z": 0.0 + }, + { + "x": 9.158943726727513, + "y": -326.51966508859414, + "z": 0.0 + }, + { + "x": 8.371127581522426, + "y": -326.46002948738, + "z": 0.0 + }, + { + "x": 7.612078683729344, + "y": -326.30731905200105, + "z": 0.0 + }, + { + "x": 6.843354071830994, + "y": -326.0342790435467, + "z": 0.0 + }, + { + "x": 6.111832201302949, + "y": -325.6764917121039, + "z": 0.0 + }, + { + "x": 5.4258839554411935, + "y": -325.2383408450517, + "z": 0.0 + }, + { + "x": 4.795255900278271, + "y": -324.7267386413145, + "z": 0.0 + }, + { + "x": 4.243661909328349, + "y": -324.1685311074008, + "z": 0.0 + }, + { + "x": 3.723198149043242, + "y": -323.4640866177237, + "z": 0.0 + }, + { + "x": 3.264010132177585, + "y": -322.71929717651653, + "z": 0.0 + }, + { + "x": 2.8687728991984285, + "y": -321.9390767190493, + "z": 0.0 + }, + { + "x": 2.5447940380472343, + "y": -321.14461962529697, + "z": 0.0 + }, + { + "x": 2.2999794472884707, + "y": -320.3485051866694, + "z": 0.0 + }, + { + "x": 2.136685208458815, + "y": -319.48807866824257, + "z": 0.0 + }, + { + "x": 2.0448919519329736, + "y": -318.57346596468284, + "z": 0.0 + }, + { + "x": 2.0088020553136645, + "y": -317.53119282269347, + "z": 0.0 + } + ] + }, + { + "id": 105, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.37701805715766, + "y": -46.14939141843673, + "z": 0.0 + }, + { + "x": 94.37686197589477, + "y": -45.122626724417536, + "z": 0.0 + }, + { + "x": 94.37670589463187, + "y": -44.0958620303983, + "z": 0.0 + }, + { + "x": 94.37654981336898, + "y": -43.069097336379166, + "z": 0.0 + }, + { + "x": 94.3763937321061, + "y": -42.04233264235998, + "z": 0.0 + }, + { + "x": 94.3762376508432, + "y": -41.01556794834073, + "z": 0.0 + }, + { + "x": 94.37608156958031, + "y": -39.9888032543216, + "z": 0.0 + }, + { + "x": 94.37592548831742, + "y": -38.96203856030242, + "z": 0.0 + }, + { + "x": 94.37576940705453, + "y": -37.93527386628323, + "z": 0.0 + }, + { + "x": 94.37561332579165, + "y": -36.90850917226405, + "z": 0.0 + }, + { + "x": 94.37545724452875, + "y": -35.8817444782448, + "z": 0.0 + }, + { + "x": 94.37530116326586, + "y": -34.85497978422568, + "z": 0.0 + }, + { + "x": 94.37514508200297, + "y": -33.82821509020649, + "z": 0.0 + }, + { + "x": 94.37498900074007, + "y": -32.80145039618724, + "z": 0.0 + }, + { + "x": 94.37483291947719, + "y": -31.774685702168114, + "z": 0.0 + }, + { + "x": 94.3746768382143, + "y": -30.74792100814893, + "z": 0.0 + }, + { + "x": 94.3745207569514, + "y": -29.721156314129683, + "z": 0.0 + }, + { + "x": 94.37436467568851, + "y": -28.694391620110554, + "z": 0.0 + }, + { + "x": 94.37420859442562, + "y": -27.667626926091366, + "z": 0.0 + }, + { + "x": 94.37405251316274, + "y": -26.64086223207218, + "z": 0.0 + }, + { + "x": 94.37389643189984, + "y": -25.614097538052935, + "z": 0.0 + }, + { + "x": 94.37374035063695, + "y": -24.587332844033806, + "z": 0.0 + }, + { + "x": 94.37358426937405, + "y": -23.56056815001456, + "z": 0.0 + }, + { + "x": 94.37342818811116, + "y": -22.533803455995436, + "z": 0.0 + }, + { + "x": 94.37327210684828, + "y": -21.507038761976247, + "z": 0.0 + }, + { + "x": 94.37311602558538, + "y": -20.480274067957005, + "z": 0.0 + }, + { + "x": 94.3729599443225, + "y": -19.453509373937926, + "z": 0.0 + }, + { + "x": 94.3728038630596, + "y": -18.42674467991863, + "z": 0.0 + }, + { + "x": 94.37264778179672, + "y": -17.3999799858995, + "z": 0.0 + }, + { + "x": 94.37249170053383, + "y": -16.373215291880314, + "z": 0.0 + }, + { + "x": 94.37233561927093, + "y": -15.346450597861072, + "z": 0.0 + }, + { + "x": 94.37217953800804, + "y": -14.31968590384194, + "z": 0.0 + }, + { + "x": 94.37202345674515, + "y": -13.292921209822753, + "z": 0.0 + }, + { + "x": 94.37186737548225, + "y": -12.26615651580351, + "z": 0.0 + }, + { + "x": 94.37171129421937, + "y": -11.239391821784379, + "z": 0.0 + } + ] + }, + { + "id": 106, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.3717113866508, + "y": -11.240607923288396, + "z": 0.0 + }, + { + "x": 86.37186746791369, + "y": -12.267372617307638, + "z": 0.0 + }, + { + "x": 86.37202354917659, + "y": -13.29413731132677, + "z": 0.0 + }, + { + "x": 86.37217963043948, + "y": -14.320902005345957, + "z": 0.0 + }, + { + "x": 86.37233571170236, + "y": -15.347666699365199, + "z": 0.0 + }, + { + "x": 86.37249179296526, + "y": -16.374431393384327, + "z": 0.0 + }, + { + "x": 86.37264787422815, + "y": -17.401196087403513, + "z": 0.0 + }, + { + "x": 86.37280395549104, + "y": -18.427960781422758, + "z": 0.0 + }, + { + "x": 86.37296003675394, + "y": -19.454725475441833, + "z": 0.0 + }, + { + "x": 86.37311611801681, + "y": -20.481490169461132, + "z": 0.0 + }, + { + "x": 86.37327219927971, + "y": -21.50825486348026, + "z": 0.0 + }, + { + "x": 86.3734282805426, + "y": -22.53501955749945, + "z": 0.0 + }, + { + "x": 86.37358436180548, + "y": -23.561784251518688, + "z": 0.0 + }, + { + "x": 86.37374044306839, + "y": -24.58854894553782, + "z": 0.0 + }, + { + "x": 86.37389652433127, + "y": -25.615313639557062, + "z": 0.0 + }, + { + "x": 86.37405260559417, + "y": -26.642078333576194, + "z": 0.0 + }, + { + "x": 86.37420868685706, + "y": -27.66884302759538, + "z": 0.0 + }, + { + "x": 86.37436476811995, + "y": -28.695607721614568, + "z": 0.0 + }, + { + "x": 86.37452084938283, + "y": -29.72237241563381, + "z": 0.0 + }, + { + "x": 86.37467693064573, + "y": -30.749137109652942, + "z": 0.0 + }, + { + "x": 86.37483301190862, + "y": -31.775901803672127, + "z": 0.0 + }, + { + "x": 86.37498909317151, + "y": -32.80266649769137, + "z": 0.0 + }, + { + "x": 86.37514517443441, + "y": -33.829431191710505, + "z": 0.0 + }, + { + "x": 86.3753012556973, + "y": -34.85619588572969, + "z": 0.0 + }, + { + "x": 86.37545733696018, + "y": -35.882960579748925, + "z": 0.0 + }, + { + "x": 86.37561341822308, + "y": -36.90972527376806, + "z": 0.0 + }, + { + "x": 86.37576949948597, + "y": -37.936489967787246, + "z": 0.0 + }, + { + "x": 86.37592558074886, + "y": -38.96325466180643, + "z": 0.0 + }, + { + "x": 86.37608166201174, + "y": -39.990019355825616, + "z": 0.0 + }, + { + "x": 86.37623774327463, + "y": -41.01678404984486, + "z": 0.0 + }, + { + "x": 86.37639382453753, + "y": -42.043548743863994, + "z": 0.0 + }, + { + "x": 86.37654990580042, + "y": -43.07031343788318, + "z": 0.0 + }, + { + "x": 86.3767059870633, + "y": -44.09707813190243, + "z": 0.0 + }, + { + "x": 86.3768620683262, + "y": -45.12384282592155, + "z": 0.0 + }, + { + "x": 86.37701814958909, + "y": -46.15060751994074, + "z": 0.0 + } + ] + }, + { + "id": 107, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 90.37171134043508, + "y": -11.239999872536387, + "z": 0.0 + }, + { + "x": 90.37186742169797, + "y": -12.266764566555574, + "z": 0.0 + }, + { + "x": 90.37202350296087, + "y": -13.293529260574761, + "z": 0.0 + }, + { + "x": 90.37217958422376, + "y": -14.320293954593948, + "z": 0.0 + }, + { + "x": 90.37233566548664, + "y": -15.347058648613135, + "z": 0.0 + }, + { + "x": 90.37249174674955, + "y": -16.37382334263232, + "z": 0.0 + }, + { + "x": 90.37264782801243, + "y": -17.400588036651506, + "z": 0.0 + }, + { + "x": 90.37280390927532, + "y": -18.427352730670695, + "z": 0.0 + }, + { + "x": 90.37295999053822, + "y": -19.45411742468988, + "z": 0.0 + }, + { + "x": 90.37311607180109, + "y": -20.48088211870907, + "z": 0.0 + }, + { + "x": 90.373272153064, + "y": -21.507646812728254, + "z": 0.0 + }, + { + "x": 90.37342823432688, + "y": -22.534411506747443, + "z": 0.0 + }, + { + "x": 90.37358431558977, + "y": -23.561176200766624, + "z": 0.0 + }, + { + "x": 90.37374039685267, + "y": -24.587940894785813, + "z": 0.0 + }, + { + "x": 90.37389647811555, + "y": -25.614705588805, + "z": 0.0 + }, + { + "x": 90.37405255937846, + "y": -26.641470282824187, + "z": 0.0 + }, + { + "x": 90.37420864064134, + "y": -27.668234976843372, + "z": 0.0 + }, + { + "x": 90.37436472190423, + "y": -28.69499967086256, + "z": 0.0 + }, + { + "x": 90.37452080316712, + "y": -29.721764364881746, + "z": 0.0 + }, + { + "x": 90.37467688443002, + "y": -30.748529058900935, + "z": 0.0 + }, + { + "x": 90.3748329656929, + "y": -31.77529375292012, + "z": 0.0 + }, + { + "x": 90.37498904695579, + "y": -32.802058446939306, + "z": 0.0 + }, + { + "x": 90.37514512821869, + "y": -33.8288231409585, + "z": 0.0 + }, + { + "x": 90.37530120948158, + "y": -34.85558783497768, + "z": 0.0 + }, + { + "x": 90.37545729074446, + "y": -35.88235252899686, + "z": 0.0 + }, + { + "x": 90.37561337200736, + "y": -36.909117223016054, + "z": 0.0 + }, + { + "x": 90.37576945327025, + "y": -37.93588191703524, + "z": 0.0 + }, + { + "x": 90.37592553453314, + "y": -38.962646611054424, + "z": 0.0 + }, + { + "x": 90.37608161579602, + "y": -39.98941130507361, + "z": 0.0 + }, + { + "x": 90.37623769705891, + "y": -41.016175999092795, + "z": 0.0 + }, + { + "x": 90.37639377832181, + "y": -42.04294069311199, + "z": 0.0 + }, + { + "x": 90.3765498595847, + "y": -43.06970538713117, + "z": 0.0 + }, + { + "x": 90.37670594084759, + "y": -44.096470081150365, + "z": 0.0 + }, + { + "x": 90.37686202211049, + "y": -45.12323477516954, + "z": 0.0 + }, + { + "x": 90.37701810337337, + "y": -46.149999469188735, + "z": 0.0 + } + ] + }, + { + "id": 108, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.37701808026551, + "y": -46.14969544381273, + "z": 0.0 + }, + { + "x": 92.37686199900263, + "y": -45.12293074979354, + "z": 0.0 + }, + { + "x": 92.37670591773973, + "y": -44.09616605577433, + "z": 0.0 + }, + { + "x": 92.37654983647684, + "y": -43.06940136175517, + "z": 0.0 + }, + { + "x": 92.37639375521395, + "y": -42.042636667735984, + "z": 0.0 + }, + { + "x": 92.37623767395105, + "y": -41.01587197371676, + "z": 0.0 + }, + { + "x": 92.37608159268817, + "y": -39.989107279697606, + "z": 0.0 + }, + { + "x": 92.37592551142528, + "y": -38.96234258567842, + "z": 0.0 + }, + { + "x": 92.37576943016239, + "y": -37.935577891659236, + "z": 0.0 + }, + { + "x": 92.3756133488995, + "y": -36.90881319764005, + "z": 0.0 + }, + { + "x": 92.3754572676366, + "y": -35.88204850362083, + "z": 0.0 + }, + { + "x": 92.37530118637372, + "y": -34.85528380960168, + "z": 0.0 + }, + { + "x": 92.37514510511083, + "y": -33.828519115582495, + "z": 0.0 + }, + { + "x": 92.37498902384793, + "y": -32.801754421563274, + "z": 0.0 + }, + { + "x": 92.37483294258504, + "y": -31.774989727544117, + "z": 0.0 + }, + { + "x": 92.37467686132216, + "y": -30.748225033524932, + "z": 0.0 + }, + { + "x": 92.37452078005926, + "y": -29.721460339505715, + "z": 0.0 + }, + { + "x": 92.37436469879637, + "y": -28.694695645486558, + "z": 0.0 + }, + { + "x": 92.37420861753348, + "y": -27.66793095146737, + "z": 0.0 + }, + { + "x": 92.3740525362706, + "y": -26.641166257448184, + "z": 0.0 + }, + { + "x": 92.3738964550077, + "y": -25.614401563428967, + "z": 0.0 + }, + { + "x": 92.37374037374481, + "y": -24.58763686940981, + "z": 0.0 + }, + { + "x": 92.37358429248191, + "y": -23.560872175390593, + "z": 0.0 + }, + { + "x": 92.37342821121902, + "y": -22.53410748137144, + "z": 0.0 + }, + { + "x": 92.37327212995613, + "y": -21.50734278735225, + "z": 0.0 + }, + { + "x": 92.37311604869323, + "y": -20.480578093333037, + "z": 0.0 + }, + { + "x": 92.37295996743036, + "y": -19.4538133993139, + "z": 0.0 + }, + { + "x": 92.37280388616746, + "y": -18.427048705294663, + "z": 0.0 + }, + { + "x": 92.37264780490457, + "y": -17.400284011275502, + "z": 0.0 + }, + { + "x": 92.37249172364169, + "y": -16.373519317256317, + "z": 0.0 + }, + { + "x": 92.37233564237879, + "y": -15.346754623237103, + "z": 0.0 + }, + { + "x": 92.3721795611159, + "y": -14.319989929217943, + "z": 0.0 + }, + { + "x": 92.37202347985301, + "y": -13.293225235198758, + "z": 0.0 + }, + { + "x": 92.37186739859011, + "y": -12.266460541179542, + "z": 0.0 + }, + { + "x": 92.37171131732723, + "y": -11.239695847160384, + "z": 0.0 + } + ] + }, + { + "id": 109, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.37171136354294, + "y": -11.24030389791239, + "z": 0.0 + }, + { + "x": 88.37186744480583, + "y": -12.267068591931606, + "z": 0.0 + }, + { + "x": 88.37202352606873, + "y": -13.293833285950765, + "z": 0.0 + }, + { + "x": 88.37217960733162, + "y": -14.320597979969953, + "z": 0.0 + }, + { + "x": 88.3723356885945, + "y": -15.347362673989167, + "z": 0.0 + }, + { + "x": 88.3724917698574, + "y": -16.374127368008324, + "z": 0.0 + }, + { + "x": 88.37264785112029, + "y": -17.40089206202751, + "z": 0.0 + }, + { + "x": 88.37280393238318, + "y": -18.427656756046726, + "z": 0.0 + }, + { + "x": 88.37296001364608, + "y": -19.45442145006586, + "z": 0.0 + }, + { + "x": 88.37311609490895, + "y": -20.4811861440851, + "z": 0.0 + }, + { + "x": 88.37327217617185, + "y": -21.507950838104257, + "z": 0.0 + }, + { + "x": 88.37342825743474, + "y": -22.534715532123446, + "z": 0.0 + }, + { + "x": 88.37358433869763, + "y": -23.561480226142656, + "z": 0.0 + }, + { + "x": 88.37374041996053, + "y": -24.588244920161817, + "z": 0.0 + }, + { + "x": 88.37389650122341, + "y": -25.61500961418103, + "z": 0.0 + }, + { + "x": 88.37405258248631, + "y": -26.64177430820019, + "z": 0.0 + }, + { + "x": 88.3742086637492, + "y": -27.668539002219376, + "z": 0.0 + }, + { + "x": 88.37436474501209, + "y": -28.695303696238565, + "z": 0.0 + }, + { + "x": 88.37452082627497, + "y": -29.72206839025778, + "z": 0.0 + }, + { + "x": 88.37467690753788, + "y": -30.74883308427694, + "z": 0.0 + }, + { + "x": 88.37483298880076, + "y": -31.775597778296124, + "z": 0.0 + }, + { + "x": 88.37498907006365, + "y": -32.80236247231534, + "z": 0.0 + }, + { + "x": 88.37514515132655, + "y": -33.8291271663345, + "z": 0.0 + }, + { + "x": 88.37530123258944, + "y": -34.85589186035369, + "z": 0.0 + }, + { + "x": 88.37545731385232, + "y": -35.88265655437289, + "z": 0.0 + }, + { + "x": 88.37561339511522, + "y": -36.90942124839206, + "z": 0.0 + }, + { + "x": 88.37576947637811, + "y": -37.93618594241124, + "z": 0.0 + }, + { + "x": 88.375925557641, + "y": -38.96295063643043, + "z": 0.0 + }, + { + "x": 88.37608163890388, + "y": -39.98971533044961, + "z": 0.0 + }, + { + "x": 88.37623772016677, + "y": -41.01648002446883, + "z": 0.0 + }, + { + "x": 88.37639380142967, + "y": -42.04324471848799, + "z": 0.0 + }, + { + "x": 88.37654988269256, + "y": -43.070009412507176, + "z": 0.0 + }, + { + "x": 88.37670596395544, + "y": -44.0967741065264, + "z": 0.0 + }, + { + "x": 88.37686204521835, + "y": -45.123538800545546, + "z": 0.0 + }, + { + "x": 88.37701812648123, + "y": -46.15030349456474, + "z": 0.0 + } + ] + }, + { + "id": 110, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.38821987213683, + "y": -119.83939056702773, + "z": 0.0 + }, + { + "x": 94.38806580280658, + "y": -118.82586116697327, + "z": 0.0 + }, + { + "x": 94.38791173347632, + "y": -117.81233176691876, + "z": 0.0 + }, + { + "x": 94.38775766414606, + "y": -116.79880236686435, + "z": 0.0 + }, + { + "x": 94.38760359481581, + "y": -115.7852729668099, + "z": 0.0 + }, + { + "x": 94.38744952548556, + "y": -114.77174356675543, + "z": 0.0 + }, + { + "x": 94.3872954561553, + "y": -113.75821416670092, + "z": 0.0 + }, + { + "x": 94.38714138682504, + "y": -112.74468476664646, + "z": 0.0 + }, + { + "x": 94.38698731749479, + "y": -111.73115536659205, + "z": 0.0 + }, + { + "x": 94.38683324816454, + "y": -110.7176259665376, + "z": 0.0 + }, + { + "x": 94.38667917883427, + "y": -109.70409656648307, + "z": 0.0 + }, + { + "x": 94.38652510950402, + "y": -108.69056716642868, + "z": 0.0 + }, + { + "x": 94.38637104017377, + "y": -107.67703776637423, + "z": 0.0 + }, + { + "x": 94.3862169708435, + "y": -106.6635083663197, + "z": 0.0 + }, + { + "x": 94.38606290151326, + "y": -105.6499789662653, + "z": 0.0 + }, + { + "x": 94.385908832183, + "y": -104.63644956621084, + "z": 0.0 + }, + { + "x": 94.38575476285276, + "y": -103.62292016615638, + "z": 0.0 + }, + { + "x": 94.38560069352249, + "y": -102.60939076610187, + "z": 0.0 + }, + { + "x": 94.38544662419223, + "y": -101.5958613660474, + "z": 0.0 + }, + { + "x": 94.38529255486198, + "y": -100.582331965993, + "z": 0.0 + }, + { + "x": 94.38513848553173, + "y": -99.56880256593854, + "z": 0.0 + }, + { + "x": 94.38498441620146, + "y": -98.55527316588402, + "z": 0.0 + }, + { + "x": 94.38483034687121, + "y": -97.54174376582962, + "z": 0.0 + }, + { + "x": 94.38467627754096, + "y": -96.52821436577517, + "z": 0.0 + }, + { + "x": 94.3845222082107, + "y": -95.51468496572065, + "z": 0.0 + }, + { + "x": 94.38436813888045, + "y": -94.50115556566624, + "z": 0.0 + }, + { + "x": 94.3842140695502, + "y": -93.48762616561179, + "z": 0.0 + }, + { + "x": 94.38406000021993, + "y": -92.47409676555726, + "z": 0.0 + }, + { + "x": 94.38390593088967, + "y": -91.46056736550281, + "z": 0.0 + }, + { + "x": 94.38375186155942, + "y": -90.4470379654484, + "z": 0.0 + }, + { + "x": 94.38359779222917, + "y": -89.43350856539395, + "z": 0.0 + }, + { + "x": 94.3834437228989, + "y": -88.41997916533943, + "z": 0.0 + }, + { + "x": 94.38328965356865, + "y": -87.40644976528503, + "z": 0.0 + }, + { + "x": 94.3831355842384, + "y": -86.39292036523057, + "z": 0.0 + }, + { + "x": 94.38298151490815, + "y": -85.37939096517611, + "z": 0.0 + }, + { + "x": 94.38282744557789, + "y": -84.36586156512159, + "z": 0.0 + }, + { + "x": 94.38267337624764, + "y": -83.3523321650672, + "z": 0.0 + }, + { + "x": 94.38251930691737, + "y": -82.33880276501267, + "z": 0.0 + }, + { + "x": 94.38236523758712, + "y": -81.32527336495828, + "z": 0.0 + }, + { + "x": 94.38221116825686, + "y": -80.31174396490375, + "z": 0.0 + }, + { + "x": 94.38205709892661, + "y": -79.29821456484936, + "z": 0.0 + }, + { + "x": 94.38190302959636, + "y": -78.2846851647949, + "z": 0.0 + }, + { + "x": 94.3817489602661, + "y": -77.27115576474037, + "z": 0.0 + }, + { + "x": 94.38159489093584, + "y": -76.25762636468598, + "z": 0.0 + }, + { + "x": 94.3814408216056, + "y": -75.24409696463151, + "z": 0.0 + }, + { + "x": 94.38128675227534, + "y": -74.23056756457706, + "z": 0.0 + }, + { + "x": 94.38113268294508, + "y": -73.21703816452253, + "z": 0.0 + }, + { + "x": 94.38097861361481, + "y": -72.20350876446808, + "z": 0.0 + }, + { + "x": 94.38082454428456, + "y": -71.18997936441367, + "z": 0.0 + }, + { + "x": 94.38067047495431, + "y": -70.17644996435922, + "z": 0.0 + }, + { + "x": 94.38051640562405, + "y": -69.1629205643047, + "z": 0.0 + }, + { + "x": 94.3803623362938, + "y": -68.1493911642503, + "z": 0.0 + } + ] + }, + { + "id": 111, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.38036242872523, + "y": -68.15060726575432, + "z": 0.0 + }, + { + "x": 86.38051649805548, + "y": -69.16413666580883, + "z": 0.0 + }, + { + "x": 86.38067056738575, + "y": -70.17766606586324, + "z": 0.0 + }, + { + "x": 86.380824636716, + "y": -71.19119546591769, + "z": 0.0 + }, + { + "x": 86.38097870604625, + "y": -72.20472486597221, + "z": 0.0 + }, + { + "x": 86.38113277537651, + "y": -73.21825426602666, + "z": 0.0 + }, + { + "x": 86.38128684470678, + "y": -74.23178366608107, + "z": 0.0 + }, + { + "x": 86.38144091403703, + "y": -75.24531306613552, + "z": 0.0 + }, + { + "x": 86.38159498336728, + "y": -76.25884246618999, + "z": 0.0 + }, + { + "x": 86.38174905269753, + "y": -77.2723718662445, + "z": 0.0 + }, + { + "x": 86.3819031220278, + "y": -78.28590126629891, + "z": 0.0 + }, + { + "x": 86.38205719135804, + "y": -79.29943066635337, + "z": 0.0 + }, + { + "x": 86.3822112606883, + "y": -80.31296006640788, + "z": 0.0 + }, + { + "x": 86.38236533001856, + "y": -81.32648946646229, + "z": 0.0 + }, + { + "x": 86.38251939934881, + "y": -82.3400188665168, + "z": 0.0 + }, + { + "x": 86.38267346867907, + "y": -83.35354826657121, + "z": 0.0 + }, + { + "x": 86.38282753800932, + "y": -84.36707766662572, + "z": 0.0 + }, + { + "x": 86.38298160733959, + "y": -85.38060706668013, + "z": 0.0 + }, + { + "x": 86.38313567666984, + "y": -86.39413646673458, + "z": 0.0 + }, + { + "x": 86.38328974600009, + "y": -87.40766586678905, + "z": 0.0 + }, + { + "x": 86.38344381533034, + "y": -88.42119526684355, + "z": 0.0 + }, + { + "x": 86.3835978846606, + "y": -89.43472466689796, + "z": 0.0 + }, + { + "x": 86.38375195399085, + "y": -90.44825406695242, + "z": 0.0 + }, + { + "x": 86.3839060233211, + "y": -91.46178346700694, + "z": 0.0 + }, + { + "x": 86.38406009265137, + "y": -92.47531286706139, + "z": 0.0 + }, + { + "x": 86.38421416198163, + "y": -93.4888422671158, + "z": 0.0 + }, + { + "x": 86.38436823131188, + "y": -94.50237166717025, + "z": 0.0 + }, + { + "x": 86.38452230064213, + "y": -95.51590106722477, + "z": 0.0 + }, + { + "x": 86.3846763699724, + "y": -96.52943046727918, + "z": 0.0 + }, + { + "x": 86.38483043930265, + "y": -97.54295986733364, + "z": 0.0 + }, + { + "x": 86.3849845086329, + "y": -98.55648926738814, + "z": 0.0 + }, + { + "x": 86.38513857796316, + "y": -99.57001866744255, + "z": 0.0 + }, + { + "x": 86.38529264729341, + "y": -100.58354806749702, + "z": 0.0 + }, + { + "x": 86.38544671662366, + "y": -101.59707746755153, + "z": 0.0 + }, + { + "x": 86.38560078595393, + "y": -102.610606867606, + "z": 0.0 + }, + { + "x": 86.38575485528419, + "y": -103.62413626766039, + "z": 0.0 + }, + { + "x": 86.38590892461444, + "y": -104.63766566771486, + "z": 0.0 + }, + { + "x": 86.38606299394469, + "y": -105.65119506776931, + "z": 0.0 + }, + { + "x": 86.38621706327494, + "y": -106.66472446782383, + "z": 0.0 + }, + { + "x": 86.3863711326052, + "y": -107.67825386787824, + "z": 0.0 + }, + { + "x": 86.38652520193546, + "y": -108.69178326793269, + "z": 0.0 + }, + { + "x": 86.3866792712657, + "y": -109.7053126679872, + "z": 0.0 + }, + { + "x": 86.38683334059597, + "y": -110.71884206804161, + "z": 0.0 + }, + { + "x": 86.38698740992622, + "y": -111.73237146809606, + "z": 0.0 + }, + { + "x": 86.38714147925647, + "y": -112.74590086815058, + "z": 0.0 + }, + { + "x": 86.38729554858674, + "y": -113.75943026820505, + "z": 0.0 + }, + { + "x": 86.387449617917, + "y": -114.77295966825945, + "z": 0.0 + }, + { + "x": 86.38760368724725, + "y": -115.78648906831391, + "z": 0.0 + }, + { + "x": 86.3877577565775, + "y": -116.80001846836836, + "z": 0.0 + }, + { + "x": 86.38791182590775, + "y": -117.81354786842289, + "z": 0.0 + }, + { + "x": 86.38806589523801, + "y": -118.82707726847728, + "z": 0.0 + }, + { + "x": 86.38821996456826, + "y": -119.84060666853175, + "z": 0.0 + } + ] + }, + { + "id": 112, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 90.38036238250952, + "y": -68.14999921500231, + "z": 0.0 + }, + { + "x": 90.38051645183977, + "y": -69.16352861505676, + "z": 0.0 + }, + { + "x": 90.38067052117003, + "y": -70.17705801511123, + "z": 0.0 + }, + { + "x": 90.38082459050028, + "y": -71.19058741516568, + "z": 0.0 + }, + { + "x": 90.38097865983053, + "y": -72.20411681522015, + "z": 0.0 + }, + { + "x": 90.3811327291608, + "y": -73.2176462152746, + "z": 0.0 + }, + { + "x": 90.38128679849106, + "y": -74.23117561532906, + "z": 0.0 + }, + { + "x": 90.38144086782131, + "y": -75.24470501538352, + "z": 0.0 + }, + { + "x": 90.38159493715156, + "y": -76.25823441543798, + "z": 0.0 + }, + { + "x": 90.38174900648181, + "y": -77.27176381549243, + "z": 0.0 + }, + { + "x": 90.38190307581208, + "y": -78.2852932155469, + "z": 0.0 + }, + { + "x": 90.38205714514233, + "y": -79.29882261560137, + "z": 0.0 + }, + { + "x": 90.38221121447258, + "y": -80.31235201565582, + "z": 0.0 + }, + { + "x": 90.38236528380284, + "y": -81.32588141571028, + "z": 0.0 + }, + { + "x": 90.38251935313309, + "y": -82.33941081576474, + "z": 0.0 + }, + { + "x": 90.38267342246336, + "y": -83.3529402158192, + "z": 0.0 + }, + { + "x": 90.3828274917936, + "y": -84.36646961587365, + "z": 0.0 + }, + { + "x": 90.38298156112387, + "y": -85.37999901592812, + "z": 0.0 + }, + { + "x": 90.38313563045412, + "y": -86.39352841598257, + "z": 0.0 + }, + { + "x": 90.38328969978437, + "y": -87.40705781603704, + "z": 0.0 + }, + { + "x": 90.38344376911462, + "y": -88.42058721609149, + "z": 0.0 + }, + { + "x": 90.38359783844489, + "y": -89.43411661614596, + "z": 0.0 + }, + { + "x": 90.38375190777514, + "y": -90.44764601620041, + "z": 0.0 + }, + { + "x": 90.38390597710539, + "y": -91.46117541625487, + "z": 0.0 + }, + { + "x": 90.38406004643565, + "y": -92.47470481630933, + "z": 0.0 + }, + { + "x": 90.38421411576591, + "y": -93.4882342163638, + "z": 0.0 + }, + { + "x": 90.38436818509616, + "y": -94.50176361641824, + "z": 0.0 + }, + { + "x": 90.38452225442641, + "y": -95.51529301647271, + "z": 0.0 + }, + { + "x": 90.38467632375668, + "y": -96.52882241652718, + "z": 0.0 + }, + { + "x": 90.38483039308693, + "y": -97.54235181658163, + "z": 0.0 + }, + { + "x": 90.38498446241718, + "y": -98.55588121663608, + "z": 0.0 + }, + { + "x": 90.38513853174744, + "y": -99.56941061669055, + "z": 0.0 + }, + { + "x": 90.3852926010777, + "y": -100.58294001674501, + "z": 0.0 + }, + { + "x": 90.38544667040794, + "y": -101.59646941679947, + "z": 0.0 + }, + { + "x": 90.38560073973821, + "y": -102.60999881685393, + "z": 0.0 + }, + { + "x": 90.38575480906847, + "y": -103.62352821690838, + "z": 0.0 + }, + { + "x": 90.38590887839872, + "y": -104.63705761696285, + "z": 0.0 + }, + { + "x": 90.38606294772897, + "y": -105.6505870170173, + "z": 0.0 + }, + { + "x": 90.38621701705922, + "y": -106.66411641707177, + "z": 0.0 + }, + { + "x": 90.38637108638949, + "y": -107.67764581712623, + "z": 0.0 + }, + { + "x": 90.38652515571974, + "y": -108.69117521718069, + "z": 0.0 + }, + { + "x": 90.38667922504999, + "y": -109.70470461723514, + "z": 0.0 + }, + { + "x": 90.38683329438025, + "y": -110.7182340172896, + "z": 0.0 + }, + { + "x": 90.3869873637105, + "y": -111.73176341734406, + "z": 0.0 + }, + { + "x": 90.38714143304075, + "y": -112.74529281739852, + "z": 0.0 + }, + { + "x": 90.38729550237102, + "y": -113.75882221745299, + "z": 0.0 + }, + { + "x": 90.38744957170128, + "y": -114.77235161750744, + "z": 0.0 + }, + { + "x": 90.38760364103153, + "y": -115.7858810175619, + "z": 0.0 + }, + { + "x": 90.38775771036178, + "y": -116.79941041761636, + "z": 0.0 + }, + { + "x": 90.38791177969203, + "y": -117.81293981767082, + "z": 0.0 + }, + { + "x": 90.3880658490223, + "y": -118.82646921772528, + "z": 0.0 + }, + { + "x": 90.38821991835255, + "y": -119.83999861777974, + "z": 0.0 + } + ] + }, + { + "id": 113, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.38821989524469, + "y": -119.83969459240373, + "z": 0.0 + }, + { + "x": 92.38806582591444, + "y": -118.82616519234926, + "z": 0.0 + }, + { + "x": 92.38791175658417, + "y": -117.8126357922948, + "z": 0.0 + }, + { + "x": 92.38775768725392, + "y": -116.79910639224036, + "z": 0.0 + }, + { + "x": 92.38760361792367, + "y": -115.7855769921859, + "z": 0.0 + }, + { + "x": 92.38744954859342, + "y": -114.77204759213143, + "z": 0.0 + }, + { + "x": 92.38729547926316, + "y": -113.75851819207696, + "z": 0.0 + }, + { + "x": 92.3871414099329, + "y": -112.7449887920225, + "z": 0.0 + }, + { + "x": 92.38698734060264, + "y": -111.73145939196806, + "z": 0.0 + }, + { + "x": 92.3868332712724, + "y": -110.71792999191359, + "z": 0.0 + }, + { + "x": 92.38667920194213, + "y": -109.7044005918591, + "z": 0.0 + }, + { + "x": 92.38652513261188, + "y": -108.69087119180469, + "z": 0.0 + }, + { + "x": 92.38637106328163, + "y": -107.67734179175022, + "z": 0.0 + }, + { + "x": 92.38621699395136, + "y": -106.66381239169573, + "z": 0.0 + }, + { + "x": 92.38606292462111, + "y": -105.65028299164129, + "z": 0.0 + }, + { + "x": 92.38590885529086, + "y": -104.63675359158685, + "z": 0.0 + }, + { + "x": 92.38575478596061, + "y": -103.62322419153239, + "z": 0.0 + }, + { + "x": 92.38560071663035, + "y": -102.60969479147789, + "z": 0.0 + }, + { + "x": 92.38544664730009, + "y": -101.59616539142343, + "z": 0.0 + }, + { + "x": 92.38529257796984, + "y": -100.58263599136902, + "z": 0.0 + }, + { + "x": 92.38513850863959, + "y": -99.56910659131455, + "z": 0.0 + }, + { + "x": 92.38498443930932, + "y": -98.55557719126006, + "z": 0.0 + }, + { + "x": 92.38483036997907, + "y": -97.54204779120562, + "z": 0.0 + }, + { + "x": 92.38467630064882, + "y": -96.52851839115118, + "z": 0.0 + }, + { + "x": 92.38452223131856, + "y": -95.51498899109669, + "z": 0.0 + }, + { + "x": 92.3843681619883, + "y": -94.50145959104225, + "z": 0.0 + }, + { + "x": 92.38421409265806, + "y": -93.48793019098778, + "z": 0.0 + }, + { + "x": 92.38406002332779, + "y": -92.47440079093329, + "z": 0.0 + }, + { + "x": 92.38390595399753, + "y": -91.46087139087885, + "z": 0.0 + }, + { + "x": 92.38375188466728, + "y": -90.44734199082441, + "z": 0.0 + }, + { + "x": 92.38359781533703, + "y": -89.43381259076995, + "z": 0.0 + }, + { + "x": 92.38344374600676, + "y": -88.42028319071545, + "z": 0.0 + }, + { + "x": 92.38328967667651, + "y": -87.40675379066104, + "z": 0.0 + }, + { + "x": 92.38313560734626, + "y": -86.39322439060658, + "z": 0.0 + }, + { + "x": 92.38298153801601, + "y": -85.37969499055211, + "z": 0.0 + }, + { + "x": 92.38282746868575, + "y": -84.36616559049762, + "z": 0.0 + }, + { + "x": 92.3826733993555, + "y": -83.3526361904432, + "z": 0.0 + }, + { + "x": 92.38251933002523, + "y": -82.33910679038871, + "z": 0.0 + }, + { + "x": 92.38236526069498, + "y": -81.32557739033427, + "z": 0.0 + }, + { + "x": 92.38221119136472, + "y": -80.31204799027978, + "z": 0.0 + }, + { + "x": 92.38205712203447, + "y": -79.29851859022537, + "z": 0.0 + }, + { + "x": 92.38190305270422, + "y": -78.2849891901709, + "z": 0.0 + }, + { + "x": 92.38174898337395, + "y": -77.27145979011641, + "z": 0.0 + }, + { + "x": 92.3815949140437, + "y": -76.25793039006197, + "z": 0.0 + }, + { + "x": 92.38144084471345, + "y": -75.2444009900075, + "z": 0.0 + }, + { + "x": 92.3812867753832, + "y": -74.23087158995307, + "z": 0.0 + }, + { + "x": 92.38113270605294, + "y": -73.21734218989857, + "z": 0.0 + }, + { + "x": 92.38097863672267, + "y": -72.20381278984411, + "z": 0.0 + }, + { + "x": 92.38082456739242, + "y": -71.19028338978967, + "z": 0.0 + }, + { + "x": 92.38067049806217, + "y": -70.17675398973523, + "z": 0.0 + }, + { + "x": 92.38051642873191, + "y": -69.16322458968074, + "z": 0.0 + }, + { + "x": 92.38036235940166, + "y": -68.1496951896263, + "z": 0.0 + } + ] + }, + { + "id": 114, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.38036240561738, + "y": -68.15030324037832, + "z": 0.0 + }, + { + "x": 88.38051647494763, + "y": -69.16383264043279, + "z": 0.0 + }, + { + "x": 88.38067054427789, + "y": -70.17736204048722, + "z": 0.0 + }, + { + "x": 88.38082461360814, + "y": -71.19089144054169, + "z": 0.0 + }, + { + "x": 88.38097868293839, + "y": -72.20442084059619, + "z": 0.0 + }, + { + "x": 88.38113275226866, + "y": -73.21795024065062, + "z": 0.0 + }, + { + "x": 88.38128682159892, + "y": -74.23147964070506, + "z": 0.0 + }, + { + "x": 88.38144089092917, + "y": -75.24500904075953, + "z": 0.0 + }, + { + "x": 88.38159496025942, + "y": -76.258538440814, + "z": 0.0 + }, + { + "x": 88.38174902958967, + "y": -77.27206784086846, + "z": 0.0 + }, + { + "x": 88.38190309891993, + "y": -78.2855972409229, + "z": 0.0 + }, + { + "x": 88.38205716825018, + "y": -79.29912664097736, + "z": 0.0 + }, + { + "x": 88.38221123758044, + "y": -80.31265604103186, + "z": 0.0 + }, + { + "x": 88.3823653069107, + "y": -81.3261854410863, + "z": 0.0 + }, + { + "x": 88.38251937624095, + "y": -82.33971484114076, + "z": 0.0 + }, + { + "x": 88.38267344557121, + "y": -83.3532442411952, + "z": 0.0 + }, + { + "x": 88.38282751490146, + "y": -84.3667736412497, + "z": 0.0 + }, + { + "x": 88.38298158423173, + "y": -85.38030304130413, + "z": 0.0 + }, + { + "x": 88.38313565356198, + "y": -86.39383244135857, + "z": 0.0 + }, + { + "x": 88.38328972289223, + "y": -87.40736184141304, + "z": 0.0 + }, + { + "x": 88.38344379222248, + "y": -88.42089124146753, + "z": 0.0 + }, + { + "x": 88.38359786155274, + "y": -89.43442064152197, + "z": 0.0 + }, + { + "x": 88.383751930883, + "y": -90.4479500415764, + "z": 0.0 + }, + { + "x": 88.38390600021324, + "y": -91.4614794416309, + "z": 0.0 + }, + { + "x": 88.38406006954351, + "y": -92.47500884168537, + "z": 0.0 + }, + { + "x": 88.38421413887377, + "y": -93.4885382417398, + "z": 0.0 + }, + { + "x": 88.38436820820402, + "y": -94.50206764179424, + "z": 0.0 + }, + { + "x": 88.38452227753427, + "y": -95.51559704184874, + "z": 0.0 + }, + { + "x": 88.38467634686454, + "y": -96.52912644190317, + "z": 0.0 + }, + { + "x": 88.38483041619479, + "y": -97.54265584195764, + "z": 0.0 + }, + { + "x": 88.38498448552504, + "y": -98.5561852420121, + "z": 0.0 + }, + { + "x": 88.3851385548553, + "y": -99.56971464206654, + "z": 0.0 + }, + { + "x": 88.38529262418555, + "y": -100.58324404212101, + "z": 0.0 + }, + { + "x": 88.3854466935158, + "y": -101.5967734421755, + "z": 0.0 + }, + { + "x": 88.38560076284607, + "y": -102.61030284222997, + "z": 0.0 + }, + { + "x": 88.38575483217633, + "y": -103.62383224228438, + "z": 0.0 + }, + { + "x": 88.38590890150658, + "y": -104.63736164233885, + "z": 0.0 + }, + { + "x": 88.38606297083683, + "y": -105.65089104239331, + "z": 0.0 + }, + { + "x": 88.38621704016708, + "y": -106.6644204424478, + "z": 0.0 + }, + { + "x": 88.38637110949735, + "y": -107.67794984250224, + "z": 0.0 + }, + { + "x": 88.3865251788276, + "y": -108.69147924255668, + "z": 0.0 + }, + { + "x": 88.38667924815785, + "y": -109.70500864261118, + "z": 0.0 + }, + { + "x": 88.38683331748811, + "y": -110.71853804266561, + "z": 0.0 + }, + { + "x": 88.38698738681836, + "y": -111.73206744272005, + "z": 0.0 + }, + { + "x": 88.38714145614861, + "y": -112.74559684277455, + "z": 0.0 + }, + { + "x": 88.38729552547888, + "y": -113.75912624282901, + "z": 0.0 + }, + { + "x": 88.38744959480914, + "y": -114.77265564288345, + "z": 0.0 + }, + { + "x": 88.38760366413939, + "y": -115.78618504293792, + "z": 0.0 + }, + { + "x": 88.38775773346964, + "y": -116.79971444299235, + "z": 0.0 + }, + { + "x": 88.38791180279989, + "y": -117.81324384304685, + "z": 0.0 + }, + { + "x": 88.38806587213016, + "y": -118.82677324310129, + "z": 0.0 + }, + { + "x": 88.3882199414604, + "y": -119.84030264315575, + "z": 0.0 + } + ] + }, + { + "id": 115, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.39832719576235, + "y": -186.329389798807, + "z": 0.0 + }, + { + "x": 94.39816991565795, + "y": -185.29473864797058, + "z": 0.0 + }, + { + "x": 94.39801263555354, + "y": -184.26008749713418, + "z": 0.0 + }, + { + "x": 94.39785535544914, + "y": -183.22543634629776, + "z": 0.0 + }, + { + "x": 94.39769807534474, + "y": -182.19078519546136, + "z": 0.0 + }, + { + "x": 94.39754079524033, + "y": -181.15613404462493, + "z": 0.0 + }, + { + "x": 94.39738351513593, + "y": -180.12148289378854, + "z": 0.0 + }, + { + "x": 94.39722623503152, + "y": -179.0868317429521, + "z": 0.0 + }, + { + "x": 94.39706895492711, + "y": -178.05218059211563, + "z": 0.0 + }, + { + "x": 94.3969116748227, + "y": -177.0175294412793, + "z": 0.0 + }, + { + "x": 94.3967543947183, + "y": -175.98287829044287, + "z": 0.0 + }, + { + "x": 94.3965971146139, + "y": -174.94822713960644, + "z": 0.0 + }, + { + "x": 94.3964398345095, + "y": -173.91357598877005, + "z": 0.0 + }, + { + "x": 94.39628255440509, + "y": -172.87892483793362, + "z": 0.0 + }, + { + "x": 94.39612527430069, + "y": -171.84427368709723, + "z": 0.0 + }, + { + "x": 94.39596799419628, + "y": -170.8096225362608, + "z": 0.0 + }, + { + "x": 94.39581071409188, + "y": -169.7749713854244, + "z": 0.0 + }, + { + "x": 94.39565343398748, + "y": -168.74032023458798, + "z": 0.0 + }, + { + "x": 94.39549615388307, + "y": -167.70566908375156, + "z": 0.0 + }, + { + "x": 94.39533887377867, + "y": -166.67101793291516, + "z": 0.0 + }, + { + "x": 94.39518159367427, + "y": -165.63636678207874, + "z": 0.0 + }, + { + "x": 94.39502431356986, + "y": -164.6017156312423, + "z": 0.0 + }, + { + "x": 94.39486703346545, + "y": -163.56706448040586, + "z": 0.0 + }, + { + "x": 94.39470975336104, + "y": -162.5324133295695, + "z": 0.0 + }, + { + "x": 94.39455247325664, + "y": -161.4977621787331, + "z": 0.0 + }, + { + "x": 94.39439519315223, + "y": -160.46311102789667, + "z": 0.0 + }, + { + "x": 94.39423791304783, + "y": -159.42845987706028, + "z": 0.0 + }, + { + "x": 94.39408063294343, + "y": -158.39380872622385, + "z": 0.0 + }, + { + "x": 94.39392335283902, + "y": -157.35915757538743, + "z": 0.0 + }, + { + "x": 94.39376607273462, + "y": -156.32450642455103, + "z": 0.0 + }, + { + "x": 94.39360879263022, + "y": -155.2898552737146, + "z": 0.0 + }, + { + "x": 94.39345151252581, + "y": -154.2552041228782, + "z": 0.0 + }, + { + "x": 94.39329423242141, + "y": -153.2205529720418, + "z": 0.0 + }, + { + "x": 94.39313695231701, + "y": -152.1859018212054, + "z": 0.0 + }, + { + "x": 94.3929796722126, + "y": -151.15125067036897, + "z": 0.0 + }, + { + "x": 94.3928223921082, + "y": -150.11659951953254, + "z": 0.0 + }, + { + "x": 94.39266511200378, + "y": -149.0819483686961, + "z": 0.0 + }, + { + "x": 94.39250783189938, + "y": -148.04729721785972, + "z": 0.0 + }, + { + "x": 94.39235055179498, + "y": -147.01264606702333, + "z": 0.0 + }, + { + "x": 94.39219327169057, + "y": -145.9779949161869, + "z": 0.0 + }, + { + "x": 94.39203599158617, + "y": -144.9433437653505, + "z": 0.0 + }, + { + "x": 94.39187871148177, + "y": -143.90869261451408, + "z": 0.0 + }, + { + "x": 94.39172143137736, + "y": -142.87404146367768, + "z": 0.0 + }, + { + "x": 94.39156415127296, + "y": -141.83939031284126, + "z": 0.0 + } + ] + }, + { + "id": 116, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.3915642437044, + "y": -141.8406064143453, + "z": 0.0 + }, + { + "x": 86.3917215238088, + "y": -142.87525756518173, + "z": 0.0 + }, + { + "x": 86.3918788039132, + "y": -143.90990871601812, + "z": 0.0 + }, + { + "x": 86.3920360840176, + "y": -144.94455986685455, + "z": 0.0 + }, + { + "x": 86.39219336412201, + "y": -145.97921101769094, + "z": 0.0 + }, + { + "x": 86.39235064422641, + "y": -147.01386216852737, + "z": 0.0 + }, + { + "x": 86.39250792433081, + "y": -148.04851331936376, + "z": 0.0 + }, + { + "x": 86.39266520443522, + "y": -149.08316447020024, + "z": 0.0 + }, + { + "x": 86.39282248453964, + "y": -150.11781562103658, + "z": 0.0 + }, + { + "x": 86.39297976464404, + "y": -151.152466771873, + "z": 0.0 + }, + { + "x": 86.39313704474844, + "y": -152.18711792270943, + "z": 0.0 + }, + { + "x": 86.39329432485285, + "y": -153.22176907354583, + "z": 0.0 + }, + { + "x": 86.39345160495725, + "y": -154.25642022438225, + "z": 0.0 + }, + { + "x": 86.39360888506165, + "y": -155.29107137521865, + "z": 0.0 + }, + { + "x": 86.39376616516606, + "y": -156.32572252605507, + "z": 0.0 + }, + { + "x": 86.39392344527046, + "y": -157.36037367689147, + "z": 0.0 + }, + { + "x": 86.39408072537486, + "y": -158.3950248277279, + "z": 0.0 + }, + { + "x": 86.39423800547927, + "y": -159.42967597856432, + "z": 0.0 + }, + { + "x": 86.39439528558367, + "y": -160.46432712940071, + "z": 0.0 + }, + { + "x": 86.39455256568807, + "y": -161.49897828023714, + "z": 0.0 + }, + { + "x": 86.39470984579248, + "y": -162.53362943107354, + "z": 0.0 + }, + { + "x": 86.39486712589688, + "y": -163.56828058191002, + "z": 0.0 + }, + { + "x": 86.3950244060013, + "y": -164.60293173274636, + "z": 0.0 + }, + { + "x": 86.3951816861057, + "y": -165.63758288358278, + "z": 0.0 + }, + { + "x": 86.3953389662101, + "y": -166.6722340344192, + "z": 0.0 + }, + { + "x": 86.39549624631451, + "y": -167.7068851852556, + "z": 0.0 + }, + { + "x": 86.39565352641891, + "y": -168.74153633609203, + "z": 0.0 + }, + { + "x": 86.39581080652331, + "y": -169.77618748692845, + "z": 0.0 + }, + { + "x": 86.39596808662772, + "y": -170.81083863776485, + "z": 0.0 + }, + { + "x": 86.39612536673212, + "y": -171.84548978860127, + "z": 0.0 + }, + { + "x": 86.39628264683653, + "y": -172.88014093943767, + "z": 0.0 + }, + { + "x": 86.39643992694093, + "y": -173.9147920902741, + "z": 0.0 + }, + { + "x": 86.39659720704533, + "y": -174.9494432411105, + "z": 0.0 + }, + { + "x": 86.39675448714974, + "y": -175.9840943919469, + "z": 0.0 + }, + { + "x": 86.39691176725414, + "y": -177.01874554278334, + "z": 0.0 + }, + { + "x": 86.39706904735854, + "y": -178.0533966936198, + "z": 0.0 + }, + { + "x": 86.39722632746296, + "y": -179.08804784445616, + "z": 0.0 + }, + { + "x": 86.39738360756736, + "y": -180.12269899529258, + "z": 0.0 + }, + { + "x": 86.39754088767177, + "y": -181.15735014612898, + "z": 0.0 + }, + { + "x": 86.39769816777617, + "y": -182.1920012969654, + "z": 0.0 + }, + { + "x": 86.39785544788057, + "y": -183.2266524478018, + "z": 0.0 + }, + { + "x": 86.39801272798498, + "y": -184.26130359863822, + "z": 0.0 + }, + { + "x": 86.39817000808938, + "y": -185.29595474947462, + "z": 0.0 + }, + { + "x": 86.39832728819378, + "y": -186.33060590031104, + "z": 0.0 + } + ] + }, + { + "id": 117, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 90.39156419748868, + "y": -141.83999836359328, + "z": 0.0 + }, + { + "x": 90.39172147759308, + "y": -142.8746495144297, + "z": 0.0 + }, + { + "x": 90.39187875769748, + "y": -143.9093006652661, + "z": 0.0 + }, + { + "x": 90.39203603780189, + "y": -144.94395181610253, + "z": 0.0 + }, + { + "x": 90.39219331790629, + "y": -145.97860296693892, + "z": 0.0 + }, + { + "x": 90.3923505980107, + "y": -147.01325411777535, + "z": 0.0 + }, + { + "x": 90.3925078781151, + "y": -148.04790526861174, + "z": 0.0 + }, + { + "x": 90.3926651582195, + "y": -149.08255641944817, + "z": 0.0 + }, + { + "x": 90.39282243832392, + "y": -150.11720757028456, + "z": 0.0 + }, + { + "x": 90.39297971842832, + "y": -151.151858721121, + "z": 0.0 + }, + { + "x": 90.39313699853273, + "y": -152.1865098719574, + "z": 0.0 + }, + { + "x": 90.39329427863713, + "y": -153.2211610227938, + "z": 0.0 + }, + { + "x": 90.39345155874153, + "y": -154.25581217363023, + "z": 0.0 + }, + { + "x": 90.39360883884594, + "y": -155.29046332446663, + "z": 0.0 + }, + { + "x": 90.39376611895034, + "y": -156.32511447530305, + "z": 0.0 + }, + { + "x": 90.39392339905474, + "y": -157.35976562613945, + "z": 0.0 + }, + { + "x": 90.39408067915915, + "y": -158.39441677697587, + "z": 0.0 + }, + { + "x": 90.39423795926355, + "y": -159.4290679278123, + "z": 0.0 + }, + { + "x": 90.39439523936795, + "y": -160.4637190786487, + "z": 0.0 + }, + { + "x": 90.39455251947236, + "y": -161.49837022948512, + "z": 0.0 + }, + { + "x": 90.39470979957676, + "y": -162.53302138032151, + "z": 0.0 + }, + { + "x": 90.39486707968116, + "y": -163.56767253115794, + "z": 0.0 + }, + { + "x": 90.39502435978558, + "y": -164.60232368199433, + "z": 0.0 + }, + { + "x": 90.39518163988998, + "y": -165.63697483283076, + "z": 0.0 + }, + { + "x": 90.39533891999439, + "y": -166.67162598366718, + "z": 0.0 + }, + { + "x": 90.39549620009879, + "y": -167.70627713450358, + "z": 0.0 + }, + { + "x": 90.3956534802032, + "y": -168.74092828534, + "z": 0.0 + }, + { + "x": 90.3958107603076, + "y": -169.77557943617643, + "z": 0.0 + }, + { + "x": 90.395968040412, + "y": -170.81023058701282, + "z": 0.0 + }, + { + "x": 90.3961253205164, + "y": -171.84488173784925, + "z": 0.0 + }, + { + "x": 90.39628260062081, + "y": -172.87953288868565, + "z": 0.0 + }, + { + "x": 90.39643988072521, + "y": -173.91418403952207, + "z": 0.0 + }, + { + "x": 90.39659716082961, + "y": -174.94883519035847, + "z": 0.0 + }, + { + "x": 90.39675444093402, + "y": -175.9834863411949, + "z": 0.0 + }, + { + "x": 90.39691172103842, + "y": -177.01813749203131, + "z": 0.0 + }, + { + "x": 90.39706900114282, + "y": -178.0527886428677, + "z": 0.0 + }, + { + "x": 90.39722628124724, + "y": -179.08743979370414, + "z": 0.0 + }, + { + "x": 90.39738356135165, + "y": -180.12209094454056, + "z": 0.0 + }, + { + "x": 90.39754084145605, + "y": -181.15674209537696, + "z": 0.0 + }, + { + "x": 90.39769812156045, + "y": -182.19139324621338, + "z": 0.0 + }, + { + "x": 90.39785540166486, + "y": -183.22604439704978, + "z": 0.0 + }, + { + "x": 90.39801268176926, + "y": -184.2606955478862, + "z": 0.0 + }, + { + "x": 90.39816996187366, + "y": -185.2953466987226, + "z": 0.0 + }, + { + "x": 90.39832724197807, + "y": -186.32999784955902, + "z": 0.0 + } + ] + }, + { + "id": 118, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.39832721887021, + "y": -186.329693824183, + "z": 0.0 + }, + { + "x": 92.3981699387658, + "y": -185.2950426733466, + "z": 0.0 + }, + { + "x": 92.3980126586614, + "y": -184.2603915225102, + "z": 0.0 + }, + { + "x": 92.397855378557, + "y": -183.22574037167377, + "z": 0.0 + }, + { + "x": 92.3976980984526, + "y": -182.19108922083737, + "z": 0.0 + }, + { + "x": 92.39754081834819, + "y": -181.15643807000095, + "z": 0.0 + }, + { + "x": 92.39738353824379, + "y": -180.12178691916455, + "z": 0.0 + }, + { + "x": 92.39722625813938, + "y": -179.08713576832812, + "z": 0.0 + }, + { + "x": 92.39706897803497, + "y": -178.05248461749167, + "z": 0.0 + }, + { + "x": 92.39691169793056, + "y": -177.0178334666553, + "z": 0.0 + }, + { + "x": 92.39675441782616, + "y": -175.98318231581888, + "z": 0.0 + }, + { + "x": 92.39659713772176, + "y": -174.94853116498246, + "z": 0.0 + }, + { + "x": 92.39643985761735, + "y": -173.91388001414606, + "z": 0.0 + }, + { + "x": 92.39628257751295, + "y": -172.87922886330963, + "z": 0.0 + }, + { + "x": 92.39612529740855, + "y": -171.84457771247324, + "z": 0.0 + }, + { + "x": 92.39596801730414, + "y": -170.80992656163681, + "z": 0.0 + }, + { + "x": 92.39581073719974, + "y": -169.77527541080042, + "z": 0.0 + }, + { + "x": 92.39565345709534, + "y": -168.740624259964, + "z": 0.0 + }, + { + "x": 92.39549617699093, + "y": -167.70597310912757, + "z": 0.0 + }, + { + "x": 92.39533889688653, + "y": -166.67132195829117, + "z": 0.0 + }, + { + "x": 92.39518161678212, + "y": -165.63667080745475, + "z": 0.0 + }, + { + "x": 92.39502433667772, + "y": -164.60201965661832, + "z": 0.0 + }, + { + "x": 92.3948670565733, + "y": -163.5673685057819, + "z": 0.0 + }, + { + "x": 92.3947097764689, + "y": -162.5327173549455, + "z": 0.0 + }, + { + "x": 92.3945524963645, + "y": -161.4980662041091, + "z": 0.0 + }, + { + "x": 92.3943952162601, + "y": -160.46341505327268, + "z": 0.0 + }, + { + "x": 92.39423793615569, + "y": -159.4287639024363, + "z": 0.0 + }, + { + "x": 92.39408065605129, + "y": -158.39411275159986, + "z": 0.0 + }, + { + "x": 92.39392337594688, + "y": -157.35946160076344, + "z": 0.0 + }, + { + "x": 92.39376609584248, + "y": -156.32481044992704, + "z": 0.0 + }, + { + "x": 92.39360881573808, + "y": -155.29015929909062, + "z": 0.0 + }, + { + "x": 92.39345153563367, + "y": -154.25550814825422, + "z": 0.0 + }, + { + "x": 92.39329425552927, + "y": -153.2208569974178, + "z": 0.0 + }, + { + "x": 92.39313697542487, + "y": -152.1862058465814, + "z": 0.0 + }, + { + "x": 92.39297969532046, + "y": -151.15155469574498, + "z": 0.0 + }, + { + "x": 92.39282241521606, + "y": -150.11690354490855, + "z": 0.0 + }, + { + "x": 92.39266513511164, + "y": -149.08225239407213, + "z": 0.0 + }, + { + "x": 92.39250785500724, + "y": -148.04760124323573, + "z": 0.0 + }, + { + "x": 92.39235057490284, + "y": -147.01295009239934, + "z": 0.0 + }, + { + "x": 92.39219329479843, + "y": -145.9782989415629, + "z": 0.0 + }, + { + "x": 92.39203601469403, + "y": -144.94364779072652, + "z": 0.0 + }, + { + "x": 92.39187873458962, + "y": -143.9089966398901, + "z": 0.0 + }, + { + "x": 92.39172145448522, + "y": -142.8743454890537, + "z": 0.0 + }, + { + "x": 92.39156417438082, + "y": -141.83969433821727, + "z": 0.0 + } + ] + }, + { + "id": 119, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.39156422059654, + "y": -141.8403023889693, + "z": 0.0 + }, + { + "x": 88.39172150070094, + "y": -142.87495353980572, + "z": 0.0 + }, + { + "x": 88.39187878080534, + "y": -143.9096046906421, + "z": 0.0 + }, + { + "x": 88.39203606090975, + "y": -144.94425584147854, + "z": 0.0 + }, + { + "x": 88.39219334101415, + "y": -145.97890699231493, + "z": 0.0 + }, + { + "x": 88.39235062111855, + "y": -147.01355814315136, + "z": 0.0 + }, + { + "x": 88.39250790122296, + "y": -148.04820929398775, + "z": 0.0 + }, + { + "x": 88.39266518132736, + "y": -149.0828604448242, + "z": 0.0 + }, + { + "x": 88.39282246143178, + "y": -150.11751159566057, + "z": 0.0 + }, + { + "x": 88.39297974153618, + "y": -151.152162746497, + "z": 0.0 + }, + { + "x": 88.39313702164058, + "y": -152.18681389733342, + "z": 0.0 + }, + { + "x": 88.39329430174499, + "y": -153.22146504816982, + "z": 0.0 + }, + { + "x": 88.39345158184939, + "y": -154.25611619900624, + "z": 0.0 + }, + { + "x": 88.3936088619538, + "y": -155.29076734984264, + "z": 0.0 + }, + { + "x": 88.3937661420582, + "y": -156.32541850067906, + "z": 0.0 + }, + { + "x": 88.3939234221626, + "y": -157.36006965151546, + "z": 0.0 + }, + { + "x": 88.394080702267, + "y": -158.39472080235188, + "z": 0.0 + }, + { + "x": 88.39423798237141, + "y": -159.4293719531883, + "z": 0.0 + }, + { + "x": 88.39439526247581, + "y": -160.4640231040247, + "z": 0.0 + }, + { + "x": 88.39455254258021, + "y": -161.49867425486113, + "z": 0.0 + }, + { + "x": 88.39470982268462, + "y": -162.53332540569752, + "z": 0.0 + }, + { + "x": 88.39486710278902, + "y": -163.56797655653398, + "z": 0.0 + }, + { + "x": 88.39502438289344, + "y": -164.60262770737035, + "z": 0.0 + }, + { + "x": 88.39518166299784, + "y": -165.63727885820677, + "z": 0.0 + }, + { + "x": 88.39533894310225, + "y": -166.6719300090432, + "z": 0.0 + }, + { + "x": 88.39549622320665, + "y": -167.7065811598796, + "z": 0.0 + }, + { + "x": 88.39565350331105, + "y": -168.74123231071601, + "z": 0.0 + }, + { + "x": 88.39581078341546, + "y": -169.77588346155244, + "z": 0.0 + }, + { + "x": 88.39596806351986, + "y": -170.81053461238884, + "z": 0.0 + }, + { + "x": 88.39612534362426, + "y": -171.84518576322526, + "z": 0.0 + }, + { + "x": 88.39628262372867, + "y": -172.87983691406166, + "z": 0.0 + }, + { + "x": 88.39643990383307, + "y": -173.91448806489808, + "z": 0.0 + }, + { + "x": 88.39659718393747, + "y": -174.94913921573448, + "z": 0.0 + }, + { + "x": 88.39675446404188, + "y": -175.9837903665709, + "z": 0.0 + }, + { + "x": 88.39691174414628, + "y": -177.01844151740733, + "z": 0.0 + }, + { + "x": 88.39706902425068, + "y": -178.05309266824375, + "z": 0.0 + }, + { + "x": 88.3972263043551, + "y": -179.08774381908015, + "z": 0.0 + }, + { + "x": 88.3973835844595, + "y": -180.12239496991657, + "z": 0.0 + }, + { + "x": 88.39754086456391, + "y": -181.15704612075297, + "z": 0.0 + }, + { + "x": 88.39769814466831, + "y": -182.1916972715894, + "z": 0.0 + }, + { + "x": 88.39785542477271, + "y": -183.2263484224258, + "z": 0.0 + }, + { + "x": 88.39801270487712, + "y": -184.2609995732622, + "z": 0.0 + }, + { + "x": 88.39816998498152, + "y": -185.2956507240986, + "z": 0.0 + }, + { + "x": 88.39832726508592, + "y": -186.33030187493503, + "z": 0.0 + } + ] + }, + { + "id": 120, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.4182362975105, + "y": -317.299388285589, + "z": 0.0 + }, + { + "x": 94.41808291952336, + "y": -316.2904068157652, + "z": 0.0 + }, + { + "x": 94.4179295415362, + "y": -315.28142534594133, + "z": 0.0 + }, + { + "x": 94.41777616354905, + "y": -314.2724438761176, + "z": 0.0 + }, + { + "x": 94.4176227855619, + "y": -313.26346240629385, + "z": 0.0 + }, + { + "x": 94.41746940757476, + "y": -312.25448093647003, + "z": 0.0 + }, + { + "x": 94.41731602958761, + "y": -311.24549946664627, + "z": 0.0 + }, + { + "x": 94.41716265160046, + "y": -310.2365179968225, + "z": 0.0 + }, + { + "x": 94.41700927361332, + "y": -309.2275365269987, + "z": 0.0 + }, + { + "x": 94.41685589562616, + "y": -308.21855505717485, + "z": 0.0 + }, + { + "x": 94.41670251763901, + "y": -307.2095735873511, + "z": 0.0 + }, + { + "x": 94.41654913965186, + "y": -306.2005921175273, + "z": 0.0 + }, + { + "x": 94.41639576166472, + "y": -305.19161064770356, + "z": 0.0 + }, + { + "x": 94.41624238367757, + "y": -304.18262917787973, + "z": 0.0 + }, + { + "x": 94.41608900569042, + "y": -303.17364770805597, + "z": 0.0 + }, + { + "x": 94.41593562770328, + "y": -302.1646662382322, + "z": 0.0 + }, + { + "x": 94.41578224971612, + "y": -301.1556847684083, + "z": 0.0 + }, + { + "x": 94.41562887172897, + "y": -300.1467032985846, + "z": 0.0 + }, + { + "x": 94.41547549374182, + "y": -299.13772182876085, + "z": 0.0 + }, + { + "x": 94.41532211575468, + "y": -298.128740358937, + "z": 0.0 + }, + { + "x": 94.41516873776753, + "y": -297.11975888911326, + "z": 0.0 + }, + { + "x": 94.41501535978038, + "y": -296.1107774192895, + "z": 0.0 + }, + { + "x": 94.41486198179324, + "y": -295.10179594946567, + "z": 0.0 + }, + { + "x": 94.41470860380609, + "y": -294.0928144796419, + "z": 0.0 + }, + { + "x": 94.41455522581893, + "y": -293.0838330098181, + "z": 0.0 + }, + { + "x": 94.4144018478318, + "y": -292.07485153999437, + "z": 0.0 + }, + { + "x": 94.41424846984464, + "y": -291.0658700701705, + "z": 0.0 + }, + { + "x": 94.41409509185749, + "y": -290.0568886003467, + "z": 0.0 + }, + { + "x": 94.41394171387034, + "y": -289.04790713052296, + "z": 0.0 + }, + { + "x": 94.4137883358832, + "y": -288.0389256606992, + "z": 0.0 + }, + { + "x": 94.41363495789605, + "y": -287.02994419087537, + "z": 0.0 + }, + { + "x": 94.41348157990889, + "y": -286.02096272105155, + "z": 0.0 + }, + { + "x": 94.41332820192176, + "y": -285.0119812512279, + "z": 0.0 + }, + { + "x": 94.41317482393461, + "y": -284.002999781404, + "z": 0.0 + }, + { + "x": 94.41302144594745, + "y": -282.9940183115802, + "z": 0.0 + }, + { + "x": 94.4128680679603, + "y": -281.9850368417565, + "z": 0.0 + }, + { + "x": 94.41271468997316, + "y": -280.97605537193266, + "z": 0.0 + }, + { + "x": 94.41256131198601, + "y": -279.9670739021089, + "z": 0.0 + }, + { + "x": 94.41240793399886, + "y": -278.9580924322851, + "z": 0.0 + }, + { + "x": 94.41225455601172, + "y": -277.9491109624613, + "z": 0.0 + }, + { + "x": 94.41210117802457, + "y": -276.94012949263754, + "z": 0.0 + }, + { + "x": 94.41194780003741, + "y": -275.9311480228137, + "z": 0.0 + }, + { + "x": 94.41179442205026, + "y": -274.92216655298995, + "z": 0.0 + }, + { + "x": 94.41164104406312, + "y": -273.9131850831662, + "z": 0.0 + }, + { + "x": 94.41148766607597, + "y": -272.9042036133424, + "z": 0.0 + }, + { + "x": 94.41133428808882, + "y": -271.8952221435186, + "z": 0.0 + }, + { + "x": 94.41118091010168, + "y": -270.88624067369483, + "z": 0.0 + }, + { + "x": 94.41102753211453, + "y": -269.87725920387106, + "z": 0.0 + }, + { + "x": 94.41087415412737, + "y": -268.86827773404724, + "z": 0.0 + }, + { + "x": 94.41072077614024, + "y": -267.85929626422353, + "z": 0.0 + }, + { + "x": 94.41056739815309, + "y": -266.8503147943997, + "z": 0.0 + }, + { + "x": 94.41041402016593, + "y": -265.8413333245759, + "z": 0.0 + }, + { + "x": 94.41026064217878, + "y": -264.8323518547522, + "z": 0.0 + }, + { + "x": 94.41010726419164, + "y": -263.82337038492835, + "z": 0.0 + }, + { + "x": 94.40995388620449, + "y": -262.8143889151046, + "z": 0.0 + }, + { + "x": 94.40980050821734, + "y": -261.8054074452808, + "z": 0.0 + }, + { + "x": 94.4096471302302, + "y": -260.79642597545705, + "z": 0.0 + }, + { + "x": 94.40949375224305, + "y": -259.7874445056333, + "z": 0.0 + }, + { + "x": 94.40934037425589, + "y": -258.7784630358094, + "z": 0.0 + }, + { + "x": 94.40918699626874, + "y": -257.7694815659857, + "z": 0.0 + }, + { + "x": 94.4090336182816, + "y": -256.76050009616193, + "z": 0.0 + }, + { + "x": 94.40888024029445, + "y": -255.75151862633814, + "z": 0.0 + }, + { + "x": 94.4087268623073, + "y": -254.74253715651437, + "z": 0.0 + }, + { + "x": 94.40857348432014, + "y": -253.73355568669052, + "z": 0.0 + }, + { + "x": 94.408420106333, + "y": -252.7245742168668, + "z": 0.0 + }, + { + "x": 94.40826672834586, + "y": -251.71559274704308, + "z": 0.0 + }, + { + "x": 94.4081133503587, + "y": -250.7066112772192, + "z": 0.0 + }, + { + "x": 94.40795997237156, + "y": -249.6976298073955, + "z": 0.0 + }, + { + "x": 94.40780659438441, + "y": -248.6886483375717, + "z": 0.0 + }, + { + "x": 94.40765321639726, + "y": -247.67966686774793, + "z": 0.0 + }, + { + "x": 94.4074998384101, + "y": -246.67068539792407, + "z": 0.0 + }, + { + "x": 94.40734646042296, + "y": -245.66170392810037, + "z": 0.0 + }, + { + "x": 94.40719308243582, + "y": -244.65272245827663, + "z": 0.0 + }, + { + "x": 94.40703970444866, + "y": -243.64374098845275, + "z": 0.0 + }, + { + "x": 94.40688632646152, + "y": -242.634759518629, + "z": 0.0 + }, + { + "x": 94.40673294847437, + "y": -241.62577804880524, + "z": 0.0 + }, + { + "x": 94.40657957048722, + "y": -240.61679657898145, + "z": 0.0 + }, + { + "x": 94.40642619250008, + "y": -239.60781510915768, + "z": 0.0 + }, + { + "x": 94.40627281451292, + "y": -238.59883363933383, + "z": 0.0 + }, + { + "x": 94.40611943652578, + "y": -237.58985216951018, + "z": 0.0 + }, + { + "x": 94.40596605853862, + "y": -236.5808706996863, + "z": 0.0 + }, + { + "x": 94.40581268055148, + "y": -235.57188922986256, + "z": 0.0 + }, + { + "x": 94.40565930256435, + "y": -234.56290776003885, + "z": 0.0 + }, + { + "x": 94.40550592457718, + "y": -233.55392629021495, + "z": 0.0 + }, + { + "x": 94.40535254659004, + "y": -232.54494482039124, + "z": 0.0 + }, + { + "x": 94.40519916860289, + "y": -231.53596335056744, + "z": 0.0 + }, + { + "x": 94.40504579061574, + "y": -230.52698188074368, + "z": 0.0 + }, + { + "x": 94.4048924126286, + "y": -229.51800041091988, + "z": 0.0 + }, + { + "x": 94.40473903464144, + "y": -228.50901894109606, + "z": 0.0 + }, + { + "x": 94.4045856566543, + "y": -227.50003747127238, + "z": 0.0 + }, + { + "x": 94.40443227866714, + "y": -226.4910560014485, + "z": 0.0 + }, + { + "x": 94.40427890068, + "y": -225.4820745316248, + "z": 0.0 + }, + { + "x": 94.40412552269285, + "y": -224.473093061801, + "z": 0.0 + }, + { + "x": 94.4039721447057, + "y": -223.46411159197723, + "z": 0.0 + }, + { + "x": 94.40381876671856, + "y": -222.45513012215343, + "z": 0.0 + }, + { + "x": 94.4036653887314, + "y": -221.4461486523296, + "z": 0.0 + }, + { + "x": 94.40351201074427, + "y": -220.43716718250593, + "z": 0.0 + }, + { + "x": 94.4033586327571, + "y": -219.42818571268205, + "z": 0.0 + }, + { + "x": 94.40320525476996, + "y": -218.4192042428583, + "z": 0.0 + }, + { + "x": 94.40305187678281, + "y": -217.41022277303452, + "z": 0.0 + }, + { + "x": 94.40289849879566, + "y": -216.40124130321075, + "z": 0.0 + }, + { + "x": 94.40274512080852, + "y": -215.39225983338696, + "z": 0.0 + }, + { + "x": 94.40259174282136, + "y": -214.38327836356314, + "z": 0.0 + }, + { + "x": 94.40243836483423, + "y": -213.37429689373946, + "z": 0.0 + }, + { + "x": 94.40228498684708, + "y": -212.36531542391563, + "z": 0.0 + }, + { + "x": 94.40213160885992, + "y": -211.35633395409178, + "z": 0.0 + }, + { + "x": 94.40197823087277, + "y": -210.34735248426807, + "z": 0.0 + }, + { + "x": 94.40182485288562, + "y": -209.33837101444428, + "z": 0.0 + }, + { + "x": 94.40167147489848, + "y": -208.3293895446205, + "z": 0.0 + } + ] + }, + { + "id": 121, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.40167156732991, + "y": -208.33060564612455, + "z": 0.0 + }, + { + "x": 86.40182494531706, + "y": -209.33958711594832, + "z": 0.0 + }, + { + "x": 86.4019783233042, + "y": -210.3485685857721, + "z": 0.0 + }, + { + "x": 86.40213170129135, + "y": -211.35755005559594, + "z": 0.0 + }, + { + "x": 86.40228507927851, + "y": -212.36653152541967, + "z": 0.0 + }, + { + "x": 86.40243845726566, + "y": -213.37551299524338, + "z": 0.0 + }, + { + "x": 86.40259183525279, + "y": -214.3844944650673, + "z": 0.0 + }, + { + "x": 86.40274521323995, + "y": -215.393475934891, + "z": 0.0 + }, + { + "x": 86.4028985912271, + "y": -216.4024574047148, + "z": 0.0 + }, + { + "x": 86.40305196921425, + "y": -217.41143887453856, + "z": 0.0 + }, + { + "x": 86.40320534720139, + "y": -218.42042034436236, + "z": 0.0 + }, + { + "x": 86.40335872518854, + "y": -219.4294018141862, + "z": 0.0 + }, + { + "x": 86.4035121031757, + "y": -220.43838328400986, + "z": 0.0 + }, + { + "x": 86.40366548116283, + "y": -221.44736475383377, + "z": 0.0 + }, + { + "x": 86.40381885915, + "y": -222.45634622365748, + "z": 0.0 + }, + { + "x": 86.40397223713714, + "y": -223.46532769348127, + "z": 0.0 + }, + { + "x": 86.40412561512429, + "y": -224.47430916330504, + "z": 0.0 + }, + { + "x": 86.40427899311143, + "y": -225.48329063312883, + "z": 0.0 + }, + { + "x": 86.40443237109858, + "y": -226.49227210295265, + "z": 0.0 + }, + { + "x": 86.40458574908574, + "y": -227.5012535727763, + "z": 0.0 + }, + { + "x": 86.40473912707287, + "y": -228.51023504260021, + "z": 0.0 + }, + { + "x": 86.40489250506003, + "y": -229.51921651242392, + "z": 0.0 + }, + { + "x": 86.40504588304718, + "y": -230.52819798224772, + "z": 0.0 + }, + { + "x": 86.40519926103433, + "y": -231.53717945207148, + "z": 0.0 + }, + { + "x": 86.40535263902147, + "y": -232.54616092189528, + "z": 0.0 + }, + { + "x": 86.40550601700862, + "y": -233.5551423917191, + "z": 0.0 + }, + { + "x": 86.40565939499578, + "y": -234.56412386154278, + "z": 0.0 + }, + { + "x": 86.40581277298291, + "y": -235.5731053313666, + "z": 0.0 + }, + { + "x": 86.40596615097006, + "y": -236.58208680119046, + "z": 0.0 + }, + { + "x": 86.40611952895722, + "y": -237.5910682710141, + "z": 0.0 + }, + { + "x": 86.40627290694435, + "y": -238.600049740838, + "z": 0.0 + }, + { + "x": 86.40642628493151, + "y": -239.60903121066173, + "z": 0.0 + }, + { + "x": 86.40657966291866, + "y": -240.6180126804855, + "z": 0.0 + }, + { + "x": 86.4067330409058, + "y": -241.6269941503093, + "z": 0.0 + }, + { + "x": 86.40688641889295, + "y": -242.63597562013305, + "z": 0.0 + }, + { + "x": 86.4070397968801, + "y": -243.6449570899569, + "z": 0.0 + }, + { + "x": 86.40719317486726, + "y": -244.65393855978056, + "z": 0.0 + }, + { + "x": 86.40734655285439, + "y": -245.6629200296044, + "z": 0.0 + }, + { + "x": 86.40749993084154, + "y": -246.67190149942823, + "z": 0.0 + }, + { + "x": 86.4076533088287, + "y": -247.68088296925197, + "z": 0.0 + }, + { + "x": 86.40780668681585, + "y": -248.68986443907573, + "z": 0.0 + }, + { + "x": 86.40796006480299, + "y": -249.69884590889953, + "z": 0.0 + }, + { + "x": 86.40811344279014, + "y": -250.70782737872335, + "z": 0.0 + }, + { + "x": 86.4082668207773, + "y": -251.716808848547, + "z": 0.0 + }, + { + "x": 86.40842019876443, + "y": -252.72579031837085, + "z": 0.0 + }, + { + "x": 86.40857357675158, + "y": -253.73477178819468, + "z": 0.0 + }, + { + "x": 86.40872695473874, + "y": -254.74375325801842, + "z": 0.0 + }, + { + "x": 86.40888033272589, + "y": -255.75273472784218, + "z": 0.0 + }, + { + "x": 86.40903371071303, + "y": -256.761716197666, + "z": 0.0 + }, + { + "x": 86.40918708870018, + "y": -257.77069766748974, + "z": 0.0 + }, + { + "x": 86.40934046668733, + "y": -258.77967913731356, + "z": 0.0 + }, + { + "x": 86.40949384467449, + "y": -259.78866060713733, + "z": 0.0 + }, + { + "x": 86.40964722266163, + "y": -260.7976420769611, + "z": 0.0 + }, + { + "x": 86.40980060064878, + "y": -261.80662354678486, + "z": 0.0 + }, + { + "x": 86.40995397863593, + "y": -262.81560501660863, + "z": 0.0 + }, + { + "x": 86.41010735662307, + "y": -263.8245864864324, + "z": 0.0 + }, + { + "x": 86.41026073461022, + "y": -264.8335679562562, + "z": 0.0 + }, + { + "x": 86.41041411259737, + "y": -265.84254942608004, + "z": 0.0 + }, + { + "x": 86.41056749058453, + "y": -266.85153089590375, + "z": 0.0 + }, + { + "x": 86.41072086857167, + "y": -267.86051236572746, + "z": 0.0 + }, + { + "x": 86.4108742465588, + "y": -268.8694938355514, + "z": 0.0 + }, + { + "x": 86.41102762454597, + "y": -269.8784753053751, + "z": 0.0 + }, + { + "x": 86.41118100253311, + "y": -270.88745677519887, + "z": 0.0 + }, + { + "x": 86.41133438052026, + "y": -271.89643824502264, + "z": 0.0 + }, + { + "x": 86.4114877585074, + "y": -272.90541971484646, + "z": 0.0 + }, + { + "x": 86.41164113649455, + "y": -273.9144011846702, + "z": 0.0 + }, + { + "x": 86.4117945144817, + "y": -274.923382654494, + "z": 0.0 + }, + { + "x": 86.41194789246885, + "y": -275.93236412431787, + "z": 0.0 + }, + { + "x": 86.412101270456, + "y": -276.9413455941416, + "z": 0.0 + }, + { + "x": 86.41225464844315, + "y": -277.95032706396535, + "z": 0.0 + }, + { + "x": 86.4124080264303, + "y": -278.95930853378917, + "z": 0.0 + }, + { + "x": 86.41256140441745, + "y": -279.96829000361294, + "z": 0.0 + }, + { + "x": 86.41271478240459, + "y": -280.9772714734367, + "z": 0.0 + }, + { + "x": 86.41286816039174, + "y": -281.9862529432605, + "z": 0.0 + }, + { + "x": 86.41302153837889, + "y": -282.99523441308435, + "z": 0.0 + }, + { + "x": 86.41317491636605, + "y": -284.00421588290806, + "z": 0.0 + }, + { + "x": 86.41332829435319, + "y": -285.0131973527318, + "z": 0.0 + }, + { + "x": 86.41348167234032, + "y": -286.0221788225557, + "z": 0.0 + }, + { + "x": 86.41363505032749, + "y": -287.0311602923794, + "z": 0.0 + }, + { + "x": 86.41378842831463, + "y": -288.04014176220323, + "z": 0.0 + }, + { + "x": 86.41394180630178, + "y": -289.049123232027, + "z": 0.0 + }, + { + "x": 86.41409518428893, + "y": -290.05810470185077, + "z": 0.0 + }, + { + "x": 86.41424856227607, + "y": -291.06708617167465, + "z": 0.0 + }, + { + "x": 86.41440194026323, + "y": -292.0760676414983, + "z": 0.0 + }, + { + "x": 86.41455531825036, + "y": -293.08504911132223, + "z": 0.0 + }, + { + "x": 86.41470869623753, + "y": -294.09403058114594, + "z": 0.0 + }, + { + "x": 86.41486207422467, + "y": -295.1030120509697, + "z": 0.0 + }, + { + "x": 86.41501545221182, + "y": -296.11199352079353, + "z": 0.0 + }, + { + "x": 86.41516883019897, + "y": -297.1209749906173, + "z": 0.0 + }, + { + "x": 86.41532220818611, + "y": -298.12995646044106, + "z": 0.0 + }, + { + "x": 86.41547558617326, + "y": -299.1389379302649, + "z": 0.0 + }, + { + "x": 86.4156289641604, + "y": -300.14791940008865, + "z": 0.0 + }, + { + "x": 86.41578234214755, + "y": -301.1569008699125, + "z": 0.0 + }, + { + "x": 86.41593572013471, + "y": -302.16588233973624, + "z": 0.0 + }, + { + "x": 86.41608909812186, + "y": -303.17486380956, + "z": 0.0 + }, + { + "x": 86.416242476109, + "y": -304.1838452793838, + "z": 0.0 + }, + { + "x": 86.41639585409615, + "y": -305.1928267492076, + "z": 0.0 + }, + { + "x": 86.4165492320833, + "y": -306.20180821903136, + "z": 0.0 + }, + { + "x": 86.41670261007044, + "y": -307.21078968885513, + "z": 0.0 + }, + { + "x": 86.41685598805759, + "y": -308.219771158679, + "z": 0.0 + }, + { + "x": 86.41700936604475, + "y": -309.2287526285027, + "z": 0.0 + }, + { + "x": 86.4171627440319, + "y": -310.23773409832654, + "z": 0.0 + }, + { + "x": 86.41731612201905, + "y": -311.2467155681503, + "z": 0.0 + }, + { + "x": 86.41746950000619, + "y": -312.2556970379741, + "z": 0.0 + }, + { + "x": 86.41762287799334, + "y": -313.2646785077979, + "z": 0.0 + }, + { + "x": 86.41777625598048, + "y": -314.27365997762166, + "z": 0.0 + }, + { + "x": 86.41792963396763, + "y": -315.2826414474455, + "z": 0.0 + }, + { + "x": 86.41808301195479, + "y": -316.29162291726925, + "z": 0.0 + }, + { + "x": 86.41823638994194, + "y": -317.300604387093, + "z": 0.0 + } + ] + }, + { + "id": 122, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 90.4016715211142, + "y": -208.32999759537253, + "z": 0.0 + }, + { + "x": 90.40182489910134, + "y": -209.3389790651963, + "z": 0.0 + }, + { + "x": 90.40197827708849, + "y": -210.3479605350201, + "z": 0.0 + }, + { + "x": 90.40213165507564, + "y": -211.35694200484386, + "z": 0.0 + }, + { + "x": 90.4022850330628, + "y": -212.36592347466765, + "z": 0.0 + }, + { + "x": 90.40243841104994, + "y": -213.37490494449142, + "z": 0.0 + }, + { + "x": 90.40259178903707, + "y": -214.3838864143152, + "z": 0.0 + }, + { + "x": 90.40274516702424, + "y": -215.39286788413898, + "z": 0.0 + }, + { + "x": 90.40289854501138, + "y": -216.40184935396277, + "z": 0.0 + }, + { + "x": 90.40305192299853, + "y": -217.41083082378654, + "z": 0.0 + }, + { + "x": 90.40320530098568, + "y": -218.41981229361033, + "z": 0.0 + }, + { + "x": 90.40335867897282, + "y": -219.42879376343413, + "z": 0.0 + }, + { + "x": 90.40351205695998, + "y": -220.4377752332579, + "z": 0.0 + }, + { + "x": 90.40366543494711, + "y": -221.4467567030817, + "z": 0.0 + }, + { + "x": 90.40381881293428, + "y": -222.45573817290546, + "z": 0.0 + }, + { + "x": 90.40397219092142, + "y": -223.46471964272925, + "z": 0.0 + }, + { + "x": 90.40412556890857, + "y": -224.47370111255302, + "z": 0.0 + }, + { + "x": 90.40427894689572, + "y": -225.4826825823768, + "z": 0.0 + }, + { + "x": 90.40443232488286, + "y": -226.49166405220058, + "z": 0.0 + }, + { + "x": 90.40458570287002, + "y": -227.50064552202434, + "z": 0.0 + }, + { + "x": 90.40473908085715, + "y": -228.50962699184814, + "z": 0.0 + }, + { + "x": 90.40489245884432, + "y": -229.5186084616719, + "z": 0.0 + }, + { + "x": 90.40504583683146, + "y": -230.5275899314957, + "z": 0.0 + }, + { + "x": 90.40519921481861, + "y": -231.53657140131946, + "z": 0.0 + }, + { + "x": 90.40535259280576, + "y": -232.54555287114326, + "z": 0.0 + }, + { + "x": 90.4055059707929, + "y": -233.55453434096702, + "z": 0.0 + }, + { + "x": 90.40565934878006, + "y": -234.56351581079082, + "z": 0.0 + }, + { + "x": 90.4058127267672, + "y": -235.57249728061458, + "z": 0.0 + }, + { + "x": 90.40596610475434, + "y": -236.58147875043838, + "z": 0.0 + }, + { + "x": 90.4061194827415, + "y": -237.59046022026214, + "z": 0.0 + }, + { + "x": 90.40627286072863, + "y": -238.5994416900859, + "z": 0.0 + }, + { + "x": 90.4064262387158, + "y": -239.6084231599097, + "z": 0.0 + }, + { + "x": 90.40657961670294, + "y": -240.61740462973347, + "z": 0.0 + }, + { + "x": 90.40673299469009, + "y": -241.62638609955727, + "z": 0.0 + }, + { + "x": 90.40688637267723, + "y": -242.63536756938103, + "z": 0.0 + }, + { + "x": 90.40703975066438, + "y": -243.64434903920483, + "z": 0.0 + }, + { + "x": 90.40719312865154, + "y": -244.6533305090286, + "z": 0.0 + }, + { + "x": 90.40734650663867, + "y": -245.6623119788524, + "z": 0.0 + }, + { + "x": 90.40749988462582, + "y": -246.67129344867615, + "z": 0.0 + }, + { + "x": 90.40765326261298, + "y": -247.68027491849995, + "z": 0.0 + }, + { + "x": 90.40780664060013, + "y": -248.6892563883237, + "z": 0.0 + }, + { + "x": 90.40796001858727, + "y": -249.6982378581475, + "z": 0.0 + }, + { + "x": 90.40811339657442, + "y": -250.70721932797127, + "z": 0.0 + }, + { + "x": 90.40826677456158, + "y": -251.71620079779504, + "z": 0.0 + }, + { + "x": 90.40842015254871, + "y": -252.72518226761883, + "z": 0.0 + }, + { + "x": 90.40857353053586, + "y": -253.7341637374426, + "z": 0.0 + }, + { + "x": 90.40872690852302, + "y": -254.7431452072664, + "z": 0.0 + }, + { + "x": 90.40888028651017, + "y": -255.75212667709016, + "z": 0.0 + }, + { + "x": 90.40903366449731, + "y": -256.76110814691395, + "z": 0.0 + }, + { + "x": 90.40918704248446, + "y": -257.7700896167377, + "z": 0.0 + }, + { + "x": 90.40934042047161, + "y": -258.7790710865615, + "z": 0.0 + }, + { + "x": 90.40949379845877, + "y": -259.7880525563853, + "z": 0.0 + }, + { + "x": 90.40964717644592, + "y": -260.7970340262091, + "z": 0.0 + }, + { + "x": 90.40980055443306, + "y": -261.80601549603284, + "z": 0.0 + }, + { + "x": 90.40995393242021, + "y": -262.8149969658566, + "z": 0.0 + }, + { + "x": 90.41010731040735, + "y": -263.8239784356804, + "z": 0.0 + }, + { + "x": 90.4102606883945, + "y": -264.8329599055042, + "z": 0.0 + }, + { + "x": 90.41041406638165, + "y": -265.84194137532796, + "z": 0.0 + }, + { + "x": 90.41056744436881, + "y": -266.85092284515173, + "z": 0.0 + }, + { + "x": 90.41072082235596, + "y": -267.8599043149755, + "z": 0.0 + }, + { + "x": 90.41087420034309, + "y": -268.8688857847993, + "z": 0.0 + }, + { + "x": 90.41102757833025, + "y": -269.8778672546231, + "z": 0.0 + }, + { + "x": 90.4111809563174, + "y": -270.88684872444685, + "z": 0.0 + }, + { + "x": 90.41133433430454, + "y": -271.8958301942706, + "z": 0.0 + }, + { + "x": 90.41148771229169, + "y": -272.90481166409444, + "z": 0.0 + }, + { + "x": 90.41164109027883, + "y": -273.9137931339182, + "z": 0.0 + }, + { + "x": 90.41179446826598, + "y": -274.92277460374197, + "z": 0.0 + }, + { + "x": 90.41194784625313, + "y": -275.9317560735658, + "z": 0.0 + }, + { + "x": 90.41210122424029, + "y": -276.94073754338956, + "z": 0.0 + }, + { + "x": 90.41225460222743, + "y": -277.9497190132133, + "z": 0.0 + }, + { + "x": 90.41240798021458, + "y": -278.95870048303715, + "z": 0.0 + }, + { + "x": 90.41256135820173, + "y": -279.9676819528609, + "z": 0.0 + }, + { + "x": 90.41271473618887, + "y": -280.9766634226847, + "z": 0.0 + }, + { + "x": 90.41286811417602, + "y": -281.9856448925085, + "z": 0.0 + }, + { + "x": 90.41302149216317, + "y": -282.99462636233227, + "z": 0.0 + }, + { + "x": 90.41317487015033, + "y": -284.00360783215604, + "z": 0.0 + }, + { + "x": 90.41332824813747, + "y": -285.01258930197986, + "z": 0.0 + }, + { + "x": 90.41348162612461, + "y": -286.0215707718036, + "z": 0.0 + }, + { + "x": 90.41363500411177, + "y": -287.0305522416274, + "z": 0.0 + }, + { + "x": 90.41378838209891, + "y": -288.0395337114512, + "z": 0.0 + }, + { + "x": 90.41394176008606, + "y": -289.048515181275, + "z": 0.0 + }, + { + "x": 90.41409513807321, + "y": -290.05749665109875, + "z": 0.0 + }, + { + "x": 90.41424851606035, + "y": -291.06647812092257, + "z": 0.0 + }, + { + "x": 90.41440189404751, + "y": -292.07545959074633, + "z": 0.0 + }, + { + "x": 90.41455527203465, + "y": -293.08444106057016, + "z": 0.0 + }, + { + "x": 90.41470865002181, + "y": -294.0934225303939, + "z": 0.0 + }, + { + "x": 90.41486202800895, + "y": -295.1024040002177, + "z": 0.0 + }, + { + "x": 90.4150154059961, + "y": -296.1113854700415, + "z": 0.0 + }, + { + "x": 90.41516878398325, + "y": -297.1203669398653, + "z": 0.0 + }, + { + "x": 90.4153221619704, + "y": -298.12934840968904, + "z": 0.0 + }, + { + "x": 90.41547553995754, + "y": -299.13832987951287, + "z": 0.0 + }, + { + "x": 90.41562891794469, + "y": -300.14731134933663, + "z": 0.0 + }, + { + "x": 90.41578229593183, + "y": -301.1562928191604, + "z": 0.0 + }, + { + "x": 90.415935673919, + "y": -302.1652742889842, + "z": 0.0 + }, + { + "x": 90.41608905190614, + "y": -303.174255758808, + "z": 0.0 + }, + { + "x": 90.41624242989329, + "y": -304.18323722863175, + "z": 0.0 + }, + { + "x": 90.41639580788043, + "y": -305.1922186984556, + "z": 0.0 + }, + { + "x": 90.41654918586758, + "y": -306.20120016827934, + "z": 0.0 + }, + { + "x": 90.41670256385473, + "y": -307.2101816381031, + "z": 0.0 + }, + { + "x": 90.41685594184187, + "y": -308.21916310792693, + "z": 0.0 + }, + { + "x": 90.41700931982903, + "y": -309.2281445777507, + "z": 0.0 + }, + { + "x": 90.41716269781618, + "y": -310.2371260475745, + "z": 0.0 + }, + { + "x": 90.41731607580333, + "y": -311.2461075173983, + "z": 0.0 + }, + { + "x": 90.41746945379047, + "y": -312.25508898722205, + "z": 0.0 + }, + { + "x": 90.41762283177762, + "y": -313.2640704570459, + "z": 0.0 + }, + { + "x": 90.41777620976477, + "y": -314.27305192686964, + "z": 0.0 + }, + { + "x": 90.41792958775191, + "y": -315.2820333966934, + "z": 0.0 + }, + { + "x": 90.41808296573907, + "y": -316.29101486651723, + "z": 0.0 + }, + { + "x": 90.41823634372622, + "y": -317.299996336341, + "z": 0.0 + } + ] + }, + { + "id": 123, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.41823632061836, + "y": -317.299692310965, + "z": 0.0 + }, + { + "x": 92.41808294263122, + "y": -316.2907108411412, + "z": 0.0 + }, + { + "x": 92.41792956464406, + "y": -315.2817293713174, + "z": 0.0 + }, + { + "x": 92.41777618665691, + "y": -314.27274790149363, + "z": 0.0 + }, + { + "x": 92.41762280866976, + "y": -313.26376643166986, + "z": 0.0 + }, + { + "x": 92.41746943068262, + "y": -312.25478496184604, + "z": 0.0 + }, + { + "x": 92.41731605269547, + "y": -311.2458034920223, + "z": 0.0 + }, + { + "x": 92.41716267470832, + "y": -310.2368220221985, + "z": 0.0 + }, + { + "x": 92.41700929672118, + "y": -309.2278405523747, + "z": 0.0 + }, + { + "x": 92.41685591873402, + "y": -308.2188590825509, + "z": 0.0 + }, + { + "x": 92.41670254074687, + "y": -307.2098776127271, + "z": 0.0 + }, + { + "x": 92.41654916275972, + "y": -306.20089614290333, + "z": 0.0 + }, + { + "x": 92.41639578477258, + "y": -305.19191467307957, + "z": 0.0 + }, + { + "x": 92.41624240678543, + "y": -304.18293320325574, + "z": 0.0 + }, + { + "x": 92.41608902879828, + "y": -303.173951733432, + "z": 0.0 + }, + { + "x": 92.41593565081114, + "y": -302.1649702636082, + "z": 0.0 + }, + { + "x": 92.41578227282398, + "y": -301.15598879378433, + "z": 0.0 + }, + { + "x": 92.41562889483683, + "y": -300.1470073239606, + "z": 0.0 + }, + { + "x": 92.41547551684968, + "y": -299.13802585413686, + "z": 0.0 + }, + { + "x": 92.41532213886254, + "y": -298.12904438431303, + "z": 0.0 + }, + { + "x": 92.41516876087539, + "y": -297.12006291448927, + "z": 0.0 + }, + { + "x": 92.41501538288824, + "y": -296.1110814446655, + "z": 0.0 + }, + { + "x": 92.4148620049011, + "y": -295.1020999748417, + "z": 0.0 + }, + { + "x": 92.41470862691395, + "y": -294.0931185050179, + "z": 0.0 + }, + { + "x": 92.41455524892679, + "y": -293.0841370351941, + "z": 0.0 + }, + { + "x": 92.41440187093966, + "y": -292.0751555653703, + "z": 0.0 + }, + { + "x": 92.4142484929525, + "y": -291.06617409554656, + "z": 0.0 + }, + { + "x": 92.41409511496535, + "y": -290.05719262572273, + "z": 0.0 + }, + { + "x": 92.4139417369782, + "y": -289.04821115589897, + "z": 0.0 + }, + { + "x": 92.41378835899106, + "y": -288.0392296860752, + "z": 0.0 + }, + { + "x": 92.41363498100391, + "y": -287.0302482162514, + "z": 0.0 + }, + { + "x": 92.41348160301675, + "y": -286.0212667464276, + "z": 0.0 + }, + { + "x": 92.41332822502962, + "y": -285.01228527660385, + "z": 0.0 + }, + { + "x": 92.41317484704247, + "y": -284.00330380678, + "z": 0.0 + }, + { + "x": 92.41302146905531, + "y": -282.9943223369562, + "z": 0.0 + }, + { + "x": 92.41286809106816, + "y": -281.9853408671325, + "z": 0.0 + }, + { + "x": 92.41271471308102, + "y": -280.97635939730867, + "z": 0.0 + }, + { + "x": 92.41256133509387, + "y": -279.9673779274849, + "z": 0.0 + }, + { + "x": 92.41240795710672, + "y": -278.95839645766114, + "z": 0.0 + }, + { + "x": 92.41225457911958, + "y": -277.9494149878373, + "z": 0.0 + }, + { + "x": 92.41210120113243, + "y": -276.94043351801355, + "z": 0.0 + }, + { + "x": 92.41194782314527, + "y": -275.9314520481897, + "z": 0.0 + }, + { + "x": 92.41179444515812, + "y": -274.92247057836596, + "z": 0.0 + }, + { + "x": 92.41164106717098, + "y": -273.9134891085422, + "z": 0.0 + }, + { + "x": 92.41148768918383, + "y": -272.9045076387184, + "z": 0.0 + }, + { + "x": 92.41133431119668, + "y": -271.8955261688946, + "z": 0.0 + }, + { + "x": 92.41118093320954, + "y": -270.88654469907084, + "z": 0.0 + }, + { + "x": 92.41102755522239, + "y": -269.8775632292471, + "z": 0.0 + }, + { + "x": 92.41087417723523, + "y": -268.86858175942325, + "z": 0.0 + }, + { + "x": 92.4107207992481, + "y": -267.8596002895995, + "z": 0.0 + }, + { + "x": 92.41056742126095, + "y": -266.8506188197757, + "z": 0.0 + }, + { + "x": 92.41041404327379, + "y": -265.84163734995195, + "z": 0.0 + }, + { + "x": 92.41026066528664, + "y": -264.8326558801282, + "z": 0.0 + }, + { + "x": 92.4101072872995, + "y": -263.82367441030436, + "z": 0.0 + }, + { + "x": 92.40995390931235, + "y": -262.8146929404806, + "z": 0.0 + }, + { + "x": 92.4098005313252, + "y": -261.80571147065683, + "z": 0.0 + }, + { + "x": 92.40964715333806, + "y": -260.79673000083307, + "z": 0.0 + }, + { + "x": 92.40949377535091, + "y": -259.7877485310093, + "z": 0.0 + }, + { + "x": 92.40934039736375, + "y": -258.7787670611855, + "z": 0.0 + }, + { + "x": 92.4091870193766, + "y": -257.7697855913617, + "z": 0.0 + }, + { + "x": 92.40903364138946, + "y": -256.76080412153794, + "z": 0.0 + }, + { + "x": 92.40888026340231, + "y": -255.75182265171415, + "z": 0.0 + }, + { + "x": 92.40872688541516, + "y": -254.74284118189038, + "z": 0.0 + }, + { + "x": 92.408573507428, + "y": -253.73385971206656, + "z": 0.0 + }, + { + "x": 92.40842012944086, + "y": -252.72487824224282, + "z": 0.0 + }, + { + "x": 92.40826675145372, + "y": -251.71589677241906, + "z": 0.0 + }, + { + "x": 92.40811337346656, + "y": -250.70691530259523, + "z": 0.0 + }, + { + "x": 92.40795999547942, + "y": -249.6979338327715, + "z": 0.0 + }, + { + "x": 92.40780661749227, + "y": -248.6889523629477, + "z": 0.0 + }, + { + "x": 92.40765323950512, + "y": -247.67997089312394, + "z": 0.0 + }, + { + "x": 92.40749986151796, + "y": -246.6709894233001, + "z": 0.0 + }, + { + "x": 92.40734648353082, + "y": -245.66200795347638, + "z": 0.0 + }, + { + "x": 92.40719310554368, + "y": -244.6530264836526, + "z": 0.0 + }, + { + "x": 92.40703972755652, + "y": -243.6440450138288, + "z": 0.0 + }, + { + "x": 92.40688634956938, + "y": -242.63506354400502, + "z": 0.0 + }, + { + "x": 92.40673297158223, + "y": -241.62608207418126, + "z": 0.0 + }, + { + "x": 92.40657959359508, + "y": -240.61710060435746, + "z": 0.0 + }, + { + "x": 92.40642621560794, + "y": -239.6081191345337, + "z": 0.0 + }, + { + "x": 92.40627283762078, + "y": -238.59913766470987, + "z": 0.0 + }, + { + "x": 92.40611945963364, + "y": -237.59015619488616, + "z": 0.0 + }, + { + "x": 92.40596608164648, + "y": -236.58117472506234, + "z": 0.0 + }, + { + "x": 92.40581270365934, + "y": -235.57219325523857, + "z": 0.0 + }, + { + "x": 92.4056593256722, + "y": -234.56321178541484, + "z": 0.0 + }, + { + "x": 92.40550594768504, + "y": -233.55423031559098, + "z": 0.0 + }, + { + "x": 92.4053525696979, + "y": -232.54524884576725, + "z": 0.0 + }, + { + "x": 92.40519919171075, + "y": -231.53626737594345, + "z": 0.0 + }, + { + "x": 92.4050458137236, + "y": -230.5272859061197, + "z": 0.0 + }, + { + "x": 92.40489243573646, + "y": -229.5183044362959, + "z": 0.0 + }, + { + "x": 92.4047390577493, + "y": -228.5093229664721, + "z": 0.0 + }, + { + "x": 92.40458567976216, + "y": -227.50034149664836, + "z": 0.0 + }, + { + "x": 92.404432301775, + "y": -226.49136002682454, + "z": 0.0 + }, + { + "x": 92.40427892378786, + "y": -225.4823785570008, + "z": 0.0 + }, + { + "x": 92.40412554580071, + "y": -224.473397087177, + "z": 0.0 + }, + { + "x": 92.40397216781356, + "y": -223.46441561735324, + "z": 0.0 + }, + { + "x": 92.40381878982642, + "y": -222.45543414752944, + "z": 0.0 + }, + { + "x": 92.40366541183926, + "y": -221.44645267770565, + "z": 0.0 + }, + { + "x": 92.40351203385212, + "y": -220.4374712078819, + "z": 0.0 + }, + { + "x": 92.40335865586496, + "y": -219.4284897380581, + "z": 0.0 + }, + { + "x": 92.40320527787782, + "y": -218.41950826823432, + "z": 0.0 + }, + { + "x": 92.40305189989067, + "y": -217.41052679841053, + "z": 0.0 + }, + { + "x": 92.40289852190352, + "y": -216.40154532858676, + "z": 0.0 + }, + { + "x": 92.40274514391638, + "y": -215.39256385876297, + "z": 0.0 + }, + { + "x": 92.40259176592922, + "y": -214.38358238893917, + "z": 0.0 + }, + { + "x": 92.40243838794208, + "y": -213.37460091911544, + "z": 0.0 + }, + { + "x": 92.40228500995494, + "y": -212.36561944929164, + "z": 0.0 + }, + { + "x": 92.40213163196778, + "y": -211.35663797946782, + "z": 0.0 + }, + { + "x": 92.40197825398063, + "y": -210.34765650964408, + "z": 0.0 + }, + { + "x": 92.40182487599348, + "y": -209.3386750398203, + "z": 0.0 + }, + { + "x": 92.40167149800634, + "y": -208.32969356999652, + "z": 0.0 + } + ] + }, + { + "id": 124, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.40167154422205, + "y": -208.33030162074854, + "z": 0.0 + }, + { + "x": 88.4018249222092, + "y": -209.3392830905723, + "z": 0.0 + }, + { + "x": 88.40197830019635, + "y": -210.3482645603961, + "z": 0.0 + }, + { + "x": 88.4021316781835, + "y": -211.3572460302199, + "z": 0.0 + }, + { + "x": 88.40228505617065, + "y": -212.36622750004366, + "z": 0.0 + }, + { + "x": 88.4024384341578, + "y": -213.3752089698674, + "z": 0.0 + }, + { + "x": 88.40259181214493, + "y": -214.38419043969125, + "z": 0.0 + }, + { + "x": 88.4027451901321, + "y": -215.393171909515, + "z": 0.0 + }, + { + "x": 88.40289856811924, + "y": -216.40215337933878, + "z": 0.0 + }, + { + "x": 88.40305194610639, + "y": -217.41113484916255, + "z": 0.0 + }, + { + "x": 88.40320532409353, + "y": -218.42011631898635, + "z": 0.0 + }, + { + "x": 88.40335870208068, + "y": -219.42909778881017, + "z": 0.0 + }, + { + "x": 88.40351208006784, + "y": -220.43807925863388, + "z": 0.0 + }, + { + "x": 88.40366545805497, + "y": -221.44706072845773, + "z": 0.0 + }, + { + "x": 88.40381883604213, + "y": -222.45604219828147, + "z": 0.0 + }, + { + "x": 88.40397221402928, + "y": -223.46502366810526, + "z": 0.0 + }, + { + "x": 88.40412559201643, + "y": -224.47400513792903, + "z": 0.0 + }, + { + "x": 88.40427897000357, + "y": -225.48298660775282, + "z": 0.0 + }, + { + "x": 88.40443234799072, + "y": -226.49196807757662, + "z": 0.0 + }, + { + "x": 88.40458572597788, + "y": -227.50094954740032, + "z": 0.0 + }, + { + "x": 88.40473910396501, + "y": -228.50993101722418, + "z": 0.0 + }, + { + "x": 88.40489248195217, + "y": -229.5189124870479, + "z": 0.0 + }, + { + "x": 88.40504585993932, + "y": -230.5278939568717, + "z": 0.0 + }, + { + "x": 88.40519923792647, + "y": -231.53687542669547, + "z": 0.0 + }, + { + "x": 88.40535261591361, + "y": -232.54585689651927, + "z": 0.0 + }, + { + "x": 88.40550599390076, + "y": -233.55483836634306, + "z": 0.0 + }, + { + "x": 88.40565937188792, + "y": -234.5638198361668, + "z": 0.0 + }, + { + "x": 88.40581274987505, + "y": -235.5728013059906, + "z": 0.0 + }, + { + "x": 88.4059661278622, + "y": -236.58178277581442, + "z": 0.0 + }, + { + "x": 88.40611950584936, + "y": -237.59076424563813, + "z": 0.0 + }, + { + "x": 88.4062728838365, + "y": -238.59974571546195, + "z": 0.0 + }, + { + "x": 88.40642626182365, + "y": -239.60872718528572, + "z": 0.0 + }, + { + "x": 88.4065796398108, + "y": -240.61770865510948, + "z": 0.0 + }, + { + "x": 88.40673301779795, + "y": -241.62669012493328, + "z": 0.0 + }, + { + "x": 88.4068863957851, + "y": -242.63567159475704, + "z": 0.0 + }, + { + "x": 88.40703977377224, + "y": -243.64465306458087, + "z": 0.0 + }, + { + "x": 88.4071931517594, + "y": -244.65363453440457, + "z": 0.0 + }, + { + "x": 88.40734652974653, + "y": -245.6626160042284, + "z": 0.0 + }, + { + "x": 88.40749990773368, + "y": -246.6715974740522, + "z": 0.0 + }, + { + "x": 88.40765328572084, + "y": -247.68057894387596, + "z": 0.0 + }, + { + "x": 88.40780666370799, + "y": -248.68956041369972, + "z": 0.0 + }, + { + "x": 88.40796004169513, + "y": -249.69854188352352, + "z": 0.0 + }, + { + "x": 88.40811341968228, + "y": -250.7075233533473, + "z": 0.0 + }, + { + "x": 88.40826679766944, + "y": -251.71650482317102, + "z": 0.0 + }, + { + "x": 88.40842017565657, + "y": -252.72548629299484, + "z": 0.0 + }, + { + "x": 88.40857355364372, + "y": -253.73446776281864, + "z": 0.0 + }, + { + "x": 88.40872693163088, + "y": -254.7434492326424, + "z": 0.0 + }, + { + "x": 88.40888030961803, + "y": -255.75243070246617, + "z": 0.0 + }, + { + "x": 88.40903368760517, + "y": -256.76141217228997, + "z": 0.0 + }, + { + "x": 88.40918706559232, + "y": -257.77039364211373, + "z": 0.0 + }, + { + "x": 88.40934044357947, + "y": -258.7793751119375, + "z": 0.0 + }, + { + "x": 88.40949382156663, + "y": -259.7883565817613, + "z": 0.0 + }, + { + "x": 88.40964719955377, + "y": -260.7973380515851, + "z": 0.0 + }, + { + "x": 88.40980057754092, + "y": -261.80631952140885, + "z": 0.0 + }, + { + "x": 88.40995395552807, + "y": -262.8153009912326, + "z": 0.0 + }, + { + "x": 88.41010733351521, + "y": -263.8242824610564, + "z": 0.0 + }, + { + "x": 88.41026071150236, + "y": -264.8332639308802, + "z": 0.0 + }, + { + "x": 88.4104140894895, + "y": -265.842245400704, + "z": 0.0 + }, + { + "x": 88.41056746747667, + "y": -266.85122687052774, + "z": 0.0 + }, + { + "x": 88.41072084546381, + "y": -267.8602083403515, + "z": 0.0 + }, + { + "x": 88.41087422345095, + "y": -268.8691898101754, + "z": 0.0 + }, + { + "x": 88.41102760143811, + "y": -269.8781712799991, + "z": 0.0 + }, + { + "x": 88.41118097942525, + "y": -270.88715274982286, + "z": 0.0 + }, + { + "x": 88.4113343574124, + "y": -271.8961342196466, + "z": 0.0 + }, + { + "x": 88.41148773539955, + "y": -272.90511568947045, + "z": 0.0 + }, + { + "x": 88.4116411133867, + "y": -273.9140971592942, + "z": 0.0 + }, + { + "x": 88.41179449137384, + "y": -274.923078629118, + "z": 0.0 + }, + { + "x": 88.41194786936099, + "y": -275.93206009894186, + "z": 0.0 + }, + { + "x": 88.41210124734815, + "y": -276.94104156876557, + "z": 0.0 + }, + { + "x": 88.4122546253353, + "y": -277.95002303858934, + "z": 0.0 + }, + { + "x": 88.41240800332244, + "y": -278.95900450841316, + "z": 0.0 + }, + { + "x": 88.41256138130959, + "y": -279.9679859782369, + "z": 0.0 + }, + { + "x": 88.41271475929673, + "y": -280.9769674480607, + "z": 0.0 + }, + { + "x": 88.41286813728388, + "y": -281.9859489178845, + "z": 0.0 + }, + { + "x": 88.41302151527103, + "y": -282.99493038770834, + "z": 0.0 + }, + { + "x": 88.41317489325819, + "y": -284.00391185753205, + "z": 0.0 + }, + { + "x": 88.41332827124533, + "y": -285.01289332735587, + "z": 0.0 + }, + { + "x": 88.41348164923247, + "y": -286.02187479717963, + "z": 0.0 + }, + { + "x": 88.41363502721963, + "y": -287.0308562670034, + "z": 0.0 + }, + { + "x": 88.41378840520677, + "y": -288.0398377368272, + "z": 0.0 + }, + { + "x": 88.41394178319392, + "y": -289.048819206651, + "z": 0.0 + }, + { + "x": 88.41409516118107, + "y": -290.05780067647476, + "z": 0.0 + }, + { + "x": 88.41424853916821, + "y": -291.0667821462986, + "z": 0.0 + }, + { + "x": 88.41440191715537, + "y": -292.07576361612234, + "z": 0.0 + }, + { + "x": 88.4145552951425, + "y": -293.0847450859462, + "z": 0.0 + }, + { + "x": 88.41470867312967, + "y": -294.09372655576993, + "z": 0.0 + }, + { + "x": 88.41486205111681, + "y": -295.1027080255937, + "z": 0.0 + }, + { + "x": 88.41501542910396, + "y": -296.1116894954175, + "z": 0.0 + }, + { + "x": 88.4151688070911, + "y": -297.1206709652413, + "z": 0.0 + }, + { + "x": 88.41532218507825, + "y": -298.12965243506505, + "z": 0.0 + }, + { + "x": 88.4154755630654, + "y": -299.1386339048889, + "z": 0.0 + }, + { + "x": 88.41562894105255, + "y": -300.14761537471264, + "z": 0.0 + }, + { + "x": 88.41578231903969, + "y": -301.15659684453647, + "z": 0.0 + }, + { + "x": 88.41593569702685, + "y": -302.16557831436023, + "z": 0.0 + }, + { + "x": 88.416089075014, + "y": -303.174559784184, + "z": 0.0 + }, + { + "x": 88.41624245300115, + "y": -304.18354125400776, + "z": 0.0 + }, + { + "x": 88.4163958309883, + "y": -305.1925227238316, + "z": 0.0 + }, + { + "x": 88.41654920897544, + "y": -306.20150419365535, + "z": 0.0 + }, + { + "x": 88.41670258696259, + "y": -307.2104856634791, + "z": 0.0 + }, + { + "x": 88.41685596494973, + "y": -308.21946713330294, + "z": 0.0 + }, + { + "x": 88.4170093429369, + "y": -309.2284486031267, + "z": 0.0 + }, + { + "x": 88.41716272092404, + "y": -310.23743007295053, + "z": 0.0 + }, + { + "x": 88.41731609891119, + "y": -311.2464115427743, + "z": 0.0 + }, + { + "x": 88.41746947689833, + "y": -312.25539301259806, + "z": 0.0 + }, + { + "x": 88.41762285488548, + "y": -313.2643744824219, + "z": 0.0 + }, + { + "x": 88.41777623287263, + "y": -314.27335595224565, + "z": 0.0 + }, + { + "x": 88.41792961085977, + "y": -315.2823374220694, + "z": 0.0 + }, + { + "x": 88.41808298884693, + "y": -316.29131889189324, + "z": 0.0 + }, + { + "x": 88.41823636683408, + "y": -317.300300361717, + "z": 0.0 + } + ] + }, + { + "id": 125, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 160.02380355703423, + "y": -46.204824954497035, + "z": 0.0 + }, + { + "x": 160.02506048809644, + "y": -45.17011983558629, + "z": 0.0 + }, + { + "x": 160.02631741915866, + "y": -44.135414716675555, + "z": 0.0 + }, + { + "x": 160.02757435022087, + "y": -43.10070959776482, + "z": 0.0 + }, + { + "x": 160.02883128128312, + "y": -42.06600447885419, + "z": 0.0 + }, + { + "x": 160.03008821234533, + "y": -41.03129935994334, + "z": 0.0 + }, + { + "x": 160.03134514340755, + "y": -39.996594241032604, + "z": 0.0 + }, + { + "x": 160.03260207446976, + "y": -38.96188912212187, + "z": 0.0 + }, + { + "x": 160.03385900553198, + "y": -37.927184003211124, + "z": 0.0 + }, + { + "x": 160.0351159365942, + "y": -36.89247888430038, + "z": 0.0 + }, + { + "x": 160.0363728676564, + "y": -35.857773765389645, + "z": 0.0 + }, + { + "x": 160.03762979871863, + "y": -34.82306864647891, + "z": 0.0 + }, + { + "x": 160.03888672978084, + "y": -33.78836352756817, + "z": 0.0 + }, + { + "x": 160.04014366084309, + "y": -32.75365840865754, + "z": 0.0 + }, + { + "x": 160.0414005919053, + "y": -31.7189532897467, + "z": 0.0 + }, + { + "x": 160.04265752296752, + "y": -30.68424817083596, + "z": 0.0 + }, + { + "x": 160.04391445402973, + "y": -29.649543051925225, + "z": 0.0 + }, + { + "x": 160.04517138509195, + "y": -28.61483793301449, + "z": 0.0 + }, + { + "x": 160.04642831615416, + "y": -27.580132814103752, + "z": 0.0 + }, + { + "x": 160.04768524721638, + "y": -26.545427695193016, + "z": 0.0 + }, + { + "x": 160.0489421782786, + "y": -25.51072257628228, + "z": 0.0 + }, + { + "x": 160.05019910934084, + "y": -24.476017457371654, + "z": 0.0 + }, + { + "x": 160.05145604040305, + "y": -23.441312338460804, + "z": 0.0 + }, + { + "x": 160.05271297146527, + "y": -22.406607219550068, + "z": 0.0 + }, + { + "x": 160.05396990252748, + "y": -21.371902100639332, + "z": 0.0 + }, + { + "x": 160.0552268335897, + "y": -20.337196981728592, + "z": 0.0 + }, + { + "x": 160.05648376465192, + "y": -19.302491862817856, + "z": 0.0 + }, + { + "x": 160.05774069571413, + "y": -18.26778674390712, + "z": 0.0 + }, + { + "x": 160.05899762677635, + "y": -17.233081624996384, + "z": 0.0 + }, + { + "x": 160.06025455783856, + "y": -16.198376506085648, + "z": 0.0 + }, + { + "x": 160.0615114889008, + "y": -15.16367138717502, + "z": 0.0 + }, + { + "x": 160.06276841996302, + "y": -14.128966268264172, + "z": 0.0 + }, + { + "x": 160.06402535102524, + "y": -13.094261149353436, + "z": 0.0 + }, + { + "x": 160.06528228208745, + "y": -12.059556030442698, + "z": 0.0 + }, + { + "x": 160.06653921314967, + "y": -11.024850911531962, + "z": 0.0 + } + ] + }, + { + "id": 126, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 152.06654511582957, + "y": -11.015132740726886, + "z": 0.0 + }, + { + "x": 152.06528818476735, + "y": -12.049837859637622, + "z": 0.0 + }, + { + "x": 152.06403125370514, + "y": -13.08454297854836, + "z": 0.0 + }, + { + "x": 152.06277432264292, + "y": -14.119248097459096, + "z": 0.0 + }, + { + "x": 152.0615173915807, + "y": -15.153953216369723, + "z": 0.0 + }, + { + "x": 152.06026046051846, + "y": -16.188658335280568, + "z": 0.0 + }, + { + "x": 152.05900352945625, + "y": -17.223363454191304, + "z": 0.0 + }, + { + "x": 152.05774659839403, + "y": -18.25806857310204, + "z": 0.0 + }, + { + "x": 152.05648966733182, + "y": -19.292773692012776, + "z": 0.0 + }, + { + "x": 152.0552327362696, + "y": -20.327478810923512, + "z": 0.0 + }, + { + "x": 152.0539758052074, + "y": -21.362183929834252, + "z": 0.0 + }, + { + "x": 152.05271887414517, + "y": -22.39688904874499, + "z": 0.0 + }, + { + "x": 152.05146194308296, + "y": -23.431594167655724, + "z": 0.0 + }, + { + "x": 152.05020501202074, + "y": -24.466299286566354, + "z": 0.0 + }, + { + "x": 152.0489480809585, + "y": -25.5010044054772, + "z": 0.0 + }, + { + "x": 152.04769114989628, + "y": -26.535709524387936, + "z": 0.0 + }, + { + "x": 152.04643421883407, + "y": -27.570414643298673, + "z": 0.0 + }, + { + "x": 152.04517728777185, + "y": -28.60511976220941, + "z": 0.0 + }, + { + "x": 152.04392035670963, + "y": -29.639824881120145, + "z": 0.0 + }, + { + "x": 152.04266342564742, + "y": -30.67453000003088, + "z": 0.0 + }, + { + "x": 152.0414064945852, + "y": -31.70923511894162, + "z": 0.0 + }, + { + "x": 152.040149563523, + "y": -32.74394023785224, + "z": 0.0 + }, + { + "x": 152.03889263246074, + "y": -33.7786453567631, + "z": 0.0 + }, + { + "x": 152.03763570139853, + "y": -34.813350475673836, + "z": 0.0 + }, + { + "x": 152.0363787703363, + "y": -35.84805559458457, + "z": 0.0 + }, + { + "x": 152.0351218392741, + "y": -36.88276071349531, + "z": 0.0 + }, + { + "x": 152.03386490821188, + "y": -37.91746583240605, + "z": 0.0 + }, + { + "x": 152.03260797714967, + "y": -38.952170951316795, + "z": 0.0 + }, + { + "x": 152.03135104608745, + "y": -39.98687607022753, + "z": 0.0 + }, + { + "x": 152.03009411502524, + "y": -41.02158118913827, + "z": 0.0 + }, + { + "x": 152.02883718396302, + "y": -42.05628630804889, + "z": 0.0 + }, + { + "x": 152.02758025290078, + "y": -43.09099142695975, + "z": 0.0 + }, + { + "x": 152.02632332183856, + "y": -44.12569654587048, + "z": 0.0 + }, + { + "x": 152.02506639077635, + "y": -45.16040166478122, + "z": 0.0 + }, + { + "x": 152.02380945971413, + "y": -46.19510678369196, + "z": 0.0 + } + ] + }, + { + "id": 127, + "map_element_id": 6, + "type": "road_line", + "geometry": [ + { + "x": 156.06654216448962, + "y": -11.019991826129424, + "z": 0.0 + }, + { + "x": 156.0652852334274, + "y": -12.05469694504016, + "z": 0.0 + }, + { + "x": 156.0640283023652, + "y": -13.089402063950898, + "z": 0.0 + }, + { + "x": 156.06277137130297, + "y": -14.124107182861634, + "z": 0.0 + }, + { + "x": 156.06151444024076, + "y": -15.158812301772372, + "z": 0.0 + }, + { + "x": 156.0602575091785, + "y": -16.193517420683108, + "z": 0.0 + }, + { + "x": 156.0590005781163, + "y": -17.228222539593844, + "z": 0.0 + }, + { + "x": 156.05774364705408, + "y": -18.26292765850458, + "z": 0.0 + }, + { + "x": 156.05648671599187, + "y": -19.297632777415316, + "z": 0.0 + }, + { + "x": 156.05522978492965, + "y": -20.332337896326052, + "z": 0.0 + }, + { + "x": 156.05397285386744, + "y": -21.367043015236792, + "z": 0.0 + }, + { + "x": 156.05271592280522, + "y": -22.401748134147528, + "z": 0.0 + }, + { + "x": 156.051458991743, + "y": -23.436453253058264, + "z": 0.0 + }, + { + "x": 156.0502020606808, + "y": -24.471158371969004, + "z": 0.0 + }, + { + "x": 156.04894512961855, + "y": -25.50586349087974, + "z": 0.0 + }, + { + "x": 156.04768819855633, + "y": -26.540568609790476, + "z": 0.0 + }, + { + "x": 156.0464312674941, + "y": -27.575273728701212, + "z": 0.0 + }, + { + "x": 156.0451743364319, + "y": -28.60997884761195, + "z": 0.0 + }, + { + "x": 156.04391740536968, + "y": -29.644683966522685, + "z": 0.0 + }, + { + "x": 156.04266047430747, + "y": -30.67938908543342, + "z": 0.0 + }, + { + "x": 156.04140354324525, + "y": -31.71409420434416, + "z": 0.0 + }, + { + "x": 156.04014661218304, + "y": -32.74879932325489, + "z": 0.0 + }, + { + "x": 156.0388896811208, + "y": -33.783504442165636, + "z": 0.0 + }, + { + "x": 156.03763275005858, + "y": -34.81820956107637, + "z": 0.0 + }, + { + "x": 156.03637581899636, + "y": -35.85291467998711, + "z": 0.0 + }, + { + "x": 156.03511888793415, + "y": -36.887619798897845, + "z": 0.0 + }, + { + "x": 156.03386195687193, + "y": -37.92232491780859, + "z": 0.0 + }, + { + "x": 156.03260502580972, + "y": -38.95703003671933, + "z": 0.0 + }, + { + "x": 156.0313480947475, + "y": -39.99173515563007, + "z": 0.0 + }, + { + "x": 156.03009116368528, + "y": -41.0264402745408, + "z": 0.0 + }, + { + "x": 156.02883423262307, + "y": -42.06114539345154, + "z": 0.0 + }, + { + "x": 156.02757730156083, + "y": -43.09585051236228, + "z": 0.0 + }, + { + "x": 156.0263203704986, + "y": -44.13055563127302, + "z": 0.0 + }, + { + "x": 156.0250634394364, + "y": -45.165260750183755, + "z": 0.0 + }, + { + "x": 156.02380650837418, + "y": -46.1999658690945, + "z": 0.0 + } + ] + }, + { + "id": 128, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 158.02380503270422, + "y": -46.20239541179576, + "z": 0.0 + }, + { + "x": 158.0250619637664, + "y": -45.16769029288503, + "z": 0.0 + }, + { + "x": 158.02631889482865, + "y": -44.13298517397429, + "z": 0.0 + }, + { + "x": 158.02757582589084, + "y": -43.098280055063555, + "z": 0.0 + }, + { + "x": 158.02883275695308, + "y": -42.06357493615286, + "z": 0.0 + }, + { + "x": 158.03008968801532, + "y": -41.02886981724207, + "z": 0.0 + }, + { + "x": 158.0313466190775, + "y": -39.99416469833133, + "z": 0.0 + }, + { + "x": 158.03260355013975, + "y": -38.959459579420596, + "z": 0.0 + }, + { + "x": 158.03386048120194, + "y": -37.92475446050986, + "z": 0.0 + }, + { + "x": 158.03511741226419, + "y": -36.89004934159911, + "z": 0.0 + }, + { + "x": 158.03637434332637, + "y": -35.85534422268837, + "z": 0.0 + }, + { + "x": 158.03763127438862, + "y": -34.82063910377764, + "z": 0.0 + }, + { + "x": 158.0388882054508, + "y": -33.7859339848669, + "z": 0.0 + }, + { + "x": 158.04014513651305, + "y": -32.75122886595622, + "z": 0.0 + }, + { + "x": 158.0414020675753, + "y": -31.71652374704543, + "z": 0.0 + }, + { + "x": 158.04265899863748, + "y": -30.681818628134693, + "z": 0.0 + }, + { + "x": 158.04391592969972, + "y": -29.647113509223956, + "z": 0.0 + }, + { + "x": 158.0451728607619, + "y": -28.61240839031322, + "z": 0.0 + }, + { + "x": 158.04642979182415, + "y": -27.577703271402484, + "z": 0.0 + }, + { + "x": 158.04768672288634, + "y": -26.542998152491748, + "z": 0.0 + }, + { + "x": 158.04894365394858, + "y": -25.508293033581012, + "z": 0.0 + }, + { + "x": 158.05020058501083, + "y": -24.47358791467033, + "z": 0.0 + }, + { + "x": 158.05145751607301, + "y": -23.438882795759532, + "z": 0.0 + }, + { + "x": 158.05271444713526, + "y": -22.404177676848796, + "z": 0.0 + }, + { + "x": 158.05397137819745, + "y": -21.36947255793806, + "z": 0.0 + }, + { + "x": 158.0552283092597, + "y": -20.334767439027324, + "z": 0.0 + }, + { + "x": 158.05648524032188, + "y": -19.300062320116588, + "z": 0.0 + }, + { + "x": 158.05774217138412, + "y": -18.265357201205852, + "z": 0.0 + }, + { + "x": 158.0589991024463, + "y": -17.230652082295116, + "z": 0.0 + }, + { + "x": 158.06025603350855, + "y": -16.19594696338438, + "z": 0.0 + }, + { + "x": 158.0615129645708, + "y": -15.161241844473697, + "z": 0.0 + }, + { + "x": 158.06276989563298, + "y": -14.126536725562904, + "z": 0.0 + }, + { + "x": 158.06402682669523, + "y": -13.091831606652168, + "z": 0.0 + }, + { + "x": 158.0652837577574, + "y": -12.057126487741428, + "z": 0.0 + }, + { + "x": 158.06654068881966, + "y": -11.022421368830692, + "z": 0.0 + } + ] + }, + { + "id": 129, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 154.06654364015958, + "y": -11.017562283428155, + "z": 0.0 + }, + { + "x": 154.0652867090974, + "y": -12.052267402338892, + "z": 0.0 + }, + { + "x": 154.06402977803515, + "y": -13.086972521249628, + "z": 0.0 + }, + { + "x": 154.06277284697296, + "y": -14.121677640160364, + "z": 0.0 + }, + { + "x": 154.06151591591072, + "y": -15.156382759071047, + "z": 0.0 + }, + { + "x": 154.06025898484847, + "y": -16.191087877981836, + "z": 0.0 + }, + { + "x": 154.0590020537863, + "y": -17.225792996892572, + "z": 0.0 + }, + { + "x": 154.05774512272404, + "y": -18.26049811580331, + "z": 0.0 + }, + { + "x": 154.05648819166186, + "y": -19.295203234714045, + "z": 0.0 + }, + { + "x": 154.0552312605996, + "y": -20.32990835362478, + "z": 0.0 + }, + { + "x": 154.05397432953743, + "y": -21.364613472535524, + "z": 0.0 + }, + { + "x": 154.05271739847518, + "y": -22.39931859144626, + "z": 0.0 + }, + { + "x": 154.051460467413, + "y": -23.434023710356996, + "z": 0.0 + }, + { + "x": 154.05020353635075, + "y": -24.46872882926768, + "z": 0.0 + }, + { + "x": 154.0489466052885, + "y": -25.50343394817847, + "z": 0.0 + }, + { + "x": 154.04768967422632, + "y": -26.538139067089205, + "z": 0.0 + }, + { + "x": 154.04643274316408, + "y": -27.57284418599994, + "z": 0.0 + }, + { + "x": 154.0451758121019, + "y": -28.607549304910677, + "z": 0.0 + }, + { + "x": 154.04391888103964, + "y": -29.642254423821413, + "z": 0.0 + }, + { + "x": 154.04266194997746, + "y": -30.67695954273215, + "z": 0.0 + }, + { + "x": 154.0414050189152, + "y": -31.711664661642892, + "z": 0.0 + }, + { + "x": 154.04014808785303, + "y": -32.746369780553565, + "z": 0.0 + }, + { + "x": 154.03889115679078, + "y": -33.78107489946437, + "z": 0.0 + }, + { + "x": 154.03763422572854, + "y": -34.81578001837511, + "z": 0.0 + }, + { + "x": 154.03637729466635, + "y": -35.850485137285844, + "z": 0.0 + }, + { + "x": 154.0351203636041, + "y": -36.88519025619658, + "z": 0.0 + }, + { + "x": 154.03386343254192, + "y": -37.919895375107316, + "z": 0.0 + }, + { + "x": 154.03260650147968, + "y": -38.95460049401807, + "z": 0.0 + }, + { + "x": 154.0313495704175, + "y": -39.9893056129288, + "z": 0.0 + }, + { + "x": 154.03009263935525, + "y": -41.02401073183954, + "z": 0.0 + }, + { + "x": 154.02883570829306, + "y": -42.05871585075022, + "z": 0.0 + }, + { + "x": 154.02757877723081, + "y": -43.09342096966101, + "z": 0.0 + }, + { + "x": 154.02632184616857, + "y": -44.12812608857175, + "z": 0.0 + }, + { + "x": 154.02506491510638, + "y": -45.16283120748248, + "z": 0.0 + }, + { + "x": 154.02380798404414, + "y": -46.197536326393234, + "z": 0.0 + } + ] + }, + { + "id": 132, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.6289421841928, + "y": -1.9886780038783365, + "z": 0.0 + }, + { + "x": 326.6656086716041, + "y": -1.9892287191981524, + "z": 0.0 + }, + { + "x": 327.7022751590154, + "y": -1.9897794345179682, + "z": 0.0 + }, + { + "x": 328.7389416464267, + "y": -1.9903301498377841, + "z": 0.0 + }, + { + "x": 329.6481209673265, + "y": -2.051642925567623, + "z": 0.0 + }, + { + "x": 330.4498985439327, + "y": -2.203155677126415, + "z": 0.0 + }, + { + "x": 331.1978530249937, + "y": -2.4472767584409145, + "z": 0.0 + }, + { + "x": 331.91071142888325, + "y": -2.782292263691531, + "z": 0.0 + }, + { + "x": 332.5781554916712, + "y": -3.2037777983596696, + "z": 0.0 + }, + { + "x": 333.1259408932245, + "y": -3.691726022653432, + "z": 0.0 + }, + { + "x": 333.6427405093016, + "y": -4.274703225647427, + "z": 0.0 + }, + { + "x": 334.0803469879803, + "y": -4.918202194072655, + "z": 0.0 + }, + { + "x": 334.43130677011334, + "y": -5.611689089596034, + "z": 0.0 + }, + { + "x": 334.6907444176936, + "y": -6.345222294572525, + "z": 0.0 + }, + { + "x": 334.85430406670866, + "y": -7.107636665231244, + "z": 0.0 + }, + { + "x": 334.8887622144692, + "y": -7.838413889011973, + "z": 0.0 + }, + { + "x": 334.89479441876426, + "y": -8.739828728813038, + "z": 0.0 + }, + { + "x": 334.8940308483802, + "y": -9.751811845644003, + "z": 0.0 + }, + { + "x": 334.8934046904095, + "y": -10.788478290232261, + "z": 0.0 + } + ] + }, + { + "id": 135, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.8934037724806, + "y": -10.791206034385668, + "z": 0.0 + }, + { + "x": 338.89405960289776, + "y": -9.705413920194768, + "z": 0.0 + }, + { + "x": 338.8947154333149, + "y": -8.619621806003762, + "z": 0.0 + }, + { + "x": 338.85649157978924, + "y": -7.465933222116847, + "z": 0.0 + }, + { + "x": 338.71233106740794, + "y": -6.217633808411568, + "z": 0.0 + }, + { + "x": 338.3765877565555, + "y": -4.891298316001286, + "z": 0.0 + }, + { + "x": 337.798941246716, + "y": -3.5812481318554616, + "z": 0.0 + }, + { + "x": 337.0732723618795, + "y": -2.428622935502248, + "z": 0.0 + }, + { + "x": 336.19506370583065, + "y": -1.3895071817883942, + "z": 0.0 + }, + { + "x": 335.1955535115176, + "y": -0.4858140473350383, + "z": 0.0 + }, + { + "x": 334.1183015498897, + "y": 0.27518596346270763, + "z": 0.0 + }, + { + "x": 332.9558134017866, + "y": 0.9052869162020982, + "z": 0.0 + }, + { + "x": 331.70985610521825, + "y": 1.390851007829021, + "z": 0.0 + }, + { + "x": 330.4587621046933, + "y": 1.7266710094051807, + "z": 0.0 + }, + { + "x": 329.17953238403936, + "y": 1.928092642384523, + "z": 0.0 + }, + { + "x": 327.9131502261558, + "y": 2.007072758973851, + "z": 0.0 + }, + { + "x": 326.71719543516656, + "y": 2.0107444404429113, + "z": 0.0 + }, + { + "x": 325.6314032761233, + "y": 2.0113212531241174, + "z": 0.0 + } + ] + }, + { + "id": 136, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 348.23212641515147, + "y": 3.9993152009727924, + "z": 0.0 + }, + { + "x": 347.2048538328333, + "y": 3.9998609259051396, + "z": 0.0 + }, + { + "x": 346.1775812505151, + "y": 4.000406650837487, + "z": 0.0 + }, + { + "x": 345.15030866819694, + "y": 4.0009523757698355, + "z": 0.0 + }, + { + "x": 344.12303608587877, + "y": 4.001498100702182, + "z": 0.0 + }, + { + "x": 343.0957635035606, + "y": 4.002043825634529, + "z": 0.0 + }, + { + "x": 342.0684909212424, + "y": 4.002589550566876, + "z": 0.0 + }, + { + "x": 341.04121833892424, + "y": 4.003135275499223, + "z": 0.0 + }, + { + "x": 340.01394575660606, + "y": 4.00368100043157, + "z": 0.0 + }, + { + "x": 338.9866731742879, + "y": 4.0042267253639166, + "z": 0.0 + }, + { + "x": 337.9594005919697, + "y": 4.004772450296263, + "z": 0.0 + }, + { + "x": 336.9321280096516, + "y": 4.005318175228612, + "z": 0.0 + }, + { + "x": 335.9048554273334, + "y": 4.005863900160959, + "z": 0.0 + }, + { + "x": 334.87758284501524, + "y": 4.006409625093306, + "z": 0.0 + }, + { + "x": 333.85031026269706, + "y": 4.0069553500256525, + "z": 0.0 + }, + { + "x": 332.8230376803789, + "y": 4.007501074957999, + "z": 0.0 + }, + { + "x": 331.7957650980607, + "y": 4.008046799890346, + "z": 0.0 + }, + { + "x": 330.76849251574254, + "y": 4.008592524822693, + "z": 0.0 + }, + { + "x": 329.74121993342436, + "y": 4.00913824975504, + "z": 0.0 + }, + { + "x": 328.7139473511062, + "y": 4.009683974687387, + "z": 0.0 + }, + { + "x": 327.686674768788, + "y": 4.010229699619733, + "z": 0.0 + }, + { + "x": 326.65940218646983, + "y": 4.0107754245520795, + "z": 0.0 + }, + { + "x": 325.63212960415166, + "y": 4.011321149484425, + "z": 0.0 + } + ] + }, + { + "id": 138, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 348.23106394183185, + "y": 1.9993154831852011, + "z": 0.0 + }, + { + "x": 347.2037913595137, + "y": 1.9998612081175484, + "z": 0.0 + }, + { + "x": 346.1765187771955, + "y": 2.0004069330498955, + "z": 0.0 + }, + { + "x": 345.1492461948773, + "y": 2.0009526579822445, + "z": 0.0 + }, + { + "x": 344.12197361255915, + "y": 2.0014983829145914, + "z": 0.0 + }, + { + "x": 343.09470103024097, + "y": 2.002044107846938, + "z": 0.0 + }, + { + "x": 342.0674284479228, + "y": 2.002589832779285, + "z": 0.0 + }, + { + "x": 341.0401558656046, + "y": 2.003135557711632, + "z": 0.0 + }, + { + "x": 340.01288328328644, + "y": 2.0036812826439783, + "z": 0.0 + }, + { + "x": 338.98561070096827, + "y": 2.004227007576325, + "z": 0.0 + }, + { + "x": 337.9583381186501, + "y": 2.004772732508672, + "z": 0.0 + }, + { + "x": 336.93106553633197, + "y": 2.005318457441021, + "z": 0.0 + }, + { + "x": 335.9037929540138, + "y": 2.0058641823733674, + "z": 0.0 + }, + { + "x": 334.8765203716956, + "y": 2.0064099073057142, + "z": 0.0 + }, + { + "x": 333.84924778937744, + "y": 2.006955632238061, + "z": 0.0 + }, + { + "x": 332.82197520705927, + "y": 2.007501357170408, + "z": 0.0 + }, + { + "x": 331.7947026247411, + "y": 2.0080470821027547, + "z": 0.0 + }, + { + "x": 330.7674300424229, + "y": 2.0085928070351016, + "z": 0.0 + }, + { + "x": 329.74015746010474, + "y": 2.0091385319674484, + "z": 0.0 + }, + { + "x": 328.71288487778656, + "y": 2.0096842568997952, + "z": 0.0 + }, + { + "x": 327.6856122954684, + "y": 2.0102299818321416, + "z": 0.0 + }, + { + "x": 326.6583397131502, + "y": 2.010775706764488, + "z": 0.0 + }, + { + "x": 325.63106713083204, + "y": 2.0113214316968344, + "z": 0.0 + } + ] + }, + { + "id": 141, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.6289421841928, + "y": -1.9886780038783478, + "z": 0.0 + }, + { + "x": 326.65621476651097, + "y": -1.9892237288106946, + "z": 0.0 + }, + { + "x": 327.68348734882915, + "y": -1.9897694537430406, + "z": 0.0 + }, + { + "x": 328.7107599311473, + "y": -1.9903151786753874, + "z": 0.0 + }, + { + "x": 329.7380325134655, + "y": -1.9908609036077343, + "z": 0.0 + }, + { + "x": 330.7653050957837, + "y": -1.991406628540081, + "z": 0.0 + }, + { + "x": 331.79257767810185, + "y": -1.991952353472428, + "z": 0.0 + }, + { + "x": 332.81985026042, + "y": -1.9924980784047743, + "z": 0.0 + }, + { + "x": 333.8471228427382, + "y": -1.9930438033371212, + "z": 0.0 + }, + { + "x": 334.8743954250564, + "y": -1.993589528269468, + "z": 0.0 + }, + { + "x": 335.90166800737455, + "y": -1.9941352532018148, + "z": 0.0 + }, + { + "x": 336.92894058969273, + "y": -1.9946809781341617, + "z": 0.0 + }, + { + "x": 337.95621317201085, + "y": -1.9952267030665103, + "z": 0.0 + }, + { + "x": 338.983485754329, + "y": -1.9957724279988571, + "z": 0.0 + }, + { + "x": 340.0107583366472, + "y": -1.996318152931204, + "z": 0.0 + }, + { + "x": 341.0380309189654, + "y": -1.9968638778635508, + "z": 0.0 + }, + { + "x": 342.06530350128355, + "y": -1.9974096027958976, + "z": 0.0 + }, + { + "x": 343.0925760836017, + "y": -1.9979553277282445, + "z": 0.0 + }, + { + "x": 344.1198486659199, + "y": -1.9985010526605913, + "z": 0.0 + }, + { + "x": 345.1471212482381, + "y": -1.9990467775929377, + "z": 0.0 + }, + { + "x": 346.17439383055626, + "y": -1.9995925025252872, + "z": 0.0 + }, + { + "x": 347.20166641287443, + "y": -2.000138227457634, + "z": 0.0 + }, + { + "x": 348.2289389951926, + "y": -2.000683952389981, + "z": 0.0 + } + ] + }, + { + "id": 144, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 348.23106394183185, + "y": 1.9993154831852011, + "z": 0.0 + }, + { + "x": 347.1918410730398, + "y": 1.9998675565487212, + "z": 0.0 + }, + { + "x": 346.15261820424786, + "y": 2.000419629912234, + "z": 0.0 + }, + { + "x": 344.98014390717617, + "y": 1.9965987241811827, + "z": 0.0 + }, + { + "x": 343.7230281627808, + "y": 1.9009683922471656, + "z": 0.0 + }, + { + "x": 342.47012348855634, + "y": 1.6620026224745803, + "z": 0.0 + }, + { + "x": 341.2550498957666, + "y": 1.2752840500252611, + "z": 0.0 + }, + { + "x": 340.09415370705904, + "y": 0.7459549510153185, + "z": 0.0 + }, + { + "x": 338.97008553424934, + "y": 0.0547038951790324, + "z": 0.0 + }, + { + "x": 337.9618192359494, + "y": -0.7794300190888428, + "z": 0.0 + }, + { + "x": 337.0809376728373, + "y": -1.7338534646852155, + "z": 0.0 + }, + { + "x": 336.3322477308536, + "y": -2.7953125992055945, + "z": 0.0 + }, + { + "x": 335.729628727284, + "y": -3.9456065362702066, + "z": 0.0 + }, + { + "x": 335.2830505480407, + "y": -5.165868569711307, + "z": 0.0 + }, + { + "x": 334.9969996904255, + "y": -6.47215966779638, + "z": 0.0 + }, + { + "x": 334.9048119411275, + "y": -7.653043324408491, + "z": 0.0 + }, + { + "x": 334.89466009567434, + "y": -8.710030720693538, + "z": 0.0 + }, + { + "x": 334.89403239362105, + "y": -9.749253546557007, + "z": 0.0 + }, + { + "x": 334.89340469156787, + "y": -10.788476372420362, + "z": 0.0 + } + ] + }, + { + "id": 147, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.8934037724806, + "y": -10.791206034385581, + "z": 0.0 + }, + { + "x": 338.8940361484256, + "y": -9.7442451184274, + "z": 0.0 + }, + { + "x": 338.89466852437056, + "y": -8.697284202469334, + "z": 0.0 + }, + { + "x": 338.89530090031553, + "y": -7.650323286511048, + "z": 0.0 + }, + { + "x": 338.9559455676802, + "y": -6.729071264869208, + "z": 0.0 + }, + { + "x": 339.0867936424396, + "y": -5.853662244728881, + "z": 0.0 + }, + { + "x": 339.34005866146424, + "y": -5.243106847878202, + "z": 0.0 + }, + { + "x": 339.70660160297115, + "y": -4.618405976128673, + "z": 0.0 + }, + { + "x": 340.17828589458026, + "y": -3.994347784720262, + "z": 0.0 + }, + { + "x": 340.7131255868176, + "y": -3.4921935668140875, + "z": 0.0 + }, + { + "x": 341.31833084755374, + "y": -3.081911018620925, + "z": 0.0 + }, + { + "x": 342.03155256917285, + "y": -2.6966663594310103, + "z": 0.0 + }, + { + "x": 342.7922949514217, + "y": -2.3886302465492624, + "z": 0.0 + }, + { + "x": 343.58289861139315, + "y": -2.167658509517684, + "z": 0.0 + }, + { + "x": 344.39393511088724, + "y": -2.036496984348239, + "z": 0.0 + }, + { + "x": 345.2195099685739, + "y": -1.9947606781298046, + "z": 0.0 + }, + { + "x": 346.13501707677966, + "y": -1.9995715841471782, + "z": 0.0 + }, + { + "x": 347.1819780359861, + "y": -2.000127768268577, + "z": 0.0 + }, + { + "x": 348.2289389951926, + "y": -2.000683952389981, + "z": 0.0 + } + ] + }, + { + "id": 150, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 144.95979698585384, + "y": -1.9544698149348096, + "z": 0.0 + }, + { + "x": 146.01070607077742, + "y": -1.9545819776888724, + "z": 0.0 + }, + { + "x": 147.061615155701, + "y": -1.9546941404429357, + "z": 0.0 + }, + { + "x": 148.11252424062454, + "y": -1.9548063031969989, + "z": 0.0 + }, + { + "x": 149.1634333255481, + "y": -1.9549184659510621, + "z": 0.0 + }, + { + "x": 150.2143424104717, + "y": -1.9550306287051253, + "z": 0.0 + }, + { + "x": 151.26525149539526, + "y": -1.9551427914591881, + "z": 0.0 + }, + { + "x": 152.3161605803188, + "y": -1.9552549542132514, + "z": 0.0 + }, + { + "x": 153.36706966524238, + "y": -1.9553671169673146, + "z": 0.0 + }, + { + "x": 154.41797875016596, + "y": -1.9554792797213778, + "z": 0.0 + }, + { + "x": 155.46888783508953, + "y": -1.955591442475441, + "z": 0.0 + }, + { + "x": 156.5197969200131, + "y": -1.9557036052295038, + "z": 0.0 + }, + { + "x": 157.57070600493665, + "y": -1.955815767983567, + "z": 0.0 + }, + { + "x": 158.62161508986023, + "y": -1.9559279307376303, + "z": 0.0 + }, + { + "x": 159.6725241747838, + "y": -1.9560400934916935, + "z": 0.0 + }, + { + "x": 160.72343325970738, + "y": -1.9561522562457565, + "z": 0.0 + }, + { + "x": 161.77434234463095, + "y": -1.9562644189998195, + "z": 0.0 + }, + { + "x": 162.8252514295545, + "y": -1.9563765817538827, + "z": 0.0 + }, + { + "x": 163.87616051447807, + "y": -1.956488744507946, + "z": 0.0 + }, + { + "x": 164.92706959940165, + "y": -1.9566009072620092, + "z": 0.0 + }, + { + "x": 165.9779786843252, + "y": -1.956713070016072, + "z": 0.0 + }, + { + "x": 167.02888776924877, + "y": -1.9568252327701352, + "z": 0.0 + }, + { + "x": 168.07979685417234, + "y": -1.9569373955241984, + "z": 0.0 + } + ] + }, + { + "id": 151, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 168.08043722975782, + "y": 4.043062570302394, + "z": 0.0 + }, + { + "x": 167.02952814483425, + "y": 4.043174733056457, + "z": 0.0 + }, + { + "x": 165.97861905991067, + "y": 4.04328689581052, + "z": 0.0 + }, + { + "x": 164.92770997498712, + "y": 4.043399058564583, + "z": 0.0 + }, + { + "x": 163.87680089006355, + "y": 4.043511221318647, + "z": 0.0 + }, + { + "x": 162.82589180513997, + "y": 4.04362338407271, + "z": 0.0 + }, + { + "x": 161.77498272021643, + "y": 4.043735546826773, + "z": 0.0 + }, + { + "x": 160.72407363529285, + "y": 4.043847709580836, + "z": 0.0 + }, + { + "x": 159.67316455036928, + "y": 4.0439598723348995, + "z": 0.0 + }, + { + "x": 158.6222554654457, + "y": 4.044072035088963, + "z": 0.0 + }, + { + "x": 157.57134638052213, + "y": 4.044184197843025, + "z": 0.0 + }, + { + "x": 156.52043729559858, + "y": 4.044296360597088, + "z": 0.0 + }, + { + "x": 155.469528210675, + "y": 4.0444085233511515, + "z": 0.0 + }, + { + "x": 154.41861912575143, + "y": 4.044520686105215, + "z": 0.0 + }, + { + "x": 153.36771004082786, + "y": 4.044632848859278, + "z": 0.0 + }, + { + "x": 152.31680095590428, + "y": 4.044745011613341, + "z": 0.0 + }, + { + "x": 151.26589187098074, + "y": 4.044857174367404, + "z": 0.0 + }, + { + "x": 150.21498278605716, + "y": 4.044969337121468, + "z": 0.0 + }, + { + "x": 149.1640737011336, + "y": 4.045081499875531, + "z": 0.0 + }, + { + "x": 148.11316461621, + "y": 4.045193662629594, + "z": 0.0 + }, + { + "x": 147.06225553128647, + "y": 4.0453058253836565, + "z": 0.0 + }, + { + "x": 146.0113464463629, + "y": 4.04541798813772, + "z": 0.0 + }, + { + "x": 144.96043736143932, + "y": 4.045530150891783, + "z": 0.0 + } + ] + }, + { + "id": 153, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 168.08022377122933, + "y": 2.0430625816935297, + "z": 0.0 + }, + { + "x": 167.02931468630575, + "y": 2.043174744447593, + "z": 0.0 + }, + { + "x": 165.97840560138218, + "y": 2.043286907201656, + "z": 0.0 + }, + { + "x": 164.92749651645863, + "y": 2.0433990699557194, + "z": 0.0 + }, + { + "x": 163.87658743153506, + "y": 2.0435112327097826, + "z": 0.0 + }, + { + "x": 162.82567834661148, + "y": 2.0436233954638454, + "z": 0.0 + }, + { + "x": 161.77476926168794, + "y": 2.0437355582179086, + "z": 0.0 + }, + { + "x": 160.72386017676436, + "y": 2.043847720971972, + "z": 0.0 + }, + { + "x": 159.6729510918408, + "y": 2.043959883726035, + "z": 0.0 + }, + { + "x": 158.6220420069172, + "y": 2.0440720464800983, + "z": 0.0 + }, + { + "x": 157.57113292199364, + "y": 2.044184209234161, + "z": 0.0 + }, + { + "x": 156.5202238370701, + "y": 2.0442963719882243, + "z": 0.0 + }, + { + "x": 155.46931475214652, + "y": 2.0444085347422876, + "z": 0.0 + }, + { + "x": 154.41840566722294, + "y": 2.044520697496351, + "z": 0.0 + }, + { + "x": 153.36749658229937, + "y": 2.044632860250414, + "z": 0.0 + }, + { + "x": 152.3165874973758, + "y": 2.044745023004477, + "z": 0.0 + }, + { + "x": 151.26567841245225, + "y": 2.04485718575854, + "z": 0.0 + }, + { + "x": 150.21476932752867, + "y": 2.0449693485126033, + "z": 0.0 + }, + { + "x": 149.1638602426051, + "y": 2.0450815112666665, + "z": 0.0 + }, + { + "x": 148.11295115768152, + "y": 2.0451936740207297, + "z": 0.0 + }, + { + "x": 147.06204207275798, + "y": 2.0453058367747925, + "z": 0.0 + }, + { + "x": 146.0111329878344, + "y": 2.0454179995288557, + "z": 0.0 + }, + { + "x": 144.96022390291083, + "y": 2.045530162282919, + "z": 0.0 + } + ] + }, + { + "id": 156, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 168.08022377122933, + "y": 2.0430625816935297, + "z": 0.0 + }, + { + "x": 167.03215555648666, + "y": 2.043174441243607, + "z": 0.0 + }, + { + "x": 165.98408734174396, + "y": 2.0432863007936906, + "z": 0.0 + }, + { + "x": 164.93601912700126, + "y": 2.043398160343768, + "z": 0.0 + }, + { + "x": 163.8132961915106, + "y": 2.0421241757558866, + "z": 0.0 + }, + { + "x": 162.63554538749878, + "y": 1.9938964723664097, + "z": 0.0 + }, + { + "x": 161.18924212443812, + "y": 1.8012417543674824, + "z": 0.0 + }, + { + "x": 159.94561456457726, + "y": 1.3847026814330234, + "z": 0.0 + }, + { + "x": 158.77286559370378, + "y": 0.8105879617646823, + "z": 0.0 + }, + { + "x": 157.65797728841068, + "y": 0.0634867955019085, + "z": 0.0 + }, + { + "x": 156.6259186782699, + "y": -0.8908683303096292, + "z": 0.0 + }, + { + "x": 155.84478339365012, + "y": -1.903125565677636, + "z": 0.0 + }, + { + "x": 155.18869595419164, + "y": -2.9949613928402687, + "z": 0.0 + }, + { + "x": 154.63742025976387, + "y": -4.235427124345479, + "z": 0.0 + }, + { + "x": 154.28840923354022, + "y": -5.5261527234395516, + "z": 0.0 + }, + { + "x": 154.13300640008842, + "y": -6.7169851437406916, + "z": 0.0 + }, + { + "x": 154.07036312990368, + "y": -7.8733599411963, + "z": 0.0 + }, + { + "x": 154.06908996665567, + "y": -8.921427388606872, + "z": 0.0 + }, + { + "x": 154.06781680340765, + "y": -9.9694948360175, + "z": 0.0 + }, + { + "x": 154.06654364015958, + "y": -11.017562283428129, + "z": 0.0 + } + ] + }, + { + "id": 159, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 158.06654068881966, + "y": -11.022421368830695, + "z": 0.0 + }, + { + "x": 158.0678149424262, + "y": -9.973456338759608, + "z": 0.0 + }, + { + "x": 158.06908919603273, + "y": -8.924491308688518, + "z": 0.0 + }, + { + "x": 158.07036344963927, + "y": -7.87552627861743, + "z": 0.0 + }, + { + "x": 158.12809867190316, + "y": -6.943618912423863, + "z": 0.0 + }, + { + "x": 158.25659403393146, + "y": -6.067036922589827, + "z": 0.0 + }, + { + "x": 158.48513964793386, + "y": -5.323581087143358, + "z": 0.0 + }, + { + "x": 158.8149636244475, + "y": -4.673993089845661, + "z": 0.0 + }, + { + "x": 159.2522144804559, + "y": -3.9833813152728115, + "z": 0.0 + }, + { + "x": 159.76566167994648, + "y": -3.3493286804224094, + "z": 0.0 + }, + { + "x": 160.31090892520865, + "y": -2.8948529552645708, + "z": 0.0 + }, + { + "x": 160.9297561931375, + "y": -2.5283305588664016, + "z": 0.0 + }, + { + "x": 161.63741261604633, + "y": -2.2190824547515597, + "z": 0.0 + }, + { + "x": 162.38408928138767, + "y": -2.0077331023102722, + "z": 0.0 + }, + { + "x": 163.04644832238787, + "y": -1.9759052621068007, + "z": 0.0 + }, + { + "x": 163.94224135337973, + "y": -1.9556456724994467, + "z": 0.0 + }, + { + "x": 164.93289945999143, + "y": -1.956601529478737, + "z": 0.0 + }, + { + "x": 165.98186525805173, + "y": -1.956713484827222, + "z": 0.0 + }, + { + "x": 167.03083105611205, + "y": -1.9568254401757135, + "z": 0.0 + }, + { + "x": 168.07979685417234, + "y": -1.9569373955241982, + "z": 0.0 + } + ] + }, + { + "id": 162, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 144.95979698426007, + "y": -1.9544698149346282, + "z": 0.0 + }, + { + "x": 146.0214304459259, + "y": -1.9545831222935375, + "z": 0.0 + }, + { + "x": 147.08306390759168, + "y": -1.9546964296524467, + "z": 0.0 + }, + { + "x": 148.1446973692575, + "y": -1.9548097370113564, + "z": 0.0 + }, + { + "x": 149.13221486047848, + "y": -1.9919571126258255, + "z": 0.0 + }, + { + "x": 150.0485746823283, + "y": -2.0938982287508408, + "z": 0.0 + }, + { + "x": 150.72698021097798, + "y": -2.3205231525512633, + "z": 0.0 + }, + { + "x": 151.4330047742707, + "y": -2.655849370064583, + "z": 0.0 + }, + { + "x": 152.11319400930614, + "y": -3.086961486560563, + "z": 0.0 + }, + { + "x": 152.7333467146321, + "y": -3.601554210063868, + "z": 0.0 + }, + { + "x": 153.17817257834116, + "y": -4.154904237046136, + "z": 0.0 + }, + { + "x": 153.54286408029446, + "y": -4.790277305034675, + "z": 0.0 + }, + { + "x": 153.83492186558323, + "y": -5.5043381450725715, + "z": 0.0 + }, + { + "x": 154.02291814670855, + "y": -6.254922652969147, + "z": 0.0 + }, + { + "x": 154.0679879528459, + "y": -6.977786630399157, + "z": 0.0 + }, + { + "x": 154.07055697560375, + "y": -7.8592499339717055, + "z": 0.0 + }, + { + "x": 154.0691229240025, + "y": -8.894296914623997, + "z": 0.0 + }, + { + "x": 154.06783328208104, + "y": -9.95592959902604, + "z": 0.0 + }, + { + "x": 154.06654364015958, + "y": -11.017562283428138, + "z": 0.0 + } + ] + }, + { + "id": 165, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 158.06654068881966, + "y": -11.022421368830686, + "z": 0.0 + }, + { + "x": 158.06781259403397, + "y": -9.975389534245748, + "z": 0.0 + }, + { + "x": 158.06908449924828, + "y": -8.928357699660815, + "z": 0.0 + }, + { + "x": 158.0703564044626, + "y": -7.88132586507588, + "z": 0.0 + }, + { + "x": 158.0176154167112, + "y": -6.737594738473985, + "z": 0.0 + }, + { + "x": 157.83319850763831, + "y": -5.492078756666, + "z": 0.0 + }, + { + "x": 157.4608111181561, + "y": -4.216004162987613, + "z": 0.0 + }, + { + "x": 156.909643893399, + "y": -3.004761201228715, + "z": 0.0 + }, + { + "x": 156.18182551246588, + "y": -1.8783912339856603, + "z": 0.0 + }, + { + "x": 155.26022549620535, + "y": -0.8584486238655522, + "z": 0.0 + }, + { + "x": 154.2540718633539, + "y": -0.017169875023373082, + "z": 0.0 + }, + { + "x": 153.17022787065991, + "y": 0.6805135933196025, + "z": 0.0 + }, + { + "x": 152.0142007369439, + "y": 1.2406282699886293, + "z": 0.0 + }, + { + "x": 150.79886301081518, + "y": 1.657965068421548, + "z": 0.0 + }, + { + "x": 149.54106696154486, + "y": 1.926489243790637, + "z": 0.0 + }, + { + "x": 148.24689406538863, + "y": 2.0368968292090828, + "z": 0.0 + }, + { + "x": 147.06031264460555, + "y": 2.0452978532285533, + "z": 0.0 + }, + { + "x": 146.007564063204, + "y": 2.045418380437568, + "z": 0.0 + }, + { + "x": 144.96053146204522, + "y": 2.0455301294573687, + "z": 0.0 + } + ] + }, + { + "id": 168, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.62105848392994, + "y": -326.5890537873581, + "z": 0.0 + }, + { + "x": 100.57439196744656, + "y": -326.5884930880701, + "z": 0.0 + }, + { + "x": 99.52772545096343, + "y": -326.58793238878195, + "z": 0.0 + }, + { + "x": 98.48105893447985, + "y": -326.5873716894939, + "z": 0.0 + }, + { + "x": 97.43439241799669, + "y": -326.5868109902058, + "z": 0.0 + }, + { + "x": 96.38772590151311, + "y": -326.58625029091775, + "z": 0.0 + }, + { + "x": 95.34105938502998, + "y": -326.5856895916296, + "z": 0.0 + }, + { + "x": 94.2943928685465, + "y": -326.5851288923415, + "z": 0.0 + }, + { + "x": 93.24772635206314, + "y": -326.5845681930534, + "z": 0.0 + }, + { + "x": 92.20105983557977, + "y": -326.58400749376534, + "z": 0.0 + }, + { + "x": 91.15439331909641, + "y": -326.58344679447725, + "z": 0.0 + }, + { + "x": 90.10772680261304, + "y": -326.58288609518917, + "z": 0.0 + }, + { + "x": 89.06106028612967, + "y": -326.5823253959011, + "z": 0.0 + }, + { + "x": 88.01439376964632, + "y": -326.581764696613, + "z": 0.0 + }, + { + "x": 86.96772725316295, + "y": -326.5812039973249, + "z": 0.0 + }, + { + "x": 85.92106073667959, + "y": -326.5806432980368, + "z": 0.0 + }, + { + "x": 84.87439422019622, + "y": -326.58008259874873, + "z": 0.0 + }, + { + "x": 83.82772770371285, + "y": -326.57952189946064, + "z": 0.0 + }, + { + "x": 82.7810611872295, + "y": -326.57896120017256, + "z": 0.0 + }, + { + "x": 81.73439467074613, + "y": -326.57840050088447, + "z": 0.0 + }, + { + "x": 80.68772815426277, + "y": -326.5778398015964, + "z": 0.0 + }, + { + "x": 79.6410616377794, + "y": -326.5772791023083, + "z": 0.0 + } + ] + }, + { + "id": 169, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 79.63784743803878, + "y": -332.57727824138493, + "z": 0.0 + }, + { + "x": 80.68451395452215, + "y": -332.577838940673, + "z": 0.0 + }, + { + "x": 81.7311804710055, + "y": -332.5783996399611, + "z": 0.0 + }, + { + "x": 82.77784698748887, + "y": -332.5789603392492, + "z": 0.0 + }, + { + "x": 83.82451350397223, + "y": -332.5795210385373, + "z": 0.0 + }, + { + "x": 84.8711800204556, + "y": -332.58008173782537, + "z": 0.0 + }, + { + "x": 85.91784653693897, + "y": -332.58064243711345, + "z": 0.0 + }, + { + "x": 86.96451305342232, + "y": -332.58120313640154, + "z": 0.0 + }, + { + "x": 88.01117956990569, + "y": -332.5817638356896, + "z": 0.0 + }, + { + "x": 89.05784608638905, + "y": -332.5823245349777, + "z": 0.0 + }, + { + "x": 90.10451260287242, + "y": -332.5828852342658, + "z": 0.0 + }, + { + "x": 91.15117911935579, + "y": -332.5834459335539, + "z": 0.0 + }, + { + "x": 92.19784563583914, + "y": -332.584006632842, + "z": 0.0 + }, + { + "x": 93.24451215232251, + "y": -332.58456733213006, + "z": 0.0 + }, + { + "x": 94.29117866880587, + "y": -332.58512803141815, + "z": 0.0 + }, + { + "x": 95.33784518528903, + "y": -332.58568873070624, + "z": 0.0 + }, + { + "x": 96.38451170177281, + "y": -332.5862494299944, + "z": 0.0 + }, + { + "x": 97.43117821825575, + "y": -332.5868101292824, + "z": 0.0 + }, + { + "x": 98.47784473473953, + "y": -332.58737082857056, + "z": 0.0 + }, + { + "x": 99.52451125122248, + "y": -332.5879315278586, + "z": 0.0 + }, + { + "x": 100.57117776770626, + "y": -332.58849222714673, + "z": 0.0 + }, + { + "x": 101.61784428418963, + "y": -332.58905292643476, + "z": 0.0 + } + ] + }, + { + "id": 171, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 79.63891883795232, + "y": -330.5772785283594, + "z": 0.0 + }, + { + "x": 80.68558535443569, + "y": -330.5778392276475, + "z": 0.0 + }, + { + "x": 81.73225187091904, + "y": -330.57839992693556, + "z": 0.0 + }, + { + "x": 82.77891838740241, + "y": -330.57896062622365, + "z": 0.0 + }, + { + "x": 83.82558490388577, + "y": -330.57952132551173, + "z": 0.0 + }, + { + "x": 84.87225142036914, + "y": -330.5800820247998, + "z": 0.0 + }, + { + "x": 85.91891793685251, + "y": -330.5806427240879, + "z": 0.0 + }, + { + "x": 86.96558445333586, + "y": -330.581203423376, + "z": 0.0 + }, + { + "x": 88.01225096981923, + "y": -330.5817641226641, + "z": 0.0 + }, + { + "x": 89.05891748630259, + "y": -330.58232482195217, + "z": 0.0 + }, + { + "x": 90.10558400278596, + "y": -330.58288552124026, + "z": 0.0 + }, + { + "x": 91.15225051926933, + "y": -330.58344622052834, + "z": 0.0 + }, + { + "x": 92.19891703575269, + "y": -330.58400691981643, + "z": 0.0 + }, + { + "x": 93.24558355223606, + "y": -330.5845676191045, + "z": 0.0 + }, + { + "x": 94.29225006871941, + "y": -330.5851283183926, + "z": 0.0 + }, + { + "x": 95.33891658520267, + "y": -330.5856890176807, + "z": 0.0 + }, + { + "x": 96.38558310168625, + "y": -330.58624971696884, + "z": 0.0 + }, + { + "x": 97.43224961816941, + "y": -330.58681041625687, + "z": 0.0 + }, + { + "x": 98.47891613465296, + "y": -330.587371115545, + "z": 0.0 + }, + { + "x": 99.52558265113612, + "y": -330.58793181483304, + "z": 0.0 + }, + { + "x": 100.5722491676197, + "y": -330.5884925141212, + "z": 0.0 + }, + { + "x": 101.61891568410306, + "y": -330.5890532134092, + "z": 0.0 + } + ] + }, + { + "id": 174, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 79.73656143631914, + "y": -330.57494595568255, + "z": 0.0 + }, + { + "x": 81.01078991817974, + "y": -330.5043945565061, + "z": 0.0 + }, + { + "x": 82.29098389026342, + "y": -330.30913395288894, + "z": 0.0 + }, + { + "x": 83.54349472443971, + "y": -329.9812404475023, + "z": 0.0 + }, + { + "x": 84.75481203058489, + "y": -329.52429641276865, + "z": 0.0 + }, + { + "x": 85.91185841679498, + "y": -328.9432312187754, + "z": 0.0 + }, + { + "x": 87.0021200663951, + "y": -328.24427205410035, + "z": 0.0 + }, + { + "x": 88.01341728803806, + "y": -327.435227246739, + "z": 0.0 + }, + { + "x": 88.93900969128816, + "y": -326.5202515246551, + "z": 0.0 + }, + { + "x": 89.75107560959849, + "y": -325.5266666688634, + "z": 0.0 + }, + { + "x": 90.45728298426096, + "y": -324.46701305568627, + "z": 0.0 + }, + { + "x": 91.06141501298855, + "y": -323.34667243019805, + "z": 0.0 + }, + { + "x": 91.5584014223586, + "y": -322.1752631134002, + "z": 0.0 + }, + { + "x": 91.94405223561498, + "y": -320.96281181528263, + "z": 0.0 + }, + { + "x": 92.21508908431153, + "y": -319.7196762819973, + "z": 0.0 + }, + { + "x": 92.3684513758152, + "y": -318.47466801576627, + "z": 0.0 + }, + { + "x": 92.41629889872712, + "y": -317.38843814933017, + "z": 0.0 + } + ] + }, + { + "id": 177, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.42017200487301, + "y": -317.2120253308748, + "z": 0.0 + }, + { + "x": 88.38681659765516, + "y": -318.1305891502018, + "z": 0.0 + }, + { + "x": 88.28041774537446, + "y": -319.0242218600872, + "z": 0.0 + }, + { + "x": 88.09128024053727, + "y": -319.90385005448945, + "z": 0.0 + }, + { + "x": 87.82103436358267, + "y": -320.7620070632413, + "z": 0.0 + }, + { + "x": 87.47196227942555, + "y": -321.5913992565817, + "z": 0.0 + }, + { + "x": 87.04698127144358, + "y": -322.3849604018104, + "z": 0.0 + }, + { + "x": 86.55563133951622, + "y": -323.1286292338267, + "z": 0.0 + }, + { + "x": 86.00674941604638, + "y": -323.8080489871936, + "z": 0.0 + }, + { + "x": 85.38404558626888, + "y": -324.4280697582773, + "z": 0.0 + }, + { + "x": 84.70104078243725, + "y": -324.97909432431106, + "z": 0.0 + }, + { + "x": 83.96458694724586, + "y": -325.4554743391213, + "z": 0.0 + }, + { + "x": 83.18264360482415, + "y": -325.8521635843414, + "z": 0.0 + }, + { + "x": 82.36363121688173, + "y": -326.16493986710304, + "z": 0.0 + }, + { + "x": 81.51634906459863, + "y": -326.3904453991263, + "z": 0.0 + }, + { + "x": 80.62976620677989, + "y": -326.5270911532118, + "z": 0.0 + }, + { + "x": 79.54380142545668, + "y": -326.57961414999033, + "z": 0.0 + } + ] + }, + { + "id": 180, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.71306615229395, + "y": -326.59122060270465, + "z": 0.0 + }, + { + "x": 100.67112994207008, + "y": -326.54267677716007, + "z": 0.0 + }, + { + "x": 99.80014390413483, + "y": -326.4238790735774, + "z": 0.0 + }, + { + "x": 98.96460511255336, + "y": -326.22842132801753, + "z": 0.0 + }, + { + "x": 98.15019178427023, + "y": -325.95695891031085, + "z": 0.0 + }, + { + "x": 97.3634224837516, + "y": -325.61153041774634, + "z": 0.0 + }, + { + "x": 96.64176504893888, + "y": -325.1957672182933, + "z": 0.0 + }, + { + "x": 95.955335319999, + "y": -324.70501710099404, + "z": 0.0 + }, + { + "x": 95.30486246131848, + "y": -324.14508005625663, + "z": 0.0 + }, + { + "x": 94.70767658356107, + "y": -323.5275983878604, + "z": 0.0 + }, + { + "x": 94.16755299214661, + "y": -322.857239798076, + "z": 0.0 + }, + { + "x": 93.68910215437192, + "y": -322.13971373665186, + "z": 0.0 + }, + { + "x": 93.28929316726665, + "y": -321.38958509793605, + "z": 0.0 + }, + { + "x": 92.97766384421965, + "y": -320.61591701150184, + "z": 0.0 + }, + { + "x": 92.72595349426187, + "y": -319.7915731229868, + "z": 0.0 + }, + { + "x": 92.549602496984, + "y": -318.9482323186387, + "z": 0.0 + }, + { + "x": 92.44993108110532, + "y": -318.0923460328693, + "z": 0.0 + }, + { + "x": 92.41617104017382, + "y": -317.2095624415087, + "z": 0.0 + } + ] + }, + { + "id": 183, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.42029960539836, + "y": -317.390819183904, + "z": 0.0 + }, + { + "x": 88.48128925622473, + "y": -318.5919860417332, + "z": 0.0 + }, + { + "x": 88.64210023950311, + "y": -319.8033681384539, + "z": 0.0 + }, + { + "x": 88.91137574924393, + "y": -320.9952761314796, + "z": 0.0 + }, + { + "x": 89.28707508064065, + "y": -322.1584901807639, + "z": 0.0 + }, + { + "x": 89.78011362258745, + "y": -323.3093921488408, + "z": 0.0 + }, + { + "x": 90.38193707085415, + "y": -324.38967433746575, + "z": 0.0 + }, + { + "x": 91.07657134093074, + "y": -325.3960522968648, + "z": 0.0 + }, + { + "x": 91.8592434071351, + "y": -326.3358427502742, + "z": 0.0 + }, + { + "x": 92.72486614969797, + "y": -327.20179380010893, + "z": 0.0 + }, + { + "x": 93.66694103194341, + "y": -327.98574947899647, + "z": 0.0 + }, + { + "x": 94.69099899029271, + "y": -328.68783136059267, + "z": 0.0 + }, + { + "x": 95.79103375491582, + "y": -329.2894683383839, + "z": 0.0 + }, + { + "x": 96.92223094720762, + "y": -329.763771091042, + "z": 0.0 + }, + { + "x": 98.09135668384951, + "y": -330.1319091695889, + "z": 0.0 + }, + { + "x": 99.28896330206895, + "y": -330.39106296260303, + "z": 0.0 + }, + { + "x": 100.48449007762834, + "y": -330.5383155470995, + "z": 0.0 + }, + { + "x": 101.52642557922788, + "y": -330.58688396887624, + "z": 0.0 + } + ] + }, + { + "id": 186, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 79.38066375782434, + "y": -1.9577239538036761, + "z": 0.0 + }, + { + "x": 80.44020915609882, + "y": -1.9573786375497457, + "z": 0.0 + }, + { + "x": 81.4997545543733, + "y": -1.9570333212958153, + "z": 0.0 + }, + { + "x": 82.55929995264778, + "y": -1.956688005041885, + "z": 0.0 + }, + { + "x": 83.61884535092224, + "y": -1.9563426887879551, + "z": 0.0 + }, + { + "x": 84.67839074919672, + "y": -1.9559973725340245, + "z": 0.0 + }, + { + "x": 85.7379361474712, + "y": -1.955652056280094, + "z": 0.0 + }, + { + "x": 86.79748154574567, + "y": -1.955306740026164, + "z": 0.0 + }, + { + "x": 87.85702694402015, + "y": -1.9549614237722337, + "z": 0.0 + }, + { + "x": 88.91657234229463, + "y": -1.9546161075183033, + "z": 0.0 + }, + { + "x": 89.97611774056911, + "y": -1.9542707912643729, + "z": 0.0 + }, + { + "x": 91.03566313884357, + "y": -1.9539254750104427, + "z": 0.0 + }, + { + "x": 92.09520853711805, + "y": -1.9535801587565125, + "z": 0.0 + }, + { + "x": 93.15475393539253, + "y": -1.953234842502582, + "z": 0.0 + }, + { + "x": 94.214299333667, + "y": -1.9528895262486519, + "z": 0.0 + }, + { + "x": 95.27384473194148, + "y": -1.9525442099947212, + "z": 0.0 + }, + { + "x": 96.33339013021596, + "y": -1.9521988937407913, + "z": 0.0 + }, + { + "x": 97.39293552849043, + "y": -1.951853577486861, + "z": 0.0 + }, + { + "x": 98.4524809267649, + "y": -1.9515082612329304, + "z": 0.0 + }, + { + "x": 99.51202632503939, + "y": -1.951162944979, + "z": 0.0 + }, + { + "x": 100.57157172331387, + "y": -1.95081762872507, + "z": 0.0 + }, + { + "x": 101.63111712158833, + "y": -1.9504723124711398, + "z": 0.0 + }, + { + "x": 102.69066251986281, + "y": -1.9501269962172092, + "z": 0.0 + } + ] + }, + { + "id": 187, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 102.68870706102459, + "y": 4.049872685131176, + "z": 0.0 + }, + { + "x": 101.6291616627501, + "y": 4.049527368877246, + "z": 0.0 + }, + { + "x": 100.56961626447564, + "y": 4.0491820526233155, + "z": 0.0 + }, + { + "x": 99.51007086620116, + "y": 4.048836736369386, + "z": 0.0 + }, + { + "x": 98.45052546792668, + "y": 4.048491420115455, + "z": 0.0 + }, + { + "x": 97.3909800696522, + "y": 4.048146103861526, + "z": 0.0 + }, + { + "x": 96.33143467137774, + "y": 4.047800787607595, + "z": 0.0 + }, + { + "x": 95.27188927310326, + "y": 4.047455471353664, + "z": 0.0 + }, + { + "x": 94.21234387482878, + "y": 4.047110155099734, + "z": 0.0 + }, + { + "x": 93.15279847655431, + "y": 4.046764838845804, + "z": 0.0 + }, + { + "x": 92.09325307827983, + "y": 4.046419522591873, + "z": 0.0 + }, + { + "x": 91.03370768000535, + "y": 4.046074206337943, + "z": 0.0 + }, + { + "x": 89.97416228173088, + "y": 4.045728890084012, + "z": 0.0 + }, + { + "x": 88.9146168834564, + "y": 4.045383573830082, + "z": 0.0 + }, + { + "x": 87.85507148518192, + "y": 4.045038257576152, + "z": 0.0 + }, + { + "x": 86.79552608690744, + "y": 4.044692941322222, + "z": 0.0 + }, + { + "x": 85.73598068863298, + "y": 4.0443476250682915, + "z": 0.0 + }, + { + "x": 84.6764352903585, + "y": 4.0440023088143615, + "z": 0.0 + }, + { + "x": 83.61688989208402, + "y": 4.0436569925604315, + "z": 0.0 + }, + { + "x": 82.55734449380955, + "y": 4.043311676306501, + "z": 0.0 + }, + { + "x": 81.49779909553507, + "y": 4.04296636005257, + "z": 0.0 + }, + { + "x": 80.43825369726059, + "y": 4.04262104379864, + "z": 0.0 + }, + { + "x": 79.37870829898611, + "y": 4.04227572754471, + "z": 0.0 + } + ] + }, + { + "id": 189, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 102.68935888063733, + "y": 2.049872791348381, + "z": 0.0 + }, + { + "x": 101.62981348236285, + "y": 2.049527475094451, + "z": 0.0 + }, + { + "x": 100.57026808408838, + "y": 2.0491821588405204, + "z": 0.0 + }, + { + "x": 99.5107226858139, + "y": 2.0488368425865904, + "z": 0.0 + }, + { + "x": 98.45117728753942, + "y": 2.0484915263326595, + "z": 0.0 + }, + { + "x": 97.39163188926494, + "y": 2.04814621007873, + "z": 0.0 + }, + { + "x": 96.33208649099048, + "y": 2.0478008938247996, + "z": 0.0 + }, + { + "x": 95.272541092716, + "y": 2.0474555775708687, + "z": 0.0 + }, + { + "x": 94.21299569444152, + "y": 2.0471102613169387, + "z": 0.0 + }, + { + "x": 93.15345029616705, + "y": 2.0467649450630088, + "z": 0.0 + }, + { + "x": 92.09390489789257, + "y": 2.046419628809078, + "z": 0.0 + }, + { + "x": 91.03435949961809, + "y": 2.046074312555148, + "z": 0.0 + }, + { + "x": 89.97481410134363, + "y": 2.045728996301217, + "z": 0.0 + }, + { + "x": 88.91526870306915, + "y": 2.045383680047287, + "z": 0.0 + }, + { + "x": 87.85572330479467, + "y": 2.045038363793357, + "z": 0.0 + }, + { + "x": 86.79617790652019, + "y": 2.0446930475394267, + "z": 0.0 + }, + { + "x": 85.73663250824572, + "y": 2.0443477312854963, + "z": 0.0 + }, + { + "x": 84.67708710997124, + "y": 2.0440024150315663, + "z": 0.0 + }, + { + "x": 83.61754171169676, + "y": 2.043657098777636, + "z": 0.0 + }, + { + "x": 82.5579963134223, + "y": 2.0433117825237055, + "z": 0.0 + }, + { + "x": 81.49845091514781, + "y": 2.0429664662697746, + "z": 0.0 + }, + { + "x": 80.43890551687333, + "y": 2.0426211500158447, + "z": 0.0 + }, + { + "x": 79.37936011859885, + "y": 2.0422758337619147, + "z": 0.0 + } + ] + }, + { + "id": 192, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 102.68935888063733, + "y": 2.049872791348381, + "z": 0.0 + }, + { + "x": 101.65832930984283, + "y": 2.0495367686827572, + "z": 0.0 + }, + { + "x": 100.62729973904831, + "y": 2.0492007460171333, + "z": 0.0 + }, + { + "x": 99.5962701682538, + "y": 2.0488647233515085, + "z": 0.0 + }, + { + "x": 98.5652405974593, + "y": 2.0485287006858846, + "z": 0.0 + }, + { + "x": 97.38922935584577, + "y": 2.0428834586190012, + "z": 0.0 + }, + { + "x": 96.14515386216075, + "y": 1.9400805582782434, + "z": 0.0 + }, + { + "x": 94.8730511827662, + "y": 1.6869661986496554, + "z": 0.0 + }, + { + "x": 93.5702008365092, + "y": 1.22505021256522, + "z": 0.0 + }, + { + "x": 92.40006126093198, + "y": 0.5649586460659338, + "z": 0.0 + }, + { + "x": 91.38046677094792, + "y": -0.23641945572233425, + "z": 0.0 + }, + { + "x": 90.48038520357372, + "y": -1.1647799831877466, + "z": 0.0 + }, + { + "x": 89.68231525354273, + "y": -2.2587983061839196, + "z": 0.0 + }, + { + "x": 89.07111740293382, + "y": -3.4722343436080396, + "z": 0.0 + }, + { + "x": 88.67845588237262, + "y": -4.704909506053416, + "z": 0.0 + }, + { + "x": 88.45581325061647, + "y": -5.935592458876994, + "z": 0.0 + }, + { + "x": 88.37108444520378, + "y": -7.116185443357953, + "z": 0.0 + }, + { + "x": 88.37124117478857, + "y": -8.14721505699659, + "z": 0.0 + }, + { + "x": 88.37139790437337, + "y": -9.17824467063517, + "z": 0.0 + }, + { + "x": 88.37155463395816, + "y": -10.20927428427378, + "z": 0.0 + }, + { + "x": 88.37171136354294, + "y": -11.240303897912387, + "z": 0.0 + } + ] + }, + { + "id": 195, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.37171131732724, + "y": -11.239695847160373, + "z": 0.0 + }, + { + "x": 92.37155114114427, + "y": -10.185993138367436, + "z": 0.0 + }, + { + "x": 92.3713909649613, + "y": -9.1322904295745, + "z": 0.0 + }, + { + "x": 92.39046094699293, + "y": -8.116508097385378, + "z": 0.0 + }, + { + "x": 92.46620178948825, + "y": -7.18354460785133, + "z": 0.0 + }, + { + "x": 92.6608232529945, + "y": -6.463353461590874, + "z": 0.0 + }, + { + "x": 92.95346991422174, + "y": -5.700548846361753, + "z": 0.0 + }, + { + "x": 93.33315107195224, + "y": -4.977802134021738, + "z": 0.0 + }, + { + "x": 93.79523274275404, + "y": -4.303944248276251, + "z": 0.0 + }, + { + "x": 94.33434349854011, + "y": -3.687590563228103, + "z": 0.0 + }, + { + "x": 94.91530511508498, + "y": -3.212018734025133, + "z": 0.0 + }, + { + "x": 95.58835243785953, + "y": -2.7800965407806846, + "z": 0.0 + }, + { + "x": 96.31607581087863, + "y": -2.4276423861668794, + "z": 0.0 + }, + { + "x": 97.08151497626868, + "y": -2.1656349088898588, + "z": 0.0 + }, + { + "x": 97.87475166175554, + "y": -1.9978261145678717, + "z": 0.0 + }, + { + "x": 98.63542969358136, + "y": -1.9584385980081804, + "z": 0.0 + }, + { + "x": 99.55485470219124, + "y": -1.950988954958867, + "z": 0.0 + }, + { + "x": 100.58325718984946, + "y": -1.950813820316731, + "z": 0.0 + }, + { + "x": 101.63695985485614, + "y": -1.9504704082669708, + "z": 0.0 + }, + { + "x": 102.69066251986281, + "y": -1.9501269962172094, + "z": 0.0 + } + ] + }, + { + "id": 198, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 79.38066375454355, + "y": -1.9577239538047368, + "z": 0.0 + }, + { + "x": 80.4388549865507, + "y": -1.9573790788868963, + "z": 0.0 + }, + { + "x": 81.49704621855781, + "y": -1.9570342039690558, + "z": 0.0 + }, + { + "x": 82.55091250262038, + "y": -1.9589222555269905, + "z": 0.0 + }, + { + "x": 83.43200894284189, + "y": -2.0445247038874914, + "z": 0.0 + }, + { + "x": 84.24925844510989, + "y": -2.220497821692533, + "z": 0.0 + }, + { + "x": 85.05338347029272, + "y": -2.4832087265729244, + "z": 0.0 + }, + { + "x": 85.74791954430364, + "y": -2.834730304320984, + "z": 0.0 + }, + { + "x": 86.3801868279096, + "y": -3.2781724066689217, + "z": 0.0 + }, + { + "x": 87.00721396453173, + "y": -3.82053723236728, + "z": 0.0 + }, + { + "x": 87.55047028720435, + "y": -4.425529897110538, + "z": 0.0 + }, + { + "x": 87.83924838450032, + "y": -4.996065916476047, + "z": 0.0 + }, + { + "x": 88.12498548627755, + "y": -5.719537641098558, + "z": 0.0 + }, + { + "x": 88.31493852913333, + "y": -6.482889975872659, + "z": 0.0 + }, + { + "x": 88.36973463992086, + "y": -7.223142553947042, + "z": 0.0 + }, + { + "x": 88.37138235691452, + "y": -8.090212015485726, + "z": 0.0 + }, + { + "x": 88.37138964653866, + "y": -9.123921345952521, + "z": 0.0 + }, + { + "x": 88.3715505050408, + "y": -10.182112621932458, + "z": 0.0 + }, + { + "x": 88.37171136354294, + "y": -11.240303897912394, + "z": 0.0 + } + ] + }, + { + "id": 201, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.37171131732723, + "y": -11.239695847160402, + "z": 0.0 + }, + { + "x": 92.37155054141259, + "y": -10.182047864601843, + "z": 0.0 + }, + { + "x": 92.37138976549794, + "y": -9.124399882043246, + "z": 0.0 + }, + { + "x": 92.37122898958329, + "y": -8.066751899484675, + "z": 0.0 + }, + { + "x": 92.32627910480849, + "y": -6.929613012356661, + "z": 0.0 + }, + { + "x": 92.20804645699295, + "y": -5.75344116010853, + "z": 0.0 + }, + { + "x": 91.82483457880105, + "y": -4.366599844967185, + "z": 0.0 + }, + { + "x": 91.30775741146184, + "y": -3.1709171435836234, + "z": 0.0 + }, + { + "x": 90.65535366129677, + "y": -2.054612645824859, + "z": 0.0 + }, + { + "x": 89.81302843243145, + "y": -0.9956783172598629, + "z": 0.0 + }, + { + "x": 88.78088189318728, + "y": -0.06351850988216023, + "z": 0.0 + }, + { + "x": 87.7192830400309, + "y": 0.671605878433852, + "z": 0.0 + }, + { + "x": 86.57424660191658, + "y": 1.2764731160905984, + "z": 0.0 + }, + { + "x": 85.22679612447331, + "y": 1.7184868629338879, + "z": 0.0 + }, + { + "x": 83.92383958195423, + "y": 1.9457360202381637, + "z": 0.0 + }, + { + "x": 82.69024866530056, + "y": 2.0386125518403038, + "z": 0.0 + }, + { + "x": 81.49495166259359, + "y": 2.0429653258289995, + "z": 0.0 + }, + { + "x": 80.43730372398524, + "y": 2.042620627975815, + "z": 0.0 + }, + { + "x": 79.37965578537688, + "y": 2.0422759301226296, + "z": 0.0 + } + ] + }, + { + "id": 204, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 347.7299857691685, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 346.65641965126474, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 345.5941204708103, + "y": -326.6040779914478, + "z": 0.0 + }, + { + "x": 344.648505329607, + "y": -326.5354346959316, + "z": 0.0 + }, + { + "x": 343.85970768027084, + "y": -326.3567253110556, + "z": 0.0 + }, + { + "x": 343.0776092882985, + "y": -326.0795876674798, + "z": 0.0 + }, + { + "x": 342.3354907746227, + "y": -325.70986091698387, + "z": 0.0 + }, + { + "x": 341.64405761177926, + "y": -325.2530356144291, + "z": 0.0 + }, + { + "x": 341.01329565219686, + "y": -324.71580515294767, + "z": 0.0 + }, + { + "x": 340.44702812359935, + "y": -324.10413828561445, + "z": 0.0 + }, + { + "x": 339.94535168652635, + "y": -323.423605878501, + "z": 0.0 + }, + { + "x": 339.51734115447863, + "y": -322.6864068001786, + "z": 0.0 + }, + { + "x": 339.1727150952545, + "y": -321.9059384428799, + "z": 0.0 + }, + { + "x": 338.9277300987599, + "y": -321.1031441814349, + "z": 0.0 + }, + { + "x": 338.7795076133734, + "y": -320.27874964250066, + "z": 0.0 + }, + { + "x": 338.71982898421777, + "y": -319.42841040751057, + "z": 0.0 + }, + { + "x": 338.70720255662206, + "y": -318.48770881866005, + "z": 0.0 + }, + { + "x": 338.70817756430256, + "y": -317.45151167767597, + "z": 0.0 + } + ] + }, + { + "id": 207, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.70817851236086, + "y": -317.44873405142624, + "z": 0.0 + }, + { + "x": 334.7075762305461, + "y": -318.5436553177717, + "z": 0.0 + }, + { + "x": 334.7195177420323, + "y": -319.7406104743982, + "z": 0.0 + }, + { + "x": 334.82049530304073, + "y": -321.0539438473358, + "z": 0.0 + }, + { + "x": 335.0761563021257, + "y": -322.3434839951809, + "z": 0.0 + }, + { + "x": 335.47768807384915, + "y": -323.58519337120737, + "z": 0.0 + }, + { + "x": 336.01658897447584, + "y": -324.77308093046884, + "z": 0.0 + }, + { + "x": 336.68589773112495, + "y": -325.8921318270369, + "z": 0.0 + }, + { + "x": 337.49447840776554, + "y": -326.94649206913607, + "z": 0.0 + }, + { + "x": 338.40328770494233, + "y": -327.87004125241145, + "z": 0.0 + }, + { + "x": 339.4213558134485, + "y": -328.69749319947175, + "z": 0.0 + }, + { + "x": 340.60990333464713, + "y": -329.43703225907564, + "z": 0.0 + }, + { + "x": 341.8285278971893, + "y": -329.9562320320991, + "z": 0.0 + }, + { + "x": 343.0891858483319, + "y": -330.3197665843113, + "z": 0.0 + }, + { + "x": 344.35589618935245, + "y": -330.53065971257524, + "z": 0.0 + }, + { + "x": 345.57053106457437, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 346.650082156251, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 347.7296332479276, + "y": -330.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 208, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.71998576749917, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 326.7680810055944, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 327.81617624368965, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 328.8642714817849, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 329.91236671988014, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 330.9604619579754, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 332.0085571960706, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 333.05665243416587, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 334.1047476722611, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 335.15284291035636, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 336.2009381484516, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 337.2490333865468, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 338.297128624642, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 339.34522386273727, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 340.3933191008325, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 341.44141433892776, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 342.489509577023, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 343.53760481511824, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 344.5857000532135, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 345.63379529130873, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 346.681890529404, + "y": -332.6099853515625, + "z": 0.0 + }, + { + "x": 347.7299857674992, + "y": -332.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 210, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.71998576749917, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 326.7680810055944, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 327.81617624368965, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 328.8642714817849, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 329.91236671988014, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 330.9604619579754, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 332.0085571960706, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 333.05665243416587, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 334.1047476722611, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 335.15284291035636, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 336.2009381484516, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 337.2490333865468, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 338.297128624642, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 339.34522386273727, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 340.3933191008325, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 341.44141433892776, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 342.489509577023, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 343.53760481511824, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 344.5857000532135, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 345.63379529130873, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 346.681890529404, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 347.7299857674992, + "y": -330.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 213, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 347.7299857674992, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 346.681890529404, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 345.63379529130873, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 344.5857000532135, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 343.53760481511824, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 342.489509577023, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 341.44141433892776, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 340.3933191008325, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 339.34522386273727, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 338.297128624642, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 337.2490333865468, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 336.2009381484516, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 335.15284291035636, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 334.1047476722611, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 333.05665243416587, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 332.0085571960706, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 330.9604619579754, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 329.91236671988014, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 328.8642714817849, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 327.81617624368965, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 326.7680810055944, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 325.71998576749917, + "y": -326.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 216, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.71998576749917, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 326.75488808239726, + "y": -330.6099853515625, + "z": 0.0 + }, + { + "x": 327.8118675524266, + "y": -330.60986349765574, + "z": 0.0 + }, + { + "x": 328.9873159954276, + "y": -330.59190530437644, + "z": 0.0 + }, + { + "x": 330.30964508378855, + "y": -330.46197316532766, + "z": 0.0 + }, + { + "x": 331.556025593915, + "y": -330.15541174962823, + "z": 0.0 + }, + { + "x": 332.7525040323843, + "y": -329.69632142549915, + "z": 0.0 + }, + { + "x": 333.88208912167863, + "y": -329.0916278346175, + "z": 0.0 + }, + { + "x": 334.9359359378983, + "y": -328.34315517349506, + "z": 0.0 + }, + { + "x": 335.913383204102, + "y": -327.4289099766568, + "z": 0.0 + }, + { + "x": 336.7008226662732, + "y": -326.4421863771143, + "z": 0.0 + }, + { + "x": 337.3694306959815, + "y": -325.3743482667254, + "z": 0.0 + }, + { + "x": 337.9121140564023, + "y": -324.2379684904723, + "z": 0.0 + }, + { + "x": 338.3230020237536, + "y": -323.0439917139242, + "z": 0.0 + }, + { + "x": 338.60091078909, + "y": -321.7401436542182, + "z": 0.0 + }, + { + "x": 338.6862609570376, + "y": -320.5934350532335, + "z": 0.0 + }, + { + "x": 338.7069274034519, + "y": -319.5212766008853, + "z": 0.0 + }, + { + "x": 338.70755249575484, + "y": -318.4863744747684, + "z": 0.0 + }, + { + "x": 338.70817758805777, + "y": -317.45147234865163, + "z": 0.0 + } + ] + }, + { + "id": 219, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.70817851236086, + "y": -317.44873405142624, + "z": 0.0 + }, + { + "x": 334.70752421679254, + "y": -318.53198507169066, + "z": 0.0 + }, + { + "x": 334.7108944649408, + "y": -319.48962363322096, + "z": 0.0 + }, + { + "x": 334.6645566804101, + "y": -320.3735075264175, + "z": 0.0 + }, + { + "x": 334.53815197133923, + "y": -321.22153723351005, + "z": 0.0 + }, + { + "x": 334.31797944922545, + "y": -322.0503964585138, + "z": 0.0 + }, + { + "x": 334.0371918614085, + "y": -322.7960531500664, + "z": 0.0 + }, + { + "x": 333.62261883383894, + "y": -323.5325454764164, + "z": 0.0 + }, + { + "x": 333.11819234412405, + "y": -324.21935487342756, + "z": 0.0 + }, + { + "x": 332.54625711396557, + "y": -324.83716484905676, + "z": 0.0 + }, + { + "x": 331.9093734688107, + "y": -325.3796459158115, + "z": 0.0 + }, + { + "x": 331.2743947648346, + "y": -325.81212613380285, + "z": 0.0 + }, + { + "x": 330.51884277883306, + "y": -326.1556341844773, + "z": 0.0 + }, + { + "x": 329.7185175964627, + "y": -326.4014722846573, + "z": 0.0 + }, + { + "x": 328.849860451563, + "y": -326.5499418368728, + "z": 0.0 + }, + { + "x": 327.8864882032299, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 326.8032369853645, + "y": -326.6099853515625, + "z": 0.0 + }, + { + "x": 325.71998576749917, + "y": -326.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 220, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.37701814958909, + "y": -46.15060751994074, + "z": 0.0 + }, + { + "x": 86.37717740097652, + "y": -47.19822655545584, + "z": 0.0 + }, + { + "x": 86.377336652364, + "y": -48.24584559097058, + "z": 0.0 + }, + { + "x": 86.37749590375142, + "y": -49.29346462648557, + "z": 0.0 + }, + { + "x": 86.37765515513887, + "y": -50.34108366200046, + "z": 0.0 + }, + { + "x": 86.3778144065263, + "y": -51.38870269751539, + "z": 0.0 + }, + { + "x": 86.37797365791373, + "y": -52.436321733030326, + "z": 0.0 + }, + { + "x": 86.37813290930116, + "y": -53.48394076854531, + "z": 0.0 + }, + { + "x": 86.3782921606886, + "y": -54.531559804060194, + "z": 0.0 + }, + { + "x": 86.37845141207603, + "y": -55.579178839575135, + "z": 0.0 + }, + { + "x": 86.37861066346346, + "y": -56.62679787509001, + "z": 0.0 + }, + { + "x": 86.37876991485088, + "y": -57.67441691060498, + "z": 0.0 + }, + { + "x": 86.37892916623831, + "y": -58.722035946119966, + "z": 0.0 + }, + { + "x": 86.37908841762575, + "y": -59.76965498163485, + "z": 0.0 + }, + { + "x": 86.37924766901318, + "y": -60.817274017149835, + "z": 0.0 + }, + { + "x": 86.37940692040063, + "y": -61.86489305266472, + "z": 0.0 + }, + { + "x": 86.37956617178806, + "y": -62.91251208817966, + "z": 0.0 + }, + { + "x": 86.37972542317549, + "y": -63.960131123694644, + "z": 0.0 + }, + { + "x": 86.37988467456293, + "y": -65.00775015920952, + "z": 0.0 + }, + { + "x": 86.38004392595036, + "y": -66.05536919472452, + "z": 0.0 + }, + { + "x": 86.3802031773378, + "y": -67.10298823023938, + "z": 0.0 + }, + { + "x": 86.38036242872523, + "y": -68.15060726575432, + "z": 0.0 + } + ] + }, + { + "id": 222, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.37701812648123, + "y": -46.15030349456474, + "z": 0.0 + }, + { + "x": 88.37717737786866, + "y": -47.19792253007975, + "z": 0.0 + }, + { + "x": 88.37733662925613, + "y": -48.24554156559458, + "z": 0.0 + }, + { + "x": 88.37749588064356, + "y": -49.29316060110955, + "z": 0.0 + }, + { + "x": 88.37765513203101, + "y": -50.340779636624454, + "z": 0.0 + }, + { + "x": 88.37781438341844, + "y": -51.38839867213939, + "z": 0.0 + }, + { + "x": 88.37797363480587, + "y": -52.43601770765432, + "z": 0.0 + }, + { + "x": 88.3781328861933, + "y": -53.483636743169285, + "z": 0.0 + }, + { + "x": 88.37829213758074, + "y": -54.53125577868419, + "z": 0.0 + }, + { + "x": 88.37845138896817, + "y": -55.57887481419913, + "z": 0.0 + }, + { + "x": 88.3786106403556, + "y": -56.62649384971404, + "z": 0.0 + }, + { + "x": 88.37876989174302, + "y": -57.67411288522898, + "z": 0.0 + }, + { + "x": 88.37892914313045, + "y": -58.721731920743935, + "z": 0.0 + }, + { + "x": 88.3790883945179, + "y": -59.76935095625885, + "z": 0.0 + }, + { + "x": 88.37924764590532, + "y": -60.8169699917738, + "z": 0.0 + }, + { + "x": 88.37940689729277, + "y": -61.864589027288716, + "z": 0.0 + }, + { + "x": 88.3795661486802, + "y": -62.91220806280366, + "z": 0.0 + }, + { + "x": 88.37972540006763, + "y": -63.95982709831861, + "z": 0.0 + }, + { + "x": 88.37988465145507, + "y": -65.00744613383353, + "z": 0.0 + }, + { + "x": 88.3800439028425, + "y": -66.0550651693485, + "z": 0.0 + }, + { + "x": 88.38020315422995, + "y": -67.10268420486338, + "z": 0.0 + }, + { + "x": 88.38036240561738, + "y": -68.15030324037832, + "z": 0.0 + } + ] + }, + { + "id": 225, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.38036235940166, + "y": -68.1496951896263, + "z": 0.0 + }, + { + "x": 92.38020310801423, + "y": -67.10207615411136, + "z": 0.0 + }, + { + "x": 92.38004385662678, + "y": -66.05445711859642, + "z": 0.0 + }, + { + "x": 92.37988460523935, + "y": -65.0068380830815, + "z": 0.0 + }, + { + "x": 92.37972535385191, + "y": -63.95921904756656, + "z": 0.0 + }, + { + "x": 92.37956610246448, + "y": -62.91160001205165, + "z": 0.0 + }, + { + "x": 92.37940685107705, + "y": -61.86398097653671, + "z": 0.0 + }, + { + "x": 92.3792475996896, + "y": -60.81636194102175, + "z": 0.0 + }, + { + "x": 92.37908834830218, + "y": -59.76874290550684, + "z": 0.0 + }, + { + "x": 92.37892909691473, + "y": -58.721123869991885, + "z": 0.0 + }, + { + "x": 92.3787698455273, + "y": -57.67350483447697, + "z": 0.0 + }, + { + "x": 92.37861059413989, + "y": -56.62588579896209, + "z": 0.0 + }, + { + "x": 92.37845134275246, + "y": -55.578266763447125, + "z": 0.0 + }, + { + "x": 92.37829209136503, + "y": -54.530647727932184, + "z": 0.0 + }, + { + "x": 92.37813283997758, + "y": -53.48302869241722, + "z": 0.0 + }, + { + "x": 92.37797358859015, + "y": -52.435409656902316, + "z": 0.0 + }, + { + "x": 92.37781433720272, + "y": -51.38779062138738, + "z": 0.0 + }, + { + "x": 92.37765508581529, + "y": -50.34017158587245, + "z": 0.0 + }, + { + "x": 92.37749583442785, + "y": -49.292552550357485, + "z": 0.0 + }, + { + "x": 92.37733658304042, + "y": -48.24493351484257, + "z": 0.0 + }, + { + "x": 92.37717733165294, + "y": -47.19731447932759, + "z": 0.0 + }, + { + "x": 92.37701808026551, + "y": -46.14969544381273, + "z": 0.0 + } + ] + }, + { + "id": 228, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.38036235940167, + "y": -68.14969518962627, + "z": 0.0 + }, + { + "x": 92.38020563148586, + "y": -67.11867655529974, + "z": 0.0 + }, + { + "x": 92.38004890357007, + "y": -66.08765792097321, + "z": 0.0 + }, + { + "x": 92.37989217565426, + "y": -65.05663928664663, + "z": 0.0 + }, + { + "x": 92.41679137427337, + "y": -64.10235778117132, + "z": 0.0 + }, + { + "x": 92.51763841393722, + "y": -63.2185243615093, + "z": 0.0 + }, + { + "x": 92.75249091121461, + "y": -62.65696816837448, + "z": 0.0 + }, + { + "x": 93.10227471287547, + "y": -61.994795466111924, + "z": 0.0 + }, + { + "x": 93.54091380759029, + "y": -61.38592526043152, + "z": 0.0 + }, + { + "x": 94.05085061454383, + "y": -60.88220034541138, + "z": 0.0 + }, + { + "x": 94.61177279804552, + "y": -60.50871182938417, + "z": 0.0 + }, + { + "x": 95.31745354601713, + "y": -60.11314605700607, + "z": 0.0 + }, + { + "x": 96.06655349456925, + "y": -59.79291989053375, + "z": 0.0 + }, + { + "x": 96.77291820728992, + "y": -59.62570533772009, + "z": 0.0 + }, + { + "x": 97.53533400954645, + "y": -59.54021825949611, + "z": 0.0 + }, + { + "x": 98.41138621242084, + "y": -59.49574533581123, + "z": 0.0 + }, + { + "x": 99.32791510795805, + "y": -59.498913341565384, + "z": 0.0 + }, + { + "x": 100.35893374654256, + "y": -59.49878770907209, + "z": 0.0 + }, + { + "x": 101.3899523851271, + "y": -59.49866207657878, + "z": 0.0 + } + ] + }, + { + "id": 231, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.38975224562901, + "y": -55.49866207127013, + "z": 0.0 + }, + { + "x": 100.32582394992707, + "y": -55.498791713896836, + "z": 0.0 + }, + { + "x": 99.2618956542251, + "y": -55.49892135652354, + "z": 0.0 + }, + { + "x": 98.1054862504992, + "y": -55.551454169643684, + "z": 0.0 + }, + { + "x": 96.88035293790236, + "y": -55.709600800613835, + "z": 0.0 + }, + { + "x": 95.62580516803563, + "y": -56.01536736447157, + "z": 0.0 + }, + { + "x": 94.39865342573762, + "y": -56.47985266677764, + "z": 0.0 + }, + { + "x": 93.22010770181319, + "y": -57.12204012206544, + "z": 0.0 + }, + { + "x": 92.16829161636967, + "y": -57.86982439322365, + "z": 0.0 + }, + { + "x": 91.20693933306167, + "y": -58.74018071847535, + "z": 0.0 + }, + { + "x": 90.35163011330322, + "y": -59.732371836590005, + "z": 0.0 + }, + { + "x": 89.62431031515885, + "y": -60.834491340795715, + "z": 0.0 + }, + { + "x": 89.04863263105773, + "y": -62.08959445679456, + "z": 0.0 + }, + { + "x": 88.66727497953346, + "y": -63.36411188416538, + "z": 0.0 + }, + { + "x": 88.45615541789783, + "y": -64.66441986249069, + "z": 0.0 + }, + { + "x": 88.38263386183175, + "y": -65.91996545091644, + "z": 0.0 + }, + { + "x": 88.38020062513496, + "y": -67.08604681103495, + "z": 0.0 + }, + { + "x": 88.38036235573624, + "y": -68.149975102343, + "z": 0.0 + } + ] + }, + { + "id": 234, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.37701812648125, + "y": -46.15030349456475, + "z": 0.0 + }, + { + "x": 88.37717693375423, + "y": -47.195000968393984, + "z": 0.0 + }, + { + "x": 88.37777555539165, + "y": -48.2813367156901, + "z": 0.0 + }, + { + "x": 88.40536174558581, + "y": -49.43975162297693, + "z": 0.0 + }, + { + "x": 88.52949263747234, + "y": -50.762481608388775, + "z": 0.0 + }, + { + "x": 88.82128865116036, + "y": -52.00236474058616, + "z": 0.0 + }, + { + "x": 89.2484022072504, + "y": -53.190350440385586, + "z": 0.0 + }, + { + "x": 89.80299449894832, + "y": -54.32344184045015, + "z": 0.0 + }, + { + "x": 90.47934840662556, + "y": -55.389785788630775, + "z": 0.0 + }, + { + "x": 91.33162733593136, + "y": -56.43659369078458, + "z": 0.0 + }, + { + "x": 92.29930560058293, + "y": -57.31710245789597, + "z": 0.0 + }, + { + "x": 93.36514677863966, + "y": -58.05707488016186, + "z": 0.0 + }, + { + "x": 94.51557849293877, + "y": -58.65445385888668, + "z": 0.0 + }, + { + "x": 95.73336626093186, + "y": -59.10041730364373, + "z": 0.0 + }, + { + "x": 97.04842615778028, + "y": -59.3924109703997, + "z": 0.0 + }, + { + "x": 98.23211531382547, + "y": -59.48624809731275, + "z": 0.0 + }, + { + "x": 99.30051746549375, + "y": -59.498916680044445, + "z": 0.0 + }, + { + "x": 100.34521494363746, + "y": -59.49878938074643, + "z": 0.0 + }, + { + "x": 101.38991242178116, + "y": -59.498662081448416, + "z": 0.0 + } + ] + }, + { + "id": 237, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.38975224562901, + "y": -55.49866207127013, + "z": 0.0 + }, + { + "x": 100.35451945543977, + "y": -55.49878821726955, + "z": 0.0 + }, + { + "x": 99.3468932688754, + "y": -55.48506343129918, + "z": 0.0 + }, + { + "x": 98.42662430100265, + "y": -55.417625466811955, + "z": 0.0 + }, + { + "x": 97.6485517457146, + "y": -55.25379666365033, + "z": 0.0 + }, + { + "x": 96.83662282215542, + "y": -55.00957602877695, + "z": 0.0 + }, + { + "x": 96.04978126895155, + "y": -54.690974948978656, + "z": 0.0 + }, + { + "x": 95.34292935792287, + "y": -54.300343147111725, + "z": 0.0 + }, + { + "x": 94.69551164695628, + "y": -53.83370293027281, + "z": 0.0 + }, + { + "x": 94.0722995794701, + "y": -53.28615869571014, + "z": 0.0 + }, + { + "x": 93.53147317384932, + "y": -52.68638943767184, + "z": 0.0 + }, + { + "x": 93.11206255307954, + "y": -52.05384596541708, + "z": 0.0 + }, + { + "x": 92.81868923420566, + "y": -51.39541274371947, + "z": 0.0 + }, + { + "x": 92.57536532669909, + "y": -50.6445173026241, + "z": 0.0 + }, + { + "x": 92.42453828934885, + "y": -49.869001139017534, + "z": 0.0 + }, + { + "x": 92.37078360217731, + "y": -49.081449119815105, + "z": 0.0 + }, + { + "x": 92.37733179938255, + "y": -48.218720127865645, + "z": 0.0 + }, + { + "x": 92.37717550149249, + "y": -47.185274954994725, + "z": 0.0 + }, + { + "x": 92.37701813297217, + "y": -46.15004216908085, + "z": 0.0 + } + ] + }, + { + "id": 240, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.77358612560033, + "y": -209.16116984879852, + "z": 0.0 + }, + { + "x": 338.77421008887006, + "y": -208.12813694877926, + "z": 0.0 + }, + { + "x": 338.7748340521398, + "y": -207.09510404876008, + "z": 0.0 + }, + { + "x": 338.7754580154094, + "y": -206.0620711487407, + "z": 0.0 + }, + { + "x": 338.77608197867914, + "y": -205.0290382487214, + "z": 0.0 + }, + { + "x": 338.69324281461707, + "y": -203.84852588242197, + "z": 0.0 + }, + { + "x": 338.4560333486288, + "y": -202.5937711387159, + "z": 0.0 + }, + { + "x": 338.04777525570614, + "y": -201.36112786701733, + "z": 0.0 + }, + { + "x": 337.47674806992393, + "y": -200.19350299029531, + "z": 0.0 + }, + { + "x": 336.7491768192449, + "y": -199.10957978959715, + "z": 0.0 + }, + { + "x": 335.80614769553483, + "y": -198.10892696995305, + "z": 0.0 + }, + { + "x": 334.8162987013774, + "y": -197.30407433766342, + "z": 0.0 + }, + { + "x": 333.7649898013082, + "y": -196.62897077714314, + "z": 0.0 + }, + { + "x": 332.6538532577283, + "y": -196.07256717159595, + "z": 0.0 + }, + { + "x": 331.4882291636196, + "y": -195.63627292305696, + "z": 0.0 + }, + { + "x": 330.21446207409156, + "y": -195.34317675727266, + "z": 0.0 + }, + { + "x": 328.97164850065326, + "y": -195.20398179351594, + "z": 0.0 + }, + { + "x": 327.8028929609527, + "y": -195.16038550288124, + "z": 0.0 + }, + { + "x": 326.7035191941376, + "y": -195.15919559105376, + "z": 0.0 + }, + { + "x": 325.67048610908853, + "y": -195.15911164734354, + "z": 0.0 + } + ] + }, + { + "id": 243, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.6698379252599, + "y": -199.15911160787866, + "z": 0.0 + }, + { + "x": 326.698705458449, + "y": -199.1591952130984, + "z": 0.0 + }, + { + "x": 327.64217907066535, + "y": -199.15744801084946, + "z": 0.0 + }, + { + "x": 328.52144543613406, + "y": -199.18957761215478, + "z": 0.0 + }, + { + "x": 329.33788028665595, + "y": -199.27490103789137, + "z": 0.0 + }, + { + "x": 330.07872304962353, + "y": -199.42141425818107, + "z": 0.0 + }, + { + "x": 330.8432506839614, + "y": -199.69421323637448, + "z": 0.0 + }, + { + "x": 331.58171654626835, + "y": -200.05387914235382, + "z": 0.0 + }, + { + "x": 332.27416847227875, + "y": -200.48357346066885, + "z": 0.0 + }, + { + "x": 332.89058912936, + "y": -200.95693908574123, + "z": 0.0 + }, + { + "x": 333.400252169316, + "y": -201.44391373597452, + "z": 0.0 + }, + { + "x": 333.85486512684065, + "y": -202.06898774610787, + "z": 0.0 + }, + { + "x": 334.22487174474423, + "y": -202.74504561156255, + "z": 0.0 + }, + { + "x": 334.5053961897313, + "y": -203.46220442450982, + "z": 0.0 + }, + { + "x": 334.6904965133175, + "y": -204.1895319344917, + "z": 0.0 + }, + { + "x": 334.7707155407038, + "y": -205.05396810570983, + "z": 0.0 + }, + { + "x": 334.7754511969514, + "y": -206.07215175838857, + "z": 0.0 + }, + { + "x": 334.77482974972054, + "y": -207.101019107294, + "z": 0.0 + }, + { + "x": 334.7742083024897, + "y": -208.12988645619942, + "z": 0.0 + }, + { + "x": 334.7735868552588, + "y": -209.15875380510485, + "z": 0.0 + } + ] + }, + { + "id": 246, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.78778715206863, + "y": -185.6487580936724, + "z": 0.0 + }, + { + "x": 334.787155504427, + "y": -186.69451323135144, + "z": 0.0 + }, + { + "x": 334.78754032623203, + "y": -187.6777088195608, + "z": 0.0 + }, + { + "x": 334.7615276547244, + "y": -188.60365903590986, + "z": 0.0 + }, + { + "x": 334.7144096790263, + "y": -189.3547372712996, + "z": 0.0 + }, + { + "x": 334.5374160576254, + "y": -190.1438427004075, + "z": 0.0 + }, + { + "x": 334.25838016768733, + "y": -190.92490920055405, + "z": 0.0 + }, + { + "x": 333.897785897096, + "y": -191.67086593067626, + "z": 0.0 + }, + { + "x": 333.5024894926457, + "y": -192.32274605647348, + "z": 0.0 + }, + { + "x": 332.9903625762737, + "y": -192.93797550733024, + "z": 0.0 + }, + { + "x": 332.3794707414414, + "y": -193.50480830036958, + "z": 0.0 + }, + { + "x": 331.7127141752909, + "y": -194.00395343470421, + "z": 0.0 + }, + { + "x": 331.05090017930854, + "y": -194.40768723334773, + "z": 0.0 + }, + { + "x": 330.3217752004306, + "y": -194.72386373113207, + "z": 0.0 + }, + { + "x": 329.51933956980224, + "y": -194.9515978352368, + "z": 0.0 + }, + { + "x": 328.67413547068077, + "y": -195.09519055948655, + "z": 0.0 + }, + { + "x": 327.76202119740066, + "y": -195.15928160435453, + "z": 0.0 + }, + { + "x": 326.7162658724131, + "y": -195.15919662684195, + "z": 0.0 + }, + { + "x": 325.67051054742564, + "y": -195.1591116493294, + "z": 0.0 + } + ] + }, + { + "id": 249, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.6698379252599, + "y": -199.15911160787866, + "z": 0.0 + }, + { + "x": 326.71138163818864, + "y": -199.1591962431579, + "z": 0.0 + }, + { + "x": 327.8492730069305, + "y": -199.15696665098056, + "z": 0.0 + }, + { + "x": 329.0903995103214, + "y": -199.08696153947074, + "z": 0.0 + }, + { + "x": 330.35957533320914, + "y": -198.88056682846835, + "z": 0.0 + }, + { + "x": 331.5936119652358, + "y": -198.5191337594488, + "z": 0.0 + }, + { + "x": 332.7739635436144, + "y": -198.00803518202366, + "z": 0.0 + }, + { + "x": 333.8828403175662, + "y": -197.35480324247453, + "z": 0.0 + }, + { + "x": 334.91750112386575, + "y": -196.5553271582158, + "z": 0.0 + }, + { + "x": 335.82219654976586, + "y": -195.64340922047694, + "z": 0.0 + }, + { + "x": 336.58969986776356, + "y": -194.65980949230965, + "z": 0.0 + }, + { + "x": 337.2504434774229, + "y": -193.60412357405087, + "z": 0.0 + }, + { + "x": 337.79922846031434, + "y": -192.48667964832532, + "z": 0.0 + }, + { + "x": 338.23048861397376, + "y": -191.31898851495265, + "z": 0.0 + }, + { + "x": 338.53979013659995, + "y": -190.11303929208032, + "z": 0.0 + }, + { + "x": 338.72349839333293, + "y": -188.8878609136412, + "z": 0.0 + }, + { + "x": 338.78652799858696, + "y": -187.7346192289334, + "z": 0.0 + }, + { + "x": 338.78715710236884, + "y": -186.69307570255887, + "z": 0.0 + }, + { + "x": 338.7877862061507, + "y": -185.65153217618433, + "z": 0.0 + } + ] + }, + { + "id": 252, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.78778715206863, + "y": -185.64875809367237, + "z": 0.0 + }, + { + "x": 334.7871697478595, + "y": -186.6709318202565, + "z": 0.0 + }, + { + "x": 334.7865523436503, + "y": -187.6931055468404, + "z": 0.0 + }, + { + "x": 334.7859349394412, + "y": -188.71527927342453, + "z": 0.0 + }, + { + "x": 334.78531753523214, + "y": -189.73745300000843, + "z": 0.0 + }, + { + "x": 334.784700131023, + "y": -190.75962672659244, + "z": 0.0 + }, + { + "x": 334.78408272681384, + "y": -191.78180045317663, + "z": 0.0 + }, + { + "x": 334.7834653226047, + "y": -192.80397417976064, + "z": 0.0 + }, + { + "x": 334.78284791839565, + "y": -193.82614790634466, + "z": 0.0 + }, + { + "x": 334.7822305141865, + "y": -194.84832163292856, + "z": 0.0 + }, + { + "x": 334.78161310997734, + "y": -195.8704953595127, + "z": 0.0 + }, + { + "x": 334.7809957057683, + "y": -196.8926690860966, + "z": 0.0 + }, + { + "x": 334.78037830155915, + "y": -197.91484281268072, + "z": 0.0 + }, + { + "x": 334.77976089735, + "y": -198.93701653926462, + "z": 0.0 + }, + { + "x": 334.77914349314085, + "y": -199.95919026584875, + "z": 0.0 + }, + { + "x": 334.7785260889318, + "y": -200.98136399243265, + "z": 0.0 + }, + { + "x": 334.77790868472266, + "y": -202.00353771901678, + "z": 0.0 + }, + { + "x": 334.7772912805135, + "y": -203.0257114456008, + "z": 0.0 + }, + { + "x": 334.77667387630447, + "y": -204.0478851721847, + "z": 0.0 + }, + { + "x": 334.7760564720953, + "y": -205.07005889876882, + "z": 0.0 + }, + { + "x": 334.77543906788617, + "y": -206.09223262535272, + "z": 0.0 + }, + { + "x": 334.774821663677, + "y": -207.11440635193674, + "z": 0.0 + }, + { + "x": 334.77420425946787, + "y": -208.13658007852086, + "z": 0.0 + }, + { + "x": 334.7735868552588, + "y": -209.15875380510488, + "z": 0.0 + } + ] + }, + { + "id": 253, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.77358576077114, + "y": -209.16237787064532, + "z": 0.0 + }, + { + "x": 340.77420316498024, + "y": -208.1402041440613, + "z": 0.0 + }, + { + "x": 340.7748205691894, + "y": -207.1180304174775, + "z": 0.0 + }, + { + "x": 340.77543797339854, + "y": -206.0958566908935, + "z": 0.0 + }, + { + "x": 340.77605537760763, + "y": -205.07368296430926, + "z": 0.0 + }, + { + "x": 340.7766727818168, + "y": -204.05150923772547, + "z": 0.0 + }, + { + "x": 340.7772901860259, + "y": -203.02933551114123, + "z": 0.0 + }, + { + "x": 340.777907590235, + "y": -202.00716178455718, + "z": 0.0 + }, + { + "x": 340.7785249944441, + "y": -200.9849880579734, + "z": 0.0 + }, + { + "x": 340.7791423986532, + "y": -199.96281433138915, + "z": 0.0 + }, + { + "x": 340.7797598028624, + "y": -198.94064060480537, + "z": 0.0 + }, + { + "x": 340.78037720707147, + "y": -197.91846687822112, + "z": 0.0 + }, + { + "x": 340.7809946112806, + "y": -196.89629315163734, + "z": 0.0 + }, + { + "x": 340.7816120154897, + "y": -195.8741194250531, + "z": 0.0 + }, + { + "x": 340.78222941969887, + "y": -194.8519456984693, + "z": 0.0 + }, + { + "x": 340.78284682390796, + "y": -193.82977197188507, + "z": 0.0 + }, + { + "x": 340.78346422811705, + "y": -192.80759824530105, + "z": 0.0 + }, + { + "x": 340.78408163232615, + "y": -191.78542451871704, + "z": 0.0 + }, + { + "x": 340.7846990365353, + "y": -190.76325079213322, + "z": 0.0 + }, + { + "x": 340.78531644074445, + "y": -189.7410770655492, + "z": 0.0 + }, + { + "x": 340.78593384495355, + "y": -188.71890333896496, + "z": 0.0 + }, + { + "x": 340.7865512491627, + "y": -187.69672961238118, + "z": 0.0 + }, + { + "x": 340.7871686533718, + "y": -186.67455588579693, + "z": 0.0 + }, + { + "x": 340.78778605758094, + "y": -185.65238215921315, + "z": 0.0 + } + ] + }, + { + "id": 255, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.77358612560033, + "y": -209.16116984879852, + "z": 0.0 + }, + { + "x": 338.7742035298095, + "y": -208.1389961222145, + "z": 0.0 + }, + { + "x": 338.77482093401863, + "y": -207.1168223956306, + "z": 0.0 + }, + { + "x": 338.7754383382278, + "y": -206.0946486690466, + "z": 0.0 + }, + { + "x": 338.7760557424368, + "y": -205.07247494246246, + "z": 0.0 + }, + { + "x": 338.776673146646, + "y": -204.05030121587856, + "z": 0.0 + }, + { + "x": 338.7772905508551, + "y": -203.02812748929443, + "z": 0.0 + }, + { + "x": 338.77790795506417, + "y": -202.00595376271036, + "z": 0.0 + }, + { + "x": 338.7785253592733, + "y": -200.98378003612646, + "z": 0.0 + }, + { + "x": 338.77914276348247, + "y": -199.96160630954233, + "z": 0.0 + }, + { + "x": 338.7797601676916, + "y": -198.93943258295843, + "z": 0.0 + }, + { + "x": 338.78037757190066, + "y": -197.9172588563743, + "z": 0.0 + }, + { + "x": 338.7809949761098, + "y": -196.8950851297904, + "z": 0.0 + }, + { + "x": 338.78161238031896, + "y": -195.87291140320627, + "z": 0.0 + }, + { + "x": 338.7822297845281, + "y": -194.85073767662237, + "z": 0.0 + }, + { + "x": 338.78284718873715, + "y": -193.82856395003824, + "z": 0.0 + }, + { + "x": 338.7834645929463, + "y": -192.80639022345423, + "z": 0.0 + }, + { + "x": 338.78408199715534, + "y": -191.78421649687021, + "z": 0.0 + }, + { + "x": 338.7846994013645, + "y": -190.7620427702863, + "z": 0.0 + }, + { + "x": 338.78531680557364, + "y": -189.7398690437023, + "z": 0.0 + }, + { + "x": 338.7859342097828, + "y": -188.71769531711817, + "z": 0.0 + }, + { + "x": 338.78655161399195, + "y": -187.69552159053427, + "z": 0.0 + }, + { + "x": 338.787169018201, + "y": -186.67334786395014, + "z": 0.0 + }, + { + "x": 338.78778642241014, + "y": -185.65117413736624, + "z": 0.0 + } + ] + }, + { + "id": 258, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.1602379954887, + "y": -59.47139504390145, + "z": 0.0 + }, + { + "x": 326.19698562451595, + "y": -59.47126871331473, + "z": 0.0 + }, + { + "x": 327.2337332535433, + "y": -59.471142382727976, + "z": 0.0 + }, + { + "x": 328.1307756152667, + "y": -59.466147760085974, + "z": 0.0 + }, + { + "x": 328.95765628647047, + "y": -59.51302416468887, + "z": 0.0 + }, + { + "x": 329.76671371919963, + "y": -59.643665082959686, + "z": 0.0 + }, + { + "x": 330.5566609642443, + "y": -59.860369230187196, + "z": 0.0 + }, + { + "x": 331.29314170067073, + "y": -60.146422142017485, + "z": 0.0 + }, + { + "x": 331.9768652134147, + "y": -60.51140009081671, + "z": 0.0 + }, + { + "x": 332.63035777743755, + "y": -60.98553706174534, + "z": 0.0 + }, + { + "x": 333.21354991847267, + "y": -61.51730905876411, + "z": 0.0 + }, + { + "x": 333.72272706335207, + "y": -62.1093268069832, + "z": 0.0 + }, + { + "x": 334.1393827093261, + "y": -62.73036296148267, + "z": 0.0 + }, + { + "x": 334.4598578619131, + "y": -63.39449632595675, + "z": 0.0 + }, + { + "x": 334.6829992249576, + "y": -64.14494231539462, + "z": 0.0 + }, + { + "x": 334.8114809369437, + "y": -64.98371014715602, + "z": 0.0 + }, + { + "x": 334.86010552665084, + "y": -65.91853702404615, + "z": 0.0 + }, + { + "x": 334.8594793197534, + "y": -66.95528447165245, + "z": 0.0 + }, + { + "x": 334.8588531128561, + "y": -67.99203191925852, + "z": 0.0 + }, + { + "x": 334.85822690595865, + "y": -69.02877936686474, + "z": 0.0 + } + ] + }, + { + "id": 261, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.85822617630015, + "y": -69.03119541055852, + "z": 0.0 + }, + { + "x": 338.85888005386664, + "y": -67.9486364339345, + "z": 0.0 + }, + { + "x": 338.859533931433, + "y": -66.86607745731037, + "z": 0.0 + }, + { + "x": 338.8453984688475, + "y": -65.75709899297308, + "z": 0.0 + }, + { + "x": 338.74794995835043, + "y": -64.5367784122154, + "z": 0.0 + }, + { + "x": 338.4478942489203, + "y": -63.1866427882245, + "z": 0.0 + }, + { + "x": 337.99539461249424, + "y": -61.93906013241491, + "z": 0.0 + }, + { + "x": 337.3918416838258, + "y": -60.757355585741436, + "z": 0.0 + }, + { + "x": 336.64510581466607, + "y": -59.65772951311925, + "z": 0.0 + }, + { + "x": 335.7099172537774, + "y": -58.63869350658027, + "z": 0.0 + }, + { + "x": 334.67415102685993, + "y": -57.77989147387602, + "z": 0.0 + }, + { + "x": 333.5955595287653, + "y": -57.070160318935095, + "z": 0.0 + }, + { + "x": 332.451682977315, + "y": -56.47803873653796, + "z": 0.0 + }, + { + "x": 331.24690110232734, + "y": -56.00756770704959, + "z": 0.0 + }, + { + "x": 329.9146552998319, + "y": -55.688996711487064, + "z": 0.0 + }, + { + "x": 328.6363731316137, + "y": -55.52987162210784, + "z": 0.0 + }, + { + "x": 327.4090981995594, + "y": -55.47289540543303, + "z": 0.0 + }, + { + "x": 326.2423097504109, + "y": -55.47126316074751, + "z": 0.0 + }, + { + "x": 325.15975058434924, + "y": -55.47139507359766, + "z": 0.0 + } + ] + }, + { + "id": 264, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.87188359293737, + "y": -46.41878349125916, + "z": 0.0 + }, + { + "x": 334.8712628344383, + "y": -47.446510576513944, + "z": 0.0 + }, + { + "x": 334.87064207593926, + "y": -48.47423766176885, + "z": 0.0 + }, + { + "x": 334.8700213174402, + "y": -49.501964747023536, + "z": 0.0 + }, + { + "x": 334.86940055894115, + "y": -50.529691832278345, + "z": 0.0 + }, + { + "x": 334.8687798004421, + "y": -51.55741891753325, + "z": 0.0 + }, + { + "x": 334.86815904194316, + "y": -52.58514600278794, + "z": 0.0 + }, + { + "x": 334.8675382834441, + "y": -53.612873088042846, + "z": 0.0 + }, + { + "x": 334.86691752494505, + "y": -54.64060017329754, + "z": 0.0 + }, + { + "x": 334.866296766446, + "y": -55.66832725855234, + "z": 0.0 + }, + { + "x": 334.86567600794694, + "y": -56.69605434380725, + "z": 0.0 + }, + { + "x": 334.865055249448, + "y": -57.72378142906193, + "z": 0.0 + }, + { + "x": 334.86443449094895, + "y": -58.75150851431674, + "z": 0.0 + }, + { + "x": 334.8638137324499, + "y": -59.77923559957164, + "z": 0.0 + }, + { + "x": 334.86319297395085, + "y": -60.80696268482633, + "z": 0.0 + }, + { + "x": 334.8625722154518, + "y": -61.83468977008113, + "z": 0.0 + }, + { + "x": 334.86195145695274, + "y": -62.862416855336036, + "z": 0.0 + }, + { + "x": 334.8613306984538, + "y": -63.89014394059084, + "z": 0.0 + }, + { + "x": 334.86070993995475, + "y": -64.91787102584553, + "z": 0.0 + }, + { + "x": 334.8600891814557, + "y": -65.94559811110031, + "z": 0.0 + }, + { + "x": 334.85946842295664, + "y": -66.97332519635523, + "z": 0.0 + }, + { + "x": 334.8588476644577, + "y": -68.00105228160993, + "z": 0.0 + }, + { + "x": 334.85822690595865, + "y": -69.02877936686471, + "z": 0.0 + } + ] + }, + { + "id": 265, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.85822581147096, + "y": -69.03240343240542, + "z": 0.0 + }, + { + "x": 340.85884656997, + "y": -68.00467634715062, + "z": 0.0 + }, + { + "x": 340.859467328469, + "y": -66.9769492618956, + "z": 0.0 + }, + { + "x": 340.86008808696806, + "y": -65.94922217664102, + "z": 0.0 + }, + { + "x": 340.8607088454671, + "y": -64.92149509138622, + "z": 0.0 + }, + { + "x": 340.8613296039661, + "y": -63.8937680061312, + "z": 0.0 + }, + { + "x": 340.8619503624651, + "y": -62.866040920876394, + "z": 0.0 + }, + { + "x": 340.86257112096416, + "y": -61.83831383562182, + "z": 0.0 + }, + { + "x": 340.8631918794632, + "y": -60.810586750367015, + "z": 0.0 + }, + { + "x": 340.8638126379622, + "y": -59.782859665112, + "z": 0.0 + }, + { + "x": 340.86443339646127, + "y": -58.755132579857424, + "z": 0.0 + }, + { + "x": 340.8650541549603, + "y": -57.72740549460262, + "z": 0.0 + }, + { + "x": 340.8656749134593, + "y": -56.699678409347605, + "z": 0.0 + }, + { + "x": 340.86629567195837, + "y": -55.67195132409302, + "z": 0.0 + }, + { + "x": 340.8669164304574, + "y": -54.64422423883823, + "z": 0.0 + }, + { + "x": 340.8675371889564, + "y": -53.616497153583204, + "z": 0.0 + }, + { + "x": 340.8681579474555, + "y": -52.58877006832863, + "z": 0.0 + }, + { + "x": 340.86877870595447, + "y": -51.561042983073605, + "z": 0.0 + }, + { + "x": 340.8693994644535, + "y": -50.53331589781903, + "z": 0.0 + }, + { + "x": 340.8700202229526, + "y": -49.50558881256423, + "z": 0.0 + }, + { + "x": 340.8706409814516, + "y": -48.47786172730921, + "z": 0.0 + }, + { + "x": 340.8712617399506, + "y": -47.45013464205463, + "z": 0.0 + }, + { + "x": 340.8718824984497, + "y": -46.42240755679985, + "z": 0.0 + } + ] + }, + { + "id": 267, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.85822617630015, + "y": -69.03119541055852, + "z": 0.0 + }, + { + "x": 338.8588469347992, + "y": -68.00346832530371, + "z": 0.0 + }, + { + "x": 338.85946769329826, + "y": -66.97574124004882, + "z": 0.0 + }, + { + "x": 338.8600884517973, + "y": -65.94801415479412, + "z": 0.0 + }, + { + "x": 338.86070921029636, + "y": -64.92028706953931, + "z": 0.0 + }, + { + "x": 338.8613299687953, + "y": -63.89255998428441, + "z": 0.0 + }, + { + "x": 338.86195072729436, + "y": -62.86483289902961, + "z": 0.0 + }, + { + "x": 338.8625714857934, + "y": -61.837105813774926, + "z": 0.0 + }, + { + "x": 338.86319224429246, + "y": -60.809378728520116, + "z": 0.0 + }, + { + "x": 338.8638130027914, + "y": -59.781651643265214, + "z": 0.0 + }, + { + "x": 338.86443376129046, + "y": -58.753924558010524, + "z": 0.0 + }, + { + "x": 338.8650545197895, + "y": -57.72619747275573, + "z": 0.0 + }, + { + "x": 338.86567527828856, + "y": -56.69847038750082, + "z": 0.0 + }, + { + "x": 338.8662960367876, + "y": -55.67074330224612, + "z": 0.0 + }, + { + "x": 338.86691679528667, + "y": -54.64301621699133, + "z": 0.0 + }, + { + "x": 338.8675375537856, + "y": -53.61528913173642, + "z": 0.0 + }, + { + "x": 338.86815831228466, + "y": -52.587562046481736, + "z": 0.0 + }, + { + "x": 338.8687790707837, + "y": -51.55983496122682, + "z": 0.0 + }, + { + "x": 338.86939982928277, + "y": -50.53210787597213, + "z": 0.0 + }, + { + "x": 338.8700205877818, + "y": -49.504380790717335, + "z": 0.0 + }, + { + "x": 338.87064134628076, + "y": -48.476653705462425, + "z": 0.0 + }, + { + "x": 338.8712621047798, + "y": -47.44892662020773, + "z": 0.0 + }, + { + "x": 338.87188286327887, + "y": -46.42119953495295, + "z": 0.0 + } + ] + }, + { + "id": 270, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.1602379954888, + "y": -59.47139504390147, + "z": 0.0 + }, + { + "x": 326.2277182989171, + "y": -59.470929141072, + "z": 0.0 + }, + { + "x": 327.35842293823544, + "y": -59.44753903629696, + "z": 0.0 + }, + { + "x": 328.6249307256167, + "y": -59.34585779940172, + "z": 0.0 + }, + { + "x": 329.83283393607644, + "y": -59.10445994848552, + "z": 0.0 + }, + { + "x": 330.99854190970746, + "y": -58.7476302319203, + "z": 0.0 + }, + { + "x": 332.12450630869796, + "y": -58.282204730606395, + "z": 0.0 + }, + { + "x": 333.2016302091149, + "y": -57.71199354431562, + "z": 0.0 + }, + { + "x": 334.2573023836869, + "y": -57.01204403553369, + "z": 0.0 + }, + { + "x": 335.20689490297315, + "y": -56.209077266950956, + "z": 0.0 + }, + { + "x": 336.0601484015708, + "y": -55.316518481772064, + "z": 0.0 + }, + { + "x": 336.81763920173995, + "y": -54.34038408681393, + "z": 0.0 + }, + { + "x": 337.4701625516368, + "y": -53.29086134605254, + "z": 0.0 + }, + { + "x": 338.02303709250344, + "y": -52.14534488442073, + "z": 0.0 + }, + { + "x": 338.43226232151005, + "y": -50.94637169459995, + "z": 0.0 + }, + { + "x": 338.69371665328697, + "y": -49.73742888246052, + "z": 0.0 + }, + { + "x": 338.8269504095423, + "y": -48.563284388624226, + "z": 0.0 + }, + { + "x": 338.87125999734656, + "y": -47.45241568426286, + "z": 0.0 + }, + { + "x": 338.87188263035193, + "y": -46.42158516849574, + "z": 0.0 + } + ] + }, + { + "id": 273, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.87188359293725, + "y": -46.41878349125915, + "z": 0.0 + }, + { + "x": 334.8712500900183, + "y": -47.46781103382229, + "z": 0.0 + }, + { + "x": 334.87465533163515, + "y": -48.3867486297635, + "z": 0.0 + }, + { + "x": 334.8340411563869, + "y": -49.20269489318105, + "z": 0.0 + }, + { + "x": 334.6984352919744, + "y": -50.00884118128332, + "z": 0.0 + }, + { + "x": 334.4706425605684, + "y": -50.79323570818916, + "z": 0.0 + }, + { + "x": 334.1537904682107, + "y": -51.545608539108564, + "z": 0.0 + }, + { + "x": 333.75211379318347, + "y": -52.25610409194188, + "z": 0.0 + }, + { + "x": 333.27060793851547, + "y": -52.9157049023742, + "z": 0.0 + }, + { + "x": 332.71479896932226, + "y": -53.51645171938627, + "z": 0.0 + }, + { + "x": 332.0924357454257, + "y": -54.05035381817845, + "z": 0.0 + }, + { + "x": 331.427675970019, + "y": -54.50254431697783, + "z": 0.0 + }, + { + "x": 330.7094081648772, + "y": -54.87085310925545, + "z": 0.0 + }, + { + "x": 329.9383881173603, + "y": -55.15215650825121, + "z": 0.0 + }, + { + "x": 329.14101179884665, + "y": -55.343337898478175, + "z": 0.0 + }, + { + "x": 328.25269461726884, + "y": -55.442782093772266, + "z": 0.0 + }, + { + "x": 327.2591991124581, + "y": -55.47113924994592, + "z": 0.0 + }, + { + "x": 326.2094748484037, + "y": -55.471267161771806, + "z": 0.0 + }, + { + "x": 325.15975058434924, + "y": -55.47139507359766, + "z": 0.0 + } + ] + }, + { + "id": 276, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.4016714980064, + "y": -208.32969356999655, + "z": 0.0 + }, + { + "x": 92.40151480877489, + "y": -207.29892941643521, + "z": 0.0 + }, + { + "x": 92.4013581195434, + "y": -206.26816526287388, + "z": 0.0 + }, + { + "x": 92.4348956724687, + "y": -205.30636743927084, + "z": 0.0 + }, + { + "x": 92.52818843367211, + "y": -204.4130756059148, + "z": 0.0 + }, + { + "x": 92.72123588524576, + "y": -203.67070398173175, + "z": 0.0 + }, + { + "x": 93.00974965835567, + "y": -202.95958489921435, + "z": 0.0 + }, + { + "x": 93.38865416626015, + "y": -202.2434998249028, + "z": 0.0 + }, + { + "x": 93.84361125052354, + "y": -201.57227382694185, + "z": 0.0 + }, + { + "x": 94.3679482439317, + "y": -200.96386620483992, + "z": 0.0 + }, + { + "x": 94.92835006058691, + "y": -200.49959300260977, + "z": 0.0 + }, + { + "x": 95.59256725930115, + "y": -200.05094130665384, + "z": 0.0 + }, + { + "x": 96.30313513512215, + "y": -199.6804342777397, + "z": 0.0 + }, + { + "x": 97.05276059073066, + "y": -199.39214154117593, + "z": 0.0 + }, + { + "x": 97.78075795641391, + "y": -199.23561854958365, + "z": 0.0 + }, + { + "x": 98.54816831250857, + "y": -199.1678728105677, + "z": 0.0 + }, + { + "x": 99.4342641119304, + "y": -199.1392723209954, + "z": 0.0 + }, + { + "x": 100.38873984010004, + "y": -199.1408053875916, + "z": 0.0 + }, + { + "x": 101.41950400216771, + "y": -199.14088914693036, + "z": 0.0 + } + ] + }, + { + "id": 279, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.4201637034698, + "y": -195.14088918733114, + "z": 0.0 + }, + { + "x": 100.34443380692284, + "y": -195.14080177410108, + "z": 0.0 + }, + { + "x": 99.22213065499349, + "y": -195.16674073057283, + "z": 0.0 + }, + { + "x": 98.00848018727567, + "y": -195.27836301508296, + "z": 0.0 + }, + { + "x": 96.7360855044522, + "y": -195.53387222132216, + "z": 0.0 + }, + { + "x": 95.49935446175567, + "y": -195.9299935677057, + "z": 0.0 + }, + { + "x": 94.30099645201025, + "y": -196.47952520287916, + "z": 0.0 + }, + { + "x": 93.18455033163266, + "y": -197.1596159343103, + "z": 0.0 + }, + { + "x": 92.15917603136714, + "y": -197.95480405569515, + "z": 0.0 + }, + { + "x": 91.22713561220709, + "y": -198.86627341871213, + "z": 0.0 + }, + { + "x": 90.40428222001984, + "y": -199.88960224606478, + "z": 0.0 + }, + { + "x": 89.70987654025186, + "y": -201.00743384361795, + "z": 0.0 + }, + { + "x": 89.15614980628833, + "y": -202.20137215494867, + "z": 0.0 + }, + { + "x": 88.7512504331463, + "y": -203.45401329200402, + "z": 0.0 + }, + { + "x": 88.50121720547153, + "y": -204.74710415228492, + "z": 0.0 + }, + { + "x": 88.40530287705167, + "y": -206.05240752625747, + "z": 0.0 + }, + { + "x": 88.40150796490579, + "y": -207.25421174575615, + "z": 0.0 + }, + { + "x": 88.40167148949946, + "y": -208.3299416334258, + "z": 0.0 + } + ] + }, + { + "id": 280, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.39832728819378, + "y": -186.330605900311, + "z": 0.0 + }, + { + "x": 86.39848653958121, + "y": -187.37822493582607, + "z": 0.0 + }, + { + "x": 86.39864579096867, + "y": -188.42584397134087, + "z": 0.0 + }, + { + "x": 86.3988050423561, + "y": -189.47346300685584, + "z": 0.0 + }, + { + "x": 86.39896429374355, + "y": -190.52108204237072, + "z": 0.0 + }, + { + "x": 86.39912354513098, + "y": -191.56870107788566, + "z": 0.0 + }, + { + "x": 86.3992827965184, + "y": -192.61632011340058, + "z": 0.0 + }, + { + "x": 86.39944204790584, + "y": -193.66393914891557, + "z": 0.0 + }, + { + "x": 86.39960129929328, + "y": -194.71155818443046, + "z": 0.0 + }, + { + "x": 86.39976055068071, + "y": -195.75917721994537, + "z": 0.0 + }, + { + "x": 86.39991980206814, + "y": -196.80679625546037, + "z": 0.0 + }, + { + "x": 86.40007905345558, + "y": -197.85441529097523, + "z": 0.0 + }, + { + "x": 86.40023830484301, + "y": -198.90203432649022, + "z": 0.0 + }, + { + "x": 86.40039755623046, + "y": -199.94965336200508, + "z": 0.0 + }, + { + "x": 86.40055680761789, + "y": -200.99727239752002, + "z": 0.0 + }, + { + "x": 86.40071605900532, + "y": -202.04489143303496, + "z": 0.0 + }, + { + "x": 86.40087531039275, + "y": -203.09251046854993, + "z": 0.0 + }, + { + "x": 86.4010345617802, + "y": -204.14012950406482, + "z": 0.0 + }, + { + "x": 86.40119381316762, + "y": -205.18774853957976, + "z": 0.0 + }, + { + "x": 86.40135306455505, + "y": -206.23536757509473, + "z": 0.0 + }, + { + "x": 86.4015123159425, + "y": -207.28298661060958, + "z": 0.0 + }, + { + "x": 86.40167156732993, + "y": -208.33060564612452, + "z": 0.0 + } + ] + }, + { + "id": 282, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.39832726508592, + "y": -186.330301874935, + "z": 0.0 + }, + { + "x": 88.39848651647335, + "y": -187.37792091045003, + "z": 0.0 + }, + { + "x": 88.39864576786081, + "y": -188.42553994596489, + "z": 0.0 + }, + { + "x": 88.39880501924824, + "y": -189.4731589814798, + "z": 0.0 + }, + { + "x": 88.39896427063569, + "y": -190.5207780169947, + "z": 0.0 + }, + { + "x": 88.39912352202312, + "y": -191.56839705250968, + "z": 0.0 + }, + { + "x": 88.39928277341055, + "y": -192.6160160880246, + "z": 0.0 + }, + { + "x": 88.39944202479798, + "y": -193.66363512353956, + "z": 0.0 + }, + { + "x": 88.39960127618542, + "y": -194.71125415905448, + "z": 0.0 + }, + { + "x": 88.39976052757285, + "y": -195.7588731945694, + "z": 0.0 + }, + { + "x": 88.39991977896028, + "y": -196.80649223008436, + "z": 0.0 + }, + { + "x": 88.40007903034773, + "y": -197.85411126559922, + "z": 0.0 + }, + { + "x": 88.40023828173516, + "y": -198.90173030111418, + "z": 0.0 + }, + { + "x": 88.4003975331226, + "y": -199.9493493366291, + "z": 0.0 + }, + { + "x": 88.40055678451003, + "y": -200.996968372144, + "z": 0.0 + }, + { + "x": 88.40071603589746, + "y": -202.04458740765898, + "z": 0.0 + }, + { + "x": 88.40087528728489, + "y": -203.0922064431739, + "z": 0.0 + }, + { + "x": 88.40103453867233, + "y": -204.1398254786888, + "z": 0.0 + }, + { + "x": 88.40119379005976, + "y": -205.18744451420378, + "z": 0.0 + }, + { + "x": 88.4013530414472, + "y": -206.2350635497187, + "z": 0.0 + }, + { + "x": 88.40151229283464, + "y": -207.2826825852336, + "z": 0.0 + }, + { + "x": 88.40167154422207, + "y": -208.33030162074851, + "z": 0.0 + } + ] + }, + { + "id": 285, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.40167149800635, + "y": -208.32969356999655, + "z": 0.0 + }, + { + "x": 92.40151224661892, + "y": -207.28207453448158, + "z": 0.0 + }, + { + "x": 92.40135299523148, + "y": -206.23445549896667, + "z": 0.0 + }, + { + "x": 92.40119374384405, + "y": -205.18683646345175, + "z": 0.0 + }, + { + "x": 92.40103449245662, + "y": -204.13921742793684, + "z": 0.0 + }, + { + "x": 92.40087524106917, + "y": -203.09159839242187, + "z": 0.0 + }, + { + "x": 92.40071598968174, + "y": -202.04397935690696, + "z": 0.0 + }, + { + "x": 92.40055673829431, + "y": -200.99636032139205, + "z": 0.0 + }, + { + "x": 92.40039748690688, + "y": -199.94874128587708, + "z": 0.0 + }, + { + "x": 92.40023823551944, + "y": -198.90112225036216, + "z": 0.0 + }, + { + "x": 92.40007898413201, + "y": -197.85350321484725, + "z": 0.0 + }, + { + "x": 92.39991973274456, + "y": -196.80588417933228, + "z": 0.0 + }, + { + "x": 92.39976048135713, + "y": -195.75826514381737, + "z": 0.0 + }, + { + "x": 92.3996012299697, + "y": -194.71064610830246, + "z": 0.0 + }, + { + "x": 92.39944197858226, + "y": -193.6630270727875, + "z": 0.0 + }, + { + "x": 92.39928272719483, + "y": -192.61540803727257, + "z": 0.0 + }, + { + "x": 92.3991234758074, + "y": -191.56778900175766, + "z": 0.0 + }, + { + "x": 92.39896422441997, + "y": -190.52016996624275, + "z": 0.0 + }, + { + "x": 92.39880497303253, + "y": -189.47255093072778, + "z": 0.0 + }, + { + "x": 92.3986457216451, + "y": -188.42493189521286, + "z": 0.0 + }, + { + "x": 92.39848647025764, + "y": -187.3773128596979, + "z": 0.0 + }, + { + "x": 92.39832721887021, + "y": -186.32969382418304, + "z": 0.0 + } + ] + }, + { + "id": 288, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.42016370346977, + "y": -195.14088918733114, + "z": 0.0 + }, + { + "x": 100.3390649845846, + "y": -195.1408013378335, + "z": 0.0 + }, + { + "x": 99.25796626569947, + "y": -195.14071348833582, + "z": 0.0 + }, + { + "x": 98.28458553074684, + "y": -195.0862693380189, + "z": 0.0 + }, + { + "x": 97.40383131032577, + "y": -194.94725467952273, + "z": 0.0 + }, + { + "x": 96.57117246015427, + "y": -194.71855142809554, + "z": 0.0 + }, + { + "x": 95.76729748780213, + "y": -194.40123909394055, + "z": 0.0 + }, + { + "x": 95.00103637582578, + "y": -193.99850278004064, + "z": 0.0 + }, + { + "x": 94.34149960300874, + "y": -193.52119580541665, + "z": 0.0 + }, + { + "x": 93.73697547677894, + "y": -192.95750232040297, + "z": 0.0 + }, + { + "x": 93.25279717064232, + "y": -192.34030109965005, + "z": 0.0 + }, + { + "x": 92.87293777889991, + "y": -191.6641748771362, + "z": 0.0 + }, + { + "x": 92.59918080829013, + "y": -190.93805588245885, + "z": 0.0 + }, + { + "x": 92.43778694671191, + "y": -190.1777214558072, + "z": 0.0 + }, + { + "x": 92.39097979703983, + "y": -189.39669912723474, + "z": 0.0 + }, + { + "x": 92.398655949702, + "y": -188.49221612587775, + "z": 0.0 + }, + { + "x": 92.3984916089792, + "y": -187.4111174159142, + "z": 0.0 + }, + { + "x": 92.39832726825641, + "y": -186.3300187059507, + "z": 0.0 + } + ] + }, + { + "id": 291, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.39832726508598, + "y": -186.33030187493506, + "z": 0.0 + }, + { + "x": 88.3984916284713, + "y": -187.41154966795028, + "z": 0.0 + }, + { + "x": 88.3997952677014, + "y": -188.55999082671315, + "z": 0.0 + }, + { + "x": 88.44947052786367, + "y": -189.81148110381363, + "z": 0.0 + }, + { + "x": 88.62649926441631, + "y": -191.13890401186998, + "z": 0.0 + }, + { + "x": 88.98204913025093, + "y": -192.4501989944092, + "z": 0.0 + }, + { + "x": 89.52455006209007, + "y": -193.71280814792257, + "z": 0.0 + }, + { + "x": 90.22524077583694, + "y": -194.85626879619775, + "z": 0.0 + }, + { + "x": 91.06876625472273, + "y": -195.89837434016766, + "z": 0.0 + }, + { + "x": 92.04228546886993, + "y": -196.8217242502015, + "z": 0.0 + }, + { + "x": 93.12956285485541, + "y": -197.60902540519552, + "z": 0.0 + }, + { + "x": 94.31193266701067, + "y": -198.2459903012798, + "z": 0.0 + }, + { + "x": 95.57847859315382, + "y": -198.72371333585062, + "z": 0.0 + }, + { + "x": 96.9321412796205, + "y": -199.02686395609504, + "z": 0.0 + }, + { + "x": 98.14627840384554, + "y": -199.12410570010613, + "z": 0.0 + }, + { + "x": 99.25702742289741, + "y": -199.14071342525216, + "z": 0.0 + }, + { + "x": 100.33827522483548, + "y": -199.14080128686422, + "z": 0.0 + }, + { + "x": 101.41952302677362, + "y": -199.1408891484763, + "z": 0.0 + } + ] + }, + { + "id": 294, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.8270901428559, + "y": -120.57876996339137, + "z": 0.0 + }, + { + "x": 334.826438409234, + "y": -121.65777942783936, + "z": 0.0 + }, + { + "x": 334.82911918092043, + "y": -122.6225829958806, + "z": 0.0 + }, + { + "x": 334.78744991664456, + "y": -123.50619030723817, + "z": 0.0 + }, + { + "x": 334.67608453095875, + "y": -124.323014465026, + "z": 0.0 + }, + { + "x": 334.48218923292706, + "y": -125.07716990733435, + "z": 0.0 + }, + { + "x": 334.17478222813236, + "y": -125.79893448616556, + "z": 0.0 + }, + { + "x": 333.736351798824, + "y": -126.49931703182511, + "z": 0.0 + }, + { + "x": 333.2075836156737, + "y": -127.14427060363033, + "z": 0.0 + }, + { + "x": 332.5869206240877, + "y": -127.73394318759833, + "z": 0.0 + }, + { + "x": 331.951783150246, + "y": -128.22595479867536, + "z": 0.0 + }, + { + "x": 331.19135709749276, + "y": -128.65090699743394, + "z": 0.0 + }, + { + "x": 330.3850127040796, + "y": -128.99600822103105, + "z": 0.0 + }, + { + "x": 329.5488060070645, + "y": -129.25864203600875, + "z": 0.0 + }, + { + "x": 328.7330851925337, + "y": -129.43304937339707, + "z": 0.0 + }, + { + "x": 327.7768843418315, + "y": -129.50432910452471, + "z": 0.0 + }, + { + "x": 326.72022650283077, + "y": -129.51556751521045, + "z": 0.0 + }, + { + "x": 325.64121694925416, + "y": -129.5150854205275, + "z": 0.0 + } + ] + }, + { + "id": 297, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.63907988605513, + "y": -133.51508486495032, + "z": 0.0 + }, + { + "x": 326.72271965974255, + "y": -133.5155690283865, + "z": 0.0 + }, + { + "x": 327.88515173096323, + "y": -133.51453576338582, + "z": 0.0 + }, + { + "x": 329.12984339921354, + "y": -133.45934839617428, + "z": 0.0 + }, + { + "x": 330.44595125527894, + "y": -133.28444611390069, + "z": 0.0 + }, + { + "x": 331.8029942989284, + "y": -132.91945053130473, + "z": 0.0 + }, + { + "x": 333.0210008030467, + "y": -132.3794292227165, + "z": 0.0 + }, + { + "x": 334.15841440892063, + "y": -131.6948662890082, + "z": 0.0 + }, + { + "x": 335.19981972688277, + "y": -130.8782083731809, + "z": 0.0 + }, + { + "x": 336.1295437018704, + "y": -129.9468692721997, + "z": 0.0 + }, + { + "x": 336.9739198883692, + "y": -128.86302591188698, + "z": 0.0 + }, + { + "x": 337.63193009535365, + "y": -127.715469424601, + "z": 0.0 + }, + { + "x": 338.14372121746567, + "y": -126.5108268803566, + "z": 0.0 + }, + { + "x": 338.5150341584568, + "y": -125.2568558340516, + "z": 0.0 + }, + { + "x": 338.741154142397, + "y": -123.97452122052148, + "z": 0.0 + }, + { + "x": 338.8257801477764, + "y": -122.74880439198486, + "z": 0.0 + }, + { + "x": 338.82643467810203, + "y": -121.66516470780864, + "z": 0.0 + }, + { + "x": 338.82708920842776, + "y": -120.58152502363232, + "z": 0.0 + } + ] + }, + { + "id": 298, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.8131424361464, + "y": -143.67238981697838, + "z": 0.0 + }, + { + "x": 340.81377637306554, + "y": -142.62284455388536, + "z": 0.0 + }, + { + "x": 340.81441030998474, + "y": -141.57329929079253, + "z": 0.0 + }, + { + "x": 340.8150442469039, + "y": -140.5237540276993, + "z": 0.0 + }, + { + "x": 340.8156781838231, + "y": -139.4742087646065, + "z": 0.0 + }, + { + "x": 340.8163121207423, + "y": -138.42466350151346, + "z": 0.0 + }, + { + "x": 340.8169460576614, + "y": -137.37511823842024, + "z": 0.0 + }, + { + "x": 340.8175799945806, + "y": -136.32557297532742, + "z": 0.0 + }, + { + "x": 340.81821393149977, + "y": -135.2760277122342, + "z": 0.0 + }, + { + "x": 340.81884786841897, + "y": -134.22648244914137, + "z": 0.0 + }, + { + "x": 340.81948180533817, + "y": -133.17693718604835, + "z": 0.0 + }, + { + "x": 340.8201157422573, + "y": -132.12739192295513, + "z": 0.0 + }, + { + "x": 340.8207496791765, + "y": -131.07784665986227, + "z": 0.0 + }, + { + "x": 340.82138361609566, + "y": -130.02830139676905, + "z": 0.0 + }, + { + "x": 340.82201755301486, + "y": -128.97875613367623, + "z": 0.0 + }, + { + "x": 340.82265148993406, + "y": -127.92921087058322, + "z": 0.0 + }, + { + "x": 340.8232854268532, + "y": -126.87966560748997, + "z": 0.0 + }, + { + "x": 340.8239193637724, + "y": -125.83012034439716, + "z": 0.0 + }, + { + "x": 340.8245533006916, + "y": -124.78057508130414, + "z": 0.0 + }, + { + "x": 340.82518723761075, + "y": -123.7310298182109, + "z": 0.0 + }, + { + "x": 340.82582117452995, + "y": -122.68148455511809, + "z": 0.0 + }, + { + "x": 340.8264551114491, + "y": -121.63193929202485, + "z": 0.0 + }, + { + "x": 340.8270890483683, + "y": -120.58239402893204, + "z": 0.0 + } + ] + }, + { + "id": 300, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.8131428009756, + "y": -143.6711817951316, + "z": 0.0 + }, + { + "x": 338.8137767378947, + "y": -142.62163653203856, + "z": 0.0 + }, + { + "x": 338.814410674814, + "y": -141.57209126894566, + "z": 0.0 + }, + { + "x": 338.81504461173313, + "y": -140.52254600585252, + "z": 0.0 + }, + { + "x": 338.8156785486523, + "y": -139.4730007427596, + "z": 0.0 + }, + { + "x": 338.81631248557153, + "y": -138.4234554796666, + "z": 0.0 + }, + { + "x": 338.8169464224907, + "y": -137.37391021657345, + "z": 0.0 + }, + { + "x": 338.8175803594098, + "y": -136.32436495348054, + "z": 0.0 + }, + { + "x": 338.81821429632896, + "y": -135.2748196903874, + "z": 0.0 + }, + { + "x": 338.8188482332482, + "y": -134.2252744272945, + "z": 0.0 + }, + { + "x": 338.81948217016736, + "y": -133.17572916420147, + "z": 0.0 + }, + { + "x": 338.8201161070865, + "y": -132.12618390110833, + "z": 0.0 + }, + { + "x": 338.82075004400576, + "y": -131.07663863801537, + "z": 0.0 + }, + { + "x": 338.8213839809249, + "y": -130.02709337492226, + "z": 0.0 + }, + { + "x": 338.82201791784405, + "y": -128.97754811182932, + "z": 0.0 + }, + { + "x": 338.8226518547633, + "y": -127.92800284873633, + "z": 0.0 + }, + { + "x": 338.82328579168245, + "y": -126.87845758564319, + "z": 0.0 + }, + { + "x": 338.8239197286016, + "y": -125.82891232255027, + "z": 0.0 + }, + { + "x": 338.82455366552085, + "y": -124.77936705945724, + "z": 0.0 + }, + { + "x": 338.82518760244, + "y": -123.72982179636412, + "z": 0.0 + }, + { + "x": 338.82582153935914, + "y": -122.6802765332712, + "z": 0.0 + }, + { + "x": 338.8264554762783, + "y": -121.63073127017807, + "z": 0.0 + }, + { + "x": 338.82708941319754, + "y": -120.58118600708515, + "z": 0.0 + } + ] + }, + { + "id": 303, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 334.8270901428559, + "y": -120.57876996339137, + "z": 0.0 + }, + { + "x": 334.8264562059368, + "y": -121.62831522648449, + "z": 0.0 + }, + { + "x": 334.82582226901764, + "y": -122.67786048957741, + "z": 0.0 + }, + { + "x": 334.8251883320984, + "y": -123.72740575267053, + "z": 0.0 + }, + { + "x": 334.82455439517923, + "y": -124.77695101576346, + "z": 0.0 + }, + { + "x": 334.8239204582601, + "y": -125.82649627885648, + "z": 0.0 + }, + { + "x": 334.82328652134083, + "y": -126.8760415419496, + "z": 0.0 + }, + { + "x": 334.8226525844217, + "y": -127.92558680504254, + "z": 0.0 + }, + { + "x": 334.82201864750255, + "y": -128.97513206813557, + "z": 0.0 + }, + { + "x": 334.8213847105833, + "y": -130.02467733122867, + "z": 0.0 + }, + { + "x": 334.82075077366414, + "y": -131.0742225943216, + "z": 0.0 + }, + { + "x": 334.820116836745, + "y": -132.12376785741475, + "z": 0.0 + }, + { + "x": 334.81948289982586, + "y": -133.17331312050766, + "z": 0.0 + }, + { + "x": 334.8188489629066, + "y": -134.22285838360068, + "z": 0.0 + }, + { + "x": 334.81821502598746, + "y": -135.27240364669382, + "z": 0.0 + }, + { + "x": 334.8175810890683, + "y": -136.32194890978673, + "z": 0.0 + }, + { + "x": 334.81694715214906, + "y": -137.37149417287986, + "z": 0.0 + }, + { + "x": 334.8163132152299, + "y": -138.42103943597277, + "z": 0.0 + }, + { + "x": 334.81567927831077, + "y": -139.4705846990658, + "z": 0.0 + }, + { + "x": 334.8150453413915, + "y": -140.52012996215893, + "z": 0.0 + }, + { + "x": 334.81441140447237, + "y": -141.56967522525184, + "z": 0.0 + }, + { + "x": 334.8137774675532, + "y": -142.61922048834498, + "z": 0.0 + }, + { + "x": 334.8131435306341, + "y": -143.668765751438, + "z": 0.0 + } + ] + }, + { + "id": 306, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 325.63907988605513, + "y": -133.51508486495032, + "z": 0.0 + }, + { + "x": 326.67019718616996, + "y": -133.5155455616756, + "z": 0.0 + }, + { + "x": 327.6267617532583, + "y": -133.51458291459252, + "z": 0.0 + }, + { + "x": 328.52292898248186, + "y": -133.543939973342, + "z": 0.0 + }, + { + "x": 329.2742202450984, + "y": -133.6035686906993, + "z": 0.0 + }, + { + "x": 330.0201302280161, + "y": -133.770079203602, + "z": 0.0 + }, + { + "x": 330.77655635636665, + "y": -134.05646514360325, + "z": 0.0 + }, + { + "x": 331.49554132796845, + "y": -134.42382055127538, + "z": 0.0 + }, + { + "x": 332.1406372704785, + "y": -134.84261994710033, + "z": 0.0 + }, + { + "x": 332.71762879810797, + "y": -135.33178515700604, + "z": 0.0 + }, + { + "x": 333.2591096594325, + "y": -135.95221620441072, + "z": 0.0 + }, + { + "x": 333.733370070378, + "y": -136.6249454962728, + "z": 0.0 + }, + { + "x": 334.1250015663861, + "y": -137.3152949237132, + "z": 0.0 + }, + { + "x": 334.4237299295962, + "y": -138.01139148784793, + "z": 0.0 + }, + { + "x": 334.63255558517926, + "y": -138.80940768845167, + "z": 0.0 + }, + { + "x": 334.7599229345734, + "y": -139.6608399232524, + "z": 0.0 + }, + { + "x": 334.81501194915836, + "y": -140.57541410661145, + "z": 0.0 + }, + { + "x": 334.8143891429836, + "y": -141.60653132155363, + "z": 0.0 + }, + { + "x": 334.81376633680884, + "y": -142.63764853649582, + "z": 0.0 + }, + { + "x": 334.8131435306341, + "y": -143.66876575143795, + "z": 0.0 + } + ] + }, + { + "id": 309, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 338.8131428009756, + "y": -143.6711817951316, + "z": 0.0 + }, + { + "x": 338.8137965285674, + "y": -142.58887111618375, + "z": 0.0 + }, + { + "x": 338.8144502561594, + "y": -141.50656043723603, + "z": 0.0 + }, + { + "x": 338.80452378079116, + "y": -140.40517317962764, + "z": 0.0 + }, + { + "x": 338.7221956474102, + "y": -139.2014638193349, + "z": 0.0 + }, + { + "x": 338.4870991066125, + "y": -137.90405253847825, + "z": 0.0 + }, + { + "x": 338.0897824986565, + "y": -136.63336212248896, + "z": 0.0 + }, + { + "x": 337.55332253795063, + "y": -135.43436195830742, + "z": 0.0 + }, + { + "x": 336.88172244657096, + "y": -134.30613350150293, + "z": 0.0 + }, + { + "x": 336.08408380664827, + "y": -133.26372464196993, + "z": 0.0 + }, + { + "x": 335.16573655510433, + "y": -132.31945660580425, + "z": 0.0 + }, + { + "x": 334.13272510353136, + "y": -131.48915637822523, + "z": 0.0 + }, + { + "x": 333.0025310364399, + "y": -130.7917272269492, + "z": 0.0 + }, + { + "x": 331.794475882789, + "y": -130.2398661749147, + "z": 0.0 + }, + { + "x": 330.527098532855, + "y": -129.84208429620077, + "z": 0.0 + }, + { + "x": 329.21984492042554, + "y": -129.60456021730624, + "z": 0.0 + }, + { + "x": 327.9164407754452, + "y": -129.51916243893834, + "z": 0.0 + }, + { + "x": 326.72353082855994, + "y": -129.5155689915624, + "z": 0.0 + }, + { + "x": 325.6412200602107, + "y": -129.51508542191746, + "z": 0.0 + } + ] + }, + { + "id": 312, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.42088944063468, + "y": -129.4149051882574, + "z": 0.0 + }, + { + "x": 100.36794814680857, + "y": -129.41443474070897, + "z": 0.0 + }, + { + "x": 99.31500685298246, + "y": -129.41396429316055, + "z": 0.0 + }, + { + "x": 98.30383981392947, + "y": -129.3924442708536, + "z": 0.0 + }, + { + "x": 97.38637308458796, + "y": -129.30831693031158, + "z": 0.0 + }, + { + "x": 96.70134847805133, + "y": -129.09939945587251, + "z": 0.0 + }, + { + "x": 95.97200630061965, + "y": -128.7864127775635, + "z": 0.0 + }, + { + "x": 95.28913333276736, + "y": -128.38290542597278, + "z": 0.0 + }, + { + "x": 94.66256411964747, + "y": -127.89454957337901, + "z": 0.0 + }, + { + "x": 94.11758375964698, + "y": -127.33284893767612, + "z": 0.0 + }, + { + "x": 93.6843654291251, + "y": -126.71718361991702, + "z": 0.0 + }, + { + "x": 93.25681560147831, + "y": -125.9858391418419, + "z": 0.0 + }, + { + "x": 92.90470702382066, + "y": -125.21397935800047, + "z": 0.0 + }, + { + "x": 92.63198694553196, + "y": -124.40914583329769, + "z": 0.0 + }, + { + "x": 92.47881422598947, + "y": -123.6229887138957, + "z": 0.0 + }, + { + "x": 92.41383599195208, + "y": -122.8063733641458, + "z": 0.0 + }, + { + "x": 92.38746542147446, + "y": -121.88066414921204, + "z": 0.0 + }, + { + "x": 92.38838000711462, + "y": -120.8929742241692, + "z": 0.0 + }, + { + "x": 92.38821994666225, + "y": -119.84003283741217, + "z": 0.0 + } + ] + }, + { + "id": 315, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.38821994146043, + "y": -119.84030264315575, + "z": 0.0 + }, + { + "x": 88.38837809887212, + "y": -120.8807250706138, + "z": 0.0 + }, + { + "x": 88.39108030715192, + "y": -122.02168901593632, + "z": 0.0 + }, + { + "x": 88.46172661639932, + "y": -123.24696762096069, + "z": 0.0 + }, + { + "x": 88.65163376427458, + "y": -124.47952527394865, + "z": 0.0 + }, + { + "x": 88.96882473851097, + "y": -125.68535880665443, + "z": 0.0 + }, + { + "x": 89.40991646639421, + "y": -126.85168614146029, + "z": 0.0 + }, + { + "x": 89.97029178657252, + "y": -127.96611544907387, + "z": 0.0 + }, + { + "x": 90.65011037520333, + "y": -129.02431893537982, + "z": 0.0 + }, + { + "x": 91.46501813655146, + "y": -130.0258988673246, + "z": 0.0 + }, + { + "x": 92.39252006977256, + "y": -130.91241263730257, + "z": 0.0 + }, + { + "x": 93.42051250911672, + "y": -131.67941488778277, + "z": 0.0 + }, + { + "x": 94.53360561789397, + "y": -132.3151872514357, + "z": 0.0 + }, + { + "x": 95.71546101136482, + "y": -132.8105823365878, + "z": 0.0 + }, + { + "x": 96.94879613644846, + "y": -133.15842236729776, + "z": 0.0 + }, + { + "x": 98.18776931068606, + "y": -133.3522153716725, + "z": 0.0 + }, + { + "x": 99.33790052318741, + "y": -133.41397492115766, + "z": 0.0 + }, + { + "x": 100.37832285881944, + "y": -133.41443977531426, + "z": 0.0 + }, + { + "x": 101.41874519445152, + "y": -133.4149046294709, + "z": 0.0 + } + ] + }, + { + "id": 318, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 101.42088944063467, + "y": -129.4149051882574, + "z": 0.0 + }, + { + "x": 100.34918903743696, + "y": -129.41442635925688, + "z": 0.0 + }, + { + "x": 99.27748863423932, + "y": -129.41394753025634, + "z": 0.0 + }, + { + "x": 98.2057882310415, + "y": -129.41346870125585, + "z": 0.0 + }, + { + "x": 96.9790928920085, + "y": -129.50438028664234, + "z": 0.0 + }, + { + "x": 95.72699472251898, + "y": -129.72334221739763, + "z": 0.0 + }, + { + "x": 94.47718775569217, + "y": -130.09316132412522, + "z": 0.0 + }, + { + "x": 93.20291881911453, + "y": -130.70787150482607, + "z": 0.0 + }, + { + "x": 92.09516742532853, + "y": -131.44996192328514, + "z": 0.0 + }, + { + "x": 91.10692769644493, + "y": -132.30271654696537, + "z": 0.0 + }, + { + "x": 90.21746230150107, + "y": -133.29915320805418, + "z": 0.0 + }, + { + "x": 89.45902662178946, + "y": -134.54870352378, + "z": 0.0 + }, + { + "x": 88.94031665824139, + "y": -135.76772659417503, + "z": 0.0 + }, + { + "x": 88.58295337176378, + "y": -137.04449451862712, + "z": 0.0 + }, + { + "x": 88.41675272056585, + "y": -138.42152323901217, + "z": 0.0 + }, + { + "x": 88.3914370555155, + "y": -139.66810737551634, + "z": 0.0 + }, + { + "x": 88.39140126344014, + "y": -140.76830532949788, + "z": 0.0 + }, + { + "x": 88.39156417551544, + "y": -141.84000582728214, + "z": 0.0 + } + ] + }, + { + "id": 321, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.39156417438086, + "y": -141.83969433821727, + "z": 0.0 + }, + { + "x": 92.39140217513292, + "y": -140.7739987822551, + "z": 0.0 + }, + { + "x": 92.391240175885, + "y": -139.7083032262929, + "z": 0.0 + }, + { + "x": 92.41837629818835, + "y": -138.69631264740704, + "z": 0.0 + }, + { + "x": 92.52586734119478, + "y": -137.8119269363075, + "z": 0.0 + }, + { + "x": 92.74283833555268, + "y": -137.0624991874285, + "z": 0.0 + }, + { + "x": 93.06753376102517, + "y": -136.3852570186857, + "z": 0.0 + }, + { + "x": 93.495580281799, + "y": -135.75125720493622, + "z": 0.0 + }, + { + "x": 94.01667929190448, + "y": -135.17314606117776, + "z": 0.0 + }, + { + "x": 94.61882399787538, + "y": -134.66403625537419, + "z": 0.0 + }, + { + "x": 95.29395983371364, + "y": -134.22585973512975, + "z": 0.0 + }, + { + "x": 96.02427251934127, + "y": -133.87263924746935, + "z": 0.0 + }, + { + "x": 96.7940290496316, + "y": -133.6140008174741, + "z": 0.0 + }, + { + "x": 97.59150682567487, + "y": -133.45419602327453, + "z": 0.0 + }, + { + "x": 98.37060357830944, + "y": -133.4171244960692, + "z": 0.0 + }, + { + "x": 99.30449982593655, + "y": -133.4138869326253, + "z": 0.0 + }, + { + "x": 100.35309976405674, + "y": -133.41442850579415, + "z": 0.0 + }, + { + "x": 101.41879522596236, + "y": -133.41490465182466, + "z": 0.0 + } + ] + }, + { + "id": 324, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 92.39156417438082, + "y": -141.83969433821727, + "z": 0.0 + }, + { + "x": 92.39140492299339, + "y": -140.79207530270236, + "z": 0.0 + }, + { + "x": 92.39124567160594, + "y": -139.7444562671874, + "z": 0.0 + }, + { + "x": 92.39108642021851, + "y": -138.69683723167248, + "z": 0.0 + }, + { + "x": 92.39092716883108, + "y": -137.64921819615756, + "z": 0.0 + }, + { + "x": 92.39076791744364, + "y": -136.6015991606426, + "z": 0.0 + }, + { + "x": 92.39060866605621, + "y": -135.55398012512768, + "z": 0.0 + }, + { + "x": 92.39044941466878, + "y": -134.50636108961277, + "z": 0.0 + }, + { + "x": 92.39029016328135, + "y": -133.45874205409785, + "z": 0.0 + }, + { + "x": 92.3901309118939, + "y": -132.41112301858288, + "z": 0.0 + }, + { + "x": 92.38997166050648, + "y": -131.36350398306797, + "z": 0.0 + }, + { + "x": 92.38981240911905, + "y": -130.31588494755306, + "z": 0.0 + }, + { + "x": 92.3896531577316, + "y": -129.2682659120381, + "z": 0.0 + }, + { + "x": 92.38949390634417, + "y": -128.22064687652318, + "z": 0.0 + }, + { + "x": 92.38933465495673, + "y": -127.17302784100823, + "z": 0.0 + }, + { + "x": 92.3891754035693, + "y": -126.12540880549332, + "z": 0.0 + }, + { + "x": 92.38901615218187, + "y": -125.07778976997838, + "z": 0.0 + }, + { + "x": 92.38885690079442, + "y": -124.03017073446344, + "z": 0.0 + }, + { + "x": 92.38869764940699, + "y": -122.98255169894853, + "z": 0.0 + }, + { + "x": 92.38853839801956, + "y": -121.93493266343359, + "z": 0.0 + }, + { + "x": 92.38837914663212, + "y": -120.88731362791864, + "z": 0.0 + }, + { + "x": 92.38821989524469, + "y": -119.83969459240373, + "z": 0.0 + } + ] + }, + { + "id": 325, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.38821996456826, + "y": -119.84060666853175, + "z": 0.0 + }, + { + "x": 86.3883792159557, + "y": -120.88822570404673, + "z": 0.0 + }, + { + "x": 86.38853846734314, + "y": -121.9358447395616, + "z": 0.0 + }, + { + "x": 86.38869771873057, + "y": -122.98346377507654, + "z": 0.0 + }, + { + "x": 86.388856970118, + "y": -124.03108281059153, + "z": 0.0 + }, + { + "x": 86.38901622150544, + "y": -125.0787018461064, + "z": 0.0 + }, + { + "x": 86.38917547289287, + "y": -126.12632088162133, + "z": 0.0 + }, + { + "x": 86.3893347242803, + "y": -127.17393991713632, + "z": 0.0 + }, + { + "x": 86.38949397566775, + "y": -128.22155895265118, + "z": 0.0 + }, + { + "x": 86.38965322705518, + "y": -129.26917798816618, + "z": 0.0 + }, + { + "x": 86.38981247844262, + "y": -130.31679702368103, + "z": 0.0 + }, + { + "x": 86.38997172983005, + "y": -131.36441605919597, + "z": 0.0 + }, + { + "x": 86.39013098121748, + "y": -132.41203509471094, + "z": 0.0 + }, + { + "x": 86.39029023260493, + "y": -133.45965413022583, + "z": 0.0 + }, + { + "x": 86.39044948399236, + "y": -134.50727316574077, + "z": 0.0 + }, + { + "x": 86.39060873537979, + "y": -135.55489220125568, + "z": 0.0 + }, + { + "x": 86.39076798676722, + "y": -136.60251123677068, + "z": 0.0 + }, + { + "x": 86.39092723815466, + "y": -137.65013027228557, + "z": 0.0 + }, + { + "x": 86.39108648954209, + "y": -138.69774930780048, + "z": 0.0 + }, + { + "x": 86.39124574092952, + "y": -139.74536834331548, + "z": 0.0 + }, + { + "x": 86.39140499231696, + "y": -140.79298737883033, + "z": 0.0 + }, + { + "x": 86.3915642437044, + "y": -141.84060641434527, + "z": 0.0 + } + ] + }, + { + "id": 327, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 88.3882199414604, + "y": -119.84030264315575, + "z": 0.0 + }, + { + "x": 88.38837919284784, + "y": -120.8879216786707, + "z": 0.0 + }, + { + "x": 88.38853844423528, + "y": -121.9355407141856, + "z": 0.0 + }, + { + "x": 88.38869769562271, + "y": -122.98315974970055, + "z": 0.0 + }, + { + "x": 88.38885694701014, + "y": -124.03077878521549, + "z": 0.0 + }, + { + "x": 88.38901619839758, + "y": -125.0783978207304, + "z": 0.0 + }, + { + "x": 88.38917544978501, + "y": -126.12601685624531, + "z": 0.0 + }, + { + "x": 88.38933470117244, + "y": -127.17363589176028, + "z": 0.0 + }, + { + "x": 88.38949395255989, + "y": -128.2212549272752, + "z": 0.0 + }, + { + "x": 88.38965320394732, + "y": -129.26887396279017, + "z": 0.0 + }, + { + "x": 88.38981245533476, + "y": -130.31649299830502, + "z": 0.0 + }, + { + "x": 88.38997170672219, + "y": -131.36411203382, + "z": 0.0 + }, + { + "x": 88.39013095810962, + "y": -132.4117310693349, + "z": 0.0 + }, + { + "x": 88.39029020949707, + "y": -133.45935010484982, + "z": 0.0 + }, + { + "x": 88.3904494608845, + "y": -134.5069691403648, + "z": 0.0 + }, + { + "x": 88.39060871227193, + "y": -135.5545881758797, + "z": 0.0 + }, + { + "x": 88.39076796365936, + "y": -136.60220721139467, + "z": 0.0 + }, + { + "x": 88.3909272150468, + "y": -137.64982624690958, + "z": 0.0 + }, + { + "x": 88.39108646643423, + "y": -138.6974452824245, + "z": 0.0 + }, + { + "x": 88.39124571782166, + "y": -139.74506431793947, + "z": 0.0 + }, + { + "x": 88.3914049692091, + "y": -140.79268335345432, + "z": 0.0 + }, + { + "x": 88.39156422059654, + "y": -141.8403023889693, + "z": 0.0 + } + ] + }, + { + "id": 330, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 154.02380798404414, + "y": -46.19753632639322, + "z": 0.0 + }, + { + "x": 154.0225405266159, + "y": -47.24090674538898, + "z": 0.0 + }, + { + "x": 154.0212957833844, + "y": -48.27687009781563, + "z": 0.0 + }, + { + "x": 154.0224864762247, + "y": -49.17072627310411, + "z": 0.0 + }, + { + "x": 153.97584197898615, + "y": -49.95867202474612, + "z": 0.0 + }, + { + "x": 153.82494572714774, + "y": -50.74658892864789, + "z": 0.0 + }, + { + "x": 153.58120716526844, + "y": -51.50973756548072, + "z": 0.0 + }, + { + "x": 153.2478967703762, + "y": -52.238362120680605, + "z": 0.0 + }, + { + "x": 152.85518620641, + "y": -52.89185296315288, + "z": 0.0 + }, + { + "x": 152.36821934903156, + "y": -53.49002530084883, + "z": 0.0 + }, + { + "x": 151.77805426447105, + "y": -54.03404810038104, + "z": 0.0 + }, + { + "x": 151.12816900523492, + "y": -54.502702172032784, + "z": 0.0 + }, + { + "x": 150.42705192181336, + "y": -54.890159888561826, + "z": 0.0 + }, + { + "x": 149.70552810564578, + "y": -55.18594301814386, + "z": 0.0 + }, + { + "x": 148.97820888894648, + "y": -55.38229475154137, + "z": 0.0 + }, + { + "x": 148.07093106567913, + "y": -55.468392753747764, + "z": 0.0 + }, + { + "x": 147.0768176160945, + "y": -55.49309497508044, + "z": 0.0 + }, + { + "x": 146.033446435009, + "y": -55.493222112765466, + "z": 0.0 + }, + { + "x": 144.99007525392352, + "y": -55.49334925045048, + "z": 0.0 + } + ] + }, + { + "id": 333, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 144.9902393330799, + "y": -59.49334926015318, + "z": 0.0 + }, + { + "x": 146.03540288443622, + "y": -59.49322190406285, + "z": 0.0 + }, + { + "x": 147.14476546935714, + "y": -59.492056076550405, + "z": 0.0 + }, + { + "x": 148.33262517418535, + "y": -59.44855439963858, + "z": 0.0 + }, + { + "x": 149.60538379079756, + "y": -59.30093427784442, + "z": 0.0 + }, + { + "x": 150.90060319706703, + "y": -58.985014243942075, + "z": 0.0 + }, + { + "x": 152.08417688840183, + "y": -58.51452779035827, + "z": 0.0 + }, + { + "x": 153.2043480453745, + "y": -57.91271930306415, + "z": 0.0 + }, + { + "x": 154.25115479129303, + "y": -57.18432584118074, + "z": 0.0 + }, + { + "x": 155.2151905323367, + "y": -56.32888544962101, + "z": 0.0 + }, + { + "x": 156.10325086827027, + "y": -55.30748534345081, + "z": 0.0 + }, + { + "x": 156.80994358117522, + "y": -54.18961306282728, + "z": 0.0 + }, + { + "x": 157.34809131646162, + "y": -53.00526040834794, + "z": 0.0 + }, + { + "x": 157.72881984867638, + "y": -51.762961636059096, + "z": 0.0 + }, + { + "x": 157.94460164983178, + "y": -50.51353312774852, + "z": 0.0 + }, + { + "x": 158.01999573792733, + "y": -49.3382052613273, + "z": 0.0 + }, + { + "x": 158.02126537267566, + "y": -48.29304247336981, + "z": 0.0 + }, + { + "x": 158.02253500742398, + "y": -47.24787968541243, + "z": 0.0 + }, + { + "x": 158.02380464217237, + "y": -46.20271689745499, + "z": 0.0 + } + ] + }, + { + "id": 336, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 154.02380798404414, + "y": -46.19753632639322, + "z": 0.0 + }, + { + "x": 154.02254778720277, + "y": -47.234929833323704, + "z": 0.0 + }, + { + "x": 154.0220914396506, + "y": -48.33150366522665, + "z": 0.0 + }, + { + "x": 154.05846020639342, + "y": -49.48693261271582, + "z": 0.0 + }, + { + "x": 154.20277863371672, + "y": -50.83323725619208, + "z": 0.0 + }, + { + "x": 154.5289316112454, + "y": -52.066952903777064, + "z": 0.0 + }, + { + "x": 154.99269289011525, + "y": -53.23838843678435, + "z": 0.0 + }, + { + "x": 155.585298044496, + "y": -54.34977085110656, + "z": 0.0 + }, + { + "x": 156.35177765287185, + "y": -55.44682750656724, + "z": 0.0 + }, + { + "x": 157.22845126186928, + "y": -56.37736680088315, + "z": 0.0 + }, + { + "x": 158.17629805628042, + "y": -57.17180132816905, + "z": 0.0 + }, + { + "x": 159.19562702626834, + "y": -57.86450820303658, + "z": 0.0 + }, + { + "x": 160.34457511087447, + "y": -58.480341225935305, + "z": 0.0 + }, + { + "x": 161.52777689166018, + "y": -58.92780889150592, + "z": 0.0 + }, + { + "x": 162.72257395798567, + "y": -59.232723132560764, + "z": 0.0 + }, + { + "x": 163.9265285225684, + "y": -59.41699686668909, + "z": 0.0 + }, + { + "x": 165.09545063910528, + "y": -59.49089938414677, + "z": 0.0 + }, + { + "x": 166.1328449037599, + "y": -59.49077297476566, + "z": 0.0 + }, + { + "x": 167.1702391684145, + "y": -59.49064656538455, + "z": 0.0 + } + ] + }, + { + "id": 339, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 167.16975175727498, + "y": -55.49064659508076, + "z": 0.0 + }, + { + "x": 166.1275959090633, + "y": -55.49077358467409, + "z": 0.0 + }, + { + "x": 165.08544006085165, + "y": -55.49090057426743, + "z": 0.0 + }, + { + "x": 164.15696033917465, + "y": -55.436032284142954, + "z": 0.0 + }, + { + "x": 163.29745779390208, + "y": -55.306797258467256, + "z": 0.0 + }, + { + "x": 162.4896909874122, + "y": -55.0948741218035, + "z": 0.0 + }, + { + "x": 161.7535005053105, + "y": -54.794716920603875, + "z": 0.0 + }, + { + "x": 161.0666842268726, + "y": -54.40656115534773, + "z": 0.0 + }, + { + "x": 160.38320540908438, + "y": -53.92896674701984, + "z": 0.0 + }, + { + "x": 159.7578042552214, + "y": -53.384689518722695, + "z": 0.0 + }, + { + "x": 159.21527538386513, + "y": -52.78400531379128, + "z": 0.0 + }, + { + "x": 158.83996895929573, + "y": -52.17304058119422, + "z": 0.0 + }, + { + "x": 158.49249840672923, + "y": -51.45319407003326, + "z": 0.0 + }, + { + "x": 158.2337868647591, + "y": -50.697068790528874, + "z": 0.0 + }, + { + "x": 158.06719322456945, + "y": -49.91369373265339, + "z": 0.0 + }, + { + "x": 158.0261760820918, + "y": -49.161618091827535, + "z": 0.0 + }, + { + "x": 158.02114821720056, + "y": -48.262173506744624, + "z": 0.0 + }, + { + "x": 158.02253863907634, + "y": -47.24489011075576, + "z": 0.0 + }, + { + "x": 158.02380462015304, + "y": -46.20273502374609, + "z": 0.0 + } + ] + }, + { + "id": 342, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 167.16975175727498, + "y": -55.49064659508076, + "z": 0.0 + }, + { + "x": 166.11356128892572, + "y": -55.490775294831636, + "z": 0.0 + }, + { + "x": 165.0573708205764, + "y": -55.490903994582524, + "z": 0.0 + }, + { + "x": 164.00118035222715, + "y": -55.49103269433341, + "z": 0.0 + }, + { + "x": 162.9449898838779, + "y": -55.4911613940843, + "z": 0.0 + }, + { + "x": 161.88879941552864, + "y": -55.49129009383519, + "z": 0.0 + }, + { + "x": 160.83260894717938, + "y": -55.49141879358608, + "z": 0.0 + }, + { + "x": 159.77641847883012, + "y": -55.49154749333697, + "z": 0.0 + }, + { + "x": 158.72022801048087, + "y": -55.491676193087855, + "z": 0.0 + }, + { + "x": 157.66403754213155, + "y": -55.49180489283874, + "z": 0.0 + }, + { + "x": 156.60784707378232, + "y": -55.49193359258962, + "z": 0.0 + }, + { + "x": 155.55165660543304, + "y": -55.492062292340506, + "z": 0.0 + }, + { + "x": 154.49546613708378, + "y": -55.492190992091395, + "z": 0.0 + }, + { + "x": 153.43927566873452, + "y": -55.49231969184228, + "z": 0.0 + }, + { + "x": 152.38308520038527, + "y": -55.49244839159317, + "z": 0.0 + }, + { + "x": 151.326894732036, + "y": -55.49257709134406, + "z": 0.0 + }, + { + "x": 150.2707042636867, + "y": -55.49270579109495, + "z": 0.0 + }, + { + "x": 149.21451379533744, + "y": -55.49283449084584, + "z": 0.0 + }, + { + "x": 148.15832332698818, + "y": -55.492963190596726, + "z": 0.0 + }, + { + "x": 147.10213285863895, + "y": -55.4930918903476, + "z": 0.0 + }, + { + "x": 146.04594239028967, + "y": -55.49322059009849, + "z": 0.0 + }, + { + "x": 144.9897519219404, + "y": -55.49334928984938, + "z": 0.0 + } + ] + }, + { + "id": 343, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 144.99048303864967, + "y": -61.493349245305076, + "z": 0.0 + }, + { + "x": 146.04667350699893, + "y": -61.49322054555419, + "z": 0.0 + }, + { + "x": 147.10286397534819, + "y": -61.4930918458033, + "z": 0.0 + }, + { + "x": 148.15905444369747, + "y": -61.49296314605242, + "z": 0.0 + }, + { + "x": 149.21524491204673, + "y": -61.49283444630153, + "z": 0.0 + }, + { + "x": 150.27143538039599, + "y": -61.49270574655064, + "z": 0.0 + }, + { + "x": 151.32762584874524, + "y": -61.49257704679975, + "z": 0.0 + }, + { + "x": 152.38381631709453, + "y": -61.49244834704887, + "z": 0.0 + }, + { + "x": 153.44000678544379, + "y": -61.49231964729798, + "z": 0.0 + }, + { + "x": 154.49619725379307, + "y": -61.492190947547094, + "z": 0.0 + }, + { + "x": 155.55238772214233, + "y": -61.492062247796206, + "z": 0.0 + }, + { + "x": 156.60857819049156, + "y": -61.49193354804532, + "z": 0.0 + }, + { + "x": 157.66476865884084, + "y": -61.491804848294436, + "z": 0.0 + }, + { + "x": 158.72095912719013, + "y": -61.49167614854355, + "z": 0.0 + }, + { + "x": 159.77714959553938, + "y": -61.49154744879266, + "z": 0.0 + }, + { + "x": 160.8333400638886, + "y": -61.49141874904177, + "z": 0.0 + }, + { + "x": 161.88953053223793, + "y": -61.49129004929089, + "z": 0.0 + }, + { + "x": 162.94572100058718, + "y": -61.49116134954, + "z": 0.0 + }, + { + "x": 164.00191146893644, + "y": -61.49103264978911, + "z": 0.0 + }, + { + "x": 165.0581019372857, + "y": -61.49090395003822, + "z": 0.0 + }, + { + "x": 166.11429240563496, + "y": -61.490775250287335, + "z": 0.0 + }, + { + "x": 167.1704828739842, + "y": -61.490646550536454, + "z": 0.0 + } + ] + }, + { + "id": 345, + "map_element_id": 2, + "type": "lane", + "geometry": [ + { + "x": 144.9902393330799, + "y": -59.49334926015318, + "z": 0.0 + }, + { + "x": 146.04642980142916, + "y": -59.49322056040229, + "z": 0.0 + }, + { + "x": 147.10262026977844, + "y": -59.493091860651404, + "z": 0.0 + }, + { + "x": 148.15881073812773, + "y": -59.492963160900516, + "z": 0.0 + }, + { + "x": 149.21500120647698, + "y": -59.49283446114963, + "z": 0.0 + }, + { + "x": 150.27119167482624, + "y": -59.49270576139874, + "z": 0.0 + }, + { + "x": 151.3273821431755, + "y": -59.49257706164785, + "z": 0.0 + }, + { + "x": 152.38357261152476, + "y": -59.492448361896976, + "z": 0.0 + }, + { + "x": 153.439763079874, + "y": -59.49231966214609, + "z": 0.0 + }, + { + "x": 154.49595354822333, + "y": -59.4921909623952, + "z": 0.0 + }, + { + "x": 155.55214401657258, + "y": -59.49206226264431, + "z": 0.0 + }, + { + "x": 156.6083344849218, + "y": -59.49193356289342, + "z": 0.0 + }, + { + "x": 157.6645249532711, + "y": -59.491804863142534, + "z": 0.0 + }, + { + "x": 158.72071542162035, + "y": -59.491676163391645, + "z": 0.0 + }, + { + "x": 159.7769058899696, + "y": -59.49154746364076, + "z": 0.0 + }, + { + "x": 160.83309635831887, + "y": -59.49141876388987, + "z": 0.0 + }, + { + "x": 161.88928682666818, + "y": -59.491290064138994, + "z": 0.0 + }, + { + "x": 162.94547729501744, + "y": -59.491161364388105, + "z": 0.0 + }, + { + "x": 164.0016677633667, + "y": -59.49103266463722, + "z": 0.0 + }, + { + "x": 165.05785823171595, + "y": -59.49090396488633, + "z": 0.0 + }, + { + "x": 166.1140487000652, + "y": -59.49077526513544, + "z": 0.0 + }, + { + "x": 167.17023916841447, + "y": -59.49064656538455, + "z": 0.0 + } + ] + }, + { + "id": 130, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.6278797108732, + "y": -3.988677721665928, + "z": 0.0 + }, + { + "x": 326.6645461982845, + "y": -3.989228436985744, + "z": 0.0 + }, + { + "x": 327.7012126856958, + "y": -3.9897791523055597, + "z": 0.0 + }, + { + "x": 328.7378791731071, + "y": -3.9903298676253756, + "z": 0.0 + }, + { + "x": 329.5221086572586, + "y": -4.047669202376462, + "z": 0.0 + }, + { + "x": 330.1072270974078, + "y": -4.173581082904699, + "z": 0.0 + }, + { + "x": 330.6144262143476, + "y": -4.360288299602975, + "z": 0.0 + }, + { + "x": 331.0954172342736, + "y": -4.608571370574099, + "z": 0.0 + }, + { + "x": 331.54334222118945, + "y": -4.915257127838937, + "z": 0.0 + }, + { + "x": 331.843444119078, + "y": -5.226392769639217, + "z": 0.0 + }, + { + "x": 332.1744536934619, + "y": -5.632692116040604, + "z": 0.0 + }, + { + "x": 332.45058407826144, + "y": -6.077457497319008, + "z": 0.0 + }, + { + "x": 332.66638742822687, + "y": -6.552465214205474, + "z": 0.0 + }, + { + "x": 332.81977427548895, + "y": -7.051954724123156, + "z": 0.0 + }, + { + "x": 332.9081272804868, + "y": -7.568500980156154, + "z": 0.0 + }, + { + "x": 332.895030754424, + "y": -7.996637987132772, + "z": 0.0 + }, + { + "x": 332.89493219600683, + "y": -8.76330403472345, + "z": 0.0 + }, + { + "x": 332.8940312132095, + "y": -9.750603823797125, + "z": 0.0 + }, + { + "x": 332.89340505523876, + "y": -10.787270268385383, + "z": 0.0 + } + ] + }, + { + "id": 145, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 340.89340340765136, + "y": -10.792414056232396, + "z": 0.0 + }, + { + "x": 340.89403578359634, + "y": -9.745453140274215, + "z": 0.0 + }, + { + "x": 340.89466815954137, + "y": -8.698492224316256, + "z": 0.0 + }, + { + "x": 340.89530053548634, + "y": -7.651531308357863, + "z": 0.0 + }, + { + "x": 340.9521407676781, + "y": -6.852378699784743, + "z": 0.0 + }, + { + "x": 341.06688111734144, + "y": -6.135182389188954, + "z": 0.0 + }, + { + "x": 341.22823435714616, + "y": -5.902494853710985, + "z": 0.0 + }, + { + "x": 341.46602833031625, + "y": -5.569414699066449, + "z": 0.0 + }, + { + "x": 341.7994848809484, + "y": -5.165549665951211, + "z": 0.0 + }, + { + "x": 342.13051965040574, + "y": -4.903219459030506, + "z": 0.0 + }, + { + "x": 342.4962616320244, + "y": -4.698227530630974, + "z": 0.0 + }, + { + "x": 343.01185668684076, + "y": -4.43994024407544, + "z": 0.0 + }, + { + "x": 343.5712427807595, + "y": -4.230705239368477, + "z": 0.0 + }, + { + "x": 344.1507044170504, + "y": -4.085364578513123, + "z": 0.0 + }, + { + "x": 344.743421203216, + "y": -4.005725125338712, + "z": 0.0 + }, + { + "x": 345.34990134625497, + "y": -3.9905056740214206, + "z": 0.0 + }, + { + "x": 346.13395460346004, + "y": -3.9995713019347696, + "z": 0.0 + }, + { + "x": 347.1809155626665, + "y": -4.000127486056169, + "z": 0.0 + }, + { + "x": 348.227876521873, + "y": -4.0006836701775725, + "z": 0.0 + } + ] + }, + { + "id": 157, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 160.06653921314967, + "y": -11.024850911531969, + "z": 0.0 + }, + { + "x": 160.0678134667562, + "y": -9.975885881460881, + "z": 0.0 + }, + { + "x": 160.06908772036275, + "y": -8.926920851389792, + "z": 0.0 + }, + { + "x": 160.07036197396928, + "y": -7.877955821318704, + "z": 0.0 + }, + { + "x": 160.1246918567833, + "y": -7.060305047376277, + "z": 0.0 + }, + { + "x": 160.2374873644286, + "y": -6.342829622261114, + "z": 0.0 + }, + { + "x": 160.41028615430943, + "y": -5.865624377840656, + "z": 0.0 + }, + { + "x": 160.62898947587635, + "y": -5.516198652845463, + "z": 0.0 + }, + { + "x": 160.9587560838947, + "y": -5.0263174378290145, + "z": 0.0 + }, + { + "x": 161.34037650307783, + "y": -4.58232228373524, + "z": 0.0 + }, + { + "x": 161.6532871645665, + "y": -4.377425268002961, + "z": 0.0 + }, + { + "x": 162.012325453008, + "y": -4.210009421664965, + "z": 0.0 + }, + { + "x": 162.4819073479187, + "y": -4.032043747178901, + "z": 0.0 + }, + { + "x": 162.97457927269812, + "y": -3.9185762591816546, + "z": 0.0 + }, + { + "x": 163.2561113559408, + "y": -3.9648852550583116, + "z": 0.0 + }, + { + "x": 164.00033558629985, + "y": -3.954801759477505, + "z": 0.0 + }, + { + "x": 164.93268600146294, + "y": -3.956601518087601, + "z": 0.0 + }, + { + "x": 165.98165179952323, + "y": -3.956713473436086, + "z": 0.0 + }, + { + "x": 167.03061759758353, + "y": -3.9568254287845774, + "z": 0.0 + }, + { + "x": 168.07958339564385, + "y": -3.956937384133062, + "z": 0.0 + } + ] + }, + { + "id": 160, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 144.95958352573157, + "y": -3.9544698035434926, + "z": 0.0 + }, + { + "x": 146.0212169873974, + "y": -3.954583110902402, + "z": 0.0 + }, + { + "x": 147.0828504490632, + "y": -3.954696418261311, + "z": 0.0 + }, + { + "x": 148.144483910729, + "y": -3.954809725620221, + "z": 0.0 + }, + { + "x": 149.05967964890726, + "y": -3.990641340582388, + "z": 0.0 + }, + { + "x": 149.83780177823488, + "y": -4.082760914533853, + "z": 0.0 + }, + { + "x": 150.1743168945339, + "y": -4.242647825577707, + "z": 0.0 + }, + { + "x": 150.61969315594254, + "y": -4.4830122559322945, + "z": 0.0 + }, + { + "x": 151.07615455507627, + "y": -4.797092817882984, + "z": 0.0 + }, + { + "x": 151.48742320962174, + "y": -5.166058802470898, + "z": 0.0 + }, + { + "x": 151.67611237008094, + "y": -5.475440162631369, + "z": 0.0 + }, + { + "x": 151.84574867678245, + "y": -5.848483006759167, + "z": 0.0 + }, + { + "x": 152.00442107081673, + "y": -6.310109105308325, + "z": 0.0 + }, + { + "x": 152.09632190626255, + "y": -6.791790352034089, + "z": 0.0 + }, + { + "x": 152.07837964471057, + "y": -7.181400923116529, + "z": 0.0 + }, + { + "x": 152.07070286095353, + "y": -7.883406095020281, + "z": 0.0 + }, + { + "x": 152.0691243996725, + "y": -8.891867371922766, + "z": 0.0 + }, + { + "x": 152.06783475775103, + "y": -9.953500056324756, + "z": 0.0 + }, + { + "x": 152.06654511582957, + "y": -11.015132740726854, + "z": 0.0 + } + ] + }, + { + "id": 175, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 86.42210766601971, + "y": -317.12405432540857, + "z": 0.0 + }, + { + "x": 86.40322153115828, + "y": -317.8749514922378, + "z": 0.0 + }, + { + "x": 86.3292625154455, + "y": -318.58491186848573, + "z": 0.0 + }, + { + "x": 86.18953213327386, + "y": -319.2846940672616, + "z": 0.0 + }, + { + "x": 85.98523313042162, + "y": -319.9683863526217, + "z": 0.0 + }, + { + "x": 85.71807956869432, + "y": -320.6302044599884, + "z": 0.0 + }, + { + "x": 85.39028468437556, + "y": -321.2645335106671, + "z": 0.0 + }, + { + "x": 85.01656276535591, + "y": -321.85141823949874, + "z": 0.0 + }, + { + "x": 84.61328386764781, + "y": -322.3733874363649, + "z": 0.0 + }, + { + "x": 84.14776694319312, + "y": -322.855932640633, + "z": 0.0 + }, + { + "x": 83.63582517526206, + "y": -323.28636997869825, + "z": 0.0 + }, + { + "x": 83.08208237236526, + "y": -323.6607081495259, + "z": 0.0 + }, + { + "x": 82.49250331518978, + "y": -323.9750097865338, + "z": 0.0 + }, + { + "x": 81.87341023048599, + "y": -324.22594956684435, + "z": 0.0 + }, + { + "x": 81.23142281271754, + "y": -324.41084517913515, + "z": 0.0 + }, + { + "x": 80.53314857363705, + "y": -324.5294262581491, + "z": 0.0 + }, + { + "x": 79.44718379231384, + "y": -324.5819492549276, + "z": 0.0 + } + ] + }, + { + "id": 178, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.8061452205714, + "y": -324.5933877050256, + "z": 0.0 + }, + { + "x": 100.76420901034751, + "y": -324.544843879481, + "z": 0.0 + }, + { + "x": 100.05547286081084, + "y": -324.4402442473347, + "z": 0.0 + }, + { + "x": 99.40097425397511, + "y": -324.2766062823703, + "z": 0.0 + }, + { + "x": 98.76392649992916, + "y": -324.05345432697123, + "z": 0.0 + }, + { + "x": 98.14938349604752, + "y": -323.77243681296414, + "z": 0.0 + }, + { + "x": 97.61695804856943, + "y": -323.4496290183012, + "z": 0.0 + }, + { + "x": 97.09931695655445, + "y": -323.0644970568544, + "z": 0.0 + }, + { + "x": 96.59466314544346, + "y": -322.6165467084913, + "z": 0.0 + }, + { + "x": 96.13171624236867, + "y": -322.1232796739841, + "z": 0.0 + }, + { + "x": 95.71288930249867, + "y": -321.58761950526895, + "z": 0.0 + }, + { + "x": 95.34255411729966, + "y": -321.0145041533784, + "z": 0.0 + }, + { + "x": 95.04377845391564, + "y": -320.42949063438977, + "z": 0.0 + }, + { + "x": 94.82285770290609, + "y": -319.84438630967213, + "z": 0.0 + }, + { + "x": 94.63316860976981, + "y": -319.18946806306946, + "z": 0.0 + }, + { + "x": 94.50330750802169, + "y": -318.5204043978035, + "z": 0.0 + }, + { + "x": 94.43423405405653, + "y": -317.8422626237095, + "z": 0.0 + }, + { + "x": 94.41410567044805, + "y": -317.1186932324719, + "z": 0.0 + } + ] + }, + { + "id": 193, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.37171129421938, + "y": -11.239391821784363, + "z": 0.0 + }, + { + "x": 94.37155111803641, + "y": -10.185689112991424, + "z": 0.0 + }, + { + "x": 94.37139094185343, + "y": -9.131986404198488, + "z": 0.0 + }, + { + "x": 94.39012123383223, + "y": -8.153369150514646, + "z": 0.0 + }, + { + "x": 94.46022890273653, + "y": -7.337998068902732, + "z": 0.0 + }, + { + "x": 94.6072688252939, + "y": -6.9230812441710645, + "z": 0.0 + }, + { + "x": 94.83259463557908, + "y": -6.385304482763309, + "z": 0.0 + }, + { + "x": 95.11913777981543, + "y": -5.877941833921707, + "z": 0.0 + }, + { + "x": 95.46350212363555, + "y": -5.407065852095712, + "z": 0.0 + }, + { + "x": 95.86171945887978, + "y": -4.978761611453896, + "z": 0.0 + }, + { + "x": 96.22923305880748, + "y": -4.719862677775668, + "z": 0.0 + }, + { + "x": 96.70185990677085, + "y": -4.441451745629632, + "z": 0.0 + }, + { + "x": 97.22041617308734, + "y": -4.211505752369636, + "z": 0.0 + }, + { + "x": 97.76345113210014, + "y": -4.0457846580773555, + "z": 0.0 + }, + { + "x": 98.32410668616481, + "y": -3.9466924676147257, + "z": 0.0 + }, + { + "x": 98.79512439302592, + "y": -3.95205280415974, + "z": 0.0 + }, + { + "x": 99.58080669915239, + "y": -3.9508205713340376, + "z": 0.0 + }, + { + "x": 100.5839090094622, + "y": -3.9508137140995263, + "z": 0.0 + }, + { + "x": 101.63761167446887, + "y": -3.9504703020497662, + "z": 0.0 + }, + { + "x": 102.69131433947555, + "y": -3.950126890000005, + "z": 0.0 + } + ] + }, + { + "id": 196, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 79.38131557415628, + "y": -3.9577238475875323, + "z": 0.0 + }, + { + "x": 80.43950680616342, + "y": -3.9573789726696917, + "z": 0.0 + }, + { + "x": 81.49769803817055, + "y": -3.957034097751851, + "z": 0.0 + }, + { + "x": 82.54733788811372, + "y": -3.958919061057222, + "z": 0.0 + }, + { + "x": 83.25546999072677, + "y": -4.0367179671995865, + "z": 0.0 + }, + { + "x": 83.85626624649599, + "y": -4.181507034273803, + "z": 0.0 + }, + { + "x": 84.45707638728011, + "y": -4.39224457294905, + "z": 0.0 + }, + { + "x": 84.89020730165043, + "y": -4.641475916963456, + "z": 0.0 + }, + { + "x": 85.27499915893839, + "y": -4.945073788381097, + "z": 0.0 + }, + { + "x": 85.72199976105516, + "y": -5.3529289744430795, + "z": 0.0 + }, + { + "x": 86.08734206940625, + "y": -5.789075209259341, + "z": 0.0 + }, + { + "x": 86.12224575164548, + "y": -6.021688634033307, + "z": 0.0 + }, + { + "x": 86.28329841826393, + "y": -6.499402208514818, + "z": 0.0 + }, + { + "x": 86.38562053758875, + "y": -7.009892905183665, + "z": 0.0 + }, + { + "x": 86.38134700142467, + "y": -7.4383508230156385, + "z": 0.0 + }, + { + "x": 86.37153594890037, + "y": -8.114997986374872, + "z": 0.0 + }, + { + "x": 86.37138966964652, + "y": -9.124225371328526, + "z": 0.0 + }, + { + "x": 86.37155052814866, + "y": -10.182416647308463, + "z": 0.0 + }, + { + "x": 86.3717113866508, + "y": -11.2406079232884, + "z": 0.0 + } + ] + }, + { + "id": 202, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 347.7299857691685, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 346.65641965126474, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 345.6051845218596, + "y": -324.60410859498836, + "z": 0.0 + }, + { + "x": 344.7850166539401, + "y": -324.540098970213, + "z": 0.0 + }, + { + "x": 344.25909294341756, + "y": -324.39700813462906, + "z": 0.0 + }, + { + "x": 343.71191033205946, + "y": -324.1828372395416, + "z": 0.0 + }, + { + "x": 343.19528786244814, + "y": -323.904106512928, + "z": 0.0 + }, + { + "x": 342.7166149375832, + "y": -323.5649536309744, + "z": 0.0 + }, + { + "x": 342.28274894064253, + "y": -323.17033164995433, + "z": 0.0 + }, + { + "x": 341.8913001189622, + "y": -322.7206360617598, + "z": 0.0 + }, + { + "x": 341.5369668432233, + "y": -322.2125067112267, + "z": 0.0 + }, + { + "x": 341.2324938951551, + "y": -321.65769348921674, + "z": 0.0 + }, + { + "x": 340.99061304977715, + "y": -321.07212369910155, + "z": 0.0 + }, + { + "x": 340.83080310229485, + "y": -320.48807252445783, + "z": 0.0 + }, + { + "x": 340.74178785032285, + "y": -319.89215389539834, + "z": 0.0 + }, + { + "x": 340.71272597311315, + "y": -319.2600014064103, + "z": 0.0 + }, + { + "x": 340.70687562977463, + "y": -318.4515480594214, + "z": 0.0 + }, + { + "x": 340.70817719947337, + "y": -317.45271969952285, + "z": 0.0 + } + ] + }, + { + "id": 217, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.7081788771901, + "y": -317.4475260295794, + "z": 0.0 + }, + { + "x": 332.7075245816217, + "y": -318.5307770498437, + "z": 0.0 + }, + { + "x": 332.7149193734867, + "y": -319.3628031526399, + "z": 0.0 + }, + { + "x": 332.69090557791753, + "y": -320.049931684257, + "z": 0.0 + }, + { + "x": 332.6128631715627, + "y": -320.679999573273, + "z": 0.0 + }, + { + "x": 332.4651120128465, + "y": -321.2974806177943, + "z": 0.0 + }, + { + "x": 332.31111388697144, + "y": -321.78577852098607, + "z": 0.0 + }, + { + "x": 332.0291584429973, + "y": -322.3238751397163, + "z": 0.0 + }, + { + "x": 331.67371169882904, + "y": -322.8360704972532, + "z": 0.0 + }, + { + "x": 331.276763504097, + "y": -323.2917244669964, + "z": 0.0 + }, + { + "x": 330.83781253302345, + "y": -323.6909312682483, + "z": 0.0 + }, + { + "x": 330.48216088504284, + "y": -323.9757259923004, + "z": 0.0 + }, + { + "x": 329.96569825792506, + "y": -324.23364793609016, + "z": 0.0 + }, + { + "x": 329.40367289306323, + "y": -324.42640954825083, + "z": 0.0 + }, + { + "x": 328.7325685885115, + "y": -324.5533841445278, + "z": 0.0 + }, + { + "x": 327.8864882032299, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 326.8032369853645, + "y": -324.6099853515625, + "z": 0.0 + }, + { + "x": 325.71998576749917, + "y": -324.6099853515625, + "z": 0.0 + } + ] + }, + { + "id": 226, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.38036233629381, + "y": -68.14939116425025, + "z": 0.0 + }, + { + "x": 94.380205608378, + "y": -67.1183725299237, + "z": 0.0 + }, + { + "x": 94.38004888046221, + "y": -66.08735389559722, + "z": 0.0 + }, + { + "x": 94.3798921525464, + "y": -65.0563352612706, + "z": 0.0 + }, + { + "x": 94.41540859131102, + "y": -64.17671650467226, + "z": 0.0 + }, + { + "x": 94.50599020801253, + "y": -63.43406355283531, + "z": 0.0 + }, + { + "x": 94.65004354411337, + "y": -63.28886531615959, + "z": 0.0 + }, + { + "x": 94.89412789099731, + "y": -62.88319975924353, + "z": 0.0 + }, + { + "x": 95.19296693797658, + "y": -62.51318763201182, + "z": 0.0 + }, + { + "x": 95.50496933112525, + "y": -62.25534955729171, + "z": 0.0 + }, + { + "x": 95.79800974776776, + "y": -62.11894221635172, + "z": 0.0 + }, + { + "x": 96.32100408123506, + "y": -61.84314210418763, + "z": 0.0 + }, + { + "x": 96.87868341854731, + "y": -61.62060831764035, + "z": 0.0 + }, + { + "x": 97.29632721050396, + "y": -61.55600143297452, + "z": 0.0 + }, + { + "x": 97.80242386355842, + "y": -61.52230377982157, + "z": 0.0 + }, + { + "x": 98.5261196610379, + "y": -61.49245168271566, + "z": 0.0 + }, + { + "x": 99.3281588135278, + "y": -61.498913326717286, + "z": 0.0 + }, + { + "x": 100.3591774521123, + "y": -61.498787694223985, + "z": 0.0 + }, + { + "x": 101.39019609069686, + "y": -61.49866206173068, + "z": 0.0 + } + ] + }, + { + "id": 235, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.38950854005927, + "y": -53.49866208611823, + "z": 0.0 + }, + { + "x": 100.35427574987001, + "y": -53.49878823211765, + "z": 0.0 + }, + { + "x": 99.37376770017384, + "y": -53.48524399821469, + "z": 0.0 + }, + { + "x": 98.56594764529008, + "y": -53.42248411699823, + "z": 0.0 + }, + { + "x": 98.02529054486976, + "y": -53.28960016706168, + "z": 0.0 + }, + { + "x": 97.39333152686908, + "y": -53.08861912710111, + "z": 0.0 + }, + { + "x": 96.78166837055359, + "y": -52.82970085027407, + "z": 0.0 + }, + { + "x": 96.28198883142825, + "y": -52.534509829604154, + "z": 0.0 + }, + { + "x": 95.8363635631698, + "y": -52.191004873637944, + "z": 0.0 + }, + { + "x": 95.37363437662033, + "y": -51.767432997723176, + "z": 0.0 + }, + { + "x": 94.99551614629756, + "y": -51.32382634745366, + "z": 0.0 + }, + { + "x": 94.75043184234941, + "y": -50.90678621458571, + "z": 0.0 + }, + { + "x": 94.61657447268031, + "y": -50.51927995168681, + "z": 0.0 + }, + { + "x": 94.46613425141832, + "y": -49.99260252804384, + "z": 0.0 + }, + { + "x": 94.38030857826998, + "y": -49.45071585721417, + "z": 0.0 + }, + { + "x": 94.36315168668953, + "y": -48.906894212072416, + "z": 0.0 + }, + { + "x": 94.37733070564443, + "y": -48.21662848944666, + "z": 0.0 + }, + { + "x": 94.37717547838463, + "y": -47.18497092961871, + "z": 0.0 + }, + { + "x": 94.37701810986431, + "y": -46.14973814370483, + "z": 0.0 + } + ] + }, + { + "id": 241, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.6696754063413, + "y": -201.15911160127555, + "z": 0.0 + }, + { + "x": 326.69854293953034, + "y": -201.1591952064953, + "z": 0.0 + }, + { + "x": 327.55662263077403, + "y": -201.1556171967777, + "z": 0.0 + }, + { + "x": 328.2878604030311, + "y": -201.17589028449774, + "z": 0.0 + }, + { + "x": 328.9011683532972, + "y": -201.22663941361236, + "z": 0.0 + }, + { + "x": 329.3792074949348, + "y": -201.2950945907748, + "z": 0.0 + }, + { + "x": 329.94437832953804, + "y": -201.48083812438352, + "z": 0.0 + }, + { + "x": 330.50391400106264, + "y": -201.73861700728952, + "z": 0.0 + }, + { + "x": 331.024340149681, + "y": -202.0449603967076, + "z": 0.0 + }, + { + "x": 331.4555572397345, + "y": -202.35002324535176, + "z": 0.0 + }, + { + "x": 331.7582337858688, + "y": -202.58574368321777, + "z": 0.0 + }, + { + "x": 332.0804677406411, + "y": -202.99176286272518, + "z": 0.0 + }, + { + "x": 332.347243162328, + "y": -203.43389313205873, + "z": 0.0 + }, + { + "x": 332.55520982858246, + "y": -203.9057956290992, + "z": 0.0 + }, + { + "x": 332.69773103323286, + "y": -204.35948999220938, + "z": 0.0 + }, + { + "x": 332.77073692392213, + "y": -205.06321647940158, + "z": 0.0 + }, + { + "x": 332.7754515617806, + "y": -206.07094373654172, + "z": 0.0 + }, + { + "x": 332.77483011454973, + "y": -207.09981108544716, + "z": 0.0 + }, + { + "x": 332.7742086673189, + "y": -208.12867843435257, + "z": 0.0 + }, + { + "x": 332.773587220088, + "y": -209.157545783258, + "z": 0.0 + } + ] + }, + { + "id": 244, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.7877875168978, + "y": -185.64755007182552, + "z": 0.0 + }, + { + "x": 332.7871558692562, + "y": -186.69330520950456, + "z": 0.0 + }, + { + "x": 332.78855716050793, + "y": -187.6139412482442, + "z": 0.0 + }, + { + "x": 332.769849486949, + "y": -188.42140076858865, + "z": 0.0 + }, + { + "x": 332.77083182214864, + "y": -188.88303279362856, + "z": 0.0 + }, + { + "x": 332.66348831483367, + "y": -189.4449902018104, + "z": 0.0 + }, + { + "x": 332.46997372679783, + "y": -190.02958668760976, + "z": 0.0 + }, + { + "x": 332.2154937201096, + "y": -190.5892499954893, + "z": 0.0 + }, + { + "x": 331.98978605376124, + "y": -191.01441570599346, + "z": 0.0 + }, + { + "x": 331.64962893870444, + "y": -191.45391576692543, + "z": 0.0 + }, + { + "x": 331.20220308693445, + "y": -191.88800872415374, + "z": 0.0 + }, + { + "x": 330.7115349826413, + "y": -192.27258396974145, + "z": 0.0 + }, + { + "x": 330.2919359880639, + "y": -192.5572890717597, + "z": 0.0 + }, + { + "x": 329.8012178989161, + "y": -192.79279664378373, + "z": 0.0 + }, + { + "x": 329.2041630135967, + "y": -192.97658802806663, + "z": 0.0 + }, + { + "x": 328.54331794047005, + "y": -193.09947345183087, + "z": 0.0 + }, + { + "x": 327.7621837163193, + "y": -193.15928161095763, + "z": 0.0 + }, + { + "x": 326.7164283913317, + "y": -193.15919663344505, + "z": 0.0 + }, + { + "x": 325.67067306634425, + "y": -193.1591116559325, + "z": 0.0 + } + ] + }, + { + "id": 256, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.1604817010584, + "y": -61.471395029053355, + "z": 0.0 + }, + { + "x": 326.19722933008575, + "y": -61.47126869846662, + "z": 0.0 + }, + { + "x": 327.233976959113, + "y": -61.47114236787987, + "z": 0.0 + }, + { + "x": 327.99131405353273, + "y": -61.46127945318263, + "z": 0.0 + }, + { + "x": 328.6109717729404, + "y": -61.482747456950605, + "z": 0.0 + }, + { + "x": 329.2103753540747, + "y": -61.5647292733214, + "z": 0.0 + }, + { + "x": 329.79705055025045, + "y": -61.71050220350344, + "z": 0.0 + }, + { + "x": 330.3123387974076, + "y": -61.889415448071965, + "z": 0.0 + }, + { + "x": 330.77626372745664, + "y": -62.11094879964533, + "z": 0.0 + }, + { + "x": 331.25491038381944, + "y": -62.43748212523404, + "z": 0.0 + }, + { + "x": 331.670805195674, + "y": -62.7900771917235, + "z": 0.0 + }, + { + "x": 332.03159138732866, + "y": -63.17706278805754, + "z": 0.0 + }, + { + "x": 332.3123598399787, + "y": -63.543989064113035, + "z": 0.0 + }, + { + "x": 332.5315476756131, + "y": -63.92517497204276, + "z": 0.0 + }, + { + "x": 332.70310131549746, + "y": -64.42779257280169, + "z": 0.0 + }, + { + "x": 332.8139116311539, + "y": -65.08228432514662, + "z": 0.0 + }, + { + "x": 332.8601058914801, + "y": -65.91732900219925, + "z": 0.0 + }, + { + "x": 332.85947968458265, + "y": -66.95407644980567, + "z": 0.0 + }, + { + "x": 332.8588534776853, + "y": -67.99082389741164, + "z": 0.0 + }, + { + "x": 332.85822727078784, + "y": -69.02757134501785, + "z": 0.0 + } + ] + }, + { + "id": 271, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.8718839577665, + "y": -46.417575469412235, + "z": 0.0 + }, + { + "x": 332.8712509968554, + "y": -47.46590647417591, + "z": 0.0 + }, + { + "x": 332.87906109993855, + "y": -48.254069835605016, + "z": 0.0 + }, + { + "x": 332.8674708247359, + "y": -48.83855051155455, + "z": 0.0 + }, + { + "x": 332.78721881227335, + "y": -49.41956063034073, + "z": 0.0 + }, + { + "x": 332.6404482078824, + "y": -49.986768950414024, + "z": 0.0 + }, + { + "x": 332.42919462016573, + "y": -50.532805912282264, + "z": 0.0 + }, + { + "x": 332.15627773256864, + "y": -51.050572148092485, + "z": 0.0 + }, + { + "x": 331.82467710757146, + "y": -51.533936458630656, + "z": 0.0 + }, + { + "x": 331.4370083989903, + "y": -51.977864296335234, + "z": 0.0 + }, + { + "x": 330.99884451709175, + "y": -52.37582157742848, + "z": 0.0 + }, + { + "x": 330.54784008475104, + "y": -52.70646836786131, + "z": 0.0 + }, + { + "x": 330.05399673340213, + "y": -52.98129341041239, + "z": 0.0 + }, + { + "x": 329.503203182546, + "y": -53.20007708555586, + "z": 0.0 + }, + { + "x": 328.93171552914595, + "y": -53.35431927828143, + "z": 0.0 + }, + { + "x": 328.1971693258425, + "y": -53.443553006846, + "z": 0.0 + }, + { + "x": 327.25895540688833, + "y": -53.47113926479402, + "z": 0.0 + }, + { + "x": 326.209231142834, + "y": -53.471267176619904, + "z": 0.0 + }, + { + "x": 325.15950687877955, + "y": -53.47139508844576, + "z": 0.0 + } + ] + }, + { + "id": 274, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.40167147489854, + "y": -208.3293895446205, + "z": 0.0 + }, + { + "x": 94.40151478566703, + "y": -207.29862539105918, + "z": 0.0 + }, + { + "x": 94.40135809643554, + "y": -206.26786123749787, + "z": 0.0 + }, + { + "x": 94.43376229952128, + "y": -205.3736891092996, + "z": 0.0 + }, + { + "x": 94.51834203531422, + "y": -204.61128923276965, + "z": 0.0 + }, + { + "x": 94.66783350768677, + "y": -204.1297875213049, + "z": 0.0 + }, + { + "x": 94.87944244681093, + "y": -203.6696897342904, + "z": 0.0 + }, + { + "x": 95.16995944891461, + "y": -203.15286855648935, + "z": 0.0 + }, + { + "x": 95.51540707027908, + "y": -202.67004371951856, + "z": 0.0 + }, + { + "x": 95.90389606178833, + "y": -202.24482846065445, + "z": 0.0 + }, + { + "x": 96.2525027421791, + "y": -201.99846580436187, + "z": 0.0 + }, + { + "x": 96.73848706787206, + "y": -201.69010810485491, + "z": 0.0 + }, + { + "x": 97.25615282237987, + "y": -201.43877363286256, + "z": 0.0 + }, + { + "x": 97.80048676780363, + "y": -201.24710934793697, + "z": 0.0 + }, + { + "x": 98.25513109950654, + "y": -201.17854678323314, + "z": 0.0 + }, + { + "x": 98.76739644056705, + "y": -201.15582125755216, + "z": 0.0 + }, + { + "x": 99.51039002690968, + "y": -201.13782300713487, + "z": 0.0 + }, + { + "x": 100.38857732118137, + "y": -201.1408053809885, + "z": 0.0 + }, + { + "x": 101.4193414832491, + "y": -201.14088914032726, + "z": 0.0 + } + ] + }, + { + "id": 286, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.42032622238838, + "y": -193.14088919393424, + "z": 0.0 + }, + { + "x": 100.33922750350321, + "y": -193.1408013444366, + "z": 0.0 + }, + { + "x": 99.25812878461814, + "y": -193.14071349493892, + "z": 0.0 + }, + { + "x": 98.39054133013732, + "y": -193.08907796797558, + "z": 0.0 + }, + { + "x": 97.69618927189643, + "y": -192.96873836108958, + "z": 0.0 + }, + { + "x": 97.07486869043078, + "y": -192.78301788211778, + "z": 0.0 + }, + { + "x": 96.47651183719475, + "y": -192.5312083437614, + "z": 0.0 + }, + { + "x": 95.90761962214879, + "y": -192.21577823107452, + "z": 0.0 + }, + { + "x": 95.47809790869928, + "y": -191.87555176993956, + "z": 0.0 + }, + { + "x": 95.07232590934612, + "y": -191.46859694320713, + "z": 0.0 + }, + { + "x": 94.79183893066474, + "y": -191.06305779436045, + "z": 0.0 + }, + { + "x": 94.58695946635352, + "y": -190.6335781301305, + "z": 0.0 + }, + { + "x": 94.44899671077283, + "y": -190.1776736582307, + "z": 0.0 + }, + { + "x": 94.38112600071274, + "y": -189.70503411601993, + "z": 0.0 + }, + { + "x": 94.383139280547, + "y": -189.21977939325217, + "z": 0.0 + }, + { + "x": 94.39865592659415, + "y": -188.4919121005017, + "z": 0.0 + }, + { + "x": 94.39849158587134, + "y": -187.41081339053818, + "z": 0.0 + }, + { + "x": 94.39832724514855, + "y": -186.32971468057468, + "z": 0.0 + } + ] + }, + { + "id": 292, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 332.82709050768517, + "y": -120.57756194154449, + "z": 0.0 + }, + { + "x": 332.82643877406326, + "y": -121.6565714059926, + "z": 0.0 + }, + { + "x": 332.83245205105794, + "y": -122.50716907762718, + "z": 0.0 + }, + { + "x": 332.81132858665546, + "y": -123.19805967071827, + "z": 0.0 + }, + { + "x": 332.7542482002542, + "y": -123.76934929727207, + "z": 0.0 + }, + { + "x": 332.6646701817641, + "y": -124.24252956643309, + "z": 0.0 + }, + { + "x": 332.5001486679359, + "y": -124.70549841621741, + "z": 0.0 + }, + { + "x": 332.2121530385942, + "y": -125.20439690327898, + "z": 0.0 + }, + { + "x": 331.8507388515651, + "y": -125.67492643492409, + "z": 0.0 + }, + { + "x": 331.39999244756945, + "y": -126.12422224787402, + "z": 0.0 + }, + { + "x": 330.99793115335666, + "y": -126.46806789445478, + "z": 0.0 + }, + { + "x": 330.4254675243916, + "y": -126.80336453193688, + "z": 0.0 + }, + { + "x": 329.8075639202306, + "y": -127.08118371157036, + "z": 0.0 + }, + { + "x": 329.1656402917356, + "y": -127.29568914951442, + "z": 0.0 + }, + { + "x": 328.59192458915544, + "y": -127.43803717191713, + "z": 0.0 + }, + { + "x": 327.75584160763026, + "y": -127.50443980675412, + "z": 0.0 + }, + { + "x": 326.7211200901641, + "y": -127.51556771483503, + "z": 0.0 + }, + { + "x": 325.6421105365875, + "y": -127.51508562015208, + "z": 0.0 + } + ] + }, + { + "id": 304, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 325.6381862987218, + "y": -135.51508466532573, + "z": 0.0 + }, + { + "x": 326.6693035988366, + "y": -135.515545362051, + "z": 0.0 + }, + { + "x": 327.55131543289843, + "y": -135.5131593711596, + "z": 0.0 + }, + { + "x": 328.3141035849688, + "y": -135.5330080851354, + "z": 0.0 + }, + { + "x": 328.7928591328234, + "y": -135.54477745834626, + "z": 0.0 + }, + { + "x": 329.2838720270384, + "y": -135.62962857414666, + "z": 0.0 + }, + { + "x": 329.8398044272193, + "y": -135.82352366647075, + "z": 0.0 + }, + { + "x": 330.3697282088117, + "y": -136.07686163386663, + "z": 0.0 + }, + { + "x": 330.8094226454465, + "y": -136.3352242557169, + "z": 0.0 + }, + { + "x": 331.19388056147704, + "y": -136.62723539200704, + "z": 0.0 + }, + { + "x": 331.6098466658718, + "y": -137.08355681539902, + "z": 0.0 + }, + { + "x": 331.9767702484966, + "y": -137.58116569359478, + "z": 0.0 + }, + { + "x": 332.26804777178256, + "y": -138.05807524433806, + "z": 0.0 + }, + { + "x": 332.48255764058945, + "y": -138.49289968548555, + "z": 0.0 + }, + { + "x": 332.65222061387357, + "y": -139.0891815282288, + "z": 0.0 + }, + { + "x": 332.76312477510174, + "y": -139.7739642368658, + "z": 0.0 + }, + { + "x": 332.81501231398755, + "y": -140.57420608476463, + "z": 0.0 + }, + { + "x": 332.8143895078128, + "y": -141.6053232997068, + "z": 0.0 + }, + { + "x": 332.81376670163803, + "y": -142.63644051464897, + "z": 0.0 + }, + { + "x": 332.81314389546327, + "y": -143.66755772959112, + "z": 0.0 + } + ] + }, + { + "id": 310, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 101.42178302796803, + "y": -127.41490538788197, + "z": 0.0 + }, + { + "x": 100.36884173414194, + "y": -127.41443494033355, + "z": 0.0 + }, + { + "x": 99.31590044031583, + "y": -127.41396449278513, + "z": 0.0 + }, + { + "x": 98.34557054873348, + "y": -127.3928796818061, + "z": 0.0 + }, + { + "x": 97.55962058570432, + "y": -127.31583473381473, + "z": 0.0 + }, + { + "x": 97.20790067612784, + "y": -127.16461138721388, + "z": 0.0 + }, + { + "x": 96.7223537644397, + "y": -126.93250374971481, + "z": 0.0 + }, + { + "x": 96.27063494180855, + "y": -126.64030547433455, + "z": 0.0 + }, + { + "x": 95.85910137782332, + "y": -126.29195836230957, + "z": 0.0 + }, + { + "x": 95.51989604462473, + "y": -125.90683342043921, + "z": 0.0 + }, + { + "x": 95.28468889415034, + "y": -125.51761502775999, + "z": 0.0 + }, + { + "x": 94.97091192766963, + "y": -124.95536653932524, + "z": 0.0 + }, + { + "x": 94.71416588676566, + "y": -124.36200598792965, + "z": 0.0 + }, + { + "x": 94.51831459567889, + "y": -123.74448953392844, + "z": 0.0 + }, + { + "x": 94.4338890820918, + "y": -123.20146492211585, + "z": 0.0 + }, + { + "x": 94.40464030373457, + "y": -122.61480564194734, + "z": 0.0 + }, + { + "x": 94.38639075227408, + "y": -121.8151086621219, + "z": 0.0 + }, + { + "x": 94.38837998400676, + "y": -120.89267019879318, + "z": 0.0 + }, + { + "x": 94.38821992355439, + "y": -119.83972881203614, + "z": 0.0 + } + ] + }, + { + "id": 319, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 94.391564151273, + "y": -141.83939031284123, + "z": 0.0 + }, + { + "x": 94.39140215202507, + "y": -140.77369475687905, + "z": 0.0 + }, + { + "x": 94.39124015277714, + "y": -139.7079992009169, + "z": 0.0 + }, + { + "x": 94.41769376377435, + "y": -138.74855890854363, + "z": 0.0 + }, + { + "x": 94.51320921265764, + "y": -138.03658731539213, + "z": 0.0 + }, + { + "x": 94.67785056645131, + "y": -137.5681944180493, + "z": 0.0 + }, + { + "x": 94.89872548747795, + "y": -137.18945653433522, + "z": 0.0 + }, + { + "x": 95.1855065404848, + "y": -136.82090632507058, + "z": 0.0 + }, + { + "x": 95.53687355774571, + "y": -136.47276500306168, + "z": 0.0 + }, + { + "x": 95.94636994374555, + "y": -136.15990448540106, + "z": 0.0 + }, + { + "x": 96.41739399178752, + "y": -135.88051851943328, + "z": 0.0 + }, + { + "x": 96.93028384483344, + "y": -135.65565452448743, + "z": 0.0 + }, + { + "x": 97.46813231162776, + "y": -135.49697314657672, + "z": 0.0 + }, + { + "x": 98.02286346439605, + "y": -135.40712496469334, + "z": 0.0 + }, + { + "x": 98.51827011133815, + "y": -135.41166569519828, + "z": 0.0 + }, + { + "x": 99.32070176238868, + "y": -135.4138213058624, + "z": 0.0 + }, + { + "x": 100.35220617672339, + "y": -135.41442830616955, + "z": 0.0 + }, + { + "x": 101.41790163862902, + "y": -135.41490445220006, + "z": 0.0 + } + ] + }, + { + "id": 328, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 152.02380945971413, + "y": -46.195106783691955, + "z": 0.0 + }, + { + "x": 152.02254200228586, + "y": -47.23847720268771, + "z": 0.0 + }, + { + "x": 152.0213199732512, + "y": -48.26703348854526, + "z": 0.0 + }, + { + "x": 152.0288322655578, + "y": -49.01153177480047, + "z": 0.0 + }, + { + "x": 152.01859274755594, + "y": -49.54736259613325, + "z": 0.0 + }, + { + "x": 151.9309711128801, + "y": -50.10404697631353, + "z": 0.0 + }, + { + "x": 151.77769148461007, + "y": -50.64525437326739, + "z": 0.0 + }, + { + "x": 151.56081242716232, + "y": -51.164236234723724, + "z": 0.0 + }, + { + "x": 151.33509844602395, + "y": -51.59210944971475, + "z": 0.0 + }, + { + "x": 151.03922219866305, + "y": -51.995446238265515, + "z": 0.0 + }, + { + "x": 150.63750602870874, + "y": -52.39113917962509, + "z": 0.0 + }, + { + "x": 150.1923699306697, + "y": -52.735138851514144, + "z": 0.0 + }, + { + "x": 149.70952634494085, + "y": -53.023302356093474, + "z": 0.0 + }, + { + "x": 149.23875404894363, + "y": -53.24117517081868, + "z": 0.0 + }, + { + "x": 148.79867298597753, + "y": -53.39036933638519, + "z": 0.0 + }, + { + "x": 148.0224206112521, + "y": -53.468981156349344, + "z": 0.0 + }, + { + "x": 147.07657391052473, + "y": -53.493094989928544, + "z": 0.0 + }, + { + "x": 146.03320272943924, + "y": -53.49322212761356, + "z": 0.0 + }, + { + "x": 144.98983154835375, + "y": -53.49334926529858, + "z": 0.0 + } + ] + }, + { + "id": 337, + "map_element_id": 15, + "type": "road_edge", + "geometry": [ + { + "x": 167.1695080517052, + "y": -53.490646609928866, + "z": 0.0 + }, + { + "x": 166.12735220349356, + "y": -53.49077359952219, + "z": 0.0 + }, + { + "x": 165.08519635528188, + "y": -53.490900589115526, + "z": 0.0 + }, + { + "x": 164.26826123122513, + "y": -53.43913165781505, + "z": 0.0 + }, + { + "x": 163.57831205067666, + "y": -53.32661522480176, + "z": 0.0 + }, + { + "x": 162.97263741222574, + "y": -53.15405915103483, + "z": 0.0 + }, + { + "x": 162.47269917418078, + "y": -52.928503300812395, + "z": 0.0 + }, + { + "x": 162.01532313836688, + "y": -52.645855553721226, + "z": 0.0 + }, + { + "x": 161.50784418968078, + "y": -52.27512648472766, + "z": 0.0 + }, + { + "x": 161.0504105618973, + "y": -51.85852801483733, + "z": 0.0 + }, + { + "x": 160.67677545057913, + "y": -51.41871503147218, + "z": 0.0 + }, + { + "x": 160.50406612361127, + "y": -51.063635052220704, + "z": 0.0 + }, + { + "x": 160.27764479185996, + "y": -50.55138900037866, + "z": 0.0 + }, + { + "x": 160.11411017382582, + "y": -50.015611342568725, + "z": 0.0 + }, + { + "x": 160.0156401774726, + "y": -49.46252361870256, + "z": 0.0 + }, + { + "x": 160.01938909966404, + "y": -48.996991646258856, + "z": 0.0 + }, + { + "x": 160.02102230073152, + "y": -48.239731358425196, + "z": 0.0 + }, + { + "x": 160.02253716340635, + "y": -47.247319653457076, + "z": 0.0 + }, + { + "x": 160.02380314448305, + "y": -46.20516456644735, + "z": 0.0 + } + ] + } + ], + "tl_states": {}, + "metadata": { + "sdc_track_index": 0, + "tracks_to_predict": [], + "objects_of_interest": [] + } +} diff --git a/data_utils/carla/carla_data_utils.py b/data_utils/carla/carla_data_utils.py new file mode 100644 index 000000000..eadf116bd --- /dev/null +++ b/data_utils/carla/carla_data_utils.py @@ -0,0 +1,67 @@ +import os +import json +from typing import List + + +def replace_roads_in_json_files(source_dir: str, destination_dir: str, town_names: List[str]): + for town_name in town_names: + print(f"Replacing roads for town: {town_name}") + source_file = os.path.join(source_dir, f"{town_name}.json") + if not os.path.isfile(source_file): + print(f"Source file for {town_name} not found, skipping.") + continue + with open(source_file, "r") as sf: + source_data = json.load(sf) + roads_value = source_data["roads"] + if roads_value is None: + print(f"No roads data found for {town_name}, skipping.") + continue + + for filename in os.listdir(destination_dir): + if filename.startswith(town_name) and filename.endswith(".json"): + dest_file = os.path.join(destination_dir, filename) + with open(dest_file, "r") as df: + dest_data = json.load(df) + dest_data["roads"] = roads_value + with open(dest_file, "w") as df: + json.dump(dest_data, df, indent=2) + + +def make_all_non_expert_agent_initial_velocity_zero(towns, destination_directory): + for filename in os.listdir(destination_directory): + print(f"Processing file: {filename}") + for town in towns: + if filename.startswith(town): + json_file = os.path.join(destination_directory, filename) + try: + with open(json_file, "r") as f: + data = json.load(f) + except json.JSONDecodeError: + print(f"Error decoding JSON in file: {json_file}, skipping.") + continue + + for agent in data.get("agents", []): + if agent.get("is_expert", False) == False: + agent["initial_velocity"] = 0 + with open(json_file, "w") as f: + json.dump(data, f, indent=2) + + objects = data.get("objects", []) + + # Make initial velocity zero for all non_expert_agents + for obj in objects: + if obj.get("mark_as_expert", False) == False: + obj["velocity"][0] = {"x": 0.0, "y": 0.0} + + data["objects"] = objects + # Save changes to the JSON file + with open(json_file, "w") as f: + json.dump(data, f, indent=2) + + +if __name__ == "__main__": + source_directory = "data/processed/carla" + destination_directory = "data/processed/carla_data" + towns = ["town01", "town02", "town04", "town06", "town10HD"] + # replace_roads_in_json_files(source_directory, destination_directory, towns) + make_all_non_expert_agent_initial_velocity_zero(towns, destination_directory) diff --git a/data_utils/carla/process_carla_roads.py b/data_utils/carla/process_carla_roads.py new file mode 100644 index 000000000..2b3992488 --- /dev/null +++ b/data_utils/carla/process_carla_roads.py @@ -0,0 +1,338 @@ +import sys +import os +import pyxodr +import json +import numpy as np +from lxml import etree +from pyxodr.road_objects.road import Road +from pyxodr.road_objects.lane import Lane, ConnectionPosition, LaneOrientation, TrafficOrientation +from pyxodr.road_objects.junction import Junction +from pyxodr.road_objects.lane_section import LaneSection +from pyxodr.road_objects.network import RoadNetwork +from shapely.geometry import Polygon +from enum import IntEnum +import random +import string + + +class MapType(IntEnum): + LANE_UNDEFINED = 0 + LANE_FREEWAY = 1 + LANE_SURFACE_STREET = 2 + LANE_BIKE_LANE = 3 + # Original definition skips 4 + ROAD_LINE_UNKNOWN = 5 + ROAD_LINE_BROKEN_SINGLE_WHITE = 6 + ROAD_LINE_SOLID_SINGLE_WHITE = 7 + ROAD_LINE_SOLID_DOUBLE_WHITE = 8 + ROAD_LINE_BROKEN_SINGLE_YELLOW = 9 + ROAD_LINE_BROKEN_DOUBLE_YELLOW = 10 + ROAD_LINE_SOLID_SINGLE_YELLOW = 11 + ROAD_LINE_SOLID_DOUBLE_YELLOW = 12 + ROAD_LINE_PASSING_DOUBLE_YELLOW = 13 + ROAD_EDGE_UNKNOWN = 14 + ROAD_EDGE_BOUNDARY = 15 + ROAD_EDGE_MEDIAN = 16 + STOP_SIGN = 17 + CROSSWALK = 18 + SPEED_BUMP = 19 + DRIVEWAY = 20 # New womd datatype in v1.2.0: Driveway entrances + UNKNOWN = -1 + NUM_TYPES = 21 + + +def save_lane_section_to_json(xodr_json, id, road_edges, road_lines, lanes, sidewalks=[]): + roads = xodr_json.get("roads", []) + for road_edge in road_edges: + # edge_polygon = Polygon(road_edge) + edge_data = { + "id": id, + "map_element_id": int(MapType.ROAD_EDGE_BOUNDARY), + "type": "road_edge", + "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in road_edge], + } + roads.append(edge_data) + id += 1 + for road_line in road_lines: + line_data = { + "id": id, + "map_element_id": int(MapType.ROAD_LINE_BROKEN_SINGLE_WHITE), + "type": "road_line", + "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in road_line], + } + roads.append(line_data) + id += 1 + for lane in lanes: + lane_data = { + "id": id, + "map_element_id": int(MapType.LANE_SURFACE_STREET), + "type": "lane", + "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in lane], + } + roads.append(lane_data) + id += 1 + # for sidewalk in sidewalks: + # sidewalk_data = { + # "id": id, + # "map_element_id": int(MapType.LANE_BIKE_LANE), + # "type": "sidewalk", + # "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in sidewalk] + # } + # roads.append(sidewalk_data) + # id += 1 + xodr_json["roads"] = roads + return id + + +def get_lane_data(lane, type="BOUNDARY", check_dir=True): + if type == "BOUNDARY": + points = lane.boundary_line + # print(f'Number of boundary pts: {len(points)}') + elif type == "CENTERLINE": + points = lane.centre_line + # print(f'Number of centerline pts: {len(points)}') + else: + raise ValueError(f"Unknown lane data type: {type}") + + if not check_dir: + return points + + # Check traffic direction + travel_dir = None + vector_lane = lane.lane_xml.find(".//userData/vectorLane") + if vector_lane is not None: + travel_dir = vector_lane.get("travelDir") + + if travel_dir == "backward": + # Reverse points for backward travel + points = points[::-1] + + return points + + +def sum_pts(road_elts): + road_geometries = [len(elt) for elt in road_elts] + return sum(road_geometries) + + +def create_empty_json(town_name): + def random_string(length=8): + return "".join(random.choices(string.ascii_letters + string.digits, k=length)) + + json_data = { + "name": town_name, + "scenario_id": random_string(12), + "objects": [], + "roads": [], + "tl_states": {}, + "metadata": {"sdc_track_index": 0, "tracks_to_predict": [], "objects_of_interest": []}, + } + return json_data + + +def generate_carla_road( + town_name, source_dir, carla_map_dir, resolution, dest_dir, max_samples, print_number_of_sample_truncations +): + src_file_path = os.path.join(source_dir, f"{town_name}.json") + dst_file_path = os.path.join(dest_dir, f"{town_name}.json") + if not os.path.isfile(src_file_path): + print(f"Warning: {src_file_path} does not exist, creating empty file.") + empty_json = create_empty_json(town_name) + with open(src_file_path, "w") as f: + json.dump(empty_json, f, indent=2) + + with open(src_file_path, "r") as f: + xodr_json = json.load(f) + xodr_json["roads"] = [] + + with open(dst_file_path, "w") as f: + json.dump(xodr_json, f, indent=2) + + odr_file = os.path.join(carla_map_dir, town_name + ".xodr") + + road_network = RoadNetwork(xodr_file_path=odr_file, resolution=resolution, max_samples=max_samples) + roads = road_network.get_roads() + print(f"Number of roads in the network: {len(roads)}") + # print(f"Type: {type(roads[0])}\nRoads: {roads}") + print(f"Number of lanes in the network: {sum([sum([len(ls.lanes) for ls in r.lane_sections]) for r in roads])}") + print( + f"Road 0 lane 1 boundary_pts: {len(roads[0].lane_sections[0].lanes[1].boundary_line)}" + ) # Sanity check to see if resolution is working + + # Go only till last "driving" lane("parking" NTD) + # "median" lane means a road edge(add after all of them appear) + # Add "sidewalk" lane as well + + id = 0 + roads_json_cnt = [[], [], []] + print(f"Network has {len(roads)} roads.") + for road_obj in roads: + # print(f"Road ID: {road_obj.id}") + lane_sections = road_obj.lane_sections + # print(f"Lane Sections: {lane_sections}") + for lane_section in lane_sections: + # print(f"Lane Section ID: {lane_section.lane_section_ordinal}") + # print(f"Number of Left Lanes: {len(lane_section.left_lanes)}") + # print(f"Number of Right Lanes: {len(lane_section.right_lanes)}") + road_edges = [] + road_lines = [] + lanes = [] + # sidwalks = [] + + left_immediate_driveable = False + right_immediate_driveable = False + + # Left Lanes + add_lane_data = False + add_edge_data = False + previous_lane = None + for i, left_lane in enumerate(lane_section.left_lanes): + if left_lane.type == "driving" or left_lane.type == "parking": + if i == 0: + left_immediate_driveable = True + + if add_lane_data: + road_line_data = get_lane_data(previous_lane, "BOUNDARY") + road_line_data = road_line_data + road_lines.append(road_line_data) + lanes.append(get_lane_data(previous_lane, "CENTERLINE")) + # Add outer edge as road edge + elif add_edge_data: + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_lane_data = True + add_edge_data = False + else: + # Add inner lane as road edge + if add_lane_data and i != 0: + lanes.append(get_lane_data(previous_lane, "CENTERLINE")) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_edge_data = True + add_lane_data = False + previous_lane = left_lane + + if add_lane_data: + lanes.append(get_lane_data(previous_lane, "CENTERLINE")) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + # elif add_edge_data: + # if previous_lane.type == 'sidewalk': + # sidwalks.append(get_lane_data(previous_lane, "BOUNDARY")) + + # print("LEFT STATS") + # print(f"Number of Road edges: {len(road_edges)}") + # print(f"Road lines: {len(road_lines)}") + # print(f"Lanes: {len(lanes)}") + # print(f"Sidewalks: {len(sidwalks)}") + + # Right Lanes + add_lane_data = False + add_edge_data = False + previous_lane = None + for i, right_lane in enumerate(lane_section.right_lanes): + if right_lane.type == "driving" or right_lane.type == "parking": + if i == 0: + right_immediate_driveable = True + + if add_lane_data: + road_line_data = get_lane_data(previous_lane, "BOUNDARY") + road_line_data = road_line_data + road_lines.append(road_line_data) + lanes.append(get_lane_data(previous_lane, "CENTERLINE")) + # Add outer edge as road edge + elif add_edge_data: + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_lane_data = True + add_edge_data = False + else: + # Add inner lane as road edge + if add_lane_data and i != 0: + lanes.append(get_lane_data(previous_lane, "CENTERLINE")) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_edge_data = True + add_lane_data = False + previous_lane = right_lane + + if add_lane_data: + lanes.append(get_lane_data(previous_lane, "CENTERLINE")) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + # elif add_edge_data: + # if previous_lane.type == 'sidewalk': + # sidwalks.append(get_lane_data(previous_lane, "BOUNDARY")) + + # print(f"Number of Road edges in {road_obj.id}: {len(road_edges)}") + # print(f"Road lines in {road_obj.id}: {len(road_lines)}") + # print(f"Lanes in {road_obj.id}: {len(lanes)}") + # print(f"Sidewalks in {road_obj.id}: {len(sidwalks)}") + + # If atleast one side has no immediate driveable lane add center as road edge + if not left_immediate_driveable or not right_immediate_driveable: + road_edges.append(lane_section.lane_section_reference_line) + else: + road_line_data = get_lane_data(previous_lane, "BOUNDARY") + road_lines.append(road_line_data) + + if len(road_lines) == 0 and len(lanes) == 0: + road_edges = [] + id = save_lane_section_to_json(xodr_json, id, road_edges, road_lines, lanes) + roads_json_cnt[0].append(len(road_edges)) + roads_json_cnt[1].append(len(road_lines)) + roads_json_cnt[2].append(len(lanes)) + # if len(lanes) == 0 and len(road_lines) != 0: + # print(f"Road: {road_obj.id}, Lane Section: {lane_section.lane_section_ordinal}") + # print(f"Road edges: {len(road_edges)}, Road lines: {len(road_lines)}, Lanes: {len(lanes)}") + # break + # break + print(f"Total roads JSON count: {sum(roads_json_cnt[0]) + sum(roads_json_cnt[1]) + sum(roads_json_cnt[2])}") + + # Save to file + with open(dst_file_path, "w") as f: + json.dump(xodr_json, f, indent=2) + + # Print logs + if print_number_of_sample_truncations: + road_network.print_logs_max_samples_hit() + + +def generate_carla_roads( + town_names, source_dir, carla_map_dir, resolution, dest_dir, max_samples, print_number_of_sample_truncations +): + if type(resolution) == float: + resolution = [resolution] * len(town_names) + elif type(resolution) != list: + raise ValueError("Resolution must be a float or a list type") + elif len(resolution) != len(town_names): + raise ValueError("Resolution must be of the same length as town_names.") + for i, town in enumerate(town_names): + print(f"Processing town: {town}") + generate_carla_road( + town, + source_dir, + carla_map_dir, + resolution[i], + dest_dir, + max_samples, + print_number_of_sample_truncations=print_number_of_sample_truncations, + ) + + +if __name__ == "__main__": + town_names = ["Town01", "Town02", "Town03", "Town04", "Town05", "Town06", "Town07", "Town10HD"] + source_dir = "data_utils/carla" + dest_dir = "data_utils/carla" + carla_map_dir = "/scratch/pm3881/Carla-0.10.0-Linux-Shipping/CarlaUnreal/Content/Carla/Maps/OpenDrive" + resolution = 1.0 # [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] # Meters + max_samples = int(1e5) # Max points to sample per reference line + print_number_of_sample_truncations = True # Enable to see the number of data points lost + generate_carla_roads( + town_names, source_dir, carla_map_dir, resolution, dest_dir, max_samples, print_number_of_sample_truncations + ) + # resolution = 1.0 + # town_name = 'town06' + # generate_carla_road( + # town_name, + # source_dir, + # carla_map_dir, + # resolution, + # dest_dir, + # max_samples, + # print_number_of_sample_truncations + # ) diff --git a/data_utils/carla/process_carla_xodr.py b/data_utils/carla/process_carla_xodr.py new file mode 100644 index 000000000..0c11b8c1b --- /dev/null +++ b/data_utils/carla/process_carla_xodr.py @@ -0,0 +1,897 @@ +import sys +import os +import json +import numpy as np +import random +from lxml import etree +import pyxodr +from pyxodr.road_objects.road import Road +from pyxodr.road_objects.lane import Lane, ConnectionPosition, LaneOrientation, TrafficOrientation +from pyxodr.road_objects.junction import Junction +from pyxodr.road_objects.lane_section import LaneSection +from pyxodr.road_objects.network import RoadNetwork +from shapely.geometry import Polygon +from enum import IntEnum + + +class MapType(IntEnum): + LANE_UNDEFINED = 0 + LANE_FREEWAY = 1 + LANE_SURFACE_STREET = 2 + LANE_BIKE_LANE = 3 + # Original definition skips 4 + ROAD_LINE_UNKNOWN = 5 + ROAD_LINE_BROKEN_SINGLE_WHITE = 6 + ROAD_LINE_SOLID_SINGLE_WHITE = 7 + ROAD_LINE_SOLID_DOUBLE_WHITE = 8 + ROAD_LINE_BROKEN_SINGLE_YELLOW = 9 + ROAD_LINE_BROKEN_DOUBLE_YELLOW = 10 + ROAD_LINE_SOLID_SINGLE_YELLOW = 11 + ROAD_LINE_SOLID_DOUBLE_YELLOW = 12 + ROAD_LINE_PASSING_DOUBLE_YELLOW = 13 + ROAD_EDGE_UNKNOWN = 14 + ROAD_EDGE_BOUNDARY = 15 + ROAD_EDGE_MEDIAN = 16 + STOP_SIGN = 17 + CROSSWALK = 18 + SPEED_BUMP = 19 + DRIVEWAY = 20 # New womd datatype in v1.2.0: Driveway entrances + UNKNOWN = -1 + NUM_TYPES = 21 + + +def save_lane_section_to_json(xodr_json, id, road_edges, road_lines, lanes, sidewalks=[]): + roads = xodr_json.get("roads", []) + for road_edge in road_edges: + # edge_polygon = Polygon(road_edge) + edge_data = { + "id": id, + "map_element_id": int(MapType.ROAD_EDGE_BOUNDARY), + "type": "road_edge", + "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in road_edge], + } + roads.append(edge_data) + id += 1 + for road_line in road_lines: + line_data = { + "id": id, + "map_element_id": int(MapType.ROAD_LINE_BROKEN_SINGLE_WHITE), + "type": "road_line", + "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in road_line], + } + roads.append(line_data) + id += 1 + for lane in lanes: + lane_data = { + "id": id, + "map_element_id": int(MapType.LANE_SURFACE_STREET), + "type": "lane", + "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in lane], + } + roads.append(lane_data) + id += 1 + # for sidewalk in sidewalks: + # sidewalk_data = { + # "id": id, + # "map_element_id": int(MapType.LANE_BIKE_LANE), + # "type": "sidewalk", + # "geometry": [{"x": float(pt[0]), "y": float(pt[1]), "z": 0.0} for pt in sidewalk] + # } + # roads.append(sidewalk_data) + # id += 1 + xodr_json["roads"] = roads + return id + + +def get_lane_data(lane, type="BOUNDARY", check_dir=True): + if type == "BOUNDARY": + points = lane.boundary_line + elif type == "CENTERLINE": + points = lane.centre_line + else: + raise ValueError(f"Unknown lane data type: {type}") + + if not check_dir: + return points + + # Check traffic direction + travel_dir = None + vector_lane = lane.lane_xml.find(".//userData/vectorLane") + if vector_lane is not None: + travel_dir = vector_lane.get("travelDir") + + if travel_dir == "backward": + # Reverse points for backward travel + points = points[::-1] + + return points + + +class RoadLinkObject: + def __init__(self, road_id, pred_lane_section_ids, succ_lane_section_ids): + self.road_id = road_id + self.pred_lane_section_ids = pred_lane_section_ids + self.succ_lane_section_ids = succ_lane_section_ids + self.lane_links_map = {} # map from (lane_id, lane_section_index) to LaneLinkObject + self.predecessor_roads = [] + self.successor_roads = [] + + +class LaneLinkObject: + def __init__(self, lane_id, lane_section_index, road_id, lane, lane_centerpoints, forward_dir, is_junction): + self.lane_id = lane_id + self.lane_section_index = lane_section_index + self.road_id = road_id + self.lane = lane + self.lane_centerpoints = lane_centerpoints + self.forward_dir = forward_dir + self.predecessor_lanes = [] + self.successor_lanes = [] + self.outgoing_edges = [] + self.incoming_edges = [] + self.is_sampled = False + self.is_junction = is_junction + + +def get_junction(road_network, junction_id): + for junction in road_network.get_junctions(): + if junction.id == junction_id: + return junction + return None + + +def get_road(road_network, road_id): + for road in road_network.get_roads(): + if road.id == road_id: + return road + return None + + +def is_forward_dir(lane): + # Check traffic direction + travel_dir = None + vector_lane = lane.lane_xml.find(".//userData/vectorLane") + if vector_lane is not None: + travel_dir = vector_lane.get("travelDir") + + if travel_dir == "forward": + return True + return False + + +def create_lane_link_elements(road_network, roads, road_link_map): + roads_json_cnt = [[], [], []] + print(f"Network has {len(roads)} roads.") + for road_obj in roads: + # print(f"Road ID: {road_obj.id}") + lane_sections = road_obj.lane_sections + + is_road_junction = False if road_obj.road_xml.attrib["junction"] == "-1" else True + pred_lane_section_ids = {} + for predecessor_xml in road_obj.road_xml.find("link").findall("predecessor"): + if predecessor_xml.attrib["elementType"] == "road": + pred_road_id = predecessor_xml.attrib["elementId"] + if predecessor_xml.attrib["contactPoint"] == "start": + pred_lane_section_ids[pred_road_id] = 0 + else: + pred_road = get_road(road_network, pred_road_id) + pred_lane_section_ids[pred_road_id] = len(pred_road.lane_sections) - 1 + + succ_lane_section_ids = {} + for successor_xml in road_obj.road_xml.find("link").findall("successor"): + if successor_xml.attrib["elementType"] == "road": + succ_road_id = successor_xml.attrib["elementId"] + if successor_xml.attrib["contactPoint"] == "start": + succ_lane_section_ids[succ_road_id] = 0 + else: + succ_road = get_road(road_network, succ_road_id) + succ_lane_section_ids[succ_road_id] = len(succ_road.lane_sections) - 1 + + road_link_object = RoadLinkObject( + road_id=road_obj.id, + pred_lane_section_ids=pred_lane_section_ids, + succ_lane_section_ids=succ_lane_section_ids, + ) + + for lane_section in lane_sections: + # print(f"Lane Section ID: {lane_section.lane_section_ordinal}") + # print(f"Number of Left Lanes: {len(lane_section.left_lanes)}") + # print(f"Number of Right Lanes: {len(lane_section.right_lanes)}") + road_edges = [] + road_lines = [] + lanes = [] + # sidwalks = [] + + left_immediate_driveable = False + right_immediate_driveable = False + + # Left Lanes + add_lane_data = False + add_edge_data = False + previous_lane = None + for i, left_lane in enumerate(lane_section.left_lanes): + if left_lane.type == "driving": # We only deal with driving lanes + if i == 0: + left_immediate_driveable = True + + if add_lane_data: + road_line_data = get_lane_data(previous_lane, "BOUNDARY") + road_line_data = road_line_data[::40] + road_lines.append(road_line_data) + waypoints = get_lane_data(previous_lane, "CENTERLINE") + lanes.append(waypoints) + road_link_object.lane_links_map[(str(previous_lane.id), lane_section.lane_section_ordinal)] = ( + LaneLinkObject( + lane_id=previous_lane.id, + lane_section_index=lane_section.lane_section_ordinal, + road_id=road_obj.id, + lane=previous_lane, + lane_centerpoints=waypoints, + forward_dir=is_forward_dir(previous_lane), + is_junction=is_road_junction, + ) + ) + # Add outer edge as road edge + elif add_edge_data: + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_lane_data = True + add_edge_data = False + else: + # Add inner lane as road edge + if add_lane_data and i != 0: + waypoints = get_lane_data(previous_lane, "CENTERLINE") + lanes.append(waypoints) + road_link_object.lane_links_map[(str(previous_lane.id), lane_section.lane_section_ordinal)] = ( + LaneLinkObject( + lane_id=previous_lane.id, + lane_section_index=lane_section.lane_section_ordinal, + road_id=road_obj.id, + lane=previous_lane, + lane_centerpoints=waypoints, + forward_dir=is_forward_dir(previous_lane), + is_junction=is_road_junction, + ) + ) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_edge_data = True + add_lane_data = False + previous_lane = left_lane + + if add_lane_data: + waypoints = get_lane_data(previous_lane, "CENTERLINE") + lanes.append(waypoints) + road_link_object.lane_links_map[(str(previous_lane.id), lane_section.lane_section_ordinal)] = ( + LaneLinkObject( + lane_id=previous_lane.id, + lane_section_index=lane_section.lane_section_ordinal, + road_id=road_obj.id, + lane=previous_lane, + lane_centerpoints=waypoints, + forward_dir=is_forward_dir(previous_lane), + is_junction=is_road_junction, + ) + ) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + # elif add_edge_data: + # if previous_lane.type == 'sidewalk': + # sidwalks.append(get_lane_data(previous_lane, "BOUNDARY")) + + # print("LEFT STATS") + # print(f"Number of Road edges: {len(road_edges)}") + # print(f"Road lines: {len(road_lines)}") + # print(f"Lanes: {len(lanes)}") + # print(f"Sidewalks: {len(sidwalks)}") + + # Right Lanes + add_lane_data = False + add_edge_data = False + previous_lane = None + for i, right_lane in enumerate(lane_section.right_lanes): + if right_lane.type == "driving": + if i == 0: + right_immediate_driveable = True + + if add_lane_data: + road_line_data = get_lane_data(previous_lane, "BOUNDARY") + road_line_data = road_line_data[::40] + road_lines.append(road_line_data) + waypoints = get_lane_data(previous_lane, "CENTERLINE") + lanes.append(waypoints) + road_link_object.lane_links_map[(str(previous_lane.id), lane_section.lane_section_ordinal)] = ( + LaneLinkObject( + lane_id=previous_lane.id, + lane_section_index=lane_section.lane_section_ordinal, + road_id=road_obj.id, + lane=previous_lane, + lane_centerpoints=waypoints, + forward_dir=is_forward_dir(previous_lane), + is_junction=is_road_junction, + ) + ) + # Add outer edge as road edge + elif add_edge_data: + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_lane_data = True + add_edge_data = False + else: + # Add inner lane as road edge + if add_lane_data and i != 0: + waypoints = get_lane_data(previous_lane, "CENTERLINE") + lanes.append(waypoints) + road_link_object.lane_links_map[(str(previous_lane.id), lane_section.lane_section_ordinal)] = ( + LaneLinkObject( + lane_id=previous_lane.id, + lane_section_index=lane_section.lane_section_ordinal, + road_id=road_obj.id, + lane=previous_lane, + lane_centerpoints=waypoints, + forward_dir=is_forward_dir(previous_lane), + is_junction=is_road_junction, + ) + ) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + add_edge_data = True + add_lane_data = False + previous_lane = right_lane + + if add_lane_data: + waypoints = get_lane_data(previous_lane, "CENTERLINE") + lanes.append(waypoints) + road_link_object.lane_links_map[(str(previous_lane.id), lane_section.lane_section_ordinal)] = ( + LaneLinkObject( + lane_id=previous_lane.id, + lane_section_index=lane_section.lane_section_ordinal, + road_id=road_obj.id, + lane=previous_lane, + lane_centerpoints=waypoints, + forward_dir=is_forward_dir(previous_lane), + is_junction=is_road_junction, + ) + ) + road_edges.append(get_lane_data(previous_lane, "BOUNDARY")) + # elif add_edge_data: + # if previous_lane.type == 'sidewalk': + # sidwalks.append(get_lane_data(previous_lane, "BOUNDARY")) + + road_link_map[road_obj.id] = road_link_object + + roads_json_cnt[0].append(len(road_edges)) + roads_json_cnt[1].append(len(road_lines)) + roads_json_cnt[2].append(len(lanes)) + # if len(lanes) == 0 and len(road_lines) != 0: + # print(f"Road: {road_obj.id}, Lane Section: {lane_section.lane_section_ordinal}") + # print(f"Road edges: {len(road_edges)}, Road lines: {len(road_lines)}, Lanes: {len(lanes)}") + # break + # break + print(f"Total roads JSON count: {sum(roads_json_cnt[0]) + sum(roads_json_cnt[1]) + sum(roads_json_cnt[2])}") + # print(f"Road edges count: {roads_json_cnt[0]}") + # print(f"Road lines count: {roads_json_cnt[1]}") + print(f"Lanes count: {sum(roads_json_cnt[2])}") + total_lane_links = sum(len(obj.lane_links_map) for obj in road_link_map.values()) + assert sum(roads_json_cnt[2]) == total_lane_links + + +def create_successor_predecessor_elements(road_network, roads, road_link_map): + stopping_points = 0 + + for road_obj in roads: + # print(f"Road ID: {road_obj.id}") + road_link_object = road_link_map[road_obj.id] + lane_sections = road_obj.lane_sections + # print(f"Lane Sections: {lane_sections}") + + for lane_link_obj in road_link_object.lane_links_map.values(): + lane_link_obj.predecessor_lanes = [] + lane_link_obj.successor_lanes = [] + + lane = lane_link_obj.lane + + link_xml = lane.lane_xml.find("link") + + successor_is_junction = True + if link_xml is not None and link_xml.findall("successor") != []: + successor_is_junction = False + # if link_xml.findall("successor") == []: + # print(f"Successor for road: {road_obj.id}, lane_section: {lane_link_obj.lane_section_index}, lane: {lane_link_obj.lane.id}: {link_xml.findall('successor')}") + # Process Successor Links + for successor in link_xml.findall("successor"): + successor_id = successor.get("id") + + if lane_link_obj.lane_section_index + 1 < len(lane_sections): + # Lane link in next lane section + lane_link_obj.successor_lanes.append( + road_link_object.lane_links_map[(successor_id, lane_link_obj.lane_section_index + 1)] + ) + else: + # Lane link in successor roads + for road_succ in road_obj.road_xml.find("link").findall("successor"): + if road_succ.attrib["elementType"] == "road": + succ_road_id = road_succ.attrib["elementId"] + succ_road = road_link_map[succ_road_id] + succ_lane_section_index = road_link_object.succ_lane_section_ids[succ_road_id] + # if (lane_link_obj.lane_id, succ_lane_section_index) not in succ_road.lane_links_map: + # print(f"Key:{(successor_id, succ_lane_section_index)} not found in lane_links_map - road_id: {succ_road_id}, lane_section_index: {succ_lane_section_index}, lane_id: {successor_id}") + lane_link_obj.successor_lanes.append( + succ_road.lane_links_map[(successor_id, succ_lane_section_index)] + ) + else: + # Junction case + successor_is_junction = True + elif successor_is_junction: + # if road_obj.id == "0" and str(lane.id) == "-1": + # print(f"Road: {road_obj.id}, lane: {lane.id}, successor is a junction") + # break + # Handle junction case + for successor in road_obj.road_xml.find("link").findall("successor"): + if successor.attrib["elementType"] == "junction": + junction_id = successor.attrib["elementId"] + # print(f"Road: {road_obj.id}, lane: {lane.id}, successor is a junction: {junction_id}") + junction = get_junction(road_network, junction_id) + # print(f"Retrieved Junction: {junction.id} from road_network") + connected_lanes = junction.get_lane_junction_lanes(str(lane.id), road_id=road_obj.id) + if len(connected_lanes) == 0 and lane_link_obj.forward_dir: + stopping_points += 1 + print( + f"Non junction road: {road_obj.id}, lane_section: {lane_link_obj.lane_section_index}, lane: {lane_link_obj.lane.id} has no junction or road links, it is an ending lane" + ) + for conn_lane in connected_lanes: + succ_road_obj = get_road(road_network, conn_lane["road_id"]) + succ_road = road_link_map[conn_lane["road_id"]] + succ_lane_section_index = conn_lane["lane_section_index"] + if succ_lane_section_index == -1: + succ_lane_section_index = len(succ_road_obj.lane_sections) - 1 + succ_lane_id = conn_lane["lane_id"] + lane_link_obj.successor_lanes.append( + succ_road.lane_links_map[(str(succ_lane_id), succ_lane_section_index)] + ) + else: + if lane_link_obj.forward_dir: + # Stopping point + stopping_points += 1 + print( + f"Non junction road: {road_obj.id}, lane_section: {lane_link_obj.lane_section_index}, lane: {lane_link_obj.lane.id} has no junction or road links, it is an ending lane" + ) + + predecessor_is_junction = True + if link_xml is not None and link_xml.findall("predecessor") != []: + predecessor_is_junction = False + # Process Predecessor Links + for predecessor in link_xml.findall("predecessor"): + predecessor_id = predecessor.get("id") + + if lane_link_obj.lane_section_index - 1 >= 0: + # Lane link in previous lane section + lane_link_obj.predecessor_lanes.append( + road_link_object.lane_links_map[(predecessor_id, lane_link_obj.lane_section_index - 1)] + ) + else: + # Lane link in predecessor roads + for road_pred in road_obj.road_xml.find("link").findall("predecessor"): + if road_pred.attrib["elementType"] == "road": + pred_id = road_pred.attrib["elementId"] + pred_road = road_link_map[pred_id] + pred_lane_section_index = road_link_object.pred_lane_section_ids[pred_id] + # print(f"Curr lane: {lane_link_obj.lane.id}, section: {lane_link_obj.lane_section_index}, road: {road_obj.id}; Pred lane: {predecessor_id}, section: {road_link_object.pred_lane_section_ids[pred_id]}, road: {pred_id}") + lane_link_obj.predecessor_lanes.append( + pred_road.lane_links_map[(predecessor_id, pred_lane_section_index)] + ) + else: + # Junction case + predecessor_is_junction = True + elif predecessor_is_junction: + # Handle junction case + for predecessor in road_obj.road_xml.find("link").findall("predecessor"): + if predecessor.attrib["elementType"] == "junction": + junction_id = predecessor.attrib["elementId"] + junction = get_junction(road_network, junction_id) + connected_lanes = junction.get_lane_junction_lanes(lane_id=str(lane.id), road_id=road_obj.id) + if len(connected_lanes) == 0 and not lane_link_obj.forward_dir: + stopping_points += 1 + print( + f"Non junction road: {road_obj.id}, lane_section: {lane_link_obj.lane_section_index}, lane: {lane_link_obj.lane.id} has no junction or road links, it is a starting lane" + ) + for conn_lane in connected_lanes: + pred_road_obj = get_road(road_network, conn_lane["road_id"]) + pred_road = road_link_map[conn_lane["road_id"]] + pred_lane_section_index = conn_lane["lane_section_index"] + if pred_lane_section_index == -1: + pred_lane_section_index = len(pred_road_obj.lane_sections) - 1 + pred_lane_id = conn_lane["lane_id"] + lane_link_obj.predecessor_lanes.append( + pred_road.lane_links_map[(str(pred_lane_id), pred_lane_section_index)] + ) + else: + if not lane_link_obj.forward_dir: + # Stopping case + stopping_points += 1 + print( + f"Non junction road: {road_obj.id}, lane_section: {lane_link_obj.lane_section_index}, lane: {lane_link_obj.lane.id} has no junction or road links, it is a starting lane" + ) + + print(f"Road network has {stopping_points} stopping points (lanes with no predecessors or successors).") + + +def add_incoming_outgoing_edges(road_network, roads, road_link_map): + for road_obj in roads: + # print(f"Road ID: {road_obj.id}") + road_link_object = road_link_map[road_obj.id] + lane_sections = road_obj.lane_sections + # print(f"Lane Sections: {lane_sections}") + + for lane_link_obj in road_link_object.lane_links_map.values(): + lane = lane_link_obj.lane + link_xml = lane.lane_xml.find("link") + + if lane_link_obj.forward_dir: + # Forward direction, predecessor lanes are incoming edges, successor lanes are outgoing edges + lane_link_obj.incoming_edges = lane_link_obj.predecessor_lanes + lane_link_obj.outgoing_edges = lane_link_obj.successor_lanes + else: + # Backward direction, predecessor lanes are outgoing edges, successor lanes are incoming edges + lane_link_obj.outgoing_edges = lane_link_obj.predecessor_lanes + lane_link_obj.incoming_edges = lane_link_obj.successor_lanes + + +def test_linkage(road_link_map): + # Testing linkage + + start_road_id = "0" + road_link_object = road_link_map[start_road_id] + + lane_link_keys = list(road_link_object.lane_links_map.keys()) + random_lane_link_key = None + if random_lane_link_key is None: + random_lane_link_key = random.choice(lane_link_keys) + lane_link_obj = road_link_object.lane_links_map[random_lane_link_key] + + print(f"Starting at road id: {start_road_id}") + print(f"Lane link key: {random_lane_link_key}") + print(f"Lane id: {lane_link_obj.lane_id}, Lane section index: {lane_link_obj.lane_section_index}") + + # Traverse outgoing edges for 10 steps + current = lane_link_obj + for step in range(10): + print(f"\nStep {step}:") + print( + f" Road id: {current.road_id}, Lane id: {current.lane_id}, Lane section index: {current.lane_section_index}" + ) + if current.outgoing_edges: + current = random.choice(current.outgoing_edges) + else: + print(" No outgoing edges. Stopping traversal.") + print("Debugging") + print( + f"Outgoing Edges: {[(edge.road_id, str(edge.lane.id), edge.lane_section_index) for edge in current.outgoing_edges]}" + ) + print( + f"Incoming Edges: {[(edge.road_id, str(edge.lane.id), edge.lane_section_index) for edge in current.incoming_edges]}" + ) + print( + f"Successors: {[(edge.road_id, str(edge.lane.id), edge.lane_section_index) for edge in current.successor_lanes]}" + ) + print( + f"Predecessors: {[(edge.road_id, str(edge.lane.id), edge.lane_section_index) for edge in current.predecessor_lanes]}" + ) + break + + +def print_traj_stats(waypoints_list, num_timestamps, episode_length): + # Extract positions + positions = [wp["position"] for wp in waypoints_list if "position" in wp] + positions = np.array(positions) + + # Compute consecutive distances + if positions.shape[0] < 2: + print("Not enough waypoints to compute statistics.") + return + + diffs = positions[1:] - positions[:-1] + distances = np.linalg.norm(diffs, axis=1) + distance_traversed = np.sum(distances) + + # Compute max speed along trajectory + timestamp_dur = episode_length / num_timestamps + speeds = distances / timestamp_dur + max_speed_traj = np.max(speeds) + + print(f"Distance traversed: {distance_traversed} meters") + print(f"Max speed in trajectory: {max_speed_traj} m/s") + + +def check_geometry(point, all_geometries): + for pt in all_geometries: + dist = np.linalg.norm(np.array(pt) - np.array(point)) + if dist < 1e-9: # threshold, adjust as needed + return False + return True + + +def generate_traj_data( + road_link_map, + num_timestamps=90, + resolution=0.1, + episode_length=9, + max_speed=10, + random_sampling_variation=1, + resample=True, + all_geometries=[], +): + # Calculate average speed (70% of max_speed) + avg_speed = 0.7 * max_speed + avg_cons_pts_dist = resolution * (1 + np.sqrt(2)) / 2 + time_step_dur = episode_length / num_timestamps + sampling_length = int((avg_speed * time_step_dur) / avg_cons_pts_dist) + + # # Calculate junction speed (20% of max_speed) + # junction_speed = 0.2 * max_speed + + # Pick a random lane_link_key + while True: + road_link_keys = list(road_link_map.keys()) + start_key = random.choice(road_link_keys) + lane_link_keys = list(road_link_map[start_key].lane_links_map.keys()) + if lane_link_keys != []: + if resample: + start_lane_key = random.choice(lane_link_keys) + lane_link_obj = road_link_map[start_key].lane_links_map[start_lane_key] + if not lane_link_obj.is_sampled: + # print(f"Starting: RoadLink key: {start_key}\n LaneLink key: {start_lane_key}") + break + else: + start_lane_key = random.choice(lane_link_keys) + lane_link_obj = road_link_map[start_key].lane_links_map[start_lane_key] + + waypoints_list = [] + current_lane_link = lane_link_obj + current_lane_link.is_sampled = True + idx = random.randint(0, len(current_lane_link.lane_centerpoints) - 1) + # if check_geometry(current_lane_link.lane_centerpoints[idx], all_geometries) == False: + # print(f"Waypoint {current_lane_link.lane_centerpoints[idx]}, lane: {current_lane_link.lane_id}, Lane_Section: {current_lane_link.lane_section_index} Road: {current_lane_link.road_id} not in all_geometries") + waypoints_list.append( + { + "timestamp": 0, + "position": current_lane_link.lane_centerpoints[idx].tolist() + if hasattr(current_lane_link.lane_centerpoints[idx], "tolist") + else list(current_lane_link.lane_centerpoints[idx]), + "lane_id": current_lane_link.lane_id, + "lane_section_index": current_lane_link.lane_section_index, + "road_id": current_lane_link.road_id, + } + ) + change_lane = False + + for t in range(num_timestamps): + if change_lane: + # Pick a random outgoing edge + if current_lane_link.outgoing_edges: + if resample: + # Filter outgoing_edges with is_sampled == False + unsampled_edges = [ + edge for edge in current_lane_link.outgoing_edges if not getattr(edge, "is_sampled", False) + ] + if unsampled_edges != []: + current_lane_link = random.choice(unsampled_edges) + else: + current_lane_link = random.choice(current_lane_link.outgoing_edges) + current_lane_link.is_sampled = True + idx = 0 # Lane connection width is zero so reset at start + else: + current_lane_link = random.choice(current_lane_link.outgoing_edges) + else: + # No outgoing edge, stop trajectory + print("No outgoing edge, stopping trajectory") + while len(waypoints_list) < num_timestamps + 1: + waypoints_list.append( + { + "position": waypoint.tolist() if hasattr(waypoint, "tolist") else list(waypoint), + "velocity": {"x": 0.0, "y": 0.0}, + "heading": 0.0 if len(waypoints_list) == 0 else waypoints_list[-1]["heading"], + "lane_id": current_lane_link.lane_id, + "lane_section_index": current_lane_link.lane_section_index, + "road_id": current_lane_link.road_id, + "change_lane": change_lane, + } + ) + break + + # Randomize sampling length + curr_sampling_length = sampling_length + random.randint(-random_sampling_variation, random_sampling_variation) + next_idx = idx + curr_sampling_length + + if next_idx < len(current_lane_link.lane_centerpoints): + waypoint = current_lane_link.lane_centerpoints[next_idx] + idx = next_idx + change_lane = False + else: + waypoint = current_lane_link.lane_centerpoints[-1] + change_lane = True + + # Velocity and heading calculation + v_x = (waypoint[0] - waypoints_list[t - 1]["position"][0]) / time_step_dur + v_y = (waypoint[1] - waypoints_list[t - 1]["position"][1]) / time_step_dur + heading = np.arctan2(v_y, v_x) + + # if check_geometry(waypoint, all_geometries) == False: + # print(f"Waypoint {waypoint}, lane: {current_lane_link.lane_id}, Lane_Section: {current_lane_link.lane_section_index} Road: {current_lane_link.road_id} not in all_geometries") + + waypoints_list.append( + { + "position": waypoint.tolist() if hasattr(waypoint, "tolist") else list(waypoint), + "velocity": {"x": v_x, "y": v_y}, + "heading": heading, + "lane_id": current_lane_link.lane_id, + "lane_section_index": current_lane_link.lane_section_index, + "road_id": current_lane_link.road_id, + } + ) + + # Change first waypoint with average velocity and avg heading keeping everything else same + waypoints_list[0] = { + "position": waypoints_list[0]["position"], + "velocity": { + "x": np.mean([wp["velocity"]["x"] for wp in waypoints_list[1:]]), + "y": np.mean([wp["velocity"]["y"] for wp in waypoints_list[1:]]), + }, + "heading": np.mean([wp["heading"] for wp in waypoints_list[1:]]), + "lane_id": waypoints_list[0]["lane_id"], + "lane_section_index": waypoints_list[0]["lane_section_index"], + "road_id": waypoints_list[0]["road_id"], + } + + return waypoints_list + + +def save_object_to_json( + xodr_json, + road_link_map, + id, + resolution=0.1, + object_type="vehicle", + all_geometries=[], + start_with_zero_velocity=True, +): + traj_data = generate_traj_data(road_link_map=road_link_map, resolution=resolution, all_geometries=all_geometries) + + z = 1.0 + headings = [] + positions = [] + velocities = [] + for i, traj in enumerate(traj_data): + x = traj["position"][0] + y = traj["position"][1] + positions.append({"x": float(x), "y": float(y), "z": float(z)}) + v_x = 0.0 if start_with_zero_velocity and i == 0 else traj["velocity"]["x"] + v_y = 0.0 if start_with_zero_velocity and i == 0 else traj["velocity"]["y"] + velocities.append({"x": float(v_x), "y": float(v_y)}) + headings.append(float(traj["heading"])) + + object_data = { + "position": list(positions), + "width": 2.0, + "length": 4.5, + "height": 1.8, + "id": id, + "heading": list(headings), + "velocity": list(velocities), + "valid": [True] * len(positions), + "goalPosition": { + "x": float(positions[-1]["x"]), + "y": float(positions[-1]["y"]), + "z": float(positions[-1]["z"]), + }, + "type": object_type, + "mark_as_expert": False, + } + + objects = xodr_json.get("objects", []) + objects.append(object_data) + xodr_json["objects"] = objects + return id + 1 + + +def generate_data_each_map( + town_names, + carla_map_dir, + resolution, + input_json_base_path, + output_json_root_dir, + num_data_per_map, + num_objects, + make_only_first_agent_controllable, + start_with_zero_velocity=True, +): + os.makedirs(output_json_root_dir, exist_ok=True) + for town_name in town_names: + json_file = town_name + ".json" + odr_file = os.path.join(carla_map_dir, "T" + town_name[1:] + ".xodr") + input_json_path = os.path.join(input_json_base_path, json_file) + + for id in range(num_data_per_map): + output_json_path = os.path.join(output_json_root_dir, f"{town_name}_{id}.json") + + road_network = RoadNetwork(xodr_file_path=odr_file, resolution=resolution) + roads = road_network.get_roads() + print(f"Number of roads in the network: {len(roads)}") + + # Create a dictionary to map road_id to RoadLinkObject + road_link_map = {} + + # First create the lane link elements + create_lane_link_elements(road_network, roads, road_link_map) + + # Create successor predecessor elements + create_successor_predecessor_elements(road_network, roads, road_link_map) + + # Now add outgoing and incoming edges based on driving direction + add_incoming_outgoing_edges(road_network, roads, road_link_map) + + # Test linkage + test_linkage(road_link_map) + + with open(input_json_path, "r") as f: + xodr_json = json.load(f) + + roads = xodr_json["roads"] + all_geometries = [] + for road in roads: + geometry = [[pt["x"], pt["y"], pt["z"]] for pt in road["geometry"]] + all_geometries.extend(geometry) + + print(f"Total number of geometry points: {len(all_geometries)}") + # print(all_geometries[:100]) + + xodr_json["objects"] = [] + + for i in range(num_objects): + id = i + 1 + save_object_to_json( + xodr_json, + road_link_map, + id, + resolution=resolution, + all_geometries=all_geometries, + start_with_zero_velocity=start_with_zero_velocity, + ) + + # Make first agent only controllable + if make_only_first_agent_controllable: + for i, obj in enumerate(xodr_json.get("objects", [])): + if i == 0: + obj["mark_as_expert"] = False + else: + obj["mark_as_expert"] = True + + # Save to file + with open(output_json_path, "w") as f: + json.dump(xodr_json, f, indent=2) + + # Verify number of objects + with open(output_json_path, "r") as f: + xodr_json = json.load(f) + assert len(xodr_json.get("objects", [])) == num_objects + + print(f"Saved {num_objects} objects to {output_json_path}") + + +if __name__ == "__main__": + town_names = ["Town01", "Town02", "Town03", "Town04", "Town05", "Town06", "Town07", "Town10HD"] + # town_names = ['Town03'] + input_json_base_path = "data_utils/carla" + output_json_root_dir = "data/processed/carla_data" + carla_map_dir = "/scratch/pm3881/Carla-0.10.0-Linux-Shipping/CarlaUnreal/Content/Carla/Maps/OpenDrive" + resolution = 0.1 + num_data_per_map = 20 + num_objects = 32 + make_only_first_agent_controllable = False + start_with_zero_velocity = True + generate_data_each_map( + town_names, + carla_map_dir, + resolution, + input_json_base_path, + output_json_root_dir, + num_data_per_map, + num_objects=num_objects, + make_only_first_agent_controllable=make_only_first_agent_controllable, + start_with_zero_velocity=start_with_zero_velocity, + ) diff --git a/data_utils/carla/roads_analysis.ipynb b/data_utils/carla/roads_analysis.ipynb new file mode 100644 index 000000000..7588438e1 --- /dev/null +++ b/data_utils/carla/roads_analysis.ipynb @@ -0,0 +1,262 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "0b6f726c", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "import json\n", + "import glob\n", + "import numpy as np\n", + "from lxml import etree\n", + "from pyxodr.road_objects.road import Road\n", + "from pyxodr.road_objects.lane import Lane, ConnectionPosition, LaneOrientation, TrafficOrientation\n", + "from pyxodr.road_objects.junction import Junction\n", + "from pyxodr.road_objects.lane_section import LaneSection\n", + "from pyxodr.road_objects.network import RoadNetwork\n", + "from shapely.geometry import Polygon\n", + "from enum import IntEnum" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79719698", + "metadata": {}, + "outputs": [], + "source": [ + "carla_map_dir = \"C:\\\\Carla-0.10.0\\\\CarlaUnreal\\\\Content\\\\Carla\\\\Maps\\\\OpenDrive\"\n", + "odr_file = os.path.join(carla_map_dir, \"Town04.xodr\")\n", + "\n", + "road_network = RoadNetwork(xodr_file_path=odr_file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6fb6ef11", + "metadata": {}, + "outputs": [], + "source": [ + "roads = road_network.get_roads()\n", + "print(f\"Number of roads in the network: {len(roads)}\")\n", + "print(f\"Type: {type(roads[0])}\\nRoads: {roads}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b75ca96f", + "metadata": {}, + "outputs": [], + "source": [ + "multi_lane_section_roads = []\n", + "\n", + "for road in roads:\n", + " if len(road.lane_sections) > 1:\n", + " multi_lane_section_roads.append(road)\n", + "\n", + "print(f\"Number of roads with multiple lane sections: {len(multi_lane_section_roads)}\")\n", + "print(\"Road IDs with multiple lane sections:\")\n", + "for road in multi_lane_section_roads:\n", + " print(f\" - {road.id}\")\n", + "\n", + "# Check for no links case in lanes of roads with multiple lane sections\n", + "for road in multi_lane_section_roads:\n", + " for lane_section in road.lane_sections:\n", + " for lane in lane_section.left_lanes:\n", + " if lane.lane_xml.attrib[\"type\"] == \"driving\":\n", + " link_xml = lane.lane_xml.find(\"link\")\n", + " if link_xml is None:\n", + " print(\n", + " f\" - Road ID: {road.id}, Lane Section ID: {lane_section.lane_section_ordinal}, Lane ID: {lane.id} has no link.\"\n", + " )\n", + " for lane in lane_section.right_lanes:\n", + " if lane.lane_xml.attrib[\"type\"] == \"driving\":\n", + " link_xml = lane.lane_xml.find(\"link\")\n", + " if link_xml is None:\n", + " print(\n", + " f\" - Road ID: {road.id}, Lane Section ID: {lane_section.lane_section_ordinal}, Lane ID: {lane.id} has no link.\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "08461732", + "metadata": {}, + "source": [ + "Conclusion: There is no driving lane without a link in a road that has multiple lane_sections" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70cb7fb6", + "metadata": {}, + "outputs": [], + "source": [ + "# Finding multi-successor/predecessor roads\n", + "\n", + "multi_successor_roads = []\n", + "multi_predecessor_roads = []\n", + "\n", + "for road in road_network.get_roads():\n", + " if len(road.road_xml.find(\"link\").findall(\"successor\")) > 1:\n", + " multi_successor_roads.append(road)\n", + " if len(road.road_xml.find(\"link\").findall(\"predecessor\")) > 1:\n", + " multi_predecessor_roads.append(road)\n", + "\n", + "print(f\"Multi-successor roads: {[road.id for road in multi_successor_roads]}\")\n", + "print(f\"Multi-predecessor roads: {[road.id for road in multi_predecessor_roads]}\")\n", + "\n", + "# Finding multi-successor/predecessor lanes\n", + "\n", + "multi_successor_lanes = []\n", + "multi_predecessor_lanes = []\n", + "\n", + "for road in road_network.get_roads():\n", + " for lane_section in road.lane_sections:\n", + " for lane in lane_section.left_lanes + lane_section.right_lanes:\n", + " if lane.lane_xml.find(\"link\") is not None:\n", + " if lane.lane_xml.find(\"link\").findall(\"successor\") is not []:\n", + " if len(lane.lane_xml.find(\"link\").findall(\"successor\")) > 1:\n", + " multi_successor_lanes.append((lane.id, lane_section.lane_section_ordinal, road.id))\n", + " if lane.lane_xml.find(\"link\").findall(\"predecessor\") is not []:\n", + " if len(lane.lane_xml.find(\"link\").findall(\"predecessor\")) > 1:\n", + " multi_predecessor_lanes.append((lane.id, lane_section.lane_section_ordinal, road.id))\n", + "\n", + "print(f\"Multi-successor lanes: {multi_successor_lanes}\")\n", + "print(f\"Multi-predecessor lanes: {multi_predecessor_lanes}\")" + ] + }, + { + "cell_type": "markdown", + "id": "5613bebc", + "metadata": {}, + "source": [ + "Conclusion: There is no road element or lane that has multiple successor or predecessor links" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac751a0b", + "metadata": {}, + "outputs": [], + "source": [ + "def get_road(road_network, road_id):\n", + " for road in road_network.get_roads():\n", + " if road.id == road_id:\n", + " return road\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e63af106", + "metadata": {}, + "outputs": [], + "source": [ + "# Check if every pred of a road has itself as a succ/pred in the pred_road\n", + "for road_obj in road_network.get_roads():\n", + " if road_obj.road_xml.attrib[\"junction\"] != \"-1\":\n", + " continue\n", + " if road_obj.road_xml.find(\"link\") is not None and road_obj.road_xml.find(\"link\").findall(\"predecessor\") is not []:\n", + " for pred in road_obj.road_xml.find(\"link\").findall(\"predecessor\"):\n", + " found = False\n", + " if pred.attrib[\"elementType\"] != \"road\":\n", + " continue\n", + " pred_road = get_road(road_network, pred.attrib[\"elementId\"])\n", + " if pred_road is not None:\n", + " if (\n", + " pred_road.road_xml.find(\"link\") is not None\n", + " and pred_road.road_xml.find(\"link\").findall(\"successor\") is not []\n", + " ):\n", + " for succ in pred_road.road_xml.find(\"link\").findall(\"successor\"):\n", + " if succ.attrib[\"elementType\"] == \"road\" and succ.attrib[\"elementId\"] == road_obj.id:\n", + " found = True\n", + " break\n", + " if (\n", + " pred_road.road_xml.find(\"link\") is not None\n", + " and pred_road.road_xml.find(\"link\").findall(\"predecessor\") is not []\n", + " ):\n", + " for pred_pred in pred_road.road_xml.find(\"link\").findall(\"predecessor\"):\n", + " if pred_pred.attrib[\"elementType\"] == \"road\" and pred_pred.attrib[\"elementId\"] == road_obj.id:\n", + " found = True\n", + " break\n", + " if not found:\n", + " print(\n", + " f\"Road {pred_road.id} is a predecessor of {road_obj.id}, but {pred_road.id} does not list {road_obj.id} as a successor/predecessor.\"\n", + " )\n", + " else:\n", + " print(f\"Predecessor road with ID {pred.attrib['elementId']} not found in the network.\")" + ] + }, + { + "cell_type": "markdown", + "id": "eba158a3", + "metadata": {}, + "source": [ + "Conclusion: Every predecessor of a road has itself as a successor/predecessor in the pred_road, and vice-versa for successor" + ] + }, + { + "cell_type": "markdown", + "id": "8a3083c5", + "metadata": {}, + "source": [ + "Roads Data Analysis in JSON" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4826ec18", + "metadata": {}, + "outputs": [], + "source": [ + "json_dir = \"../carla\" # replace with your directory path\n", + "json_files = glob.glob(os.path.join(json_dir, \"*.json\"))\n", + "\n", + "for jf in json_files:\n", + " with open(jf, \"r\") as f:\n", + " print(f\"\\nReading JSON file: {jf}\")\n", + " data = json.load(f)\n", + " if \"roads\" in data:\n", + " num_geometries = [len(road[\"geometry\"]) for road in data[\"roads\"]]\n", + " print(f\"Number of roads in the JSON: {len(data['roads'])}\")\n", + " print(f\"Max geometries in a road: {max(num_geometries)}\")\n", + " print(f\"Min geometries in a road: {min(num_geometries)}\")\n", + " print(f\"Total geometries in the JSON: {sum(num_geometries)}\")\n", + " print(f\"Average geometries per road: {sum(num_geometries) / len(num_geometries)}\")\n", + " else:\n", + " print(\"No 'roads' key found in this JSON file.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/gym_env.py b/examples/gym_env.py index 8d8597893..94e525d93 100644 --- a/examples/gym_env.py +++ b/examples/gym_env.py @@ -1,6 +1,7 @@ import gym import pufferlib.emulation + class SampleGymEnv(gym.Env): def __init__(self): self.observation_space = gym.spaces.Box(low=-1, high=1, shape=(1,)) @@ -12,7 +13,8 @@ def reset(self): def step(self, action): return self.observation_space.sample(), 0.0, False, {} -if __name__ == '__main__': + +if __name__ == "__main__": gym_env = SampleGymEnv() gymnasium_env = pufferlib.GymToGymnasium(gym_env) puffer_env = pufferlib.emulation.GymnasiumPufferEnv(gymnasium_env) diff --git a/examples/gymnasium_env.py b/examples/gymnasium_env.py index bb7f00c34..e33ae289e 100644 --- a/examples/gymnasium_env.py +++ b/examples/gymnasium_env.py @@ -1,6 +1,7 @@ import gymnasium import pufferlib.emulation + class SampleGymnasiumEnv(gymnasium.Env): def __init__(self): self.observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(1,)) @@ -12,7 +13,8 @@ def reset(self): def step(self, action): return self.observation_space.sample(), 0.0, False, False, {} -if __name__ == '__main__': + +if __name__ == "__main__": gymnasium_env = SampleGymnasiumEnv() puffer_env = pufferlib.emulation.GymnasiumPufferEnv(gymnasium_env) observation, info = puffer_env.reset() diff --git a/examples/pettingzoo_env.py b/examples/pettingzoo_env.py index 7b1b1abf4..333069229 100644 --- a/examples/pettingzoo_env.py +++ b/examples/pettingzoo_env.py @@ -2,10 +2,11 @@ import pettingzoo import pufferlib.emulation + class SamplePettingzooEnv(pettingzoo.ParallelEnv): def __init__(self): - self.possible_agents = ['agent_0', 'agent_1'] - self.agents = ['agent_0', 'agent_1'] + self.possible_agents = ["agent_0", "agent_1"] + self.agents = ["agent_0", "agent_1"] def observation_space(self, agent): return gymnasium.spaces.Box(low=-1, high=1, shape=(1,)) @@ -25,7 +26,8 @@ def step(self, action): infos = {agent: {} for agent in self.agents} return observations, rewards, terminals, truncations, infos -if __name__ == '__main__': + +if __name__ == "__main__": env = SamplePettingzooEnv() puffer_env = pufferlib.emulation.PettingZooPufferEnv(env) observations, infos = puffer_env.reset() diff --git a/examples/puffer_env.py b/examples/puffer_env.py index 8e89dc14a..f18bad977 100644 --- a/examples/puffer_env.py +++ b/examples/puffer_env.py @@ -1,6 +1,7 @@ import gymnasium import pufferlib.emulation + class SamplePufferEnv(pufferlib.PufferEnv): def __init__(self, buf=None, seed=0): self.single_observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(1,)) @@ -14,16 +15,17 @@ def reset(self, seed=0): def step(self, action): self.observations[:] = self.observation_space.sample() - infos = [{'infos': 'is a list of dictionaries'}] + infos = [{"infos": "is a list of dictionaries"}] return self.observations, self.rewards, self.terminals, self.truncations, infos -if __name__ == '__main__': + +if __name__ == "__main__": puffer_env = SamplePufferEnv() observations, infos = puffer_env.reset() actions = puffer_env.action_space.sample() observations, rewards, terminals, truncations, infos = puffer_env.step(actions) - print('Puffer envs use a vector interface and in-place array updates') - print('Observation:', observations) - print('Reward:', rewards) - print('Terminal:', terminals) - print('Truncation:', truncations) + print("Puffer envs use a vector interface and in-place array updates") + print("Observation:", observations) + print("Reward:", rewards) + print("Terminal:", terminals) + print("Truncation:", truncations) diff --git a/examples/pufferl.py b/examples/pufferl.py index 4294e7063..baa51baaa 100644 --- a/examples/pufferl.py +++ b/examples/pufferl.py @@ -6,7 +6,8 @@ # Equivalent to running puffer train puffer_breakout def cli(): - pufferl.train('puffer_breakout') + pufferl.train("puffer_breakout") + class Policy(torch.nn.Module): def __init__(self, env): @@ -29,17 +30,24 @@ def forward_eval(self, observations, state=None): def forward(self, observations, state=None): return self.forward_eval(observations, state) + # Managing your own trainer -if __name__ == '__main__': - env_name = 'puffer_breakout' +if __name__ == "__main__": + env_name = "puffer_breakout" env_creator = pufferlib.ocean.env_creator(env_name) - vecenv = pufferlib.vector.make(env_creator, num_envs=2, num_workers=2, batch_size=1, - backend=pufferlib.vector.Multiprocessing, env_kwargs={'num_envs': 4096}) + vecenv = pufferlib.vector.make( + env_creator, + num_envs=2, + num_workers=2, + batch_size=1, + backend=pufferlib.vector.Multiprocessing, + env_kwargs={"num_envs": 4096}, + ) policy = Policy(vecenv.driver_env).cuda() - args = pufferl.load_config('default') - args['train']['env'] = env_name + args = pufferl.load_config("default") + args["train"]["env"] = env_name - trainer = pufferl.PuffeRL(args['train'], vecenv, policy) + trainer = pufferl.PuffeRL(args["train"], vecenv, policy) for epoch in range(10): trainer.evaluate() diff --git a/examples/render.py b/examples/render.py index d66f076ec..b3965859f 100644 --- a/examples/render.py +++ b/examples/render.py @@ -1,8 +1,7 @@ - from pufferlib.ocean.breakout import breakout + env = breakout.Breakout() env.reset() while True: env.step(env.action_space.sample()) frame = env.render() - diff --git a/examples/structured_env.py b/examples/structured_env.py index 6dc678445..f7af0bee3 100644 --- a/examples/structured_env.py +++ b/examples/structured_env.py @@ -1,12 +1,15 @@ import gymnasium import pufferlib.emulation + class SampleGymnasiumEnv(gymnasium.Env): def __init__(self): - self.observation_space = gymnasium.spaces.Dict({ - 'foo': gymnasium.spaces.Box(low=-1, high=1, shape=(2,)), - 'bar': gymnasium.spaces.Box(low=2, high=3, shape=(3,)), - }) + self.observation_space = gymnasium.spaces.Dict( + { + "foo": gymnasium.spaces.Box(low=-1, high=1, shape=(2,)), + "bar": gymnasium.spaces.Box(low=2, high=3, shape=(3,)), + } + ) self.action_space = gymnasium.spaces.MultiDiscrete([2, 5]) def reset(self): @@ -15,20 +18,22 @@ def reset(self): def step(self, action): return self.observation_space.sample(), 0.0, False, False, {} -if __name__ == '__main__': + +if __name__ == "__main__": gymnasium_env = SampleGymnasiumEnv() puffer_env = pufferlib.emulation.GymnasiumPufferEnv(gymnasium_env) flat_observation, info = puffer_env.reset() flat_action = puffer_env.action_space.sample() flat_observation, reward, terminal, truncation, info = puffer_env.step(flat_action) - print(f'PufferLib flattens observations and actions:\n{flat_observation}\n{flat_action}') + print(f"PufferLib flattens observations and actions:\n{flat_observation}\n{flat_action}") observation = flat_observation.view(puffer_env.obs_dtype) - print(f'You can unflatten observations with numpy:\n{observation}') + print(f"You can unflatten observations with numpy:\n{observation}") import torch import pufferlib.pytorch + flat_torch_observation = torch.from_numpy(flat_observation) torch_dtype = pufferlib.pytorch.nativize_dtype(puffer_env.emulated) torch_observation = pufferlib.pytorch.nativize_tensor(flat_torch_observation, torch_dtype) - print(f'But we suggest unflattening observations with torch in your model forward pass:\n{torch_observation}') + print(f"But we suggest unflattening observations with torch in your model forward pass:\n{torch_observation}") diff --git a/examples/vectorization.py b/examples/vectorization.py index 4a6ff2ecb..62c999789 100644 --- a/examples/vectorization.py +++ b/examples/vectorization.py @@ -2,6 +2,7 @@ import pufferlib.emulation import pufferlib.vector + class SamplePufferEnv(pufferlib.PufferEnv): def __init__(self, foo=0, bar=1, buf=None, seed=0): self.single_observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(1,)) @@ -19,60 +20,63 @@ def reset(self, seed=0): def step(self, action): self.observations[:] = self.observation_space.sample() - infos = [{'infos': 'is a list of dictionaries'}] + infos = [{"infos": "is a list of dictionaries"}] return self.observations, self.rewards, self.terminals, self.truncations, infos def close(self): pass -if __name__ == '__main__': - serial_vecenv = pufferlib.vector.make( - SamplePufferEnv, num_envs=2, backend=pufferlib.vector.Serial) + +if __name__ == "__main__": + serial_vecenv = pufferlib.vector.make(SamplePufferEnv, num_envs=2, backend=pufferlib.vector.Serial) observations, infos = serial_vecenv.reset() actions = serial_vecenv.action_space.sample() o, r, d, t, i = serial_vecenv.step(actions) - print('Serial VecEnv:') - print('Observations:', o) - print('Rewards:', r) - print('Terminals:', t) - print('Truncations:', d) + print("Serial VecEnv:") + print("Observations:", o) + print("Rewards:", r) + print("Terminals:", t) + print("Truncations:", d) # Pass arguments to all environments like this serial_vecenv = pufferlib.vector.make( - SamplePufferEnv, num_envs=2, backend=pufferlib.vector.Serial, - env_args=[3], env_kwargs={'bar': 4} + SamplePufferEnv, num_envs=2, backend=pufferlib.vector.Serial, env_args=[3], env_kwargs={"bar": 4} ) - print('Foo: ', [env.foo for env in serial_vecenv.envs]) - print('Bar: ', [env.bar for env in serial_vecenv.envs]) + print("Foo: ", [env.foo for env in serial_vecenv.envs]) + print("Bar: ", [env.bar for env in serial_vecenv.envs]) # Or to each environment like this serial_vecenv = pufferlib.vector.make( - [SamplePufferEnv, SamplePufferEnv], num_envs=2, backend=pufferlib.vector.Serial, - env_args=[[3], [4]], env_kwargs=[{'bar': 4}, {'bar': 5}] + [SamplePufferEnv, SamplePufferEnv], + num_envs=2, + backend=pufferlib.vector.Serial, + env_args=[[3], [4]], + env_kwargs=[{"bar": 4}, {"bar": 5}], ) - print('Foo: ', [env.foo for env in serial_vecenv.envs]) - print('Bar: ', [env.bar for env in serial_vecenv.envs]) + print("Foo: ", [env.foo for env in serial_vecenv.envs]) + print("Bar: ", [env.bar for env in serial_vecenv.envs]) - vecenv = pufferlib.vector.make(SamplePufferEnv, - num_envs=2, num_workers=2, batch_size=1, backend=pufferlib.vector.Multiprocessing) - vecenv.async_reset() # You can also use the synchronous API with Multiprocessing + vecenv = pufferlib.vector.make( + SamplePufferEnv, num_envs=2, num_workers=2, batch_size=1, backend=pufferlib.vector.Multiprocessing + ) + vecenv.async_reset() # You can also use the synchronous API with Multiprocessing o, r, d, t, i, env_ids, masks = vecenv.recv() actions = vecenv.action_space.sample() - print('Policy computes actions for all agents in batch_size=1 of the total num_envs=2 environments') - print('Actions:', actions) + print("Policy computes actions for all agents in batch_size=1 of the total num_envs=2 environments") + print("Actions:", actions) vecenv.send(actions) # New observations are ready while the other envs are running in the background o, r, d, t, i, env_ids, masks = vecenv.recv() - print('Observations:', o) + print("Observations:", o) # Make sure to close the vecenv when you're done vecenv.close() try: - vecenv = pufferlib.vector.make(SamplePufferEnv, - num_envs=1, num_workers=2, batch_size=3, backend=pufferlib.vector.Multiprocessing) + vecenv = pufferlib.vector.make( + SamplePufferEnv, num_envs=1, num_workers=2, batch_size=3, backend=pufferlib.vector.Multiprocessing + ) except pufferlib.APIUsageError: - #Make sure num_envs divides num_workers, and both num_envs and num_workers should divide batch_size + # Make sure num_envs divides num_workers, and both num_envs and num_workers should divide batch_size pass - diff --git a/pufferlib/__init__.py b/pufferlib/__init__.py index f86643a4e..7fb2007e2 100644 --- a/pufferlib/__init__.py +++ b/pufferlib/__init__.py @@ -1,23 +1,26 @@ __version__ = 3.0 import os + path = __path__[0] -link_to = os.path.join(path, 'resources') +link_to = os.path.join(path, "resources") try: - os.symlink(link_to, 'resources') + os.symlink(link_to, "resources") except FileExistsError: pass # Silence noisy dependencies import warnings + warnings.filterwarnings("ignore", category=DeprecationWarning) # Silence noisy packages import sys + original_stdout = sys.stdout original_stderr = sys.stderr -sys.stdout = open(os.devnull, 'w') -sys.stderr = open(os.devnull, 'w') +sys.stdout = open(os.devnull, "w") +sys.stderr = open(os.devnull, "w") try: import gymnasium import pygame @@ -29,4 +32,3 @@ sys.stderr = original_stderr from pufferlib.pufferlib import * -from pufferlib import environments diff --git a/pufferlib/cleanrl_ppo_atari.py b/pufferlib/cleanrl_ppo_atari.py index 595f62ab2..c086dc88d 100644 --- a/pufferlib/cleanrl_ppo_atari.py +++ b/pufferlib/cleanrl_ppo_atari.py @@ -1,5 +1,4 @@ # docs and experiment results can be found at https://docs.cleanrl.dev/rl-algorithms/ppo/#ppo_ataripy -import os import random import time from dataclasses import dataclass @@ -21,9 +20,10 @@ NoopResetEnv, ) + @dataclass class Args: - exp_name: str = 'cleanrl_ppo_atari' + exp_name: str = "cleanrl_ppo_atari" """the name of this experiment""" seed: int = 1 """seed of the experiment""" @@ -182,6 +182,7 @@ def get_action_and_value(self, x, action=None): # PufferLib vectorization makes CleanRL ~65% faster! import pufferlib.vector import pufferlib.environments.atari + envs = pufferlib.vector.make( pufferlib.environments.atari.env_creator(args.env_id), env_kwargs=dict(framestack=4), diff --git a/pufferlib/config/atari.ini b/pufferlib/config/atari.ini deleted file mode 100644 index 75d1557ab..000000000 --- a/pufferlib/config/atari.ini +++ /dev/null @@ -1,49 +0,0 @@ -[base] -package = atari - -env_name = adventure air_raid alien amidar assault asterix asteroids atlantis2 atlantis backgammon bank_heist basic_math battle_zone beam_rider berzerk blackjack bowling boxing breakout carnival casino centipede chopper_command combat crazy_climber crossbow darkchambers defender demon_attack donkey_kong double_dunk earthworld elevator_action enduro entombed et fishing_derby flag_capture freeway frogger frostbite galaxian gopher gravitar hangman haunted_house hero human_cannonball ice_hockey jamesbond journey_escape joust kaboom kangaroo keystone_kapers king_kong klax koolaid krull kung_fu_master laser_gates lost_luggage mario_bros maze_craze miniature_golf montezuma_revenge mr_do ms_pacman name_this_game othello pacman phoenix pitfall2 pitfall pong pooyan private_eye qbert riverraid road_runner robotank seaquest sir_lancelot skiing solaris space_invaders space_war star_gunner superman surround tennis tetris tic_tac_toe_3d time_pilot trondead turmoil tutankham up_n_down venture video_checkers video_chess video_cube video_pinball warlords wizard_of_wor word_zapper yars_revenge zaxxon - -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 128 -num_workers = 16 -batch_size = 64 - -[train] -batch_size = 8192 -minibatch_size = 2048 -update_epochs = 1 -bptt_horizon = 64 -total_timesteps = 10_000_000 -anneal_lr = False - -[env] -frameskip = 1 -repeat_action_probability = 0.0 - -[sweep.parameters.env.parameters.frameskip] -distribution = uniform -min = 1 -max = 10 - -#[sweep.parameters.env.parameters.repeat_action_probability] -#distribution = uniform -#min = 0 -#max = 1 - -[sweep.parameters.train.parameters.total_timesteps] -distribution = uniform -min = 5_000_000 -max = 200_000_000 - -[sweep.parameters.train.parameters.batch_size] -distribution = uniform -min = 16384 -max = 65536 - -[sweep.parameters.train.parameters.minibatch_size] -distribution = uniform -min = 512 -max = 8192 diff --git a/pufferlib/config/box2d.ini b/pufferlib/config/box2d.ini deleted file mode 100644 index a87ae3627..000000000 --- a/pufferlib/config/box2d.ini +++ /dev/null @@ -1,11 +0,0 @@ -[base] -package = box2d -env_name = car-racing - -[train] -num_envs = 48 -num_workers = 24 -env_batch_size = 48 -zero_copy = False -batch_size = 16384 -minibatch_size = 2048 diff --git a/pufferlib/config/bsuite.ini b/pufferlib/config/bsuite.ini deleted file mode 100644 index bab515466..000000000 --- a/pufferlib/config/bsuite.ini +++ /dev/null @@ -1,7 +0,0 @@ -[base] -package = bsuite -env_name = bandit/0 - -[train] -total_timesteps = 1_000_000 -num_envs = 1 diff --git a/pufferlib/config/butterfly.ini b/pufferlib/config/butterfly.ini deleted file mode 100644 index a616c2917..000000000 --- a/pufferlib/config/butterfly.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = butterfly -env_name = cooperative_pong_v5 diff --git a/pufferlib/config/classic_control.ini b/pufferlib/config/classic_control.ini deleted file mode 100644 index 8bc4f44e4..000000000 --- a/pufferlib/config/classic_control.ini +++ /dev/null @@ -1,8 +0,0 @@ -[base] -package = classic_control -env_name = cartpole mountaincar - -[train] -total_timesteps = 500_000 -num_envs = 64 -env_batch_size = 64 diff --git a/pufferlib/config/classic_control_continuous.ini b/pufferlib/config/classic_control_continuous.ini deleted file mode 100644 index cd0603c2e..000000000 --- a/pufferlib/config/classic_control_continuous.ini +++ /dev/null @@ -1,8 +0,0 @@ -[base] -package = classic_control_continuous -env_name = mountaincar-continuous - -[train] -total_timesteps = 500_000 -num_envs = 64 -env_batch_size = 64 diff --git a/pufferlib/config/craftax.ini b/pufferlib/config/craftax.ini deleted file mode 100644 index 2c5b2023f..000000000 --- a/pufferlib/config/craftax.ini +++ /dev/null @@ -1,24 +0,0 @@ -[base] -package = craftax -env_name = Craftax-Symbolic-v1 Craftax-Classic-Symbolic-v1 -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 1024 - -[vec] -num_envs = 1 -num_workers = 1 -batch_size = 1 - -[train] -total_timesteps = 100_000_000 -checkpoint_interval = 200 -update_epochs = 4 -batch_size = 131072 -minibatch_size = 8192 -learning_rate = 0.0002 -gamma = 0.99 -gae_lambda = 0.8 -ent_coef = 0.01 diff --git a/pufferlib/config/crafter.ini b/pufferlib/config/crafter.ini deleted file mode 100644 index d46260889..000000000 --- a/pufferlib/config/crafter.ini +++ /dev/null @@ -1,10 +0,0 @@ -[base] -package = crafter -env_name = crafter - -[train] -num_envs = 96 -num_workers = 24 -env_batch_size = 48 -zero_copy = False -batch_size = 6144 diff --git a/pufferlib/config/default.ini b/pufferlib/config/default.ini index 0990fdb13..810de828c 100644 --- a/pufferlib/config/default.ini +++ b/pufferlib/config/default.ini @@ -18,7 +18,7 @@ seed = 42 [rnn] [train] -name = pufferai +name = pufferai project = ablations seed = 42 @@ -61,7 +61,7 @@ prio_alpha = 0.8 prio_beta0 = 0.2 [sweep] -method = Protein +method = Protein metric = score goal = maximize downsample = 10 @@ -73,138 +73,138 @@ downsample = 10 #mean = 8 #scale = auto -# TODO: Elim from base -[sweep.train.total_timesteps] -distribution = log_normal -min = 5e7 -max = 1e10 -mean = 1e8 -scale = time - -[sweep.train.bptt_horizon] -distribution = uniform_pow2 -min = 16 -max = 64 -mean = 64 -scale = auto - -[sweep.train.minibatch_size] -distribution = uniform_pow2 -min = 8192 -max = 65536 -mean = 32768 -scale = auto - -[sweep.train.learning_rate] -distribution = log_normal -min = 0.00001 -mean = 0.01 -max = 0.1 -scale = 0.5 - -[sweep.train.ent_coef] -distribution = log_normal -min = 0.00001 -mean = 0.01 -max = 0.2 -scale = auto - -[sweep.train.gamma] -distribution = logit_normal -min = 0.8 -mean = 0.98 -max = 0.9999 -scale = auto - -[sweep.train.gae_lambda] -distribution = logit_normal -min = 0.6 -mean = 0.95 -max = 0.995 -scale = auto - -[sweep.train.vtrace_rho_clip] -distribution = uniform -min = 0.0 -max = 5.0 -mean = 1.0 -scale = auto - -[sweep.train.vtrace_c_clip] -distribution = uniform -min = 0.0 -max = 5.0 -mean = 1.0 -scale = auto - -#[sweep.train.update_epochs] -#distribution = int_uniform -#min = 1 -#max = 8 -#mean = 1 -#scale = 2.0 - -[sweep.train.clip_coef] -distribution = uniform -min = 0.01 -max = 1.0 -mean = 0.2 -scale = auto - -# Optimal vf clip can be lower than 0.1, -# but this results in jank unstable runs -[sweep.train.vf_clip_coef] -distribution = uniform -min = 0.1 -max = 5.0 -mean = 0.2 -scale = auto - -[sweep.train.vf_coef] -distribution = uniform -min = 0.0 -max = 5.0 -mean = 2.0 -scale = auto - -[sweep.train.max_grad_norm] -distribution = uniform -min = 0.0 -mean = 1.0 -max = 5.0 -scale = auto - -[sweep.train.adam_beta1] -distribution = logit_normal -min = 0.5 -mean = 0.9 -max = 0.999 -scale = auto - -[sweep.train.adam_beta2] -distribution = logit_normal -min = 0.9 -mean = 0.999 -max = 0.99999 -scale = auto - -[sweep.train.adam_eps] -distribution = log_normal -min = 1e-14 -mean = 1e-8 -max = 1e-4 -scale = auto - -[sweep.train.prio_alpha] -distribution = logit_normal -min = 0.1 -mean = 0.85 -max = 0.99 -scale = auto - -[sweep.train.prio_beta0] -distribution = logit_normal -min = 0.1 -mean = 0.85 -max = 0.99 -scale = auto +; # TODO: Elim from base +; [sweep.train.total_timesteps] +; distribution = log_normal +; min = 5e7 +; max = 1e10 +; mean = 1e8 +; scale = time + +; [sweep.train.bptt_horizon] +; distribution = uniform_pow2 +; min = 16 +; max = 64 +; mean = 64 +; scale = auto + +; [sweep.train.minibatch_size] +; distribution = uniform_pow2 +; min = 8192 +; max = 65536 +; mean = 32768 +; scale = auto + +; [sweep.train.learning_rate] +; distribution = log_normal +; min = 0.00001 +; mean = 0.01 +; max = 0.1 +; scale = 0.5 + +; [sweep.train.ent_coef] +; distribution = log_normal +; min = 0.00001 +; mean = 0.01 +; max = 0.2 +; scale = auto + +; [sweep.train.gamma] +; distribution = logit_normal +; min = 0.8 +; mean = 0.98 +; max = 0.9999 +; scale = auto + +; [sweep.train.gae_lambda] +; distribution = logit_normal +; min = 0.6 +; mean = 0.95 +; max = 0.995 +; scale = auto + +; [sweep.train.vtrace_rho_clip] +; distribution = uniform +; min = 0.0 +; max = 5.0 +; mean = 1.0 +; scale = auto + +; [sweep.train.vtrace_c_clip] +; distribution = uniform +; min = 0.0 +; max = 5.0 +; mean = 1.0 +; scale = auto + +; #[sweep.train.update_epochs] +; #distribution = int_uniform +; #min = 1 +; #max = 8 +; #mean = 1 +; #scale = 2.0 + +; [sweep.train.clip_coef] +; distribution = uniform +; min = 0.01 +; max = 1.0 +; mean = 0.2 +; scale = auto + +; # Optimal vf clip can be lower than 0.1, +; # but this results in jank unstable runs +; [sweep.train.vf_clip_coef] +; distribution = uniform +; min = 0.1 +; max = 5.0 +; mean = 0.2 +; scale = auto + +; [sweep.train.vf_coef] +; distribution = uniform +; min = 0.0 +; max = 5.0 +; mean = 2.0 +; scale = auto + +; [sweep.train.max_grad_norm] +; distribution = uniform +; min = 0.0 +; mean = 1.0 +; max = 5.0 +; scale = auto + +; [sweep.train.adam_beta1] +; distribution = logit_normal +; min = 0.5 +; mean = 0.9 +; max = 0.999 +; scale = auto + +; [sweep.train.adam_beta2] +; distribution = logit_normal +; min = 0.9 +; mean = 0.999 +; max = 0.99999 +; scale = auto + +; [sweep.train.adam_eps] +; distribution = log_normal +; min = 1e-14 +; mean = 1e-8 +; max = 1e-4 +; scale = auto + +; [sweep.train.prio_alpha] +; distribution = logit_normal +; min = 0.1 +; mean = 0.85 +; max = 0.99 +; scale = auto + +; [sweep.train.prio_beta0] +; distribution = logit_normal +; min = 0.1 +; mean = 0.85 +; max = 0.99 +; scale = auto diff --git a/pufferlib/config/dm_control.ini b/pufferlib/config/dm_control.ini deleted file mode 100644 index 8cce146e1..000000000 --- a/pufferlib/config/dm_control.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = dm_control -env_name = dmc diff --git a/pufferlib/config/dm_lab.ini b/pufferlib/config/dm_lab.ini deleted file mode 100644 index 8350696de..000000000 --- a/pufferlib/config/dm_lab.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = dm_lab -env_name = dml diff --git a/pufferlib/config/doom.ini b/pufferlib/config/doom.ini deleted file mode 100644 index 62cf850e7..000000000 --- a/pufferlib/config/doom.ini +++ /dev/null @@ -1,12 +0,0 @@ -[base] -package = vizdoom -env_name = doom - -[train] -num_envs = 144 -num_workers = 24 -env_batch_size = 48 -zero_copy = False -batch_size = 8192 -minibatch_size = 2048 -update_epochs = 1 diff --git a/pufferlib/config/gpudrive.ini b/pufferlib/config/gpudrive.ini deleted file mode 100644 index 41052ecfe..000000000 --- a/pufferlib/config/gpudrive.ini +++ /dev/null @@ -1,46 +0,0 @@ -[base] -package = gpudrive -env_name = gpudrive -policy_name = Policy -rnn_name = Recurrent - -[env] -num_worlds = 512 - -[train] -total_timesteps = 10_000_000 -num_envs = 1 -num_workers = 1 -env_batch_size = 1 -zero_copy = False -batch_size = 262_144 -update_epochs = 5 -minibatch_size = 32768 -bptt_horizon = 4 -anneal_lr = False -gae_lambda = 0.95 -gamma = 0.99 -clip_coef = 0.2 -vf_coef = 0.5 -vf_clip_coef = 0.2 -max_grad_norm = 0.5 -ent_coef = 0.00 -learning_rate = 0.0003 -checkpoint_interval = 1000 -device = cuda - -[sweep.metric] -goal = maximize -name = environment/goal_achieved - -[sweep.parameters.train.parameters.batch_size] -distribution = uniform -min = 32768 -max = 524288 - -[sweep.parameters.train.parameters.minibatch_size] -distribution = uniform -min = 2048 -max = 32768 - - diff --git a/pufferlib/config/griddly.ini b/pufferlib/config/griddly.ini deleted file mode 100644 index ab23d7ba8..000000000 --- a/pufferlib/config/griddly.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = griddly -env_name = spiders diff --git a/pufferlib/config/gvgai.ini b/pufferlib/config/gvgai.ini deleted file mode 100644 index 21459bca5..000000000 --- a/pufferlib/config/gvgai.ini +++ /dev/null @@ -1,36 +0,0 @@ -[base] -package = gvgai -env_name = zelda -policy_name = Policy -rnn_name = Recurrent - -[train] -total_timesteps = 1_000_000 -checkpoint_interval = 1000 -learning_rate = 0.00024290984560207393 -num_envs = 96 -num_workers = 24 -env_batch_size = 32 -update_epochs = 1 -zero_copy = False -bptt_horizon = 16 -batch_size = 4096 -minibatch_size = 1024 -compile = False -anneal_lr = False -device = cuda - -[sweep.metric] -goal = maximize -name = environment/reward - -[sweep.parameters.train.parameters.total_timesteps] -distribution = log_uniform_values -min = 500_000_000 -max = 10_000_000_000 - -[sweep.parameters.train.parameters.batch_size] -values = [4096, 8192, 16384] - -[sweep.parameters.train.parameters.minibatch_size] -values = [512, 1024, 2048, 4096] diff --git a/pufferlib/config/kinetix.ini b/pufferlib/config/kinetix.ini deleted file mode 100644 index e9f6e02ac..000000000 --- a/pufferlib/config/kinetix.ini +++ /dev/null @@ -1,53 +0,0 @@ -[base] -package = kinetix -env_name = kinetix-pixels-discrete kinetix-symbolic-discrete -policy_name = PixelsPolicy -rnn_name = Recurrent - -[env] -num_envs = 2048 - -[train] -total_timesteps = 100_000_000 -checkpoint_interval = 200 -num_envs = 1 -num_workers = 1 -env_batch_size = 1 -update_epochs = 8 -batch_size = 131072 -minibatch_size = 4096 -learning_rate = 0.0003 -gamma = 0.995 -gae_lambda = 0.9 -ent_coef = 0.01 - -[sweep] -method = protein -name = sweep - -[sweep.metric] -goal = maximize -name = GoalR -min = 0 -max = None - -[sweep.train.total_timesteps] -distribution = log_normal -min = 5e6 -max = 1e9 -mean = 1e7 -scale = auto - -[sweep.train.batch_size] -distribution = uniform_pow2 -min = 32768 -max = 131072 -mean = 65536 -scale = auto - -[sweep.train.minibatch_size] -distribution = uniform_pow2 -min = 1024 -max = 32768 -mean = 8192 -scale = auto diff --git a/pufferlib/config/magent.ini b/pufferlib/config/magent.ini deleted file mode 100644 index 0df7336f8..000000000 --- a/pufferlib/config/magent.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = magent -env_name = battle_v4 diff --git a/pufferlib/config/mani_skill.ini b/pufferlib/config/mani_skill.ini deleted file mode 100644 index 56e990d41..000000000 --- a/pufferlib/config/mani_skill.ini +++ /dev/null @@ -1,69 +0,0 @@ - -[base] -package = mani_skill -env_name = mani_pickcube mani_pushcube mani_stackcube mani_peginsertion -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 4096 -sim_steps_per_control = 5 -control_freq = 100 -solver_position_iterations = 15 - -[vec] -backend = PufferEnv -num_envs = 1 - -[train] -total_timesteps = 15_000_000 -adam_beta1 = 0.9832254546070032 -adam_beta2 = 0.9996089758513379 -adam_eps = 0.0000024542110227211678 -bptt_horizon = 64 -clip_coef = 0.6609987983481933 -ent_coef = 0.001194131610607018 -gae_lambda = 0.968478898646462 -gamma = 0.8880001899050386 -learning_rate = 0.04729013902338006 -max_grad_norm = 1.9301595176438802 -minibatch_size = 32768 -prio_alpha = 0.9531362058849446 -prio_beta0 = 0.8285186322612919 -vf_clip_coef = 0.2581908677409054 -vf_coef = 2.6102252379894217 -vtrace_c_clip = 2.008516783867587 -vtrace_rho_clip = 0.7482202150166445 - -[sweep] -method = Protein -metric = success_once -downsample = 0 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 2e7 -max = 5e7 -mean = 4e7 -scale = time - -[sweep.env.sim_steps_per_control] -distribution = int_uniform -min = 1 -max = 10 -mean = 5 -scale = auto - -[sweep.env.control_freq] -distribution = int_uniform -min = 10 -max = 100 -mean = 20 -scale = auto - -[sweep.env.solver_position_iterations] -distribution = int_uniform -min = 4 -max = 30 -mean = 15 -scale = auto diff --git a/pufferlib/config/metta.ini b/pufferlib/config/metta.ini deleted file mode 100644 index 355e2b3a2..000000000 --- a/pufferlib/config/metta.ini +++ /dev/null @@ -1,67 +0,0 @@ -[base] -package = metta -env_name = metta -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 64 -num_workers = 16 - -[env] -render_mode = auto -ore_reward = 0.17088483842567775 -battery_reward = 0.9882859711234822 -heart_reward = 1.0 - -[train] -total_timesteps = 300_000_000 -batch_size = auto -adam_beta1 = 0.8923106632311335 -adam_beta2 = 0.9632470625784862 -adam_eps = 1.3537431449843922e-7 -clip_coef = 0.14919147162017737 -ent_coef = 0.016700174334611493 -gae_lambda = 0.8443676864928215 -gamma = 0.997950174315581 -learning_rate = 0.018470110879570414 -max_grad_norm = 2.572849891206465 -minibatch_size = 32768 -bptt_horizon = 64 -prio_alpha = 0.7918451491719373 -prio_beta0 = 0.5852686803034238 -vf_clip_coef = 0.1569624916309049 -vf_coef = 3.2211333828684454 -vtrace_c_clip = 2.134490283650365 -vtrace_rho_clip = 2.296343917695581 - -[sweep] -metric = agent/heart.gained - -[sweep.train.total_timesteps] -distribution = log_normal -min = 1e8 -max = 5e8 -mean = 3e8 -scale = auto - -[sweep.env.ore_reward] -distribution = uniform -min = 0.0 -mean = 0.25 -max = 1.0 -scale = auto - -[sweep.env.battery_reward] -distribution = uniform -min = 0.0 -mean = 0.5 -max = 1.0 -scale = auto - -[sweep.env.heart_reward] -distribution = uniform -min = 0.0 -mean = 1.0 -max = 1.0 -scale = auto diff --git a/pufferlib/config/microrts.ini b/pufferlib/config/microrts.ini deleted file mode 100644 index 3c1da0f24..000000000 --- a/pufferlib/config/microrts.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = microrts -env_name = GlobalAgentCombinedRewardEnv diff --git a/pufferlib/config/minerl.ini b/pufferlib/config/minerl.ini deleted file mode 100644 index 25190f698..000000000 --- a/pufferlib/config/minerl.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = minerl -env_name = MineRLNavigateDense-v0 diff --git a/pufferlib/config/minigrid.ini b/pufferlib/config/minigrid.ini deleted file mode 100644 index c44178d9f..000000000 --- a/pufferlib/config/minigrid.ini +++ /dev/null @@ -1,19 +0,0 @@ -[base] -package = minigrid -env_name = minigrid - -[vec] -num_envs = 48 -num_workers = 6 -batch_size = 48 - -[train] -total_timesteps = 1_000_000 -batch_size = 6144 -minibatch_size = 768 -update_epochs = 4 -anneal_lr = False -gae_lambda = 0.95 -gamma = 0.95 -ent_coef = 0.025 -learning_rate = 2.5e-4 diff --git a/pufferlib/config/minihack.ini b/pufferlib/config/minihack.ini deleted file mode 100644 index e356a5518..000000000 --- a/pufferlib/config/minihack.ini +++ /dev/null @@ -1,15 +0,0 @@ -[base] -package = minihack -env_name = minihack - -[vec] -num_envs = 128 -num_workers = 16 -batch_size = 64 - -[train] -batch_size = 8192 -minibatch_size = 2048 -update_epochs = 1 -bptt_horizon = 64 -total_timesteps = 10_000_000 diff --git a/pufferlib/config/mujoco.ini b/pufferlib/config/mujoco.ini deleted file mode 100644 index 9de3495a1..000000000 --- a/pufferlib/config/mujoco.ini +++ /dev/null @@ -1,29 +0,0 @@ -[base] -package = mujoco -env_name = HalfCheetah-v4 Hopper-v4 Swimmer-v4 Walker2d-v4 Ant-v4 Humanoid-v4 Reacher-v4 InvertedPendulum-v4 InvertedDoublePendulum-v4 Pusher-v4 HumanoidStandup-v4 -policy_name = Policy -rnn_name = Recurrent - -[env] -render_mode = rgb_array - -[vec] -num_envs = 512 -num_workers = 16 -batch_size = auto - -[train] -total_timesteps = 5_000_000 -learning_rate = 3e-4 -gamma = 0.99 -gae_lambda = 0.95 -update_epochs = 10 -clip_coef = 0.2 -vf_coef = 0.5 -vf_clip_coef = 0.2 -max_grad_norm = 0.5 -ent_coef = 0.0 -checkpoint_interval = 200 -batch_size = 32768 -minibatch_size = 4096 -bptt_horizon = 64 diff --git a/pufferlib/config/nethack.ini b/pufferlib/config/nethack.ini deleted file mode 100644 index 9e35dbff5..000000000 --- a/pufferlib/config/nethack.ini +++ /dev/null @@ -1,16 +0,0 @@ -[base] -package = nethack -env_name = nethack - -[vec] -num_envs = 128 -num_workers = 16 -batch_size = 64 - -[train] -batch_size = 8192 -minibatch_size = 2048 -update_epochs = 1 -bptt_horizon = 64 -total_timesteps = 10_000_000 -anneal_lr = False diff --git a/pufferlib/config/nmmo.ini b/pufferlib/config/nmmo.ini deleted file mode 100644 index 4a4f419ca..000000000 --- a/pufferlib/config/nmmo.ini +++ /dev/null @@ -1,10 +0,0 @@ -[base] -package = nmmo -env_name = nmmo - -[train] -num_envs = 4 -env_batch_size = 4 -num_workers = 4 -batch_size = 4096 -minibatch_size = 2048 diff --git a/pufferlib/config/ocean/asteroids.ini b/pufferlib/config/ocean/asteroids.ini deleted file mode 100644 index 754d62418..000000000 --- a/pufferlib/config/ocean/asteroids.ini +++ /dev/null @@ -1,37 +0,0 @@ -[base] -package = ocean -env_name = puffer_asteroids -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 -size = 500 - -[train] -adam_beta1 = 0.975493290069733 -adam_beta2 = 0.9999436458974764 -adam_eps = 6.915036275112011e-08 -anneal_lr = true -batch_size = auto -bptt_horizon = 64 -checkpoint_interval = 200 -clip_coef = 0.18588778503512546 -ent_coef = 0.0016620361911332262 -gae_lambda = 0.8400278040617952 -gamma = 0.9998708818940873 -learning_rate = 0.00502237062536979 -max_grad_norm = 0.7306435358436453 -max_minibatch_size = 32768 -minibatch_size = 8192 -prio_alpha = 0.9165093859993415 -prio_beta0 = 0.8869674411376214 -total_timesteps = 100_000_000 -update_epochs = 1 -vf_clip_coef = 0.1 -vf_coef = 2.960148388519086 -vtrace_c_clip = 1.0767718761515104 -vtrace_rho_clip = 4.132507367126342 diff --git a/pufferlib/config/ocean/battle.ini b/pufferlib/config/ocean/battle.ini deleted file mode 100644 index dcb4baa0f..000000000 --- a/pufferlib/config/ocean/battle.ini +++ /dev/null @@ -1,66 +0,0 @@ -[base] -package = ocean -env_name = puffer_battle -policy_name = Policy -rnn_name = Recurrent - -[policy] -hidden_size = 512 - -[rnn] -input_size = 512 -hidden_size = 512 - -[vec] -num_envs = 16 - -[env] -num_envs = 4 -num_agents = 128 -num_armies = 2 -size_x = 2 -size_y = 1.0 -size_z = 2 - -[train] -total_timesteps = 50_000_000 - -#adam_beta1 = 0.9672322418397323 -#adam_beta2 = 0.9877607751795193 -#adam_eps = 3.1721115738865995e-12 -#clip_coef = 0.43568934504743784 -#ent_coef = 0.0009836417478975427 -#gae_lambda = 0.9668222538234107 -#gamma = 0.990709789440733 -#learning_rate = 0.006246420318636455 -#max_grad_norm = 1.7919049246329588 -#minibatch_size = 65536 -#prio_alpha = 0.09999999999999998 -#prio_beta0 = 0.7406397128300295 -#vf_clip_coef = 1.6190073090306314 -#vf_coef = 3.4918587292978454 -#vtrace_c_clip = 0.5344573247342275 -#vtrace_rho_clip = 1.2893540729776307 - -#learning_rate = 0.0015534438005054883 -#gamma = 0.9923382806478448 -#minibatch_size = 32768 - -#adam_beta1 = 0.5797997352318079 -#adam_beta2 = 0.9001752474216785 -#adam_eps = 6.121819610318236e-8 -#clip_coef = 0.3616904471336408 -#ent_coef = 0.020160134702951138 -#gae_lambda = 0.5999999999999999 -#gamma = 0.9923382806478448 -#learning_rate = 0.0015534438005054883 -#max_grad_norm = 0.5104995231814086 -#minibatch_size = 65536 -#prio_alpha = 0.5594506255128311 -#prio_beta0 = 0.9852161239512259 -#vf_clip_coef = 0.1 -#vf_coef = 1.8024088377114245 -#vtrace_c_clip = 1.7578256946375268 -#vtrace_rho_clip = 1.0041987439042879 - - diff --git a/pufferlib/config/ocean/blastar.ini b/pufferlib/config/ocean/blastar.ini deleted file mode 100644 index ddde00396..000000000 --- a/pufferlib/config/ocean/blastar.ini +++ /dev/null @@ -1,23 +0,0 @@ -[base] -package = ocean -env_name = puffer_blastar -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 4096 - -[train] -total_timesteps = 200_000_000 -gamma = 0.95 -learning_rate = 0.05 -minibatch_size = 32768 - -[sweep] -metric = environment/enemy_crossed_screen -goal = minimize - -[sweep.parameters.train.parameters.total_timesteps] -distribution = uniform -min = 10_000_000 -max = 100_000_000 diff --git a/pufferlib/config/ocean/boids.ini b/pufferlib/config/ocean/boids.ini deleted file mode 100644 index 2f8412d24..000000000 --- a/pufferlib/config/ocean/boids.ini +++ /dev/null @@ -1,72 +0,0 @@ -[base] -package = ocean -env_name = puffer_boids -policy_name = Boids -rnn_name = Recurrent -; rnn_name = None - -[env] -num_envs = 64 -num_boids = 64 -; num_envs = 1 -; num_boids = 1 -margin_turn_factor = 0.0 -centering_factor = 0.00 -avoid_factor = 1.00 -matching_factor = 1.00 - -[vec] -num_workers = 2 -num_envs = 2 -batch_size = auto - -[train] -total_timesteps = 100_000_000 -gamma = 0.95 -learning_rate = 0.025 -minibatch_size = 16384 -; minibatch_size = 1 - -; [sweep] -; method = protein -; metric = episode_length - -; [sweep.train.total_timesteps] -; distribution = log_normal -; min = 1e6 -; max = 1e7 -; mean = 5e6 -; scale = 0.5 - -; [sweep.train.gamma] -; distribution = log_normal -; min = 0.9 -; max = 0.999 -; mean = 0.97 - -; [sweep.train.gae_lambda] -; distribution = log_normal -; min = 0.7 -; max = 0.999 -; mean = 0.95 - -; [sweep.train.learning_rate] -; distribution = log_normal -; min = 0.0001 -; max = 0.001 -; mean = 0.00025 -; scale = 0.5 - -; [sweep.train.batch_size] -; min = 32768 -; max = 131072 -; mean = 65536 -; scale = 0.5 - -; [sweep.train.minibatch_size] -; min = 512 -; max = 2048 -; mean = 1024 -; scale = 0.5 - - diff --git a/pufferlib/config/ocean/breakout.ini b/pufferlib/config/ocean/breakout.ini deleted file mode 100644 index a1cfdbc10..000000000 --- a/pufferlib/config/ocean/breakout.ini +++ /dev/null @@ -1,54 +0,0 @@ -[base] -package = ocean -env_name = puffer_breakout -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 -frameskip = 4 - -[policy] -hidden_size = 128 - -[rnn] -input_size = 128 -hidden_size = 128 - -[train] -total_timesteps = 90_000_000 -adam_beta1 = 0.8946507418260217 -adam_beta2 = 0.9 -adam_eps = 0.0001 -batch_size = auto -bptt_horizon = 64 -clip_coef = 0.19696765958267629 -ent_coef = 0.0005690816545012474 -gae_lambda = 0.747650023961198 -gamma = 0.9997053654668936 -learning_rate = 0.044482546441415506 -max_grad_norm = 2.2356112188495723 -minibatch_size = 32768 -prio_alpha = 0.98967001208896 -prio_beta0 = 0.09999999999999998 -vf_clip_coef = 2.178492167689251 -vf_coef = 1.6832989594296321 -vtrace_c_clip = 2.878171091654008 -vtrace_rho_clip = 0.7876748061547312 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 3e7 -max = 2e8 -mean = 8e7 -scale = auto - -[sweep.env.frameskip] -distribution = int_uniform -min = 1 -max = 8 -mean = 4 -scale = 2.0 diff --git a/pufferlib/config/ocean/cartpole.ini b/pufferlib/config/ocean/cartpole.ini deleted file mode 100644 index 6ecfb7db0..000000000 --- a/pufferlib/config/ocean/cartpole.ini +++ /dev/null @@ -1,56 +0,0 @@ -[base] -package = ocean -env_name = puffer_cartpole -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 4096 - -[train] -total_timesteps = 20_000_000 -gamma = 0.95 -learning_rate = 0.05 -minibatch_size = 32768 - -[sweep] -method = protein -metric = episode_length - -[sweep.train.total_timesteps] -distribution = log_normal -min = 1e6 -max = 1e7 -mean = 5e6 -scale = 0.5 - -[sweep.train.gamma] -distribution = log_normal -min = 0.9 -max = 0.999 -mean = 0.97 - -[sweep.train.gae_lambda] -distribution = log_normal -min = 0.7 -max = 0.999 -mean = 0.95 - -[sweep.train.learning_rate] -distribution = log_normal -min = 0.0001 -max = 0.001 -mean = 0.00025 -scale = 0.5 - -[sweep.train.batch_size] -min = 32768 -max = 131072 -mean = 65536 -scale = 0.5 - -[sweep.train.minibatch_size] -min = 512 -max = 2048 -mean = 1024 -scale = 0.5 diff --git a/pufferlib/config/ocean/checkers.ini b/pufferlib/config/ocean/checkers.ini deleted file mode 100644 index 91eb417f5..000000000 --- a/pufferlib/config/ocean/checkers.ini +++ /dev/null @@ -1,17 +0,0 @@ -[base] -package = ocean -env_name = puffer_checkers -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 4096 -size = 8 - -[vec] -num_envs = 8 - -[train] -total_timesteps = 1_000_000_000 -minibatch_size = 65536 -gamma = 0.95 diff --git a/pufferlib/config/ocean/connect4.ini b/pufferlib/config/ocean/connect4.ini deleted file mode 100644 index 252b79350..000000000 --- a/pufferlib/config/ocean/connect4.ini +++ /dev/null @@ -1,37 +0,0 @@ -[base] -package = ocean -env_name = puffer_connect4 -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 1024 - -[vec] -num_envs = 8 - -[train] -total_timesteps = 22_000_000 -adam_beta1 = 0.7332525176640032 -adam_beta2 = 0.9992588002434659 -adam_eps = 0.0001 -clip_coef = 0.3344358533613167 -ent_coef = 0.00004214003802569246 -gae_lambda = 0.8969790930039623 -gamma = 0.9945932652529774 -learning_rate = 0.1 -max_grad_norm = 1.0219144411399215 -minibatch_size = 32768 -prio_alpha = 0.9057091953725436 -prio_beta0 = 0.6320607520016285 -vf_clip_coef = 1.9948775471721416 -vf_coef = 2.3734839181925462 -vtrace_c_clip = 0.5659747235622431 -vtrace_rho_clip = 1.4499061438546799 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 1e7 -max = 2e8 -mean = 3e7 -scale = 0.5 diff --git a/pufferlib/config/ocean/continuous.ini b/pufferlib/config/ocean/continuous.ini deleted file mode 100644 index fd35cd485..000000000 --- a/pufferlib/config/ocean/continuous.ini +++ /dev/null @@ -1,13 +0,0 @@ -[base] -package = ocean -env_name = puffer_continuous - -[train] -total_timesteps = 1_000_000 -anneal_lr = False -num_envs = 64 -batch_size = 16384 -minibatch_size = 4096 -update_epochs = 1 -gamma = 0.8 -ent_coef = 0.05 diff --git a/pufferlib/config/ocean/convert.ini b/pufferlib/config/ocean/convert.ini deleted file mode 100644 index 53c91519e..000000000 --- a/pufferlib/config/ocean/convert.ini +++ /dev/null @@ -1,22 +0,0 @@ -[base] -package = ocean -env_name = puffer_convert -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 16 - -[env] -num_envs = 1 -num_agents = 1024 -num_factories = 32 -num_resources = 8 - -[train] -total_timesteps = 100_000_000 -gamma = 0.99 -learning_rate = 0.015 -minibatch_size = 32768 -ent_coef = 0.02 - diff --git a/pufferlib/config/ocean/convert_circle.ini b/pufferlib/config/ocean/convert_circle.ini deleted file mode 100644 index 60dfd3467..000000000 --- a/pufferlib/config/ocean/convert_circle.ini +++ /dev/null @@ -1,24 +0,0 @@ -[base] -package = ocean -env_name = puffer_convert_circle -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 16 - -[env] -num_envs = 1 -num_agents = 1024 -num_factories = 32 -num_resources = 8 -equidistant = 1 -radius = 400 - -[train] -total_timesteps = 100_000_000 -gamma = 0.99 -learning_rate = 0.015 -minibatch_size = 32768 -ent_coef = 0.02 - diff --git a/pufferlib/config/ocean/cpr.ini b/pufferlib/config/ocean/cpr.ini deleted file mode 100644 index d64697cd9..000000000 --- a/pufferlib/config/ocean/cpr.ini +++ /dev/null @@ -1,51 +0,0 @@ -[base] -package = ocean -env_name = puffer_cpr -rnn_name = Recurrent - -[env] -num_envs = 512 -vision = 3 -num_agents = [8] -report_interval=1 -reward_food = 0.1 -interactive_food_reward = 0.2 -reward_move = +0.00 -food_base_spawn_rate = 2e-3 - -[train] -total_timesteps = 60_000_000 -bptt_horizon = 16 -checkpoint_interval = 200 -learning_rate = 0.0008524 -gamma = 0.9989 -gae_lambda = 0.99 -vf_coef = 1 -ent_coef = 0.01 -adam_beta1 = 0.9 -adam_beta2 = 0.999 -adam_eps = 1e-12 -max_grad_norm = 0.5 -vf_clip_coef = 0.1 -update_epochs = 1 - -[sweep.env.reward_food] -distribution = log_normal -min = 0.0001 -max = 0.01 -mean = 0.001 -scale = auto - -[sweep.env.interactive_food_reward] -distribution = log_normal -min = 0.0001 -max = 0.02 -mean = 0.002 -scale = auto - -[sweep.train.total_timesteps] -distribution = log_normal -min = 50e6 -max = 75e6 -mean = 60e6 -scale = time diff --git a/pufferlib/config/ocean/drive.ini b/pufferlib/config/ocean/drive.ini index 6e86dbd20..81567f730 100644 --- a/pufferlib/config/ocean/drive.ini +++ b/pufferlib/config/ocean/drive.ini @@ -5,10 +5,10 @@ policy_name = Drive rnn_name = Recurrent [vec] -num_workers = 8 -num_envs = 8 +num_workers = 16 +num_envs = 16 batch_size = 2 -#backend = Serial +; backend = Serial [policy] input_size = 64 @@ -20,19 +20,41 @@ hidden_size = 256 [env] num_agents = 256 +; Options: discrete, continuous +action_type = discrete +; Options: classic, jerk +dynamics_model = classic reward_vehicle_collision = -0.5 reward_offroad_collision = -0.2 -spawn_immunity_timer = 50 +reward_ade = 0.0 +dt = 0.1 +reward_goal = 1.0 reward_goal_post_respawn = 0.25 -reward_vehicle_collision_post_respawn = -0.5 +; Meters around goal to be considered "reached" +goal_radius = 2.0 +; What to do when the goal is reached. Options: 0:"respawn", 1:"generate_new_goals", 2:"stop" +goal_behavior = 0 +; Options: 0 - Ignore, 1 - Stop, 2 - Remove +collision_behavior = 0 +; Options: 0 - Ignore, 1 - Stop, 2 - Remove +offroad_behavior = 0 +; Number of steps before reset +scenario_length = 91 resample_frequency = 910 num_maps = 8 +; Determines which step of the trajectory to initialize the agents at upon reset +init_steps = 0 +; Options: "control_vehicles", "control_agents", "control_tracks_to_predict", "control_sdc_only" +control_mode = "control_vehicles" +; Options: "created_all_valid", "create_only_controlled" +init_mode = "create_all_valid" [train] total_timesteps = 2_000_000_000 -#learning_rate = 0.02 -#gamma = 0.985 +; learning_rate = 0.02 +; gamma = 0.985 anneal_lr = True +; Needs to be: num_agents * num_workers * BPTT horizon batch_size = auto minibatch_size = 11648 max_minibatch_size = 11648 @@ -41,10 +63,10 @@ adam_beta1 = 0.9 adam_beta2 = 0.999 adam_eps = 1e-8 clip_coef = 0.2 -ent_coef = 0.001 +ent_coef = 0.005 gae_lambda = 0.95 gamma = 0.98 -learning_rate = 0.001 +learning_rate = 0.003 max_grad_norm = 1 prio_alpha = 0.8499999999999999 prio_beta0 = 0.8499999999999999 @@ -54,35 +76,67 @@ vf_coef = 2 vtrace_c_clip = 1 vtrace_rho_clip = 1 checkpoint_interval = 1000 +; Rendering options +render = True +render_interval = 1000 +; If True, show exactly what the agent sees in agent observation +obs_only = True +; Show grid lines +show_grid = False +; Draws lines from ego agent observed ORUs and road elements to show detection range +show_lasers = False +; Display human xy logs in the background +show_human_logs = True +; Options: str to path (e.g., "resources/drive/binaries/map_001.bin"), None +render_map = none +[eval] +eval_interval = 1000 +backend = PufferEnv +; WOSAC (Waymo Open Sim Agents Challenge) evaluation settings +; If True, enables evaluation on realism metrics each time we save a checkpoint +wosac_realism_eval = False +wosac_num_rollouts = 32 # Number of policy rollouts per scene +wosac_init_steps = 10 # When to start the simulation +wosac_num_agents = 256 # Total number of WOSAC agents to evaluate +wosac_control_mode = "control_tracks_to_predict" # Control the tracks to predict +wosac_init_mode = "create_all_valid" # Initialize from the tracks to predict +wosac_goal_behavior = 2 # Stop when reaching the goal +wosac_goal_radius = 2.0 # Can shrink goal radius for WOSAC evaluation +wosac_sanity_check = False +wosac_aggregate_results = True # Only return aggregate results across all scenes +; If True, enable human replay evaluation (pair policy-controlled agent with human replays) +human_replay_eval = False +human_replay_control_mode = "control_sdc_only" # Control only the self-driving car +human_replay_num_agents = 64 # This equals the number of scenarios, since we control one agent in each +[sweep.train.learning_rate] +distribution = log_normal +min = 0.001 +mean = 0.003 +max = 0.005 +scale = auto -[sweep.train.total_timesteps] +[sweep.train.ent_coef] distribution = log_normal -min = 1e8 -max = 4e8 -mean = 2e8 -scale = time - -[sweep.env.reward_vehicle_collision] -distribution = uniform -min = -1.0 -max = 0.0 -mean = -0.2 -scale = auto - -[sweep.env.reward_offroad_collision] +min = 0.001 +mean = 0.005 +max = 0.01 +scale = auto + + +[sweep.env.goal_radius] distribution = uniform -min = -1.0 -max = 0.0 -mean = -0.2 +min = 2.0 +max = 20.0 +mean = 10.0 scale = auto -[sweep.env.spawn_immunity_timer] +[sweep.env.reward_ade] distribution = uniform -min = 1 -max = 91 -mean = 30 +min = -0.1 +max = 0.0 +mean = -0.02 scale = auto [sweep.env.reward_goal_post_respawn] @@ -91,10 +145,3 @@ min = 0.0 max = 1.0 mean = 0.5 scale = auto - -[sweep.env.reward_vehicle_collision_post_respawn] -distribution = uniform -min = -1.0 -max = 0.0 -mean = -0.2 -scale = auto diff --git a/pufferlib/config/ocean/drone_race.ini b/pufferlib/config/ocean/drone_race.ini deleted file mode 100644 index cfb9d9772..000000000 --- a/pufferlib/config/ocean/drone_race.ini +++ /dev/null @@ -1,37 +0,0 @@ -[base] -package = ocean -env_name = puffer_drone_race -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 - -[train] -adam_beta1 = 0.9610890980775877 -adam_beta2 = 0.9999260775286266 -adam_eps = 7.782906079040132e-10 -anneal_lr = true -batch_size = auto -bptt_horizon = 64 -checkpoint_interval = 200 -clip_coef = 0.05982655642208556 -ent_coef = 0.002465076521024325 -gae_lambda = 0.9641173414828333 -gamma = 0.997472126425902 -learning_rate = 0.010933756713881205 -max_grad_norm = 1.6317688647793107 -max_minibatch_size = 32768 -minibatch_size = 32768 -prio_alpha = 0.8968873016577552 -prio_beta0 = 0.8672928227817938 -total_timesteps = 100_000_000 -update_epochs = 1 -#use_rnn = false -vf_clip_coef = 0.5869845581530236 -vf_coef = 2.1319065538539963 -vtrace_c_clip = 2.714930379733876 -vtrace_rho_clip = 3.8183814893708057 diff --git a/pufferlib/config/ocean/drone_swarm.ini b/pufferlib/config/ocean/drone_swarm.ini deleted file mode 100644 index 59915325a..000000000 --- a/pufferlib/config/ocean/drone_swarm.ini +++ /dev/null @@ -1,57 +0,0 @@ -[base] -package = ocean -env_name = puffer_drone_swarm -policy_name = Policy -rnn_name = Recurrent - -[policy] -hidden_size = 128 - -[rnn] -input_size = 128 -hidden_size = 128 - -[vec] -num_envs = 8 - -[env] -num_envs = 16 -num_drones = 64 -max_rings = 10 - -[train] -adam_beta1 = 0.9610890980775877 -adam_beta2 = 0.9999260775286266 -adam_eps = 7.782906079040132e-10 -anneal_lr = true -batch_size = auto -bptt_horizon = 64 -checkpoint_interval = 200 -clip_coef = 0.05982655642208556 -ent_coef = 0.002465076521024325 -gae_lambda = 0.9641173414828333 -gamma = 0.997472126425902 -learning_rate = 0.010933756713881205 -#learning_rate = 0.005 -max_grad_norm = 1.6317688647793107 -max_minibatch_size = 32768 -minibatch_size = 32768 -prio_alpha = 0.8968873016577552 -prio_beta0 = 0.8672928227817938 -total_timesteps = 500_000_000 -update_epochs = 1 -#use_rnn = false -vf_clip_coef = 0.5869845581530236 -vf_coef = 2.1319065538539963 -vtrace_c_clip = 2.714930379733876 -vtrace_rho_clip = 3.8183814893708057 - -[sweep] -downsample = 0 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 2e8 -max = 4e8 -mean = 2e8 -scale = time diff --git a/pufferlib/config/ocean/enduro.ini b/pufferlib/config/ocean/enduro.ini deleted file mode 100644 index 6586183fb..000000000 --- a/pufferlib/config/ocean/enduro.ini +++ /dev/null @@ -1,42 +0,0 @@ -[base] -package = ocean -env_name = puffer_enduro -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 1024 - -[vec] -num_envs = 1 - -[train] -total_timesteps = 400_000_000 -adam_beta1 = 0.9602226117399812 -adam_beta2 = 0.999983918771099 -adam_eps = 2.109767652202695e-9 -bptt_horizon = 64 -clip_coef = 0.5716251062832933 -ent_coef = 0.009778379693175061 -gae_lambda = 0.9924829173144767 -gamma = 0.9433427558493771 -learning_rate = 0.014263349414255656 -max_grad_norm = 0.42249653686869115 -max_minibatch_size = 32768 -minibatch_size = 65536 -prio_alpha = 0.22253503344197678 -prio_beta0 = 0.7866639848626998 -vf_clip_coef = 0.01 -vf_coef = 3.2952964839081016 -vtrace_c_clip = 3.060525785199293 -vtrace_rho_clip = 5 - -[sweep] -metric = days_completed - -[sweep.train.total_timesteps] -distribution = log_normal -min = 5e7 -max = 4e8 -mean = 2e8 -scale = auto diff --git a/pufferlib/config/ocean/freeway.ini b/pufferlib/config/ocean/freeway.ini deleted file mode 100644 index 2acceb7a0..000000000 --- a/pufferlib/config/ocean/freeway.ini +++ /dev/null @@ -1,27 +0,0 @@ -[base] -package = ocean -env_name = puffer_freeway -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 -frameskip = 4 -use_dense_rewards = True -env_randomization = True -difficulty = 0 -level = -1 - -[train] -total_timesteps = 500_000_000 -minibatch_size = 32768 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 3e8 -max = 4e8 -mean = 3e8 -scale = auto diff --git a/pufferlib/config/ocean/g2048.ini b/pufferlib/config/ocean/g2048.ini deleted file mode 100644 index 9cbfb1f62..000000000 --- a/pufferlib/config/ocean/g2048.ini +++ /dev/null @@ -1,40 +0,0 @@ -[base] -package = ocean -env_name = puffer_g2048 -policy_name = Policy -rnn_name = Recurrent - -[policy] -hidden_size = 128 - -[rnn] -input_size = 128 -hidden_size = 128 - -[vec] -num_envs = 4 - -[env] -num_envs = 4096 - -[train] -total_timesteps = 5_000_000_000 -adam_beta1 = 0.9529488439604378 -adam_beta2 = 0.9993901829477296 -adam_eps = 2.745365927413118e-7 -bptt_horizon = 64 -clip_coef = 0.596573170393339 -ent_coef = 0.02107417730003862 -gae_lambda = 0.9940613415815854 -gamma = 0.9889857974154952 -#learning_rate = 0.0032402460796988127 -learning_rate = 0.001 -max_grad_norm = 1.0752406726589745 -minibatch_size = 16384 -prio_alpha = 0.25297099593586336 -prio_beta0 = 0.940606268942572 -vf_clip_coef = 0.1 -vf_coef = 1.6362878279900643 -vtrace_c_clip = 0 -vtrace_rho_clip = 1.2917509971869054 -anneal_lr = False diff --git a/pufferlib/config/ocean/go.ini b/pufferlib/config/ocean/go.ini deleted file mode 100644 index fdf6867d4..000000000 --- a/pufferlib/config/ocean/go.ini +++ /dev/null @@ -1,72 +0,0 @@ -[base] -package = ocean -env_name = puffer_go -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 1024 -reward_move_pass = -0.6026362603175613 -reward_move_valid = 0 -reward_move_invalid = -0.5393516480382454 -reward_opponent_capture = -0.3152783593705354 -reward_player_capture = 0.42122681325442923 -grid_size = 7 - -[vec] -num_envs = 8 - -[train] -total_timesteps = 100_000_000 -adam_beta1 = 0.5686370767889766 -adam_beta2 = 0.9999454817221638 -adam_eps = 2.007252656207671e-12 -bptt_horizon = 64 -clip_coef = 0.17930104885238807 -ent_coef = 0.0018946598458748304 -gae_lambda = 0.9831319174802507 -gamma = 0.9480351741863737 -learning_rate = 0.031603809039284864 -max_grad_norm = 1.320177349287771 -minibatch_size = 8192 -prio_alpha = 0.6979639079178326 -prio_beta0 = 0.5614257332458639 -vf_clip_coef = 1.1755607092687304 -vf_coef = 1.6195967557187005 -vtrace_c_clip = 0 -vtrace_rho_clip = 4.060318960532289 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 1e8 -max = 5e8 -mean = 2e8 -scale = 0.25 - -[sweep.env.reward_move_invalid] -distribution = uniform -min = -1.0 -max = 0.0 -mean = -0.5 -scale = 0.5 - -[sweep.env.reward_move_pass] -distribution = uniform -min = -1.0 -max = 0.0 -mean = -0.5 -scale = 0.5 - -[sweep.env.reward_player_capture] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 0.5 -scale = 0.5 - -[sweep.env.reward_opponent_capture] -distribution = uniform -min = -1.0 -max = 0.0 -mean = -0.5 -scale = 0.5 diff --git a/pufferlib/config/ocean/grid.ini b/pufferlib/config/ocean/grid.ini deleted file mode 100644 index 65bd540b6..000000000 --- a/pufferlib/config/ocean/grid.ini +++ /dev/null @@ -1,72 +0,0 @@ -[base] -package = ocean -env_name = puffer_grid -policy_name = Policy -rnn_name = Recurrent - -[policy] -hidden_size = 512 - -[rnn] -input_size = 512 -hidden_size = 512 - -[vec] -#num_envs = 8 -num_envs = 1 - -[env] -max_size = 47 -num_envs = 1024 -#num_envs = 4096 -num_maps = 8192 - -[train] -# Best params -#total_timesteps = 435_000_000 -#adam_beta1 = 0.9801350114303844 -#adam_beta2 = 0.9931056135397744 -#adam_eps = 6.024885743259763e-8 -#clip_coef = 0.283658795325587 -#ent_coef = 0.007885530106105381 -#gae_lambda = 0.9574676436577135 -#gamma = 0.9961782334639131 -#learning_rate = 0.0007890771333884192 -#max_grad_norm = 2.5271346931510053 -#minibatch_size = 8192 -#prio_alpha = 0.8735470630752789 -#prio_beta0 = 0.6533958384978629 -#vf_clip_coef = 1.9338563232919095 -#vf_coef = 3.915248046963283 -#vtrace_c_clip = 1.018588814067991 -#vtrace_rho_clip = 2.4215244529216466 - -# New sweep best params -total_timesteps = 435_000_000 -adam_beta1 = 0.9493079570168755 -adam_beta2 = 0.9998213228757207 -adam_eps = 2.16720639574209e-8 -bptt_horizon = 64 -clip_coef = 0.399530686596841 -ent_coef = 0.0017271288609381147 -gae_lambda = 0.9491722822649111 -gamma = 0.9877360824574745 -learning_rate = 0.0012892859713461897 -max_grad_norm = 3.016348031602564 -minibatch_size = 8192 -prio_alpha = 0.8219794821639037 -prio_beta0 = 0.9447478232810274 -vf_clip_coef = 0.6051579400844748 -vf_coef = 2.323141961227481 -vtrace_c_clip = 1.2499497264614237 -vtrace_rho_clip = 4.7398234531013985 - -[sweep] -downsample = 0 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 3e8 -max = 6e8 -mean = 3e8 -scale = time diff --git a/pufferlib/config/ocean/impulse_wars.ini b/pufferlib/config/ocean/impulse_wars.ini deleted file mode 100644 index d4d1ad7b5..000000000 --- a/pufferlib/config/ocean/impulse_wars.ini +++ /dev/null @@ -1,118 +0,0 @@ -[base] -package = ocean -env_name = puffer_impulse_wars -policy_name = ImpulseWarsPolicy -rnn_name = ImpulseWarsLSTM -max_suggestion_cost = 10_800 - -[policy] -cnn_channels = 64 -input_size = 512 -hidden_size = 512 - -# These must match what's set in env below -continuous = False -num_drones = 2 -is_training = True - -[vec] -num_envs = 16 -num_workers = 16 -batch_size = 4 - -[env] -num_envs = 256 -num_drones = 2 -num_agents = 1 -enable_teams = False -sitting_duck = False -continuous = False -is_training = True - -[train] -total_timesteps = 100_000_000 -checkpoint_interval = 250 - -learning_rate = 0.005 - -compile = False -compile_mode = reduce-overhead -compile_fullgraph = False -device = cuda - -[sweep.env.num_envs] -distribution = uniform_pow2 -min = 16 -max = 512 -mean = 128 -scale = auto - -[sweep.train.total_timesteps] -distribution = log_normal -min = 250_000_000 -max = 1_500_000_000 -mean = 500_000_000 -scale = time - -[sweep.train.batch_size] -distribution = uniform_pow2 -min = 65_536 -max = 1_048_576 -mean = 262_144 -scale = auto - -[sweep.train.bptt_horizon] -distribution = uniform_pow2 -min = 64 -max = 256 -mean = 128 -scale = auto - -[sweep.train.minibatch_size] -distribution = uniform_pow2 -min = 1024 -max = 262_144 -mean = 16_384 -scale = auto - -[sweep.train.learning_rate] -distribution = log_normal -min = 0.00001 -mean = 0.001 -max = 0.1 -scale = 0.5 - -[sweep.train.ent_coef] -distribution = log_normal -min = 0.000001 -mean = 0.001 -max = 0.2 -scale = auto - -[sweep.train.gamma] -distribution = logit_normal -min = 0.8 -mean = 0.98 -max = 0.99999 -scale = auto - -[sweep.train.gae_lambda] -distribution = logit_normal -min = 0.6 -mean = 0.93 -max = 0.995 -scale = auto - -[sweep.train.vf_coef] -distribution = uniform -min = 0.0 -max = 5.0 -mean = 1.0 -scale = auto - -[sweep.train.max_grad_norm] -distribution = uniform -min = 0.0 -mean = 1.0 -max = 5.0 -scale = auto diff --git a/pufferlib/config/ocean/matsci.ini b/pufferlib/config/ocean/matsci.ini deleted file mode 100644 index 9183c27fa..000000000 --- a/pufferlib/config/ocean/matsci.ini +++ /dev/null @@ -1,17 +0,0 @@ -[base] -package = ocean -env_name = puffer_matsci -policy_name = Policy - -[vec] -num_envs = 8 - -[env] -num_envs = 8 -num_atoms = 128 - -[train] -total_timesteps = 50_000_000 -minibatch_size = 32768 - - diff --git a/pufferlib/config/ocean/memory.ini b/pufferlib/config/ocean/memory.ini deleted file mode 100644 index 2bb5abeb8..000000000 --- a/pufferlib/config/ocean/memory.ini +++ /dev/null @@ -1,15 +0,0 @@ -[base] -package = ocean -env_name = puffer_memory -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 1024 - -[vec] -num_envs = 8 - -[train] -total_timesteps = 50_000_000 -minibatch_size = 32768 diff --git a/pufferlib/config/ocean/moba.ini b/pufferlib/config/ocean/moba.ini deleted file mode 100644 index 2e0e8cea3..000000000 --- a/pufferlib/config/ocean/moba.ini +++ /dev/null @@ -1,46 +0,0 @@ -[base] -package = ocean -env_name = puffer_moba -policy_name = MOBA -rnn_name = Recurrent - -[env] -reward_death = 0.0 -reward_xp = 0.0016926873475313188 -reward_distance = 0.0 -reward_tower = 4.525112152099609 -num_envs = 128 - -[vec] -num_envs = 8 - -[train] -total_timesteps = 150_000_000 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 2e7 -max = 2e8 -mean = 1e8 -scale = auto - -[sweep.env.reward_death] -distribution = uniform -min = -1.0 -max = 0 -mean = 0 -scale = auto - -[sweep.env.reward_xp] -distribution = uniform -min = 0.0 -max = 0.05 -mean = 0.0015 -scale = auto - -[sweep.env.reward_tower] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 1.0 -scale = auto diff --git a/pufferlib/config/ocean/nmmo3.ini b/pufferlib/config/ocean/nmmo3.ini deleted file mode 100644 index c04c77dc3..000000000 --- a/pufferlib/config/ocean/nmmo3.ini +++ /dev/null @@ -1,75 +0,0 @@ -[base] -package = ocean -env_name = puffer_nmmo3 -policy_name = NMMO3 -rnn_name = NMMO3LSTM - -[vec] -num_envs = 8 - -[env] -reward_combat_level = 1.0 -reward_prof_level = 1.0 -reward_item_level = 1.0 -reward_market = 0.0 -reward_death = -1.0 -num_envs = 1 - -[train] -total_timesteps = 107000000000 -checkpoint_interval = 1000 -learning_rate = 0.0004573146765703167 -gamma = 0.7647543366891623 -gae_lambda = 0.996005622445478 -ent_coef = 0.01210084358004069 -max_grad_norm = 0.6075578331947327 -vf_coef = 0.3979089612467003 -bptt_horizon = 64 -batch_size = 524288 -minibatch_size = 32768 -max_minibatch_size = 32768 - -[sweep] -metric = min_comb_prof - -[sweep.env.num_envs] -distribution = uniform_pow2 -min = 1 -max = 8 -mean = 4 -scale = 0.5 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 2e8 -max = 1e9 -mean = 5e8 -scale = 0.5 - -[sweep.env.reward_combat_level] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 0.5 -scale = auto - -[sweep.env.reward_prof_level] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 0.5 -scale = auto - -[sweep.env.reward_item_level] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 1.0 -scale = auto - -[sweep.env.reward_death] -distribution = uniform -min = -1.0 -max = 0.0 -mean = -1.0 -scale = auto diff --git a/pufferlib/config/ocean/oldgrid.ini b/pufferlib/config/ocean/oldgrid.ini deleted file mode 100644 index 3cae63aa9..000000000 --- a/pufferlib/config/ocean/oldgrid.ini +++ /dev/null @@ -1,79 +0,0 @@ -[base] -package = ocean -env_name = puffer_oldgrid -vec = multiprocessing -policy_name = Policy -rnn_name = Recurrent - -#[policy] -#hidden_size = 512 - -#[rnn] -#input_size = 512 -#hidden_size = 512 - -[env] -#map_size = 31 -max_map_size = 31 -num_envs = 512 -num_maps = 8192 -#num_maps = 1 - -[train] -total_timesteps = 180_000_000 -checkpoint_interval = 1000 -learning_rate = 0.0005978750098629419 -gamma = 0.9944336976183826 -gae_lambda = 0.9474288929489364 -ent_coef = 0.00001 -use_e3b = True - -num_envs = 1 -num_workers = 1 -env_batch_size = 1 -update_epochs = 4 -bptt_horizon = 16 -batch_size = 131072 -minibatch_size = 16384 -compile = False -device = cuda -e3b_coef = 0.01 - -[sweep] -method = protein -name = sweep - -[sweep.metric] -goal = maximize -name = score -min = 0 -max = 1 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 5e7 -max = 2e8 -mean = 1e8 -scale = auto - -[sweep.train.e3b_coef] -distribution = logit_normal -min = 0.0001 -max = 0.99 -mean = 0.001 -scale = auto - -[sweep.train.e3b_lambda] -distribution = log_normal -min = 0.01 -max = 10.0 -mean = 0.1 -scale = auto - -[sweep.train.e3b_norm] -distribution = log_normal -min = 0.0001 -max = 0.1 -mean = 0.001 -scale = auto - diff --git a/pufferlib/config/ocean/pacman.ini b/pufferlib/config/ocean/pacman.ini deleted file mode 100644 index 45055e79b..000000000 --- a/pufferlib/config/ocean/pacman.ini +++ /dev/null @@ -1,33 +0,0 @@ -[base] -package = ocean -env_name = puffer_pacman -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 -randomize_starting_position = 1 - -[train] -total_timesteps = 110_000_000 -adam_beta1 = 0.9038605017693528 -adam_beta2 = 0.9974184818428597 -adam_eps = 0.000023302187415940045 -bptt_horizon = 64 -clip_coef = 0.08819998793159559 -ent_coef = 0.003877776836171818 -gae_lambda = 0.9548561279014964 -gamma = 0.9808956918725869 -learning_rate = 0.07834293253084383 -max_grad_norm = 1.4336515067572169 -minibatch_size = 32768 -prio_alpha = 0.912262602688309 -prio_beta0 = 0.8868847849454541 -vf_clip_coef = 0.2143010707893266 -vf_coef = 0.31518694995467555 -vtrace_c_clip = 0.30575543665366217 -vtrace_rho_clip = 1.5301756939690652 - diff --git a/pufferlib/config/ocean/pong.ini b/pufferlib/config/ocean/pong.ini deleted file mode 100644 index a0bf24d93..000000000 --- a/pufferlib/config/ocean/pong.ini +++ /dev/null @@ -1,46 +0,0 @@ -[base] -package = ocean -env_name = puffer_pong -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 4 - -[env] -num_envs = 1024 -frameskip = 8 - -[train] -total_timesteps = 12_000_000 -adam_beta1 = 0.9766295300012044 -adam_beta2 = 0.9998113167362397 -adam_eps = 6.301709731262074e-9 -bptt_horizon = 64 -clip_coef = 0.22131450913204256 -ent_coef = 0.0020310049268479863 -gae_lambda = 0.8854219852971792 -gamma = 0.9608378504980243 -learning_rate = 0.07109386062895108 -max_grad_norm = 1.7820203601055993 -minibatch_size = 32768 -prio_alpha = 0.09999999999999998 -prio_beta0 = 0.7475661360032159 -vf_clip_coef = 2.7025841941932303 -vf_coef = 1.9960893747329385 -vtrace_c_clip = 1.0873122745787867 -vtrace_rho_clip = 2.784150207139061 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 1e7 -max = 2e8 -mean = 8e7 -scale = auto - -[sweep.env.frameskip] -distribution = int_uniform -min = 1 -max = 8 -mean = 4 -scale = 2.0 diff --git a/pufferlib/config/ocean/pysquared.ini b/pufferlib/config/ocean/pysquared.ini deleted file mode 100644 index 7ff0653bc..000000000 --- a/pufferlib/config/ocean/pysquared.ini +++ /dev/null @@ -1,16 +0,0 @@ -[base] -package = ocean -env_name = puffer_pysquared -policy_name = Policy -rnn_name = Recurrent - -# Set up to match our C version defaults for speed comparison -[vec] -num_envs = 8192 -num_workers = 2 - -[train] -total_timesteps = 20_000_000 -gamma = 0.95 -learning_rate = 0.05 -minibatch_size = 32768 diff --git a/pufferlib/config/ocean/rware.ini b/pufferlib/config/ocean/rware.ini deleted file mode 100644 index 705e0af3e..000000000 --- a/pufferlib/config/ocean/rware.ini +++ /dev/null @@ -1,26 +0,0 @@ -[base] -package = ocean -env_name = puffer_rware -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 128 -map_choice = 2 -num_agents = 8 -num_requested_shelves = 8 - -[train] -total_timesteps = 100_000_000 -learning_rate = 0.05 -minibatch_size = 32768 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 3e7 -max = 3e8 -mean = 1e8 -scale = 0.25 diff --git a/pufferlib/config/ocean/sanity.ini b/pufferlib/config/ocean/sanity.ini deleted file mode 100644 index 7d7abc40b..000000000 --- a/pufferlib/config/ocean/sanity.ini +++ /dev/null @@ -1,30 +0,0 @@ -[base] -package = ocean -env_name = puffer_bandit puffer_memory puffer_multiagent puffer_password puffer_spaces puffer_stochastic -policy_name = Policy -rnn_name = Recurrent - -[train] -total_timesteps = 50_000 -learning_rate = 0.017 -num_envs = 8 -num_workers = 2 -env_batch_size = 8 -batch_size = 1024 -minibatch_size = 128 -bptt_horizon = 4 -device = cpu - -[sweep.train.batch_size] -distribution = uniform -min = 512 -max = 2048 -mean = 1024 -scale = 0.5 - -[sweep.train.minibatch_size] -distribution = uniform -min = 64 -max = 512 -mean = 128 -scale = 0.5 diff --git a/pufferlib/config/ocean/snake.ini b/pufferlib/config/ocean/snake.ini deleted file mode 100644 index 3827b0252..000000000 --- a/pufferlib/config/ocean/snake.ini +++ /dev/null @@ -1,62 +0,0 @@ -[base] -package = ocean -env_name = puffer_snake -policy_name = Snake -#policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 4 -width = 640 -height = 360 -num_snakes = 256 -num_food = 4096 -vision = 5 -leave_corpse_on_death = True -reward_food = 0.1 -reward_corpse = 0.1 -reward_death = -1.0 - -[vec] -num_envs = 16 - -[train] -total_timesteps = 500_000_000 -adam_beta1 = 0.6762060389098516 -adam_beta2 = 0.9 -adam_eps = 0.000002764249390410885 -bptt_horizon = 64 -clip_coef = 0.7379459916127813 -ent_coef = 0.010507292602201058 -gae_lambda = 0.6006253996849398 -gamma = 0.9997067226101388 -learning_rate = 0.016779905178021273 -max_grad_norm = 0.6504710763256233 -minibatch_size = 32768 -prio_alpha = 0.6082618023318664 -prio_beta0 = 0.447524297405661 -vf_clip_coef = 2.830994746057568 -vf_coef = 3.9655925817980053 -vtrace_c_clip = 0 -vtrace_rho_clip = 0.9285200248552337 - -[sweep.env.reward_food] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 0.0 -scale = auto - -[sweep.env.reward_death] -distribution = uniform -min = -1.0 -max = 0.0 -mean = 0.0 -scale = auto - -[sweep.train.total_timesteps] -distribution = log_normal -min = 5e7 -max = 2e8 -mean = 1e8 -scale = auto diff --git a/pufferlib/config/ocean/squared.ini b/pufferlib/config/ocean/squared.ini deleted file mode 100644 index ac9f69d0f..000000000 --- a/pufferlib/config/ocean/squared.ini +++ /dev/null @@ -1,14 +0,0 @@ -[base] -package = ocean -env_name = puffer_squared -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 4096 - -[train] -total_timesteps = 20_000_000 -gamma = 0.95 -learning_rate = 0.05 -minibatch_size = 32768 diff --git a/pufferlib/config/ocean/tactical.ini b/pufferlib/config/ocean/tactical.ini deleted file mode 100644 index 762cd1005..000000000 --- a/pufferlib/config/ocean/tactical.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = ocean -env_name = puffer_tactical diff --git a/pufferlib/config/ocean/target.ini b/pufferlib/config/ocean/target.ini deleted file mode 100644 index a65bdad32..000000000 --- a/pufferlib/config/ocean/target.ini +++ /dev/null @@ -1,18 +0,0 @@ -[base] -package = ocean -env_name = puffer_target -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 512 -num_agents = 8 -num_goals = 4 - -[train] -total_timesteps = 100_000_000 -gamma = 0.99 -learning_rate = 0.015 -minibatch_size = 32768 -ent_coef = 0.02 - diff --git a/pufferlib/config/ocean/template.ini b/pufferlib/config/ocean/template.ini deleted file mode 100644 index 4784c83e7..000000000 --- a/pufferlib/config/ocean/template.ini +++ /dev/null @@ -1,10 +0,0 @@ -[base] -package = ocean -env_name = puffer_template -policy_name = Policy - -[env] -num_envs = 4096 - -[train] -total_timesteps = 10_000_000 diff --git a/pufferlib/config/ocean/terraform.ini b/pufferlib/config/ocean/terraform.ini deleted file mode 100644 index b64d19dc0..000000000 --- a/pufferlib/config/ocean/terraform.ini +++ /dev/null @@ -1,61 +0,0 @@ -[base] -package = ocean -env_name = puffer_terraform -policy_name = Terraform -rnn_name = Recurrent - -[vec] -num_envs = 8 -#backend = Serial -[env] -num_envs = 1024 -num_agents = 1 -reset_frequency = 1024 -reward_scale = 0.11 - -[policy] -hidden_size = 256 - -[rnn] -input_size = 256 -hidden_size = 256 - -[train] -total_timesteps = 1_000_000_000 -adam_beta1 = 0.8792313963264954 -adam_beta2 = 0.9980457691558037 -adam_eps = 0.0000060001757672174796 -bptt_horizon = 64 -ent_coef = 0.007047731279570716 -gae_lambda = 0.95 -gamma = 0.98 -learning_rate = 0.005 -max_grad_norm = 1.1870216773228415 -minibatch_size = 32768 -prio_alpha = 0.498348178927537 -prio_beta0 = 0.7687009564385903 -vf_clip_coef = 1.4509861770544443 -vf_coef = 3.175722544969796 -vtrace_c_clip = 0.937506506536413 -vtrace_rho_clip = 1.208308436542831 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 2e8 -max = 6e8 -mean = 4e8 -scale = time - -#[sweep.env.reset_frequency] -#distribution = int_uniform -#min = 1024 -#max = 16384 -#mean = 8192 -#scale = auto - -[sweep.env.reward_scale] -distribution = log_normal -min = 0.01 -max = 1 -mean = 0.5 -scale = auto diff --git a/pufferlib/config/ocean/tetris.ini b/pufferlib/config/ocean/tetris.ini deleted file mode 100644 index 0391ca317..000000000 --- a/pufferlib/config/ocean/tetris.ini +++ /dev/null @@ -1,29 +0,0 @@ -[base] -package = ocean -env_name = puffer_tetris -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 -deck_size = 3 - -[train] -total_timesteps = 2_000_000_000 -batch_size = auto -bptt_horizon = 64 -minibatch_size = 32768 - -[sweep] -metric = score -goal = maximize - -[sweep.train.total_timesteps] -distribution = log_normal -min = 2e8 -max = 4e8 -mean = 3e8 -scale = auto diff --git a/pufferlib/config/ocean/tower_climb.ini b/pufferlib/config/ocean/tower_climb.ini deleted file mode 100644 index ce6f75d59..000000000 --- a/pufferlib/config/ocean/tower_climb.ini +++ /dev/null @@ -1,57 +0,0 @@ -[base] -package = ocean -env_name = puffer_tower_climb -policy_name = TowerClimb -rnn_name = TowerClimbLSTM - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 -num_maps = 50 -reward_climb_row = 0.636873185634613 -reward_fall_row = -0.15898257493972778 -reward_illegal_move = -0.003928301855921745 -reward_move_block = 0.235064297914505 - -[train] -total_timesteps = 150_000_000 -#gamma = 0.98 -#learning_rate = 0.05 -minibatch_size = 32768 - -[sweep.train.total_timesteps] -distribution = uniform -min = 50_000_000 -max = 200_000_000 -mean = 100_000_000 -scale = 0.5 - -[sweep.env.reward_climb_row] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 0.5 -scale = auto - -[sweep.env.reward_fall_row] -distribution = uniform -min = -1.0 -max = 0.0 -mean = -0.5 -scale = auto - -[sweep.env.reward_illegal_move] -distribution = uniform -min = -1e-2 -max = -1e-4 -mean = -1e-3 -scale = auto - -[sweep.env.reward_move_block] -distribution = uniform -min = 0.0 -max = 1.0 -mean = 0.5 -scale = auto diff --git a/pufferlib/config/ocean/trash_pickup.ini b/pufferlib/config/ocean/trash_pickup.ini deleted file mode 100644 index bb235abab..000000000 --- a/pufferlib/config/ocean/trash_pickup.ini +++ /dev/null @@ -1,28 +0,0 @@ -[base] -package = ocean -env_name = puffer_trash_pickup -policy_name = TrashPickup -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 128 -grid_size = 20 -num_agents = 8 -num_trash = 40 -num_bins = 2 -max_steps = 500 -report_interval = 32 -agent_sight_range = 5 - -[train] -total_timesteps = 100_000_000 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 3e7 -max = 2e8 -mean = 1e8 -scale = 0.5 diff --git a/pufferlib/config/ocean/tripletriad.ini b/pufferlib/config/ocean/tripletriad.ini deleted file mode 100644 index aae55d096..000000000 --- a/pufferlib/config/ocean/tripletriad.ini +++ /dev/null @@ -1,21 +0,0 @@ -[base] -package = ocean -env_name = puffer_tripletriad -policy_name = Policy -rnn_name = Recurrent - -[env] -num_envs = 1024 - -[vec] -num_envs = 8 - -[train] -total_timesteps = 100_000_000 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 5e7 -max = 2e8 -mean = 1e8 -scale = 0.25 diff --git a/pufferlib/config/ocean/whisker_racer.ini b/pufferlib/config/ocean/whisker_racer.ini deleted file mode 100644 index b7dd87f11..000000000 --- a/pufferlib/config/ocean/whisker_racer.ini +++ /dev/null @@ -1,176 +0,0 @@ -[base] -package = ocean -env_name = puffer_whisker_racer -policy_name = Policy -rnn_name = Recurrent - -[vec] -num_envs = 8 - -[env] -num_envs = 1024 -frameskip = 4 -width = 1080 -height = 720 -track_width = 75 -num_radial_sectors = 180 -num_points = 16 -bezier_resolution = 4 -turn_pi_frac = 40 -w_ang = 0.777 # 0.586 # 0.523 -reward_yellow = 0.2 -reward_green = -0.001 -corner_thresh = 0.5 # dot product for hairpins -ftmp1 = 0.5 #0.9 -ftmp2 = 3.0 #1.05 -ftmp3 = 0.3 # 0.2 -ftmp4 = 0.0 -mode7 = 0 -render_many = 0 -rng = 6 -method = 2 - -[policy] -hidden_size = 128 - -[rnn] -input_size = 128 -hidden_size = 128 - -[train] -adam_beta1 = 0.9446160612709289 -adam_beta2 = 0.9898294105500932 -adam_eps = 3.599894131847621e-14 -batch_size = auto -bptt_horizon = 64 -clip_coef = 0.18182501031893042 -ent_coef = 0.014660408908451323 -gae_lambda = 0.9560790493173461 -gamma = 0.9966518685154753 -learning_rate = 0.009774918469042404 -max_grad_norm = 1.1402446026380597 -#max_minibatch_size = 32768 -#min_minibatch_size = 32768 -minibatch_size = 32768 #16384 32768 -prio_alpha = 0.8186786991771018 -prio_beta0 = 0.49639773186725333 -total_timesteps = 200_000_000 -vf_clip_coef = 1.1492700894337171 -vf_coef = 1.2551354745222134 -vtrace_c_clip = 0 -vtrace_rho_clip = 2.2637237926128577 - -[sweep] -method = Protein -metric = score -goal = maximize -downsample = 10 - -[sweep.train.total_timesteps] -distribution = log_normal -min = 19_000_000 -max = 20_000_000 -mean = 19_500_000 -scale = auto - -[sweep.env.track_width] -distribution = log_normal -min = 30 -max = 75 -mean = 50 -scale = auto - -[sweep.train.learning_rate] -distribution = log_normal -min = 0.03 -mean = 0.08 -max = 0.15 -scale = 0.5 - -[sweep.env.turn_pi_frac] -distribution = uniform -min = 30 -max = 75 -mean = 50 -scale = auto - -[sweep.env.num_radial_sectors] -distribution = uniform -min = 8 -max = 100 -mean = 18 -scale = auto - -[sweep.env.w_ang] -distribution = uniform -min = 0.523 # PI / 6 -max = 0.785 # PI / 4 -mean = 0.654 -scale = auto - -#[sweep.env.frameskip] -#distribution = categorical -#values = 1, 4, 8 - -[sweep.env.reward_yellow] -distribution = uniform -min = 0.20 -max = 0.50 -mean = 0.30 -scale = auto - -[sweep.env.reward_green] -distribution = uniform -min = -0.001 -max = 0.0 -mean = -0.0001 -scale = auto - -#[sweep.train.vtrace_rho_clip] -#distribution = uniform -#min = 1.0 -#max = 5.0 -#mean = 1.5 -#scale = auto - -#[sweep.train.max_grad_norm] -#distribution = uniform -#min = 1.0 -#mean = 1.25 -#max = 5.0 -#scale = auto - -#[sweep.train.vf_coef] -#distribution = uniform -#min = 0.0 -#max = 1.0 -#mean = 1.8 -#scale = auto - -#[sweep.train.gae_lambda] -#distribution = logit_normal -#min = 0.96 -#mean = 0.984 -#max = 0.9995 -#scale = auto - -#[sweep.train.vtrace_c_clip] -#distribution = uniform -#min = 0.0 -#max = 4.0 -#mean = 1.0 -#scale = auto - -[sweep.train.minibatch_size] -distribution = uniform_pow2 -min = 8192 -max = 32768 -mean = 16384 -scale = auto - -#[sweep.train.ent_coef] -#distribution = log_normal -#min = 0.00001 -#mean = 0.005 -#max = 0.05 -#scale = auto diff --git a/pufferlib/config/open_spiel.ini b/pufferlib/config/open_spiel.ini deleted file mode 100644 index a3210cb79..000000000 --- a/pufferlib/config/open_spiel.ini +++ /dev/null @@ -1,7 +0,0 @@ -[base] -package = open_spiel -env_name = connect_four - -[train] -num_envs = 32 -batch_size = 4096 diff --git a/pufferlib/config/pokemon_red.ini b/pufferlib/config/pokemon_red.ini deleted file mode 100644 index a0c2485ce..000000000 --- a/pufferlib/config/pokemon_red.ini +++ /dev/null @@ -1,17 +0,0 @@ -[base] -package = pokemon_red -env_name = pokemon_red - -[train] -total_timesteps = 1_000_000 -num_envs = 96 -num_workers = 24 -env_batch_size = 32 -zero_copy = False -update_epochs = 3 -gamma = 0.998 -batch_size = 65536 -minibatch_size = 2048 -compile = True -learning_rate = 2.0e-4 -anneal_lr = False diff --git a/pufferlib/config/procgen.ini b/pufferlib/config/procgen.ini deleted file mode 100644 index cc7c6cba8..000000000 --- a/pufferlib/config/procgen.ini +++ /dev/null @@ -1,15 +0,0 @@ -[base] -package = procgen -env_name = bigfish bossfight caveflyer chaser climber coinrun dodgeball fruitbot heist jumper leaper maze miner ninja plunder starpilot - -[vec] -num_envs = 128 -num_workers = 16 -batch_size = 64 - -[train] -batch_size = 8192 -minibatch_size = 2048 -update_epochs = 1 -bptt_horizon = 64 -total_timesteps = 25_000_000 diff --git a/pufferlib/config/slimevolley.ini b/pufferlib/config/slimevolley.ini deleted file mode 100644 index 847672f6d..000000000 --- a/pufferlib/config/slimevolley.ini +++ /dev/null @@ -1,12 +0,0 @@ -[base] -package = slimevolley -env_name = slimevolley - -[train] -num_envs=1536 -num_workers=24 -env_batch_size=512 -zero_copy=False -batch_size=65536 -minibatch_size=8192 -update_epochs=1 diff --git a/pufferlib/config/stable_retro.ini b/pufferlib/config/stable_retro.ini deleted file mode 100644 index 8f2239986..000000000 --- a/pufferlib/config/stable_retro.ini +++ /dev/null @@ -1,3 +0,0 @@ -[base] -package = stable_retro -env_name = Airstriker-Genesis diff --git a/pufferlib/config/starcraft.ini b/pufferlib/config/starcraft.ini deleted file mode 100644 index 9af2bfb11..000000000 --- a/pufferlib/config/starcraft.ini +++ /dev/null @@ -1,2 +0,0 @@ -[base] -package = smac diff --git a/pufferlib/config/trade_sim.ini b/pufferlib/config/trade_sim.ini deleted file mode 100644 index 2a7f5b405..000000000 --- a/pufferlib/config/trade_sim.ini +++ /dev/null @@ -1,30 +0,0 @@ -[base] -package = trade_sim -env_name = trade_sim -policy_name = Policy -rnn_name = Recurrent - -[vec] -backend = Multiprocessing -num_envs = 1024 -num_workers = 16 -batch_size = 512 - -#[env] -#num_envs = 128 - -[train] -total_timesteps = 100_000_000 -gamma = 0.95 -learning_rate = 0.05 -minibatch_size = 32768 - -[sweep] -metric = final_capital - -[sweep.train.total_timesteps] -distribution = log_normal -min = 2e7 -max = 1e8 -mean = 5e7 -scale = auto diff --git a/pufferlib/emulation.py b/pufferlib/emulation.py index f576f530f..f50ec03b1 100644 --- a/pufferlib/emulation.py +++ b/pufferlib/emulation.py @@ -1,5 +1,3 @@ -from pdb import set_trace as T - import numpy as np import warnings @@ -10,24 +8,26 @@ import pufferlib.spaces from pufferlib.spaces import Discrete, Tuple, Dict + def emulate(struct, sample): if isinstance(sample, dict): for k, v in sample.items(): emulate(struct[k], v) elif isinstance(sample, tuple): for i, v in enumerate(sample): - emulate(struct[f'f{i}'], v) + emulate(struct[f"f{i}"], v) else: struct[()] = sample + def make_buffer(arr_dtype, struct_dtype, struct, n=None): - '''None instead of 1 makes it work for 1 agent PZ envs''' - ''' + """None instead of 1 makes it work for 1 agent PZ envs""" + """ if n is None: struct = np.zeros(1, dtype=struct_dtype) else: struct = np.zeros(n, dtype=struct_dtype) - ''' + """ arr = struct.view(arr_dtype) @@ -38,29 +38,31 @@ def make_buffer(arr_dtype, struct_dtype, struct, n=None): return arr + def _nativize(struct, space): if isinstance(space, Discrete): return struct.item() elif isinstance(space, Tuple): - return tuple(_nativize(struct[f'f{i}'], elem) - for i, elem in enumerate(space)) + return tuple(_nativize(struct[f"f{i}"], elem) for i, elem in enumerate(space)) elif isinstance(space, Dict): - return {k: _nativize(struct[k], value) - for k, value in space.items()} + return {k: _nativize(struct[k], value) for k, value in space.items()} else: return struct + def nativize(arr, space, struct_dtype): struct = np.asarray(arr).view(struct_dtype)[0] return _nativize(struct, space) + # TODO: Uncomment? -''' +""" try: from pufferlib.extensions import emulate, nativize except ImportError: warnings.warn('PufferLib Cython extensions not installed. Using slow Python versions') -''' +""" + def get_dtype_bounds(dtype): if dtype == bool: @@ -80,7 +82,7 @@ def dtype_from_space(space): if isinstance(space, pufferlib.spaces.Tuple): dtype = [] for i, elem in enumerate(space): - dtype.append((f'f{i}', dtype_from_space(elem))) + dtype.append((f"f{i}", dtype_from_space(elem))) elif isinstance(space, pufferlib.spaces.Dict): dtype = [] for k, value in space.items(): @@ -94,6 +96,7 @@ def dtype_from_space(space): return np.dtype(dtype, align=True) + def flatten_space(space): if isinstance(space, pufferlib.spaces.Tuple): subspaces = [] @@ -108,6 +111,7 @@ def flatten_space(space): else: return [space] + def emulate_observation_space(space): emulated_dtype = dtype_from_space(space) @@ -126,6 +130,7 @@ def emulate_observation_space(space): emulated_space = gymnasium.spaces.Box(low=mmin, high=mmax, shape=(numel,), dtype=dtype) return emulated_space, emulated_dtype + def emulate_action_space(space): if isinstance(space, pufferlib.spaces.Box): return space, space.dtype @@ -148,10 +153,8 @@ def __init__(self, env=None, env_creator=None, env_args=[], env_kwargs={}, buf=N self.is_observation_checked = False self.is_action_checked = False - self.observation_space, self.obs_dtype = emulate_observation_space( - self.env.observation_space) - self.action_space, self.atn_dtype = emulate_action_space( - self.env.action_space) + self.observation_space, self.obs_dtype = emulate_observation_space(self.env.observation_space) + self.action_space, self.atn_dtype = emulate_action_space(self.env.action_space) self.single_observation_space = self.observation_space self.single_action_space = self.action_space self.num_agents = 1 @@ -163,14 +166,14 @@ def __init__(self, env=None, env_creator=None, env_args=[], env_kwargs={}, buf=N emulated_observation_dtype=self.obs_dtype, ) - self.render_modes = 'human rgb_array'.split() + self.render_modes = "human rgb_array".split() pufferlib.set_buffers(self, buf) if isinstance(self.env.observation_space, pufferlib.spaces.Box): self.obs_struct = self.observations else: self.obs_struct = self.observations.view(self.obs_dtype) - + @property def render_mode(self): return self.env.render_mode @@ -184,8 +187,7 @@ def reset(self, seed=None): ob, info = _seed_and_reset(self.env, seed) if not self.is_observation_checked: - self.is_observation_checked = check_space( - ob, self.env.observation_space) + self.is_observation_checked = check_space(ob, self.env.observation_space) if self.is_obs_emulated: emulate(self.obs_struct, ob) @@ -196,15 +198,15 @@ def reset(self, seed=None): self.terminals[0] = False self.truncations[0] = False self.masks[0] = True - + return self.observations, info - + def step(self, action): - '''Execute an action and return (observation, reward, done, info)''' + """Execute an action and return (observation, reward, done, info)""" if not self.initialized: - raise pufferlib.APIUsageError('step() called before reset()') + raise pufferlib.APIUsageError("step() called before reset()") if self.done: - raise pufferlib.APIUsageError('step() called after environment is done') + raise pufferlib.APIUsageError("step() called after environment is done") # Unpack actions from multidiscrete into the original action space if self.is_atn_emulated: @@ -216,12 +218,10 @@ def step(self, action): action = action[0] if not self.is_action_checked: - self.is_action_checked = check_space( - action, self.env.action_space) + self.is_action_checked = check_space(action, self.env.action_space) ob, reward, done, truncated, info = self.env.step(action) - if self.is_obs_emulated: emulate(self.obs_struct, ob) else: @@ -231,7 +231,7 @@ def step(self, action): self.terminals[0] = done self.truncations[0] = truncated self.masks[0] = True - + self.done = done or truncated return self.observations, reward, done, truncated, info @@ -241,6 +241,7 @@ def render(self): def close(self): return self.env.close() + class PettingZooPufferEnv: def __init__(self, env=None, env_creator=None, env_args=[], env_kwargs={}, buf=None, seed=0): self.env = make_object(env, env_creator, env_args, env_kwargs) @@ -254,15 +255,13 @@ def __init__(self, env=None, env_creator=None, env_args=[], env_kwargs={}, buf=N single_agent = self.possible_agents[0] self.env_single_observation_space = self.env.observation_space(single_agent) self.env_single_action_space = self.env.action_space(single_agent) - self.single_observation_space, self.obs_dtype = ( - emulate_observation_space(self.env_single_observation_space)) - self.single_action_space, self.atn_dtype = ( - emulate_action_space(self.env_single_action_space)) + self.single_observation_space, self.obs_dtype = emulate_observation_space(self.env_single_observation_space) + self.single_action_space, self.atn_dtype = emulate_action_space(self.env_single_action_space) self.is_obs_emulated = self.single_observation_space is not self.env_single_observation_space self.is_atn_emulated = self.single_action_space is not self.env_single_action_space self.emulated = dict( - observation_dtype = self.single_observation_space.dtype, - emulated_observation_dtype = self.obs_dtype, + observation_dtype=self.single_observation_space.dtype, + emulated_observation_dtype=self.obs_dtype, ) self.num_agents = len(self.possible_agents) @@ -290,14 +289,14 @@ def done(self): return len(self.agents) == 0 or self.all_done def observation_space(self, agent): - '''Returns the observation space for a single agent''' + """Returns the observation space for a single agent""" if agent not in self.possible_agents: raise pufferlib.InvalidAgentError(agent, self.possible_agents) return self.single_observation_space def action_space(self, agent): - '''Returns the action space for a single agent''' + """Returns the action space for a single agent""" if agent not in self.possible_agents: raise pufferlib.InvalidAgentError(agent, self.possible_agents) @@ -315,8 +314,7 @@ def reset(self, seed=None): if not self.is_observation_checked: for k, ob in obs.items(): - self.is_observation_checked = check_space( - ob, self.env.observation_space(k)) + self.is_observation_checked = check_space(ob, self.env.observation_space(k)) # Call user featurizer and flatten the observations self.observations[:] = 0 @@ -338,16 +336,17 @@ def reset(self, seed=None): return self.dict_obs, info def step(self, actions): - '''Step the environment and return (observations, rewards, dones, infos)''' + """Step the environment and return (observations, rewards, dones, infos)""" if not self.initialized: - raise pufferlib.APIUsageError('step() called before reset()') + raise pufferlib.APIUsageError("step() called before reset()") if self.done: - raise pufferlib.APIUsageError('step() called after environment is done') + raise pufferlib.APIUsageError("step() called after environment is done") if isinstance(actions, np.ndarray): if not self.is_action_checked and len(actions) != self.num_agents: raise pufferlib.APIUsageError( - f'Actions specified as len {len(actions)} but environment has {self.num_agents} agents') + f"Actions specified as len {len(actions)} but environment has {self.num_agents} agents" + ) actions = {agent: actions[i] for i, agent in enumerate(self.possible_agents)} @@ -357,10 +356,7 @@ def step(self, actions): if agent not in self.possible_agents: raise pufferlib.InvalidAgentError(agent, self.possible_agents) - self.is_action_checked = check_space( - next(iter(actions.values())), - self.single_action_space - ) + self.is_action_checked = check_space(next(iter(actions.values())), self.single_action_space) # Unpack actions from multidiscrete into the original action space unpacked_actions = {} @@ -393,7 +389,7 @@ def step(self, actions): self.masks[i] = False continue - ob = obs[agent] + ob = obs[agent] self.mask[agent] = True if self.is_obs_emulated: emulate(self.obs_struct[i], ob) @@ -404,10 +400,12 @@ def step(self, actions): self.terminals[i] = dones[agent] self.truncations[i] = truncateds[agent] self.masks[i] = True - + self.all_done = all(dones.values()) or all(truncateds.values()) rewards = pad_agent_data(rewards, self.possible_agents, 0) - dones = pad_agent_data(dones, self.possible_agents, True) # You changed this from false to match api test... is this correct? + dones = pad_agent_data( + dones, self.possible_agents, True + ) # You changed this from false to match api test... is this correct? truncateds = pad_agent_data(truncateds, self.possible_agents, False) return self.dict_obs, rewards, dones, truncateds, infos @@ -417,23 +415,24 @@ def render(self): def close(self): return self.env.close() + def pad_agent_data(data, agents, pad_value): - return {agent: data[agent] if agent in data else pad_value - for agent in agents} - + return {agent: data[agent] if agent in data else pad_value for agent in agents} + + def make_object(object_instance=None, object_creator=None, creator_args=[], creator_kwargs={}): if (object_instance is None) == (object_creator is None): - raise ValueError('Exactly one of object_instance or object_creator must be provided') + raise ValueError("Exactly one of object_instance or object_creator must be provided") if object_instance is not None: if callable(object_instance) or inspect.isclass(object_instance): - raise TypeError('object_instance must be an instance, not a function or class') + raise TypeError("object_instance must be an instance, not a function or class") return object_instance if object_creator is not None: if not callable(object_creator): - raise TypeError('object_creator must be a callable') - + raise TypeError("object_creator must be a callable") + if creator_args is None: creator_args = [] @@ -442,19 +441,19 @@ def make_object(object_instance=None, object_creator=None, creator_args=[], crea return object_creator(*creator_args, **creator_kwargs) + def check_space(data, space): try: contains = space.contains(data) except: - raise pufferlib.APIUsageError( - f'Error checking space {space} with sample :\n{data}') + raise pufferlib.APIUsageError(f"Error checking space {space} with sample :\n{data}") if not contains: - raise pufferlib.APIUsageError( - f'Data:\n{data}\n not in space:\n{space}') - + raise pufferlib.APIUsageError(f"Data:\n{data}\n not in space:\n{space}") + return True + def _seed_and_reset(env, seed): if seed is None: # Gym bug: does not reset env correctly @@ -469,10 +468,11 @@ def _seed_and_reset(env, seed): obs, info = env.reset() except: obs, info = env.reset() - warnings.warn('WARNING: Environment does not support seeding.', DeprecationWarning) + warnings.warn("WARNING: Environment does not support seeding.", DeprecationWarning) return obs, info + class GymnaxPufferEnv(pufferlib.PufferEnv): def __init__(self, env, env_params, num_envs=1, buf=None): from gymnax.spaces import gymnax_space_to_gym_space @@ -490,27 +490,32 @@ def __init__(self, env, env_params, num_envs=1, buf=None): self.env = env import jax + self.reset_fn = jax.jit(jax.vmap(env.reset, in_axes=(0, None))) self.step_fn = jax.jit(jax.vmap(env.step, in_axes=(0, 0, 0, None))) self.rng = jax.random.PRNGKey(0) def reset(self, rng, params=None): import jax + self.rng, _rng = jax.random.split(self.rng) self.rngs = jax.random.split(_rng, self.num_agents) obs, self.state = self.reset_fn(self.rngs, params) from torch.utils import dlpack as torch_dlpack + self.observations = torch_dlpack.from_dlpack(jax.dlpack.to_dlpack(obs)) return self.observations, [] def step(self, action): import jax - #self.rng, _rng = jax.random.split(self.rng) - #rngs = jax.random.split(_rng, self.num_agents) + + # self.rng, _rng = jax.random.split(self.rng) + # rngs = jax.random.split(_rng, self.num_agents) obs, self.state, reward, done, info = self.step_fn(self.rngs, self.state, action, self.env_params) # Convert JAX array to DLPack, then to PyTorch tensor from torch.utils import dlpack as torch_dlpack + self.observations = torch_dlpack.from_dlpack(jax.dlpack.to_dlpack(obs)) self.rewards = np.asarray(reward) self.terminals = np.asarray(done) diff --git a/pufferlib/environments/__init__.py b/pufferlib/environments/__init__.py deleted file mode 100644 index d6c969523..000000000 --- a/pufferlib/environments/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -from pdb import set_trace as T -import pufferlib - -def try_import(module_path, package_name=None): - if package_name is None: - package_name = module_path - try: - package = __import__(module_path) - except ImportError as e: - raise ImportError( - f'{e.args[0]}\n\n' - 'This is probably an installation error. Try: ' - f'pip install pufferlib[{package_name}]. ' - - 'Note that some environments have non-python dependencies. ' - 'These are included in PufferTank. Or, you can install ' - 'manually by following the instructions provided by the ' - 'environment meaintainers. But some are finicky, so we ' - 'recommend using PufferTank.' - ) from e - return package diff --git a/pufferlib/environments/atari/__init__.py b/pufferlib/environments/atari/__init__.py deleted file mode 100644 index 59cda9e7c..000000000 --- a/pufferlib/environments/atari/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/atari/environment.py b/pufferlib/environments/atari/environment.py deleted file mode 100644 index 92295ecaa..000000000 --- a/pufferlib/environments/atari/environment.py +++ /dev/null @@ -1,215 +0,0 @@ -from pdb import set_trace as T -import numpy as np -import functools - -import gymnasium as gym - -import pufferlib -import pufferlib.emulation -import pufferlib.environments - -def env_creator(name='breakout'): - return functools.partial(make, name) - -def make(name, obs_type='grayscale', frameskip=4, - full_action_space=False, framestack=1, - repeat_action_probability=0.0, render_mode='rgb_array', - buf=None, seed=0): - '''Atari creation function''' - pufferlib.environments.try_import('ale_py', 'AtariEnv') - - ale_render_mode = render_mode - if render_mode == 'human': - ale_render_mode = 'rgb_array' - obs_type = 'rgb' - frameskip = 1 - full_action_space = True - upscale = 4 - elif render_mode == 'raylib': - ale_render_mode = 'rgb_array' - upscale = 8 - - from ale_py import AtariEnv - env = AtariEnv(name, obs_type=obs_type, frameskip=frameskip, - repeat_action_probability=repeat_action_probability, - full_action_space=full_action_space, - render_mode=ale_render_mode) - - action_set = env._action_set - - if render_mode != 'human': - env = pufferlib.ResizeObservation(env, downscale=2) - - if framestack > 1: - env = gym.wrappers.FrameStack(env, framestack) - - if render_mode in ('human', 'raylib'): - env = RaylibClient(env, action_set, frameskip, upscale) - else: - env = AtariPostprocessor(env) # Don't use standard postprocessor - - env = pufferlib.EpisodeStats(env) - env = pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - return env - -class AtariPostprocessor(gym.Wrapper): - '''Atari breaks the normal PufferLib postprocessor because - it sends terminal=True every live, not every episode''' - def __init__(self, env): - super().__init__(env) - shape = env.observation_space.shape - if len(shape) < 3: - shape = (1, *shape) - else: - shape = (shape[2], shape[0], shape[1]) - - self.observation_space = gym.spaces.Box(low=0, high=255, - shape=shape, dtype=env.observation_space.dtype) - - def unsqueeze_transpose(self, obs): - if len(obs.shape) == 3: - return np.transpose(obs, (2, 0, 1)) - else: - return np.expand_dims(obs, 0) - - def reset(self, seed=None, options=None): - obs, _ = self.env.reset(seed=seed) - return self.unsqueeze_transpose(obs), {} - - def step(self, action): - obs, reward, terminal, truncated, _ = self.env.step(action) - return self.unsqueeze_transpose(obs), reward, terminal, truncated, {} - -class RaylibClient(gym.Wrapper): - def __init__(self, env, action_set, frameskip=4, upscale=4): - self.env = env - - self.keymap = {} - for i, atn in enumerate(action_set): - self.keymap[atn.value] = i - - obs_shape = env.observation_space.shape - if len(obs_shape) == 2: - height, width = obs_shape - channels = 1 - else: - height, width, channels = obs_shape - - height *= upscale - width *= upscale - from raylib import rl, colors - rl.InitWindow(width, height, "Atari".encode()) - rl.SetTargetFPS(60//frameskip) - self.rl = rl - self.colors = colors - - - import numpy as np - rendered = np.zeros((width, height, 4), dtype=np.uint8) - - import pyray - from cffi import FFI - raylib_image = pyray.Image(FFI().from_buffer(rendered.data), - width, height, 1, pyray.PIXELFORMAT_UNCOMPRESSED_R8G8B8) - self.texture = rl.LoadTextureFromImage(raylib_image) - self.action = 0 - - self.upscale = upscale - self.rescaler = np.ones((upscale, upscale, 1), dtype=np.uint8) - - def any_key_pressed(self, keys): - for key in keys: - if self.rl.IsKeyPressed(key): - return True - return False - - def any_key_down(self, keys): - for key in keys: - if self.rl.IsKeyDown(key): - return True - return False - - def down(self): - return self.any_key_down([self.rl.KEY_S, self.rl.KEY_DOWN]) - - def up(self): - return self.any_key_down([self.rl.KEY_W, self.rl.KEY_UP]) - - def left(self): - return self.any_key_down([self.rl.KEY_A, self.rl.KEY_LEFT]) - - def right(self): - return self.any_key_down([self.rl.KEY_D, self.rl.KEY_RIGHT]) - - def render(self): - from ale_py import Action - - rl = self.rl - if rl.IsKeyPressed(rl.KEY_ESCAPE): - exit(0) - - elif rl.IsKeyDown(rl.KEY_SPACE): - if self.left() and self.down(): - action = Action.DOWNLEFTFIRE.value - elif self.right() and self.down(): - action = Action.DOWNRIGHTFIRE.value - elif self.left() and self.up(): - action = Action.UPLEFTFIRE.value - elif self.right() and self.up(): - action = Action.UPRIGHTFIRE.value - elif self.left(): - action = Action.LEFTFIRE.value - elif self.right(): - action = Action.RIGHTFIRE.value - elif self.up(): - action = Action.UPFIRE.value - elif self.down(): - action = Action.DOWNFIRE.value - else: - action = Action.FIRE.value - elif self.left() and self.down(): - action = Action.DOWNLEFT.value - elif self.right() and self.down(): - action = Action.DOWNRIGHT.value - elif self.left() and self.up(): - action = Action.UPLEFT.value - elif self.right() and self.up(): - action = Action.UPRIGHT.value - elif self.left(): - action = Action.LEFT.value - elif self.right(): - action = Action.RIGHT.value - elif self.up(): - action = Action.UP.value - else: - action = Action.NOOP.value - - if action in self.keymap: - self.action = self.keymap[action] - else: - self.action = Action.NOOP.value - - #frame = self.env.render() - frame = self.frame - if len(frame.shape) < 3: - frame = np.expand_dims(frame, 2) - frame = np.repeat(frame, 3, axis=2) - - if self.upscale > 1: - frame = np.kron(frame, self.rescaler) - - rl.BeginDrawing() - rl.ClearBackground(self.colors.BLACK) - rl.UpdateTexture(self.texture, frame.tobytes()) - rl.DrawTextureEx(self.texture, (0, 0), 0, 1, self.colors.WHITE) - rl.EndDrawing() - - def reset(self, seed=None, options=None): - obs, info = self.env.reset(seed=seed, options=options) - self.frame = obs - return obs, info - - def step(self, action): - obs, reward, terminal, truncated, info = self.env.step(self.action) - self.frame = obs - return obs, reward, terminal, truncated, info diff --git a/pufferlib/environments/atari/torch.py b/pufferlib/environments/atari/torch.py deleted file mode 100644 index cae86bbe4..000000000 --- a/pufferlib/environments/atari/torch.py +++ /dev/null @@ -1,18 +0,0 @@ -import pufferlib.models - - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=512, hidden_size=512): - super().__init__(env, policy, input_size, hidden_size) - -class Policy(pufferlib.models.Convolutional): - def __init__(self, env, input_size=512, hidden_size=512, output_size=512, - framestack=1, flat_size=64*6*9, **kwargs): - super().__init__( - env=env, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - framestack=framestack, - flat_size=flat_size, - ) diff --git a/pufferlib/environments/box2d/__init__.py b/pufferlib/environments/box2d/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/box2d/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/box2d/environment.py b/pufferlib/environments/box2d/environment.py deleted file mode 100644 index f2fd12b7e..000000000 --- a/pufferlib/environments/box2d/environment.py +++ /dev/null @@ -1,21 +0,0 @@ -from pdb import set_trace as T - -import gymnasium -import functools - -import pufferlib.emulation -import pufferlib.environments -import pufferlib.postprocess - - -def env_creator(name='car-racing'): - return functools.partial(make, name=name) - -def make(name, domain_randomize=True, continuous=False, render_mode='rgb_array', buf=None): - if name == 'car-racing': - name = 'CarRacing-v2' - - env = gymnasium.make(name, render_mode=render_mode, - domain_randomize=domain_randomize, continuous=continuous) - env = pufferlib.postprocess.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) diff --git a/pufferlib/environments/box2d/torch.py b/pufferlib/environments/box2d/torch.py deleted file mode 100644 index b7cdd6acb..000000000 --- a/pufferlib/environments/box2d/torch.py +++ /dev/null @@ -1,24 +0,0 @@ -from functools import partial -import torch - -import pufferlib.models - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, - input_size=128, hidden_size=128, num_layers=1): - super().__init__(env, policy, - input_size, hidden_size, num_layers) - -class Policy(pufferlib.models.Convolutional): - def __init__(self, env, - input_size=128, hidden_size=128, output_size=128, - framestack=3, flat_size=64*8*8): - super().__init__( - env=env, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - framestack=framestack, - flat_size=flat_size, - channels_last=True, - ) diff --git a/pufferlib/environments/bsuite/__init__.py b/pufferlib/environments/bsuite/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/bsuite/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/bsuite/environment.py b/pufferlib/environments/bsuite/environment.py deleted file mode 100644 index f39c661e5..000000000 --- a/pufferlib/environments/bsuite/environment.py +++ /dev/null @@ -1,44 +0,0 @@ -from pdb import set_trace as T -import gym -import functools - -import pufferlib.emulation -import pufferlib.wrappers - -import bsuite -from bsuite.utils import gym_wrapper - -def env_creator(name='bandit/0'): - return functools.partial(make, name) - -def make(name='bandit/0', results_dir='experiments/bsuite', overwrite=True, buf=None): - '''BSuite environments''' - bsuite = pufferlib.environments.try_import('bsuite') - from bsuite.utils import gym_wrapper - env = bsuite.load_and_record_to_csv(name, results_dir, overwrite=overwrite) - env = gym_wrapper.GymFromDMEnv(env) - env = BSuiteStopper(env) - env = pufferlib.wrappers.GymToGymnasium(env) - env = pufferlib.emulation.GymnasiumPufferEnv(env, buf=buf) - return env - -class BSuiteStopper: - def __init__(self, env): - self.env = env - self.num_episodes = 0 - - self.step = self.env.step - self.render = self.env.render - self.close = self.env.close - self.observation_space = self.env.observation_space - self.action_space = self.env.action_space - - def reset(self): - '''Forces the environment to stop after the - number of episodes required by bsuite''' - self.num_episodes += 1 - - if self.num_episodes >= self.env.bsuite_num_episodes: - exit(0) - - return self.env.reset() diff --git a/pufferlib/environments/bsuite/torch.py b/pufferlib/environments/bsuite/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/bsuite/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/butterfly/__init__.py b/pufferlib/environments/butterfly/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/butterfly/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/butterfly/environment.py b/pufferlib/environments/butterfly/environment.py deleted file mode 100644 index 994e53e9e..000000000 --- a/pufferlib/environments/butterfly/environment.py +++ /dev/null @@ -1,25 +0,0 @@ -from pdb import set_trace as T -from pettingzoo.utils.conversions import aec_to_parallel_wrapper -import functools - -import pufferlib.emulation -import pufferlib.environments - - -def env_creator(name='cooperative_pong_v5'): - return functools.partial(make, name) - -def make(name, buf=None): - pufferlib.environments.try_import('pettingzoo.butterfly', 'butterfly') - if name == 'cooperative_pong_v5': - from pettingzoo.butterfly import cooperative_pong_v5 as pong - env_cls = pong.raw_env - elif name == 'knights_archers_zombies_v10': - from pettingzoo.butterfly import knights_archers_zombies_v10 as kaz - env_cls = kaz.raw_env - else: - raise ValueError(f'Unknown environment: {name}') - - env = env_cls() - env = aec_to_parallel_wrapper(env) - return pufferlib.emulation.PettingZooPufferEnv(env=env, buf=buf) diff --git a/pufferlib/environments/butterfly/torch.py b/pufferlib/environments/butterfly/torch.py deleted file mode 100644 index 5965a3971..000000000 --- a/pufferlib/environments/butterfly/torch.py +++ /dev/null @@ -1,26 +0,0 @@ -import pufferlib.models - - -class Policy(pufferlib.models.Convolutional): - def __init__( - self, - env, - flat_size=3520, - channels_last=True, - downsample=4, - input_size=512, - hidden_size=128, - output_size=128, - **kwargs - ): - super().__init__( - env, - framestack=3, - flat_size=flat_size, - channels_last=channels_last, - downsample=downsample, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - **kwargs - ) diff --git a/pufferlib/environments/classic_control/__init__.py b/pufferlib/environments/classic_control/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/classic_control/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/classic_control/environment.py b/pufferlib/environments/classic_control/environment.py deleted file mode 100644 index 9da76d3e9..000000000 --- a/pufferlib/environments/classic_control/environment.py +++ /dev/null @@ -1,40 +0,0 @@ -import gymnasium -from gymnasium.envs import classic_control -import functools -import numpy as np - -import pufferlib -import pufferlib.emulation -import pufferlib.postprocess - -ALIASES = { - 'cartpole': 'CartPole-v0', - 'mountaincar': 'MountainCar-v0', -} - -def env_creator(name='cartpole'): - return functools.partial(make, name) - -def make(name, render_mode='rgb_array', buf=None): - '''Create an environment by name''' - - if name in ALIASES: - name = ALIASES[name] - - env = gymnasium.make(name, render_mode=render_mode) - if name == 'MountainCar-v0': - env = MountainCarWrapper(env) - - #env = gymnasium.wrappers.NormalizeObservation(env) - env = gymnasium.wrappers.TransformObservation(env, lambda obs: np.clip(obs, -1, 1)) - #env = gymnasium.wrappers.NormalizeReward(env, gamma=gamma) - env = gymnasium.wrappers.TransformReward(env, lambda reward: np.clip(reward, -1, 1)) - env = pufferlib.postprocess.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class MountainCarWrapper(gymnasium.Wrapper): - def step(self, action): - obs, reward, terminated, truncated, info = self.env.step(action) - reward = abs(obs[0]+0.5) - return obs, reward, terminated, truncated, info - diff --git a/pufferlib/environments/classic_control/torch.py b/pufferlib/environments/classic_control/torch.py deleted file mode 100644 index ed3afb863..000000000 --- a/pufferlib/environments/classic_control/torch.py +++ /dev/null @@ -1,6 +0,0 @@ -import pufferlib.models - - -class Policy(pufferlib.models.Default): - def __init__(self, env, hidden_size=64): - super().__init__(env, hidden_size) diff --git a/pufferlib/environments/classic_control_continuous/__init__.py b/pufferlib/environments/classic_control_continuous/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/classic_control_continuous/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/classic_control_continuous/environment.py b/pufferlib/environments/classic_control_continuous/environment.py deleted file mode 100644 index 47f293657..000000000 --- a/pufferlib/environments/classic_control_continuous/environment.py +++ /dev/null @@ -1,27 +0,0 @@ -import gymnasium -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.postprocess - - -def env_creator(name='MountainCarContinuous-v0'): - return functools.partial(make, name) - -def make(name, render_mode='rgb_array', buf=None): - '''Create an environment by name''' - env = gymnasium.make(name, render_mode=render_mode) - if name == 'MountainCarContinuous-v0': - env = MountainCarWrapper(env) - - env = pufferlib.postprocess.ClipAction(env) - env = pufferlib.postprocess.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class MountainCarWrapper(gymnasium.Wrapper): - def step(self, action): - obs, reward, terminated, truncated, info = self.env.step(action) - reward = abs(obs[0]+0.5) - return obs, reward, terminated, truncated, info - diff --git a/pufferlib/environments/classic_control_continuous/torch.py b/pufferlib/environments/classic_control_continuous/torch.py deleted file mode 100644 index ed3afb863..000000000 --- a/pufferlib/environments/classic_control_continuous/torch.py +++ /dev/null @@ -1,6 +0,0 @@ -import pufferlib.models - - -class Policy(pufferlib.models.Default): - def __init__(self, env, hidden_size=64): - super().__init__(env, hidden_size) diff --git a/pufferlib/environments/craftax/__init__.py b/pufferlib/environments/craftax/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/craftax/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/craftax/environment.py b/pufferlib/environments/craftax/environment.py deleted file mode 100644 index deb85ccd9..000000000 --- a/pufferlib/environments/craftax/environment.py +++ /dev/null @@ -1,13 +0,0 @@ -import functools - -import pufferlib -import pufferlib.emulation - -def env_creator(name='Craftax-Symbolic-v1'): - return functools.partial(make, name) - -def make(name, num_envs=2048, buf=None): - from craftax.craftax_env import make_craftax_env_from_name - env = make_craftax_env_from_name(name, auto_reset=True) - env_params = env.default_params - return pufferlib.emulation.GymnaxPufferEnv(env, env_params, num_envs=num_envs, buf=buf) diff --git a/pufferlib/environments/craftax/torch.py b/pufferlib/environments/craftax/torch.py deleted file mode 100644 index dded19779..000000000 --- a/pufferlib/environments/craftax/torch.py +++ /dev/null @@ -1,72 +0,0 @@ -import torch -from torch import nn - -import pufferlib.models - -Recurrent = pufferlib.models.LSTMWrapper - -''' -CRAFTAX_CHANNELS = 83 - -# Are these transposed? -CRAFTAX_ROWS = 11 -CRAFTAX_COLS = 9 - -N_MAP = CRAFTAX_ROWS * CRAFTAX_COLS * CRAFTAX_CHANNELS -N_FLAT = 51 -''' - -CRAFTAX_ROWS = 7 -CRAFTAX_COLS = 9 -CRAFTAX_CHANNELS = 21 -N_MAP = CRAFTAX_ROWS * CRAFTAX_COLS * CRAFTAX_CHANNELS -N_FLAT = 22 - - -class Policy(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128, **kwargs): - super().__init__() - self.map_encoder = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(21, cnn_channels, 3, stride=2)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.ReLU(), - nn.Flatten(), - ) - self.flat_encoder = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(N_FLAT, hidden_size)), - nn.ReLU(), - ) - self.proj = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(2*cnn_channels + hidden_size, hidden_size)), - nn.ReLU(), - ) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - self.is_continuous = False - - def forward(self, observations): - hidden, lookup = self.encode_observations(observations) - actions, value = self.decode_actions(hidden, lookup) - return actions, value - - def encode_observations(self, observations): - map_obs = observations[:, :N_MAP].view( - -1, CRAFTAX_ROWS, CRAFTAX_COLS, CRAFTAX_CHANNELS - ).permute(0, 3, 1, 2) - map_obs = self.map_encoder(map_obs) - flat_obs = observations[:, N_MAP:] - flat_obs = self.flat_encoder(flat_obs) - features = torch.cat([map_obs, flat_obs], dim=1) - features = self.proj(features) - return features, None - - def decode_actions(self, flat_hidden, lookup, concat=None): - action = self.actor(flat_hidden) - value = self.value_fn(flat_hidden) - return action, value diff --git a/pufferlib/environments/crafter/__init__.py b/pufferlib/environments/crafter/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/crafter/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/crafter/environment.py b/pufferlib/environments/crafter/environment.py deleted file mode 100644 index e320e347d..000000000 --- a/pufferlib/environments/crafter/environment.py +++ /dev/null @@ -1,46 +0,0 @@ -from pdb import set_trace as T - -import gym -import gymnasium -import shimmy -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.postprocess -import pufferlib.utils - - -class TransposeObs(gym.Wrapper): - def observation(self, observation): - return observation.transpose(2, 0, 1) - -def env_creator(name='crafter'): - return functools.partial(make, name) - -def make(name, buf=None): - '''Crafter creation function''' - if name == 'crafter': - name = 'CrafterReward-v1' - - pufferlib.environments.try_import('crafter') - env = gym.make(name) - env.reset = pufferlib.utils.silence_warnings(env.reset) - env = shimmy.GymV21CompatibilityV0(env=env) - env = RenderWrapper(env) - env = TransposeObs(env) - env = pufferlib.postprocess.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class RenderWrapper(gym.Wrapper): - def __init__(self, env): - super().__init__(env) - self.env = env - - @property - def render_mode(self): - return 'rgb_array' - - def render(self, *args, **kwargs): - return self.env.unwrapped.env.unwrapped.render((256,256)) diff --git a/pufferlib/environments/crafter/torch.py b/pufferlib/environments/crafter/torch.py deleted file mode 100644 index f6f4e5237..000000000 --- a/pufferlib/environments/crafter/torch.py +++ /dev/null @@ -1,26 +0,0 @@ -import pufferlib.models - - -class Policy(pufferlib.models.Convolutional): - def __init__( - self, - env, - flat_size=1024, - channels_last=True, - downsample=1, - input_size=512, - hidden_size=128, - output_size=128, - **kwargs - ): - super().__init__( - env, - framestack=3, - flat_size=flat_size, - channels_last=channels_last, - downsample=downsample, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - **kwargs - ) diff --git a/pufferlib/environments/dm_control/__init__.py b/pufferlib/environments/dm_control/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/dm_control/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/dm_control/environment.py b/pufferlib/environments/dm_control/environment.py deleted file mode 100644 index f492fa659..000000000 --- a/pufferlib/environments/dm_control/environment.py +++ /dev/null @@ -1,24 +0,0 @@ -from pdb import set_trace as T - -import gym -import shimmy -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments - - -def env_creator(name='walker'): - '''Deepmind Control environment creation function - - No support for bindings yet because PufferLib does - not support continuous action spaces.''' - return functools.partial(make, name) - -def make(name, task_name='walk', buf=None): - '''Untested. Let us know in Discord if you want to use dmc in PufferLib.''' - dm_control = pufferlib.environments.try_import('dm_control.suite', 'dmc') - env = dm_control.suite.load(name, task_name) - env = shimmy.DmControlCompatibilityV0(env=env) - return pufferlib.emulation.GymnasiumPufferEnv(env, buf=buf) diff --git a/pufferlib/environments/dm_control/torch.py b/pufferlib/environments/dm_control/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/dm_control/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/dm_lab/__init__.py b/pufferlib/environments/dm_lab/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/dm_lab/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/dm_lab/environment.py b/pufferlib/environments/dm_lab/environment.py deleted file mode 100644 index 17bbde770..000000000 --- a/pufferlib/environments/dm_lab/environment.py +++ /dev/null @@ -1,24 +0,0 @@ -from pdb import set_trace as T - -import gym -import shimmy -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments - - -def env_creator(name='seekavoid_arena_01'): - '''Deepmind Lab binding creation function - dm-lab requires extensive setup. Use PufferTank.''' - return functools.partial(make, name=name) - -def make(name, buf=None): - '''Deepmind Lab binding creation function - dm-lab requires extensive setup. Currently dropped frop PufferTank. - Let us know if you need this for your work.''' - dm_lab = pufferlib.environments.try_import('deepmind_lab', 'dm-lab') - env = dm_lab.Lab(name, ['RGB_INTERLEAVED']) - env = shimmy.DmLabCompatibilityV0(env=env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) diff --git a/pufferlib/environments/dm_lab/torch.py b/pufferlib/environments/dm_lab/torch.py deleted file mode 100644 index c8e411498..000000000 --- a/pufferlib/environments/dm_lab/torch.py +++ /dev/null @@ -1,26 +0,0 @@ -import pufferlib.models - - -class Policy(pufferlib.models.Convolutional): - def __init__( - self, - env, - flat_size=3136, - channels_last=True, - downsample=1, - input_size=512, - hidden_size=128, - output_size=128, - **kwargs - ): - super().__init__( - env, - framestack=3, - flat_size=flat_size, - channels_last=channels_last, - downsample=downsample, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - **kwargs - ) diff --git a/pufferlib/environments/gpudrive/__init__.py b/pufferlib/environments/gpudrive/__init__.py deleted file mode 100644 index 59cda9e7c..000000000 --- a/pufferlib/environments/gpudrive/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/gpudrive/environment.py b/pufferlib/environments/gpudrive/environment.py deleted file mode 100644 index 31302e65d..000000000 --- a/pufferlib/environments/gpudrive/environment.py +++ /dev/null @@ -1,146 +0,0 @@ -import os -import numpy as np -from pathlib import Path -import torch -import gymnasium - -from pygpudrive.env.config import EnvConfig, RenderConfig, SceneConfig, SelectionDiscipline -from pygpudrive.env.env_torch import GPUDriveTorchEnv - -def env_creator(name='gpudrive'): - return PufferGPUDrive - -class PufferGPUDrive: - def __init__(self, device='cuda', max_cont_agents=64, num_worlds=64, k_unique_scenes=1): - self.device = device - self.max_cont_agents = max_cont_agents - self.num_worlds = num_worlds - self.k_unique_scenes = k_unique_scenes - self.total_agents = max_cont_agents * num_worlds - - # Set working directory to the base directory 'gpudrive' - working_dir = os.path.join(Path.cwd(), '../gpudrive') - os.chdir(working_dir) - - scene_config = SceneConfig( - path='biggest_file/', - num_scenes=num_worlds, - discipline=SelectionDiscipline.K_UNIQUE_N, - k_unique_scenes=k_unique_scenes, - ) - - env_config = EnvConfig( - steer_actions = torch.round( - torch.linspace(-1.0, 1.0, 3), decimals=3), - accel_actions = torch.round( - torch.linspace(-3, 3, 3), decimals=3 - ) - ) - - render_config = RenderConfig( - resolution=(512, 512), # Quality of the rendered images - ) - - self.env = GPUDriveTorchEnv( - config=env_config, - scene_config=scene_config, - render_config=render_config, - max_cont_agents=max_cont_agents, - device=device, - ) - - self.obs_size = self.env.observation_space.shape[-1] - self.action_space = self.env.action_space - self.observation_space = self.env.observation_space - self.observation_space = gymnasium.spaces.Box( - low=0, high=255, shape=(self.obs_size,), dtype=np.float32) - self.single_observation_space = self.observation_space - self.single_action_space = self.action_space - self.done = False - self.emulated = None - self.render_mode = 'rgb_array' - self.num_live = [] - - self.controlled_agent_mask = self.env.cont_agent_mask.clone() - self.obs = self.env.reset()[self.controlled_agent_mask] - self.num_controlled = self.controlled_agent_mask.sum().item() - self.num_agents = self.obs.shape[0] - self.env_id = np.array([i for i in range(self.num_agents)]) - self.mask = np.ones(self.num_agents, dtype=bool) - self.actions = torch.zeros((num_worlds, max_cont_agents), - dtype=torch.int64).to(self.device) - - def _obs_and_mask(self, obs): - #self.buf.masks[:] = self.env.cont_agent_mask.numpy().ravel() * self.live_agent_mask - #return np.asarray(obs).reshape(NUM_WORLDS*MAX_NUM_OBJECTS, self.obs_size) - #return obs.numpy().reshape(NUM_WORLDS*MAX_NUM_OBJECTS, self.obs_size)[:, :6] - return obs.view(self.total_agents, self.obs_size) - - def close(self): - '''There is no point in closing the env because - Madrona doesn't close correctly anyways. You will want - to cache this copy for later use. Cuda errors if you don't''' - pass - #self.env.close() - #del self.env.sim - - def reset(self, seed=None, options=None): - self.reward = torch.zeros(self.num_agents, dtype=torch.float32).to(self.device) - self.terminal = torch.zeros(self.num_agents, dtype=torch.bool).to(self.device) - self.truncated = torch.zeros(self.num_agents, dtype=torch.bool).to(self.device) - - self.episode_returns = torch.zeros(self.num_agents, dtype=torch.float32).to(self.device) - self.episode_lengths = torch.zeros(self.num_agents, dtype=torch.float32).to(self.device) - self.live_agent_mask = torch.ones((self.num_worlds, self.max_cont_agents), dtype=bool).to(self.device) - return self.obs, self.reward, self.terminal, self.truncated, [], self.env_id, self.mask - - def step(self, action): - action = torch.from_numpy(action).to(self.device) - self.actions[self.controlled_agent_mask] = action - self.env.step_dynamics(self.actions) - - obs = self.env.get_obs()[self.controlled_agent_mask] - reward = self.env.get_rewards()[self.controlled_agent_mask] - terminal = self.env.get_dones().bool() - - done_worlds = torch.where( - (terminal.nan_to_num(0) * self.controlled_agent_mask).sum(dim=1) - == self.controlled_agent_mask.sum(dim=1) - )[0].cpu() - - self.episode_returns += reward - self.episode_lengths += 1 - self.mask = self.live_agent_mask[self.controlled_agent_mask].cpu().numpy() - self.live_agent_mask[terminal] = 0 - terminal = terminal[self.controlled_agent_mask] - - info = [] - self.num_live.append(self.mask.sum()) - - if len(done_worlds) > 0: - controlled_mask = self.controlled_agent_mask[done_worlds] - info_tensor = self.env.get_infos()[done_worlds][controlled_mask] - num_finished_agents = controlled_mask.sum().item() - info.append({ - 'off_road': info_tensor[:, 0].sum().item() / num_finished_agents, - 'veh_collisions': info_tensor[:, 1].sum().item() / num_finished_agents, - 'non_veh_collisions': info_tensor[:, 2].sum().item() / num_finished_agents, - 'goal_achieved': info_tensor[:, 3].sum().item() / num_finished_agents, - 'num_finished_agents': num_finished_agents, - 'episode_length': self.episode_lengths[done_worlds].mean().item(), - 'mean_reward_per_episode': self.episode_returns[done_worlds].mean().item(), - 'control_density': self.num_controlled / self.num_agents, - 'data_density': np.mean(self.num_live) / self.num_agents, - }) - - self.num_live = [] - for idx in done_worlds: - self.env.sim.reset(idx) - self.episode_returns[idx] = 0 - self.episode_lengths[idx] = 0 - self.live_agent_mask[idx] = self.controlled_agent_mask[idx] - - return obs, reward, terminal, self.truncated, info, self.env_id, self.mask - - def render(self, world_render_idx=0): - return self.env.render(world_render_idx=world_render_idx) diff --git a/pufferlib/environments/gpudrive/torch.py b/pufferlib/environments/gpudrive/torch.py deleted file mode 100644 index 5e684e507..000000000 --- a/pufferlib/environments/gpudrive/torch.py +++ /dev/null @@ -1,90 +0,0 @@ -from torch import nn -import torch -import torch.nn.functional as F - -from functools import partial -import pufferlib.models - -from pufferlib.models import Default as Policy -Recurrent = pufferlib.models.LSTMWrapper - -EGO_STATE_DIM = 6 -PARTNER_DIM = 10 -ROAD_MAP_DIM = 13 - -MAX_CONTROLLED_VEHICLES = 32 -ROADMAP_AGENT_FEAT_DIM = MAX_CONTROLLED_VEHICLES - 1 -TOP_K_ROADPOINTS = 64 # Number of visible roadpoints from the road graph - -def unpack_obs(obs_flat): - """ - Unpack the flattened observation into the ego state and visible state. - Args: - obs_flat (torch.Tensor): flattened observation tensor of shape (batch_size, obs_dim) - Return: - ego_state, road_objects, stop_signs, road_graph (torch.Tensor). - """ - # Unpack ego and visible state - ego_state = obs_flat[:, :EGO_STATE_DIM] - vis_state = obs_flat[:, EGO_STATE_DIM :] - # Visible state object order: road_objects, road_points - # Find the ends of each section - ro_end_idx = PARTNER_DIM * ROADMAP_AGENT_FEAT_DIM - rg_end_idx = ro_end_idx + (ROAD_MAP_DIM * TOP_K_ROADPOINTS) - - # Unflatten and reshape to (batch_size, num_objects, object_dim) - road_objects = (vis_state[:, :ro_end_idx]).reshape( - -1, ROADMAP_AGENT_FEAT_DIM, PARTNER_DIM - ) - road_graph = (vis_state[:, ro_end_idx:rg_end_idx]).reshape( - -1, - TOP_K_ROADPOINTS, - ROAD_MAP_DIM, - ) - return ego_state, road_objects, road_graph - -class Policy(nn.Module): - def __init__(self, env, input_size=64, hidden_size=128, **kwargs): - super().__init__() - self.ego_embed = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(EGO_STATE_DIM, input_size)), - torch.nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Linear(input_size, input_size)), - ) - - self.partner_embed = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(PARTNER_DIM, input_size)), - torch.nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Linear(input_size, input_size)), - ) - - self.road_map_embed = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(ROAD_MAP_DIM, input_size)), - torch.nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Linear(input_size, input_size)), - ) - - self.proj = pufferlib.pytorch.layer_init(nn.Linear(3*input_size, hidden_size)) - - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations): - hidden, lookup = self.encode_observations(observations) - actions, value = self.decode_actions(hidden, lookup) - return actions, value - - def encode_observations(self, observations): - ego_state, road_objects, road_graph = unpack_obs(observations) - ego_embed = self.ego_embed(ego_state) - partner_embed, _ = self.partner_embed(road_objects).max(dim=1) - road_map_embed, _ = self.road_map_embed(road_graph).max(dim=1) - embed = torch.cat([ego_embed, partner_embed, road_map_embed], dim=1) - return self.proj(embed), None - - def decode_actions(self, flat_hidden, lookup, concat=None): - action = self.actor(flat_hidden) - value = self.value_fn(flat_hidden) - return action, value diff --git a/pufferlib/environments/griddly/__init__.py b/pufferlib/environments/griddly/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/griddly/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/griddly/environment.py b/pufferlib/environments/griddly/environment.py deleted file mode 100644 index fdf8c2430..000000000 --- a/pufferlib/environments/griddly/environment.py +++ /dev/null @@ -1,37 +0,0 @@ -from pdb import set_trace as T - -import gym -import shimmy -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.postprocess - -ALIASES = { - 'spiders': 'GDY-Spiders-v0', -} - -def env_creator(name='spiders'): - return functools.partial(make, name) - -# TODO: fix griddly -def make(name, buf=None): - '''Griddly creation function - - Note that Griddly environments do not have observation spaces until - they are created and reset''' - if name in ALIASES: - name = ALIASES[name] - - import warnings - warnings.warn('Griddly has been segfaulting in the latest build and we do not know why. Submit a PR if you find a fix!') - pufferlib.environments.try_import('griddly') - with pufferlib.utils.Suppress(): - env = gym.make(name) - env.reset() # Populate observation space - - env = shimmy.GymV21CompatibilityV0(env=env) - env = pufferlib.postprocess.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env, buf=buf) diff --git a/pufferlib/environments/griddly/torch.py b/pufferlib/environments/griddly/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/griddly/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/gvgai/environment.py b/pufferlib/environments/gvgai/environment.py deleted file mode 100644 index fa4da86fd..000000000 --- a/pufferlib/environments/gvgai/environment.py +++ /dev/null @@ -1,28 +0,0 @@ -from pdb import set_trace as T -import numpy as np -import functools - -import gym - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.utils -import pufferlib.postprocess -import pufferlib.wrappers - -def env_creator(name='zelda'): - if name == 'zelda': - name = 'gvgai-zelda-lvl0-v0' - return functools.partial(make, name) - -def make(name, obs_type='grayscale', frameskip=4, full_action_space=False, - repeat_action_probability=0.0, render_mode='rgb_array', buf=None): - '''Atari creation function''' - pufferlib.environments.try_import('gym_gvgai') - env = gym.make(name) - env = pufferlib.wrappers.GymToGymnasium(env) - env = pufferlib.postprocess.EpisodeStats(env) - env = pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - return env - diff --git a/pufferlib/environments/kinetix/__init__.py b/pufferlib/environments/kinetix/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/kinetix/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/kinetix/environment.py b/pufferlib/environments/kinetix/environment.py deleted file mode 100644 index 116a5e12e..000000000 --- a/pufferlib/environments/kinetix/environment.py +++ /dev/null @@ -1,180 +0,0 @@ -import functools -import numpy as np - -import pufferlib - -train_levels = [ - "s/h0_weak_thrust", - "s/h7_unicycle_left", - "s/h3_point_the_thruster", - "s/h4_thrust_aim", - "s/h1_thrust_over_ball", - "s/h5_rotate_fall", - "s/h9_explode_then_thrust_over", - "s/h6_unicycle_right", - "s/h8_unicycle_balance", - "s/h2_one_wheel_car", - "m/h0_unicycle", - "m/h1_car_left", - "m/h2_car_right", - "m/h3_car_thrust", - "m/h4_thrust_the_needle", - "m/h5_angry_birds", - "m/h6_thrust_over", - "m/h7_car_flip", - "m/h8_weird_vehicle", - "m/h9_spin_the_right_way", - "m/h10_thrust_right_easy", - "m/h11_thrust_left_easy", - "m/h12_thrustfall_left", - "m/h13_thrustfall_right", - "m/h14_thrustblock", - "m/h15_thrustshoot", - "m/h16_thrustcontrol_right", - "m/h17_thrustcontrol_left", - "m/h18_thrust_right_very_easy", - "m/h19_thrust_left_very_easy", - "m/arm_left", - "m/arm_right", - "m/arm_up", - "m/arm_hard", - "l/h0_angrybirds", - "l/h1_car_left", - "l/h2_car_ramp", - "l/h3_car_right", - "l/h4_cartpole", - "l/h5_flappy_bird", - "l/h6_lorry", - "l/h7_maze_1", - "l/h8_maze_2", - "l/h9_morph_direction", - "l/h10_morph_direction_2", - "l/h11_obstacle_avoidance", - "l/h12_platformer_1", - "l/h13_platformer_2", - "l/h14_simple_thruster", - "l/h15_swing_up", - "l/h16_thruster_goal", - "l/h17_unicycle", - "l/hard_beam_balance", - "l/hard_cartpole_thrust", - "l/hard_cartpole_wheels", - "l/hard_lunar_lander", - "l/hard_pinball", - "l/grasp_hard", - "l/grasp_easy", - "l/mjc_half_cheetah", - "l/mjc_half_cheetah_easy", - "l/mjc_hopper", - "l/mjc_hopper_easy", - "l/mjc_swimmer", - "l/mjc_walker", - "l/mjc_walker_easy", - "l/car_launch", - "l/car_swing_around", - "l/chain_lander", - "l/chain_thrust", - "l/gears", - "l/lever_puzzle", - "l/pr", - "l/rail", -] - - -def env_creator(name="kinetix"): - from kinetix.environment.env import ObservationType, ActionType - - _, obs, act = name.split("-") - if obs == "symbolic": obs = "symbolic_flat" - - try: - obs = ObservationType.from_string(obs) - except ValueError: - raise ValueError(f"Unknown observation type: {obs}.") - try: - act = ActionType.from_string(act) - except ValueError: - raise ValueError(f"Unknown action type: {act}.") - - return functools.partial(KinetixPufferEnv, observation_type=obs, action_type=act) - - -def make(name, *args, **kwargs): - return KinetixPufferEnv(*args, **kwargs) - - -class KinetixPufferEnv(pufferlib.environment.PufferEnv): - def __init__(self, observation_type, action_type, num_envs=1, buf=None): - - from kinetix.environment.env import make_kinetix_env, ObservationType, ActionType - from kinetix.environment.env_state import EnvParams, StaticEnvParams - from kinetix.environment.ued.ued_state import UEDParams - from kinetix.environment.ued.ued import make_reset_fn_list_of_levels - - import jax - from gymnax.environments.spaces import gymnax_space_to_gym_space - - self.observation_type = observation_type - self.action_type = action_type - - # Use default parameters - env_params = EnvParams() - static_env_params = StaticEnvParams().replace() - - # Create the environment - env = make_kinetix_env( - observation_type=observation_type, # ObservationType.PIXELS, - action_type=action_type, # ActionType.DISCRETE, - reset_fn=make_reset_fn_list_of_levels(train_levels, static_env_params), - env_params=env_params, - static_env_params=static_env_params, - ) - - self.single_observation_space = gymnax_space_to_gym_space(env.observation_space(env_params)) - self.single_action_space = gymnax_space_to_gym_space(env.action_space(env_params)) - self.num_agents = num_envs - - self.env = env - - super().__init__(buf) - self.env_params = env_params - self.env = env - - self.reset_fn = jax.jit(jax.vmap(env.reset, in_axes=(0, None))) - self.step_fn = jax.jit(jax.vmap(env.step, in_axes=(0, 0, 0, None))) - self.rng = jax.random.PRNGKey(0) - - def reset(self, rng, params=None): - import jax - from torch.utils import dlpack as torch_dlpack - - self.rng, _rng = jax.random.split(self.rng) - self.rngs = jax.random.split(_rng, self.num_agents) - obs, self.state = self.reset_fn(self.rngs, params) - obs = self._obs_to_tensor(obs) - - self.observations = torch_dlpack.from_dlpack(jax.dlpack.to_dlpack(obs)) - return self.observations, [] - - def step(self, action): - import jax - from torch.utils import dlpack as torch_dlpack - - obs, self.state, reward, done, info = self.step_fn(self.rngs, self.state, action, self.env_params) - obs = self._obs_to_tensor(obs) - - # Convert JAX array to DLPack, then to PyTorch tensor - self.observations = torch_dlpack.from_dlpack(jax.dlpack.to_dlpack(obs)) - self.rewards = np.asarray(reward) - self.terminals = np.asarray(done) - infos = [{k: v.mean().item() for k, v in info.items()} | {"reward": self.rewards.mean()} ] - return self.observations, self.rewards, self.terminals, self.terminals, infos - - def _obs_to_tensor(self, obs): - from kinetix.environment.env import ObservationType - if self.observation_type == ObservationType.PIXELS: - return obs.image - elif self.observation_type == ObservationType.SYMBOLIC_FLAT: - return obs - else: - raise ValueError(f"Unknown observation type: {self.observation_type}.") diff --git a/pufferlib/environments/kinetix/torch.py b/pufferlib/environments/kinetix/torch.py deleted file mode 100644 index d713d3d27..000000000 --- a/pufferlib/environments/kinetix/torch.py +++ /dev/null @@ -1,44 +0,0 @@ -import torch -from torch import nn - -import pufferlib.models - -Recurrent = pufferlib.models.LSTMWrapper - -from pufferlib.models import Default as Policy -SymbolicPolicy = Policy - -class PixelsPolicy(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128, **kwargs): - super().__init__() - self.hidden_size = 128 - self.map_encoder = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Conv2d(3, cnn_channels, 8, stride=4)), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Conv2d(cnn_channels, cnn_channels, 4, stride=2)), - nn.ReLU(), - nn.Flatten(), - ) - self.proj = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(14 * 14 * cnn_channels, hidden_size)), - nn.ReLU(), - ) - self.actor = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, 1), std=1) - - self.is_continuous = False - - def forward(self, observations): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def encode_observations(self, observations): - encoded = self.map_encoder(observations.permute(0, 3, 1, 2)) - features = self.proj(encoded) - return features - - def decode_actions(self, flat_hidden, concat=None): - action = self.actor(flat_hidden) - value = self.value_fn(flat_hidden) - return action, value diff --git a/pufferlib/environments/links_awaken/__init__.py b/pufferlib/environments/links_awaken/__init__.py deleted file mode 100644 index 1a9590595..000000000 --- a/pufferlib/environments/links_awaken/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make_env - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/links_awaken/environment.py b/pufferlib/environments/links_awaken/environment.py deleted file mode 100644 index 8016c3cf5..000000000 --- a/pufferlib/environments/links_awaken/environment.py +++ /dev/null @@ -1,15 +0,0 @@ -from pdb import set_trace as T - -import gymnasium - -from links_awaken import LinksAwakenV1 as env_creator - -import pufferlib.emulation - - -def make_env(headless: bool = True, state_path=None, buf=None): - '''Links Awakening''' - env = env_creator(headless=headless, state_path=state_path) - env = gymnasium.wrappers.ResizeObservation(env, shape=(72, 80)) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, - postprocessor_cls=pufferlib.emulation.BasicPostprocessor, buf=buf) diff --git a/pufferlib/environments/links_awaken/torch.py b/pufferlib/environments/links_awaken/torch.py deleted file mode 100644 index 4c92d2fde..000000000 --- a/pufferlib/environments/links_awaken/torch.py +++ /dev/null @@ -1,21 +0,0 @@ -import pufferlib.models -from pufferlib.pytorch import LSTM - - -class Recurrent: - input_size = 512 - hidden_size = 512 - num_layers = 1 - -class Policy(pufferlib.models.Convolutional): - def __init__(self, env, input_size=512, hidden_size=512, output_size=512, - framestack=3, flat_size=64*5*6): - super().__init__( - env=env, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - framestack=framestack, - flat_size=flat_size, - channels_last=True, - ) diff --git a/pufferlib/environments/magent/__init__.py b/pufferlib/environments/magent/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/magent/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/magent/environment.py b/pufferlib/environments/magent/environment.py deleted file mode 100644 index 10fbfc2ad..000000000 --- a/pufferlib/environments/magent/environment.py +++ /dev/null @@ -1,25 +0,0 @@ -from pdb import set_trace as T -from pettingzoo.utils.conversions import aec_to_parallel_wrapper -import functools - -import pufferlib.emulation -import pufferlib.environments -import pufferlib.wrappers - - -def env_creator(name='battle_v4'): - return functools.partial(make, name) - pufferlib.environments.try_import('pettingzoo.magent', 'magent') - -def make(name, buf=None): - '''MAgent Battle V4 creation function''' - if name == 'battle_v4': - from pettingzoo.magent import battle_v4 - env_cls = battle_v4.env - else: - raise ValueError(f'Unknown environment name {name}') - - env = env_cls() - env = aec_to_parallel_wrapper(env) - env = pufferlib.wrappers.PettingZooTruncatedWrapper(env) - return pufferlib.emulation.PettingZooPufferEnv(env=env, buf=buf) diff --git a/pufferlib/environments/magent/torch.py b/pufferlib/environments/magent/torch.py deleted file mode 100644 index eb43bcd72..000000000 --- a/pufferlib/environments/magent/torch.py +++ /dev/null @@ -1,41 +0,0 @@ -from torch import nn - -import pufferlib.models - - -class Policy(pufferlib.models.Policy): - '''Based off of the DQN policy in MAgent''' - def __init__(self, env, hidden_size=256, output_size=256, kernel_num=32): - '''The CleanRL default Atari policy: a stack of three convolutions followed by a linear layer - - Takes framestack as a mandatory keyword arguments. Suggested default is 1 frame - with LSTM or 4 frames without.''' - super().__init__(env) - self.num_actions = self.action_space.n - - self.network = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Conv2d(5, kernel_num, 3)), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Conv2d(kernel_num, kernel_num, 3)), - nn.ReLU(), - nn.Flatten(), - pufferlib.pytorch.layer_init(nn.Linear(kernel_num*9*9, hidden_size)), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Linear(hidden_size, hidden_size)), - nn.ReLU(), - ) - - self.actor = pufferlib.pytorch.layer_init(nn.Linear(output_size, self.num_actions), std=0.01) - self.value_function = pufferlib.pytorch.layer_init(nn.Linear(output_size, 1), std=1) - - def critic(self, hidden): - return self.value_function(hidden) - - def encode_observations(self, observations): - observations = observations.permute(0, 3, 1, 2) - return self.network(observations), None - - def decode_actions(self, hidden, lookup): - action = self.actor(hidden) - value = self.value_function(hidden) - return action, value diff --git a/pufferlib/environments/mani_skill/__init__.py b/pufferlib/environments/mani_skill/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/mani_skill/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/mani_skill/environment.py b/pufferlib/environments/mani_skill/environment.py deleted file mode 100644 index 1d42c5ebf..000000000 --- a/pufferlib/environments/mani_skill/environment.py +++ /dev/null @@ -1,118 +0,0 @@ -import functools -import numpy as np -from collections import defaultdict - -import mani_skill.envs -from mani_skill.vector.wrappers.gymnasium import ManiSkillVectorEnv - -import gymnasium as gym -import torch - -import pufferlib - -ALIASES = { - 'mani_pickcube': 'PickCube-v1', - 'mani_pushcube': 'PushCube-v1', - 'mani_stackcube': 'StackCube-v1', - 'mani_peginsertion': 'PegInsertionSide-v1', -} - -def env_creator(name='PickCube-v1', **kwargs): - return functools.partial(make, name) - -def make(name, num_envs=1, render_mode='rgb_array', buf=None, seed=0, **kwargs): - '''Create an environment by name''' - - if name in ALIASES: - name = ALIASES[name] - - return ManiPufferEnv(name, num_envs=num_envs, render_mode=render_mode, buf=buf, seed=seed, **kwargs) - -class ManiPufferEnv(pufferlib.PufferEnv): - def __init__(self, name, num_envs=1, solver_position_iterations=15, - sim_steps_per_control=5, control_freq=20, render_mode='rgb_array', - log_interval=16, buf=None, seed=0): - sim_freq = int(sim_steps_per_control * control_freq) - sim_config = { - 'scene_config': { - 'solver_position_iterations': solver_position_iterations - }, - 'sim_freq': sim_freq, - 'control_freq': control_freq - } - self.env = gym.make(name, reward_mode='delta', num_envs=num_envs, - render_mode=render_mode, sim_config=sim_config) - self.env = ManiSkillVectorEnv(self.env, auto_reset=True, ignore_terminations=False, record_metrics=True) - self.agents_per_batch = num_envs - - obs_space = self.env.observation_space - self.single_observation_space = gym.spaces.Box( - low=obs_space.low[0], - high=obs_space.high[0], - shape=obs_space.shape[1:], - dtype=obs_space.dtype, - ) - - atn_space = self.env.action_space - self.single_action_space = gym.spaces.Box( - low=atn_space.low[0], - high=atn_space.high[0], - shape=atn_space.shape[1:], - dtype=atn_space.dtype, - ) - - self.render_mode = render_mode - self.num_agents = num_envs - self.log_interval = log_interval - self.tick = 0 - - self.env_id = np.arange(num_envs) - - self.logs = defaultdict(list) - - super().__init__(buf) - - def _flatten_info(self, info): - if "final_info" in info: - mask = info["_final_info"] - for k, v in info["final_info"]["episode"].items(): - self.logs[k].append(v[mask].float().mean().item()) - - def reset(self, seed=0): - obs, info = self.env.reset() - #self.observations = torch.nan_to_num(obs) - self.observations = torch.clamp(torch.nan_to_num(obs), -5, 5) - self.observations = obs / 20.0 - self._flatten_info(info) - return obs, [] - - def step(self, actions): - obs, reward, terminated, truncated, info = self.env.step(actions) - collapsed = torch.where(torch.isnan(obs).sum(1) > 0)[0] - if len(collapsed) > 0: - obs, _ = self.env.reset(options={'env_idx': collapsed}) - - self.observations = torch.clamp(torch.nan_to_num(obs), -5, 5) - #self.observations = obs / 20.0 #torch.nan_to_num(obs) - self.rewards = reward - self.terminated = terminated - self.truncated = truncated - self._flatten_info(info) - - self.infos = [] - self.tick += 1 - if self.tick % self.log_interval == 0: - info = {} - for k, v in self.logs.items(): - info[k] = np.mean(v) - - self.logs = defaultdict(list) - self.infos.append(info) - - return obs, reward, terminated, truncated, self.infos - - def render(self): - return self.env.render()[0].cpu().numpy() - - def close(self): - self.env.close() diff --git a/pufferlib/environments/mani_skill/torch.py b/pufferlib/environments/mani_skill/torch.py deleted file mode 100644 index abb8eaa18..000000000 --- a/pufferlib/environments/mani_skill/torch.py +++ /dev/null @@ -1,71 +0,0 @@ -import numpy as np - -import torch -import torch.nn as nn - -import pufferlib -from pufferlib.models import Default as Policy -from pufferlib.models import LSTMWrapper as Recurrent - -class FakePolicy(nn.Module): - '''Default PyTorch policy. Flattens obs and applies a linear layer. - - PufferLib is not a framework. It does not enforce a base class. - You can use any PyTorch policy that returns actions and values. - We structure our forward methods as encode_observations and decode_actions - to make it easier to wrap policies with LSTMs. You can do that and use - our LSTM wrapper or implement your own. To port an existing policy - for use with our LSTM wrapper, simply put everything from forward() before - the recurrent cell into encode_observations and put everything after - into decode_actions. - ''' - def __init__(self, env, hidden_size=256): - super().__init__() - self.hidden_size = hidden_size - - n_obs = np.prod(env.single_observation_space.shape) - n_atn = env.single_action_space.shape[0] - self.decoder_mean = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(n_obs, 256)), - nn.Tanh(), - pufferlib.pytorch.layer_init(nn.Linear(256, 256)), - nn.Tanh(), - pufferlib.pytorch.layer_init(nn.Linear(256, 256)), - nn.Tanh(), - pufferlib.pytorch.layer_init(nn.Linear(256, n_atn), std=0.01), - ) - self.decoder_logstd = nn.Parameter(torch.zeros( - 1, env.single_action_space.shape[0])) - - self.value = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(n_obs, 256)), - nn.Tanh(), - pufferlib.pytorch.layer_init(nn.Linear(256, 256)), - nn.Tanh(), - pufferlib.pytorch.layer_init(nn.Linear(256, 256)), - nn.Tanh(), - pufferlib.pytorch.layer_init(nn.Linear(256, 1), std=1), - ) - - def forward_eval(self, observations, state=None): - hidden = self.encode_observations(observations, state=state) - logits, values = self.decode_actions(hidden) - return logits, values - - def forward(self, observations, state=None): - return self.forward_eval(observations, state) - - def encode_observations(self, observations, state=None): - '''Encodes a batch of observations into hidden states. Assumes - no time dimension (handled by LSTM wrappers).''' - return observations - - def decode_actions(self, hidden): - '''Decodes a batch of hidden states into (multi)discrete actions. - Assumes no time dimension (handled by LSTM wrappers).''' - mean = self.decoder_mean(hidden) - logstd = self.decoder_logstd.expand_as(mean) - std = torch.exp(logstd) - logits = torch.distributions.Normal(mean, std) - values = self.value(hidden) - return logits, values diff --git a/pufferlib/environments/metta/__init__.py b/pufferlib/environments/metta/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/metta/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/metta/environment.py b/pufferlib/environments/metta/environment.py deleted file mode 100644 index 21acc4610..000000000 --- a/pufferlib/environments/metta/environment.py +++ /dev/null @@ -1,74 +0,0 @@ -import functools -import numpy as np -import gymnasium - -import pufferlib - -from omegaconf import OmegaConf -from metta.mettagrid.mettagrid_env import MettaGridEnv -from metta.mettagrid.curriculum.core import SingleTaskCurriculum -from metta.mettagrid.replay_writer import ReplayWriter - -def env_creator(name='metta'): - return functools.partial(make, name) - -def make(name, config='pufferlib/environments/metta/metta.yaml', render_mode='auto', buf=None, seed=0, - ore_reward=0.17088483842567775, battery_reward=0.9882859711234822, heart_reward=1.0): - '''Metta creation function''' - - OmegaConf.register_new_resolver("div", oc_divide, replace=True) - cfg = OmegaConf.load(config) - - # Update rewards under the new structure: agent.rewards.inventory - inventory_rewards = cfg['game']['agent']['rewards']['inventory'] - inventory_rewards['ore_red'] = float(ore_reward) - inventory_rewards['heart'] = float(heart_reward) - inventory_rewards['battery_red'] = float(battery_reward) - - curriculum = SingleTaskCurriculum('puffer', cfg) - return MettaPuff(curriculum, render_mode=render_mode, buf=buf, seed=seed) - -def oc_divide(a, b): - """ - Divide a by b, returning an int if both inputs are ints and result is a whole number, - otherwise return a float. - """ - result = a / b - # If both inputs are integers and the result is a whole number, return as int - if isinstance(a, int) and isinstance(b, int) and result.is_integer(): - return int(result) - return result - -class MettaPuff(MettaGridEnv): - def __init__(self, curriculum, render_mode='human', buf=None, seed=0): - self.replay_writer = None - #if render_mode == 'auto': - # self.replay_writer = ReplayWriter("metta/") - - super().__init__( - curriculum=curriculum, - render_mode=render_mode, - buf=buf, - replay_writer=self.replay_writer - ) - self.action_space = pufferlib.spaces.joint_space(self.single_action_space, self.num_agents) - self.actions = self.actions.astype(np.int32) - - @property - def single_action_space(self): - return gymnasium.spaces.MultiDiscrete(super().single_action_space.nvec, dtype=np.int32) - - def step(self, actions): - obs, rew, term, trunc, info = super().step(actions) - - if all(term) or all(trunc): - self.reset() - if 'agent_raw' in info: - del info['agent_raw'] - if 'episode_rewards' in info: - info['score'] = info['episode_rewards'] - - else: - info = [] - - return obs, rew, term, trunc, [info] diff --git a/pufferlib/environments/metta/metta.yaml b/pufferlib/environments/metta/metta.yaml deleted file mode 100644 index 6d66806fc..000000000 --- a/pufferlib/environments/metta/metta.yaml +++ /dev/null @@ -1,251 +0,0 @@ -name: "GDY-MettaGrid" - -report_stats_interval: 100 -normalize_rewards: false - -sampling: 0 -desync_episodes: true - -game: - # Required list of inventory items - inventory_item_names: - - ore_red - - ore_blue - - ore_green - - battery_red - - battery_blue - - battery_green - - heart - - armor - - laser - - blueprint - - num_agents: 64 - obs_width: 11 - obs_height: 11 - num_observation_tokens: 200 - max_steps: 1000 - - # Global observation tokens configuration - global_obs: - episode_completion_pct: true - last_action: true - last_reward: true - resource_rewards: false - - # Show recipe inputs in observations for all converters - recipe_details_obs: false - - agent: - default_resource_limit: 10 - resource_limits: - heart: 255 - freeze_duration: 10 - action_failure_penalty: 0.0 - - rewards: - inventory: - heart: 1 - stats: {} - - groups: - agent: - id: 0 - sprite: 0 - props: {} - - objects: - altar: - type_id: 8 - input_resources: - battery_red: 3 - output_resources: - heart: 1 - max_output: 5 - conversion_ticks: 1 - cooldown: 10 - initial_resource_count: 0 - - wall: - type_id: 1 - swappable: false - - block: - type_id: 14 - swappable: true - - mine_red: - type_id: 2 - output_resources: - ore_red: 1 - color: 0 - max_output: 5 - conversion_ticks: 1 - cooldown: 50 - initial_resource_count: 0 - - mine_blue: - type_id: 3 - color: 1 - output_resources: - ore_blue: 1 - max_output: 5 - conversion_ticks: 1 - cooldown: 50 - initial_resource_count: 0 - - mine_green: - type_id: 4 - output_resources: - ore_green: 1 - color: 2 - max_output: 5 - conversion_ticks: 1 - cooldown: 50 - initial_resource_count: 0 - - generator_red: - type_id: 5 - input_resources: - ore_red: 1 - output_resources: - battery_red: 1 - color: 0 - max_output: 5 - conversion_ticks: 1 - cooldown: 25 - initial_resource_count: 0 - - generator_blue: - type_id: 6 - input_resources: - ore_blue: 1 - output_resources: - battery_blue: 1 - color: 1 - max_output: 5 - conversion_ticks: 1 - cooldown: 25 - initial_resource_count: 0 - - generator_green: - type_id: 7 - input_resources: - ore_green: 1 - output_resources: - battery_green: 1 - color: 2 - max_output: 5 - conversion_ticks: 1 - cooldown: 25 - initial_resource_count: 0 - - armory: - type_id: 9 - input_resources: - ore_red: 3 - output_resources: - armor: 1 - max_output: 5 - conversion_ticks: 1 - cooldown: 10 - initial_resource_count: 0 - - lasery: - type_id: 10 - input_resources: - ore_red: 1 - battery_red: 2 - output_resources: - laser: 1 - max_output: 5 - conversion_ticks: 1 - cooldown: 10 - initial_resource_count: 0 - - lab: - type_id: 11 - input_resources: - ore_red: 3 - battery_red: 3 - output_resources: - blueprint: 1 - max_output: 5 - conversion_ticks: 1 - cooldown: 5 - initial_resource_count: 0 - - factory: - type_id: 12 - input_resources: - blueprint: 1 - ore_red: 5 - battery_red: 5 - output_resources: - armor: 5 - laser: 5 - max_output: 5 - conversion_ticks: 1 - cooldown: 5 - initial_resource_count: 0 - - temple: - type_id: 13 - input_resources: - heart: 1 - blueprint: 1 - output_resources: - heart: 5 - max_output: 5 - conversion_ticks: 1 - cooldown: 5 - initial_resource_count: 0 - - actions: - noop: - enabled: true - move: - enabled: true - rotate: - enabled: true - put_items: - enabled: true - get_items: - enabled: true - attack: - enabled: true - consumed_resources: - laser: 1 - defense_resources: - armor: 1 - swap: - enabled: true - change_color: - enabled: false - change_glyph: - enabled: false - number_of_glyphs: 4 - - map_builder: - _target_: metta.mettagrid.room.multi_room.MultiRoom - num_rooms: 1 - border_width: 6 - - room: - _target_: metta.mettagrid.room.random.Random - width: 64 - height: 64 - border_width: 0 - - agents: 64 - - objects: - mine_red: 128 - generator_red: 64 - altar: 32 - armory: 0 - lasery: 0 - lab: 0 - factory: 0 - temple: 0 - wall: 0 diff --git a/pufferlib/environments/metta/torch.py b/pufferlib/environments/metta/torch.py deleted file mode 100644 index 62aa4be3a..000000000 --- a/pufferlib/environments/metta/torch.py +++ /dev/null @@ -1,119 +0,0 @@ -import numpy as np -import einops -import torch -from torch import nn -from torch.nn import functional as F - -import pufferlib.models - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=512, hidden_size=512): - super().__init__(env, policy, input_size, hidden_size) - -class Policy(nn.Module): - def __init__(self, env, cnn_channels=128, hidden_size=512, **kwargs): - super().__init__() - self.hidden_size = hidden_size - self.is_continuous = False - - self.out_width = 11 - self.out_height = 11 - self.num_layers = 22 - - self.network= nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(self.num_layers, cnn_channels, 5, stride=3)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.ReLU(), - nn.Flatten(), - pufferlib.pytorch.layer_init(nn.Linear(cnn_channels, hidden_size//2)), - nn.ReLU(), - ) - - self.self_encoder = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(self.num_layers, hidden_size//2)), - nn.ReLU(), - ) - - #max_vec = torch.tensor([ 1., 9., 1., 30., 1., 3., 255., 26., 1., 1., 1., 1., - # 1., 47., 3., 3., 2., 1., 1., 1., 1., 1.])[None, :, None, None] - max_vec = torch.tensor([9., 1., 1., 10., 3., 254., 1., 1., 235., 8., 9., 250., 29., 1., 1., 8., 1., 1., 6., 3., 1., 2.])[None, :, None, None] - #max_vec = torch.ones(22)[None, :, None, None] - self.register_buffer('max_vec', max_vec) - - action_nvec = env.single_action_space.nvec - self.actor = nn.ModuleList([pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, n), std=0.01) for n in action_nvec]) - - self.value = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - hidden, lookup = self.encode_observations(observations) - actions, value = self.decode_actions(hidden, lookup) - return (actions, value), hidden - - def encode_observations(self, observations, state=None): - - token_observations = observations - B = token_observations.shape[0] - TT = 1 - if token_observations.dim() != 3: # hardcoding for shape [B, M, 3] - TT = token_observations.shape[1] - token_observations = einops.rearrange(token_observations, "b t m c -> (b t) m c") - - assert token_observations.shape[-1] == 3, f"Expected 3 channels per token. Got shape {token_observations.shape}" - token_observations[token_observations == 255] = 0 - - # coords_byte contains x and y coordinates in a single byte (first 4 bits are x, last 4 bits are y) - coords_byte = token_observations[..., 0].to(torch.uint8) - - # Extract x and y coordinate indices (0-15 range, but we need to make them long for indexing) - x_coord_indices = ((coords_byte >> 4) & 0x0F).long() # Shape: [B_TT, M] - y_coord_indices = (coords_byte & 0x0F).long() # Shape: [B_TT, M] - atr_indices = token_observations[..., 1].long() # Shape: [B_TT, M], ready for embedding - atr_values = token_observations[..., 2].float() # Shape: [B_TT, M] - - # In ObservationShaper we permute. Here, we create the observations pre-permuted. - # We'd like to pre-create this as part of initialization, but we don't know the batch size or time steps at - # that point. - box_obs = torch.zeros( - (B * TT, 22, self.out_width, self.out_height), - dtype=atr_values.dtype, - device=token_observations.device, - ) - batch_indices = torch.arange(B * TT, device=token_observations.device).unsqueeze(-1).expand_as(atr_values) - - # Add bounds checking to prevent out-of-bounds access - valid_tokens = coords_byte != 0xFF - valid_tokens = valid_tokens & (x_coord_indices < self.out_width) & (y_coord_indices < self.out_height) - valid_tokens = valid_tokens & (atr_indices < 22) # Also check attribute indices - - box_obs[ - batch_indices[valid_tokens], - atr_indices[valid_tokens], - x_coord_indices[valid_tokens], - y_coord_indices[valid_tokens], - ] = atr_values[valid_tokens] - - observations = box_obs - - #max_vec = box_obs.max(0)[0].max(1)[0].max(1)[0] - #self.max_vec = torch.maximum(self.max_vec, max_vec[None, :, None, None]) - #if (np.random.rand() < 0.001): - # breakpoint() - - features = observations / self.max_vec - #mmax = features.max(0)[0].max(1)[0].max(1)[0] - #self.max_vec = torch.maximum(self.max_vec, mmax[None, :, None, None]) - self_features = self.self_encoder(features[:, :, 5, 5]) - cnn_features = self.network(features) - return torch.cat([self_features, cnn_features], dim=1) - - def decode_actions(self, hidden): - #hidden = self.layer_norm(hidden) - logits = [dec(hidden) for dec in self.actor] - value = self.value(hidden) - return logits, value diff --git a/pufferlib/environments/microrts/__init__.py b/pufferlib/environments/microrts/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/microrts/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/microrts/environment.py b/pufferlib/environments/microrts/environment.py deleted file mode 100644 index 6c00f4d52..000000000 --- a/pufferlib/environments/microrts/environment.py +++ /dev/null @@ -1,50 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import warnings -import shimmy -import functools - -import pufferlib.emulation -import pufferlib.environments - - -def env_creator(name='GlobalAgentCombinedRewardEnv'): - return functools.partial(make, name) - -def make(name, buf=None): - '''Gym MicroRTS creation function - - This library appears broken. Step crashes in Java. - ''' - pufferlib.environments.try_import('gym_microrts') - if name == 'GlobalAgentCombinedRewardEnv': - from gym_microrts.envs import GlobalAgentCombinedRewardEnv - else: - raise ValueError(f'Unknown environment: {name}') - - with pufferlib.utils.Suppress(): - return GlobalAgentCombinedRewardEnv() - - env.reset = pufferlib.utils.silence_warnings(env.reset) - env.step = pufferlib.utils.silence_warnings(env.step) - - env = MicroRTS(env) - env = shimmy.GymV21CompatibilityV0(env=env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class MicroRTS: - def __init__(self, env): - self.env = env - self.observation_space = self.env.observation_space - self.action_space = self.env.action_space - self.render = self.env.render - self.close = self.env.close - self.seed = self.env.seed - - def reset(self): - return self.env.reset().astype(np.int32) - - def step(self, action): - o, r, d, i = self.env.step(action) - return o.astype(np.int32), r, d, i diff --git a/pufferlib/environments/microrts/torch.py b/pufferlib/environments/microrts/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/microrts/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/minerl/__init__.py b/pufferlib/environments/minerl/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/minerl/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/minerl/environment.py b/pufferlib/environments/minerl/environment.py deleted file mode 100644 index 771c3c600..000000000 --- a/pufferlib/environments/minerl/environment.py +++ /dev/null @@ -1,28 +0,0 @@ -from pdb import set_trace as T - -import gym -import shimmy -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.utils - - -def env_creator(name='MineRLBasaltFindCave-v0'): - return functools.partial(make, name=name) - -def make(name, buf=None): - '''Minecraft environment creation function''' - - pufferlib.environments.try_import('minerl') - - # Monkey patch to add .itmes to old gym.spaces.Dict - #gym.spaces.Dict.items = lambda self: self.spaces.items() - - #with pufferlib.utils.Suppress(): - env = gym.make(name) - - env = shimmy.GymV21CompatibilityV0(env=env) - return pufferlib.emulation.GymnasiumPufferEnv(env, buf=buf) diff --git a/pufferlib/environments/minerl/torch.py b/pufferlib/environments/minerl/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/minerl/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/minigrid/__init__.py b/pufferlib/environments/minigrid/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/minigrid/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/minigrid/environment.py b/pufferlib/environments/minigrid/environment.py deleted file mode 100644 index 2bf373b7e..000000000 --- a/pufferlib/environments/minigrid/environment.py +++ /dev/null @@ -1,54 +0,0 @@ -from pdb import set_trace as T - -import gymnasium -import functools - -import pufferlib.emulation -import pufferlib.environments - -ALIASES = { - 'minigrid': 'MiniGrid-LavaGapS7-v0', -} - - -def env_creator(name='minigrid'): - return functools.partial(make, name=name) - -def make(name, render_mode='rgb_array', buf=None, seed=0): - if name in ALIASES: - name = ALIASES[name] - - minigrid = pufferlib.environments.try_import('minigrid') - env = gymnasium.make(name, render_mode=render_mode) - env = MiniGridWrapper(env) - env = pufferlib.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class MiniGridWrapper: - def __init__(self, env): - self.env = env - self.observation_space = gymnasium.spaces.Dict({ - k: v for k, v in self.env.observation_space.items() if - k != 'mission' - }) - self.action_space = self.env.action_space - self.close = self.env.close - self.render = self.env.render - self.close = self.env.close - self.render_mode = 'rgb_array' - - def reset(self, seed=None, options=None): - self.tick = 0 - obs, info = self.env.reset(seed=seed) - del obs['mission'] - return obs, info - - def step(self, action): - obs, reward, done, truncated, info = self.env.step(action) - del obs['mission'] - - self.tick += 1 - if self.tick == 100: - done = True - - return obs, reward, done, truncated, info diff --git a/pufferlib/environments/minigrid/torch.py b/pufferlib/environments/minigrid/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/minigrid/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/minihack/__init__.py b/pufferlib/environments/minihack/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/minihack/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/minihack/environment.py b/pufferlib/environments/minihack/environment.py deleted file mode 100644 index d70ea03d5..000000000 --- a/pufferlib/environments/minihack/environment.py +++ /dev/null @@ -1,62 +0,0 @@ -from pdb import set_trace as T - -import gym -import shimmy -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments - - -EXTRA_OBS_KEYS = [ - 'tty_chars', - 'tty_colors', - 'tty_cursor', -] - -ALIASES = { - 'minihack': 'MiniHack-River-v0', -} - -def env_creator(name='minihack'): - return functools.partial(make, name) - -def make(name, buf=None, seed=0): - '''NetHack binding creation function''' - if name in ALIASES: - name = ALIASES[name] - - import minihack - pufferlib.environments.try_import('minihack') - obs_key = minihack.base.MH_DEFAULT_OBS_KEYS + EXTRA_OBS_KEYS - env = gym.make(name, observation_keys=obs_key) - env = shimmy.GymV21CompatibilityV0(env=env) - env = MinihackWrapper(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class MinihackWrapper: - def __init__(self, env): - self.env = env - self.observation_space = self.env.observation_space - self.action_space = self.env.action_space - self.close = self.env.close - self.close = self.env.close - self.render_mode = 'ansi' - - def reset(self, seed=None): - obs, info = self.env.reset(seed=seed) - self.obs = obs - return obs, info - - def step(self, action): - obs, reward, done, truncated, info = self.env.step(action) - self.obs = obs - return obs, reward, done, truncated, info - - def render(self): - import nle - chars = nle.nethack.tty_render( - self.obs['tty_chars'], self.obs['tty_colors'], self.obs['tty_cursor']) - return chars - diff --git a/pufferlib/environments/minihack/torch.py b/pufferlib/environments/minihack/torch.py deleted file mode 100644 index 7781cb677..000000000 --- a/pufferlib/environments/minihack/torch.py +++ /dev/null @@ -1,8 +0,0 @@ -from pdb import set_trace as T - -import pufferlib.pytorch -from pufferlib.environments.nethack import Policy - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=512, hidden_size=512, num_layers=1): - super().__init__(env, policy, input_size, hidden_size, num_layers) diff --git a/pufferlib/environments/mujoco/__init__.py b/pufferlib/environments/mujoco/__init__.py deleted file mode 100644 index 1c91a2b0f..000000000 --- a/pufferlib/environments/mujoco/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import * - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/mujoco/environment.py b/pufferlib/environments/mujoco/environment.py deleted file mode 100644 index f9adad9f8..000000000 --- a/pufferlib/environments/mujoco/environment.py +++ /dev/null @@ -1,59 +0,0 @@ - -from pdb import set_trace as T - -import functools - -import numpy as np -import gymnasium - -import pufferlib -import pufferlib.emulation -import pufferlib.environments - - -def single_env_creator(env_name, capture_video, gamma, - run_name=None, idx=None, obs_norm=True, pufferl=False, render_mode='rgb_array', buf=None, seed=0): - if capture_video and idx == 0: - assert run_name is not None, "run_name must be specified when capturing videos" - env = gymnasium.make(env_name, render_mode="rgb_array") - env = gymnasium.wrappers.RecordVideo(env, f"videos/{run_name}") - else: - env = gymnasium.make(env_name, render_mode=render_mode) - - env = pufferlib.ClipAction(env) # NOTE: this changed actions space - env = pufferlib.EpisodeStats(env) - - if obs_norm: - env = gymnasium.wrappers.NormalizeObservation(env) - env = gymnasium.wrappers.TransformObservation(env, lambda obs: np.clip(obs, -10, 10), env.observation_space) - - env = gymnasium.wrappers.NormalizeReward(env, gamma=gamma) - env = gymnasium.wrappers.TransformReward(env, lambda reward: np.clip(reward, -10, 10)) - - if pufferl is True: - env = pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - - return env - - -def cleanrl_env_creator(env_name, run_name, capture_video, gamma, idx): - kwargs = { - "env_name": env_name, - "run_name": run_name, - "capture_video": capture_video, - "gamma": gamma, - "idx": idx, - "pufferl": False, - } - return functools.partial(single_env_creator, **kwargs) - - -# Keep it simple for pufferl demo, for now -def env_creator(env_name="HalfCheetah-v4", gamma=0.99): - default_kwargs = { - "env_name": env_name, - "capture_video": False, - "gamma": gamma, - "pufferl": True, - } - return functools.partial(single_env_creator, **default_kwargs) diff --git a/pufferlib/environments/mujoco/torch.py b/pufferlib/environments/mujoco/torch.py deleted file mode 100644 index aca09b55d..000000000 --- a/pufferlib/environments/mujoco/torch.py +++ /dev/null @@ -1,3 +0,0 @@ - -from pufferlib.models import LSTMWrapper as Recurrent -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/nethack/Hack-Regular.ttf b/pufferlib/environments/nethack/Hack-Regular.ttf deleted file mode 100644 index 097db1814..000000000 Binary files a/pufferlib/environments/nethack/Hack-Regular.ttf and /dev/null differ diff --git a/pufferlib/environments/nethack/__init__.py b/pufferlib/environments/nethack/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/nethack/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/nethack/environment.py b/pufferlib/environments/nethack/environment.py deleted file mode 100644 index 3bda89f9b..000000000 --- a/pufferlib/environments/nethack/environment.py +++ /dev/null @@ -1,51 +0,0 @@ -from pdb import set_trace as T - -import shimmy -import gym -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -#from .wrapper import RenderCharImagesWithNumpyWrapper - -def env_creator(name='nethack'): - return functools.partial(make, name) - -def make(name, buf=None, seed=0): - '''NetHack binding creation function''' - if name == 'nethack': - name = 'NetHackScore-v0' - - nle = pufferlib.environments.try_import('nle') - env = gym.make(name) - #env = RenderCharImagesWithNumpyWrapper(env) - env = shimmy.GymV21CompatibilityV0(env=env) - env = NethackWrapper(env) - env = pufferlib.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class NethackWrapper: - def __init__(self, env): - self.env = env - self.observation_space = self.env.observation_space - self.action_space = self.env.action_space - self.close = self.env.close - self.close = self.env.close - self.render_mode = 'ansi' - - def reset(self, seed=None): - obs, info = self.env.reset(seed=seed) - self.obs = obs - return obs, info - - def step(self, action): - obs, reward, done, truncated, info = self.env.step(action) - self.obs = obs - return obs, reward, done, truncated, info - - def render(self): - import nle - chars = nle.nethack.tty_render( - self.obs['tty_chars'], self.obs['tty_colors'], self.obs['tty_cursor']) - return chars diff --git a/pufferlib/environments/nethack/torch.py b/pufferlib/environments/nethack/torch.py deleted file mode 100644 index 4530f06a9..000000000 --- a/pufferlib/environments/nethack/torch.py +++ /dev/null @@ -1,68 +0,0 @@ -from pdb import set_trace as T - -import torch -import torch.nn as nn -import torch.nn.functional as F - -import pufferlib.models -import pufferlib.pytorch -from pufferlib.pytorch import layer_init - - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=256, hidden_size=256, num_layers=1): - super().__init__(env, policy, input_size, hidden_size, num_layers) - -class Policy(nn.Module): - def __init__(self, env): - super().__init__() - self.dtype = pufferlib.pytorch.nativize_dtype(env.emulated) - - self.blstats_net = nn.Sequential( - nn.Embedding(256, 32), - nn.Flatten(), - ) - - self.char_embed = nn.Embedding(256, 32) - self.chars_net = nn.Sequential( - layer_init(nn.Conv2d(32, 32, 5, stride=(2, 3))), - nn.ReLU(), - layer_init(nn.Conv2d(32, 64, 5, stride=(1, 3))), - nn.ReLU(), - layer_init(nn.Conv2d(64, 64, 3, stride=1)), - nn.ReLU(), - nn.Flatten(), - ) - - self.proj = nn.Linear(864+960, 256) - self.actor = layer_init(nn.Linear(256, 8), std=0.01) - self.critic = layer_init(nn.Linear(256, 1), std=1) - - def forward(self, x, state=None): - hidden = self.encode_observations(x) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - hidden = self.encode_observations(x) - actions, value = self.decode_actions(hidden) - return actions, value - - def encode_observations(self, x): - x = x.type(torch.uint8) # Undo bad cleanrl cast - x = pufferlib.pytorch.nativize_tensor(x, self.dtype) - - blstats = torch.clip(x['blstats'] + 1, 0, 255).int() - blstats = self.blstats_net(blstats) - - chars = self.char_embed(x['chars'].int()) - chars = torch.permute(chars, (0, 3, 1, 2)) - chars = self.chars_net(chars) - - concat = torch.cat([blstats, chars], dim=1) - return self.proj(concat) - - def decode_actions(self, hidden): - value = self.critic(hidden) - action = self.actor(hidden) - return action, value diff --git a/pufferlib/environments/nethack/wrapper.py b/pufferlib/environments/nethack/wrapper.py deleted file mode 100644 index 17781a6ac..000000000 --- a/pufferlib/environments/nethack/wrapper.py +++ /dev/null @@ -1,306 +0,0 @@ -"""Taken & adapted from Chaos Dwarf in Nethack Challenge Starter Kit: -https://github.com/Miffyli/nle-sample-factory-baseline - - -MIT License - -Copyright (c) 2021 Anssi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -import os - -import cv2 -import gym -import numpy as np -from numba import njit -from nle import nethack -from PIL import Image -from PIL import ImageDraw -from PIL import ImageFont - -#import render_utils - -SMALL_FONT_PATH = os.path.join(__package__.replace(".", "/"), "Hack-Regular.ttf") - -# Mapping of 0-15 colors used. -# Taken from bottom image here. It seems about right -# https://i.stack.imgur.com/UQVe5.png -COLORS = [ - "#000000", - "#800000", - "#008000", - "#808000", - "#000080", - "#800080", - "#008080", - "#808080", # - flipped these ones around - "#C0C0C0", # | the gray-out dull stuff - "#FF0000", - "#00FF00", - "#FFFF00", - "#0000FF", - "#FF00FF", - "#00FFFF", - "#FFFFFF", -] - - -@njit -def _tile_characters_to_image( - out_image, - chars, - colors, - output_height_chars, - output_width_chars, - char_array, - offset_h, - offset_w, -): - """ - Build an image using cached images of characters in char_array to out_image - """ - char_height = char_array.shape[3] - char_width = char_array.shape[4] - for h in range(output_height_chars): - h_char = h + offset_h - # Stuff outside boundaries is not visible, so - # just leave it black - if h_char < 0 or h_char >= chars.shape[0]: - continue - for w in range(output_width_chars): - w_char = w + offset_w - if w_char < 0 or w_char >= chars.shape[1]: - continue - char = chars[h_char, w_char] - color = colors[h_char, w_char] - h_pixel = h * char_height - w_pixel = w * char_width - out_image[ - :, h_pixel : h_pixel + char_height, w_pixel : w_pixel + char_width - ] = char_array[char, color] - - -def _initialize_char_array(font_size, rescale_font_size): - """Draw all characters in PIL and cache them in numpy arrays - - if rescale_font_size is given, assume it is (width, height) - - Returns a np array of (num_chars, num_colors, char_height, char_width, 3) - """ - font = ImageFont.truetype(SMALL_FONT_PATH, font_size) - dummy_text = "".join( - [(chr(i) if chr(i).isprintable() else " ") for i in range(256)] - ) - _, _, image_width, image_height = font.getbbox(dummy_text) - # Above can not be trusted (or its siblings).... - image_width = int(np.ceil(image_width / 256) * 256) - - char_width = rescale_font_size[0] - char_height = rescale_font_size[1] - - char_array = np.zeros((256, 16, char_height, char_width, 3), dtype=np.uint8) - image = Image.new("RGB", (image_width, image_height)) - image_draw = ImageDraw.Draw(image) - for color_index in range(16): - image_draw.rectangle((0, 0, image_width, image_height), fill=(0, 0, 0)) - image_draw.text((0, 0), dummy_text, fill=COLORS[color_index], spacing=0) - - arr = np.array(image).copy() - arrs = np.array_split(arr, 256, axis=1) - for char_index in range(256): - char = arrs[char_index] - if rescale_font_size: - char = cv2.resize(char, rescale_font_size, interpolation=cv2.INTER_AREA) - char_array[char_index, color_index] = char - return char_array - - -class RenderCharImagesWithNumpyWrapper(gym.Wrapper): - """ - Render characters as images, using PIL to render characters like we humans see on screen - but then some caching and numpy stuff to speed up things. - - To speed things up, crop image around the player. - """ - - def __init__( - self, - env, - font_size=9, - crop_size=12, - rescale_font_size=(6, 6), - blstats_cursor=False, - ): - super().__init__(env) - self.char_array = _initialize_char_array(font_size, rescale_font_size) - self.char_height = self.char_array.shape[2] - self.char_width = self.char_array.shape[3] - # Transpose for CHW - self.char_array = self.char_array.transpose(0, 1, 4, 2, 3) - - self.crop_size = crop_size - self.blstats_cursor = blstats_cursor - - self.half_crop_size = crop_size // 2 - self.output_height_chars = crop_size - self.output_width_chars = crop_size - self.chw_image_shape = ( - 3, - self.output_height_chars * self.char_height, - self.output_width_chars * self.char_width, - ) - - self.observation_space = gym.spaces.Box( - low=0, high=255, shape=self.chw_image_shape, dtype=np.uint8 - ) - - ''' - obs_spaces = { - "screen_image": gym.spaces.Box( - low=0, high=255, shape=self.chw_image_shape, dtype=np.uint8 - ) - } - obs_spaces.update( - [ - (k, self.env.observation_space[k]) - for k in self.env.observation_space - if k not in ["tty_chars", "tty_colors"] - ] - ) - self.observation_space = gym.spaces.Dict(obs_spaces) - ''' - - self.render_mode = 'rgb_array' - - def _render_text_to_image(self, obs): - chars = obs["tty_chars"] - colors = obs["tty_colors"] - offset_w = 0 - offset_h = 0 - if self.crop_size: - # Center around player - if self.blstats_cursor: - center_x, center_y = obs["blstats"][:2] - else: - center_y, center_x = obs["tty_cursor"] - offset_h = center_y - self.half_crop_size - offset_w = center_x - self.half_crop_size - - out_image = np.zeros(self.chw_image_shape, dtype=np.uint8) - - _tile_characters_to_image( - out_image=out_image, - chars=chars, - colors=colors, - output_height_chars=self.output_height_chars, - output_width_chars=self.output_width_chars, - char_array=self.char_array, - offset_h=offset_h, - offset_w=offset_w, - ) - - return out_image - obs["screen_image"] = out_image - del obs["tty_chars"] - del obs["tty_colors"] - return obs - - def step(self, action): - obs, reward, done, info = self.env.step(action) - self.obs = obs - obs = self._render_text_to_image(obs) - return obs, reward, done, info - - def reset(self): - obs = self.env.reset() - self.obs = obs - obs = self._render_text_to_image(obs) - return obs - - def render(self, mode='rgb_array'): - return self.obs - - -class RenderCharImagesWithNumpyWrapperV2(gym.Wrapper): - """ - Same as V1, but simpler and faster. - """ - - def __init__( - self, - env, - font_size=9, - crop_size=12, - rescale_font_size=(6, 6), - ): - super().__init__(env) - self.char_array = _initialize_char_array(font_size, rescale_font_size) - self.char_height = self.char_array.shape[2] - self.char_width = self.char_array.shape[3] - # Transpose for CHW - self.char_array = self.char_array.transpose(0, 1, 4, 2, 3) - self.char_array = np.ascontiguousarray(self.char_array) - self.crop_size = crop_size - - crop_rows = crop_size or nethack.nethack.TERMINAL_SHAPE[0] - crop_cols = crop_size or nethack.nethack.TERMINAL_SHAPE[1] - - self.chw_image_shape = ( - 3, - crop_rows * self.char_height, - crop_cols * self.char_width, - ) - - obs_spaces = { - "screen_image": gym.spaces.Box( - low=0, high=255, shape=self.chw_image_shape, dtype=np.uint8 - ) - } - obs_spaces.update( - [ - (k, self.env.observation_space[k]) - for k in self.env.observation_space - if k not in ["tty_chars", "tty_colors"] - ] - ) - self.observation_space = gym.spaces.Dict(obs_spaces) - - def _populate_obs(self, obs): - screen = np.zeros(self.chw_image_shape, order="C", dtype=np.uint8) - render_utils.render_crop( - obs["tty_chars"], - obs["tty_colors"], - obs["tty_cursor"], - self.char_array, - screen, - crop_size=self.crop_size, - ) - obs["screen_image"] = screen - - def step(self, action): - obs, reward, done, info = self.env.step(action) - self._populate_obs(obs) - return obs, reward, done, info - - def reset(self): - obs = self.env.reset() - self._populate_obs(obs) - return obs diff --git a/pufferlib/environments/nmmo/__init__.py b/pufferlib/environments/nmmo/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/nmmo/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/nmmo/environment.py b/pufferlib/environments/nmmo/environment.py deleted file mode 100644 index f4ef1c998..000000000 --- a/pufferlib/environments/nmmo/environment.py +++ /dev/null @@ -1,77 +0,0 @@ -from pdb import set_trace as T -import numpy as np -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.wrappers -import pufferlib.postprocess - - -def env_creator(name='nmmo'): - return functools.partial(make, name) - -def make(name, *args, buf=None, **kwargs): - '''Neural MMO creation function''' - nmmo = pufferlib.environments.try_import('nmmo') - env = nmmo.Env(*args, **kwargs) - env = NMMOWrapper(env) - env = pufferlib.postprocess.MultiagentEpisodeStats(env) - env = pufferlib.postprocess.MeanOverAgents(env) - return pufferlib.emulation.PettingZooPufferEnv(env=env, buf=buf) - -class NMMOWrapper(pufferlib.postprocess.PettingZooWrapper): - '''Remove task spam''' - @property - def render_mode(self): - return 'rgb_array' - - def render(self): - '''Quick little renderer for NMMO''' - tiles = self.env.tile_map[:, :, 2].astype(np.uint8) - render = np.zeros((tiles.shape[0], tiles.shape[1], 3), dtype=np.uint8) - BROWN = (136, 69, 19) - render[tiles == 1] = (0, 0, 255) - render[tiles == 2] = (0, 255, 0) - render[tiles == 3] = BROWN - render[tiles == 4] = (64, 255, 64) - render[tiles == 5] = (128, 128, 128) - render[tiles == 6] = BROWN - render[tiles == 7] = (255, 128, 128) - render[tiles == 8] = BROWN - render[tiles == 9] = (128, 255, 128) - render[tiles == 10] = BROWN - render[tiles == 11] = (128, 128, 255) - render[tiles == 12] = BROWN - render[tiles == 13] = (192, 255, 192) - render[tiles == 14] = (0, 0, 255) - render[tiles == 15] = (64, 64, 255) - - for agent in self.env.realm.players.values(): - agent_r = agent.row.val - agent_c = agent.col.val - render[agent_r, agent_c, :] = (255, 255, 0) - - for npc in self.env.realm.npcs.values(): - agent_r = npc.row.val - agent_c = npc.col.val - render[agent_r, agent_c, :] = (255, 0, 0) - - return render - - def reset(self, seed=None): - obs, infos = self.env.reset(seed=seed) - self.obs = obs - return obs, infos - - def step(self, actions): - obs, rewards, dones, truncateds, infos = self.env.step(actions) - infos = {k: list(v['task'].values())[0] for k, v in infos.items()} - self.obs = obs - return obs, rewards, dones, truncateds, infos - - def close(self): - return self.env.close() - - diff --git a/pufferlib/environments/nmmo/torch.py b/pufferlib/environments/nmmo/torch.py deleted file mode 100644 index e98134f3a..000000000 --- a/pufferlib/environments/nmmo/torch.py +++ /dev/null @@ -1,96 +0,0 @@ -from pdb import set_trace as T - -import torch -import torch.nn.functional as F - -import pufferlib -import pufferlib.emulation -import pufferlib.models -import pufferlib.pytorch -from pufferlib.environments import try_import - -try_import("nmmo") -from nmmo.entity.entity import EntityState - - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=256, hidden_size=256, num_layers=1): - super().__init__(env, policy, input_size, hidden_size, num_layers) - -class Policy(torch.nn.Module): - NUM_ATTRS = 34 - EntityId = EntityState.State.attr_name_to_col["id"] - tile_offset = torch.tensor([i*256 for i in range(3)]) - entity_offset = torch.tensor([i*256 for i in range(3, 34)]) - - def __init__(self, env, input_size=256, hidden_size=256, output_size=256): - super().__init__() - self.dtype = pufferlib.pytorch.nativize_dtype(env.emulated) - - # A dumb example encoder that applies a linear layer to agent self features - self.embedding = torch.nn.Embedding(self.NUM_ATTRS*256, 32) - self.tile_conv_1 = torch.nn.Conv2d(96, 32, 3) - self.tile_conv_2 = torch.nn.Conv2d(32, 8, 3) - self.tile_fc = torch.nn.Linear(8*11*11, input_size) - - self.entity_fc = torch.nn.Linear(31*32, input_size) - - self.proj_fc = torch.nn.Linear(2*input_size, input_size) - - self.decoders = torch.nn.ModuleList([torch.nn.Linear(hidden_size, n) - for n in env.single_action_space.nvec]) - self.value_head = torch.nn.Linear(hidden_size, 1) - - def forward(self, x): - hidden, lookup = self.encode_observations(x) - actions, value = self.decode_actions(hidden, lookup) - return actions, value - - def encode_observations(self, env_outputs): - env_outputs = pufferlib.pytorch.nativize_tensor(env_outputs, self.dtype) - - tile = env_outputs['Tile'] - # Center on player - # This is cursed without clone?? - tile[:, :, :2] -= tile[:, 112:113, :2].clone() - tile[:, :, :2] += 7 - tile = self.embedding( - tile.long().clip(0, 255) + self.tile_offset.to(tile.device) - ) - - agents, tiles, features, embed = tile.shape - tile = tile.view(agents, tiles, features*embed).transpose(1, 2).view(agents, features*embed, 15, 15) - - tile = self.tile_conv_1(tile) - tile = F.relu(tile) - tile = self.tile_conv_2(tile) - tile = F.relu(tile) - tile = tile.contiguous().view(agents, -1) - tile = self.tile_fc(tile) - tile = F.relu(tile) - - # Pull out rows corresponding to the agent - agentEmb = env_outputs["Entity"] - my_id = env_outputs["AgentId"][:,0] - entity_ids = agentEmb[:,:,self.EntityId] - mask = (entity_ids == my_id.unsqueeze(1)) & (entity_ids != 0) - mask = mask.int() - row_indices = torch.where(mask.any(dim=1), mask.argmax(dim=1), torch.zeros_like(mask.sum(dim=1))) - entity = agentEmb[torch.arange(agentEmb.shape[0]), row_indices] - - entity = self.embedding( - entity.long().clip(0, 255) + self.entity_offset.to(entity.device) - ) - agents, attrs, embed = entity.shape - entity = entity.view(agents, attrs*embed) - - entity = self.entity_fc(entity) - entity = F.relu(entity) - - obs = torch.cat([tile, entity], dim=-1) - return self.proj_fc(obs), None - - def decode_actions(self, hidden, lookup, concat=True): - value = self.value_head(hidden) - actions = [dec(hidden) for dec in self.decoders] - return actions, value diff --git a/pufferlib/environments/open_spiel/__init__.py b/pufferlib/environments/open_spiel/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/open_spiel/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/open_spiel/environment.py b/pufferlib/environments/open_spiel/environment.py deleted file mode 100644 index a5679b759..000000000 --- a/pufferlib/environments/open_spiel/environment.py +++ /dev/null @@ -1,56 +0,0 @@ -from pdb import set_trace as T -import numpy as np -import functools - -import pufferlib -from pufferlib import namespace -import pufferlib.emulation -import pufferlib.environments - - -def env_creator(name='connect_four'): - '''OpenSpiel creation function''' - return functools.partial(make, name) - -def make( - name, - multiplayer=False, - n_rollouts=5, - max_simulations=10, - min_simulations=None, - buf=None - ): - '''OpenSpiel creation function''' - pyspiel = pufferlib.environments.try_import('pyspiel', 'open_spiel') - env = pyspiel.load_game(name) - - if min_simulations is None: - min_simulations = max_simulations - - from pufferlib.environments.open_spiel.gymnasium_environment import ( - OpenSpielGymnasiumEnvironment - ) - from pufferlib.environments.open_spiel.pettingzoo_environment import ( - OpenSpielPettingZooEnvironment - ) - - kwargs = dict( - env=env, - n_rollouts=int(n_rollouts), - min_simulations=int(min_simulations), - max_simulations=int(max_simulations), - ) - - if multiplayer: - env = OpenSpielPettingZooEnvironment(**kwargs) - wrapper_cls = pufferlib.emulation.PettingZooPufferEnv - else: - env = OpenSpielGymnasiumEnvironment(**kwargs) - wrapper_cls = pufferlib.emulation.GymnasiumPufferEnv - - return wrapper_cls( - env=env, - postprocessor_cls=pufferlib.emulation.BasicPostprocessor, - buf=buf, - ) - diff --git a/pufferlib/environments/open_spiel/gymnasium_environment.py b/pufferlib/environments/open_spiel/gymnasium_environment.py deleted file mode 100644 index ae5bfba59..000000000 --- a/pufferlib/environments/open_spiel/gymnasium_environment.py +++ /dev/null @@ -1,88 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -from open_spiel.python.algorithms import mcts - -import pufferlib -from pufferlib import namespace -from pufferlib.environments.open_spiel.utils import ( - solve_chance_nodes, - get_obs_and_infos, - observation_space, - action_space, - init, - render, - close, -) - - -def create_bots(state, seed): - assert seed is not None, 'seed must be set' - rnd_state = np.random.RandomState(seed) - - evaluator = mcts.RandomRolloutEvaluator( - n_rollouts=state.n_rollouts, - random_state=rnd_state - ) - - return [mcts.MCTSBot( - game=state.env, - uct_c=2, - max_simulations=a, - evaluator=evaluator, - random_state=rnd_state, - child_selection_fn=mcts.SearchNode.puct_value, - solve=True, - ) for a in range(state.min_simulations, state.max_simulations + 1)] - -def reset(state, seed = None, options = None): - state.state = state.env.new_initial_state() - - if not state.has_reset: - state.has_reset = True - state.seed_value = seed - np.random.seed(seed) - state.all_bots = create_bots(state, seed) - - state.bot = np.random.choice(state.all_bots) - - if np.random.rand() < 0.5: - bot_atn = state.bot.step(state.state) - state.state.apply_action(bot_atn) - - obs, infos = get_obs_and_infos(state) - player = state.state.current_player() - return obs[player], infos[player] - -def step(state, action): - player = state.state.current_player() - solve_chance_nodes(state) - state.state.apply_action(action) - - # Take other move with a bot - if not state.state.is_terminal(): - bot_atn = state.bot.step(state.state) - solve_chance_nodes(state) - state.state.apply_action(bot_atn) - - # Now that we have applied all actions, get the next obs. - obs, all_infos = get_obs_and_infos(state) - reward = state.state.returns()[player] - info = all_infos[player] - - # Are we done? - terminated = state.state.is_terminal() - if terminated: - key = f'win_mcts_{state.bot.max_simulations}' - info[key] = int(reward==1) - - return obs[player], reward, terminated, False, info - -class OpenSpielGymnasiumEnvironment: - __init__ = init - step = step - reset = reset - observation_space = property(observation_space) - action_space = property(action_space) - render = render - close = close diff --git a/pufferlib/environments/open_spiel/pettingzoo_environment.py b/pufferlib/environments/open_spiel/pettingzoo_environment.py deleted file mode 100644 index cbfca0af4..000000000 --- a/pufferlib/environments/open_spiel/pettingzoo_environment.py +++ /dev/null @@ -1,68 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import pufferlib -from pufferlib import namespace - -from pufferlib.environments.open_spiel.utils import ( - solve_chance_nodes, - get_obs_and_infos, - observation_space, - action_space, - init, - render, - close, -) - -def agents(state): - return state.agents - -def possible_agents(state): - return list(range(state.env.num_players())) - -def pz_observation_space(state, agent): - return observation_space(state) - -def pz_action_space(state, agent): - return action_space(state) - -def reset(state, seed = None, options = None): - state.state = state.env.new_initial_state() - obs, infos = get_obs_and_infos(state) - state.agents = state.possible_agents - - if not state.has_reset: - state.has_reset = True - state.seed_value = seed - np.random.seed(seed) - - return obs, infos - -def step(state, actions): - curr_player = state.state.current_player() - solve_chance_nodes(state) - state.state.apply_action(actions[curr_player]) - obs, infos = get_obs_and_infos(state) - rewards = {ag: r for ag, r in enumerate(state.state.returns())} - - # Are we done? - is_terminated = state.state.is_terminal() - terminateds = {a: False for a in obs} - truncateds = {a: False for a in obs} - - if is_terminated: - terminateds = {a: True for a in state.possible_agents} - state.agents = [] - - return obs, rewards, terminateds, truncateds, infos - -class OpenSpielPettingZooEnvironment: - __init__ = init - step = step - reset = reset - agents = lambda state: state.agents - possible_agents = property(possible_agents) - observation_space = pz_observation_space - action_space = pz_action_space - render = render - close = close diff --git a/pufferlib/environments/open_spiel/torch.py b/pufferlib/environments/open_spiel/torch.py deleted file mode 100644 index 4d1845b6b..000000000 --- a/pufferlib/environments/open_spiel/torch.py +++ /dev/null @@ -1,44 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import torch -from torch import nn - -import pufferlib.emulation -from pufferlib.models import Policy as Base - -class Policy(Base): - def __init__(self, env, input_size=128, hidden_size=128): - '''Default PyTorch policy, meant for debugging. - This should run with any environment but is unlikely to learn anything. - - Uses a single linear layer + relu to encode observations and a list of - linear layers to decode actions. The value function is a single linear layer. - ''' - super().__init__(env) - - self.flat_observation_space = env.flat_observation_space - self.flat_observation_structure = env.flat_observation_structure - - self.encoder = nn.Linear(np.prod( - env.structured_observation_space['obs'].shape), hidden_size) - self.decoder = nn.Linear(hidden_size, self.action_space.n) - - self.value_head = nn.Linear(hidden_size, 1) - - def encode_observations(self, observations): - '''Linear encoder function''' - observations = pufferlib.emulation.unpack_batched_obs(observations, - self.flat_observation_space, self.flat_observation_structure) - obs = observations['obs'].view(observations['obs'].shape[0], -1) - self.action_mask = observations['action_mask'] - - hidden = torch.relu(self.encoder(obs)) - return hidden, None - - def decode_actions(self, hidden, lookup, concat=True): - '''Concatenated linear decoder function''' - value = self.value_head(hidden) - action = self.decoder(hidden) - action = action.masked_fill(self.action_mask == 0, -1e9) - return action, value \ No newline at end of file diff --git a/pufferlib/environments/open_spiel/utils.py b/pufferlib/environments/open_spiel/utils.py deleted file mode 100644 index 8eab105c2..000000000 --- a/pufferlib/environments/open_spiel/utils.py +++ /dev/null @@ -1,99 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import gymnasium - -from pufferlib import namespace - - -def init(self, - env, - n_rollouts, - min_simulations, - max_simulations - ): - #state.num_agents = state.env.num_players() - return namespace(self, - env=env, - type=env.get_type(), - n_rollouts=n_rollouts, - min_simulations=min_simulations, - max_simulations=max_simulations, - state=None, - agents=[], - has_reset=False, - ) - -def observation_space(state): - return gymnasium.spaces.Dict({ - 'obs': gymnasium.spaces.Box( - low=0.0, - high=1.0, - shape=(state.env.observation_tensor_size(),), - dtype=np.float32, - ), - 'action_mask': gymnasium.spaces.Box( - low=0, - high=1, - shape=(action_space(state).n,), - dtype=np.int8 - ) - }) - -def action_space(state): - return gymnasium.spaces.Discrete( - state.env.num_distinct_actions()) - -def render(state, mode=None) -> None: - if mode == "human": - print(state.state) - -def close(state): - pass - -def act(state, action): - solve_chance_nodes(state) - state.state.apply_action(action) - -def get_obs_and_infos(state): - # Before calculating an observation, there could be chance nodes - # (that may have an effect on the actual observations). - # E.g. After reset, figure out initial (random) positions of the - # agents. - solve_chance_nodes(state) - - if state.state.is_terminal(): - return ( - state.last_obs, - {player: {} for player in range(state.env.num_players())}, - ) - - # Sequential game: - curr_player = state.state.current_player() - mask = state.state.legal_actions(curr_player) - np_mask = np.zeros(action_space(state).n) - np_mask[mask] = 1 - - state.last_obs = {player: { - 'obs': np.reshape(state.state.observation_tensor(), - [-1]).astype(np.float32), - 'action_mask': np_mask.astype(np.int8), - } for player in range(state.env.num_players())} - - state.last_info = {curr_player: {}} - - return ( - {curr_player: state.last_obs[curr_player]}, - state.last_info, - ) - -def solve_chance_nodes(state): - # Before applying action(s), there could be chance nodes. - # E.g. if env has to figure out, which agent's action should get - # resolved first in a simultaneous node. - # Chance node(s): Sample a (non-player) action and apply. - while state.state.is_chance_node(): - assert state.state.current_player() == -1 - actions, probs = zip(*state.state.chance_outcomes()) - action = np.random.choice(actions, p=probs) - state.state.apply_action(action) diff --git a/pufferlib/environments/pokemon_red/__init__.py b/pufferlib/environments/pokemon_red/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/pokemon_red/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/pokemon_red/environment.py b/pufferlib/environments/pokemon_red/environment.py deleted file mode 100644 index fe0bd85e7..000000000 --- a/pufferlib/environments/pokemon_red/environment.py +++ /dev/null @@ -1,31 +0,0 @@ -from pdb import set_trace as T - -import gymnasium -import functools - -from pokegym import Environment - -import pufferlib.emulation -import pufferlib.postprocess - - -def env_creator(name='pokemon_red'): - return functools.partial(make, name) - -def make(name, headless: bool = True, state_path=None, buf=None, seed=0): - '''Pokemon Red''' - env = Environment(headless=headless, state_path=state_path) - env = RenderWrapper(env) - env = pufferlib.postprocess.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf, seed=seed) - -class RenderWrapper(gymnasium.Wrapper): - def __init__(self, env): - self.env = env - - @property - def render_mode(self): - return 'rgb_array' - - def render(self): - return self.env.screen.screen_ndarray() diff --git a/pufferlib/environments/pokemon_red/torch.py b/pufferlib/environments/pokemon_red/torch.py deleted file mode 100644 index 370b023bf..000000000 --- a/pufferlib/environments/pokemon_red/torch.py +++ /dev/null @@ -1,36 +0,0 @@ -from functools import partial -import torch - -import pufferlib.models - - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, - input_size=512, hidden_size=512, num_layers=1): - super().__init__(env, policy, - input_size, hidden_size, num_layers) - -class Policy(pufferlib.models.Convolutional): - def __init__(self, env, - input_size=512, hidden_size=512, output_size=512, - framestack=4, flat_size=64*5*6): - super().__init__( - env=env, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - framestack=framestack, - flat_size=flat_size, - channels_last=True, - ) - - -''' -class Policy(pufferlib.models.ProcgenResnet): - def __init__(self, env, cnn_width=16, mlp_width=512): - super().__init__( - env=env, - cnn_width=cnn_width, - mlp_width=mlp_width, - ) -''' diff --git a/pufferlib/environments/procgen/__init__.py b/pufferlib/environments/procgen/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/procgen/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/procgen/environment.py b/pufferlib/environments/procgen/environment.py deleted file mode 100644 index 3d889b73f..000000000 --- a/pufferlib/environments/procgen/environment.py +++ /dev/null @@ -1,74 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import gym -import gymnasium -import shimmy -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments - -from stable_baselines3.common.atari_wrappers import ( - MaxAndSkipEnv, -) - -def env_creator(name='bigfish'): - return functools.partial(make, name) - -def make(name, num_envs=1, num_levels=0, start_level=0, - distribution_mode='easy', render_mode=None, buf=None, seed=0): - '''Atari creation function with default CleanRL preprocessing based on Stable Baselines3 wrappers''' - assert int(num_envs) == float(num_envs), "num_envs must be an integer" - num_envs = int(num_envs) - - procgen = pufferlib.environments.try_import('procgen') - envs = procgen.ProcgenEnv( - env_name=name, - num_envs=num_envs, - num_levels=num_levels, - start_level=start_level, - distribution_mode=distribution_mode, - render_mode=render_mode, - ) - envs = gym.wrappers.TransformObservation(envs, lambda obs: obs["rgb"]) - envs.single_action_space = envs.action_space - envs.single_observation_space = envs.observation_space["rgb"] - envs.is_vector_env = True - envs = gym.wrappers.RecordEpisodeStatistics(envs) - envs = gym.wrappers.NormalizeReward(envs) - envs = gym.wrappers.TransformReward(envs, lambda reward: np.clip(reward, -10, 10)) - assert isinstance(envs.single_action_space, gym.spaces.Discrete), "only discrete action space is supported" - envs = ProcgenWrapper(envs) - envs = shimmy.GymV21CompatibilityV0(env=envs, render_mode=render_mode) - #envs = gymnasium.wrappers.GrayScaleObservation(envs) - #envs = gymnasium.wrappers.FrameStack(envs, 4)#, framestack) - #envs = MaxAndSkipEnv(envs, skip=2) - envs = pufferlib.EpisodeStats(envs) - return pufferlib.emulation.GymnasiumPufferEnv(env=envs, buf=buf) - -class ProcgenWrapper: - def __init__(self, env): - self.env = env - self.observation_space = self.env.observation_space['rgb'] - self.action_space = self.env.action_space - - @property - def render_mode(self): - return 'rgb_array' - - def reset(self, seed=None): - obs = self.env.reset()[0] - return obs - - def render(self, mode=None): - return self.env.env.env.env.env.env.get_info()[0]['rgb'] - - def close(self): - return self.env.close() - - def step(self, actions): - actions = np.asarray(actions).reshape(1) - obs, rewards, dones, infos = self.env.step(actions) - return obs[0], rewards[0], dones[0], infos[0] diff --git a/pufferlib/environments/procgen/torch.py b/pufferlib/environments/procgen/torch.py deleted file mode 100644 index 924926c34..000000000 --- a/pufferlib/environments/procgen/torch.py +++ /dev/null @@ -1,50 +0,0 @@ -from pdb import set_trace as T -from torch import nn -import pufferlib.models - -# This policy ended up being useful broadly -# so I included it in the defaults - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=256, hidden_size=256, num_layers=1): - super().__init__(env, policy, input_size, hidden_size, num_layers) - -class Policy (nn.Module): - def __init__(self, env, *args, input_size=256, hidden_size=256, - output_size=256, **kwargs): - '''The CleanRL default NatureCNN policy used for Atari. - It's just a stack of three convolutions followed by a linear layer - - Takes framestack as a mandatory keyword argument. Suggested default is 1 frame - with LSTM or 4 frames without.''' - super().__init__() - - self.network= nn.Sequential( - pufferlib.pytorch.layer_init(nn.Conv2d(3, 16, 8, stride=4)), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Conv2d(16, 32, 4, stride=2)), - nn.ReLU(), - nn.Flatten(), - pufferlib.pytorch.layer_init(nn.Linear(1152, hidden_size)), - nn.ReLU(), - ) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(output_size, 1), std=1) - - def forward(self, observations): - hidden, lookup = self.encode_observations(observations) - actions, value = self.decode_actions(hidden, lookup) - return actions, value - - def encode_observations(self, observations): - observations = observations.permute(0, 3, 1, 2) - return self.network(observations.float() / 255.0), None - - def decode_actions(self, flat_hidden, lookup, concat=None): - action = self.actor(flat_hidden) - value = self.value_fn(flat_hidden) - return action, value - -Policy = pufferlib.models.ProcgenResnet diff --git a/pufferlib/environments/slimevolley/__init__.py b/pufferlib/environments/slimevolley/__init__.py deleted file mode 100644 index 59cda9e7c..000000000 --- a/pufferlib/environments/slimevolley/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/slimevolley/environment.py b/pufferlib/environments/slimevolley/environment.py deleted file mode 100644 index a46d79562..000000000 --- a/pufferlib/environments/slimevolley/environment.py +++ /dev/null @@ -1,70 +0,0 @@ -from pdb import set_trace as T -import numpy as np -import functools - -import gym -import shimmy - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.utils -import pufferlib.postprocess - - -def env_creator(name='SlimeVolley-v0'): - return functools.partial(make, name) - -def make(name, render_mode='rgb_array', buf=None): - if name == 'slimevolley': - name = 'SlimeVolley-v0' - - from slimevolleygym import SlimeVolleyEnv - SlimeVolleyEnv.atari_mode = True - env = SlimeVolleyEnv() - env.policy.predict = lambda obs: np.random.randint(0, 2, 3) - env = SlimeVolleyMultiDiscrete(env) - env = SkipWrapper(env, repeat_count=4) - env = shimmy.GymV21CompatibilityV0(env=env) - env = pufferlib.postprocess.EpisodeStats(env) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class SlimeVolleyMultiDiscrete(gym.Wrapper): - def __init__(self, env): - super().__init__(env) - #self.action_space = gym.spaces.MultiDiscrete( - # [2 for _ in range(env.action_space.n)]) - - def reset(self, seed=None): - return self.env.reset().astype(np.float32) - - def step(self, action): - obs, reward, done, info = self.env.step(action) - return obs.astype(np.float32), reward, done, info - -class SkipWrapper(gym.Wrapper): - """ - Generic common frame skipping wrapper - Will perform action for `x` additional steps - """ - def __init__(self, env, repeat_count): - super(SkipWrapper, self).__init__(env) - self.repeat_count = repeat_count - self.stepcount = 0 - - def step(self, action): - done = False - total_reward = 0 - current_step = 0 - while current_step < (self.repeat_count + 1) and not done: - self.stepcount += 1 - obs, reward, done, info = self.env.step(action) - total_reward += reward - current_step += 1 - - return obs, total_reward, done, info - - def reset(self): - self.stepcount = 0 - return self.env.reset() - diff --git a/pufferlib/environments/slimevolley/torch.py b/pufferlib/environments/slimevolley/torch.py deleted file mode 100644 index e5188f3b7..000000000 --- a/pufferlib/environments/slimevolley/torch.py +++ /dev/null @@ -1,4 +0,0 @@ -import pufferlib.models - -Recurrent = pufferlib.models.LSTMWrapper -Policy = pufferlib.models.Default diff --git a/pufferlib/environments/smac/__init__.py b/pufferlib/environments/smac/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/smac/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/smac/environment.py b/pufferlib/environments/smac/environment.py deleted file mode 100644 index e104e860b..000000000 --- a/pufferlib/environments/smac/environment.py +++ /dev/null @@ -1,23 +0,0 @@ -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.wrappers - - -def env_creator(name='smac'): - return functools.partial(make, name) - -def make(name, buf=None): - '''Starcraft Multiagent Challenge creation function - - Support for SMAC is WIP because environments do not function without - an action-masked baseline policy.''' - pufferlib.environments.try_import('smac') - from smac.env.pettingzoo.StarCraft2PZEnv import _parallel_env as smac_env - - env = smac_env(1000) - env = pufferlib.wrappers.PettingZooTruncatedWrapper(env) - env = pufferlib.emulation.PettingZooPufferEnv(env, buf=buf) - return env diff --git a/pufferlib/environments/smac/torch.py b/pufferlib/environments/smac/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/smac/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/stable_retro/__init__.py b/pufferlib/environments/stable_retro/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/stable_retro/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/stable_retro/environment.py b/pufferlib/environments/stable_retro/environment.py deleted file mode 100644 index 092708613..000000000 --- a/pufferlib/environments/stable_retro/environment.py +++ /dev/null @@ -1,72 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import gymnasium as gym -import functools - -import pufferlib -import pufferlib.emulation -import pufferlib.environments - - -def env_creator(name='Airstriker-Genesis'): - return functools.partial(make, name) - -def make(name='Airstriker-Genesis', framestack=4, buf=None): - '''Atari creation function with default CleanRL preprocessing based on Stable Baselines3 wrappers''' - retro = pufferlib.environments.try_import('retro', 'stable-retro') - - from stable_baselines3.common.atari_wrappers import ( - ClipRewardEnv, - EpisodicLifeEnv, - FireResetEnv, - MaxAndSkipEnv, - ) - with pufferlib.utils.Suppress(): - env = retro.make(name) - - env = gym.wrappers.RecordEpisodeStatistics(env) - env = MaxAndSkipEnv(env, skip=4) - env = ClipRewardEnv(env) - env = gym.wrappers.ResizeObservation(env, (84, 84)) - env = gym.wrappers.GrayScaleObservation(env) - env = gym.wrappers.FrameStack(env, framestack) - return pufferlib.emulation.GymnasiumPufferEnv( - env=env, postprocessor_cls=AtariFeaturizer, buf=buf) - -class AtariFeaturizer(pufferlib.emulation.Postprocessor): - def reset(self, obs): - self.epoch_return = 0 - self.epoch_length = 0 - self.done = False - - #@property - #def observation_space(self): - # return gym.spaces.Box(0, 255, (1, 84, 84), dtype=np.uint8) - - def observation(self, obs): - return np.array(obs) - return np.array(obs[1], dtype=np.float32) - - def reward_done_truncated_info(self, reward, done, truncated, info): - return reward, done, truncated, info - if 'lives' in info: - if info['lives'] == 0 and done: - info['return'] = info['episode']['r'] - info['length'] = info['episode']['l'] - info['time'] = info['episode']['t'] - return reward, True, info - return reward, False, info - - if self.done: - return reward, done, info - - if done: - info['return'] = self.epoch_return - info['length'] = self.epoch_length - self.done = True - else: - self.epoch_length += 1 - self.epoch_return += reward - - return reward, done, info diff --git a/pufferlib/environments/stable_retro/torch.py b/pufferlib/environments/stable_retro/torch.py deleted file mode 100644 index 0ab602183..000000000 --- a/pufferlib/environments/stable_retro/torch.py +++ /dev/null @@ -1,19 +0,0 @@ -import pufferlib.models - - -class Recurrent: - input_size = 512 - hidden_size = 512 - num_layers = 1 - -class Policy(pufferlib.models.Convolutional): - def __init__(self, env, input_size=512, hidden_size=512, output_size=512, - framestack=4, flat_size=64*7*7): - super().__init__( - env=env, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - framestack=framestack, - flat_size=flat_size, - ) diff --git a/pufferlib/environments/test/__init__.py b/pufferlib/environments/test/__init__.py deleted file mode 100644 index 9d3bfe421..000000000 --- a/pufferlib/environments/test/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -from .environment import ( - GymnasiumPerformanceEnv, - PettingZooPerformanceEnv, - GymnasiumTestEnv, - PettingZooTestEnv, - make_all_mock_environments, - MOCK_OBSERVATION_SPACES, - MOCK_ACTION_SPACES, -) - -from .mock_environments import MOCK_SINGLE_AGENT_ENVIRONMENTS -from .mock_environments import MOCK_MULTI_AGENT_ENVIRONMENTS - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/test/environment.py b/pufferlib/environments/test/environment.py deleted file mode 100644 index ff82c6e47..000000000 --- a/pufferlib/environments/test/environment.py +++ /dev/null @@ -1,439 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import time -import hashlib - -import gym -import gymnasium -from gymnasium.spaces import Box, Discrete, Dict, Tuple -from pufferlib import spaces -from pettingzoo.utils.env import ParallelEnv - -import pufferlib -import pufferlib.emulation -import pufferlib.utils - - -HIGH = 100 -LOW = 0 - -MOCK_OBSERVATION_SPACES = [ - # Atari space - Box(low=0, high=255, shape=(4, 84, 84), dtype=np.uint8), - - # NetHack space - Dict({ - 'blstats': Box(-2147483648, 2147483647, (27,), 'int64'), - 'chars': Box(0, 255, (21, 79), 'uint8'), - 'colors': Box(0, 15, (21, 79), 'uint8'), - 'glyphs': Box(0, 5976, (21, 79), 'int16'), - 'inv_glyphs': Box(0, 5976, (55,), 'int16'), - 'inv_letters': Box(0, 127, (55,), 'uint8'), - 'inv_oclasses': Box(0, 18, (55,), 'uint8'), - 'inv_strs': Box(0, 255, (55, 80), 'uint8'), - 'message': Box(0, 255, (256,), 'uint8'), - 'screen_descriptions': Box(0, 127, (21, 79, 80), 'uint8'), - 'specials': Box(0, 255, (21, 79), 'uint8'), - 'tty_chars': Box(0, 255, (24, 80), 'uint8'), - 'tty_colors': Box(0, 31, (24, 80), 'int8'), - 'tty_cursor': Box(0, 255, (2,), 'uint8'), - }), - - # Neural MMO space - Dict({ - 'ActionTargets': Dict({ - 'Attack': Dict({ - 'Style': Box(0, 1, (3,), 'int8'), - 'Target': Box(0, 1, (100,), 'int8'), - }), - 'Buy': Dict({ - 'MarketItem': Box(0, 1, (1024,), 'int8'), - }), - 'Comm': Dict({ - 'Token': Box(0, 1, (50,), 'int8'), - }), - 'Destroy': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - }), - 'Give': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - 'Target': Box(0, 1, (100,), 'int8'), - }), - 'GiveGold': Dict({ - 'Price': Box(0, 1, (99,), 'int8'), - 'Target': Box(0, 1, (100,), 'int8'), - }), - 'Move': Dict({ - 'Direction': Box(0, 1, (5,), 'int8'), - }), - 'Sell': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - 'Price': Box(0, 1, (99,), 'int8'), - }), - 'Use': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - }) - }), - 'AgentId': Discrete(129), - 'CurrentTick': Discrete(1025), - 'Entity': Box(-32768, 32767, (100, 23), 'int16'), - 'Inventory': Box(-32768, 32767, (12, 16), 'int16'), - 'Market': Box(-32768, 32767, (1024, 16), 'int16'), - 'Task': Box(-32770.0, 32770.0, (1024,), 'float16'), - 'Tile': Box(-32768, 32767, (225, 3), 'int16'), - }), - - # Simple spaces - Discrete(5), - Box(low=LOW, high=HIGH, shape=(4,), dtype=np.float32), - - # Nested spaces - Dict({ - "foo": Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - "bar": Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - }), - Tuple((Discrete(3), Discrete(4))), - Tuple(( - Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - Discrete(3), - Dict({ - "baz": Box(low=LOW, high=HIGH, shape=(1,), dtype=np.float32), - "qux": Box(low=LOW, high=HIGH, shape=(1,), dtype=np.float32), - }), - )), - Dict({ - "foo": Tuple(( - Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - Discrete(3), - )), - "bar": Dict({ - "baz": Discrete(2), - "qux": Discrete(4), - }), - }), -] - -MOCK_ACTION_SPACES = [ - # NetHack action space - Discrete(5), - - # Neural MMO action space - Dict({ - 'Attack': Dict({ - 'Style': Discrete(3), - 'Target': Discrete(100), - }), - 'Buy': Dict({ - 'MarketItem': Discrete(1024), - }), - 'Comm': Dict({ - 'Token': Discrete(50), - }), - 'Destroy': Dict({ - 'InventoryItem': Discrete(12), - }), - 'Give': Dict({ - 'InventoryItem': Discrete(12), - 'Target': Discrete(100), - }), - 'GiveGold': Dict({ - 'Price': Discrete(99), - 'Target': Discrete(100), - }), - 'Move': Dict({ - 'Direction': Discrete(5), - }), - 'Sell': Dict({ - 'InventoryItem': Discrete(12), - 'Price': Discrete(99), - }), - 'Use': Dict({ - 'InventoryItem': Discrete(12), - }) - }), - - # Nested spaces - Tuple((Discrete(2), Discrete(3))), - Dict({ - "foo": Discrete(4), - "bar": Discrete(2), - }), - Tuple(( - Discrete(4), - Dict({ - "baz": Discrete(2), - "qux": Discrete(2), - }), - )), - Dict({ - "foo": Tuple(( - Discrete(2), - Discrete(3), - )), - "bar": Dict({ - "baz": Discrete(2), - "qux": Discrete(4), - }), - }), -] - -MOCK_TEAMS = { - 'None': None, - 'single': { - 'team_1': ['agent_1'], - 'team_2': ['agent_2'], - 'team_3': ['agent_3'], - 'team_4': ['agent_4'], - 'team_5': ['agent_5'], - 'team_6': ['agent_6'], - 'team_7': ['agent_7'], - 'team_8': ['agent_8'], - 'team_9': ['agent_9'], - 'team_10': ['agent_10'], - 'team_11': ['agent_11'], - 'team_12': ['agent_12'], - 'team_13': ['agent_13'], - 'team_14': ['agent_14'], - 'team_15': ['agent_15'], - 'team_16': ['agent_16'], - }, - 'pairs': { - 'team_1': ['agent_1', 'agent_2'], - 'team_2': ['agent_3', 'agent_4'], - 'team_3': ['agent_5', 'agent_6'], - 'team_4': ['agent_7', 'agent_8'], - 'team_5': ['agent_9', 'agent_10'], - 'team_6': ['agent_11', 'agent_12'], - 'team_7': ['agent_13', 'agent_14'], - 'team_8': ['agent_15', 'agent_16'], - }, - 'mixed': { - 'team_1': ['agent_1', 'agent_2'], - 'team_2': ['agent_3', 'agent_4', 'agent_5', 'agent_6'], - 'team_3': ['agent_7', 'agent_8', 'agent_9'], - 'team_4': ['agent_10', 'agent_11', 'agent_12', 'agent_13', 'agent_14'], - 'team_5': ['agent_15', 'agent_16'], - }, -} - -DEFAULT_OBSERVATION_SPACE = gymnasium.spaces.Box( - low=-2**20, high=2**20, - shape=(1,), dtype=np.float32 -) -DEFAULT_ACTION_SPACE = gymnasium.spaces.Discrete(2) - - -def make_all_mock_environments(): - mock_single_agent_environments = [] - mock_multi_agent_environments = [] - for obs_space in MOCK_OBSERVATION_SPACES: - for act_space in MOCK_ACTION_SPACES: - mock_single_agent_environments.append( - GymnasiumTestEnv( - observation_space=obs_space, - action_space=act_space, - ) - ) - - mock_multi_agent_environments.append( - PettingZooTestEnv( - observation_space=obs_space, - action_space=act_space, - initial_agents=16, - max_agents=16, - spawn_per_tick=0, - death_per_tick=1, - ) - ) - return mock_single_agent_environments, mock_multi_agent_environments - -def do_work(delay_mean, delay_std): - start, idx = time.process_time(), 0 - target_time = delay_mean + delay_std*np.random.randn() - while time.process_time() - start < target_time: - idx += 1 - return - -class GymnasiumPerformanceEnv: - def __init__(self, delay_mean=0, delay_std=0): - self.observation_space = DEFAULT_OBSERVATION_SPACE - self.action_space = DEFAULT_ACTION_SPACE - self.observation = self.observation_space.sample() - - self.delay_mean = delay_mean - self.delay_std = delay_std - - # Test performance independent of PufferLib seeding - np.random.seed(time.time_ns() % 2**32) - - def reset(self, seed=None): - return self.observation, {} - - def step(self, action): - do_work(self.delay_mean, self.delay_std) - return self.observation, 0, False, False, {} - - def close(self): - pass - -class PettingZooPerformanceEnv: - def __init__(self, delay_mean, delay_std): - self.possible_agents = [1] - self.agents = [1] - self.done = False - - self.delay_mean = delay_mean - self.delay_std = delay_std - - def observation_space(self, agent): - return DEFAULT_OBSERVATION_SPACE - - def action_space(self, agent): - return DEFAULT_ACTION_SPACE - - def reset(self, seed=None): - return {1: self.observation_space(1).sample()}, {1: {}} - - def step(self, actions): - obs = {1: np.array([0], dtype=np.float32)} - rewards = {1: 1} - dones = {1: False} - truncateds = {1: False} - infos = {1: {}} - - do_work(self.delay_mean, self.delay_std) - - return obs, rewards, dones, truncateds, infos - - def close(self): - pass - -class GymnasiumTestEnv(gym.Env): - def __init__(self, - observation_space=DEFAULT_OBSERVATION_SPACE, - action_space=DEFAULT_ACTION_SPACE): - self.observation_space = observation_space - self.action_space = action_space - - def reset(self, seed=None): - self.tick = 0 - self.rng = pufferlib.utils.RandomState(seed) - - ob = _sample_space('agent_1', self.tick, self.observation_space) - return ob, {} - - def step(self, actions): - reward = self.tick - done = self.tick < 10 - self.tick += 1 - - ob = _sample_space('agent_1', self.tick, self.observation_space) - return ob, reward, done, False, {'dead': done} - - def close(self): - pass - -class PettingZooTestEnv(ParallelEnv): - def __init__(self, - observation_space=DEFAULT_OBSERVATION_SPACE, - action_space=DEFAULT_ACTION_SPACE, - initial_agents=16, max_agents=16, - spawn_per_tick=0, death_per_tick=1, - homogeneous_spaces=True): - self._observation_space = observation_space - self._action_space = action_space - self.initial_agents = initial_agents - self.max_agents = max_agents - self.spawn_per_tick = spawn_per_tick - self.death_per_tick = death_per_tick - self.homogeneous_spaces = homogeneous_spaces - - self.possible_agents = [f'agent_{i+1}' for i in range(max_agents)] - self.agents = [] - - def reset(self, seed=None): - self.tick = 0 - self.agents = self.possible_agents[:self.initial_agents] - - obs = {a: _sample_space(a, self.tick, self._observation_space) - for a in self.agents} - infos = {a: {} for a in self.agents} - return obs, infos - - def step(self, actions): - obs, rewards, dones, truncateds, infos = {}, {}, {}, {}, {} - self.tick += 1 - - dead = self.agents[:self.death_per_tick] - for kill in dead: - self.agents.remove(kill) - # TODO: Make pufferlib work without pad obs - # but still require rewards, dones, and optionally infos - obs[kill] = _sample_space(kill, self.tick, - self._observation_space, zero=True) - rewards[kill] = -1 - dones[kill] = True - truncateds[kill] = False - infos[kill] = {'dead': True} - - # TODO: Fix this - assert self.spawn_per_tick == 0 - for spawn in range(self.spawn_per_tick): - # TODO: Make pufferlib check if an agent respawns on the - # Same tick as it dies (is this good or bad?) - spawn = self.rng.choice(self.possible_agents) - if spawn not in self.agents + dead: - self.agents.append(spawn) - - for agent in self.agents: - obs[agent] = _sample_space(agent, self.tick, self._observation_space) - rewards[agent] = 0.1 * _agent_str_to_int(agent) - dones[agent] = False - truncateds[agent] = False - infos[agent] = {'dead': False} - - return obs, rewards, dones, truncateds, infos - - def observation_space(self, agent) -> gym.Space: - return self._observation_space - - def action_space(self, agent) -> gym.Space: - return self._action_space - - def render(self, mode='human'): - pass - - def close(self): - pass - -### Other Mock environments and utilities -def _agent_str_to_int(agent): - return int(agent.split('_')[-1]) - -def _sample_space(agent, tick, space, zero=False): - if isinstance(agent, str): - agent = float(agent.split('_')[-1]) - - if isinstance(space, spaces.Discrete): - if zero: - return 0 - return int((10*agent + tick) % space.n) - elif isinstance(space, spaces.Box): - if zero: - return np.zeros(space.shape, dtype=space.dtype) - - # Try to make a relatively unique data pattern - # without using RNG - nonce = 10*agent + tick - low = space.low - high = space.high - sample = low + np.arange(low.size).reshape(space.shape) + nonce - sample = (sample % high).astype(space.dtype) - return sample - elif isinstance(space, spaces.Tuple): - return tuple(_sample_space(agent, tick, s, zero) for s in space.spaces) - elif isinstance(space, spaces.Dict): - return {k: _sample_space(agent, tick, v, zero) for k, v in space.spaces.items()} - else: - raise ValueError(f"Invalid space type: {type(space)}") diff --git a/pufferlib/environments/test/mock_environments.py b/pufferlib/environments/test/mock_environments.py deleted file mode 100644 index 8e8db330e..000000000 --- a/pufferlib/environments/test/mock_environments.py +++ /dev/null @@ -1,429 +0,0 @@ -from pdb import set_trace as T -import numpy as np - -import time -import hashlib -from functools import partial - -import gymnasium as gym -from gymnasium.spaces import Box, Discrete, Dict, Tuple -from pettingzoo.utils.env import ParallelEnv - -import pufferlib -import pufferlib.emulation -import pufferlib.utils - - -HIGH = 100 -LOW = 0 - -def make_performance_env(delay=0, bandwidth=1): - return pufferlib.emulation.PettingZooPufferEnv( - env_creator=PerformanceEnv, - env_args=[delay, bandwidth], - ) - -class PerformanceEnv: - def __init__(self, delay=0, bandwith=1): - self.agents = [1] - self.possible_agents = [1] - self.done = False - - self.delay = delay - assert bandwith > 0 - self.bandwidth = bandwith - - def reset(self, seed=None): - return {1: self.observation_space(1).sample()}, {1: {}} - - def step(self, actions): - obs = {1: np.array([0], dtype=np.float32)} - rewards = {1: 1} - dones = {1: False} - truncateds = {1: False} - infos = {1: {}} - - # Busy wait so process does not swap on sleep - end = time.perf_counter() + self.delay - while time.perf_counter() < end: - pass - - return obs, rewards, dones, truncateds, infos - - def observation_space(self, agent): - return Box( - low=-2**20, high=2**20, - shape=(self.bandwidth,), dtype=np.float32 - ) - - def action_space(self, agent): - return Discrete(2) - - -### Other Mock environments and utilities -def _agent_str_to_int(agent): - return int(agent.split('_')[-1]) - - -def _sample_space(agent, tick, space, zero=False): - if isinstance(agent, str): - agent = float(agent.split('_')[-1]) - - if isinstance(space, Discrete): - if zero: - return 0 - return int((10*agent + tick) % space.n) - elif isinstance(space, Box): - if zero: - return np.zeros(space.shape, dtype=space.dtype) - - # Try to make a relatively unique data pattern - # without using RNG - nonce = 10*agent + tick - low = space.low - high = space.high - sample = low + np.arange(low.size).reshape(space.shape) + nonce - sample = (sample % high).astype(space.dtype) - return sample - elif isinstance(space, Tuple): - return tuple(_sample_space(agent, tick, s, zero) for s in space.spaces) - elif isinstance(space, Dict): - return {k: _sample_space(agent, tick, v, zero) for k, v in space.spaces.items()} - else: - raise ValueError(f"Invalid space type: {type(space)}") - -class GymnasiumTestEnv(gym.Env): - def __init__(self, observation_space, action_space): - self.observation_space = observation_space - self.action_space = action_space - - def reset(self, seed=None): - self.tick = 0 - self.rng = pufferlib.utils.RandomState(seed) - - ob = _sample_space('agent_1', self.tick, self.observation_space) - return ob, {} - - def step(self, actions): - reward = self.tick - done = self.tick < 10 - self.tick += 1 - - ob = _sample_space('agent_1', self.tick, self.observation_space) - return ob, reward, done, False, {'dead': done} - - -def make_mock_singleagent_env(observation_space, action_space): - return partial( - GymnasiumTestEnv, - observation_space=observation_space, - action_space=action_space, - ) - -class TestEnv(ParallelEnv): - def __init__(self, observation_space, action_space, initial_agents, - max_agents, spawn_per_tick, death_per_tick): - self.single_observation_space = observation_space - self.single_action_space = action_space - self.initial_agents = initial_agents - self.max_agents = max_agents - self.spawn_per_tick = spawn_per_tick - self.death_per_tick = death_per_tick - - self.possible_agents = [f'agent_{i+1}' for i in range(max_agents)] - self.agents = [] - - def reset(self, seed=None): - self.tick = 0 - self.agents = self.possible_agents[:self.initial_agents] - - obs = {a: _sample_space(a, self.tick, self.single_observation_space) - for a in self.agents} - infos = {a: {} for a in self.agents} - return obs, infos - - def step(self, actions): - obs, rewards, dones, truncateds, infos = {}, {}, {}, {}, {} - self.tick += 1 - - dead = self.agents[:self.death_per_tick] - for kill in dead: - self.agents.remove(kill) - # TODO: Make pufferlib work without pad obs - # but still require rewards, dones, and optionally infos - obs[kill] = _sample_space(kill, self.tick, self.single_observation_space, zero=True) - rewards[kill] = -1 - dones[kill] = True - truncateds[kill] = False - infos[kill] = {'dead': True} - - # TODO: Fix this - assert self.spawn_per_tick == 0 - for spawn in range(self.spawn_per_tick): - # TODO: Make pufferlib check if an agent respawns on the - # Same tick as it dies (is this good or bad?) - spawn = self.rng.choice(self.possible_agents) - if spawn not in self.agents + dead: - self.agents.append(spawn) - - for agent in self.agents: - obs[agent] = _sample_space(agent, self.tick, self.single_observation_space) - rewards[agent] = 0.1 * _agent_str_to_int(agent) - dones[agent] = False - truncateds[agent] = False - infos[agent] = {'dead': False} - - return obs, rewards, dones, truncateds, infos - - def observation_space(self, agent) -> gym.Space: - return self.single_observation_space - - def action_space(self, agent) -> gym.Space: - return self.single_action_space - - def render(self, mode='human'): - pass - - def close(self): - pass - -def make_mock_multiagent_env( - observation_space, - action_space, - initial_agents, - max_agents, - spawn_per_tick, - death_per_tick, - homogeneous_spaces=True): - return partial( - TestEnv, - observation_space=observation_space, - action_space=action_space, - initial_agents=initial_agents, - max_agents=max_agents, - spawn_per_tick=spawn_per_tick, - death_per_tick=death_per_tick, - ) - - -MOCK_OBSERVATION_SPACES = [ - # Atari space - Box(low=0, high=255, shape=(4, 84, 84), dtype=np.uint8), - - # NetHack space - Dict({ - 'blstats': Box(-2147483648, 2147483647, (27,), 'int64'), - 'chars': Box(0, 255, (21, 79), 'uint8'), - 'colors': Box(0, 15, (21, 79), 'uint8'), - 'glyphs': Box(0, 5976, (21, 79), 'int16'), - 'inv_glyphs': Box(0, 5976, (55,), 'int16'), - 'inv_letters': Box(0, 127, (55,), 'uint8'), - 'inv_oclasses': Box(0, 18, (55,), 'uint8'), - 'inv_strs': Box(0, 255, (55, 80), 'uint8'), - 'message': Box(0, 255, (256,), 'uint8'), - 'screen_descriptions': Box(0, 127, (21, 79, 80), 'uint8'), - 'specials': Box(0, 255, (21, 79), 'uint8'), - 'tty_chars': Box(0, 255, (24, 80), 'uint8'), - 'tty_colors': Box(0, 31, (24, 80), 'int8'), - 'tty_cursor': Box(0, 255, (2,), 'uint8'), - }), - - # Neural MMO space - Dict({ - 'ActionTargets': Dict({ - 'Attack': Dict({ - 'Style': Box(0, 1, (3,), 'int8'), - 'Target': Box(0, 1, (100,), 'int8'), - }), - 'Buy': Dict({ - 'MarketItem': Box(0, 1, (1024,), 'int8'), - }), - 'Comm': Dict({ - 'Token': Box(0, 1, (50,), 'int8'), - }), - 'Destroy': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - }), - 'Give': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - 'Target': Box(0, 1, (100,), 'int8'), - }), - 'GiveGold': Dict({ - 'Price': Box(0, 1, (99,), 'int8'), - 'Target': Box(0, 1, (100,), 'int8'), - }), - 'Move': Dict({ - 'Direction': Box(0, 1, (5,), 'int8'), - }), - 'Sell': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - 'Price': Box(0, 1, (99,), 'int8'), - }), - 'Use': Dict({ - 'InventoryItem': Box(0, 1, (12,), 'int8'), - }) - }), - 'AgentId': Discrete(129), - 'CurrentTick': Discrete(1025), - 'Entity': Box(-32768, 32767, (100, 23), 'int16'), - 'Inventory': Box(-32768, 32767, (12, 16), 'int16'), - 'Market': Box(-32768, 32767, (1024, 16), 'int16'), - 'Task': Box(-32770.0, 32770.0, (1024,), 'float16'), - 'Tile': Box(-32768, 32767, (225, 3), 'int16'), - }), - - # Simple spaces - Discrete(5), - Box(low=LOW, high=HIGH, shape=(4,), dtype=np.float32), - - # Nested spaces - Dict({ - "foo": Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - "bar": Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - }), - Tuple((Discrete(3), Discrete(4))), - Tuple(( - Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - Discrete(3), - Dict({ - "baz": Box(low=LOW, high=HIGH, shape=(1,), dtype=np.float32), - "qux": Box(low=LOW, high=HIGH, shape=(1,), dtype=np.float32), - }), - )), - Dict({ - "foo": Tuple(( - Box(low=LOW, high=HIGH, shape=(2,), dtype=np.float32), - Discrete(3), - )), - "bar": Dict({ - "baz": Discrete(2), - "qux": Discrete(4), - }), - }), -] - - -MOCK_ACTION_SPACES = [ - # NetHack action space - Discrete(5), - - # Neural MMO action space - Dict({ - 'Attack': Dict({ - 'Style': Discrete(3), - 'Target': Discrete(100), - }), - 'Buy': Dict({ - 'MarketItem': Discrete(1024), - }), - 'Comm': Dict({ - 'Token': Discrete(50), - }), - 'Destroy': Dict({ - 'InventoryItem': Discrete(12), - }), - 'Give': Dict({ - 'InventoryItem': Discrete(12), - 'Target': Discrete(100), - }), - 'GiveGold': Dict({ - 'Price': Discrete(99), - 'Target': Discrete(100), - }), - 'Move': Dict({ - 'Direction': Discrete(5), - }), - 'Sell': Dict({ - 'InventoryItem': Discrete(12), - 'Price': Discrete(99), - }), - 'Use': Dict({ - 'InventoryItem': Discrete(12), - }) - }), - - # Nested spaces - Tuple((gym.spaces.Discrete(2), gym.spaces.Discrete(3))), - Dict({ - "foo": Discrete(4), - "bar": Discrete(2), - }), - Tuple(( - Discrete(4), - Dict({ - "baz": Discrete(2), - "qux": Discrete(2), - }), - )), - Dict({ - "foo": Tuple(( - Discrete(2), - Discrete(3), - )), - "bar": Dict({ - "baz": Discrete(2), - "qux": Discrete(4), - }), - }), -] - -MOCK_TEAMS = { - 'None': None, - 'single': { - 'team_1': ['agent_1'], - 'team_2': ['agent_2'], - 'team_3': ['agent_3'], - 'team_4': ['agent_4'], - 'team_5': ['agent_5'], - 'team_6': ['agent_6'], - 'team_7': ['agent_7'], - 'team_8': ['agent_8'], - 'team_9': ['agent_9'], - 'team_10': ['agent_10'], - 'team_11': ['agent_11'], - 'team_12': ['agent_12'], - 'team_13': ['agent_13'], - 'team_14': ['agent_14'], - 'team_15': ['agent_15'], - 'team_16': ['agent_16'], - }, - 'pairs': { - 'team_1': ['agent_1', 'agent_2'], - 'team_2': ['agent_3', 'agent_4'], - 'team_3': ['agent_5', 'agent_6'], - 'team_4': ['agent_7', 'agent_8'], - 'team_5': ['agent_9', 'agent_10'], - 'team_6': ['agent_11', 'agent_12'], - 'team_7': ['agent_13', 'agent_14'], - 'team_8': ['agent_15', 'agent_16'], - }, - 'mixed': { - 'team_1': ['agent_1', 'agent_2'], - 'team_2': ['agent_3', 'agent_4', 'agent_5', 'agent_6'], - 'team_3': ['agent_7', 'agent_8', 'agent_9'], - 'team_4': ['agent_10', 'agent_11', 'agent_12', 'agent_13', 'agent_14'], - 'team_5': ['agent_15', 'agent_16'], - }, -} - -MOCK_SINGLE_AGENT_ENVIRONMENTS = [] -MOCK_MULTI_AGENT_ENVIRONMENTS = [] -for obs_space in MOCK_OBSERVATION_SPACES: - for act_space in MOCK_ACTION_SPACES: - MOCK_SINGLE_AGENT_ENVIRONMENTS.append( - make_mock_singleagent_env( - observation_space=obs_space, - action_space=act_space, - ) - ) - - MOCK_MULTI_AGENT_ENVIRONMENTS.append( - make_mock_multiagent_env( - observation_space=obs_space, - action_space=act_space, - initial_agents=16, - max_agents=16, - spawn_per_tick=0, - death_per_tick=1, - ) - ) diff --git a/pufferlib/environments/test/torch.py b/pufferlib/environments/test/torch.py deleted file mode 100644 index 8b13194e9..000000000 --- a/pufferlib/environments/test/torch.py +++ /dev/null @@ -1 +0,0 @@ -from pufferlib.models import Default as Policy diff --git a/pufferlib/environments/trade_sim/__init__.py b/pufferlib/environments/trade_sim/__init__.py deleted file mode 100644 index eff86ef02..000000000 --- a/pufferlib/environments/trade_sim/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator, make - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/trade_sim/environment.py b/pufferlib/environments/trade_sim/environment.py deleted file mode 100644 index 0c245c0b2..000000000 --- a/pufferlib/environments/trade_sim/environment.py +++ /dev/null @@ -1,37 +0,0 @@ -import functools -import numpy as np - -import pufferlib - -from nof1.simulation.env import TradingEnvironment - -def env_creator(name='metta'): - return functools.partial(make, name) - -def make(name, config_path='../nof1-trading-sim/config/experiment_cv.yaml', render_mode='human', buf=None, seed=1): - '''Crafter creation function''' - from nof1.utils.config_manager import ConfigManager - from nof1.data_ingestion.historical_data_reader import HistoricalDataReader - - config_manager = ConfigManager(config_path) - config = config_manager.config - data_reader = HistoricalDataReader(config_manager) - states, prices, atrs, timestamps = data_reader.preprocess_data() - - # Create environment - env = TradingEnvironmentPuff(config_manager.config, states=states, prices=prices, atrs=atrs, timestamps=timestamps) - return pufferlib.emulation.GymnasiumPufferEnv(env, buf=buf) - -class TradingEnvironmentPuff(TradingEnvironment): - def reset(self): - obs, info = super().reset() - return obs.astype(np.float32), info - - def step(self, action): - obs, reward, terminated, truncated, info = super().step(action) - - if not terminated and not truncated: - info = {} - - return obs.astype(np.float32), reward, terminated, truncated, info - diff --git a/pufferlib/environments/trade_sim/torch.py b/pufferlib/environments/trade_sim/torch.py deleted file mode 100644 index be832843f..000000000 --- a/pufferlib/environments/trade_sim/torch.py +++ /dev/null @@ -1,4 +0,0 @@ -import pufferlib.models - -Policy = pufferlib.models.Default -Recurrent = pufferlib.models.LSTMWrapper diff --git a/pufferlib/environments/vizdoom/__init__.py b/pufferlib/environments/vizdoom/__init__.py deleted file mode 100644 index 59cda9e7c..000000000 --- a/pufferlib/environments/vizdoom/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .environment import env_creator - -try: - import torch -except ImportError: - pass -else: - from .torch import Policy - try: - from .torch import Recurrent - except: - Recurrent = None diff --git a/pufferlib/environments/vizdoom/environment.py b/pufferlib/environments/vizdoom/environment.py deleted file mode 100644 index d98a263f9..000000000 --- a/pufferlib/environments/vizdoom/environment.py +++ /dev/null @@ -1,69 +0,0 @@ -from pdb import set_trace as T -import numpy as np -import functools - -import gymnasium as gym - -import pufferlib -import pufferlib.emulation -import pufferlib.environments -import pufferlib.utils -import pufferlib.postprocess - - -def env_creator(name='doom'): - return functools.partial(make, name) - -def make(name, framestack=1, render_mode='rgb_array', buf=None): - '''Atari creation function with default CleanRL preprocessing based on Stable Baselines3 wrappers''' - if name == 'doom': - name = 'VizdoomHealthGatheringSupreme-v0' - - #pufferlib.environments.try_import('vizdoom', 'gymnasium_wrapper') - from stable_baselines3.common.atari_wrappers import ( - ClipRewardEnv, - EpisodicLifeEnv, - FireResetEnv, - MaxAndSkipEnv, - NoopResetEnv, - ) - # Make does not work without this imported - # TODO: Fix try_import - from vizdoom import gymnasium_wrapper - with pufferlib.utils.Suppress(): - env = gym.make(name, render_mode=render_mode) - - env = DoomWrapper(env) # Don't use standard postprocessor - - #env = gym.wrappers.RecordEpisodeStatistics(env) - #env = NoopResetEnv(env, noop_max=30) - #env = MaxAndSkipEnv(env, skip=4) - #env = EpisodicLifeEnv(env) - #if "FIRE" in env.unwrapped.get_action_meanings(): - # env = FireResetEnv(env) - #env = ClipRewardEnv(env) - #env = gym.wrappers.GrayScaleObservation(env) - #env = gym.wrappers.FrameStack(env, framestack) - return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) - -class DoomWrapper(gym.Wrapper): - '''Gymnasium env does not expose proper options for screen scale and - render format. This is slow. So we do it ourselves. Not it is fast. Yay!''' - def __init__(self, env): - super().__init__(env.unwrapped) - if env.observation_space['screen'].shape[0] != 120: - raise ValueError('Wrong screen resolution. Doom does not provide ' - 'a way to change this. You must edit scenarios/.cfg' - 'This is inside your local ViZDoom installation. Likely in python system packages' - 'Set screen resolution to RES_160X120 and screen format to GRAY8') - - self.observation_space = gym.spaces.Box( - low=0, high=255, shape=(60, 80, 1), dtype=np.uint8) - - def reset(self, seed=None, options=None): - obs, info = self.env.reset(seed=seed, options=options) - return obs['screen'][::2, ::2], {} - - def step(self, action): - obs, reward, terminal, truncated, info = self.env.step(action) - return obs['screen'][::2, ::2], reward, terminal, truncated, info diff --git a/pufferlib/environments/vizdoom/torch.py b/pufferlib/environments/vizdoom/torch.py deleted file mode 100644 index 4fd6964a5..000000000 --- a/pufferlib/environments/vizdoom/torch.py +++ /dev/null @@ -1,19 +0,0 @@ -import pufferlib.models - - -class Recurrent(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=512, hidden_size=512, num_layers=1): - super().__init__(env, policy, input_size, hidden_size, num_layers) - -class Policy(pufferlib.models.Convolutional): - def __init__(self, env, input_size=512, hidden_size=512, output_size=512, - framestack=1, flat_size=64*4*6): - super().__init__( - env=env, - input_size=input_size, - hidden_size=hidden_size, - output_size=output_size, - framestack=framestack, - flat_size=flat_size, - channels_last=True - ) diff --git a/pufferlib/extensions/puffernet.h b/pufferlib/extensions/puffernet.h index 414cd74ac..ac1a19e29 100644 --- a/pufferlib/extensions/puffernet.h +++ b/pufferlib/extensions/puffernet.h @@ -39,18 +39,38 @@ struct Weights { void _load_weights(const char* filename, float* weights, size_t num_weights) { FILE* file = fopen(filename, "rb"); if (!file) { - perror("Error opening file"); + fprintf(stderr, "Error: Could not open weights file: %s\n", filename); + exit(1); } + + // Get file size fseek(file, 0, SEEK_END); + long file_size = ftell(file); rewind(file); + size_t read_size = fread(weights, sizeof(float), num_weights, file); fclose(file); if (read_size != num_weights) { - perror("Error reading file"); + fprintf(stderr, "Error: Failed to read weights from file: %s\n", filename); + fprintf(stderr, " Expected to read: %zu weights\n", num_weights); + fprintf(stderr, " Actually read: %zu weights\n", read_size); + exit(1); } } -Weights* load_weights(const char* filename, size_t num_weights) { +Weights* load_weights(const char* filename) { + FILE* file = fopen(filename, "rb"); + if (!file) { + perror("Error opening weights file"); + return NULL; + } + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + fclose(file); + + size_t num_weights = file_size / sizeof(float); + printf("Loading %zu weights from %s\n", num_weights, filename); + Weights* weights = calloc(1, sizeof(Weights) + num_weights*sizeof(float)); weights->data = (float*)(weights + 1); _load_weights(filename, weights->data, num_weights); @@ -76,7 +96,7 @@ void _relu(float* input, float* output, int size) { void _gelu(float* input, float* output, int size) { for (int i = 0; i < size; i++) { - output[i] = 0.5f*input[i]*(1 + tanhf(0.6628526501011142 * (input[i] + 0.044715f*input[i]*input[i]*input[i]))); + output[i] = 0.5f*input[i]*(1 + tanhf(0.7978845608028654 * (input[i] + 0.044715f*input[i]*input[i]*input[i]))); } } @@ -120,7 +140,7 @@ void _conv2d(float* input, float* weights, float* bias, for (int w = 0; w < w_out; w++) { int out_adr = ( b*out_channels*h_out*w_out - + oc*h_out*w_out+ + + oc*h_out*w_out+ + h*w_out + w ); @@ -516,7 +536,7 @@ struct Conv3D { Conv3D* make_conv3d(Weights* weights, int batch_size, int in_width, int in_height, int in_depth, int in_channels, int out_channels, int kernel_size, int stride) { - + size_t buffer_size = batch_size*out_channels*in_depth*in_height*in_width*sizeof(float); int num_weights = out_channels*in_channels*kernel_size*kernel_size*kernel_size; Conv3D* layer = calloc(1, sizeof(Conv3D) + buffer_size); @@ -629,7 +649,7 @@ LayerNorm* make_layernorm(Weights* weights, int batch_size, int input_dim) { }; return layer; } - + void layernorm(LayerNorm* layer, float* input) { _layernorm(input, layer->weights, layer->bias, layer->output, layer->batch_size, layer->input_dim); diff --git a/pufferlib/extensions/puffernet.pyx b/pufferlib/extensions/puffernet.pyx index c2dffd200..6dfac40c2 100644 --- a/pufferlib/extensions/puffernet.pyx +++ b/pufferlib/extensions/puffernet.pyx @@ -91,4 +91,3 @@ def puf_argmax_multidiscrete(cnp.ndarray input, cnp.ndarray output, int batch_size, cnp.ndarray logit_sizes, int num_actions): _argmax_multidiscrete( input.data, output.data, batch_size, logit_sizes.data, num_actions) - diff --git a/pufferlib/models.py b/pufferlib/models.py index fa43d7071..0893a9db4 100644 --- a/pufferlib/models.py +++ b/pufferlib/models.py @@ -1,4 +1,3 @@ -from pdb import set_trace as T import numpy as np import torch @@ -10,7 +9,7 @@ class Default(nn.Module): - '''Default PyTorch policy. Flattens obs and applies a linear layer. + """Default PyTorch policy. Flattens obs and applies a linear layer. PufferLib is not a framework. It does not enforce a base class. You can use any PyTorch policy that returns actions and values. @@ -20,18 +19,17 @@ class Default(nn.Module): for use with our LSTM wrapper, simply put everything from forward() before the recurrent cell into encode_observations and put everything after into decode_actions. - ''' + """ + def __init__(self, env, hidden_size=128): super().__init__() self.hidden_size = hidden_size - self.is_multidiscrete = isinstance(env.single_action_space, - pufferlib.spaces.MultiDiscrete) - self.is_continuous = isinstance(env.single_action_space, - pufferlib.spaces.Box) + self.is_multidiscrete = isinstance(env.single_action_space, pufferlib.spaces.MultiDiscrete) + self.is_continuous = isinstance(env.single_action_space, pufferlib.spaces.Box) try: - self.is_dict_obs = isinstance(env.env.observation_space, pufferlib.spaces.Dict) + self.is_dict_obs = isinstance(env.env.observation_space, pufferlib.spaces.Dict) except: - self.is_dict_obs = isinstance(env.observation_space, pufferlib.spaces.Dict) + self.is_dict_obs = isinstance(env.observation_space, pufferlib.spaces.Dict) if self.is_dict_obs: self.dtype = pufferlib.pytorch.nativize_dtype(env.emulated) @@ -43,24 +41,21 @@ def __init__(self, env, hidden_size=128): pufferlib.pytorch.layer_init(nn.Linear(num_obs, hidden_size)), nn.GELU(), ) - + if self.is_multidiscrete: self.action_nvec = tuple(env.single_action_space.nvec) num_atns = sum(self.action_nvec) - self.decoder = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, num_atns), std=0.01) + self.decoder = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, num_atns), std=0.01) elif not self.is_continuous: num_atns = env.single_action_space.n - self.decoder = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, num_atns), std=0.01) + self.decoder = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, num_atns), std=0.01) else: self.decoder_mean = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.shape[0]), std=0.01) - self.decoder_logstd = nn.Parameter(torch.zeros( - 1, env.single_action_space.shape[0])) + nn.Linear(hidden_size, env.single_action_space.shape[0]), std=0.01 + ) + self.decoder_logstd = nn.Parameter(torch.zeros(1, env.single_action_space.shape[0])) - self.value = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) + self.value = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, 1), std=1) def forward_eval(self, observations, state=None): hidden = self.encode_observations(observations, state=state) @@ -71,19 +66,19 @@ def forward(self, observations, state=None): return self.forward_eval(observations, state) def encode_observations(self, observations, state=None): - '''Encodes a batch of observations into hidden states. Assumes - no time dimension (handled by LSTM wrappers).''' + """Encodes a batch of observations into hidden states. Assumes + no time dimension (handled by LSTM wrappers).""" batch_size = observations.shape[0] if self.is_dict_obs: observations = pufferlib.pytorch.nativize_tensor(observations, self.dtype) observations = torch.cat([v.view(batch_size, -1) for v in observations.values()], dim=1) - else: + else: observations = observations.view(batch_size, -1) return self.encoder(observations.float()) def decode_actions(self, hidden): - '''Decodes a batch of hidden states into (multi)discrete actions. - Assumes no time dimension (handled by LSTM wrappers).''' + """Decodes a batch of hidden states into (multi)discrete actions. + Assumes no time dimension (handled by LSTM wrappers).""" if self.is_multidiscrete: logits = self.decoder(hidden).split(self.action_nvec, dim=1) elif self.is_continuous: @@ -97,12 +92,13 @@ def decode_actions(self, hidden): values = self.value(hidden) return logits, values + class LSTMWrapper(nn.Module): def __init__(self, env, policy, input_size=128, hidden_size=128): - '''Wraps your policy with an LSTM without letting you shoot yourself in the + """Wraps your policy with an LSTM without letting you shoot yourself in the foot with bad transpose and shape operations. This saves much pain. Requires that your policy define encode_observations and decode_actions. - See the Default policy for an example.''' + See the Default policy for an example.""" super().__init__() self.obs_shape = env.single_observation_space.shape @@ -112,7 +108,7 @@ def __init__(self, env, policy, input_size=128, hidden_size=128): self.is_continuous = self.policy.is_continuous for name, param in self.named_parameters(): - if 'layer_norm' in name: + if "layer_norm" in name: continue if "bias" in name: nn.init.constant_(param, 0) @@ -127,96 +123,107 @@ def __init__(self, env, policy, input_size=128, hidden_size=128): self.cell.bias_ih = self.lstm.bias_ih_l0 self.cell.bias_hh = self.lstm.bias_hh_l0 - #self.pre_layernorm = nn.LayerNorm(hidden_size) - #self.post_layernorm = nn.LayerNorm(hidden_size) + # self.pre_layernorm = nn.LayerNorm(hidden_size) + # self.post_layernorm = nn.LayerNorm(hidden_size) def forward_eval(self, observations, state): - '''Forward function for inference. 3x faster than using LSTM directly''' + """Forward function for inference. 3x faster than using LSTM directly""" hidden = self.policy.encode_observations(observations, state=state) - h = state['lstm_h'] - c = state['lstm_c'] + h = state["lstm_h"] + c = state["lstm_c"] # TODO: Don't break compile if h is not None: - assert h.shape[0] == c.shape[0] == observations.shape[0], 'LSTM state must be (h, c)' + assert h.shape[0] == c.shape[0] == observations.shape[0], "LSTM state must be (h, c)" lstm_state = (h, c) else: lstm_state = None - #hidden = self.pre_layernorm(hidden) + # hidden = self.pre_layernorm(hidden) hidden, c = self.cell(hidden, lstm_state) - #hidden = self.post_layernorm(hidden) - state['hidden'] = hidden - state['lstm_h'] = hidden - state['lstm_c'] = c + # hidden = self.post_layernorm(hidden) + state["hidden"] = hidden + state["lstm_h"] = hidden + state["lstm_c"] = c logits, values = self.policy.decode_actions(hidden) return logits, values def forward(self, observations, state): - '''Forward function for training. Uses LSTM for fast time-batching''' + """Forward function for training. Uses LSTM for fast time-batching""" x = observations - lstm_h = state['lstm_h'] - lstm_c = state['lstm_c'] + lstm_h = state["lstm_h"] + lstm_c = state["lstm_c"] x_shape, space_shape = x.shape, self.obs_shape x_n, space_n = len(x_shape), len(space_shape) if x_shape[-space_n:] != space_shape: - raise ValueError('Invalid input tensor shape', x.shape) + raise ValueError("Invalid input tensor shape", x.shape) if x_n == space_n + 1: B, TT = x_shape[0], 1 elif x_n == space_n + 2: B, TT = x_shape[:2] else: - raise ValueError('Invalid input tensor shape', x.shape) + raise ValueError("Invalid input tensor shape", x.shape) if lstm_h is not None: - assert lstm_h.shape[1] == lstm_c.shape[1] == B, 'LSTM state must be (h, c)' + assert lstm_h.shape[1] == lstm_c.shape[1] == B, "LSTM state must be (h, c)" lstm_state = (lstm_h, lstm_c) else: lstm_state = None - x = x.reshape(B*TT, *space_shape) + x = x.reshape(B * TT, *space_shape) hidden = self.policy.encode_observations(x, state) - assert hidden.shape == (B*TT, self.input_size) + assert hidden.shape == (B * TT, self.input_size) hidden = hidden.reshape(B, TT, self.input_size) hidden = hidden.transpose(0, 1) - #hidden = self.pre_layernorm(hidden) + # hidden = self.pre_layernorm(hidden) hidden, (lstm_h, lstm_c) = self.lstm.forward(hidden, lstm_state) hidden = hidden.float() - - #hidden = self.post_layernorm(hidden) + + # hidden = self.post_layernorm(hidden) hidden = hidden.transpose(0, 1) - flat_hidden = hidden.reshape(B*TT, self.hidden_size) + flat_hidden = hidden.reshape(B * TT, self.hidden_size) logits, values = self.policy.decode_actions(flat_hidden) values = values.reshape(B, TT) - #state.batch_logits = logits.reshape(B, TT, -1) - state['hidden'] = hidden - state['lstm_h'] = lstm_h.detach() - state['lstm_c'] = lstm_c.detach() + # state.batch_logits = logits.reshape(B, TT, -1) + state["hidden"] = hidden + state["lstm_h"] = lstm_h.detach() + state["lstm_c"] = lstm_c.detach() return logits, values + class Convolutional(nn.Module): - def __init__(self, env, *args, framestack, flat_size, - input_size=512, hidden_size=512, output_size=512, - channels_last=False, downsample=1, **kwargs): - '''The CleanRL default NatureCNN policy used for Atari. + def __init__( + self, + env, + *args, + framestack, + flat_size, + input_size=512, + hidden_size=512, + output_size=512, + channels_last=False, + downsample=1, + **kwargs, + ): + """The CleanRL default NatureCNN policy used for Atari. It's just a stack of three convolutions followed by a linear layer - + Takes framestack as a mandatory keyword argument. Suggested default is 1 frame - with LSTM or 4 frames without.''' + with LSTM or 4 frames without.""" super().__init__() self.channels_last = channels_last self.downsample = downsample - #TODO: Remove these from required params + # TODO: Remove these from required params self.hidden_size = hidden_size self.is_continuous = False - self.network= nn.Sequential( + self.network = nn.Sequential( pufferlib.pytorch.layer_init(nn.Conv2d(framestack, 32, 8, stride=4)), nn.ReLU(), pufferlib.pytorch.layer_init(nn.Conv2d(32, 64, 4, stride=2)), @@ -227,10 +234,8 @@ def __init__(self, env, *args, framestack, flat_size, pufferlib.pytorch.layer_init(nn.Linear(flat_size, hidden_size)), nn.ReLU(), ) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(output_size, 1), std=1) + self.actor = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, env.single_action_space.n), std=0.01) + self.value_fn = pufferlib.pytorch.layer_init(nn.Linear(output_size, 1), std=1) def forward(self, observations, state=None): hidden = self.encode_observations(observations) @@ -244,7 +249,7 @@ def encode_observations(self, observations, state=None): if self.channels_last: observations = observations.permute(0, 3, 1, 2) if self.downsample > 1: - observations = observations[:, :, ::self.downsample, ::self.downsample] + observations = observations[:, :, :: self.downsample, :: self.downsample] return self.network(observations.float() / 255.0) def decode_actions(self, flat_hidden): @@ -252,15 +257,17 @@ def decode_actions(self, flat_hidden): value = self.value_fn(flat_hidden) return action, value + class ProcgenResnet(nn.Module): - '''Procgen baseline from the AICrowd NeurIPS 2020 competition - Based on the ResNet architecture that was used in the Impala paper.''' + """Procgen baseline from the AICrowd NeurIPS 2020 competition + Based on the ResNet architecture that was used in the Impala paper.""" + def __init__(self, env, cnn_width=16, mlp_width=256): super().__init__() h, w, c = env.single_observation_space.shape shape = (c, h, w) conv_seqs = [] - for out_channels in [cnn_width, 2*cnn_width, 2*cnn_width]: + for out_channels in [cnn_width, 2 * cnn_width, 2 * cnn_width]: conv_seq = ConvSequence(shape, out_channels) shape = conv_seq.get_output_shape() conv_seqs.append(conv_seq) @@ -271,10 +278,8 @@ def __init__(self, env, cnn_width=16, mlp_width=256): nn.ReLU(), ] self.network = nn.Sequential(*conv_seqs) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(mlp_width, env.single_action_space.n), std=0.01) - self.value = pufferlib.pytorch.layer_init( - nn.Linear(mlp_width, 1), std=1) + self.actor = pufferlib.pytorch.layer_init(nn.Linear(mlp_width, env.single_action_space.n), std=0.01) + self.value = pufferlib.pytorch.layer_init(nn.Linear(mlp_width, 1), std=1) def forward(self, observations, state=None): hidden = self.encode_observations(observations) @@ -287,13 +292,14 @@ def forward_train(self, observations, state=None): def encode_observations(self, x): hidden = self.network(x.permute((0, 3, 1, 2)) / 255.0) return hidden - + def decode_actions(self, hidden): - '''linear decoder function''' + """linear decoder function""" action = self.actor(hidden) value = self.value(hidden) return action, value + class ResidualBlock(nn.Module): def __init__(self, channels): super().__init__() @@ -308,12 +314,15 @@ def forward(self, x): x = self.conv1(x) return x + inputs + class ConvSequence(nn.Module): def __init__(self, input_shape, out_channels): super().__init__() self._input_shape = input_shape self._out_channels = out_channels - self.conv = nn.Conv2d(in_channels=self._input_shape[0], out_channels=self._out_channels, kernel_size=3, padding=1) + self.conv = nn.Conv2d( + in_channels=self._input_shape[0], out_channels=self._out_channels, kernel_size=3, padding=1 + ) self.res_block0 = ResidualBlock(self._out_channels) self.res_block1 = ResidualBlock(self._out_channels) diff --git a/pufferlib/ocean/__init__.py b/pufferlib/ocean/__init__.py index 1c91a2b0f..55822f8a5 100644 --- a/pufferlib/ocean/__init__.py +++ b/pufferlib/ocean/__init__.py @@ -6,6 +6,7 @@ pass else: from .torch import Policy + try: from .torch import Recurrent except: diff --git a/pufferlib/ocean/asteroids/asteroids.c b/pufferlib/ocean/asteroids/asteroids.c deleted file mode 100644 index b74aae994..000000000 --- a/pufferlib/ocean/asteroids/asteroids.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "asteroids.h" - -int main() { - Asteroids env = {.size = 500, .frameskip = 1}; - env.observations = (float *)calloc(4 + 2 * 50, sizeof(float)); - env.actions = (int *)calloc(1, sizeof(int)); - env.rewards = (float *)calloc(1, sizeof(float)); - env.terminals = (unsigned char *)calloc(1, sizeof(unsigned char)); - - c_reset(&env); - c_render(&env); - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) { - env.actions[0] = 0; - } else if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) { - env.actions[0] = 1; - } else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) { - env.actions[0] = 2; - } else if (IsKeyDown(KEY_SPACE)) { - env.actions[0] = 3; - } else { - env.actions[0] = -1; - } - } else { - env.actions[0] = rand() % 4; - } - c_step(&env); - c_render(&env); - } - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} diff --git a/pufferlib/ocean/asteroids/asteroids.h b/pufferlib/ocean/asteroids/asteroids.h deleted file mode 100644 index 50569831b..000000000 --- a/pufferlib/ocean/asteroids/asteroids.h +++ /dev/null @@ -1,600 +0,0 @@ -#pragma once - -#include "raylib.h" -#include -#include -#include -#include - -#define MAX_PARTICLES 10 -#define MAX_ASTEROIDS 20 - -const unsigned char FORWARD = 0; -const unsigned char TURN_LEFT = 1; -const unsigned char TURN_RIGHT = 2; -const unsigned char SHOOT = 3; - -const float FRICTION = 0.95f; -const float SPEED = 0.6f; -const float PARTICLE_SPEED = 7.0f; -const float ROTATION_SPEED = 0.1f; -const float ASTEROID_SPEED = 3.0f; -const int SHOOT_DELAY = 18; - -const int MAX_TICK = 3600; - -const int DEBUG = 0; - -// for render only game over state -static int global_game_over_timer = 0; -static int global_game_over_started = 0; -static int global_render_flag = 0; - -typedef struct { - float perf; - float score; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct { - Vector2 position; - Vector2 velocity; -} Particle; - -typedef struct { - Vector2 position; - Vector2 velocity; - int radius; - int radius_sq; - Vector2 shape[12]; - int num_vertices; -} Asteroid; - -typedef struct { - Asteroid asteroid; - float distance; -} AsteroidDistance; - -typedef struct { - Log log; - float *observations; - int *actions; - float *rewards; - unsigned char *terminals; - int size; - Vector2 player_position; - Vector2 player_vel; - float player_angle; - int player_radius; - int thruster_on; - Particle particles[MAX_PARTICLES]; - int particle_index; - Asteroid asteroids[MAX_ASTEROIDS]; - int asteroid_index; - int last_shot; - int tick; - int score; - float episode_return; - int frameskip; -} Asteroids; - -float random_float(float low, float high) { - return low + (high - low) * ((float)rand() / (float)RAND_MAX); -} - -void generate_asteroid_shape(Asteroid *as) { - as->num_vertices = 8 + (as->radius / 10); - - for (int v = 0; v < as->num_vertices; v++) { - float angle = (2.0f * PI * v) / as->num_vertices; - float radius_variation = - as->radius * (0.7f + 0.6f * random_float(0.0f, 1.0f)); - as->shape[v].x = cosf(angle) * radius_variation; - as->shape[v].y = sinf(angle) * radius_variation; - } -} - -float clamp(float val, float low, float high) { - return fmin(fmax(val, low), high); -} - -Vector2 rotate_vector(Vector2 point, Vector2 center, float angle) { - float s = sinf(angle); - float c = cosf(angle); - - // Translate point back to origin: - point.x -= center.x; - point.y -= center.y; - - // Rotate point - float xnew = point.x * c - point.y * s; - float ynew = point.x * s + point.y * c; - - // Translate point back: - point.x = xnew + center.x; - point.y = ynew + center.y; - return point; -} - -Vector2 get_direction_vector(Asteroids *env) { - float px = env->player_position.x; - float py = env->player_position.y; - Vector2 dir = (Vector2){px, py - 1}; - dir = rotate_vector(dir, env->player_position, env->player_angle); - return (Vector2){dir.x - px, dir.y - py}; -} - -void move_particles(Asteroids *env) { - Particle p; - for (int i = 0; i < MAX_PARTICLES; i++) { - p = env->particles[i]; - p.position.x += p.velocity.x * PARTICLE_SPEED; - p.position.y += p.velocity.y * PARTICLE_SPEED; - env->particles[i] = p; - } -} - -void move_asteroids(Asteroids *env) { - Asteroid *as; - for (int i = 0; i < MAX_ASTEROIDS; i++) { - as = &env->asteroids[i]; - if (as->radius == 0) - continue; - - as->position.x += as->velocity.x * ASTEROID_SPEED; - as->position.y += as->velocity.y * ASTEROID_SPEED; - } -} - -Vector2 angle_to_vector(float angle) { - Vector2 v; - v.x = cosf(angle); - v.y = sinf(angle); - return v; -} - -void spawn_asteroids(Asteroids *env) { - float px, py; - float angle; - if (rand() % 10 == 0) { - switch (rand() % 4) { - case 0: - // left edge - px = 0; - py = rand() % env->size; - angle = random_float(-PI / 2, PI / 2); - break; - case 1: - // right edge - px = env->size; - py = rand() % env->size; - angle = random_float(PI / 2, 3 * PI / 2); - break; - case 2: - // top edge - px = rand() % env->size; - py = 0; - angle = random_float(PI, 2 * PI); - break; - default: - // bottom edge - px = rand() % env->size; - py = env->size; - angle = random_float(0, PI); - break; - } - - Vector2 direction = angle_to_vector(angle); - Vector2 start_pos = (Vector2){px, py}; - Asteroid as; - switch (rand() % 3) { - case 0: - // small - as = (Asteroid){start_pos, direction, 10, 100}; - break; - case 1: - // medium - as = (Asteroid){start_pos, direction, 20, 400}; - break; - default: - // big - as = (Asteroid){start_pos, direction, 40, 1600}; - break; - } - env->asteroid_index = (env->asteroid_index + 1) % MAX_ASTEROIDS; - env->asteroids[env->asteroid_index] = as; - if (global_render_flag) - generate_asteroid_shape(&env->asteroids[env->asteroid_index]); - } -} - -int particle_asteroid_collision(Asteroids *env, Particle *p, Asteroid *as) { - float dx = p->position.x - as->position.x; - float dy = p->position.y - as->position.y; - return as->radius_sq > dx * dx + dy * dy; -} - -void split_asteroid(Asteroids *env, Asteroid *as) { - int new_radius = as->radius == 40 ? 20 : 10; - - float original_angle = atan2f(as->velocity.y, as->velocity.x); - - float offset1 = random_float(-PI / 4, PI / 4); - float offset2 = random_float(-PI / 4, PI / 4); - - float angle1 = original_angle + offset1; - float angle2 = original_angle + offset2; - - Vector2 direction1 = angle_to_vector(angle1); - Vector2 direction2 = angle_to_vector(angle2); - - float len1 = sqrtf(direction1.x * direction1.x + direction1.y * direction1.y); - float len2 = sqrtf(direction2.x * direction2.x + direction2.y * direction2.y); - if (len1 > 0) { - direction1.x /= len1; - direction1.y /= len1; - } - if (len2 > 0) { - direction2.x /= len2; - direction2.y /= len2; - } - - Vector2 start_pos = (Vector2){as->position.x, as->position.y}; - - int new_index1 = (env->asteroid_index + 1) % MAX_ASTEROIDS; - int new_index2 = (new_index1 + 1) % MAX_ASTEROIDS; - - as->position = start_pos; - as->velocity = direction1; - as->radius = new_radius; - as->radius_sq = new_radius * new_radius; - env->asteroids[new_index1] = (Asteroid){start_pos, direction2, new_radius}; - env->asteroid_index = new_index2; - - // Generate shapes for the new asteroids - generate_asteroid_shape(as); - generate_asteroid_shape(&env->asteroids[new_index1]); -} - -void check_particle_asteroid_collision(Asteroids *env) { - Particle *p; - Asteroid *as; - for (int i = 0; i < MAX_PARTICLES; i++) { - p = &env->particles[i]; - if (p->position.x == 0 && p->position.y == 0) - continue; - - for (int j = 0; j < MAX_ASTEROIDS; j++) { - as = &env->asteroids[j]; - if (as->radius == 0) - continue; - - if (particle_asteroid_collision(env, p, as)) { - memset(p, 0, sizeof(*p)); - env->score += 1; - env->rewards[0] += 1.0f; - - switch (as->radius) { - case 10: - memset(as, 0, sizeof(*as)); - break; - case 20: - split_asteroid(env, as); - break; - default: - split_asteroid(env, as); - break; - } - break; - } - } - } -} - -void check_player_asteroid_collision(Asteroids *env) { - float min_dist; - float dx, dy; - Asteroid *as; - for (int i = 0; i < MAX_ASTEROIDS; i++) { - as = &env->asteroids[i]; - if (as->radius == 0) - continue; - - min_dist = env->player_radius + as->radius; - dx = env->player_position.x - as->position.x; - dy = env->player_position.y - as->position.y; - if (min_dist * min_dist > dx * dx + dy * dy) { - env->terminals[0] = 1; - env->rewards[0] = -1.0f; - return; - } - } -} - -void compute_observations(Asteroids *env) { - int observation_indx = 0; - env->observations[observation_indx++] = env->player_position.x / env->size; - env->observations[observation_indx++] = env->player_position.y / env->size; - env->observations[observation_indx++] = env->player_vel.x; - env->observations[observation_indx++] = env->player_vel.y; - - // Create temporary array to store asteroids with their distances - AsteroidDistance asteroid_distances[MAX_ASTEROIDS]; - - int num_active_asteroids = 0; - - // Calculate distances and store active asteroids - for (int i = 0; i < MAX_ASTEROIDS; i++) { - Asteroid as = env->asteroids[i]; - if (as.radius == 0) - continue; - - float dx = as.position.x - env->player_position.x; - float dy = as.position.y - env->player_position.y; - float distance = dx * dx + dy * dy; - - asteroid_distances[num_active_asteroids].asteroid = as; - asteroid_distances[num_active_asteroids].distance = distance; - num_active_asteroids++; - } - - // Sort asteroids by distance (bubble sort for simplicity) - for (int i = 0; i < num_active_asteroids - 1; i++) { - for (int j = 0; j < num_active_asteroids - i - 1; j++) { - if (asteroid_distances[j].distance > asteroid_distances[j + 1].distance) { - AsteroidDistance temp = asteroid_distances[j]; - asteroid_distances[j] = asteroid_distances[j + 1]; - asteroid_distances[j + 1] = temp; - } - } - } - - // Output sorted asteroids to observations (up to MAX_ASTEROIDS) - for (int i = 0; i < MAX_ASTEROIDS; i++) { - if (i < num_active_asteroids) { - Asteroid as = asteroid_distances[i].asteroid; - env->observations[observation_indx++] = - (as.position.x - env->player_position.x) / env->size; - env->observations[observation_indx++] = - (as.position.y - env->player_position.y) / env->size; - env->observations[observation_indx++] = as.velocity.x; - env->observations[observation_indx++] = as.velocity.y; - env->observations[observation_indx++] = (float)as.radius / 40; - } else { - // Pad with zeros for missing asteroids to ensure fixed observation size - env->observations[observation_indx++] = 0.0f; // relative x - env->observations[observation_indx++] = 0.0f; // relative y - env->observations[observation_indx++] = 0.0f; // velocity x - env->observations[observation_indx++] = 0.0f; // velocity y - env->observations[observation_indx++] = 0.0f; // radius - } - } -} - -void add_log(Asteroids *env) { - env->log.perf += env->score / 100.0f; - env->log.score += env->score; - env->log.episode_length += env->tick; - env->log.episode_return += env->episode_return; - env->log.n++; -} - -void c_reset(Asteroids *env) { - env->player_position = (Vector2){env->size / 2.0f, env->size / 2.0f}; - env->player_angle = 0.0f; - env->player_radius = 12; - env->player_vel = (Vector2){0, 0}; - env->thruster_on = 0; - memset(env->particles, 0, sizeof(Particle) * MAX_PARTICLES); - memset(env->asteroids, 0, sizeof(Asteroid) * MAX_ASTEROIDS); - env->particle_index = 0; - env->asteroid_index = 0; - env->tick = 0; - env->score = 0; - env->episode_return = 0; - env->last_shot = 0; -} - -void step_frame(Asteroids *env, int action) { - // slow down each step - env->player_vel.x *= FRICTION; - env->player_vel.y *= FRICTION; - - Vector2 dir = get_direction_vector(env); - - if (action == TURN_LEFT) - env->player_angle -= ROTATION_SPEED; - if (action == TURN_RIGHT) - env->player_angle += ROTATION_SPEED; - if (action == FORWARD) { - env->player_vel.x += dir.x * SPEED; - env->player_vel.y += dir.y * SPEED; - env->thruster_on = 1; - } - - int elapsed = env->tick - env->last_shot; - - if (action == SHOOT && elapsed >= SHOOT_DELAY) { - env->last_shot = env->tick; - env->particle_index = (env->particle_index + 1) % MAX_PARTICLES; - Vector2 start_pos = (Vector2){env->player_position.x + 20 * dir.x, - env->player_position.y + 20 * dir.y}; - env->particles[env->particle_index] = (Particle){start_pos, dir}; - } - - // Explicit Euler - env->player_position.x += env->player_vel.x; - env->player_position.y += env->player_vel.y; - - move_particles(env); - spawn_asteroids(env); - move_asteroids(env); - check_particle_asteroid_collision(env); - check_player_asteroid_collision(env); - - if (env->player_position.x < 0) - env->player_position.x = env->size; - if (env->player_position.y < 0) - env->player_position.y = env->size; - if (env->player_position.x > env->size) - env->player_position.x = 0; - if (env->player_position.y > env->size) - env->player_position.y = 0; -} - -void c_step(Asteroids *env) { - env->rewards[0] = 0; - env->terminals[0] = 0; - env->thruster_on = 0; - - // only when rendering - if (global_game_over_timer > 0) - return; - - int action = env->actions[0]; - for (int i = 0; i < env->frameskip; i++) { - env->tick += 1; - step_frame(env, action); - } - - env->episode_return += env->rewards[0]; - if (env->terminals[0] == 1 || env->tick > MAX_TICK) { - env->terminals[0] = 1; - add_log(env); - c_reset(env); - return; - } - - // env->rewards[0] = env->score; - compute_observations(env); -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -void draw_player(Asteroids *env) { - if (global_game_over_timer > 0) - return; - - float px = env->player_position.x; - float py = env->player_position.y; - - if (DEBUG) { - DrawPixel(px, py, RED); - Vector2 dir = get_direction_vector(env); - dir = (Vector2){dir.x * 10.0f, dir.y * 10.f}; - Vector2 t = (Vector2){dir.x + px, dir.y + py}; - DrawLineV(env->player_position, t, RED); - DrawCircleLines(px, py, env->player_radius, RED); - } - - Vector2 ps[8]; - - // ship - ps[0] = (Vector2){px - 10, py + 10}; - ps[1] = (Vector2){px + 10, py + 10}; - ps[2] = (Vector2){px, py - 20}; - ps[3] = (Vector2){px - 9, py + 6}; - ps[4] = (Vector2){px + 9, py + 6}; - ps[5] = (Vector2){px - 5, py + 6}; - ps[6] = (Vector2){px + 5, py + 6}; - ps[7] = (Vector2){px, py + 14}; - - for (int i = 0; i < 8; i++) - ps[i] = rotate_vector(ps[i], env->player_position, env->player_angle); - - DrawLineV(ps[0], ps[2], PUFF_RED); - DrawLineV(ps[1], ps[2], PUFF_RED); - - DrawLineV(ps[3], ps[4], PUFF_RED); - - if (env->thruster_on) { - DrawLineV(ps[5], ps[7], PUFF_RED); - DrawLineV(ps[6], ps[7], PUFF_RED); - } -} - -void draw_particles(Asteroids *env) { - for (int i = 0; i < MAX_PARTICLES; i++) { - DrawCircle(env->particles[i].position.x, env->particles[i].position.y, 2, PUFF_RED); - } -} - -void draw_asteroids(Asteroids *env) { - Asteroid as; - for (int i = 0; i < MAX_ASTEROIDS; i++) { - as = env->asteroids[i]; - if (as.radius == 0) - continue; - - if (DEBUG) - DrawCircleLines(as.position.x, as.position.y, as.radius, RED); - - for (int v = 0; v < as.num_vertices; v++) { - int next_v = (v + 1) % as.num_vertices; - Vector2 pos1 = {as.position.x + as.shape[v].x, - as.position.y + as.shape[v].y}; - Vector2 pos2 = {as.position.x + as.shape[next_v].x, - as.position.y + as.shape[next_v].y}; - DrawLineV(pos1, pos2, PUFF_CYAN); - } - } -} - -void c_render(Asteroids *env) { - if (!IsWindowReady()) { - InitWindow(env->size, env->size, "PufferLib Asteroids"); - SetConfigFlags(FLAG_MSAA_4X_HINT); - SetTargetFPS(60); - global_render_flag = 1; - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - if (env->terminals[0] == 1 && !global_game_over_started) { - global_game_over_started = 1; - global_game_over_timer = 120; - } - - if (global_game_over_timer > 0) { - global_game_over_timer--; - } else { - global_game_over_started = 0; - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - draw_player(env); - draw_particles(env); - draw_asteroids(env); - - DrawText(TextFormat("Score: %d", env->score), 10, 10, 20, PUFF_WHITE); - DrawText(TextFormat("%d s", (int)(env->tick / 60)), env->size - 40, 10, 20, PUFF_WHITE); - - if (global_game_over_timer > 0) { - const char *game_over_text = "GAME OVER"; - int text_width = MeasureText(game_over_text, 40); - int x = (env->size - text_width) / 2; - int y = env->size / 2 - 20; - - float alpha = (float)global_game_over_timer / 120.0f; - int alpha_value = (int)(alpha * 255); - - Color text_color = ColorAlpha(PUFF_RED, alpha_value); - DrawTextEx(GetFontDefault(), game_over_text, (Vector2){x, y}, 40, 2, text_color); - } - - EndDrawing(); -} - -void c_close(Asteroids *env) { - if (IsWindowReady()) { - CloseWindow(); - } -} diff --git a/pufferlib/ocean/asteroids/asteroids.py b/pufferlib/ocean/asteroids/asteroids.py deleted file mode 100644 index cc3f6f9eb..000000000 --- a/pufferlib/ocean/asteroids/asteroids.py +++ /dev/null @@ -1,53 +0,0 @@ -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.asteroids import binding - -class Asteroids(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, log_interval=128, buf=None, seed=0, size=500, frameskip=4): - obs_shape = 4 + 5 * 20 # player pos, player vel, [asteroid pos, asteroid vel, asteroid size] x num asteroids - self.single_observation_space = gymnasium.spaces.Box(low=-5, high=5, - shape=(obs_shape,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(4) # forward, left, right, shoot - self.render_mode = render_mode - self.num_agents = num_envs - - super().__init__(buf) - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, size=size, frameskip=frameskip) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - info = [binding.vec_log(self.c_envs)] - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 4096 - env = Asteroids(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, 5, (CACHE, N)) - - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[steps % CACHE]) - steps += 1 - - sps = int(env.num_agents*steps / (time.time() - start)) - print(f'Asteroids SPS: {sps:,}') diff --git a/pufferlib/ocean/asteroids/binding.c b/pufferlib/ocean/asteroids/binding.c deleted file mode 100644 index 493d3ab9d..000000000 --- a/pufferlib/ocean/asteroids/binding.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "asteroids.h" - -#define Env Asteroids -#include "../env_binding.h" - -static int my_init(Env *env, PyObject *args, PyObject *kwargs) { - env->size = unpack(kwargs, "size"); - env->frameskip = unpack(kwargs, "frameskip"); - return 0; -} - -static int my_log(PyObject *dict, Log *log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/battle/battle.c b/pufferlib/ocean/battle/battle.c deleted file mode 100644 index c6193318f..000000000 --- a/pufferlib/ocean/battle/battle.c +++ /dev/null @@ -1,209 +0,0 @@ -/* Pure C demo file for Battle. Build it with: - * bash scripts/build_ocean.sh battle local (debug) - * bash scripts/build_ocean.sh battle fast - * We suggest building and debugging your env in pure C first. You - * get faster builds and better error messages - */ -#include "battle.h" - -/* Puffernet is our lightweight cpu inference library that - * lets you load basic PyTorch model architectures so that - * you can run them in pure C or on the web via WASM - */ -#include "puffernet.h" - -int main() { - // Weights are exported by running puffer export - //Weights* weights = load_weights("resources/puffer_battle_weights.bin", 137743); - - //int logit_sizes[2] = {9, 5}; - //LinearLSTM* net = make_linearlstm(weights, num_agents, num_obs, logit_sizes, 2); - - Battle env = { - .width = 1980, - .height = 1020, - .size_x = 8, - .size_y = 2.0, - .size_z = 8, - .num_agents = 1024, - .num_armies = 8, - }; - init(&env); - - // Allocate these manually since they aren't being passed from Python - int num_obs = 3*env.num_armies + 4*16 + 22 + 8; - env.observations = calloc(env.num_agents*num_obs, sizeof(float)); - env.actions = calloc(3*env.num_agents, sizeof(int)); - env.rewards = calloc(env.num_agents, sizeof(float)); - env.terminals = calloc(env.num_agents, sizeof(unsigned char)); - - // Always call reset and render first - c_reset(&env); - c_render(&env); - - int ctrl = 0; - - while (!WindowShouldClose()) { - /* - for (int i=0; iarmy; - float vx = env.observations[num_obs*i + 3*army]; - float vz = env.observations[num_obs*i + 3*army + 2]; - float yaw = env.observations[num_obs*i + 3*army + 3]; - float pitch = env.observations[num_obs*i + 3*army + 4]; - float x = env.observations[num_obs*i + 3*army + 6]; - float y = env.observations[num_obs*i + 3*army + 7]; - float z = env.observations[num_obs*i + 3*army + 8]; - - if (agent->unit == INFANTRY || agent->unit == TANK || agent->unit == ARTILLERY) { - env.actions[3*i] = (vx > 0.0f) ? 6 : 2; - env.actions[3*i + 2] = (vz > 0.0f) ? 6 : 2; - } else { - float desired_pitch = atan2f(-y, sqrt(x*x + z*z)); - float pitch_error = desired_pitch - pitch; - if (pitch_error > 0) { - env.actions[3*i] = 6; // pitch up - } else if (pitch_error < 0) { - env.actions[3*i] = 2; // pitch down - } - env.actions[3*i] = 4; - - env.actions[3*i + 1] = 4; - - // Roll control - float desired_yaw = atan2f(-x, -z); // Direction to origin - float yaw_error = desired_yaw - yaw; - - // Normalize yaw_error to [-PI, PI] - if (yaw_error > PI) yaw_error -= 2*PI; - if (yaw_error < -PI) yaw_error += 2*PI; - - //printf("%f %f\n", yaw_error, yaw); - - if (yaw_error > 0.1f) { - env.actions[3*i + 1] = 2; // roll left - } else if (yaw_error < -0.1f) { - env.actions[3*i + 1] = 6; // roll right - } else { - env.actions[3*i + 1] = 4; // neutral roll (assuming 0 is valid) - } - - } - //env.actions[3*i] = 4; - //env.actions[3*i + 1] = 4; - //env.actions[3*i + 2] = 4; - //float dpitch = atan2f(dz, sqrtf(dx*dx + dy*dy)); - //float droll = asinf(dz/sqrtf(dx*dx + dy*dy + dz*dz)); - //env.actions[3*i] = 6; - //env.actions[3*i + 1] = (dpitch > 0.0f) ? 6 : 2; - //env.actions[3*i + 2] = (droll > 0.0f) ? 6 : 2; - //env.actions[3*i] = rand() % 9; - //env.actions[3*i + 1] = rand() % 9; - //env.actions[3*i + 2] = rand() % 9; - //env.actions[3*i] = 4.0f; - //env.actions[3*i + 1] = 4.0f; - //env.actions[3*i + 2] = 4.0f; - } - */ - - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyPressed(KEY_TAB)) { - ctrl = (ctrl + 1) % env.num_agents; - } - int i = ctrl; - float x = env.observations[num_obs*i + 3*env.num_armies + 6]; - float y = env.observations[num_obs*i + 3*env.num_armies + 7]; - float z = env.observations[num_obs*i + 3*env.num_armies + 8]; - - Camera3D* camera = &(env.client->camera); - camera->target = (Vector3){x, y, z}; - - - Entity* agent = &env.agents[i]; - Vector3 forward = Vector3RotateByQuaternion((Vector3){0, 0, 1}, agent->orientation); - - Vector3 local_up = Vector3RotateByQuaternion((Vector3){0, 1, 0}, agent->orientation); - local_up = Vector3Normalize(local_up); - - camera->target = (Vector3){agent->x, agent->y, agent->z}; - - camera->position = (Vector3){ - agent->x + 0.5*(- forward.x), - agent->y + 0.5*(- forward.y) + 0.5f, - agent->z + 0.5*(- forward.z) - }; - - - /* - Entity* agent = &env.agents[i]; - Vec3 forward = quat_rotate(agent->orientation, (Vec3){0, 0, -1}); // Ship's local forward - vec3_normalize(&forward); - - Vec3 local_up = quat_rotate(agent->orientation, (Vec3){0, 1, 0}); // Ship's local up - vec3_normalize(&local_up); - - // Compute negative forward and negative local up - Vec3 neg_forward = {-forward.x, -forward.y, -forward.z}; - Vec3 neg_local_up = {-local_up.x, -local_up.y, -local_up.z}; - - // Compute the vector 45 degrees between neg_forward and neg_local_up - // Since forward and local_up are orthogonal, averaging gives a 45-degree angle - Vec3 camera_dir = { - neg_forward.x + neg_local_up.x, - neg_forward.y + neg_local_up.y, - neg_forward.z + neg_local_up.z - }; - vec3_normalize(&camera_dir); - printf("camera_dir: %f %f %f\n", camera_dir.x, camera_dir.y, camera_dir.z); - - // Scale by desired distance and offset from ship's position - camera->position = (Vector3){ - agent->x + 1.0f*camera_dir.x, - agent->y + 1.0f*camera_dir.y, - agent->z + 1.0f*camera_dir.z - }; - - float dd = sqrtf(vx*vx + vy*vy + vz*vz); - float forward_x = vx / dd; - float forward_y = vy / dd; - float forward_z = vz / dd; - - float dist = 0.5f; - camera->position = (Vector3){ - x - dist*forward_x, - y - dist*forward_y + 0.5f, - z - dist*forward_z - }; - */ - - env.actions[3*i] = 4; - if (IsKeyDown(KEY_W)) { - env.actions[3*i] = 6; - } else if (IsKeyDown(KEY_S)) { - env.actions[3*i] = 2; - } - - env.actions[3*i + 1] = 4; - if (IsKeyDown(KEY_A)) { - env.actions[3*i + 1] = 2; - } else if (IsKeyDown(KEY_D)) { - env.actions[3*i + 1] = 6; - } - } - - //forward_linearlstm(net, env.observations, env.actions); - compute_observations(&env); - c_step(&env); - c_render(&env); - } - - // Try to clean up after yourself - //free_linearlstm(net); - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} - diff --git a/pufferlib/ocean/battle/battle.h b/pufferlib/ocean/battle/battle.h deleted file mode 100644 index 35d97c77e..000000000 --- a/pufferlib/ocean/battle/battle.h +++ /dev/null @@ -1,1199 +0,0 @@ -/* Battle: a sample multiagent env about puffers eating stars. - * Use this as a tutorial and template for your own multiagent envs. - * We suggest starting with the Squared env for a simpler intro. - * Star PufferLib on GitHub to support. It really, really helps! - */ - -#include -#include -#include -#include -#include -#include -#include "raylib.h" -#include "raymath.h" -#include "rlgl.h" -#include "simplex.h" - -#define RLIGHTS_IMPLEMENTATION -#include "rlights.h" - - -#if defined(PLATFORM_DESKTOP) - #define GLSL_VERSION 330 -#else - #define GLSL_VERSION 100 -#endif - -#define MAX_SPEED 0.01f -#define MAX_FACTORY_SPEED 0.001f - -#define DRONE 0 -#define MOTHERSHIP 1 -#define FIGHTER 2 -#define BOMBER 3 -#define INFANTRY 4 -#define TANK 5 -#define ARTILLERY 6 -#define BASE 7 - -#define AGENT_OBS 16 - -static inline float clampf(float v, float min, float max) { - if (v < min) - return min; - if (v > max) - return max; - return v; -} - -float clip(float val, float min, float max) { - if (val < min) { - return min; - } else if (val > max) { - return max; - } - return val; -} - -float clip_angle(float theta) { - if (theta < -PI) { - return theta + 2.0f*PI; - } else if (theta > PI) { - return theta - 2.0f*PI; - } - return theta; -} - -float randf(float min, float max) { - return min + (max - min)*(float)rand()/(float)RAND_MAX; -} - -float randi(int min, int max) { - return min + (max - min)*(float)rand()/(float)RAND_MAX; -} - -typedef struct { - float perf; - float score; - float collision_rate; - float oob_rate; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct { - Camera3D camera; - Light light; - Model models[8]; - Mesh* mesh; - Model model; - Shader light_shader; - Shader terrain_shader; - Texture2D terrain_texture; - Texture2D vehicle_texture; - int terrain_shader_loc; - unsigned char *terrain_data; -} Client; - -typedef struct { - float x; - float y; - float z; - float vx; - float vy; - float vz; - float speed; - float health; - float max_turn; - float max_speed; - float attack_damage; - float attack_range; - Quaternion orientation; - int army; - int unit; - int target; - int episode_length; - float episode_return; -} Entity; - -typedef struct { - Log log; - Client* client; - Entity* agents; - Entity* bases; - float* observations; - float* actions; - float* rewards; - unsigned char* terminals; - int width; - int height; - float size_x; - float size_y; - float size_z; - int terrain_width; - int terrain_height; - int num_agents; - int num_armies; - float* terrain; -} Battle; - -int map_idx(Battle* env, float x, float y) { - return env->terrain_width*(int)y + (int)x; -} - -float ground_height(Battle* env, float x, float z) { - int agent_map_x = 128*x + 128*env->size_x; - int agent_map_z = 128*z + 128*env->size_z; - if (agent_map_x == 256*env->size_x) { - agent_map_x -= 1; - } - if (agent_map_z == 256*env->size_z) { - agent_map_z -= 1; - } - int idx = map_idx(env, agent_map_x, agent_map_z); - float terrain_height = env->terrain[idx]; - return (terrain_height - 128.0f*env->size_y) / 128.0f; -} - -void perlin_noise(float* map, int width, int height, - float base_frequency, int octaves, int offset_x, int offset_y, float glob_scale) { - float frequencies[octaves]; - for (int i = 0; i < octaves; i++) { - frequencies[i] = base_frequency*pow(2, i); - } - - float min_value = FLT_MAX; - float max_value = FLT_MIN; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - for (int oct = 0; oct < octaves; oct++) { - float freq = frequencies[oct]; - map[adr] += (1.0/pow(2, oct))*noise2(freq*c + offset_x, freq*r + offset_y); - } - float val = map[adr]; - if (val < min_value) { - min_value = val; - } - if (val > max_value) { - max_value = val; - } - } - } - - float scale = 1.0/(max_value - min_value); - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - map[adr] = glob_scale * scale * (map[adr] - min_value); - if (map[adr] < 16.0f) { - map[adr] = 0.0f; - } else { - map[adr] -= 16.0f; - } - } - } -} - -void init(Battle* env) { - env->agents = calloc(env->num_agents, sizeof(Entity)); - env->bases = calloc(env->num_armies, sizeof(Entity)); - env->terrain_width = 256*env->size_x; - env->terrain_height = 256*env->size_z; - env->terrain = calloc(env->terrain_width*env->terrain_height, sizeof(float)); - perlin_noise(env->terrain, env->terrain_width, env->terrain_height, 1.0/2048.0, 8, 0, 0, 256); -} - -void update_abilities(Entity* agent) { - if (agent->unit == DRONE) { - agent->health = 0.4f; - agent->attack_damage = 0.1f; - agent->attack_range = 0.15f; - agent->max_turn = 2.0f; - agent->max_speed = 1.0f; - } else if (agent->unit == FIGHTER) { - agent->health = 1.0f; - agent->attack_damage = 0.5f; - agent->attack_range = 0.25f; - agent->max_turn = 1.0f; - agent->max_speed = 0.75f; - } else if (agent->unit == MOTHERSHIP) { - agent->health = 10.0f; - agent->attack_damage = 2.0f; - agent->attack_range = 0.4f; - agent->max_turn = 0.5f; - agent->max_speed = 0.5f; - } else if (agent->unit == BOMBER) { - agent->health = 1.0f; - agent->attack_damage = 1.0f; - agent->attack_range = 0.1f; - agent->max_turn = 0.5f; - agent->max_speed = 0.5f; - } else if (agent->unit == INFANTRY) { - agent->health = 0.2f; - agent->attack_damage = 0.2f; - agent->attack_range = 0.2f; - agent->max_turn = 2.0f; - agent->max_speed = 0.25f; - } else if (agent->unit == TANK) { - agent->health = 2.0f; - agent->attack_damage = 0.5f; - agent->attack_range = 0.25f; - agent->max_turn = 0.25f; - agent->max_speed = 0.75f; - } else if (agent->unit == ARTILLERY) { - agent->health = 2.0f; - agent->attack_damage = 2.0f; - agent->attack_range = 0.7f; - agent->max_turn = 0.5f; - agent->max_speed = 0.25f; - } -} - -void respawn(Battle* env, int idx) { - Entity* agent = &env->agents[idx]; - int army = agent->army; - agent->orientation = QuaternionIdentity(); - agent->vx = 0; - agent->vy = 0; - agent->vz = 0; - - if (agent->unit == DRONE) { - int team_mothership_idx = 64*(idx / 64); // Hardcoded per army - agent->x = env->agents[team_mothership_idx].x; - agent->y = env->agents[team_mothership_idx].y; - agent->z = env->agents[team_mothership_idx].z; - if (agent->unit == INFANTRY || agent->unit == TANK || agent->unit == ARTILLERY) { - agent->y = ground_height(env, agent->x, agent->z); - } - return; - } - - Entity* base = &env->bases[army]; - agent->x = base->x; - agent->z = base->z; - float height = ground_height(env, agent->x, agent->z); - if (agent->unit == INFANTRY || agent->unit == TANK || agent->unit == ARTILLERY) { - agent->y = height; - } else { - agent->y = clampf(height + 0.2f, -env->size_y, env->size_y); - } - - return; -} - - -bool attack_air(Entity *agent, Entity *target) { - float dx = target->x - agent->x; - float dy = target->y - agent->y; - float dz = target->z - agent->z; - float dd = sqrtf(dx*dx + dy*dy + dz*dz); - - if (dd > agent->attack_range) { - return false; - } - - Vector3 forward = Vector3RotateByQuaternion((Vector3){0, 0, 1}, agent->orientation); - forward = Vector3Normalize(forward); - - // Unit vec to target - Vector3 to_target = {dx, dy, dz}; - to_target = Vector3Normalize(to_target); - - float angle = Vector3Angle(forward, to_target); - if (angle < PI/6.0f) { - return true; - } - return false; -} - -bool attack_ground(Entity *agent, Entity *target) { - if (target->unit == FIGHTER) { - return false; - } - if (target->unit == MOTHERSHIP) { - return false; - } - if (target->unit == BOMBER) { - return false; - } - if (target->unit == DRONE) { - return false; - } - - float dx = target->x - agent->x; - float dz = target->z - agent->z; - float dd = sqrtf(dx*dx + dz*dz); - - if (dd > agent->attack_range) { - return false; - } - - Vector3 forward = Vector3RotateByQuaternion((Vector3){0, 0, 1}, agent->orientation); - forward = Vector3Normalize(forward); - - // Unit vec to target - Vector3 to_target = {dx, 0, dz}; - to_target = Vector3Normalize(to_target); - - float angle = Vector3Angle(forward, to_target); - if (angle < PI/6) { - return true; - } - return false; -} - -bool attack_bomber(Entity *agent, Entity *target) { - if (target->unit == DRONE) { - return false; - } - if (target->unit == FIGHTER) { - return false; - } - if (target->unit == MOTHERSHIP) { - return false; - } - if (target->unit == BOMBER) { - return false; - } - - float dx = target->x - agent->x; - float dz = target->z - agent->z; - float dd = sqrtf(dx*dx + dz*dz); - - if (dd > agent->attack_range) { - return false; - } - - return true; -} - -bool attack_aa(Entity *agent, Entity *target) { - if (target->unit == INFANTRY) { - return false; - } - if (target->unit == TANK) { - return false; - } - if (target->unit == ARTILLERY) { - return false; - } - - float dx = target->x - agent->x; - float dy = target->y - agent->y; - float dz = target->z - agent->z; - float dd = sqrtf(dx*dx + dz*dz); - - if (dd > agent->attack_range) { - return false; - } - - Vector3 forward = Vector3RotateByQuaternion((Vector3){0, 0, 1}, agent->orientation); - forward = Vector3Normalize(forward); - - // Unit vec to target - Vector3 to_target = {dx, dy, dz}; - to_target = Vector3Normalize(to_target); - - float angle = Vector3Angle(forward, to_target); - if (angle < PI/6) { - return true; - } -} - -void move_basic(Battle* env, Entity* agent, float* actions) { - float d_vx = actions[0]/100.0f; - float d_vy = actions[1]/100.0f; - float d_vz = actions[2]/100.0f; - - agent->vx += d_vx; - agent->vy += d_vy; - agent->vz += d_vz; - - agent->vx = clip(agent->vx, -MAX_SPEED, MAX_SPEED); - agent->vy = clip(agent->vy, -MAX_SPEED, MAX_SPEED); - agent->vz = clip(agent->vz, -MAX_SPEED, MAX_SPEED); - - agent->x += agent->vx; - agent->y += agent->vy; - agent->z += agent->vz; - - agent->x = clip(agent->x, -env->size_x, env->size_x); - agent->y = clip(agent->y, -env->size_y, env->size_y); - agent->z = clip(agent->z, -env->size_z, env->size_z); -} - -void move_ground(Battle* env, Entity* agent, float* actions) { - float d_theta = -actions[1]/10.0f; - - // Update speed and clamp - agent->speed = agent->max_speed * MAX_SPEED; - - Quaternion q_y = QuaternionFromAxisAngle((Vector3){0, 1, 0}, d_theta); - agent->orientation = QuaternionMultiply(q_y, agent->orientation); - - Vector3 forward = Vector3RotateByQuaternion((Vector3){0, 0, 1}, agent->orientation); - forward = Vector3Normalize(forward); - - agent->speed = agent->max_speed * MAX_SPEED; - agent->vx = agent->speed * forward.x; - agent->vz = agent->speed * forward.z; - agent->x += agent->vx; - agent->z += agent->vz; - - agent->x = clip(agent->x, -env->size_x, env->size_x); - agent->z = clip(agent->z, -env->size_z, env->size_z); - agent->y = ground_height(env, agent->x, agent->z); -} - -Entity* nearest_enemy(Battle* env, Entity* agent) { - Entity* nearest = NULL; - float nearest_dist = 999999; - for (int i=0; inum_agents; i++) { - Entity* other = &env->agents[i]; - if (other->army == agent->army) { - continue; - } - float dx = other->x - agent->x; - float dy = other->y - agent->y; - float dz = other->z - agent->z; - float dd = dx*dx + dy*dy + dz*dz; - if (dd < nearest_dist) { - nearest_dist = dd; - nearest = other; - } - } - return nearest; -} - -// Cheats physics and moves directly to the nearest enemy -void scripted_move(Battle* env, Entity* agent, bool is_air) { - Entity* target = nearest_enemy(env, agent); - if (target == NULL) { - return; - } - float dx = target->x - agent->x; - float dy = target->y - agent->y; - float dz = target->z - agent->z; - - // Add some noise - dx += randf(-0.1f, 0.1f); - dy += randf(-0.1f, 0.1f); - dz += randf(-0.1f, 0.1f); - - float dd = dx*dx + dz*dz; - if (is_air) { - dd += dy*dy; - } - - dd = sqrtf(dd); - dx /= dd; - dy /= dd; - dz /= dd; - - - float target_x; - float target_y; - float target_z; - if (dd > 0.05f) { - target_x = agent->x + dx*agent->max_speed*MAX_SPEED; - target_y = agent->y + dy*agent->max_speed*MAX_SPEED; - target_z = agent->z + dz*agent->max_speed*MAX_SPEED; - } else { - target_x = agent->x - dx*agent->max_speed*MAX_SPEED; - target_y = agent->y - dy*agent->max_speed*MAX_SPEED; - target_z = agent->z - dz*agent->max_speed*MAX_SPEED; - } - - float height = ground_height(env, target_x, target_z); - if (is_air) { - if (target_y < height + 0.5f) { - target_y = height + 0.5f; - } - } else { - target_y = height; - } - - agent->x = target_x; - agent->y = target_y; - agent->z = target_z; - - agent->x = clip(agent->x, -env->size_x, env->size_x); - agent->y = clip(agent->y, -env->size_y, env->size_y); - agent->z = clip(agent->z, -env->size_z, env->size_z); - - // Update orientation to target - Vector3 target_forward = {dx, 0, dz}; - if (is_air) { - target_forward.y = dy; - } - target_forward = Vector3Normalize(target_forward); - - Vector3 current_forward = Vector3RotateByQuaternion((Vector3){0, 0, 1}, agent->orientation); - current_forward = Vector3Normalize(current_forward); - - Quaternion q = QuaternionFromVector3ToVector3(current_forward, target_forward); - agent->orientation = QuaternionMultiply(q, agent->orientation); -} - -void move_ship(Battle* env, Entity* agent, float* actions, int i) { - // Compute deltas from actions (same as original) - float d_pitch = agent->max_turn * actions[0] / 10.0f; - float d_roll = agent->max_turn * actions[1] / 10.0f; - - // Update speed and clamp - agent->speed = agent->max_speed * MAX_SPEED; - - Vector3 forward = Vector3RotateByQuaternion((Vector3){0, 0, 1}, agent->orientation); - forward = Vector3Normalize(forward); - - Vector3 local_up = Vector3RotateByQuaternion((Vector3){0, 1, 0}, agent->orientation); - local_up = Vector3Normalize(local_up); - - Vector3 right = Vector3CrossProduct(forward, local_up); // Ship's local right - right = Vector3Normalize(right); - - // Create rotation quaternions - /* - if (i == 0) { - printf("actions: %d %d %d\n", actions[0], actions[1], actions[2]); - printf("orientation: %f %f %f %f\n", agent->orientation.w, agent->orientation.x, agent->orientation.y, agent->orientation.z); - printf("Local up: %f %f %f\n", local_up.x, local_up.y, local_up.z); - printf("Forward: %f %f %f\n", forward.x, forward.y, forward.z); - printf("Right: %f %f %f\n", right.x, right.y, right.z); - printf("d_pitch: %f\n, d_roll: %f\n", d_pitch, d_roll); - } - */ - - float d_yaw = 0.0; - Quaternion q_yaw = QuaternionFromAxisAngle(local_up, d_yaw); - Quaternion q_roll = QuaternionFromAxisAngle(forward, d_roll); - Quaternion q_pitch = QuaternionFromAxisAngle(right, d_pitch); - - /* - if (i == 0) { - printf("q_yaw: %f %f %f %f\n", q_yaw.w, q_yaw.x, q_yaw.y, q_yaw.z); - printf("q_roll: %f %f %f %f\n", q_roll.w, q_roll.x, q_roll.y, q_roll.z); - printf("q_pitch: %f %f %f %f\n", q_pitch.w, q_pitch.x, q_pitch.y, q_pitch.z); - } - */ - - Quaternion q = QuaternionMultiply(q_roll, QuaternionMultiply(q_pitch, q_yaw)); - q = QuaternionNormalize(q); - - forward = Vector3RotateByQuaternion(forward, q); - forward = Vector3Normalize(forward); - - agent->orientation = QuaternionMultiply(q, agent->orientation); - - // Jank plane physics - Vector3 v = { - agent->speed * (forward.x + local_up.x), - agent->speed * (forward.y + local_up.y - 1.0f), - agent->speed * (forward.z + local_up.z) - }; - - agent->x += v.x; - agent->y += v.y; - agent->z += v.z; - - // Just for visualization - agent->vx = v.x; - agent->vy = v.y; - agent->vz = v.z; - - // Clamp position to environment bounds - agent->x = clampf(agent->x, -env->size_x, env->size_x); - agent->y = clampf(agent->y, -env->size_y, env->size_y); - agent->z = clampf(agent->z, -env->size_z, env->size_z); -} - -typedef struct { - float distance; - float dx; - float dy; - float dz; - float same_team; - int idx; -} AgentObs; - -int compare_agent_obs(const void* a, const void* b) { - AgentObs* oa = (AgentObs*)a; - AgentObs* ob = (AgentObs*)b; - if (oa->distance < ob->distance) { - return -1; - } else if (oa->distance > ob->distance) { - return 1; - } - return 0; -} - -void compute_observations(Battle* env) { - AgentObs agent_obs[env->num_agents]; - - int obs_idx = 0; - for (int a=0; anum_agents/2; a++) { - assert(obs_idx == a*(6*env->num_armies + 19 + 8)); - - // Distance to each base - Entity* agent = &env->agents[a]; - int team = agent->army; - float dists[env->num_armies]; - for (int i=0; inum_armies; i++) { - dists[i] = 999999; - } - for (int f=0; fnum_armies; f++) { - Entity* base = &env->bases[f]; - float dx = base->x - agent->x; - float dy = base->y - agent->y; - float dz = base->z - agent->z; - float dd = dx*dx + dy*dy + dz*dz; - int type = f % env->num_armies; - if (dd < dists[type]) { - dists[type] = dd; - env->observations[obs_idx + 3*type] = dx; - env->observations[obs_idx + 3*type + 1] = dy; - env->observations[obs_idx + 3*type + 2] = dz; - } - } - obs_idx += 3*env->num_armies; - - - // Distance to each agent. Slow O(n^2) naive implementation - float x = agent->x; - float y = agent->y; - float z = agent->z; - for (int i=0; inum_agents; i++) { - Entity* other = &env->agents[i]; - float dx = other->x - x; - float dy = other->y - y; - float dz = other->z - z; - float distance = dx*dx + dy*dy + dz*dz; - AgentObs* o = &agent_obs[i]; - o->dx = dx; - o->dy = dy; - o->dz = dz; - if (other->army == agent->army) { - o->same_team = 1.0f; - o->distance = 99999.0f; - } else { - o->same_team = 0.0f; - o->distance = distance; - } - o->idx = i; - } - qsort(agent_obs, env->num_agents, sizeof(AgentObs), compare_agent_obs); - - for (int i=0; iobservations[obs_idx++] = agent_obs[i].dx; - env->observations[obs_idx++] = agent_obs[i].dy; - env->observations[obs_idx++] = agent_obs[i].dz; - env->observations[obs_idx++] = agent_obs[i].same_team; - } - - // Individual agent stats - env->observations[obs_idx++] = agent->vx/MAX_SPEED; - env->observations[obs_idx++] = agent->vy/MAX_SPEED; - env->observations[obs_idx++] = agent->vz/MAX_SPEED; - env->observations[obs_idx++] = agent->orientation.w; - env->observations[obs_idx++] = agent->orientation.x; - env->observations[obs_idx++] = agent->orientation.y; - env->observations[obs_idx++] = agent->orientation.z; - env->observations[obs_idx++] = agent->x; - env->observations[obs_idx++] = agent->y; - env->observations[obs_idx++] = agent->z; - env->observations[obs_idx++] = agent->y - ground_height(env, agent->x, agent->z); - env->observations[obs_idx++] = abs(agent->x) - 0.95f*env->size_x; - env->observations[obs_idx++] = abs(agent->z) - 0.95f*env->size_z; - env->observations[obs_idx++] = abs(agent->y) - 0.95f*env->size_y; - env->observations[obs_idx++] = agent->speed; - env->observations[obs_idx++] = agent->health; - env->observations[obs_idx++] = agent->max_turn; - env->observations[obs_idx++] = agent->max_speed; - env->observations[obs_idx++] = agent->attack_damage; - env->observations[obs_idx++] = agent->attack_range; - env->observations[obs_idx++] = env->rewards[a]; - env->observations[obs_idx++] = env->terminals[a]; - - // Hardcoded 8 unit types - memset(&env->observations[obs_idx], 0, 8*sizeof(float)); - env->observations[obs_idx + agent->unit] = 1.0f; - obs_idx += 8; - } -} - -// Required function -void c_reset(Battle* env) { - int agents_per_army = env->num_agents / env->num_armies; - for (int i=0; inum_armies; i++) { - bool spawn = false; - Entity* base = &env->bases[i]; - while (!spawn) { - base->x = randf(0.5 - env->size_x, env->size_x - 0.5); - base->z = randf(0.5 - env->size_z, env->size_z - 0.5); - base->y = ground_height(env, base->x, base->z); - base->army = i; - spawn = true; - - for (int j=0; jbases[j]; - float dx = other->x - base->x; - float dz = other->z - base->z; - float dd = sqrtf(dx*dx + dz*dz); - if (dd < 2.0f) { - spawn = false; - break; - } - } - } - } - - for (int army=0; armynum_armies; army++) { - for (int i=0; iagents[idx]; - if (i % 64 == 0) { - agent->unit = MOTHERSHIP; - } else if (i % 64 <= 4) { - agent->unit = TANK; - } else if (i % 64 <= 6) { - agent->unit = ARTILLERY; - } else if (i % 64 <= 10) { - agent->unit = BOMBER; - } else if (i % 64 <= 14) { - agent->unit = FIGHTER; - } else if (i % 64 <= 32) { - agent->unit = INFANTRY; - } else { - agent->unit = DRONE; - } - - agent->army = army; - agent->orientation = QuaternionIdentity(); - agent->episode_length = 0; - agent->target = -1; - update_abilities(agent); - respawn(env, idx); - } - } - compute_observations(env); -} - -void c_step(Battle* env) { - memset(env->rewards, 0, env->num_agents/2*sizeof(float)); - memset(env->terminals, 0, env->num_agents/2*sizeof(unsigned char)); - - for (int i=0; inum_agents; i++) { - Entity* agent = &env->agents[i]; - agent->episode_length += 1; - agent->target = -1; - - bool done = false; - float collision = 0.0f; - float oob = 0.0f; - float reward = 0.0f; - if (agent->health <= 0) { - done = true; - reward = 0.0f; - } - if (agent->unit == DRONE || agent->unit == FIGHTER || agent->unit == BOMBER || agent->unit == MOTHERSHIP) { - // Crash into terrain - float terrain_height = ground_height(env, agent->x, agent->z); - if (agent->y < terrain_height) { - collision = 1.0f; - done = true; - reward = -1.0f; - } - } - if ( - agent->x < -0.95f*env->size_x || agent->x > 0.95f*env->size_x || - agent->z < -0.95f*env->size_z || agent->z > 0.95f*env->size_z || - agent->y > 0.95f*env->size_y - ) { - done = true; - reward = -1.0f; - oob = 1.0f; - } - - if (done) { - update_abilities(agent); - respawn(env, i); - agent->episode_return += reward; - if (i < env->num_agents/2) { - env->rewards[i] = reward; - env->terminals[i] = 1; - env->log.score = env->log.episode_return; - env->log.episode_length += agent->episode_length; - env->log.episode_return += agent->episode_return; - env->log.collision_rate += collision; - env->log.oob_rate += oob; - env->log.n++; - - } - agent->episode_length = 0; - agent->episode_return = 0; - } - - //move_basic(env, agent, env->actions + 3*i); - if (agent->unit == INFANTRY || agent->unit == TANK || agent->unit == ARTILLERY) { - if (i < env->num_agents/2) { - move_ground(env, agent, env->actions + 3*i); - } else { - scripted_move(env, agent, false); - } - } else { - if (i < env->num_agents/2) { - move_ship(env, agent, env->actions + 3*i, i); - } else { - scripted_move(env, agent, true); - } - } - } - - for (int i=0; inum_agents; i++) { - Entity* agent = &env->agents[i]; - for (int j=0; jnum_agents; j++) { - if (j == i) { - continue; - } - Entity* target = &env->agents[j]; - if (agent->army == target->army) { - continue; - } - bool can_attack = false; - if (agent->unit == INFANTRY || agent->unit == TANK) { - can_attack = attack_ground(agent, target); - } else if (agent->unit == ARTILLERY) { - can_attack = attack_aa(agent, target); - } else if (agent->unit == BOMBER) { - can_attack = attack_bomber(agent, target); - } else { - can_attack = attack_air(agent, target); - } - if (!can_attack) { - continue; - } - agent->target = j; - if (i < env->num_agents/2) { - env->rewards[i] += 0.25f; - agent->episode_return += 0.25f; - } - target->health -= agent->attack_damage; - break; - } - } - - if (rand() % 9000 == 0) { - c_reset(env); - } - - compute_observations(env); -} - -Color COLORS[8] = { - (Color){0, 255, 255, 255}, - (Color){255, 0, 0, 255}, - (Color){0, 255, 0, 255}, - (Color){255, 255, 0, 255}, - (Color){255, 0, 255, 255}, - (Color){0, 0, 255, 255}, - (Color){128, 255, 0, 255}, - (Color){255, 128, 0, 255}, -}; - -Mesh* create_heightmap_mesh(float* heightMap, Vector3 size) { - int mapX = size.x; - int mapZ = size.z; - - // NOTE: One vertex per pixel - Mesh* mesh = (Mesh*)calloc(1, sizeof(Mesh)); - mesh->triangleCount = (mapX - 1)*(mapZ - 1)*2; // One quad every four pixels - - mesh->vertexCount = mesh->triangleCount*3; - - mesh->vertices = (float *)RL_MALLOC(mesh->vertexCount*3*sizeof(float)); - mesh->normals = (float *)RL_MALLOC(mesh->vertexCount*3*sizeof(float)); - mesh->texcoords = (float *)RL_MALLOC(mesh->vertexCount*2*sizeof(float)); - mesh->colors = NULL; - UploadMesh(mesh, false); - return mesh; -} - -void update_heightmap_mesh(Mesh* mesh, float* heightMap, Vector3 size) { - int mapX = size.x; - int mapZ = size.z; - - int vCounter = 0; // Used to count vertices float by float - int tcCounter = 0; // Used to count texcoords float by float - int nCounter = 0; // Used to count normals float by float - - //Vector3 scaleFactor = { size.x/(mapX - 1), 1.0f, size.z/(mapZ - 1) }; - Vector3 scaleFactor = { 1.0f, 1.0f, 1.0f}; - - Vector3 vA = { 0 }; - Vector3 vB = { 0 }; - Vector3 vC = { 0 }; - Vector3 vN = { 0 }; - - for (int z = 0; z < mapZ-1; z++) - { - for (int x = 0; x < mapX-1; x++) - { - // Fill vertices array with data - //---------------------------------------------------------- - - // one triangle - 3 vertex - mesh->vertices[vCounter] = (float)x*scaleFactor.x; - mesh->vertices[vCounter + 1] = heightMap[x + z*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 2] = (float)z*scaleFactor.z; - - mesh->vertices[vCounter + 3] = (float)x*scaleFactor.x; - mesh->vertices[vCounter + 4] = heightMap[x + (z + 1)*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z; - - mesh->vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x; - mesh->vertices[vCounter + 7] = heightMap[(x + 1) + z*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 8] = (float)z*scaleFactor.z; - - // Another triangle - 3 vertex - mesh->vertices[vCounter + 9] = mesh->vertices[vCounter + 6]; - mesh->vertices[vCounter + 10] = mesh->vertices[vCounter + 7]; - mesh->vertices[vCounter + 11] = mesh->vertices[vCounter + 8]; - - mesh->vertices[vCounter + 12] = mesh->vertices[vCounter + 3]; - mesh->vertices[vCounter + 13] = mesh->vertices[vCounter + 4]; - mesh->vertices[vCounter + 14] = mesh->vertices[vCounter + 5]; - - mesh->vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x; - mesh->vertices[vCounter + 16] = heightMap[(x + 1) + (z + 1)*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z; - vCounter += 18; // 6 vertex, 18 floats - - // Fill texcoords array with data - //-------------------------------------------------------------- - mesh->texcoords[tcCounter] = (float)x/(mapX - 1); - mesh->texcoords[tcCounter + 1] = (float)z/(mapZ - 1); - - mesh->texcoords[tcCounter + 2] = (float)x/(mapX - 1); - mesh->texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1); - - mesh->texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1); - mesh->texcoords[tcCounter + 5] = (float)z/(mapZ - 1); - - mesh->texcoords[tcCounter + 6] = mesh->texcoords[tcCounter + 4]; - mesh->texcoords[tcCounter + 7] = mesh->texcoords[tcCounter + 5]; - - mesh->texcoords[tcCounter + 8] = mesh->texcoords[tcCounter + 2]; - mesh->texcoords[tcCounter + 9] = mesh->texcoords[tcCounter + 3]; - - mesh->texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1); - mesh->texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1); - tcCounter += 12; // 6 texcoords, 12 floats - - // Fill normals array with data - //-------------------------------------------------------------- - for (int i = 0; i < 18; i += 9) - { - vA.x = mesh->vertices[nCounter + i]; - vA.y = mesh->vertices[nCounter + i + 1]; - vA.z = mesh->vertices[nCounter + i + 2]; - - vB.x = mesh->vertices[nCounter + i + 3]; - vB.y = mesh->vertices[nCounter + i + 4]; - vB.z = mesh->vertices[nCounter + i + 5]; - - vC.x = mesh->vertices[nCounter + i + 6]; - vC.y = mesh->vertices[nCounter + i + 7]; - vC.z = mesh->vertices[nCounter + i + 8]; - - vN = Vector3Normalize(Vector3CrossProduct(Vector3Subtract(vB, vA), Vector3Subtract(vC, vA))); - - mesh->normals[nCounter + i] = vN.x; - mesh->normals[nCounter + i + 1] = vN.y; - mesh->normals[nCounter + i + 2] = vN.z; - - mesh->normals[nCounter + i + 3] = vN.x; - mesh->normals[nCounter + i + 4] = vN.y; - mesh->normals[nCounter + i + 5] = vN.z; - - mesh->normals[nCounter + i + 6] = vN.x; - mesh->normals[nCounter + i + 7] = vN.y; - mesh->normals[nCounter + i + 8] = vN.z; - } - - nCounter += 18; // 6 vertex, 18 floats - } - } - - // Upload vertex data to GPU (static mesh) - UpdateMeshBuffer(*mesh, 0, mesh->vertices, mesh->vertexCount * 3 * sizeof(float), 0); // Update vertices - UpdateMeshBuffer(*mesh, 2, mesh->normals, mesh->vertexCount * 3 * sizeof(float), 0); // Update normals -} - - -// Required function. Should handle creating the client on first call -void c_render(Battle* env) { - if (env->client == NULL) { - SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(env->width, env->height, "PufferLib Battle"); - SetTargetFPS(30); - Client* client = (Client*)calloc(1, sizeof(Client)); - env->client = client; - client->models[DRONE] = LoadModel("resources/battle/drone.glb"); - client->models[FIGHTER] = LoadModel("resources/battle/fighter.glb"); - client->models[MOTHERSHIP] = LoadModel("resources/battle/mothership.glb"); - client->models[BOMBER] = LoadModel("resources/battle/bomber.glb"); - client->models[INFANTRY] = LoadModel("resources/battle/car.glb"); - client->models[TANK] = LoadModel("resources/battle/tank.glb"); - client->models[ARTILLERY] = LoadModel("resources/battle/artillery.glb"); - client->models[BASE] = LoadModel("resources/battle/base.glb"); - //env->client->ship = LoadModel("resources/puffer.glb"); - - char vsPath[256]; - char fsPath[256]; - sprintf(vsPath, "resources/tower_climb/shaders/gls%i/lighting.vs", GLSL_VERSION); - sprintf(fsPath, "resources/tower_climb/shaders/gls%i/lighting.fs", GLSL_VERSION); - client->light_shader = LoadShader(vsPath, fsPath); - client->light = CreateLight(LIGHT_DIRECTIONAL, - (Vector3){ 0.0f, 10.0f, 0.0f }, // High above for top lighting - (Vector3){ 0.5f, -1.0f, 0.3f }, // Direction: down and slightly forward - (Color){ 180, 180, 190, 255 }, // Softer warm white for tops - client->light_shader); - - for (int i = 0; i < 8; i++) { - Model* m = &client->models[i]; - for (int j = 0; j < m->materialCount; j++) { - //m->materials[j].maps[MATERIAL_MAP_DIFFUSE].texture = client->vehicle_texture; - m->materials[j].shader = client->light_shader; - } - } - - Camera3D camera = { 0 }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) - camera.fovy = 45.0f; // Camera field-of-view Y - camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - camera.position = (Vector3){ 0, 5*env->size_y, -3*env->size_z}; - camera.target = (Vector3){ 0, 0, 0}; - client->camera = camera; - - client->mesh = create_heightmap_mesh(env->terrain, (Vector3){env->terrain_width, 1, env->terrain_height}); - client->model = LoadModelFromMesh(*client->mesh); - update_heightmap_mesh(client->mesh, env->terrain, (Vector3){env->terrain_width, 1, env->terrain_height}); - - client->terrain_shader = LoadShader( - TextFormat("resources/battle/shader_%i.vs", GLSL_VERSION), - TextFormat("resources/battle/shader_%i.fs", GLSL_VERSION) - ); - - Image img = GenImageColor(env->terrain_width, env->terrain_height, WHITE); - ImageFormat(&img, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - client->terrain_texture = LoadTextureFromImage(img); - UnloadImage(img); - - client->terrain_shader_loc = GetShaderLocation(client->terrain_shader, "terrain"); - SetShaderValueTexture(client->terrain_shader, client->terrain_shader_loc, client->terrain_texture); - - client->terrain_data = calloc(4*env->terrain_width*env->terrain_height, sizeof(unsigned char)); - for (int i = 0; i < env->terrain_width*env->terrain_height; i++) { - client->terrain_data[4*i] = env->terrain[i]; - client->terrain_data[4*i+3] = 255; - } - UpdateTexture(client->terrain_texture, client->terrain_data); - SetShaderValueTexture(client->terrain_shader, client->terrain_shader_loc, client->terrain_texture); - - int shader_width_loc = GetShaderLocation(client->terrain_shader, "width"); - SetShaderValue(client->terrain_shader, shader_width_loc, &env->terrain_width, SHADER_UNIFORM_INT); - - int shader_height_loc = GetShaderLocation(client->terrain_shader, "height"); - SetShaderValue(client->terrain_shader, shader_height_loc, &env->terrain_height, SHADER_UNIFORM_INT); - - } - - // Standard across our envs so exiting is always the same - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - Client* client = env->client; - UpdateCamera(&client->camera, CAMERA_THIRD_PERSON); - //UpdateLightValues(client->light); - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - BeginMode3D(client->camera); - - //BeginShaderMode(client->terrain_shader); - client->model.materials[0].shader = client->terrain_shader; - Vector3 pos = {-env->size_x, -env->size_y, -env->size_z}; - DrawModel(client->model, pos, 1.0/128.0f, (Color){156, 50, 20, 255}); - //EndShaderMode(); - - - for (int f=0; fnum_armies; f++) { - Entity* base = &env->bases[f]; - float y = ground_height(env, base->x, base->z); - DrawModel(client->models[BASE], (Vector3){base->x, y, base->z}, 0.05f, COLORS[base->army]); - } - - for (int i=0; inum_agents; i++) { - Entity* agent = &env->agents[i]; - - Vector3 pos = {agent->x, agent->y, agent->z}; - Matrix transform = QuaternionToMatrix(agent->orientation); - Model model = client->models[agent->unit]; - model.transform = transform; - - Vector3 scale = (Vector3){0.01f, 0.01f, 0.01f}; - if (agent->unit == DRONE) { - scale = (Vector3){0.01f, 0.01f, 0.01f}; - } else if (agent->unit == MOTHERSHIP) { - scale = (Vector3){0.03f, 0.03f, 0.03f}; - } else if (agent->unit == FIGHTER) { - scale = (Vector3){0.015f, 0.015f, 0.015f}; - } else if (agent->unit == BOMBER) { - scale = (Vector3){0.015f, 0.015f, 0.015f}; - } else if (agent->unit == INFANTRY) { - scale = (Vector3){0.005f, 0.005f, 0.005f}; - } else if (agent->unit == TANK) { - scale = (Vector3){0.01f, 0.01f, 0.01f}; - } else if (agent->unit == ARTILLERY) { - scale = (Vector3){0.02f, 0.02f, 0.02f}; - } - - Color color = COLORS[agent->army]; - Vector3 rot = {0.0f, 1.0f, 0.0f}; - DrawModelEx(model, pos, rot, 0, scale, color); - - if (agent->target >= 0) { - Entity* target = &env->agents[agent->target]; - DrawLine3D( - (Vector3){agent->x, agent->y, agent->z}, - (Vector3){target->x, target->y, target->z}, - COLORS[agent->army] - ); - } - } - - DrawCubeWires( - (Vector3){0, 0, 0}, - 2*env->size_x, 2*env->size_y, 2*env->size_z, - (Color){0, 255, 255, 128} - ); - - EndMode3D(); - EndDrawing(); -} - -// Required function. Should clean up anything you allocated -// Do not free env->observations, actions, rewards, terminals -void c_close(Battle* env) { - free(env->agents); - free(env->bases); - if (env->client != NULL) { - Client* client = env->client; - //UnloadTexture(client->sprites); - CloseWindow(); - free(client); - } -} diff --git a/pufferlib/ocean/battle/battle.py b/pufferlib/ocean/battle/battle.py deleted file mode 100644 index 2beb888e5..000000000 --- a/pufferlib/ocean/battle/battle.py +++ /dev/null @@ -1,85 +0,0 @@ -'''A simple sample environment. Use this as a template for your own envs.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.battle import binding - -class Battle(pufferlib.PufferEnv): - def __init__(self, num_envs=1, width=1920, height=1080, size_x=1.0, - size_y=1.0, size_z=1.0, num_agents=1024, num_factories=32, - num_armies=4, render_mode=None, log_interval=128, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(num_armies*3 + 4*16 + 22 + 8,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Box( - low=-1, high=1, shape=(3,), dtype=np.float32) - self.render_mode = render_mode - self.num_agents = num_envs*num_agents - self.log_interval = log_interval - - if num_armies < 1 or num_armies > 8: - raise pufferlib.APIUsageError('num_armies must be in [1, 8]') - if num_agents % num_armies != 0: - raise pufferlib.APIUsageError('num_agents must be a multiple of num_armies') - - super().__init__(buf) - c_envs = [] - for i in range(num_envs): - c_env = binding.env_init( - self.observations[i*num_agents:(i+1)*num_agents], - self.actions[i*num_agents:(i+1)*num_agents], - self.rewards[i*num_agents:(i+1)*num_agents], - self.terminals[i*num_agents:(i+1)*num_agents], - self.truncations[i*num_agents:(i+1)*num_agents], - seed, width=width, height=height, size_x=size_x, size_y=size_y, size_z=size_z, - num_agents=num_agents*2, num_factories=num_factories, - num_armies=num_armies) - c_envs.append(c_env) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - self.actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 512 - - env = Battle(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(env.single_action_space.nvec, size=(CACHE, 2)) - - i = 0 - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[i % CACHE]) - steps += env.num_agents - i += 1 - - print('Battle SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/battle/binding.c b/pufferlib/ocean/battle/binding.c deleted file mode 100644 index 255c6988d..000000000 --- a/pufferlib/ocean/battle/binding.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "battle.h" - -#define Env Battle -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->size_x = unpack(kwargs, "size_x"); - env->size_y = unpack(kwargs, "size_y"); - env->size_z = unpack(kwargs, "size_z"); - env->num_agents = unpack(kwargs, "num_agents"); - env->num_armies = unpack(kwargs, "num_armies"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "collision_rate", log->collision_rate); - assign_to_dict(dict, "oob_rate", log->oob_rate); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/battle/rlights.h b/pufferlib/ocean/battle/rlights.h deleted file mode 100644 index a6ca56995..000000000 --- a/pufferlib/ocean/battle/rlights.h +++ /dev/null @@ -1,170 +0,0 @@ -/********************************************************************************************** -* -* raylib.lights - Some useful functions to deal with lights data -* -* CONFIGURATION: -* -* #define RLIGHTS_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2017-2024 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RLIGHTS_H -#define RLIGHTS_H - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define MAX_LIGHTS 4 // Max dynamic lights supported by shader - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -// Light data -typedef struct { - int type; - bool enabled; - Vector3 position; - Vector3 target; - Color color; - float attenuation; - - // Shader locations - int enabledLoc; - int typeLoc; - int positionLoc; - int targetLoc; - int colorLoc; - int attenuationLoc; -} Light; - -// Light type -typedef enum { - LIGHT_DIRECTIONAL = 0, - LIGHT_POINT -} LightType; - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader); // Create a light and get shader locations -void UpdateLightValues(Shader shader, Light light); // Send light properties to shader - -#ifdef __cplusplus -} -#endif - -#endif // RLIGHTS_H - - -/*********************************************************************************** -* -* RLIGHTS IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RLIGHTS_IMPLEMENTATION) - -#include "raylib.h" - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -// ... - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -// ... - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static int lightsCount = 0; // Current amount of created lights - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -// ... - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- - -// Create a light and get shader locations -Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader) -{ - Light light = { 0 }; - - if (lightsCount < MAX_LIGHTS) - { - light.enabled = true; - light.type = type; - light.position = position; - light.target = target; - light.color = color; - - // NOTE: Lighting shader naming must be the provided ones - light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightsCount)); - light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightsCount)); - light.positionLoc = GetShaderLocation(shader, TextFormat("lights[%i].position", lightsCount)); - light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightsCount)); - light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightsCount)); - - UpdateLightValues(shader, light); - - lightsCount++; - } - - return light; -} - -// Send light properties to shader -// NOTE: Light shader locations should be available -void UpdateLightValues(Shader shader, Light light) -{ - // Send to shader light enabled state and type - SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT); - SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT); - - // Send to shader light position values - float position[3] = { light.position.x, light.position.y, light.position.z }; - SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3); - - // Send to shader light target position values - float target[3] = { light.target.x, light.target.y, light.target.z }; - SetShaderValue(shader, light.targetLoc, target, SHADER_UNIFORM_VEC3); - - // Send to shader light color values - float color[4] = { (float)light.color.r/(float)255, (float)light.color.g/(float)255, - (float)light.color.b/(float)255, (float)light.color.a/(float)255 }; - SetShaderValue(shader, light.colorLoc, color, SHADER_UNIFORM_VEC4); -} - -#endif // RLIGHTS_IMPLEMENTATION diff --git a/pufferlib/ocean/battle/simplex.h b/pufferlib/ocean/battle/simplex.h deleted file mode 100644 index 07b3e7db3..000000000 --- a/pufferlib/ocean/battle/simplex.h +++ /dev/null @@ -1,148 +0,0 @@ -// Original work (noise library) Copyright (c) 2008 Casey Duncan -// Modified work (vec_noise library) Copyright (c) 2017 Zev Benjamin -// Single-file C port (this file) Copyright (c) 2024 Joseph Suarez -// Distributed under the MIT license. This is a simple copy-paste job. -// I did this because the original code mixed Python bindings into the -// C source, so there wasn't a clean way to use it as a C standalone. - -#include -const float GRAD3[][3] = { - {1,1,0},{-1,1,0},{1,-1,0},{-1,-1,0}, - {1,0,1},{-1,0,1},{1,0,-1},{-1,0,-1}, - {0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}, - {1,0,-1},{-1,0,-1},{0,-1,1},{0,1,1}}; - -const float GRAD4[][4] = { - {0,1,1,1}, {0,1,1,-1}, {0,1,-1,1}, {0,1,-1,-1}, - {0,-1,1,1}, {0,-1,1,-1}, {0,-1,-1,1}, {0,-1,-1,-1}, - {1,0,1,1}, {1,0,1,-1}, {1,0,-1,1}, {1,0,-1,-1}, - {-1,0,1,1}, {-1,0,1,-1}, {-1,0,-1,1}, {-1,0,-1,-1}, - {1,1,0,1}, {1,1,0,-1}, {1,-1,0,1}, {1,-1,0,-1}, - {-1,1,0,1}, {-1,1,0,-1}, {-1,-1,0,1}, {-1,-1,0,-1}, - {1,1,1,0}, {1,1,-1,0}, {1,-1,1,0}, {1,-1,-1,0}, - {-1,1,1,0}, {-1,1,-1,0}, {-1,-1,1,0}, {-1,-1,-1,0}}; - -// At the possible cost of unaligned access, we use char instead of -// int here to try to ensure that this table fits in L1 cache -const unsigned char PERM[] = { - 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, - 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, - 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, - 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, - 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, - 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, - 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, - 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, - 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, - 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, - 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, - 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, - 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, - 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, - 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, - 141, 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, - 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, - 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, - 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, - 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, - 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, - 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, - 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, - 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, - 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, - 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, - 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, - 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, - 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, - 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, - 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, - 180}; - -const unsigned char SIMPLEX[][4] = { - {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {1,2,3,0},{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{1,3,2,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},{1,0,2,3},{1,0,3,2},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,1,3}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, - {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1}, - {3,2,1,0}}; - -#define fastfloor(n) (int)(n) - (((n) < 0.0f) & ((n) != (int)(n))) - -// Fast sine/cosine functions from -// http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/page__st__80 -// Note the input to these functions is not radians -// instead x = [0, 2] for r = [0, 2*PI] - -inline float fast_sin(float x) -{ - // Convert the input value to a range of -1 to 1 - // x = x * (1.0f / PI); - - // Wrap around - volatile float z = (x + 25165824.0f); - x = x - (z - 25165824.0f); - - #if LOW_SINE_PRECISION - return 4.0f * (x - x * fabsf(x)); - #else - { - float y = x - x * fabsf(x); - const float Q = 3.1f; - const float P = 3.6f; - return y * (Q + P * fabsf(y)); - } - #endif -} - -inline float fast_cos(float x) -{ - return fast_sin(x + 0.5f); -} - -// 2D simplex skew factors -#define F2 0.3660254037844386f // 0.5 * (sqrt(3.0) - 1.0) -#define G2 0.21132486540518713f // (3.0 - sqrt(3.0)) / 6.0 - -float -noise2(float x, float y) -{ - int i1, j1, II, JJ, c; - float s = (x + y) * F2; - float i = floorf(x + s); - float j = floorf(y + s); - float t = (i + j) * G2; - - float xx[3], yy[3], f[3]; - float noise[3] = {0.0f, 0.0f, 0.0f}; - int g[3]; - - xx[0] = x - (i - t); - yy[0] = y - (j - t); - - i1 = xx[0] > yy[0]; - j1 = xx[0] <= yy[0]; - - xx[2] = xx[0] + G2 * 2.0f - 1.0f; - yy[2] = yy[0] + G2 * 2.0f - 1.0f; - xx[1] = xx[0] - i1 + G2; - yy[1] = yy[0] - j1 + G2; - - II = (int) i & 255; - JJ = (int) j & 255; - g[0] = PERM[II + PERM[JJ]] % 12; - g[1] = PERM[II + i1 + PERM[JJ + j1]] % 12; - g[2] = PERM[II + 1 + PERM[JJ + 1]] % 12; - - for (c = 0; c <= 2; c++) - f[c] = 0.5f - xx[c]*xx[c] - yy[c]*yy[c]; - - for (c = 0; c <= 2; c++) - if (f[c] > 0) - noise[c] = f[c]*f[c]*f[c]*f[c] * (GRAD3[g[c]][0]*xx[c] + GRAD3[g[c]][1]*yy[c]); - - return (noise[0] + noise[1] + noise[2]) * 70.0f; -} diff --git a/pufferlib/ocean/benchmark/README.md b/pufferlib/ocean/benchmark/README.md new file mode 100644 index 000000000..2e08295f4 --- /dev/null +++ b/pufferlib/ocean/benchmark/README.md @@ -0,0 +1,68 @@ +# Waymo Open Sim Agent Challenge (WOSAC) benchmark + +## Usage + +WOSAC evaluation with random policy +```bash +puffer eval puffer_drive --eval.wosac-realism-eval True +``` + +WOSAC evaluation with your checkpoint +```bash +puffer eval puffer_drive --eval.wosac-realism-eval True --load-model-path .pt +``` + +## Links + +- [Challenge and leaderboard](https://waymo.com/open/challenges/2025/sim-agents/) +- [Sim agent challenge tutorial](https://github.com/waymo-research/waymo-open-dataset/blob/master/tutorial/tutorial_sim_agents.ipynb) +- [Reference paper introducing WOSAC](https://arxiv.org/pdf/2305.12032) +- [Metrics entry point](https://github.com/waymo-research/waymo-open-dataset/blob/master/src/waymo_open_dataset/wdl_limited/sim_agents_metrics/metrics.py) +- [Log-likelihood estimators](https://github.com/waymo-research/waymo-open-dataset/blob/master/src/waymo_open_dataset/wdl_limited/sim_agents_metrics/estimators.py) +- Configurations [proto file](https://github.com/waymo-research/waymo-open-dataset/blob/99a4cb3ff07e2fe06c2ce73da001f850f628e45a/src/waymo_open_dataset/protos/sim_agents_metrics.proto#L51) [default sim agent challenge configs](https://github.com/waymo-research/waymo-open-dataset/blob/master/src/waymo_open_dataset/wdl_limited/sim_agents_metrics/challenge_2025_sim_agents_config.textproto) + + +## Implementation + +- For the sim agent challenge we compute the log likelihood with `aggregate_objects=False`, which means that we use [`_log_likelihood_estimate_timeseries_agent_level()`](https://github.com/waymo-research/waymo-open-dataset/blob/99a4cb3ff07e2fe06c2ce73da001f850f628e45a/src/waymo_open_dataset/wdl_limited/sim_agents_metrics/estimators.py#L17) +- As such, the interpretation is as follows: + +Steps [for every scene]: +1. Rollout policy in environment K times → (n_agents, n_rollouts, n_steps) +2. Obtain log data → (n_agents, 1, n_steps) +3. Obtain features from (x, y, z, heading tuples) +4. Compute log-likelihood metrics from features + - a. Flatten across time (assume independence) → (n_agents, n_rollouts * n_steps) + - b. Use the per-agent simulated features to construct a probability distribution + - c. Take the per-agent ground-truth values and find the bin that is closed for each + - d. Take log of the probability for each bin → (n_agents, n_steps) +5. Likelihood score is exp(sum(log_probs)/n_steps) → (n_agents, 1) \in [0, 1] + +## Notes + +- Currently, only kinematics realism score is implemented. Next steps would be to add the interactive realism score, and the map realism score: + + - Interactive realism score: requires grouping agents per scenario, and computing pairwise distances between agents over time. + - Map realism score: requires access to the map and computing offroad status. + + Those two scores might require heavy computations, so we will consider reimplementic all the metrics in torch. + +- About the time-independence assumption: + + 1. This is the assumption used in the official WOSAC evaluation, their argument is that it would give more flexibility to the sim agents models: + + > Given the time series nature of simulation data, two choices emerge for how to treat samples over multiple timesteps for a given object for a given run segment: to treat them as time-independent or time-dependent samples. In the latter case, users would be expected to not only reconstruct the general behaviors present in the logged data in one rollout, but also recreate those behaviors over the exact same time intervals. To allow more flexibility in agent behavior, we use the former formulation when computing NLLs, defining each component metric m as an average (in log-space) over the time-axis, masked by validity. + + 2. However this will lead to the score of a perfect logged oracle being inferior to 1.0, and makes it less interpretable. Here are the scores of a logged oracle using the time-independence assumption (setup: 1024 agents, 48 rollouts): + + ``` + Linear speed: 0.5640 + Linear acceleration: 0.4658 + Angular speed: 0.5543 + Angular acceleration: 0.6589 + + Kinematics realism score: 0.5607 + ``` + These scores go to 1.0 if we use the time-dependent estimator, execpt for the smoothing factor that is used to avoid bins with 0 probability. + + Using the time-dependent estimator means generating n_steps histograms per agent, using num_rollouts samples per histogram, while time-independence means generating one histogram per agent using n_rollouts * n_steps samples. With the speed of PufferDrive, we might be able to increase n_rollouts to have more samples per histogram. diff --git a/pufferlib/ocean/cpr/__init__.py b/pufferlib/ocean/benchmark/__init__.py similarity index 100% rename from pufferlib/ocean/cpr/__init__.py rename to pufferlib/ocean/benchmark/__init__.py diff --git a/pufferlib/ocean/benchmark/estimators.py b/pufferlib/ocean/benchmark/estimators.py new file mode 100644 index 000000000..7af3592a5 --- /dev/null +++ b/pufferlib/ocean/benchmark/estimators.py @@ -0,0 +1,168 @@ +"""Simplified estimators to compute log-likelihood of simulated trajs based on https://github.com/waymo-research/waymo-open-dataset/blob/master/src/waymo_open_dataset/wdl_limited/sim_agents_metrics/estimators.py""" + +import numpy as np +import matplotlib.pyplot as plt +from typing import Optional + +np.set_printoptions(suppress=True) + + +def histogram_estimate( + log_samples: np.ndarray, + sim_samples: np.ndarray, + min_val: float, + max_val: float, + num_bins: int, + additive_smoothing: float = 0.1, +) -> np.ndarray: + """Computes log-likelihoods of samples based on histograms. + + Args: + log_samples: Shape (n_agents, sample_size) - samples to evaluate + sim_samples: Shape (n_agents, sample_size) - samples to build distribution from + min_val: Minimum value for histogram bins + max_val: Maximum value for histogram bins + num_bins: Number of histogram bins + additive_smoothing: Pseudocount for Laplace smoothing (default: 0.1) + sanity_check: If True, plot visualization for debugging + + Returns: + Shape (n_agents, sample_size) - log-likelihood of each log sample + under the corresponding sim distribution + """ + + n_agents, sample_size = sim_samples.shape + + # Clip samples to valid range + log_samples_clipped = np.clip(log_samples, min_val, max_val) + sim_samples_clipped = np.clip(sim_samples, min_val, max_val) + + # Create bin edges + edges = np.linspace(min_val, max_val, num_bins + 1) + + # Create histogram for each agent from sim samples + sim_counts = np.array([np.histogram(sim_samples_clipped[i], bins=edges)[0] for i in range(n_agents)]) + + # Apply smoothing and normalize to probabilities + sim_counts = sim_counts.astype(float) + additive_smoothing + sim_probs = sim_counts / sim_counts.sum(axis=1, keepdims=True) + + # Find which bin each log sample belongs to + # digitize returns values in [1, num_bins], so subtract 1 for 0-indexing + # right=False means bins are [left, right) except last bin which is [left, right] + log_bins = np.digitize(log_samples_clipped, edges, right=False) - 1 + + # Clip to valid bin indices (handles edge case where value == max_val) + log_bins = np.clip(log_bins, 0, num_bins - 1) + + # Get log probabilities for each sample + agent_indices = np.arange(n_agents)[:, None] + log_probs = np.log(sim_probs[agent_indices, log_bins]) + + return log_probs + + +def log_likelihood_estimate_timeseries( + log_values: np.ndarray, + sim_values: np.ndarray, + min_val: float, + max_val: float, + num_bins: int, + additive_smoothing: float = 0.1, + treat_timesteps_independently: bool = True, + sanity_check: bool = False, + plot_agent_idx: int = 0, +) -> np.ndarray: + """Computes log-likelihood estimates for time-series simulated features on a per-agent basis. + + Args: + log_values: Shape (n_agents, 1, n_steps) + sim_values: Shape (n_agents, n_rollouts, n_steps) + min_val: Minimum value for histogram bins + max_val: Maximum value for histogram bins + num_bins: Number of histogram bins + additive_smoothing: Pseudocount for Laplace smoothing + treat_timesteps_independently: If True, treat each timestep independently + sanity_check: If True, plot visualizations for debugging + plot_agent_idx: Which agent to plot if sanity_check=True + plot_rollouts: How many rollouts to show if sanity_check=True + + Returns: + A tensor of shape (n_objects, n_steps) containing the log probability + estimates of the log features under the simulated distribution of the same + feature. + """ + n_agents, n_rollouts, n_steps = sim_values.shape + + if treat_timesteps_independently: + # Ignore temporal structure: We end up with (n_agents, n_rollouts * n_steps) + log_flat = log_values.reshape(n_agents, n_steps) + sim_flat = sim_values.reshape(n_agents, n_rollouts * n_steps) + + else: + # If values in time are instead to be compared per-step, reshape: + # - `sim_values` as (n_objects * n_steps, n_rollouts) + # - `log_values` as (n_objects * n_steps, 1) + log_flat = log_values.reshape(n_agents * n_steps, 1) + sim_flat = sim_values.transpose(0, 2, 1).reshape(n_agents * n_steps, n_rollouts) + + # Compute log-likelihoods + log_probs = histogram_estimate(log_flat, sim_flat, min_val, max_val, num_bins, additive_smoothing) + + # Depending on `independent_timesteps`, the likelihoods might be flattened, so + # reshape back to the initial `log_values` shape. + log_probs = log_probs.reshape(n_agents, n_steps) + + # Sanity check visualization + if sanity_check: + _plot_histogram_sanity_check(log_flat, sim_flat, log_probs, plot_agent_idx) + + return log_probs + + +def _plot_histogram_sanity_check( + log_samples: np.ndarray, + sim_samples: np.ndarray, + log_probs: np.ndarray, + idx: int, +): + """Plot data as sanity check.""" + + for idx in range(log_samples.shape[0]): + fig, axes = plt.subplots(1, 3, figsize=(15, 4)) + fig.suptitle(f"Histogram Log-Likelihood Sanity Check for Agent {idx}") + + # Plot 1: Simulated distribution (histogram) + axes[0].hist(sim_samples[idx], density=True, alpha=0.7, color="blue") + axes[0].set_xlabel("Value") + axes[0].set_ylabel("Density") + axes[0].set_title("Simulated distribution") + axes[0].grid(alpha=0.3) + + # Plot 2: Ground-truth values overlaid on simulated + axes[1].hist(sim_samples[idx], density=True, alpha=0.5, color="blue", label="Simulated") + axes[1].scatter( + log_samples[idx], + np.zeros_like(log_samples[idx]), + color="green", + marker="|", + s=200, + linewidths=2, + label="Ground-truth", + zorder=5, + ) + axes[1].set_xlabel("Value") + axes[1].set_ylabel("Density") + axes[1].set_title("Ground-truth vs Simulated") + axes[1].legend() + axes[1].grid(alpha=0.3) + + # Plot 3: Log-likelihood values + axes[2].hist(log_samples[idx], alpha=0.7, color="orange") + axes[2].set_ylabel("Log-likelihood") + axes[2].set_title("Log-likelihood of Ground-truth") + axes[2].grid(alpha=0.3) + axes[2].axhline(y=0, color="k", linestyle="--", alpha=0.3) + + plt.tight_layout() + plt.savefig(f"histogram_sanity_check_agent_{idx}.png") diff --git a/pufferlib/ocean/benchmark/evaluator.py b/pufferlib/ocean/benchmark/evaluator.py new file mode 100644 index 000000000..4c50a422a --- /dev/null +++ b/pufferlib/ocean/benchmark/evaluator.py @@ -0,0 +1,492 @@ +"""WOSAC evaluation class for PufferDrive.""" + +import torch +import time +import numpy as np +import pandas as pd +from pprint import pprint +from typing import Dict, Optional +import matplotlib.pyplot as plt +import configparser +import os + +import pufferlib +from pufferlib.ocean.benchmark import metrics +from pufferlib.ocean.benchmark import estimators + + +_METRIC_FIELD_NAMES = [ + "linear_speed", + "linear_acceleration", + "angular_speed", + "angular_acceleration", +] + + +class WOSACEvaluator: + """Evaluates policys on the Waymo Open Sim Agent Challenge (WOSAC) in PufferDrive. Info and links in the readme.""" + + def __init__(self, config: Dict): + self.config = config + self.num_steps = 91 # Hardcoded for WOSAC (9.1s at 10Hz) + self.init_steps = config.get("eval", {}).get("wosac_init_steps", 0) + self.sim_steps = self.num_steps - self.init_steps + self.num_rollouts = config.get("eval", {}).get("wosac_num_rollouts", 32) + + wosac_metrics_path = os.path.join(os.path.dirname(__file__), "wosac.ini") + self.metrics_config = configparser.ConfigParser() + self.metrics_config.read(wosac_metrics_path) + + def _compute_metametric(self, metrics: pd.Series) -> float: + metametric = 0.0 + for field_name in _METRIC_FIELD_NAMES: + likelihood_field_name = "likelihood_" + field_name + weight = self.metrics_config.getfloat(field_name, "metametric_weight") + metric_score = metrics[likelihood_field_name] + metametric += weight * metric_score + + weight_sum = sum(self.metrics_config.getfloat(fn, "metametric_weight") for fn in _METRIC_FIELD_NAMES) + return metametric / weight_sum + + def _get_histogram_params(self, metric_name: str): + return ( + self.metrics_config.getfloat(metric_name, "histogram.min_val"), + self.metrics_config.getfloat(metric_name, "histogram.max_val"), + self.metrics_config.getint(metric_name, "histogram.num_bins"), + self.metrics_config.getfloat(metric_name, "histogram.additive_smoothing_pseudocount"), + self.metrics_config.getboolean(metric_name, "independent_timesteps"), + ) + + def collect_ground_truth_trajectories(self, puffer_env): + """Collect ground truth data for evaluation. + Returns: + trajectories: dict with keys 'x', 'y', 'z', 'heading', 'id' + each of shape (num_agents, 1, num_steps) for trajectory data + """ + return puffer_env.get_ground_truth_trajectories() + + def collect_simulated_trajectories(self, args, puffer_env, policy): + """Roll out policy in env and collect trajectories. + Returns: + trajectories: dict with keys 'x', 'y', 'z', 'heading' each of shape + (num_agents, num_rollouts, num_steps) + """ + + driver = puffer_env.driver_env + num_agents = puffer_env.observation_space.shape[0] + device = args["train"]["device"] + + trajectories = { + "x": np.zeros((num_agents, self.num_rollouts, self.sim_steps), dtype=np.float32), + "y": np.zeros((num_agents, self.num_rollouts, self.sim_steps), dtype=np.float32), + "z": np.zeros((num_agents, self.num_rollouts, self.sim_steps), dtype=np.float32), + "heading": np.zeros((num_agents, self.num_rollouts, self.sim_steps), dtype=np.float32), + "id": np.zeros((num_agents, self.num_rollouts, self.sim_steps), dtype=np.int32), + } + + for rollout_idx in range(self.num_rollouts): + print(f"\rCollecting rollout {rollout_idx + 1}/{self.num_rollouts}...", end="", flush=True) + obs, info = puffer_env.reset() + state = {} + if args["train"]["use_rnn"]: + state = dict( + lstm_h=torch.zeros(num_agents, policy.hidden_size, device=device), + lstm_c=torch.zeros(num_agents, policy.hidden_size, device=device), + ) + + for time_idx in range(self.sim_steps): + # Get global state + agent_state = driver.get_global_agent_state() + trajectories["x"][:, rollout_idx, time_idx] = agent_state["x"] + trajectories["y"][:, rollout_idx, time_idx] = agent_state["y"] + trajectories["z"][:, rollout_idx, time_idx] = agent_state["z"] + trajectories["heading"][:, rollout_idx, time_idx] = agent_state["heading"] + trajectories["id"][:, rollout_idx, time_idx] = agent_state["id"] + + # Step policy + with torch.no_grad(): + ob_tensor = torch.as_tensor(obs).to(device) + logits, value = policy.forward_eval(ob_tensor, state) + action, logprob, _ = pufferlib.pytorch.sample_logits(logits) + action_np = action.cpu().numpy().reshape(puffer_env.action_space.shape) + + if isinstance(logits, torch.distributions.Normal): + action_np = np.clip(action_np, puffer_env.action_space.low, puffer_env.action_space.high) + + obs, _, _, _, _ = puffer_env.step(action_np) + + return trajectories + + def compute_metrics( + self, + ground_truth_trajectories: Dict, + simulated_trajectories: Dict, + aggregate_results: bool = False, + ) -> Dict: + """Compute realism metrics comparing simulated and ground truth trajectories. + + Args: + ground_truth_trajectories: Dict with keys ['x', 'y', 'z', 'heading', 'id'] + Each trajectory has shape (n_agents, n_rollouts, n_steps) + simulated_trajectories: Dict with same keys plus 'scenario_id' + shape (n_agents, n_steps) for trajectories + shape (n_agents,) for id + list of length n_agents for scenario_id + + Note: z-position currently not used. + + Returns: + Dictionary with scores per scenario_id + """ + # Ensure the id order matches exactly for simulated and ground truth + assert np.array_equal(simulated_trajectories["id"][:, 0:1, 0], ground_truth_trajectories["id"]), ( + "Agent IDs don't match between simulated and ground truth trajectories" + ) + + # Extract trajectories + sim_x = simulated_trajectories["x"] + sim_y = simulated_trajectories["y"] + sim_heading = simulated_trajectories["heading"] + ref_x = ground_truth_trajectories["x"] + ref_y = ground_truth_trajectories["y"] + ref_heading = ground_truth_trajectories["heading"] + ref_valid = ground_truth_trajectories["valid"] + + # Compute features + # Kinematics-related features + sim_linear_speed, sim_linear_accel, sim_angular_speed, sim_angular_accel = metrics.compute_kinematic_features( + sim_x, sim_y, sim_heading + ) + + ref_linear_speed, ref_linear_accel, ref_angular_speed, ref_angular_accel = metrics.compute_kinematic_features( + ref_x, ref_y, ref_heading + ) + + # Get the log speed (linear and angular) validity. Since this is computed by + # a delta between steps i-1 and i+1, we verify that both of these are + # valid (logical and). + speed_validity, acceleration_validity = metrics.compute_kinematic_validity(ref_valid) + + # Compute realism metrics + # Average Displacement Error (ADE) and minADE + # Note: This metric is not included in the scoring meta-metric, as per WOSAC rules. + ade, min_ade = metrics.compute_displacement_error(sim_x, sim_y, ref_x, ref_y, ref_valid) + + # Log-likelihood metrics + # Kinematic features log-likelihoods + min_val, max_val, num_bins, additive_smoothing, independent_timesteps = self._get_histogram_params( + "linear_speed" + ) + linear_speed_log_likelihood = estimators.log_likelihood_estimate_timeseries( + log_values=ref_linear_speed, + sim_values=sim_linear_speed, + treat_timesteps_independently=independent_timesteps, + min_val=min_val, + max_val=max_val, + num_bins=num_bins, + additive_smoothing=additive_smoothing, + sanity_check=False, + ) + + min_val, max_val, num_bins, additive_smoothing, independent_timesteps = self._get_histogram_params( + "linear_acceleration" + ) + linear_accel_log_likelihood = estimators.log_likelihood_estimate_timeseries( + log_values=ref_linear_accel, + sim_values=sim_linear_accel, + treat_timesteps_independently=independent_timesteps, + min_val=min_val, + max_val=max_val, + num_bins=num_bins, + additive_smoothing=additive_smoothing, + sanity_check=False, + ) + + min_val, max_val, num_bins, additive_smoothing, independent_timesteps = self._get_histogram_params( + "angular_speed" + ) + angular_speed_log_likelihood = estimators.log_likelihood_estimate_timeseries( + log_values=ref_angular_speed, + sim_values=sim_angular_speed, + treat_timesteps_independently=independent_timesteps, + min_val=min_val, + max_val=max_val, + num_bins=num_bins, + additive_smoothing=additive_smoothing, + sanity_check=False, + ) + + min_val, max_val, num_bins, additive_smoothing, independent_timesteps = self._get_histogram_params( + "angular_acceleration" + ) + angular_accel_log_likelihood = estimators.log_likelihood_estimate_timeseries( + log_values=ref_angular_accel, + sim_values=sim_angular_accel, + treat_timesteps_independently=independent_timesteps, + min_val=min_val, + max_val=max_val, + num_bins=num_bins, + additive_smoothing=additive_smoothing, + sanity_check=False, + ) + + speed_likelihood = np.exp( + metrics._reduce_average_with_validity( + linear_speed_log_likelihood, + speed_validity[:, 0, :], + axis=1, + ) + ) + + accel_likelihood = np.exp( + metrics._reduce_average_with_validity( + linear_accel_log_likelihood, + acceleration_validity[:, 0, :], + axis=1, + ) + ) + + angular_speed_likelihood = np.exp( + metrics._reduce_average_with_validity( + angular_speed_log_likelihood, + speed_validity[:, 0, :], + axis=1, + ) + ) + + angular_accel_likelihood = np.exp( + metrics._reduce_average_with_validity( + angular_accel_log_likelihood, + acceleration_validity[:, 0, :], + axis=1, + ) + ) + + # Get agent IDs and scenario IDs + agent_ids = ground_truth_trajectories["id"] + scenario_ids = ground_truth_trajectories["scenario_id"] + + df = pd.DataFrame( + { + "agent_id": agent_ids.flatten(), + "scenario_id": scenario_ids.flatten(), + "ade": ade, + "min_ade": min_ade, + "likelihood_linear_speed": speed_likelihood, + "likelihood_linear_acceleration": accel_likelihood, + "likelihood_angular_speed": angular_speed_likelihood, + "likelihood_angular_acceleration": angular_accel_likelihood, + } + ) + + scene_level_results = df.groupby("scenario_id")[ + [ + "ade", + "min_ade", + "likelihood_linear_speed", + "likelihood_linear_acceleration", + "likelihood_angular_speed", + "likelihood_angular_acceleration", + ] + ].mean() + + scene_level_results["realism_meta_score"] = scene_level_results.apply(self._compute_metametric, axis=1) + scene_level_results["num_agents"] = df.groupby("scenario_id").size() + scene_level_results = scene_level_results[ + ["num_agents"] + [col for col in scene_level_results.columns if col != "num_agents"] + ] + + if aggregate_results: + aggregate_metrics = scene_level_results.mean().to_dict() + aggregate_metrics["total_num_agents"] = scene_level_results["num_agents"].sum() + # Convert numpy types to Python native types + return {k: v.item() if hasattr(v, "item") else v for k, v in aggregate_metrics.items()} + else: + print("\n Scene-level results:\n") + print(scene_level_results) + + print(f"\n Overall realism meta score: {scene_level_results['realism_meta_score'].mean():.4f}") + print(f"\n Overall minADE: {scene_level_results['min_ade'].mean():.4f}") + print(f"\n Overall ADE: {scene_level_results['ade'].mean():.4f}") + + # print(f"\n Full agent-level results:\n") + # print(df) + return scene_level_results + + def _quick_sanity_check(self, gt_trajectories, simulated_trajectories, agent_idx=None, max_agents_to_plot=10): + if agent_idx is None: + agent_indices = range(np.clip(simulated_trajectories["x"].shape[0], 1, max_agents_to_plot)) + + else: + agent_indices = [agent_idx] + + for agent_idx in agent_indices: + valid_mask = gt_trajectories["valid"][agent_idx, 0, :] == 1 + invalid_mask = ~valid_mask + + last_valid_idx = np.where(valid_mask)[0][-1] if valid_mask.any() else 0 + goal_x = gt_trajectories["x"][agent_idx, 0, last_valid_idx] + goal_y = gt_trajectories["y"][agent_idx, 0, last_valid_idx] + goal_radius = 2.0 # Note: Hardcoded here; ideally pass from config + + fig, axs = plt.subplots(1, 3, figsize=(12, 4)) + + axs[0].set_title(f"Simulated rollouts (x, y) for agent id: {simulated_trajectories['id'][agent_idx, 0][0]}") + + for i in range(self.num_rollouts): + # Sample random color for each rollout + color = plt.cm.tab20(i % 20) + axs[0].scatter( + simulated_trajectories["x"][agent_idx, i, :], + simulated_trajectories["y"][agent_idx, i, :], + alpha=0.1, + color=color, + ) + + axs[1].set_title( + f"Simulated rollouts (x, y) and GT; agent id: {simulated_trajectories['id'][agent_idx, 0][0]}" + ) + + axs[1].scatter( + simulated_trajectories["x"][agent_idx, :, valid_mask], + simulated_trajectories["y"][agent_idx, :, valid_mask], + color="b", + alpha=0.1, + zorder=4, + ) + + axs[1].scatter( + gt_trajectories["x"][agent_idx, 0, valid_mask], + gt_trajectories["y"][agent_idx, 0, valid_mask], + color="g", + label="Ground truth", + alpha=0.5, + ) + + axs[1].scatter( + gt_trajectories["x"][agent_idx, 0, 0], + gt_trajectories["y"][agent_idx, 0, 0], + color="darkgreen", + marker="*", + s=200, + label="Log start", + zorder=5, + alpha=0.5, + ) + axs[1].scatter( + simulated_trajectories["x"][agent_idx, :, 0], + simulated_trajectories["y"][agent_idx, :, 0], + color="darkblue", + marker="*", + s=200, + label="Agent start", + zorder=5, + alpha=0.5, + ) + + circle = plt.Circle( + (goal_x, goal_y), + goal_radius, + color="g", + fill=False, + linewidth=2, + linestyle="--", + label=f"Goal radius ({goal_radius}m)", + zorder=0, + ) + axs[1].add_patch(circle) + + axs[1].set_xlabel("x") + axs[1].set_ylabel("y") + axs[1].legend() + axs[1].set_aspect("equal", adjustable="datalim") + + axs[2].set_title(f"Heading timeseries for agent ID: {simulated_trajectories['id'][agent_idx, 0][0]}") + time_steps = list(range(self.sim_steps)) + for r in range(self.num_rollouts): + axs[2].plot( + time_steps, + simulated_trajectories["heading"][agent_idx, r, :], + color="b", + alpha=0.1, + label="Simulated" if r == 0 else "", + ) + axs[2].plot(time_steps, gt_trajectories["heading"][agent_idx, 0, :], color="g", label="Ground truth") + + if invalid_mask.any(): + invalid_timesteps = np.where(invalid_mask)[0] + axs[2].scatter( + invalid_timesteps, + gt_trajectories["heading"][agent_idx, 0, invalid_mask], + color="r", + marker="^", + s=100, + label="Invalid", + zorder=6, + edgecolors="darkred", + linewidths=1, + ) + + axs[2].set_xlabel("Time step") + axs[2].legend() + + plt.tight_layout() + + plt.savefig(f"trajectory_comparison_agent_{agent_idx}.png") + + +class HumanReplayEvaluator: + """Evaluates policies against human replays in PufferDrive.""" + + def __init__(self, config: Dict): + self.config = config + self.sim_steps = 91 - self.config["env"]["init_steps"] + + def rollout(self, args, puffer_env, policy): + """Roll out policy in env with human replays. Store statistics. + + In human replay mode, only the SDC (self-driving car) is controlled by the policy + while all other agents replay their human trajectories. This tests how compatible + the policy is with (static) human partners. + + Args: + args: Config dict with train settings (device, use_rnn, etc.) + puffer_env: PufferLib environment wrapper + policy: Trained policy to evaluate + + Returns: + dict: Aggregated metrics including: + - avg_collisions_per_agent: Average collisions per agent + - avg_offroad_per_agent: Average offroad events per agent + """ + import numpy as np + import torch + import pufferlib + + num_agents = puffer_env.observation_space.shape[0] + device = args["train"]["device"] + + obs, info = puffer_env.reset() + state = {} + if args["train"]["use_rnn"]: + state = dict( + lstm_h=torch.zeros(num_agents, policy.hidden_size, device=device), + lstm_c=torch.zeros(num_agents, policy.hidden_size, device=device), + ) + + for time_idx in range(self.sim_steps): + # Step policy + with torch.no_grad(): + ob_tensor = torch.as_tensor(obs).to(device) + logits, value = policy.forward_eval(ob_tensor, state) + action, logprob, _ = pufferlib.pytorch.sample_logits(logits) + action_np = action.cpu().numpy().reshape(puffer_env.action_space.shape) + + if isinstance(logits, torch.distributions.Normal): + action_np = np.clip(action_np, puffer_env.action_space.low, puffer_env.action_space.high) + + obs, rewards, dones, truncs, info_list = puffer_env.step(action_np) + + if len(info_list) > 0: # Happens at the end of episode + results = info_list[0] + return results diff --git a/pufferlib/ocean/benchmark/metrics.py b/pufferlib/ocean/benchmark/metrics.py new file mode 100644 index 000000000..bc1f049e0 --- /dev/null +++ b/pufferlib/ocean/benchmark/metrics.py @@ -0,0 +1,214 @@ +"""Metrics computation for WOSAC realism evaluation. Adapted from +https://github.com/waymo-research/waymo-open-dataset/blob/master/src/waymo_open_dataset/wdl_limited/sim_agents_metrics/metrics.py +""" + +import numpy as np +from typing import Dict, Tuple + + +def compute_displacement_error( + pred_x: np.ndarray, + pred_y: np.ndarray, + ref_x: np.ndarray, + ref_y: np.ndarray, + ref_valid: np.ndarray, +) -> np.ndarray: + """Compute average displacement error (ADE) between simulated and ground truth trajectories. + + Args: + pred_x, pred_y: Simulated positions, shape (n_agents, n_rollouts, n_steps) + ref_x, ref_y: Ground truth positions, shape (n_agents, 1, n_steps) + ref_valid: Valid timesteps, shape (n_agents, 1, n_steps) + + Returns: + Average displacement error per agent per rollout, shape (n_agents, n_rollouts) + """ + + ref_traj = np.stack([ref_x, ref_y], axis=-1) # (n_agents, 1, n_steps, 2) + pred_traj = np.stack([pred_x, pred_y], axis=-1) + + # Compute displacement error for each timestep and every agent and rollout + displacement = np.linalg.norm(pred_traj - ref_traj, axis=-1) # (n_agents, n_rollouts, n_steps) + + # Mask invalid timesteps + displacement = np.where(ref_valid, displacement, 0.0) + + # Aggregate + valid_count = np.sum(ref_valid, axis=2) # (n_agents, 1) + + # Compute ADE + ade_per_rollout = np.sum(displacement, axis=2) / np.maximum(valid_count, 1) # (n_agents, n_rollouts) + + ade = ade_per_rollout.mean(axis=-1) # (n_agents,) + + # The rollout with the minimum ADE for each agent + min_ade = np.min(ade_per_rollout, axis=1) # (n_agents,) + + return ade, min_ade + + +def central_diff(t: np.ndarray, pad_value: float) -> np.ndarray: + """Computes the central difference along the last axis. + + This function is used to compute 1st order derivatives (speeds) when called + once. Calling this function twice is used to compute 2nd order derivatives + (accelerations) instead. + This function returns the central difference as + df(x)/dx = [f(x+h)-f(x-h)] / 2h. + + Args: + t: A float array of shape [..., steps]. + pad_value: To maintain the original tensor shape, this value is prepended + once and appended once to the difference. + + Returns: + An array of shape [..., steps] containing the central differences, + appropriately prepended and appended with `pad_value` to maintain the + original shape. + """ + # Prepare the array containing the value(s) to pad the result with. + pad_shape = (*t.shape[:-1], 1) + pad_array = np.full(pad_shape, pad_value) + diff_t = (t[..., 2:] - t[..., :-2]) / 2 + return np.concatenate([pad_array, diff_t, pad_array], axis=-1) + + +def central_logical_and(t: np.ndarray, pad_value: bool) -> np.ndarray: + """Computes the central `logical_and` along the last axis. + + This function is used to compute the validity tensor for 1st and 2nd order + derivatives using central difference, where element [i] is valid only if + both elements [i-1] and [i+1] are valid. + + Args: + t: A bool array of shape [..., steps]. + pad_value: To maintain the original tensor shape, this value is prepended + once and appended once to the difference. + + Returns: + An array of shape [..., steps] containing the central `logical_and`, + appropriately prepended and appended with `pad_value` to maintain the + original shape. + """ + # Prepare the array containing the value(s) to pad the result with. + pad_shape = (*t.shape[:-1], 1) + pad_array = np.full(pad_shape, pad_value) + diff_t = np.logical_and(t[..., 2:], t[..., :-2]) + return np.concatenate([pad_array, diff_t, pad_array], axis=-1) + + +def compute_displacement_error_3d( + x: np.ndarray, y: np.ndarray, z: np.ndarray, ref_x: np.ndarray, ref_y: np.ndarray, ref_z: np.ndarray +) -> np.ndarray: + """Computes displacement error (in x,y,z) w.r.t. a reference trajectory. + + Note: This operation doesn't put any constraint on the shape of the arrays, + except that they are all consistent with each other, so this can be used + with any arbitrary array shape. + + Args: + x: The x-component of the predicted trajectories. + y: The y-component of the predicted trajectories + ref_x: The x-component of the reference trajectories. + ref_y: The y-component of the reference trajectories. + + Returns: + A float array with the same shape as all the arguments, containing + the 3D distance between the predicted trajectories and the reference + trajectories. + """ + return np.linalg.norm(np.stack([x, y], axis=-1) - np.stack([ref_x, ref_y], axis=-1), ord=2, axis=-1) + + +def compute_kinematic_features( + x: np.ndarray, + y: np.ndarray, + heading: np.ndarray, + seconds_per_step: float = 0.1, +) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """Computes kinematic features (speeds and accelerations). + + Note: Everything is assumed to be valid, filtering must be done afterwards. + To maintain the original tensor length, speeds are prepended and appended + with 1 np.nan, while accelerations with 2 np.nan (since central difference + invalidated the two extremes). + + Args: + x: A float array of shape (..., num_steps) containing x coordinates. + y: A float array of shape (..., num_steps) containing y coordinates. + heading: A float array of shape (..., num_steps,) containing heading. + seconds_per_step: The duration (in seconds) of one step. Defaults to 0.1s. + + Returns: + A tuple containing the following 4 arrays: + linear_speed: Magnitude of speed in (x, y, z). Shape (..., num_steps). + linear_acceleration: Linear signed acceleration (changes in linear speed). + Shape (..., num_steps). + angular_speed: Angular speed (changes in heading). Shape (..., num_steps). + angular_acceleration: Angular acceleration (changes in `angular_speed`). + Shape (..., num_steps). + """ + # Linear speed and acceleration. + dpos = central_diff(np.stack([x, y], axis=0), pad_value=np.nan) + linear_speed = np.linalg.norm(dpos, ord=2, axis=0) / seconds_per_step + linear_accel = central_diff(linear_speed, pad_value=np.nan) / seconds_per_step + # Angular speed and acceleration. + dh_step = _wrap_angle(central_diff(heading, pad_value=np.nan) * 2) / 2 + dh = dh_step / seconds_per_step + d2h_step = _wrap_angle(central_diff(dh_step, pad_value=np.nan) * 2) / 2 + d2h = d2h_step / (seconds_per_step**2) + return linear_speed, linear_accel, dh, d2h + + +def _wrap_angle(angle: np.ndarray) -> np.ndarray: + """Wraps angles in the range [-pi, pi].""" + return (angle + np.pi) % (2 * np.pi) - np.pi + + +def compute_kinematic_validity(valid: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: + """Return validity tensors for speeds and accelerations. + + Computes validity using central logical_and: element [i] is valid only if + both elements [i-1] and [i+1] are valid. Applied once for speeds and twice + for accelerations. + + Args: + valid: A boolean array of shape (..., num_steps) containing whether a + certain object is valid at that step. + + Returns: + speed_validity: A validity array for speed fields (central_and applied once). + acceleration_validity: A validity array for acceleration fields (central_and applied twice). + """ + # First application for speeds + pad_shape = (*valid.shape[:-1], 1) + pad_tensor = np.full(pad_shape, False) + speed_validity = np.concatenate([pad_tensor, np.logical_and(valid[..., 2:], valid[..., :-2]), pad_tensor], axis=-1) + + # Second application for accelerations + pad_tensor = np.full(pad_shape, False) + acceleration_validity = np.concatenate( + [pad_tensor, np.logical_and(speed_validity[..., 2:], speed_validity[..., :-2]), pad_tensor], axis=-1 + ) + + return speed_validity, acceleration_validity + + +def _reduce_average_with_validity(tensor: np.ndarray, validity: np.ndarray, axis: int = None) -> np.ndarray: + """Returns the tensor's average, only selecting valid items. + + Args: + tensor: A float array of any shape. + validity: A boolean array of the same shape as `tensor`. + axis: The axis or axes along which to average. If None, averages over all axes. + + Returns: + A float or array containing the average of the valid elements of `tensor`. + """ + if tensor.shape != validity.shape: + raise ValueError( + f"Shapes of `tensor` and `validity` must be the same. (Actual: {tensor.shape}, {validity.shape})." + ) + cond_sum = np.sum(np.where(validity, tensor, np.zeros_like(tensor)), axis=axis, keepdims=False) + valid_sum = np.sum(validity.astype(np.float32), axis=axis, keepdims=False) + return cond_sum / valid_sum diff --git a/pufferlib/ocean/benchmark/metrics_sanity_check.py b/pufferlib/ocean/benchmark/metrics_sanity_check.py new file mode 100644 index 000000000..bb0acfbeb --- /dev/null +++ b/pufferlib/ocean/benchmark/metrics_sanity_check.py @@ -0,0 +1,98 @@ +""" +Validation script for WOSAC log-likelihood metrics. + +Idea is to check how the log-likelihood metrics change as we replace +increasing numbers of random rollouts with ground-truth data. +""" + +import argparse +import numpy as np + +from pufferlib.pufferl import load_config, load_env, load_policy +from pufferlib.ocean.benchmark.evaluator import WOSACEvaluator + + +def replace_rollouts_with_gt(simulated_traj, gt_traj, num_replacements): + """Replace first N rollouts with ground truth.""" + modified = {} + for key in simulated_traj: + if key in ["x", "y", "z", "heading"]: + modified[key] = simulated_traj[key].copy() + modified[key][:, :num_replacements, :] = np.broadcast_to( + gt_traj[key], (gt_traj[key].shape[0], num_replacements, gt_traj[key].shape[2]) + ) + else: + modified[key] = simulated_traj[key].copy() + return modified + + +def run_validation_experiment(config, vecenv, policy): + evaluator = WOSACEvaluator(config) + + gt_trajectories = evaluator.collect_ground_truth_trajectories(vecenv) + simulated_trajectories = evaluator.collect_simulated_trajectories(config, vecenv, policy) + + results = {} + for num_gt in [0, 1, 2, 8, 16, 32]: + modified_sim = replace_rollouts_with_gt(simulated_trajectories, gt_trajectories, num_gt) + scene_results = evaluator.compute_metrics(gt_trajectories, modified_sim) + + results[num_gt] = { + "ade": scene_results["ade"].mean(), + "min_ade": scene_results["min_ade"].mean(), + "likelihood_linear_speed": scene_results["likelihood_linear_speed"].mean(), + "likelihood_linear_acceleration": scene_results["likelihood_linear_acceleration"].mean(), + "likelihood_angular_speed": scene_results["likelihood_angular_speed"].mean(), + "likelihood_angular_acceleration": scene_results["likelihood_angular_acceleration"].mean(), + "realism_metametric": scene_results["realism_metametric"].mean(), + } + + return results + + +def format_results_table(results): + lines = [ + "## WOSAC Log-Likelihood Validation Results\n", + "| GT Rollouts | ADE | minADE | Linear Speed | Linear Accel | Angular Speed | Angular Accel | Realism Metametric |", + "|-------------|--------|--------|--------------|--------------|---------------|---------------|--------------------|\n", + ] + + for num_gt in sorted(results.keys()): + label = f"{num_gt:2d} (random)" if num_gt == 0 else f"{num_gt:2d} (all GT)" if num_gt == 32 else f"{num_gt:2d}" + r = results[num_gt] + lines.append( + f"| {label:11s} | {r['ade']:6.4f} | {r['min_ade']:6.4f} | {r['likelihood_linear_speed']:12.4f} | " + f"{r['likelihood_linear_acceleration']:12.4f} | {r['likelihood_angular_speed']:13.4f} | " + f"{r['likelihood_angular_acceleration']:13.4f} | {r['realism_metametric']:18.4f} |" + ) + + return "\n".join(lines) + + +def main(): + parser = argparse.ArgumentParser(description="Validate WOSAC log-likelihood metrics") + parser.add_argument("--env", default="puffer_drive") + parser.add_argument("--config", default="config/ocean/drive.ini") + args = parser.parse_args() + + config = load_config(args.env) + config["vec"]["backend"] = "PufferEnv" + config["vec"]["num_envs"] = 1 + config["eval"]["enabled"] = True + config["eval"]["wosac_num_rollouts"] = 32 + + config["env"]["num_agents"] = config["eval"]["wosac_num_agents"] + config["env"]["init_mode"] = config["eval"]["wosac_init_mode"] + config["env"]["control_mode"] = config["eval"]["wosac_control_mode"] + config["env"]["init_steps"] = config["eval"]["wosac_init_steps"] + config["env"]["goal_behavior"] = config["eval"]["wosac_goal_behavior"] + + vecenv = load_env(args.env, config) + policy = load_policy(config, vecenv, args.env) + + results = run_validation_experiment(config, vecenv, policy) + print("\n" + format_results_table(results)) + + +if __name__ == "__main__": + main() diff --git a/pufferlib/ocean/benchmark/wosac.ini b/pufferlib/ocean/benchmark/wosac.ini new file mode 100644 index 000000000..a7632e65d --- /dev/null +++ b/pufferlib/ocean/benchmark/wosac.ini @@ -0,0 +1,70 @@ +# Taken from WOSAC 2025 Sim Agents Challenge configuration +# Link: https://github.com/waymo-research/waymo-open-dataset/blob/master/src/waymo_open_dataset/wdl_limited/sim_agents_metrics/challenge_2025_sim_agents_config.textproto + +[linear_speed] +histogram.min_val = 0.0 +histogram.max_val = 25.0 +histogram.num_bins = 10 +histogram.additive_smoothing_pseudocount = 0.1 +independent_timesteps = true +metametric_weight = 0.05 + +[linear_acceleration] +histogram.min_val = -12.0 +histogram.max_val = 12.0 +histogram.num_bins = 11 +histogram.additive_smoothing_pseudocount = 0.1 +independent_timesteps = true +metametric_weight = 0.05 + +[angular_speed] +histogram.min_val = -0.628 +histogram.max_val = 0.628 +histogram.num_bins = 11 +histogram.additive_smoothing_pseudocount = 0.1 +independent_timesteps = true +metametric_weight = 0.05 + +[angular_acceleration] +histogram.min_val = -3.14 +histogram.max_val = 3.14 +histogram.num_bins = 11 +histogram.additive_smoothing_pseudocount = 0.1 +independent_timesteps = true +metametric_weight = 0.05 + +[distance_to_nearest_object] +histogram.min_val = -5.0 +histogram.max_val = 40.0 +histogram.num_bins = 10 +histogram.additive_smoothing_pseudocount = 0.1 +independent_timesteps = true +metametric_weight = 0.1 + +[collision_indication] +bernoulli = true +metametric_weight = 0.25 + +[distance_to_road_edge] +histogram.min_val = -20.0 +histogram.max_val = 40 +histogram.num_bins = 10 +histogram.additive_smoothing_pseudocount = 0.1 +independent_timesteps = true +metametric_weight = 0.05 + +[offroad_indication] +bernoulli = true +metametric_weight = 0.25 + +[time_to_collision] +histogram.min_val = 0.0 +histogram.max_val = 5.0 +histogram.num_bins = 10 +histogram.additive_smoothing_pseudocount = 0.1 +independent_timesteps = true +metametric_weight = 0.1 + +[traffic_light_violation] +bernoulli = true +metametric_weight = 0.05 diff --git a/pufferlib/ocean/blastar/binding.c b/pufferlib/ocean/blastar/binding.c deleted file mode 100644 index 03e318a12..000000000 --- a/pufferlib/ocean/blastar/binding.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "blastar.h" -#define Env Blastar -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->num_obs = unpack(kwargs, "num_obs"); - init(env, env->num_obs); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "lives", log->lives); - assign_to_dict(dict, "vertical_closeness_rew", log->vertical_closeness_rew); - assign_to_dict(dict, "fired_bullet_rew", log->fired_bullet_rew); - assign_to_dict(dict, "kill_streak", log->kill_streak); - assign_to_dict(dict, "hit_enemy_with_bullet_rew", log->hit_enemy_with_bullet_rew); - assign_to_dict(dict, "avg_score_difference", log->avg_score_difference); - return 0; -} diff --git a/pufferlib/ocean/blastar/blastar.c b/pufferlib/ocean/blastar/blastar.c deleted file mode 100644 index a4400288d..000000000 --- a/pufferlib/ocean/blastar/blastar.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "blastar.h" -#include -#include -#include -#include -#include "puffernet.h" - -const char* WEIGHTS_PATH = "resources/blastar/blastar_weights.bin"; -#define OBSERVATIONS_SIZE 10 -#define ACTIONS_SIZE 6 -#define NUM_WEIGHTS 134407 - -void get_input(Blastar* env) { - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) { - env->actions[0] = 1; // Left - } else if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) { - env->actions[0] = 2; // Right - } else if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) { - env->actions[0] = 3; // Up - } else if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) { - env->actions[0] = 4; // Down - } else if (IsKeyDown(KEY_SPACE)) { - env->actions[0] = 5; // Fire - } else { - env->actions[0] = 0; // Noop - } -} - -int demo() { - Weights* weights = load_weights(WEIGHTS_PATH, NUM_WEIGHTS); - int logit_sizes[1] = {ACTIONS_SIZE}; - LinearLSTM* net = make_linearlstm(weights, 1, OBSERVATIONS_SIZE, logit_sizes, 1); - Blastar env = { - .num_obs = OBSERVATIONS_SIZE, - }; - allocate(&env, env.num_obs); - Client* client = make_client(&env); - unsigned int seed = 12345; - srand(seed); - c_reset(&env); - int running = 1; - while (running) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - get_input(&env); // Human input - } else { - forward_linearlstm(net, env.observations, env.actions); // AI input - } - c_step(&env); - c_render(&env); - if (WindowShouldClose() || env.game_over) { - running = 0; - } - } - free_linearlstm(net); - free(weights); - close_client(client); - free_allocated(&env); - return 0; -} - -void perftest(float test_time) { - Blastar env = { - .num_obs = OBSERVATIONS_SIZE, - }; - allocate(&env, env.num_obs); - unsigned int seed = 12345; - srand(seed); - c_reset(&env); - int start = time(NULL); - int steps = 0; - while (time(NULL) - start < test_time) { - env.actions[0] = rand() % ACTIONS_SIZE; // Random actions - c_step(&env); - steps++; - } - int end = time(NULL); - printf("Steps per second: %f\n", steps / (float)(end - start)); - free_allocated(&env); -} - -int main() { - demo(); - // perftest(10.0f); - return 0; -} diff --git a/pufferlib/ocean/blastar/blastar.h b/pufferlib/ocean/blastar/blastar.h deleted file mode 100644 index 7020c0c1c..000000000 --- a/pufferlib/ocean/blastar/blastar.h +++ /dev/null @@ -1,499 +0,0 @@ -#include -#include -#include -#include -#include - -#include "raylib.h" - -#define SCREEN_WIDTH 640 -#define SCREEN_HEIGHT 480 -#define PLAYER_MAX_LIVES 10 -#define ENEMY_SPAWN_Y 50 -#define ENEMY_SPAWN_X -30 -#define INIT_BULLET_SPEED 3.0f -#define MAX_SCORE (5 * PLAYER_MAX_LIVES) -#define BULLET_SPEED (INIT_BULLET_SPEED * SPEED_SCALE) - -static const float SPEED_SCALE = 4.0f; -static const int ENEMY_WIDTH = 16; -static const int ENEMY_HEIGHT = 17; -static const int PLAYER_WIDTH = 17; -static const int PLAYER_HEIGHT = 17; -static const int PLAYER_BULLET_WIDTH = 17; -static const int PLAYER_BULLET_HEIGHT = 6; - -typedef struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float lives; - float vertical_closeness_rew; - float fired_bullet_rew; - float kill_streak; - float hit_enemy_with_bullet_rew; - float avg_score_difference; - float n; -} Log; - -typedef struct Bullet { - float x; - float y; - bool active; -} Bullet; - -typedef struct Enemy { - float x; - float y; - float enemy_speed; - bool active; - bool attacking; - int crossed_screen; - Bullet bullet; -} Enemy; - -typedef struct Player { - float x; - float y; - float player_speed; - int score; - int lives; - Bullet bullet; - bool bullet_fired; - bool player_stuck; -} Player; - -typedef struct Client { - Texture2D player_texture; - Texture2D enemy_texture; - Texture2D player_bullet_texture; - Texture2D enemy_bullet_texture; - Texture2D explosion_texture; -} Client; - -typedef struct Blastar { - Client* client; - int reset_count; - int num_obs; - bool game_over; - int tick; - int player_explosion_timer; - int enemy_explosion_timer; - int kill_streak; - int enemy_respawns; - Player player; - Enemy enemy; - float* observations; - int* actions; - float* rewards; - unsigned char* terminals; - Log log; -} Blastar; - -void add_log(Blastar* env) { - env->log.episode_length += env->tick; - env->log.lives = env->player.lives; - env->log.score = env->player.score; - env->log.perf = env->player.score / MAX_SCORE; - env->log.kill_streak = env->kill_streak; - env->log.n += 1; -} - -static inline void scale_speeds(Blastar* env) { - env->player.player_speed *= SPEED_SCALE; - env->enemy.enemy_speed *= SPEED_SCALE; -} - -void c_reset(Blastar* env) { - env->game_over = false; - env->tick = 0; - env->player_explosion_timer = 0; - env->enemy_explosion_timer = 0; - env->player.player_speed = 2.0f; - env->enemy.enemy_speed = 1.0f; - scale_speeds(env); - env->player.x = (float)(rand() % (SCREEN_WIDTH - PLAYER_WIDTH)); - env->player.y = (float)(rand() % (SCREEN_HEIGHT - PLAYER_HEIGHT)); - env->player.score = 0; - env->player.lives = PLAYER_MAX_LIVES; - env->player.bullet_fired = false; - env->player.player_stuck = false; - env->player.bullet.active = false; - env->player.bullet.x = env->player.x; - env->player.bullet.y = env->player.y; - env->kill_streak = 0; - env->enemy.x = ENEMY_SPAWN_X; - env->enemy.y = ENEMY_SPAWN_Y; - env->enemy.active = true; - env->enemy.attacking = false; - if (env->reset_count < 1) { - env->enemy_respawns = 0; - env->enemy.crossed_screen = 0; - } - env->enemy.bullet.active = false; - env->enemy.bullet.x = env->enemy.x; - env->enemy.bullet.y = env->enemy.y; - env->reset_count++; - - env->log = (Log){0}; -} - -void c_close(Blastar* env) { -} - -void init(Blastar* env, int num_obs) { - env->reset_count = 0; - env->num_obs = num_obs; - c_reset(env); -} - -void allocate(Blastar* env, int num_obs) { - init(env, num_obs); - env->observations = (float*)calloc(env->num_obs, sizeof(float)); - env->actions = (int*)calloc(1, sizeof(int)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void free_allocated(Blastar* env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); -} - -static inline void calculate_center(float x, float y, int width, int height, float* center_x, float* center_y) { - *center_x = x + width / 2.0f; - *center_y = y + height / 2.0f; -} - -void compute_observations(Blastar* env) { - env->log.lives = env->player.lives; - env->log.score = env->player.score; - - memset(env->observations, 0, env->num_obs * sizeof(float)); - env->observations[0] = env->player.x / SCREEN_WIDTH; - env->observations[1] = env->player.y / SCREEN_HEIGHT; - env->observations[2] = env->enemy.x / SCREEN_WIDTH; - env->observations[3] = env->enemy.y / SCREEN_HEIGHT; - if (env->player.bullet.active) { - env->observations[4] = env->player.bullet.x / SCREEN_WIDTH; - env->observations[5] = env->player.bullet.y / SCREEN_HEIGHT; - env->observations[6] = 1.0f; - } - if (env->enemy.bullet.active) { - env->observations[7] = env->enemy.bullet.x / SCREEN_WIDTH; - env->observations[8] = env->enemy.bullet.y / SCREEN_HEIGHT; - env->observations[9] = 1.0f; - } -} - -bool check_collision(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2) { - if (x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2) { - return true; - } - return false; -} - -void c_step(Blastar* env) { - if (env->game_over) { - if (env->terminals) env->terminals[0] = 1; - add_log(env); - c_reset(env); - return; - } - - env->tick++; - env->log.episode_length += 1; - float rew = 0.0f; - env->rewards[0] = rew; - float fired_bullet_rew = 0.0f; - float vertical_closeness_rew = 0.0f; - float hit_enemy_with_bullet_rew = 0.0f; - int crossed_screen = 0; - int action = env->actions[0]; - - if (env->player_explosion_timer > 0) { - env->player_explosion_timer--; - env->kill_streak = 0; - if (env->player_explosion_timer == 0) { - env->player.player_stuck = false; - env->player.bullet.active = false; - } - compute_observations(env); - add_log(env); - return; - } - - if (env->enemy_explosion_timer > 0) { - env->enemy_explosion_timer--; - if (env->enemy_explosion_timer == 0) { - env->enemy.crossed_screen = 0; - float respawn_bias = 0.1f; - if ((float)rand() / (float)RAND_MAX > respawn_bias) { - env->enemy.x = -ENEMY_WIDTH; - env->enemy.y = rand() % (SCREEN_HEIGHT - ENEMY_HEIGHT); - env->enemy_respawns += 1; - } - env->enemy.active = true; - env->enemy.attacking = false; - } - compute_observations(env); - add_log(env); - return; - } - - if (env->enemy.y > (SCREEN_HEIGHT - (ENEMY_HEIGHT * 3.5f))) { - env->enemy.y = (SCREEN_HEIGHT - (ENEMY_HEIGHT * 3.5f)); - } - - if (!env->player.player_stuck) { - if (action == 1 && env->player.x > 0) env->player.x -= env->player.player_speed; - if (action == 2 && env->player.x < SCREEN_WIDTH - PLAYER_WIDTH) env->player.x += env->player.player_speed; - if (action == 3 && env->player.y > 0) env->player.y -= env->player.player_speed; - if (action == 4 && env->player.y < SCREEN_HEIGHT - PLAYER_HEIGHT) env->player.y += env->player.player_speed; - } - - if (action == 5 && (!env->enemy.bullet.active)) { - if (env->player.bullet.active) { - env->player.bullet.active = false; - } else { - fired_bullet_rew += 0.0005f; - } - env->player.bullet.active = true; - env->player.bullet.x = env->player.x + PLAYER_WIDTH / 2 - PLAYER_BULLET_WIDTH / 2; - env->player.bullet.y = env->player.y; - } - - if (env->player.bullet.active) { - env->player.bullet.y -= BULLET_SPEED; - if (env->player.bullet.y < 0) { - env->player.bullet.active = false; - } - } - - float player_center_x; - float enemy_center_x; - float dummy; - calculate_center(env->player.x, env->player.y, PLAYER_WIDTH, PLAYER_HEIGHT, &player_center_x, &dummy); - calculate_center(env->enemy.x, env->enemy.y, ENEMY_WIDTH, ENEMY_HEIGHT, &enemy_center_x, &dummy); - - if (!env->enemy.attacking) { - env->enemy.x += env->enemy.enemy_speed; - if (env->enemy.x > SCREEN_WIDTH) { - env->enemy.x = -ENEMY_WIDTH; - crossed_screen += 1; - } - } - - if (fabs(player_center_x - enemy_center_x) < SPEED_SCALE && - !env->enemy.attacking && env->enemy.active && - env->enemy.y < env->player.y - (ENEMY_HEIGHT / 2)) { - if (rand() % 2 == 0) { - env->enemy.attacking = true; - if (!env->enemy.bullet.active) { - env->enemy.bullet.active = true; - calculate_center(env->enemy.x, env->enemy.y, ENEMY_WIDTH, ENEMY_HEIGHT, &enemy_center_x, &dummy); - env->enemy.bullet.x = enemy_center_x - 5.0f; - env->enemy.bullet.y = env->enemy.y + ENEMY_HEIGHT; - env->player.bullet.active = false; - env->player.player_stuck = true; - } - } else { - env->enemy.attacking = false; - env->enemy.x += env->enemy.enemy_speed; - } - } - - if (env->enemy.bullet.active) { - env->enemy.bullet.y += BULLET_SPEED; - if (env->enemy.bullet.y > SCREEN_HEIGHT) { - env->enemy.bullet.active = false; - env->player.player_stuck = false; - env->enemy.attacking = false; - } - } - - if (check_collision(env->player.x, env->player.y, PLAYER_WIDTH, PLAYER_HEIGHT, - env->enemy.x, env->enemy.y, ENEMY_WIDTH, ENEMY_HEIGHT)) { - env->player.lives--; - env->enemy.active = false; - env->enemy_explosion_timer = 30; - env->enemy.x = -ENEMY_WIDTH; - env->enemy.y = rand() % (SCREEN_HEIGHT - ENEMY_HEIGHT); - env->player_explosion_timer = 30; - env->player.player_stuck = false; - - if (env->player.lives <= 0) { - env->player.lives = 0; - env->game_over = true; - if (env->terminals) env->terminals[0] = 1; - add_log(env); - compute_observations(env); - c_reset(env); - } - compute_observations(env); - add_log(env); - return; - } - - if (env->player.bullet.active && env->player.y > env->enemy.y + ENEMY_HEIGHT && - check_collision(env->player.bullet.x, env->player.bullet.y, PLAYER_BULLET_WIDTH, PLAYER_BULLET_HEIGHT, - env->enemy.x, env->enemy.y, ENEMY_WIDTH, ENEMY_HEIGHT) && - env->enemy.active) { - env->player.bullet.active = false; - env->enemy.active = false; - env->kill_streak += 1; - fired_bullet_rew += 1.5f; - env->player.score += 1; - env->log.score += 1.0f; - env->enemy_explosion_timer = 30; - float enemy_x_normalized = 1.0f - (env->enemy.x / SCREEN_WIDTH); - hit_enemy_with_bullet_rew += (crossed_screen == 0) ? (4.5f * enemy_x_normalized) - : (3.5f * enemy_x_normalized); - } - - if (env->enemy.bullet.active && - check_collision(env->enemy.bullet.x, env->enemy.bullet.y, 10, 12, - env->player.x, env->player.y, PLAYER_WIDTH, PLAYER_HEIGHT)) { - env->enemy.bullet.active = false; - env->player.lives--; - env->player_explosion_timer = 30; - env->player.player_stuck = false; - env->enemy.attacking = false; - env->enemy.x = -ENEMY_WIDTH; - env->enemy.y = rand() % (SCREEN_HEIGHT - ENEMY_HEIGHT); - - if (env->player.lives <= 0) { - env->player.lives = 0; - env->game_over = true; - if (env->terminals) { - env->terminals[0] = 1; - } - compute_observations(env); - add_log(env); - c_reset(env); - } - } - - if (!(env->player.y > env->enemy.y + ENEMY_HEIGHT)) { - vertical_closeness_rew = 0.0f; - fired_bullet_rew = 0.0f; - hit_enemy_with_bullet_rew = 0.0f; - } else { - float v_delta_distance = env->player.y - env->enemy.y; - v_delta_distance = 2.0f - (v_delta_distance / SCREEN_HEIGHT); - vertical_closeness_rew = 0.01f * v_delta_distance; - } - - float avg_score_difference = 0.0f; - if (env->player.score > 0) { - avg_score_difference = (float)env->player.score / (env->tick + 1); - } - - env->log.avg_score_difference = avg_score_difference; - env->log.fired_bullet_rew = fired_bullet_rew; - env->log.kill_streak = env->kill_streak; - env->log.hit_enemy_with_bullet_rew = hit_enemy_with_bullet_rew; - env->log.vertical_closeness_rew = vertical_closeness_rew; - env->enemy.crossed_screen = crossed_screen; - - rew += fired_bullet_rew + vertical_closeness_rew + hit_enemy_with_bullet_rew + avg_score_difference; - rew *= (1.0f + env->kill_streak * 0.1f); - - if (!(env->player.y > env->enemy.y + ENEMY_HEIGHT && fabs(player_center_x - enemy_center_x) > ENEMY_WIDTH * 0.3f)) { - rew = fminf(rew, 0.0f); - } - - if (env->player.x > SCREEN_WIDTH / 2.0f) { - env->log.episode_return = 0; - rew = 0.0f; - } - - env->rewards[0] = rew; - env->log.episode_return += rew; - - if (env->player.score > MAX_SCORE) { - env->game_over = true; - env->terminals[0] = 1; - compute_observations(env); - add_log(env); - c_reset(env); - } - - compute_observations(env); -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -Client* make_client(Blastar* env) { - InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Blastar"); - Client* client = (Client*)malloc(sizeof(Client)); - SetTargetFPS(60); - client->player_texture = LoadTexture("resources/blastar/player_ship.png"); - client->enemy_texture = LoadTexture("resources/blastar/enemy_ship.png"); - client->player_bullet_texture = LoadTexture("resources/blastar/player_bullet.png"); - client->enemy_bullet_texture = LoadTexture("resources/blastar/enemy_bullet.png"); - client->explosion_texture = LoadTexture("resources/blastar/player_death_explosion.png"); - env->client = client; - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -void c_render(Blastar* env) { - if (env->client == NULL) { - make_client(env); - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - Client* client = env->client; - - if (WindowShouldClose()) { - env->game_over = true; - close_client(client); - env->client = NULL; - exit(0); - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - if (env->game_over && env->player.lives <=0) { - DrawText("GAME OVER", SCREEN_WIDTH / 2 - MeasureText("GAME OVER", 30) / 2, SCREEN_HEIGHT / 2 - 15, 30, PUFF_RED); - DrawText(TextFormat("FINAL SCORE: %d", env->player.score), SCREEN_WIDTH / 2 - MeasureText(TextFormat("FINAL SCORE: %d", env->player.score), 20)/2, SCREEN_HEIGHT / 2 + 25, 20, PUFF_CYAN); - } else { - if (env->player_explosion_timer > 0) { - DrawTexture(client->explosion_texture, env->player.x, env->player.y, WHITE); - } else if (env->player.lives > 0) { - DrawTexture(client->player_texture, env->player.x, env->player.y, WHITE); - } - if (env->enemy_explosion_timer > 0) { - DrawTexture(client->explosion_texture, env->enemy.x, env->enemy.y, WHITE); - } else if (env->enemy.active) { - DrawTexture(client->enemy_texture, env->enemy.x, env->enemy.y, WHITE); - } - if (env->player.bullet.active) { - DrawTexture(client->player_bullet_texture, env->player.bullet.x, env->player.bullet.y, WHITE); - } - if (env->enemy.bullet.active) { - DrawTexture(client->enemy_bullet_texture, env->enemy.bullet.x, env->enemy.bullet.y, WHITE); - } - if (env->player.player_stuck) { - DrawText("Status Beam", SCREEN_WIDTH - MeasureText("Status Beam", 20) - 10, SCREEN_HEIGHT / 3, 20, PUFF_RED); - } - DrawText(TextFormat("SCORE: %d", env->player.score), 10, 10, 20, PUFF_CYAN); - DrawText(TextFormat("LIVES: %d", env->player.lives), SCREEN_WIDTH - MeasureText(TextFormat("LIVES: %d", env->player.lives), 20) - 10, 10, 20, PUFF_CYAN); - } - EndDrawing(); -} diff --git a/pufferlib/ocean/blastar/blastar.py b/pufferlib/ocean/blastar/blastar.py deleted file mode 100644 index e93b3067e..000000000 --- a/pufferlib/ocean/blastar/blastar.py +++ /dev/null @@ -1,71 +0,0 @@ -import numpy as np -import gymnasium -import pufferlib -from pufferlib.ocean.blastar import binding - -class Blastar(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=1, shape=(10,), dtype=np.float32 - ) - self.single_action_space = gymnasium.spaces.Discrete(6) - self.render_mode = render_mode - self.num_agents = num_envs - self.num_obs = self.single_observation_space.shape[0] - self.tick = 0 - self.log_interval = 1 - - super().__init__(buf) - self.c_envs = binding.vec_init( - self.observations, - self.actions, - self.rewards, - self.terminals, - self.truncations, - num_envs, - seed, - num_obs=self.num_obs - ) - - def reset(self, seed=None): - self.tick = 0 - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = Blastar(num_envs=1000) - env.reset(0) - tick = 0 - - rng = np.random.default_rng() - actions = rng.integers(0, 6, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print('SPS:', env.num_agents * tick / (time.time() - start)) - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/boids/binding.c b/pufferlib/ocean/boids/binding.c deleted file mode 100644 index a3483d652..000000000 --- a/pufferlib/ocean/boids/binding.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "boids.h" - -#define Env Boids -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->num_boids = unpack(kwargs, "num_boids"); - env->report_interval = unpack(kwargs, "report_interval"); - env->margin_turn_factor = unpack(kwargs, "margin_turn_factor"); - env->centering_factor = unpack(kwargs, "centering_factor"); - env->avoid_factor = unpack(kwargs, "avoid_factor"); - env->matching_factor = unpack(kwargs, "matching_factor"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/boids/boids.c b/pufferlib/ocean/boids/boids.c deleted file mode 100644 index 735f8e8c9..000000000 --- a/pufferlib/ocean/boids/boids.c +++ /dev/null @@ -1,80 +0,0 @@ -// Standalone C demo for Boids environment -// Compile using: ./scripts/build_ocean.sh boids [local|fast] -// Run with: ./boids - -#include -#include "boids.h" - -// --- Demo Configuration --- -#define NUM_BOIDS_DEMO 20 // Number of boids for the standalone demo -#define MAX_STEPS_DEMO 500 // Max steps per episode in the demo -#define ACTION_SCALE 3.0f // Corresponds to action space [-3.0, 3.0] - -// Dummy action generation: random velocity changes for each boid -void generate_dummy_actions(Boids* env) { - for (unsigned int i = 0; i < env->num_boids; ++i) { - // Generate random floats in [-1, 1] range - float rand_vx = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - float rand_vy = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - - // Scale to the action space [-ACTION_SCALE, ACTION_SCALE] - env->actions[i * 2 + 0] = rand_vx * ACTION_SCALE; - env->actions[i * 2 + 1] = rand_vy * ACTION_SCALE; - } -} - -void demo() { - // Initialize Boids environment struct - Boids env = {0}; - env.num_boids = NUM_BOIDS_DEMO; - - // In the Python binding, these pointers are assigned from NumPy arrays. - // Here, we need to allocate them explicitly. - size_t obs_size = env.num_boids * 4; // num_boids * (x, y, vx, vy) - size_t act_size = env.num_boids * 2; // num_boids * (dvx, dvy) - env.observations = (float*)calloc(obs_size, sizeof(float)); - env.actions = (float*)calloc(act_size, sizeof(float)); - env.rewards = (float*)calloc(env.num_boids, sizeof(float)); // Env-level reward - - if (!env.observations || !env.actions || !env.rewards) { - fprintf(stderr, "ERROR: Failed to allocate memory for demo buffers.\n"); - free(env.observations); free(env.actions); free(env.rewards); - return; - } - - init(&env); - Client* client = make_client(&env); - - if (client == NULL) { - fprintf(stderr, "ERROR: Failed to create rendering client during initial setup.\n"); - c_close(&env); - free(env.observations); free(env.actions); free(env.rewards); - return; - } - env.client = client; - - // Initial reset - c_reset(&env); - int total_steps = 0; - - printf("Starting Boids demo with %d boids. Press ESC to exit.\n", env.num_boids); - - while (!WindowShouldClose() && total_steps < MAX_STEPS_DEMO) { // Raylib function to check if ESC is pressed or window closed - generate_dummy_actions(&env); - c_step(&env); - c_render(&env); - total_steps++; - } - - c_close(&env); - free(env.observations); - free(env.actions); - free(env.rewards); - // ---------------------------------------- -} - -int main() { - srand(time(NULL)); // Seed random number generator - demo(); - return 0; -} diff --git a/pufferlib/ocean/boids/boids.h b/pufferlib/ocean/boids/boids.h deleted file mode 100644 index bf2bf6331..000000000 --- a/pufferlib/ocean/boids/boids.h +++ /dev/null @@ -1,296 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "raylib.h" - -#define TOP_MARGIN 50 -#define BOTTOM_MARGIN 50 -#define LEFT_MARGIN 50 -#define RIGHT_MARGIN 50 -#define VELOCITY_CAP 5 -#define VISUAL_RANGE 20 -#define PROTECTED_RANGE 100 -#define WIDTH 1080 -#define HEIGHT 720 -#define BOID_WIDTH 32 -#define BOID_HEIGHT 32 -#define BOID_TEXTURE_PATH "./resources/puffers_128.png" - -typedef struct { - float perf; - float score; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct { - float x; - float y; -} Velocity; - -typedef struct { - float x; - float y; - Velocity velocity; -} Boid; - -typedef struct Client Client; -typedef struct { - // an array of shape (num_boids, 4) with the 4 values correspoinding to (x, y, velocity x, velocity y) - float* observations; - // an array of shape (num_boids, 2) with the 2 values correspoinding to (velocity x, velocity y) - float* actions; - // an array of shape (1) with the summed up reward for all boids - float* rewards; - unsigned char* terminals; // Not being used but is required by env_binding.h - Boid* boids; - unsigned int num_boids; - float margin_turn_factor; - float centering_factor; - float avoid_factor; - float matching_factor; - unsigned tick; - Log log; - Log* boid_logs; - unsigned report_interval; - Client* client; - -} Boids; - -static inline float flmax(float a, float b) { return a > b ? a : b; } -static inline float flmin(float a, float b) { return a > b ? b : a; } -static inline float flclip(float x,float lo,float hi) { return flmin(hi,flmax(lo,x)); } -static inline float rndf(float lo,float hi) { return lo + (float)rand()/(float)RAND_MAX*(hi-lo); } - -static void respawn_boid(Boids *env, unsigned int i) { - env->boids[i].x = rndf(LEFT_MARGIN, WIDTH - RIGHT_MARGIN); - env->boids[i].y = rndf(BOTTOM_MARGIN, HEIGHT - TOP_MARGIN); - env->boids[i].velocity.x = 0; - env->boids[i].velocity.y = 0; - env->boid_logs[i] = (Log){0}; -} - -void init(Boids *env) { - env->boids = (Boid*)calloc(env->num_boids, sizeof(Boid)); - env->boid_logs = (Log*)calloc(env->num_boids, sizeof(Log)); - env->log = (Log){0}; - env->tick = 0; - - for (unsigned current_indx = 0; current_indx < env->num_boids; current_indx++) { - env->boids[current_indx].x = rndf(LEFT_MARGIN, WIDTH - RIGHT_MARGIN); - env->boids[current_indx].y = rndf(BOTTOM_MARGIN, HEIGHT - TOP_MARGIN); - env->boids[current_indx].velocity.x = 0; - env->boids[current_indx].velocity.y = 0; - } -} - - -static void compute_observations(Boids *env) { - unsigned base_indx; - - int idx = 0; - for (unsigned i=0; inum_boids; i++) { - for (unsigned j=0; jnum_boids; j++) { - env->observations[idx++] = (env->boids[j].x - env->boids[i].x) / WIDTH; - env->observations[idx++] = (env->boids[j].y - env->boids[i].y) / HEIGHT; - env->observations[idx++] = (env->boids[j].velocity.x - env->boids[i].velocity.x) / VELOCITY_CAP; - env->observations[idx++] = (env->boids[j].velocity.y - env->boids[i].velocity.y) / VELOCITY_CAP; - } - } -} - -void c_reset(Boids *env) { - env->log = (Log){0}; - env->tick = 0; - for (unsigned boid_indx = 0; boid_indx < env->num_boids; boid_indx++) { - respawn_boid(env, boid_indx); - } - compute_observations(env); -} - -void c_step(Boids *env) { - Boid* current_boid; - Boid observed_boid; - float vis_vx_sum, vis_vy_sum, vis_x_sum, vis_y_sum, vis_x_avg, vis_y_avg, vis_vx_avg, vis_vy_avg; - float diff_x, diff_y, dist, protected_dist_sum, current_boid_reward; - unsigned visual_count, protected_count; - bool manual_control = IsKeyDown(KEY_LEFT_SHIFT); - float mouse_x = (float)GetMouseX(); - float mouse_y = (float)GetMouseY(); - - env->tick++; - env->rewards[0] = 0; - env->log.score = 0; - for (unsigned current_indx = 0; current_indx < env->num_boids; current_indx++) { - // apply action - current_boid = &env->boids[current_indx]; - if (manual_control) { - current_boid->velocity.x = flclip(current_boid->velocity.x + (mouse_x - current_boid->x), -VELOCITY_CAP, VELOCITY_CAP); - current_boid->velocity.y = flclip(current_boid->velocity.y + (mouse_y - current_boid->y), -VELOCITY_CAP, VELOCITY_CAP); - } else { - current_boid->velocity.x = flclip(current_boid->velocity.x + 2*env->actions[current_indx * 2 + 0], -VELOCITY_CAP, VELOCITY_CAP); - current_boid->velocity.y = flclip(current_boid->velocity.y + 2*env->actions[current_indx * 2 + 1], -VELOCITY_CAP, VELOCITY_CAP); - } - current_boid->x = flclip(current_boid->x + current_boid->velocity.x, 0, WIDTH - BOID_WIDTH); - current_boid->y = flclip(current_boid->y + current_boid->velocity.y, 0, HEIGHT - BOID_HEIGHT); - - // reward calculation - current_boid_reward = 0.0f, protected_dist_sum = 0.0f, protected_count = 0.0f; - visual_count = 0.0f, vis_vx_sum = 0.0f, vis_vy_sum = 0.0f, vis_x_sum = 0.0f, vis_y_sum = 0.0f; - for (unsigned observed_indx = 0; observed_indx < env->num_boids; observed_indx++) { - if (current_indx == observed_indx) continue; - observed_boid = env->boids[observed_indx]; - diff_x = current_boid->x - observed_boid.x; - diff_y = current_boid->y - observed_boid.y; - dist = sqrtf(diff_x*diff_x + diff_y*diff_y); - if (dist < PROTECTED_RANGE) { - protected_dist_sum += (PROTECTED_RANGE - dist); - protected_count++; - } else if (dist < VISUAL_RANGE) { - vis_x_sum += observed_boid.x; - vis_y_sum += observed_boid.y; - vis_vx_sum += observed_boid.velocity.x; - vis_vy_sum += observed_boid.velocity.y; - visual_count++; - } - } - if (protected_count > 0) { - //current_boid_reward -= fabsf(protected_dist_sum / protected_count) * env->avoid_factor; - current_boid_reward -= flclip(protected_count/5.0, 0.0f, 1.0f) * env->avoid_factor; - } - if (visual_count) { - vis_x_avg = vis_x_sum / visual_count; - vis_y_avg = vis_y_sum / visual_count; - vis_vx_avg = vis_vx_sum / visual_count; - vis_vy_avg = vis_vy_sum / visual_count; - - current_boid_reward -= fabsf(vis_vx_avg - current_boid->velocity.x) * env->matching_factor; - current_boid_reward -= fabsf(vis_vy_avg - current_boid->velocity.y) * env->matching_factor; - current_boid_reward -= fabsf(vis_x_avg - current_boid->x) * env->centering_factor; - current_boid_reward -= fabsf(vis_y_avg - current_boid->y) * env->centering_factor; - } - if (current_boid->y < TOP_MARGIN || current_boid->y > HEIGHT - BOTTOM_MARGIN) { - current_boid_reward -= env->margin_turn_factor; - } else { - current_boid_reward += env->margin_turn_factor; - } - if (current_boid->x < LEFT_MARGIN || current_boid->x > WIDTH - RIGHT_MARGIN) { - current_boid_reward -= env->margin_turn_factor; - } else { - current_boid_reward += env->margin_turn_factor; - } - // Normalization - // env->rewards[current_indx] = current_boid_reward / 15.0f; - // printf("current_boid_reward: %f\n", current_boid_reward); - env->rewards[current_indx] = current_boid_reward / 2.0f; - - //log updates - if (env->tick == env->report_interval) { - env->log.score += env->rewards[current_indx]; - env->log.n += 1.0f; - - /* clear per-boid log for next episode */ - // env->boid_logs[boid_indx] = (Log){0}; - env->tick = 0; - } - } - //env->log.score /= env->num_boids; - - compute_observations(env); -} - -typedef struct Client Client; -struct Client { - float width; - float height; - Texture2D boid_texture; -}; - -void c_close_client(Client* client) { - UnloadTexture(client->boid_texture); - CloseWindow(); - free(client); -} - -void c_close(Boids* env) { - free(env->boids); - free(env->boid_logs); - if (env->client != NULL) { - c_close_client(env->client); - } -} - -Client* make_client(Boids* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - - client->width = WIDTH; - client->height = HEIGHT; - - InitWindow(WIDTH, HEIGHT, "PufferLib Boids"); - SetTargetFPS(60); - - if (!IsWindowReady()) { - TraceLog(LOG_ERROR, "Window failed to initialize\n"); - free(client); - return NULL; - } - - client->boid_texture = LoadTexture(BOID_TEXTURE_PATH); - if (client->boid_texture.id == 0) { - TraceLog(LOG_ERROR, "Failed to load texture: %s", BOID_TEXTURE_PATH); - c_close_client(client); - return NULL; - } - - return client; -} - -void c_render(Boids* env) { - if (env->client == NULL) { - env->client = make_client(env); - if (env->client == NULL) { - TraceLog(LOG_ERROR, "Failed to initialize client for rendering\n"); - return; - } - } - - if (!WindowShouldClose() && IsWindowReady()) { - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - for (unsigned boid_indx = 0; boid_indx < env->num_boids; boid_indx++) { - DrawTexturePro( - env->client->boid_texture, - (Rectangle){ - (env->boids[boid_indx].velocity.x > 0) ? 0 : 128, - 0, - 128, - 128, - }, - (Rectangle){ - env->boids[boid_indx].x, - env->boids[boid_indx].y, - BOID_WIDTH, - BOID_HEIGHT - }, - (Vector2){0, 0}, - 0, - WHITE - ); - } - - EndDrawing(); - } else { - TraceLog(LOG_WARNING, "Window is not ready or should close"); - } -} diff --git a/pufferlib/ocean/boids/boids.py b/pufferlib/ocean/boids/boids.py deleted file mode 100644 index 329b36dea..000000000 --- a/pufferlib/ocean/boids/boids.py +++ /dev/null @@ -1,126 +0,0 @@ -''' -High-perf Boids -Inspired by https://people.ece.cornell.edu/land/courses/ece4760/labs/s2021/Boids/Boids.html -''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.boids import binding - -class Boids(pufferlib.PufferEnv): - def __init__( - self, - num_envs=1, - buf=None, - render_mode=None, - seed=0, - report_interval=1, - num_boids=1, - margin_turn_factor=1.0, - centering_factor=0.0, - avoid_factor=0.0, - matching_factor=0.0 - ): - ACTION_SPACE_SIZE = 2 - self.num_agents = num_envs * num_boids - self.num_boids = num_boids - - self.single_observation_space = gymnasium.spaces.Box( - -1000.0, 1000.0, shape=(num_boids*4,), dtype=np.float32 - ) - - #self.single_action_space = gymnasium.spaces.Box( - # -np.inf, np.inf, shape=(ACTION_SPACE_SIZE,), dtype=np.float32 - #) - self.single_action_space = gymnasium.spaces.MultiDiscrete([5, 5]) - - self.render_mode = render_mode - self.report_interval = report_interval - - super().__init__(buf) - self.actions = self.actions.astype(np.float32) - - # Create C binding with flattened action buffer - # We need to manually create a flattened action buffer to pass to C - #self.flat_actions = np.zeros((self.num_agents * ACTION_SPACE_SIZE), dtype=np.float32) - - c_envs = [] - for env_num in range(num_envs): - c_envs.append(binding.env_init( - self.observations[env_num*num_boids:(env_num+1)*num_boids], - #self.flat_actions[env_num*num_boids*ACTION_SPACE_SIZE:(env_num+1)*num_boids*ACTION_SPACE_SIZE], - self.actions[env_num*num_boids:(env_num+1)*num_boids], - self.rewards[env_num*num_boids:(env_num+1)*num_boids], - self.terminals[env_num*num_boids:(env_num+1)*num_boids], - self.truncations[env_num*num_boids:(env_num+1)*num_boids], - seed, - num_boids=num_boids, - report_interval=self.report_interval, - margin_turn_factor=margin_turn_factor, - centering_factor=centering_factor, - avoid_factor=avoid_factor, - matching_factor=matching_factor, - )) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - self.tick = 0 - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - # Clip actions to valid range - clipped_actions = (actions.astype(np.float32) - 2.0) / 4.0 - #clipped_actions = np.clip(actions, -1.0, 1.0) - - # Copy the clipped actions to our flat actions buffer for C binding - # Flatten from [num_agents, num_boids, 2] to a 1D array for C - # TODO: Check if I even need this? its not like I'm using the actions anywhere else - #self.flat_actions[:] = clipped_actions.reshape(-1) - - # Save the original actions for the experience buffer - # TODO: Same thing with this - self.actions[:] = clipped_actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.report_interval == 0: - log_data = binding.vec_log(self.c_envs) - if log_data: - info.append(log_data) - - # print(f"OBSERVATIONS: {self.observations}") - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = Boids(num_envs=1000) - env.reset() - tick = 0 - - # Generate random actions with proper shape: [cache_size, num_agents, action_dim] - actions = np.random.uniform(-3.0, 3.0, (atn_cache, env.num_agents, 2)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: {env.num_agents * tick / (time.time() - start)}') - - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/breakout/binding.c b/pufferlib/ocean/breakout/binding.c deleted file mode 100644 index e5fb3d02c..000000000 --- a/pufferlib/ocean/breakout/binding.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "breakout.h" - -#define Env Breakout -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->frameskip = unpack(kwargs, "frameskip"); - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->paddle_width = unpack(kwargs, "paddle_width"); - env->paddle_height = unpack(kwargs, "paddle_height"); - env->ball_width = unpack(kwargs, "ball_width"); - env->ball_height = unpack(kwargs, "ball_height"); - env->brick_width = unpack(kwargs, "brick_width"); - env->brick_height = unpack(kwargs, "brick_height"); - env->brick_rows = unpack(kwargs, "brick_rows"); - env->brick_cols = unpack(kwargs, "brick_cols"); - env->continuous = unpack(kwargs, "continuous"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/breakout/breakout.c b/pufferlib/ocean/breakout/breakout.c deleted file mode 100644 index e74f66a45..000000000 --- a/pufferlib/ocean/breakout/breakout.c +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include "breakout.h" -#include "puffernet.h" - -void demo() { - Weights* weights = load_weights("resources/breakout/breakout_weights.bin", 147844); - int logit_sizes[1] = {3}; - LinearLSTM* net = make_linearlstm(weights, 1, 118, logit_sizes, 1); - - Breakout env = { - .frameskip = 1, - .width = 576, - .height = 330, - .paddle_width = 62, - .paddle_height = 8, - .ball_width = 32, - .ball_height = 32, - .brick_width = 32, - .brick_height = 12, - .brick_rows = 6, - .brick_cols = 18, - .continuous = 0, - }; - allocate(&env); - - env.client = make_client(&env); - - c_reset(&env); - int frame = 0; - SetTargetFPS(60); - while (!WindowShouldClose()) { - // User can take control of the paddle - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if(env.continuous) { - float move = GetMouseWheelMove(); - float clamped_wheel = fmaxf(-1.0f, fminf(1.0f, move)); - env.actions[0] = clamped_wheel; - } else { - env.actions[0] = 0.0; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) env.actions[0] = 1; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) env.actions[0] = 2; - } - } else if (frame % 4 == 0) { - // Apply frameskip outside the env for smoother rendering - int* actions = (int*)env.actions; - forward_linearlstm(net, env.observations, actions); - env.actions[0] = actions[0]; - } - - frame = (frame + 1) % 4; - c_step(&env); - c_render(&env); - } - free_linearlstm(net); - free(weights); - free_allocated(&env); - close_client(env.client); -} - -void test_performance(int timeout) { - Breakout env = { - .width = 512, - .height = 512, - .paddle_width = 20, - .paddle_height = 70, - .ball_width = 10, - .ball_height = 15, - .brick_width = 10, - .brick_height = 10, - .brick_rows = 5, - .brick_cols = 10, - .continuous = 0, - }; - allocate(&env); - c_reset(&env); - - int start = time(NULL); - int num_steps = 0; - while (time(NULL) - start < timeout) { - env.actions[0] = rand() % 3; - c_step(&env); - num_steps++; - } - - int end = time(NULL); - float sps = num_steps / (end - start); - printf("Test Environment SPS: %f\n", sps); - free_allocated(&env); -} - -int main() { - demo(); - //test_performance(10); -} diff --git a/pufferlib/ocean/breakout/breakout.h b/pufferlib/ocean/breakout/breakout.h deleted file mode 100644 index c63f8e2ab..000000000 --- a/pufferlib/ocean/breakout/breakout.h +++ /dev/null @@ -1,557 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" - -#define NOOP 0 -#define LEFT 1 -#define RIGHT 2 -#define MAX_BALL_SPEED 448 -#define HALF_PADDLE_WIDTH 31 -#define Y_OFFSET 50 -#define TICK_RATE 1.0f/60.0f - -#define BRICK_INDEX_NO_COLLISION -4 -#define BRICK_INDEX_SIDEWALL_COLLISION -3 -#define BRICK_INDEX_BACKWALL_COLLISION -2 -#define BRICK_INDEX_PADDLE_COLLISION -1 - -typedef struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct Client { - float width; - float height; - float paddle_width; - float paddle_height; - float ball_width; - float ball_height; - Texture2D ball; -} Client; - -typedef struct Breakout { - Client* client; - Log log; - float* observations; - float* actions; - float* rewards; - unsigned char* terminals; - int score; - float paddle_x; - float paddle_y; - float ball_x; - float ball_y; - float ball_vx; - float ball_vy; - float* brick_x; - float* brick_y; - float* brick_states; - int balls_fired; - float paddle_width; - float paddle_height; - float ball_speed; - int hits; - int width; - int height; - int num_bricks; - int brick_rows; - int brick_cols; - int ball_width; - int ball_height; - int brick_width; - int brick_height; - int num_balls; - int max_score; - int half_max_score; - int tick; - int frameskip; - unsigned char hit_brick; - int continuous; -} Breakout; - -typedef struct CollisionInfo CollisionInfo; -struct CollisionInfo { - float t; - float overlap; - float x; - float y; - float vx; - float vy; - int brick_index; -}; - -void generate_brick_positions(Breakout* env) { - env->half_max_score=0; - for (int row = 0; row < env->brick_rows; row++) { - for (int col = 0; col < env->brick_cols; col++) { - int idx = row * env->brick_cols + col; - env->brick_x[idx] = col*env->brick_width; - env->brick_y[idx] = row*env->brick_height + Y_OFFSET; - env->half_max_score += 7 - 3 * (idx / env->brick_cols / 2); - } - } - env->max_score=2*env->half_max_score; -} - -void init(Breakout* env) { - env->tick = 0; - env->num_bricks = env->brick_rows * env->brick_cols; - assert(env->num_bricks > 0); - - env->brick_x = (float*)calloc(env->num_bricks, sizeof(float)); - env->brick_y = (float*)calloc(env->num_bricks, sizeof(float)); - env->brick_states = (float*)calloc(env->num_bricks, sizeof(float)); - env->num_balls = -1; - generate_brick_positions(env); -} - -void allocate(Breakout* env) { - init(env); - env->observations = (float*)calloc(11 + env->num_bricks, sizeof(float)); - env->actions = (float*)calloc(1, sizeof(float)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void c_close(Breakout* env) { - free(env->brick_x); - free(env->brick_y); - free(env->brick_states); -} - -void free_allocated(Breakout* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void add_log(Breakout* env) { - env->log.episode_length += env->tick; - env->log.episode_return += env->score; - env->log.score += env->score; - env->log.perf += env->score / (float)env->max_score; - env->log.n += 1; -} - -void compute_observations(Breakout* env) { - env->observations[0] = env->paddle_x / env->width; - env->observations[1] = env->paddle_y / env->height; - env->observations[2] = env->ball_x / env->width; - env->observations[3] = env->ball_y / env->height; - env->observations[4] = env->ball_vx / 512.0f; - env->observations[5] = env->ball_vy / 512.0f; - env->observations[6] = env->balls_fired / 5.0f; - env->observations[7] = env->score / 864.0f; - env->observations[8] = env->num_balls / 5.0f; - env->observations[9] = env->paddle_width / (2.0f * HALF_PADDLE_WIDTH); - for (int i = 0; i < env->num_bricks; i++) { - env->observations[10 + i] = env->brick_states[i]; - } -} - -// Collision of a stationary vertical line segment (xw,yw) to (xw,yw+hw) -// with a moving line segment (x+vx*t,y+vy*t) to (x+vx*t,y+vy*t+h). -static inline bool calc_vline_collision(float xw, float yw, float hw, float x, - float y, float vx, float vy, float h, CollisionInfo* col) { - float t_new = (xw - x) / vx; - float topmost = fmin(yw + hw, y + h + vy * t_new); - float botmost = fmax(yw, y + vy * t_new); - float overlap_new = topmost - botmost; - - // Collision finds the smallest time of collision with the greatest overlap - // between the ball and the wall. - if (overlap_new > 0.0f && t_new > 0.0f && t_new <= 1.0f && - (t_new < col->t || (t_new == col->t && overlap_new > col->overlap))) { - col->t = t_new; - col->overlap = overlap_new; - col->x = xw; - col->y = y + vy * t_new; - col->vx = -vx; - col->vy = vy; - return true; - } - return false; -} -static inline bool calc_hline_collision(float xw, float yw, float ww, - float x, float y, float vx, float vy, float w, CollisionInfo* col) { - float t_new = (yw - y) / vy; - float rightmost = fminf(xw + ww, x + w + vx * t_new); - float leftmost = fmaxf(xw, x + vx * t_new); - float overlap_new = rightmost - leftmost; - - // Collision finds the smallest time of collision with the greatest overlap between the ball and the wall. - if (overlap_new > 0.0f && t_new > 0.0f && t_new <= 1.0f && - (t_new < col->t || (t_new == col->t && overlap_new > col->overlap))) { - col->t = t_new; - col->overlap = overlap_new; - col->x = x + vx * t_new; - col->y = yw; - col->vx = vx; - col->vy = -vy; - return true; - } - return false; -} -static inline void calc_brick_collision(Breakout* env, int idx, - CollisionInfo* collision_info) { - bool collision = false; - // Brick left wall collides with ball right side - if (env->ball_vx > 0) { - if (calc_vline_collision(env->brick_x[idx], env->brick_y[idx], env->brick_height, - env->ball_x + env->ball_width, env->ball_y, env->ball_vx, env->ball_vy, env->ball_height, collision_info)) { - collision = true; - collision_info->x -= env->ball_width; - } - } - - // Brick right wall collides with ball left side - if (env->ball_vx < 0) { - if (calc_vline_collision(env->brick_x[idx] + env->brick_width, env->brick_y[idx], env->brick_height, - env->ball_x, env->ball_y, env->ball_vx, env->ball_vy, env->ball_height, collision_info)) { - collision = true; - } - } - - // Brick top wall collides with ball bottom side - if (env->ball_vy > 0) { - if (calc_hline_collision(env->brick_x[idx], env->brick_y[idx], env->brick_width, - env->ball_x, env->ball_y + env->ball_height, env->ball_vx, env->ball_vy, env->ball_width, collision_info)) { - collision = true; - collision_info->y -= env->ball_height; - } - } - - // Brick bottom wall collides with ball top side - if (env->ball_vy < 0) { - if (calc_hline_collision(env->brick_x[idx], env->brick_y[idx] + env->brick_height, env->brick_width, - env->ball_x, env->ball_y, env->ball_vx, env->ball_vy, env->ball_width, collision_info)) { - collision = true; - } - } - if (collision) { - collision_info->brick_index = idx; - } -} -static inline int column_index(Breakout* env, float x) { - return (int)(floorf(x / env->brick_width)); -} -static inline int row_index(Breakout* env, float y) { - return (int)(floorf((y - Y_OFFSET) / env->brick_height)); -} - -void calc_all_brick_collisions(Breakout* env, CollisionInfo* collision_info) { - int column_from = column_index(env, fminf(env->ball_x + env->ball_vx, env->ball_x)); - column_from = fmaxf(column_from, 0); - int column_to = column_index(env, fmaxf(env->ball_x + env->ball_width + env->ball_vx, env->ball_x + env->ball_width)); - column_to = fminf(column_to, env->brick_cols - 1); - int row_from = row_index(env, fminf(env->ball_y + env->ball_vy, env->ball_y)); - row_from = fmaxf(row_from, 0); - int row_to = row_index(env, fmaxf(env->ball_y + env->ball_height + env->ball_vy, env->ball_y + env->ball_height)); - row_to = fminf(row_to, env->brick_rows - 1); - - for (int row = row_from; row <= row_to; row++) { - for (int column = column_from; column <= column_to; column++) { - int brick_index = row * env->brick_cols + column; - if (env->brick_states[brick_index] == 0.0) - calc_brick_collision(env, brick_index, collision_info); - } - } -} - -bool calc_paddle_ball_collisions(Breakout* env, CollisionInfo* collision_info) { - float base_angle = M_PI / 4.0f; - - // Check if ball is above the paddle - if (env->ball_y + env->ball_height + env->ball_vy < env->paddle_y) { - return false; - } - - // Check for collision - // If we've found another collision (eg the ball hits the wall before the paddle) - // this correctly skips the paddle collision. - if (!calc_hline_collision(env->paddle_x, env->paddle_y, env->paddle_width, - env->ball_x, env->ball_y + env->ball_height, env->ball_vx, env->ball_vy, env->ball_width, - collision_info) || collision_info->t > 1.0f) { - return false; - } - - collision_info->y -= env->ball_height; - collision_info->brick_index = BRICK_INDEX_PADDLE_COLLISION; - - env->hit_brick = false; - float relative_intersection = ((env->ball_x + - env->ball_width / 2) - - env->paddle_x) / - env->paddle_width; - float angle = -base_angle + relative_intersection * 2 * base_angle; - env->ball_vx = sin(angle) * env->ball_speed * TICK_RATE; - env->ball_vy = -cos(angle) * env->ball_speed * TICK_RATE; - env->hits += 1; - if (env->hits % 4 == 0 && env->ball_speed < MAX_BALL_SPEED) { - env->ball_speed += 64; - } - if (env->score == env->half_max_score) { - for (int i = 0; i < env->num_bricks; i++) { - env->brick_states[i] = 0.0; - } - } - return true; -} - -void calc_all_wall_collisions(Breakout* env, CollisionInfo* collision_info) { - if (env->ball_vx < 0) { - if (calc_vline_collision(0, 0, env->height, - env->ball_x, env->ball_y, env->ball_vx, env->ball_vy, env->ball_height, - collision_info)) { - collision_info->brick_index = BRICK_INDEX_SIDEWALL_COLLISION; - } - } - if (env->ball_vx > 0) { - if (calc_vline_collision(env->width, 0, env->height, - env->ball_x + env->ball_width, env->ball_y, env->ball_vx, env->ball_vy, env->ball_height, - collision_info)) { - collision_info->x -= env->ball_width; - collision_info->brick_index = BRICK_INDEX_SIDEWALL_COLLISION; - } - } - if (env->ball_vy < 0) { - if (calc_hline_collision(0, 0, env->width, - env->ball_x, env->ball_y, env->ball_vx, env->ball_vy, env->ball_width, - collision_info)) { - collision_info->brick_index = BRICK_INDEX_BACKWALL_COLLISION; - } - } -} - -// With rare floating point conditions, the ball could escape the bounds. -// Let's handle that explicitly. -void check_wall_bounds(Breakout* env) { - if (env->ball_x < 0) - env->ball_x += MAX_BALL_SPEED * 1.1f * TICK_RATE; - if (env->ball_x > env->width) - env->ball_x -= MAX_BALL_SPEED * 1.1f * TICK_RATE; - if (env->ball_y < 0) - env->ball_y += MAX_BALL_SPEED * 1.1f * TICK_RATE; -} - -void destroy_brick(Breakout* env, int brick_idx) { - float gained_points = 7 - 3 * ((brick_idx / env->brick_cols) / 2); - - env->score += gained_points; - env->brick_states[brick_idx] = 1.0; - - env->rewards[0] += gained_points; - - if (brick_idx / env->brick_cols < 3) { - env->ball_speed = MAX_BALL_SPEED; - } -} - -bool handle_collisions(Breakout* env) { - CollisionInfo collision_info = { - .t = 2.0f, - .overlap = -1.0f, - .x = 0.0f, - .y = 0.0f, - .vx = 0.0f, - .vy = 0.0f, - .brick_index = BRICK_INDEX_NO_COLLISION, - }; - - check_wall_bounds(env); - - calc_all_brick_collisions(env, &collision_info); - calc_all_wall_collisions(env, &collision_info); - calc_paddle_ball_collisions(env, &collision_info); - if (collision_info.brick_index != BRICK_INDEX_PADDLE_COLLISION - && collision_info.t <= 1.0f) { - env->ball_x = collision_info.x; - env->ball_y = collision_info.y; - env->ball_vx = collision_info.vx; - env->ball_vy = collision_info.vy; - if (collision_info.brick_index >= 0) { - destroy_brick(env, collision_info.brick_index); - } - if (collision_info.brick_index == BRICK_INDEX_BACKWALL_COLLISION) { - env->paddle_width = HALF_PADDLE_WIDTH; - } - } - return collision_info.brick_index != BRICK_INDEX_NO_COLLISION; -} - -void reset_round(Breakout* env) { - env->balls_fired = 0; - env->hit_brick = false; - env->hits = 0; - env->ball_speed = 256; - env->paddle_width = 2 * HALF_PADDLE_WIDTH; - - env->paddle_x = env->width / 2.0 - env->paddle_width / 2; - env->paddle_y = env->height - env->paddle_height - 10; - - env->ball_x = env->paddle_x + (env->paddle_width / 2 - env->ball_width / 2); - env->ball_y = env->height / 2 - 30; - - env->ball_vx = 0.0; - env->ball_vy = 0.0; -} - -void c_reset(Breakout* env) { - env->score = 0; - env->num_balls = 5; - for (int i = 0; i < env->num_bricks; i++) { - env->brick_states[i] = 0.0; - } - reset_round(env); - env->tick = 0; - compute_observations(env); -} - -void step_frame(Breakout* env, float action) { - float act = 0.0; - if (env->balls_fired == 0) { - env->balls_fired = 1; - float direction = M_PI / 3.25f; - - env->ball_vy = cos(direction) * env->ball_speed * TICK_RATE; - env->ball_vx = sin(direction) * env->ball_speed * TICK_RATE; - if (rand() % 2 == 0) { - env->ball_vx = -env->ball_vx; - } - } - else if (action == LEFT) { - act = -1.0; - } else if (action == RIGHT) { - act = 1.0; - } - if (env->continuous){ - act = action; - } - env->paddle_x += act * 620 * TICK_RATE; - if (env->paddle_x <= 0){ - env->paddle_x = fmaxf(0, env->paddle_x); - } else { - env->paddle_x = fminf(env->width - env->paddle_width, env->paddle_x); - } - - //Handle collisions. - //Regular timestepping is done only if there are no collisions. - if(!handle_collisions(env)){ - env->ball_x += env->ball_vx; - env->ball_y += env->ball_vy; - } - - if (env->ball_y >= env->paddle_y + env->paddle_height) { - env->num_balls -= 1; - reset_round(env); - } - if (env->num_balls < 0 || env->score == env->max_score) { - env->terminals[0] = 1; - add_log(env); - c_reset(env); - } -} - -void c_step(Breakout* env) { - env->terminals[0] = 0; - env->rewards[0] = 0.0; - - float action = env->actions[0]; - for (int i = 0; i < env->frameskip; i++) { - env->tick += 1; - step_frame(env, action); - } - - compute_observations(env); -} - -Color BRICK_COLORS[6] = {RED, ORANGE, YELLOW, GREEN, SKYBLUE, BLUE}; - -Client* make_client(Breakout* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = env->width; - client->height = env->height; - client->paddle_width = env->paddle_width; - client->paddle_height = env->paddle_height; - client->ball_width = env->ball_width; - client->ball_height = env->ball_height; - - InitWindow(env->width, env->height, "PufferLib Breakout"); - SetTargetFPS(60 / env->frameskip); - - client->ball = LoadTexture("resources/shared/puffers_128.png"); - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -void c_render(Breakout* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - - Client* client = env->client; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - if (IsKeyPressed(KEY_TAB)) { - ToggleFullscreen(); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - DrawRectangle(env->paddle_x, env->paddle_y, - env->paddle_width, env->paddle_height, (Color){0, 255, 255, 255}); - - // Draw ball - DrawTexturePro( - client->ball, - (Rectangle){ - (env->ball_vx > 0) ? 0 : 128, - 0, 128, 128, - }, - (Rectangle){ - env->ball_x, - env->ball_y, - env->ball_width, - env->ball_height - }, - (Vector2){0, 0}, - 0, - WHITE - ); - - for (int row = 0; row < env->brick_rows; row++) { - for (int col = 0; col < env->brick_cols; col++) { - int brick_idx = row * env->brick_cols + col; - if (env->brick_states[brick_idx] == 1) { - continue; - } - int x = env->brick_x[brick_idx]; - int y = env->brick_y[brick_idx]; - Color brick_color = BRICK_COLORS[row]; - DrawRectangle(x, y, env->brick_width, env->brick_height, brick_color); - } - } - - DrawText(TextFormat("Score: %i", env->score), 10, 10, 20, WHITE); - DrawText(TextFormat("Balls: %i", env->num_balls), client->width - 80, 10, 20, WHITE); - EndDrawing(); - - //PlaySound(client->sound); -} diff --git a/pufferlib/ocean/breakout/breakout.py b/pufferlib/ocean/breakout/breakout.py deleted file mode 100644 index f482701a8..000000000 --- a/pufferlib/ocean/breakout/breakout.py +++ /dev/null @@ -1,93 +0,0 @@ -'''High-perf Pong - -Inspired from https://gist.github.com/Yttrmin/18ecc3d2d68b407b4be1 -& https://jair.org/index.php/jair/article/view/10819/25823 -& https://www.youtube.com/watch?v=PSQt5KGv7Vk -''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.breakout import binding - -class Breakout(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, - frameskip=4, width=576, height=330, - paddle_width=62, paddle_height=8, - ball_width=32, ball_height=32, - brick_width=32, brick_height=12, - brick_rows=6, brick_cols=18, continuous=False, log_interval=128, - buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(10 + brick_rows*brick_cols,), dtype=np.float32) - self.render_mode = render_mode - self.num_agents = num_envs - self.continuous = continuous - self.log_interval = log_interval - self.tick = 0 - - if continuous: - self.single_action_space = gymnasium.spaces.Box(low=-1, high=1, - shape=(1,), dtype=np.float32) - else: - self.single_action_space = gymnasium.spaces.Discrete(3) - - super().__init__(buf) - if continuous: - self.actions = self.actions.flatten() - else: - self.actions = self.actions.astype(np.float32) - - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, frameskip=frameskip, width=width, height=height, - paddle_width=paddle_width, paddle_height=paddle_height, ball_width=ball_width, ball_height=ball_height, - brick_width=brick_width, brick_height=brick_height, brick_rows=brick_rows, - brick_cols=brick_cols, continuous=continuous - ) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - if self.continuous: - self.actions[:] = np.clip(actions.flatten(), -1.0, 1.0) - else: - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = Breakout(num_envs=1000) - env.reset() - tick = 0 - - actions = np.random.randint(0, 2, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: %f', env.num_agents * tick / (time.time() - start)) - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/cartpole/binding.c b/pufferlib/ocean/cartpole/binding.c deleted file mode 100644 index 8bda2cfd1..000000000 --- a/pufferlib/ocean/cartpole/binding.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "cartpole.h" -#define Env Cartpole -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->continuous = unpack(kwargs, "continuous"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "x_threshold_termination", log->x_threshold_termination); - assign_to_dict(dict, "pole_angle_termination", log->pole_angle_termination); - assign_to_dict(dict, "max_steps_termination", log->max_steps_termination); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/cartpole/cartpole.c b/pufferlib/ocean/cartpole/cartpole.c deleted file mode 100644 index 20e834b37..000000000 --- a/pufferlib/ocean/cartpole/cartpole.c +++ /dev/null @@ -1,72 +0,0 @@ -// local compile/eval implemented for discrete actions only -// eval with python demo.py --mode eval --env puffer_cartpole --eval-mode-path - -#include -#include -#include -#include -#include "cartpole.h" -#include "puffernet.h" - -#define NUM_WEIGHTS 133123 -#define OBSERVATIONS_SIZE 4 -#define ACTIONS_SIZE 2 -#define CONTINUOUS 0 - -const char* WEIGHTS_PATH = "resources/cartpole/cartpole_weights.bin"; - -float movement(int discrete_action, int userControlMode) { - if (userControlMode) { - return (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) ? 1.0f : -1.0f; - } else { - return (discrete_action == 1) ? 1.0f : -1.0f; - } -} - -void demo() { - Weights* weights = load_weights(WEIGHTS_PATH, NUM_WEIGHTS); - LinearLSTM* net; - - int logit_sizes[1] = {ACTIONS_SIZE}; - net = make_linearlstm(weights, 1, OBSERVATIONS_SIZE, logit_sizes, 1); - Cartpole env = {0}; - env.continuous = CONTINUOUS; - allocate(&env); - c_reset(&env); - c_render(&env); - - SetTargetFPS(60); - - while (!WindowShouldClose()) { - int userControlMode = IsKeyDown(KEY_LEFT_SHIFT); - - if (!userControlMode) { - int action_value; - forward_linearlstm(net, env.observations, &action_value); - env.actions[0] = movement(action_value, 0); - } else { - env.actions[0] = movement(env.actions[0], userControlMode); - } - - c_step(&env); - - BeginDrawing(); - ClearBackground(RAYWHITE); - c_render(&env); - EndDrawing(); - - if (env.terminals[0]) { - c_reset(&env); - } - } - - free_linearlstm(net); - free(weights); - free_allocated(&env); -} - -int main() { - srand(time(NULL)); - demo(); - return 0; -} diff --git a/pufferlib/ocean/cartpole/cartpole.h b/pufferlib/ocean/cartpole/cartpole.h deleted file mode 100644 index d3d00d581..000000000 --- a/pufferlib/ocean/cartpole/cartpole.h +++ /dev/null @@ -1,212 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" - -#define GRAVITY 9.8f -#define MASSCART 1.0f -#define MASSPOLE 0.1f -#define TOTAL_MASS (MASSPOLE + MASSCART) -#define LENGTH 0.5f // half pole length -#define POLEMASS_LENGTH (MASSPOLE * LENGTH) -#define FORCE_MAG 10.0f -#define TAU 0.02f // timestep duration - -#define X_THRESHOLD 2.4f -#define THETA_THRESHOLD_RADIANS (12 * 2 * M_PI / 360) -#define MAX_STEPS 200 -#define WIDTH 600 -#define HEIGHT 200 -#define SCALE 100 - -typedef struct Log Log; -struct Log { - float perf; - float episode_length; - float x_threshold_termination; - float pole_angle_termination; - float max_steps_termination; - float n; - float score; -}; - -typedef struct Client Client; -struct Client { -}; - -typedef struct Cartpole Cartpole; -struct Cartpole { - float* observations; - float* actions; - float* rewards; - unsigned char* terminals; - unsigned char* truncations; - Log log; - Client* client; - float x; - float x_dot; - float theta; - float theta_dot; - int tick; - int continuous; - float episode_return; -}; - -void add_log(Cartpole* env) { - if (env->episode_return > 0) { - env->log.perf = env->episode_return / MAX_STEPS; - } else { - env->log.perf = 0.0f; - } - env->log.episode_length += env->tick; - env->log.score += env->tick; - env->log.x_threshold_termination += (env->x < -X_THRESHOLD || env->x > X_THRESHOLD); - env->log.pole_angle_termination += (env->theta < -THETA_THRESHOLD_RADIANS || env->theta > THETA_THRESHOLD_RADIANS); - env->log.max_steps_termination += (env->tick >= MAX_STEPS); - env->log.n += 1; -} - -void init(Cartpole* env) { - env->tick = 0; - memset(&env->log, 0, sizeof(Log)); -} - -void allocate(Cartpole* env) { - init(env); - env->observations = (float*)calloc(4, sizeof(float)); - env->actions = (float*)calloc(1, sizeof(float)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void free_allocated(Cartpole* env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); -} - -void c_close(Cartpole* env) { -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -Client* make_client(Cartpole* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - InitWindow(WIDTH, HEIGHT, "puffer Cartpole"); - SetTargetFPS(60); - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -void c_render(Cartpole* env) { - if (IsKeyDown(KEY_ESCAPE)) - exit(0); - if (IsKeyPressed(KEY_TAB)) - ToggleFullscreen(); - - if (env->client == NULL) { - env->client = make_client(env); - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - DrawLine(0, HEIGHT / 1.5, WIDTH, HEIGHT / 1.5, PUFF_CYAN); - float cart_x = WIDTH / 2 + env->x * SCALE; - float cart_y = HEIGHT / 1.6; - DrawRectangle((int)(cart_x - 20), (int)(cart_y - 10), 40, 20, PUFF_CYAN); - float pole_length = 2.0f * 0.5f * SCALE; - float pole_x2 = cart_x + sinf(env->theta) * pole_length; - float pole_y2 = cart_y - cosf(env->theta) * pole_length; - DrawLineEx((Vector2){cart_x, cart_y}, (Vector2){pole_x2, pole_y2}, 5, PUFF_RED); - DrawText(TextFormat("Steps: %i", env->tick), 10, 10, 20, PUFF_WHITE); - DrawText(TextFormat("Cart Position: %.2f", env->x), 10, 40, 20, PUFF_WHITE); - DrawText(TextFormat("Pole Angle: %.2f", env->theta * 180.0f / M_PI), 10, 70, 20, PUFF_WHITE); - EndDrawing(); -} - -void compute_observations(Cartpole* env) { - env->observations[0] = env->x; - env->observations[1] = env->x_dot; - env->observations[2] = env->theta; - env->observations[3] = env->theta_dot; -} - -void c_reset(Cartpole* env) { - env->episode_return = 0.0f; - env->x = ((float)rand() / (float)RAND_MAX) * 0.08f - 0.04f; - env->x_dot = ((float)rand() / (float)RAND_MAX) * 0.08f - 0.04f; - env->theta = ((float)rand() / (float)RAND_MAX) * 0.08f - 0.04f; - env->theta_dot = ((float)rand() / (float)RAND_MAX) * 0.08f - 0.04f; - env->tick = 0; - - compute_observations(env); -} - -void c_step(Cartpole* env) { - // float force = 0.0; - // if (env->continuous) { - // force = env->actions[0] * FORCE_MAG; - // } else { - // force = (env->actions[0] > 0.5f) ? FORCE_MAG : -FORCE_MAG; - // } - - float a = env->actions[0]; - - /* ===== runtime sanity check –– delete after debugging ===== */ - if (!isfinite(a) || a < -1.0001f || a > 1.0001f) { - fprintf(stderr, - "[BAD ACTION] tick=%d raw=%.6f\n", - env->tick, a); - fflush(stderr); - } - /* ========================================================== */ - - if (!isfinite(a)) a = 0.0f; - a = fminf(fmaxf(a, -1.0f), 1.0f); - env->actions[0] = a; - - float force = env->continuous ? a * FORCE_MAG - : (a > 0.5f ? FORCE_MAG : -FORCE_MAG); - - float costheta = cosf(env->theta); - float sintheta = sinf(env->theta); - - float temp = (force + POLEMASS_LENGTH * env->theta_dot * env->theta_dot * sintheta) / TOTAL_MASS; - float thetaacc = (GRAVITY * sintheta - costheta * temp) / - (LENGTH * (4.0f / 3.0f - MASSPOLE * costheta * costheta / TOTAL_MASS)); - float xacc = temp - POLEMASS_LENGTH * thetaacc * costheta / TOTAL_MASS; - - env->x += TAU * env->x_dot; - env->x_dot += TAU * xacc; - env->theta += TAU * env->theta_dot; - env->theta_dot += TAU * thetaacc; - - env->tick += 1; - - bool terminated = env->x < -X_THRESHOLD || env->x > X_THRESHOLD || - env->theta < -THETA_THRESHOLD_RADIANS || env->theta > THETA_THRESHOLD_RADIANS; - bool truncated = env->tick >= MAX_STEPS; - bool done = terminated || truncated; - - env->rewards[0] = done ? 0.0f : 1.0f; - env->episode_return += env->rewards[0]; - env->terminals[0] = terminated ? 1 : 0; - - if (done) { - add_log(env); - c_reset(env); - } - - compute_observations(env); -} diff --git a/pufferlib/ocean/cartpole/cartpole.py b/pufferlib/ocean/cartpole/cartpole.py deleted file mode 100644 index 735a669a6..000000000 --- a/pufferlib/ocean/cartpole/cartpole.py +++ /dev/null @@ -1,99 +0,0 @@ -import numpy as np -import gymnasium -import pufferlib -from pufferlib.ocean.cartpole import binding - -class Cartpole(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode='human', report_interval=1, continuous=False, buf=None, seed=0): - self.render_mode = render_mode - self.num_agents = num_envs - self.report_interval = report_interval - self.tick = 0 - self.continuous = continuous - self.human_action = None - - self.num_obs = 4 - self.single_observation_space = gymnasium.spaces.Box( - low=-np.inf, high=np.inf, shape=(self.num_obs,), dtype=np.float32 - ) - if self.continuous: - self.single_action_space = gymnasium.spaces.Box( - low=-1.0, high=1.0, shape=(1,) - ) - - else: - self.single_action_space = gymnasium.spaces.Discrete(2) - - super().__init__(buf) - self.actions = np.zeros(num_envs, dtype=np.float32) - - self.c_envs = binding.vec_init( - self.observations, - self.actions, - self.rewards, - self.terminals, - self.truncations, - num_envs, - seed, - continuous=int(self.continuous), - ) - - def reset(self, seed=None): - self.tick = 0 - if seed is None: - binding.vec_reset(self.c_envs, 0) - else: - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - if self.continuous: - self.actions[:] = np.clip(actions.flatten(), -1.0, 1.0) - else: - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.report_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return ( - self.observations, - self.rewards, - self.terminals, - self.truncations, - info - ) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=8192, continuous=True): - """Benchmark environment performance.""" - num_envs = 4096 - env = Cartpole(num_envs=num_envs, continuous=continuous) - env.reset() - tick = 0 - - if env.continuous: - actions = np.random.uniform(-1, 1, (atn_cache, num_envs, 1)).astype(np.float32) - else: - actions = np.random.randint(0, env.single_action_space.n, (atn_cache, num_envs)).astype(np.int8) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - sps = num_envs * tick / (time.time() - start) - print(f'SPS: {sps:,}') - -if __name__ == '__main__': - test_performance() - diff --git a/pufferlib/ocean/checkers/binding.c b/pufferlib/ocean/checkers/binding.c deleted file mode 100644 index b53f1dc6b..000000000 --- a/pufferlib/ocean/checkers/binding.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "checkers.h" - -#define Env Checkers -#include "../env_binding.h" - -static int my_init(Env *env, PyObject *args, PyObject *kwargs) { - env->size = unpack(kwargs, "size"); - return 0; -} - -static int my_log(PyObject *dict, Log *log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "winrate", log->winrate); - return 0; -} diff --git a/pufferlib/ocean/checkers/checkers.c b/pufferlib/ocean/checkers/checkers.c deleted file mode 100644 index c232d56a8..000000000 --- a/pufferlib/ocean/checkers/checkers.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "checkers.h" - -int main() { - Checkers env = {.size = 8}; - env.observations = - (unsigned char *)calloc(env.size * env.size, sizeof(unsigned char)); - env.actions = (int *)calloc(1, sizeof(int)); - env.rewards = (float *)calloc(1, sizeof(float)); - env.terminals = (unsigned char *)calloc(1, sizeof(unsigned char)); - - c_reset(&env); - c_render(&env); - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env.actions[0] = 0; - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) - env.actions[0] = 1; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) - env.actions[0] = 2; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) - env.actions[0] = 3; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) - env.actions[0] = 4; - } else { - env.actions[0] = rand() % 5; - } - c_step(&env); - c_render(&env); - } - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} diff --git a/pufferlib/ocean/checkers/checkers.h b/pufferlib/ocean/checkers/checkers.h deleted file mode 100644 index 8976ecf2c..000000000 --- a/pufferlib/ocean/checkers/checkers.h +++ /dev/null @@ -1,825 +0,0 @@ -#pragma once - -#include "raylib.h" -#include -#include -#include -#include -#include - -#define EMPTY 0 -#define AGENT 1 -#define OPPONENT 3 -#define AGENT_PAWN 1 -#define AGENT_KING 2 -#define OPPONENT_PAWN 3 -#define OPPONENT_KING 4 - -// Required struct. Only use floats! -typedef struct { - float perf; - float score; - float episode_return; - float episode_length; - float winrate; - float n; -} Log; - -// Required that you have some struct for your env -// Recommended that you name it the same as the env file -typedef struct { - Log log; - unsigned char *observations; - int *actions; - float *rewards; - unsigned char *terminals; - int size; - int tick; - int current_player; - int agent_pieces; - int opponent_pieces; - int capture_available_cache; - int capture_available_valid; - int game_over_cache; - int game_over_valid; -} Checkers; - -typedef struct { - int r; - int c; -} Position; - -typedef struct { - Position from; - Position to; -} Move; - -float clamp(float val, float low, float high) { - return fmin(fmax(val, low), high); -} - -Move decode_action(Checkers *env, int action) { - int num_move_types = 8; - int pos = action / num_move_types; - int move_type = action % num_move_types; - - Move m; - m.from.r = pos / env->size; - m.from.c = pos % env->size; - m.to.r = m.from.r; - m.to.c = m.from.c; - - switch (move_type) { - case 0: - m.to.r = m.from.r - 1; - m.to.c = m.from.c - 1; - break; - case 1: - m.to.r = m.from.r - 1; - m.to.c = m.from.c + 1; - break; - case 2: - m.to.r = m.from.r + 1; - m.to.c = m.from.c - 1; - break; - case 3: - m.to.r = m.from.r + 1; - m.to.c = m.from.c + 1; - break; - case 4: - m.to.r = m.from.r - 2; - m.to.c = m.from.c - 2; - break; - case 5: - m.to.r = m.from.r - 2; - m.to.c = m.from.c + 2; - break; - case 6: - m.to.r = m.from.r + 2; - m.to.c = m.from.c - 2; - break; - case 7: - m.to.r = m.from.r + 2; - m.to.c = m.from.c + 2; - break; - } - - return m; -} - -int p2i(Checkers *env, Position p) { return p.r * env->size + p.c; } - -int check_in_bounds(Checkers *env, Position p) { - return 0 <= p.r && p.r < env->size && 0 <= p.c && p.c < env->size; -} - -int get_piece(Checkers *env, Position p) { - if (!check_in_bounds(env, p)) { - return EMPTY; - } - return env->observations[p2i(env, p)]; -} - -int get_piece_type(Checkers *env, Position p) { - int piece = get_piece(env, p); - if (piece == AGENT_PAWN || piece == AGENT_KING) - return AGENT; - if (piece == OPPONENT_PAWN || piece == OPPONENT_KING) - return OPPONENT; - return EMPTY; -} - -int get_move_direction(Checkers *env, Move m) { - return m.to.r > m.from.r ? 1 : -1; -} - -int valid_move_direction(Checkers *env, Move m) { - int piece = get_piece(env, m.from); - if (piece == AGENT_PAWN) - return get_move_direction(env, m) == 1 ? 1 : 0; - if (piece == OPPONENT_PAWN) - return get_move_direction(env, m) == -1 ? 1 : 0; - return 1; -} - -int is_diagonal_move(Move m) { - int dr = m.to.r - m.from.r; - int dc = m.to.c - m.from.c; - return (dr == dc) || (dr == -dc); -} -int move_size(Move m) { return abs(m.from.r - m.to.r); } - -int is_valid_move_no_capture(Checkers *env, Move m) { - if (!check_in_bounds(env, m.from) || !check_in_bounds(env, m.to)) - return 0; - - if (get_piece_type(env, m.from) != env->current_player) - return 0; - - if (get_piece(env, m.to) != EMPTY) - return 0; - - if (!valid_move_direction(env, m)) - return 0; - - if (!is_diagonal_move(m)) - return 0; - - if (move_size(m) != 1 && move_size(m) != 2) - return 0; - - if (move_size(m) == 2) { - int other_player = env->current_player == AGENT ? OPPONENT : AGENT; - Position between_pos = - (Position){(m.from.r + m.to.r) / 2, (m.from.c + m.to.c) / 2}; - if (get_piece_type(env, between_pos) != other_player) - return 0; - } - - return 1; -} - -int capture_available(Checkers *env) { - if (env->capture_available_valid) { - return env->capture_available_cache; - } - - int current_pawn = env->current_player == AGENT ? AGENT_PAWN : OPPONENT_PAWN; - int current_king = env->current_player == AGENT ? AGENT_KING : OPPONENT_KING; - - for (int i = 0; i < env->size * env->size; i++) { - int piece = env->observations[i]; - if (piece != current_pawn && piece != current_king) - continue; - - int r = i / env->size; - int c = i % env->size; - - int directions[4][2] = {{-2, -2}, {-2, 2}, {2, -2}, {2, 2}}; - for (int d = 0; d < 4; d++) { - int new_r = r + directions[d][0]; - int new_c = c + directions[d][1]; - - if (new_r < 0 || new_r >= env->size || new_c < 0 || new_c >= env->size) - continue; - - if (env->observations[new_r * env->size + new_c] != EMPTY) - continue; - - int mid_r = r + directions[d][0] / 2; - int mid_c = c + directions[d][1] / 2; - int mid_piece = env->observations[mid_r * env->size + mid_c]; - - int opponent_pawn = - env->current_player == AGENT ? OPPONENT_PAWN : AGENT_PAWN; - int opponent_king = - env->current_player == AGENT ? OPPONENT_KING : AGENT_KING; - - if (mid_piece == opponent_pawn || mid_piece == opponent_king) { - int move_dir = directions[d][0] > 0 ? 1 : -1; - int valid_dir = env->current_player == AGENT ? 1 : -1; - if (move_dir != valid_dir) - continue; - - env->capture_available_cache = 1; - env->capture_available_valid = 1; - return 1; - } - } - } - - env->capture_available_cache = 0; - env->capture_available_valid = 1; - return 0; -} - -int is_valid_move(Checkers *env, Move m) { - if (capture_available(env) && move_size(m) != 2) - return 0; - return is_valid_move_no_capture(env, m); -} - -int num_legal_moves(Checkers *env) { - int res = 0; - int current_pawn = env->current_player == AGENT ? AGENT_PAWN : OPPONENT_PAWN; - int current_king = env->current_player == AGENT ? AGENT_KING : OPPONENT_KING; - int has_captures = capture_available(env); - - for (int i = 0; i < env->size * env->size; i++) { - int piece = env->observations[i]; - if (piece != current_pawn && piece != current_king) - continue; - - int r = i / env->size; - int c = i % env->size; - - int directions[8][2] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}, - {-2, -2}, {-2, 2}, {2, -2}, {2, 2}}; - - for (int d = 0; d < 8; d++) { - int new_r = r + directions[d][0]; - int new_c = c + directions[d][1]; - - if (new_r < 0 || new_r >= env->size || new_c < 0 || new_c >= env->size) - continue; - - if (env->observations[new_r * env->size + new_c] != EMPTY) - continue; - - int move_size = abs(directions[d][0]); - - if (has_captures && move_size != 2) - continue; - - if (piece == current_pawn) { - int move_dir = directions[d][0] > 0 ? 1 : -1; - int valid_dir = env->current_player == AGENT ? 1 : -1; - if (move_dir != valid_dir) - continue; - } - - if (move_size == 2) { - int mid_r = r + directions[d][0] / 2; - int mid_c = c + directions[d][1] / 2; - int mid_piece = env->observations[mid_r * env->size + mid_c]; - - int opponent_pawn = - env->current_player == AGENT ? OPPONENT_PAWN : AGENT_PAWN; - int opponent_king = - env->current_player == AGENT ? OPPONENT_KING : AGENT_KING; - - if (mid_piece != opponent_pawn && mid_piece != opponent_king) - continue; - } - - res++; - } - } - - return res; -} - -int num_pieces_by_player(Checkers *env, int player) { - if (player == AGENT) { - return env->agent_pieces; - } else { - return env->opponent_pieces; - } -} - -int try_make_king(Checkers *env) { - int promoted = 0; - - for (int i = 0; i < env->size; i++) { - if (env->observations[i] == OPPONENT_PAWN) { - env->observations[i] = OPPONENT_KING; - promoted = 1; - } - } - for (int i = 0; i < env->size; i++) { - if (env->observations[env->size * (env->size - 1) + i] == AGENT_PAWN) { - env->observations[env->size * (env->size - 1) + i] = AGENT_KING; - promoted = 1; - } - } - - if (promoted) { - env->capture_available_valid = 0; - env->game_over_valid = 0; - } - - return promoted; -} - -int is_game_over(Checkers *env) { - if (env->game_over_valid) { - return env->game_over_cache; - } - - int current_player_pieces = num_pieces_by_player(env, env->current_player); - int other_player = env->current_player == AGENT ? OPPONENT : AGENT; - int other_player_pieces = num_pieces_by_player(env, other_player); - - if (current_player_pieces == 0 || other_player_pieces == 0) { - env->game_over_cache = 1; - env->game_over_valid = 1; - return 1; - } - - int has_captures = capture_available(env); - if (has_captures) { - env->game_over_cache = 0; - env->game_over_valid = 1; - return 0; - } - - int current_pawn = env->current_player == AGENT ? AGENT_PAWN : OPPONENT_PAWN; - int current_king = env->current_player == AGENT ? AGENT_KING : OPPONENT_KING; - - for (int i = 0; i < env->size * env->size; i++) { - int piece = env->observations[i]; - if (piece != current_pawn && piece != current_king) - continue; - - int r = i / env->size; - int c = i % env->size; - - int directions[4][2] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}}; - for (int d = 0; d < 4; d++) { - int new_r = r + directions[d][0]; - int new_c = c + directions[d][1]; - - if (new_r < 0 || new_r >= env->size || new_c < 0 || new_c >= env->size) - continue; - if (env->observations[new_r * env->size + new_c] != EMPTY) - continue; - - if (piece == current_pawn) { - int move_dir = directions[d][0] > 0 ? 1 : -1; - int valid_dir = env->current_player == AGENT ? 1 : -1; - if (move_dir != valid_dir) - continue; - } - - env->game_over_cache = 0; - env->game_over_valid = 1; - return 0; - } - } - - env->game_over_cache = 1; - env->game_over_valid = 1; - return 1; -} - -int get_winner(Checkers *env) { - if (env->agent_pieces == 0) { - return OPPONENT; - } - - if (env->opponent_pieces == 0) { - return AGENT; - } - - if (is_game_over(env)) { - return env->current_player == AGENT ? OPPONENT : AGENT; - } - - return EMPTY; -} - -void make_move(Checkers *env, int action) { - Move m = decode_action(env, action); - if (!is_valid_move(env, m)) { - env->rewards[0] = -1.0f; // reward for invalid move - return; - } - - int moving_piece = get_piece(env, m.from); - env->observations[p2i(env, m.from)] = EMPTY; - env->observations[p2i(env, m.to)] = moving_piece; - - int capture_occurred = 0; - float reward = 0.0f; - - if (move_size(m) == 2) { - Position between_pos = - (Position){(m.from.r + m.to.r) / 2, (m.from.c + m.to.c) / 2}; - int captured_piece = env->observations[p2i(env, between_pos)]; - env->observations[p2i(env, between_pos)] = EMPTY; - capture_occurred = 1; - - if (captured_piece == AGENT_PAWN || captured_piece == AGENT_KING) { - env->agent_pieces--; - reward -= 0.05f; // reward for losing pieces - } else if (captured_piece == OPPONENT_PAWN || - captured_piece == OPPONENT_KING) { - env->opponent_pieces--; - } - } - - env->capture_available_valid = 0; - env->game_over_valid = 0; - - int promotion_occurred = try_make_king(env); - - if (capture_occurred && env->current_player == OPPONENT) { - reward += 0.1f; // reward for capturing - } else if (env->current_player == AGENT) { - reward += 0.01f; // reward for successful moves - } - - if (move_size(m) == 1 || !capture_available(env)) { - int other_player = env->current_player == AGENT ? OPPONENT : AGENT; - env->current_player = other_player; - } - - if (promotion_occurred) { - for (int i = 0; i < env->size; i++) { - if (env->observations[env->size * (env->size - 1) + i] == AGENT_KING) { - reward += 0.05f; // reward for promotion - break; - } - } - } - - if (is_game_over(env)) { - env->terminals[0] = 1; - int winner = get_winner(env); - reward = winner == AGENT ? 1.0f : -1.0f; - } - - env->rewards[0] = clamp(reward, -1.0f, 1.0f); -} - -void scripted_first_move(Checkers *env) { - int current_pawn = env->current_player == AGENT ? AGENT_PAWN : OPPONENT_PAWN; - int current_king = env->current_player == AGENT ? AGENT_KING : OPPONENT_KING; - int has_captures = capture_available(env); - - for (int i = 0; i < env->size * env->size; i++) { - int piece = env->observations[i]; - if (piece != current_pawn && piece != current_king) - continue; - - int r = i / env->size; - int c = i % env->size; - - int directions[8][2] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}, - {-2, -2}, {-2, 2}, {2, -2}, {2, 2}}; - - for (int d = 0; d < 8; d++) { - int new_r = r + directions[d][0]; - int new_c = c + directions[d][1]; - - if (new_r < 0 || new_r >= env->size || new_c < 0 || new_c >= env->size) - continue; - if (env->observations[new_r * env->size + new_c] != EMPTY) - continue; - - int move_size = abs(directions[d][0]); - - if (has_captures && move_size != 2) - continue; - - if (piece == current_pawn) { - int move_dir = directions[d][0] > 0 ? 1 : -1; - int valid_dir = env->current_player == AGENT ? 1 : -1; - if (move_dir != valid_dir) - continue; - } - - if (move_size == 2) { - int mid_r = r + directions[d][0] / 2; - int mid_c = c + directions[d][1] / 2; - int mid_piece = env->observations[mid_r * env->size + mid_c]; - - int opponent_pawn = - env->current_player == AGENT ? OPPONENT_PAWN : AGENT_PAWN; - int opponent_king = - env->current_player == AGENT ? OPPONENT_KING : AGENT_KING; - - if (mid_piece != opponent_pawn && mid_piece != opponent_king) - continue; - } - - int action = i * 8 + d; - make_move(env, action); - return; - } - } -} - -// Helper function to evaluate position value -float evaluate_position(Checkers *env) { - float score = 0.0f; - - for (int i = 0; i < env->size * env->size; i++) { - int piece = env->observations[i]; - int r = i / env->size; - - if (piece == AGENT_PAWN) { - score += 1.0f + (r * 0.1f); // Pawns are worth more as they advance - } else if (piece == AGENT_KING) { - score += 2.0f; - } else if (piece == OPPONENT_PAWN) { - score -= 1.0f + ((env->size - 1 - r) * 0.1f); - } else if (piece == OPPONENT_KING) { - score -= 2.0f; - } - } - - return score; -} - -void scripted_random_move(Checkers *env) { - int current_pawn = env->current_player == AGENT ? AGENT_PAWN : OPPONENT_PAWN; - int current_king = env->current_player == AGENT ? AGENT_KING : OPPONENT_KING; - int has_captures = capture_available(env); - - srand(time(NULL)); - int i; - int num_positions = env->size * env->size; - while (1) { - i = random() % num_positions; - int piece = env->observations[i]; - if (piece != current_pawn && piece != current_king) - continue; - - int r = i / env->size; - int c = i % env->size; - - int directions[8][2] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}, - {-2, -2}, {-2, 2}, {2, -2}, {2, 2}}; - - for (int d = 0; d < 8; d++) { - int new_r = r + directions[d][0]; - int new_c = c + directions[d][1]; - - if (new_r < 0 || new_r >= env->size || new_c < 0 || new_c >= env->size) - continue; - if (env->observations[new_r * env->size + new_c] != EMPTY) - continue; - - int move_size = abs(directions[d][0]); - - if (has_captures && move_size != 2) - continue; - - if (piece == current_pawn) { - int move_dir = directions[d][0] > 0 ? 1 : -1; - int valid_dir = env->current_player == AGENT ? 1 : -1; - if (move_dir != valid_dir) - continue; - } - - if (move_size == 2) { - int mid_r = r + directions[d][0] / 2; - int mid_c = c + directions[d][1] / 2; - int mid_piece = env->observations[mid_r * env->size + mid_c]; - - int opponent_pawn = - env->current_player == AGENT ? OPPONENT_PAWN : AGENT_PAWN; - int opponent_king = - env->current_player == AGENT ? OPPONENT_KING : AGENT_KING; - - if (mid_piece != opponent_pawn && mid_piece != opponent_king) - continue; - } - - int action = i * 8 + d; - make_move(env, action); - return; - } - } -} - -void scripted_step(Checkers *env, int difficulty) { - switch (difficulty) { - case 0: - scripted_first_move(env); - break; - case 1: - scripted_random_move(env); - break; - default: - scripted_random_move(env); - break; - } -} - -void update_piece_counts(Checkers *env) { - env->agent_pieces = 0; - env->opponent_pieces = 0; - - for (int i = 0; i < env->size * env->size; i++) { - int piece = env->observations[i]; - if (piece == AGENT_PAWN || piece == AGENT_KING) { - env->agent_pieces++; - } else if (piece == OPPONENT_PAWN || piece == OPPONENT_KING) { - env->opponent_pieces++; - } - } - - env->capture_available_valid = 0; - env->game_over_valid = 0; -} - -void add_log(Checkers *env) { - env->log.perf += (env->rewards[0] > 0) ? 1 : 0; - env->log.score += evaluate_position(env); - env->log.episode_length += env->tick; - env->log.episode_return += env->rewards[0]; - if (env->terminals[0] == 1) - env->log.winrate += get_winner(env) == AGENT ? 1.0f : 0.0f; - env->log.n += 1; -} - -// Required function -void c_reset(Checkers *env) { - env->tick = 0; - env->terminals[0] = 0; - env->rewards[0] = 0.0f; - - int tiles = env->size * env->size; - for (int i = 0; i < tiles; i++) - env->observations[i] = EMPTY; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < env->size; j++) { - if ((i + j) % 2) - env->observations[i * env->size + j] = AGENT_PAWN; - } - } - for (int i = env->size - 3; i < env->size; i++) { - for (int j = 0; j < env->size; j++) { - if ((i + j) % 2) - env->observations[i * env->size + j] = OPPONENT_PAWN; - } - } - - env->current_player = AGENT; - - update_piece_counts(env); -} - -// Required function -void c_step(Checkers *env) { - env->tick += 1; - int action = env->actions[0]; - env->rewards[0] = 0.0f; - env->terminals[0] = 0; - - make_move(env, action); - - env->rewards[0] = clamp(env->rewards[0], -1.0f, 1.0f); - if (env->terminals[0] == 1) { - add_log(env); - c_reset(env); - return; - } - - scripted_step(env, 1); - if (env->terminals[0] == 1) { - add_log(env); - c_reset(env); - return; - } -} - -// Required function. Should handle creating the client on first call -void c_render(Checkers *env) { - const Color BG1 = (Color){27, 27, 27, 255}; - const Color BG2 = (Color){13, 13, 13, 255}; - - int cell_size = 64; - int window_width = cell_size * env->size; - int window_height = cell_size * env->size; - int radius = cell_size / 3; - int king_offset = 14; - - if (!IsWindowReady()) { - SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(window_width, window_height, "Puffer Checkers"); - SetTargetFPS(30); - } else if (GetScreenWidth() != window_width || - GetScreenHeight() != window_height) { - SetWindowSize(window_width, window_height); - } - - if (IsKeyDown(KEY_ESCAPE)) { - CloseWindow(); - exit(0); - return; - } - - BeginDrawing(); - ClearBackground(BG1); - - Color piece_color; - for (int i = 0; i < env->size; i++) { - for (int j = 0; j < env->size; j++) { - int piece = env->observations[i * env->size + j]; - if ((i + j) % 2 == 0) - DrawRectangle(j * cell_size - 1, i * cell_size - 1, cell_size + 1, - cell_size + 1, BG2); - if (piece == EMPTY) - continue; - - int center_x = j * cell_size + cell_size / 2; - int center_y = i * cell_size + cell_size / 2; - - switch (piece) { - case AGENT_PAWN: - piece_color = BLUE; - DrawCircle(center_x, center_y, radius, piece_color); - DrawCircleGradient(center_x - radius / 3, center_y - radius / 3, - radius / 3, (Color){255, 255, 255, 80}, - (Color){255, 255, 255, 10}); - DrawCircleGradient(center_x, center_y, radius, - (Color){255, 255, 255, 50}, - (Color){255, 255, 255, 5}); - break; - - case AGENT_KING: - piece_color = BLUE; - DrawCircle(center_x, center_y, radius, piece_color); - DrawCircleGradient(center_x, center_y, radius, - (Color){255, 255, 255, 50}, - (Color){255, 255, 255, 5}); - - DrawCircleGradient(center_x, center_y - king_offset / 2, radius, - (Color){20, 20, 20, 60}, (Color){20, 20, 20, 30}); - DrawCircle(center_x, center_y - king_offset, radius, piece_color); - DrawCircleGradient( - center_x - radius / 3, center_y - radius / 3 - king_offset, - radius / 3, (Color){255, 255, 255, 80}, (Color){255, 255, 255, 10}); - DrawCircleGradient(center_x, center_y - king_offset, radius, - (Color){255, 255, 255, 50}, - (Color){255, 255, 255, 5}); - break; - - case OPPONENT_PAWN: - piece_color = RED; - DrawCircle(center_x, center_y, radius, piece_color); - DrawCircleGradient(center_x - radius / 3, center_y - radius / 3, - radius / 3, (Color){255, 255, 255, 80}, - (Color){255, 255, 255, 10}); - DrawCircleGradient(center_x, center_y, radius, - (Color){255, 255, 255, 50}, - (Color){255, 255, 255, 5}); - break; - - case OPPONENT_KING: - piece_color = RED; - DrawCircle(center_x, center_y, radius, piece_color); - DrawCircleGradient(center_x, center_y, radius, - (Color){255, 255, 255, 50}, - (Color){255, 255, 255, 5}); - - DrawCircleGradient(center_x, center_y - king_offset / 2, radius, - (Color){20, 20, 20, 60}, (Color){20, 20, 20, 30}); - DrawCircle(center_x, center_y - king_offset, radius, piece_color); - DrawCircleGradient( - center_x - radius / 3, center_y - radius / 3 - king_offset, - radius / 3, (Color){255, 255, 255, 80}, (Color){255, 255, 255, 10}); - DrawCircleGradient(center_x, center_y - king_offset, radius, - (Color){255, 255, 255, 50}, - (Color){255, 255, 255, 5}); - break; - - default: - break; - } - } - } - - EndDrawing(); -} - -// Required function. Should clean up anything you allocated -// Do not free env->observations, actions, rewards, terminals -void c_close(Checkers *env) { - if (IsWindowReady()) { - CloseWindow(); - } -} diff --git a/pufferlib/ocean/checkers/checkers.py b/pufferlib/ocean/checkers/checkers.py deleted file mode 100644 index 8c2195cfa..000000000 --- a/pufferlib/ocean/checkers/checkers.py +++ /dev/null @@ -1,65 +0,0 @@ -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.checkers import binding - -class Checkers(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, log_interval=128, size=8, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(size*size,), dtype=np.uint8) - num_move_types = 8 # Move types are: NW, NE, SW, SE, 2*NW, 2*NE, 2*SW, 2*SE, - action_space_size = size * size * num_move_types - self.single_action_space = gymnasium.spaces.Discrete(action_space_size) - self.render_mode = render_mode - self.num_agents = num_envs - self.log_interval = log_interval - - super().__init__(buf) - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, size=size) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - - self.actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 4096 - size = 3 - - env = Checkers(num_envs=N, size=size) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, size * size * 6, (CACHE, N)) - - i = 0 - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[i % CACHE]) - steps += N - i += 1 - - print('Checkers SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/connect4/binding.c b/pufferlib/ocean/connect4/binding.c deleted file mode 100644 index de8d7877a..000000000 --- a/pufferlib/ocean/connect4/binding.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "connect4.h" -#define Env CConnect4 -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/connect4/connect4.c b/pufferlib/ocean/connect4/connect4.c deleted file mode 100644 index cf617b7af..000000000 --- a/pufferlib/ocean/connect4/connect4.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "connect4.h" -#include "puffernet.h" -#include "time.h" - -const unsigned char NOOP = 8; - -void interactive() { - Weights* weights = load_weights("resources/connect4/connect4_weights.bin", 138632); - int logit_sizes[] = {7}; - LinearLSTM* net = make_linearlstm(weights, 1, 42, logit_sizes, 1); - - CConnect4 env = { - }; - allocate_cconnect4(&env); - c_reset(&env); - - env.client = make_client(); - float observations[42] = {0}; - int actions[1] = {0}; - - int tick = 0; - while (!WindowShouldClose()) { - env.actions[0] = NOOP; - // user inputs 1 - 7 key pressed - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if(IsKeyPressed(KEY_ONE)) env.actions[0] = 0; - if(IsKeyPressed(KEY_TWO)) env.actions[0] = 1; - if(IsKeyPressed(KEY_THREE)) env.actions[0] = 2; - if(IsKeyPressed(KEY_FOUR)) env.actions[0] = 3; - if(IsKeyPressed(KEY_FIVE)) env.actions[0] = 4; - if(IsKeyPressed(KEY_SIX)) env.actions[0] = 5; - if(IsKeyPressed(KEY_SEVEN)) env.actions[0] = 6; - } else if (tick % 30 == 0) { - for (int i = 0; i < 42; i++) { - observations[i] = env.observations[i]; - } - forward_linearlstm(net, (float*)&observations, (int*)&actions); - env.actions[0] = actions[0]; - } - - tick = (tick + 1) % 60; - if (env.actions[0] >= 0 && env.actions[0] <= 6) { - c_step(&env); - } - - c_render(&env); - } - free_linearlstm(net); - free(weights); - close_client(env.client); - free_allocated_cconnect4(&env); -} - -void performance_test() { - long test_time = 10; - CConnect4 env = { - }; - allocate_cconnect4(&env); - c_reset(&env); - - long start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - env.actions[0] = rand() % 7; - c_step(&env); - i++; - } - long end = time(NULL); - printf("SPS: %ld\n", i / (end - start)); - free_allocated_cconnect4(&env); -} - -int main() { - // performance_test(); - interactive(); - return 0; -} diff --git a/pufferlib/ocean/connect4/connect4.h b/pufferlib/ocean/connect4/connect4.h deleted file mode 100644 index dde12a4a1..000000000 --- a/pufferlib/ocean/connect4/connect4.h +++ /dev/null @@ -1,392 +0,0 @@ -#include -#include -#include -#include -#include "raylib.h" - -#define WIN_CONDITION 4 -const int PLAYER_WIN = 1.0; -const int ENV_WIN = -1.0; -const unsigned char DONE = 1; -const unsigned char NOT_DONE = 0; -const int ROWS = 6; -const int COLUMNS = 7; -const int WIDTH = 672; -const int HEIGHT = 576; -const int PIECE_WIDTH = 96; -const int PIECE_HEIGHT = 96; - -const float MAX_VALUE = 31; -const float WIN_VALUE = 30; -const float DRAW_VALUE = 0; - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -typedef struct Client Client; -typedef struct CConnect4 CConnect4; -struct CConnect4 { - // Pufferlib inputs / outputs - float* observations; - int* actions; - float* rewards; - unsigned char* terminals; - Log log; - Client* client; - - // Bit string representation from: - // https://towardsdatascience.com/creating-the-perfect-connect-four-ai-bot-c165115557b0 - // & http://blog.gamesolver.org/solving-connect-four/01-introduction/ - uint64_t player_pieces; - uint64_t env_pieces; - - int tick; -}; - -void allocate_cconnect4(CConnect4* env) { - env->observations = (float*)calloc(42, sizeof(float)); - env->actions = (int*)calloc(1, sizeof(int)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); - env->rewards = (float*)calloc(1, sizeof(float)); -} - -void free_allocated_cconnect4(CConnect4* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); -} - -void c_close(CConnect4* env) { -} - -void add_log(CConnect4* env) { - env->log.perf += (float)(env->rewards[0] == PLAYER_WIN); - env->log.score += env->rewards[0]; - env->log.episode_return += env->rewards[0]; - env->log.episode_length += env->log.episode_length; - env->log.n += 1; -} - -void init(CConnect4* env) { - env->log = (Log){0}; - env->tick = 0; -} - -// Get the bit at the top of 'column'. Column can be played if bit is 0 -uint64_t top_mask(uint64_t column) { - return (UINT64_C(1) << (ROWS - 1)) << column * (ROWS + 1); -} - -// Get a bit mask for where a piece played at 'column' would end up. -uint64_t bottom_mask(uint64_t column) { - return UINT64_C(1) << column * (ROWS + 1); -} - -// A bit mask used to create unique representation of the game state. -uint64_t c_bottom() { - return UINT64_C(1) << (COLUMNS - 1) * (ROWS + 1); -} - -bool invalid_move(int column, uint64_t mask) { - return (mask & top_mask(column)) != 0; -} - -uint64_t play(int column, uint64_t mask, uint64_t other_pieces) { - mask |= mask + bottom_mask(column); // Somehow faster than |= bottom_mask(column) - return other_pieces ^ mask; -} - -// A full board has this specifc value -bool draw(uint64_t mask) { - return mask == 4432406249472; -} - -// Determine if 'pieces' contains at least one line of connected pieces. -bool won(uint64_t pieces) { - // Horizontal - uint64_t m = pieces & (pieces >> (ROWS + 1)); - if(m & (m >> (2 * (ROWS + 1)))) { - return true; - } - - // Diagonal 1 - m = pieces & (pieces >> ROWS); - if(m & (m >> (2 * ROWS))) { - return true; - } - - // Diagonal 2 - m = pieces & (pieces >> (ROWS + 2)); - if(m & (m >> (2 * (ROWS + 2)))) { - return true; - } - - // Vertical; - m = pieces & (pieces >> 1); - if(m & (m >> 2)) { - return true; - } - - return false; -} - -// https://en.wikipedia.org/wiki/Negamax#Negamax_variant_with_no_color_parameter -float negamax(uint64_t pieces, uint64_t other_pieces, int depth) { - uint64_t piece_mask = pieces | other_pieces; - if (won(other_pieces)) { - return pow(10, depth); - } - if (won(pieces)) { - return 0; - } - - if (depth == 0 || draw(piece_mask)) { - return 0; - } - - float value = 0; - for (uint64_t column = 0; column < 7; column ++) { - if (invalid_move(column, piece_mask)) { - continue; - } - uint64_t child_pieces = play(column, piece_mask, other_pieces); - value -= negamax(other_pieces, child_pieces, depth - 1); - } - return value; -} - -int compute_env_move(CConnect4* env) { - uint64_t piece_mask = env->player_pieces | env->env_pieces; - uint64_t hash = env->player_pieces + piece_mask + c_bottom(); - - // Hard coded opening book to handle some early game traps - // TODO: Add more opening book moves - switch (hash) { - case 4398050705408: - // Respond to _ _ _ o _ _ _ - // with _ _ x o _ _ _ - return 2; - case 4398583382016: - // Respond to _ _ _ _ o _ _ - // with _ _ _ x o _ _ - return 3; - } - - float best_value = 9999; - float values[7]; - for (int i = 0; i < 7; i++) { - values[i] = 9999; - } - for (uint64_t column = 0; column < 7; column ++) { - if (invalid_move(column, piece_mask)) { - continue; - } - uint64_t child_env_pieces = play(column, piece_mask, env->player_pieces); - if (won(child_env_pieces)) { - return column; - } - float val = -negamax(env->player_pieces, child_env_pieces, 3); - values[column] = val; - if (val < best_value) { - best_value = val; - } - } - int num_ties = 0; - for (uint64_t column = 0; column < 7; column ++) { - if (values[column] == best_value) { - num_ties++; - } - } - //printf("Values: %f, %f, %f, %f, %f, %f, %f\n", values[0], values[1], values[2], values[3], values[4], values[5], values[6]); - int best_tie = rand() % num_ties; - for (uint64_t column = 0; column < 7; column ++) { - if (values[column] == best_value) { - if (best_tie == 0) { - return column; - - } - best_tie--; - } - } - return 0; -} - -void compute_observation(CConnect4* env) { - // Populate observations from bitstring game representation - // http://blog.gamesolver.org/solving-connect-four/06-bitboard/ - uint64_t player_pieces = env->player_pieces; - uint64_t env_pieces = env->env_pieces; - - int obs_idx = 0; - for (int i = 0; i < 49; i++) { - // Skip the sentinel row - if ((i + 1) % 7 == 0) { - continue; - } - - int p0_bit = (player_pieces >> i) & 1; - if (p0_bit == 1) { - env->observations[obs_idx] = PLAYER_WIN; - } - int p1_bit = (env_pieces >> i) & 1; - if (p1_bit == 1) { - env->observations[obs_idx] = ENV_WIN; - } - obs_idx += 1; - } -} - -void c_reset(CConnect4* env) { - env->log = (Log){0}; - env->terminals[0] = NOT_DONE; - env->player_pieces = 0; - env->env_pieces = 0; - for (int i = 0; i < 42; i ++) { - env->observations[i] = 0.0; - } -} - -void finish_game(CConnect4* env, float reward) { - env->rewards[0] = reward; - env->terminals[0] = DONE; - add_log(env); - compute_observation(env); -} - -void c_step(CConnect4* env) { - env->log.episode_length += 1; - env->rewards[0] = 0.0; - - if (env->terminals[0] == DONE) { - c_reset(env); - return; - } - - // Player action (PLAYER_WIN) - uint64_t column = env->actions[0]; - uint64_t piece_mask = env->player_pieces | env->env_pieces; - if (invalid_move(column, piece_mask)) { - finish_game(env, ENV_WIN); - return; - } - - env->player_pieces = play(column, piece_mask, env->env_pieces); - if (won(env->player_pieces)) { - finish_game(env, PLAYER_WIN); - return; - } - - // Environment action (ENV_WIN) - column = compute_env_move(env); - piece_mask = env->player_pieces | env->env_pieces; - if (invalid_move(column, piece_mask)) { - finish_game(env, PLAYER_WIN); - return; - } - - env->env_pieces = play(column, piece_mask, env->player_pieces); - if (won(env->env_pieces)) { - finish_game(env, ENV_WIN); - return; - } - - compute_observation(env); -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -typedef struct Client Client; -struct Client { - float width; - float height; - Texture2D puffers; -}; - -Client* make_client() { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = WIDTH; - client->height = HEIGHT; - - InitWindow(WIDTH, HEIGHT, "PufferLib Ray Connect4"); - SetTargetFPS(60); - - client->puffers = LoadTexture("resources/shared/puffers_128.png"); - return client; -} - -void c_render(CConnect4* env) { - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - if (env->client == NULL) { - env->client = make_client(); - } - - Client* client = env->client; - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - int y_offset = client->height - PIECE_HEIGHT; - int obs_idx = 0; - for (int i = 0; i < 49; i++) { - // TODO: Simplify this by iterating over the observation more directly - if ((i + 1) % 7 == 0) { - continue; - } - - int row = i % (ROWS + 1); - int column = i / (ROWS + 1); - int y = y_offset - row * PIECE_HEIGHT; - int x = column * PIECE_WIDTH; - - Color piece_color=PURPLE; - int color_idx = 0; - if (env->observations[obs_idx] == 0.0) { - piece_color = BLACK; - } else if (env->observations[obs_idx] == PLAYER_WIN) { - piece_color = PUFF_CYAN; - color_idx = 1; - } else if (env->observations[obs_idx] == ENV_WIN) { - piece_color = PUFF_RED; - color_idx = 2; - } - - obs_idx += 1; - Color board_color = (Color){0, 80, 80, 255}; - DrawRectangle(x , y , PIECE_WIDTH, PIECE_WIDTH, board_color); - DrawCircle(x + PIECE_WIDTH/2, y + PIECE_WIDTH/2, PIECE_WIDTH/2, piece_color); - if (color_idx == 0) { - continue; - } - - DrawTexturePro( - client->puffers, - (Rectangle){ - (color_idx == 1) ? 0 : 128, - 0, 128, 128, - }, - (Rectangle){x+16, y+16, PIECE_WIDTH-32, PIECE_WIDTH-32}, - (Vector2){0, 0}, - 0, - WHITE - ); - } - EndDrawing(); -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} diff --git a/pufferlib/ocean/connect4/connect4.py b/pufferlib/ocean/connect4/connect4.py deleted file mode 100644 index 27cdffb25..000000000 --- a/pufferlib/ocean/connect4/connect4.py +++ /dev/null @@ -1,82 +0,0 @@ -'''High-perf Pong - -Inspired from https://gist.github.com/Yttrmin/18ecc3d2d68b407b4be1 -& https://jair.org/index.php/jair/article/view/10819/25823 -& https://www.youtube.com/watch?v=PSQt5KGv7Vk -''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.connect4 import binding - - -class Connect4(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, report_interval=128, - buf=None, seed=0): - - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(42,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(7) - self.report_interval = report_interval - self.render_mode = render_mode - self.num_agents = num_envs - - super().__init__(buf=buf) - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed) - - def reset(self, seed=None): - self.tick = 0 - if seed is None: - binding.vec_reset(self.c_envs, 0) - else: - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - self.tick += 1 - - info = [] - if self.tick % self.report_interval == 0: - log = binding.vec_log(self.c_envs) - if log['episode_length'] > 0: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - - -def test_performance(timeout=10, atn_cache=1024, num_envs=1024): - import time - - env = Connect4(num_envs=num_envs) - env.reset() - tick = 0 - - actions = np.random.randint( - 0, - env.single_action_space.n + 1, - (atn_cache, num_envs), - ) - - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: {num_envs * tick / (time.time() - start)}') - - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/connect4/connect4game b/pufferlib/ocean/connect4/connect4game deleted file mode 100755 index d2677ee4f..000000000 Binary files a/pufferlib/ocean/connect4/connect4game and /dev/null differ diff --git a/pufferlib/ocean/convert/binding.c b/pufferlib/ocean/convert/binding.c deleted file mode 100644 index e53f6e943..000000000 --- a/pufferlib/ocean/convert/binding.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "convert.h" - -#define Env Convert -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->num_agents = unpack(kwargs, "num_agents"); - env->num_factories = unpack(kwargs, "num_factories"); - env->num_resources = unpack(kwargs, "num_resources"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/convert/convert.c b/pufferlib/ocean/convert/convert.c deleted file mode 100644 index 09f0d41f4..000000000 --- a/pufferlib/ocean/convert/convert.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "convert.h" -#include "puffernet.h" - -int main() { - Convert env = { - .width = 1920, - .height = 1080, - .num_agents = 1024, - .num_factories = 32, - .num_resources = 8, - }; - init(&env); - - int num_obs = 2*env.num_resources + 4 + env.num_resources; - env.observations = calloc(env.num_agents*num_obs, sizeof(float)); - env.actions = calloc(2*env.num_agents, sizeof(int)); - env.rewards = calloc(env.num_agents, sizeof(float)); - env.terminals = calloc(env.num_agents, sizeof(unsigned char)); - - Weights* weights = load_weights("resources/convert/convert_weights.bin", 137743); - int logit_sizes[2] = {9, 5}; - LinearLSTM* net = make_linearlstm(weights, env.num_agents, num_obs, logit_sizes, 2); - - c_reset(&env); - c_render(&env); - - while (!WindowShouldClose()) { - for (int i=0; i -#include -#include -#include "raylib.h" - -typedef struct { - float perf; - float score; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct { - Texture2D sprites; -} Client; - -typedef struct { - float x; - float y; - float heading; - float speed; - int item; - int episode_length; -} Agent; - -typedef struct { - float x; - float y; - float heading; - int item; -} Factory; - -typedef struct { - Log log; - Client* client; - Agent* agents; - Factory* factories; - float* observations; - int* actions; - float* rewards; - unsigned char* terminals; - int width; - int height; - int num_agents; - int num_factories; - int num_resources; -} Convert; - -void init(Convert* env) { - env->agents = calloc(env->num_agents, sizeof(Agent)); - env->factories = calloc(env->num_factories, sizeof(Factory)); -} - -int compare_floats(const void* a, const void* b) { - return (*(float*)a - *(float*)b) > 0; -} - -void compute_observations(Convert* env) { - int obs_idx = 0; - for (int a=0; anum_agents; a++) { - Agent* agent = &env->agents[a]; - float dists[env->num_resources]; - for (int i=0; inum_resources; i++) { - dists[i] = 999999; - } - for (int f=0; fnum_factories; f++) { - Factory* factory = &env->factories[f]; - float dx = factory->x - agent->x; - float dy = factory->y - agent->y; - float dd = dx*dx + dy*dy; - int type = f % env->num_resources; - if (dd < dists[type]) { - dists[type] = dd; - env->observations[obs_idx + 2*type] = dx/env->width; - env->observations[obs_idx + 2*type + 1] = dy/env->height; - } - } - obs_idx += 2*env->num_resources; - env->observations[obs_idx++] = agent->heading/(2*PI); - env->observations[obs_idx++] = env->rewards[a]; - env->observations[obs_idx++] = agent->x/env->width; - env->observations[obs_idx++] = agent->y/env->height; - memset(&env->observations[obs_idx], 0, env->num_resources*sizeof(float)); - env->observations[obs_idx + agent->item] = 1.0f; - obs_idx += env->num_resources; - } -} - -void c_reset(Convert* env) { - for (int i=0; inum_agents; i++) { - env->agents[i].x = 16 + rand()%(env->width-16); - env->agents[i].y = 16 + rand()%(env->height-16); - env->agents[i].item = rand() % env->num_resources; - env->agents[i].episode_length = 0; - } - for (int i=0; inum_factories; i++) { - env->factories[i].x = 16 + rand()%(env->width-16); - env->factories[i].y = 16 + rand()%(env->height-16); - env->factories[i].item = i % env->num_resources; - env->factories[i].heading = (rand() % 360)*PI/180.0f; - } - compute_observations(env); -} - -float clip(float val, float min, float max) { - if (val < min) { - return min; - } else if (val > max) { - return max; - } - return val; -} - -void c_step(Convert* env) { - for (int i=0; inum_agents; i++) { - env->terminals[i] = 0; - env->rewards[i] = 0; - Agent* agent = &env->agents[i]; - agent->episode_length += 1; - - agent->heading += ((float)env->actions[2*i] - 4.0f)/12.0f; - agent->heading = clip(agent->heading, 0, 2*PI); - - agent->speed += 1.0f*((float)env->actions[2*i + 1] - 2.0f); - agent->speed = clip(agent->speed, -20.0f, 20.0f); - - agent->x += agent->speed*cosf(agent->heading); - agent->x = clip(agent->x, 16, env->width-16); - - agent->y += agent->speed*sinf(agent->heading); - agent->y = clip(agent->y, 16, env->height-16); - - if (rand() % env->num_agents == 0) { - env->agents[i].x = rand() % env->width; - env->agents[i].y = rand() % env->height; - } - - for (int f=0; fnum_factories; f++) { - Factory* factory = &env->factories[f]; - float dx = (factory->x - agent->x); - float dy = (factory->y - agent->y); - float dist = sqrt(dx*dx + dy*dy); - if (dist > 32) { - continue; - } - if (factory->item == agent->item) { - agent->item = (agent->item + 1) % env->num_resources; - env->log.perf += 1.0f; - env->log.score += 1.0f; - env->log.episode_length += agent->episode_length; - env->log.n++; - env->rewards[i] = 1.0f; - agent->episode_length = 0; - } - } - } - for (int f=0; fnum_factories; f++) { - Factory* factory = &env->factories[f]; - factory->x += 2.0f*cosf(factory->heading); - factory->y += 2.0f*sinf(factory->heading); - - float factory_x = clip(factory->x, 16, env->width-16); - float factory_y = clip(factory->y, 16, env->height-16); - - if (factory_x != factory->x || factory_y != factory->y) { - factory->heading = (rand() % 360)*PI/180.0f; - factory->x = factory_x; - factory->y = factory_y; - } - } - compute_observations(env); -} - -void c_render(Convert* env) { - if (env->client == NULL) { - InitWindow(env->width, env->height, "PufferLib Convert"); - SetTargetFPS(30); - env->client = (Client*)calloc(1, sizeof(Client)); - env->client->sprites = LoadTexture("resources/shared/puffers.png"); - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - for (int f=0; fnum_factories; f++) { - Factory* factory = &env->factories[f]; - DrawTexturePro( - env->client->sprites, - (Rectangle){ - 64*factory->item, 512, 64, 64, - }, - (Rectangle){ - factory->x - 32, - factory->y - 32, - 64, - 64 - }, - (Vector2){0, 0}, - 0, - WHITE - ); - } - - for (int i=0; inum_agents; i++) { - Agent* agent = &env->agents[i]; - float heading = agent->heading; - int y = 576; - if (heading < PI/2 || heading > 3*PI/2) { - y += 32; - } - DrawTexturePro( - env->client->sprites, - (Rectangle){ - 32*agent->item, y, 32, 32, - }, - (Rectangle){ - agent->x - 16, - agent->y - 16, - 32, - 32 - }, - (Vector2){0, 0}, - 0, - WHITE - ); - } - - EndDrawing(); -} - -void c_close(Convert* env) { - free(env->agents); - free(env->factories); - if (env->client != NULL) { - Client* client = env->client; - UnloadTexture(client->sprites); - CloseWindow(); - free(client); - } -} diff --git a/pufferlib/ocean/convert/convert.py b/pufferlib/ocean/convert/convert.py deleted file mode 100644 index 1f57cead8..000000000 --- a/pufferlib/ocean/convert/convert.py +++ /dev/null @@ -1,82 +0,0 @@ -'''A simple sample environment. Use this as a template for your own envs.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.convert import binding - -class Convert(pufferlib.PufferEnv): - def __init__(self, num_envs=1, width=1920, height=1080, num_agents=1024, num_factories=32, - num_resources=8, render_mode=None, log_interval=128, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(2*num_resources + 4 + num_resources,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.MultiDiscrete([9, 5]) - - self.render_mode = render_mode - self.num_agents = num_envs*num_agents - self.log_interval = log_interval - - if num_resources < 1 or num_resources > 8: - raise pufferlib.APIUsageError('num_resources must be in [1, 8]') - - super().__init__(buf) - c_envs = [] - for i in range(num_envs): - c_env = binding.env_init( - self.observations[i*num_agents:(i+1)*num_agents], - self.actions[i*num_agents:(i+1)*num_agents], - self.rewards[i*num_agents:(i+1)*num_agents], - self.terminals[i*num_agents:(i+1)*num_agents], - self.truncations[i*num_agents:(i+1)*num_agents], - seed, width=width, height=height, - num_agents=num_agents, num_factories=num_factories, - num_resources=num_resources) - c_envs.append(c_env) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - self.actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 512 - - env = Convert(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(env.single_action_space.nvec, size=(CACHE, 2)) - - i = 0 - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[i % CACHE]) - steps += env.num_agents - i += 1 - - print('Convert SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/convert_circle/binding.c b/pufferlib/ocean/convert_circle/binding.c deleted file mode 100644 index ae91dedfd..000000000 --- a/pufferlib/ocean/convert_circle/binding.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "convert_circle.h" - -#define Env ConvertCircle -#include "../env_binding.h" - -static int my_init(Env *env, PyObject *args, PyObject *kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->num_agents = unpack(kwargs, "num_agents"); - env->num_factories = unpack(kwargs, "num_factories"); - env->num_resources = unpack(kwargs, "num_resources"); - env->equidistant = unpack(kwargs, "equidistant"); - env->radius = unpack(kwargs, "radius"); - init(env); - return 0; -} - -static int my_log(PyObject *dict, Log *log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/convert_circle/convert_circle.c b/pufferlib/ocean/convert_circle/convert_circle.c deleted file mode 100644 index d88936108..000000000 --- a/pufferlib/ocean/convert_circle/convert_circle.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "convert_circle.h" -#include "puffernet.h" -#include -#include - -int main() { - ConvertCircle env = { - .width = 1920, - .height = 1080, - .num_agents = 128, - .num_factories = 16, - .num_resources = 6, - .equidistant = 1, - .radius = 400, - }; - srand(time(NULL)); - init(&env); - - int num_obs = 2 * env.num_resources + 4 + env.num_resources; - env.observations = calloc(env.num_agents * num_obs, sizeof(float)); - env.actions = calloc(2 * env.num_agents, sizeof(int)); - env.rewards = calloc(env.num_agents, sizeof(float)); - env.terminals = calloc(env.num_agents, sizeof(unsigned char)); - - Weights *weights = - load_weights("resources/convert/convert_weights.bin", 137743); - int logit_sizes[2] = {9, 5}; - LinearLSTM *net = - make_linearlstm(weights, env.num_agents, num_obs, logit_sizes, 2); - - c_reset(&env); - c_render(&env); - - while (!WindowShouldClose()) { - for (int i = 0; i < env.num_agents; i++) { - env.actions[2 * i] = rand() % 9; - env.actions[2 * i + 1] = rand() % 5; - } - - forward_linearlstm(net, env.observations, env.actions); - compute_observations(&env); - c_step(&env); - c_render(&env); - } - - free_linearlstm(net); - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} diff --git a/pufferlib/ocean/convert_circle/convert_circle.h b/pufferlib/ocean/convert_circle/convert_circle.h deleted file mode 100644 index 00a4b772a..000000000 --- a/pufferlib/ocean/convert_circle/convert_circle.h +++ /dev/null @@ -1,252 +0,0 @@ -/* ConvertCircle: a sample multiagent env about puffers eating stars. - * Use this as a tutorial and template for your own multiagent envs. - * We suggest starting with the Squared env for a simpler intro. - * Star PufferLib on GitHub to support. It really, really helps! - */ - -#include "raylib.h" -#include -#include -#include - -typedef struct { - float perf; - float score; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct { - Texture2D sprites; -} Client; - -typedef struct { - float x; - float y; - float heading; - float speed; - int item; - int episode_length; -} Agent; - -typedef struct { - float x; - float y; - float heading; - int item; -} Factory; - -typedef struct { - Log log; - Client *client; - Agent *agents; - Factory *factories; - float *observations; - int *actions; - float *rewards; - unsigned char *terminals; - int width; - int height; - int num_agents; - int num_factories; - int num_resources; - int equidistant; - int radius; -} ConvertCircle; - -static inline float random_float(float low, float high) { - return low + (high - low) * ((float)rand() / (float)RAND_MAX); -} - -void init(ConvertCircle *env) { - env->agents = calloc(env->num_agents, sizeof(Agent)); - env->factories = calloc(env->num_factories, sizeof(Factory)); -} - -int compare_floats(const void *a, const void *b) { - return (*(float *)a - *(float *)b) > 0; -} - -void compute_observations(ConvertCircle *env) { - int obs_idx = 0; - for (int a = 0; a < env->num_agents; a++) { - Agent *agent = &env->agents[a]; - float dists[env->num_resources]; - for (int i = 0; i < env->num_resources; i++) { - dists[i] = 999999; - } - for (int f = 0; f < env->num_factories; f++) { - Factory *factory = &env->factories[f]; - float dx = factory->x - agent->x; - float dy = factory->y - agent->y; - float dd = dx * dx + dy * dy; - int type = f % env->num_resources; - if (dd < dists[type]) { - dists[type] = dd; - env->observations[obs_idx + 2 * type] = dx / env->width; - env->observations[obs_idx + 2 * type + 1] = dy / env->height; - } - } - obs_idx += 2 * env->num_resources; - env->observations[obs_idx++] = agent->heading / (2 * PI); - env->observations[obs_idx++] = env->rewards[a]; - env->observations[obs_idx++] = agent->x / env->width; - env->observations[obs_idx++] = agent->y / env->height; - memset(&env->observations[obs_idx], 0, env->num_resources * sizeof(float)); - env->observations[obs_idx + agent->item] = 1.0f; - obs_idx += env->num_resources; - } -} - -void c_reset(ConvertCircle *env) { - for (int i = 0; i < env->num_agents; i++) { - env->agents[i].x = env->width / 2.0f + random_float(-10.0f, 10.0f); - env->agents[i].y = env->height / 2.0f + random_float(-10.0f, 10.0f); - env->agents[i].item = rand() % env->num_resources; - env->agents[i].episode_length = 0; - } - float angle; - float delta_angle = 2.0f * PI / env->num_factories; - for (int i = 0; i < env->num_factories; i++) { - if (env->equidistant) { - angle = i * delta_angle; - } else { - angle = random_float(0, 2.0f * PI); - } - env->factories[i].x = env->width / 2.0f + env->radius * cosf(angle); - env->factories[i].y = env->height / 2.0f + env->radius * sinf(angle); - env->factories[i].item = i % env->num_resources; - env->factories[i].heading = (rand() % 360) * PI / 180.0f; - } - compute_observations(env); -} - -float clip(float val, float min, float max) { - if (val < min) { - return min; - } else if (val > max) { - return max; - } - return val; -} - -void c_step(ConvertCircle *env) { - for (int i = 0; i < env->num_agents; i++) { - env->terminals[i] = 0; - env->rewards[i] = 0; - Agent *agent = &env->agents[i]; - agent->episode_length += 1; - - agent->heading += ((float)env->actions[2 * i] - 4.0f) / 12.0f; - agent->heading = clip(agent->heading, 0, 2 * PI); - - agent->speed += 1.0f * ((float)env->actions[2 * i + 1] - 2.0f); - agent->speed = clip(agent->speed, -20.0f, 20.0f); - - agent->x += agent->speed * cosf(agent->heading); - agent->x = clip(agent->x, 16, env->width - 16); - - agent->y += agent->speed * sinf(agent->heading); - agent->y = clip(agent->y, 16, env->height - 16); - - if (rand() % env->num_agents == 0) { - env->agents[i].x = env->width / 2.0f + random_float(-10.0f, 10.0f); - env->agents[i].y = env->height / 2.0f + random_float(-10.0f, 10.0f); - } - - for (int f = 0; f < env->num_factories; f++) { - Factory *factory = &env->factories[f]; - float dx = (factory->x - agent->x); - float dy = (factory->y - agent->y); - float dist = sqrt(dx * dx + dy * dy); - if (dist > 32) { - continue; - } - if (factory->item == agent->item) { - agent->item = (agent->item + 1) % env->num_resources; - env->log.perf += 1.0f; - env->log.score += 1.0f; - env->log.episode_length += agent->episode_length; - env->log.n++; - env->rewards[i] = 1.0f; - agent->episode_length = 0; - } - } - } - for (int f = 0; f < env->num_factories; f++) { - Factory *factory = &env->factories[f]; - factory->x += 0.0f * cosf(factory->heading); - factory->y += 0.0f * sinf(factory->heading); - - float factory_x = clip(factory->x, 16, env->width - 16); - float factory_y = clip(factory->y, 16, env->height - 16); - - if (factory_x != factory->x || factory_y != factory->y) { - factory->heading = (rand() % 360) * PI / 180.0f; - factory->x = factory_x; - factory->y = factory_y; - } - } - compute_observations(env); -} - -void c_render(ConvertCircle *env) { - if (env->client == NULL) { - InitWindow(env->width, env->height, "PufferLib ConvertCircle"); - SetTargetFPS(30); - env->client = (Client *)calloc(1, sizeof(Client)); - env->client->sprites = LoadTexture("resources/shared/puffers.png"); - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - for (int f = 0; f < env->num_factories; f++) { - Factory *factory = &env->factories[f]; - DrawTexturePro(env->client->sprites, - (Rectangle){ - 64 * factory->item, - 512, - 64, - 64, - }, - (Rectangle){factory->x - 32, factory->y - 32, 64, 64}, - (Vector2){0, 0}, 0, WHITE); - } - - for (int i = 0; i < env->num_agents; i++) { - Agent *agent = &env->agents[i]; - float heading = agent->heading; - int y = 576; - if (heading < PI / 2 || heading > 3 * PI / 2) { - y += 32; - } - DrawTexturePro(env->client->sprites, - (Rectangle){ - 32 * agent->item, - y, - 32, - 32, - }, - (Rectangle){agent->x - 16, agent->y - 16, 32, 32}, - (Vector2){0, 0}, 0, WHITE); - } - - EndDrawing(); -} - -void c_close(ConvertCircle *env) { - free(env->agents); - free(env->factories); - if (env->client != NULL) { - Client *client = env->client; - UnloadTexture(client->sprites); - CloseWindow(); - free(client); - } -} diff --git a/pufferlib/ocean/convert_circle/convert_circle.py b/pufferlib/ocean/convert_circle/convert_circle.py deleted file mode 100644 index f8682535b..000000000 --- a/pufferlib/ocean/convert_circle/convert_circle.py +++ /dev/null @@ -1,83 +0,0 @@ -'''A simple sample environment. Use this as a template for your own envs.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.convert_circle import binding - -class ConvertCircle(pufferlib.PufferEnv): - def __init__(self, num_envs=1, width=1920, height=1080, num_agents=1024, num_factories=32, - num_resources=8, equidistant=0, radius=30, render_mode=None, log_interval=128, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(2*num_resources + 4 + num_resources,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.MultiDiscrete([9, 5]) - - self.render_mode = render_mode - self.num_agents = num_envs*num_agents - self.log_interval = log_interval - - if num_resources < 1 or num_resources > 8: - raise pufferlib.APIUsageError('num_resources must be in [1, 8]') - - super().__init__(buf) - c_envs = [] - for i in range(num_envs): - c_env = binding.env_init( - self.observations[i*num_agents:(i+1)*num_agents], - self.actions[i*num_agents:(i+1)*num_agents], - self.rewards[i*num_agents:(i+1)*num_agents], - self.terminals[i*num_agents:(i+1)*num_agents], - self.truncations[i*num_agents:(i+1)*num_agents], - seed, width=width, height=height, - num_agents=num_agents, num_factories=num_factories, - num_resources=num_resources, equidistant=equidistant, - radius=radius) - c_envs.append(c_env) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - self.actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 512 - - env = ConvertCircle(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(env.single_action_space.nvec, size=(CACHE, 2)) - - i = 0 - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[i % CACHE]) - steps += env.num_agents - i += 1 - - print('ConvertCircle SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/cpr/binding.c b/pufferlib/ocean/cpr/binding.c deleted file mode 100644 index 751165185..000000000 --- a/pufferlib/ocean/cpr/binding.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "cpr.h" - -#define Env CCpr -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->num_agents = unpack(kwargs, "num_agents"); - env->vision = unpack(kwargs, "vision"); - env->reward_food = unpack(kwargs, "reward_food"); - env->interactive_food_reward = unpack(kwargs, "interactive_food_reward"); - env->reward_move = unpack(kwargs, "reward_move"); - env->food_base_spawn_rate = unpack(kwargs, "food_base_spawn_rate"); - init_ccpr(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "moves", log->moves); - assign_to_dict(dict, "food_nb", log->food_nb); - assign_to_dict(dict, "alive_steps", log->alive_steps); - return 0; -} diff --git a/pufferlib/ocean/cpr/cpr.c b/pufferlib/ocean/cpr/cpr.c deleted file mode 100644 index c70026474..000000000 --- a/pufferlib/ocean/cpr/cpr.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include "cpr.h" -#include "puffernet.h" - -int main() { - CCpr env = { - .num_agents = 4, - .width = 32, - .height = 32, - .vision = 3, - .reward_food = 1.0f, - .interactive_food_reward = 5.0f, - .food_base_spawn_rate = 2e-3, - }; - allocate_ccpr(&env); - c_reset(&env); - c_render(&env); - - Weights* weights = load_weights("resources/cpr/cpr_weights.bin", 139270); - int logit_sizes[] = {5}; - LinearLSTM* net = make_linearlstm(weights, env.num_agents, 49, logit_sizes, 1); - - while (!WindowShouldClose()) { - // User can take control of the first puffer - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) - env.actions[0] = 0; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) - env.actions[0] = 1; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) - env.actions[0] = 2; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) - env.actions[0] = 3; - - printf("Getting user input %d\n", env.actions[0]); - sleep(2); - } else { - for (int i = 0; i < env.num_agents*49; i++) { - net->obs[i] = env.observations[i]; - } - forward_linearlstm(net, net->obs, env.actions); - } - - c_step(&env); - c_render(&env); - } - //close_renderer(renderer); - free_CCpr(&env); - - return 0; -} diff --git a/pufferlib/ocean/cpr/cpr.h b/pufferlib/ocean/cpr/cpr.h deleted file mode 100644 index 110b01e62..000000000 --- a/pufferlib/ocean/cpr/cpr.h +++ /dev/null @@ -1,691 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "raylib.h" - -#include "grid.h" - -#define EMPTY 0 -#define NORMAL_FOOD 1 -#define INTERACTIVE_FOOD 2 -// Anything above Wall should be obstacles -#define WALL 3 -#define AGENTS 4 - -#define LOG_BUFFER_SIZE 8192 - -#define SET_BIT(arr, i) (arr[(i) / 8] |= (1 << ((i) % 8))) -#define CLEAR_BIT(arr, i) (arr[(i) / 8] &= ~(1 << ((i) % 8))) -#define CHECK_BIT(arr, i) (arr[(i) / 8] & (1 << ((i) % 8))) -#define min(a, b) ((a) < (b) ? (a) : (b)) - -#define REWARD_20_HP -0 -#define REWARD_80_HP 0 -#define REWARD_DEATH -1.0f - - -#define LOG_SCORE_REWARD_SMALL 0.1f -#define LOG_SCORE_REWARD_MEDIUM 0.2f -#define LOG_SCORE_REWARD_MOVE - 0.0 -#define LOG_SCORE_REWARD_DEATH -1 - -#define HP_REWARD_FOOD_MEDIUM 50 -#define HP_REWARD_FOOD_SMALL 20 -#define HP_LOSS_PER_STEP 1 -#define MAX_HP 100 - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float moves; - float food_nb; - float alive_steps; - float n; -}; - -typedef struct Agent Agent; -struct Agent { - int r; - int c; - int id; - float hp; - int direction; -}; - -typedef struct FoodList FoodList; -struct FoodList { - int *indexes; // Grid flattened index positions - int size; -}; - -FoodList *allocate_foodlist(int size) { - FoodList *foods = (FoodList *)calloc(1, sizeof(FoodList)); - foods->indexes = (int *)calloc(size, sizeof(int)); - foods->size = 0; - return foods; -} - -void free_foodlist(FoodList *foods) { - free(foods->indexes); - free(foods); -} - -typedef struct Renderer Renderer; -typedef struct CCpr CCpr; -struct CCpr { - Renderer* client; - int width; - int height; - int num_agents; - - int vision; - int vision_window; - int obs_size; - - int tick; - - float reward_food; - float reward_move; - float interactive_food_reward; - - unsigned char *grid; - unsigned char *observations; - int *actions; - float *rewards; - unsigned char *terminals; - unsigned char *truncations; - unsigned char *masks; - - Agent *agents; - - Log log; - Log* agent_logs; - - uint8_t *interactive_food_agent_count; - - FoodList *foods; - float food_base_spawn_rate; -}; - -void add_log(CCpr *env, Log *log) { - env->log.perf = fmaxf(0, 1.0 - 0.01*log->alive_steps); - env->log.episode_return += log->episode_return; - env->log.score += log->score; - env->log.moves += log->moves / log->alive_steps; - env->log.alive_steps += log->alive_steps; - env->log.n += 1; -} - -void init_ccpr(CCpr *env) { - env->grid = - (unsigned char *)calloc(env->width * env->height, sizeof(unsigned char)); - env->agents = (Agent *)calloc(env->num_agents, sizeof(Agent)); - env->vision_window = 2 * env->vision + 1; - env->obs_size = env->vision_window * env->vision_window;// + 1; - env->interactive_food_agent_count = - (uint8_t *)calloc((env->width * env->height + 7) / 8, sizeof(uint8_t)); - env->foods = allocate_foodlist(env->width * env->height); - env->agent_logs = (Log *)calloc(env->num_agents, sizeof(Log)); - env->masks = (unsigned char *)calloc(env->num_agents, sizeof(unsigned char)); -} - -void allocate_ccpr(CCpr *env) { - // Called by C stuff - int obs_size = (2 * env->vision + 1) * (2 * env->vision + 1); //+ 1; - env->observations = (unsigned char *)calloc(env->num_agents * obs_size, - sizeof(unsigned char)); - env->actions = (int *)calloc(env->num_agents, sizeof(unsigned int)); - env->rewards = (float *)calloc(env->num_agents, sizeof(float)); - env->terminals = - (unsigned char *)calloc(env->num_agents, sizeof(unsigned char)); - env->truncations = (unsigned char*)calloc(env->num_agents, sizeof(unsigned char)); - init_ccpr(env); -} - -void c_close(CCpr *env) { - free(env->grid); - free(env->agents); - free(env->interactive_food_agent_count); - free_foodlist(env->foods); - free(env->masks); - free(env->agent_logs); -} - -void free_CCpr(CCpr *env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - free(env->truncations); - c_close(env); -} - -int grid_index(CCpr *env, int r, int c) { return r * env->width + c; } -int get_agent_tile_from_id(int agent_id) { return AGENTS + agent_id; } - -int get_agent_id_from_tile(int tile) { return tile - AGENTS; } - -void add_food(CCpr *env, int grid_idx, int food_type) { - // Add food to the grid and the food_list at grid_idx - assert(env->grid[grid_idx] == EMPTY); - env->grid[grid_idx] = food_type; - FoodList *foods = env->foods; - foods->indexes[foods->size++] = grid_idx; -} - -void reward_agent(CCpr *env, int agent_id, float reward) { - // We don't reward if agent is full life - // Agent *agent = &env->agents[agent_id]; - // if (agent->hp >= MAX_HP) { - // return; - // } - env->rewards[agent_id] += reward; - env->agent_logs[agent_id].episode_return += reward; -} - -void spawn_food(CCpr *env, int food_type) { - // Randomly spawns such food in the grid - int idx, tile; - do { - int r = rand() % (env->height - 1); - int c = rand() % (env->width - 1); - idx = r * env->width + c; - tile = env->grid[idx]; - } while (tile != EMPTY); - add_food(env, idx, food_type); -} - -void remove_food(CCpr *env, int grid_idx) { - // Removes food from the grid and food_list - env->grid[grid_idx] = EMPTY; - FoodList *foods = env->foods; - for (int i = 0; i < foods->size; i++) { - if (foods->indexes[i] == grid_idx) { - foods->indexes[i] = foods->indexes[foods->size - 1]; - foods->size--; - return; - } - } -} - -void init_foods(CCpr *env) { - // On reset spawns x number of each food randomly. - int available_tiles = (env->width * env->height) - - (2 * env->vision * env->width + - 2 * env->vision * (env->height - 2 * env->vision)); - int normalizer = (env->width * env->height) / 576; - int normal = available_tiles / (20 * normalizer); - int interactive = available_tiles / (50 * normalizer); - for (int i = 0; i < normal; i++) { - spawn_food(env, NORMAL_FOOD); - } - for (int i = 0; i < interactive; i++) { - spawn_food(env, INTERACTIVE_FOOD); - } -} - -void spawn_foods(CCpr *env) { - // After each step, check existing foods and spawns new food in the - // neighborhood Iterates over food_list for efficiency instead of the entire - // grid. - FoodList *foods = env->foods; - int original_size = foods->size; - for (int i = 0; i < original_size; i++) { - int idx = foods->indexes[i]; - int offset = idx - env->width - 1; // Food spawn in 1 radius - int r = offset / env->width; - int c = offset % env->width; - for (int ri = 0; ri < 3; ri++) { - for (int ci = 0; ci < 3; ci++) { - int grid_idx = grid_index(env, (r + ri), (c + ci)); - if (env->grid[grid_idx] != EMPTY) { - continue; - } - switch (env->grid[idx]) { - // %Chance spawning new food - case NORMAL_FOOD: - if ((rand() / (double)RAND_MAX) < env->food_base_spawn_rate) { - add_food(env, grid_idx, env->grid[idx]); - } - break; - case INTERACTIVE_FOOD: - if ((rand() / (double)RAND_MAX) < - (env->food_base_spawn_rate / 10.0)) { - add_food(env, grid_idx, env->grid[idx]); - } - break; - } - } - } - } - - // // Each turn there is random probability for a food to spawn at a random - // // location To cope with resource depletion - // int normalizer = (env->width * env->height) / 576; - // if ((rand() / (double)RAND_MAX) < - // min((env->food_base_spawn_rate * 2 * normalizer), 1e-2)) { - // spawn_food(env, NORMAL_FOOD); - // } - // if ((rand() / (double)RAND_MAX) < - // min((env->food_base_spawn_rate / 5.0 * normalizer), 5e-3)) { - // spawn_food(env, INTERACTIVE_FOOD); - // } -} - -void compute_observations(CCpr *env) { - // For full obs - // memcpy(env->observations, env->grid, - // env->width * env->height * sizeof(unsigned char)); - // return; - - // For partial obs - for (int i = 0; i < env->num_agents; i++) { - Agent *agent = &env->agents[i]; - // env->observations[env->vision_window*env->vision_window + i*env->obs_size] = agent->hp; - if (agent->hp == 0) { - continue; - } - int obs_offset = i * env->obs_size; - int r_offset = agent->r - env->vision; - int c_offset = agent->c - env->vision; - for (int r = 0; r < 2 * env->vision + 1; r++) { - for (int c = 0; c < 2 * env->vision + 1; c++) { - int grid_idx = (r_offset + r) * env->width + c_offset + c; - int obs_idx = obs_offset + r * env->vision_window + c; - env->observations[obs_idx] = env->grid[grid_idx]; - } - } - - - } -} - -void add_hp(CCpr *env, int agent_id, float hp) { - Agent *agent = &env->agents[agent_id]; - agent->hp += hp; - if (agent->hp > MAX_HP) { - agent->hp = MAX_HP; - } else if (agent->hp <= 0) { - agent->hp = 0; - env->agent_logs[agent->id].score += LOG_SCORE_REWARD_DEATH; - reward_agent(env, agent_id, REWARD_DEATH); - env->terminals[agent->id] = 1; - add_log(env, &env->agent_logs[agent_id]); - } -} - -void remove_hp(CCpr *env, int agent_id, float hp) { - add_hp(env, agent_id, -hp); -} - -void save_grid_to_file(CCpr *env, const char *filename) { - FILE *file = fopen(filename, "w"); - if (!file) { - perror("Failed to open file"); - return; - } - fprintf(file, "#ifndef GRID_H\n#define GRID_H\n\n"); - fprintf(file, "#define GRID_HEIGHT %d\n", env->height); - fprintf(file, "#define GRID_WIDTH %d\n\n", env->width); - fprintf(file, "static const unsigned char grid[GRID_HEIGHT][GRID_WIDTH] = {\n"); - - for (int r = 0; r < env->height; r++) { - fprintf(file, " {"); - for (int c = 0; c < env->width; c++) { - unsigned char val = env->grid[r * env->width + c]; - fprintf(file, "0x%02X%s", val, (c == env->width - 1) ? "" : ", "); - } - fprintf(file, "}%s\n", (r == env->height - 1) ? "" : ","); - } - fprintf(file, "};\n\n#endif // GRID_H\n"); - fclose(file); -} - -void make_grid_from_scratch(CCpr *env){ - memset(env->grid, EMPTY, (env->height * env->width) * sizeof(env->grid[0])); - // top walling - for (int r = 0; r < env->vision; r++) { - memset(env->grid + (r * env->width), WALL, - env->width * sizeof(env->grid[0])); - } - // left side walling - for (int r = 0; r < env->height; r++) { - memset(env->grid + (r * env->width), WALL, - env->vision * sizeof(env->grid[0])); - } - // bottom walling - for (int r = env->height - env->vision; r < env->height; r++) { - memset(env->grid + (r * env->width), WALL, - env->width * sizeof(env->grid[0])); - } - - // right side walling - for (int r = 0; r < env->height; r++) { - memset(env->grid + (r * env->width) + (env->width - env->vision), WALL, - env->vision * sizeof(env->grid[0])); - } - save_grid_to_file(env, "grid.h"); -} - -void spawn_agent(CCpr *env, int i){ - Agent *agent = &env->agents[i]; - agent->id = i; - agent->hp = 80; - int adr = 0; - - bool allocated = false; - while (!allocated) { - adr = rand() % (env->height * env->width); - if (env->grid[adr] == EMPTY) { - int r = adr / env->width; - int c = adr % env->width; - agent->r = r; - agent->c = c; - allocated = true; - } - } - assert(env->grid[adr] == EMPTY); - env->grid[adr] = get_agent_tile_from_id(agent->id); - env->agent_logs[i] = (Log){0}; -} -void c_reset(CCpr *env) { - env->tick = 0; - memset(env->agent_logs, 0, env->num_agents * sizeof(Log)); - env->log = (Log){0}; - env->foods->size = 0; - memset(env->foods->indexes, 0, env->width * env->height * sizeof(int)); - // make_grid_from_scratch(env); - memcpy(env->grid, grid_32_32_3v, env->width * env->height * sizeof(unsigned char)); - - for (int i = 0; i < env->num_agents; i++) { - spawn_agent(env, i); - } - - init_foods(env); - memset(env->observations, 0, env->num_agents * env->obs_size * sizeof(unsigned char)); - //memset(env->truncations, 0, env->num_agents * sizeof(unsigned char)); - memset(env->terminals, 0, env->num_agents * sizeof(unsigned char)); - memset(env->masks, 1, env->num_agents * sizeof(unsigned char)); - compute_observations(env); -} - -void reward_agents_near(CCpr *env, int food_index) { - int food_r = food_index / env->width; - int food_c = food_index % env->width; - - // TODO: could iterate over neighbors of food index and check if is agent - // (remove iteration cost) - for (int i = 0; i < env->num_agents; i++) { - int ac = env->agents[i].c; - int ar = env->agents[i].r; - - if ((ac == food_c && (ar == food_r - 1 || ar == food_r + 1)) || - (ar == food_r && (ac == food_c - 1 || ac == food_c + 1))) { - reward_agent(env, i, env->interactive_food_reward); - env->agent_logs[i].score += LOG_SCORE_REWARD_MEDIUM; - add_hp(env, i, HP_REWARD_FOOD_MEDIUM); - } - } - remove_food(env, food_index); -} - -void step_agent(CCpr *env, int i) { - - Agent *agent = &env->agents[i]; - - int action = env->actions[i]; - - int dr = 0; - int dc = 0; - - switch (action) { - case 0: - dr = -1; - agent->direction = 3; - break; // UP - case 1: - dr = 1; - agent->direction = 1; - break; // DOWN - case 2: - dc = -1; - agent->direction = 2; - break; // LEFT - case 3: - dc = 1; - agent->direction = 0; - break; // RIGHT - case 4: - return; // No moves - } - env->agent_logs[i].moves += 1; - - // Get next row and column - - int next_r = agent->r + dr; - int next_c = agent->c + dc; - - int prev_grid_idx = grid_index(env, agent->r, agent->c); - int next_grid_idx = env->width * next_r + next_c; - int tile = env->grid[next_grid_idx]; - - // Anything above should be obstacle - // In this case the agent position does not change - // We still have some checks to perform - if (tile >= INTERACTIVE_FOOD) { - env->agent_logs[i].score += LOG_SCORE_REWARD_MOVE; - reward_agent(env, i, env->reward_move); - next_r = agent->r; - next_c = agent->c; - next_grid_idx = env->width * next_r + next_c; - tile = env->grid[next_grid_idx]; - } - switch (tile) { - case NORMAL_FOOD: - reward_agent(env, i, env->reward_food); - env->agent_logs[i].score += LOG_SCORE_REWARD_SMALL; - add_hp(env, i, HP_REWARD_FOOD_SMALL); - remove_food(env, next_grid_idx); - break; - case EMPTY: - env->agent_logs[i].score += LOG_SCORE_REWARD_MOVE; - reward_agent(env, i, env->reward_move); - break; - } - - // Interactive food logic - int neighboors[4] = { - grid_index(env, next_r - 1, next_c), // Up - grid_index(env, next_r + 1, next_c), // Down - grid_index(env, next_r, next_c + 1), // Right - grid_index(env, next_r, next_c - 1) // Left - }; - - for (int j = 0; j < 4; j++) { - int grid_idx = neighboors[j]; - // If neighbooring grid tile is interactive food - if (env->grid[grid_idx] == INTERACTIVE_FOOD) { - // If was already marked as "ready to collect" - if (CHECK_BIT(env->interactive_food_agent_count, grid_idx)) { - reward_agents_near(env, grid_idx); - } else { - // First agent detected - SET_BIT(env->interactive_food_agent_count, grid_idx); - } - } - } - - // update the grid tiles values - int agent_tile = get_agent_tile_from_id(agent->id); - env->grid[prev_grid_idx] = EMPTY; - env->grid[next_grid_idx] = agent_tile; - agent->r = next_r; - agent->c = next_c; - - return; -} - -void clear_agent(CCpr *env, int agent_id) { - Agent *agent = &env->agents[agent_id]; - if (agent->r < 0 || agent->c < 0) { - return; - } - int grid_idx = grid_index(env, agent->r, agent->c); - env->grid[grid_idx] = EMPTY; - agent->r = -1; - agent->c = -1; -} - -void c_step(CCpr *env) { - env->tick++; - - memset(env->rewards, 0, env->num_agents * sizeof(float)); - memset(env->interactive_food_agent_count, 0, - (env->width * env->height + 7) / 8); - - for (int i = 0; i < env->num_agents; i++) { - if (env->agents[i].hp == 0) { - env->masks[i] = 0; - clear_agent(env, i); - continue; - } - step_agent(env, i); - remove_hp(env, i, HP_LOSS_PER_STEP); - } - - spawn_foods(env); - - //We loop again here because in the future an entity might have attacked an agent in the process - int alive_agents = 0; - for (int i = 0; i < env->num_agents; i++) { - if (env->agents[i].hp > 0) { - env->agent_logs[i].alive_steps += 1; - alive_agents += 1; - if (env->agents[i].hp < 20) { - reward_agent(env, i, REWARD_20_HP); - env->agent_logs[i].score += REWARD_20_HP; - } else if (env->agents[i].hp > 80) { - reward_agent(env, i, REWARD_80_HP); - env->agent_logs[i].score += REWARD_80_HP; - } - } - // else { - // int grid_idx = grid_index(env, env->agents[i].r, env->agents[i].c); - // env->grid[grid_idx] = EMPTY; - // spawn_agent(env, i); - // } - } - /* - if (alive_agents == 0) { - env->agent_logs[i].moves = 0; - }else{ - env->agent_logs[i].moves /= alive_agents; - } - env->agent_logs[i].food_nb = env->foods->size; - env->agent_logs[i].alive_steps = env->tick; - */ - env->log.food_nb = env->foods->size; - compute_observations(env); - if (alive_agents == 0 || env->tick > 1000) { - c_reset(env); - if (alive_agents == 0) { - memset(env->terminals, 1, env->num_agents * sizeof(unsigned char)); - } - } -} - -// Raylib client -Color COLORS[] = { - (Color){255, 0, 0, 255}, (Color){170, 170, 170, 255}, - (Color){255, 255, 0, 255}, (Color){0, 255, 0, 255}, - (Color){0, 255, 255, 255}, (Color){0, 128, 255, 255}, - (Color){128, 128, 128, 255}, (Color){255, 0, 0, 255}, - (Color){255, 255, 255, 255}, (Color){255, 85, 85, 255}, - (Color){170, 170, 170, 255}, (Color){0, 255, 255, 255}, - (Color){0, 0, 255, 255}, (Color){6, 24, 24, 255}, -}; - -Rectangle UV_COORDS[7] = { - (Rectangle){0, 0, 0, 0}, (Rectangle){512, 0, 128, 128}, - (Rectangle){0, 0, 0, 0}, (Rectangle){0, 0, 128, 128}, - (Rectangle){128, 0, 128, 128}, (Rectangle){256, 0, 128, 128}, - (Rectangle){384, 0, 128, 128}, -}; - -struct Renderer { - int cell_size; - int width; - int height; - Texture2D puffer; -}; - -Renderer *init_renderer(int cell_size, int width, int height) { - Renderer *renderer = (Renderer *)calloc(1, sizeof(Renderer)); - renderer->cell_size = cell_size; - renderer->width = width; - renderer->height = height; - - InitWindow(width * cell_size, height * cell_size, "CPR"); - SetTargetFPS(10); - - renderer->puffer = LoadTexture("resources/shared/puffers_128.png"); - return renderer; -} - -void close_renderer(Renderer *renderer) { - CloseWindow(); - free(renderer); -} - -void c_render(CCpr *env) { - if (env->client == NULL) { - env->client = init_renderer(32, env->width, env->height); - }; - Renderer *renderer = env->client; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - int ts = renderer->cell_size; - for (int r = 0; r < env->height; r++) { - for (int c = 0; c < env->width; c++) { - int adr = grid_index(env, r, c); - int tile = env->grid[adr]; - if (tile == EMPTY) { - continue; - } else if (tile == WALL) { - DrawRectangle(c * ts, r * ts, ts, ts, (Color){227, 227, 227, 255}); - } else if (tile == NORMAL_FOOD || tile == INTERACTIVE_FOOD) { - DrawRectangle(c * ts, r * ts, ts, ts, COLORS[tile]); - } else { - - int agent_id = get_agent_id_from_tile(tile); - int col_id = agent_id % (sizeof(COLORS) / sizeof(COLORS[0])); - Color color = COLORS[col_id]; - int starting_sprite_x = 0; - float rotation = env->agents[agent_id].direction * 90.0f; - if (rotation == 180) { - starting_sprite_x = 128; - rotation = 0; - } - Rectangle source_rect = (Rectangle){starting_sprite_x, 0, 128, 128}; - Rectangle dest_rect = (Rectangle){c * ts + ts/2, r * ts + ts/2, ts, ts}; - DrawTexturePro(renderer->puffer, source_rect, dest_rect, - (Vector2){ts/2, ts/2}, rotation, color); - } - } - } - EndDrawing(); -} diff --git a/pufferlib/ocean/cpr/cpr.py b/pufferlib/ocean/cpr/cpr.py deleted file mode 100644 index bd2c78d77..000000000 --- a/pufferlib/ocean/cpr/cpr.py +++ /dev/null @@ -1,109 +0,0 @@ -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.cpr import binding - -class PyCPR(pufferlib.PufferEnv): - def __init__(self, - num_envs=1, - widths=[32], - heights=[32], - num_agents=[8], - vision=3, - reward_food=1.0, - interactive_food_reward=5.0, - reward_move=-0.01, - food_base_spawn_rate=2e-3, - report_interval=1, - render_mode=None, - buf=None, - seed=0, - ): - widths = num_envs*widths - heights = num_envs*heights - num_agents = num_envs*num_agents - - self.single_observation_space = gymnasium.spaces.Box(low=0, high=255, shape=((2*vision+1)*(2*vision+1),), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.Discrete(5) - self.render_mode = render_mode - self.num_agents = sum(num_agents) - - self.tick = 0 - self.report_interval = report_interval - - super().__init__(buf) - c_envs = [] - for i in range(num_envs): - n = num_agents[i] - env_id = binding.env_init( - self.observations[i*n:(i+1)*n], - self.actions[i*n:(i+1)*n], - self.rewards[i*n:(i+1)*n], - self.terminals[i*n:(i+1)*n], - self.truncations[i*n:(i+1)*n], - i + seed * num_envs, - width=widths[i], - height=heights[i], - num_agents=num_agents[i], - vision=vision, - reward_food=reward_food, - interactive_food_reward=interactive_food_reward, - reward_move=reward_move, - food_base_spawn_rate=food_base_spawn_rate, - ) - c_envs.append(env_id) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=None): - self.tick = 0 - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - self.tick += 1 - - info = [] - if self.tick % self.report_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == "__main__": - env = PyCPR() - env.reset() - tick = 0 - timeout=30 - - tot_agents = env.num_agents - actions = np.random.randint(0,5,(1024,tot_agents)) - - import time - start = time.time() - # while time.time() - start < timeout: - while tick < 500: - atns = actions[tick % 1024] - env.step(atns) - if -1 in env.rewards: - breakpoint() - # env.render() - tick += 1 - - print(f'SPS: {int(tot_agents * tick / (time.time() - start)):_}') - - env.close() - - - - diff --git a/pufferlib/ocean/cpr/grid.h b/pufferlib/ocean/cpr/grid.h deleted file mode 100644 index 768299966..000000000 --- a/pufferlib/ocean/cpr/grid.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef GRID_H -#define GRID_H - -#define GRID_HEIGHT 32 -#define GRID_WIDTH 32 - -static const unsigned char grid_32_32_3v[GRID_HEIGHT][GRID_WIDTH] = { - {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}, - {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03} -}; - -#endif // GRID_H diff --git a/pufferlib/ocean/drive/README.md b/pufferlib/ocean/drive/README.md new file mode 100644 index 000000000..a37907b20 --- /dev/null +++ b/pufferlib/ocean/drive/README.md @@ -0,0 +1,108 @@ +# PufferDrive + +This readme contains several important assumptions and definions about the `PufferDrive` environment. + +## Agent initialization and control + +### `init_mode` + +Determines which agents are **created** in the environment. + +| Option | Description | +| ------------------------ | ---------------------------------------------------------------------------- | +| `create_all_valid` | Create all entities valid at initialization (`traj_valid[init_steps] == 1`). | +| `create_only_controlled` | Create only those agents that are controlled by the policy. | + +### `control_mode` + +Determines which created agents are **controlled** by the policy. + +| Option | Description | +| ----------------------------------------- | ------------------------------------------------------------------------------------------------- | +| `control_vehicles` (default) | Control only valid **vehicles** (not experts, beyond `MIN_DISTANCE_TO_GOAL`, under `MAX_AGENTS`). | +| `control_agents` | Control all valid **agent types** (vehicles, cyclists, pedestrians). | +| `control_tracks_to_predict` *(WOMD only)* | Control agents listed in the `tracks_to_predict` metadata. | + + +## Termination conditions (`done`) + +Episodes are never truncated before reaching `episode_len`. The `goal_behavior` argument controls agent behavior after reaching a goal early: + +* **`goal_behavior=0` (default):** Agents respawn at their initial position after reaching their goal (last valid log position). +* **`goal_behavior=1`:** Agents receive new goals indefinitely after reaching each goal. +* **`goal_behavior=2`:** Agents stop after reaching their goal. + +## Logged performance metrics + +We record multiple performance metrics during training, aggregated over all *active agents* (alive and controlled). Key metrics include: + +- `score`: Goals reached cleanly (goal was achieved without collision or going off-road) +- `collision_rate`: Binary flag (0 or 1) if agent hit another vehicle. +- `offroad_rate`: Binary flag (0 or 1) if agent left road bounds. +- `completion_rate`: Whether the agent reached its goal in this episode (even if it collided or went off-road). + + +### Metric aggregation + +The `num_agents` parameter in `drive.ini` defines the total number of agents used to collect experience. +At runtime, **Puffer** uses `num_maps` to create enough environments to populate the buffer with `num_agents`, distributing them evenly across `num_envs`. + +Because agents are respawned immediately after reaching their goal, they remain active throughout the episode. + +At the end of each episode (i.e., when `timestep == TRAJECTORY_LENGTH`), metrics are logged once via: + +```C +if (env->timestep == TRAJECTORY_LENGTH) { + add_log(env); + c_reset(env); + return; +} +``` + +Metrics are normalized and aggregated in `vec_log` (`pufferlib/ocean/env_binding.h`). They are averaged over all active agents across all environments. For example, the aggregated collision rate is computed as: + +$$ +r^{agg}_{\text{collision}} = \frac{\mathbb{I}[\text{collided in episode}]}{N} +$$ + +where $N$ is the number of controlled agents. +This value represents the fraction of agents that collided at least once during the episode. So, cases **A** and **B** below would yield identical off-road and collision rates: + +![alt text](../../resources/drive/examples_a_b.png) + +Since these metrics do not capture *multiple* events per agent, we additionally log the **average number of collision and off-road events per episode**. This is computed as: + +$$ +c^{avg}_{\text{collision}} = \frac{\text{total number of collision events across all agents and environments}}{N} +$$ + +where $N$ is the total number of controlled agents. +For example, an `avg_collisions_per_agent` value of 4 indicates that, on average, each agent collides four times per episode. + +### Effect of respawning on metrics + +By default, agents are reset to their initial position when they reach their goal before the episode ends. Upon respawn, `respawn_timestep` is updated from `-1` to the current step index. + +This raises the question: **how does repeated respawning affect aggregated metrics?** + +To begin, note that the environment is a bit different before and after respawn. After an agent respawns, all other agents are "removed" from the environment. As a result, collisions with other agents cannot occur post-respawn. + +This effectively transforms the scenario into a single-agent environment, simplifying the task since the agent no longer needs to coordinate with others. + +![alt text](../../resources/drive/pre_and_post_respawn.png) + +#### `score` + +Consider an episode of 91 steps where an agent is initialized relatively close to the goal position and reaches its goal three times: + +1. **First attempt:** reaches the goal without collisions +2. **Second attempt:** reaches the goal without collisions +3. **Third attempt:** reaches the goal but goes off-road along the way + +![alt text](../../resources/drive/realistic_collision_event_post_respawn.png) + +The highlighted trajectory shows the first attempt. In this case, the recorded score is `0.0` — a single off-road event invalidates the score for the entire episode. This behavior is desired: the score metric is unforgiving. + +#### `offroad_rate` and `collision_rate` + +Same logic holds as above. diff --git a/pufferlib/ocean/drive/binding.c b/pufferlib/ocean/drive/binding.c index 45fed372e..1edd05bc4 100644 --- a/pufferlib/ocean/drive/binding.c +++ b/pufferlib/ocean/drive/binding.c @@ -70,11 +70,16 @@ static int my_put(Env* env, PyObject* args, PyObject* kwargs) { static PyObject* my_shared(PyObject* self, PyObject* args, PyObject* kwargs) { int num_agents = unpack(kwargs, "num_agents"); int num_maps = unpack(kwargs, "num_maps"); + int init_mode = unpack(kwargs, "init_mode"); + int control_mode = unpack(kwargs, "control_mode"); + int init_steps = unpack(kwargs, "init_steps"); + int goal_behavior = unpack(kwargs, "goal_behavior"); clock_gettime(CLOCK_REALTIME, &ts); srand(ts.tv_nsec); int total_agent_count = 0; int env_count = 0; int max_envs = num_agents; + int maps_checked = 0; PyObject* agent_offsets = PyList_New(max_envs+1); PyObject* map_ids = PyList_New(max_envs); // getting env count @@ -82,9 +87,96 @@ static PyObject* my_shared(PyObject* self, PyObject* args, PyObject* kwargs) { char map_file[100]; int map_id = rand() % num_maps; Drive* env = calloc(1, sizeof(Drive)); + env->init_mode = init_mode; + env->control_mode = control_mode; + env->init_steps = init_steps; + env->goal_behavior = goal_behavior; sprintf(map_file, "resources/drive/binaries/map_%03d.bin", map_id); load_map_binary(map_file, env); + + // Skip map if it contains traffic lights + bool has_traffic_light = false; + for(int j=0; jnum_traffic_elements; j++) { + if(env->traffic_elements[j].type == TRAFFIC_LIGHT) { + has_traffic_light = true; + break; + } + } + if(has_traffic_light) { + maps_checked++; + + // Safeguard: if we've checked all available maps and all have traffic lights, raise an error + if(maps_checked >= num_maps) { + for(int j=0;jnum_total_agents;j++) free_agent(&env->agents[j]); + for (int j=0;jnum_road_elements;j++) free_road_element(&env->road_elements[j]); + for (int j=0;jnum_traffic_elements;j++) free_traffic_element(&env->traffic_elements[j]); + free(env->agents); + free(env->road_elements); + free(env->traffic_elements); + free(env->active_agent_indices); + free(env->static_agent_indices); + free(env->expert_static_agent_indices); + free(env); + Py_DECREF(agent_offsets); + Py_DECREF(map_ids); + char error_msg[256]; + sprintf(error_msg, "All %d available maps contain traffic lights which are not supported", num_maps); + PyErr_SetString(PyExc_ValueError, error_msg); + return NULL; + } + + for(int j=0;jnum_total_agents;j++) free_agent(&env->agents[j]); + for (int j=0;jnum_road_elements;j++) free_road_element(&env->road_elements[j]); + for (int j=0;jnum_traffic_elements;j++) free_traffic_element(&env->traffic_elements[j]); + free(env->agents); + free(env->road_elements); + free(env->traffic_elements); + free(env->active_agent_indices); + free(env->static_agent_indices); + free(env->expert_static_agent_indices); + free(env); + continue; + } + set_active_agents(env); + + // Skip map if it doesn't contain any controllable agents + if(env->active_agent_count == 0) { + maps_checked++; + + // Safeguard: if we've checked all available maps and found no active agents, raise an error + if(maps_checked >= num_maps) { + for(int j=0;jnum_total_agents;j++) free_agent(&env->agents[j]); + for (int j=0;jnum_road_elements;j++) free_road_element(&env->road_elements[j]); + for (int j=0;jnum_traffic_elements;j++) free_traffic_element(&env->traffic_elements[j]); + free(env->agents); + free(env->road_elements); + free(env->traffic_elements); + free(env->active_agent_indices); + free(env->static_agent_indices); + free(env->expert_static_agent_indices); + free(env); + Py_DECREF(agent_offsets); + Py_DECREF(map_ids); + char error_msg[256]; + sprintf(error_msg, "No controllable agents found in any of the %d available maps", num_maps); + PyErr_SetString(PyExc_ValueError, error_msg); + return NULL; + } + + for(int j=0;jnum_total_agents;j++) free_agent(&env->agents[j]); + for (int j=0;jnum_road_elements;j++) free_road_element(&env->road_elements[j]); + for (int j=0;jnum_traffic_elements;j++) free_traffic_element(&env->traffic_elements[j]); + free(env->agents); + free(env->road_elements); + free(env->traffic_elements); + free(env->active_agent_indices); + free(env->static_agent_indices); + free(env->expert_static_agent_indices); + free(env); + continue; + } + // Store map_id PyObject* map_id_obj = PyLong_FromLong(map_id); PyList_SetItem(map_ids, env_count, map_id_obj); @@ -93,19 +185,18 @@ static PyObject* my_shared(PyObject* self, PyObject* args, PyObject* kwargs) { PyList_SetItem(agent_offsets, env_count, offset); total_agent_count += env->active_agent_count; env_count++; - for(int i = 0; i < env->num_objects; i++){ - free_entity(&env->entities[i]); - } - free(env->entities); - for(int i = 0; i < env->num_roads; i++){ - free_map_entity(&env->map_entities[i]); - } - free(env->map_entities); + for(int j=0;jnum_total_agents;j++) free_agent(&env->agents[j]); + for (int j=0;jnum_road_elements;j++) free_road_element(&env->road_elements[j]); + for (int j=0;jnum_traffic_elements;j++) free_traffic_element(&env->traffic_elements[j]); + free(env->agents); + free(env->road_elements); + free(env->traffic_elements); free(env->active_agent_indices); - free(env->static_car_indices); - free(env->expert_static_car_indices); + free(env->static_agent_indices); + free(env->expert_static_agent_indices); free(env); } + //printf("Generated %d environments to cover %d agents (requested %d agents)\n", env_count, total_agent_count, num_agents); if(total_agent_count >= num_agents){ total_agent_count = num_agents; } @@ -115,59 +206,68 @@ static PyObject* my_shared(PyObject* self, PyObject* args, PyObject* kwargs) { // resize lists PyObject* resized_agent_offsets = PyList_GetSlice(agent_offsets, 0, env_count + 1); PyObject* resized_map_ids = PyList_GetSlice(map_ids, 0, env_count); - // - //Py_DECREF(agent_offsets); - //Py_DECREF(map_ids); - // create a tuple PyObject* tuple = PyTuple_New(3); PyTuple_SetItem(tuple, 0, resized_agent_offsets); PyTuple_SetItem(tuple, 1, resized_map_ids); PyTuple_SetItem(tuple, 2, final_env_count); return tuple; - - //Py_DECREF(num); - /* - for(int i = 0;ihuman_agent_idx = unpack(kwargs, "human_agent_idx"); - env->reward_vehicle_collision = unpack(kwargs, "reward_vehicle_collision"); - env->reward_offroad_collision = unpack(kwargs, "reward_offroad_collision"); - env->reward_goal_post_respawn = unpack(kwargs, "reward_goal_post_respawn"); - env->reward_vehicle_collision_post_respawn = unpack(kwargs, "reward_vehicle_collision_post_respawn"); - env->spawn_immunity_timer = unpack(kwargs, "spawn_immunity_timer"); + env->ini_file = unpack_str(kwargs, "ini_file"); + env_init_config conf = {0}; + if(ini_parse(env->ini_file, handler, &conf) < 0) { + printf("Error while loading %s", env->ini_file); + } + if (kwargs && PyDict_GetItemString(kwargs, "scenario_length")) { + conf.scenario_length = (int)unpack(kwargs, "scenario_length"); + } + if (conf.scenario_length <= 0) { + PyErr_SetString(PyExc_ValueError, "scenario_length must be > 0 (set in INI or kwargs)"); + return -1; + } + env->action_type = conf.action_type; + env->dynamics_model = conf.dynamics_model; + env->reward_vehicle_collision = conf.reward_vehicle_collision; + env->reward_offroad_collision = conf.reward_offroad_collision; + env->reward_goal = conf.reward_goal; + env->reward_goal_post_respawn = conf.reward_goal_post_respawn; + env->reward_ade = conf.reward_ade; + env->scenario_length = conf.scenario_length; + env->collision_behavior = conf.collision_behavior; + env->offroad_behavior = conf.offroad_behavior; + env->max_controlled_agents = unpack(kwargs, "max_controlled_agents"); + env->dt = conf.dt; + env->init_mode = (int)unpack(kwargs, "init_mode"); + env->control_mode = (int)unpack(kwargs, "control_mode"); + env->goal_behavior = (int)unpack(kwargs, "goal_behavior"); + env->goal_radius = (float)unpack(kwargs, "goal_radius"); int map_id = unpack(kwargs, "map_id"); int max_agents = unpack(kwargs, "max_agents"); - + int init_steps = unpack(kwargs, "init_steps"); char map_file[100]; sprintf(map_file, "resources/drive/binaries/map_%03d.bin", map_id); - env->num_agents = max_agents; + env->num_max_agents = max_agents; env->map_name = strdup(map_file); + env->init_steps = init_steps; + env->timestep = init_steps; init(env); return 0; } static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); + assign_to_dict(dict, "n", log->n); assign_to_dict(dict, "offroad_rate", log->offroad_rate); + assign_to_dict(dict, "episode_length", log->episode_length); assign_to_dict(dict, "collision_rate", log->collision_rate); + assign_to_dict(dict, "episode_return", log->episode_return); assign_to_dict(dict, "dnf_rate", log->dnf_rate); - assign_to_dict(dict, "n", log->n); + assign_to_dict(dict, "avg_displacement_error", log->avg_displacement_error); assign_to_dict(dict, "completion_rate", log->completion_rate); - assign_to_dict(dict, "clean_collision_rate", log->clean_collision_rate); + assign_to_dict(dict, "lane_alignment_rate", log->lane_alignment_rate); + assign_to_dict(dict, "score", log->score); + assign_to_dict(dict, "avg_offroad_per_agent", log->avg_offroad_per_agent); + assign_to_dict(dict, "avg_collisions_per_agent", log->avg_collisions_per_agent); return 0; } diff --git a/pufferlib/ocean/drive/datatypes.h b/pufferlib/ocean/drive/datatypes.h new file mode 100644 index 000000000..ff9b37e5d --- /dev/null +++ b/pufferlib/ocean/drive/datatypes.h @@ -0,0 +1,212 @@ +#define UNKNOWN 0 + +// -- AGENT TYPE +#define VEHICLE 1 +#define PEDESTRIAN 2 +#define CYCLIST 3 +#define OTHER 4 + +// -- ROAD TYPE +#define LANE_FREEWAY 1 +#define LANE_SURFACE_STREET 2 +#define LANE_BIKE_LANE 3 + +#define ROAD_LINE_UNKNOWN 10 +#define ROAD_LINE_BROKEN_SINGLE_WHITE 11 +#define ROAD_LINE_SOLID_SINGLE_WHITE 12 +#define ROAD_LINE_SOLID_DOUBLE_WHITE 13 +#define ROAD_LINE_BROKEN_SINGLE_YELLOW 14 +#define ROAD_LINE_BROKEN_DOUBLE_YELLOW 15 +#define ROAD_LINE_SOLID_SINGLE_YELLOW 16 +#define ROAD_LINE_SOLID_DOUBLE_YELLOW 17 +#define ROAD_LINE_PASSING_DOUBLE_YELLOW 18 + +#define ROAD_EDGE_UNKNOWN 20 +#define ROAD_EDGE_BOUNDARY 21 +#define ROAD_EDGE_MEDIAN 22 +#define ROAD_EDGE_SIDEWALK 23 + +#define CROSSWALK 31 +#define SPEED_BUMP 32 +#define DRIVEWAY 33 + +// -- TRAFFIC CONTROL TYPE +#define TRAFFIC_LIGHT 1 +#define STOP_SIGN 2 +#define YIELD_SIGN 3 +#define SPEED_LIMIT_SIGN 4 + + +int is_road_lane(int type){ + return (type >= 0 && type <= 9); +} + +int is_drivable_road_lane(int type){ + return (type == LANE_FREEWAY || type == LANE_SURFACE_STREET); +} + +int is_road_line(int type){ + return (type >= 10 && type <= 19); +} + +int is_road_edge(int type){ + return (type >= 20 && type <= 29); +} + +int is_road(int type){ + return is_road_lane(type) || is_road_line(type) || is_road_edge(type); +} + +int is_controllable_agent(int type){ + return (type == VEHICLE || type == PEDESTRIAN || type == CYCLIST); +} + +int normalize_road_type(int type){ + if(is_road_lane(type)){ + return 0; + } else if(is_road_line(type)){ + return 1; + } else if(is_road_edge(type)){ + return 2; + } else { + return -1; + } +} + +int unnormalize_road_type(int norm_type){ + if(norm_type == 0){ + return LANE_SURFACE_STREET; + } else if(norm_type == 1){ + return ROAD_LINE_BROKEN_SINGLE_WHITE; + } else if(norm_type == 2){ + return ROAD_EDGE_BOUNDARY; + } else { + return -1; // Invalid + } +} + + + +struct Agent { + int id; + int type; + + // Log trajectory + int trajectory_length; + float* log_trajectory_x; + float* log_trajectory_y; + float* log_trajectory_z; + float* log_heading; + float* log_velocity_x; + float* log_velocity_y; + float* log_length; + float* log_width; + float* log_height; + int* log_valid; + + // Simulation state + float sim_x; + float sim_y; + float sim_z; + float sim_heading; + float sim_vx; + float sim_vy; + float sim_length; + float sim_width; + float sim_height; + int sim_valid; + + // Route information + int route_length; + int* route; + + // Metrics and status tracking + int collision_state; + float metrics_array[5]; // [collision, offroad, reached_goal, lane_aligned, avg_displacement_error] + int current_lane_idx; + int collided_before_goal; + int sampled_new_goal; + int reached_goal_this_episode; + int num_goals_reached; + int active_agent; + int mark_as_expert; + float cumulative_displacement; + int displacement_sample_count; + + // Goal positions + float goal_position_x; + float goal_position_y; + float goal_position_z; + float init_goal_x; // Initialized from goal_position + float init_goal_y; // Initialized from goal_position + + // Respawn tracking + int respawn_timestep; + int respawn_count; + + int stopped; // 0/1 -> freeze if set + int removed; //0/1 -> remove from sim if set + + // Jerk dynamics + float a_long; + float a_lat; + float jerk_long; + float jerk_lat; + float steering_angle; + float wheelbase; +}; + +struct RoadMapElement { + int id; + int type; + + int segment_length; + float* x; + float* y; + float* z; + + // Lane specific info + int num_entries; + int* entry_lanes; + int num_exits; + int* exit_lanes; + float speed_limit; +}; + +struct TrafficControlElement { + int id; + int type; + + int state_length; + int* states; + float x; + float y; + float z; + int controlled_lane; +}; + +void free_agent(struct Agent* agent){ + free(agent->log_trajectory_x); + free(agent->log_trajectory_y); + free(agent->log_trajectory_z); + free(agent->log_heading); + free(agent->log_velocity_x); + free(agent->log_velocity_y); + free(agent->log_length); + free(agent->log_width); + free(agent->log_height); + free(agent->log_valid); + free(agent->route); +} + +void free_road_element(struct RoadMapElement* element){ + free(element->x); + free(element->y); + free(element->z); + free(element->entry_lanes); + free(element->exit_lanes); +} + +void free_traffic_element(struct TrafficControlElement* element){ + free(element->states); +} diff --git a/pufferlib/ocean/drive/docs/imgs/available_launch_configs.png b/pufferlib/ocean/drive/docs/imgs/available_launch_configs.png new file mode 100644 index 000000000..632dba19d Binary files /dev/null and b/pufferlib/ocean/drive/docs/imgs/available_launch_configs.png differ diff --git a/pufferlib/ocean/drive/docs/imgs/breakpoint_list.png b/pufferlib/ocean/drive/docs/imgs/breakpoint_list.png new file mode 100644 index 000000000..fb31b3190 Binary files /dev/null and b/pufferlib/ocean/drive/docs/imgs/breakpoint_list.png differ diff --git a/pufferlib/ocean/drive/docs/imgs/debugger_output.png b/pufferlib/ocean/drive/docs/imgs/debugger_output.png new file mode 100644 index 000000000..67802c621 Binary files /dev/null and b/pufferlib/ocean/drive/docs/imgs/debugger_output.png differ diff --git a/pufferlib/ocean/drive/docs/imgs/find_the_debugger_interface.png b/pufferlib/ocean/drive/docs/imgs/find_the_debugger_interface.png new file mode 100644 index 000000000..313722ffb Binary files /dev/null and b/pufferlib/ocean/drive/docs/imgs/find_the_debugger_interface.png differ diff --git a/pufferlib/ocean/drive/docs/setup_your_debugger.md b/pufferlib/ocean/drive/docs/setup_your_debugger.md new file mode 100644 index 000000000..f465099e1 --- /dev/null +++ b/pufferlib/ocean/drive/docs/setup_your_debugger.md @@ -0,0 +1,149 @@ +# Setting Up a C/Python Debugger for PufferAI + +Debuggers are extremely useful tools. They let you step through code +instruction by instruction and observe how variables change over time. +This makes it much easier to understand confusing parts of the codebase +and avoids relying solely on your internal "world model" of what the +code *should* do. Debuggers also eliminate the need for excessive +`print` statements, which are tedious to manage and clean up afterward. + +If you want a general introduction to debuggers, I recommend watching a +beginner-friendly [vscode debugger tutorial video](https://www.youtube.com/watch?v=3HiLLByBWkg) on YouTube. + +> **Disclaimer:**\ +> This guide was written by a beginner who tried this setup and +> succeeded. If you know better methods or improvements, please feel +> free to contribute! + +------------------------------------------------------------------------ + +## Overview + +Setting up a Python debugger is straightforward, and plenty of guides +exist online.\ +However, debugging a Python application that spawns **multiple C/C++ +threads**, such as PufferAI environments, requires a bit more care. + +This guide is for **Unix-based systems (e.g., Ubuntu)** using +**VSCode**, which seems to be the default in the lab. + +At a high level, our goal is to: + +1. Install the tools needed to trace C/C++ code. +2. Set up VSCode to launch Python under `gdb`. +3. Tell VSCode which Python module to run. +4. Configure `gdb` to follow the child C++ processes spawned by Python. + +------------------------------------------------------------------------ + +## 1. Install `gdb` + +We'll start by installing the GNU Debugger: + +``` bash +sudo apt update && sudo apt install -y gdb +which gdb # expect: /usr/bin/gdb +gdb --version +``` + +------------------------------------------------------------------------ + +## 2. Configure VSCode + +Inside your **PufferDrive** directory, create a `.vscode` folder: + +``` bash +mkdir PufferDrive/.vscode +``` + +Now create a file called **`launch.json`** with the following content: + +``` jsonc +{ + "configurations": [ + // --- Python debugger only --- + { + "name": "Python: Debug puffer train puffer_drive", + "type": "python", + "request": "launch", + "module": "pufferlib.pufferl", + "args": ["train", "puffer_drive"], + "cwd": "${workspaceFolder}", + "python": "/home/ricky/git/PufferDrive/.venv/bin/python", // <- TODO: point to your python interpreter + "justMyCode": false, + "env": { + "PYTHONPATH": "${workspaceFolder}" + } + }, + + // --- Python running under gdb (C/C++ breakpoints work) --- + { + "name": "C (gdb): Debug C inside puffer_drive", + "type": "cppdbg", + "request": "launch", + "program": "/home/ricky/git/PufferDrive/.venv/bin/python", // <- TODO: point to your python interpreter + "args": ["-m", "pufferlib.pufferl", "train", "puffer_drive"], + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + + "miDebuggerPath": "/usr/bin/gdb", // <- TODO: point to your gdb path (see previous step to check the path) + + "externalConsole": false, + "stopAtEntry": false, + "env": { "PYTHONPATH": "${workspaceFolder}" }, + + "setupCommands": [ + { "text": "set breakpoint pending on" }, + { "text": "set follow-fork-mode child" } + ] + } + ] +} +``` + +**Notes** + +- The first configuration is the standard Python debugger. +- The second runs Python **inside `gdb`**, enabling C/C++ + breakpoints. +- The `"follow-fork-mode child"` line is crucial---PufferAI + environments spawn child processes that must be traced. + +------------------------------------------------------------------------ + +## 3. Using the Debugger + +### Setting Breakpoints + +Open the C/C++ file you want to inspect in VSCode.\ +Click to the left of any line to place a breakpoint (a red dot).\ +VSCode will pause execution when that line is reached. + +### Opening the Debug Panel + +Open the debugger sidebar in VSCode: + +![](imgs/find_the_debugger_interface.png) + +### Selecting the Debugger Configuration + +Use the dropdown to select: **"C (gdb): Debug C inside puffer_drive"** + +![](imgs/available_launch_configs.png) + +At the lower left, you should now see your list of breakpoints.\ +You can check/uncheck each one to enable or disable it: + +![](imgs/breakpoint_list.png) + +### Example Debugger Output + +Here is an example of what a debug session may look like: + +![](imgs/debugger_output.png) + +- The yellow arrow around the red dot on the left of the code editor highlights the point at which the code paused. +- On the top left you have a "Variables" tab that allows you to explore all existing variables and their values. +- Underneath, the "Watch" section lets you write simple code expression that manipulate the existing variables and print the output. + +For more information on how to then move forward to the next breakpoint, step only the next code instruction etc. I refer you to the [vscode debugger tutorial video](https://www.youtube.com/watch?v=3HiLLByBWkg). diff --git a/pufferlib/ocean/drive/drive.c b/pufferlib/ocean/drive/drive.c index f2537d265..4b2645836 100644 --- a/pufferlib/ocean/drive/drive.c +++ b/pufferlib/ocean/drive/drive.c @@ -1,244 +1,65 @@ -#include -#include #include "drive.h" -#include "puffernet.h" +#include "drivenet.h" +#include +#include "../env_config.h" -typedef struct DriveNet DriveNet; -struct DriveNet { - int num_agents; - float* obs_self; - float* obs_partner; - float* obs_road; - float* partner_linear_output; - float* road_linear_output; - float* partner_layernorm_output; - float* road_layernorm_output; - float* partner_linear_output_two; - float* road_linear_output_two; - Linear* ego_encoder; - Linear* road_encoder; - Linear* partner_encoder; - LayerNorm* ego_layernorm; - LayerNorm* road_layernorm; - LayerNorm* partner_layernorm; - Linear* ego_encoder_two; - Linear* road_encoder_two; - Linear* partner_encoder_two; - MaxDim1* partner_max; - MaxDim1* road_max; - CatDim1* cat1; - CatDim1* cat2; - GELU* gelu; - Linear* shared_embedding; - ReLU* relu; - LSTM* lstm; - Linear* actor; - Linear* value_fn; - Multidiscrete* multidiscrete; -}; +// Use this test if the network changes to ensure that the forward pass +// matches the torch implementation to the 3rd or ideally 4th decimal place +void test_drivenet() { + int num_obs = 1848; + int num_actions = 2; + int num_agents = 4; -DriveNet* init_drivenet(Weights* weights, int num_agents) { - DriveNet* net = calloc(1, sizeof(DriveNet)); - int hidden_size = 256; - int input_size = 64; - - net->num_agents = num_agents; - net->obs_self = calloc(num_agents*7, sizeof(float)); // 7 features - net->obs_partner = calloc(num_agents*63*7, sizeof(float)); // 63 objects, 7 features - net->obs_road = calloc(num_agents*200*13, sizeof(float)); // 200 objects, 13 features - net->partner_linear_output = calloc(num_agents*63*input_size, sizeof(float)); - net->road_linear_output = calloc(num_agents*200*input_size, sizeof(float)); - net->partner_linear_output_two = calloc(num_agents*63*input_size, sizeof(float)); - net->road_linear_output_two = calloc(num_agents*200*input_size, sizeof(float)); - net->partner_layernorm_output = calloc(num_agents*63*input_size, sizeof(float)); - net->road_layernorm_output = calloc(num_agents*200*input_size, sizeof(float)); - net->ego_encoder = make_linear(weights, num_agents, 7, input_size); - net->ego_layernorm = make_layernorm(weights, num_agents, input_size); - net->ego_encoder_two = make_linear(weights, num_agents, input_size, input_size); - net->road_encoder = make_linear(weights, num_agents, 13, input_size); - net->road_layernorm = make_layernorm(weights, num_agents, input_size); - net->road_encoder_two = make_linear(weights, num_agents, input_size, input_size); - net->partner_encoder = make_linear(weights, num_agents, 7, input_size); - net->partner_layernorm = make_layernorm(weights, num_agents, input_size); - net->partner_encoder_two = make_linear(weights, num_agents, input_size, input_size); - net->partner_max = make_max_dim1(num_agents, 63, input_size); - net->road_max = make_max_dim1(num_agents, 200, input_size); - net->cat1 = make_cat_dim1(num_agents, input_size, input_size); - net->cat2 = make_cat_dim1(num_agents, input_size + input_size, input_size); - net->gelu = make_gelu(num_agents, 3*input_size); - net->shared_embedding = make_linear(weights, num_agents, input_size*3, hidden_size); - net->relu = make_relu(num_agents, hidden_size); - net->actor = make_linear(weights, num_agents, hidden_size, 20); - net->value_fn = make_linear(weights, num_agents, hidden_size, 1); - net->lstm = make_lstm(weights, num_agents, hidden_size, 256); - memset(net->lstm->state_h, 0, num_agents*256*sizeof(float)); - memset(net->lstm->state_c, 0, num_agents*256*sizeof(float)); - int logit_sizes[2] = {7, 13}; - net->multidiscrete = make_multidiscrete(num_agents, logit_sizes, 2); - return net; -} - -void free_drivenet(DriveNet* net) { - free(net->obs_self); - free(net->obs_partner); - free(net->obs_road); - free(net->partner_linear_output); - free(net->road_linear_output); - free(net->partner_linear_output_two); - free(net->road_linear_output_two); - free(net->ego_encoder); - free(net->road_encoder); - free(net->partner_encoder); - free(net->ego_layernorm); - free(net->road_layernorm); - free(net->partner_layernorm); - free(net->ego_encoder_two); - free(net->road_encoder_two); - free(net->partner_encoder_two); - free(net->partner_max); - free(net->road_max); - free(net->cat1); - free(net->cat2); - free(net->gelu); - free(net->shared_embedding); - free(net->relu); - free(net->multidiscrete); - free(net->actor); - free(net->value_fn); - free(net->lstm); - free(net); -} - -void forward(DriveNet* net, float* observations, int* actions) { - // Clear previous observations - memset(net->obs_self, 0, net->num_agents * 7 * sizeof(float)); - memset(net->obs_partner, 0, net->num_agents * 63 * 7 * sizeof(float)); - memset(net->obs_road, 0, net->num_agents * 200 * 13 * sizeof(float)); - - // Reshape observations into 2D boards and additional features - float (*obs_self)[7] = (float (*)[7])net->obs_self; - float (*obs_partner)[63][7] = (float (*)[63][7])net->obs_partner; - float (*obs_road)[200][13] = (float (*)[200][13])net->obs_road; - - for (int b = 0; b < net->num_agents; b++) { - int b_offset = b * (7 + 63*7 + 200*7); // offset for each batch - int partner_offset = b_offset + 7; - int road_offset = b_offset + 7 + 63*7; - // Process self observation - for(int i = 0; i < 7; i++) { - obs_self[b][i] = observations[b_offset + i]; - } - - // Process partner observation - for(int i = 0; i < 63; i++) { - for(int j = 0; j < 7; j++) { - obs_partner[b][i][j] = observations[partner_offset + i*7 + j]; - } - } - - // Process road observation - for(int i = 0; i < 200; i++) { - for(int j = 0; j < 7; j++) { - obs_road[b][i][j] = observations[road_offset + i*7 + j]; - } - for(int j = 0; j < 7; j++) { - if(j == observations[road_offset+i*7 + 6]) { - obs_road[b][i][6 + j] = 1.0f; - } else { - obs_road[b][i][6 + j] = 0.0f; - } - } - } + float* observations = calloc(num_agents*num_obs, sizeof(float)); + for (int i = 0; i < num_obs*num_agents; i++) { + observations[i] = i % 7; } - // Forward pass through the network - linear(net->ego_encoder, net->obs_self); - layernorm(net->ego_layernorm, net->ego_encoder->output); - linear(net->ego_encoder_two, net->ego_layernorm->output); - for (int b = 0; b < net->num_agents; b++) { - for (int obj = 0; obj < 63; obj++) { - // Get the 7 features for this object - float* obj_features = &net->obs_partner[b*63*7 + obj*7]; - // Apply linear layer to this object - _linear(obj_features, net->partner_encoder->weights, net->partner_encoder->bias, - &net->partner_linear_output[b*63*64 + obj*64], 1, 7, 64); - } - } + int* actions = calloc(num_agents*num_actions, sizeof(int)); - for (int b = 0; b < net->num_agents; b++) { - for (int obj = 0; obj < 63; obj++) { - float* after_first = &net->partner_linear_output[b*63*64 + obj*64]; - _layernorm(after_first, net->partner_layernorm->weights, net->partner_layernorm->bias, - &net->partner_layernorm_output[b*63*64 + obj*64], 1, 64); - } - } - for (int b = 0; b < net->num_agents; b++) { - for (int obj = 0; obj < 63; obj++) { - // Get the 7 features for this object - float* obj_features = &net->partner_layernorm_output[b*63*64 + obj*64]; - // Apply linear layer to this object - _linear(obj_features, net->partner_encoder_two->weights, net->partner_encoder_two->bias, - &net->partner_linear_output_two[b*63*64 + obj*64], 1, 64, 64); - - } - } - - // Process road objects: apply linear to each object individually - for (int b = 0; b < net->num_agents; b++) { - for (int obj = 0; obj < 200; obj++) { - // Get the 13 features for this object - float* obj_features = &net->obs_road[b*200*13 + obj*13]; - // Apply linear layer to this object - _linear(obj_features, net->road_encoder->weights, net->road_encoder->bias, - &net->road_linear_output[b*200*64 + obj*64], 1, 13, 64); - } - } - - // Apply layer norm and second linear to each road object - for (int b = 0; b < net->num_agents; b++) { - for (int obj = 0; obj < 200; obj++) { - float* after_first = &net->road_linear_output[b*200*64 + obj*64]; - _layernorm(after_first, net->road_layernorm->weights, net->road_layernorm->bias, - &net->road_layernorm_output[b*200*64 + obj*64], 1, 64); - } - } - for (int b = 0; b < net->num_agents; b++) { - for (int obj = 0; obj < 200; obj++) { - float* after_first = &net->road_layernorm_output[b*200*64 + obj*64]; - _linear(after_first, net->road_encoder_two->weights, net->road_encoder_two->bias, - &net->road_linear_output_two[b*200*64 + obj*64], 1, 64, 64); + //Weights* weights = load_weights("resources/drive/puffer_drive_weights.bin"); + Weights* weights = load_weights("puffer_drive_weights.bin"); + DriveNet* net = init_drivenet(weights, num_agents); + + forward(net, observations, actions); + for (int i = 0; i < num_agents*num_actions; i++) { + printf("idx: %d, action: %d, logits:", i, actions[i]); + for (int j = 0; j < num_actions; j++) { + printf(" %.6f", net->actor->output[i*num_actions + j]); } + printf("\n"); } - - max_dim1(net->partner_max, net->partner_linear_output_two); - max_dim1(net->road_max, net->road_linear_output_two); - cat_dim1(net->cat1, net->ego_encoder_two->output, net->road_max->output); - cat_dim1(net->cat2, net->cat1->output, net->partner_max->output); - gelu(net->gelu, net->cat2->output); - linear(net->shared_embedding, net->gelu->output); - relu(net->relu, net->shared_embedding->output); - lstm(net->lstm, net->relu->output); - linear(net->actor, net->lstm->state_h); - linear(net->value_fn, net->lstm->state_h); - - // Get action by taking argmax of actor output - softmax_multidiscrete(net->multidiscrete, net->actor->output, actions); + free_drivenet(net); + free(weights); } + void demo() { + // Read configuration from INI file + env_init_config conf = {0}; + const char* ini_file = "pufferlib/config/ocean/drive.ini"; + if(ini_parse(ini_file, handler, &conf) < 0) { + fprintf(stderr, "Error: Could not load %s. Cannot determine environment configuration.\n", ini_file); + exit(1); + } Drive env = { - .dynamics_model = CLASSIC, .human_agent_idx = 0, - .reward_vehicle_collision = -0.1f, - .reward_offroad_collision = -0.1f, - .map_name = "resources/drive/binaries/map_942.bin", - .spawn_immunity_timer = 50 + .dynamics_model = conf.dynamics_model, + .reward_vehicle_collision = conf.reward_vehicle_collision, + .reward_offroad_collision = conf.reward_offroad_collision, + .reward_ade = conf.reward_ade, + .goal_radius = conf.goal_radius, + .dt = conf.dt, + .map_name = "resources/drive/binaries/map_000.bin", + .init_steps = conf.init_steps, + .collision_behavior = conf.collision_behavior, + .offroad_behavior = conf.offroad_behavior, }; allocate(&env); c_reset(&env); c_render(&env); - Weights* weights = load_weights("resources/drive/puffer_drive_weights.bin", 595925); - DriveNet* net = init_drivenet(weights, env.active_agent_count); + Weights* weights = load_weights("resources/drive/puffer_drive_weights.bin"); + DriveNet* net = init_drivenet(weights, env.active_agent_count, env.dynamics_model); //Client* client = make_client(&env); int accel_delta = 2; int steer_delta = 4; @@ -276,7 +97,7 @@ void demo() { if(actions[env.human_agent_idx][1] > 12) { actions[env.human_agent_idx][1] = 12; } - } + } if(IsKeyPressed(KEY_TAB)){ env.human_agent_idx = (env.human_agent_idx + 1) % env.active_agent_count; } @@ -292,11 +113,25 @@ void demo() { } void performance_test() { + // Read configuration from INI file + env_init_config conf = {0}; + const char* ini_file = "pufferlib/config/ocean/drive.ini"; + if(ini_parse(ini_file, handler, &conf) < 0) { + fprintf(stderr, "Error: Could not load %s. Cannot determine environment configuration.\n", ini_file); + exit(1); + } + long test_time = 10; Drive env = { - .dynamics_model = CLASSIC, .human_agent_idx = 0, - .map_name = "resources/drive/binaries/map_942.bin" + .dynamics_model = conf.dynamics_model, + .reward_vehicle_collision = conf.reward_vehicle_collision, + .reward_offroad_collision = conf.reward_offroad_collision, + .reward_ade = conf.reward_ade, + .goal_radius = conf.goal_radius, + .dt = conf.dt, + .map_name = "resources/drive/binaries/map_000.bin", + .init_steps = conf.init_steps, }; clock_t start_time, end_time; double cpu_time_used; @@ -310,7 +145,7 @@ void performance_test() { long start = time(NULL); int i = 0; int (*actions)[2] = (int(*)[2])env.actions; - + while (time(NULL) - start < test_time) { // Set random actions for all agents for(int j = 0; j < env.active_agent_count; j++) { @@ -319,7 +154,7 @@ void performance_test() { actions[j][0] = accel; // -1, 0, or 1 actions[j][1] = steer; // Random steering } - + c_step(&env); i++; } @@ -329,7 +164,8 @@ void performance_test() { } int main() { + //performance_test(); demo(); - // performance_test(); + //test_drivenet(); return 0; } diff --git a/pufferlib/ocean/drive/drive.h b/pufferlib/ocean/drive/drive.h index 9001d063a..d88b2803e 100644 --- a/pufferlib/ocean/drive/drive.h +++ b/pufferlib/ocean/drive/drive.h @@ -10,33 +10,55 @@ #include "raymath.h" #include "rlgl.h" #include +#include "error.h" +#include "datatypes.h" +// GridMapEntity types +#define ENTITY_TYPE_DYNAMIC_AGENT 1 +#define ENTITY_TYPE_ROAD_ELEMENT 2 +#define ENTITY_TYPE_TRAFFIC_CONTROL 3 -// Trajectory Length -#define TRAJECTORY_LENGTH 91 +#define INVALID_POSITION -10000.0f + +// Initialization modes +#define INIT_ALL_VALID 0 +#define INIT_ONLY_CONTROLLABLE_AGENTS 1 + +// Control modes +#define CONTROL_VEHICLES 0 +#define CONTROL_AGENTS 1 +#define CONTROL_TRACKS_TO_PREDICT 2 +#define CONTROL_SDC_ONLY 3 + +// Minimum distance to goal position +#define MIN_DISTANCE_TO_GOAL 2.0f // Actions #define NOOP 0 // Dynamics Models #define CLASSIC 0 -#define INVERTIBLE_BICYLE 1 -#define DELTA_LOCAL 2 -#define STATE_DYNAMICS 3 +#define JERK 1 -// collision state +// Collision state #define NO_COLLISION 0 #define VEHICLE_COLLISION 1 #define OFFROAD 2 -// grid cell size +// Metrics array indices +#define COLLISION_IDX 0 +#define OFFROAD_IDX 1 +#define REACHED_GOAL_IDX 2 +#define LANE_ALIGNED_IDX 3 +#define AVG_DISPLACEMENT_ERROR_IDX 4 + +// Grid cell size #define GRID_CELL_SIZE 5.0f -#define MAX_ENTITIES_PER_CELL 10 -#define SLOTS_PER_CELL (MAX_ENTITIES_PER_CELL*2 + 1) +#define MAX_ENTITIES_PER_CELL 30 // Depends on resolution of data Formula: 3 * (2 + GRID_CELL_SIZE*sqrt(2)/resolution) => For each entity type in gridmap, diagonal poly-lines -> sqrt(2), include diagonal ends -> 2 // Max road segment observation entities #define MAX_ROAD_SEGMENT_OBSERVATIONS 200 -#define MAX_CARS 64 +#define MAX_AGENTS 64 // Observation Space Constants #define MAX_SPEED 100.0f #define MAX_VEH_LEN 30.0f @@ -51,12 +73,22 @@ #define MAX_RG_COORD 1000.0f #define MAX_ROAD_SCALE 100.0f #define MAX_ROAD_SEGMENT_LENGTH 100.0f +#define STOP_AGENT 1 +#define REMOVE_AGENT 2 -// Acceleration Values -static const float ACCELERATION_VALUES[7] = {-4.0000f, -2.6670f, -1.3330f, -0.0000f, 1.3330f, 2.6670f, 4.0000f}; -// static const float STEERING_VALUES[13] = {-3.1420f, -2.6180f, -2.0940f, -1.5710f, -1.0470f, -0.5240f, 0.0000f, 0.5240f, -// 1.0470f, 1.5710f, 2.0940f, 2.6180f, 3.1420f}; +//GOAL BEHAVIOUR +#define GOAL_RESPAWN 0 +#define GOAL_GENERATE_NEW 1 +#define GOAL_STOP 2 + +// Jerk action space (for JERK dynamics model) +static const float JERK_LONG[4] = {-15.0f, -4.0f, 0.0f, 4.0f}; +static const float JERK_LAT[3] = {-4.0f, 0.0f, 4.0f}; + +// Classic action space (for CLASSIC dynamics model) +static const float ACCELERATION_VALUES[7] = {-4.0000f, -2.6670f, -1.3330f, -0.0000f, 1.3330f, 2.6670f, 4.0000f}; static const float STEERING_VALUES[13] = {-1.000f, -0.833f, -0.667f, -0.500f, -0.333f, -0.167f, 0.000f, 0.167f, 0.333f, 0.500f, 0.667f, 0.833f, 1.000f}; + static const float offsets[4][2] = { {-1, 1}, // top-left {1, 1}, // top-right @@ -77,104 +109,32 @@ struct timespec ts; typedef struct Drive Drive; typedef struct Client Client; typedef struct Log Log; +typedef struct Graph Graph; +typedef struct AdjListNode AdjListNode; +typedef struct Agent Agent; +typedef struct RoadMapElement RoadMapElement; +typedef struct TrafficControlElement TrafficControlElement; struct Log { float episode_return; float episode_length; - float perf; float score; float offroad_rate; float collision_rate; - float clean_collision_rate; + float num_goals_reached; float completion_rate; float dnf_rate; float n; + float lane_alignment_rate; + float avg_displacement_error; + float active_agent_count; + float expert_static_agent_count; + float static_agent_count; + float avg_offroad_per_agent; + float avg_collisions_per_agent; }; -typedef struct Entity Entity; -struct Entity { - int type; - int array_size; - float* traj_x; - float* traj_y; - float* traj_z; - float* traj_vx; - float* traj_vy; - float* traj_vz; - float* traj_heading; - int* traj_valid; - float width; - float length; - float height; - float goal_position_x; - float goal_position_y; - float goal_position_z; - int mark_as_expert; - int collision_state; - float x; - float y; - float z; - float vx; - float vy; - float vz; - float heading; - float heading_x; - float heading_y; - int valid; - int reached_goal; - int respawn_timestep; - int collided_before_goal; - int reached_goal_this_episode; - int active_agent; -}; - -// Based on the original GPUDrive MapType(WOMD) -typedef enum MapEntityType MapEntityType; -enum MapEntityType{ - NONE = 0, - VEHICLE = 1, - PEDESTRIAN = 2, - CYCLIST = 3, - ROAD_LANE = 4, - ROAD_LINE = 5, - ROAD_EDGE = 6, - STOP_SIGN = 7, - CROSSWALK = 8, - SPEED_BUMP = 9, - DRIVEWAY = 10, - NUM_TYPES = 11, -}; - -typedef struct MapEntity MapEntity; -struct MapEntity { - int id; - int type; - MapEntityType map_type; - int array_size; - float* traj_x; - float* traj_y; - float* traj_z; -}; - -void free_map_entity(MapEntity* map_entity){ - // free trajectory arrays - if(map_entity->traj_x) free(map_entity->traj_x); - if(map_entity->traj_y) free(map_entity->traj_y); - if(map_entity->traj_z) free(map_entity->traj_z); -} - -void free_entity(Entity* entity){ - // free trajectory arrays only if they're not NULL - if (entity->traj_x) free(entity->traj_x); - if (entity->traj_y) free(entity->traj_y); - if (entity->traj_z) free(entity->traj_z); - if (entity->traj_vx) free(entity->traj_vx); - if (entity->traj_vy) free(entity->traj_vy); - if (entity->traj_vz) free(entity->traj_vz); - if (entity->traj_heading) free(entity->traj_heading); - if (entity->traj_valid) free(entity->traj_valid); -} - +// Utility functions float relative_distance(float a, float b){ float distance = sqrtf(powf(a - b, 2)); return distance; @@ -187,156 +147,400 @@ float relative_distance_2d(float x1, float y1, float x2, float y2){ return distance; } +float clip(float value, float min, float max) { + if (value < min) return min; + if (value > max) return max; + return value; +} + +float compute_displacement_error(Agent* agent, int timestep) { + // Check if timestep is within valid range + if (timestep < 0 || timestep >= agent->trajectory_length) { + return 0.0f; + } + + // Check if reference trajectory is valid at this timestep + if (!agent->log_valid[timestep]) { + return 0.0f; + } + + // Get reference position from logged trajectory at current timestep + float ref_x = agent->log_trajectory_x[timestep]; + float ref_y = agent->log_trajectory_y[timestep]; + + if (ref_x == INVALID_POSITION || ref_y == INVALID_POSITION) { + return 0.0f; + } + + // Compute deltas: Euclidean distance between simulated and reference position + float dx = agent->sim_x - ref_x; + float dy = agent->sim_y - ref_y; + float displacement = sqrtf(dx*dx + dy*dy); + + return displacement; +} + +typedef struct GridMapEntity GridMapEntity; +struct GridMapEntity { + int entity_type; // Entity type: 1=Agent, 2=RoadMapElement, 3=TrafficControlElement + int entity_idx; // Index into the corresponding typed array + int geometry_idx; // Index into entity's trajectory/geometry array +}; + +typedef struct GridMap GridMap; +struct GridMap { + float top_left_x; + float top_left_y; + float bottom_right_x; + float bottom_right_y; + int grid_cols; + int grid_rows; + int cell_size_x; + int cell_size_y; + int* cell_entities_count; // number of entities in each cell of the GridMap + GridMapEntity** cells; // list of gridEntities in each cell of the GridMap + + // Extras/Optimizations + int vision_range; + int* neighbor_cache_count; // number of entities in each cells neighbor cache + GridMapEntity** neighbor_cache_entities; // preallocated array to hold neighbor entities +}; + struct Drive { Client* client; float* observations; - int* actions; + float* actions; float* rewards; unsigned char* terminals; Log log; Log* logs; - int num_agents; + int num_max_agents; int active_agent_count; int* active_agent_indices; + int action_type; int human_agent_idx; - Entity* entities; - MapEntity* map_entities; - int num_entities; - int num_cars; - int num_objects; - int num_roads; - int static_car_count; - int* static_car_indices; - int expert_static_car_count; - int* expert_static_car_indices; + Agent* agents; + RoadMapElement* road_elements; + TrafficControlElement* traffic_elements; + Graph* topology_graph; + int num_total_agents; + int num_road_elements; + int num_traffic_elements; + int num_actors; + int static_agent_count; + int* static_agent_indices; + int expert_static_agent_count; + int* expert_static_agent_indices; int timestep; + int init_steps; int dynamics_model; - float* map_corners; - int* grid_cells; // holds entity ids and geometry index per cell - int grid_cols; - int grid_rows; - int vision_range; + GridMap* grid_map; int* neighbor_offsets; - int* neighbor_cache_entities; - int* neighbor_cache_indices; + int scenario_length; float reward_vehicle_collision; float reward_offroad_collision; + float reward_ade; char* map_name; float world_mean_x; float world_mean_y; - int spawn_immunity_timer; + float dt; + float reward_goal; float reward_goal_post_respawn; - float reward_vehicle_collision_post_respawn; + float goal_radius; + int max_controlled_agents; + int logs_capacity; + int goal_behavior; + char* ini_file; + int collision_behavior; + int offroad_behavior; + int control_non_vehicles; + // Metadata fields + char scenario_id[128]; + int map_index; + char dataset_name[64]; + int log_length; + int sdc_index; + int num_objects_of_interest; + int* objects_of_interest; + int num_tracks_to_predict; + int* tracks_to_predict; + int init_mode; + int control_mode; }; void add_log(Drive* env) { for(int i = 0; i < env->active_agent_count; i++){ - Entity* e = &env->entities[env->active_agent_indices[i]]; - if(e->reached_goal_this_episode){ + Agent* agent = &env->agents[env->active_agent_indices[i]]; + + if(agent->reached_goal_this_episode){ env->log.completion_rate += 1.0f; } int offroad = env->logs[i].offroad_rate; env->log.offroad_rate += offroad; int collided = env->logs[i].collision_rate; env->log.collision_rate += collided; - int clean_collided = env->logs[i].clean_collision_rate; - env->log.clean_collision_rate += clean_collided; - if(e->reached_goal_this_episode && !e->collided_before_goal){ + float avg_offroad_per_agent = env->logs[i].avg_offroad_per_agent; + env->log.avg_offroad_per_agent += avg_offroad_per_agent; + float avg_collisions_per_agent = env->logs[i].avg_collisions_per_agent; + env->log.avg_collisions_per_agent += avg_collisions_per_agent; + int num_goals_reached = env->logs[i].num_goals_reached; + env->log.num_goals_reached += num_goals_reached; + if(agent->reached_goal_this_episode && !agent->collided_before_goal){ env->log.score += 1.0f; - env->log.perf += 1.0f; } - if(!offroad && !collided && !e->reached_goal_this_episode){ + if(!offroad && !collided && !agent->reached_goal_this_episode){ env->log.dnf_rate += 1.0f; } + int lane_aligned = env->logs[i].lane_alignment_rate; + env->log.lane_alignment_rate += lane_aligned; + float displacement_error = env->logs[i].avg_displacement_error; + env->log.avg_displacement_error += displacement_error; env->log.episode_length += env->logs[i].episode_length; env->log.episode_return += env->logs[i].episode_return; + // Log composition counts per agent so vec_log averaging recovers the per-env value + env->log.active_agent_count += env->active_agent_count; + env->log.expert_static_agent_count += env->expert_static_agent_count; + env->log.static_agent_count += env->static_agent_count; env->log.n += 1; } } -void load_map_binary(const char* filename, Drive* env) { + +struct AdjListNode { + int dest; + struct AdjListNode* next; +}; + +struct Graph { + int V; + struct AdjListNode** array; +}; + +// Function to create a new adjacency list node +struct AdjListNode* newAdjListNode(int dest) { + struct AdjListNode* newNode = malloc(sizeof(struct AdjListNode)); + newNode->dest = dest; + newNode->next = NULL; + return newNode; +} + +// Function to create a graph of V vertices +struct Graph* createGraph(int V) { + struct Graph* graph = malloc(sizeof(struct Graph)); + graph->V = V; + graph->array = calloc(V, sizeof(struct AdjListNode*)); + return graph; +} + +// Function to get next lanes from a given lane entity index +// Returns the number of next lanes found, fills next_lanes array with entity indices +int getNextLanes(struct Graph* graph, int entity_idx, int* next_lanes, int max_lanes) { + if (!graph || entity_idx < 0 || entity_idx >= graph->V) { + return 0; + } + + int count = 0; + struct AdjListNode* node = graph->array[entity_idx]; + + while (node && count < max_lanes) { + next_lanes[count] = node->dest; + count++; + node = node->next; + } + + return count; +} + +// Function to free the topology graph +void freeTopologyGraph(struct Graph* graph) { + if (!graph) return; + + for (int i = 0; i < graph->V; i++) { + struct AdjListNode* node = graph->array[i]; + while (node) { + struct AdjListNode* temp = node; + node = node->next; + free(temp); + } + } + + free(graph->array); + free(graph); +} + + +int load_map_binary(const char* filename, Drive* drive) { FILE* file = fopen(filename, "rb"); - if (!file) return; - fread(&env->num_objects, sizeof(int), 1, file); - fread(&env->num_roads, sizeof(int), 1, file); - env->num_entities = env->num_objects + env->num_roads; - Entity* entities = (Entity*)malloc(env->num_objects * sizeof(Entity)); - for (int i = 0; i < env->num_objects; i++) { - // Read base entity data - fread(&entities[i].type, sizeof(int), 1, file); - fread(&entities[i].array_size, sizeof(int), 1, file); - if(entities[i].type == 0) - { - printf("Warning: Entity %d has type 0 (NONE)\n", i); - } - // Allocate arrays based on type - int size = entities[i].array_size; - entities[i].traj_x = (float*)malloc(size * sizeof(float)); - entities[i].traj_y = (float*)malloc(size * sizeof(float)); - entities[i].traj_z = (float*)malloc(size * sizeof(float)); - if (entities[i].type == 1 || entities[i].type == 2 || entities[i].type == 3) { - entities[i].traj_vx = (float*)malloc(size * sizeof(float)); - entities[i].traj_vy = (float*)malloc(size * sizeof(float)); - entities[i].traj_vz = (float*)malloc(size * sizeof(float)); - entities[i].traj_heading = (float*)malloc(size * sizeof(float)); - entities[i].traj_valid = (int*)malloc(size * sizeof(int)); - } - // Read array data - fread(entities[i].traj_x, sizeof(float), size, file); - fread(entities[i].traj_y, sizeof(float), size, file); - fread(entities[i].traj_z, sizeof(float), size, file); - if (entities[i].type == 1 || entities[i].type == 2 || entities[i].type == 3) { - fread(entities[i].traj_vx, sizeof(float), size, file); - fread(entities[i].traj_vy, sizeof(float), size, file); - fread(entities[i].traj_vz, sizeof(float), size, file); - fread(entities[i].traj_heading, sizeof(float), size, file); - fread(entities[i].traj_valid, sizeof(int), size, file); - } - // Read remaining scalar fields - fread(&entities[i].width, sizeof(float), 1, file); - fread(&entities[i].length, sizeof(float), 1, file); - fread(&entities[i].height, sizeof(float), 1, file); - fread(&entities[i].goal_position_x, sizeof(float), 1, file); - fread(&entities[i].goal_position_y, sizeof(float), 1, file); - fread(&entities[i].goal_position_z, sizeof(float), 1, file); - fread(&entities[i].mark_as_expert, sizeof(int), 1, file); - } - - MapEntity* map_entities = (MapEntity*)malloc(env->num_roads * sizeof(MapEntity)); - // Read road entities - for (int i = 0; i < env->num_roads; i++) { - // Read base entity data - fread(&map_entities[i].type, sizeof(int), 1, file); - fread(&map_entities[i].array_size, sizeof(int), 1, file); - if(map_entities[i].type == 0) - { - printf("Warning: Map Entity %d has type 0 (NONE)\n", i); - } - // Type Handling - if (map_entities[i].type < 4 || map_entities[i].type > (int)NUM_TYPES) { - map_entities[i].map_type = (MapEntityType)(map_entities[i].type); - } - - // Allocate arrays based on type - int size = map_entities[i].array_size; - map_entities[i].traj_x = (float*)malloc(size * sizeof(float)); - map_entities[i].traj_y = (float*)malloc(size * sizeof(float)); - map_entities[i].traj_z = (float*)malloc(size * sizeof(float)); - // Read array data - fread(map_entities[i].traj_x, sizeof(float), size, file); - fread(map_entities[i].traj_y, sizeof(float), size, file); - fread(map_entities[i].traj_z, sizeof(float), size, file); - } - + if (!file) return -1; + + int num_total_agents, num_roads, num_traffic; + fread(&num_total_agents, sizeof(int), 1, file); + fread(&num_roads, sizeof(int), 1, file); + fread(&num_traffic, sizeof(int), 1, file); + + drive->num_total_agents = num_total_agents; + drive->num_road_elements = num_roads; + drive->num_traffic_elements = num_traffic; + + if (num_total_agents > 0) { + drive->agents = (Agent*)calloc(num_total_agents, sizeof(Agent)); + } + + if (num_roads > 0) { + drive->road_elements = (RoadMapElement*)calloc(num_roads, sizeof(RoadMapElement)); + } + + if (num_traffic > 0) { + drive->traffic_elements = (TrafficControlElement*)calloc(num_traffic, sizeof(TrafficControlElement)); + } + + for (int i = 0; i < num_total_agents; i++) { + Agent* agent = &drive->agents[i]; + + fread(&agent->id, sizeof(int), 1, file); + fread(&agent->type, sizeof(int), 1, file); + fread(&agent->trajectory_length, sizeof(int), 1, file); + + int tlen = agent->trajectory_length; + + agent->log_trajectory_x = (float*)malloc(tlen * sizeof(float)); + agent->log_trajectory_y = (float*)malloc(tlen * sizeof(float)); + agent->log_trajectory_z = (float*)malloc(tlen * sizeof(float)); + agent->log_heading = (float*)malloc(tlen * sizeof(float)); + agent->log_velocity_x = (float*)malloc(tlen * sizeof(float)); + agent->log_velocity_y = (float*)malloc(tlen * sizeof(float)); + agent->log_length = (float*)malloc(tlen * sizeof(float)); + agent->log_width = (float*)malloc(tlen * sizeof(float)); + agent->log_height = (float*)malloc(tlen * sizeof(float)); + agent->log_valid = (int*)malloc(tlen * sizeof(int)); + + fread(agent->log_trajectory_x, sizeof(float), tlen, file); + fread(agent->log_trajectory_y, sizeof(float), tlen, file); + fread(agent->log_trajectory_z, sizeof(float), tlen, file); + fread(agent->log_heading, sizeof(float), tlen, file); + fread(agent->log_velocity_x, sizeof(float), tlen, file); + fread(agent->log_velocity_y, sizeof(float), tlen, file); + fread(agent->log_length, sizeof(float), tlen, file); + fread(agent->log_width, sizeof(float), tlen, file); + fread(agent->log_height, sizeof(float), tlen, file); + fread(agent->log_valid, sizeof(int), tlen, file); + + fread(&agent->route_length, sizeof(int), 1, file); + + if (agent->route_length > 0) { + agent->route = (int*)malloc(agent->route_length * sizeof(int)); + fread(agent->route, sizeof(int), agent->route_length, file); + } else { + agent->route = NULL; + } + + fread(&agent->goal_position_x, sizeof(float), 1, file); + fread(&agent->goal_position_y, sizeof(float), 1, file); + fread(&agent->goal_position_z, sizeof(float), 1, file); + fread(&agent->mark_as_expert, sizeof(int), 1, file); + } + + for (int i = 0; i < num_roads; i++) { + RoadMapElement* road = &drive->road_elements[i]; + + fread(&road->id, sizeof(int), 1, file); + fread(&road->type, sizeof(int), 1, file); + fread(&road->segment_length, sizeof(int), 1, file); + + int slen = road->segment_length; + + road->x = (float*)malloc(slen * sizeof(float)); + road->y = (float*)malloc(slen * sizeof(float)); + road->z = (float*)malloc(slen * sizeof(float)); + + fread(road->x, sizeof(float), slen, file); + fread(road->y, sizeof(float), slen, file); + fread(road->z, sizeof(float), slen, file); + + if (is_road_lane(road->type)) { + fread(&road->num_entries, sizeof(int), 1, file); + if (road->num_entries > 0) { + road->entry_lanes = (int*)malloc(road->num_entries * sizeof(int)); + fread(road->entry_lanes, sizeof(int), road->num_entries, file); + } else { + road->entry_lanes = NULL; + } + + fread(&road->num_exits, sizeof(int), 1, file); + if (road->num_exits > 0) { + road->exit_lanes = (int*)malloc(road->num_exits * sizeof(int)); + fread(road->exit_lanes, sizeof(int), road->num_exits, file); + } else { + road->exit_lanes = NULL; + } + + fread(&road->speed_limit, sizeof(float), 1, file); + } else { + road->entry_lanes = NULL; + road->exit_lanes = NULL; + road->speed_limit = 0.0f; + } + } + + for (int i = 0; i < num_traffic; i++) { + TrafficControlElement* traffic = &drive->traffic_elements[i]; + + fread(&traffic->id, sizeof(int), 1, file); + fread(&traffic->type, sizeof(int), 1, file); + fread(&traffic->x, sizeof(float), 1, file); + fread(&traffic->y, sizeof(float), 1, file); + fread(&traffic->z, sizeof(float), 1, file); + fread(&traffic->state_length, sizeof(int), 1, file); + + int state_len = traffic->state_length; + + traffic->states = (int*)malloc(state_len * sizeof(int)); + fread(traffic->states, sizeof(int), state_len, file); + + // Read controlled_lanes array (but only store first one in single controlled_lane field) + int num_controlled_lanes; + fread(&num_controlled_lanes, sizeof(int), 1, file); + if (num_controlled_lanes > 0) { + fread(&traffic->controlled_lane, sizeof(int), 1, file); + // Skip remaining controlled lanes if any + for (int j = 1; j < num_controlled_lanes; j++) { + int dummy; + fread(&dummy, sizeof(int), 1, file); + } + } else { + traffic->controlled_lane = -1; + } + } + + fread(drive->scenario_id, sizeof(char), 128, file); + fread(&drive->map_index, sizeof(int), 1, file); + fread(drive->dataset_name, sizeof(char), 64, file); + fread(&drive->log_length, sizeof(int), 1, file); + fread(&drive->sdc_index, sizeof(int), 1, file); + fread(&drive->num_objects_of_interest, sizeof(int), 1, file); + + if (drive->num_objects_of_interest > 0) { + drive->objects_of_interest = (int*)malloc(drive->num_objects_of_interest * sizeof(int)); + fread(drive->objects_of_interest, sizeof(int), drive->num_objects_of_interest, file); + } else { + drive->objects_of_interest = NULL; + } + + fread(&drive->num_tracks_to_predict, sizeof(int), 1, file); + + if (drive->num_tracks_to_predict > 0) { + drive->tracks_to_predict = (int*)malloc(drive->num_tracks_to_predict * sizeof(int)); + fread(drive->tracks_to_predict, sizeof(int), drive->num_tracks_to_predict, file); + } else { + drive->tracks_to_predict = NULL; + } + fclose(file); - env->entities = entities; - env->map_entities = map_entities; + return 0; } void set_start_position(Drive* env){ - //InitWindow(800, 600, "GPU Drive"); - //BeginDrawing(); - for(int i = 0; i < env->num_objects; i++){ + for(int i = 0; i < env->num_total_agents; i++){ int is_active = 0; for(int j = 0; j < env->active_agent_count; j++){ if(env->active_agent_indices[j] == i){ @@ -344,127 +548,244 @@ void set_start_position(Drive* env){ break; } } - Entity* e = &env->entities[i]; - e->x = e->traj_x[0]; - e->y = e->traj_y[0]; - e->z = e->traj_z[0]; - //printf("Entity %d is at (%f, %f, %f)\n", i, e->x, e->y, e->z); - //if (e->type < 4) { - // DrawRectangle(200+2*e->x, 200+2*e->y, 2.0, 2.0, RED); - //} - if(e->type >3 || e->type == 0){ - continue; - } + Agent* agent = &env->agents[i]; + + // Clamp init_steps to ensure we don't go out of bounds + int step = env->init_steps; + if (step >= agent->trajectory_length) step = agent->trajectory_length - 1; + if (step < 0) step = 0; + + // Initialize simulation trajectory from logged trajectory at init_steps + agent->sim_x = agent->log_trajectory_x[step]; + agent->sim_y = agent->log_trajectory_y[step]; + agent->sim_z = agent->log_trajectory_z[step]; + agent->sim_heading = agent->log_heading[step]; + agent->sim_valid = agent->log_valid[step]; + agent->sim_length = agent->log_length[step]; + agent->sim_width = agent->log_width[step]; + agent->sim_height = agent->log_height[step]; + + if(agent->type == UNKNOWN) continue; + if(is_active == 0){ - e->vx = 0; - e->vy = 0; - e->vz = 0; - e->reached_goal = 0; - e->collided_before_goal = 0; - } else{ - e->vx = e->traj_vx[0]; - e->vy = e->traj_vy[0]; - e->vz = e->traj_vz[0]; - } - e->heading = e->traj_heading[0]; - e->heading_x = cosf(e->heading); - e->heading_y = sinf(e->heading); - e->valid = e->traj_valid[0]; - e->collision_state = 0; - e->respawn_timestep = -1; + agent->sim_vx = 0.0f; + agent->sim_vy = 0.0f; + agent->collided_before_goal = 0; + } else { + agent->sim_vx = agent->log_velocity_x[step]; + agent->sim_vy = agent->log_velocity_y[step]; + } + + // Shrink width and length slightly to avoid initial collisions + agent->sim_length *= 0.7f; + agent->sim_width *= 0.7f; + + // Initialize metrics and state + agent->collision_state = 0; + agent->metrics_array[COLLISION_IDX] = 0.0f; + agent->metrics_array[OFFROAD_IDX] = 0.0f; + agent->metrics_array[REACHED_GOAL_IDX] = 0.0f; + agent->metrics_array[LANE_ALIGNED_IDX] = 0.0f; + agent->metrics_array[AVG_DISPLACEMENT_ERROR_IDX] = 0.0f; + agent->cumulative_displacement = 0.0f; + agent->displacement_sample_count = 0; + agent->respawn_timestep = -1; + agent->stopped = 0; + agent->removed = 0; + agent->respawn_count = 0; + + // Dynamics + agent->a_long = 0.0f; + agent->a_lat = 0.0f; + agent->jerk_long = 0.0f; + agent->jerk_lat = 0.0f; + agent->steering_angle = 0.0f; + agent->wheelbase = 0.6f * agent->sim_length; } } int getGridIndex(Drive* env, float x1, float y1) { - if (env->map_corners[0] >= env->map_corners[2] || env->map_corners[1] >= env->map_corners[3]) { - printf("Invalid grid coordinates\n"); + if (env->grid_map->top_left_x >= env->grid_map->bottom_right_x || env->grid_map->bottom_right_y >= env->grid_map->top_left_y) { return -1; // Invalid grid coordinates } - float worldWidth = env->map_corners[2] - env->map_corners[0]; // Positive value - float worldHeight = env->map_corners[3] - env->map_corners[1]; // Positive value - int cellsX = (int)ceil(worldWidth / GRID_CELL_SIZE); // Number of columns - int cellsY = (int)ceil(worldHeight / GRID_CELL_SIZE); // Number of rows - float relativeX = x1 - env->map_corners[0]; // Distance from left - float relativeY = y1 - env->map_corners[1]; // Distance from top + + float relativeX = x1 - env->grid_map->top_left_x; // Distance from left + float relativeY = y1 - env->grid_map->bottom_right_y; // Distance from bottom int gridX = (int)(relativeX / GRID_CELL_SIZE); // Column index int gridY = (int)(relativeY / GRID_CELL_SIZE); // Row index - if (gridX < 0 || gridX >= cellsX || gridY < 0 || gridY >= cellsY) { + if (gridX < 0 || gridX >= env->grid_map->grid_cols || gridY < 0 || gridY >= env->grid_map->grid_rows) { return -1; // Return -1 for out of bounds } - int index = (gridY*cellsX) + gridX; + int index = (gridY*env->grid_map->grid_cols) + gridX; return index; } -void add_entity_to_grid(Drive* env, int grid_index, int entity_idx, int geometry_idx){ +void add_entity_to_grid(Drive* env, int grid_index, int entity_type, int entity_idx, int geometry_idx, int* cell_entities_insert_index){ if(grid_index == -1){ return; } - int base_index = grid_index * SLOTS_PER_CELL; - int count = env->grid_cells[base_index]; - if(count>= MAX_ENTITIES_PER_CELL) return; - env->grid_cells[base_index + count*2 + 1] = entity_idx; - env->grid_cells[base_index + count*2 + 2] = geometry_idx; - env->grid_cells[base_index] = count + 1; - + + int count = cell_entities_insert_index[grid_index]; + if(count >= env->grid_map->cell_entities_count[grid_index]) { + printf("Error: Exceeded precomputed entity count for grid cell %d. Current count: %d, Max count(Precomputed): %d\n", grid_index, count, env->grid_map->cell_entities_count[grid_index]); + return; + } + + env->grid_map->cells[grid_index][count].entity_type = entity_type; + env->grid_map->cells[grid_index][count].entity_idx = entity_idx; + env->grid_map->cells[grid_index][count].geometry_idx = geometry_idx; + cell_entities_insert_index[grid_index] = count + 1; +} + + +void init_topology_graph(Drive* env){ + // Count ROAD_LANE entities in road_elements + int road_lane_count = 0; + for(int i = 0; i < env->num_road_elements; i++){ + if(is_drivable_road_lane(env->road_elements[i].type)){ + road_lane_count++; + } + } + + if(road_lane_count == 0){ + env->topology_graph = NULL; + return; + } + + // Create graph with road_elements as vertices + env->topology_graph = createGraph(env->num_road_elements); + + // Connect ROAD_LANE entities based on geometric connectivity + for(int i = 0; i < env->num_road_elements; i++){ + if(!is_drivable_road_lane(env->road_elements[i].type)) continue; + + RoadMapElement* lane_i = &env->road_elements[i]; + if(lane_i->segment_length < 2) continue; // Need at least 2 points + + // Get end point of current lane + float end_x = lane_i->x[lane_i->segment_length - 1]; + float end_y = lane_i->y[lane_i->segment_length - 1]; + float end_vector_x = lane_i->x[lane_i->segment_length - 1] - lane_i->x[lane_i->segment_length - 2]; + float end_vector_y = lane_i->y[lane_i->segment_length - 1] - lane_i->y[lane_i->segment_length - 2]; + float end_heading = atan2f(end_vector_y, end_vector_x); + + // Find lanes that start near this lane's end + for(int j = 0; j < env->num_road_elements; j++){ + if(i == j || !is_drivable_road_lane(env->road_elements[j].type)) continue; + + RoadMapElement* lane_j = &env->road_elements[j]; + if(lane_j->segment_length < 2) continue; + + // Get start point of potential next lane + float start_x = lane_j->x[0]; + float start_y = lane_j->y[0]; + float start_vector_x = lane_j->x[1] - lane_j->x[0]; + float start_vector_y = lane_j->y[1] - lane_j->y[0]; + float start_heading = atan2f(start_vector_y, start_vector_x); + + // Check if end of lane_i is close to start of lane_j + float distance = relative_distance_2d(end_x, end_y, start_x, start_y); + float heading_diff = fabsf(end_heading - start_heading); + + // Lane connectivity thresholds: + // - 0.01m distance: lanes must connect within 1cm (very strict for clean topology) + // - 0.1 (~5.7 degrees) heading difference: allow slight curves + if(distance < 0.01f && heading_diff < 0.1f){ + // Add directed edge from i to j (lane i connects to lane j) + struct AdjListNode* node = newAdjListNode(j); + node->next = env->topology_graph->array[i]; + env->topology_graph->array[i] = node; + } + } + } } void init_grid_map(Drive* env){ + // Allocate memory for the grid map structure + env->grid_map = (GridMap*)malloc(sizeof(GridMap)); + // Find top left and bottom right points of the map float top_left_x; float top_left_y; float bottom_right_x; float bottom_right_y; int first_valid_point = 0; - for(int i = 0; i < env->num_roads; i++){ - if(env->map_entities[i].type > 3 && env->map_entities[i].type < 7){ - // Check all points in the trajectory for road elements - MapEntity* e = &env->map_entities[i]; - for(int j = 0; j < e->array_size; j++){ - if(e->traj_x[j] == -10000) continue; - if(e->traj_y[j] == -10000) continue; - if(!first_valid_point) { - top_left_x = bottom_right_x = e->traj_x[j]; - top_left_y = bottom_right_y = e->traj_y[j]; - first_valid_point = true; - continue; - } - if(e->traj_x[j] < top_left_x) top_left_x = e->traj_x[j]; - if(e->traj_x[j] > bottom_right_x) bottom_right_x = e->traj_x[j]; - if(e->traj_y[j] < top_left_y) top_left_y = e->traj_y[j]; - if(e->traj_y[j] > bottom_right_y) bottom_right_y = e->traj_y[j]; + for(int i = 0; i < env->num_road_elements; i++){ + // Check all points in the geometry for road elements (ROAD_LANE, ROAD_LINE, ROAD_EDGE) + if (!is_road(env->road_elements[i].type)) continue; + RoadMapElement* element = &env->road_elements[i]; + for(int j = 0; j < element->segment_length; j++){ + if(element->x[j] == INVALID_POSITION) continue; + if(element->y[j] == INVALID_POSITION) continue; + if(!first_valid_point) { + top_left_x = bottom_right_x = element->x[j]; + top_left_y = bottom_right_y = element->y[j]; + first_valid_point = true; + continue; } + if(element->x[j] < top_left_x) top_left_x = element->x[j]; + if(element->x[j] > bottom_right_x) bottom_right_x = element->x[j]; + if(element->y[j] > top_left_y) top_left_y = element->y[j]; + if(element->y[j] < bottom_right_y) bottom_right_y = element->y[j]; } } - env->map_corners = (float*)calloc(4, sizeof(float)); - env->map_corners[0] = top_left_x; - env->map_corners[1] = top_left_y; - env->map_corners[2] = bottom_right_x; - env->map_corners[3] = bottom_right_y; - + env->grid_map->top_left_x = top_left_x; + env->grid_map->top_left_y = top_left_y; + env->grid_map->bottom_right_x = bottom_right_x; + env->grid_map->bottom_right_y = bottom_right_y; + env->grid_map->cell_size_x = GRID_CELL_SIZE; + env->grid_map->cell_size_y = GRID_CELL_SIZE; + // Calculate grid dimensions float grid_width = bottom_right_x - top_left_x; - float grid_height = bottom_right_y - top_left_y; - env->grid_cols = ceil(grid_width / GRID_CELL_SIZE); - env->grid_rows = ceil(grid_height / GRID_CELL_SIZE); - int grid_cell_count = env->grid_cols*env->grid_rows; - env->grid_cells = (int*)calloc(grid_cell_count*SLOTS_PER_CELL, sizeof(int)); + float grid_height = top_left_y - bottom_right_y; + env->grid_map->grid_cols = ceil(grid_width / GRID_CELL_SIZE); + env->grid_map->grid_rows = ceil(grid_height / GRID_CELL_SIZE); + int grid_cell_count = env->grid_map->grid_cols*env->grid_map->grid_rows; + env->grid_map->cells = (GridMapEntity**)calloc(grid_cell_count, sizeof(GridMapEntity*)); + env->grid_map->cell_entities_count = (int*)calloc(grid_cell_count, sizeof(int)); + + // Calculate number of entities in each grid cell + for(int i = 0; i < env->num_road_elements; i++){ + for(int j = 0; j < env->road_elements[i].segment_length - 1; j++){ + float x_center = (env->road_elements[i].x[j] + env->road_elements[i].x[j+1]) / 2; + float y_center = (env->road_elements[i].y[j] + env->road_elements[i].y[j+1]) / 2; + int grid_index = getGridIndex(env, x_center, y_center); + if (grid_index == -1) continue; // Skip out-of-bounds entities + env->grid_map->cell_entities_count[grid_index]++; + } + } + + int cell_entities_insert_index[grid_cell_count]; // Helper array for insertion index + memset(cell_entities_insert_index, 0, grid_cell_count * sizeof(int)); + + // Initialize grid cells + for(int grid_index = 0; grid_index < grid_cell_count; grid_index++){ + env->grid_map->cells[grid_index] = (GridMapEntity*)calloc(env->grid_map->cell_entities_count[grid_index], sizeof(GridMapEntity)); + } + for(int i = 0;inum_roads; i++){ - if(env->map_entities[i].type > 3 && env->map_entities[i].type < 7){ - for(int j = 0; j < env->map_entities[i].array_size - 1; j++){ - float x_center = (env->map_entities[i].traj_x[j] + env->map_entities[i].traj_x[j+1]) / 2; - float y_center = (env->map_entities[i].traj_y[j] + env->map_entities[i].traj_y[j+1]) / 2; - int grid_index = getGridIndex(env, x_center, y_center); - add_entity_to_grid(env, grid_index, i, j); - } + for(int i = 0; i < env->num_road_elements; i++){ + for(int j = 0; j < env->road_elements[i].segment_length - 1; j++){ + float x_center = (env->road_elements[i].x[j] + env->road_elements[i].x[j+1]) / 2; + float y_center = (env->road_elements[i].y[j] + env->road_elements[i].y[j+1]) / 2; + int grid_index = getGridIndex(env, x_center, y_center); + if (grid_index == -1) continue; // Skip out-of-bounds entities + add_entity_to_grid(env, grid_index, ENTITY_TYPE_ROAD_ELEMENT, i, j, cell_entities_insert_index); } - } + } } void init_neighbor_offsets(Drive* env) { // Allocate memory for the offsets - env->neighbor_offsets = (int*)calloc(env->vision_range*env->vision_range*2, sizeof(int)); + env->neighbor_offsets = (int*)calloc(env->grid_map->vision_range*env->grid_map->vision_range*2, sizeof(int)); // neighbor offsets in a spiral pattern int dx[] = {1, 0, -1, 0}; int dy[] = {0, 1, 0, -1}; @@ -475,7 +796,7 @@ void init_neighbor_offsets(Drive* env) { int steps_taken = 0; // Steps taken in current direction int segments_completed = 0; // Count of direction segments completed int total = 0; // Total offsets added - int max_offsets = env->vision_range*env->vision_range; + int max_offsets = env->grid_map->vision_range*env->grid_map->vision_range; // Start at center (0,0) int curr_idx = 0; env->neighbor_offsets[curr_idx++] = 0; // x offset @@ -487,7 +808,7 @@ void init_neighbor_offsets(Drive* env) { x += dx[dir]; y += dy[dir]; // Only add if within vision range bounds - if (abs(x) <= env->vision_range/2 && abs(y) <= env->vision_range/2) { + if (abs(x) <= env->grid_map->vision_range/2 && abs(y) <= env->grid_map->vision_range/2) { env->neighbor_offsets[curr_idx++] = x; env->neighbor_offsets[curr_idx++] = y; total++; @@ -507,61 +828,74 @@ void init_neighbor_offsets(Drive* env) { void cache_neighbor_offsets(Drive* env){ int count = 0; - int cell_count = env->grid_cols*env->grid_rows; + int cell_count = env->grid_map->grid_cols*env->grid_map->grid_rows; + env->grid_map->neighbor_cache_entities = (GridMapEntity**)calloc(cell_count, sizeof(GridMapEntity*)); + env->grid_map->neighbor_cache_count = (int*)calloc(cell_count + 1, sizeof(int)); for(int i = 0; i < cell_count; i++){ - int cell_x = i % env->grid_cols; // Convert to 2D coordinates - int cell_y = i / env->grid_cols; - env->neighbor_cache_indices[i] = count; - for(int j = 0; j< env->vision_range*env->vision_range; j++){ + int cell_x = i % env->grid_map->grid_cols; // Convert to 2D coordinates + int cell_y = i / env->grid_map->grid_cols; + int current_cell_neighbor_count = 0; + for(int j = 0; j < env->grid_map->vision_range*env->grid_map->vision_range; j++){ int x = cell_x + env->neighbor_offsets[j*2]; int y = cell_y + env->neighbor_offsets[j*2+1]; - int grid_index = env->grid_cols*y + x; - if(x < 0 || x >= env->grid_cols || y < 0 || y >= env->grid_rows) continue; - int grid_count = env->grid_cells[grid_index*SLOTS_PER_CELL]; - count += grid_count * 2; + int grid_index = env->grid_map->grid_cols*y + x; + if(x < 0 || x >= env->grid_map->grid_cols || y < 0 || y >= env->grid_map->grid_rows) continue; + int grid_count = env->grid_map->cell_entities_count[grid_index]; + current_cell_neighbor_count += grid_count; + } + env->grid_map->neighbor_cache_count[i] = current_cell_neighbor_count; + count += current_cell_neighbor_count; + if(current_cell_neighbor_count == 0) { + env->grid_map->neighbor_cache_entities[i] = NULL; + continue; } + env->grid_map->neighbor_cache_entities[i] = (GridMapEntity*)calloc(current_cell_neighbor_count, sizeof(GridMapEntity)); } - env->neighbor_cache_indices[cell_count] = count; - env->neighbor_cache_entities = (int*)calloc(count, sizeof(int)); + + env->grid_map->neighbor_cache_count[cell_count] = count; for(int i = 0; i < cell_count; i ++){ - int neighbor_cache_base_index = 0; - int cell_x = i % env->grid_cols; // Convert to 2D coordinates - int cell_y = i / env->grid_cols; - for(int j = 0; jvision_range*env->vision_range; j++){ + int cell_x = i % env->grid_map->grid_cols; // Convert to 2D coordinates + int cell_y = i / env->grid_map->grid_cols; + int base_index = 0; + for(int j = 0; j < env->grid_map->vision_range*env->grid_map->vision_range; j++){ int x = cell_x + env->neighbor_offsets[j*2]; int y = cell_y + env->neighbor_offsets[j*2+1]; - int grid_index = env->grid_cols*y + x; - if(x < 0 || x >= env->grid_cols || y < 0 || y >= env->grid_rows) continue; - int grid_count = env->grid_cells[grid_index*SLOTS_PER_CELL]; - int base_index = env->neighbor_cache_indices[i]; - int src_idx = grid_index*SLOTS_PER_CELL + 1; - int dst_idx = base_index + neighbor_cache_base_index; - // Copy grid_count pairs (entity_idx, geometry_idx) at once - memcpy(&env->neighbor_cache_entities[dst_idx], - &env->grid_cells[src_idx], - grid_count * 2 * sizeof(int)); + int grid_index = env->grid_map->grid_cols*y + x; + if(x < 0 || x >= env->grid_map->grid_cols || y < 0 || y >= env->grid_map->grid_rows) continue; + int grid_count = env->grid_map->cell_entities_count[grid_index]; + + // Skip if no entities or source is NULL + if(grid_count == 0 || env->grid_map->cells[grid_index] == NULL) { + continue; + } - // Update index outside the loop - neighbor_cache_base_index += grid_count * 2; + int src_idx = grid_index; + int dst_idx = base_index; + // Copy grid_count pairs (entity_idx, geometry_idx) at once + memcpy(&env->grid_map->neighbor_cache_entities[i][dst_idx], + env->grid_map->cells[src_idx], + grid_count * sizeof(GridMapEntity)); + // for(int k = 0; k < grid_count; k++){ + // env->grid_map->neighbor_cache_entities[i][dst_idx + k] = env->grid_map->cells[src_idx][k]; + // } + base_index += grid_count; } } } -int get_neighbor_cache_entities(Drive* env, int cell_idx, int* entities, int max_entities) { - if (cell_idx < 0 || cell_idx >= (env->grid_cols * env->grid_rows)) { +int get_neighbor_cache_entities(Drive* env, int cell_idx, GridMapEntity* entities, int max_entities) { + GridMap* grid_map = env->grid_map; + if (cell_idx < 0 || cell_idx >= (grid_map->grid_cols * grid_map->grid_rows)) { return 0; // Invalid cell index } - int base_index = env->neighbor_cache_indices[cell_idx]; - int end_index = env->neighbor_cache_indices[cell_idx + 1]; - int count = end_index - base_index; - int pairs = count / 2; // Entity ID and geometry ID pairs + + int count = grid_map->neighbor_cache_count[cell_idx]; // Limit to available space - if (pairs > max_entities) { - pairs = max_entities; - count = pairs * 2; + if (count > max_entities) { + count = max_entities; } - memcpy(entities, env->neighbor_cache_entities + base_index, count * sizeof(int)); - return pairs; + memcpy(entities, grid_map->neighbor_cache_entities[cell_idx], count * sizeof(GridMapEntity)); + return count; } void set_means(Drive* env) { @@ -569,57 +903,106 @@ void set_means(Drive* env) { float mean_y = 0.0f; int64_t point_count = 0; - // Compute single mean for all entities (vehicles and roads) - for (int i = 0; i < env->num_objects; i++) { - if (env->entities[i].type == VEHICLE) { - for (int j = 0; j < env->entities[i].array_size; j++) { - // Assume a validity flag exists (e.g., valid[j]); adjust if not available - if (env->entities[i].traj_valid[j]) { // Add validity check if applicable - point_count++; - mean_x += (env->entities[i].traj_x[j] - mean_x) / point_count; - mean_y += (env->entities[i].traj_y[j] - mean_y) / point_count; - } + // Compute mean from dynamic agents + for (int i = 0; i < env->num_total_agents; i++) { + Agent* agent = &env->agents[i]; + for (int j = 0; j < agent->trajectory_length; j++) { + if (agent->log_valid[j]) { + point_count++; + mean_x += (agent->log_trajectory_x[j] - mean_x) / point_count; + mean_y += (agent->log_trajectory_y[j] - mean_y) / point_count; } } } - for (int i = 0; i < env->num_roads; i++) { - for (int j = 0; j < env->map_entities[i].array_size; j++) { + + // Compute mean from road elements + for (int i = 0; i < env->num_road_elements; i++) { + RoadMapElement* element = &env->road_elements[i]; + for (int j = 0; j < element->segment_length; j++) { + if(element->x[j] == INVALID_POSITION) continue; point_count++; - mean_x += (env->map_entities[i].traj_x[j] - mean_x) / point_count; - mean_y += (env->map_entities[i].traj_y[j] - mean_y) / point_count; + mean_x += (element->x[j] - mean_x) / point_count; + mean_y += (element->y[j] - mean_y) / point_count; } } + env->world_mean_x = mean_x; env->world_mean_y = mean_y; - for (int i = 0; i < env->num_objects; i++) { - if (env->entities[i].type == VEHICLE) { - for (int j = 0; j < env->entities[i].array_size; j++) { - if(env->entities[i].traj_x[j] == -10000) continue; - env->entities[i].traj_x[j] -= mean_x; - env->entities[i].traj_y[j] -= mean_y; - } - env->entities[i].goal_position_x -= mean_x; - env->entities[i].goal_position_y -= mean_y; + // Subtract mean from dynamic agents + for (int i = 0; i < env->num_total_agents; i++) { + Agent* agent = &env->agents[i]; + for (int j = 0; j < agent->trajectory_length; j++) { + if(agent->log_trajectory_x[j] == INVALID_POSITION) continue; + agent->log_trajectory_x[j] -= mean_x; + agent->log_trajectory_y[j] -= mean_y; + } + // Normalize current sim position (scalars) + if(agent->sim_x != INVALID_POSITION) { + agent->sim_x -= mean_x; + agent->sim_y -= mean_y; } + agent->goal_position_x -= mean_x; + agent->goal_position_y -= mean_y; + agent->init_goal_x -= mean_x; + agent->init_goal_y -= mean_y; } - for (int i = 0; i < env->num_roads; i++) { - for (int j = 0; j < env->map_entities[i].array_size; j++) { - if(env->map_entities[i].traj_x[j] == -10000) continue; - env->map_entities[i].traj_x[j] -= mean_x; - env->map_entities[i].traj_y[j] -= mean_y; + + // Subtract mean from road elements + for (int i = 0; i < env->num_road_elements; i++) { + RoadMapElement* element = &env->road_elements[i]; + for (int j = 0; j < element->segment_length; j++) { + if(element->x[j] == INVALID_POSITION) continue; + element->x[j] -= mean_x; + element->y[j] -= mean_y; } } } -void move_expert(Drive* env, int* actions, int agent_idx){ - Entity* agent = &env->entities[agent_idx]; - agent->x = agent->traj_x[env->timestep]; - agent->y = agent->traj_y[env->timestep]; - agent->z = agent->traj_z[env->timestep]; - agent->heading = agent->traj_heading[env->timestep]; - agent->heading_x = cosf(agent->heading); - agent->heading_y = sinf(agent->heading); +void move_expert(Drive* env, float* actions, int agent_idx){ + Agent* agent = &env->agents[agent_idx]; + int t = env->timestep; + + // Timestep out of bounds + if (t < 0 || t >= agent->trajectory_length) { + agent->sim_x = INVALID_POSITION; + agent->sim_y = INVALID_POSITION; + agent->sim_z = 0.0f; + agent->sim_heading = 0.0f; + agent->sim_vx = 0.0f; + agent->sim_vy = 0.0f; + agent->sim_valid = 0; + // agent->sim_length = 0.0f; + // agent->sim_width = 0.0f; + // agent->sim_height = 0.0f; + return; + } + // Invalid log entry + if (agent->log_valid && agent->log_valid[t] == 0) { + agent->sim_x = INVALID_POSITION; + agent->sim_y = INVALID_POSITION; + agent->sim_z = 0.0f; + agent->sim_heading = 0.0f; + agent->sim_vx = 0.0f; + agent->sim_vy = 0.0f; + agent->sim_valid = 0; + // agent->sim_length = 0.0f; + // agent->sim_width = 0.0f; + // agent->sim_height = 0.0f; + return; + } + + // Copy from logged trajectory to simulated state + agent->sim_x = agent->log_trajectory_x[t]; + agent->sim_y = agent->log_trajectory_y[t]; + agent->sim_z = agent->log_trajectory_z[t]; + agent->sim_heading = agent->log_heading[t]; + agent->sim_vx = agent->log_velocity_x[t]; + agent->sim_vy = agent->log_velocity_y[t]; + // agent->sim_length = agent->log_length[t]; + // agent->sim_width = agent->log_width[t]; + // agent->sim_height = agent->log_height[t]; + agent->sim_valid = agent->log_valid[t]; } bool check_line_intersection(float p1[2], float p2[2], float q1[2], float q2[2]) { @@ -632,31 +1015,31 @@ bool check_line_intersection(float p1[2], float p2[2], float q1[2], float q2[2]) float dy1 = p2[1] - p1[1]; float dx2 = q2[0] - q1[0]; float dy2 = q2[1] - q1[1]; - + // Calculate cross products float cross = dx1 * dy2 - dy1 * dx2; - + // If lines are parallel if (cross == 0) return false; - + // Calculate relative vectors between start points float dx3 = p1[0] - q1[0]; float dy3 = p1[1] - q1[1]; - + // Calculate parameters for intersection point float s = (dx1 * dy3 - dy1 * dx3) / cross; float t = (dx2 * dy3 - dy2 * dx3) / cross; - + // Check if intersection point lies within both line segments return (s >= 0 && s <= 1 && t >= 0 && t <= 1); } -int checkNeighbors(Drive* env, float x, float y, int* entity_list, int max_size, const int (*local_offsets)[2], int offset_size) { +int checkNeighbors(Drive* env, float x, float y, GridMapEntity* entity_list, int max_size, const int (*local_offsets)[2], int offset_size) { // Get the grid index for the given position (x, y) int index = getGridIndex(env, x, y); if (index == -1) return 0; // Return 0 size if position invalid // Calculate 2D grid coordinates - int cellsX = env->grid_cols; + int cellsX = env->grid_map->grid_cols; int gridX = index % cellsX; int gridY = index / cellsX; int entity_list_count = 0; @@ -665,48 +1048,53 @@ int checkNeighbors(Drive* env, float x, float y, int* entity_list, int max_size, int nx = gridX + local_offsets[i][0]; int ny = gridY + local_offsets[i][1]; // Ensure the neighbor is within grid bounds - if(nx < 0 || nx >= env->grid_cols || ny < 0 || ny >= env->grid_rows) continue; - int neighborIndex = (ny * env->grid_cols + nx) * SLOTS_PER_CELL; - int count = env->grid_cells[neighborIndex]; + if(nx < 0 || nx >= env->grid_map->grid_cols || ny < 0 || ny >= env->grid_map->grid_rows) continue; + int neighborIndex = ny * env->grid_map->grid_cols + nx; + int count = env->grid_map->cell_entities_count[neighborIndex]; // Add entities from this cell to the list for (int j = 0; j < count && entity_list_count < max_size; j++) { - int entityId = env->grid_cells[neighborIndex + 1 + j*2]; - int geometry_idx = env->grid_cells[neighborIndex + 2 + j*2]; - entity_list[entity_list_count] = entityId; - entity_list[entity_list_count + 1] = geometry_idx; - entity_list_count += 2; + int entityId = env->grid_map->cells[neighborIndex][j].entity_idx; + int geometry_idx = env->grid_map->cells[neighborIndex][j].geometry_idx; + int entity_type = env->grid_map->cells[neighborIndex][j].entity_type; + entity_list[entity_list_count].entity_idx = entityId; + entity_list[entity_list_count].geometry_idx = geometry_idx; + entity_list[entity_list_count].entity_type = entity_type; + entity_list_count += 1; } } return entity_list_count; } -int check_aabb_collision(Entity* car1, Entity* car2) { +int check_aabb_collision(Agent* car1, Agent* car2) { // Get car corners in world space - float cos1 = car1->heading_x; - float sin1 = car1->heading_y; - float cos2 = car2->heading_x; - float sin2 = car2->heading_y; - + float heading1 = car1->sim_heading; + float cos1 = cosf(heading1); + float sin1 = sinf(heading1); + + float heading2 = car2->sim_heading; + float cos2 = cosf(heading2); + float sin2 = sinf(heading2); + // Calculate half dimensions - float half_len1 = car1->length * 0.5f; - float half_width1 = car1->width * 0.5f; - float half_len2 = car2->length * 0.5f; - float half_width2 = car2->width * 0.5f; - + float half_len1 = car1->sim_length * 0.5f; + float half_width1 = car1->sim_width * 0.5f; + float half_len2 = car2->sim_length * 0.5f; + float half_width2 = car2->sim_width * 0.5f; + // Calculate car1's corners in world space float car1_corners[4][2] = { - {car1->x + (half_len1 * cos1 - half_width1 * sin1), car1->y + (half_len1 * sin1 + half_width1 * cos1)}, - {car1->x + (half_len1 * cos1 + half_width1 * sin1), car1->y + (half_len1 * sin1 - half_width1 * cos1)}, - {car1->x + (-half_len1 * cos1 - half_width1 * sin1), car1->y + (-half_len1 * sin1 + half_width1 * cos1)}, - {car1->x + (-half_len1 * cos1 + half_width1 * sin1), car1->y + (-half_len1 * sin1 - half_width1 * cos1)} + {car1->sim_x + (half_len1 * cos1 - half_width1 * sin1), car1->sim_y + (half_len1 * sin1 + half_width1 * cos1)}, + {car1->sim_x + (half_len1 * cos1 + half_width1 * sin1), car1->sim_y + (half_len1 * sin1 - half_width1 * cos1)}, + {car1->sim_x + (-half_len1 * cos1 - half_width1 * sin1), car1->sim_y + (-half_len1 * sin1 + half_width1 * cos1)}, + {car1->sim_x + (-half_len1 * cos1 + half_width1 * sin1), car1->sim_y + (-half_len1 * sin1 - half_width1 * cos1)} }; - + // Calculate car2's corners in world space float car2_corners[4][2] = { - {car2->x + (half_len2 * cos2 - half_width2 * sin2), car2->y + (half_len2 * sin2 + half_width2 * cos2)}, - {car2->x + (half_len2 * cos2 + half_width2 * sin2), car2->y + (half_len2 * sin2 - half_width2 * cos2)}, - {car2->x + (-half_len2 * cos2 - half_width2 * sin2), car2->y + (-half_len2 * sin2 + half_width2 * cos2)}, - {car2->x + (-half_len2 * cos2 + half_width2 * sin2), car2->y + (-half_len2 * sin2 - half_width2 * cos2)} + {car2->sim_x + (half_len2 * cos2 - half_width2 * sin2), car2->sim_y + (half_len2 * sin2 + half_width2 * cos2)}, + {car2->sim_x + (half_len2 * cos2 + half_width2 * sin2), car2->sim_y + (half_len2 * sin2 - half_width2 * cos2)}, + {car2->sim_x + (-half_len2 * cos2 - half_width2 * sin2), car2->sim_y + (-half_len2 * sin2 + half_width2 * cos2)}, + {car2->sim_x + (-half_len2 * cos2 + half_width2 * sin2), car2->sim_y + (-half_len2 * sin2 - half_width2 * cos2)} }; // Get the axes to check (normalized vectors perpendicular to each edge) @@ -721,206 +1109,398 @@ int check_aabb_collision(Entity* car1, Entity* car2) { for(int i = 0; i < 4; i++) { float min1 = INFINITY, max1 = -INFINITY; float min2 = INFINITY, max2 = -INFINITY; - + // Project car1's corners onto the axis for(int j = 0; j < 4; j++) { float proj = car1_corners[j][0] * axes[i][0] + car1_corners[j][1] * axes[i][1]; min1 = fminf(min1, proj); max1 = fmaxf(max1, proj); } - + // Project car2's corners onto the axis for(int j = 0; j < 4; j++) { float proj = car2_corners[j][0] * axes[i][0] + car2_corners[j][1] * axes[i][1]; min2 = fminf(min2, proj); max2 = fmaxf(max2, proj); } - + // If there's a gap on this axis, the boxes don't intersect if(max1 < min2 || min1 > max2) { return 0; // No collision } } - + // If we get here, there's no separating axis, so the boxes intersect return 1; // Collision } int collision_check(Drive* env, int agent_idx) { - Entity* agent = &env->entities[agent_idx]; - if(agent->x == -10000.0f ) return -1; - float half_length = agent->length/2.0f; - float half_width = agent->width/2.0f; - float cos_heading = cosf(agent->heading); - float sin_heading = sinf(agent->heading); - float corners[4][2]; - for (int i = 0; i < 4; i++) { - corners[i][0] = agent->x + (offsets[i][0]*half_length*cos_heading - offsets[i][1]*half_width*sin_heading); - corners[i][1] = agent->y + (offsets[i][0]*half_length*sin_heading + offsets[i][1]*half_width*cos_heading); - } - int collided = 0; + Agent* agent = &env->agents[agent_idx]; + + if(agent->sim_x == INVALID_POSITION) return -1; + int car_collided_with_index = -1; - int entity_list[MAX_ENTITIES_PER_CELL*2*25]; // Array big enough for all neighboring cells - int list_size = checkNeighbors(env, agent->x, agent->y, entity_list, MAX_ENTITIES_PER_CELL*2*25, collision_offsets, 25); - for (int i = 0; i < list_size ; i+=2) { - if(entity_list[i] == -1) continue; - if(entity_list[i] == agent_idx) continue; - MapEntity* map_entity = &env->map_entities[entity_list[i]]; - if(map_entity->type != ROAD_EDGE) continue; - int geometry_idx = entity_list[i + 1]; - float start[2] = {map_entity->traj_x[geometry_idx], map_entity->traj_y[geometry_idx]}; - float end[2] = {map_entity->traj_x[geometry_idx + 1], map_entity->traj_y[geometry_idx + 1]}; - for (int k = 0; k < 4; k++) { // Check each edge of the bounding box - int next = (k + 1) % 4; - if (check_line_intersection(corners[k], corners[next], start, end)) { - collided = OFFROAD; - break; - } - } - if (collided == OFFROAD) break; - } - for(int i = 0; i < MAX_CARS; i++){ + + if (agent->respawn_timestep != -1) return car_collided_with_index; // Skip respawning entities + + for(int i = 0; i < MAX_AGENTS; i++){ int index = -1; if(i < env->active_agent_count){ index = env->active_agent_indices[i]; - } else if (i < env->num_cars){ - index = env->static_car_indices[i - env->active_agent_count]; + } else if (i < env->num_actors){ + index = env->static_agent_indices[i - env->active_agent_count]; } if(index == -1) continue; if(index == agent_idx) continue; - Entity* entity = &env->entities[index]; - float x1 = entity->x; - float y1 = entity->y; - float dist = ((x1 - agent->x)*(x1 - agent->x) + (y1 - agent->y)*(y1 - agent->y)); + + Agent* other_agent = &env->agents[index]; + if (other_agent->respawn_timestep != -1) continue; // Skip respawning entities + + float dist = ((other_agent->sim_x - agent->sim_x)*(other_agent->sim_x - agent->sim_x) + (other_agent->sim_y - agent->sim_y)*(other_agent->sim_y - agent->sim_y)); if(dist > 225.0f) continue; - if(check_aabb_collision(agent, entity)) { - collided = VEHICLE_COLLISION; + if(check_aabb_collision(agent, other_agent)) { car_collided_with_index = index; break; } } - agent->collision_state = collided; - // spawn immunity for collisions with other agent cars as agent_idx respawns - int is_active_agent = env->entities[agent_idx].active_agent; - int respawned = env->entities[agent_idx].respawn_timestep != -1; - int exceeded_spawn_immunity_agent = (env->timestep - env->entities[agent_idx].respawn_timestep) >= env->spawn_immunity_timer; - if(collided == VEHICLE_COLLISION && is_active_agent == 1 && respawned){ - agent->collision_state = 0; - } - // spawn immunity for collisions with other cars who just respawned - if(collided == OFFROAD) return -1; - if(car_collided_with_index ==-1) return -1; + return car_collided_with_index; +} - int respawned_collided_with_car = env->entities[car_collided_with_index].respawn_timestep != -1; - int exceeded_spawn_immunity_collided_with_car = (env->timestep - env->entities[car_collided_with_index].respawn_timestep) >= env->spawn_immunity_timer; - int within_spawn_immunity_collided_with_car = (env->timestep - env->entities[car_collided_with_index].respawn_timestep) < env->spawn_immunity_timer; - if (respawned_collided_with_car) { - agent->collision_state = 0; +int check_lane_aligned(Agent* car, RoadMapElement* lane, int geometry_idx, int timestep) { + // Validate lane geometry length + if (!lane || lane->segment_length < 2) return 0; + + // Clamp geometry index to valid segment range [0, segment_length-2] + if (geometry_idx < 0) geometry_idx = 0; + if (geometry_idx >= lane->segment_length - 1) geometry_idx = lane->segment_length - 2; + + // Compute local lane segment heading + float heading_x1, heading_y1; + if (geometry_idx > 0) { + heading_x1 = lane->x[geometry_idx] - lane->x[geometry_idx - 1]; + heading_y1 = lane->y[geometry_idx] - lane->y[geometry_idx - 1]; + } else { + // For first segment, just use the forward direction + heading_x1 = lane->x[geometry_idx + 1] - lane->x[geometry_idx]; + heading_y1 = lane->y[geometry_idx + 1] - lane->y[geometry_idx]; } - return car_collided_with_index; + float heading_x2 = lane->x[geometry_idx + 1] - lane->x[geometry_idx]; + float heading_y2 = lane->y[geometry_idx + 1] - lane->y[geometry_idx]; + + float heading_1 = atan2f(heading_y1, heading_x1); + float heading_2 = atan2f(heading_y2, heading_x2); + float heading = (heading_1 + heading_2) / 2.0f; + + // Normalize to [-pi, pi] + if (heading > M_PI) heading -= 2.0f * M_PI; + if (heading < -M_PI) heading += 2.0f * M_PI; + + // Compute heading difference + float car_heading = car->sim_heading; // radians + float heading_diff = fabsf(car_heading - heading); + + if (heading_diff > M_PI) heading_diff = 2.0f * M_PI - heading_diff; + + // within 15 degrees + return (heading_diff < (M_PI / 12.0f)) ? 1 : 0; } -int valid_active_agent(Drive* env, int agent_idx){ - float cos_heading = cosf(env->entities[agent_idx].traj_heading[0]); - float sin_heading = sinf(env->entities[agent_idx].traj_heading[0]); - float goal_x = env->entities[agent_idx].goal_position_x - env->entities[agent_idx].traj_x[0]; - float goal_y = env->entities[agent_idx].goal_position_y - env->entities[agent_idx].traj_y[0]; - // Rotate to ego vehicle's frame - float rel_goal_x = goal_x*cos_heading + goal_y*sin_heading; - float rel_goal_y = -goal_x*sin_heading + goal_y*cos_heading; - float distance_to_goal = relative_distance_2d(0, 0, rel_goal_x, rel_goal_y); - env->entities[agent_idx].width *= 0.7f; - env->entities[agent_idx].length *= 0.7f; - if(distance_to_goal >= 2.0f && env->entities[agent_idx].mark_as_expert == 0 && env->active_agent_count < env->num_agents){ - return distance_to_goal; - } - return 0; +void reset_agent_metrics(Drive* env, int agent_idx){ + Agent* agent = &env->agents[agent_idx]; + agent->metrics_array[COLLISION_IDX] = 0.0f; // vehicle collision + agent->metrics_array[OFFROAD_IDX] = 0.0f; // offroad + agent->metrics_array[LANE_ALIGNED_IDX] = 0.0f; // lane aligned + agent->metrics_array[AVG_DISPLACEMENT_ERROR_IDX] = 0.0f; + agent->collision_state = 0; } -void set_active_agents(Drive* env){ - env->active_agent_count = 0; - env->static_car_count = 0; - env->num_cars = 1; - env->expert_static_car_count = 0; - int active_agent_indices[MAX_CARS]; - int static_car_indices[MAX_CARS]; - int expert_static_car_indices[MAX_CARS]; - - if(env->num_agents ==0){ - env->num_agents = MAX_CARS; - } - int first_agent_id = env->num_objects-1; - float distance_to_goal = valid_active_agent(env, first_agent_id); - if(distance_to_goal){ - env->active_agent_count = 1; - active_agent_indices[0] = first_agent_id; - env->entities[first_agent_id].active_agent = 1; - env->num_cars = 1; - } else { - env->active_agent_count = 0; - env->num_cars = 0; - } - for(int i = 0; i < env->num_objects-1 && env->num_cars < MAX_CARS; i++){ - if(env->entities[i].type != 1) continue; - if(env->entities[i].traj_valid[0] != 1) continue; - env->num_cars++; - float distance_to_goal = valid_active_agent(env, i); - if(distance_to_goal > 0){ - active_agent_indices[env->active_agent_count] = i; - env->active_agent_count++; - env->entities[i].active_agent = 1; - } else { - static_car_indices[env->static_car_count] = i; - env->static_car_count++; - env->entities[i].active_agent = 0; - if(env->entities[i].mark_as_expert == 1 || (distance_to_goal >=2.0f && env->active_agent_count == env->num_agents)){ - expert_static_car_indices[env->expert_static_car_count] = i; - env->expert_static_car_count++; - env->entities[i].mark_as_expert = 1; - } - } +float point_to_segment_distance_2d(float px, float py, float x1, float y1, float x2, float y2) { + float dx = x2 - x1; + float dy = y2 - y1; + + if (dx == 0 && dy == 0) { + // The segment is a point + return sqrtf((px - x1) * (px - x1) + (py - y1) * (py - y1)); } - // set up initial active agents - env->active_agent_indices = (int*)malloc(env->active_agent_count * sizeof(int)); - env->static_car_indices = (int*)malloc(env->static_car_count * sizeof(int)); - env->expert_static_car_indices = (int*)malloc(env->expert_static_car_count * sizeof(int)); - for(int i=0;iactive_agent_count;i++){ - env->active_agent_indices[i] = active_agent_indices[i]; - }; - for(int i=0;istatic_car_count;i++){ - env->static_car_indices[i] = static_car_indices[i]; - + + // Calculate the t that minimizes the distance + float t = ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy); + + // Clamp t to the segment + if (t < 0) t = 0; + else if (t > 1) t = 1; + + // Find the closest point on the segment + float closestX = x1 + t * dx; + float closestY = y1 + t * dy; + + // Return the distance from p to the closest point + return sqrtf((px - closestX) * (px - closestX) + (py - closestY) * (py - closestY)); +} + +void compute_agent_metrics(Drive* env, int agent_idx) { + Agent* agent = &env->agents[agent_idx]; + + reset_agent_metrics(env, agent_idx); + + if(agent->sim_x == INVALID_POSITION) return; // invalid agent position + + // Compute displacement error + float displacement_error = compute_displacement_error(agent, env->timestep); + + if (displacement_error > 0.0f) { // Only count valid displacements + agent->cumulative_displacement += displacement_error; + agent->displacement_sample_count++; + + // Compute running average + agent->metrics_array[AVG_DISPLACEMENT_ERROR_IDX] = + agent->cumulative_displacement / agent->displacement_sample_count; } - for(int i=0;iexpert_static_car_count;i++){ - env->expert_static_car_indices[i] = expert_static_car_indices[i]; + + int collided = 0; + float half_length = agent->sim_length/2.0f; + float half_width = agent->sim_width/2.0f; + float cos_heading = cosf(agent->sim_heading); + float sin_heading = sinf(agent->sim_heading); + float min_distance = (float)INT16_MAX; + + int closest_lane_entity_idx = -1; + int closest_lane_geometry_idx = -1; + + float corners[4][2]; + for (int i = 0; i < 4; i++) { + corners[i][0] = agent->sim_x + (offsets[i][0]*half_length*cos_heading - offsets[i][1]*half_width*sin_heading); + corners[i][1] = agent->sim_y + (offsets[i][0]*half_length*sin_heading + offsets[i][1]*half_width*cos_heading); } - return; -} -void remove_bad_trajectories(Drive* env){ - set_start_position(env); - int collided_agents[env->active_agent_count]; - int collided_with_indices[env->active_agent_count]; - memset(collided_agents, 0, env->active_agent_count * sizeof(int)); + GridMapEntity entity_list[MAX_ENTITIES_PER_CELL*25]; // Array big enough for all neighboring cells + int list_size = checkNeighbors(env, agent->sim_x, agent->sim_y, entity_list, MAX_ENTITIES_PER_CELL*25, collision_offsets, 25); + for (int i = 0; i < list_size; i++) { + if(entity_list[i].entity_idx == -1) continue; + + // Get the road element (only road elements are in grid) + if(entity_list[i].entity_type != ENTITY_TYPE_ROAD_ELEMENT) continue; + + RoadMapElement* element = &env->road_elements[entity_list[i].entity_idx]; + int geometry_idx = entity_list[i].geometry_idx; + + // Check for offroad collision with road edges + if(is_road_edge(element->type)) { + float start[2] = {element->x[geometry_idx], element->y[geometry_idx]}; + float end[2] = {element->x[geometry_idx + 1], element->y[geometry_idx + 1]}; + for (int k = 0; k < 4; k++) { // Check each edge of the bounding box + int next = (k + 1) % 4; + if (check_line_intersection(corners[k], corners[next], start, end)) { + collided = OFFROAD; + break; + } + } + } + + if (collided == OFFROAD) break; + + // Find closest point on the road centerline to the agent + if(is_drivable_road_lane(element->type)) { + int elem_idx = entity_list[i].entity_idx; + + float start[2] = {element->x[geometry_idx], element->y[geometry_idx]}; + float end[2] = {element->x[geometry_idx + 1], element->y[geometry_idx + 1]}; + + float dist = point_to_segment_distance_2d(agent->sim_x, agent->sim_y, start[0], start[1], end[0], end[1]); + float heading_diff = fabsf(atan2f(end[1]-start[1], end[0]-start[0]) - agent->sim_heading); + + // Normalize heading difference to [0, pi] + if (heading_diff > M_PI) heading_diff = 2.0f * M_PI - heading_diff; + + // Penalize if heading differs by more than 30 degrees + if (heading_diff > (M_PI / 6.0f)) dist += 3.0f; + + if (dist < min_distance) { + min_distance = dist; + closest_lane_entity_idx = elem_idx; + closest_lane_geometry_idx = geometry_idx; + } + } + } + + // check if aligned with closest lane and set current lane + // 4.0m threshold: agents more than 4 meters from any lane are considered off-road + if (min_distance > 4.0f || closest_lane_entity_idx == -1) { + agent->metrics_array[LANE_ALIGNED_IDX] = 0.0f; + agent->current_lane_idx = -1; + } else { + agent->current_lane_idx = closest_lane_entity_idx; + + int lane_aligned = check_lane_aligned(agent, &env->road_elements[closest_lane_entity_idx], closest_lane_geometry_idx, env->timestep); + agent->metrics_array[LANE_ALIGNED_IDX] = lane_aligned; + } + + // Check for vehicle collisions + int car_collided_with_index = collision_check(env, agent_idx); + if (car_collided_with_index != -1) collided = VEHICLE_COLLISION; + + agent->collision_state = collided; + + return; +} + +bool should_control_agent(Drive* env, int agent_idx){ + // Check if we have room for more agents or are already at capacity + if (env->active_agent_count >= env->num_max_agents) { + return false; + } + + Agent* agent = &env->agents[agent_idx]; + + if (env->control_mode == CONTROL_SDC_ONLY) { + return (agent_idx == env->sdc_index); + } + + // Special mode: control only agents in prediction track list + if (env->control_mode == CONTROL_TRACKS_TO_PREDICT) { + for (int j = 0; j < env->num_tracks_to_predict; j++) { + if (env->tracks_to_predict[j] == agent_idx) { + return true; + } + } + return false; + } + + // Standard mode: check type, distance to goal, and expert status + bool type_is_controllable = false; + if (env->control_mode == CONTROL_VEHICLES) { + type_is_controllable = (agent->type == VEHICLE); + } else { // CONTROL_AGENTS mode + type_is_controllable = (agent->type == VEHICLE || agent->type == PEDESTRIAN || agent->type == CYCLIST); + } + + if (!type_is_controllable || agent->mark_as_expert) { + return false; + } + + // Check distance to goal in agent's local frame + float cos_heading = cosf(agent->log_heading[0]); + float sin_heading = sinf(agent->log_heading[0]); + float goal_dx = agent->goal_position_x - agent->log_trajectory_x[0]; + float goal_dy = agent->goal_position_y - agent->log_trajectory_y[0]; + + // Transform to agent's local frame + float local_goal_x = goal_dx * cos_heading + goal_dy * sin_heading; + float local_goal_y = -goal_dx * sin_heading + goal_dy * cos_heading; + float distance_to_goal = relative_distance_2d(0, 0, local_goal_x, local_goal_y); + + return distance_to_goal >= MIN_DISTANCE_TO_GOAL; +} + +void set_active_agents(Drive* env){ + // Initialize + env->active_agent_count = 0; // Policy-controlled agents + env->static_agent_count = 0; // Non-moving background agents + env->expert_static_agent_count = 0; // Expert replay agents (non-controlled) + env->num_actors = 0; // Total agents created + + int active_agent_indices[MAX_AGENTS]; + int static_agent_indices[MAX_AGENTS]; + int expert_static_agent_indices[MAX_AGENTS]; + + if(env->num_max_agents == 0){ + env->num_max_agents = MAX_AGENTS; + } + + // Iterate through entities to find agents to create and/or control + for(int i = 0; i < env->num_total_agents && env->num_actors < MAX_AGENTS; i++){ + + Agent* agent = &env->agents[i]; + + // Skip if not valid at initialization + if (agent->log_valid[env->init_steps] != 1) { + continue; + } + + // Determine if entity should be created + bool should_create = false; + if (env->init_mode == INIT_ALL_VALID) { + should_create = true; // All valid entities + } else if (env->control_mode == CONTROL_VEHICLES) { + should_create = (agent->type == VEHICLE); + } else { // Control all agents + should_create = (is_controllable_agent(agent->type)); + } + + if (!should_create) continue; + + env->num_actors++; + + // Determine if this agent should be policy-controlled + bool is_controlled = false; + + is_controlled = should_control_agent(env, i); + + if(is_controlled){ + active_agent_indices[env->active_agent_count] = i; + env->active_agent_count++; + env->agents[i].active_agent = 1; + } else if (env->init_mode != INIT_ONLY_CONTROLLABLE_AGENTS) { + static_agent_indices[env->static_agent_count] = i; + env->static_agent_count++; + env->agents[i].active_agent = 0; + if(env->agents[i].mark_as_expert == 1 || env->active_agent_count == env->num_max_agents) { + expert_static_agent_indices[env->expert_static_agent_count] = i; + env->expert_static_agent_count++; + env->agents[i].mark_as_expert = 1; + } + } + } + + // Set up initial active agents + env->active_agent_indices = (int*)malloc(env->active_agent_count * sizeof(int)); + env->static_agent_indices = (int*)malloc(env->static_agent_count * sizeof(int)); + env->expert_static_agent_indices = (int*)malloc(env->expert_static_agent_count * sizeof(int)); + for(int i=0;iactive_agent_count;i++){ + env->active_agent_indices[i] = active_agent_indices[i]; + }; + for(int i=0;istatic_agent_count;i++){ + env->static_agent_indices[i] = static_agent_indices[i]; + } + for(int i=0;iexpert_static_agent_count;i++){ + env->expert_static_agent_indices[i] = expert_static_agent_indices[i]; + } + + return; +} + +void remove_bad_trajectories(Drive* env){ + + if (env->control_mode != CONTROL_TRACKS_TO_PREDICT) { + return; // Leave all trajectories in WOSAC control mode + } + + set_start_position(env); + int collided_agents[env->active_agent_count]; + int collided_with_indices[env->active_agent_count]; + memset(collided_agents, 0, env->active_agent_count * sizeof(int)); + for (int i = 0; i < env->active_agent_count; ++i) { + collided_with_indices[i] = -1; + } // move experts through trajectories to check for collisions and remove as illegal agents - for(int t = 0; t < TRAJECTORY_LENGTH; t++){ + for(int t = 0; t < env->scenario_length; t++){ for(int i = 0; i < env->active_agent_count; i++){ int agent_idx = env->active_agent_indices[i]; move_expert(env, env->actions, agent_idx); } - for(int i = 0; i < env->expert_static_car_count; i++){ - int expert_idx = env->expert_static_car_indices[i]; - if(env->entities[expert_idx].x == -10000) continue; + for(int i = 0; i < env->expert_static_agent_count; i++){ + int expert_idx = env->expert_static_agent_indices[i]; + if(env->agents[expert_idx].sim_x == INVALID_POSITION) continue; move_expert(env, env->actions, expert_idx); } // check collisions for(int i = 0; i < env->active_agent_count; i++){ int agent_idx = env->active_agent_indices[i]; - env->entities[agent_idx].collision_state = 0; + env->agents[agent_idx].collision_state = 0; int collided_with_index = collision_check(env, agent_idx); - if(env->entities[agent_idx].collision_state > 0 && collided_agents[i] == 0){ + if((collided_with_index >= 0) && collided_agents[i] == 0){ collided_agents[i] = 1; collided_with_indices[i] = collided_with_index; } @@ -930,70 +1510,84 @@ void remove_bad_trajectories(Drive* env){ for(int i = 0; i< env->active_agent_count; i++){ if(collided_with_indices[i] == -1) continue; - for(int j = 0; j < env->static_car_count; j++){ - int static_car_idx = env->static_car_indices[j]; - if(static_car_idx != collided_with_indices[i]) continue; - env->entities[static_car_idx].traj_x[0] = -10000; - env->entities[static_car_idx].traj_y[0] = -10000; + for(int j = 0; j < env->static_agent_count; j++){ + int static_agent_idx = env->static_agent_indices[j]; + if(static_agent_idx != collided_with_indices[i]) continue; + env->agents[static_agent_idx].log_trajectory_x[0] = INVALID_POSITION; + env->agents[static_agent_idx].log_trajectory_y[0] = INVALID_POSITION; } } env->timestep = 0; } + +void init_goal_positions(Drive* env){ + for(int x = 0;xactive_agent_count; x++){ + int agent_idx = env->active_agent_indices[x]; + env->agents[agent_idx].init_goal_x = env->agents[agent_idx].goal_position_x; + env->agents[agent_idx].init_goal_y = env->agents[agent_idx].goal_position_y; + } +} + void init(Drive* env){ env->human_agent_idx = 0; env->timestep = 0; load_map_binary(env->map_name, env); - if (env->entities == NULL || env->map_entities == NULL) { - printf("ERROR: Failed to load map: %s\n", env->map_name); - exit(1); // Or handle gracefully - } - env->dynamics_model = CLASSIC; set_means(env); init_grid_map(env); - env->vision_range = 21; + if (env->goal_behavior==GOAL_GENERATE_NEW) init_topology_graph(env); + env->grid_map->vision_range = 21; init_neighbor_offsets(env); - env->neighbor_cache_indices = (int*)calloc((env->grid_cols*env->grid_rows) + 1, sizeof(int)); cache_neighbor_offsets(env); + env->logs_capacity = 0; set_active_agents(env); + env->logs_capacity = env->active_agent_count; remove_bad_trajectories(env); set_start_position(env); + init_goal_positions(env); env->logs = (Log*)calloc(env->active_agent_count, sizeof(Log)); } void c_close(Drive* env){ - for(int i = 0; i < env->num_objects; i++){ - free_entity(&env->entities[i]); - } - free(env->entities); - for(int i = 0; i < env->num_roads; i++){ - free_map_entity(&env->map_entities[i]); - } - free(env->map_entities); + for(int i = 0; i < env->num_total_agents; i++) free_agent(&env->agents[i]); + for(int i = 0; i < env->num_road_elements; i++) free_road_element(&env->road_elements[i]); + for(int i = 0; i < env->num_traffic_elements; i++) free_traffic_element(&env->traffic_elements[i]); + free(env->agents); + free(env->road_elements); + free(env->traffic_elements); free(env->active_agent_indices); free(env->logs); - free(env->map_corners); - free(env->grid_cells); + // GridMap cleanup + int grid_cell_count = env->grid_map->grid_cols*env->grid_map->grid_rows; + for(int grid_index = 0; grid_index < grid_cell_count; grid_index++){ + free(env->grid_map->cells[grid_index]); + } + free(env->grid_map->cells); + free(env->grid_map->cell_entities_count); free(env->neighbor_offsets); - free(env->neighbor_cache_entities); - free(env->neighbor_cache_indices); - free(env->static_car_indices); - free(env->expert_static_car_indices); + + for(int i = 0; i < grid_cell_count; i++){ + free(env->grid_map->neighbor_cache_entities[i]); + } + free(env->grid_map->neighbor_cache_entities); + free(env->grid_map->neighbor_cache_count); + free(env->grid_map); + free(env->static_agent_indices); + free(env->expert_static_agent_indices); + free(env->objects_of_interest); + free(env->tracks_to_predict); + freeTopologyGraph(env->topology_graph); // free(env->map_name); + free(env->ini_file); } void allocate(Drive* env){ init(env); - int max_obs = 7 + 7*(MAX_CARS - 1) + 7*MAX_ROAD_SEGMENT_OBSERVATIONS; - // printf("max obs: %d\n", max_obs*env->active_agent_count); - // printf("num cars: %d\n", env->num_cars); - // printf("num static cars: %d\n", env->static_car_count); - // printf("active agent count: %d\n", env->active_agent_count); - // printf("num objects: %d\n", env->num_objects); + int ego_dim = (env->dynamics_model == JERK) ? 10 : 7; + int max_obs = ego_dim + 7*(MAX_AGENTS - 1) + 7*MAX_ROAD_SEGMENT_OBSERVATIONS; env->observations = (float*)calloc(env->active_agent_count*max_obs, sizeof(float)); - env->actions = (int*)calloc(env->active_agent_count*2, sizeof(int)); + env->actions = (float*)calloc(env->active_agent_count*2, sizeof(float)); env->rewards = (float*)calloc(env->active_agent_count, sizeof(float)); env->terminals= (unsigned char*)calloc(env->active_agent_count, sizeof(unsigned char)); - // printf("allocated\n"); } void free_allocated(Drive* env){ @@ -1017,115 +1611,272 @@ float normalize_heading(float heading){ return heading; } +float normalize_value(float value, float min, float max){ + return (value - min) / (max - min); +} + void move_dynamics(Drive* env, int action_idx, int agent_idx){ - if(env->dynamics_model == CLASSIC){ - // clip acceleration & steering - Entity* agent = &env->entities[agent_idx]; - // Extract action components directly from the multi-discrete action array - int (*action_array)[2] = (int(*)[2])env->actions; - int acceleration_index = action_array[action_idx][0]; - int steering_index = action_array[action_idx][1]; - float acceleration = ACCELERATION_VALUES[acceleration_index]; - float steering = STEERING_VALUES[steering_index]; + Agent* agent = &env->agents[agent_idx]; + if (agent->removed) return; + + if (agent->stopped) { + agent->sim_vx = 0.0f; + agent->sim_vy = 0.0f; + return; + } + + if (env->dynamics_model == CLASSIC) { + // Classic dynamics model + float acceleration = 0.0f; + float steering = 0.0f; + + if (env->action_type == 1) { // continuous + float (*action_array_f)[2] = (float(*)[2])env->actions; + acceleration = action_array_f[action_idx][0]; + steering = action_array_f[action_idx][1]; + + acceleration *= ACCELERATION_VALUES[6]; + steering *= STEERING_VALUES[12]; + } else { // discrete + // Interpret action as a single integer: a = accel_idx * num_steer + steer_idx + int* action_array = (int*)env->actions; + int num_accel = sizeof(ACCELERATION_VALUES) / sizeof(ACCELERATION_VALUES[0]); + int num_steer = sizeof(STEERING_VALUES) / sizeof(STEERING_VALUES[0]); + int action_val = action_array[action_idx]; + int acceleration_index = action_val / num_steer; + int steering_index = action_val % num_steer; + acceleration = ACCELERATION_VALUES[acceleration_index]; + steering = STEERING_VALUES[steering_index]; + } // Current state - float x = agent->x; - float y = agent->y; - float heading = agent->heading; - float vx = agent->vx; - float vy = agent->vy; - + float x = agent->sim_x; + float y = agent->sim_y; + float heading = agent->sim_heading; + float vx = agent->sim_vx; + float vy = agent->sim_vy; + float length = agent->sim_length; + // Calculate current speed float speed = sqrtf(vx*vx + vy*vy); - - // Time step (adjust as needed) - const float dt = 0.1f; + // Update speed with acceleration - speed = speed + 0.5f*acceleration*dt; - // if (speed < 0) speed = 0; // Prevent going backward + speed = speed + acceleration * env->dt; speed = clipSpeed(speed); - // compute yaw rate + + // Compute yaw rate float beta = tanh(.5*tanf(steering)); - // new heading - float yaw_rate = (speed*cosf(beta)*tanf(steering)) / agent->length; - // new velocity + + // New heading + float yaw_rate = (speed*cosf(beta)*tanf(steering)) / length; + + // New velocity float new_vx = speed*cosf(heading + beta); float new_vy = speed*sinf(heading + beta); + // Update position - x = x + (new_vx*dt); - y = y + (new_vy*dt); - heading = heading + yaw_rate*dt; - // heading = normalize_heading(heading); + x = x + (new_vx*env->dt); + y = y + (new_vy*env->dt); + heading = heading + yaw_rate*env->dt; + // Apply updates to the agent's state - agent->x = x; - agent->y = y; - agent->heading = heading; - agent->heading_x = cosf(heading); - agent->heading_y = sinf(heading); - agent->vx = new_vx; - agent->vy = new_vy; + agent->sim_x = x; + agent->sim_y = y; + agent->sim_heading = heading; + agent->sim_vx = new_vx; + agent->sim_vy = new_vy; + } else { + // JERK dynamics model + // Extract action components + float a_long, a_lat; + if (env->action_type == 1) { // continuous + float (*action_array_f)[2] = (float(*)[2])env->actions; + + // Asymmetric scaling for longitudinal jerk to match discrete action space + // Discrete: JERK_LONG = [-15, -4, 0, 4] (more braking than acceleration) + float a_long_action = action_array_f[action_idx][0]; // [-1, 1] + if (a_long_action < 0) { + a_long = a_long_action * (-JERK_LONG[0]); // Negative: [-1, 0] → [-15, 0] (braking) + } else { + a_long = a_long_action * JERK_LONG[3]; // Positive: [0, 1] → [0, 4] (acceleration) + } + + // Symmetric scaling for lateral jerk + a_lat = action_array_f[action_idx][1] * JERK_LAT[2]; + } else { // discrete + int (*action_array)[2] = (int(*)[2])env->actions; + int a_long_idx = action_array[action_idx][0]; + int a_lat_idx = action_array[action_idx][1]; + a_long = JERK_LONG[a_long_idx]; + a_lat = JERK_LAT[a_lat_idx]; + } + + // Calculate new acceleration + float a_long_new = agent->a_long + a_long * env->dt; + float a_lat_new = agent->a_lat + a_lat * env->dt; + + // Make it easy to stop with 0 accel + if (agent->a_long * a_long_new < 0) { + a_long_new = 0.0f; + } else { + a_long_new = clip(a_long_new, -5.0f, 2.5f); + } + + if (agent->a_lat * a_lat_new < 0) { + a_lat_new = 0.0f; + } else { + a_lat_new = clip(a_lat_new, -4.0f, 4.0f); + } + + float heading_x = cosf(agent->sim_heading); + float heading_y = sinf(agent->sim_heading); + + // Calculate new velocity + float v_dot_heading = agent->sim_vx * heading_x + agent->sim_vy * heading_y; + float signed_v = copysignf(sqrtf(agent->sim_vx*agent->sim_vx + agent->sim_vy*agent->sim_vy), v_dot_heading); + float v_new = signed_v + 0.5f * (a_long_new + agent->a_long) * env->dt; + + // Make it easy to stop with 0 vel + if (signed_v * v_new < 0) { + v_new = 0.0f; + } else { + v_new = clip(v_new, -2.0f, 20.0f); + } + + // Calculate new steering angle + float signed_curvature = a_lat_new / fmaxf(v_new * v_new, 1e-5f); + signed_curvature = copysignf(fmaxf(fabsf(signed_curvature), 1e-5f), signed_curvature); + float steering_angle = atanf(signed_curvature * agent->wheelbase); + float delta_steer = clip(steering_angle - agent->steering_angle, -0.6f * env->dt, 0.6f * env->dt); + float new_steering_angle = clip(agent->steering_angle + delta_steer, -0.55f, 0.55f); + + // Update curvature and accel to account for limited steering + signed_curvature = tanf(new_steering_angle) / agent->wheelbase; + a_lat_new = v_new * v_new * signed_curvature; + + // Calculate resulting movement using bicycle dynamics + float d = 0.5f * (v_new + signed_v) * env->dt; + float theta = d * signed_curvature; + float dx_local, dy_local; + + if (fabsf(signed_curvature) < 1e-5f || fabsf(theta) < 1e-5f) { + dx_local = d; + dy_local = 0.0f; + } else { + dx_local = sinf(theta) / signed_curvature; + dy_local = (1.0f - cosf(theta)) / signed_curvature; + } + + float dx = dx_local * heading_x - dy_local * heading_y; + float dy = dx_local * heading_y + dy_local * heading_x; + + // Update agent state + agent->sim_x += dx; + agent->sim_y += dy; + agent->jerk_long = (a_long_new - agent->a_long) / env->dt; + agent->jerk_lat = (a_lat_new - agent->a_lat) / env->dt; + agent->a_long = a_long_new; + agent->a_lat = a_lat_new; + agent->sim_heading = normalize_heading(agent->sim_heading + theta); + agent->sim_vx = v_new * cosf(agent->sim_heading); + agent->sim_vy = v_new * sinf(agent->sim_heading); + agent->steering_angle = new_steering_angle; } + return; } -float normalize_value(float value, float min, float max){ - return (value - min) / (max - min); +void c_get_global_agent_state(Drive* env, float* x_out, float* y_out, float* z_out, float* heading_out, int* id_out) { + for(int i = 0; i < env->active_agent_count; i++){ + int agent_idx = env->active_agent_indices[i]; + Agent* agent = &env->agents[agent_idx]; + + // For WOSAC, we need the original world coordinates, so we add the world means back + x_out[i] = agent->sim_x + env->world_mean_x; + y_out[i] = agent->sim_y + env->world_mean_y; + z_out[i] = agent->sim_z; + heading_out[i] = agent->sim_heading; + id_out[i] = env->tracks_to_predict[i]; + } } -float reverse_normalize_value(float value, float min, float max){ - return value*50.0f; +void c_get_global_ground_truth_trajectories(Drive* env, float* x_out, float* y_out, float* z_out, float* heading_out, int* valid_out, int* id_out, int* scenario_id_out) { + for(int i = 0; i < env->active_agent_count; i++){ + int agent_idx = env->active_agent_indices[i]; + Agent* agent = &env->agents[agent_idx]; + id_out[i] = env->tracks_to_predict[i]; + scenario_id_out[i] = env->map_index; + + for(int t = env->init_steps; t < agent->trajectory_length; t++){ + int out_idx = i * (agent->trajectory_length - env->init_steps) + (t - env->init_steps); + // Add world means back to get original world coordinates + x_out[out_idx] = agent->log_trajectory_x[t] + env->world_mean_x; + y_out[out_idx] = agent->log_trajectory_y[t] + env->world_mean_y; + z_out[out_idx] = agent->log_trajectory_z[t]; + heading_out[out_idx] = agent->log_heading[t]; + valid_out[out_idx] = agent->log_valid[t]; + } + } } void compute_observations(Drive* env) { - int max_obs = 7 + 7*(MAX_CARS - 1) + 7*MAX_ROAD_SEGMENT_OBSERVATIONS; + int ego_dim = (env->dynamics_model == JERK) ? 10 : 7; + int max_obs = ego_dim + 7*(MAX_AGENTS - 1) + 7*MAX_ROAD_SEGMENT_OBSERVATIONS; memset(env->observations, 0, max_obs*env->active_agent_count*sizeof(float)); - float (*observations)[max_obs] = (float(*)[max_obs])env->observations; + float (*observations)[max_obs] = (float(*)[max_obs])env->observations; for(int i = 0; i < env->active_agent_count; i++) { float* obs = &observations[i][0]; - Entity* ego_entity = &env->entities[env->active_agent_indices[i]]; - if(ego_entity->type > 3) break; - if(ego_entity->respawn_timestep != -1) { - obs[6] = 1; - //continue; - } - float cos_heading = ego_entity->heading_x; - float sin_heading = ego_entity->heading_y; - float ego_speed = sqrtf(ego_entity->vx*ego_entity->vx + ego_entity->vy*ego_entity->vy); + Agent* ego_entity = &env->agents[env->active_agent_indices[i]]; + + float ego_heading = ego_entity->sim_heading; + float cos_heading = cosf(ego_heading); + float sin_heading = sinf(ego_heading); + float ego_speed = sqrtf(ego_entity->sim_vx*ego_entity->sim_vx + ego_entity->sim_vy*ego_entity->sim_vy); + // Set goal distances - float goal_x = ego_entity->goal_position_x - ego_entity->x; - float goal_y = ego_entity->goal_position_y - ego_entity->y; + float goal_x = ego_entity->goal_position_x - ego_entity->sim_x; + float goal_y = ego_entity->goal_position_y - ego_entity->sim_y; + // Rotate to ego vehicle's frame float rel_goal_x = goal_x*cos_heading + goal_y*sin_heading; float rel_goal_y = -goal_x*sin_heading + goal_y*cos_heading; - //obs[0] = normalize_value(rel_goal_x, MIN_REL_GOAL_COORD, MAX_REL_GOAL_COORD); - //obs[1] = normalize_value(rel_goal_y, MIN_REL_GOAL_COORD, MAX_REL_GOAL_COORD); + obs[0] = rel_goal_x* 0.005f; obs[1] = rel_goal_y* 0.005f; - //obs[2] = ego_speed / MAX_SPEED; - obs[2] = ego_speed * 0.01f; - obs[3] = ego_entity->width / MAX_VEH_WIDTH; - obs[4] = ego_entity->length / MAX_VEH_LEN; - obs[5] = (ego_entity->collision_state > 0) ? 1 : 0; - + obs[2] = ego_speed / MAX_SPEED; + obs[3] = ego_entity->sim_width / MAX_VEH_WIDTH; + obs[4] = ego_entity->sim_length / MAX_VEH_LEN; + obs[5] = (ego_entity->collision_state > 0) ? 1.0f : 0.0f; + + if (env->dynamics_model == JERK) { + obs[6] = ego_entity->steering_angle / M_PI; + // Asymmetric normalization for a_long to match action space + obs[7] = (ego_entity->a_long < 0) ? ego_entity->a_long / (-JERK_LONG[0]) : ego_entity->a_long / JERK_LONG[3]; + obs[8] = ego_entity->a_lat / JERK_LAT[2]; + obs[9] = (ego_entity->respawn_timestep != -1) ? 1 : 0; + } else { + obs[6] = (ego_entity->respawn_timestep != -1) ? 1 : 0; + } + // Relative Pos of other cars - int obs_idx = 7; // Start after goal distances + int obs_idx = ego_dim; int cars_seen = 0; - for(int j = 0; j < MAX_CARS; j++) { + for(int j = 0; j < MAX_AGENTS; j++) { int index = -1; if(j < env->active_agent_count){ index = env->active_agent_indices[j]; - } else if (j < env->num_cars){ - index = env->static_car_indices[j - env->active_agent_count]; - } + } else if (j < env->num_actors){ + index = env->static_agent_indices[j - env->active_agent_count]; + } if(index == -1) continue; - if(env->entities[index].type > 3) break; + if(env->agents[index].type > 3) break; if(index == env->active_agent_indices[i]) continue; // Skip self, but don't increment obs_idx - Entity* other_entity = &env->entities[index]; + Agent* other_entity = &env->agents[index]; if(ego_entity->respawn_timestep != -1) continue; if(other_entity->respawn_timestep != -1) continue; // Store original relative positions - float dx = other_entity->x - ego_entity->x; - float dy = other_entity->y - ego_entity->y; + float dx = other_entity->sim_x - ego_entity->sim_x; + float dy = other_entity->sim_y - ego_entity->sim_y; float dist = (dx*dx + dy*dy); if(dist > 2500.0f) continue; // Rotate to ego vehicle's frame @@ -1134,43 +1885,66 @@ void compute_observations(Drive* env) { // Store observations with correct indexing obs[obs_idx] = rel_x * 0.02f; obs[obs_idx + 1] = rel_y * 0.02f; - obs[obs_idx + 2] = other_entity->width / MAX_VEH_WIDTH; - obs[obs_idx + 3] = other_entity->length / MAX_VEH_LEN; + obs[obs_idx + 2] = other_entity->sim_width / MAX_VEH_WIDTH; + obs[obs_idx + 3] = other_entity->sim_length / MAX_VEH_LEN; // relative heading - float rel_heading_x = other_entity->heading_x * ego_entity->heading_x + - other_entity->heading_y * ego_entity->heading_y; // cos(a-b) = cos(a)cos(b) + sin(a)sin(b) - float rel_heading_y = other_entity->heading_y * ego_entity->heading_x - - other_entity->heading_x * ego_entity->heading_y; // sin(a-b) = sin(a)cos(b) - cos(a)sin(b) + float other_heading = other_entity->sim_heading; + float other_cos = cosf(other_heading); + float other_sin = sinf(other_heading); + float rel_heading_x = other_cos * cos_heading + + other_sin * sin_heading; // cos(a-b) = cos(a)cos(b) + sin(a)sin(b) + float rel_heading_y = other_sin * cos_heading - + other_cos * sin_heading; // sin(a-b) = sin(a)cos(b) - cos(a)sin(b) obs[obs_idx + 4] = rel_heading_x; obs[obs_idx + 5] = rel_heading_y; // obs[obs_idx + 4] = cosf(rel_heading) / MAX_ORIENTATION_RAD; // obs[obs_idx + 5] = sinf(rel_heading) / MAX_ORIENTATION_RAD; // // relative speed - float other_speed = sqrtf(other_entity->vx*other_entity->vx + other_entity->vy*other_entity->vy); + float other_speed = sqrtf(other_entity->sim_vx*other_entity->sim_vx + other_entity->sim_vy*other_entity->sim_vy); obs[obs_idx + 6] = other_speed / MAX_SPEED; cars_seen++; obs_idx += 7; // Move to next observation slot } - int remaining_partner_obs = (MAX_CARS - 1 - cars_seen) * 7; + int remaining_partner_obs = (MAX_AGENTS - 1 - cars_seen) * 7; memset(&obs[obs_idx], 0, remaining_partner_obs * sizeof(float)); obs_idx += remaining_partner_obs; // map observations - int entity_list[MAX_ROAD_SEGMENT_OBSERVATIONS*2]; // Array big enough for all neighboring cells - int grid_idx = getGridIndex(env, ego_entity->x, ego_entity->y); + GridMapEntity entity_list[MAX_ENTITIES_PER_CELL*25]; + int grid_idx = getGridIndex(env, ego_entity->sim_x, ego_entity->sim_y); + int list_size = get_neighbor_cache_entities(env, grid_idx, entity_list, MAX_ROAD_SEGMENT_OBSERVATIONS); - for(int k = 0; k < list_size; k++){ - int entity_idx = entity_list[k*2]; - int geometry_idx = entity_list[k*2+1]; - MapEntity* entity = &env->map_entities[entity_idx]; - float start_x = entity->traj_x[geometry_idx]; - float start_y = entity->traj_y[geometry_idx]; - float end_x = entity->traj_x[geometry_idx+1]; - float end_y = entity->traj_y[geometry_idx+1]; + + for(int k = 0; k < list_size; k++) { + int entity_type = entity_list[k].entity_type; + int entity_idx = entity_list[k].entity_idx; + int geometry_idx = entity_list[k].geometry_idx; + + // Only process road elements in observations + if(entity_type != ENTITY_TYPE_ROAD_ELEMENT) continue; + + // Validate entity_idx before accessing + if(entity_idx < 0 || entity_idx >= env->num_road_elements) { + printf("ERROR: Invalid road element idx %d (max: %d)\n", entity_idx, env->num_road_elements-1); + continue; + } + + RoadMapElement* element = &env->road_elements[entity_idx]; + + // Validate geometry_idx before accessing + if(geometry_idx < 0 || geometry_idx > element->segment_length) { + printf("ERROR: Invalid geometry_idx %d for road element %d (max: %d)\n", + geometry_idx, entity_idx, element->segment_length-1); + continue; + } + float start_x = element->x[geometry_idx]; + float start_y = element->y[geometry_idx]; + float end_x = element->x[geometry_idx+1]; + float end_y = element->y[geometry_idx+1]; float mid_x = (start_x + end_x) / 2.0f; float mid_y = (start_y + end_y) / 2.0f; - float rel_x = mid_x - ego_entity->x; - float rel_y = mid_y - ego_entity->y; + float rel_x = mid_x - ego_entity->sim_x; + float rel_y = mid_y - ego_entity->sim_y; float x_obs = rel_x*cos_heading + rel_y*sin_heading; float y_obs = -rel_x*sin_heading + rel_y*cos_heading; float length = relative_distance_2d(mid_x, mid_y, end_x, end_y); @@ -1194,7 +1968,7 @@ void compute_observations(Drive* env) { obs[obs_idx + 3] = width / MAX_ROAD_SCALE; obs[obs_idx + 4] = cos_angle; obs[obs_idx + 5] = sin_angle; - obs[obs_idx + 6] = entity->type - 4.0f; + obs[obs_idx + 6] = normalize_road_type(element->type); obs_idx += 7; } int remaining_obs = (MAX_ROAD_SEGMENT_OBSERVATIONS - list_size) * 7; @@ -1203,47 +1977,215 @@ void compute_observations(Drive* env) { } } +static int find_forward_projection_on_lane(RoadMapElement* lane, Agent* agent, int timestep, int* out_segment_idx, float* out_fraction) { + int best_idx = -1; + float best_dist_sq = 1e30f; + + float agent_x = agent->sim_x; + float agent_y = agent->sim_y; + float agent_heading = agent->sim_heading; + float agent_heading_x = cosf(agent_heading); + float agent_heading_y = sinf(agent_heading); + + for (int i = 1; i < lane->segment_length; i++) { + float x0 = lane->x[i - 1]; + float y0 = lane->y[i - 1]; + float x1 = lane->x[i]; + float y1 = lane->y[i]; + float dx = x1 - x0; + float dy = y1 - y0; + float seg_len_sq = dx * dx + dy * dy; + if (seg_len_sq < 1e-6f) continue; + + float to_agent_x = agent_x - x0; + float to_agent_y = agent_y - y0; + float t = (to_agent_x * dx + to_agent_y * dy) / seg_len_sq; + if (t < 0.0f) t = 0.0f; + else if (t > 1.0f) t = 1.0f; + + float proj_x = x0 + t * dx; + float proj_y = y0 + t * dy; + + float rel_x = proj_x - agent_x; + float rel_y = proj_y - agent_y; + float forward = rel_x * agent_heading_x + rel_y * agent_heading_y; + if (forward < 0.0f) continue; + + float dist_sq = rel_x * rel_x + rel_y * rel_y; + if (dist_sq < best_dist_sq) { + best_dist_sq = dist_sq; + best_idx = i; + *out_fraction = t; + } + } + + if (best_idx != -1) { + *out_segment_idx = best_idx; + return 1; + } + + return 0; +} + +void compute_new_goal(Drive* env, int agent_idx) { + Agent* agent = &env->agents[agent_idx]; + int t = env->timestep; + int current_lane = agent->current_lane_idx; + + if (current_lane == -1) return; // No current lane + + // Target distance: 40m ahead along the lane topology from agent's current position + float target_distance = 40.0f; + int current_entity = current_lane; + RoadMapElement* lane = &env->road_elements[current_entity]; + + float agent_x = agent->sim_x; + float agent_y = agent->sim_y; + float agent_heading = agent->sim_heading; + float agent_heading_x = cosf(agent_heading); + float agent_heading_y = sinf(agent_heading); + + int initial_segment_idx = 1; + float initial_fraction = 0.0f; + if (!find_forward_projection_on_lane(lane, agent, t, &initial_segment_idx, &initial_fraction)) { + int forward_idx = -1; + for (int i = 0; i < lane->segment_length; i++) { + float to_point_x = lane->x[i] - agent_x; + float to_point_y = lane->y[i] - agent_y; + float dot = to_point_x * agent_heading_x + to_point_y * agent_heading_y; + if (dot > 0.0f) { + forward_idx = i; + break; + } + } + + if (forward_idx == -1) { + agent->goal_position_x = lane->x[lane->segment_length - 1]; + agent->goal_position_y = lane->y[lane->segment_length - 1]; + agent->sampled_new_goal = 0; + return; + } + + initial_segment_idx = forward_idx; + if (initial_segment_idx == 0) initial_segment_idx = 1; + initial_fraction = 0.0f; + } + + float remaining_distance = target_distance; + int first_lane = 1; + + // Traverse the topology graph starting from the vehicle's position forward + while (current_entity != -1) { + lane = &env->road_elements[current_entity]; + + int start_idx = first_lane ? initial_segment_idx : 1; + // Ensure start_idx is at least 1 to avoid accessing x[i-1] with i=0 + if (start_idx < 1) start_idx = 1; + first_lane = 0; + + for (int i = start_idx; i < lane->segment_length; i++) { + float prev_x = lane->x[i - 1]; + float prev_y = lane->y[i - 1]; + float next_x = lane->x[i]; + float next_y = lane->y[i]; + float seg_dx = next_x - prev_x; + float seg_dy = next_y - prev_y; + float segment_length = relative_distance_2d(prev_x, prev_y, next_x, next_y); + + if (remaining_distance <= segment_length) { + agent->goal_position_x = next_x; + agent->goal_position_y = next_y; + agent->sampled_new_goal = 0; + return; + } + + remaining_distance -= segment_length; + } + + int connected_lanes[5]; + int num_connected = getNextLanes(env->topology_graph, current_entity, connected_lanes, 5); + + if (num_connected == 0) { + agent->goal_position_x = lane->x[lane->segment_length - 1]; + agent->goal_position_y = lane->y[lane->segment_length - 1]; + agent->sampled_new_goal = 0; + return; // No further lanes to traverse + } + + int random_idx = agent_idx % num_connected; + current_entity = connected_lanes[random_idx]; + } +} + void c_reset(Drive* env){ - env->timestep = 0; + env->timestep = env->init_steps; set_start_position(env); for(int x = 0;xactive_agent_count; x++){ env->logs[x] = (Log){0}; int agent_idx = env->active_agent_indices[x]; - env->entities[agent_idx].respawn_timestep = -1; - env->entities[agent_idx].reached_goal = 0; - env->entities[agent_idx].collided_before_goal = 0; - env->entities[agent_idx].reached_goal_this_episode = 0; - collision_check(env, agent_idx); + env->agents[agent_idx].respawn_timestep = -1; + env->agents[agent_idx].respawn_count = 0; + env->agents[agent_idx].collided_before_goal = 0; + env->agents[agent_idx].reached_goal_this_episode = 0; + env->agents[agent_idx].metrics_array[COLLISION_IDX] = 0.0f; + env->agents[agent_idx].metrics_array[OFFROAD_IDX] = 0.0f; + env->agents[agent_idx].metrics_array[REACHED_GOAL_IDX] = 0.0f; + env->agents[agent_idx].metrics_array[LANE_ALIGNED_IDX] = 0.0f; + env->agents[agent_idx].metrics_array[AVG_DISPLACEMENT_ERROR_IDX] = 0.0f; + env->agents[agent_idx].cumulative_displacement = 0.0f; + env->agents[agent_idx].displacement_sample_count = 0; + env->agents[agent_idx].stopped = 0; + env->agents[agent_idx].removed = 0; + + if (env->goal_behavior==GOAL_GENERATE_NEW) { + env->agents[agent_idx].goal_position_x = env->agents[agent_idx].init_goal_x; + env->agents[agent_idx].goal_position_y = env->agents[agent_idx].init_goal_y; + env->agents[agent_idx].sampled_new_goal = 0; + } + + compute_agent_metrics(env, agent_idx); } compute_observations(env); } void respawn_agent(Drive* env, int agent_idx){ - env->entities[agent_idx].x = env->entities[agent_idx].traj_x[0]; - env->entities[agent_idx].y = env->entities[agent_idx].traj_y[0]; - env->entities[agent_idx].heading = env->entities[agent_idx].traj_heading[0]; - env->entities[agent_idx].heading_x = cosf(env->entities[agent_idx].heading); - env->entities[agent_idx].heading_y = sinf(env->entities[agent_idx].heading); - env->entities[agent_idx].vx = env->entities[agent_idx].traj_vx[0]; - env->entities[agent_idx].vy = env->entities[agent_idx].traj_vy[0]; - env->entities[agent_idx].reached_goal = 0; - env->entities[agent_idx].respawn_timestep = env->timestep; + Agent* agent = &env->agents[agent_idx]; + agent->sim_x = agent->log_trajectory_x[0]; + agent->sim_y = agent->log_trajectory_y[0]; + agent->sim_heading = agent->log_heading[0]; + agent->sim_vx = agent->log_velocity_x[0]; + agent->sim_vy = agent->log_velocity_y[0]; + agent->metrics_array[COLLISION_IDX] = 0.0f; + agent->metrics_array[OFFROAD_IDX] = 0.0f; + agent->metrics_array[REACHED_GOAL_IDX] = 0.0f; + agent->metrics_array[LANE_ALIGNED_IDX] = 0.0f; + agent->metrics_array[AVG_DISPLACEMENT_ERROR_IDX] = 0.0f; + agent->cumulative_displacement = 0.0f; + agent->displacement_sample_count = 0; + agent->respawn_timestep = env->timestep; + agent->stopped = 0; + agent->removed = 0; + agent->a_long = 0.0f; + agent->a_lat = 0.0f; + agent->jerk_long = 0.0f; + agent->jerk_lat = 0.0f; + agent->steering_angle = 0.0f; } void c_step(Drive* env){ memset(env->rewards, 0, env->active_agent_count * sizeof(float)); memset(env->terminals, 0, env->active_agent_count * sizeof(unsigned char)); env->timestep++; - if(env->timestep == TRAJECTORY_LENGTH){ + if(env->timestep == env->scenario_length){ add_log(env); c_reset(env); - return; + return; } - // Move statix experts - for (int i = 0; i < env->expert_static_car_count; i++) { - int expert_idx = env->expert_static_car_indices[i]; - if(env->entities[expert_idx].x == -10000.0f) continue; + // Move static experts + for (int i = 0; i < env->expert_static_agent_count; i++) { + int expert_idx = env->expert_static_agent_indices[i]; + if(env->agents[expert_idx].sim_x == INVALID_POSITION) continue; move_expert(env, env->actions, expert_idx); } // Process actions for all active agents @@ -1251,72 +2193,103 @@ void c_step(Drive* env){ env->logs[i].score = 0.0f; env->logs[i].episode_length += 1; int agent_idx = env->active_agent_indices[i]; - env->entities[agent_idx].collision_state = 0; + env->agents[agent_idx].collision_state = 0; move_dynamics(env, i, agent_idx); - // move_expert(env, env->actions, agent_idx); } for(int i = 0; i < env->active_agent_count; i++){ int agent_idx = env->active_agent_indices[i]; - env->entities[agent_idx].collision_state = 0; - //if(env->entities[agent_idx].respawn_timestep != -1) continue; - collision_check(env, agent_idx); - int collision_state = env->entities[agent_idx].collision_state; - + env->agents[agent_idx].collision_state = 0; + + compute_agent_metrics(env, agent_idx); + int collision_state = env->agents[agent_idx].collision_state; + if(collision_state > 0){ - if(collision_state == VEHICLE_COLLISION && env->entities[agent_idx].respawn_timestep == -1){ - if(env->entities[agent_idx].respawn_timestep != -1) { - env->rewards[i] = env->reward_vehicle_collision_post_respawn; - env->logs[i].episode_return += env->reward_vehicle_collision_post_respawn; - } else { - env->rewards[i] = env->reward_vehicle_collision; - env->logs[i].episode_return += env->reward_vehicle_collision; - env->logs[i].clean_collision_rate = 1.0f; - } + if(collision_state == VEHICLE_COLLISION){ + env->rewards[i] = env->reward_vehicle_collision; + env->logs[i].episode_return += env->reward_vehicle_collision; env->logs[i].collision_rate = 1.0f; + env->logs[i].avg_collisions_per_agent += 1.0f; } else if(collision_state == OFFROAD){ env->rewards[i] = env->reward_offroad_collision; env->logs[i].offroad_rate = 1.0f; env->logs[i].episode_return += env->reward_offroad_collision; + env->logs[i].avg_offroad_per_agent += 1.0f; } - if(!env->entities[agent_idx].reached_goal_this_episode){ - env->entities[agent_idx].collided_before_goal = 1; + if(!env->agents[agent_idx].reached_goal_this_episode){ + env->agents[agent_idx].collided_before_goal = 1; } - //printf("agent %d collided\n", agent_idx); } float distance_to_goal = relative_distance_2d( - env->entities[agent_idx].x, - env->entities[agent_idx].y, - env->entities[agent_idx].goal_position_x, - env->entities[agent_idx].goal_position_y); - if(distance_to_goal < 2.0f){ - if(env->entities[agent_idx].respawn_timestep != -1){ + env->agents[agent_idx].sim_x, + env->agents[agent_idx].sim_y, + env->agents[agent_idx].goal_position_x, + env->agents[agent_idx].goal_position_y + ); + + // Reward agent if it is within X meters of goal + if (distance_to_goal < env->goal_radius){ + if (env->goal_behavior == GOAL_RESPAWN && env->agents[agent_idx].respawn_timestep != -1){ env->rewards[i] += env->reward_goal_post_respawn; env->logs[i].episode_return += env->reward_goal_post_respawn; - } else { - env->rewards[i] += 1.0f; - env->logs[i].episode_return += 1.0f; - //env->terminals[i] = 1; + } else if (env->goal_behavior == GOAL_GENERATE_NEW) { + env->rewards[i] += env->reward_goal; + env->logs[i].episode_return += env->reward_goal; + env->agents[agent_idx].sampled_new_goal = 1; + env->logs[i].num_goals_reached += 1; + } else { // Zero out the velocity so that the agent stops at the goal + env->rewards[i] = env->reward_goal; + env->logs[i].episode_return = env->reward_goal; + env->logs[i].num_goals_reached = 1; + env->agents[agent_idx].stopped = 1; + env->agents[agent_idx].sim_vx=env->agents[agent_idx].sim_vy = 0.0f; } - env->entities[agent_idx].reached_goal = 1; - env->entities[agent_idx].reached_goal_this_episode = 1; + env->agents[agent_idx].reached_goal_this_episode = 1; + env->agents[agent_idx].metrics_array[REACHED_GOAL_IDX] = 1.0f; } + + if(env->agents[agent_idx].sampled_new_goal && env->goal_behavior == GOAL_GENERATE_NEW){ + compute_new_goal(env, agent_idx); + } + + + int lane_aligned = env->agents[agent_idx].metrics_array[LANE_ALIGNED_IDX]; + env->logs[i].lane_alignment_rate = lane_aligned; + + // Apply ADE reward + float current_ade = env->agents[agent_idx].metrics_array[AVG_DISPLACEMENT_ERROR_IDX]; + if(current_ade > 0.0f && env->reward_ade != 0.0f) { + float ade_reward = env->reward_ade * current_ade; + env->rewards[i] += ade_reward; + env->logs[i].episode_return += ade_reward; + } + env->logs[i].avg_displacement_error = current_ade; } - for(int i = 0; i < env->active_agent_count; i++){ - int agent_idx = env->active_agent_indices[i]; - int reached_goal = env->entities[agent_idx].reached_goal; - int collision_state = env->entities[agent_idx].collision_state; - if(reached_goal){ - respawn_agent(env, agent_idx); - //env->entities[agent_idx].x = -10000; - //env->entities[agent_idx].y = -10000; - //env->entities[agent_idx].respawn_timestep = env->timestep; + if (env->goal_behavior==GOAL_RESPAWN) { + for(int i = 0; i < env->active_agent_count; i++){ + int agent_idx = env->active_agent_indices[i]; + int reached_goal = env->agents[agent_idx].metrics_array[REACHED_GOAL_IDX]; + if(reached_goal){ + respawn_agent(env, agent_idx); + env->agents[agent_idx].respawn_count++; + } + } + } + else if (env->goal_behavior==GOAL_STOP) { + for(int i = 0; i < env->active_agent_count; i++){ + int agent_idx = env->active_agent_indices[i]; + int reached_goal = env->agents[agent_idx].metrics_array[REACHED_GOAL_IDX]; + if(reached_goal){ + env->agents[agent_idx].stopped = 1; + env->agents[agent_idx].sim_vx = env->agents[agent_idx].sim_vy = 0.0f; + } } } + compute_observations(env); -} +} const Color STONE_GRAY = (Color){80, 80, 80, 255}; const Color PUFF_RED = (Color){187, 0, 0, 255}; @@ -1324,6 +2297,7 @@ const Color PUFF_CYAN = (Color){0, 187, 187, 255}; const Color PUFF_WHITE = (Color){241, 241, 241, 241}; const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; const Color PUFF_BACKGROUND2 = (Color){18, 72, 72, 255}; +const Color LIGHTGREEN = (Color){152, 255, 152, 255}; typedef struct Client Client; struct Client { @@ -1333,8 +2307,8 @@ struct Client { Vector3 camera_target; float camera_zoom; Camera3D camera; - Model cars[6]; - int car_assignments[MAX_CARS]; // To keep car model assignments consistent per vehicle + Model cars[6]; + int car_assignments[MAX_AGENTS]; // To keep car model assignments consistent per vehicle Vector3 default_camera_position; Vector3 default_camera_target; }; @@ -1353,20 +2327,18 @@ Client* make_client(Drive* env){ client->cars[3] = LoadModel("resources/drive/YellowCar.glb"); client->cars[4] = LoadModel("resources/drive/GreenCar.glb"); client->cars[5] = LoadModel("resources/drive/GreyCar.glb"); - for (int i = 0; i < MAX_CARS; i++) { + for (int i = 0; i < MAX_AGENTS; i++) { client->car_assignments[i] = (rand() % 4) + 1; } // Get initial target position from first active agent - float map_center_x = (env->map_corners[0] + env->map_corners[2]) / 2.0f; - float map_center_y = (env->map_corners[1] + env->map_corners[3]) / 2.0f; Vector3 target_pos = { - 0, + 0, 0, // Y is up 1 // Z is depth }; - + // Set up camera to look at target from above and behind - client->default_camera_position = (Vector3){ + client->default_camera_position = (Vector3){ 0, // Same X as target 120.0f, // 20 units above target 175.0f // 20 units behind target @@ -1386,17 +2358,17 @@ void handle_camera_controls(Client* client) { static Vector2 prev_mouse_pos = {0}; static bool is_dragging = false; float camera_move_speed = 0.5f; - + // Handle mouse drag for camera movement if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { prev_mouse_pos = GetMousePosition(); is_dragging = true; } - + if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { is_dragging = false; } - + if (is_dragging) { Vector2 current_mouse_pos = GetMousePosition(); Vector2 delta = { @@ -1407,7 +2379,7 @@ void handle_camera_controls(Client* client) { // Update camera position (only X and Y) client->camera.position.x += delta.x; client->camera.position.y += delta.y; - + // Update camera target (only X and Y) client->camera.target.x += delta.x; client->camera.target.y += delta.y; @@ -1425,12 +2397,12 @@ void handle_camera_controls(Client* client) { client->camera.position.y - client->camera.target.y, client->camera.position.z - client->camera.target.z }; - + // Scale the direction vector by the zoom factor direction.x *= zoom_factor; direction.y *= zoom_factor; direction.z *= zoom_factor; - + // Update the camera position based on the scaled direction client->camera.position.x = client->camera.target.x + direction.x; client->camera.position.y = client->camera.target.y + direction.y; @@ -1438,12 +2410,12 @@ void handle_camera_controls(Client* client) { } } -void draw_agent_obs(Drive* env, int agent_index){ +void draw_agent_obs(Drive* env, int agent_index, int mode, int obs_only, int lasers){ // Diamond dimensions float diamond_height = 3.0f; // Total height of diamond float diamond_width = 1.5f; // Width of diamond float diamond_z = 8.0f; // Base Z position - + // Define diamond points Vector3 top_point = (Vector3){0.0f, 0.0f, diamond_z + diamond_height/2}; // Top point Vector3 bottom_point = (Vector3){0.0f, 0.0f, diamond_z - diamond_height/2}; // Bottom point @@ -1451,32 +2423,54 @@ void draw_agent_obs(Drive* env, int agent_index){ Vector3 back_point = (Vector3){0.0f, -diamond_width/2, diamond_z}; // Back point Vector3 left_point = (Vector3){-diamond_width/2, 0.0f, diamond_z}; // Left point Vector3 right_point = (Vector3){diamond_width/2, 0.0f, diamond_z}; // Right point - + // Draw the diamond faces // Top pyramid - DrawTriangle3D(top_point, front_point, right_point, PUFF_CYAN); // Front-right face - DrawTriangle3D(top_point, right_point, back_point, PUFF_CYAN); // Back-right face - DrawTriangle3D(top_point, back_point, left_point, PUFF_CYAN); // Back-left face - DrawTriangle3D(top_point, left_point, front_point, PUFF_CYAN); // Front-left face - - // Bottom pyramid - DrawTriangle3D(bottom_point, right_point, front_point, PUFF_CYAN); // Front-right face - DrawTriangle3D(bottom_point, back_point, right_point, PUFF_CYAN); // Back-right face - DrawTriangle3D(bottom_point, left_point, back_point, PUFF_CYAN); // Back-left face - DrawTriangle3D(bottom_point, front_point, left_point, PUFF_CYAN); // Front-left face - if(!IsKeyDown(KEY_LEFT_CONTROL)){ + + if(mode ==0){ + DrawTriangle3D(top_point, front_point, right_point, PUFF_CYAN); // Front-right face + DrawTriangle3D(top_point, right_point, back_point, PUFF_CYAN); // Back-right face + DrawTriangle3D(top_point, back_point, left_point, PUFF_CYAN); // Back-left face + DrawTriangle3D(top_point, left_point, front_point, PUFF_CYAN); // Front-left face + + // Bottom pyramid + DrawTriangle3D(bottom_point, right_point, front_point, PUFF_CYAN); // Front-right face + DrawTriangle3D(bottom_point, back_point, right_point, PUFF_CYAN); // Back-right face + DrawTriangle3D(bottom_point, left_point, back_point, PUFF_CYAN); // Back-left face + DrawTriangle3D(bottom_point, front_point, left_point, PUFF_CYAN); // Front-left face + } + if(!IsKeyDown(KEY_LEFT_CONTROL) && obs_only==0){ return; } - int max_obs = 7 + 7*(MAX_CARS - 1) + 7*MAX_ROAD_SEGMENT_OBSERVATIONS; + + int ego_dim = (env->dynamics_model == JERK) ? 10 : 7; + int max_obs = ego_dim + 7*(MAX_AGENTS - 1) + 7*MAX_ROAD_SEGMENT_OBSERVATIONS; float (*observations)[max_obs] = (float(*)[max_obs])env->observations; float* agent_obs = &observations[agent_index][0]; + // self + int active_idx = env->active_agent_indices[agent_index]; + float heading_self = env->agents[active_idx].sim_heading; + float heading_self_x = cosf(heading_self); + float heading_self_y = sinf(heading_self); + float px = env->agents[active_idx].sim_x; + float py = env->agents[active_idx].sim_y; // draw goal float goal_x = agent_obs[0] * 200; float goal_y = agent_obs[1] * 200; - DrawSphere((Vector3){goal_x, goal_y, 1}, 0.5f, GREEN); + if(mode == 0 ){ + DrawSphere((Vector3){goal_x, goal_y, 1}, 0.5f, LIGHTGREEN); + DrawCircle3D((Vector3){goal_x, goal_y, 0.1f}, env->goal_radius, (Vector3){0, 0, 1}, 90.0f, Fade(LIGHTGREEN, 0.3f)); + } + + if (mode == 1){ + float goal_x_world = px + (goal_x * heading_self_x - goal_y*heading_self_y); + float goal_y_world = py + (goal_x * heading_self_y + goal_y*heading_self_x); + DrawSphere((Vector3){goal_x_world, goal_y_world, 1}, 0.5f, LIGHTGREEN); + DrawCircle3D((Vector3){goal_x_world, goal_y_world, 0.1f}, env->goal_radius, (Vector3){0, 0, 1}, 90.0f, Fade(LIGHTGREEN, 0.3f)); + } // First draw other agent observations - int obs_idx = 7; // Start after goal distances - for(int j = 0; j < MAX_CARS - 1; j++) { + int obs_idx = ego_dim; // Start after ego obs + for(int j = 0; j < MAX_AGENTS - 1; j++) { if(agent_obs[obs_idx] == 0 || agent_obs[obs_idx + 1] == 0) { obs_idx += 7; // Move to next agent observation continue; @@ -1484,19 +2478,89 @@ void draw_agent_obs(Drive* env, int agent_index){ // Draw position of other agents float x = agent_obs[obs_idx] * 50; float y = agent_obs[obs_idx + 1] * 50; - DrawLine3D( - (Vector3){0, 0, 0}, - (Vector3){x, y, 1}, - ORANGE - ); + if(lasers && mode == 0){ + DrawLine3D( + (Vector3){0, 0, 0}, + (Vector3){x, y, 1}, + ORANGE + ); + } + + float partner_x = px + (x*heading_self_x - y*heading_self_y); + float partner_y = py + (x*heading_self_y + y*heading_self_x); + if(lasers && mode ==1){ + DrawLine3D( + (Vector3){px, py, 1}, + (Vector3){partner_x,partner_y,1}, + ORANGE + ); + } + + float half_width = 0.5*agent_obs[obs_idx + 2]*MAX_VEH_WIDTH; + float half_len = 0.5*agent_obs[obs_idx + 3]*MAX_VEH_LEN; float theta_x = agent_obs[obs_idx + 4]; float theta_y = agent_obs[obs_idx + 5]; float partner_angle = atan2f(theta_y, theta_x); + float cos_heading = cosf(partner_angle); + float sin_heading = sinf(partner_angle); + Vector3 corners[4] = { + (Vector3){ + x + (half_len * cos_heading - half_width * sin_heading), + y + (half_len * sin_heading + half_width * cos_heading), + 1 + }, + (Vector3){ + x + (half_len * cos_heading + half_width * sin_heading), + y + (half_len * sin_heading - half_width * cos_heading), + 1 + }, + (Vector3){ + x + (-half_len * cos_heading + half_width * sin_heading), + y + (-half_len * sin_heading - half_width * cos_heading), + 1 + }, + (Vector3){ + x + (-half_len * cos_heading - half_width * sin_heading), + y + (-half_len * sin_heading + half_width * cos_heading), + 1 + }, + }; + + if(mode ==0){ + for (int j = 0; j < 4; j++) { + DrawLine3D(corners[j], corners[(j+1)%4], ORANGE); + } + } + + if(mode ==1){ + Vector3 world_corners[4]; + for (int j = 0; j < 4; j++) { + float lx = corners[j].x; + float ly = corners[j].y; + + world_corners[j].x = px + (lx * heading_self_x - ly * heading_self_y); + world_corners[j].y = py + (lx * heading_self_y + ly * heading_self_x); + world_corners[j].z = 1; + } + for (int j = 0; j < 4; j++) { + DrawLine3D(world_corners[j], world_corners[(j+1)%4], ORANGE); + } + } + // draw an arrow above the car pointing in the direction that the partner is going float arrow_length = 7.5f; float arrow_x = x + arrow_length*cosf(partner_angle); float arrow_y = y + arrow_length*sinf(partner_angle); - DrawLine3D((Vector3){x, y, 1}, (Vector3){arrow_x, arrow_y, 1}, PUFF_WHITE); + float arrow_x_world; + float arrow_y_world; + if(mode ==0){ + DrawLine3D((Vector3){x, y, 1}, (Vector3){arrow_x, arrow_y, 1}, PUFF_WHITE); + } + if(mode == 1){ + arrow_x_world = px + (arrow_x * heading_self_x - arrow_y*heading_self_y); + arrow_y_world = py + (arrow_x * heading_self_y + arrow_y*heading_self_x); + DrawLine3D((Vector3){partner_x, partner_y, 1}, (Vector3){arrow_x_world, arrow_y_world, 1}, PUFF_WHITE); + } // Calculate perpendicular offsets for arrow head float arrow_size = 2.0f; // Size of the arrow head float dx = arrow_x - x; @@ -1506,27 +2570,54 @@ void draw_agent_obs(Drive* env, int agent_index){ // Normalize direction vector dx /= length; dy /= length; - + // Calculate perpendicular vector - float px = -dy * arrow_size; - float py = dx * arrow_size; - + + float perp_x = -dy * arrow_size; + float perp_y = dx * arrow_size; + + float arrow_x_end1 = arrow_x - dx*arrow_size + perp_x; + float arrow_y_end1 = arrow_y - dy*arrow_size + perp_y; + float arrow_x_end2 = arrow_x - dx*arrow_size - perp_x; + float arrow_y_end2 = arrow_y - dy*arrow_size - perp_y; + // Draw the two lines forming the arrow head - DrawLine3D( - (Vector3){arrow_x, arrow_y, 1}, - (Vector3){arrow_x - dx*arrow_size + px, arrow_y - dy*arrow_size + py, 1}, - PUFF_WHITE - ); - DrawLine3D( - (Vector3){arrow_x, arrow_y, 1}, - (Vector3){arrow_x - dx*arrow_size - px, arrow_y - dy*arrow_size - py, 1}, - PUFF_WHITE - ); + if(mode ==0){ + DrawLine3D( + (Vector3){arrow_x, arrow_y, 1}, + (Vector3){arrow_x_end1, arrow_y_end1, 1}, + PUFF_WHITE + ); + DrawLine3D( + (Vector3){arrow_x, arrow_y, 1}, + (Vector3){arrow_x_end2, arrow_y_end2, 1}, + PUFF_WHITE + ); + } + + if(mode==1){ + float arrow_x_end1_world = px + (arrow_x_end1 * heading_self_x - arrow_y_end1*heading_self_y); + float arrow_y_end1_world = py + (arrow_x_end1 * heading_self_y + arrow_y_end1*heading_self_x); + float arrow_x_end2_world = px + (arrow_x_end2 * heading_self_x - arrow_y_end2*heading_self_y); + float arrow_y_end2_world = py + (arrow_x_end2 * heading_self_y + arrow_y_end2*heading_self_x); + DrawLine3D( + (Vector3){arrow_x_world, arrow_y_world, 1}, + (Vector3){arrow_x_end1_world, arrow_y_end1_world, 1}, + PUFF_WHITE + ); + DrawLine3D( + (Vector3){arrow_x_world, arrow_y_world, 1}, + (Vector3){arrow_x_end2_world, arrow_y_end2_world, 1}, + PUFF_WHITE + ); + + } } + obs_idx += 7; // Move to next agent observation (7 values per agent) } // Then draw map observations - int map_start_idx = 7 + 7*(MAX_CARS - 1); // Start after agent observations + int map_start_idx = 7 + 7*(MAX_AGENTS - 1); // Start after agent observations for(int k = 0; k < MAX_ROAD_SEGMENT_OBSERVATIONS; k++) { // Loop through potential map entities int entity_idx = map_start_idx + k*7; if(agent_obs[entity_idx] == 0 && agent_obs[entity_idx + 1] == 0){ @@ -1535,9 +2626,9 @@ void draw_agent_obs(Drive* env, int agent_index){ Color lineColor = BLUE; // Default color int entity_type = (int)agent_obs[entity_idx + 6]; // Choose color based on entity type - if(entity_type+4 != ROAD_EDGE){ - continue; - } + int unnormalized_type = unnormalize_road_type(entity_type); + if(!is_road_edge(unnormalized_type)) continue; + lineColor = PUFF_CYAN; // For road segments, draw line between start and end points float x_middle = agent_obs[entity_idx] * 50; @@ -1552,9 +2643,27 @@ void draw_agent_obs(Drive* env, int agent_index){ float y_start = y_middle - segment_length*sinf(rel_angle); float x_end = x_middle + segment_length*cosf(rel_angle); float y_end = y_middle + segment_length*sinf(rel_angle); - DrawLine3D((Vector3){0,0,0}, (Vector3){x_middle, y_middle, 1}, lineColor); - DrawCube((Vector3){x_middle, y_middle, 1}, 0.5f, 0.5f, 0.5f, lineColor); - DrawLine3D((Vector3){x_start, y_start, 1}, (Vector3){x_end, y_end, 1}, BLUE); + + + if(lasers && mode ==0){ + DrawLine3D((Vector3){0,0,0}, (Vector3){x_middle, y_middle, 1}, lineColor); + } + + if(mode ==1){ + float x_middle_world = px + (x_middle*heading_self_x - y_middle*heading_self_y); + float y_middle_world = py + (x_middle*heading_self_y + y_middle*heading_self_x); + float x_start_world = px + (x_start*heading_self_x - y_start*heading_self_y); + float y_start_world = py + (x_start*heading_self_y + y_start*heading_self_x); + float x_end_world = px + (x_end*heading_self_x - y_end*heading_self_y); + float y_end_world = py + (x_end*heading_self_y + y_end*heading_self_x); + DrawCube((Vector3){x_middle_world, y_middle_world, 1}, 0.5f, 0.5f, 0.5f, lineColor); + DrawLine3D((Vector3){x_start_world, y_start_world, 1}, (Vector3){x_end_world, y_end_world, 1}, BLUE); + if(lasers) DrawLine3D((Vector3){px,py,1}, (Vector3){x_middle_world, y_middle_world, 1}, lineColor); + } + if(mode ==0){ + DrawCube((Vector3){x_middle, y_middle, 1}, 0.5f, 0.5f, 0.5f, lineColor); + DrawLine3D((Vector3){x_start, y_start, 1}, (Vector3){x_end, y_end, 1}, BLUE); + } } } @@ -1565,58 +2674,59 @@ void draw_road_edge(Drive* env, float start_x, float start_y, float end_x, float // Calculate curb dimensions float curb_height = 0.5f; // Height of the curb float curb_width = 0.3f; // Width/thickness of the curb - + float road_z = 0.2f; // Ensure z-level for roads is below agents + // Calculate direction vector between start and end Vector3 direction = { end_x - start_x, end_y - start_y, 0.0f }; - + // Calculate length of the segment float length = sqrtf(direction.x * direction.x + direction.y * direction.y); - + // Normalize direction vector Vector3 normalized_dir = { direction.x / length, direction.y / length, 0.0f }; - + // Calculate perpendicular vector for width Vector3 perpendicular = { -normalized_dir.y, normalized_dir.x, 0.0f }; - + // Calculate the four bottom corners of the curb Vector3 b1 = { start_x - perpendicular.x * curb_width/2, start_y - perpendicular.y * curb_width/2, - 1.0f + road_z }; Vector3 b2 = { start_x + perpendicular.x * curb_width/2, start_y + perpendicular.y * curb_width/2, - 1.0f + road_z }; Vector3 b3 = { end_x + perpendicular.x * curb_width/2, end_y + perpendicular.y * curb_width/2, - 1.0f + road_z }; Vector3 b4 = { end_x - perpendicular.x * curb_width/2, end_y - perpendicular.y * curb_width/2, - 1.0f + road_z }; - + // Draw the curb faces // Bottom face DrawTriangle3D(b1, b2, b3, CURB_BOTTOM); DrawTriangle3D(b1, b3, b4, CURB_BOTTOM); - + // Top face (raised by curb_height) Vector3 t1 = {b1.x, b1.y, b1.z + curb_height}; Vector3 t2 = {b2.x, b2.y, b2.z + curb_height}; @@ -1624,7 +2734,7 @@ void draw_road_edge(Drive* env, float start_x, float start_y, float end_x, float Vector3 t4 = {b4.x, b4.y, b4.z + curb_height}; DrawTriangle3D(t1, t3, t2, CURB_TOP); DrawTriangle3D(t1, t4, t3, CURB_TOP); - + // Side faces DrawTriangle3D(b1, t1, b2, CURB_SIDE); DrawTriangle3D(t1, t2, b2, CURB_SIDE); @@ -1636,69 +2746,130 @@ void draw_road_edge(Drive* env, float start_x, float start_y, float end_x, float DrawTriangle3D(t4, t1, b1, CURB_SIDE); } -void c_render(Drive* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - Client* client = env->client; - BeginDrawing(); - Color road = (Color){35, 35, 37, 255}; - ClearBackground(road); - BeginMode3D(client->camera); - handle_camera_controls(env->client); - - // Draw a grid to help with orientation +void draw_scene(Drive* env, Client* client, int mode, int obs_only, int lasers, int show_grid){ + // Draw a grid to help with orientation // DrawGrid(20, 1.0f); - DrawLine3D((Vector3){env->map_corners[0], env->map_corners[1], 0}, (Vector3){env->map_corners[2], env->map_corners[1], 0}, PUFF_CYAN); - DrawLine3D((Vector3){env->map_corners[0], env->map_corners[1], 0}, (Vector3){env->map_corners[0], env->map_corners[3], 0}, PUFF_CYAN); - DrawLine3D((Vector3){env->map_corners[2], env->map_corners[1], 0}, (Vector3){env->map_corners[2], env->map_corners[3], 0}, PUFF_CYAN); - DrawLine3D((Vector3){env->map_corners[0], env->map_corners[3], 0}, (Vector3){env->map_corners[2], env->map_corners[3], 0}, PUFF_CYAN); - for(int i = 0; i < env->num_entities; i++) { - // Draw cars - if(env->entities[i].type == 1 || env->entities[i].type == 2) { - // Check if this vehicle is an active agent - bool is_active_agent = false; - bool is_static_car = false; - int agent_index = -1; - for(int j = 0; j < env->active_agent_count; j++) { - if(env->active_agent_indices[j] == i) { - is_active_agent = true; - agent_index = j; - break; - } + DrawLine3D((Vector3){env->grid_map->top_left_x, env->grid_map->top_left_y, 0}, (Vector3){env->grid_map->bottom_right_x, env->grid_map->top_left_y, 0}, PUFF_CYAN); + DrawLine3D((Vector3){env->grid_map->top_left_x, env->grid_map->bottom_right_y, 0}, (Vector3){env->grid_map->top_left_x, env->grid_map->top_left_y, 0}, PUFF_CYAN); + DrawLine3D((Vector3){env->grid_map->bottom_right_x, env->grid_map->bottom_right_y, 0}, (Vector3){env->grid_map->bottom_right_x, env->grid_map->top_left_y, 0}, PUFF_CYAN); + DrawLine3D((Vector3){env->grid_map->top_left_x, env->grid_map->bottom_right_y, 0}, (Vector3){env->grid_map->bottom_right_x, env->grid_map->bottom_right_y, 0}, PUFF_CYAN); + + for(int i = 0; i < env->num_total_agents; i++) { + Agent* agent = &env->agents[i]; + // Draw objects + // Check if this vehicle is an active agent + bool is_active_agent = false; + bool is_static_agent = false; + int agent_index = -1; + for(int j = 0; j < env->active_agent_count; j++) { + if(env->active_agent_indices[j] == i) { + is_active_agent = true; + agent_index = j; + break; } - for(int j = 0; j < env->static_car_count; j++) { - if(env->static_car_indices[j] == i) { - is_static_car = true; - break; - } + } + for(int j = 0; j < env->static_agent_count; j++) { + if(env->static_agent_indices[j] == i) { + is_static_agent = true; + break; } - // HIDE CARS ON RESPAWN - IMPORTANT TO KNOW VISUAL SETTING - if(!is_active_agent && !is_static_car || env->entities[i].respawn_timestep != -1){ + } + // HIDE CARS ON RESPAWN - IMPORTANT TO KNOW VISUAL SETTING + if((!is_active_agent && !is_static_agent) || agent->respawn_timestep != -1){ + continue; + } + Vector3 position; + float heading; + position = (Vector3){ + agent->sim_x, + agent->sim_y, + 1 + }; + heading = agent->sim_heading; + // Create size vector + Vector3 size = { + agent->sim_length, + agent->sim_width, + agent->sim_height + }; + + bool is_expert = (!is_active_agent) && (agent->mark_as_expert == 1); + + // Save current transform + if(mode==1){ + float cos_heading = cosf(heading); + float sin_heading = sinf(heading); + + // Calculate half dimensions + float half_len = agent->sim_length * 0.5f; + float half_width = agent->sim_width * 0.5f; + + // Calculate the four corners of the collision box + Vector3 corners[4] = { + (Vector3){ + position.x + (half_len * cos_heading - half_width * sin_heading), + position.y + (half_len * sin_heading + half_width * cos_heading), + position.z + }, + + + (Vector3){ + position.x + (half_len * cos_heading + half_width * sin_heading), + position.y + (half_len * sin_heading - half_width * cos_heading), + position.z + }, + (Vector3){ + position.x + (-half_len * cos_heading + half_width * sin_heading), + position.y + (-half_len * sin_heading - half_width * cos_heading), + position.z + }, + (Vector3){ + position.x + (-half_len * cos_heading - half_width * sin_heading), + position.y + (-half_len * sin_heading + half_width * cos_heading), + position.z + }, + + + }; + + if(agent_index == env->human_agent_idx && !agent->metrics_array[REACHED_GOAL_IDX]) { + draw_agent_obs(env, agent_index, mode, obs_only, lasers); + } + if((obs_only || IsKeyDown(KEY_LEFT_CONTROL)) && agent_index != env->human_agent_idx){ continue; } - Vector3 position; - float heading; - position = (Vector3){ - env->entities[i].x, - env->entities[i].y, - 1 - }; - heading = env->entities[i].heading; - // Create size vector - Vector3 size = { - env->entities[i].length, - env->entities[i].width, - env->entities[i].height + + // --- Draw the car --- + + Vector3 carPos = { position.x, position.y, position.z }; + Color car_color = GRAY; // default for static + if (is_expert) car_color = GOLD; // expert replay + if (is_active_agent) car_color = BLUE; // policy-controlled + if (is_active_agent && agent->collision_state > 0) car_color = RED; + rlSetLineWidth(3.0f); + for (int j = 0; j < 4; j++) { + DrawLine3D(corners[j], corners[(j+1)%4], car_color); + } + // --- Draw a heading arrow pointing forward --- + Vector3 arrowStart = position; + Vector3 arrowEnd = { + position.x + cos_heading * half_len * 1.5f, // extend arrow beyond car + position.y + sin_heading * half_len * 1.5f, + position.z }; - // Save current transform + + DrawLine3D(arrowStart, arrowEnd, car_color); + DrawSphere(arrowEnd, 0.2f, car_color); // arrow tip + + } + else { rlPushMatrix(); // Translate to position, rotate around Y axis, then draw rlTranslatef(position.x, position.y, position.z); rlRotatef(heading*RAD2DEG, 0.0f, 0.0f, 1.0f); // Convert radians to degrees - // Determine color based on active status and other conditions - Color object_color = PUFF_BACKGROUND2; // Default color for non-active vehicles - Color outline_color = PUFF_CYAN; + // Determine color based on status + Color object_color = PUFF_BACKGROUND2; // fill color unused for model tint + Color outline_color = PUFF_CYAN; // not used for model tint Model car_model = client->cars[5]; if(is_active_agent){ car_model = client->cars[client->car_assignments[i %64]]; @@ -1707,16 +2878,16 @@ void c_render(Drive* env) { object_color = PUFF_CYAN; outline_color = PUFF_WHITE; } - if(is_active_agent && env->entities[i].collision_state > 0) { + if(is_active_agent && agent->collision_state > 0) { car_model = client->cars[0]; // Collided agent } // Draw obs for human selected agent - if(agent_index == env->human_agent_idx && !env->entities[agent_index].reached_goal) { - draw_agent_obs(env, agent_index); + if(agent_index == env->human_agent_idx && !agent->metrics_array[REACHED_GOAL_IDX]) { + draw_agent_obs(env, agent_index, mode, obs_only, lasers); } // Draw cube for cars static and active // Calculate scale factors based on desired size and model dimensions - + BoundingBox bounds = GetModelBoundingBox(car_model); Vector3 model_size = { bounds.max.x - bounds.min.x, @@ -1728,140 +2899,260 @@ void c_render(Drive* env) { size.y / model_size.y, size.z / model_size.z }; - DrawModelEx(car_model, (Vector3){0, 0, 0}, (Vector3){1, 0, 0}, 90.0f, scale, WHITE); - rlPopMatrix(); - - float cos_heading = env->entities[i].heading_x; - float sin_heading = env->entities[i].heading_y; - - // Calculate half dimensions - float half_len = env->entities[i].length * 0.5f; - float half_width = env->entities[i].width * 0.5f; - - // Calculate the four corners of the collision box - Vector3 corners[4] = { - (Vector3){ - position.x + (half_len * cos_heading - half_width * sin_heading), - position.y + (half_len * sin_heading + half_width * cos_heading), - position.z - }, - (Vector3){ - position.x + (half_len * cos_heading + half_width * sin_heading), - position.y + (half_len * sin_heading - half_width * cos_heading), - position.z - }, - (Vector3){ - position.x + (-half_len * cos_heading - half_width * sin_heading), - position.y + (-half_len * sin_heading + half_width * cos_heading), - position.z - }, - (Vector3){ - position.x + (-half_len * cos_heading + half_width * sin_heading), - position.y + (-half_len * sin_heading - half_width * cos_heading), - position.z - } - }; - - // Draw the corners as spheres - /* - for(int j = 0; j < 4; j++) { - DrawSphere(corners[j], 0.3f, RED); // Draw red spheres at each corner - } - */ - for(int j = 0; j < 4; j++) { - DrawLine3D(corners[j], corners[(j+1)%4], PURPLE); // Draw red lines between corners + if((obs_only || IsKeyDown(KEY_LEFT_CONTROL)) && agent_index != env->human_agent_idx){ + rlPopMatrix(); + continue; } - // FPV Camera Control - if(IsKeyDown(KEY_SPACE) && env->human_agent_idx== agent_index){ - if(env->entities[agent_index].reached_goal){ - env->human_agent_idx = rand() % env->active_agent_count; - } - Vector3 camera_position = (Vector3){ - position.x - (25.0f * cosf(heading)), - position.y - (25.0f * sinf(heading)), - position.z + 15 - }; - Vector3 camera_target = (Vector3){ - position.x + 40.0f * cosf(heading), - position.y + 40.0f * sinf(heading), - position.z - 5.0f + DrawModelEx(car_model, (Vector3){0, 0, 0}, (Vector3){1, 0, 0}, 90.0f, scale, WHITE); + { + float cos_heading = cosf(heading); + float sin_heading = sinf(heading); + float half_len = agent->sim_length * 0.5f; + float half_width = agent->sim_width * 0.5f; + Vector3 corners[4] = { + (Vector3){ 0 + ( half_len * cos_heading - half_width * sin_heading), 0 + ( half_len * sin_heading + half_width * cos_heading), 0 }, + (Vector3){ 0 + ( half_len * cos_heading + half_width * sin_heading), 0 + ( half_len * sin_heading - half_width * cos_heading), 0 }, + (Vector3){ 0 + (-half_len * cos_heading + half_width * sin_heading), 0 + (-half_len * sin_heading - half_width * cos_heading), 0 }, + (Vector3){ 0 + (-half_len * cos_heading - half_width * sin_heading), 0 + (-half_len * sin_heading + half_width * cos_heading), 0 }, }; - client->camera.position = camera_position; - client->camera.target = camera_target; - client->camera.up = (Vector3){0, 0, 1}; - } - if(IsKeyReleased(KEY_SPACE)){ - client->camera.position = client->default_camera_position; - client->camera.target = client->default_camera_target; - client->camera.up = (Vector3){0, 0, 1}; + Color wire_color = GRAY; // static + if (!is_active_agent && agent->mark_as_expert == 1) wire_color = GOLD; // expert replay + if (is_active_agent) wire_color = BLUE; // policy + if (is_active_agent && agent->collision_state > 0) wire_color = RED; + rlSetLineWidth(2.0f); + for (int j = 0; j < 4; j++) { + DrawLine3D(corners[j], corners[(j+1)%4], wire_color); + } } - // Draw goal position for active agents + rlPopMatrix(); + } - if(!is_active_agent || env->entities[i].valid == 0) { - continue; - } - if(!IsKeyDown(KEY_LEFT_CONTROL)){ - DrawSphere((Vector3){ - env->entities[i].goal_position_x, - env->entities[i].goal_position_y, - 1 - }, 0.5f, DARKGREEN); + // FPV Camera Control + if(IsKeyDown(KEY_SPACE) && env->human_agent_idx== agent_index){ + if(agent->metrics_array[REACHED_GOAL_IDX]){ + env->human_agent_idx = rand() % env->active_agent_count; } + Vector3 camera_position = (Vector3){ + position.x - (25.0f * cosf(heading)), + position.y - (25.0f * sinf(heading)), + position.z + 15 + }; + + Vector3 camera_target = (Vector3){ + position.x + 40.0f * cosf(heading), + position.y + 40.0f * sinf(heading), + position.z - 5.0f + }; + client->camera.position = camera_position; + client->camera.target = camera_target; + client->camera.up = (Vector3){0, 0, 1}; + } + if(IsKeyReleased(KEY_SPACE)){ + client->camera.position = client->default_camera_position; + client->camera.target = client->default_camera_target; + client->camera.up = (Vector3){0, 0, 1}; } - // Draw road elements - if(env->entities[i].type <=3 && env->entities[i].type >= 7){ + // Draw goal position for active agents + + if(!is_active_agent || agent->sim_valid == 0) { continue; } - for(int j = 0; j < env->entities[i].array_size - 1; j++) { + if(!IsKeyDown(KEY_LEFT_CONTROL) && obs_only==0){ + DrawSphere((Vector3){ + agent->goal_position_x, + agent->goal_position_y, + 1 + }, 0.5f, DARKGREEN); + + DrawCircle3D((Vector3){ + agent->goal_position_x, + agent->goal_position_y, + 0.1f + }, env->goal_radius, (Vector3){0, 0, 1}, 90.0f, Fade(LIGHTGREEN, 0.3f)); + } + + } + for (int i = 0; i < env->num_road_elements; i++) { + RoadMapElement* element = &env->road_elements[i]; + + for(int j = 0; j < element->segment_length - 1; j++) { Vector3 start = { - env->entities[i].traj_x[j], - env->entities[i].traj_y[j], + element->x[j], + element->y[j], 1 }; Vector3 end = { - env->entities[i].traj_x[j + 1], - env->entities[i].traj_y[j + 1], + element->x[j + 1], + element->y[j + 1], 1 }; Color lineColor = GRAY; - if (env->entities[i].type == ROAD_LANE) lineColor = GRAY; - else if (env->entities[i].type == ROAD_LINE) lineColor = BLUE; - else if (env->entities[i].type == ROAD_EDGE) lineColor = WHITE; - else if (env->entities[i].type == DRIVEWAY) lineColor = RED; - if(env->entities[i].type != ROAD_EDGE){ - continue; - } - if(!IsKeyDown(KEY_LEFT_CONTROL)){ + + if (is_road_lane(element->type)) lineColor = GRAY; + else if (is_road_line(element->type)) lineColor = BLUE; + else if (is_road_edge(element->type)) lineColor = WHITE; + else if (element->type == DRIVEWAY) lineColor = RED; + if(!IsKeyDown(KEY_LEFT_CONTROL) && obs_only==0){ draw_road_edge(env, start.x, start.y, end.x, end.y); - // DrawLine3D(start, end, lineColor); - // DrawCube(start, 0.5f, 0.5f, 0.5f, lineColor); - // DrawCube(end, 0.5f, 0.5f, 0.5f, lineColor); } } } - // Draw grid cells using the stored bounds - float grid_start_x = env->map_corners[0]; - float grid_start_y = env->map_corners[1]; - for(int i = 0; i < env->grid_cols; i++) { - for(int j = 0; j < env->grid_rows; j++) { - float x = grid_start_x + i*GRID_CELL_SIZE; - float y = grid_start_y + j*GRID_CELL_SIZE; - // int index = i * env->grid_rows + j; - DrawCubeWires( - (Vector3){x + GRID_CELL_SIZE/2, y + GRID_CELL_SIZE/2, 1}, - GRID_CELL_SIZE, GRID_CELL_SIZE, 0.1f, PUFF_BACKGROUND2); + if(show_grid) { + // Draw grid cells using the stored bounds + float grid_start_x = env->grid_map->top_left_x; + float grid_start_y = env->grid_map->bottom_right_y; + for(int i = 0; i < env->grid_map->grid_cols; i++) { + for(int j = 0; j < env->grid_map->grid_rows; j++) { + float x = grid_start_x + i*GRID_CELL_SIZE; + float y = grid_start_y + j*GRID_CELL_SIZE; + DrawCubeWires( + (Vector3){x + GRID_CELL_SIZE/2, y + GRID_CELL_SIZE/2, 1}, + GRID_CELL_SIZE, GRID_CELL_SIZE, 0.1f, PUFF_BACKGROUND2); + } } } + EndMode3D(); + + // Draw track indices for the tracks to predict + if (mode == 1 && env->control_mode == CONTROL_TRACKS_TO_PREDICT) { + float map_width = env->grid_map->bottom_right_x - env->grid_map->top_left_x; + float map_height = env->grid_map->top_left_y - env->grid_map->bottom_right_y; + float pixels_per_world_unit = client->height / map_height; + + for (int i = 0; i < env->active_agent_count; i++) { + // Ignore respawned agents + if (env->agents[i].respawn_timestep != -1) { + continue; + } + int agent_idx = env->active_agent_indices[i]; + int womd_track_idx = env->tracks_to_predict[i]; + + float raw_x = -env->agents[agent_idx].sim_x * pixels_per_world_unit; + float raw_y = env->agents[agent_idx].sim_y * pixels_per_world_unit; + + int screen_x = (int)raw_x + client->width/2 + 20; + int screen_y = (int)raw_y + client->height/2 - 25; + + if (screen_x >= 0 && screen_x <= client->width && + screen_y >= 0 && screen_y <= client->height) { + char text[32]; + snprintf(text, sizeof(text), "%d", womd_track_idx); + int text_width = MeasureText(text, 20); + DrawText(text, screen_x - text_width/2, screen_y, 20, PUFF_WHITE); + } + } + } +} + +void saveTopDownImage(Drive* env, Client* client, const char *filename, RenderTexture2D target, int map_height, int obs, int lasers, int trajectories, int frame_count, float* path, int log_trajectories, int show_grid){ + // Top-down orthographic camera + Camera3D camera = {0}; + camera.position = (Vector3){ 0.0f, 0.0f, 500.0f }; // above the scene + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // look at origin + camera.up = (Vector3){ 0.0f, -1.0f, 0.0f }; + camera.fovy = map_height; + camera.projection = CAMERA_ORTHOGRAPHIC; + Color road = (Color){35, 35, 37, 255}; + + BeginTextureMode(target); + ClearBackground(road); + BeginMode3D(camera); + rlEnableDepthTest(); + + // Draw log trajectories FIRST (in background at lower Z-level) + if(log_trajectories){ + for(int i = 0; i < env->num_total_agents; i++) { + Agent* agent = &env->agents[i]; + int idx = env->active_agent_indices[i]; + for(int j=0; jtrajectory_length; j++){ + float x = agent->log_trajectory_x[j]; + float y = agent->log_trajectory_y[j]; + float valid = agent->log_valid[j]; + if(!valid) continue; + DrawSphere((Vector3){x,y,0.5f}, 0.3f, Fade(LIGHTGREEN, 0.6f)); + } + } + } + + // Draw current path trajectories SECOND (slightly higher than log trajectories) + if(trajectories){ + for(int i=0; iactive_agent_indices[env->human_agent_idx]; + Agent* agent = &env->agents[agent_idx]; + + Camera3D camera = {0}; + // Position camera behind and above the agent + camera.position = (Vector3){ + agent->sim_x - (25.0f * cosf(agent->sim_heading)), + agent->sim_y - (25.0f * sinf(agent->sim_heading)), + 15.0f + }; + camera.target = (Vector3){ + agent->sim_x + 40.0f * cosf(agent->sim_heading), + agent->sim_y + 40.0f * sinf(agent->sim_heading), + 1.0f + }; + camera.up = (Vector3){ 0.0f, 0.0f, 1.0f }; + camera.fovy = 45.0f; + camera.projection = CAMERA_PERSPECTIVE; + + Color road = (Color){35, 35, 37, 255}; + + BeginTextureMode(target); + ClearBackground(road); + BeginMode3D(camera); + rlEnableDepthTest(); + draw_scene(env, client, 0, obs_only, lasers, show_grid); // mode=0 for agent view + EndMode3D(); + EndTextureMode(); + + // Save to file + Image img = LoadImageFromTexture(target.texture); + ImageFlipVertical(&img); + ExportImage(img, filename); + UnloadImage(img); +} + +void c_render(Drive* env) { + if (env->client == NULL) { + env->client = make_client(env); + } + Client* client = env->client; + BeginDrawing(); + Color road = (Color){35, 35, 37, 255}; + ClearBackground(road); + BeginMode3D(client->camera); + handle_camera_controls(env->client); + draw_scene(env, client, 0, 0, 0, 0); // Draw debug info - DrawText(TextFormat("Camera Position: (%.2f, %.2f, %.2f)", - client->camera.position.x, - client->camera.position.y, + DrawText(TextFormat("Camera Position: (%.2f, %.2f, %.2f)", + client->camera.position.x, + client->camera.position.y, client->camera.position.z), 10, 10, 20, PUFF_WHITE); - DrawText(TextFormat("Camera Target: (%.2f, %.2f, %.2f)", - client->camera.target.x, - client->camera.target.y, + DrawText(TextFormat("Camera Target: (%.2f, %.2f, %.2f)", + client->camera.target.x, + client->camera.target.y, client->camera.target.z), 10, 30, 20, PUFF_WHITE); DrawText(TextFormat("Timestep: %d", env->timestep), 10, 50, 20, PUFF_WHITE); // acceleration & steering @@ -1869,13 +3160,20 @@ void c_render(Drive* env) { DrawText(TextFormat("Controlling Agent: %d", env->human_agent_idx), 10, 70, 20, PUFF_WHITE); DrawText(TextFormat("Agent Index: %d", human_idx), 10, 90, 20, PUFF_WHITE); // Controls help - DrawText("Controls: W/S - Accelerate/Brake, A/D - Steer, 1-4 - Switch Agent", + DrawText("Controls: W/S - Accelerate/Brake, A/D - Steer, 1-4 - Switch Agent", 10, client->height - 30, 20, PUFF_WHITE); // acceleration & steering - DrawText(TextFormat("Acceleration: %d", env->actions[env->human_agent_idx * 2]), 10, 110, 20, PUFF_WHITE); - DrawText(TextFormat("Steering: %d", env->actions[env->human_agent_idx * 2 + 1]), 10, 130, 20, PUFF_WHITE); - DrawText(TextFormat("Grid Rows: %d", env->grid_rows), 10, 150, 20, PUFF_WHITE); - DrawText(TextFormat("Grid Cols: %d", env->grid_cols), 10, 170, 20, PUFF_WHITE); + if (env->action_type == 1) { // continuous (float) + float (*action_array_f)[2] = (float(*)[2])env->actions; + DrawText(TextFormat("Acceleration: %.2f", action_array_f[env->human_agent_idx][0]), 10, 110, 20, PUFF_WHITE); + DrawText(TextFormat("Steering: %.2f", action_array_f[env->human_agent_idx][1]), 10, 130, 20, PUFF_WHITE); + } else { // discrete (int) + int (*action_array)[2] = (int(*)[2])env->actions; + DrawText(TextFormat("Acceleration: %d", action_array[env->human_agent_idx][0]), 10, 110, 20, PUFF_WHITE); + DrawText(TextFormat("Steering: %d", action_array[env->human_agent_idx][1]), 10, 130, 20, PUFF_WHITE); + } + DrawText(TextFormat("Grid Rows: %d", env->grid_map->grid_rows), 10, 150, 20, PUFF_WHITE); + DrawText(TextFormat("Grid Cols: %d", env->grid_map->grid_cols), 10, 170, 20, PUFF_WHITE); EndDrawing(); } diff --git a/pufferlib/ocean/drive/drive.py b/pufferlib/ocean/drive/drive.py index 107396725..ad6dbd62e 100644 --- a/pufferlib/ocean/drive/drive.py +++ b/pufferlib/ocean/drive/drive.py @@ -3,48 +3,143 @@ import json import struct import os -import random import pufferlib from pufferlib.ocean.drive import binding -class Drive(pufferlib.PufferEnv): - def __init__(self, render_mode=None, report_interval=1, - width=1280, height=1024, - human_agent_idx=0, - reward_vehicle_collision=-0.1, - reward_offroad_collision=-0.1, - reward_goal_post_respawn=0.5, - reward_vehicle_collision_post_respawn=-0.25, - spawn_immunity_timer=30, - resample_frequency = 91, - num_maps=100, - num_agents=512, - buf = None, - seed=1): +class Drive(pufferlib.PufferEnv): + def __init__( + self, + render_mode=None, + report_interval=1, + width=1280, + height=1024, + human_agent_idx=0, + reward_vehicle_collision=-0.1, + reward_offroad_collision=-0.1, + reward_goal=1.0, + reward_goal_post_respawn=0.5, + reward_ade=0.0, + goal_behavior=0, + goal_radius=2.0, + collision_behavior=0, + offroad_behavior=0, + dt=0.1, + scenario_length=None, + resample_frequency=91, + num_maps=100, + num_agents=512, + action_type="discrete", + dynamics_model="classic", + max_controlled_agents=-1, + buf=None, + seed=1, + init_steps=0, + init_mode="create_all_valid", + control_mode="control_vehicles", + ): # env + self.dt = dt self.render_mode = render_mode self.num_maps = num_maps self.report_interval = report_interval self.reward_vehicle_collision = reward_vehicle_collision self.reward_offroad_collision = reward_offroad_collision + self.reward_goal = reward_goal self.reward_goal_post_respawn = reward_goal_post_respawn - self.reward_vehicle_collision_post_respawn = reward_vehicle_collision_post_respawn - self.spawn_immunity_timer = spawn_immunity_timer + self.goal_radius = goal_radius + self.goal_behavior = goal_behavior + self.collision_behavior = collision_behavior + self.offroad_behavior = offroad_behavior + self.reward_ade = reward_ade self.human_agent_idx = human_agent_idx + self.scenario_length = scenario_length self.resample_frequency = resample_frequency - self.num_obs = 7 + 63*7 + 200*7 - self.single_observation_space = gymnasium.spaces.Box(low=-1, high=1, - shape=(self.num_obs,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.MultiDiscrete([7, 13]) - # self.single_action_space = gymnasium.spaces.Box( - # low=-1, high=1, shape=(2,), dtype=np.float32 - # ) + self.dynamics_model = dynamics_model + + # Observation space calculation + if dynamics_model == "classic": + ego_features = 7 + elif dynamics_model == "jerk": + ego_features = 10 + else: + raise ValueError(f"dynamics_model must be 'classic' or 'jerk'. Got: {dynamics_model}") + + self.ego_features = ego_features + partner_features = 7 + road_features = 7 + max_partner_objects = 63 + max_road_objects = 200 + self.num_obs = ego_features + max_partner_objects * partner_features + max_road_objects * road_features + self.single_observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(self.num_obs,), dtype=np.float32) + self.init_steps = init_steps + self.init_mode_str = init_mode + self.control_mode_str = control_mode + + if self.control_mode_str == "control_vehicles": + self.control_mode = 0 + elif self.control_mode_str == "control_agents": + self.control_mode = 1 + elif self.control_mode_str == "control_tracks_to_predict": + self.control_mode = 2 + elif self.control_mode_str == "control_sdc_only": + self.control_mode = 3 + else: + raise ValueError( + f"control_mode must be one of 'control_vehicles', 'control_tracks_to_predict', or 'control_agents'. Got: {self.control_mode_str}" + ) + if self.init_mode_str == "create_all_valid": + self.init_mode = 0 + elif self.init_mode_str == "create_only_controlled": + self.init_mode = 1 + else: + raise ValueError( + f"init_mode must be one of 'create_all_valid' or 'create_only_controlled'. Got: {self.init_mode_str}" + ) + + if action_type == "discrete": + if dynamics_model == "classic": + # Joint action space (assume dependence) + self.single_action_space = gymnasium.spaces.MultiDiscrete([7 * 13]) + # Multi discrete (assume independence) + # self.single_action_space = gymnasium.spaces.MultiDiscrete([7, 13]) + elif dynamics_model == "jerk": + self.single_action_space = gymnasium.spaces.MultiDiscrete([4, 3]) + else: + raise ValueError(f"dynamics_model must be 'classic' or 'jerk'. Got: {dynamics_model}") + elif action_type == "continuous": + self.single_action_space = gymnasium.spaces.Box(low=-1, high=1, shape=(2,), dtype=np.float32) + else: + raise ValueError(f"action_space must be 'discrete' or 'continuous'. Got: {action_type}") + + self._action_type_flag = 0 if action_type == "discrete" else 1 + # Check if resources directory exists binary_path = "resources/drive/binaries/map_000.bin" if not os.path.exists(binary_path): - raise FileNotFoundError(f"Required directory {binary_path} not found. Please ensure the Drive maps are downloaded and installed correctly per docs.") - agent_offsets, map_ids, num_envs = binding.shared(num_agents=num_agents, num_maps=num_maps) + raise FileNotFoundError( + f"Required directory {binary_path} not found. Please ensure the Drive maps are downloaded and installed correctly per docs." + ) + + # Check maps availability + available_maps = len([name for name in os.listdir("resources/drive/binaries") if name.endswith(".bin")]) + if num_maps > available_maps: + raise ValueError( + f"num_maps ({num_maps}) exceeds available maps in directory ({available_maps}). Please reduce num_maps or add more maps to resources/drive/binaries." + ) + self.max_controlled_agents = int(max_controlled_agents) + + # Iterate through all maps to count total agents that can be initialized for each map + agent_offsets, map_ids, num_envs = binding.shared( + num_agents=num_agents, + num_maps=num_maps, + init_mode=self.init_mode, + control_mode=self.control_mode, + init_steps=self.init_steps, + max_controlled_agents=self.max_controlled_agents, + goal_behavior=self.goal_behavior, + ) + self.num_agents = num_agents self.agent_offsets = agent_offsets self.map_ids = map_ids @@ -53,7 +148,7 @@ def __init__(self, render_mode=None, report_interval=1, env_ids = [] for i in range(num_envs): cur = agent_offsets[i] - nxt = agent_offsets[i+1] + nxt = agent_offsets[i + 1] env_id = binding.env_init( self.observations[cur:nxt], self.actions[cur:nxt], @@ -61,14 +156,26 @@ def __init__(self, render_mode=None, report_interval=1, self.terminals[cur:nxt], self.truncations[cur:nxt], seed, + action_type=self._action_type_flag, human_agent_idx=human_agent_idx, reward_vehicle_collision=reward_vehicle_collision, reward_offroad_collision=reward_offroad_collision, + reward_goal=reward_goal, reward_goal_post_respawn=reward_goal_post_respawn, - reward_vehicle_collision_post_respawn=reward_vehicle_collision_post_respawn, - spawn_immunity_timer=spawn_immunity_timer, + reward_ade=reward_ade, + goal_radius=goal_radius, + goal_behavior=self.goal_behavior, + collision_behavior=self.collision_behavior, + offroad_behavior=self.offroad_behavior, + dt=dt, + scenario_length=(int(scenario_length) if scenario_length is not None else None), + max_controlled_agents=self.max_controlled_agents, map_id=map_ids[i], - max_agents = nxt-cur + max_agents=nxt - cur, + ini_file="pufferlib/config/ocean/drive.ini", + init_steps=init_steps, + init_mode=self.init_mode, + control_mode=self.control_mode, ) env_ids.append(env_id) @@ -83,24 +190,32 @@ def step(self, actions): self.terminals[:] = 0 self.actions[:] = actions binding.vec_step(self.c_envs) - self.tick+=1 + self.tick += 1 info = [] if self.tick % self.report_interval == 0: log = binding.vec_log(self.c_envs) if log: info.append(log) - #print(log) - if(self.tick > 0 and self.resample_frequency > 0 and self.tick % self.resample_frequency == 0): + # print(log) + if self.tick > 0 and self.resample_frequency > 0 and self.tick % self.resample_frequency == 0: self.tick = 0 will_resample = 1 if will_resample: binding.vec_close(self.c_envs) - agent_offsets, map_ids, num_envs = binding.shared(num_agents=self.num_agents, num_maps=self.num_maps) + agent_offsets, map_ids, num_envs = binding.shared( + num_agents=self.num_agents, + num_maps=self.num_maps, + init_mode=self.init_mode, + control_mode=self.control_mode, + init_steps=self.init_steps, + max_controlled_agents=self.max_controlled_agents, + goal_behavior=self.goal_behavior, + ) env_ids = [] - seed = np.random.randint(0, 2**32-1) + seed = np.random.randint(0, 2**32 - 1) for i in range(num_envs): cur = agent_offsets[i] - nxt = agent_offsets[i+1] + nxt = agent_offsets[i + 1] env_id = binding.env_init( self.observations[cur:nxt], self.actions[cur:nxt], @@ -108,32 +223,102 @@ def step(self, actions): self.terminals[cur:nxt], self.truncations[cur:nxt], seed, + action_type=self._action_type_flag, human_agent_idx=self.human_agent_idx, reward_vehicle_collision=self.reward_vehicle_collision, reward_offroad_collision=self.reward_offroad_collision, + reward_goal=self.reward_goal, reward_goal_post_respawn=self.reward_goal_post_respawn, - reward_vehicle_collision_post_respawn=self.reward_vehicle_collision_post_respawn, - spawn_immunity_timer=self.spawn_immunity_timer, + reward_ade=self.reward_ade, + goal_radius=self.goal_radius, + goal_behavior=self.goal_behavior, + collision_behavior=self.collision_behavior, + offroad_behavior=self.offroad_behavior, + dt=self.dt, + scenario_length=(int(self.scenario_length) if self.scenario_length is not None else None), + max_controlled_agents=self.max_controlled_agents, map_id=map_ids[i], - max_agents = nxt-cur + max_agents=nxt - cur, + ini_file="pufferlib/config/ocean/drive.ini", + init_steps=self.init_steps, + init_mode=self.init_mode, + control_mode=self.control_mode, ) env_ids.append(env_id) self.c_envs = binding.vectorize(*env_ids) binding.vec_reset(self.c_envs, seed) self.terminals[:] = 1 - return (self.observations, self.rewards, - self.terminals, self.truncations, info) + return (self.observations, self.rewards, self.terminals, self.truncations, info) + + def get_global_agent_state(self): + """Get current global state of all active agents. + + Returns: + dict with keys 'x', 'y', 'z', 'heading', 'id' containing numpy arrays + of shape (num_active_agents,) + """ + num_agents = self.num_agents + + states = { + "x": np.zeros(num_agents, dtype=np.float32), + "y": np.zeros(num_agents, dtype=np.float32), + "z": np.zeros(num_agents, dtype=np.float32), + "heading": np.zeros(num_agents, dtype=np.float32), + "id": np.zeros(num_agents, dtype=np.int32), + } + + binding.vec_get_global_agent_state( + self.c_envs, states["x"], states["y"], states["z"], states["heading"], states["id"] + ) + + return states + + def get_ground_truth_trajectories(self): + """Get ground truth trajectories for all active agents. + + Returns: + dict with keys 'x', 'y', 'z', 'heading', 'valid', 'id', 'scenario_id' containing numpy arrays. + """ + num_agents = self.num_agents + + trajectories = { + "x": np.zeros((num_agents, self.scenario_length - self.init_steps), dtype=np.float32), + "y": np.zeros((num_agents, self.scenario_length - self.init_steps), dtype=np.float32), + "z": np.zeros((num_agents, self.scenario_length - self.init_steps), dtype=np.float32), + "heading": np.zeros((num_agents, self.scenario_length - self.init_steps), dtype=np.float32), + "valid": np.zeros((num_agents, self.scenario_length - self.init_steps), dtype=np.int32), + "id": np.zeros(num_agents, dtype=np.int32), + "scenario_id": np.zeros(num_agents, dtype=np.int32), + } + + binding.vec_get_global_ground_truth_trajectories( + self.c_envs, + trajectories["x"], + trajectories["y"], + trajectories["z"], + trajectories["heading"], + trajectories["valid"], + trajectories["id"], + trajectories["scenario_id"], + ) + + for key in trajectories: + trajectories[key] = trajectories[key][:, None] + + return trajectories def render(self): binding.vec_render(self.c_envs, 0) - + def close(self): binding.vec_close(self.c_envs) + def calculate_area(p1, p2, p3): # Calculate the area of the triangle using the determinant method - return 0.5 * abs((p1['x'] - p3['x']) * (p2['y'] - p1['y']) - (p1['x'] - p2['x']) * (p3['y'] - p1['y'])) + return 0.5 * abs((p1["x"] - p3["x"]) * (p2["y"] - p1["y"]) - (p1["x"] - p2["x"]) * (p3["y"] - p1["y"])) + def simplify_polyline(geometry, polyline_reduction_threshold): """Simplify the given polyline using a method inspired by Visvalingham-Whyatt, optimized for Python.""" @@ -174,169 +359,214 @@ def simplify_polyline(geometry, polyline_reduction_threshold): return [geometry[i] for i in range(num_points) if not skip[i]] -def save_map_binary(map_data, output_file): + +def save_map_binary(map_data, output_file, unique_map_id): trajectory_length = 91 """Saves map data in a binary format readable by C""" - with open(output_file, 'wb') as f: + with open(output_file, "wb") as f: + # Get metadata + metadata = map_data.get("metadata", {}) + sdc_track_index = metadata.get("sdc_track_index", -1) # -1 as default if not found + tracks_to_predict = metadata.get("tracks_to_predict", []) + + # Write sdc_track_index + f.write(struct.pack("i", sdc_track_index)) + + # Write tracks_to_predict info (indices only) + f.write(struct.pack("i", len(tracks_to_predict))) + for track in tracks_to_predict: + track_index = track.get("track_index", -1) + f.write(struct.pack("i", track_index)) + # Count total entities - print(len(map_data.get('objects', []))) - print(len(map_data.get('roads', []))) - num_objects = len(map_data.get('objects', [])) - num_roads = len(map_data.get('roads', [])) + print(len(map_data.get("objects", []))) + print(len(map_data.get("roads", []))) + num_objects = len(map_data.get("objects", [])) + num_roads = len(map_data.get("roads", [])) # num_entities = num_objects + num_roads - f.write(struct.pack('i', num_objects)) - f.write(struct.pack('i', num_roads)) + f.write(struct.pack("i", num_objects)) + f.write(struct.pack("i", num_roads)) # f.write(struct.pack('i', num_entities)) # Write objects - for obj in map_data.get('objects', []): + for obj in map_data.get("objects", []): + # Write unique map id + f.write(struct.pack("i", unique_map_id)) + # Write base entity data - obj_type = obj.get('type', 1) - if(obj_type =='vehicle'): + obj_type = obj.get("type", 1) + if obj_type == "vehicle": obj_type = 1 - elif(obj_type == 'pedestrian'): + elif obj_type == "pedestrian": obj_type = 2 - elif(obj_type == 'cyclist'): + elif obj_type == "cyclist": obj_type = 3 - if(obj_type == 0): - print("Warning: Object with type 0 found") - f.write(struct.pack('i', obj_type)) # type - # f.write(struct.pack('i', obj.get('id', 0))) # id - f.write(struct.pack('i', trajectory_length)) # array_size + f.write(struct.pack("i", obj_type)) # type + f.write(struct.pack("i", obj.get("id", 0))) # id + f.write(struct.pack("i", trajectory_length)) # array_size # Write position arrays - positions = obj.get('position', []) + positions = obj.get("position", []) for i in range(trajectory_length): - pos = positions[i] if i < len(positions) else {'x': 0.0, 'y': 0.0, 'z': 0.0} - f.write(struct.pack('f', float(pos.get('x', 0.0)))) + pos = positions[i] if i < len(positions) else {"x": 0.0, "y": 0.0, "z": 0.0} + f.write(struct.pack("f", float(pos.get("x", 0.0)))) for i in range(trajectory_length): - pos = positions[i] if i < len(positions) else {'x': 0.0, 'y': 0.0, 'z': 0.0} - f.write(struct.pack('f', float(pos.get('y', 0.0)))) + pos = positions[i] if i < len(positions) else {"x": 0.0, "y": 0.0, "z": 0.0} + f.write(struct.pack("f", float(pos.get("y", 0.0)))) for i in range(trajectory_length): - pos = positions[i] if i < len(positions) else {'x': 0.0, 'y': 0.0, 'z': 0.0} - f.write(struct.pack('f', float(pos.get('z', 0.0)))) + pos = positions[i] if i < len(positions) else {"x": 0.0, "y": 0.0, "z": 0.0} + f.write(struct.pack("f", float(pos.get("z", 0.0)))) # Write velocity arrays - velocities = obj.get('velocity', []) - for arr, key in [(velocities, 'x'), (velocities, 'y'), (velocities, 'z')]: + velocities = obj.get("velocity", []) + for arr, key in [(velocities, "x"), (velocities, "y"), (velocities, "z")]: for i in range(trajectory_length): - vel = arr[i] if i < len(arr) else {'x': 0.0, 'y': 0.0, 'z': 0.0} - f.write(struct.pack('f', float(vel.get(key, 0.0)))) - + vel = arr[i] if i < len(arr) else {"x": 0.0, "y": 0.0, "z": 0.0} + f.write(struct.pack("f", float(vel.get(key, 0.0)))) + # Write heading and valid arrays - headings = obj.get('heading', []) - f.write(struct.pack(f'{trajectory_length}f', *[float(headings[i]) if i < len(headings) else 0.0 for i in range(trajectory_length)])) - - valids = obj.get('valid', []) - f.write(struct.pack(f'{trajectory_length}i', *[int(valids[i]) if i < len(valids) else 0 for i in range(trajectory_length)])) - + headings = obj.get("heading", []) + f.write( + struct.pack( + f"{trajectory_length}f", + *[float(headings[i]) if i < len(headings) else 0.0 for i in range(trajectory_length)], + ) + ) + + valids = obj.get("valid", []) + f.write( + struct.pack( + f"{trajectory_length}i", + *[int(valids[i]) if i < len(valids) else 0 for i in range(trajectory_length)], + ) + ) + # Write scalar fields - f.write(struct.pack('f', float(obj.get('width', 0.0)))) - f.write(struct.pack('f', float(obj.get('length', 0.0)))) - f.write(struct.pack('f', float(obj.get('height', 0.0)))) - goal_pos = obj.get('goalPosition', {'x': 0, 'y': 0, 'z': 0}) # Get goalPosition object with default - f.write(struct.pack('f', float(goal_pos.get('x', 0.0)))) # Get x value - f.write(struct.pack('f', float(goal_pos.get('y', 0.0)))) # Get y value - f.write(struct.pack('f', float(goal_pos.get('z', 0.0)))) # Get z value - f.write(struct.pack('i', obj.get('mark_as_expert', 0))) - + f.write(struct.pack("f", float(obj.get("width", 0.0)))) + f.write(struct.pack("f", float(obj.get("length", 0.0)))) + f.write(struct.pack("f", float(obj.get("height", 0.0)))) + goal_pos = obj.get("goalPosition", {"x": 0, "y": 0, "z": 0}) # Get goalPosition object with default + f.write(struct.pack("f", float(goal_pos.get("x", 0.0)))) # Get x value + f.write(struct.pack("f", float(goal_pos.get("y", 0.0)))) # Get y value + f.write(struct.pack("f", float(goal_pos.get("z", 0.0)))) # Get z value + f.write(struct.pack("i", obj.get("mark_as_expert", 0))) + # Write roads - for idx, road in enumerate(map_data.get('roads', [])): - geometry = road.get('geometry', []) - road_type = road.get('map_element_id', 0) - road_type_word = road.get('type', 0) - if(road_type_word == "lane"): + for idx, road in enumerate(map_data.get("roads", [])): + f.write(struct.pack("i", unique_map_id)) + + geometry = road.get("geometry", []) + road_type = road.get("map_element_id", 0) + road_type_word = road.get("type", 0) + if road_type_word == "lane": road_type = 2 - elif(road_type_word == "road_edge"): + elif road_type_word == "road_edge": road_type = 15 # breakpoint() - if(len(geometry) > 10 and road_type <=16): - geometry = simplify_polyline(geometry, .1) + if len(geometry) > 10 and road_type <= 16: + geometry = simplify_polyline(geometry, 0.1) size = len(geometry) # breakpoint() - if(road_type >=0 and road_type <=3): + if road_type >= 0 and road_type <= 3: road_type = 4 - elif(road_type >=5 and road_type <=13): + elif road_type >= 5 and road_type <= 13: road_type = 5 - elif(road_type >=14 and road_type <=16): + elif road_type >= 14 and road_type <= 16: road_type = 6 - elif(road_type == 17): + elif road_type == 17: road_type = 7 - elif(road_type == 18): + elif road_type == 18: road_type = 8 - elif(road_type == 19): + elif road_type == 19: road_type = 9 - elif(road_type == 20): + elif road_type == 20: road_type = 10 # Write base entity data - f.write(struct.pack('i', road_type)) # type - # f.write(struct.pack('i', road.get('id', 0))) # id - f.write(struct.pack('i', size)) # array_size - + f.write(struct.pack("i", road_type)) # type + f.write(struct.pack("i", road.get("id", 0))) # id + f.write(struct.pack("i", size)) # array_size + # Write position arrays - for coord in ['x', 'y', 'z']: + for coord in ["x", "y", "z"]: for point in geometry: - f.write(struct.pack('f', float(point.get(coord, 0.0)))) + f.write(struct.pack("f", float(point.get(coord, 0.0)))) + + # Write scalar fields + f.write(struct.pack("f", float(road.get("width", 0.0)))) + f.write(struct.pack("f", float(road.get("length", 0.0)))) + f.write(struct.pack("f", float(road.get("height", 0.0)))) + goal_pos = road.get("goalPosition", {"x": 0, "y": 0, "z": 0}) # Get goalPosition object with default + f.write(struct.pack("f", float(goal_pos.get("x", 0.0)))) # Get x value + f.write(struct.pack("f", float(goal_pos.get("y", 0.0)))) # Get y value + f.write(struct.pack("f", float(goal_pos.get("z", 0.0)))) # Get z value + f.write(struct.pack("i", road.get("mark_as_expert", 0))) -def load_map(map_name, binary_output=None): + +def load_map(map_name, unique_map_id, binary_output=None): """Loads a JSON map and optionally saves it as binary""" - with open(map_name, 'r') as f: + with open(map_name, "r") as f: map_data = json.load(f) - + if binary_output: - save_map_binary(map_data, binary_output) + save_map_binary(map_data, binary_output, unique_map_id) + def process_all_maps(): """Process all maps and save them as binaries""" - import os from pathlib import Path - # Handle potential symlink conflict - resources_path = Path("resources") - if resources_path.is_symlink(): - print("Removing conflicting symlink at 'resources'") - resources_path.unlink() - # Create the binaries directory if it doesn't exist binary_dir = Path("resources/drive/binaries") binary_dir.mkdir(parents=True, exist_ok=True) # Path to the training data - data_dir = Path("data/carla") - - # Get all JSON files in data directory + data_dir = Path("data/processed/training") + + # Get all JSON files in the training directory json_files = sorted(data_dir.glob("*.json")) - + print(f"Found {len(json_files)} JSON files") - + # Process each JSON file for i, map_path in enumerate(json_files[:10000]): binary_file = f"map_{i:03d}.bin" # Use zero-padded numbers for consistent sorting binary_path = binary_dir / binary_file - + print(f"Processing {map_path.name} -> {binary_file}") # try: - load_map(str(map_path), str(binary_path)) + load_map(str(map_path), i, str(binary_path)) # except Exception as e: # print(f"Error processing {map_path.name}: {e}") -def test_performance(timeout=10, atn_cache=1024, num_agents=1024, num_maps=8): + +def test_performance(timeout=10, atn_cache=1024, num_agents=1024): import time - print("Initializing Drive environment for performance test...") - env = Drive(num_agents=num_agents, num_maps=num_maps) + + env = Drive( + num_agents=num_agents, + num_maps=1, + control_mode="control_vehicles", + init_mode="create_all_valid", + init_steps=0, + scenario_length=91, + ) + env.reset() + tick = 0 - actions = np.stack([ - np.random.randint(0, space.n + 1, (atn_cache, num_agents)) - for space in env.single_action_space - ], axis=-1) - print("Starting performance test...") + actions = np.stack( + [np.random.randint(0, space.n + 1, (atn_cache, num_agents)) for space in env.single_action_space], axis=-1 + ) + start = time.time() while time.time() - start < timeout: - atn = actions[tick % atn_cache] + atn = actions[tick % atn_cache] env.step(atn) tick += 1 - print(f'SPS: {num_agents * tick / (time.time() - start)}') + print(f"SPS: {num_agents * tick / (time.time() - start)}") + env.close() -if __name__ == '__main__': - # test_performance(num_agents=32) + + +if __name__ == "__main__": + # test_performance() process_all_maps() diff --git a/pufferlib/ocean/drive/drivenet.h b/pufferlib/ocean/drive/drivenet.h new file mode 100644 index 000000000..8b05e7020 --- /dev/null +++ b/pufferlib/ocean/drive/drivenet.h @@ -0,0 +1,247 @@ +#include +#include "puffernet.h" +#include +#include +#include +#include +#include +#include + +typedef struct DriveNet DriveNet; +struct DriveNet { + int num_agents; + int ego_dim; + float* obs_self; + float* obs_partner; + float* obs_road; + float* partner_linear_output; + float* road_linear_output; + float* partner_layernorm_output; + float* road_layernorm_output; + float* partner_linear_output_two; + float* road_linear_output_two; + Linear* ego_encoder; + Linear* road_encoder; + Linear* partner_encoder; + LayerNorm* ego_layernorm; + LayerNorm* road_layernorm; + LayerNorm* partner_layernorm; + Linear* ego_encoder_two; + Linear* road_encoder_two; + Linear* partner_encoder_two; + MaxDim1* partner_max; + MaxDim1* road_max; + CatDim1* cat1; + CatDim1* cat2; + GELU* gelu; + Linear* shared_embedding; + ReLU* relu; + LSTM* lstm; + Linear* actor; + Linear* value_fn; + Multidiscrete* multidiscrete; +}; + +DriveNet* init_drivenet(Weights* weights, int num_agents, int dynamics_model) { + DriveNet* net = calloc(1, sizeof(DriveNet)); + int hidden_size = 256; + int input_size = 64; + + int ego_dim = (dynamics_model == JERK) ? 10 : 7; + + // Determine action space size based on dynamics model + int action_size, logit_sizes[2]; + int action_dim; + if (dynamics_model == CLASSIC) { + action_size = 7 * 13; // Joint action space + logit_sizes[0] = 7 * 13; + action_dim = 1; + } else { // JERK + action_size = 7; // 4 + 3 + logit_sizes[0] = 4; + logit_sizes[1] = 3; + action_dim = 2; + } + + net->num_agents = num_agents; + net->ego_dim = ego_dim; + net->obs_self = calloc(num_agents*ego_dim, sizeof(float)); + net->obs_partner = calloc(num_agents*63*7, sizeof(float)); + net->obs_road = calloc(num_agents*200*13, sizeof(float)); + net->partner_linear_output = calloc(num_agents*63*input_size, sizeof(float)); + net->road_linear_output = calloc(num_agents*200*input_size, sizeof(float)); + net->partner_linear_output_two = calloc(num_agents*63*input_size, sizeof(float)); + net->road_linear_output_two = calloc(num_agents*200*input_size, sizeof(float)); + net->partner_layernorm_output = calloc(num_agents*63*input_size, sizeof(float)); + net->road_layernorm_output = calloc(num_agents*200*input_size, sizeof(float)); + net->ego_encoder = make_linear(weights, num_agents, ego_dim, input_size); + net->ego_layernorm = make_layernorm(weights, num_agents, input_size); + net->ego_encoder_two = make_linear(weights, num_agents, input_size, input_size); + net->road_encoder = make_linear(weights, num_agents, 13, input_size); + net->road_layernorm = make_layernorm(weights, num_agents, input_size); + net->road_encoder_two = make_linear(weights, num_agents, input_size, input_size); + net->partner_encoder = make_linear(weights, num_agents, 7, input_size); + net->partner_layernorm = make_layernorm(weights, num_agents, input_size); + net->partner_encoder_two = make_linear(weights, num_agents, input_size, input_size); + net->partner_max = make_max_dim1(num_agents, 63, input_size); + net->road_max = make_max_dim1(num_agents, 200, input_size); + net->cat1 = make_cat_dim1(num_agents, input_size, input_size); + net->cat2 = make_cat_dim1(num_agents, input_size + input_size, input_size); + net->gelu = make_gelu(num_agents, 3*input_size); + net->shared_embedding = make_linear(weights, num_agents, input_size*3, hidden_size); + net->relu = make_relu(num_agents, hidden_size); + net->actor = make_linear(weights, num_agents, hidden_size, action_size); + net->value_fn = make_linear(weights, num_agents, hidden_size, 1); + net->lstm = make_lstm(weights, num_agents, hidden_size, 256); + memset(net->lstm->state_h, 0, num_agents*256*sizeof(float)); + memset(net->lstm->state_c, 0, num_agents*256*sizeof(float)); + net->multidiscrete = make_multidiscrete(num_agents, logit_sizes, action_dim); + return net; +} + +void free_drivenet(DriveNet* net) { + free(net->obs_self); + free(net->obs_partner); + free(net->obs_road); + free(net->partner_linear_output); + free(net->road_linear_output); + free(net->partner_linear_output_two); + free(net->road_linear_output_two); + free(net->partner_layernorm_output); + free(net->road_layernorm_output); + free(net->ego_encoder); + free(net->road_encoder); + free(net->partner_encoder); + free(net->ego_layernorm); + free(net->road_layernorm); + free(net->partner_layernorm); + free(net->ego_encoder_two); + free(net->road_encoder_two); + free(net->partner_encoder_two); + free(net->partner_max); + free(net->road_max); + free(net->cat1); + free(net->cat2); + free(net->gelu); + free(net->shared_embedding); + free(net->relu); + free(net->multidiscrete); + free(net->actor); + free(net->value_fn); + free(net->lstm); + free(net); +} + +void forward(DriveNet* net, float* observations, int* actions) { + int ego_dim = net->ego_dim; + + // Clear previous observations + memset(net->obs_self, 0, net->num_agents * ego_dim * sizeof(float)); + memset(net->obs_partner, 0, net->num_agents * 63 * 7 * sizeof(float)); + memset(net->obs_road, 0, net->num_agents * 200 * 13 * sizeof(float)); + + for (int b = 0; b < net->num_agents; b++) { + int b_offset = b * (ego_dim + 63*7 + 200*7); + int partner_offset = b_offset + ego_dim; + int road_offset = b_offset + ego_dim + 63*7; + + // Process self observation + for(int i = 0; i < ego_dim; i++) { + net->obs_self[b * ego_dim + i] = observations[b_offset + i]; + } + + // Process partner observation + for(int i = 0; i < 63; i++) { + for(int j = 0; j < 7; j++) { + net->obs_partner[b*63*7 + i*7 + j] = observations[partner_offset + i*7 + j]; + } + } + + // Process road observation + for(int i = 0; i < 200; i++) { + for(int j = 0; j < 7; j++) { + net->obs_road[b*200*13 + i*13 + j] = observations[road_offset + i*7 + j]; + } + for(int j = 0; j < 7; j++) { + if(j == observations[road_offset+i*7 + 6]) { + net->obs_road[b*200*13 + i*13 + 6 + j] = 1.0f; + } else { + net->obs_road[b*200*13 + i*13 + 6 + j] = 0.0f; + } + } + } + } + + // Forward pass through the network + linear(net->ego_encoder, net->obs_self); + layernorm(net->ego_layernorm, net->ego_encoder->output); + linear(net->ego_encoder_two, net->ego_layernorm->output); + for (int b = 0; b < net->num_agents; b++) { + for (int obj = 0; obj < 63; obj++) { + // Get the 7 features for this object + float* obj_features = &net->obs_partner[b*63*7 + obj*7]; + // Apply linear layer to this object + _linear(obj_features, net->partner_encoder->weights, net->partner_encoder->bias, + &net->partner_linear_output[b*63*64 + obj*64], 1, 7, 64); + } + } + + for (int b = 0; b < net->num_agents; b++) { + for (int obj = 0; obj < 63; obj++) { + float* after_first = &net->partner_linear_output[b*63*64 + obj*64]; + _layernorm(after_first, net->partner_layernorm->weights, net->partner_layernorm->bias, + &net->partner_layernorm_output[b*63*64 + obj*64], 1, 64); + } + } + for (int b = 0; b < net->num_agents; b++) { + for (int obj = 0; obj < 63; obj++) { + // Get the 7 features for this object + float* obj_features = &net->partner_layernorm_output[b*63*64 + obj*64]; + // Apply linear layer to this object + _linear(obj_features, net->partner_encoder_two->weights, net->partner_encoder_two->bias, + &net->partner_linear_output_two[b*63*64 + obj*64], 1, 64, 64); + + } + } + + // Process road objects: apply linear to each object individually + for (int b = 0; b < net->num_agents; b++) { + for (int obj = 0; obj < 200; obj++) { + // Get the 13 features for this object + float* obj_features = &net->obs_road[b*200*13 + obj*13]; + // Apply linear layer to this object + _linear(obj_features, net->road_encoder->weights, net->road_encoder->bias, + &net->road_linear_output[b*200*64 + obj*64], 1, 13, 64); + } + } + + // Apply layer norm and second linear to each road object + for (int b = 0; b < net->num_agents; b++) { + for (int obj = 0; obj < 200; obj++) { + float* after_first = &net->road_linear_output[b*200*64 + obj*64]; + _layernorm(after_first, net->road_layernorm->weights, net->road_layernorm->bias, + &net->road_layernorm_output[b*200*64 + obj*64], 1, 64); + } + } + for (int b = 0; b < net->num_agents; b++) { + for (int obj = 0; obj < 200; obj++) { + float* after_first = &net->road_layernorm_output[b*200*64 + obj*64]; + _linear(after_first, net->road_encoder_two->weights, net->road_encoder_two->bias, + &net->road_linear_output_two[b*200*64 + obj*64], 1, 64, 64); + } + } + + max_dim1(net->partner_max, net->partner_linear_output_two); + max_dim1(net->road_max, net->road_linear_output_two); + cat_dim1(net->cat1, net->ego_encoder_two->output, net->road_max->output); + cat_dim1(net->cat2, net->cat1->output, net->partner_max->output); + gelu(net->gelu, net->cat2->output); + linear(net->shared_embedding, net->gelu->output); + relu(net->relu, net->shared_embedding->output); + lstm(net->lstm, net->relu->output); + linear(net->actor, net->lstm->state_h); + linear(net->value_fn, net->lstm->state_h); + + // Get action by taking argmax of actor output + softmax_multidiscrete(net->multidiscrete, net->actor->output, actions); +} diff --git a/pufferlib/ocean/drive/error.h b/pufferlib/ocean/drive/error.h new file mode 100644 index 000000000..b1eb78e7e --- /dev/null +++ b/pufferlib/ocean/drive/error.h @@ -0,0 +1,81 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include + +// Error types enumeration +typedef enum { + ERROR_NONE = 0, + ERROR_NULL_POINTER, + ERROR_INVALID_ARGUMENT, + ERROR_OUT_OF_BOUNDS, + ERROR_MEMORY_ALLOCATION, + ERROR_FILE_NOT_FOUND, + ERROR_INITIALIZATION_FAILED, + ERROR_UNKNOWN +} ErrorType; + +const char* error_type_to_string(ErrorType type) { + switch (type) { + case ERROR_NONE: return "No Error"; + case ERROR_NULL_POINTER: return "Null Pointer"; + case ERROR_INVALID_ARGUMENT: return "Invalid Argument"; + case ERROR_OUT_OF_BOUNDS: return "Out of Bounds"; + case ERROR_MEMORY_ALLOCATION: return "Memory Allocation Failed"; + case ERROR_FILE_NOT_FOUND: return "File Not Found"; + case ERROR_INITIALIZATION_FAILED: return "Initialization Failed"; + default: return "Unknown Error"; + } +} + +// Enhanced error function with custom message support +void raise_error_with_message(ErrorType type, const char* format, ...) { + printf("Error occurred: %s", error_type_to_string(type)); + + if (format != NULL) { + printf(" - "); + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + } + printf("\n"); + exit(EXIT_FAILURE); +} + +// Simple error function (backward compatibility) +void raise_error(ErrorType type) { + raise_error_with_message(type, NULL); +} + +// Convenience macros for common error patterns +#define RAISE_FILE_ERROR(path) \ + raise_error_with_message(ERROR_FILE_NOT_FOUND, "at path: %s", path) + +#define RAISE_BOUNDS_ERROR() \ + raise_error(ERROR_OUT_OF_BOUNDS) + +#define RAISE_BOUNDS_ERROR_WITH_BOUNDS(index, min, max) \ + raise_error_with_message(ERROR_OUT_OF_BOUNDS, "index %d exceeds minimum of %d and maximum %d", index, min, max) + +#define RAISE_NULL_ERROR() \ + raise_error(ERROR_NULL_POINTER) + +#define RAISE_NULL_ERROR_WITH_NAME(var_name) \ + raise_error_with_message(ERROR_NULL_POINTER, "variable '%s' is null", var_name) + +#define RAISE_MEMORY_ERROR() \ + raise_error(ERROR_MEMORY_ALLOCATION) + +#define RAISE_MEMORY_ERROR_WITH_SIZE(size) \ + raise_error_with_message(ERROR_MEMORY_ALLOCATION, "failed to allocate %zu bytes", size) + +#define RAISE_INVALID_ARG_ERROR() \ + raise_error(ERROR_INVALID_ARGUMENT) + +#define RAISE_INVALID_ARG_ERROR_WITH_ARG(arg_name, value) \ + raise_error_with_message(ERROR_INVALID_ARGUMENT, "invalid value for '%s': %d", arg_name, value) +#endif diff --git a/pufferlib/ocean/drive/visualize.c b/pufferlib/ocean/drive/visualize.c new file mode 100644 index 000000000..9642e2cbd --- /dev/null +++ b/pufferlib/ocean/drive/visualize.c @@ -0,0 +1,527 @@ +#include +#include +#include +#include +#include +#include +#include "rlgl.h" +#include +#include +#include +#include +#include "error.h" +#include "drive.h" +#include "drivenet.h" +#include "libgen.h" +#include "../env_config.h" +#define TRAJECTORY_LENGTH_DEFAULT 91 + +typedef struct { + int pipefd[2]; + pid_t pid; +} VideoRecorder; + +bool OpenVideo(VideoRecorder *recorder, const char *output_filename, int width, int height) { + if (pipe(recorder->pipefd) == -1) { + fprintf(stderr, "Failed to create pipe\n"); + return false; + } + + recorder->pid = fork(); + if (recorder->pid == -1) { + fprintf(stderr, "Failed to fork\n"); + return false; + } + + char size_str[64]; + snprintf(size_str, sizeof(size_str), "%dx%d", width, height); + + if (recorder->pid == 0) { // Child process: run ffmpeg + close(recorder->pipefd[1]); + dup2(recorder->pipefd[0], STDIN_FILENO); + close(recorder->pipefd[0]); + // Close all other file descriptors to prevent leaks + for (int fd = 3; fd < 256; fd++) { + close(fd); + } + execlp("ffmpeg", "ffmpeg", + "-y", + "-f", "rawvideo", + "-pix_fmt", "rgba", + "-s", size_str, + "-r", "30", + "-i", "-", + "-c:v", "libx264", + "-pix_fmt", "yuv420p", + "-preset", "ultrafast", + "-crf", "23", + "-loglevel", "error", + output_filename, + NULL); + TraceLog(LOG_ERROR, "Failed to launch ffmpeg"); + return false; + } + + close(recorder->pipefd[0]); // Close read end in parent + return true; +} + +void WriteFrame(VideoRecorder *recorder, int width, int height) { + unsigned char *screen_data = rlReadScreenPixels(width, height); + write(recorder->pipefd[1], screen_data, width * height * 4 * sizeof(*screen_data)); + RL_FREE(screen_data); +} + +void CloseVideo(VideoRecorder *recorder) { + close(recorder->pipefd[1]); + waitpid(recorder->pid, NULL, 0); +} + +void renderTopDownView(Drive* env, Client* client, int map_height, int obs, int lasers, int trajectories, int frame_count, float* path, int log_trajectories, int show_grid, int img_width, int img_height) { + + BeginDrawing(); + + // Top-down orthographic camera + Camera3D camera = {0}; + camera.position = (Vector3){ 0.0f, 0.0f, 500.0f }; // above the scene + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // look at origin + camera.up = (Vector3){ 0.0f, -1.0f, 0.0f }; + camera.fovy = map_height; + camera.projection = CAMERA_ORTHOGRAPHIC; + + client->width = img_width; + client->height = img_height; + + Color road = (Color){35, 35, 37, 255}; + ClearBackground(road); + BeginMode3D(camera); + rlEnableDepthTest(); + + // Draw human replay trajectories if enabled + if(log_trajectories){ + for(int i=0; iactive_agent_count; i++){ + int idx = env->active_agent_indices[i]; + Vector3 prev_point = {0}; + bool has_prev = false; + + for(int j = 0; j < env->agents[idx].trajectory_length; j++){ + float x = env->agents[idx].log_trajectory_x[j]; + float y = env->agents[idx].log_trajectory_y[j]; + float valid = env->agents[idx].log_valid[j]; + + if(!valid) { + has_prev = false; + continue; + } + + Vector3 curr_point = {x, y, 0.5f}; + + if(has_prev) { + DrawLine3D(prev_point, curr_point, Fade(LIGHTGREEN, 0.6f)); + } + + prev_point = curr_point; + has_prev = true; + } + } +} + + // Draw agent trajs + if(trajectories){ + for(int i=0; iactive_agent_indices[env->human_agent_idx]; + Agent* agent = &env->agents[agent_idx]; + + BeginDrawing(); + + Camera3D camera = {0}; + // Position camera behind and above the agent + camera.position = (Vector3){ + agent->sim_x - (25.0f * cosf(agent->sim_heading)), + agent->sim_y - (25.0f * sinf(agent->sim_heading)), + 15.0f + }; + camera.target = (Vector3){ + agent->sim_x + 40.0f * cosf(agent->sim_heading), + agent->sim_y + 40.0f * sinf(agent->sim_heading), + 1.0f + }; + camera.up = (Vector3){ 0.0f, 0.0f, 1.0f }; + camera.fovy = 45.0f; + camera.projection = CAMERA_PERSPECTIVE; + + Color road = (Color){35, 35, 37, 255}; + + ClearBackground(road); + BeginMode3D(camera); + rlEnableDepthTest(); + draw_scene(env, client, 0, obs_only, lasers, show_grid); // mode=0 for agent view + EndMode3D(); + EndDrawing(); +} + +static int run_cmd(const char *cmd) { + int rc = system(cmd); + if (rc != 0) { + fprintf(stderr, "[ffmpeg] command failed (%d): %s\n", rc, cmd); + } + return rc; +} + +// Make a high-quality GIF from numbered PNG frames like frame_000.png +static int make_gif_from_frames(const char *pattern, int fps, + const char *palette_path, + const char *out_gif) { + char cmd[1024]; + + // 1) Generate palette (no quotes needed for simple filter) + // NOTE: if your frames start at 000, you don't need -start_number. + snprintf(cmd, sizeof(cmd), + "ffmpeg -y -framerate %d -i %s -vf palettegen %s", + fps, pattern, palette_path); + if (run_cmd(cmd) != 0) return -1; + + // 2) Use palette to encode the GIF + snprintf(cmd, sizeof(cmd), + "ffmpeg -y -framerate %d -i %s -i %s -lavfi paletteuse -loop 0 %s", + fps, pattern, palette_path, out_gif); + if (run_cmd(cmd) != 0) return -1; + + return 0; +} + + +int eval_gif(const char* map_name, const char* policy_name, int show_grid, int obs_only, int lasers, int log_trajectories, int frame_skip, float goal_radius, int init_steps, int max_controlled_agents, const char* view_mode, const char* output_topdown, const char* output_agent, int num_maps, int scenario_length_override, int init_mode, int control_mode, int goal_behavior) { + + // Parse configuration from INI file + env_init_config conf = {0}; // Initialize to zero + const char* ini_file = "pufferlib/config/ocean/drive.ini"; + if(ini_parse(ini_file, handler, &conf) < 0) { + fprintf(stderr, "Error: Could not load %s. Cannot determine environment configuration.\n", ini_file); + return -1; + } + + char map_buffer[100]; + if (map_name == NULL) { + srand(time(NULL)); + int random_map = rand() % num_maps; + sprintf(map_buffer, "resources/drive/binaries/map_%03d.bin", random_map); // random map file + map_name = map_buffer; + } + + if (frame_skip <= 0) { + frame_skip = 1; // Default: render every frame + } + + // Check if map file exists + FILE* map_file = fopen(map_name, "rb"); + if (map_file == NULL) { + RAISE_FILE_ERROR(map_name); + } + fclose(map_file); + + FILE* policy_file = fopen(policy_name, "rb"); + if (policy_file == NULL) { + RAISE_FILE_ERROR(policy_name); + } + fclose(policy_file); + + Drive env = { + .dynamics_model = conf.dynamics_model, + .reward_vehicle_collision = conf.reward_vehicle_collision, + .reward_offroad_collision = conf.reward_offroad_collision, + .reward_ade = conf.reward_ade, + .goal_radius = conf.goal_radius, + .dt = conf.dt, + .map_name = (char*)map_name, + .init_steps = init_steps, + .max_controlled_agents = max_controlled_agents, + .collision_behavior = conf.collision_behavior, + .offroad_behavior = conf.offroad_behavior, + .goal_behavior = goal_behavior, + .init_mode = init_mode, + .control_mode = control_mode, + }; + + env.scenario_length = (scenario_length_override > 0) ? scenario_length_override : + (conf.scenario_length > 0) ? conf.scenario_length : TRAJECTORY_LENGTH_DEFAULT; + allocate(&env); + + // Set which vehicle to focus on for obs mode + env.human_agent_idx = 0; + + c_reset(&env); + // Make client for rendering + Client* client = (Client*)calloc(1, sizeof(Client)); + env.client = client; + + SetConfigFlags(FLAG_WINDOW_HIDDEN); + + SetTargetFPS(6000); + + float map_width = env.grid_map->bottom_right_x - env.grid_map->top_left_x; + float map_height = env.grid_map->top_left_y - env.grid_map->bottom_right_y; + + printf("Map size: %.1fx%.1f\n", map_width, map_height); + float scale = 6.0f; // Can be used to increase the video quality + + // Calculate video width and height; round to nearest even number + int img_width = (int)roundf(map_width * scale / 2.0f) * 2; + int img_height = (int)roundf(map_height * scale / 2.0f) * 2; + InitWindow(img_width, img_height, "Puffer Drive"); + SetConfigFlags(FLAG_MSAA_4X_HINT); + + Weights* weights = load_weights(policy_name); + printf("Active agents in map: %d\n", env.active_agent_count); + DriveNet* net = init_drivenet(weights, env.active_agent_count, env.dynamics_model); + + int frame_count = env.scenario_length > 0 ? env.scenario_length : TRAJECTORY_LENGTH_DEFAULT; + int log_trajectory = log_trajectories; + char filename_topdown[256]; + char filename_agent[256]; + + if (output_topdown != NULL && output_agent != NULL) { + strcpy(filename_topdown, output_topdown); + strcpy(filename_agent, output_agent); + } else { + char policy_base[256]; + strcpy(policy_base, policy_name); + *strrchr(policy_base, '.') = '\0'; + + char map[256]; + strcpy(map, basename((char*)map_name)); + *strrchr(map, '.') = '\0'; + + // Create video directory if it doesn't exist + char video_dir[256]; + sprintf(video_dir, "%s/video", policy_base); + char mkdir_cmd[512]; + snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p \"%s\"", video_dir); + system(mkdir_cmd); + + sprintf(filename_topdown, "%s/video/%s_topdown.mp4", policy_base, map); + sprintf(filename_agent, "%s/video/%s_agent.mp4", policy_base, map); + } + + bool render_topdown = (strcmp(view_mode, "both") == 0 || strcmp(view_mode, "topdown") == 0); + bool render_agent = (strcmp(view_mode, "both") == 0 || strcmp(view_mode, "agent") == 0); + + printf("Rendering: %s\n", view_mode); + + int rendered_frames = 0; + double startTime = GetTime(); + + VideoRecorder topdown_recorder, agent_recorder; + + if (render_topdown) { + if (!OpenVideo(&topdown_recorder, filename_topdown, img_width, img_height)) { + CloseWindow(); + return -1; + } + } + + if (render_agent) { + if (!OpenVideo(&agent_recorder, filename_agent, img_width, img_height)) { + if (render_topdown) CloseVideo(&topdown_recorder); + CloseWindow(); + return -1; + } + } + + if (render_topdown) { + printf("Recording topdown view...\n"); + for(int i = 0; i < frame_count; i++) { + if (i % frame_skip == 0) { + renderTopDownView(&env, client, map_height, 0, 0, 0, frame_count, NULL, log_trajectories, show_grid, img_width, img_height); + WriteFrame(&topdown_recorder, img_width, img_height); + rendered_frames++; + } + int (*actions)[2] = (int(*)[2])env.actions; + forward(net, env.observations, (int*)env.actions); + c_step(&env); + } + + } + + if (render_agent) { + c_reset(&env); + printf("Recording agent view...\n"); + for(int i = 0; i < frame_count; i++) { + if (i % frame_skip == 0) { + renderAgentView(&env, client, map_height, obs_only, lasers, show_grid); + WriteFrame(&agent_recorder, img_width, img_height); + rendered_frames++; + } + int (*actions)[2] = (int(*)[2])env.actions; + forward(net, env.observations, (int*)env.actions); + c_step(&env); + } + } + + double endTime = GetTime(); + double elapsedTime = endTime - startTime; + double writeFPS = (elapsedTime > 0) ? rendered_frames / elapsedTime : 0; + + printf("Wrote %d frames in %.2f seconds (%.2f FPS) to %s \n", + rendered_frames, elapsedTime, writeFPS, filename_topdown); + + if (render_topdown) { + CloseVideo(&topdown_recorder); + } + if (render_agent) { + CloseVideo(&agent_recorder); + } + CloseWindow(); + + // Clean up resources + free(client); + free_allocated(&env); + free_drivenet(net); + free(weights); + return 0; +} + +int main(int argc, char* argv[]) { + int show_grid = 0; + int obs_only = 0; + int lasers = 0; + int log_trajectories = 1; + int frame_skip = 1; + float goal_radius = 2.0f; + int init_steps = 0; + const char* map_name = NULL; + const char* policy_name = "resources/drive/puffer_drive_weights.bin"; + int max_controlled_agents = -1; + int num_maps = 1; + int scenario_length_cli = -1; + int init_mode = 0; + int control_mode = 0; + int goal_behavior = 0; + + const char* view_mode = "both"; // "both", "topdown", "agent" + const char* output_topdown = NULL; + const char* output_agent = NULL; + + // Parse command line arguments + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--show-grid") == 0) { + show_grid = 1; + } else if (strcmp(argv[i], "--obs-only") == 0) { + obs_only = 1; + } else if (strcmp(argv[i], "--lasers") == 0) { + lasers = 1; + } else if (strcmp(argv[i], "--log-trajectories") == 0) { + log_trajectories = 1; + } else if (strcmp(argv[i], "--frame-skip") == 0) { + if (i + 1 < argc) { + frame_skip = atoi(argv[i + 1]); + i++; // Skip the next argument since we consumed it + if (frame_skip <= 0) { + frame_skip = 1; // Ensure valid value + } + } + } else if (strcmp(argv[i], "--goal-radius") == 0) { + if (i + 1 < argc) { + goal_radius = atof(argv[i + 1]); + i++; + if (goal_radius <= 0) { + goal_radius = 2.0f; // Ensure valid value + } + } + } else if (strcmp(argv[i], "--map-name") == 0) { + // Check if there's a next argument for the map path + if (i + 1 < argc) { + map_name = argv[i + 1]; + i++; // Skip the next argument since we used it as map path + } else { + fprintf(stderr, "Error: --map-name option requires a map file path\n"); + return 1; + } + } else if (strcmp(argv[i], "--policy-name") == 0) { + if (i + 1 < argc) { + policy_name = argv[i + 1]; + i++; + } else { + fprintf(stderr, "Error: --policy-name option requires a policy file path\n"); + return 1; + } + } else if (strcmp(argv[i], "--view") == 0) { + if (i + 1 < argc) { + view_mode = argv[i + 1]; + i++; + if (strcmp(view_mode, "both") != 0 && + strcmp(view_mode, "topdown") != 0 && + strcmp(view_mode, "agent") != 0) { + fprintf(stderr, "Error: --view must be 'both', 'topdown', or 'agent'\n"); + return 1; + } + } else { + fprintf(stderr, "Error: --view option requires a value (both/topdown/agent)\n"); + return 1; + } + } else if (strcmp(argv[i], "--output-topdown") == 0) { + if (i + 1 < argc) { + output_topdown = argv[i + 1]; + i++; + } + } else if (strcmp(argv[i], "--output-agent") == 0) { + if (i + 1 < argc) { + output_agent = argv[i + 1]; + i++; + } + } else if (strcmp(argv[i], "--init-steps") == 0) { + if (i + 1 < argc) { + init_steps = atoi(argv[i + 1]); + i++; + if (init_steps < 0) { + init_steps = 0; + } + } + } else if (strcmp(argv[i], "--init-mode") == 0) { + if (i + 1 < argc) { + init_mode = atoi(argv[i + 1]); + i++; + } + } else if (strcmp(argv[i], "--control-mode") == 0) { + if (i + 1 < argc) { + control_mode = atoi(argv[i + 1]); + i++; + } + } else if (strcmp(argv[i], "--max-controlled-agents") == 0) { + if (i + 1 < argc) { + max_controlled_agents = atoi(argv[i + 1]); + i++; + } + } else if (strcmp(argv[i], "--num-maps") == 0) { + if (i + 1 < argc) { + num_maps = atoi(argv[i + 1]); + i++; + } + } else if (strcmp(argv[i], "--scenario-length") == 0) { + if (i + 1 < argc) { + scenario_length_cli = atoi(argv[i + 1]); + i++; + } + } else if (strcmp(argv[i], "--goal-behavior") == 0) { + if (i + 1 < argc) { + goal_behavior = atoi(argv[i + 1]); + i++; + } + } + } + + eval_gif(map_name, policy_name, show_grid, obs_only, lasers, log_trajectories, frame_skip, goal_radius, init_steps, max_controlled_agents, view_mode, output_topdown, output_agent, num_maps, scenario_length_cli, init_mode, control_mode, goal_behavior); + return 0; +} diff --git a/pufferlib/ocean/drone_race/binding.c b/pufferlib/ocean/drone_race/binding.c deleted file mode 100644 index 9b3591ffe..000000000 --- a/pufferlib/ocean/drone_race/binding.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "drone_race.h" - -#define Env DroneRace -#include "../env_binding.h" - -static int my_init(Env *env, PyObject *args, PyObject *kwargs) { - env->max_rings = unpack(kwargs, "max_rings"); - env->max_moves = unpack(kwargs, "max_moves"); - init(env); - return 0; -} - -static int my_log(PyObject *dict, Log *log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/drone_race/drone_race.c b/pufferlib/ocean/drone_race/drone_race.c deleted file mode 100644 index 3646a44dc..000000000 --- a/pufferlib/ocean/drone_race/drone_race.c +++ /dev/null @@ -1,178 +0,0 @@ -// Standalone C demo for DroneRace environment -// Compile using: ./scripts/build_ocean.sh drone [local|fast] -// Run with: ./drone - -#include "drone_race.h" -#include "puffernet.h" -#include - -#ifdef __EMSCRIPTEN__ -#include -#endif - -double randn(double mean, double std) { - static int has_spare = 0; - static double spare; - - if (has_spare) { - has_spare = 0; - return mean + std * spare; - } - - has_spare = 1; - double u, v, s; - do { - u = 2.0 * rand() / RAND_MAX - 1.0; - v = 2.0 * rand() / RAND_MAX - 1.0; - s = u * u + v * v; - } while (s >= 1.0 || s == 0.0); - - s = sqrt(-2.0 * log(s) / s); - spare = v * s; - return mean + std * (u * s); -} - -typedef struct LinearContLSTM LinearContLSTM; -struct LinearContLSTM { - int num_agents; - float *obs; - float *log_std; - Linear *encoder; - GELU *gelu1; - LSTM *lstm; - Linear *actor; - Linear *value_fn; - int num_actions; -}; - -LinearContLSTM *make_linearcontlstm(Weights *weights, int num_agents, int input_dim, - int logit_sizes[], int num_actions) { - LinearContLSTM *net = calloc(1, sizeof(LinearContLSTM)); - net->num_agents = num_agents; - net->obs = calloc(num_agents * input_dim, sizeof(float)); - net->num_actions = logit_sizes[0]; - net->log_std = weights->data; - weights->idx += net->num_actions; - net->encoder = make_linear(weights, num_agents, input_dim, 128); - net->gelu1 = make_gelu(num_agents, 128); - int atn_sum = 0; - for (int i = 0; i < num_actions; i++) { - atn_sum += logit_sizes[i]; - } - net->actor = make_linear(weights, num_agents, 128, atn_sum); - net->value_fn = make_linear(weights, num_agents, 128, 1); - net->lstm = make_lstm(weights, num_agents, 128, 128); - return net; -} - -void free_linearcontlstm(LinearContLSTM *net) { - free(net->obs); - free(net->encoder); - free(net->gelu1); - free(net->actor); - free(net->value_fn); - free(net->lstm); - free(net); -} - -void forward_linearcontlstm(LinearContLSTM *net, float *observations, float *actions) { - linear(net->encoder, observations); - gelu(net->gelu1, net->encoder->output); - lstm(net->lstm, net->gelu1->output); - linear(net->actor, net->lstm->state_h); - linear(net->value_fn, net->lstm->state_h); - for (int i = 0; i < net->num_actions; i++) { - float std = expf(net->log_std[i]); - float mean = net->actor->output[i]; - actions[i] = randn(mean, std); - } -} - -void generate_dummy_actions(DroneRace *env) { - // Generate random floats in [-1, 1] range - env->actions[0] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - env->actions[1] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - env->actions[2] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - env->actions[3] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; -} - -#ifdef __EMSCRIPTEN__ -typedef struct { - DroneRace *env; - LinearContLSTM *net; - Weights *weights; -} WebRenderArgs; - -void emscriptenStep(void *e) { - WebRenderArgs *args = (WebRenderArgs *)e; - DroneRace *env = args->env; - LinearContLSTM *net = args->net; - - forward_linearcontlstm(net, env->observations, env->actions); - c_step(env); - c_render(env); - return; -} - -WebRenderArgs *web_args = NULL; -#endif - -int main() { - srand(time(NULL)); // Seed random number generator - - DroneRace *env = calloc(1, sizeof(DroneRace)); - env->max_moves = 1000; - env->max_rings = 10; - - size_t obs_size = 25; - size_t act_size = 4; - env->observations = (float *)calloc(obs_size, sizeof(float)); - env->actions = (float *)calloc(act_size, sizeof(float)); - env->rewards = (float *)calloc(1, sizeof(float)); - env->terminals = (unsigned char *)calloc(1, sizeof(float)); - - Weights *weights = load_weights("resources/drone/drone_weights.bin", 136073); - int logit_sizes[1] = {4}; - LinearContLSTM *net = make_linearcontlstm(weights, 1, 25, logit_sizes, 1); - - if (!env->observations || !env->actions || !env->rewards) { - fprintf(stderr, "ERROR: Failed to allocate memory for demo buffers.\n"); - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - free(env); - return 0; - } - - init(env); - c_reset(env); - -#ifdef __EMSCRIPTEN__ - WebRenderArgs *args = calloc(1, sizeof(WebRenderArgs)); - args->env = env; - args->net = net; - args->weights = weights; - web_args = args; - - emscripten_set_main_loop_arg(emscriptenStep, args, 0, true); -#else - c_render(env); - - while (!WindowShouldClose()) { - forward_linearcontlstm(net, env->observations, env->actions); - c_step(env); - c_render(env); - } - - c_close(env); - free_linearcontlstm(net); - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - free(env); -#endif - - return 0; -} diff --git a/pufferlib/ocean/drone_race/drone_race.h b/pufferlib/ocean/drone_race/drone_race.h deleted file mode 100644 index 78acfc8e6..000000000 --- a/pufferlib/ocean/drone_race/drone_race.h +++ /dev/null @@ -1,466 +0,0 @@ -// Originally made by Sam Turner and Finlay Sanders, 2025. -// Included in pufferlib under the original project's MIT license. -// https://github.com/stmio/drone - -#include -#include -#include -#include -#include -#include -#include - -#include "raylib.h" -#include "dronelib.h" - -typedef struct Client Client; -struct Client { - Camera3D camera; - float width; - float height; - - float camera_distance; - float camera_azimuth; - float camera_elevation; - bool is_dragging; - Vector2 last_mouse_pos; - - // Trailing path buffer (for rendering only) - Vec3 trail[TRAIL_LENGTH]; - int trail_index; - int trail_count; -}; - -typedef struct DroneRace DroneRace; -struct DroneRace { - float *observations; - float *actions; - float *rewards; - unsigned char *terminals; - - Log log; - int tick; - int report_interval; - int score; - float episodic_return; - - int max_rings; - int ring_idx; - Ring *ring_buffer; - - int max_moves; - int moves_left; - - Drone drone; - Client *client; -}; - -void init(DroneRace *env) { - env->log = (Log){0}; - env->tick = 0; - // one extra ring for observation (requires current ring, next ring) - // max_rings and moves_left are initialised in binding.c - env->ring_buffer = (Ring *)malloc((env->max_rings + 1) * sizeof(Ring)); -} - -void add_log(DroneRace *env) { - env->log.score += env->score; - env->log.episode_return += env->episodic_return; - env->log.episode_length += env->tick; - env->log.perf += (float)env->ring_idx / (float)env->max_rings; - env->log.n += 1.0f; -} - -void compute_observations(DroneRace *env) { - Drone *drone = &env->drone; - - Quat q_inv = quat_inverse(drone->quat); - Ring curr_ring = env->ring_buffer[env->ring_idx]; - Ring next_ring = env->ring_buffer[env->ring_idx + 1]; - - Vec3 to_curr_ring = quat_rotate(q_inv, sub3(curr_ring.pos, drone->pos)); - Vec3 to_next_ring = quat_rotate(q_inv, sub3(next_ring.pos, drone->pos)); - - Vec3 curr_ring_norm = quat_rotate(q_inv, curr_ring.normal); - Vec3 next_ring_norm = quat_rotate(q_inv, next_ring.normal); - - Vec3 linear_vel_body = quat_rotate(q_inv, drone->vel); - Vec3 drone_up_world = quat_rotate(drone->quat, (Vec3){0.0f, 0.0f, 1.0f}); - - env->observations[0] = to_curr_ring.x / GRID_SIZE; - env->observations[1] = to_curr_ring.y / GRID_SIZE; - env->observations[2] = to_curr_ring.z / GRID_SIZE; - - env->observations[3] = curr_ring_norm.x; - env->observations[4] = curr_ring_norm.y; - env->observations[5] = curr_ring_norm.z; - - env->observations[6] = to_next_ring.x / GRID_SIZE; - env->observations[7] = to_next_ring.y / GRID_SIZE; - env->observations[8] = to_next_ring.z / GRID_SIZE; - - env->observations[9] = next_ring_norm.x; - env->observations[10] = next_ring_norm.y; - env->observations[11] = next_ring_norm.z; - - env->observations[12] = linear_vel_body.x / drone->max_vel; - env->observations[13] = linear_vel_body.y / drone->max_vel; - env->observations[14] = linear_vel_body.z / drone->max_vel; - - env->observations[15] = drone->omega.x / drone->max_omega; - env->observations[16] = drone->omega.y / drone->max_omega; - env->observations[17] = drone->omega.z / drone->max_omega; - - env->observations[18] = drone_up_world.x; - env->observations[19] = drone_up_world.y; - env->observations[20] = drone_up_world.z; - - env->observations[21] = drone->quat.w; - env->observations[22] = drone->quat.x; - env->observations[23] = drone->quat.y; - env->observations[24] = drone->quat.z; -} - -void c_reset(DroneRace *env) { - env->tick = 0; - env->score = 0; - env->episodic_return = 0.0f; - - env->moves_left = env->max_moves; - - env->ring_idx = 0; - - Drone *drone = &env->drone; - - float size = rndf(0.05f, 0.8); - init_drone(drone, size, 0.1f); - - //init_drone(drone, 0.8f, 0.0f); - //init_drone(drone, 0.05f, 0.0f); - - // creates rings at least MARGIN apart - float ring_radius = 2.0f; - if (env->max_rings + 1 > 0) { - env->ring_buffer[0] = rndring(ring_radius); - } - - for (int i = 1; i < env->max_rings + 1; i++) { - do { - env->ring_buffer[i] = rndring(ring_radius); - } while (norm3(sub3(env->ring_buffer[i].pos, env->ring_buffer[i - 1].pos)) < 2.0f*ring_radius); - } - - // start drone at least MARGIN away from the first ring - do { - drone->pos = (Vec3){rndf(-9, 9), rndf(-9, 9), rndf(-9, 9)}; - } while (norm3(sub3(drone->pos, env->ring_buffer[0].pos)) < 2.0f*ring_radius); - - drone->prev_pos = drone->pos; - drone->vel = (Vec3){0.0f, 0.0f, 0.0f}; - drone->omega = (Vec3){0.0f, 0.0f, 0.0f}; - drone->quat = (Quat){1.0f, 0.0f, 0.0f, 0.0f}; - compute_observations(env); -} - -void c_step(DroneRace *env) { - - env->tick++; - env->rewards[0] = 0; - env->terminals[0] = 0; - env->log.score = 0; - - Drone *drone = &env->drone; - move_drone(drone, env->actions); - - // check out of bounds - bool out_of_bounds = drone->pos.x < -GRID_SIZE || drone->pos.x > GRID_SIZE || - drone->pos.y < -GRID_SIZE || drone->pos.y > GRID_SIZE || - drone->pos.z < -GRID_SIZE || drone->pos.z > GRID_SIZE; - - if (out_of_bounds) { - env->rewards[0] -= 1; - env->episodic_return -= 1; - env->terminals[0] = 1; - add_log(env); - c_reset(env); - compute_observations(env); - return; - } - - Ring *ring = &env->ring_buffer[env->ring_idx]; - float reward = check_ring(drone, ring); - env->rewards[0] += reward; - env->episodic_return += reward; - - if (reward > 0) { - env->score++; - env->ring_idx++; - } else if (reward < 0) { - env->terminals[0] = 1; - add_log(env); - c_reset(env); - return; - } - - // truncate - env->moves_left -= 1; - if (env->moves_left == 0 || env->ring_idx == env->max_rings) { - env->terminals[0] = 1; - add_log(env); - c_reset(env); - return; - } - - drone->prev_pos = drone->pos; - - compute_observations(env); -} - -void c_close_client(Client *client) { - CloseWindow(); - free(client); -} - -void c_close(DroneRace *env) { - free(env->ring_buffer); - - if (env->client != NULL) { - c_close_client(env->client); - } -} - -static void update_camera_position(Client *c) { - float r = c->camera_distance; - float az = c->camera_azimuth; - float el = c->camera_elevation; - - float x = r * cosf(el) * cosf(az); - float y = r * cosf(el) * sinf(az); - float z = r * sinf(el); - - c->camera.position = (Vector3){x, y, z}; - c->camera.target = (Vector3){0, 0, 0}; -} - -void handle_camera_controls(Client *client) { - Vector2 mouse_pos = GetMousePosition(); - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - client->is_dragging = true; - client->last_mouse_pos = mouse_pos; - } - - if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - client->is_dragging = false; - } - - if (client->is_dragging && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - Vector2 mouse_delta = {mouse_pos.x - client->last_mouse_pos.x, - mouse_pos.y - client->last_mouse_pos.y}; - - float sensitivity = 0.005f; - - client->camera_azimuth -= mouse_delta.x * sensitivity; - - client->camera_elevation += mouse_delta.y * sensitivity; - client->camera_elevation = - clampf(client->camera_elevation, -PI / 2.0f + 0.1f, PI / 2.0f - 0.1f); - - client->last_mouse_pos = mouse_pos; - - update_camera_position(client); - } - - float wheel = GetMouseWheelMove(); - if (wheel != 0) { - client->camera_distance -= wheel * 2.0f; - client->camera_distance = clampf(client->camera_distance, 5.0f, 50.0f); - update_camera_position(client); - } -} - -Client *make_client(DroneRace *env) { - Client *client = (Client *)calloc(1, sizeof(Client)); - - client->width = WIDTH; - client->height = HEIGHT; - - SetConfigFlags(FLAG_MSAA_4X_HINT); // antialiasing - InitWindow(WIDTH, HEIGHT, "PufferLib DroneRace"); - -#ifndef __EMSCRIPTEN__ - SetTargetFPS(60); -#endif - - if (!IsWindowReady()) { - TraceLog(LOG_ERROR, "Window failed to initialize\n"); - free(client); - return NULL; - } - - client->camera_distance = 40.0f; - client->camera_azimuth = 0.0f; - client->camera_elevation = PI / 10.0f; - client->is_dragging = false; - client->last_mouse_pos = (Vector2){0.0f, 0.0f}; - - client->camera.up = (Vector3){0.0f, 0.0f, 1.0f}; - client->camera.fovy = 45.0f; - client->camera.projection = CAMERA_PERSPECTIVE; - - update_camera_position(client); - - // Initialize trail buffer - client->trail_index = 0; - client->trail_count = 0; - Drone *drone = &env->drone; - for (int i = 0; i < TRAIL_LENGTH; i++) { - client->trail[i] = drone->pos; - } - - return client; -} - -void DrawRing3D(Ring ring, float thickness, Color entryColor, Color exitColor) { - float half_thick = thickness / 2.0f; - - Vector3 center_pos = {ring.pos.x, ring.pos.y, ring.pos.z}; - - Vector3 entry_start_pos = {center_pos.x - half_thick * ring.normal.x, - center_pos.y - half_thick * ring.normal.y, - center_pos.z - half_thick * ring.normal.z}; - - DrawCylinderWiresEx(entry_start_pos, center_pos, ring.radius, ring.radius, 32, entryColor); - - Vector3 exit_end_pos = {center_pos.x + half_thick * ring.normal.x, - center_pos.y + half_thick * ring.normal.y, - center_pos.z + half_thick * ring.normal.z}; - - DrawCylinderWiresEx(center_pos, exit_end_pos, ring.radius, ring.radius, 32, exitColor); -} - -void c_render(DroneRace *env) { - Drone *drone = &env->drone; - if (env->client == NULL) { - env->client = make_client(env); - if (env->client == NULL) { - TraceLog(LOG_ERROR, "Failed to initialize client for rendering\n"); - return; - } - } - - if (WindowShouldClose()) { - c_close(env); - exit(0); - } - - if (IsKeyDown(KEY_ESCAPE)) { - c_close(env); - exit(0); - } - - handle_camera_controls(env->client); - - Client *client = env->client; - client->trail[client->trail_index] = drone->pos; - client->trail_index = (client->trail_index + 1) % TRAIL_LENGTH; - if (client->trail_count < TRAIL_LENGTH) - client->trail_count++; - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - BeginMode3D(client->camera); - - // draws bounding cube - DrawCubeWires((Vector3){0.0f, 0.0f, 0.0f}, GRID_SIZE * 2.0f, GRID_SIZE * 2.0f, GRID_SIZE * 2.0f, - WHITE); - - // draws drone body - float r = drone->arm_len; - DrawSphere((Vector3){drone->pos.x, drone->pos.y, drone->pos.z}, r/2.0f, RED); - - // draws rotors according to thrust - float T[4]; - for (int i = 0; i < 4; i++) { - float rpm = (env->actions[i] + 1.0f) * 0.5f * drone->max_rpm; - T[i] = drone->k_thrust * rpm * rpm; - } - - const float rotor_radius = r / 4.0f; - const float visual_arm_len = 1.0f * drone->arm_len; - - Vec3 rotor_offsets_body[4] = {{+r, 0.0f, 0.0f}, - {-r, 0.0f, 0.0f}, - {0.0f, +r, 0.0f}, - {0.0f, -r, 0.0f}}; - - Color base_colors[4] = {ORANGE, PURPLE, LIME, SKYBLUE}; - - for (int i = 0; i < 4; i++) { - Vec3 world_off = quat_rotate(drone->quat, rotor_offsets_body[i]); - - Vector3 rotor_pos = {drone->pos.x + world_off.x, drone->pos.y + world_off.y, - drone->pos.z + world_off.z}; - - float rpm = (env->actions[i] + 1.0f) * 0.5f * drone->max_rpm; - float intensity = 0.75f + 0.25f * (rpm / drone->max_rpm); - - Color rotor_color = (Color){(unsigned char)(base_colors[i].r * intensity), - (unsigned char)(base_colors[i].g * intensity), - (unsigned char)(base_colors[i].b * intensity), 255}; - - DrawSphere(rotor_pos, rotor_radius, rotor_color); - - DrawCylinderEx((Vector3){drone->pos.x, drone->pos.y, drone->pos.z}, rotor_pos, 0.02f, 0.02f, 8, - BLACK); - } - - // draws line with direction and magnitude of velocity / 10 - if (norm3(drone->vel) > 0.1f) { - DrawLine3D((Vector3){drone->pos.x, drone->pos.y, drone->pos.z}, - (Vector3){drone->pos.x + drone->vel.x * 0.1f, drone->pos.y + drone->vel.y * 0.1f, - drone->pos.z + drone->vel.z * 0.1f}, - MAGENTA); - } - - // Draw trailing path - for (int i = 1; i < client->trail_count; i++) { - int idx0 = (client->trail_index + i) % TRAIL_LENGTH; - int idx1 = (client->trail_index + i - 1) % TRAIL_LENGTH; - float alpha = (float)i / client->trail_count * 0.8f; // fade out - Color trail_color = ColorAlpha(YELLOW, alpha); - DrawLine3D((Vector3){client->trail[idx0].x, client->trail[idx0].y, client->trail[idx0].z}, - (Vector3){client->trail[idx1].x, client->trail[idx1].y, client->trail[idx1].z}, - trail_color); - } - - // draws current and previous ring - float ring_thickness = 0.2f; - DrawRing3D(env->ring_buffer[env->ring_idx], ring_thickness, GREEN, BLUE); - if (env->ring_idx > 0) { - DrawRing3D(env->ring_buffer[env->ring_idx - 1], ring_thickness, GREEN, BLUE); - } - - EndMode3D(); - - // Draw 2D stats - DrawText(TextFormat("Targets left: %d", env->max_rings - env->ring_idx), 10, 10, 20, WHITE); - DrawText(TextFormat("Moves left: %d", env->moves_left), 10, 40, 20, WHITE); - DrawText(TextFormat("Episode Return: %.2f", env->episodic_return), 10, 70, 20, WHITE); - - DrawText("Motor Thrusts:", 10, 110, 20, WHITE); - DrawText(TextFormat("Front: %.3f", T[0]), 10, 135, 18, ORANGE); - DrawText(TextFormat("Back: %.3f", T[1]), 10, 155, 18, PURPLE); - DrawText(TextFormat("Right: %.3f", T[2]), 10, 175, 18, LIME); - DrawText(TextFormat("Left: %.3f", T[3]), 10, 195, 18, SKYBLUE); - - DrawText(TextFormat("Pos: (%.1f, %.1f, %.1f)", drone->pos.x, drone->pos.y, drone->pos.z), 10, 225, 18, - WHITE); - DrawText(TextFormat("Vel: %.2f m/s", norm3(drone->vel)), 10, 245, 18, WHITE); - - DrawText("Left click + drag: Rotate camera", 10, 275, 16, LIGHTGRAY); - DrawText("Mouse wheel: Zoom in/out", 10, 295, 16, LIGHTGRAY); - - EndDrawing(); -} diff --git a/pufferlib/ocean/drone_race/drone_race.py b/pufferlib/ocean/drone_race/drone_race.py deleted file mode 100644 index 9b8c9af86..000000000 --- a/pufferlib/ocean/drone_race/drone_race.py +++ /dev/null @@ -1,95 +0,0 @@ -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.drone_race import binding - -class DroneRace(pufferlib.PufferEnv): - def __init__( - self, - num_envs=16, - render_mode=None, - report_interval=1, - buf=None, - seed=0, - max_rings=10, - max_moves=1000, - ): - self.single_observation_space = gymnasium.spaces.Box( - low=-1, - high=1, - shape=(25,), - dtype=np.float32, - ) - - self.single_action_space = gymnasium.spaces.Box( - low=-1, high=1, shape=(4,), dtype=np.float32 - ) - - self.num_agents = num_envs - self.render_mode = render_mode - self.report_interval = report_interval - self.tick = 0 - - super().__init__(buf) - self.actions = self.actions.astype(np.float32) - - c_envs = [] - for env_num in range(num_envs): - c_envs.append(binding.env_init( - self.observations[env_num:(env_num+1)], - self.actions[env_num:(env_num+1)], - self.rewards[env_num:(env_num+1)], - self.terminals[env_num:(env_num+1)], - self.truncations[env_num:(env_num+1)], - env_num, - report_interval=self.report_interval, - max_rings=max_rings, - max_moves=max_moves, - )) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=None): - self.tick = 0 - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.report_interval == 0: - log_data = binding.vec_log(self.c_envs) - if log_data: - info.append(log_data) - - return (self.observations, self.rewards, self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = DroneRace(num_envs=1000) - env.reset() - tick = 0 - - actions = [env.action_space.sample() for _ in range(atn_cache)] - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f"SPS: {env.num_agents * tick / (time.time() - start)}") - -if __name__ == "__main__": - test_performance() diff --git a/pufferlib/ocean/drone_race/dronelib.h b/pufferlib/ocean/drone_race/dronelib.h deleted file mode 100644 index 8445405fd..000000000 --- a/pufferlib/ocean/drone_race/dronelib.h +++ /dev/null @@ -1,374 +0,0 @@ -// Originally made by Sam Turner and Finlay Sanders, 2025. -// Included in pufferlib under the original project's MIT license. -// https://github.com/stmio/drone - -#include -#include -#include -#include -#include -#include -#include - -#include "raylib.h" - -// Visualisation properties -#define WIDTH 1080 -#define HEIGHT 720 -#define TRAIL_LENGTH 50 -#define HORIZON 1024 - -// Physical constants for the drone -#define BASE_MASS 1.0f // kg -#define BASE_IXX 0.01f // kgm^2 -#define BASE_IYY 0.01f // kgm^2 -#define BASE_IZZ 0.02f // kgm^2 -#define BASE_ARM_LEN 0.1f // m -#define BASE_K_THRUST 3e-5f // thrust coefficient -#define BASE_K_ANG_DAMP 0.2f // angular damping coefficient -#define BASE_K_DRAG 1e-6f // drag (torque) coefficient -#define BASE_B_DRAG 0.1f // linear drag coefficient -#define BASE_GRAVITY 9.81f // m/s^2 -#define BASE_MAX_RPM 750.0f // rad/s -#define BASE_MAX_VEL 50.0f // m/s -#define BASE_MAX_OMEGA 50.0f // rad/s - -// Simulation properties -#define GRID_SIZE 10.0f -#define MARGIN (GRID_SIZE - 1) -#define V_TARGET 0.05f -#define DT 0.05f -#define DT_RNG 0.1f - -// Corner to corner distance -#define MAX_DIST sqrtf(3*(2*GRID_SIZE)*(2*GRID_SIZE)) - -typedef struct Log Log; -struct Log { - float episode_return; - float episode_length; - float collision_rate; - float oob; - float score; - float perf; - float n; -}; - -typedef struct { - float w, x, y, z; -} Quat; - -typedef struct { - float x, y, z; -} Vec3; - -static inline float clampf(float v, float min, float max) { - if (v < min) - return min; - if (v > max) - return max; - return v; -} - -static inline float rndf(float a, float b) { - return a + ((float)rand() / (float)RAND_MAX) * (b - a); -} - -static inline Vec3 add3(Vec3 a, Vec3 b) { return (Vec3){a.x + b.x, a.y + b.y, a.z + b.z}; } - -static inline Vec3 sub3(Vec3 a, Vec3 b) { return (Vec3){a.x - b.x, a.y - b.y, a.z - b.z}; } - -static inline Vec3 scalmul3(Vec3 a, float b) { return (Vec3){a.x * b, a.y * b, a.z * b}; } - -static inline float dot3(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } - -static inline float norm3(Vec3 a) { return sqrtf(dot3(a, a)); } - -static inline void clamp3(Vec3 *vec, float min, float max) { - vec->x = clampf(vec->x, min, max); - vec->y = clampf(vec->y, min, max); - vec->z = clampf(vec->z, min, max); -} - -static inline void clamp4(float a[4], float min, float max) { - a[0] = clampf(a[0], min, max); - a[1] = clampf(a[1], min, max); - a[2] = clampf(a[2], min, max); - a[3] = clampf(a[3], min, max); -} - -static inline Quat quat_mul(Quat q1, Quat q2) { - Quat out; - out.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z; - out.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y; - out.y = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x; - out.z = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w; - return out; -} - -static inline void quat_normalize(Quat *q) { - float n = sqrtf(q->w * q->w + q->x * q->x + q->y * q->y + q->z * q->z); - if (n > 0.0f) { - q->w /= n; - q->x /= n; - q->y /= n; - q->z /= n; - } -} - -static inline Vec3 quat_rotate(Quat q, Vec3 v) { - Quat qv = {0.0f, v.x, v.y, v.z}; - Quat tmp = quat_mul(q, qv); - Quat q_conj = {q.w, -q.x, -q.y, -q.z}; - Quat res = quat_mul(tmp, q_conj); - return (Vec3){res.x, res.y, res.z}; -} - -static inline Quat quat_inverse(Quat q) { return (Quat){q.w, -q.x, -q.y, -q.z}; } - -Quat rndquat() { - float u1 = rndf(0.0f, 1.0f); - float u2 = rndf(0.0f, 1.0f); - float u3 = rndf(0.0f, 1.0f); - - float sqrt_1_minus_u1 = sqrtf(1.0f - u1); - float sqrt_u1 = sqrtf(u1); - - float pi_2_u2 = 2.0f * M_PI * u2; - float pi_2_u3 = 2.0f * M_PI * u3; - - Quat q; - q.w = sqrt_1_minus_u1 * sinf(pi_2_u2); - q.x = sqrt_1_minus_u1 * cosf(pi_2_u2); - q.y = sqrt_u1 * sinf(pi_2_u3); - q.z = sqrt_u1 * cosf(pi_2_u3); - - return q; -} - -typedef struct { - Vec3 pos; - Quat orientation; - Vec3 normal; - float radius; -} Ring; - -Ring rndring(float radius) { - Ring ring; - - ring.pos.x = rndf(-GRID_SIZE + 2*radius, GRID_SIZE - 2*radius); - ring.pos.y = rndf(-GRID_SIZE + 2*radius, GRID_SIZE - 2*radius); - ring.pos.z = rndf(-GRID_SIZE + 2*radius, GRID_SIZE - 2*radius); - - ring.orientation = rndquat(); - - Vec3 base_normal = {0.0f, 0.0f, 1.0f}; - ring.normal = quat_rotate(ring.orientation, base_normal); - - ring.radius = radius; - - return ring; -} - -typedef struct { - Vec3 pos[TRAIL_LENGTH]; - int index; - int count; -} Trail; - - -typedef struct { - Vec3 spawn_pos; - Vec3 pos; // global position (x, y, z) - Vec3 prev_pos; - Vec3 vel; // linear velocity (u, v, w) - Quat quat; // roll/pitch/yaw (phi/theta/psi) as a quaternion - Vec3 omega; // angular velocity (p, q, r) - - Vec3 target_pos; - Vec3 target_vel; - - float last_abs_reward; - float last_target_reward; - float last_collision_reward; - float episode_return; - float collisions; - int episode_length; - float score; - int ring_idx; - - // Physical properties. Modeled as part of the drone - // to make domain randomization easier. - float mass; // kg - float ixx; // kgm^2 - float iyy; // kgm^2 - float izz; // kgm^2 - float arm_len; // m - float k_thrust; // thrust coefficient - float k_ang_damp; // angular damping coefficient - float k_drag; // drag (torque) coefficient - float b_drag; // linear drag coefficient - float gravity; // m/s^2 - float max_rpm; // rad/s - float max_vel; // m/s - float max_omega; // rad/s -} Drone; - -// Physical constants for the drone -#define BASE_MASS 1.0f // kg -#define BASE_IXX 0.01f // kgm^2 -#define BASE_IYY 0.01f // kgm^2 -#define BASE_IZZ 0.02f // kgm^2 -#define BASE_ARM_LEN 0.1f // m -#define BASE_K_THRUST 3e-5f // thrust coefficient -#define BASE_K_ANG_DAMP 0.2f // angular damping coefficient -#define BASE_K_DRAG 1e-6f // drag (torque) coefficient -#define BASE_B_DRAG 0.1f // linear drag coefficient -#define BASE_GRAVITY 9.81f // m/s^2 -#define BASE_MAX_RPM 750.0f // rad/s -#define BASE_MAX_VEL 50.0f // m/s -#define BASE_MAX_OMEGA 50.0f // rad/s - - -void init_drone(Drone* drone, float size, float dr) { - - drone->arm_len = size / 2.0f; - - // m ~ x^3 - float mass_scale = powf(drone->arm_len, 3.0f) / powf(BASE_ARM_LEN, 3.0f); - drone->mass = BASE_MASS * mass_scale * rndf(1.0f - dr, 1.0f + dr); - - // I ~ mx^2 - float base_Iscale = BASE_MASS * BASE_ARM_LEN * BASE_ARM_LEN; - float I_scale = drone->mass * powf(drone->arm_len, 2.0f) / base_Iscale; - drone->ixx = BASE_IXX * I_scale * rndf(1.0f - dr, 1.0f + dr); - drone->iyy = BASE_IYY * I_scale * rndf(1.0f - dr, 1.0f + dr); - drone->izz = BASE_IZZ * I_scale * rndf(1.0f - dr, 1.0f + dr); - - // k_thrust ~ m/l - float k_thrust_scale = (drone->mass * drone->arm_len) / (BASE_MASS * BASE_ARM_LEN); - drone->k_thrust = BASE_K_THRUST * k_thrust_scale * rndf(1.0f - dr, 1.0f + dr); - - // k_ang_damp ~ I - float base_avg_inertia = (BASE_IXX + BASE_IYY + BASE_IZZ) / 3.0f; - float avg_inertia = (drone->ixx + drone->iyy + drone->izz) / 3.0f; - float avg_inertia_scale = avg_inertia / base_avg_inertia; - drone->k_ang_damp = BASE_K_ANG_DAMP * avg_inertia_scale * rndf(1.0f - dr, 1.0f + dr); - - // drag ~ x^2 - float drag_scale = powf(drone->arm_len, 2.0f) / powf(BASE_ARM_LEN, 2.0f); - drone->k_drag = BASE_K_DRAG * drag_scale * rndf(1.0f - dr, 1.0f + dr); - drone->b_drag = BASE_B_DRAG * drag_scale * rndf(1.0f - dr, 1.0f + dr); - - // Small gravity randomization - drone->gravity = BASE_GRAVITY * rndf(0.99f, 1.01f); - - // RPM ~ 1/x - float rpm_scale = (BASE_ARM_LEN) / (drone->arm_len); - drone->max_rpm = BASE_MAX_RPM * rpm_scale * rndf(1.0f - dr, 1.0f + dr); - - drone->max_vel = BASE_MAX_VEL; - drone->max_omega = BASE_MAX_OMEGA; -} - -void move_drone(Drone* drone, float* actions) { - clamp4(actions, -1.0f, 1.0f); - - // motor thrusts - float T[4]; - for (int i = 0; i < 4; i++) { - T[i] = drone->k_thrust*powf((actions[i] + 1.0f) * 0.5f * drone->max_rpm, 2.0f); - } - - - // body frame net force - Vec3 F_body = {0.0f, 0.0f, T[0] + T[1] + T[2] + T[3]}; - - // body frame torques - Vec3 M = {drone->arm_len*(T[1] - T[3]), drone->arm_len*(T[2] - T[0]), - drone->k_drag*(T[0] - T[1] + T[2] - T[3])}; - - // applies angular damping to torques - M.x -= drone->k_ang_damp * drone->omega.x; - M.y -= drone->k_ang_damp * drone->omega.y; - M.z -= drone->k_ang_damp * drone->omega.z; - - // body frame force -> world frame force - Vec3 F_world = quat_rotate(drone->quat, F_body); - - // world frame linear drag - F_world.x -= drone->b_drag * drone->vel.x; - F_world.y -= drone->b_drag * drone->vel.y; - F_world.z -= drone->b_drag * drone->vel.z; - - // world frame gravity - Vec3 accel = { - F_world.x / drone->mass, - F_world.y / drone->mass, - (F_world.z / drone->mass) - drone->gravity - }; - - // from the definition of q dot - Quat omega_q = {0.0f, drone->omega.x, drone->omega.y, drone->omega.z}; - Quat q_dot = quat_mul(drone->quat, omega_q); - - q_dot.w *= 0.5f; - q_dot.x *= 0.5f; - q_dot.y *= 0.5f; - q_dot.z *= 0.5f; - - // Domain randomized dt - float dt = DT * rndf(1.0f - DT_RNG, 1.0 + DT_RNG); - - // integrations - drone->pos.x += drone->vel.x * dt; - drone->pos.y += drone->vel.y * dt; - drone->pos.z += drone->vel.z * dt; - - drone->vel.x += accel.x * dt; - drone->vel.y += accel.y * dt; - drone->vel.z += accel.z * dt; - - drone->omega.x += (M.x / drone->ixx) * dt; - drone->omega.y += (M.y / drone->iyy) * dt; - drone->omega.z += (M.z / drone->izz) * dt; - - clamp3(&drone->vel, -drone->max_vel, drone->max_vel); - clamp3(&drone->omega, -drone->max_omega, drone->max_omega); - - drone->quat.w += q_dot.w * dt; - drone->quat.x += q_dot.x * dt; - drone->quat.y += q_dot.y * dt; - drone->quat.z += q_dot.z * dt; - - quat_normalize(&drone->quat); -} - -float check_ring(Drone* drone, Ring* ring) { - // previous dot product negative if on the 'entry' side of the ring's plane - float prev_dot = dot3(sub3(drone->prev_pos, ring->pos), ring->normal); - - // new dot product positive if on the 'exit' side of the ring's plane - float new_dot = dot3(sub3(drone->pos, ring->pos), ring->normal); - - bool valid_dir = (prev_dot < 0.0f && new_dot > 0.0f); - bool invalid_dir = (prev_dot > 0.0f && new_dot < 0.0f); - - // if we have crossed the plane of the ring - if (valid_dir || invalid_dir) { - // find intesection with ring's plane - Vec3 dir = sub3(drone->pos, drone->prev_pos); - float t = -prev_dot / dot3(ring->normal, dir); // possible nan - - Vec3 intersection = add3(drone->prev_pos, scalmul3(dir, t)); - float dist = norm3(sub3(intersection, ring->pos)); - - // reward or terminate based on distance to ring center - if (dist < (ring->radius - 0.5) && valid_dir) { - return 1.0f; - } else if (dist < ring->radius + 0.5) { - return -1.0f; - } - } - return 0.0f; -} diff --git a/pufferlib/ocean/drone_swarm/binding.c b/pufferlib/ocean/drone_swarm/binding.c deleted file mode 100644 index a7c930f62..000000000 --- a/pufferlib/ocean/drone_swarm/binding.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "drone_swarm.h" - -#define Env DroneSwarm -#include "../env_binding.h" - -static int my_init(Env *env, PyObject *args, PyObject *kwargs) { - env->num_agents = unpack(kwargs, "num_agents"); - env->max_rings = unpack(kwargs, "max_rings"); - init(env); - return 0; -} - -static int my_log(PyObject *dict, Log *log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "rings_passed", log->rings_passed); - assign_to_dict(dict, "collision_rate", log->collision_rate); - assign_to_dict(dict, "oob", log->oob); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/drone_swarm/drone_swarm.c b/pufferlib/ocean/drone_swarm/drone_swarm.c deleted file mode 100644 index 9a765209b..000000000 --- a/pufferlib/ocean/drone_swarm/drone_swarm.c +++ /dev/null @@ -1,180 +0,0 @@ -// Standalone C demo for DroneSwarm environment -// Compile using: ./scripts/build_ocean.sh drone [local|fast] -// Run with: ./drone - -#include "drone_swarm.h" -#include "puffernet.h" -#include - -#ifdef __EMSCRIPTEN__ -#include -#endif - -double randn(double mean, double std) { - static int has_spare = 0; - static double spare; - - if (has_spare) { - has_spare = 0; - return mean + std * spare; - } - - has_spare = 1; - double u, v, s; - do { - u = 2.0 * rand() / RAND_MAX - 1.0; - v = 2.0 * rand() / RAND_MAX - 1.0; - s = u * u + v * v; - } while (s >= 1.0 || s == 0.0); - - s = sqrt(-2.0 * log(s) / s); - spare = v * s; - return mean + std * (u * s); -} - -typedef struct LinearContLSTM LinearContLSTM; -struct LinearContLSTM { - int num_agents; - float *obs; - float *log_std; - Linear *encoder; - GELU *gelu1; - LSTM *lstm; - Linear *actor; - Linear *value_fn; - int num_actions; -}; - -LinearContLSTM *make_linearcontlstm(Weights *weights, int num_agents, int input_dim, - int logit_sizes[], int num_actions) { - LinearContLSTM *net = calloc(1, sizeof(LinearContLSTM)); - net->num_agents = num_agents; - net->obs = calloc(num_agents * input_dim, sizeof(float)); - net->num_actions = logit_sizes[0]; - net->log_std = weights->data; - weights->idx += net->num_actions; - net->encoder = make_linear(weights, num_agents, input_dim, 128); - net->gelu1 = make_gelu(num_agents, 128); - int atn_sum = 0; - for (int i = 0; i < num_actions; i++) { - atn_sum += logit_sizes[i]; - } - net->actor = make_linear(weights, num_agents, 128, atn_sum); - net->value_fn = make_linear(weights, num_agents, 128, 1); - net->lstm = make_lstm(weights, num_agents, 128, 128); - return net; -} - -void free_linearcontlstm(LinearContLSTM *net) { - free(net->obs); - free(net->encoder); - free(net->gelu1); - free(net->actor); - free(net->value_fn); - free(net->lstm); - free(net); -} - -void forward_linearcontlstm(LinearContLSTM *net, float *observations, float *actions) { - linear(net->encoder, observations); - gelu(net->gelu1, net->encoder->output); - lstm(net->lstm, net->gelu1->output); - linear(net->actor, net->lstm->state_h); - linear(net->value_fn, net->lstm->state_h); - for (int i = 0; i < net->num_actions; i++) { - float std = expf(net->log_std[i]); - float mean = net->actor->output[i]; - actions[i] = randn(mean, std); - } -} - -void generate_dummy_actions(DroneSwarm *env) { - // Generate random floats in [-1, 1] range - env->actions[0] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - env->actions[1] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - env->actions[2] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; - env->actions[3] = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; -} - -#ifdef __EMSCRIPTEN__ -typedef struct { - DroneSwarm *env; - LinearContLSTM *net; - Weights *weights; -} WebRenderArgs; - -void emscriptenStep(void *e) { - WebRenderArgs *args = (WebRenderArgs *)e; - DroneSwarm *env = args->env; - LinearContLSTM *net = args->net; - - forward_linearcontlstm(net, env->observations, env->actions); - c_step(env); - c_render(env); - return; -} - -WebRenderArgs *web_args = NULL; -#endif - -int main() { - srand(time(NULL)); // Seed random number generator - - DroneSwarm *env = calloc(1, sizeof(DroneSwarm)); - env->num_agents = 64; - env->max_rings = 10; - env->task = TASK_ORBIT; - init(env); - - size_t obs_size = 41; - size_t act_size = 4; - env->observations = (float *)calloc(env->num_agents * obs_size, sizeof(float)); - env->actions = (float *)calloc(env->num_agents * act_size, sizeof(float)); - env->rewards = (float *)calloc(env->num_agents, sizeof(float)); - env->terminals = (unsigned char *)calloc(env->num_agents, sizeof(float)); - - //Weights *weights = load_weights("resources/drone/drone_weights.bin", 136073); - //int logit_sizes[1] = {4}; - //LinearContLSTM *net = make_linearcontlstm(weights, env->num_agents, obs_size, logit_sizes, 1); - - if (!env->observations || !env->actions || !env->rewards) { - fprintf(stderr, "ERROR: Failed to allocate memory for demo buffers.\n"); - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - free(env); - return 0; - } - - init(env); - c_reset(env); - -#ifdef __EMSCRIPTEN__ - WebRenderArgs *args = calloc(1, sizeof(WebRenderArgs)); - args->env = env; - args->net = net; - args->weights = weights; - web_args = args; - - emscripten_set_main_loop_arg(emscriptenStep, args, 0, true); -#else - c_render(env); - - while (!WindowShouldClose()) { - //forward_linearcontlstm(net, env->observations, env->actions); - c_step(env); - c_render(env); - } - - c_close(env); - //free_linearcontlstm(net); - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - free(env); -#endif - - return 0; -} diff --git a/pufferlib/ocean/drone_swarm/drone_swarm.h b/pufferlib/ocean/drone_swarm/drone_swarm.h deleted file mode 100644 index 9e8bcf767..000000000 --- a/pufferlib/ocean/drone_swarm/drone_swarm.h +++ /dev/null @@ -1,778 +0,0 @@ -// Originally made by Sam Turner and Finlay Sanders, 2025. -// Included in pufferlib under the original project's MIT license. -// https://github.com/stmio/drone - -#include -#include -#include -#include -#include -#include -#include - -#include "raylib.h" -#include "dronelib.h" - -#define TASK_IDLE 0 -#define TASK_HOVER 1 -#define TASK_ORBIT 2 -#define TASK_FOLLOW 3 -#define TASK_CUBE 4 -#define TASK_CONGO 5 -#define TASK_FLAG 6 -#define TASK_RACE 7 -#define TASK_N 8 - -char* TASK_NAMES[TASK_N] = { - "Idle", "Hover", "Orbit", "Follow", - "Cube", "Congo", "FLAG", "Race" -}; - -#define R (Color){255, 0, 0, 255} -#define W (Color){255, 255, 255, 255} -#define B (Color){0, 0, 255, 255} -Color FLAG_COLORS[64] = { - B, B, B, B, R, R, R, R, - B, B, B, B, W, W, W, W, - B, B, B, B, R, R, R, R, - B, B, B, B, W, W, W, W, - R, R, R, R, R, R, R, R, - W, W, W, W, W, W, W, W, - R, R, R, R, R, R, R, R, - W, W, W, W, W, W, W, W -}; -#undef R -#undef W -#undef B - -typedef struct Client Client; -struct Client { - Camera3D camera; - float width; - float height; - - float camera_distance; - float camera_azimuth; - float camera_elevation; - bool is_dragging; - Vector2 last_mouse_pos; - - // Trailing path buffer (for rendering only) - Trail* trails; -}; - -typedef struct { - float *observations; - float *actions; - float *rewards; - unsigned char *terminals; - - Log log; - int tick; - int report_interval; - - int task; - int num_agents; - Drone* agents; - - int max_rings; - Ring* ring_buffer; - - Client *client; -} DroneSwarm; - -void init(DroneSwarm *env) { - env->agents = calloc(env->num_agents, sizeof(Drone)); - env->ring_buffer = calloc(env->max_rings, sizeof(Ring)); - env->log = (Log){0}; - env->tick = 0; -} - -void add_log(DroneSwarm *env, int idx, bool oob) { - Drone *agent = &env->agents[idx]; - env->log.score += agent->score; - env->log.episode_return += agent->episode_return; - env->log.episode_length += agent->episode_length; - env->log.collision_rate += agent->collisions / (float)agent->episode_length; - env->log.perf += agent->score / (float)agent->episode_length; - if (oob) { - env->log.oob += 1.0f; - } - env->log.n += 1.0f; - - agent->episode_length = 0; - agent->episode_return = 0.0f; -} - -Drone* nearest_drone(DroneSwarm* env, Drone *agent) { - float min_dist = 999999.0f; - Drone *nearest = NULL; - for (int i = 0; i < env->num_agents; i++) { - Drone *other = &env->agents[i]; - if (other == agent) { - continue; - } - float dx = agent->pos.x - other->pos.x; - float dy = agent->pos.y - other->pos.y; - float dz = agent->pos.z - other->pos.z; - float dist = sqrtf(dx*dx + dy*dy + dz*dz); - if (dist < min_dist) { - min_dist = dist; - nearest = other; - } - } - if (nearest == NULL) { - int x = 0; - - } - return nearest; -} - -void compute_observations(DroneSwarm *env) { - int idx = 0; - for (int i = 0; i < env->num_agents; i++) { - Drone *agent = &env->agents[i]; - - Quat q_inv = quat_inverse(agent->quat); - Vec3 linear_vel_body = quat_rotate(q_inv, agent->vel); - Vec3 drone_up_world = quat_rotate(agent->quat, (Vec3){0.0f, 0.0f, 1.0f}); - - // TODO: Need abs observations now right? - env->observations[idx++] = linear_vel_body.x / agent->max_vel; - env->observations[idx++] = linear_vel_body.y / agent->max_vel; - env->observations[idx++] = linear_vel_body.z / agent->max_vel; - - env->observations[idx++] = agent->omega.x / agent->max_omega; - env->observations[idx++] = agent->omega.y / agent->max_omega; - env->observations[idx++] = agent->omega.z / agent->max_omega; - - env->observations[idx++] = drone_up_world.x; - env->observations[idx++] = drone_up_world.y; - env->observations[idx++] = drone_up_world.z; - - env->observations[idx++] = agent->quat.w; - env->observations[idx++] = agent->quat.x; - env->observations[idx++] = agent->quat.y; - env->observations[idx++] = agent->quat.z; - - env->observations[idx++] = agent->rpms[0] / agent->max_rpm; - env->observations[idx++] = agent->rpms[1] / agent->max_rpm; - env->observations[idx++] = agent->rpms[2] / agent->max_rpm; - env->observations[idx++] = agent->rpms[3] / agent->max_rpm; - - env->observations[idx++] = agent->pos.x / GRID_X; - env->observations[idx++] = agent->pos.y / GRID_Y; - env->observations[idx++] = agent->pos.z / GRID_Z; - - env->observations[idx++] = agent->spawn_pos.x / GRID_X; - env->observations[idx++] = agent->spawn_pos.y / GRID_Y; - env->observations[idx++] = agent->spawn_pos.z / GRID_Z; - - float dx = agent->target_pos.x - agent->pos.x; - float dy = agent->target_pos.y - agent->pos.y; - float dz = agent->target_pos.z - agent->pos.z; - env->observations[idx++] = clampf(dx, -1.0f, 1.0f); - env->observations[idx++] = clampf(dy, -1.0f, 1.0f); - env->observations[idx++] = clampf(dz, -1.0f, 1.0f); - env->observations[idx++] = dx / GRID_X; - env->observations[idx++] = dy / GRID_Y; - env->observations[idx++] = dz / GRID_Z; - - env->observations[idx++] = agent->last_collision_reward; - env->observations[idx++] = agent->last_target_reward; - env->observations[idx++] = agent->last_abs_reward; - - // Multiagent obs - Drone* nearest = nearest_drone(env, agent); - if (env->num_agents > 1) { - env->observations[idx++] = clampf(nearest->pos.x - agent->pos.x, -1.0f, 1.0f); - env->observations[idx++] = clampf(nearest->pos.y - agent->pos.y, -1.0f, 1.0f); - env->observations[idx++] = clampf(nearest->pos.z - agent->pos.z, -1.0f, 1.0f); - } else { - env->observations[idx++] = 0.0f; - env->observations[idx++] = 0.0f; - env->observations[idx++] = 0.0f; - } - - // Ring obs - if (env->task == TASK_RACE) { - Ring ring = env->ring_buffer[agent->ring_idx]; - Vec3 to_ring = quat_rotate(q_inv, sub3(ring.pos, agent->pos)); - Vec3 ring_norm = quat_rotate(q_inv, ring.normal); - env->observations[idx++] = to_ring.x / GRID_X; - env->observations[idx++] = to_ring.y / GRID_Y; - env->observations[idx++] = to_ring.z / GRID_Z; - env->observations[idx++] = ring_norm.x; - env->observations[idx++] = ring_norm.y; - env->observations[idx++] = ring_norm.z; - } else { - env->observations[idx++] = 0.0f; - env->observations[idx++] = 0.0f; - env->observations[idx++] = 0.0f; - env->observations[idx++] = 0.0f; - env->observations[idx++] = 0.0f; - env->observations[idx++] = 0.0f; - } - } -} - -void move_target(DroneSwarm* env, Drone *agent) { - agent->target_pos.x += agent->target_vel.x; - agent->target_pos.y += agent->target_vel.y; - agent->target_pos.z += agent->target_vel.z; - if (agent->target_pos.x < -GRID_X || agent->target_pos.x > GRID_X) { - agent->target_vel.x = -agent->target_vel.x; - } - if (agent->target_pos.y < -GRID_Y || agent->target_pos.y > GRID_Y) { - agent->target_vel.y = -agent->target_vel.y; - } - if (agent->target_pos.z < -GRID_Z || agent->target_pos.z > GRID_Z) { - agent->target_vel.z = -agent->target_vel.z; - } -} - -void set_target_idle(DroneSwarm* env, int idx) { - Drone *agent = &env->agents[idx]; - agent->target_pos = (Vec3){rndf(-MARGIN_X, MARGIN_X), rndf(-MARGIN_Y, MARGIN_Y), rndf(-MARGIN_Z, MARGIN_Z)}; - agent->target_vel = (Vec3){rndf(-V_TARGET, V_TARGET), rndf(-V_TARGET, V_TARGET), rndf(-V_TARGET, V_TARGET)}; -} - -void set_target_hover(DroneSwarm* env, int idx) { - Drone *agent = &env->agents[idx]; - agent->target_pos = agent->pos; - agent->target_vel = (Vec3){0.0f, 0.0f, 0.0f}; -} - -void set_target_orbit(DroneSwarm* env, int idx) { - // Fibbonacci sphere algorithm - float R = 8.0f; - float phi = PI * (sqrt(5.0f) - 1.0f); - float y = 1.0f - 2*((float)idx / (float)env->num_agents); - float radius = sqrtf(1.0f - y*y); - - float theta = phi * idx; - - float x = cos(theta) * radius; - float z = sin(theta) * radius; - - Drone *agent = &env->agents[idx]; - agent->target_pos = (Vec3){R*x, R*z, R*y}; // convert to z up - agent->target_vel = (Vec3){0.0f, 0.0f, 0.0f}; -} - -void set_target_follow(DroneSwarm* env, int idx) { - Drone* agent = &env->agents[idx]; - if (idx == 0) { - set_target_idle(env, idx); - } else { - agent->target_pos = env->agents[0].target_pos; - agent->target_vel = env->agents[0].target_vel; - } -} - -void set_target_cube(DroneSwarm* env, int idx) { - Drone* agent = &env->agents[idx]; - float z = idx / 16; - idx = idx % 16; - float x = (float)(idx % 4); - float y = (float)(idx / 4); - agent->target_pos = (Vec3){4*x - 6, 4*y - 6, 4*z - 6}; - agent->target_vel = (Vec3){0.0f, 0.0f, 0.0f}; -} - -void set_target_congo(DroneSwarm* env, int idx) { - if (idx == 0) { - set_target_idle(env, idx); - return; - } - Drone* follow = &env->agents[idx - 1]; - Drone* lead = &env->agents[idx]; - lead->target_pos = follow->target_pos; - lead->target_vel = follow->target_vel; - - // TODO: Slow hack - for (int i = 0; i < 40; i++) { - move_target(env, lead); - } -} - -void set_target_flag(DroneSwarm* env, int idx) { - Drone* agent = &env->agents[idx]; - float x = (float)(idx % 8); - float y = (float)(idx / 8); - x = 2.0f*x - 7; - y = 5 - 1.5f*y; - agent->target_pos = (Vec3){0.0f, x, y}; - agent->target_vel = (Vec3){0.0f, 0.0f, 0.0f}; -} - -void set_target_race(DroneSwarm* env, int idx) { - Drone* agent = &env->agents[idx]; - agent->target_pos = env->ring_buffer[agent->ring_idx].pos; - agent->target_vel = (Vec3){0.0f, 0.0f, 0.0f}; -} - -void set_target(DroneSwarm* env, int idx) { - if (env->task == TASK_IDLE) { - set_target_idle(env, idx); - } else if (env->task == TASK_HOVER) { - set_target_hover(env, idx); - } else if (env->task == TASK_ORBIT) { - set_target_orbit(env, idx); - } else if (env->task == TASK_FOLLOW) { - set_target_follow(env, idx); - } else if (env->task == TASK_CUBE) { - set_target_cube(env, idx); - } else if (env->task == TASK_CONGO) { - set_target_congo(env, idx); - } else if (env->task == TASK_FLAG) { - set_target_flag(env, idx); - } else if (env->task == TASK_RACE) { - set_target_race(env, idx); - } -} - -float compute_reward(DroneSwarm* env, Drone *agent, bool collision) { - // Distance reward - float dx = (agent->pos.x - agent->target_pos.x); - float dy = (agent->pos.y - agent->target_pos.y); - float dz = (agent->pos.z - agent->target_pos.z); - float dist = sqrtf(dx*dx + dy*dy + dz*dz); - float dist_reward = 1.0 - dist/MAX_DIST; - //dist = clampf(dist, 0.0f, 1.0f); - //float dist_reward = 1.0f - dist; - - // Density penalty - float density_reward = 0.0f; - if (collision && env->num_agents > 1) { - Drone *nearest = nearest_drone(env, agent); - dx = agent->pos.x - nearest->pos.x; - dy = agent->pos.y - nearest->pos.y; - dz = agent->pos.z - nearest->pos.z; - float min_dist = sqrtf(dx*dx + dy*dy + dz*dz); - if (min_dist < 1.0f) { - density_reward = -1.0f; - agent->collisions += 1.0f; - } - } - - float abs_reward = dist_reward + density_reward; - - // Prevent negative dist and density from making a positive reward - if (dist_reward < 0.0f && density_reward < 0.0f) { - abs_reward *= -1.0f; - } - - float delta_reward = abs_reward - agent->last_abs_reward; - - agent->last_collision_reward = density_reward; - agent->last_target_reward = dist_reward; - agent->last_abs_reward = abs_reward; - - agent->episode_length++; - agent->score += abs_reward; - - return delta_reward; -} - -void reset_agent(DroneSwarm* env, Drone *agent, int idx) { - agent->episode_return = 0.0f; - agent->episode_length = 0; - agent->collisions = 0.0f; - agent->score = 0.0f; - agent->pos = (Vec3){rndf(-9, 9), rndf(-9, 9), rndf(-9, 9)}; - agent->spawn_pos = agent->pos; - agent->vel = (Vec3){0.0f, 0.0f, 0.0f}; - agent->omega = (Vec3){0.0f, 0.0f, 0.0f}; - agent->quat = (Quat){1.0f, 0.0f, 0.0f, 0.0f}; - agent->ring_idx = 0; - - //float size = 0.2f; - //init_drone(agent, size, 0.0f); - float size = rndf(0.1f, 0.4); - init_drone(agent, size, 0.1f); - compute_reward(env, agent, env->task != TASK_RACE); -} - -void c_reset(DroneSwarm *env) { - env->tick = 0; - //env->task = rand() % (TASK_N - 1); - //env->task = TASK_FLAG; - env->task = TASK_CONGO; - //env->task = rand() % (TASK_N - 1); - /* - if (rand() % 4) { - env->task = TASK_RACE; - } else { - env->task = rand() % (TASK_N - 1); - } - */ - //env->task = TASK_RACE; - //env->task = TASK_HOVER; - //env->task = TASK_FLAG; - - for (int i = 0; i < env->num_agents; i++) { - Drone *agent = &env->agents[i]; - reset_agent(env, agent, i); - set_target(env, i); - } - - for (int i = 0; i < env->max_rings; i++) { - Ring *ring = &env->ring_buffer[i]; - *ring = (Ring){0}; - } - if (env->task == TASK_RACE) { - float ring_radius = 2.0f; - if (env->max_rings + 1 > 0) { - env->ring_buffer[0] = rndring(ring_radius); - } - - for (int i = 1; i < env->max_rings; i++) { - do { - env->ring_buffer[i] = rndring(ring_radius); - } while (norm3(sub3(env->ring_buffer[i].pos, env->ring_buffer[i - 1].pos)) < 2.0f*ring_radius); - } - - // start drone at least MARGIN away from the first ring - for (int i = 0; i < env->num_agents; i++) { - Drone *drone = &env->agents[i]; - do { - drone->pos = (Vec3){rndf(-9, 9), rndf(-9, 9), rndf(-9, 9)}; - } while (norm3(sub3(drone->pos, env->ring_buffer[0].pos)) < 2.0f*ring_radius); - } - } - - compute_observations(env); -} - -void c_step(DroneSwarm *env) { - env->tick = (env->tick + 1) % HORIZON; - for (int i = 0; i < env->num_agents; i++) { - Drone *agent = &env->agents[i]; - env->rewards[i] = 0; - env->terminals[i] = 0; - - float* atn = &env->actions[4*i]; - move_drone(agent, atn); - - // check out of bounds - bool out_of_bounds = agent->pos.x < -GRID_X || agent->pos.x > GRID_X || - agent->pos.y < -GRID_Y || agent->pos.y > GRID_Y || - agent->pos.z < -GRID_Z || agent->pos.z > GRID_Z; - - move_target(env, agent); - - float reward = 0.0f; - if (env->task == TASK_RACE) { - Ring *ring = &env->ring_buffer[agent->ring_idx]; - reward = compute_reward(env, agent, true); - float passed_ring = check_ring(agent, ring); - if (passed_ring > 0) { - agent->ring_idx = (agent->ring_idx + 1) % env->max_rings; - env->log.rings_passed += 1.0f; - set_target(env, i); - compute_reward(env, agent, true); - } - reward += passed_ring; - } else { - // Delta reward - reward = compute_reward(env, agent, true); - } - - env->rewards[i] += reward; - agent->episode_return += reward; - - if (out_of_bounds) { - env->rewards[i] -= 1; - env->terminals[i] = 1; - add_log(env, i, true); - reset_agent(env, agent, i); - } else if (env->tick >= HORIZON - 1) { - env->terminals[i] = 1; - add_log(env, i, false); - } - } - if (env->tick >= HORIZON - 1) { - c_reset(env); - } - - compute_observations(env); -} - -void c_close_client(Client *client) { - CloseWindow(); - free(client); -} - -void c_close(DroneSwarm *env) { - if (env->client != NULL) { - c_close_client(env->client); - } -} - -static void update_camera_position(Client *c) { - float r = c->camera_distance; - float az = c->camera_azimuth; - float el = c->camera_elevation; - - float x = r * cosf(el) * cosf(az); - float y = r * cosf(el) * sinf(az); - float z = r * sinf(el); - - c->camera.position = (Vector3){x, y, z}; - c->camera.target = (Vector3){0, 0, 0}; -} - -void handle_camera_controls(Client *client) { - Vector2 mouse_pos = GetMousePosition(); - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - client->is_dragging = true; - client->last_mouse_pos = mouse_pos; - } - - if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - client->is_dragging = false; - } - - if (client->is_dragging && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - Vector2 mouse_delta = {mouse_pos.x - client->last_mouse_pos.x, - mouse_pos.y - client->last_mouse_pos.y}; - - float sensitivity = 0.005f; - - client->camera_azimuth -= mouse_delta.x * sensitivity; - - client->camera_elevation += mouse_delta.y * sensitivity; - client->camera_elevation = - clampf(client->camera_elevation, -PI / 2.0f + 0.1f, PI / 2.0f - 0.1f); - - client->last_mouse_pos = mouse_pos; - - update_camera_position(client); - } - - float wheel = GetMouseWheelMove(); - if (wheel != 0) { - client->camera_distance -= wheel * 2.0f; - client->camera_distance = clampf(client->camera_distance, 5.0f, 50.0f); - update_camera_position(client); - } -} - -Client *make_client(DroneSwarm *env) { - Client *client = (Client *)calloc(1, sizeof(Client)); - - client->width = WIDTH; - client->height = HEIGHT; - - SetConfigFlags(FLAG_MSAA_4X_HINT); // antialiasing - InitWindow(WIDTH, HEIGHT, "PufferLib DroneSwarm"); - -#ifndef __EMSCRIPTEN__ - SetTargetFPS(60); -#endif - - if (!IsWindowReady()) { - TraceLog(LOG_ERROR, "Window failed to initialize\n"); - free(client); - return NULL; - } - - client->camera_distance = 40.0f; - client->camera_azimuth = 0.0f; - client->camera_elevation = PI / 10.0f; - client->is_dragging = false; - client->last_mouse_pos = (Vector2){0.0f, 0.0f}; - - client->camera.up = (Vector3){0.0f, 0.0f, 1.0f}; - client->camera.fovy = 45.0f; - client->camera.projection = CAMERA_PERSPECTIVE; - - update_camera_position(client); - - // Initialize trail buffer - client->trails = (Trail*)calloc(env->num_agents, sizeof(Trail)); - for (int i = 0; i < env->num_agents; i++) { - Trail* trail = &client->trails[i]; - trail->index = 0; - trail->count = 0; - for (int j = 0; j < TRAIL_LENGTH; j++) { - trail->pos[j] = env->agents[i].pos; - } - } - - return client; -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -void DrawRing3D(Ring ring, float thickness, Color entryColor, Color exitColor) { - float half_thick = thickness / 2.0f; - - Vector3 center_pos = {ring.pos.x, ring.pos.y, ring.pos.z}; - - Vector3 entry_start_pos = {center_pos.x - half_thick * ring.normal.x, - center_pos.y - half_thick * ring.normal.y, - center_pos.z - half_thick * ring.normal.z}; - - DrawCylinderWiresEx(entry_start_pos, center_pos, ring.radius, ring.radius, 32, entryColor); - - Vector3 exit_end_pos = {center_pos.x + half_thick * ring.normal.x, - center_pos.y + half_thick * ring.normal.y, - center_pos.z + half_thick * ring.normal.z}; - - DrawCylinderWiresEx(center_pos, exit_end_pos, ring.radius, ring.radius, 32, exitColor); -} - - -void c_render(DroneSwarm *env) { - if (env->client == NULL) { - env->client = make_client(env); - if (env->client == NULL) { - TraceLog(LOG_ERROR, "Failed to initialize client for rendering\n"); - return; - } - } - - if (WindowShouldClose()) { - c_close(env); - exit(0); - } - - if (IsKeyDown(KEY_ESCAPE)) { - c_close(env); - exit(0); - } - - if (IsKeyPressed(KEY_SPACE)) { - env->task = (env->task + 1) % TASK_N; - for (int i = 0; i < env->num_agents; i++) { - set_target(env, i); - } - } - - handle_camera_controls(env->client); - - Client *client = env->client; - - for (int i = 0; i < env->num_agents; i++) { - Drone *agent = &env->agents[i]; - Trail *trail = &client->trails[i]; - trail->pos[trail->index] = agent->pos; - trail->index = (trail->index + 1) % TRAIL_LENGTH; - if (trail->count < TRAIL_LENGTH) { - trail->count++; - } - if (env->terminals[i]) { - trail->index = 0; - trail->count = 0; - } - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - BeginMode3D(client->camera); - - // draws bounding cube - DrawCubeWires((Vector3){0.0f, 0.0f, 0.0f}, GRID_X * 2.0f, - GRID_Y * 2.0f, GRID_Z * 2.0f, WHITE); - - for (int i = 0; i < env->num_agents; i++) { - Drone *agent = &env->agents[i]; - - // draws drone body - Color body_color = FLAG_COLORS[i]; - DrawSphere((Vector3){agent->pos.x, agent->pos.y, agent->pos.z}, 0.3f, body_color); - - // draws rotors according to thrust - float T[4]; - for (int j = 0; j < 4; j++) { - float rpm = (env->actions[4*i + j] + 1.0f) * 0.5f * agent->max_rpm; - T[j] = agent->k_thrust * rpm * rpm; - } - - const float rotor_radius = 0.15f; - const float visual_arm_len = agent->arm_len * 4.0f; - - Vec3 rotor_offsets_body[4] = {{+visual_arm_len, 0.0f, 0.0f}, - {-visual_arm_len, 0.0f, 0.0f}, - {0.0f, +visual_arm_len, 0.0f}, - {0.0f, -visual_arm_len, 0.0f}}; - - //Color base_colors[4] = {ORANGE, PURPLE, LIME, SKYBLUE}; - Color base_colors[4] = {body_color, body_color, body_color, body_color}; - - for (int j = 0; j < 4; j++) { - Vec3 world_off = quat_rotate(agent->quat, rotor_offsets_body[j]); - - Vector3 rotor_pos = {agent->pos.x + world_off.x, agent->pos.y + world_off.y, - agent->pos.z + world_off.z}; - - float rpm = (env->actions[4*i + j] + 1.0f) * 0.5f * agent->max_rpm; - float intensity = 0.75f + 0.25f * (rpm / agent->max_rpm); - - Color rotor_color = (Color){(unsigned char)(base_colors[j].r * intensity), - (unsigned char)(base_colors[j].g * intensity), - (unsigned char)(base_colors[j].b * intensity), 255}; - - DrawSphere(rotor_pos, rotor_radius, rotor_color); - - DrawCylinderEx((Vector3){agent->pos.x, agent->pos.y, agent->pos.z}, rotor_pos, 0.02f, 0.02f, 8, - BLACK); - } - - // draws line with direction and magnitude of velocity / 10 - if (norm3(agent->vel) > 0.1f) { - DrawLine3D((Vector3){agent->pos.x, agent->pos.y, agent->pos.z}, - (Vector3){agent->pos.x + agent->vel.x * 0.1f, agent->pos.y + agent->vel.y * 0.1f, - agent->pos.z + agent->vel.z * 0.1f}, - MAGENTA); - } - - // Draw trailing path - Trail *trail = &client->trails[i]; - if (trail->count <= 2) { - continue; - } - for (int j = 0; j < trail->count - 1; j++) { - int idx0 = (trail->index - j - 1 + TRAIL_LENGTH) % TRAIL_LENGTH; - int idx1 = (trail->index - j - 2 + TRAIL_LENGTH) % TRAIL_LENGTH; - float alpha = (float)(TRAIL_LENGTH - j) / (float)trail->count * 0.8f; // fade out - Color trail_color = ColorAlpha((Color){0, 187, 187, 255}, alpha); - DrawLine3D((Vector3){trail->pos[idx0].x, trail->pos[idx0].y, trail->pos[idx0].z}, - (Vector3){trail->pos[idx1].x, trail->pos[idx1].y, trail->pos[idx1].z}, - trail_color); - } - - } - - // Rings - if (env->task == TASK_RACE) { - float ring_thickness = 0.2f; - for (int i = 0; i < env->max_rings; i++) { - Ring ring = env->ring_buffer[i]; - DrawRing3D(ring, ring_thickness, GREEN, BLUE); - } - } - - if (IsKeyDown(KEY_TAB)) { - for (int i = 0; i < env->num_agents; i++) { - Drone *agent = &env->agents[i]; - Vec3 target_pos = agent->target_pos; - DrawSphere((Vector3){target_pos.x, target_pos.y, target_pos.z}, 0.45f, (Color){0, 255, 255, 100}); - } - } - - EndMode3D(); - - DrawText("Left click + drag: Rotate camera", 10, 10, 16, PUFF_WHITE); - DrawText("Mouse wheel: Zoom in/out", 10, 30, 16, PUFF_WHITE); - DrawText(TextFormat("Task: %s", TASK_NAMES[env->task]), 10, 50, 16, PUFF_WHITE); - - EndDrawing(); -} diff --git a/pufferlib/ocean/drone_swarm/drone_swarm.py b/pufferlib/ocean/drone_swarm/drone_swarm.py deleted file mode 100644 index 3503a7aa4..000000000 --- a/pufferlib/ocean/drone_swarm/drone_swarm.py +++ /dev/null @@ -1,94 +0,0 @@ -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.drone_swarm import binding - -class DroneSwarm(pufferlib.PufferEnv): - def __init__( - self, - num_envs=16, - num_drones=64, - max_rings=5, - render_mode=None, - report_interval=1024, - buf=None, - seed=0, - ): - self.single_observation_space = gymnasium.spaces.Box( - low=-1, - high=1, - shape=(41,), - dtype=np.float32, - ) - - self.single_action_space = gymnasium.spaces.Box( - low=-1, high=1, shape=(4,), dtype=np.float32 - ) - - self.num_agents = num_envs*num_drones - self.render_mode = render_mode - self.report_interval = report_interval - self.tick = 0 - - super().__init__(buf) - self.actions = self.actions.astype(np.float32) - - c_envs = [] - for i in range(num_envs): - c_envs.append(binding.env_init( - self.observations[i*num_drones:(i+1)*num_drones], - self.actions[i*num_drones:(i+1)*num_drones], - self.rewards[i*num_drones:(i+1)*num_drones], - self.terminals[i*num_drones:(i+1)*num_drones], - self.truncations[i*num_drones:(i+1)*num_drones], - i, - num_agents=num_drones, - max_rings=max_rings, - )) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=None): - self.tick = 0 - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.report_interval == 0: - log_data = binding.vec_log(self.c_envs) - if log_data: - info.append(log_data) - - return (self.observations, self.rewards, self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = DroneSwarm(num_envs=1000) - env.reset() - tick = 0 - - actions = [env.action_space.sample() for _ in range(atn_cache)] - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f"SPS: {env.num_agents * tick / (time.time() - start)}") - -if __name__ == "__main__": - test_performance() diff --git a/pufferlib/ocean/drone_swarm/dronelib.h b/pufferlib/ocean/drone_swarm/dronelib.h deleted file mode 100644 index 32435e81b..000000000 --- a/pufferlib/ocean/drone_swarm/dronelib.h +++ /dev/null @@ -1,414 +0,0 @@ -// Originally made by Sam Turner and Finlay Sanders, 2025. -// Included in pufferlib under the original project's MIT license. -// https://github.com/stmio/drone - -#include -#include -#include -#include -#include -#include -#include - -#include "raylib.h" - -// Visualisation properties -#define WIDTH 1080 -#define HEIGHT 720 -#define TRAIL_LENGTH 50 -#define HORIZON 1024 - -// Physical constants for the drone -#define BASE_MASS 1.0f // kg -#define BASE_IXX 0.01f // kgm^2 -#define BASE_IYY 0.01f // kgm^2 -#define BASE_IZZ 0.02f // kgm^2 -#define BASE_ARM_LEN 0.1f // m -#define BASE_K_THRUST 3e-5f // thrust coefficient -#define BASE_K_ANG_DAMP 0.2f // angular damping coefficient -#define BASE_K_DRAG 1e-6f // drag (torque) coefficient -#define BASE_B_DRAG 0.1f // linear drag coefficient -#define BASE_GRAVITY 9.81f // m/s^2 -#define BASE_MAX_RPM 750.0f // rad/s -#define BASE_MAX_VEL 50.0f // m/s -#define BASE_MAX_OMEGA 50.0f // rad/s -#define BASE_K_MOT 0.1f // s (Motor lag constant) -#define BASE_J_MOT 1e-5f // kgm^2 (Motor rotational inertia) - -// Simulation properties -#define GRID_X 30.0f -#define GRID_Y 30.0f -#define GRID_Z 10.0f -#define MARGIN_X (GRID_X - 1) -#define MARGIN_Y (GRID_Y - 1) -#define MARGIN_Z (GRID_Z - 1) -#define V_TARGET 0.05f -#define DT 0.05f -#define DT_RNG 0.0f - -// Corner to corner distance -#define MAX_DIST sqrtf((2*GRID_X)*(2*GRID_X) + (2*GRID_Y)*(2*GRID_Y) + (2*GRID_Z)*(2*GRID_Z)) - -typedef struct Log Log; -struct Log { - float episode_return; - float episode_length; - float rings_passed; - float collision_rate; - float oob; - float score; - float perf; - float n; -}; - -typedef struct { - float w, x, y, z; -} Quat; - -typedef struct { - float x, y, z; -} Vec3; - -static inline float clampf(float v, float min, float max) { - if (v < min) - return min; - if (v > max) - return max; - return v; -} - -static inline float rndf(float a, float b) { - return a + ((float)rand() / (float)RAND_MAX) * (b - a); -} - -static inline Vec3 add3(Vec3 a, Vec3 b) { return (Vec3){a.x + b.x, a.y + b.y, a.z + b.z}; } - -static inline Vec3 sub3(Vec3 a, Vec3 b) { return (Vec3){a.x - b.x, a.y - b.y, a.z - b.z}; } - -static inline Vec3 scalmul3(Vec3 a, float b) { return (Vec3){a.x * b, a.y * b, a.z * b}; } - -static inline float dot3(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } - -static inline float norm3(Vec3 a) { return sqrtf(dot3(a, a)); } - -static inline void clamp3(Vec3 *vec, float min, float max) { - vec->x = clampf(vec->x, min, max); - vec->y = clampf(vec->y, min, max); - vec->z = clampf(vec->z, min, max); -} - -static inline void clamp4(float a[4], float min, float max) { - a[0] = clampf(a[0], min, max); - a[1] = clampf(a[1], min, max); - a[2] = clampf(a[2], min, max); - a[3] = clampf(a[3], min, max); -} - -static inline Quat quat_mul(Quat q1, Quat q2) { - Quat out; - out.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z; - out.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y; - out.y = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x; - out.z = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w; - return out; -} - -static inline void quat_normalize(Quat *q) { - float n = sqrtf(q->w * q->w + q->x * q->x + q->y * q->y + q->z * q->z); - if (n > 0.0f) { - q->w /= n; - q->x /= n; - q->y /= n; - q->z /= n; - } -} - -static inline Vec3 quat_rotate(Quat q, Vec3 v) { - Quat qv = {0.0f, v.x, v.y, v.z}; - Quat tmp = quat_mul(q, qv); - Quat q_conj = {q.w, -q.x, -q.y, -q.z}; - Quat res = quat_mul(tmp, q_conj); - return (Vec3){res.x, res.y, res.z}; -} - -static inline Quat quat_inverse(Quat q) { return (Quat){q.w, -q.x, -q.y, -q.z}; } - -Quat rndquat() { - float u1 = rndf(0.0f, 1.0f); - float u2 = rndf(0.0f, 1.0f); - float u3 = rndf(0.0f, 1.0f); - - float sqrt_1_minus_u1 = sqrtf(1.0f - u1); - float sqrt_u1 = sqrtf(u1); - - float pi_2_u2 = 2.0f * M_PI * u2; - float pi_2_u3 = 2.0f * M_PI * u3; - - Quat q; - q.w = sqrt_1_minus_u1 * sinf(pi_2_u2); - q.x = sqrt_1_minus_u1 * cosf(pi_2_u2); - q.y = sqrt_u1 * sinf(pi_2_u3); - q.z = sqrt_u1 * cosf(pi_2_u3); - - return q; -} - -typedef struct { - Vec3 pos; - Quat orientation; - Vec3 normal; - float radius; -} Ring; - -Ring rndring(float radius) { - Ring ring; - - ring.pos.x = rndf(-GRID_X + 2*radius, GRID_X - 2*radius); - ring.pos.y = rndf(-GRID_Y + 2*radius, GRID_Y - 2*radius); - ring.pos.z = rndf(-GRID_Z + 2*radius, GRID_Z - 2*radius); - - ring.orientation = rndquat(); - - Vec3 base_normal = {0.0f, 0.0f, 1.0f}; - ring.normal = quat_rotate(ring.orientation, base_normal); - - ring.radius = radius; - - return ring; -} - -typedef struct { - Vec3 pos[TRAIL_LENGTH]; - int index; - int count; -} Trail; - - -typedef struct { - Vec3 spawn_pos; - Vec3 pos; // global position (x, y, z) - Vec3 prev_pos; - Vec3 vel; // linear velocity (u, v, w) - Quat quat; // roll/pitch/yaw (phi/theta/psi) as a quaternion - Vec3 omega; // angular velocity (p, q, r) - float rpms[4]; // motor RPMs - - Vec3 target_pos; - Vec3 target_vel; - - float last_abs_reward; - float last_target_reward; - float last_collision_reward; - float episode_return; - float collisions; - int episode_length; - float score; - int ring_idx; - - // Physical properties. Modeled as part of the drone - // to make domain randomization easier. - float mass; // kg - float ixx; // kgm^2 - float iyy; // kgm^2 - float izz; // kgm^2 - float arm_len; // m - float k_thrust; // thrust coefficient - float k_ang_damp; // angular damping coefficient - float k_drag; // drag (torque) coefficient - float b_drag; // linear drag coefficient - float gravity; // m/s^2 - float max_rpm; // rad/s - float max_vel; // m/s - float max_omega; // rad/s - float k_mot; // s - float j_mot; // kgm^2 -} Drone; - - -void init_drone(Drone* drone, float size, float dr) { - drone->arm_len = size / 2.0f; - - // m ~ x^3 - float mass_scale = powf(drone->arm_len, 3.0f) / powf(BASE_ARM_LEN, 3.0f); - drone->mass = BASE_MASS * mass_scale * rndf(1.0f - dr, 1.0f + dr); - - // I ~ mx^2 - float base_Iscale = BASE_MASS * BASE_ARM_LEN * BASE_ARM_LEN; - float I_scale = drone->mass * powf(drone->arm_len, 2.0f) / base_Iscale; - drone->ixx = BASE_IXX * I_scale * rndf(1.0f - dr, 1.0f + dr); - drone->iyy = BASE_IYY * I_scale * rndf(1.0f - dr, 1.0f + dr); - drone->izz = BASE_IZZ * I_scale * rndf(1.0f - dr, 1.0f + dr); - - // k_thrust ~ m/l - float k_thrust_scale = (drone->mass * drone->arm_len) / (BASE_MASS * BASE_ARM_LEN); - drone->k_thrust = BASE_K_THRUST * k_thrust_scale * rndf(1.0f - dr, 1.0f + dr); - - // k_ang_damp ~ I - float base_avg_inertia = (BASE_IXX + BASE_IYY + BASE_IZZ) / 3.0f; - float avg_inertia = (drone->ixx + drone->iyy + drone->izz) / 3.0f; - float avg_inertia_scale = avg_inertia / base_avg_inertia; - drone->k_ang_damp = BASE_K_ANG_DAMP * avg_inertia_scale * rndf(1.0f - dr, 1.0f + dr); - - // drag ~ x^2 - float drag_scale = powf(drone->arm_len, 2.0f) / powf(BASE_ARM_LEN, 2.0f); - drone->k_drag = BASE_K_DRAG * drag_scale * rndf(1.0f - dr, 1.0f + dr); - drone->b_drag = BASE_B_DRAG * drag_scale * rndf(1.0f - dr, 1.0f + dr); - - // Small gravity randomization - drone->gravity = BASE_GRAVITY * rndf(0.99f, 1.01f); - - // RPM ~ 1/x - float rpm_scale = (BASE_ARM_LEN) / (drone->arm_len); - drone->max_rpm = BASE_MAX_RPM * rpm_scale * rndf(1.0f - dr, 1.0f + dr); - - drone->max_vel = BASE_MAX_VEL; - drone->max_omega = BASE_MAX_OMEGA; - - for (int i = 0; i < 4; i++) { - drone->rpms[i] = 0.0f; - } - drone->k_mot = BASE_K_MOT * rndf(1.0f - dr, 1.0f + dr); - drone->j_mot = BASE_J_MOT * I_scale * rndf(1.0f - dr, 1.0f + dr); -} - -void explicit_euler(Drone* drone, Vec3 v_dot, Quat q_dot, Vec3 w_dot, float rpm_dot[4], float dt) { - drone->pos.x += drone->vel.x * dt; - drone->pos.y += drone->vel.y * dt; - drone->pos.z += drone->vel.z * dt; - - drone->vel.x += v_dot.x * dt; - drone->vel.y += v_dot.y * dt; - drone->vel.z += v_dot.z * dt; - - drone->omega.x += w_dot.x * dt; - drone->omega.y += w_dot.y * dt; - drone->omega.z += w_dot.z * dt; - - drone->quat.w += q_dot.w * dt; - drone->quat.x += q_dot.x * dt; - drone->quat.y += q_dot.y * dt; - drone->quat.z += q_dot.z * dt; - - drone->rpms[0] += rpm_dot[0] * dt; - drone->rpms[1] += rpm_dot[1] * dt; - drone->rpms[2] += rpm_dot[2] * dt; - drone->rpms[3] += rpm_dot[3] * dt; -} - -void move_drone(Drone* drone, float* actions) { - // Physics outlined in: - // https://pmc.ncbi.nlm.nih.gov/articles/PMC10468397/pdf/41586_2023_Article_6419.pdf - clamp4(actions, -1.0f, 1.0f); - - // first order rpm lag - float target_rpms[4]; - for (int i = 0; i < 4; i++) { - target_rpms[i] = (actions[i] + 1.0f) * 0.5f * drone->max_rpm; - } - - // rpm rates - float rpm_dot[4]; - for (int i = 0; i < 4; i++) { - rpm_dot[i] = (1.0f / drone->k_mot) * (target_rpms[i] - drone->rpms[i]); - } - - // motor thrusts - float T[4]; - for (int i = 0; i < 4; i++) { - T[i] = drone->k_thrust * powf(drone->rpms[i], 2.0f); - } - - // body frame net force - Vec3 F_prop_body = {0.0f, 0.0f, T[0] + T[1] + T[2] + T[3]}; - - // body frame force -> world frame force - Vec3 F_prop = quat_rotate(drone->quat, F_prop_body); - - // world frame linear drag - Vec3 F_aero; - F_aero.x = -drone->b_drag * drone->vel.x; - F_aero.y = -drone->b_drag * drone->vel.y; - F_aero.z = -drone->b_drag * drone->vel.z; - - // velocity rates, a = F/m - Vec3 v_dot; - v_dot.x = (F_prop.x + F_aero.x) / drone->mass; - v_dot.y = (F_prop.y + F_aero.y) / drone->mass; - v_dot.z = ((F_prop.z + F_aero.z) / drone->mass) - drone->gravity; - - // quaternion rates - Quat omega_q = {0.0f, drone->omega.x, drone->omega.y, drone->omega.z}; - Quat q_dot = quat_mul(drone->quat, omega_q); - q_dot.w *= 0.5f; - q_dot.x *= 0.5f; - q_dot.y *= 0.5f; - q_dot.z *= 0.5f; - - // body frame torques - Vec3 Tau_prop; - Tau_prop.x = drone->arm_len*(T[1] - T[3]); - Tau_prop.y = drone->arm_len*(T[2] - T[0]); - Tau_prop.z = drone->k_drag*(T[0] - T[1] + T[2] - T[3]); - - // torque from chaging motor speeds - float Tau_mot_z = drone->j_mot * (rpm_dot[0] - rpm_dot[1] + rpm_dot[2] - rpm_dot[3]); - - // torque from angular damping - Vec3 Tau_aero; - Tau_aero.x = -drone->k_ang_damp * drone->omega.x; - Tau_aero.y = -drone->k_ang_damp * drone->omega.y; - Tau_aero.z = -drone->k_ang_damp * drone->omega.z; - - // gyroscopic torque - Vec3 Tau_iner; - Tau_iner.x = (drone->iyy - drone->izz) * drone->omega.y * drone->omega.z; - Tau_iner.y = (drone->izz - drone->ixx) * drone->omega.z * drone->omega.x; - Tau_iner.z = (drone->ixx - drone->iyy) * drone->omega.x * drone->omega.y; - - // angular velocity rates - Vec3 w_dot; - w_dot.x = (Tau_prop.x + Tau_aero.x + Tau_iner.x) / drone->ixx; - w_dot.y = (Tau_prop.y + Tau_aero.y + Tau_iner.y) / drone->iyy; - w_dot.z = (Tau_prop.z + Tau_aero.z + Tau_iner.z + Tau_mot_z) / drone->izz; - - // Domain randomized dt - float dt = DT * rndf(1.0f - DT_RNG, 1.0 + DT_RNG); - - // update drone state - drone->prev_pos = drone->pos; - explicit_euler(drone, v_dot, q_dot, w_dot, rpm_dot, dt); - - // clamp and normalise for observations - clamp3(&drone->vel, -drone->max_vel, drone->max_vel); - clamp3(&drone->omega, -drone->max_omega, drone->max_omega); - quat_normalize(&drone->quat); -} - -float check_ring(Drone* drone, Ring* ring) { - // previous dot product negative if on the 'entry' side of the ring's plane - float prev_dot = dot3(sub3(drone->prev_pos, ring->pos), ring->normal); - - // new dot product positive if on the 'exit' side of the ring's plane - float new_dot = dot3(sub3(drone->pos, ring->pos), ring->normal); - - bool valid_dir = (prev_dot < 0.0f && new_dot > 0.0f); - bool invalid_dir = (prev_dot > 0.0f && new_dot < 0.0f); - - // if we have crossed the plane of the ring - if (valid_dir || invalid_dir) { - // find intesection with ring's plane - Vec3 dir = sub3(drone->pos, drone->prev_pos); - float t = -prev_dot / dot3(ring->normal, dir); // possible nan - - Vec3 intersection = add3(drone->prev_pos, scalmul3(dir, t)); - float dist = norm3(sub3(intersection, ring->pos)); - - // reward or terminate based on distance to ring center - if (dist < (ring->radius - 0.5) && valid_dir) { - return 1.0f; - } else if (dist < ring->radius + 0.5) { - return -0.0f; - } - } - return 0.0f; -} diff --git a/pufferlib/ocean/enduro/binding.c b/pufferlib/ocean/enduro/binding.c deleted file mode 100644 index 04919d12d..000000000 --- a/pufferlib/ocean/enduro/binding.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "enduro.h" -#include -#define Env Enduro -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->car_width = unpack(kwargs, "car_width"); - env->car_height = unpack(kwargs, "car_height"); - env->max_enemies = unpack(kwargs, "max_enemies"); - env->continuous = unpack(kwargs, "continuous"); - - PyObject* seed_val = PyDict_GetItemString(kwargs, "seed"); - if (seed_val) { - env->seed = unpack(kwargs, "seed"); - // Initialize the RNG state with the seed - env->rng_state = env->seed; - } - - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "reward", log->reward); - assign_to_dict(dict, "step_rew_car_passed_no_crash", log->step_rew_car_passed_no_crash); - assign_to_dict(dict, "crashed_penalty", log->crashed_penalty); - assign_to_dict(dict, "passed_cars", log->passed_cars); - assign_to_dict(dict, "passed_by_enemy", log->passed_by_enemy); - assign_to_dict(dict, "cars_to_pass", log->cars_to_pass); - assign_to_dict(dict, "days_completed", log->days_completed); - assign_to_dict(dict, "days_failed", log->days_failed); - assign_to_dict(dict, "collisions_player_vs_car", log->collisions_player_vs_car); - assign_to_dict(dict, "collisions_player_vs_road", log->collisions_player_vs_road); - return 0; -} diff --git a/pufferlib/ocean/enduro/enduro.c b/pufferlib/ocean/enduro/enduro.c deleted file mode 100644 index 01cbe15fa..000000000 --- a/pufferlib/ocean/enduro/enduro.c +++ /dev/null @@ -1,97 +0,0 @@ -// puffer_enduro.c - -#define MAX_ENEMIES 10 - -#include -#include -#include -#include -#include "enduro.h" -#include "raylib.h" -#include "puffernet.h" - -void get_input(Enduro* env) { - if ((IsKeyDown(KEY_DOWN) && IsKeyDown(KEY_RIGHT)) || (IsKeyDown(KEY_S) && IsKeyDown(KEY_D))) { - env->actions[0] = ACTION_DOWNRIGHT; // Decelerate and move right - } else if ((IsKeyDown(KEY_DOWN) && IsKeyDown(KEY_LEFT)) || (IsKeyDown(KEY_S) && IsKeyDown(KEY_A))) { - env->actions[0] = ACTION_DOWNLEFT; // Decelerate and move left - } else if (IsKeyDown(KEY_SPACE) && (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D))) { - env->actions[0] = ACTION_RIGHTFIRE; // Accelerate and move right - } else if (IsKeyDown(KEY_SPACE) && (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A))) { - env->actions[0] = ACTION_LEFTFIRE; // Accelerate and move left - } else if (IsKeyDown(KEY_SPACE)) { - env->actions[0] = ACTION_FIRE; // Accelerate - } else if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) { - env->actions[0] = ACTION_DOWN; // Decelerate - } else if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) { - env->actions[0] = ACTION_LEFT; // Move left - } else if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) { - env->actions[0] = ACTION_RIGHT; // Move right - } else { - env->actions[0] = ACTION_NOOP; // No action - } -} - -int demo() { - Weights* weights = load_weights("resources/enduro/enduro_weights.bin", 142218); - int logit_sizes[1] = {9}; - LinearLSTM* net = make_linearlstm(weights, 1, 68, logit_sizes, 1); - - Enduro env = { - .num_envs = 1, - .max_enemies = MAX_ENEMIES, - .obs_size = OBSERVATIONS_MAX_SIZE - }; - - allocate(&env); - - init(&env); - c_reset(&env); - c_render(&env); - - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - get_input(&env); - } else { - forward_linearlstm(net, env.observations, env.actions); - } - - c_step(&env); - c_render(&env); - } - - free_linearlstm(net); - free(weights); - free_allocated(&env); - return 0; -} - -void perftest(float test_time) { - Enduro env = { - .num_envs = 1, - .max_enemies = MAX_ENEMIES, - .obs_size = OBSERVATIONS_MAX_SIZE - }; - - allocate(&env); - init(&env); - c_reset(&env); - - int start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - env.actions[0] = rand()%9; - c_step(&env); - i++; - } - - int end = time(NULL); - printf("SPS: %f\n", i / (float)(end - start)); - free_allocated(&env); -} - -int main() { - demo(); - //perftest(10.0f); - return 0; -} diff --git a/pufferlib/ocean/enduro/enduro.h b/pufferlib/ocean/enduro/enduro.h deleted file mode 100644 index 0961caf3c..000000000 --- a/pufferlib/ocean/enduro/enduro.h +++ /dev/null @@ -1,2268 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "raylib.h" - -// Constant defs -#define MAX_ENEMIES 10 -#define OBSERVATIONS_MAX_SIZE (8 + (5 * MAX_ENEMIES) + 9 + 1) -#define TARGET_FPS 60 // Used to calculate wiggle spawn frequency -#define LOG_BUFFER_SIZE 4096 -#define SCREEN_WIDTH 152 -#define SCREEN_HEIGHT 210 -#define PLAYABLE_AREA_TOP 0 -#define PLAYABLE_AREA_BOTTOM 154 -#define PLAYABLE_AREA_LEFT 0 -#define PLAYABLE_AREA_RIGHT 152 -#define ACTION_HEIGHT (PLAYABLE_AREA_BOTTOM - PLAYABLE_AREA_TOP) -#define CAR_WIDTH 16 -#define CAR_HEIGHT 11 -#define CRASH_NOOP_DURATION_CAR_VS_CAR 90 // How long controls are disabled after car v car collision -#define CRASH_NOOP_DURATION_CAR_VS_ROAD 20 // How long controls are disabled after car v road edge collision -#define INITIAL_CARS_TO_PASS 200 -#define VANISHING_POINT_Y 52 -#define VANISHING_POINT_TRANSITION_SPEED 1.0f -#define CURVE_TRANSITION_SPEED 0.05f -#define LOGICAL_VANISHING_Y (VANISHING_POINT_Y + 12) // Separate logical vanishing point for cars disappearing -#define PLAYER_MAX_Y (ACTION_HEIGHT - CAR_HEIGHT) // Max y is car length from bottom -#define PLAYER_MIN_Y (ACTION_HEIGHT - CAR_HEIGHT - 9) // Min y is 2 car lengths from bottom -#define ACCELERATION_RATE 0.2f -#define DECELERATION_RATE 0.01f -#define MIN_SPEED -2.5f -#define MAX_SPEED 7.5f -#define ENEMY_CAR_SPEED 0.1f - -const float MAX_SPAWN_INTERVALS[] = {0.5f, 0.25f, 0.4f}; - -// Times of day logic -#define NUM_BACKGROUND_TRANSITIONS 16 -// Seconds spent in each time of day -static const float BACKGROUND_TRANSITION_TIMES[] = { - 20.0f, 40.0f, 60.0f, 100.0f, 108.0f, 114.0f, 116.0f, 120.0f, - 124.0f, 130.0f, 134.0f, 138.0f, 170.0f, 198.0f, 214.0f, 232.0f -}; - -// Curve constants -#define CURVE_STRAIGHT 0 -#define CURVE_LEFT -1 -#define CURVE_RIGHT 1 -#define NUM_LANES 3 -#define CURVE_VANISHING_POINT_SHIFT 55.0f -#define CURVE_PLAYER_SHIFT_FACTOR 0.025f // Moves player car towards outside edge of curves - -// Curve wiggle effect timing and amplitude -#define WIGGLE_AMPLITUDE 10.0f // Maximum 'bump-in' offset in pixels -#define WIGGLE_SPEED 10.1f // Speed at which the wiggle moves down the screen -#define WIGGLE_LENGTH 26.0f // Vertical length of the wiggle effect - - -// Rendering constants -#define SCORE_DIGITS 5 -#define CARS_DIGITS 4 -#define DIGIT_WIDTH 8 -#define DIGIT_HEIGHT 9 - -// Magic numbers - don't change -// The below block is specific to resolution 152x210px -#define INITIAL_PLAYER_X 69.0f // Adjusted from 77.0f -#define PLAYER_MIN_X 57.5f // Adjusted from 65.5f -#define PLAYER_MAX_X 83.5f // Adjusted from 91.5f -#define VANISHING_POINT_X 78.0f // Adjusted from 86 -#define VANISHING_POINT_X_LEFT 102.0f // Adjusted from 110.0f -#define VANISHING_POINT_X_RIGHT 54.0f // Adjusted from 62.0f -#define ROAD_LEFT_OFFSET 46.0f // Adjusted from 50.0f -#define ROAD_RIGHT_OFFSET 47.0f // Adjusted from 51.0f - -#define CONTINUOUS_SCALE (1) // Scale enemy cars continuously with y? - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float reward; - float step_rew_car_passed_no_crash; - float crashed_penalty; - float passed_cars; - float passed_by_enemy; - float cars_to_pass; - float days_completed; - float days_failed; - float collisions_player_vs_car; - float collisions_player_vs_road; - float n; -}; - -// Car struct for enemy cars -typedef struct Car { - int lane; // Lane index: 0=left lane, 1=mid, 2=right lane - float x; // Current x position - float y; // Current y position - float last_x; // x post last step - float last_y; // y post last step - int passed; // Flag to indicate if car has been passed by player - int colorIndex; // Car color idx (0-5) -} Car; - -// Rendering struct -typedef struct GameState { - Texture2D spritesheet; - RenderTexture2D renderTarget; // for scaling up render - // Indices into asset_map[] for various assets - int backgroundIndices[16]; - int mountainIndices[16]; - int digitIndices[11]; // 0-9 and "CAR" - int greenDigitIndices[10]; // Green digits 0-9 - int yellowDigitIndices[10]; // Yellow digits 0-9 - // Enemy car indices: [color][tread] - int enemyCarIndices[6][2]; - int enemyCarNightTailLightsIndex; - int enemyCarNightFogTailLightsIndex; - // Player car indices - int playerCarLeftTreadIndex; - int playerCarRightTreadIndex; - // Flag indices - int levelCompleteFlagLeftIndex; - int levelCompleteFlagRightIndex; - // For car animation - float carAnimationTimer; - float carAnimationInterval; - unsigned char showLeftTread; - float mountainPosition; // Position of the mountain texture - // Variables for alternating flags - unsigned char victoryAchieved; - int flagTimer; - unsigned char showLeftFlag; // true shows left flag, false shows right flag - int victoryDisplayTimer; // Timer for how long victory effects have been displayed - // Variables for scrolling yellow digits - float yellowDigitOffset; // Offset for scrolling effect - int yellowDigitCurrent; // Current yellow digit being displayed - int yellowDigitNext; // Next yellow digit to scroll in - // Variables for scrolling digits - float scoreDigitOffsets[SCORE_DIGITS]; // Offset for scrolling effect for each digit - int scoreDigitCurrents[SCORE_DIGITS]; // Current digit being displayed for each position - int scoreDigitNexts[SCORE_DIGITS]; // Next digit to scroll in for each position - unsigned char scoreDigitScrolling[SCORE_DIGITS]; // Scrolling state for each digit - int scoreTimer; // Timer to control score increment - int day; - int carsLeftGameState; - int score; // Score for scoreboard rendering - // Background state vars - int currentBackgroundIndex; - int previousBackgroundIndex; - float elapsedTime; - // Variable needed from Enduro to maintain separation - float speed; - float min_speed; - float max_speed; - int current_curve_direction; - float current_curve_factor; - float player_x; - float player_y; - float initial_player_x; - float vanishing_point_x; - float t_p; - unsigned char dayCompleted; -} GameState; - -typedef struct Client Client; -typedef struct Enduro Enduro; -// Game environment struct -typedef struct Enduro { - Client* client; - Log log; - float* observations; - int* actions; - float* rewards; - unsigned char* terminals; - size_t obs_size; - int num_envs; - float width; - float height; - float car_width; - float car_height; - int max_enemies; - float elapsedTimeEnv; - int initial_cars_to_pass; - float min_speed; - float max_speed; - float player_x; - float player_y; - float speed; - int score; - int day; - int lane; - int tick; - int numEnemies; - int carsToPass; - float collision_cooldown_car_vs_car; // Timer for car vs car collisions - float collision_cooldown_car_vs_road; // Timer for car vs road edge collisions - int drift_direction; // Which way player car drifts whilst noops after crash w/ other car - float action_height; - Car enemyCars[MAX_ENEMIES]; - float initial_player_x; - float road_scroll_offset; - // Road curve variables - int current_curve_stage; - int steps_in_current_stage; - int current_curve_direction; // 1: Right, -1: Left, 0: Straight - float current_curve_factor; - float target_curve_factor; - float current_step_threshold; - float target_vanishing_point_x; // Next curve direction vanishing point - float current_vanishing_point_x; // Current interpolated vanishing point - float base_target_vanishing_point_x; // Target for the base vanishing point - float vanishing_point_x; - float base_vanishing_point_x; - float t_p; - // Roadside wiggle effect - float wiggle_y; // Current y position of the wiggle - float wiggle_speed; // Speed at which the wiggle moves down the screen - float wiggle_length; // Vertical length of the wiggle effect - float wiggle_amplitude; // How far into road wiggle extends - unsigned char wiggle_active; // Whether the wiggle is active - // Player car acceleration - int currentGear; - float gearSpeedThresholds[4]; // Speeds at which gear changes occur - float gearAccelerationRates[4]; // Acceleration rates per gear - float gearTimings[4]; // Time durations per gear - float gearElapsedTime; // Time spent in current gear - // Enemy spawning - float enemySpawnTimer; - float enemySpawnInterval; // Spawn interval based on current stage - // Enemy movement speed - float enemySpeed; - // Day completed (victory) variables - unsigned char dayCompleted; - // Logging - float last_road_left; - float last_road_right; - int closest_edge_lane; - int last_spawned_lane; - // Vars that increment log struct - float tracking_episode_return; - float tracking_episode_length; - float tracking_score; - float tracking_reward; - float tracking_step_rew_car_passed_no_crash; - float tracking_crashed_penalty; - float tracking_passed_cars; - float tracking_passed_by_enemy; - float tracking_cars_to_pass; - float tracking_days_completed; - float tracking_days_failed; - float tracking_collisions_player_vs_car; - float tracking_collisions_player_vs_road; - - // Rendering - float parallaxFactor; - // Variables for time of day - float dayTransitionTimes[NUM_BACKGROUND_TRANSITIONS]; - int dayTimeIndex; - int currentDayTimeIndex; - int previousDayTimeIndex; - // RNG - unsigned int rng_state; - int reset_count; - // Rewards - // Reward flag for stepwise rewards if car passed && !crashed - // Effectively spreads out reward for passing cars - unsigned char car_passed_no_crash_active; - float step_rew_car_passed_no_crash; - float crashed_penalty; - int continuous; - - int seed; -} Enduro; - -// Action enumeration -typedef enum { - ACTION_NOOP = 0, - ACTION_FIRE = 1, - ACTION_RIGHT = 2, - ACTION_LEFT = 3, - ACTION_DOWN = 4, - ACTION_DOWNRIGHT = 5, - ACTION_DOWNLEFT = 6, - ACTION_RIGHTFIRE = 7, - ACTION_LEFTFIRE = 8, -} Action; - -Rectangle asset_map[] = { - (Rectangle){ 328, 15, 152, 210 }, // 0_bg - (Rectangle){ 480, 15, 152, 210 }, // 1_bg - (Rectangle){ 632, 15, 152, 210 }, // 2_bg - (Rectangle){ 784, 15, 152, 210 }, // 3_bg - (Rectangle){ 0, 225, 152, 210 }, // 4_bg - (Rectangle){ 152, 225, 152, 210 }, // 5_bg - (Rectangle){ 304, 225, 152, 210 }, // 6_bg - (Rectangle){ 456, 225, 152, 210 }, // 7_bg - (Rectangle){ 608, 225, 152, 210 }, // 8_bg - (Rectangle){ 760, 225, 152, 210 }, // 9_bg - (Rectangle){ 0, 435, 152, 210 }, // 10_bg - (Rectangle){ 152, 435, 152, 210 }, // 11_bg - (Rectangle){ 304, 435, 152, 210 }, // 12_bg - (Rectangle){ 456, 435, 152, 210 }, // 13_bg - (Rectangle){ 608, 435, 152, 210 }, // 14_bg - (Rectangle){ 760, 435, 152, 210 }, // 15_bg - (Rectangle){ 0, 0, 100, 6 }, // 0_mtns - (Rectangle){ 100, 0, 100, 6 }, // 1_mtns - (Rectangle){ 200, 0, 100, 6 }, // 2_mtns - (Rectangle){ 300, 0, 100, 6 }, // 3_mtns - (Rectangle){ 400, 0, 100, 6 }, // 4_mtns - (Rectangle){ 500, 0, 100, 6 }, // 5_mtns - (Rectangle){ 600, 0, 100, 6 }, // 6_mtns - (Rectangle){ 700, 0, 100, 6 }, // 7_mtns - (Rectangle){ 800, 0, 100, 6 }, // 8_mtns - (Rectangle){ 0, 6, 100, 6 }, // 9_mtns - (Rectangle){ 100, 6, 100, 6 }, // 10_mtns - (Rectangle){ 200, 6, 100, 6 }, // 11_mtns - (Rectangle){ 300, 6, 100, 6 }, // 12_mtns - (Rectangle){ 400, 6, 100, 6 }, // 13_mtns - (Rectangle){ 500, 6, 100, 6 }, // 14_mtns - (Rectangle){ 600, 6, 100, 6 }, // 15_mtns - (Rectangle){ 700, 6, 8, 9 }, // digits_0 - (Rectangle){ 708, 6, 8, 9 }, // digits_1 - (Rectangle){ 716, 6, 8, 9 }, // digits_2 - (Rectangle){ 724, 6, 8, 9 }, // digits_3 - (Rectangle){ 732, 6, 8, 9 }, // digits_4 - (Rectangle){ 740, 6, 8, 9 }, // digits_5 - (Rectangle){ 748, 6, 8, 9 }, // digits_6 - (Rectangle){ 756, 6, 8, 9 }, // digits_7 - (Rectangle){ 764, 6, 8, 9 }, // digits_8 - (Rectangle){ 772, 6, 8, 9 }, // digits_9 - (Rectangle){ 780, 6, 8, 9 }, // digits_car - (Rectangle){ 788, 6, 8, 9 }, // green_digits_0 - (Rectangle){ 796, 6, 8, 9 }, // green_digits_1 - (Rectangle){ 804, 6, 8, 9 }, // green_digits_2 - (Rectangle){ 812, 6, 8, 9 }, // green_digits_3 - (Rectangle){ 820, 6, 8, 9 }, // green_digits_4 - (Rectangle){ 828, 6, 8, 9 }, // green_digits_5 - (Rectangle){ 836, 6, 8, 9 }, // green_digits_6 - (Rectangle){ 844, 6, 8, 9 }, // green_digits_7 - (Rectangle){ 852, 6, 8, 9 }, // green_digits_8 - (Rectangle){ 860, 6, 8, 9 }, // green_digits_9 - (Rectangle){ 932, 6, 8, 9 }, // yellow_digits_0 - (Rectangle){ 0, 15, 8, 9 }, // yellow_digits_1 - (Rectangle){ 8, 15, 8, 9 }, // yellow_digits_2 - (Rectangle){ 16, 15, 8, 9 }, // yellow_digits_3 - (Rectangle){ 24, 15, 8, 9 }, // yellow_digits_4 - (Rectangle){ 32, 15, 8, 9 }, // yellow_digits_5 - (Rectangle){ 40, 15, 8, 9 }, // yellow_digits_6 - (Rectangle){ 48, 15, 8, 9 }, // yellow_digits_7 - (Rectangle){ 56, 15, 8, 9 }, // yellow_digits_8 - (Rectangle){ 64, 15, 8, 9 }, // yellow_digits_9 - (Rectangle){ 72, 15, 16, 11 }, // enemy_car_blue_left_tread - (Rectangle){ 88, 15, 16, 11 }, // enemy_car_blue_right_tread - (Rectangle){ 104, 15, 16, 11 }, // enemy_car_gold_left_tread - (Rectangle){ 120, 15, 16, 11 }, // enemy_car_gold_right_tread - (Rectangle){ 168, 15, 16, 11 }, // enemy_car_pink_left_tread - (Rectangle){ 184, 15, 16, 11 }, // enemy_car_pink_right_tread - (Rectangle){ 200, 15, 16, 11 }, // enemy_car_salmon_left_tread - (Rectangle){ 216, 15, 16, 11 }, // enemy_car_salmon_right_tread - (Rectangle){ 232, 15, 16, 11 }, // enemy_car_teal_left_tread - (Rectangle){ 248, 15, 16, 11 }, // enemy_car_teal_right_tread - (Rectangle){ 264, 15, 16, 11 }, // enemy_car_yellow_left_tread - (Rectangle){ 280, 15, 16, 11 }, // enemy_car_yellow_right_tread - (Rectangle){ 136, 15, 16, 11 }, // enemy_car_night_fog_tail_lights - (Rectangle){ 152, 15, 16, 11 }, // enemy_car_night_tail_lights - (Rectangle){ 296, 15, 16, 11 }, // player_car_left_tread - (Rectangle){ 312, 15, 16, 11 }, // player_car_right_tread - (Rectangle){ 900, 6, 32, 9 }, // level_complete_flag_right - (Rectangle){ 868, 6, 32, 9 }, // level_complete_flag_left -}; - -enum AssetIndices { - ASSET_BG_0 = 0, - ASSET_BG_1 = 1, - ASSET_BG_2 = 2, - ASSET_BG_3 = 3, - ASSET_BG_4 = 4, - ASSET_BG_5 = 5, - ASSET_BG_6 = 6, - ASSET_BG_7 = 7, - ASSET_BG_8 = 8, - ASSET_BG_9 = 9, - ASSET_BG_10 = 10, - ASSET_BG_11 = 11, - ASSET_BG_12 = 12, - ASSET_BG_13 = 13, - ASSET_BG_14 = 14, - ASSET_BG_15 = 15, - - ASSET_MOUNTAIN_0 = 16, - ASSET_MOUNTAIN_1, - ASSET_MOUNTAIN_2, - ASSET_MOUNTAIN_3, - ASSET_MOUNTAIN_4, - ASSET_MOUNTAIN_5, - ASSET_MOUNTAIN_6, - ASSET_MOUNTAIN_7, - ASSET_MOUNTAIN_8, - ASSET_MOUNTAIN_9, - ASSET_MOUNTAIN_10, - ASSET_MOUNTAIN_11, - ASSET_MOUNTAIN_12, - ASSET_MOUNTAIN_13, - ASSET_MOUNTAIN_14, - ASSET_MOUNTAIN_15, - ASSET_DIGITS_0 = 32, - ASSET_DIGITS_1 = 33, - ASSET_DIGITS_2 = 34, - ASSET_DIGITS_3 = 35, - ASSET_DIGITS_4 = 36, - ASSET_DIGITS_5 = 37, - ASSET_DIGITS_6 = 38, - ASSET_DIGITS_7 = 39, - ASSET_DIGITS_8 = 40, - ASSET_DIGITS_9 = 41, - ASSET_DIGITS_CAR = 42, - ASSET_GREEN_DIGITS_0 = 43, - ASSET_GREEN_DIGITS_1 = 44, - ASSET_GREEN_DIGITS_2 = 45, - ASSET_GREEN_DIGITS_3 = 46, - ASSET_GREEN_DIGITS_4 = 47, - ASSET_GREEN_DIGITS_5 = 48, - ASSET_GREEN_DIGITS_6 = 49, - ASSET_GREEN_DIGITS_7 = 50, - ASSET_GREEN_DIGITS_8 = 51, - ASSET_GREEN_DIGITS_9 = 52, - ASSET_YELLOW_DIGITS_0 = 53, - ASSET_YELLOW_DIGITS_1 = 54, - ASSET_YELLOW_DIGITS_2 = 55, - ASSET_YELLOW_DIGITS_3 = 56, - ASSET_YELLOW_DIGITS_4 = 57, - ASSET_YELLOW_DIGITS_5 = 58, - ASSET_YELLOW_DIGITS_6 = 59, - ASSET_YELLOW_DIGITS_7 = 60, - ASSET_YELLOW_DIGITS_8 = 61, - ASSET_YELLOW_DIGITS_9 = 62, - ASSET_ENEMY_CAR_BLUE_LEFT_TREAD = 63, - ASSET_ENEMY_CAR_BLUE_RIGHT_TREAD = 64, - ASSET_ENEMY_CAR_GOLD_LEFT_TREAD = 65, - ASSET_ENEMY_CAR_GOLD_RIGHT_TREAD = 66, - ASSET_ENEMY_CAR_PINK_LEFT_TREAD = 67, - ASSET_ENEMY_CAR_PINK_RIGHT_TREAD = 68, - ASSET_ENEMY_CAR_SALMON_LEFT_TREAD = 69, - ASSET_ENEMY_CAR_SALMON_RIGHT_TREAD = 70, - ASSET_ENEMY_CAR_TEAL_LEFT_TREAD = 71, - ASSET_ENEMY_CAR_TEAL_RIGHT_TREAD = 72, - ASSET_ENEMY_CAR_YELLOW_LEFT_TREAD = 73, - ASSET_ENEMY_CAR_YELLOW_RIGHT_TREAD = 74, - ASSET_ENEMY_CAR_NIGHT_FOG_TAIL_LIGHTS = 75, - ASSET_ENEMY_CAR_NIGHT_TAIL_LIGHTS = 76, - ASSET_PLAYER_CAR_LEFT_TREAD = 77, - ASSET_PLAYER_CAR_RIGHT_TREAD = 78, - ASSET_LEVEL_COMPLETE_FLAG_RIGHT = 79, - ASSET_LEVEL_COMPLETE_FLAG_LEFT = 80, -}; - -// Client struct -struct Client { - float width; - float height; - GameState gameState; -}; - -// Prototypes // -// RNG -unsigned int xorshift32(unsigned int *state); - -// Logging -void add_log(Enduro* env); - -// Environment functions -void allocate(Enduro* env); -void init(Enduro* env); -void free_allocated(Enduro* env); -void reset_round(Enduro* env); -void c_reset(Enduro* env); -unsigned char check_collision(Enduro* env, Car* car); -int get_player_lane(Enduro* env); -float get_car_scale(float y); -void add_enemy_car(Enduro* env); -void update_time_of_day(Enduro* env); -void accelerate(Enduro* env); -void c_step(Enduro* env); -void update_road_curve(Enduro* env); -float quadratic_bezier(float bottom_x, float control_x, float top_x, float t); -float road_edge_x(Enduro* env, float y, float offset, unsigned char left); -float car_x_in_lane(Enduro* env, int lane, float y); -void compute_observations(Enduro* env); - -// Client functions -Client* make_client(Enduro* env); -void close_client(Client* client); -void render_car(GameState* gameState); - -// GameState rendering functions -void initRaylib(GameState* gameState); -void loadTextures(GameState* gameState); -void updateCarAnimation(GameState* gameState); -void updateScoreboard(GameState* gameState); -void renderBackground(GameState* gameState); -void renderScoreboard(GameState* gameState); -void updateMountains(GameState* gameState); -void renderMountains(GameState* gameState); -void updateVictoryEffects(GameState* gameState); -void c_render(Enduro* env); -void cleanup(GameState* gameState); - -// Function definitions // -// RNG -unsigned int xorshift32(unsigned int *state) { - unsigned int x = *state; - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - *state = x; - return x; -} - -void add_log(Enduro* env) { - env->log.episode_return += env->tracking_episode_return; - env->log.episode_length += env->tracking_episode_length; - env->log.score += env->tracking_score; - env->log.perf += fminf(env->tracking_days_completed/10.0f, 1.0f); - env->log.reward += env->tracking_reward; - env->log.step_rew_car_passed_no_crash += env->tracking_step_rew_car_passed_no_crash; - env->log.crashed_penalty += env->tracking_crashed_penalty; - env->log.passed_cars += env->tracking_passed_cars; - env->log.passed_by_enemy += env->tracking_passed_by_enemy; - env->log.cars_to_pass += env->tracking_cars_to_pass; - env->log.days_completed += env->tracking_days_completed; - env->log.days_failed += env->tracking_days_failed; - env->log.collisions_player_vs_car += env->tracking_collisions_player_vs_car; - env->log.collisions_player_vs_road += env->tracking_collisions_player_vs_road; - - // Episode counter - env->log.n += 1.0f; -} - -void init(Enduro* env) { - env->rng_state = env->seed; - env->reset_count = 0; - env->last_road_left = -1.0f; - env->last_road_right = -1.0f; - - if (env->rng_state == 0) { // Activate with seed==0 - // Start the environment at the beginning of the day - env->rng_state = 0; - env->elapsedTimeEnv = 0.0f; - env->currentDayTimeIndex = 0; - env->previousDayTimeIndex = NUM_BACKGROUND_TRANSITIONS; - } else { - // Randomize elapsed time within the day's total duration - float total_day_duration = BACKGROUND_TRANSITION_TIMES[NUM_BACKGROUND_TRANSITIONS - 1]; - env->elapsedTimeEnv = ((float)xorshift32(&env->rng_state) / (float)UINT32_MAX) * total_day_duration; - - // Determine the current time index - env->currentDayTimeIndex = 0; - for (int i = 0; i < NUM_BACKGROUND_TRANSITIONS - 1; i++) { - if (env->elapsedTimeEnv >= env->dayTransitionTimes[i] && - env->elapsedTimeEnv < env->dayTransitionTimes[i + 1]) { - env->currentDayTimeIndex = i; - break; - } - } - - // Handle the last interval - if (env->elapsedTimeEnv >= BACKGROUND_TRANSITION_TIMES[NUM_BACKGROUND_TRANSITIONS - 1]) { - env->currentDayTimeIndex = NUM_BACKGROUND_TRANSITIONS - 1; - } - } - - env->numEnemies = 0; - for (int i = 0; i < MAX_ENEMIES; i++) { - env->enemyCars[i].lane = -1; // Default invalid lane - env->enemyCars[i].y = 0.0f; - env->enemyCars[i].passed = 0; - } - - env->obs_size = OBSERVATIONS_MAX_SIZE; - env->max_enemies = MAX_ENEMIES; - env->score = 0; - env->numEnemies = 0; - env->player_x = INITIAL_PLAYER_X; - env->player_y = PLAYER_MAX_Y; - env->speed = MIN_SPEED; - env->carsToPass = INITIAL_CARS_TO_PASS; - env->width = SCREEN_WIDTH; - env->height = SCREEN_HEIGHT; - env->car_width = CAR_WIDTH; - env->car_height = CAR_HEIGHT; - - memcpy(env->dayTransitionTimes, BACKGROUND_TRANSITION_TIMES, sizeof(BACKGROUND_TRANSITION_TIMES)); - - env->tick = 0; - env->collision_cooldown_car_vs_car = 0.0f; - env->collision_cooldown_car_vs_road = 0.0f; - env->action_height = ACTION_HEIGHT; - env->elapsedTimeEnv = 0.0f; - env->enemySpawnTimer = 0.0f; - env->enemySpawnInterval = 0.8777f; - env->last_spawned_lane = -1; - env->closest_edge_lane = -1; - env->base_vanishing_point_x = 86.0f; - env->current_vanishing_point_x = 86.0f; - env->target_vanishing_point_x = 86.0f; - env->vanishing_point_x = 86.0f; - env->initial_player_x = INITIAL_PLAYER_X; - env->player_y = PLAYER_MAX_Y; - env->min_speed = MIN_SPEED; - env->enemySpeed = ENEMY_CAR_SPEED; - env->max_speed = MAX_SPEED; - env->initial_cars_to_pass = INITIAL_CARS_TO_PASS; - env->day = 1; - env->drift_direction = 0; // Means in noop, but only if crashed state - env->crashed_penalty = 0.0f; - env->car_passed_no_crash_active = 1; - env->step_rew_car_passed_no_crash = 0.0f; - env->current_curve_stage = 0; // 0: straight - env->steps_in_current_stage = 0; - env->current_curve_direction = CURVE_STRAIGHT; - env->current_curve_factor = 0.0f; - env->target_curve_factor = 0.0f; - env->current_step_threshold = 0.0f; - env->wiggle_y = VANISHING_POINT_Y; - env->wiggle_speed = WIGGLE_SPEED; - env->wiggle_length = WIGGLE_LENGTH; - env->wiggle_amplitude = WIGGLE_AMPLITUDE; - env->wiggle_active = true; - env->currentGear = 0; - env->gearElapsedTime = 0.0f; - env->gearTimings[0] = 4.0f; - env->gearTimings[1] = 2.5f; - env->gearTimings[2] = 3.25f; - env->gearTimings[3] = 1.5f; - float totalSpeedRange = env->max_speed - env->min_speed; - float totalTime = 0.0f; - for (int i = 0; i < 4; i++) { - totalTime += env->gearTimings[i]; - } - float cumulativeSpeed = env->min_speed; - for (int i = 0; i < 4; i++) { - float gearTime = env->gearTimings[i]; - float gearSpeedIncrement = totalSpeedRange * (gearTime / totalTime); - env->gearSpeedThresholds[i] = cumulativeSpeed + gearSpeedIncrement; - env->gearAccelerationRates[i] = gearSpeedIncrement / (gearTime * TARGET_FPS); - cumulativeSpeed = env->gearSpeedThresholds[i]; - } - - // Randomize the initial time of day for each environment - if (env->rng_state == 0) { - env->elapsedTimeEnv = 0; - env->currentDayTimeIndex = 0; - env->dayTimeIndex = 0; - env->previousDayTimeIndex = 0; - } else { - float total_day_duration = BACKGROUND_TRANSITION_TIMES[15]; - env->elapsedTimeEnv = ((float)rand_r(&env->rng_state) / (float)RAND_MAX) * total_day_duration; - env->currentDayTimeIndex = 0; - env->dayTimeIndex = 0; - env->previousDayTimeIndex = 0; - - // Advance currentDayTimeIndex to match randomized elapsedTimeEnv - for (int i = 0; i < NUM_BACKGROUND_TRANSITIONS; i++) { - if (env->elapsedTimeEnv >= env->dayTransitionTimes[i]) { - env->currentDayTimeIndex = i; - } else { - break; - } - } - - env->previousDayTimeIndex = (env->currentDayTimeIndex > 0) ? env->currentDayTimeIndex - 1 : NUM_BACKGROUND_TRANSITIONS - 1; - } - - env->road_scroll_offset = 1.0f; - env->base_target_vanishing_point_x = 86.0f; - env->t_p = 86.0f; - env->parallaxFactor = 1.0f; - env->dayCompleted = 0; - env->lane = 1; - env->terminals[0] = 0; - - // Reset rewards and logs - env->rewards[0] = 0.0f; - // Initialize tracking variables - env->tracking_episode_return = 0.0f; - env->tracking_episode_length = 0.0f; - env->tracking_score = 0.0f; - env->tracking_reward = 0.0f; - env->tracking_step_rew_car_passed_no_crash = 0.0f; - env->tracking_crashed_penalty = 0.0f; - env->tracking_passed_cars = 0.0f; - env->tracking_passed_by_enemy = 0.0f; - env->tracking_cars_to_pass = INITIAL_CARS_TO_PASS; - env->tracking_days_completed = 0.0f; - env->tracking_days_failed = 0.0f; - env->tracking_collisions_player_vs_car = 0.0f; - env->tracking_collisions_player_vs_road = 0.0f; - - env->log.episode_return = 0.0f; - env->log.episode_length = 0.0f; - env->log.score = 0.0f; - env->log.reward = 0.0f; - env->log.step_rew_car_passed_no_crash = 0.0f; - env->log.crashed_penalty = 0.0f; - env->log.passed_cars = 0.0f; - env->log.passed_by_enemy = 0.0f; - env->log.cars_to_pass = INITIAL_CARS_TO_PASS; - env->log.days_completed = 0; - env->log.days_failed = 0; - env->log.collisions_player_vs_car = 0.0f; - env->log.collisions_player_vs_road = 0.0f; - env->log.n = 0.0f; -} - -void allocate(Enduro* env) { - env->observations = (float*)calloc(env->obs_size, sizeof(float)); - env->actions = (int*)calloc(1, sizeof(int)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void free_allocated(Enduro* env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); -} - -void c_close(Enduro* env) { -} - -// Called when a day is failed by player -// Restarts the game at Day 1 -void reset_round(Enduro* env) { - // Preserve RNG state - unsigned int preserved_rng_state = env->rng_state; - - // Reset most environment variables - env->score = 0; - env->carsToPass = INITIAL_CARS_TO_PASS; - env->day = 1; - env->tick = 0; - env->numEnemies = 0; - env->speed = env->min_speed; - env->player_x = env->initial_player_x; - env->player_y = PLAYER_MAX_Y; - env->car_passed_no_crash_active = 0; - env->step_rew_car_passed_no_crash = 0.0f; - env->crashed_penalty = 0.0f; - env->collision_cooldown_car_vs_car = 0.0f; - env->collision_cooldown_car_vs_road = 0.0f; - env->enemySpawnTimer = 0.0f; - env->enemySpawnInterval = 0.8777f; - env->last_spawned_lane = -1; - env->closest_edge_lane = -1; - env->base_vanishing_point_x = 86.0f; - env->current_vanishing_point_x = 86.0f; - env->target_vanishing_point_x = 86.0f; - env->vanishing_point_x = 86.0f; - env->initial_player_x = INITIAL_PLAYER_X; - env->player_y = PLAYER_MAX_Y; - env->min_speed = MIN_SPEED; - env->enemySpeed = ENEMY_CAR_SPEED; - env->max_speed = MAX_SPEED; - env->initial_cars_to_pass = INITIAL_CARS_TO_PASS; - env->day = 1; - env->drift_direction = 0; // Means in noop, but only if crashed state - env->crashed_penalty = 0.0f; - env->car_passed_no_crash_active = 1; - env->step_rew_car_passed_no_crash = 0.0f; - env->current_curve_stage = 0; // 0: straight - env->steps_in_current_stage = 0; - env->current_curve_direction = CURVE_STRAIGHT; - env->current_curve_factor = 0.0f; - env->target_curve_factor = 0.0f; - env->current_step_threshold = 0.0f; - env->wiggle_y = VANISHING_POINT_Y; - env->wiggle_speed = WIGGLE_SPEED; - env->wiggle_length = WIGGLE_LENGTH; - env->wiggle_amplitude = WIGGLE_AMPLITUDE; - env->wiggle_active = true; - env->currentGear = 0; - env->gearElapsedTime = 0.0f; - env->gearTimings[0] = 4.0f; - env->gearTimings[1] = 2.5f; - env->gearTimings[2] = 3.25f; - env->gearTimings[3] = 1.5f; - float totalSpeedRange = env->max_speed - env->min_speed; - float totalTime = 0.0f; - for (int i = 0; i < 4; i++) { - totalTime += env->gearTimings[i]; - } - float cumulativeSpeed = env->min_speed; - for (int i = 0; i < 4; i++) { - float gearTime = env->gearTimings[i]; - float gearSpeedIncrement = totalSpeedRange * (gearTime / totalTime); - env->gearSpeedThresholds[i] = cumulativeSpeed + gearSpeedIncrement; - env->gearAccelerationRates[i] = gearSpeedIncrement / (gearTime * TARGET_FPS); - cumulativeSpeed = env->gearSpeedThresholds[i]; - } - - // Reset enemy cars - for (int i = 0; i < MAX_ENEMIES; i++) { - env->enemyCars[i].lane = -1; - env->enemyCars[i].y = 0.0f; - env->enemyCars[i].passed = 0; - } - - // Reset rewards and logs - env->rewards[0] = 0.0f; - env->tracking_episode_return = 0.0f; - env->tracking_episode_length = 0.0f; - env->tracking_score = 0.0f; - env->tracking_reward = 0.0f; - env->tracking_step_rew_car_passed_no_crash = 0.0f; - env->tracking_crashed_penalty = 0.0f; - env->tracking_passed_cars = 0.0f; - env->tracking_passed_by_enemy = 0.0f; - env->tracking_cars_to_pass = INITIAL_CARS_TO_PASS; - env->tracking_collisions_player_vs_car = 0.0f; - env->tracking_collisions_player_vs_road = 0.0f; - - // Restore preserved RNG state to maintain reproducibility - env->rng_state = preserved_rng_state; - - // Restart the environment at the beginning of the day - env->elapsedTimeEnv = 0.0f; - env->currentDayTimeIndex = 0; - env->previousDayTimeIndex = NUM_BACKGROUND_TRANSITIONS - 1; - - // Reset flags and transient states - env->dayCompleted = 0; - env->terminals[0] = 0; - env->rewards[0] = 0.0f; -} - -// Reset all init vars; only called once after init -void c_reset(Enduro* env) { - // No random after first reset - int reset_seed = (env->reset_count == 0) ? xorshift32(&env->rng_state) : 0; - env->rng_state = reset_seed; - init(env); - env->reset_count += 1; -} - -unsigned char check_collision(Enduro* env, Car* car) { - // Compute the scale factor based on vanishing point reference - float depth = (car->y - VANISHING_POINT_Y) / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - float scale = fmax(0.1f, 0.9f * depth); - float car_width = CAR_WIDTH * scale; - float car_height = CAR_HEIGHT * scale; - float car_center_x = car_x_in_lane(env, car->lane, car->y); - float car_x = car_center_x - car_width / 2.0f; - return !(env->player_x > car_x + car_width - || env->player_x + CAR_WIDTH < car_x - || env->player_y > car->y + car_height - || env->player_y + CAR_HEIGHT < car->y); -} - -// Determines which of the 3 lanes the player's car is in -int get_player_lane(Enduro* env) { - float player_center_x = env->player_x + CAR_WIDTH / 2.0f; - float offset = (env->player_x - env->initial_player_x) * 0.5f; - float left_edge = road_edge_x(env, env->player_y, offset, true); - float right_edge = road_edge_x(env, env->player_y, offset, false); - float lane_width = (right_edge - left_edge) / 3.0f; - env->lane = (int)((player_center_x - left_edge) / lane_width); - if (env->lane < 0) env->lane = 0; - if (env->lane > 2) env->lane = 2; - return env->lane; -} - -float get_car_scale(float y) { - float depth = (y - VANISHING_POINT_Y) / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - return fmaxf(0.1f, 0.9f * depth); -} - -void add_enemy_car(Enduro* env) { - if (env->numEnemies >= MAX_ENEMIES) { - return; - } - - int player_lane = get_player_lane(env); - int possible_lanes[NUM_LANES]; - int num_possible_lanes = 0; - - // Determine the furthest lane from the player - int furthest_lane; - if (player_lane == 0) { - furthest_lane = 2; - } else if (player_lane == 2) { - furthest_lane = 0; - } else { - // Player is in the middle lane - // Decide based on player's position relative to the road center - float player_center_x = env->player_x + CAR_WIDTH / 2.0f; - float road_center_x = (road_edge_x(env, env->player_y, 0, true) + - road_edge_x(env, env->player_y, 0, false)) / 2.0f; - if (player_center_x < road_center_x) { - furthest_lane = 2; // Player is on the left side, choose rightmost lane - } else { - furthest_lane = 0; // Player is on the right side, choose leftmost lane - } - } - - if (env->speed <= 0.0f) { - // Only spawn in the lane furthest from the player - possible_lanes[num_possible_lanes++] = furthest_lane; - } else { - for (int i = 0; i < NUM_LANES; i++) { - possible_lanes[num_possible_lanes++] = i; - } - } - - if (num_possible_lanes == 0) { - return; // Rare - } - - // Randomly select a lane - int lane = possible_lanes[rand() % num_possible_lanes]; - // Preferentially spawn in the last_spawned_lane 30% of the time - if (rand() % 100 < 60 && env->last_spawned_lane != -1) { - lane = env->last_spawned_lane; - } - env->last_spawned_lane = lane; - // Init car - Car car = { - .lane = lane, - .x = car_x_in_lane(env, lane, VANISHING_POINT_Y), - .y = (env->speed > 0.0f) ? VANISHING_POINT_Y + 10.0f : PLAYABLE_AREA_BOTTOM + CAR_HEIGHT, - .last_x = car_x_in_lane(env, lane, VANISHING_POINT_Y), - .last_y = VANISHING_POINT_Y, - .passed = false, - .colorIndex = rand() % 6 - }; - // Ensure minimum spacing between cars in the same lane - float depth = (car.y - VANISHING_POINT_Y) / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - float scale = fmax(0.1f, 0.9f * depth + 0.1f); - float scaled_car_length = CAR_HEIGHT * scale; - // Randomize min spacing between 1.0f and 6.0f car lengths - float dynamic_spacing_factor = (rand() / (float)RAND_MAX) * 6.0f + 0.5f; - float min_spacing = dynamic_spacing_factor * scaled_car_length; - for (int i = 0; i < env->numEnemies; i++) { - Car* existing_car = &env->enemyCars[i]; - if (existing_car->lane != car.lane) { - continue; - } - float y_distance = fabs(existing_car->y - car.y); - if (y_distance < min_spacing) { - return; // Too close, do not spawn this car - } - } - // Ensure not occupying all lanes within vertical range of 6 car lengths - float min_vertical_range = 6.0f * CAR_HEIGHT; - int lanes_occupied = 0; - unsigned char lane_occupied[NUM_LANES] = { false }; - for (int i = 0; i < env->numEnemies; i++) { - Car* existing_car = &env->enemyCars[i]; - float y_distance = fabs(existing_car->y - car.y); - if (y_distance < min_vertical_range) { - lane_occupied[existing_car->lane] = true; - } - } - for (int i = 0; i < NUM_LANES; i++) { - if (lane_occupied[i]) lanes_occupied++; - } - if (lanes_occupied >= NUM_LANES - 1 && !lane_occupied[lane]) { - return; - } - env->enemyCars[env->numEnemies++] = car; -} - -void update_time_of_day(Enduro* env) { - float elapsedTime = env->elapsedTimeEnv; - float totalDuration = env->dayTransitionTimes[15]; - - if (elapsedTime >= totalDuration) { - elapsedTime -= totalDuration; - env->elapsedTimeEnv = elapsedTime; // Reset elapsed time - env->dayTimeIndex = 0; - } - - env->previousDayTimeIndex = env->currentDayTimeIndex; - - while (env->dayTimeIndex < 15 && - elapsedTime >= env->dayTransitionTimes[env->dayTimeIndex]) { - env->dayTimeIndex++; - } - env->currentDayTimeIndex = env->dayTimeIndex % 16; -} - -void clamp_speed(Enduro* env) { - if (env->speed < env->min_speed || env->speed > env->max_speed) { - env->speed = fmaxf(env->min_speed, fminf(env->speed, env->max_speed)); - } -} - -void clamp_gear(Enduro* env) { - if (env->currentGear < 0 || env->currentGear > 3) { - env->currentGear = 0; - } -} - -void accelerate(Enduro* env) { - clamp_speed(env); - clamp_gear(env); - - if (env->speed < env->max_speed) { - // Gear transition - if (env->speed >= env->gearSpeedThresholds[env->currentGear] && env->currentGear < 3) { - env->currentGear++; - env->gearElapsedTime = 0.0f; - } - - // Calculate new speed - float accel = env->gearAccelerationRates[env->currentGear]; - float multiplier = (env->currentGear == 0) ? 4.0f : 2.0f; - env->speed += accel * multiplier; - - clamp_speed(env); - - // Cap speed to gear threshold - if (env->speed > env->gearSpeedThresholds[env->currentGear]) { - env->speed = env->gearSpeedThresholds[env->currentGear]; - } - } - clamp_speed(env); -} - -void c_step(Enduro* env) { - env->rewards[0] = 0.0f; - env->elapsedTimeEnv += (1.0f / TARGET_FPS); - update_time_of_day(env); - update_road_curve(env); - env->tracking_episode_length += 1; - env->terminals[0] = 0; - env->road_scroll_offset += env->speed; - - // Update enemy car positions - for (int i = 0; i < env->numEnemies; i++) { - Car* car = &env->enemyCars[i]; - // Compute movement speed adjusted for scaling - float scale = get_car_scale(car->y); - float movement_speed = env->speed * scale * 0.75f; - // Update car position - car->y += movement_speed; - } - - // Calculate road edges - float road_left = road_edge_x(env, env->player_y, 0, true); - float road_right = road_edge_x(env, env->player_y, 0, false) - CAR_WIDTH; - - env->last_road_left = road_left; - env->last_road_right = road_right; - - // Reduced handling on snow - unsigned char isSnowStage = (env->currentDayTimeIndex == 3); - float movement_amount = 0.5f; // Default - if (isSnowStage) { - movement_amount = 0.3f; // Snow - } - - // Player movement logic == action space (Discrete[9]) - if (env->collision_cooldown_car_vs_car <= 0 && env->collision_cooldown_car_vs_road <= 0) { - int act = env->actions[0]; - movement_amount = (((env->speed - env->min_speed) / (env->max_speed - env->min_speed)) + 0.3f); // Magic number - switch (act) { - case ACTION_NOOP: - break; - case ACTION_FIRE: - accelerate(env); - break; - case ACTION_RIGHT: - env->player_x += movement_amount; - if (env->player_x > road_right) env->player_x = road_right; - break; - case ACTION_LEFT: - env->player_x -= movement_amount; - if (env->player_x < road_left) env->player_x = road_left; - break; - case ACTION_DOWN: - if (env->speed > env->min_speed) env->speed -= DECELERATION_RATE; - break; - case ACTION_DOWNRIGHT: - if (env->speed > env->min_speed) env->speed -= DECELERATION_RATE; - env->player_x += movement_amount; - if (env->player_x > road_right) env->player_x = road_right; - break; - case ACTION_DOWNLEFT: - if (env->speed > env->min_speed) env->speed -= DECELERATION_RATE; - env->player_x -= movement_amount; - if (env->player_x < road_left) env->player_x = road_left; - break; - case ACTION_RIGHTFIRE: - accelerate(env); - env->player_x += movement_amount; - if (env->player_x > road_right) env->player_x = road_right; - break; - case ACTION_LEFTFIRE: - accelerate(env); - env->player_x -= movement_amount; - if (env->player_x < road_left) env->player_x = road_left; - break; - - // Reset crashed_penalty - env->crashed_penalty = 0.0f; - } - - } else { - - if (env->collision_cooldown_car_vs_car > 0) { - env->collision_cooldown_car_vs_car -= 1; - env->crashed_penalty = -0.01f; - } - if (env->collision_cooldown_car_vs_road > 0) { - env->collision_cooldown_car_vs_road -= 1; - env->crashed_penalty = -0.01f; - } - - // Drift towards furthest road edge - if (env->drift_direction == 0) { // drift_direction is 0 when noop starts - env->drift_direction = (env->player_x > (road_left + road_right) / 2) ? -1 : 1; - // Remove enemy cars in middle lane and lane player is drifting towards - // only if they are behind the player (y > player_y) to avoid crashes - for (int i = 0; i < env->numEnemies; i++) { - Car* car = &env->enemyCars[i]; - - if ((car->lane == 1 || car->lane == env->lane + env->drift_direction) && (car->y > env->player_y)) { - for (int j = i; j < env->numEnemies - 1; j++) { - env->enemyCars[j] = env->enemyCars[j + 1]; - } - env->numEnemies--; - i--; - } - } - } - // Drift distance per step - if (env->collision_cooldown_car_vs_road > 0) { - env->player_x += env->drift_direction * 0.12f; - } else { - env->player_x += env->drift_direction * 0.25f; - } - } - - // Update player's lane - env->lane = get_player_lane(env); - - // Compute is_lane_empty and nearest_car_distance - float nearest_car_distance[NUM_LANES]; - bool is_lane_empty[NUM_LANES]; - - float MAX_DISTANCE = PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y; // Maximum possible distance - - for (int l = 0; l < NUM_LANES; l++) { - nearest_car_distance[l] = MAX_DISTANCE; - is_lane_empty[l] = true; - } - - for (int i = 0; i < env->numEnemies; i++) { - Car* car = &env->enemyCars[i]; - if (car->lane >= 0 && car->lane < NUM_LANES && car->y < env->player_y) { - float distance = env->player_y - car->y; - if (distance < nearest_car_distance[car->lane]) { - nearest_car_distance[car->lane] = distance; - is_lane_empty[car->lane] = false; - } - } - } - - // Give a reward if the player's lane is empty in front - float reward_amount = 0.05f; // Empty lane reward - if (is_lane_empty[env->lane]) { - env->rewards[0] += reward_amount; - } - - // Road curve/vanishing point movement logic - // Adjust player's x position based on the current curve - float curve_shift = -env->current_curve_factor * CURVE_PLAYER_SHIFT_FACTOR * fabs(env->speed); - env->player_x += curve_shift; - // Clamp player x position to within road edges - if (env->player_x < road_left) env->player_x = road_left; - if (env->player_x > road_right) env->player_x = road_right; - // Update player's horizontal position ratio, t_p - float t_p = (env->player_x - PLAYER_MIN_X) / (PLAYER_MAX_X - PLAYER_MIN_X); - t_p = fmaxf(0.0f, fminf(1.0f, t_p)); - env->t_p = t_p; - // Base vanishing point based on player's horizontal movement (without curve) - env->base_vanishing_point_x = VANISHING_POINT_X_LEFT - t_p * (VANISHING_POINT_X_LEFT - VANISHING_POINT_X_RIGHT); - // Adjust vanishing point based on current curve - float curve_vanishing_point_shift = env->current_curve_direction * CURVE_VANISHING_POINT_SHIFT; - env->vanishing_point_x = env->base_vanishing_point_x + curve_vanishing_point_shift; - - // After curve shift - // Wiggle logic - if (env->wiggle_active) { - float min_wiggle_period = 5.8f; // Slow wiggle period at min speed - float max_wiggle_period = 0.3f; // Fast wiggle period at max speed - // Apply non-linear scaling for wiggle period using square root - float speed_normalized = (env->speed - env->min_speed) / (env->max_speed - env->min_speed); - speed_normalized = fmaxf(0.0f, fminf(1.0f, speed_normalized)); // Clamp between 0 and 1 - // Adjust wiggle period with non-linear scale - float current_wiggle_period = min_wiggle_period - powf(speed_normalized, 0.25) * (min_wiggle_period - max_wiggle_period); - // Calculate wiggle speed based on adjusted wiggle period - env->wiggle_speed = (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y) / (current_wiggle_period * TARGET_FPS); - // Update wiggle position - env->wiggle_y += env->wiggle_speed; - // Reset wiggle when it reaches the bottom - if (env->wiggle_y > PLAYABLE_AREA_BOTTOM) { - env->wiggle_y = VANISHING_POINT_Y; - } - } - - // Player car moves forward slightly according to speed - // Update player y position based on speed - env->player_y = PLAYER_MAX_Y - (env->speed - env->min_speed) / (env->max_speed - env->min_speed) * (PLAYER_MAX_Y - PLAYER_MIN_Y); - // Clamp player_y to measured range - if (env->player_y < PLAYER_MIN_Y) env->player_y = PLAYER_MIN_Y; - if (env->player_y > PLAYER_MAX_Y) env->player_y = PLAYER_MAX_Y; - - // Check for and handle collisions between player and road edges - if (env->player_x <= road_left || env->player_x >= road_right) { - env->tracking_collisions_player_vs_road++; - env->rewards[0] -= 0.5f; - env->speed = fmaxf((env->speed - 1.25f), MIN_SPEED); - env->collision_cooldown_car_vs_road = CRASH_NOOP_DURATION_CAR_VS_ROAD; - env->drift_direction = 0; // Reset drift direction, has priority over car collisions - env->player_x = fmaxf(road_left + 1, fminf(road_right - 1, env->player_x)); - } - - // Enemy car logic - for (int i = 0; i < env->numEnemies; i++) { - Car* car = &env->enemyCars[i]; - - // Remove off-screen cars that move below the screen - if (car->y > PLAYABLE_AREA_BOTTOM + CAR_HEIGHT * 5) { - // Remove car from array if it moves below the screen - for (int j = i; j < env->numEnemies - 1; j++) { - env->enemyCars[j] = env->enemyCars[j + 1]; - } - env->numEnemies--; - i--; - continue; - } - - // Remove cars that reach or surpass the logical vanishing point if moving up (player speed negative) - if (env->speed < 0 && car->y <= LOGICAL_VANISHING_Y) { - // Remove car from array if it reaches the logical vanishing point if moving down (player speed positive) - for (int j = i; j < env->numEnemies - 1; j++) { - env->enemyCars[j] = env->enemyCars[j + 1]; - } - env->numEnemies--; - i--; - continue; - } - - // If the car is behind the player and speed <= 0, move it to the furthest lane - if (env->speed <= 0 && car->y >= env->player_y + CAR_HEIGHT) { - // Determine the furthest lane - int furthest_lane; - int player_lane = get_player_lane(env); - if (player_lane == 0) { - furthest_lane = 2; - } else if (player_lane == 2) { - furthest_lane = 0; - } else { - // Player is in the middle lane - // Decide based on player's position relative to the road center - float player_center_x = env->player_x + CAR_WIDTH / 2.0f; - float road_center_x = (road_edge_x(env, env->player_y, 0, true) + - road_edge_x(env, env->player_y, 0, false)) / 2.0f; - if (player_center_x < road_center_x) { - furthest_lane = 2; // Player is on the left side - } else { - furthest_lane = 0; // Player is on the right side - } - } - car->lane = furthest_lane; - continue; - } - - // Check for passing logic **only if not on collision cooldown** - if (env->speed > 0 && car->last_y < env->player_y + CAR_HEIGHT - && car->y >= env->player_y + CAR_HEIGHT - && env->collision_cooldown_car_vs_car <= 0 - && env->collision_cooldown_car_vs_road <= 0) { - if (env->carsToPass > 0) { - env->carsToPass -= 1; - } - if (!car->passed) { - env->tracking_passed_cars += 1; - env->rewards[0] += 1.0f; // Car passed reward - env->car_passed_no_crash_active = 1; // Stepwise rewards activated - env->step_rew_car_passed_no_crash += 0.001f; // Stepwise reward - } - car->passed = true; - } else if (env->speed < 0 && car->last_y > env->player_y && car->y <= env->player_y) { - int maxCarsToPass = (env->day == 1) ? 200 : 300; // Day 1: 200 cars, Day 2+: 300 cars - if (env->carsToPass == maxCarsToPass) { - // Do nothing; log the event - env->tracking_passed_by_enemy += 1.0f; - } else { - env->carsToPass += 1; - env->tracking_passed_by_enemy += 1.0f; - env->rewards[0] -= 0.1f; - } - } - - // Preserve last x and y for passing, obs - car->last_y = car->y; - car->last_x = car->x; - - // Check for and handle collisions between player and enemy cars - if (env->collision_cooldown_car_vs_car <= 0 && check_collision(env, car)) { - env->tracking_collisions_player_vs_car++; - env->rewards[0] -= 0.5f; - env->speed = 1 + MIN_SPEED; - env->collision_cooldown_car_vs_car = CRASH_NOOP_DURATION_CAR_VS_CAR; - env->drift_direction = 0; // Reset drift direction - env->car_passed_no_crash_active = 0; // Stepwise rewards deactivated until next car passed - env->step_rew_car_passed_no_crash = 0.0f; // Reset stepwise reward - } - } - - // Calculate enemy spawn interval based on player speed and day - // Values measured from original gameplay - float min_spawn_interval = 0.5f; // 0.8777f; // Minimum spawn interval - float max_spawn_interval; - int dayIndex = env->day - 1; - int numMaxSpawnIntervals = sizeof(MAX_SPAWN_INTERVALS) / sizeof(MAX_SPAWN_INTERVALS[0]); - - if (dayIndex < numMaxSpawnIntervals) { - max_spawn_interval = MAX_SPAWN_INTERVALS[dayIndex]; - } else { - // For days beyond first, decrease max_spawn_interval further - max_spawn_interval = MAX_SPAWN_INTERVALS[numMaxSpawnIntervals - 1] - (dayIndex - numMaxSpawnIntervals + 1) * 0.1f; - if (max_spawn_interval < 0.1f) { - max_spawn_interval = 0.1f; - } - } - - // Ensure min_spawn_interval is greater than or equal to max_spawn_interval - if (min_spawn_interval < max_spawn_interval) { - min_spawn_interval = max_spawn_interval; - } - - // Calculate speed factor for enemy spawning - float speed_factor = (env->speed - env->min_speed) / (env->max_speed - env->min_speed); - if (speed_factor < 0.0f) speed_factor = 0.0f; - if (speed_factor > 1.0f) speed_factor = 1.0f; - - // Interpolate between min and max spawn intervals to scale to player speed - env->enemySpawnInterval = min_spawn_interval - speed_factor * (min_spawn_interval - max_spawn_interval) * 1.5f; - - // Update enemy spawn timer - env->enemySpawnTimer += (1.0f / TARGET_FPS); - if (env->enemySpawnTimer >= env->enemySpawnInterval) { - env->enemySpawnTimer -= env->enemySpawnInterval; - - if (env->numEnemies < MAX_ENEMIES) { - // Add a clumping factor based on speed - float clump_probability = fminf((env->speed - env->min_speed) / (env->max_speed - env->min_speed), 1.0f); // Scales with speed - int num_to_spawn = 1; - - // Randomly decide to spawn more cars in a clump - if ((rand() / (float)RAND_MAX) < clump_probability) { - num_to_spawn = 1 + rand() % 2; // Spawn 1 to 3 cars - } - - // Track occupied lanes to prevent over-blocking - int occupied_lanes[NUM_LANES] = {0}; - - for (int i = 0; i < num_to_spawn && env->numEnemies < MAX_ENEMIES; i++) { - // Find an unoccupied lane - int lane; - do { - lane = rand() % NUM_LANES; - } while (occupied_lanes[lane]); - - // Mark the lane as occupied - occupied_lanes[lane] = 1; - - // Add the enemy car - int previous_num_enemies = env->numEnemies; - add_enemy_car(env); - if (env->numEnemies > previous_num_enemies) { - Car* new_car = &env->enemyCars[env->numEnemies - 1]; - new_car->lane = lane; - new_car->y -= i * (CAR_HEIGHT * 3); // Vertical spacing for clump - } - } - } - } - - // Day completed logic - if (env->carsToPass <= 0 && !env->dayCompleted) { - env->dayCompleted = true; - } - - // Handle day transition when background cycles back to 0 - if (env->currentDayTimeIndex == 0 && env->previousDayTimeIndex == 15) { - // Background cycled back to 0 - if (env->dayCompleted) { - env->tracking_days_completed += 1; - env->day += 1; - env->rewards[0] += 1.0f; - env->carsToPass = 300; // Always 300 after the first day - env->dayCompleted = false; - add_log(env); - - } else { - // Player failed to pass required cars, soft-reset environment - env->tracking_days_failed += 1.0f; - env->terminals[0] = true; - add_log(env); - compute_observations(env); // Call compute_observations before reset to log - reset_round(env); // Reset round == soft reset - return; - } - } - - // Reward each step after a car is passed until a collision occurs. - // Then, no rewards per step until next car is passed. - if (env->car_passed_no_crash_active) { - env->rewards[0] += env->step_rew_car_passed_no_crash; - } - - env->rewards[0] += env->crashed_penalty; - env->tracking_crashed_penalty = env->crashed_penalty; - env->tracking_step_rew_car_passed_no_crash = env->step_rew_car_passed_no_crash; - env->tracking_reward = env->rewards[0]; - env->tracking_episode_return = env->rewards[0]; - env->tick++; - - float normalizedSpeed = fminf(fmaxf(env->speed, 1.0f), 2.0f); - env->score += (int)normalizedSpeed; - - env->tracking_score = env->score; - int local_cars_to_pass = env->carsToPass; - env->tracking_cars_to_pass = (int)local_cars_to_pass; - - compute_observations(env); -} - -void compute_observations(Enduro* env) { - float* obs = env->observations; - int obs_index = 0; - - // Most obs normalized to [0, 1] - // Bounding box around player - float player_x_norm = (env->player_x - env->last_road_left) / (env->last_road_right - env->last_road_left); - float player_y_norm = (PLAYER_MAX_Y - env->player_y) / (PLAYER_MAX_Y - PLAYER_MIN_Y); - // float player_width_norm = CAR_WIDTH / (env->last_road_right - env->last_road_left); - // float player_height_norm = CAR_HEIGHT / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - - // Player position and speed - // idx 1-3 - obs[obs_index++] = player_x_norm; - obs[obs_index++] = player_y_norm; - obs[obs_index++] = (env->speed - MIN_SPEED) / (MAX_SPEED - MIN_SPEED); - - // Road edges (separate lines for clarity) - float road_left = road_edge_x(env, env->player_y, 0, true); - float road_right = road_edge_x(env, env->player_y, 0, false) - CAR_WIDTH; - - // Road edges and last road edges - // idx 4-7 - obs[obs_index++] = (road_left - PLAYABLE_AREA_LEFT) / - (PLAYABLE_AREA_RIGHT - PLAYABLE_AREA_LEFT); - obs[obs_index++] = (road_right - PLAYABLE_AREA_LEFT) / - (PLAYABLE_AREA_RIGHT - PLAYABLE_AREA_LEFT); - obs[obs_index++] = (env->last_road_left - PLAYABLE_AREA_LEFT) / - (PLAYABLE_AREA_RIGHT - PLAYABLE_AREA_LEFT); - obs[obs_index++] = (env->last_road_right - PLAYABLE_AREA_LEFT) / - (PLAYABLE_AREA_RIGHT - PLAYABLE_AREA_LEFT); - - // Player lane number (0, 1, 2) - // idx 8 - obs[obs_index++] = (float)get_player_lane(env) / (NUM_LANES - 1); - - // Enemy cars (numEnemies * 5 values (x, y, delta y, same lane as player car)) = 10 * 4 = 50 values - // idx 9-58 - for (int i = 0; i < env->max_enemies; i++) { - Car* car = &env->enemyCars[i]; - - if (car->y > VANISHING_POINT_Y && car->y < PLAYABLE_AREA_BOTTOM) { - // Enemy car buffer zone - float buffer_x = CAR_WIDTH * 0.5f; - float buffer_y = CAR_HEIGHT * 0.5f; - - // Normalize car x position relative to road edges - float car_x_norm = ((car->x - buffer_x) - env->last_road_left) / (env->last_road_right - env->last_road_left); - car_x_norm = fmaxf(0.0f, fminf(1.0f, car_x_norm)); // Clamp between 0 and 1 - // Normalize car y position relative to the full road height - float car_y_norm = (PLAYABLE_AREA_BOTTOM - (car->y - buffer_y)) / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - car_y_norm = fmaxf(0.0f, fminf(1.0f, car_y_norm)); // Clamp between 0 and 1 - // Calculate delta_x for lateral movement - float delta_x_norm = (car->last_x - car->x) / (env->last_road_right - env->last_road_left); - // Calculate delta_y for relative speed - float delta_y_norm = (car->last_y - car->y) / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - // Determine if the car is in the same lane as the player - int is_same_lane = (car->lane == env->lane); - // Add normalized car x position - obs[obs_index++] = car_x_norm; - // Add normalized car y position - obs[obs_index++] = car_y_norm; - // Add normalized delta x (lateral movement) - obs[obs_index++] = delta_x_norm; - // Add normalized delta y (relative speed) - obs[obs_index++] = delta_y_norm; - // Add lane information (binary flag for lane match) - obs[obs_index++] = (float)is_same_lane; - } else { - // Default values for cars that don't exist - obs[obs_index++] = 0.5f; // Neutral x position - obs[obs_index++] = 0.5f; // Neutral y position - obs[obs_index++] = 0.0f; // No movement (delta_x = 0) - obs[obs_index++] = 0.0f; // No movement (delta_y = 0) - obs[obs_index++] = 0.0f; // Not in the same lane - } - } - - // Curve direction - // idx 59 - obs[obs_index++] = (float)(env->current_curve_direction + 1) / 2.0f; - - // Observation for player's drift due to road curvature - // idx 60-62 - // Drift direction and magnitude - float drift_magnitude = env->current_curve_factor * CURVE_PLAYER_SHIFT_FACTOR * fabs(env->speed); - float drift_direction = (env->current_curve_factor > 0) ? 1.0f : -1.0f; // 1 for right drift, -1 for left drift - - // Normalize drift magnitude (assume max absolute curve factor is 1.0 for normalization) - float max_drift_magnitude = CURVE_PLAYER_SHIFT_FACTOR * env->max_speed; - float normalized_drift_magnitude = fabs(drift_magnitude) / max_drift_magnitude; - - // Add drift direction (-1.0 to 1.0), normalized magnitude (0.0 to 1.0), and curve factor (-1.0 to 1.0) - obs[obs_index++] = drift_direction; - obs[obs_index++] = normalized_drift_magnitude; - obs[obs_index++] = env->current_curve_factor; - - // Time of day - // idx 63 - float total_day_length = env->dayTransitionTimes[15]; - obs[obs_index++] = fmodf(env->elapsedTimeEnv, total_day_length) / total_day_length; - - // Cars to pass - // idx 64 - obs[obs_index++] = (float)env->carsToPass / env->initial_cars_to_pass; - - // Compute per-lane observations: nearest enemy car distances in each lane - // idx 65-67 - float nearest_car_distance[NUM_LANES]; - bool is_lane_empty[NUM_LANES]; - - float MAX_DISTANCE = PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y; // Maximum possible distance - - for (int l = 0; l < NUM_LANES; l++) { - nearest_car_distance[l] = MAX_DISTANCE; - is_lane_empty[l] = true; - } - - for (int i = 0; i < env->numEnemies; i++) { - Car* car = &env->enemyCars[i]; - if (car->lane >= 0 && car->lane < NUM_LANES && car->y < env->player_y) { - float distance = env->player_y - car->y; - if (distance < nearest_car_distance[car->lane]) { - nearest_car_distance[car->lane] = distance; - is_lane_empty[car->lane] = false; - } - } - } - - // Add per-lane normalized distances to observations - for (int l = 0; l < NUM_LANES; l++) { - float normalized_distance; - if (is_lane_empty[l]) { - normalized_distance = 1.0f; // No enemy car in front in this lane - } else { - normalized_distance = nearest_car_distance[l] / MAX_DISTANCE; - } - obs[obs_index++] = normalized_distance; - } -} - -// When to curve road and how to curve it, including dense smooth transitions -// An ugly, dense function, but it is necessary -void update_road_curve(Enduro* env) { - int* current_curve_stage = &env->current_curve_stage; - int* steps_in_current_stage = &env->steps_in_current_stage; - - // Map speed to the scale between 0.5 and 3.5 - float speed_scale = 0.5f + ((fabs(env->speed) / env->max_speed) * (MAX_SPEED - MIN_SPEED)); - float vanishing_point_transition_speed = VANISHING_POINT_TRANSITION_SPEED + speed_scale; - - // Randomize step thresholds and curve directions - int step_thresholds[3]; - int curve_directions[3]; - int last_direction = 0; // Tracks the last curve direction, initialized to straight (0) - - for (int i = 0; i < 3; i++) { - // Generate random step thresholds - step_thresholds[i] = 1500 + rand() % 3801; // Random value between 1500 and 3800 - - // Generate a random curve direction (-1, 0, 1) with rules - int direction_choices[] = {-1, 0, 1}; - int next_direction; - - do { - next_direction = direction_choices[rand() % 3]; - } while ((last_direction == -1 && next_direction == 1) || (last_direction == 1 && next_direction == -1)); - - curve_directions[i] = next_direction; - last_direction = next_direction; - } - - // Use step thresholds and curve directions dynamically - env->current_step_threshold = step_thresholds[*current_curve_stage % 3]; - (*steps_in_current_stage)++; - - if (*steps_in_current_stage >= step_thresholds[*current_curve_stage % 3]) { - env->target_curve_factor = (float)curve_directions[*current_curve_stage % 3]; - *steps_in_current_stage = 0; - *current_curve_stage = (*current_curve_stage + 1) % 3; - } - - // Determine sizes of step_thresholds and curve_directions - size_t step_thresholds_size = sizeof(step_thresholds) / sizeof(step_thresholds[0]); - size_t curve_directions_size = sizeof(curve_directions) / sizeof(curve_directions[0]); - - // Find the maximum size - size_t max_size = (step_thresholds_size > curve_directions_size) ? step_thresholds_size : curve_directions_size; - - // Adjust arrays dynamically - int adjusted_step_thresholds[max_size]; - int adjusted_curve_directions[max_size]; - - for (size_t i = 0; i < max_size; i++) { - adjusted_step_thresholds[i] = step_thresholds[i % step_thresholds_size]; - adjusted_curve_directions[i] = curve_directions[i % curve_directions_size]; - } - - // Use adjusted arrays for current calculations - env->current_step_threshold = adjusted_step_thresholds[*current_curve_stage % max_size]; - (*steps_in_current_stage)++; - - if (*steps_in_current_stage >= adjusted_step_thresholds[*current_curve_stage]) { - env->target_curve_factor = (float)adjusted_curve_directions[*current_curve_stage % max_size]; - *steps_in_current_stage = 0; - *current_curve_stage = (*current_curve_stage + 1) % max_size; - } - - if (env->current_curve_factor < env->target_curve_factor) { - env->current_curve_factor = fminf(env->current_curve_factor + CURVE_TRANSITION_SPEED, env->target_curve_factor); - } else if (env->current_curve_factor > env->target_curve_factor) { - env->current_curve_factor = fmaxf(env->current_curve_factor - CURVE_TRANSITION_SPEED, env->target_curve_factor); - } - env->current_curve_direction = fabsf(env->current_curve_factor) < 0.1f ? CURVE_STRAIGHT - : (env->current_curve_factor > 0) ? CURVE_RIGHT : CURVE_LEFT; - - // Move the vanishing point gradually - env->base_target_vanishing_point_x = VANISHING_POINT_X_LEFT - env->t_p * (VANISHING_POINT_X_LEFT - VANISHING_POINT_X_RIGHT); - float target_shift = env->current_curve_direction * CURVE_VANISHING_POINT_SHIFT; - env->target_vanishing_point_x = env->base_target_vanishing_point_x + target_shift; - - if (env->current_vanishing_point_x < env->target_vanishing_point_x) { - env->current_vanishing_point_x = fminf(env->current_vanishing_point_x + vanishing_point_transition_speed, env->target_vanishing_point_x); - } else if (env->current_vanishing_point_x > env->target_vanishing_point_x) { - env->current_vanishing_point_x = fmaxf(env->current_vanishing_point_x - vanishing_point_transition_speed, env->target_vanishing_point_x); - } - env->vanishing_point_x = env->current_vanishing_point_x; -} - -// B(t) = (1−t)^2 * P0​+2(1−t) * t * P1​+t^2 * P2​, t∈[0,1] -// Quadratic bezier curve helper function -float quadratic_bezier(float bottom_x, float control_x, float top_x, float t) { - float one_minus_t = 1.0f - t; - return one_minus_t * one_minus_t * bottom_x + - 2.0f * one_minus_t * t * control_x + - t * t * top_x; -} - -// Computes the edges of the road. Use for both L and R. -// Lots of magic numbers to replicate as exactly as possible -// original Atari 2600 Enduro road rendering. -float road_edge_x(Enduro* env, float y, float offset, unsigned char left) { - float t = (PLAYABLE_AREA_BOTTOM - y) / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - float base_offset = left ? -ROAD_LEFT_OFFSET : ROAD_RIGHT_OFFSET; - float bottom_x = env->base_vanishing_point_x + base_offset + offset; - float top_x = env->current_vanishing_point_x + offset; - float edge_x; - if (fabsf(env->current_curve_factor) < 0.01f) { - // Straight road - edge_x = bottom_x + t * (top_x - bottom_x); - } else { - // Adjust curve offset based on curve direction - float curve_offset = (env->current_curve_factor > 0 ? -30.0f : 30.0f) * fabsf(env->current_curve_factor); - float control_x = bottom_x + (top_x - bottom_x) * 0.5f + curve_offset; - // Calculate edge using Bézier curve for proper curvature - edge_x = quadratic_bezier(bottom_x, control_x, top_x, t); - } - - // Wiggle effect - float wiggle_offset = 0.0f; - if (env->wiggle_active && y >= env->wiggle_y && y <= env->wiggle_y + env->wiggle_length) { - float t_wiggle = (y - env->wiggle_y) / env->wiggle_length; // Ranges from 0 to 1 - // Trapezoidal wave calculation - if (t_wiggle < 0.15f) { - // Connection to road edge - wiggle_offset = env->wiggle_amplitude * (t_wiggle / 0.15f); - } else if (t_wiggle < 0.87f) { - // Flat top of wiggle - wiggle_offset = env->wiggle_amplitude; - } else { - // Reconnection to road edge - wiggle_offset = env->wiggle_amplitude * ((1.0f - t_wiggle) / 0.13f); - } - // Wiggle towards road center - wiggle_offset *= (left ? 1.0f : -1.0f); - // Scale wiggle offset based on y position, starting at 0.03f at the vanishing point - float depth = (y - VANISHING_POINT_Y) / (PLAYABLE_AREA_BOTTOM - VANISHING_POINT_Y); - float scale = 0.03f + (depth * depth); - if (scale > 0.3f) { - scale = 0.3f; - } - wiggle_offset *= scale; - } - // Apply the wiggle offset - edge_x += wiggle_offset; - return edge_x; -} - -// Computes x position of car in a given lane -float car_x_in_lane(Enduro* env, int lane, float y) { - // Set offset to 0 to ensure enemy cars align with the road rendering - float offset = 0.0f; - float left_edge = road_edge_x(env, y, offset, true); - float right_edge = road_edge_x(env, y, offset, false); - float lane_width = (right_edge - left_edge) / NUM_LANES; - return left_edge + lane_width * (lane + 0.5f); -} - -// Handles rendering logic -Client* make_client(Enduro* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - - // State data from env (Enduro*) - client->width = env->width; - client->height = env->height; - - initRaylib(&client->gameState); // Pass gameState here - loadTextures(&client->gameState); - - return client; -} - -void close_client(Client* client) { - cleanup(&client->gameState); - CloseWindow(); - free(client); -} - -void render_car(GameState* gameState) { - int carAssetIndex = gameState->showLeftTread ? gameState->playerCarLeftTreadIndex : gameState->playerCarRightTreadIndex; - Rectangle srcRect = asset_map[carAssetIndex]; - Vector2 position = { gameState->player_x, gameState->player_y }; - DrawTextureRec(gameState->spritesheet, srcRect, position, WHITE); -} - -void initRaylib(GameState* gameState) { - if (!IsWindowReady()) { - InitWindow(SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2, "puffer_enduro"); - SetTargetFPS(60); - gameState->renderTarget = LoadRenderTexture(SCREEN_WIDTH, SCREEN_HEIGHT); - } -} - -void loadTextures(GameState* gameState) { - // Load background and mountain textures for different times of day per original env - gameState->spritesheet = LoadTexture("resources/enduro/enduro_spritesheet.png"); - - // Initialize animation variables - gameState->carAnimationTimer = 0.0f; - gameState->carAnimationInterval = 0.05f; // Init; updated based on speed - gameState->showLeftTread = true; - gameState->mountainPosition = 0.0f; - - // Initialize victory effect variables - gameState->showLeftFlag = true; - gameState->flagTimer = 0; - gameState->victoryDisplayTimer = 0; - gameState->victoryAchieved = false; - - // Initialize scoreboard variables - gameState->score = 0; - gameState->scoreTimer = 0; - gameState->carsLeftGameState = 0; - gameState->day = 1; - - // Initialize score digits arrays - for (int i = 0; i < SCORE_DIGITS; i++) { - gameState->scoreDigitCurrents[i] = 0; - gameState->scoreDigitNexts[i] = 0; - gameState->scoreDigitOffsets[i] = 0.0f; - gameState->scoreDigitScrolling[i] = false; - } - - // Initialize time-of-day variables - gameState->elapsedTime = 0.0f; - gameState->currentBackgroundIndex = 0; - gameState->previousBackgroundIndex = 0; - - // Initialize background and mountain indices - for (int i = 0; i < 16; ++i) { - gameState->backgroundIndices[i] = ASSET_BG_0 + i; - gameState->mountainIndices[i] = ASSET_MOUNTAIN_0 + i; - } - - // Initialize digit indices - for (int i = 0; i < 10; ++i) { - gameState->digitIndices[i] = ASSET_DIGITS_0 + i; - gameState->greenDigitIndices[i] = ASSET_GREEN_DIGITS_0 + i; - gameState->yellowDigitIndices[i] = ASSET_YELLOW_DIGITS_0 + i; - } - gameState->digitIndices[10] = ASSET_DIGITS_CAR; // Index for "CAR" - - // Initialize enemy car indices - int baseEnemyCarIndex = ASSET_ENEMY_CAR_BLUE_LEFT_TREAD; - for (int color = 0; color < 6; ++color) { - for (int tread = 0; tread < 2; ++tread) { - gameState->enemyCarIndices[color][tread] = baseEnemyCarIndex + color * 2 + tread; - } - } - - // Load other asset indices - gameState->enemyCarNightTailLightsIndex = ASSET_ENEMY_CAR_NIGHT_TAIL_LIGHTS; - gameState->enemyCarNightFogTailLightsIndex = ASSET_ENEMY_CAR_NIGHT_FOG_TAIL_LIGHTS; - gameState->playerCarLeftTreadIndex = ASSET_PLAYER_CAR_LEFT_TREAD; - gameState->playerCarRightTreadIndex = ASSET_PLAYER_CAR_RIGHT_TREAD; - gameState->levelCompleteFlagLeftIndex = ASSET_LEVEL_COMPLETE_FLAG_LEFT; - gameState->levelCompleteFlagRightIndex = ASSET_LEVEL_COMPLETE_FLAG_RIGHT; - - // Initialize animation variables - gameState->carAnimationTimer = 0.0f; - gameState->carAnimationInterval = 0.05f; // Initial interval, will be updated based on speed - gameState->showLeftTread = true; - gameState->mountainPosition = 0.0f; -} - -void cleanup(GameState* gameState) { - UnloadRenderTexture(gameState->renderTarget); - UnloadTexture(gameState->spritesheet); -} - -void updateCarAnimation(GameState* gameState) { - // Update the animation interval based on the player's speed - // Faster speed means faster alternation - float minInterval = 0.005f; // Minimum interval at max speed - float maxInterval = 0.075f; // Maximum interval at min speed - - float speedRatio = (gameState->speed - gameState->min_speed) / (gameState->max_speed - gameState->min_speed); - gameState->carAnimationInterval = maxInterval - (maxInterval - minInterval) * speedRatio; - - // Update the animation timer - gameState->carAnimationTimer += GetFrameTime(); // Time since last frame - - if (gameState->carAnimationTimer >= gameState->carAnimationInterval) { - gameState->carAnimationTimer = 0.0f; - gameState->showLeftTread = !gameState->showLeftTread; // Switch texture - } -} - -void updateScoreboard(GameState* gameState) { - float normalizedSpeed = fminf(fmaxf(gameState->speed, 1.0f), 2.0f); - // Determine the frame interval for score increment based on speed - int frameInterval = (int)(30 / normalizedSpeed); - gameState->scoreTimer++; - - if (gameState->scoreTimer >= frameInterval) { - gameState->scoreTimer = 0; - // Increment the score based on normalized speed - gameState->score += (int)normalizedSpeed; - if (gameState->score > 99999) { - gameState->score = 0; - } - // Determine which digits have changed and start scrolling them - int tempScore = gameState->score; - for (int i = SCORE_DIGITS - 1; i >= 0; i--) { - int newDigit = tempScore % 10; - tempScore /= 10; - if (newDigit != gameState->scoreDigitCurrents[i]) { - gameState->scoreDigitNexts[i] = newDigit; - gameState->scoreDigitOffsets[i] = 0.0f; - gameState->scoreDigitScrolling[i] = true; - } - } - } - // Update scrolling digits - float scrollSpeed = 0.55f * normalizedSpeed; - for (int i = 0; i < SCORE_DIGITS; i++) { - if (gameState->scoreDigitScrolling[i]) { - gameState->scoreDigitOffsets[i] += scrollSpeed; // Scroll speed - if (gameState->scoreDigitOffsets[i] >= DIGIT_HEIGHT) { - gameState->scoreDigitOffsets[i] = 0.0f; - gameState->scoreDigitCurrents[i] = gameState->scoreDigitNexts[i]; - gameState->scoreDigitScrolling[i] = false; // Stop scrolling - } - } - } -} - -void renderBackground(GameState* gameState) { - int bgIndex = gameState->backgroundIndices[gameState->currentBackgroundIndex]; - Rectangle srcRect = asset_map[bgIndex]; - DrawTextureRec(gameState->spritesheet, srcRect, (Vector2){0, 0}, WHITE); -} - -void renderScoreboard(GameState* gameState) { - int digitWidth = DIGIT_WIDTH; - int digitHeight = DIGIT_HEIGHT; - // Convert bottom-left coordinates to top-left origin - // -8 for x resolution change from 160 to 152 - int scoreStartX = 56 + digitWidth - 8; - int scoreStartY = 173 - digitHeight; - int dayX = 56 - 8; - int dayY = 188 - digitHeight; - int carsX = 72 - 8; - int carsY = 188 - digitHeight; - - // Render score with scrolling effect - for (int i = 0; i < SCORE_DIGITS; ++i) { - int digitX = scoreStartX + i * digitWidth; - int currentDigitIndex = gameState->scoreDigitCurrents[i]; - int nextDigitIndex = gameState->scoreDigitNexts[i]; - - int currentAssetIndex, nextAssetIndex; - if (i == SCORE_DIGITS - 1) { - // Use yellow digits for the last digit - currentAssetIndex = gameState->yellowDigitIndices[currentDigitIndex]; - nextAssetIndex = gameState->yellowDigitIndices[nextDigitIndex]; - } else { - // Use regular digits - currentAssetIndex = gameState->digitIndices[currentDigitIndex]; - nextAssetIndex = gameState->digitIndices[nextDigitIndex]; - } - Rectangle srcRectCurrentFull = asset_map[currentAssetIndex]; - Rectangle srcRectNextFull = asset_map[nextAssetIndex]; - - if (gameState->scoreDigitScrolling[i]) { - // Scrolling effect for this digit - float offset = gameState->scoreDigitOffsets[i]; - // Render current digit moving up - Rectangle srcRectCurrent = srcRectCurrentFull; - srcRectCurrent.height = digitHeight - (int)offset; - Rectangle destRectCurrent = { digitX, scoreStartY + (int)offset, digitWidth, digitHeight - (int)offset }; - DrawTexturePro( - gameState->spritesheet, - srcRectCurrent, - destRectCurrent, - (Vector2){ 0, 0 }, - 0.0f, - WHITE - ); - // Render next digit coming up from below - Rectangle srcRectNext = srcRectNextFull; - srcRectNext.y += digitHeight - (int)offset; - srcRectNext.height = (int)offset; - Rectangle destRectNext = { digitX, scoreStartY, digitWidth, (int)offset }; - DrawTexturePro( - gameState->spritesheet, - srcRectNext, - destRectNext, - (Vector2){ 0, 0 }, - 0.0f, - WHITE - ); - } else { - // No scrolling, render the current digit normally - Rectangle srcRect = asset_map[currentAssetIndex]; - Vector2 position = { digitX, scoreStartY }; - DrawTextureRec(gameState->spritesheet, srcRect, position, WHITE); - } - } - - // Render day number - int day = gameState->day % 10; - int dayTextureIndex = day; - // Pass dayCompleted condition from Enduro to GameState - if (gameState->dayCompleted) { - gameState->victoryAchieved = true; - } - Rectangle daySrcRect; - if (gameState->victoryAchieved) { - // Green day digits during victory - int assetIndex = gameState->greenDigitIndices[dayTextureIndex]; - daySrcRect = asset_map[assetIndex]; - } else { - // Use normal digits - int assetIndex = gameState->digitIndices[dayTextureIndex]; - daySrcRect = asset_map[assetIndex]; - } - Vector2 dayPosition = { dayX, dayY }; - DrawTextureRec(gameState->spritesheet, daySrcRect, dayPosition, WHITE); - - // Render "CAR" digit or flags for cars to pass - if (gameState->victoryAchieved) { - // Alternate between level_complete_flag_left and level_complete_flag_right - int flagAssetIndex = gameState->showLeftFlag ? gameState->levelCompleteFlagLeftIndex : gameState->levelCompleteFlagRightIndex; - Rectangle flagSrcRect = asset_map[flagAssetIndex]; - Rectangle destRect = { carsX, carsY, flagSrcRect.width, flagSrcRect.height }; - DrawTexturePro( - gameState->spritesheet, - flagSrcRect, - destRect, - (Vector2){ 0, 0 }, - 0.0f, - WHITE - ); - } else { - // Render "CAR" label - int carAssetIndex = gameState->digitIndices[10]; // Index for "CAR" - Rectangle carSrcRect = asset_map[carAssetIndex]; - Vector2 carPosition = { carsX, carsY }; - DrawTextureRec(gameState->spritesheet, carSrcRect, carPosition, WHITE); - - // Render the remaining digits for cars to pass - int cars = gameState->carsLeftGameState; - if (cars < 0) cars = 0; // Ensure cars is not negative - for (int i = 1; i < CARS_DIGITS; ++i) { - int divisor = (int)pow(10, CARS_DIGITS - i - 1); - int digit = (cars / divisor) % 10; - if (digit < 0 || digit > 9) digit = 0; // Clamp digit to valid range - int digitX = carsX + i * (digitWidth + 1); // Add spacing between digits - int assetIndex = gameState->digitIndices[digit]; - Rectangle srcRect = asset_map[assetIndex]; - Vector2 position = { digitX, carsY }; - DrawTextureRec(gameState->spritesheet, srcRect, position, WHITE); - } - } -} - -// Triggers the day completed 'victory' display -// Solely for flapping flag visual effect -void updateVictoryEffects(GameState* gameState) { - if (!gameState->victoryAchieved) { - return; - } - gameState->flagTimer++; - // Modulo triggers flag direction change - // Flag renders in that direction until next change - if (gameState->flagTimer % 50 == 0) { - gameState->showLeftFlag = !gameState->showLeftFlag; - } - gameState->victoryDisplayTimer++; - if (gameState->victoryDisplayTimer >= 10) { - gameState->victoryDisplayTimer = 0; - } -} - -void updateMountains(GameState* gameState) { - // Mountain scrolling effect when road is curving - float baseSpeed = 0.0f; - float curveStrength = fabsf(gameState->current_curve_factor); - float speedMultiplier = 1.0f; // Scroll speed - float scrollSpeed = baseSpeed + curveStrength * speedMultiplier; - int mountainIndex = gameState->mountainIndices[0]; // Use any mountain index since width is consistent - int mountainWidth = asset_map[mountainIndex].width; - if (gameState->current_curve_direction == 1) { // Turning left - gameState->mountainPosition += scrollSpeed; - if (gameState->mountainPosition >= mountainWidth) { - gameState->mountainPosition -= mountainWidth; - } - } else if (gameState->current_curve_direction == -1) { // Turning right - gameState->mountainPosition -= scrollSpeed; - if (gameState->mountainPosition <= -mountainWidth) { - gameState->mountainPosition += mountainWidth; - } - } -} - -void renderMountains(GameState* gameState) { - int mountainIndex = gameState->mountainIndices[gameState->currentBackgroundIndex]; - Rectangle srcRect = asset_map[mountainIndex]; - int mountainWidth = srcRect.width; - int mountainY = 45; // Y position per original environment - - float playerCenterX = (PLAYER_MIN_X + PLAYER_MAX_X) / 2.0f; - float playerOffset = gameState->player_x - playerCenterX; - float parallaxFactor = 0.5f; - float adjustedOffset = -playerOffset * parallaxFactor; - float mountainX = -gameState->mountainPosition + adjustedOffset; - - BeginScissorMode(PLAYABLE_AREA_LEFT, 0, SCREEN_WIDTH - PLAYABLE_AREA_LEFT, SCREEN_HEIGHT); - for (int x = (int)mountainX; x < SCREEN_WIDTH; x += mountainWidth) { - DrawTextureRec(gameState->spritesheet, srcRect, (Vector2){x, mountainY}, WHITE); - } - for (int x = (int)mountainX - mountainWidth; x > -mountainWidth; x -= mountainWidth) { - DrawTextureRec(gameState->spritesheet, srcRect, (Vector2){x, mountainY}, WHITE); - } - EndScissorMode(); -} - -void c_render(Enduro* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - // Client* client = env->client; - GameState* gameState = &env->client->gameState; - - // Copy env state to gameState - gameState->speed = env->speed; - gameState->min_speed = env->min_speed; - gameState->max_speed = env->max_speed; - gameState->current_curve_direction = env->current_curve_direction; - gameState->current_curve_factor = env->current_curve_factor; - gameState->player_x = env->player_x; - gameState->player_y = env->player_y; - gameState->initial_player_x = env->initial_player_x; - gameState->vanishing_point_x = env->vanishing_point_x; - gameState->t_p = env->t_p; - gameState->dayCompleted = env->dayCompleted; - gameState->currentBackgroundIndex = env->currentDayTimeIndex; - gameState->carsLeftGameState = env->carsToPass; - gameState->day = env->day; - gameState->elapsedTime = env->elapsedTimeEnv; - // -1 represents reset of env - if (env->score == 0) { - gameState->score = 0; - } - - // Render to a texture for scaling up - BeginTextureMode(gameState->renderTarget); - - // Do not call BeginDrawing() here - ClearBackground(BLACK); - BeginBlendMode(BLEND_ALPHA); - - renderBackground(gameState); - updateCarAnimation(gameState); - updateMountains(gameState); - renderMountains(gameState); - - int bgIndex = gameState->currentBackgroundIndex; - unsigned char isNightFogStage = (bgIndex == 13); - unsigned char isNightStage = (bgIndex == 12 || bgIndex == 13 || bgIndex == 14); - - // During night fog stage, clip rendering to y >= 92 - float clipStartY = isNightFogStage ? 92.0f : VANISHING_POINT_Y; - float clipHeight = PLAYABLE_AREA_BOTTOM - clipStartY; - Rectangle clipRect = { PLAYABLE_AREA_LEFT, clipStartY, PLAYABLE_AREA_RIGHT - PLAYABLE_AREA_LEFT, clipHeight }; - BeginScissorMode(clipRect.x, clipRect.y, clipRect.width, clipRect.height); - - // Render road edges - float roadStartY = isNightFogStage ? 92.0f : VANISHING_POINT_Y; - Vector2 previousLeftPoint = {0}, previousRightPoint = {0}; - unsigned char firstPoint = true; - - for (float y = roadStartY; y <= PLAYABLE_AREA_BOTTOM; y += 0.75f) { - float adjusted_y = (env->speed < 0) ? y : y + fmod(env->road_scroll_offset, 0.75f); - if (adjusted_y > PLAYABLE_AREA_BOTTOM) continue; - - float left_edge = road_edge_x(env, adjusted_y, 0, true); - float right_edge = road_edge_x(env, adjusted_y, 0, false); - - // Road color based on y position - Color roadColor; - if (adjusted_y >= 52 && adjusted_y < 91) { - roadColor = (Color){74, 74, 74, 255}; - } else if (adjusted_y >= 91 && adjusted_y < 106) { - roadColor = (Color){111, 111, 111, 255}; - } else if (adjusted_y >= 106 && adjusted_y <= 154) { - roadColor = (Color){170, 170, 170, 255}; - } else { - roadColor = WHITE; - } - - Vector2 currentLeftPoint = {left_edge, adjusted_y}; - Vector2 currentRightPoint = {right_edge, adjusted_y}; - - if (!firstPoint) { - DrawLineV(previousLeftPoint, currentLeftPoint, roadColor); - DrawLineV(previousRightPoint, currentRightPoint, roadColor); - } - - previousLeftPoint = currentLeftPoint; - previousRightPoint = currentRightPoint; - firstPoint = false; - } - - // Render enemy cars scaled stages for distance/closeness effect - unsigned char skipFogCars = isNightFogStage; - for (int i = 0; i < env->numEnemies; i++) { - Car* car = &env->enemyCars[i]; - - // Don't render cars in fog - if (skipFogCars && car->y < 92.0f) { - continue; - } - - // Determine the car scale based on distance - float car_scale = get_car_scale(car->y); - // Select the correct texture based on the car's color and current tread - int carAssetIndex; - if (isNightStage) { - carAssetIndex = (bgIndex == 13) ? gameState->enemyCarNightFogTailLightsIndex : gameState->enemyCarNightTailLightsIndex; - } else { - int colorIndex = car->colorIndex; - int treadIndex = gameState->showLeftTread ? 0 : 1; - carAssetIndex = gameState->enemyCarIndices[colorIndex][treadIndex]; - } - Rectangle srcRect = asset_map[carAssetIndex]; - - // Compute car coordinates - float car_center_x = car_x_in_lane(env, car->lane, car->y); - float car_x = car_center_x - (srcRect.width * car_scale) / 2.0f; - float car_y = car->y - (srcRect.height * car_scale) / 2.0f; - - DrawTexturePro( - gameState->spritesheet, - srcRect, - (Rectangle){ car_x, car_y, srcRect.width * car_scale, srcRect.height * car_scale }, - (Vector2){ 0, 0 }, - 0.0f, - WHITE - ); - } - - render_car(gameState); // Unscaled player car - - EndScissorMode(); - EndBlendMode(); - updateVictoryEffects(gameState); - updateScoreboard(gameState); - renderScoreboard(gameState); - - // Update GameState data from environment data; - env->client->gameState.victoryAchieved = env->dayCompleted; - - // Finish rendering to the texture - EndTextureMode(); - - // Now render the scaled-up texture to the screen - BeginDrawing(); - ClearBackground(BLACK); - - // Draw the render texture scaled up - DrawTexturePro( - gameState->renderTarget.texture, - (Rectangle){ 0, 0, (float)gameState->renderTarget.texture.width, -(float)gameState->renderTarget.texture.height }, - (Rectangle){ 0, 0, (float)SCREEN_WIDTH * 2, (float)SCREEN_HEIGHT * 2 }, - (Vector2){ 0, 0 }, - 0.0f, - WHITE - ); - - EndDrawing(); -} diff --git a/pufferlib/ocean/enduro/enduro.py b/pufferlib/ocean/enduro/enduro.py deleted file mode 100644 index 286e8219e..000000000 --- a/pufferlib/ocean/enduro/enduro.py +++ /dev/null @@ -1,81 +0,0 @@ -import random -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.enduro import binding - -class Enduro(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, - width=152, height=210, car_width=16, car_height=11, - max_enemies=10, frameskip=1, continuous=False, - log_interval=128, buf=None, seed=None): - - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=1, shape=(8 + (5 * max_enemies) + 9 + 1,), dtype=np.float32 - ) - # Example: 9 discrete actions - self.single_action_space = gymnasium.spaces.Discrete(9) - - self.render_mode = render_mode - self.num_agents = num_envs - self.continuous = continuous - self.log_interval = log_interval - self.human_action = None - self.tick = 0 - if seed is None: - self.seed = random.randint(1, 1000000) - else: - self.seed = 0 - super().__init__(buf) - - self.c_envs = binding.vec_init( - self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, self.seed, - width=width, height=height, car_width=car_width, - car_height=car_height, max_enemies=max_enemies, - frameskip=frameskip, continuous=continuous - ) - - def reset(self, seed=None): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = Enduro(num_envs=2) - env.reset(env.seed) - tick = 0 - - actions = np.random.randint(0, env.single_action_space.n, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: {env.num_agents * tick / (time.time() - start)}') - -if __name__ == '__main__': - test_performance() \ No newline at end of file diff --git a/pufferlib/ocean/env_binding.h b/pufferlib/ocean/env_binding.h index f64b6148b..f11ea0f39 100644 --- a/pufferlib/ocean/env_binding.h +++ b/pufferlib/ocean/env_binding.h @@ -1,3 +1,4 @@ +#include "env_config.h" #include #include @@ -134,15 +135,15 @@ static PyObject* env_init(PyObject* self, PyObject* args, PyObject* kwargs) { return NULL; } // env->truncations = PyArray_DATA(truncations); - - + + PyObject* seed_arg = PyTuple_GetItem(args, 5); if (!PyObject_TypeCheck(seed_arg, &PyLong_Type)) { PyErr_SetString(PyExc_TypeError, "seed must be an integer"); return NULL; } int seed = PyLong_AsLong(seed_arg); - + // Assumes each process has the same number of environments srand(seed); @@ -410,10 +411,10 @@ static PyObject* vec_init(PyObject* self, PyObject* args, PyObject* kwargs) { return NULL; } vec->envs[i] = env; - + // // Make sure the log is initialized to 0 memset(&env->log, 0, sizeof(Log)); - + env->observations = (void*)((char*)PyArray_DATA(observations) + i*PyArray_STRIDE(observations, 0)); env->actions = (void*)((char*)PyArray_DATA(actions) + i*PyArray_STRIDE(actions, 0)); env->rewards = (void*)((char*)PyArray_DATA(rewards) + i*PyArray_STRIDE(rewards, 0)); @@ -423,7 +424,7 @@ static PyObject* vec_init(PyObject* self, PyObject* args, PyObject* kwargs) { // Assumes each process has the same number of environments int env_seed = i + seed*vec->num_envs; srand(env_seed); - + // Add the seed to kwargs for this environment PyObject* py_seed = PyLong_FromLong(env_seed); if (PyDict_SetItemString(kwargs, "seed", py_seed) < 0) { @@ -496,7 +497,7 @@ static PyObject* vec_reset(PyObject* self, PyObject* args) { return NULL; } int seed = PyLong_AsLong(seed_arg); - + for (int i = 0; i < vec->num_envs; i++) { // Assumes each process has the same number of environments srand(i + seed*vec->num_envs); @@ -542,7 +543,7 @@ static PyObject* vec_render(PyObject* self, PyObject* args) { return NULL; } int env_id = PyLong_AsLong(env_id_arg); - + c_render(vec->envs[env_id]); Py_RETURN_NONE; } @@ -612,6 +613,214 @@ static PyObject* vec_close(PyObject* self, PyObject* args) { Py_RETURN_NONE; } +static PyObject* get_global_agent_state(PyObject* self, PyObject* args) { + if (PyTuple_Size(args) != 5) { + PyErr_SetString(PyExc_TypeError, "get_global_agent_state requires 5 arguments"); + return NULL; + } + + Env* env = unpack_env(args); + if (!env) { + return NULL; + } + + Drive* drive = (Drive*)env; // Cast to Drive* + + // Get the numpy arrays from arguments + PyObject* x_arr = PyTuple_GetItem(args, 1); + PyObject* y_arr = PyTuple_GetItem(args, 2); + PyObject* z_arr = PyTuple_GetItem(args, 3); + PyObject* heading_arr = PyTuple_GetItem(args, 4); + PyObject* id_arr = PyTuple_GetItem(args, 5); + + if (!PyArray_Check(x_arr) || !PyArray_Check(y_arr) || + !PyArray_Check(z_arr) || !PyArray_Check(heading_arr) || + !PyArray_Check(id_arr)) { + PyErr_SetString(PyExc_TypeError, "All output arrays must be NumPy arrays"); + return NULL; + } + + float* x_data = (float*)PyArray_DATA((PyArrayObject*)x_arr); + float* y_data = (float*)PyArray_DATA((PyArrayObject*)y_arr); + float* z_data = (float*)PyArray_DATA((PyArrayObject*)z_arr); + float* heading_data = (float*)PyArray_DATA((PyArrayObject*)heading_arr); + int* id_data = (int*)PyArray_DATA((PyArrayObject*)id_arr); + + c_get_global_agent_state(drive, x_data, y_data, z_data, heading_data, id_data); + + Py_RETURN_NONE; +} +static PyObject* vec_get_global_agent_state(PyObject* self, PyObject* args) { + if (PyTuple_Size(args) != 6) { + PyErr_SetString(PyExc_TypeError, "vec_get_global_agent_state requires 6 arguments"); + return NULL; + } + + VecEnv* vec = unpack_vecenv(args); + if (!vec) { + return NULL; + } + + // Get the numpy arrays from arguments + PyObject* x_arr = PyTuple_GetItem(args, 1); + PyObject* y_arr = PyTuple_GetItem(args, 2); + PyObject* z_arr = PyTuple_GetItem(args, 3); + PyObject* heading_arr = PyTuple_GetItem(args, 4); + PyObject* id_arr = PyTuple_GetItem(args, 5); + + if (!PyArray_Check(x_arr) || !PyArray_Check(y_arr) || + !PyArray_Check(z_arr) || !PyArray_Check(heading_arr) || + !PyArray_Check(id_arr)) { + PyErr_SetString(PyExc_TypeError, "All output arrays must be NumPy arrays"); + return NULL; + } + + PyArrayObject* x_array = (PyArrayObject*)x_arr; + PyArrayObject* y_array = (PyArrayObject*)y_arr; + PyArrayObject* z_array = (PyArrayObject*)z_arr; + PyArrayObject* heading_array = (PyArrayObject*)heading_arr; + PyArrayObject* id_array = (PyArrayObject*)id_arr; + + // Get base pointers to the arrays + float* x_base = (float*)PyArray_DATA(x_array); + float* y_base = (float*)PyArray_DATA(y_array); + float* z_base = (float*)PyArray_DATA(z_array); + float* heading_base = (float*)PyArray_DATA(heading_array); + int* id_base = (int*)PyArray_DATA(id_array); + + // Iterate through environments and write to correct offsets + int offset = 0; + for (int i = 0; i < vec->num_envs; i++) { + Drive* drive = (Drive*)vec->envs[i]; + + // Write to the arrays at the current offset + c_get_global_agent_state(drive, + &x_base[offset], + &y_base[offset], + &z_base[offset], + &heading_base[offset], + &id_base[offset]); + + // Move offset forward by the number of agents in this environment + offset += drive->active_agent_count; + } + + Py_RETURN_NONE; +} + +static PyObject* get_ground_truth_trajectories(PyObject* self, PyObject* args) { + if (PyTuple_Size(args) != 7) { + PyErr_SetString(PyExc_TypeError, "get_ground_truth_trajectories requires 7 arguments"); + return NULL; + } + + Env* env = unpack_env(args); + if (!env) { + return NULL; + } + + Drive* drive = (Drive*)env; + + // Get the numpy arrays from arguments + PyObject* x_arr = PyTuple_GetItem(args, 1); + PyObject* y_arr = PyTuple_GetItem(args, 2); + PyObject* z_arr = PyTuple_GetItem(args, 3); + PyObject* heading_arr = PyTuple_GetItem(args, 4); + PyObject* valid_arr = PyTuple_GetItem(args, 5); + PyObject* id_arr = PyTuple_GetItem(args, 6); + PyObject* scenario_id_arr = PyTuple_GetItem(args, 7); + + if (!PyArray_Check(x_arr) || !PyArray_Check(y_arr) || + !PyArray_Check(z_arr) || !PyArray_Check(heading_arr) || + !PyArray_Check(valid_arr) || !PyArray_Check(id_arr) || !PyArray_Check(scenario_id_arr)) { + PyErr_SetString(PyExc_TypeError, "All output arrays must be NumPy arrays"); + return NULL; + } + + float* x_data = (float*)PyArray_DATA((PyArrayObject*)x_arr); + float* y_data = (float*)PyArray_DATA((PyArrayObject*)y_arr); + float* z_data = (float*)PyArray_DATA((PyArrayObject*)z_arr); + float* heading_data = (float*)PyArray_DATA((PyArrayObject*)heading_arr); + int* valid_data = (int*)PyArray_DATA((PyArrayObject*)valid_arr); + int* id_data = (int*)PyArray_DATA((PyArrayObject*)id_arr); + int* scenario_id_data = (int*)PyArray_DATA((PyArrayObject*)scenario_id_arr); + + c_get_global_ground_truth_trajectories(drive, x_data, y_data, z_data, heading_data, valid_data, id_data, scenario_id_data); + + Py_RETURN_NONE; +} + +static PyObject* vec_get_global_ground_truth_trajectories(PyObject* self, PyObject* args) { + if (PyTuple_Size(args) != 8) { + PyErr_SetString(PyExc_TypeError, "vec_get_global_ground_truth_trajectories requires 8 arguments"); + return NULL; + } + + VecEnv* vec = unpack_vecenv(args); + if (!vec) { + return NULL; + } + + // Get the numpy arrays from arguments + PyObject* x_arr = PyTuple_GetItem(args, 1); + PyObject* y_arr = PyTuple_GetItem(args, 2); + PyObject* z_arr = PyTuple_GetItem(args, 3); + PyObject* heading_arr = PyTuple_GetItem(args, 4); + PyObject* valid_arr = PyTuple_GetItem(args, 5); + PyObject* id_arr = PyTuple_GetItem(args, 6); + PyObject* scenario_id_arr = PyTuple_GetItem(args, 7); + + if (!PyArray_Check(x_arr) || !PyArray_Check(y_arr) || + !PyArray_Check(z_arr) || !PyArray_Check(heading_arr) || + !PyArray_Check(valid_arr) || !PyArray_Check(id_arr) || !PyArray_Check(scenario_id_arr)) { + PyErr_SetString(PyExc_TypeError, "All output arrays must be NumPy arrays"); + return NULL; + } + + PyArrayObject* x_array = (PyArrayObject*)x_arr; + PyArrayObject* y_array = (PyArrayObject*)y_arr; + PyArrayObject* z_array = (PyArrayObject*)z_arr; + PyArrayObject* heading_array = (PyArrayObject*)heading_arr; + PyArrayObject* valid_array = (PyArrayObject*)valid_arr; + PyArrayObject* id_array = (PyArrayObject*)id_arr; + PyArrayObject* scenario_id_array = (PyArrayObject*)scenario_id_arr; + + // Get base pointers to the arrays + float* x_base = (float*)PyArray_DATA(x_array); + float* y_base = (float*)PyArray_DATA(y_array); + float* z_base = (float*)PyArray_DATA(z_array); + float* heading_base = (float*)PyArray_DATA(heading_array); + int* valid_base = (int*)PyArray_DATA(valid_array); + int* id_base = (int*)PyArray_DATA(id_array); + int* scenario_id_base = (int*)PyArray_DATA(scenario_id_array); + + // Get number of timesteps from array shape + npy_intp* x_shape = PyArray_DIMS(x_array); + int num_timesteps = x_shape[1]; // Second dimension for 2D arrays + + // Iterate through environments and write to correct offsets + int agent_offset = 0; // Offset for 1D arrays (id, scenario_id) + int traj_offset = 0; // Offset for 2D arrays (x, y, z, heading, valid) + + for (int i = 0; i < vec->num_envs; i++) { + Drive* drive = (Drive*)vec->envs[i]; + + c_get_global_ground_truth_trajectories(drive, + &x_base[traj_offset], + &y_base[traj_offset], + &z_base[traj_offset], + &heading_base[traj_offset], + &valid_base[traj_offset], + &id_base[agent_offset], + &scenario_id_base[agent_offset]); + + // Move offsets forward + agent_offset += drive->active_agent_count; + traj_offset += drive->active_agent_count * num_timesteps; + } + + Py_RETURN_NONE; +} static double unpack(PyObject* kwargs, char* key) { PyObject* val = PyDict_GetItemString(kwargs, key); if (val == NULL) { @@ -640,6 +849,32 @@ static double unpack(PyObject* kwargs, char* key) { return 1; } +static char* unpack_str(PyObject* kwargs, char* key) { + PyObject* val = PyDict_GetItemString(kwargs, key); + if (val == NULL) { + char error_msg[100]; + snprintf(error_msg, sizeof(error_msg), "Missing required keyword argument '%s'", key); + PyErr_SetString(PyExc_TypeError, error_msg); + return NULL; + } + if (!PyUnicode_Check(val)) { + char error_msg[100]; + snprintf(error_msg, sizeof(error_msg), "Keyword argument '%s' must be a string", key); + PyErr_SetString(PyExc_TypeError, error_msg); + return NULL; + } + const char* str_val = PyUnicode_AsUTF8(val); + if (str_val == NULL) { + // PyUnicode_AsUTF8 sets an error on failure + return NULL; + } + char* ret = strdup(str_val); + if (ret == NULL) { + PyErr_SetString(PyExc_MemoryError, "strdup failed in unpack_str"); + } + return ret; +} + // Method table static PyMethodDef methods[] = { {"env_init", (PyCFunction)env_init, METH_VARARGS | METH_KEYWORDS, "Init environment with observation, action, reward, terminal, truncation arrays"}, @@ -657,6 +892,10 @@ static PyMethodDef methods[] = { {"vec_render", vec_render, METH_VARARGS, "Render the vector of environments"}, {"vec_close", vec_close, METH_VARARGS, "Close the vector of environments"}, {"shared", (PyCFunction)my_shared, METH_VARARGS | METH_KEYWORDS, "Shared state"}, + {"get_global_agent_state", get_global_agent_state, METH_VARARGS, "Get global agent state"}, + {"vec_get_global_agent_state", vec_get_global_agent_state, METH_VARARGS, "Get agent state from vectorized env"}, + {"get_ground_truth_trajectories", get_ground_truth_trajectories, METH_VARARGS, "Get ground truth trajectories"}, + {"vec_get_global_ground_truth_trajectories", vec_get_global_ground_truth_trajectories, METH_VARARGS, "Get ground truth trajectories from vectorized env"}, MY_METHODS, {NULL, NULL, 0, NULL} }; diff --git a/pufferlib/ocean/env_config.h b/pufferlib/ocean/env_config.h new file mode 100644 index 000000000..e8400c51c --- /dev/null +++ b/pufferlib/ocean/env_config.h @@ -0,0 +1,98 @@ +#ifndef ENV_CONFIG_H +#define ENV_CONFIG_H + +#include <../../inih-r62/ini.h> +#include +#include +#include + +// Config struct for parsing INI files - contains all environment configuration +typedef struct +{ + int action_type; + int dynamics_model; + float reward_vehicle_collision; + float reward_offroad_collision; + float reward_goal; + float reward_goal_post_respawn; + float reward_vehicle_collision_post_respawn; + float reward_ade; + float goal_radius; + int collision_behavior; + int offroad_behavior; + int spawn_immunity_timer; + float dt; + int goal_behavior; + int scenario_length; + int init_steps; + int init_mode; + int control_mode; +} env_init_config; + +// INI file parser handler - parses all environment configuration from drive.ini +static int handler( + void* config, + const char* section, + const char* name, + const char* value +) { + env_init_config* env_config = (env_init_config*)config; + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + + if (MATCH("env", "action_type")) { + if (strcmp(value, "\"discrete\"") == 0 ||strcmp(value, "discrete") == 0) { + env_config->action_type = 0; // DISCRETE + } else if (strcmp(value, "\"continuous\"") == 0 || strcmp(value, "continuous") == 0) { + env_config->action_type = 1; // CONTINUOUS + } else { + printf("Warning: Unknown action_type value '%s', defaulting to DISCRETE\n", value); + env_config->action_type = 0; // Default to DISCRETE + } + } else if (MATCH("env", "dynamics_model")) { + if (strcmp(value, "\"classic\"") == 0 || strcmp(value, "classic") == 0) { + env_config->dynamics_model = 0; // CLASSIC + } else if (strcmp(value, "\"jerk\"") == 0 || strcmp(value, "jerk") == 0) { + env_config->dynamics_model = 1; // JERK + } else { + printf("Warning: Unknown dynamics_model value '%s', defaulting to JERK\n", value); + env_config->dynamics_model = 1; // Default to JERK + } + } else if (MATCH("env", "goal_behavior")) { + env_config->goal_behavior = atoi(value); + } else if (MATCH("env", "reward_vehicle_collision")) { + env_config->reward_vehicle_collision = atof(value); + } else if (MATCH("env", "reward_offroad_collision")) { + env_config->reward_offroad_collision = atof(value); + } else if (MATCH("env", "reward_goal")) { + env_config->reward_goal = atof(value); + } else if (MATCH("env", "reward_goal_post_respawn")) { + env_config->reward_goal_post_respawn = atof(value); + } else if (MATCH("env", "reward_vehicle_collision_post_respawn")) { + env_config->reward_vehicle_collision_post_respawn = atof(value); + } else if (MATCH("env", "reward_ade")) { + env_config->reward_ade = atof(value); + } else if (MATCH("env", "goal_radius")) { + env_config->goal_radius = atof(value); + } else if(MATCH("env", "collision_behavior")){ + env_config->collision_behavior = atoi(value); + } else if(MATCH("env", "offroad_behavior")){ + env_config->offroad_behavior = atoi(value); + } else if (MATCH("env", "spawn_immunity_timer")) { + env_config->spawn_immunity_timer = atoi(value); + } else if (MATCH("env", "dt")) { + env_config->dt = atof(value); + } else if (MATCH("env", "scenario_length")) { + env_config->scenario_length = atoi(value); + } else if (MATCH("env", "init_steps")) { + env_config->init_steps = atoi(value); + } else if (MATCH("env", "init_mode")) { + env_config->init_mode = atoi(value); + } else if (MATCH("env", "control_mode")) { + env_config->control_mode = atoi(value); + } + + #undef MATCH + return 1; +} + +#endif // ENV_CONFIG_H diff --git a/pufferlib/ocean/environment.py b/pufferlib/ocean/environment.py index 24c0e7193..feba8bead 100644 --- a/pufferlib/ocean/environment.py +++ b/pufferlib/ocean/environment.py @@ -1,6 +1,7 @@ import importlib import pufferlib.emulation + def lazy_import(module_path, attr): """ Returns a callable that, when called with any arguments, will @@ -9,165 +10,190 @@ def lazy_import(module_path, attr): """ return lambda *args, **kwargs: getattr(__import__(module_path, fromlist=[attr]), attr)(*args, **kwargs) -def make_foraging(width=1080, height=720, num_agents=4096, horizon=512, - discretize=True, food_reward=0.1, render_mode='rgb_array'): + +def make_foraging( + width=1080, height=720, num_agents=4096, horizon=512, discretize=True, food_reward=0.1, render_mode="rgb_array" +): from .grid import grid + init_fn = grid.init_foraging reward_fn = grid.reward_foraging - return grid.PufferGrid(width, height, num_agents, - horizon, discretize=discretize, food_reward=food_reward, init_fn=init_fn, reward_fn=reward_fn, render_mode=render_mode) - -def make_predator_prey(width=1080, height=720, num_agents=4096, horizon=512, - discretize=True, food_reward=0.1, render_mode='rgb_array'): + return grid.PufferGrid( + width, + height, + num_agents, + horizon, + discretize=discretize, + food_reward=food_reward, + init_fn=init_fn, + reward_fn=reward_fn, + render_mode=render_mode, + ) + + +def make_predator_prey( + width=1080, height=720, num_agents=4096, horizon=512, discretize=True, food_reward=0.1, render_mode="rgb_array" +): from .grid import grid + init_fn = grid.init_predator_prey reward_fn = grid.reward_predator_prey - return grid.PufferGrid(width, height, num_agents, - horizon, discretize=discretize, food_reward=food_reward, - init_fn=init_fn, reward_fn=reward_fn, - render_mode=render_mode) - -def make_group(width=1080, height=720, num_agents=4096, horizon=512, - discretize=True, food_reward=0.1, render_mode='rgb_array'): + return grid.PufferGrid( + width, + height, + num_agents, + horizon, + discretize=discretize, + food_reward=food_reward, + init_fn=init_fn, + reward_fn=reward_fn, + render_mode=render_mode, + ) + + +def make_group( + width=1080, height=720, num_agents=4096, horizon=512, discretize=True, food_reward=0.1, render_mode="rgb_array" +): from .grid import grid + init_fn = grid.init_group reward_fn = grid.reward_group - return grid.PufferGrid(width, height, num_agents, - horizon, discretize=discretize, food_reward=food_reward, - init_fn=init_fn, reward_fn=reward_fn, - render_mode=render_mode) - -def make_puffer(width=1080, height=720, num_agents=4096, horizon=512, - discretize=True, food_reward=0.1, render_mode='rgb_array'): + return grid.PufferGrid( + width, + height, + num_agents, + horizon, + discretize=discretize, + food_reward=food_reward, + init_fn=init_fn, + reward_fn=reward_fn, + render_mode=render_mode, + ) + + +def make_puffer( + width=1080, height=720, num_agents=4096, horizon=512, discretize=True, food_reward=0.1, render_mode="rgb_array" +): from .grid import grid + init_fn = grid.init_puffer reward_fn = grid.reward_puffer - return grid.PufferGrid(width, height, num_agents, - horizon, discretize=discretize, food_reward=food_reward, - init_fn=init_fn, reward_fn=reward_fn, - render_mode=render_mode) + return grid.PufferGrid( + width, + height, + num_agents, + horizon, + discretize=discretize, + food_reward=food_reward, + init_fn=init_fn, + reward_fn=reward_fn, + render_mode=render_mode, + ) + + +def make_puffergrid( + render_mode="raylib", vision_range=5, num_envs=4096, num_maps=1000, max_map_size=9, report_interval=128, buf=None +): + return PufferGrid(render_mode, vision_range, num_envs, num_maps, max_map_size, report_interval, buf) -def make_puffergrid(render_mode='raylib', vision_range=5, - num_envs=4096, num_maps=1000, max_map_size=9, - report_interval=128, buf=None): - return PufferGrid(render_mode, vision_range, num_envs, - num_maps, max_map_size, report_interval, buf) def make_continuous(discretize=False, buf=None, **kwargs): from . import sanity + env = sanity.Continuous(discretize=discretize) if not discretize: env = pufferlib.ClipAction(env) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) + def make_squared(distance_to_target=3, num_targets=1, buf=None, **kwargs): from . import sanity + env = sanity.Squared(distance_to_target=distance_to_target, num_targets=num_targets, **kwargs) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf, **kwargs) + def make_bandit(num_actions=10, reward_scale=1, reward_noise=1, buf=None): from . import sanity - env = sanity.Bandit(num_actions=num_actions, reward_scale=reward_scale, - reward_noise=reward_noise) + + env = sanity.Bandit(num_actions=num_actions, reward_scale=reward_scale, reward_noise=reward_noise) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) + def make_memory(mem_length=2, mem_delay=2, buf=None, **kwargs): from . import sanity + env = sanity.Memory(mem_length=mem_length, mem_delay=mem_delay) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) + def make_password(password_length=5, buf=None, **kwargs): from . import sanity + env = sanity.Password(password_length=password_length) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) + def make_performance(delay_mean=0, delay_std=0, bandwidth=1, buf=None, **kwargs): from . import sanity + env = sanity.Performance(delay_mean=delay_mean, delay_std=delay_std, bandwidth=bandwidth) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) + def make_performance_empiric(count_n=0, count_std=0, bandwidth=1, buf=None, **kwargs): from . import sanity + env = sanity.PerformanceEmpiric(count_n=count_n, count_std=count_std, bandwidth=bandwidth) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) + def make_stochastic(p=0.7, horizon=100, buf=None, **kwargs): from . import sanity + env = sanity.Stochastic(p=p, horizon=100) env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf) + def make_spaces(buf=None, **kwargs): from . import sanity + env = sanity.Spaces() env = pufferlib.EpisodeStats(env) return pufferlib.emulation.GymnasiumPufferEnv(env=env, buf=buf, **kwargs) + def make_multiagent(buf=None, **kwargs): from . import sanity + env = sanity.Multiagent() env = pufferlib.MultiagentEpisodeStats(env) return pufferlib.emulation.PettingZooPufferEnv(env=env, buf=buf) + MAKE_FUNCTIONS = { - 'battle': 'Battle', - 'breakout': 'Breakout', - 'blastar': 'Blastar', - 'convert': 'Convert', - 'convert_circle': 'ConvertCircle', - 'pong': 'Pong', - 'freeway': 'Freeway', - 'enduro': 'Enduro', - 'tetris': 'Tetris', - 'cartpole': 'Cartpole', - 'moba': 'Moba', - 'matsci': 'Matsci', - 'memory': 'Memory', - 'boids': 'Boids', - 'drone_race': 'DroneRace', - 'drone_swarm': 'DroneSwarm', - 'nmmo3': 'NMMO3', - 'snake': 'Snake', - 'squared': 'Squared', - 'pysquared': 'PySquared', - 'connect4': 'Connect4', - 'g2048': 'G2048', - 'terraform': 'Terraform', - 'template': 'Template', - 'tripletriad': 'TripleTriad', - 'tactical': 'Tactical', - 'target': 'Target', - 'go': 'Go', - 'rware': 'Rware', - 'trash_pickup': 'TrashPickupEnv', - 'tower_climb': 'TowerClimb', - 'grid': 'Grid', - 'cpr': 'PyCPR', - 'impulse_wars': 'ImpulseWars', - 'drive': 'Drive', - 'pacman': 'Pacman', - 'checkers': 'Checkers', - 'asteroids': 'Asteroids', - 'whisker_racer': 'WhiskerRacer', - 'spaces': make_spaces, - 'multiagent': make_multiagent, + "drive": "Drive", + "spaces": make_spaces, + "multiagent": make_multiagent, } -def env_creator(name='squared', *args, **kwargs): - if 'puffer_' not in name: - raise pufferlib.APIUsageError(f'Invalid environment name: {name}') + +def env_creator(name="squared", *args, **kwargs): + if "puffer_" not in name: + raise pufferlib.APIUsageError(f"Invalid environment name: {name}") # TODO: Robust sanity / ocean imports - name = name.replace('puffer_', '') + name = name.replace("puffer_", "") try: - module = importlib.import_module(f'pufferlib.ocean.{name}.{name}') + module = importlib.import_module(f"pufferlib.ocean.{name}.{name}") return getattr(module, MAKE_FUNCTIONS[name]) except ModuleNotFoundError: return MAKE_FUNCTIONS[name] diff --git a/pufferlib/ocean/freeway/binding.c b/pufferlib/ocean/freeway/binding.c deleted file mode 100644 index 42eea05bb..000000000 --- a/pufferlib/ocean/freeway/binding.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "freeway.h" - -#define Env Freeway -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->frameskip = unpack(kwargs, "frameskip"); - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->player_width = unpack(kwargs, "player_width"); - env->player_height = unpack(kwargs, "player_height"); - env->car_width = unpack(kwargs, "car_width"); - env->car_height = unpack(kwargs, "car_height"); - env->lane_size = unpack(kwargs, "lane_size"); - env->level = unpack(kwargs, "level"); - env->difficulty = unpack(kwargs, "difficulty"); - env->use_dense_rewards = unpack(kwargs, "use_dense_rewards"); - env->env_randomization = unpack(kwargs, "env_randomization"); - env->enable_human_player = unpack(kwargs, "enable_human_player"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "up_action_frac", log->up_action_frac); - assign_to_dict(dict, "hits", log->hits); - return 0; -} diff --git a/pufferlib/ocean/freeway/freeway.c b/pufferlib/ocean/freeway/freeway.c deleted file mode 100644 index f36f2f9c8..000000000 --- a/pufferlib/ocean/freeway/freeway.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include "freeway.h" -#include "puffernet.h" -#include - -int main() { - Weights* weights = load_weights("resources/freeway/freeway_weights.bin", 137092); - int logit_sizes[1] = {3}; - LinearLSTM* net = make_linearlstm(weights, 1, 34, logit_sizes, 1); - - Freeway env = { - .frameskip=4, - .width=1216, - .height=720, - .player_width=64, - .player_height=64, - .car_width=64, - .car_height=40, - .lane_size=64, - .difficulty=0, - .level=4, - .use_dense_rewards=1, - .env_randomization=1, - .enable_human_player=1, - }; - allocate(&env); - - env.client = make_client(&env); - - c_reset(&env); - while (!WindowShouldClose()) { - forward_linearlstm(net, env.observations, env.actions); - env.human_actions[0] = 0; - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) env.human_actions[0] = 1; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) env.human_actions[0] = 2; - c_step(&env); - c_render(&env); - - } - free_allocated(&env); - close_client(env.client); -} diff --git a/pufferlib/ocean/freeway/freeway.h b/pufferlib/ocean/freeway/freeway.h deleted file mode 100644 index c132fcab6..000000000 --- a/pufferlib/ocean/freeway/freeway.h +++ /dev/null @@ -1,622 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" -#include "freeway_levels.h" - -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -#define NOOP 0 -#define UP 1 -#define DOWN 2 - -// Gameplay related -#define TICK_RATE 1.0f/60.0f -#define GAME_LENGTH 136.0f // Game length in seconds -#define RANDOMIZE_SPEED_FREQ 360 // How many ticks before randomize the speed of the enemies -#define TICKS_STUNT 40 -#define PENALTY_HIT -0.01f // Penalty for hitting an enemy -// Rendering related -#define HALF_LINEWIDTH 2 -#define DASH_SPACING 32 -#define DASH_SIZE 32 - -// Based on https://ale.farama.org/environments/freeway/ -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float up_action_frac; - float hits; - float n; -}; - -typedef struct FreewayPlayer FreewayPlayer; -struct FreewayPlayer { - float player_x; - float player_y; - int best_lane_idx;// furthest lane achieved so far - int ticks_stunts_left; - int score; - int hits; - int is_human; -}; - -typedef struct FreewayEnemy FreewayEnemy; -struct FreewayEnemy { - float enemy_x; - float enemy_y; - float enemy_initial_x; - float enemy_vx; // velocity in pixels per second - int speed_randomization; // 0 for no randomization, 1 for randomization - int initial_speed_idx; // index of the initial speed in the speed array - int current_speed_idx; // index of the current speed in the speed array - int is_enabled; - int lane_idx; // lane index - int type; - int enemy_width; - int enemy_height; -}; - -typedef struct Client Client; -typedef struct Freeway Freeway; -struct Freeway { - Client* client; - Log log; - float* observations; - int* actions; - int* human_actions; - float* rewards; - unsigned char* terminals; - - FreewayPlayer ai_player; // Player-Related - FreewayPlayer human_player; - int player_width; - int player_height; - float ep_return; - int up_count; - - FreewayEnemy* enemies; // Enemy-Related - int car_width; - int car_height; - int truck_width; - int truck_height; - - int difficulty; // Global - int level; - int lane_size; - float road_start; - float road_end; - int width; - int height; - int tick; - float time_left; - int frameskip; - int use_dense_rewards; - int env_randomization; - int enable_human_player; -}; - -void load_level(Freeway* env, int level) { - FreewayEnemy* enemy; - for (int lane = 0; lane < NUM_LANES; lane++) { - for (int i = 0; i < MAX_ENEMIES_PER_LANE; i++){ - enemy = &env->enemies[lane * MAX_ENEMIES_PER_LANE + i]; - enemy->is_enabled = (i < ENEMIES_PER_LANE[level][lane]); - enemy->enemy_x = 0.0f; - enemy->enemy_initial_x = ENEMIES_INITIAL_X[level][lane][i] * env->width; - enemy->speed_randomization = SPEED_RANDOMIZATION[level]; - enemy->initial_speed_idx = ENEMIES_INITIAL_SPEED_IDX[level][lane]; - enemy->current_speed_idx = enemy->initial_speed_idx; - enemy->lane_idx = lane; - enemy->enemy_y = (env->road_start + (env->road_end - env->road_start) * lane / (float) NUM_LANES) - env->lane_size / 2; - enemy->type = ENEMIES_TYPES[level][lane]; - enemy->enemy_width = enemy->type == 0 ? env->car_width : env->truck_width; - enemy->enemy_height = enemy->type == 0 ? env->car_height : env->truck_height; - - enemy->enemy_vx = enemy->lane_idx < NUM_LANES/2 ? SPEED_VALUES[enemy->current_speed_idx] * TICK_RATE * env->width: -SPEED_VALUES[enemy->current_speed_idx] * TICK_RATE * env->width; - - } - } -} - -void init(Freeway* env) { - env->ai_player.player_x = env->width / 4; - env->ai_player.player_y = env->height / 2; - env->ai_player.best_lane_idx = 0; - env->ai_player.ticks_stunts_left = 0; - env->ai_player.score = 0; - env->ai_player.is_human = 0; - env->ai_player.hits = 0; - - env->human_player.player_x = 3 * env->width / 4; - env->human_player.player_y = env->height / 2; - env->human_player.best_lane_idx = 0; - env->human_player.ticks_stunts_left = 0; - env->human_player.score = 0; - env->human_player.is_human = 1; - env->human_player.hits = 0; - - - env->truck_height = env->car_height; - env->truck_width = 2*env->car_width; - env->road_start = env->height / 2 + (NUM_LANES * env->lane_size) / 2; - env->road_end = env->road_start - (NUM_LANES * env->lane_size); - //enemies - env->enemies = (FreewayEnemy*)calloc(NUM_LANES*MAX_ENEMIES_PER_LANE, sizeof(FreewayEnemy)); - env->human_actions = (int*)calloc(1, sizeof(int)); - if ((env->level < 0) || (env->level >= NUM_LEVELS)) { - env->level = rand() % NUM_LEVELS; - } - load_level(env, env->level); -} - -void allocate(Freeway* env) { - init(env); - env->observations = (float*)calloc(4 + NUM_LANES*MAX_ENEMIES_PER_LANE, sizeof(float)); - env->actions = (int*)calloc(1, sizeof(int)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void c_close(Freeway* env) { - free(env->human_actions); - free(env->enemies); -} - -void free_allocated(Freeway* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void add_log(Freeway* env) { - env->log.episode_length += env->tick; - env->log.episode_return += env->ep_return; - env->log.score += env->ai_player.score; - env->log.perf += env->ai_player.score / ((float) HUMAN_HIGH_SCORE[env->level] * (GAME_LENGTH / 136.0f )); - env->log.up_action_frac += env->up_count / (float) env->tick; - env->log.hits += env->ai_player.hits; - env->log.n += 1; -} - -void compute_observations(Freeway* env) { - env->observations[0] = env->ai_player.player_y / env->height; - env->observations[1] = env->ai_player.best_lane_idx /(float) NUM_LANES; - env->observations[2] = env->ai_player.score / (float) HUMAN_HIGH_SCORE[env->level]; - env->observations[3] = (env->ai_player.ticks_stunts_left > 0); - - FreewayEnemy* enemy; - for (int lane = 0; lane < NUM_LANES; lane++) { - for (int i = 0; i < MAX_ENEMIES_PER_LANE; i++){ - enemy = &env->enemies[lane*MAX_ENEMIES_PER_LANE + i]; - if (enemy->is_enabled){ - env->observations[4 + lane * MAX_ENEMIES_PER_LANE + i] = enemy->enemy_x / env->width; - env->observations[4 + lane * MAX_ENEMIES_PER_LANE + i] += (lane < NUM_LANES/2 ? enemy->enemy_height/(2 * env->width): -enemy->enemy_height/(2 * env->width)); - } - else { - env->observations[4 + lane * MAX_ENEMIES_PER_LANE + i] = 0.0f; - } - } - } -} - -void spawn_enemies(Freeway* env) { - float lane_offset_x; - FreewayEnemy* enemy; - for (int lane = 0; lane < NUM_LANES; lane++) { - lane_offset_x = env->width * (rand() / (float) RAND_MAX); - for (int i = 0; i < MAX_ENEMIES_PER_LANE; i++){ - enemy = &env->enemies[lane * MAX_ENEMIES_PER_LANE + i]; - if (enemy->is_enabled){ - enemy->enemy_x = enemy->enemy_initial_x; - if (lane>=NUM_LANES/2){ - enemy->enemy_x = env->width - enemy->enemy_x; - } - if (env->env_randomization){ - enemy->enemy_x += lane_offset_x; - } - } - } - } -} - -void reset_player(Freeway* env, FreewayPlayer* player) { - player->player_y = env->height - env->player_height / 2; - player->ticks_stunts_left = 0; -} - -bool check_collision(float player_min_x, float player_max_x, - float player_miny, float player_maxy, - float enemy_minx, float enemy_maxx, - float enemy_miny, float enemy_maxy) { - return (player_min_x < enemy_maxx && player_max_x > enemy_minx && - player_miny < enemy_maxy && player_maxy > enemy_miny); -} - -bool check_enemy_collisions(Freeway* env, FreewayPlayer* player){ - FreewayEnemy* enemy; - for (int lane = 0; lane < NUM_LANES; lane++) { - for (int i = 0; i < MAX_ENEMIES_PER_LANE; i++) { - enemy = &env->enemies[lane*MAX_ENEMIES_PER_LANE + i]; - if (enemy->is_enabled) { - float player_min_x = player->player_x - env->player_width / 2; - float player_max_x = player->player_x + env->player_width / 2; - float player_miny = player->player_y - env->player_height / 2; - float player_maxy = player->player_y + env->player_height / 2; - - float enemy_minx = enemy->enemy_x - enemy->enemy_width / 2; - float enemy_maxx = enemy->enemy_x + enemy->enemy_width / 2; - float enemy_miny = enemy->enemy_y - enemy->enemy_height / 2; - float enemy_maxy = enemy->enemy_y + enemy->enemy_height / 2; - - if (check_collision(player_min_x, player_max_x, player_miny, player_maxy, - enemy_minx, enemy_maxx, enemy_miny, enemy_maxy)) { - return true; - } - } - } - } - return false; -} - -void reached_end(Freeway* env, FreewayPlayer* player){ - reset_player(env, player); - player->best_lane_idx = 0; - player->score += 1; -} - -void clip_player_position(Freeway* env, FreewayPlayer* player){ - if (player->player_y <= env->player_height/2){ - player->player_y = fmaxf(env->player_height/2, player->player_y); - } else { - player->player_y = fminf(env->height - env->player_height/2, player->player_y); - } -} - -void clip_enemy_position(Freeway* env, FreewayEnemy* enemy){ - if (enemy->enemy_x > env->width + enemy->enemy_width / 2) { - enemy->enemy_x -= env->width; - } - else if (enemy->enemy_x < -enemy->enemy_width / 2){ - enemy->enemy_x += env->width; - } -} - -void randomize_enemy_speed(Freeway* env) { - FreewayEnemy* enemy; - for (int lane = 0; lane < NUM_LANES; lane++) { - int delta_speed = (rand() % 3) - 1; // Randomly increase or decrease speed - for (int i = 0; i < MAX_ENEMIES_PER_LANE; i++) { - if (enemy->speed_randomization) { - enemy = &env->enemies[lane*MAX_ENEMIES_PER_LANE + i]; - enemy->current_speed_idx = min(max(enemy->initial_speed_idx-2, enemy->current_speed_idx), enemy->initial_speed_idx+2); - enemy->current_speed_idx = min(max(0, enemy->current_speed_idx + delta_speed), 5); - enemy->enemy_vx = enemy->lane_idx < NUM_LANES/2 ? SPEED_VALUES[enemy->current_speed_idx] * TICK_RATE * env->width: -SPEED_VALUES[enemy->current_speed_idx] * TICK_RATE * env->width; - } - } - } -} - -void move_enemies(Freeway* env) { - FreewayEnemy* enemy; - for (int lane = 0; lane < NUM_LANES; lane++) { - for (int i = 0; i < MAX_ENEMIES_PER_LANE; i++) { - enemy = &env->enemies[lane*MAX_ENEMIES_PER_LANE + i]; - if (enemy->is_enabled) { - enemy->enemy_x += enemy->enemy_vx; - } - clip_enemy_position(env, enemy); - } - } -} - -void step_player(Freeway* env, FreewayPlayer* player, int action) { - float player_dy = 0.0; - - if (action == DOWN) { - player_dy = -1.0; - } - else if (action == UP) { - player_dy = 1.0; - env->up_count += 1; - } - - if (player->ticks_stunts_left == 0){ - player->player_y -= player_dy * BASE_PLAYER_SPEED * env->height * TICK_RATE; - } - else { - player->ticks_stunts_left -= 1; - if (env->difficulty == 0){ - player->player_y += 1.5f * env->lane_size / (float) TICKS_STUNT; - } - } - clip_player_position(env, player); - - if (player->ticks_stunts_left == 0) { - if (check_enemy_collisions(env, player) && player->ticks_stunts_left < TICKS_STUNT/4){ - player->hits+=1; - player->ticks_stunts_left = TICKS_STUNT; - if (env->use_dense_rewards){ - env->rewards[0] += PENALTY_HIT; - env->ep_return += PENALTY_HIT; - } - if (env->difficulty == 1){ - reset_player(env, player); - } - } - } - - if (player->player_y <= env->road_start - (player->best_lane_idx+1) * env->lane_size){ - player->best_lane_idx += 1; - if (env->use_dense_rewards){ - env->rewards[0] += 1.0 / (float) NUM_LANES; - env->ep_return += 1.0 / (float) NUM_LANES; - } - else{ - if (player->best_lane_idx == NUM_LANES){ - env->rewards[0] = 1.0; - env->ep_return += 1.0; - } - } - } - - if (player->best_lane_idx == NUM_LANES) { - reached_end(env, player); - env->rewards[0] += 1.0; - env->ep_return += 1.0; - } -} -void c_reset(Freeway* env) { - env->ai_player.player_y = env->height / 2; - env->ai_player.best_lane_idx = 0; - env->ai_player.ticks_stunts_left = 0; - env->ai_player.score = 0; - env->ai_player.hits = 0; - - env->human_player.player_y = env->height / 2; - env->human_player.best_lane_idx = 0; - env->human_player.ticks_stunts_left = 0; - env->human_player.score = 0; - env->human_player.hits = 0; - - env->ep_return = 0.0; - env->tick = 0; - env->up_count = 0; - env->time_left = GAME_LENGTH; - reset_player(env, &env->ai_player); - reset_player(env, &env->human_player); - spawn_enemies(env); - compute_observations(env); -} - -void c_step(Freeway* env) { - env->terminals[0] = 0; - env->rewards[0] = 0.0; - int ai_action = env->actions[0]; - int human_action = env->human_actions[0]; - env->time_left = GAME_LENGTH - env->tick*TICK_RATE; - - for (int i = 0; i < env->frameskip; i++) { - env->tick += 1; - step_player(env, &env->ai_player, ai_action); - if (env->enable_human_player){ - step_player(env, &env->human_player, human_action); - } - move_enemies(env); - } - if (env->tick * TICK_RATE >= GAME_LENGTH) { - env->terminals[0] = 1.0; - add_log(env); - c_reset(env); - } - if (env->tick % RANDOMIZE_SPEED_FREQ == 0) { - randomize_enemy_speed(env); - } - compute_observations(env); -} - - - -typedef struct Client Client; -struct Client { - Texture2D chicken; - Texture2D puffer; - Texture2D car_body; - Texture2D car_wheels; - Texture2D truck_body; - Texture2D truck_wheels; -}; - -static inline bool file_exists(const char* path) { - return access(path, F_OK) != -1; -} - -Client* make_client(Freeway* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - - InitWindow(env->width, env->height, "PufferLib Freeway"); - SetTargetFPS(60/env->frameskip); - client->car_body = LoadTexture("resources/freeway/tex_car_body.png"); - client->car_wheels = LoadTexture("resources/freeway/tex_car_wheels.png"); - client->chicken = LoadTexture("resources/freeway/tex_chicken0.png"); - client->puffer = LoadTexture("resources/shared/puffers.png"); - client->truck_body = LoadTexture("resources/freeway/tex_truck_body.png"); - client->truck_wheels = LoadTexture("resources/freeway/tex_truck_wheels.png"); - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -Color CAR_COLORS[10] = { - (Color){ 139, 0, 0, 255 }, // Dark Red - (Color){ 255, 140, 0, 255 }, // Dark Orange - (Color){ 204, 204, 0, 255 }, // Dark Yellow - (Color){ 0, 100, 0, 255 }, // Dark Green - (Color){ 0, 0, 139, 255 }, // Dark Blue - (Color){ 139, 0, 0, 255 }, // Dark Red - (Color){ 255, 140, 0, 255 }, // Dark Orange - (Color){ 204, 204, 0, 255 }, // Dark Yellow - (Color){ 0, 100, 0, 255 }, // Dark Green - (Color){ 0, 0, 139, 255 } // Dark Blue -}; -void c_render(Freeway* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - - Client* client = env->client; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - if (IsKeyPressed(KEY_TAB)) { - ToggleFullscreen(); - } - - BeginDrawing(); - ClearBackground((Color){170, 170, 170, 255}); - - // Draw the road - DrawRectangle( - 0, - env->road_end, - env->width, env->road_start - env->road_end, (Color){150, 150, 150, 255} - ); - DrawRectangle( - 0, - env->road_end- HALF_LINEWIDTH, - env->width, 2*HALF_LINEWIDTH, (Color){0, 255} - ); - DrawRectangle( - 0, - env->road_start - HALF_LINEWIDTH, - env->width, 2*HALF_LINEWIDTH, (Color){0, 255} - ); - - for (int lane = 1; lane < NUM_LANES; lane++) { - if (lane != NUM_LANES/2){ - for (int dash = 0; dash < env->width / (DASH_SPACING + DASH_SIZE) ; dash++){ - int dash_start = DASH_SPACING / 2 + (DASH_SPACING + DASH_SIZE) * dash; - DrawRectangle( - dash_start, - env->road_start + (env->road_end - env->road_start) * lane/NUM_LANES - HALF_LINEWIDTH, - DASH_SIZE, 2*HALF_LINEWIDTH, (Color){235, 235, 235, 255} - ); - } - } - } - - for (int dash = 0; dash < env->width / (DASH_SPACING + DASH_SIZE) ; dash++){ - int dash_start = DASH_SPACING / 2 + (DASH_SPACING + DASH_SIZE) * dash; - DrawRectangle( - dash_start, - env->road_start + (env->road_end - env->road_start) / 2 - 3*HALF_LINEWIDTH, - DASH_SIZE, 2*HALF_LINEWIDTH, (Color){235, 235, 100, 255} - ); - DrawRectangle( - dash_start, - env->road_start + (env->road_end - env->road_start) / 2 + HALF_LINEWIDTH, - DASH_SIZE, 2*HALF_LINEWIDTH, (Color){235, 235, 100, 255} - ); - } - - // Draw ai player - DrawTexturePro( - client->puffer, - (Rectangle){ - 0, 0, 128, 128, - }, - (Rectangle){ - env->ai_player.player_x - env->player_width / 2, - env->ai_player.player_y - env->player_height / 2, - env->player_width, - env->player_height, - }, - (Vector2){0, 0}, - 0, - WHITE - ); - - DrawTexturePro( - client->puffer, - (Rectangle){ - 128, 128, 128, 128, - }, - (Rectangle){ - env->human_player.player_x - env->player_width / 2, - env->human_player.player_y - env->player_height / 2, - env->player_width, - env->player_height, - }, - (Vector2){0, 0}, - 0, - WHITE - ); - - // Draw enemies - Rectangle src_rec; - FreewayEnemy* enemy; - for (int lane = 0; lane < NUM_LANES; lane++) { - for (int i = 0; i < MAX_ENEMIES_PER_LANE; i++) { - enemy = &env->enemies[lane*MAX_ENEMIES_PER_LANE + i]; - if (enemy->is_enabled) { - Texture2D body = enemy->type == 0 ? client->car_body : client->truck_body; - Texture2D wheels = enemy->type == 0 ? client->car_wheels : client->truck_wheels; - if (lane < NUM_LANES/2) { - src_rec= enemy->type == 0 ? (Rectangle){16,0,16,10} : (Rectangle){32,10,32,10}; - } - else { - src_rec = enemy->type == 0 ? (Rectangle){16 + 16, 0, -16, 10} : (Rectangle){32 + 32, 10, -32, 10}; - } - DrawTexturePro( - body, - src_rec, - (Rectangle){ - enemy->enemy_x - enemy->enemy_width / 2, - enemy->enemy_y - enemy->enemy_height/ 2, - enemy->enemy_width, - enemy->enemy_height, - }, - (Vector2){0, 0}, - 0, - CAR_COLORS[lane] - ); - DrawTexturePro( - wheels, - src_rec, - (Rectangle){ - enemy->enemy_x - enemy->enemy_width / 2, - enemy->enemy_y - enemy->enemy_height/ 2, - enemy->enemy_width, - enemy->enemy_height, - }, - (Vector2){0, 0}, - 0, - CAR_COLORS[lane] - ); - } - } - } - - // Draw UI - int rounded_time_left = round(env->time_left); - DrawText(TextFormat("P1 Score: %i", env->ai_player.score), 10, 3, 40, (Color) {255, 160, 160, 255}); - DrawText(TextFormat("P2 Score: %i", env->human_player.score), round(0.77*env->width), 3, 40, (Color) {255, 160, 160, 255}); - DrawText(TextFormat("Time: %i", rounded_time_left), round(0.45*env->width) - 40, 3, 40, (Color) {255, 160, 160, 255}); - - EndDrawing(); - - //PlaySound(client->sound); -} diff --git a/pufferlib/ocean/freeway/freeway.py b/pufferlib/ocean/freeway/freeway.py deleted file mode 100644 index 6e33cea87..000000000 --- a/pufferlib/ocean/freeway/freeway.py +++ /dev/null @@ -1,129 +0,0 @@ -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.freeway import binding - - -class Freeway(pufferlib.PufferEnv): - def __init__( - self, - num_envs=1, - render_mode=None, - frameskip=4, - width=1216, - height=720, - player_width=64, - player_height=64, - car_width=64, - car_height=40, - lane_size=64, - difficulty=0, - level=0, - use_dense_rewards=True, - env_randomization=True, - enable_human_player=False, - log_interval=128, - buf=None, - seed=0, - ): - assert level < 8, "Level should be in {0, 1, 2, 3, 4, 5, 6, 7} or -1. Level -1 is a random mix of all 8 supported levels." - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=1, shape=(34,), dtype=np.float32 - ) - self.render_mode = render_mode - self.num_agents = num_envs - self.log_interval = log_interval - self.tick = 0 - - self.single_action_space = gymnasium.spaces.Discrete(3) - - super().__init__(buf) - - self.c_envs = binding.vec_init( - self.observations, - self.actions, - self.rewards, - self.terminals, - self.truncations, - num_envs, - seed, - frameskip=frameskip, - width=width, - height=height, - player_width=player_width, - player_height=player_height, - car_width=car_width, - car_height=car_height, - lane_size=lane_size, - difficulty=difficulty, - level = level, - enable_human_player=enable_human_player, - env_randomization=env_randomization, - use_dense_rewards=use_dense_rewards, - ) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=60, level = 0,atn_cache=1024): - env = Freeway(num_envs=1024, level=level) - env.reset() - tick = 0 - - actions = np.random.randint(0, 3, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - env.close() - print(f'SPS: %f', env.num_agents * tick / (time.time() - start)) - -def test_render(timeout=60, level = 0,atn_cache=1024): - env = Freeway(num_envs=1, level=level) - env.reset(seed=0) - tick = 0 - - actions = np.random.randint(0, 3, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - obs, rew, term, trunc, i = env.step(atn) - env.render() - tick += 1 - if tick == 100: - env.reset() - env.close() - - -if __name__ == '__main__': - # test_performance() - for level in range(0,8): - test_performance(level = level, timeout=5, atn_cache=1024) - diff --git a/pufferlib/ocean/freeway/freeway_levels.h b/pufferlib/ocean/freeway/freeway_levels.h deleted file mode 100644 index 22e5d7340..000000000 --- a/pufferlib/ocean/freeway/freeway_levels.h +++ /dev/null @@ -1,184 +0,0 @@ -#define BASE_ROAD_SPEED 1.0f/13.0f // inverse number of seconds to go from the left to the right (slowest enemy) -#define MULT_ROAD_SPEED 1.35 // Factor of increase for the road speed (approximates the ratio between min speed and max speed of lvl1 (13/2.5)^(1/5)) -#define BASE_PLAYER_SPEED 1.0f/3.5f // inverse number of seconds to go from the bottom to the top of the screen for the player -#define MAX_ENEMIES_PER_LANE 3 -#define NUM_LEVELS 8 -#define NUM_LANES 10 - -const float SPEED0 = BASE_ROAD_SPEED; -const float SPEED1 = MULT_ROAD_SPEED*BASE_ROAD_SPEED; -const float SPEED2 = MULT_ROAD_SPEED*MULT_ROAD_SPEED*BASE_ROAD_SPEED; -const float SPEED3 = MULT_ROAD_SPEED*MULT_ROAD_SPEED*MULT_ROAD_SPEED*BASE_ROAD_SPEED; -const float SPEED4 = MULT_ROAD_SPEED*MULT_ROAD_SPEED*MULT_ROAD_SPEED*MULT_ROAD_SPEED*BASE_ROAD_SPEED; -const float SPEED5 = MULT_ROAD_SPEED*MULT_ROAD_SPEED*MULT_ROAD_SPEED*MULT_ROAD_SPEED*MULT_ROAD_SPEED*BASE_ROAD_SPEED; - -const float SPEED_VALUES[6] = {SPEED0, SPEED1, SPEED2, SPEED3, SPEED4, SPEED5}; - -const int HUMAN_HIGH_SCORE[] = {25, 20, 18, 20, 25, 18, 15, 18}; - -const int ENEMIES_PER_LANE[NUM_LEVELS][NUM_LANES] = { - // Level 0 - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - // Level 1 - {1, 2, 2, 3, 2, 1, 2, 3, 2, 2}, - // Level 2 - {3, 3, 1, 3, 1, 1, 3, 1, 3, 1}, - // Level 3 - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - // Level 4 - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - // Level 5 - {1, 2, 2, 3, 2, 1, 2, 3, 2, 2}, - // Level 6 - {3, 3, 1, 3, 1, 1, 3, 1, 3, 1}, - // Level 7 - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, -}; - -const float ENEMIES_TYPES[NUM_LEVELS][NUM_LANES] = { - // Level 0 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - // Level 1 - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, - // Level 2 - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, - // Level 3 - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - // Level 4 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - // Level 5 - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, - // Level 6 - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, - // Level 7 - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, -}; - -const int SPEED_RANDOMIZATION[NUM_LEVELS]= {0,0,0,0,1,1,1,1}; - - -const float ENEMIES_INITIAL_X[NUM_LEVELS][NUM_LANES][MAX_ENEMIES_PER_LANE] = { - // Level 0 - { - {0.0, 0.0, 0.0}, // lane 0 to 9 - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - }, - { - // Level 1 - {0.0, 0.0, 0.0}, // lane 0 to 9 - {0.0, 0.1, 0.0}, - {0.0, 0.2, 0.0}, - {0.0, 0.1, 0.2}, - {0.0, 0.4, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.4, 0.0}, - {0.0, 0.1, 0.2}, - {0.0, 0.2, 0.0}, - {0.0, 0.1, 0.0}, - }, - { - // Level 2 - {0.0, 0.2, 0.4}, // lane 0 to 9 - {0.0, 0.2, 0.4}, - {0.0, 0.0, 0.0}, - {0.0, 0.2, 0.4}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.2, 0.4}, - {0.0, 0.0, 0.0}, - {0.0, 0.2, 0.4}, - {0.0, 0.2, 0.4}, - }, - { - // Level 3 - {0.0, 0.0, 0.0}, // lane 0 to 9 - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - }, - // Level 4 - { - {0.0, 0.0, 0.0}, // lane 0 to 9 - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - }, - { - // Level 5 - {0.0, 0.0, 0.0}, // lane 0 to 9 - {0.0, 0.1, 0.0}, - {0.0, 0.2, 0.0}, - {0.0, 0.1, 0.2}, - {0.0, 0.4, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.4, 0.0}, - {0.0, 0.1, 0.2}, - {0.0, 0.2, 0.0}, - {0.0, 0.1, 0.0}, - }, - { - // Level 6 - {0.0, 0.2, 0.4}, // lane 0 to 9 - {0.0, 0.2, 0.4}, - {0.0, 0.0, 0.0}, - {0.0, 0.2, 0.4}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.2, 0.4}, - {0.0, 0.0, 0.0}, - {0.0, 0.2, 0.4}, - {0.0, 0.2, 0.4}, - }, - { - // Level 7 - {0.0, 0.0, 0.0}, // lane 0 to 9 - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - } -}; - -const float ENEMIES_INITIAL_SPEED_IDX[NUM_LEVELS][NUM_LANES] = { - // Level 0 - {0,1,2,3,4,4,3,2,1,0}, - // Level 1 - {0,1,3,4,5,5,4,3,1,0}, - // Level 2 - {0,1,3,4,5,5,4,3,1,0}, - // Level 3 - {5,4,2,4,5,5,4,2,4,5}, - // Level 4 - {0,1,2,3,4,4,3,2,1,0}, - // Level 5 - {0,1,3,4,5,5,4,3,1,0}, - // Level 6 - {0,1,3,4,5,5,4,3,1,0}, - // Level 7 - {5,4,2,4,5,5,4,2,4,5}, -}; \ No newline at end of file diff --git a/pufferlib/ocean/g2048/2048.h b/pufferlib/ocean/g2048/2048.h deleted file mode 100755 index 64d69353a..000000000 --- a/pufferlib/ocean/g2048/2048.h +++ /dev/null @@ -1,382 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" - -#define SIZE 4 -#define EMPTY 0 -#define UP 1 -#define DOWN 2 -#define LEFT 3 -#define RIGHT 4 - -// Precomputed constants -#define REWARD_MULTIPLIER 0.09090909f -#define INVALID_MOVE_PENALTY -0.05f -#define GAME_OVER_PENALTY -1.0f - -typedef struct { - float perf; - float score; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct { - Log log; // Required - unsigned char* observations; // Cheaper in memory if encoded in uint_8 - int* actions; // Required - float* rewards; // Required - unsigned char* terminals; // Required - int score; - int tick; - unsigned char grid[SIZE][SIZE]; - float episode_reward; // Accumulate episode reward - - // Cached values to avoid recomputation - int empty_count; - bool game_over_cached; - bool grid_changed; -} Game; - -// Precomputed color table for rendering optimization -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; - -static Color tile_colors[12] = { - {6, 24, 24, 255}, // Empty/background - {187, 187, 187, 255}, // 2 - {170, 187, 187, 255}, // 4 - {150, 187, 187, 255}, // 8 - {130, 187, 187, 255}, // 16 - {110, 187, 187, 255}, // 32 - {90, 187, 187, 255}, // 64 - {70, 187, 187, 255}, // 128 - {50, 187, 187, 255}, // 256 - {30, 187, 187, 255}, // 512 - {10, 187, 187, 255}, // 1024 - {0, 187, 187, 255} // 2048+ -}; - -// --- Logging --- -void add_log(Game* game); - -// --- Required functions for env_binding.h --- -void c_reset(Game* env); -void c_step(Game* env); -void c_render(Game* env); -void c_close(Game* env); - -// Inline function for updating observations (avoid function call overhead) -static inline void update_observations(Game* game) { - for (int i = 0; i < SIZE; i++) { - for (int j = 0; j < SIZE; j++) { - game->observations[i * SIZE + j] = game->grid[i][j]; - } - } -} - -// Cache empty cell count during grid operations -static inline void update_empty_count(Game* game) { - int count = 0; - for (int i = 0; i < SIZE; i++) { - for (int j = 0; j < SIZE; j++) { - if (game->grid[i][j] == EMPTY) count++; - } - } - game->empty_count = count; -} - -void add_log(Game* game) { - game->log.score = (float)(1 << game->score); - game->log.perf += ((float)game->score) * REWARD_MULTIPLIER; - game->log.episode_length += game->tick; - game->log.episode_return += game->episode_reward; - game->log.n += 1; -} - -void c_reset(Game* game) { - for (int i = 0; i < SIZE; i++) { - for (int j = 0; j < SIZE; j++) { - game->grid[i][j] = EMPTY; - } - } - - game->score = 0; - game->tick = 0; - game->episode_reward = 0; - game->empty_count = SIZE * SIZE; - game->game_over_cached = false; - game->grid_changed = true; - - if (game->terminals) game->terminals[0] = 0; - - // Add two random tiles at the start - optimized version - for (int added = 0; added < 2; ) { - int pos = rand() % (SIZE * SIZE); - int i = pos / SIZE; - int j = pos % SIZE; - if (game->grid[i][j] == EMPTY) { - game->grid[i][j] = (rand() % 10 == 0) ? 2 : 1; - added++; - game->empty_count--; - } - } - - update_observations(game); -} - -void add_random_tile(Game* game) { - if (game->empty_count == 0) return; - - // Use reservoir sampling for better performance - int chosen_pos = -1; - int count = 0; - - for (int pos = 0; pos < SIZE * SIZE; pos++) { - int i = pos / SIZE; - int j = pos % SIZE; - if (game->grid[i][j] == EMPTY) { - count++; - if (rand() % count == 0) { - chosen_pos = pos; - } - } - } - - if (chosen_pos >= 0) { - int i = chosen_pos / SIZE; - int j = chosen_pos % SIZE; - game->grid[i][j] = (rand() % 10 == 0) ? 2 : 1; - game->empty_count--; - game->grid_changed = true; - } - - update_observations(game); -} - -// Optimized slide and merge with fewer memory operations -static inline bool slide_and_merge(unsigned char* row, float* reward) { - bool moved = false; - int write_pos = 0; - - // Single pass: slide and identify merge candidates - for (int read_pos = 0; read_pos < SIZE; read_pos++) { - if (row[read_pos] != EMPTY) { - if (write_pos != read_pos) { - row[write_pos] = row[read_pos]; - row[read_pos] = EMPTY; - moved = true; - } - write_pos++; - } - } - - // Merge pass - for (int i = 0; i < SIZE - 1; i++) { - if (row[i] != EMPTY && row[i] == row[i + 1]) { - row[i]++; - *reward += ((float)row[i]) * REWARD_MULTIPLIER; - // Shift remaining elements left - for (int j = i + 1; j < SIZE - 1; j++) { - row[j] = row[j + 1]; - } - row[SIZE - 1] = EMPTY; - moved = true; - } - } - - return moved; -} - -bool move(Game* game, int direction, float* reward) { - bool moved = false; - unsigned char temp[SIZE]; - - if (direction == UP || direction == DOWN) { - for (int col = 0; col < SIZE; col++) { - // Extract column - for (int i = 0; i < SIZE; i++) { - int idx = (direction == UP) ? i : SIZE - 1 - i; - temp[i] = game->grid[idx][col]; - } - - if (slide_and_merge(temp, reward)) { - moved = true; - // Write back column - for (int i = 0; i < SIZE; i++) { - int idx = (direction == UP) ? i : SIZE - 1 - i; - game->grid[idx][col] = temp[i]; - } - } - } - } else { - for (int row = 0; row < SIZE; row++) { - // Extract row - for (int i = 0; i < SIZE; i++) { - int idx = (direction == LEFT) ? i : SIZE - 1 - i; - temp[i] = game->grid[row][idx]; - } - - if (slide_and_merge(temp, reward)) { - moved = true; - // Write back row - for (int i = 0; i < SIZE; i++) { - int idx = (direction == LEFT) ? i : SIZE - 1 - i; - game->grid[row][idx] = temp[i]; - } - } - } - } - - if (!moved) { - *reward = INVALID_MOVE_PENALTY; - } else { - game->grid_changed = true; - game->game_over_cached = false; // Invalidate cache - } - - return moved; -} - -bool is_game_over(Game* game) { - // Use cached result if grid hasn't changed - if (!game->grid_changed && game->game_over_cached) { - return game->game_over_cached; - } - - // Quick check: if there are empty cells, game is not over - if (game->empty_count > 0) { - game->game_over_cached = false; - game->grid_changed = false; - return false; - } - - // Check for possible merges - for (int i = 0; i < SIZE; i++) { - for (int j = 0; j < SIZE; j++) { - unsigned char current = game->grid[i][j]; - if (i < SIZE - 1 && current == game->grid[i + 1][j]) { - game->game_over_cached = false; - game->grid_changed = false; - return false; - } - if (j < SIZE - 1 && current == game->grid[i][j + 1]) { - game->game_over_cached = false; - game->grid_changed = false; - return false; - } - } - } - - game->game_over_cached = true; - game->grid_changed = false; - return true; -} - -// Optimized score calculation -static inline unsigned char calc_score(Game* game) { - unsigned char max_tile = 0; - // Unroll loop for better performance - for (int i = 0; i < SIZE; i++) { - for (int j = 0; j < SIZE; j++) { - if (game->grid[i][j] > max_tile) { - max_tile = game->grid[i][j]; - } - } - } - return max_tile; -} - -void c_step(Game* game) { - float reward = 0.0f; - bool did_move = move(game, game->actions[0] + 1, &reward); - game->tick++; - - if (did_move) { - add_random_tile(game); - game->score = calc_score(game); - update_empty_count(game); // Update after adding tile - } - - bool game_over = is_game_over(game); - game->terminals[0] = game_over ? 1 : 0; - - if (game_over) { - reward = GAME_OVER_PENALTY; - } - - game->rewards[0] = reward; - game->episode_reward += reward; - - update_observations(game); - - if (game->terminals[0]) { - add_log(game); - c_reset(game); - } -} - -// Rendering optimizations -void c_render(Game* game) { - static bool window_initialized = false; - static char score_text[32]; - static const int px = 100; - - if (!window_initialized) { - InitWindow(px * SIZE, px * SIZE + 50, "2048"); - SetTargetFPS(30); // Increased for smoother rendering - window_initialized = true; - } - - if (IsKeyDown(KEY_ESCAPE)) { - CloseWindow(); - exit(0); - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - // Draw grid - for (int i = 0; i < SIZE; i++) { - for (int j = 0; j < SIZE; j++) { - int val = game->grid[i][j]; - - // Use precomputed colors - Color color = (val == 0) ? tile_colors[0] : - (val <= 11) ? tile_colors[val] : - (Color){60, 60, 60, 255}; - - DrawRectangle(j * px, i * px, px - 5, px - 5, color); - - if (val > 0) { - int display_val = 1 << val; // Power of 2 - // Pre-format text to avoid repeated formatting - snprintf(score_text, sizeof(score_text), "%d", display_val); - if (display_val < 1000) { - DrawText(score_text, j * px + 30, i * px + 40, 32, PUFF_WHITE); - } else { - DrawText(score_text, j * px + 20, i * px + 40, 32, PUFF_WHITE); - } - } - } - } - - // Draw score (format once per frame) - snprintf(score_text, sizeof(score_text), "Score: %d", 1 << game->score); - DrawText(score_text, 10, px * SIZE + 10, 24, PUFF_WHITE); - - EndDrawing(); -} - -void c_close(Game* game) { - if (IsWindowReady()) { - CloseWindow(); - } -} diff --git a/pufferlib/ocean/g2048/binding.c b/pufferlib/ocean/g2048/binding.c deleted file mode 100644 index f3c22e152..000000000 --- a/pufferlib/ocean/g2048/binding.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "2048.h" - -#define Env Game -#include "../env_binding.h" - -// 2048.h does not have a 'size' field, so my_init can just return 0 -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - // No custom initialization needed for 2048 - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} \ No newline at end of file diff --git a/pufferlib/ocean/g2048/g2048.c b/pufferlib/ocean/g2048/g2048.c deleted file mode 100644 index 195f4fc38..000000000 --- a/pufferlib/ocean/g2048/g2048.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "2048.h" -#include "puffernet.h" - -int main() { - srand(time(NULL)); - Game env; - unsigned char observations[SIZE * SIZE] = {0}; - unsigned char terminals[1] = {0}; - int actions[1] = {0}; - float rewards[1] = {0}; - - env.observations = observations; - env.terminals = terminals; - env.actions = actions; - env.rewards = rewards; - - Weights* weights = load_weights("resources/g2048/g2048_weights.bin", 134917); - int logit_sizes[1] = {4}; - LinearLSTM* net = make_linearlstm(weights, 1, 16, logit_sizes, 1); - c_reset(&env); - c_render(&env); - - // Main game loop - int frame = 0; - while (!WindowShouldClose()) { - c_render(&env); - frame++; - - int action = 0; - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyPressed(KEY_W) || IsKeyPressed(KEY_UP)) action = UP; - else if (IsKeyPressed(KEY_S) || IsKeyPressed(KEY_DOWN)) action = DOWN; - else if (IsKeyPressed(KEY_A) || IsKeyPressed(KEY_LEFT)) action = LEFT; - else if (IsKeyPressed(KEY_D) || IsKeyPressed(KEY_RIGHT)) action = RIGHT; - env.actions[0] = action - 1; - } else if (frame % 10 != 0) { - continue; - } else { - action = 1; - for (int i = 0; i < 16; i++) { - net->obs[i] = env.observations[i]; - } - forward_linearlstm(net, net->obs, env.actions); - } - - if (action != 0) { - c_step(&env); - } - } - - free_linearlstm(net); - c_close(&env); - printf("Game Over! Final Max Tile: %d\n", env.score); - return 0; -} diff --git a/pufferlib/ocean/g2048/g2048.py b/pufferlib/ocean/g2048/g2048.py deleted file mode 100644 index a18bb0dc7..000000000 --- a/pufferlib/ocean/g2048/g2048.py +++ /dev/null @@ -1,69 +0,0 @@ -'''2048 Gymnasium-compatible environment using the C backend.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.g2048 import binding - -class G2048(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, log_interval=128, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=100, shape=(4,4), dtype=np.uint8 - ) - self.single_action_space = gymnasium.spaces.Discrete(4) - self.render_mode = render_mode - self.num_agents = num_envs - self.log_interval = log_interval - - super().__init__(buf) - self.c_envs = binding.vec_init( - self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed - ) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - - self.actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return ( - self.observations, self.rewards, - self.terminals, self.truncations, info - ) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 128 - - env = G2048(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, 4, (CACHE, N)) - - i = 0 - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[i % CACHE]) - steps += N - i += 1 - - print('2048 SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/go/binding.c b/pufferlib/ocean/go/binding.c deleted file mode 100644 index 272c0ba41..000000000 --- a/pufferlib/ocean/go/binding.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "go.h" -#define Env CGo -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->grid_size = unpack(kwargs, "grid_size"); - env->board_width = unpack(kwargs, "board_width"); - env->board_height = unpack(kwargs, "board_height"); - env->grid_square_size = unpack(kwargs, "grid_square_size"); - env->moves_made = unpack(kwargs, "moves_made"); - env->komi = unpack(kwargs, "komi"); - env->score = unpack(kwargs, "score"); - env->last_capture_position = unpack(kwargs, "last_capture_position"); - env->reward_move_pass = unpack(kwargs, "reward_move_pass"); - env->reward_move_invalid = unpack(kwargs, "reward_move_invalid"); - env->reward_move_valid = unpack(kwargs, "reward_move_valid"); - env->reward_player_capture = unpack(kwargs, "reward_player_capture"); - env->reward_opponent_capture = unpack(kwargs, "reward_opponent_capture"); - - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/go/go.c b/pufferlib/ocean/go/go.c deleted file mode 100644 index fcd6614ea..000000000 --- a/pufferlib/ocean/go/go.c +++ /dev/null @@ -1,229 +0,0 @@ -#include -#include "go.h" -#include "puffernet.h" - -typedef struct GoNet GoNet; -struct GoNet { - int num_agents; - float* obs_2d; - float* obs_1d; - Conv2D* conv1; - ReLU* relu1; - Conv2D* conv2; - Linear* flat; - CatDim1* cat; - Linear* proj; - ReLU* relu3; - LSTM* lstm; - Linear* actor; - Linear* value_fn; - Multidiscrete* multidiscrete; -}; -GoNet* init_gonet(Weights* weights, int num_agents, int grid_size) { - GoNet* net = calloc(1, sizeof(GoNet)); - int hidden_size = 128; - int cnn_channels = 64; - int conv1_output_size = grid_size - 2; - int output_size = grid_size - 4; - int cnn_flat_size = cnn_channels * output_size * output_size; - - net->num_agents = num_agents; - net->obs_2d = calloc(num_agents * grid_size * grid_size * 2, sizeof(float)); // 2 channels for player and opponent - net->obs_1d = calloc(num_agents * 2, sizeof(float)); // 2 additional features - - net->conv1 = make_conv2d(weights, num_agents, grid_size, grid_size, 2, cnn_channels, 3, 1); - net->relu1 = make_relu(num_agents, cnn_channels * conv1_output_size * conv1_output_size); - net->conv2 = make_conv2d(weights, num_agents, conv1_output_size, conv1_output_size, cnn_channels, cnn_channels, 3, 1); - net->flat = make_linear(weights, num_agents, 2, 32); - net->cat = make_cat_dim1(num_agents, cnn_flat_size, 32); - net->proj = make_linear(weights, num_agents, cnn_flat_size + 32, hidden_size); - net->relu3 = make_relu(num_agents, hidden_size); - net->actor = make_linear(weights, num_agents, hidden_size, grid_size*grid_size + 1); // +1 for pass move - net->value_fn = make_linear(weights, num_agents, hidden_size, 1); - net->lstm = make_lstm(weights, num_agents, hidden_size, 128); - int logit_sizes[6] = {grid_size*grid_size+1}; - net->multidiscrete = make_multidiscrete(num_agents, logit_sizes, 1); - return net; -} - -void free_gonet(GoNet* net) { - free(net->obs_2d); - free(net->obs_1d); - free(net->conv1); - free(net->relu1); - free(net->conv2); - free(net->flat); - free(net->cat); - free(net->relu3); - free(net->proj); - free(net->lstm); - free(net->actor); - free(net->value_fn); - free(net); -} - -void forward(GoNet* net, float* observations, int* actions, int grid_size) { - int full_board = grid_size * grid_size; - // Clear previous observations - memset(net->obs_2d, 0, net->num_agents * grid_size * grid_size * 2 * sizeof(float)); - memset(net->obs_1d, 0, net->num_agents * 2 * sizeof(float)); - - // Reshape observations into 2D boards and additional features - float (*obs_2d)[2][grid_size][grid_size] = (float (*)[2][grid_size][grid_size])net->obs_2d; - float (*obs_1d)[2] = (float (*)[2])net->obs_1d; - - for (int b = 0; b < net->num_agents; b++) { - int b_offset = b * (full_board * 2 + 2); // offset for each batch - - // Process black stones board - for (int i = 0; i < grid_size; i++) { - for (int j = 0; j < grid_size; j++) { - obs_2d[b][0][i][j] = observations[b_offset + i*grid_size + j]; - } - } - - // Process white stones board - for (int i = 0; i < grid_size; i++) { - for (int j = 0; j < grid_size; j++) { - obs_2d[b][1][i][j] = observations[b_offset + full_board + i*grid_size + j]; - } - } - - // Process additional features - obs_1d[b][0] = observations[b_offset + full_board * 2]; - obs_1d[b][1] = observations[b_offset + full_board * 2 + 1]; - } - - // Forward pass through the network - conv2d(net->conv1, net->obs_2d); - relu(net->relu1, net->conv1->output); - conv2d(net->conv2, net->relu1->output); - - linear(net->flat, net->obs_1d); - - cat_dim1(net->cat, net->conv2->output, net->flat->output); - linear(net->proj, net->cat->output); - relu(net->relu3, net->proj->output); - - lstm(net->lstm, net->relu3->output); - linear(net->actor, net->lstm->state_h); - linear(net->value_fn, net->lstm->state_h); - - // Get action by taking argmax of actor output - softmax_multidiscrete(net->multidiscrete, net->actor->output, actions); - -} - -void demo(int grid_size) { - - CGo env = { - .width = 950, - .height = 64*(grid_size+1), - .grid_size = grid_size, - .board_width = 64*(grid_size+1) + 400, - .board_height = 64*(grid_size+1), - .grid_square_size = 64, - .moves_made = 0, - .komi = 7.5, - .reward_move_pass = -0.25, - .reward_move_invalid = -0.1, - .reward_move_valid = 0.1 - }; - - Weights* weights = load_weights("resources/go/go_weights.bin", 254867); - GoNet* net = init_gonet(weights, 1, grid_size); - allocate(&env); - c_reset(&env); - c_render(&env); - - int tick = 0; - while (!WindowShouldClose()) { - // User can take control of the paddle - if(tick % 12 == 0) { - tick = 0; - int human_action = env.actions[0]; - forward(net, env.observations, env.actions, grid_size); - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env.actions[0] = human_action; - } - c_step(&env); - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env.actions[0] = -1; - } - } - tick++; - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - Vector2 mousePos = GetMousePosition(); - - // Calculate the offset for the board - int boardOffsetX = env.grid_square_size; - int boardOffsetY = env.grid_square_size; - - // Adjust mouse position relative to the board - int relativeX = mousePos.x - boardOffsetX; - int relativeY = mousePos.y - boardOffsetY; - - // Calculate cell indices for the corners - int cellX = (relativeX + env.grid_square_size / 2) / env.grid_square_size; - int cellY = (relativeY + env.grid_square_size / 2) / env.grid_square_size; - - // Ensure the click is within the game board - if (cellX >= 0 && cellX <= env.grid_size && cellY >= 0 && cellY <= env.grid_size) { - // Calculate the point index (1-19) based on the click position - int pointIndex = cellY * (env.grid_size) + cellX + 1; - env.actions[0] = (unsigned short)pointIndex; - } - // Check if pass button is clicked - int passButtonX = env.width - 300; - int passButtonY = 200; - int passButtonWidth = 100; - int passButtonHeight = 50; - - if (mousePos.x >= passButtonX && mousePos.x <= passButtonX + passButtonWidth && - mousePos.y >= passButtonY && mousePos.y <= passButtonY + passButtonHeight) { - env.actions[0] = 0; // Send action 0 for pass - } - } - } - c_render(&env); - } - //close_client(client); - free_allocated(&env); -} - -void performance_test() { - long test_time = 10; - CGo env = { - .width = 1000, - .height = 800, - .grid_size = 9, - .board_width = 600, - .board_height = 600, - .grid_square_size = 600/9, - .moves_made = 0, - .komi = 7.5, - .reward_move_pass = -0.25, - .reward_move_invalid = -0.1, - .reward_move_valid = 0.1 - }; - allocate(&env); - c_reset(&env); - - long start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - env.actions[0] = rand() % (env.grid_size)*(env.grid_size); - c_step(&env); - i++; - } - long end = time(NULL); - printf("SPS: %ld\n", i / (end - start)); - free_allocated(&env); -} - -int main() { - demo(7); - // performance_test(); - return 0; -} diff --git a/pufferlib/ocean/go/go.h b/pufferlib/ocean/go/go.h deleted file mode 100644 index f96864ae0..000000000 --- a/pufferlib/ocean/go/go.h +++ /dev/null @@ -1,810 +0,0 @@ -#include -#include -#include -#include -#include -#include "raylib.h" - -#define NOOP 0 -#define MOVE_MIN 1 -#define TICK_RATE 1.0f/60.0f -#define NUM_DIRECTIONS 4 -#define ENV_WIN -1 -#define PLAYER_WIN 1 -static const int DIRECTIONS[NUM_DIRECTIONS][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; -// LD_LIBRARY_PATH=raylib/lib ./go - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -typedef struct Group Group; -struct Group { - int parent; - int rank; - int size; - int liberties; -}; - -int find(Group* groups, int x) { - if (groups[x].parent != x) - groups[x].parent = find(groups, groups[x].parent); - return groups[x].parent; -} - -void union_groups(Group* groups, int pos1, int pos2) { - pos1 = find(groups, pos1); - pos2 = find(groups, pos2); - - if (pos1 == pos2) return; - - if (groups[pos1].rank < groups[pos2].rank) { - groups[pos1].parent = pos2; - groups[pos2].size += groups[pos1].size; - groups[pos2].liberties += groups[pos1].liberties; - } else if (groups[pos1].rank > groups[pos2].rank) { - groups[pos2].parent = pos1; - groups[pos1].size += groups[pos2].size; - groups[pos1].liberties += groups[pos2].liberties; - } else { - groups[pos2].parent = pos1; - groups[pos1].rank++; - groups[pos1].size += groups[pos2].size; - groups[pos1].liberties += groups[pos2].liberties; - } -} - -typedef struct Client Client; -typedef struct CGo CGo; -struct CGo { - Client* client; - float* observations; - int* actions; - float* rewards; - unsigned char* terminals; - Log log; - float score; - int width; - int height; - int* board_x; - int* board_y; - int board_width; - int board_height; - int grid_square_size; - int grid_size; - int* board_states; - int* previous_board_state; - int last_capture_position; - int* temp_board_states; - int moves_made; - int* capture_count; - float komi; - int* visited; - Group* groups; - Group* temp_groups; - float reward_move_pass; - float reward_move_invalid; - float reward_move_valid; - float reward_player_capture; - float reward_opponent_capture; - float tick; -}; - -void add_log(CGo* env) { - env->log.episode_length += env->tick; - - // Calculate perf as a win rate (1.0 if win, 0.0 if loss) - float win_value = 0.0; - if (env->score > 0) { - win_value = 1.0; // Win - } - else if (env->score < 0) { - win_value = 0.0; // Loss - } - else { - win_value = 0.0; // Tie - } - - env->log.perf = (env->log.perf * env->log.n + win_value) / (env->log.n + 1.0); - - env->log.score += env->score; - env->log.episode_return += env->rewards[0]; - env->log.n += 1.0; -} - -void generate_board_positions(CGo* env) { - for (int i = 0; i < (env->grid_size-1) * (env->grid_size-1); i++) { - int row = i / (env->grid_size-1); - int col = i % (env->grid_size-1); - env->board_x[i] = col * (env->grid_square_size-1); - env->board_y[i] = row * (env->grid_square_size-1); - } -} - -void init_groups(CGo* env) { - for (int i = 0; i < (env->grid_size)*(env->grid_size); i++) { - env->groups[i].parent = i; - env->groups[i].rank = 0; - env->groups[i].size = 1; - env->groups[i].liberties = 0; - } -} - -void init(CGo* env) { - int board_render_size = (env->grid_size-1)*(env->grid_size-1); - int grid_size = env->grid_size*env->grid_size; - env->board_x = (int*)calloc(board_render_size, sizeof(int)); - env->board_y = (int*)calloc(board_render_size, sizeof(int)); - env->board_states = (int*)calloc(grid_size, sizeof(int)); - env->visited = (int*)calloc(grid_size, sizeof(int)); - env->previous_board_state = (int*)calloc(grid_size, sizeof(int)); - env->temp_board_states = (int*)calloc(grid_size, sizeof(int)); - env->capture_count = (int*)calloc(2, sizeof(int)); - env->groups = (Group*)calloc(grid_size, sizeof(Group)); - env->temp_groups = (Group*)calloc(grid_size, sizeof(Group)); - generate_board_positions(env); - init_groups(env); -} - -void allocate(CGo* env) { - init(env); - env->observations = (float*)calloc((env->grid_size)*(env->grid_size)*2 + 2, sizeof(float)); - env->actions = (int*)calloc(1, sizeof(int)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void c_close(CGo* env) { - free(env->board_x); - free(env->board_y); - free(env->board_states); - free(env->visited); - free(env->previous_board_state); - free(env->temp_board_states); - free(env->capture_count); - free(env->temp_groups); - free(env->groups); -} - -void free_allocated(CGo* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void compute_observations(CGo* env) { - int observation_indx=0; - for (int i = 0; i < (env->grid_size)*(env->grid_size); i++) { - if(env->board_states[i] ==1 ){ - env->observations[observation_indx] = 1.0; - } - else { - env->observations[observation_indx] = 0.0; - } - observation_indx++; - } - for (int i = 0; i < (env->grid_size)*(env->grid_size); i++) { - if(env->board_states[i] ==2 ){ - env->observations[observation_indx] = 1.0; - } - else { - env->observations[observation_indx] = 0.0; - } - observation_indx++; - } - env->observations[observation_indx] = env->capture_count[0]; - env->observations[observation_indx+1] = env->capture_count[1]; - -} - -int is_valid_position(CGo* env, int x, int y) { - return (x >= 0 && x < env->grid_size && y >= 0 && y < env->grid_size); -} - -void reset_visited(CGo* env) { - memset(env->visited, 0, sizeof(int) * (env->grid_size) * (env->grid_size)); -} - -void flood_fill(CGo* env, int x, int y, int* territory, int player) { - if (!is_valid_position(env, x, y)) { - return; - } - - int pos = y * (env->grid_size) + x; - if (env->visited[pos] || env->board_states[pos] != 0) { - return; - } - env->visited[pos] = 1; - territory[player]++; - // Check adjacent positions - for (int i = 0; i < 4; i++) { - flood_fill(env, x + DIRECTIONS[i][0], y + DIRECTIONS[i][1], territory, player); - } -} - -void compute_score_tromp_taylor(CGo* env) { - int player_score = 0; - int opponent_score = 0; - reset_visited(env); - - // Queue for BFS - int queue_size = (env->grid_size) * (env->grid_size); - int queue[queue_size]; - - // First count stones - for (int i = 0; i < queue_size; i++) { - if (env->board_states[i] == 1) { - player_score++; - } else if (env->board_states[i] == 2) { - opponent_score++; - } - } - - // Then process empty territories - for (int start_pos = 0; start_pos < queue_size; start_pos++) { - // Skip if not empty or already visited - if (env->board_states[start_pos] != 0 || env->visited[start_pos]) { - continue; - } - - // Initialize BFS - int front = 0, rear = 0; - int territory_size = 0; - int bordering_player = 0; // 0=neutral, 1=player1, 2=player2, 3=mixed - - queue[rear++] = start_pos; - env->visited[start_pos] = 1; - - // Process connected empty points - while (front < rear) { - int pos = queue[front++]; - territory_size++; - int x = pos % env->grid_size; - int y = pos / env->grid_size; - - // Check all adjacent positions - for (int i = 0; i < 4; i++) { - int nx = x + DIRECTIONS[i][0]; - int ny = y + DIRECTIONS[i][1]; - - if (!is_valid_position(env, nx, ny)) { - continue; - } - - int npos = ny * env->grid_size + nx; - - if (env->board_states[npos] == 0 && !env->visited[npos]) { - // Add unvisited empty points to queue - queue[rear++] = npos; - env->visited[npos] = 1; - } else if (bordering_player == 0) { - bordering_player = env->board_states[npos]; - } else if (bordering_player != env->board_states[npos]) { - bordering_player = 3; // Mixed territory - } - } - } - - // Assign territory points - if (bordering_player == 1) { - player_score += territory_size; - } else if (bordering_player == 2) { - opponent_score += territory_size; - } - // Mixed territories (bordering_player == 3) are neutral and not counted - } - - env->score = (float)player_score - (float)opponent_score - env->komi; -} - -int find_in_group(int* group, int group_size, int value) { - for (int i = 0; i < group_size; i++) { - if (group[i] == value) { - return 1; // Found - } - } - return 0; // Not found -} - - -void capture_group(CGo* env, int* board, int root, int* affected_groups, int* affected_count) { - // Reset visited array - reset_visited(env); - - // Use a queue for BFS - int queue_size = (env->grid_size) * (env->grid_size); - int queue[queue_size]; - int front = 0, rear = 0; - - int captured_player = board[root]; // Player whose stones are being captured - int capturing_player = 3 - captured_player; // Player who captures - - queue[rear++] = root; - env->visited[root] = 1; - - while (front != rear) { - int pos = queue[front++]; - board[pos] = 0; // Remove stone - env->capture_count[capturing_player - 1]++; // Update capturing player's count - if(capturing_player-1 == 0){ - env->rewards[0] += env->reward_player_capture; - env->log.episode_return += env->reward_player_capture; - } else{ - env->rewards[0] += env->reward_opponent_capture; - env->log.episode_return += env->reward_opponent_capture; - } - int x = pos % (env->grid_size); - int y = pos / (env->grid_size); - - for (int i = 0; i < 4; i++) { - int nx = x + DIRECTIONS[i][0]; - int ny = y + DIRECTIONS[i][1]; - int npos = ny * (env->grid_size) + nx; - - if (!is_valid_position(env, nx, ny)) { - continue; - } - - if (board[npos] == captured_player && !env->visited[npos]) { - env->visited[npos] = 1; - queue[rear++] = npos; - } - else if (board[npos] == capturing_player) { - int adj_root = find(env->temp_groups, npos); - if (find_in_group(affected_groups, *affected_count, adj_root)) { - continue; - } - affected_groups[(*affected_count)] = adj_root; - (*affected_count)++; - } - } - } -} - - -int count_liberties(CGo* env, int root, int* queue) { - reset_visited(env); - int liberties = 0; - int front = 0; - int rear = 0; - - queue[rear++] = root; - env->visited[root] = 1; - while (front < rear) { - int pos = queue[front++]; - int x = pos % (env->grid_size); - int y = pos / (env->grid_size); - - for (int i = 0; i < 4; i++) { - int nx = x + DIRECTIONS[i][0]; - int ny = y + DIRECTIONS[i][1]; - if (!is_valid_position(env, nx, ny)) { - continue; - } - - int npos = ny * (env->grid_size) + nx; - if (env->visited[npos]) { - continue; - } - - int temp_npos = env->temp_board_states[npos]; - if (temp_npos == 0) { - liberties++; - env->visited[npos] = 1; - } else if (temp_npos == env->temp_board_states[root]) { - queue[rear++] = npos; - env->visited[npos] = 1; - } - } - } - return liberties; -} - -int is_ko(CGo* env) { - for (int i = 0; i < (env->grid_size) * (env->grid_size); i++) { - if (env->temp_board_states[i] != env->previous_board_state[i]) { - return 0; // Not a ko - } - } - return 1; // Is a ko -} - -int make_move(CGo* env, int pos, int player){ - int x = pos % (env->grid_size); - int y = pos / (env->grid_size); - // cannot place stone on occupied tile - if (env->board_states[pos] != 0) { - return 0 ; - } - // temp structures - memcpy(env->temp_board_states, env->board_states, sizeof(int) * (env->grid_size) * (env->grid_size)); - memcpy(env->temp_groups, env->groups, sizeof(Group) * (env->grid_size) * (env->grid_size)); - // create new group - env->temp_board_states[pos] = player; - env->temp_groups[pos].parent = pos; - env->temp_groups[pos].rank = 0; - env->temp_groups[pos].size = 1; - env->temp_groups[pos].liberties = 0; - - int max_affected_groups = (env->grid_size) * (env->grid_size); - int affected_groups[max_affected_groups]; - int affected_count = 0; - affected_groups[affected_count++] = pos; - - int queue[(env->grid_size) * (env->grid_size)]; - - // Perform unions and track affected groups - for (int i = 0; i < 4; i++) { - int nx = x + DIRECTIONS[i][0]; - int ny = y + DIRECTIONS[i][1]; - int npos = ny * (env->grid_size) + nx; - if (!is_valid_position(env, nx, ny)) { - continue; - } - if (env->temp_board_states[npos] == player) { - union_groups(env->temp_groups, pos, npos); - affected_groups[affected_count++] = npos; - } else if (env->temp_board_states[npos] == 3 - player) { - affected_groups[affected_count++] = npos; - } - } - - // Recalculate liberties only for affected groups - for (int i = 0; i < affected_count; i++) { - int root = find(env->temp_groups, affected_groups[i]); - env->temp_groups[root].liberties = count_liberties(env, root, queue); - } - - // Check for captures - bool captured = false; - for (int i = 0; i < affected_count; i++) { - int root = find(env->temp_groups, affected_groups[i]); - if (env->temp_board_states[root] == 3 - player && env->temp_groups[root].liberties == 0) { - capture_group(env, env->temp_board_states, root, affected_groups, &affected_count); - captured = true; - } - } - // If captures occurred, recalculate liberties again - if (captured) { - for (int i = 0; i < affected_count; i++) { - int root = find(env->temp_groups, affected_groups[i]); - env->temp_groups[root].liberties = count_liberties(env, root, queue); - } - // Check for ko rule violation - if(is_ko(env)) { - return 0; - } - } - // self capture - int root = find(env->temp_groups, pos); - if (env->temp_groups[root].liberties == 0) { - return 0; - } - memcpy(env->board_states, env->temp_board_states, sizeof(int) * (env->grid_size) * (env->grid_size)); - memcpy(env->groups, env->temp_groups, sizeof(Group) * (env->grid_size) * (env->grid_size)); - return 1; - -} - - -void enemy_random_move(CGo* env){ - int num_positions = (env->grid_size)*(env->grid_size); - int positions[num_positions]; - int count = 0; - - // Collect all empty positions - for(int i = 0; i < num_positions; i++){ - if(env->board_states[i] == 0){ - positions[count++] = i; - } - } - // Shuffle the positions - for(int i = count - 1; i > 0; i--){ - int j = rand() % (i + 1); - int temp = positions[i]; - positions[i] = positions[j]; - positions[j] = temp; - } - // Try to make a move in a random empty position - for(int i = 0; i < count; i++){ - if(make_move(env, positions[i], 2)){ - return; - } - } - // If no move is possible, pass or end the game - env->terminals[0] = 1; -} - -int find_group_liberty(CGo* env, int root){ - reset_visited(env); - int queue[(env->grid_size)*(env->grid_size)]; - int front = 0, rear = 0; - queue[rear++] = root; - env->visited[root] = 1; - - while(front < rear){ - int pos = queue[front++]; - int x = pos % (env->grid_size); - int y = pos / (env->grid_size); - - for(int i = 0; i < 4; i++){ - int nx = x + DIRECTIONS[i][0]; - int ny = y + DIRECTIONS[i][1]; - int npos = ny * (env->grid_size) + nx; - if(!is_valid_position(env, nx, ny)){ - continue; - } - if(env->board_states[npos] == 0){ - return npos; // Found a liberty - } else if(env->board_states[npos] == env->board_states[root] && !env->visited[npos]){ - env->visited[npos] = 1; - queue[rear++] = npos; - } - } - } - return -1; // Should not happen if liberties > 0 -} - -void enemy_greedy_hard(CGo* env){ - // Attempt to capture opponent stones in atari - int liberties[4][(env->grid_size) * (env->grid_size)]; - int liberty_counts[4] = {0}; - for(int i = 0; i < (env->grid_size)*(env->grid_size); i++){ - if(env->board_states[i]==0){ - continue; - } - if (env->board_states[i]==1){ - int root = find(env->groups, i); - int group_liberties = env->groups[root].liberties; - if (group_liberties >= 1 && group_liberties <= 4) { - int liberty = find_group_liberty(env, root); - liberties[group_liberties - 1][liberty_counts[group_liberties - 1]++] = liberty; - } - } else if (env->board_states[i]==2){ - int root = find(env->groups, i); - int group_liberties = env->groups[root].liberties; - if (group_liberties==1) { - int liberty = find_group_liberty(env, root); - liberties[group_liberties - 1][liberty_counts[group_liberties - 1]++] = liberty; - } - } - } - // make move to attack or defend - for (int priority = 0; priority < 4; priority++) { - for (int i = 0; i < liberty_counts[priority]; i++) { - if (make_move(env, liberties[priority][i], 2)) { - return; - } - } - } - // random move - enemy_random_move(env); -} - -void enemy_greedy_easy(CGo* env){ - // Attempt to capture opponent stones in atari - for(int i = 0; i < (env->grid_size)*(env->grid_size); i++){ - if(env->board_states[i] != 1){ - continue; - } - int root = find(env->groups, i); - if(env->groups[root].liberties == 1){ - int liberty = find_group_liberty(env, root); - if(make_move(env, liberty, 2)){ - return; // Successful capture - } - } - } - // Protect own stones in atari - for(int i = 0; i < (env->grid_size)*(env->grid_size); i++){ - if(env->board_states[i] != 2){ - continue; - } - // Enemy's own stones - int root = find(env->groups, i); - if(env->groups[root].liberties == 1){ - int liberty = find_group_liberty(env, root); - if(make_move(env, liberty, 2)){ - return; // Successful defense - } - } - } - // Play a random legal move - enemy_random_move(env); -} - -void c_reset(CGo* env) { - env->tick = 0; - // We don't reset the log struct - leave it accumulating like in Pong - env->terminals[0] = 0; - env->score = 0; - for (int i = 0; i < (env->grid_size)*(env->grid_size); i++) { - env->board_states[i] = 0; - env->temp_board_states[i] = 0; - env->visited[i] = 0; - env->previous_board_state[i] = 0; - env->groups[i].parent = i; - env->groups[i].rank = 0; - env->groups[i].size = 0; - env->groups[i].liberties = 0; - } - env->capture_count[0] = 0; - env->capture_count[1] = 0; - env->last_capture_position = -1; - env->moves_made = 0; - compute_observations(env); -} - -void end_game(CGo* env){ - compute_score_tromp_taylor(env); - if (env->score > 0) { - env->rewards[0] = 1.0; - } - else if (env->score < 0) { - env->rewards[0] = -1.0; - } - else { - env->rewards[0] = 0.0; - } - add_log(env); - c_reset(env); -} - -void c_step(CGo* env) { - env->tick += 1; - env->rewards[0] = 0.0; - int action = (int)env->actions[0]; - // useful for training , can prob be a hyper param. Recommend to increase with larger board size - float max_moves = 3 * env->grid_size * env->grid_size; - if (env->tick > max_moves) { - env->terminals[0] = 1; - end_game(env); - compute_observations(env); - return; - } - if(action == NOOP){ - env->rewards[0] = env->reward_move_pass; - env->log.episode_return += env->reward_move_pass; - enemy_greedy_hard(env); - if (env->terminals[0] == 1) { - end_game(env); - return; - } - compute_observations(env); - return; - } - if (action >= MOVE_MIN && action <= (env->grid_size)*(env->grid_size)) { - memcpy(env->previous_board_state, env->board_states, sizeof(int) * (env->grid_size) * (env->grid_size)); - if(make_move(env, action-1, 1)) { - env->moves_made++; - env->rewards[0] = env->reward_move_valid; - env->log.episode_return += env->reward_move_valid; - enemy_greedy_hard(env); - - } else { - env->rewards[0] = env->reward_move_invalid; - env->log.episode_return += env->reward_move_invalid; - } - compute_observations(env); - } - - if(env->rewards[0] > 1){ - env->rewards[0] = 1; - } - if(env->rewards[0] < -1){ - env->rewards[0] = -1; - } - - if (env->terminals[0] == 1) { - end_game(env); - return; - } - - compute_observations(env); -} - -const Color STONE_GRAY = (Color){80, 80, 80, 255}; -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; -const Color PUFF_BACKGROUND2 = (Color){18, 72, 72, 255}; - -struct Client { - float width; - float height; -}; - -Client* make_client(int width, int height) { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = width; - client->height = height; - InitWindow(width, height, "PufferLib Ray Go"); - SetTargetFPS(60); - return client; -} - -void c_render(CGo* env) { - if (env->client == NULL) { - env->client = make_client(env->width, env->height); - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - int board_size = (env->grid_size + 1) * env->grid_square_size; - DrawRectangle(0, 0, board_size, board_size, PUFF_BACKGROUND); - DrawRectangle( - env->grid_square_size, - env->grid_square_size, - board_size - 2*env->grid_square_size, - board_size - 2*env->grid_square_size, - PUFF_BACKGROUND2 - ); - int start = env->grid_square_size; - int end = board_size - env->grid_square_size; - for (int i = 1; i <= env->grid_size; i++) { - DrawLineEx( - (Vector2){start, i*start}, - (Vector2){end, i*start}, - 4, PUFF_BACKGROUND - ); - DrawLineEx( - (Vector2){i*start, start}, - (Vector2){i*start, end}, - 4, PUFF_BACKGROUND - ); - } - - for (int i = 0; i < (env->grid_size) * (env->grid_size); i++) { - int position_state = env->board_states[i]; - int row = i / (env->grid_size); - int col = i % (env->grid_size); - int x = col * env->grid_square_size; - int y = row * env->grid_square_size; - // Calculate the circle position based on the grid - int circle_x = x + env->grid_square_size; - int circle_y = y + env->grid_square_size; - // if player draw circle tile for black - int inner = (env->grid_square_size / 2) - 4; - int outer = (env->grid_square_size / 2) - 2; - if (position_state == 1) { - DrawCircleGradient(circle_x, circle_y, outer, STONE_GRAY, BLACK); - } - // if enemy draw circle tile for white - if (position_state == 2) { - DrawCircleGradient(circle_x, circle_y, inner, WHITE, GRAY); - } - } - // design a pass button - int left = (env->grid_size + 1)*env->grid_square_size; - int top = env->grid_square_size; - DrawRectangle(left, top + 90, 100, 50, GRAY); - DrawText("Pass", left + 25, top + 105, 20, PUFF_WHITE); - - // show capture count for both players - DrawText( - TextFormat("Player 1 Capture Count: %d", env->capture_count[0]), - left, top, 20, PUFF_WHITE - ); - DrawText( - TextFormat("Player 2 Capture Count: %d", env->capture_count[1]), - left, top + 40, 20, PUFF_WHITE - ); - EndDrawing(); -} -void close_client(Client* client) { - CloseWindow(); - free(client); -} diff --git a/pufferlib/ocean/go/go.py b/pufferlib/ocean/go/go.py deleted file mode 100644 index 06b87155d..000000000 --- a/pufferlib/ocean/go/go.py +++ /dev/null @@ -1,92 +0,0 @@ -'''High-perf Pong - -Inspired from https://gist.github.com/Yttrmin/18ecc3d2d68b407b4be1 -& https://jair.org/index.php/jair/article/view/10819/25823 -& https://www.youtube.com/watch?v=PSQt5KGv7Vk -''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.go import binding - -class Go(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, log_interval=1, - width=950, height=800, - grid_size=7, - board_width=600, board_height=600, - grid_square_size=600/9, - moves_made=0, - komi=7.5, - score = 0.0, - last_capture_position=-1, - reward_move_pass = -0.25, - reward_move_invalid = -0.1, - reward_move_valid = 0.1, - reward_player_capture = 0.25, - reward_opponent_capture = -0.25, - buf = None, seed=0): - - # env - self.num_agents = num_envs - self.render_mode = render_mode - self.log_interval = log_interval - self.tick = 0 - self.num_obs = (grid_size) * (grid_size)*2 + 2 - self.num_act = (grid_size) * (grid_size) + 1 - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(self.num_obs,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(self.num_act) - - super().__init__(buf=buf) - height = 64*(grid_size+1) - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, width=width, height=height, grid_size=grid_size, - board_width=board_width, board_height=board_height, grid_square_size=grid_square_size, - moves_made=moves_made, komi=komi, score=score, last_capture_position=last_capture_position, - reward_move_pass=reward_move_pass, reward_move_invalid=reward_move_invalid, - reward_move_valid=reward_move_valid, reward_player_capture=reward_player_capture, - reward_opponent_capture=reward_opponent_capture) - - def reset(self, seed=None): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - self.tick += 1 - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - num_envs=1000 - env = Go(num_envs=num_envs) - env.reset() - tick = 0 - - actions = np.random.randint(0, env.single_action_space.n, (atn_cache, num_envs)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - sps = num_envs * tick / (time.time() - start) - print(f'SPS: {sps:,}') -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/grid/__init__.py b/pufferlib/ocean/grid/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pufferlib/ocean/grid/binding.c b/pufferlib/ocean/grid/binding.c deleted file mode 100644 index 449a8a587..000000000 --- a/pufferlib/ocean/grid/binding.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "grid.h" - -#define Env Grid -#define MY_SHARED -#include "../env_binding.h" - -static PyObject* my_shared(PyObject* self, PyObject* args, PyObject* kwargs) { - int num_maps = unpack(kwargs, "num_maps"); - int max_size = unpack(kwargs, "max_size"); - int size = unpack(kwargs, "size"); - State* levels = calloc(num_maps, sizeof(State)); - - if (max_size <= 5) { - PyErr_SetString(PyExc_ValueError, "max_size must be >5"); - return NULL; - } - - // Temporary env used to gen maps - Grid env; - env.max_size = max_size; - init_grid(&env); - - srand(time(NULL)); - int start_seed = rand(); - for (int i = 0; i < num_maps; i++) { - int sz = size; - if (size == -1) { - sz = 5 + (rand() % (max_size-5)); - } - - if (sz % 2 == 0) { - sz -= 1; - } - - float difficulty = (float)rand()/(float)(RAND_MAX); - create_maze_level(&env, sz, sz, difficulty, start_seed + i); - init_state(&levels[i], max_size, 1); - get_state(&env, &levels[i]); - } - - return PyLong_FromVoidPtr(levels); -} - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->max_size = unpack(kwargs, "max_size"); - env->num_maps = unpack(kwargs, "num_maps"); - init_grid(env); - - PyObject* handle_obj = PyDict_GetItemString(kwargs, "state"); - if (!PyObject_TypeCheck(handle_obj, &PyLong_Type)) { - PyErr_SetString(PyExc_TypeError, "state handle must be an integer"); - return 1; - } - - State* levels = (State*)PyLong_AsVoidPtr(handle_obj); - if (!levels) { - PyErr_SetString(PyExc_ValueError, "Invalid state handle"); - return 1; - } - - env->levels = levels; - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/grid/c_grid.pyx b/pufferlib/ocean/grid/c_grid.pyx deleted file mode 100644 index 64c720a89..000000000 --- a/pufferlib/ocean/grid/c_grid.pyx +++ /dev/null @@ -1,206 +0,0 @@ -# distutils: define_macros=NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION -# cython: language_level=3 -# cython: boundscheck=False -# cython: initializedcheck=False -# cython: wraparound=False -# cython: cdivision=True -# cython: nonecheck=False -# cython: profile=False - -from libc.stdlib cimport rand - -cdef: - int EMPTY = 0 - int FOOD = 1 - int WALL = 2 - int AGENT_1 = 3 - int AGENT_2 = 4 - int AGENT_3 = 5 - int AGENT_4 = 6 - - int PASS = 0 - int NORTH = 1 - int SOUTH = 2 - int EAST = 3 - int WEST = 4 - -cdef class Environment: - cdef: - int width - int height - int num_agents - int horizon - int vision_range - float agent_speed - bint discretize - float food_reward - int expected_lifespan - int obs_size - - unsigned char[:, :] grid - unsigned char[:, :, :] observations - float[:] rewards - float[:, :] agent_positions - float[:, :] spawn_position_cands - int[:] agent_colors - - def __init__(self, grid, agent_positions, spawn_position_cands, agent_colors, - observations, rewards, int width, int height, int num_agents, int horizon, - int vision_range, float agent_speed, bint discretize, float food_reward, - int expected_lifespan): - self.width = width - self.height = height - self.num_agents = num_agents - self.horizon = horizon - self.vision_range = vision_range - self.agent_speed = agent_speed - self.discretize = discretize - self.food_reward = food_reward - self.expected_lifespan = expected_lifespan - self.obs_size = 2*self.vision_range + 1 - - self.grid = grid - self.observations = observations - self.rewards = rewards - self.agent_positions = agent_positions - self.spawn_position_cands = spawn_position_cands - self.agent_colors = agent_colors - - cdef void compute_observations(self): - cdef: - float y - float x - int r - int c - int agent_idx - - for agent_idx in range(self.num_agents): - y = self.agent_positions[agent_idx, 0] - x = self.agent_positions[agent_idx, 1] - r = int(y) - c = int(x) - self.observations[agent_idx, :] = self.grid[ - r-self.vision_range:r+self.vision_range+1, - c-self.vision_range:c+self.vision_range+1 - ] - - cdef void spawn_food(self): - cdef int r, c, tile - while True: - r = rand() % (self.height - 1) - c = rand() % (self.width - 1) - tile = self.grid[r, c] - if tile == EMPTY: - self.grid[r, c] = FOOD - return - - cdef void spawn_agent(self, int agent_idx): - cdef int old_r, old_c, r, c, tile - - # Delete agent from old position - old_r = int(self.agent_positions[agent_idx, 0]) - old_c = int(self.agent_positions[agent_idx, 1]) - self.grid[old_r, old_c] = EMPTY - - r = rand() % (self.height - 1) - c = rand() % (self.width - 1) - tile = self.grid[r, c] - if tile == EMPTY: - # Spawn agent in new position - self.grid[r, c] = self.agent_colors[agent_idx] - self.agent_positions[agent_idx, 0] = r - self.agent_positions[agent_idx, 1] = c - return - - def reset(self, seed=0): - # Add borders - cdef int left = int(self.agent_speed * self.vision_range) - cdef int right = self.width - int(self.agent_speed*self.vision_range) - 1 - cdef int bottom = self.height - int(self.agent_speed*self.vision_range) - 1 - self.grid[:left, :] = WALL - self.grid[:, :left] = WALL - self.grid[bottom:, :] = WALL - self.grid[:, right:] = WALL - - # Agent spawning - cdef: - int spawn_idx - float y - float x - int disc_y - int disc_x - int agent_idx = 0 - - for spawn_idx in range(self.width*self.height): - y = self.spawn_position_cands[spawn_idx, 0] - x = self.spawn_position_cands[spawn_idx, 1] - disc_y = int(y) - disc_x = int(x) - - if self.grid[disc_y, disc_x] == EMPTY: - self.grid[disc_y, disc_x] = self.agent_colors[agent_idx] - self.agent_positions[agent_idx, 0] = y - self.agent_positions[agent_idx, 1] = x - agent_idx += 1 - if agent_idx == self.num_agents: - break - - self.compute_observations() - - def step(self, np_actions): - cdef: - float[:, :] actions_continuous - unsigned int[:, :] actions_discrete - int agent_idx - float y - float x - float vel_y - float vel_x - int disc_y - int disc_x - int disc_dest_y - int disc_dest_x - - if self.discretize: - actions_discrete = np_actions - else: - actions_continuous = np_actions - - for agent_idx in range(self.num_agents): - if self.discretize: - # Convert [0, 1, 2] to [-1, 0, 1] - vel_y = float(actions_discrete[agent_idx, 0]) - 1.0 - vel_x = float(actions_discrete[agent_idx, 1]) - 1.0 - else: - vel_y = actions_continuous[agent_idx, 0] - vel_x = actions_continuous[agent_idx, 1] - - y = self.agent_positions[agent_idx, 0] - x = self.agent_positions[agent_idx, 1] - dest_y = y + self.agent_speed * vel_y - dest_x = x + self.agent_speed * vel_x - - # Discretize - disc_y = int(y) - disc_x = int(x) - disc_dest_y = int(dest_y) - disc_dest_x = int(dest_x) - - if self.grid[disc_dest_y, disc_dest_x] == FOOD: - self.grid[disc_dest_y, disc_dest_x] = EMPTY - self.rewards[agent_idx] = self.food_reward - self.spawn_food() - - if self.grid[disc_dest_y, disc_dest_x] == 0: - self.grid[disc_y, disc_x] = EMPTY - self.grid[disc_dest_y, disc_dest_x] = self.agent_colors[agent_idx] - - # Continuous position update - self.agent_positions[agent_idx, 0] = dest_y - self.agent_positions[agent_idx, 1] = dest_x - - # Randomly respawn agents - if rand() % self.expected_lifespan == 0: - self.spawn_agent(agent_idx) - - self.compute_observations() diff --git a/pufferlib/ocean/grid/cy_grid.pyx b/pufferlib/ocean/grid/cy_grid.pyx deleted file mode 100644 index bfafe25c2..000000000 --- a/pufferlib/ocean/grid/cy_grid.pyx +++ /dev/null @@ -1,186 +0,0 @@ -# distutils: define_macros=NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION -# cython: language_level=3 -# cython: boundscheck=False -# cython: initializedcheck=False -# cython: wraparound=False -# cython: cdivision=True -# cython: nonecheck=False -# cython: profile=True - -from libc.stdlib cimport calloc, free, rand - -cdef extern from "grid.h": - int LOG_BUFFER_SIZE - - ctypedef struct Log: - float episode_return; - float episode_length; - float score; - - ctypedef struct LogBuffer - LogBuffer* allocate_logbuffer(int) - void free_logbuffer(LogBuffer*) - Log aggregate_and_clear(LogBuffer*) - - ctypedef struct Agent: - float y; - float x; - float prev_y; - float prev_x; - float spawn_y; - float spawn_x; - int color; - float direction; - int held; - - ctypedef struct Grid: - int width; - int height; - int num_agents; - int horizon; - int vision; - float speed; - int obs_size; - int max_size; - bint discretize; - Log log; - LogBuffer* log_buffer; - Agent* agents; - unsigned char* grid; - int* counts; - unsigned char* observations; - float* actions; - float* rewards; - unsigned char* dones; - - ctypedef struct State: - int width; - int height; - int num_agents; - Agent* agents; - unsigned char* grid; - - cdef: - void create_maze_level(Grid* env, int width, int height, float difficulty, int seed) - void load_locked_room_env(unsigned char* observations, - unsigned int* actions, float* rewards, float* dones) - void init_grid(Grid* env) - void reset(Grid* env, int seed) - void compute_observations(Grid* env) - bint step(Grid* env) - ctypedef struct Renderer - Renderer* init_renderer(int cell_size, int width, int height) - void render_global(Renderer*erenderer, Grid* env, float frac, float overlay) - void clear_overlay(Renderer* renderer) - void close_renderer(Renderer* renderer) - void init_state(State* state, int max_size, int num_agents) - void free_state(State* state) - void get_state(Grid* env, State* state) - void set_state(Grid* env, State* state) - -import numpy as np -cimport numpy as cnp - -cdef class CGrid: - cdef: - Grid* envs - State* levels - Renderer* client - LogBuffer* logs - int num_envs - int num_maps - int max_size - - def __init__(self, unsigned char[:, :] observations, float[:] actions, - float[:] rewards, unsigned char[:] terminals, int num_envs, int num_maps, - int size, int max_size): - - self.num_envs = num_envs - self.num_maps = num_maps - if size > max_size: - max_size = size - - self.max_size = max_size - - self.client = NULL - self.levels = calloc(num_maps, sizeof(State)) - self.envs = calloc(num_envs, sizeof(Grid)) - self.logs = allocate_logbuffer(LOG_BUFFER_SIZE) - - cdef int i - for i in range(num_envs): - self.envs[i] = Grid( - observations = &observations[i, 0], - actions = &actions[i], - rewards = &rewards[i], - dones = &terminals[i], - log_buffer = self.logs, - max_size = max_size, - num_agents = 1, - vision = 5, - speed = 1, - discretize = True, - ) - init_grid(&self.envs[i]) - - cdef float difficulty - cdef int sz - for i in range(num_maps): - - # RNG or fixed size - if size == -1: - sz = np.random.randint(5, max_size) - else: - sz = size - - if sz % 2 == 0: - sz -= 1 - - difficulty = np.random.rand() - create_maze_level(&self.envs[0], sz, sz, difficulty, i) - init_state(&self.levels[i], max_size, 1) - get_state(&self.envs[0], &self.levels[i]) - - def reset(self): - cdef int i, idx - for i in range(self.num_envs): - idx = rand() % self.num_maps - reset(&self.envs[i], i) - set_state(&self.envs[i], &self.levels[idx]) - compute_observations(&self.envs[i]) - - def step(self): - cdef: - int i, idx - bint done - - for i in range(self.num_envs): - done = step(&self.envs[i]) - if done: - idx = rand() % self.num_maps - reset(&self.envs[i], i) - set_state(&self.envs[i], &self.levels[idx]) - - if i == 0 and self.client != NULL: - clear_overlay(self.client) - - def render(self, int cell_size=16, float overlay=0.0): - if self.client == NULL: - import os - cwd = os.getcwd() - os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) - self.client = init_renderer(cell_size, self.max_size, self.max_size) - os.chdir(cwd) - - render_global(self.client, &self.envs[0], 0, overlay) - - def log(self): - cdef Log log = aggregate_and_clear(self.logs) - return log - - def close(self): - if self.client != NULL: - close_renderer(self.client) - self.client = NULL - - #free_envs(self.envs, self.num_envs) diff --git a/pufferlib/ocean/grid/grid.c b/pufferlib/ocean/grid/grid.c deleted file mode 100644 index 3a267a313..000000000 --- a/pufferlib/ocean/grid/grid.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "grid.h" - -int main() { - int max_size = 32; - int width = 32; - int height = 32; - int num_agents = 1; - int horizon = 128; - float speed = 1; - int vision = 5; - bool discretize = true; - - int render_cell_size = 32; - int seed = 0; - - Grid* env = allocate_grid(max_size, num_agents, horizon, - vision, speed, discretize); - - //env->width = 32; - //env->height = 32; env->agents[0].spawn_x = 16; - //env->agents[0].spawn_y = 16; - //env->agents[0].color = 6; - //reset(env, seed); - //load_locked_room_preset(env); - - - State* levels = calloc(1, sizeof(State)); - - create_maze_level(env, 31, 31, 0.85, seed); - init_state(levels, max_size, num_agents); - get_state(env, levels); - env->num_maps = 1; - env->levels = levels; - //generate_locked_room(env); - //State state; - //init_state(&state, env->max_size, env->num_agents); - //get_state(env, &state); - - /* - width = height = 31; - env->width=31; - env->height=31; - env->agents[0].spawn_x = 1; - env->agents[0].spawn_y = 1; - reset(env, seed); - generate_growing_tree_maze(env->grid, env->width, env->height, max_size, 0.85, 0); - env->grid[(env->height-2)*env->max_size + (env->width - 2)] = GOAL; - */ - - int tick = 0; - c_render(env); - while (!WindowShouldClose()) { - // User can take control of the first agent - env->actions[0] = ATN_FORWARD; - Agent* agent = &env->agents[0]; - - // TODO: Why are up and down flipped? - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)){ - //env->actions[0] = ATN_FORWARD; - agent->direction = 3.0*PI/2.0; - } else if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) { - //env->actions[0] = ATN_BACK; - agent->direction = PI/2.0; - } else if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) { - //env->actions[0] = ATN_LEFT; - agent->direction = PI; - } else if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) { - //env->actions[0] = ATN_RIGHT; - agent->direction = 0; - } else { - env->actions[0] = ATN_PASS; - } - } else { - for (int i = 0; i < num_agents; i++) { - env->actions[i] = rand() % 5; - } - } - - //env->actions[0] = actions[t]; - tick = (tick + 1)%12; - bool done = false; - if (tick % 1 == 0) { - c_step(env); - //printf("direction: %f\n", env->agents[0].direction); - - } - c_render(env); - } - free_allocated_grid(env); - return 0; -} - diff --git a/pufferlib/ocean/grid/grid.h b/pufferlib/ocean/grid/grid.h deleted file mode 100644 index c82001d75..000000000 --- a/pufferlib/ocean/grid/grid.h +++ /dev/null @@ -1,767 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" - -#define TWO_PI 2.0*PI -#define MAX_SIZE 40 - -#define ATN_PASS 0 -#define ATN_FORWARD 1 -#define ATN_LEFT 2 -#define ATN_RIGHT 3 -#define ATN_BACK 4 - -#define DIR_WEST 0.0; -#define DIR_NORTH PI/2.0; -#define DIR_EAST PI; -#define DIR_SOUTH 3.0*PI/2.0; - -#define EMPTY 0 -#define WALL 1 -#define LAVA 2 -#define GOAL 3 -#define REWARD 4 -#define OBJECT 5 -#define AGENT 6 -#define KEY 14 -#define DOOR_LOCKED 20 -#define DOOR_OPEN 26 - -#define LOG_BUFFER_SIZE 4096 - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -// 8 unique agents -bool is_agent(int idx) { - return idx >= AGENT && idx < AGENT + 8; -} -int rand_color() { - return AGENT + rand()%8; -} - -// 6 unique keys and doors -bool is_key(int idx) { - return idx >= KEY && idx < KEY + 6; -} -bool is_locked_door(int idx) { - return idx >= DOOR_LOCKED && idx < DOOR_LOCKED + 6; -} -bool is_open_door(int idx) { - return idx >= DOOR_OPEN && idx <= DOOR_OPEN + 6; -} -bool is_correct_key(int key, int door) { - return key == door - 6; -} - -typedef struct Agent Agent; -struct Agent { - float y; - float x; - float prev_y; - float prev_x; - float spawn_y; - float spawn_x; - int color; - float direction; - int held; -}; - -typedef struct Renderer Renderer; -typedef struct State State; -typedef struct Grid Grid; -struct Grid{ - Renderer* renderer; - State* levels; - int num_maps; - int width; - int height; - int num_agents; - int horizon; - int vision; - int tick; - float speed; - int obs_size; - int max_size; - bool discretize; - Log log; - Agent* agents; - unsigned char* grid; - int* counts; - unsigned char* observations; - float* actions; - float* rewards; - unsigned char* terminals; -}; - -void init_grid(Grid* env) { - env->num_agents = 1; - env->vision = 5; - env->speed = 1; - env->discretize = true; - env->obs_size = 2*env->vision + 1; - int env_mem= env->max_size * env->max_size; - env->grid = calloc(env_mem, sizeof(unsigned char)); - env->counts = calloc(env_mem, sizeof(int)); - env->agents = calloc(env->num_agents, sizeof(Agent)); -} - -Grid* allocate_grid(int max_size, int num_agents, int horizon, - int vision, float speed, bool discretize) { - Grid* env = (Grid*)calloc(1, sizeof(Grid)); - env->max_size = max_size; - env->num_agents = num_agents; - env->horizon = horizon; - env->vision = vision; - env->speed = speed; - env->discretize = discretize; - int obs_size = 2*vision + 1; - env->observations = calloc( - num_agents*obs_size*obs_size, sizeof(unsigned char)); - env->actions = calloc(num_agents, sizeof(float)); - env->rewards = calloc(num_agents, sizeof(float)); - env->terminals = calloc(num_agents, sizeof(unsigned char)); - init_grid(env); - return env; -} - -void c_close(Grid* env) { - free(env->grid); - free(env->counts); - free(env->agents); - free(env); -} - -void free_allocated_grid(Grid* env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - c_close(env); -} - -bool in_bounds(Grid* env, int y, int c) { - return (y >= 0 && y <= env->height - && c >= 0 && c <= env->width); -} - -int grid_offset(Grid* env, int y, int x) { - return y*env->max_size + x; -} - -void add_log(Grid* env, int idx) { - env->log.perf += env->rewards[idx]; - env->log.score += env->rewards[idx]; - env->log.episode_return += env->rewards[idx]; - env->log.episode_length += env->tick; - env->log.n += 1.0; -} - -void compute_observations(Grid* env) { - memset(env->observations, 0, env->obs_size*env->obs_size*env->num_agents); - for (int agent_idx = 0; agent_idx < env->num_agents; agent_idx++) { - Agent* agent = &env->agents[agent_idx]; - float y = agent->y; - float x = agent->x; - int start_r = y - env->vision; - if (start_r < 0) { - start_r = 0; - } - - int start_c = x - env->vision; - if (start_c < 0) { - start_c = 0; - } - - int end_r = y + env->vision; - if (end_r >= env->max_size) { - end_r = env->max_size - 1; - } - - int end_c = x + env->vision; - if (end_c >= env->max_size) { - end_c = env->max_size - 1; - } - - int obs_offset = agent_idx*env->obs_size*env->obs_size; - for (int r = start_r; r <= end_r; r++) { - for (int c = start_c; c <= end_c; c++) { - int r_idx = r - y + env->vision; - int c_idx = c - x + env->vision; - int obs_adr = obs_offset + r_idx*env->obs_size + c_idx; - int adr = grid_offset(env, r, c); - env->observations[obs_adr] = env->grid[adr]; - } - } - /* - int obs_adr = 0; - for (int r = 0; r < env->obs_size; r++) { - for (int c = 0; c < env->obs_size; c++) { - printf("%d ", env->observations[obs_adr]); - obs_adr++; - } - printf("\n"); - } - */ - } -} - -void make_border(Grid*env) { - for (int r = 0; r < env->height; r++) { - int adr = grid_offset(env, r, 0); - env->grid[adr] = WALL; - adr = grid_offset(env, r, env->width-1); - env->grid[adr] = WALL; - } - for (int c = 0; c < env->width; c++) { - int adr = grid_offset(env, 0, c); - env->grid[adr] = WALL; - adr = grid_offset(env, env->height-1, c); - env->grid[adr] = WALL; - } -} - -void spawn_agent(Grid* env, int idx, int x, int y) { - Agent* agent = &env->agents[idx]; - int spawn_y = y; - int spawn_x = x; - assert(in_bounds(env, spawn_y, spawn_x)); - int adr = grid_offset(env, spawn_y, spawn_x); - assert(env->grid[adr] == EMPTY); - agent->spawn_y = spawn_y; - agent->spawn_x = spawn_x; - agent->y = agent->spawn_y; - agent->x = agent->spawn_x; - agent->prev_y = agent->y; - agent->prev_x = agent->x; - env->grid[adr] = agent->color; - agent->direction = 0; - agent->held = -1; - agent->color = AGENT; -} - -struct State { - int width; - int height; - int num_agents; - Agent* agents; - unsigned char* grid; -}; - -void init_state(State* state, int max_size, int num_agents) { - state->agents = calloc(num_agents, sizeof(Agent)); - state->grid = calloc(max_size*max_size, sizeof(unsigned char)); -} - -void free_state(State* state) { - free(state->agents); - free(state->grid); - free(state); -} - -void get_state(Grid* env, State* state) { - state->width = env->width; - state->height = env->height; - state->num_agents = env->num_agents; - memcpy(state->agents, env->agents, env->num_agents*sizeof(Agent)); - memcpy(state->grid, env->grid, env->max_size*env->max_size); -} - -void set_state(Grid* env, State* state) { - env->width = state->width; - env->height = state->height; - env->horizon = 2*env->width*env->height; - env->num_agents = state->num_agents; - memcpy(env->agents, state->agents, env->num_agents*sizeof(Agent)); - memcpy(env->grid, state->grid, env->max_size*env->max_size); -} - -void c_reset(Grid* env) { - memset(env->grid, 0, env->max_size*env->max_size); - memset(env->counts, 0, env->max_size*env->max_size*sizeof(int)); - env->tick = 0; - int idx = rand() % env->num_maps; - set_state(env, &env->levels[idx]); - compute_observations(env); -} - -int move_to(Grid* env, int agent_idx, float y, float x) { - Agent* agent = &env->agents[agent_idx]; - if (!in_bounds(env, y, x)) { - return 1; - } - - int adr = grid_offset(env, round(y), round(x)); - int dest = env->grid[adr]; - if (dest == WALL) { - return 1; - } else if (dest == REWARD || dest == GOAL) { - env->rewards[agent_idx] = 1.0; - env->terminals[agent_idx] = 1; - add_log(env, agent_idx); - } else if (is_key(dest)) { - if (agent->held != -1) { - return 1; - } - agent->held = dest; - } else if (is_locked_door(dest)) { if (!is_correct_key(agent->held, dest)) { return 1; - } - agent->held = -1; - env->grid[adr] = EMPTY; - } - - int start_y = round(agent->y); - int start_x = round(agent->x); - int start_adr = grid_offset(env, start_y, start_x); - env->grid[start_adr] = EMPTY; - - env->grid[adr] = agent->color; - agent->y = y; - agent->x = x; - return 0; -} - -bool step_agent(Grid* env, int idx) { - Agent* agent = &env->agents[idx]; - agent->prev_y = agent->y; - agent->prev_x = agent->x; - - float atn = env->actions[idx]; - float direction = agent->direction; - - if (env->discretize) { - int iatn = (int)atn; - if (iatn == ATN_PASS) { - return true; - } else if (iatn == ATN_FORWARD) { - } else if (iatn == ATN_LEFT) { - direction -= PI/2.0; - } else if (iatn == ATN_RIGHT) { - direction += PI/2.0; - } else if (iatn == ATN_BACK) { - direction += PI; - } else { - printf("Invalid action: %f\n", atn); - exit(1); - } - if (direction < 0) { - direction += TWO_PI; - } else if (direction >= TWO_PI) { - direction -= TWO_PI; - } - } else { - assert(atn >= -1.0); - assert(atn <= 1.0); - direction += PI*atn; - } - - float x = agent->x; - float y = agent->y; - float dx = env->speed*cos(direction); - float dy = env->speed*sin(direction); - agent->direction = direction; - if (env->discretize) { - float dest_x = x + dx; - float dest_y = y + dy; - if (!in_bounds(env, dest_y, dest_x)) { - return false; - } - int err = move_to(env, idx, dest_y, dest_x); - if (err) { - return false; - } - } else { - for (int substep = 1; substep <= 4; substep++) { - float dest_x = x + dx/(float)substep; - float dest_y = y + dy/(float)substep; - int err = move_to(env, idx, dest_y, dest_x); - if (!err) { - continue; - } else if (substep == 1) { - return false; - } else { - break; - } - } - } - - int x_int = agent->x; - int y_int = agent->y; - int adr = grid_offset(env, y_int, x_int); - env->counts[adr]++; - //env->rewards[idx] += 0.01 / (float)env->counts[adr]; - //env->log.episode_return += 0.01 / (float)env->counts[adr]; - return true; -} - -void c_step(Grid* env) { - memset(env->terminals, 0, env->num_agents); - memset(env->rewards, 0, env->num_agents*sizeof(float)); - env->tick++; - - for (int i = 0; i < env->num_agents; i++) { - step_agent(env, i); - } - compute_observations(env); - - bool done = true; - for (int i = 0; i < env->num_agents; i++) { - if (!env->terminals[i]) { - done = false; - break; - } - } - - if (env->tick >= env->horizon) { - done = true; - add_log(env, 0); - } - - if (done) { - c_reset(env); - int idx = rand() % env->num_maps; - set_state(env, &env->levels[idx]); - compute_observations(env); - } -} - -// Raylib client -Color COLORS[] = { - (Color){6, 24, 24, 255}, - (Color){0, 0, 255, 255}, - (Color){0, 128, 255, 255}, - (Color){128, 128, 128, 255}, - (Color){255, 0, 0, 255}, - (Color){255, 255, 255, 255}, - (Color){255, 85, 85, 255}, - (Color){170, 170, 170, 255}, - (Color){0, 255, 255, 255}, - (Color){255, 255, 0, 255}, -}; - -Rectangle UV_COORDS[7] = { - (Rectangle){0, 0, 0, 0}, - (Rectangle){512, 0, 128, 128}, - (Rectangle){0, 0, 0, 0}, - (Rectangle){0, 0, 128, 128}, - (Rectangle){128, 0, 128, 128}, - (Rectangle){256, 0, 128, 128}, - (Rectangle){384, 0, 128, 128}, -}; - -struct Renderer { - int cell_size; - int width; - int height; - Texture2D puffer; - float* overlay; -}; - -Renderer* init_renderer(int cell_size, int width, int height) { - Renderer* renderer = (Renderer*)calloc(1, sizeof(Renderer)); - renderer->cell_size = cell_size; - renderer->width = width; - renderer->height = height; - - renderer->overlay = (float*)calloc(width*height, sizeof(float)); - - InitWindow(width*cell_size, height*cell_size, "PufferLib Grid"); - SetTargetFPS(60); - - renderer->puffer = LoadTexture("resources/shared/puffers_128.png"); - return renderer; -} - -void clear_overlay(Renderer* renderer) { - memset(renderer->overlay, 0, renderer->width*renderer->height*sizeof(float)); -} - -void close_renderer(Renderer* renderer) { - CloseWindow(); - free(renderer->overlay); - free(renderer); -} - -void c_render(Grid* env) { - // TODO: fractional rendering - float frac = 0.0; - float overlay = 0.0; - if (env->renderer == NULL) { - env->renderer = init_renderer(16, env->max_size, env->max_size); - } - Renderer* renderer = env->renderer; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - Agent* agent = &env->agents[0]; - int r = agent->y; - int c = agent->x; - int adr = grid_offset(env, r, c); - //renderer->overlay[adr] = overlay; - //renderer->overlay[adr] -= 0.1; - //renderer->overlay[adr] = -1 + 1.0/(float)env->counts[adr]; - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - int ts = renderer->cell_size; - for (int r = 0; r < env->height; r++) { - for (int c = 0; c < env->width; c++){ - adr = grid_offset(env, r, c); - int tile = env->grid[adr]; - if (tile == EMPTY) { - continue; - overlay = renderer->overlay[adr]; - if (overlay == 0) { - continue; - } - Color color; - if (overlay < 0) { - overlay = -fmaxf(-1.0, overlay); - color = (Color){255.0*overlay, 0, 0, 255}; - } else { - overlay = fminf(1.0, overlay); - color = (Color){0, 255.0*overlay, 0, 255}; - } - DrawRectangle(c*ts, r*ts, ts, ts, color); - } - - Color color; - if (tile == WALL) { - color = (Color){128, 128, 128, 255}; - } else if (tile == GOAL) { - color = GREEN; - } else if (is_locked_door(tile)) { - int weight = 40*(tile - DOOR_LOCKED); - color = (Color){weight, 0, 0, 255}; - } else if (is_open_door(tile)) { - int weight = 40*(tile - DOOR_OPEN); - color = (Color){0, weight, 0, 255}; - } else if (is_key(tile)) { - int weight = 40*(tile - KEY); - color = (Color){0, 0, weight, 255}; - } else { - continue; - } - - DrawRectangle(c*ts, r*ts, ts, ts, color); - } - } - - for (int i = 0; i < env->num_agents; i++) { - agent = &env->agents[0]; - float y = agent->y + (frac - 1)*(agent->y - agent->prev_y); - float x = agent->x + (frac - 1)*(agent->x - agent->prev_x); - int u = 0; - int v = 0; - Rectangle source_rect = (Rectangle){u, v, 128, 128}; - Rectangle dest_rect = (Rectangle){x*ts, y*ts, ts, ts}; - DrawTexturePro(renderer->puffer, source_rect, dest_rect, - (Vector2){0, 0}, 0, WHITE); - } - - EndDrawing(); -} - -void generate_locked_room(Grid* env) { - assert(env->max_size >= 19); - env->width = 19; - env->height = 19; - env->num_agents = 1; - env->horizon = 1000; - env->speed = 1; - env->vision = 3; - env->discretize = true; - - Agent* agent = &env->agents[0]; - agent->x = 9; - agent->y = 9; - agent->prev_x = 9; - agent->prev_y = 9; - agent->spawn_y = 9; - agent->spawn_x = 9; - agent->color = 6; - agent->held = -1; - - make_border(env); - - for (int r = 0; r < env->height; r++) { - int adr = grid_offset(env, r, 7); - env->grid[adr] = WALL; - adr = grid_offset(env, r, 11); - env->grid[adr] = WALL; - } - for (int c = 0; c < 7; c++) { - int adr = grid_offset(env, 6, c); - env->grid[adr] = WALL; - adr = grid_offset(env, 12, c); - env->grid[adr] = WALL; - } - for (int c = 11; c < env->width; c++) { - int adr = grid_offset(env, 6, c); - env->grid[adr] = WALL; - adr = grid_offset(env, 12, c); - env->grid[adr] = WALL; - } - int adr = grid_offset(env, 3, 7); - env->grid[adr] = DOOR_OPEN; - adr = grid_offset(env, 9, 7); - env->grid[adr] = DOOR_OPEN + 1; - adr = grid_offset(env, 15, 7); - env->grid[adr] = DOOR_OPEN + 2; - adr = grid_offset(env, 3, 11); - env->grid[adr] = DOOR_OPEN + 3; - adr = grid_offset(env, 9, 11); - env->grid[adr] = DOOR_OPEN + 4; - adr = grid_offset(env, 15, 11); - env->grid[adr] = DOOR_LOCKED + 5; - - adr = grid_offset(env, 4, 15); - env->grid[adr] = KEY + 5; - - adr = grid_offset(env, 16, 17); - env->grid[adr] = GOAL; -} - -void generate_growing_tree_maze(unsigned char* grid, - int width, int height, int max_size, float difficulty, int seed) { - srand(seed); - int dx[4] = {-1, 0, 1, 0}; - int dy[4] = {0, 1, 0, -1}; - int dirs[4] = {0, 1, 2, 3}; - int cells[2*width*height]; - int num_cells = 1; - - bool visited[width*height]; - memset(visited, false, width*height); - - memset(grid, WALL, max_size*height); - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*max_size + c; - if (r % 2 == 1 && c % 2 == 1) { - grid[adr] = EMPTY; - } - } - } - - int x_init = rand() % (width - 1); - int y_init = rand() % (height - 1); - - if (x_init % 2 == 0) { - x_init++; - } - if (y_init % 2 == 0) { - y_init++; - } - - int adr = y_init*height + x_init; - visited[adr] = true; - cells[0] = x_init; - cells[1] = y_init; - - //int cell = 32; - //InitWindow(width*cell, height*cell, "PufferLib Ray Grid"); - //SetTargetFPS(60); - - while (num_cells > 0) { - if (rand() % 1000 > 1000*difficulty) { - int i = rand() % num_cells; - int tmp_x = cells[2*num_cells - 2]; - int tmp_y = cells[2*num_cells - 1]; - cells[2*num_cells - 2] = cells[2*i]; - cells[2*num_cells - 1] = cells[2*i + 1]; - cells[2*i] = tmp_x; - cells[2*i + 1] = tmp_y; - - } - - int x = cells[2*num_cells - 2]; - int y = cells[2*num_cells - 1]; - - int nx, ny; - - // In-place direction shuffle - for (int i = 0; i < 4; i++) { - int ii = i + rand() % (4 - i); - int tmp = dirs[i]; - dirs[i] = dirs[ii]; - dirs[ii] = tmp; - } - - bool made_path = false; - for (int dir_i = 0; dir_i < 4; dir_i++) { - int dir = dirs[dir_i]; - nx = x + 2*dx[dir]; - ny = y + 2*dy[dir]; - - if (nx <= 0 || nx >= width-1 || ny <= 0 || ny >= height-1) { - continue; - } - - int visit_adr = ny*width + nx; - if (visited[visit_adr]) { - continue; - } - - visited[visit_adr] = true; - cells[2*num_cells] = nx; - cells[2*num_cells + 1] = ny; - - nx = x + dx[dir]; - ny = y + dy[dir]; - - int adr = ny*max_size + nx; - grid[adr] = EMPTY; - num_cells++; - - made_path = true; - - /* - if (IsKeyPressed(KEY_ESCAPE)) { - exit(0); - } - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - Color color = (Color){128, 128, 128, 255}; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++){ - int adr = r*max_size + c; - int tile = grid[adr]; - if (tile == WALL) { - DrawRectangle(c*cell, r*cell, cell, cell, color); - } - } - } - EndDrawing(); - */ - - break; - } - if (!made_path) { - num_cells--; - } - } -} - -void create_maze_level(Grid* env, int width, int height, float difficulty, int seed) { - env->width = width; - env->height = height; - generate_growing_tree_maze(env->grid, width, height, env->max_size, difficulty, seed); - make_border(env); - spawn_agent(env, 0, 1, 1); - int goal_adr = grid_offset(env, env->height - 2, env->width - 2); - env->grid[goal_adr] = GOAL; -} diff --git a/pufferlib/ocean/grid/grid.py b/pufferlib/ocean/grid/grid.py deleted file mode 100644 index 0229d6fe7..000000000 --- a/pufferlib/ocean/grid/grid.py +++ /dev/null @@ -1,67 +0,0 @@ -import numpy as np -import os - -import gymnasium - -import pufferlib -from pufferlib.ocean.grid import binding - -class Grid(pufferlib.PufferEnv): - def __init__(self, render_mode='raylib', vision_range=5, - num_envs=4096, num_maps=1000, map_size=-1, max_size=9, - report_interval=128, buf=None, seed=0): - assert map_size <= max_size - self.obs_size = 2*vision_range + 1 - self.single_observation_space = gymnasium.spaces.Box(low=0, high=255, - shape=(self.obs_size*self.obs_size,), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.Discrete(5) - self.render_mode = render_mode - self.num_agents = num_envs - self.report_interval = report_interval - super().__init__(buf=buf) - self.float_actions = np.zeros_like(self.actions).astype(np.float32) - self.c_state = binding.shared(num_maps=num_maps, max_size=max_size, size=map_size) - self.c_envs = binding.vec_init(self.observations, self.float_actions, - self.rewards, self.terminals, self.truncations, num_envs, seed, - state=self.c_state, max_size=max_size, num_maps=num_maps) - pass - - def reset(self, seed=None): - self.tick = 0 - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.float_actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.report_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - self.tick += 1 - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self, overlay=0): - binding.vec_render(self.c_envs, overlay) - - def close(self): - pass - #binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = CGrid(num_envs=1000) - env.reset() - tick = 0 - - actions = np.random.randint(0, 2, (atn_cache, env.num_envs)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: %f', env.num_envs * tick / (time.time() - start)) diff --git a/pufferlib/ocean/impulse_wars/.clang-format b/pufferlib/ocean/impulse_wars/.clang-format deleted file mode 100644 index d9ba19d3d..000000000 --- a/pufferlib/ocean/impulse_wars/.clang-format +++ /dev/null @@ -1,229 +0,0 @@ ---- -Language: Cpp -AccessModifierOffset: -2 -AlignAfterOpenBracket: BlockIndent -AlignArrayOfStructures: None -AlignConsecutiveAssignments: - Enabled: false - AcrossEmptyLines: false - AcrossComments: false - AlignCompound: false - AlignFunctionPointers: false - PadOperators: true -AlignConsecutiveBitFields: - Enabled: false - AcrossEmptyLines: false - AcrossComments: false - AlignCompound: false - AlignFunctionPointers: false - PadOperators: false -AlignConsecutiveDeclarations: - Enabled: false - AcrossEmptyLines: false - AcrossComments: false - AlignCompound: false - AlignFunctionPointers: false - PadOperators: false -AlignConsecutiveMacros: - Enabled: false - AcrossEmptyLines: false - AcrossComments: false - AlignCompound: false - AlignFunctionPointers: false - PadOperators: false -AlignConsecutiveShortCaseStatements: - Enabled: false - AcrossEmptyLines: false - AcrossComments: false - AlignCaseColons: false -AlignEscapedNewlines: Right -AlignOperands: Align -AlignTrailingComments: - Kind: Always - OverEmptyLines: 0 -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowBreakBeforeNoexceptSpecifier: Never -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortCompoundRequirementOnASingleLine: true -AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: MultiLine -AttributeMacros: - - __capability -BinPackArguments: false -BinPackParameters: false -BitFieldColonSpacing: Both -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterExternBlock: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakAdjacentStringLiterals: true -BreakAfterAttributes: Leave -BreakAfterJavaFieldAnnotations: false -BreakArrays: true -BreakBeforeBinaryOperators: None -BreakBeforeClosingBracket: Always -BreakBeforeConceptDeclarations: Always -BreakBeforeBraces: Custom -BreakBeforeInlineASMColon: OnlyMultiline -BreakBeforeTernaryOperators: true -BreakConstructorInitializers: BeforeColon -BreakInheritanceList: BeforeColon -BreakStringLiterals: true -ColumnLimit: 0 -CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: LogicalBlock -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: false -IfMacros: - - KJ_IF_MAYBE -IncludeBlocks: Preserve -IncludeIsMainRegex: '(Test)?$' -IncludeIsMainSourceRegex: '' -IndentAccessModifiers: false -IndentCaseBlocks: false -IndentCaseLabels: false -IndentExternBlock: AfterExternBlock -IndentGotoLabels: true -IndentPPDirectives: None -IndentRequiresClause: true -IndentWidth: 4 -IndentWrappedFunctionNames: false -InsertBraces: true -InsertNewlineAtEOF: false -InsertTrailingCommas: Wrapped -IntegerLiteralSeparator: - Binary: 0 - BinaryMinDigits: 0 - Decimal: 0 - DecimalMinDigits: 0 - Hex: 0 - HexMinDigits: 0 -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -KeepEmptyLinesAtEOF: false -LambdaBodyIndentation: Signature -LineEnding: DeriveLF -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 2 -ObjCBreakBeforeNestedBlockParam: true -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PackConstructorInitializers: Never -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakOpenParenthesis: 0 -PenaltyBreakScopeResolution: 500 -PenaltyBreakString: 1000 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 1000000 -PenaltyIndentedWhitespace: 0 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right -PPIndentWidth: -1 -QualifierAlignment: Leave -ReferenceAlignment: Pointer -ReflowComments: true -RemoveBracesLLVM: false -RemoveParentheses: Leave -RemoveSemicolon: false -RequiresClausePosition: OwnLine -RequiresExpressionIndentation: OuterScope -SeparateDefinitionBlocks: Leave -ShortNamespaceLines: 1 -SkipMacroDefinitionBody: false -SortIncludes: CaseSensitive -SortJavaStaticImport: Before -SortUsingDeclarations: LexicographicNumeric -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceAroundPointerQualifiers: Default -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeJsonColon: false -SpaceBeforeParens: ControlStatements -SpaceBeforeParensOptions: - AfterControlStatements: true - AfterForeachMacros: true - AfterFunctionDefinitionName: false - AfterFunctionDeclarationName: false - AfterIfMacros: true - AfterOverloadedOperator: false - AfterPlacementOperator: true - AfterRequiresInClause: false - AfterRequiresInExpression: false - BeforeNonEmptyParentheses: false -SpaceBeforeRangeBasedForLoopColon: true -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: Never -SpacesInContainerLiterals: true -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 -SpacesInParens: Never -SpacesInParensOptions: - InCStyleCasts: false - InConditionalStatements: false - InEmptyParentheses: false - Other: false -SpacesInSquareBrackets: false -Standard: Latest -StatementAttributeLikeMacros: - - Q_EMIT -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 4 -UseTab: Never -VerilogBreakBetweenInstancePorts: true -WhitespaceSensitiveMacros: - - BOOST_PP_STRINGIZE - - CF_SWIFT_NAME - - NS_SWIFT_NAME - - PP_STRINGIZE - - STRINGIZE -... - diff --git a/pufferlib/ocean/impulse_wars/CMakeLists.txt b/pufferlib/ocean/impulse_wars/CMakeLists.txt deleted file mode 100644 index fec78fa53..000000000 --- a/pufferlib/ocean/impulse_wars/CMakeLists.txt +++ /dev/null @@ -1,138 +0,0 @@ -# 3.22 was released on Nov 2021, should be widely available -cmake_minimum_required(VERSION 3.22) -include(FetchContent) - -project( - impulse-wars - DESCRIPTION "Impulse Wars" - LANGUAGES C -) - -message(INFO " C Compiler: ${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_VERSION} ${CMAKE_C_COMPILER_ID}") - -# use ccache if available to speed up subsequent builds -find_program(CCACHE_FOUND "ccache") -if(CCACHE_FOUND) - set(CMAKE_C_COMPILER_LAUNCHER "ccache") -endif() - -# enable some C23 features, the c2x standard is a WIP standard supported -# by gcc since 9 (May 2019) and clang since 9 (Sep 2019) -set(CMAKE_C_FLAGS_INIT " -std=c2x") - -# force position independent code everywhere to prevent some rare -# linker errors depending on what compiler is used -add_compile_options("-fPIC") - -if(CMAKE_BUILD_TYPE MATCHES Debug) - # leak detection doesn't work correctly when the code is called by - # Python, so disable it - if(DEFINED BUILD_PYTHON_MODULE) - add_compile_options("-fno-omit-frame-pointer" "-fsanitize=address,undefined,bounds,pointer-overflow") - add_link_options("-shared-libasan" "-fno-omit-frame-pointer" "-fsanitize=address,undefined,bounds,pointer-overflow") - else() - add_compile_options("-fno-omit-frame-pointer" "-fsanitize=address,undefined,bounds,pointer-overflow,leak") - add_link_options("-fno-omit-frame-pointer" "-fsanitize=address,undefined,bounds,pointer-overflow,leak") - endif() - - # mold is an extremely fast linker, use it if available - # only use mold in debug mode, link time optimization currently doesn't - # work with mold and provides large speedups - find_program(MOLD_FOUND "mold") - if(MOLD_FOUND) - add_link_options("-fuse-ld=mold") - endif() -else() - add_compile_options("-flto" "-fno-math-errno") - if (NOT DEFINED EMSCRIPTEN) - # emscripten doesn't support -march=native, it doesn't make sense - # for WASM anyway - add_compile_options("-march=native") - else() - # tell emscripten to generate an HTML file that can be used to - # test the WASM, and ensure necessary code is transformed to be - # async friendly; it allows the game to be run much more smoothly - set(CMAKE_EXECUTABLE_SUFFIX ".html") - add_link_options("-sASYNCIFY") - endif() - # ensure the linker used is from the same compiler toolchain, or else - # link time optimization will probably fail; if we're using - # emscripten it will use it's own linker - if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT DEFINED EMSCRIPTEN) - add_link_options("-fuse-ld=lld") - endif() - - # add_compile_options("-pg") - # add_link_options("-pg") -endif() - -set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(FETCHCONTENT_QUIET FALSE) - -# fetch and configure dependencies -FetchContent_Declare( - raylib - URL https://github.com/raysan5/raylib/archive/c1ab645ca298a2801097931d1079b10ff7eb9df8.zip # 5.5 -) -set(BUILD_SHARED_LIBS OFF CACHE BOOL "Statically link raylib" FORCE) -set(WITH_PIC "Compile static library as position-independent code" ON) -set(CUSTOMIZE_BUILD ON CACHE BOOL "Customize raylib build settings" FORCE) -set(USE_AUDIO OFF CACHE BOOL "Don't build unused audio module" FORCE) -FetchContent_MakeAvailable(raylib) - -# if box2d is fetched first installing built python module will fail -# for reasons unbeknownst to mere mortals -# maybe due to install prefix schenanigans? -FetchContent_Declare( - box2d - URL https://github.com/capnspacehook/box2d/archive/df25d747be0ab2fd9425eece022d2ec897c2028d.zip -) -set(BOX2D_ENABLE_SIMD ON CACHE BOOL "Enable SIMD math (faster)" FORCE) -set(BOX2D_AVX2 ON CACHE BOOL "Enable AVX2 (faster)" FORCE) -add_compile_definitions(B2_MAX_WORLDS=65534) -FetchContent_MakeAvailable(box2d) -# this is set to off by box2d to enable cross platform determinism, but -# I don't care about that and want the small speedup instead -target_compile_options(box2d PRIVATE "-ffp-contract=fast") - -function(configure_target target_name) - target_include_directories( - ${target_name} PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}/include" - ) - - # Mark box2d as a system include directory to suppress warnings from it - target_include_directories(${target_name} SYSTEM PRIVATE "${box2d_SOURCE_DIR}/src") - - target_link_libraries(${target_name} PRIVATE raylib box2d) - - target_compile_options(${target_name} PRIVATE - "-Werror" "-Wall" "-Wextra" "-Wpedantic" - "-Wno-implicit-fallthrough" "-Wno-variadic-macros" "-Wno-strict-prototypes" "-Wno-gnu-statement-expression" - ) -endfunction() - -if(DEFINED BUILD_PYTHON_MODULE) - find_package( - Python - COMPONENTS Interpreter Development.Module NumPy - REQUIRED - ) - - python_add_library(binding MODULE binding.c WITH_SOABI) - - target_include_directories(binding PRIVATE - ${Python_NumPy_INCLUDE_DIRS} - ) - - configure_target(binding) - - install(TARGETS binding DESTINATION .) -elseif(DEFINED BUILD_DEMO) - add_executable(demo "${CMAKE_CURRENT_SOURCE_DIR}/impulse_wars.c") - configure_target(demo) -elseif(DEFINED BUILD_BENCHMARK) - add_executable(benchmark "${CMAKE_CURRENT_SOURCE_DIR}/benchmark.c") - configure_target(benchmark) -endif() diff --git a/pufferlib/ocean/impulse_wars/Makefile b/pufferlib/ocean/impulse_wars/Makefile deleted file mode 100644 index ce593669d..000000000 --- a/pufferlib/ocean/impulse_wars/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -RELEASE_PYTHON_MODULE_DIR := python-module-release -DEBUG_PYTHON_MODULE_DIR := python-module-debug -DEBUG_DIR := debug-demo -RELEASE_DIR := release-demo -RELEASE_WEB_DIR := release-demo-web -BENCHMARK_DIR := benchmark - -DEBUG_BUILD_TYPE := Debug -RELEASE_BUILD_TYPE := Release - -# install build dependencies if this is a fresh build, Python won't -# install build dependencies when --no-build-isolation is passed -# build with no isolation so that builds can be cached and/or incremental - -# build Python module in release mode -.PHONY: python-module-release -python-module-release: - @test -d $(RELEASE_PYTHON_MODULE_DIR) || pip install scikit-build-core autopxd2 cython - @pip install --no-build-isolation --config-settings=editable.rebuild=true -Cbuild-dir=$(RELEASE_PYTHON_MODULE_DIR) -v . - -# build Python module in debug mode -.PHONY: python-module-debug -python-module-debug: - @test -d $(DEBUG_PYTHON_MODULE_DIR) || pip install scikit-build-core autopxd2 cython - @pip install --no-build-isolation --config-settings=editable.rebuild=true --config-settings=cmake.build-type="Debug" -Cbuild-dir=$(DEBUG_PYTHON_MODULE_DIR) -v . - -# build C demo in debug mode -.PHONY: debug-demo -debug-demo: - @mkdir -p $(DEBUG_DIR) - @cd $(DEBUG_DIR) && \ - cmake -GNinja -DCMAKE_BUILD_TYPE=$(DEBUG_BUILD_TYPE) -DBUILD_DEMO=true -DCMAKE_C_COMPILER=clang-20 .. && \ - cmake --build . - -# build C demo in release mode -.PHONY: release-demo -release-demo: - @mkdir -p $(RELEASE_DIR) - @cd $(RELEASE_DIR) && \ - cmake -GNinja -DCMAKE_BUILD_TYPE=$(RELEASE_BUILD_TYPE) -DBUILD_DEMO=true -DCMAKE_C_COMPILER=clang-20 .. && \ - cmake --build . - -# build C demo in release mode for web -.PHONY: release-demo-web -release-demo-web: - @mkdir -p $(RELEASE_WEB_DIR) - @cd $(RELEASE_WEB_DIR) && \ - emcmake cmake -GNinja -DCMAKE_BUILD_TYPE=$(RELEASE_BUILD_TYPE) -DPLATFORM=Web -DBUILD_DEMO=true .. && \ - cmake --build . - -# build C benchmark -.PHONY: benchmark -benchmark: - @mkdir -p $(BENCHMARK_DIR) - @cd $(BENCHMARK_DIR) && \ - cmake -GNinja -DCMAKE_BUILD_TYPE=$(RELEASE_BUILD_TYPE) -DBUILD_BENCHMARK=true -DCMAKE_C_COMPILER=clang-20 .. && \ - cmake --build . - -.PHONY: clean -clean: - @rm -rf build $(RELEASE_PYTHON_MODULE_DIR) $(DEBUG_PYTHON_MODULE_DIR) $(DEBUG_DIR) $(RELEASE_DIR) $(RELEASE_WEB_DIR) $(BENCHMARK_DIR) diff --git a/pufferlib/ocean/impulse_wars/README.md b/pufferlib/ocean/impulse_wars/README.md deleted file mode 100644 index accca7438..000000000 --- a/pufferlib/ocean/impulse_wars/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Impulse Wars - -To build, you need to have the following: -- cmake -- make -- ninja -- raylib required deps installed: https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux - -Run `make && cp python-module-release/binding.*.so .` to build the python module in release mode. -`puffer_impulse_wars` env should now be trainable. - -When watching evaluations, you need to set all instances of `is_training = False` and `render = True` in the config file. diff --git a/pufferlib/ocean/impulse_wars/benchmark.c b/pufferlib/ocean/impulse_wars/benchmark.c deleted file mode 100644 index 3071bf91b..000000000 --- a/pufferlib/ocean/impulse_wars/benchmark.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "env.h" - -void randActions(iwEnv *e) { - // e->lastRandState = e->randState; - uint8_t actionOffset = 0; - for (uint8_t i = 0; i < e->numDrones; i++) { - e->actions[actionOffset + 0] = randFloat(&e->randState, -1.0f, 1.0f); - e->actions[actionOffset + 1] = randFloat(&e->randState, -1.0f, 1.0f); - e->actions[actionOffset + 2] = randFloat(&e->randState, -1.0f, 1.0f); - e->actions[actionOffset + 3] = randFloat(&e->randState, -1.0f, 1.0f); - e->actions[actionOffset + 4] = randFloat(&e->randState, -1.0f, 1.0f); - e->actions[actionOffset + 5] = randFloat(&e->randState, -1.0f, 1.0f); - e->actions[actionOffset + 6] = randFloat(&e->randState, -1.0f, 1.0f); - - actionOffset += CONTINUOUS_ACTION_SIZE; - } -} - -void perfTest(const uint32_t numSteps) { - const uint8_t NUM_DRONES = 2; - - iwEnv *e = fastCalloc(1, sizeof(iwEnv)); - - posix_memalign((void **)&e->observations, sizeof(void *), alignedSize(NUM_DRONES * obsBytes(NUM_DRONES), sizeof(float))); - e->rewards = fastCalloc(NUM_DRONES, sizeof(float)); - e->actions = fastCalloc(NUM_DRONES * CONTINUOUS_ACTION_SIZE, sizeof(float)); - e->masks = fastCalloc(NUM_DRONES, sizeof(uint8_t)); - e->terminals = fastCalloc(NUM_DRONES, sizeof(uint8_t)); - e->truncations = fastCalloc(NUM_DRONES, sizeof(uint8_t)); - - // rayClient *client = createRayClient(); - // e->client = client; - - uint64_t seed = time(NULL); - printf("seed: %lu\n", seed); - initEnv(e, NUM_DRONES, 0, -1, seed, false, false, true, false); - initMaps(e); - - // randActions(e); - setupEnv(e); - stepEnv(e); - - uint32_t steps = 0; - while (steps != numSteps) { - // randActions(e); - stepEnv(e); - steps++; - } - - destroyEnv(e); - destroyMaps(); - free(e->observations); - fastFree(e->actions); - fastFree(e->rewards); - fastFree(e->masks); - fastFree(e->terminals); - fastFree(e->truncations); - fastFree(e); -} - -int main(void) { - perfTest(2500000); - return 0; -} diff --git a/pufferlib/ocean/impulse_wars/binding.c b/pufferlib/ocean/impulse_wars/binding.c deleted file mode 100644 index d51837b73..000000000 --- a/pufferlib/ocean/impulse_wars/binding.c +++ /dev/null @@ -1,157 +0,0 @@ -#include - -#include "env.h" - -static PyObject *get_consts(PyObject *self, PyObject *args); - -#define Env iwEnv -#define MY_SHARED -#define MY_METHODS {"get_consts", get_consts, METH_VARARGS, "Get constants"} - -#include "../env_binding.h" - -#define setDictVal(dict, key, val) \ - if (PyDict_SetItemString(dict, key, PyLong_FromLong(val)) < 0) { \ - PyErr_SetString(PyExc_RuntimeError, "Failed to set " key " in dict"); \ - return NULL; \ - } - -static PyObject *get_consts(PyObject *self, PyObject *args) { - PyObject *dronesArg = PyTuple_GetItem(args, 0); - if (!PyObject_TypeCheck(dronesArg, &PyLong_Type)) { - PyErr_SetString(PyExc_TypeError, "num_drones must be an integer"); - return NULL; - } - const uint8_t numDrones = (uint8_t)PyLong_AsLong(dronesArg); - - PyObject *dict = PyDict_New(); - if (PyErr_Occurred()) { - return NULL; - } - - const uint16_t droneObsOffset = ENEMY_DRONE_OBS_OFFSET + ((numDrones - 1) * ENEMY_DRONE_OBS_SIZE); - - setDictVal(dict, "obsBytes", obsBytes(numDrones)); - setDictVal(dict, "mapObsSize", MAP_OBS_SIZE); - setDictVal(dict, "discreteObsSize", discreteObsSize(numDrones)); - setDictVal(dict, "continuousObsSize", continuousObsSize(numDrones)); - setDictVal(dict, "continuousObsBytes", continuousObsSize(numDrones) * sizeof(float)); - setDictVal(dict, "wallTypes", NUM_WALL_TYPES); - setDictVal(dict, "weaponTypes", NUM_WEAPONS + 1); - setDictVal(dict, "mapObsRows", MAP_OBS_ROWS); - setDictVal(dict, "mapObsColumns", MAP_OBS_COLUMNS); - setDictVal(dict, "continuousObsOffset", alignedSize(MAP_OBS_SIZE, sizeof(float))); - setDictVal(dict, "numNearWallObs", NUM_NEAR_WALL_OBS); - setDictVal(dict, "nearWallTypesObsOffset", NEAR_WALL_TYPES_OBS_OFFSET); - setDictVal(dict, "nearWallPosObsSize", NEAR_WALL_POS_OBS_SIZE); - setDictVal(dict, "nearWallObsSize", NEAR_WALL_OBS_SIZE); - setDictVal(dict, "nearWallPosObsOffset", NEAR_WALL_POS_OBS_OFFSET); - setDictVal(dict, "numFloatingWallObs", NUM_FLOATING_WALL_OBS); - setDictVal(dict, "floatingWallTypesObsOffset", FLOATING_WALL_TYPES_OBS_OFFSET); - setDictVal(dict, "floatingWallInfoObsSize", FLOATING_WALL_INFO_OBS_SIZE); - setDictVal(dict, "floatingWallObsSize", FLOATING_WALL_OBS_SIZE); - setDictVal(dict, "floatingWallInfoObsOffset", FLOATING_WALL_INFO_OBS_OFFSET); - setDictVal(dict, "numWeaponPickupObs", NUM_WEAPON_PICKUP_OBS); - setDictVal(dict, "weaponPickupTypesObsOffset", WEAPON_PICKUP_WEAPONS_OBS_OFFSET); - setDictVal(dict, "weaponPickupPosObsSize", WEAPON_PICKUP_POS_OBS_SIZE); - setDictVal(dict, "weaponPickupObsSize", WEAPON_PICKUP_OBS_SIZE); - setDictVal(dict, "weaponPickupPosObsOffset", WEAPON_PICKUP_POS_OBS_OFFSET); - setDictVal(dict, "numProjectileObs", NUM_PROJECTILE_OBS); - setDictVal(dict, "projectileDroneObsOffset", PROJECTILE_DRONE_OBS_OFFSET); - setDictVal(dict, "projectileTypesObsOffset", PROJECTILE_WEAPONS_OBS_OFFSET); - setDictVal(dict, "projectileInfoObsSize", PROJECTILE_INFO_OBS_SIZE); - setDictVal(dict, "projectileObsSize", PROJECTILE_OBS_SIZE); - setDictVal(dict, "projectileInfoObsOffset", PROJECTILE_INFO_OBS_OFFSET); - setDictVal(dict, "enemyDroneWeaponsObsOffset", ENEMY_DRONE_WEAPONS_OBS_OFFSET); - setDictVal(dict, "enemyDroneObsOffset", ENEMY_DRONE_OBS_OFFSET); - setDictVal(dict, "enemyDroneObsSize", ENEMY_DRONE_OBS_SIZE); - setDictVal(dict, "droneObsOffset", droneObsOffset); - setDictVal(dict, "droneObsSize", DRONE_OBS_SIZE); - setDictVal(dict, "miscObsSize", MISC_OBS_SIZE); - setDictVal(dict, "miscObsOffset", droneObsOffset + DRONE_OBS_SIZE); - - setDictVal(dict, "maxDrones", MAX_DRONES); - setDictVal(dict, "contActionsSize", CONTINUOUS_ACTION_SIZE); - - return dict; -} - -static PyObject *my_shared(PyObject *self, PyObject *args, PyObject *kwargs) { - VecEnv *ve = unpack_vecenv(args); - initMaps(ve->envs[0]); - - for (uint16_t i = 0; i < ve->num_envs; i++) { - iwEnv *e = (iwEnv *)ve->envs[i]; - setupEnv(e); - } - - return Py_None; -} - -static int my_init(iwEnv *e, PyObject *args, PyObject *kwargs) { - initEnv( - e, - (uint8_t)unpack(kwargs, "num_drones"), - (uint8_t)unpack(kwargs, "num_agents"), - (int8_t)unpack(kwargs, "map_idx"), - (uint64_t)unpack(kwargs, "seed"), - (bool)unpack(kwargs, "enable_teams"), - (bool)unpack(kwargs, "sitting_duck"), - (bool)unpack(kwargs, "is_training"), - (bool)unpack(kwargs, "continuous") - ); - return 0; -} - -#define _LOG_BUF_SIZE 128 - -char *droneLog(char *buf, const uint8_t droneIdx, const char *name) { - snprintf(buf, _LOG_BUF_SIZE, "drone_%d_%s", droneIdx, name); - return buf; -} - -char *weaponLog(char *buf, const uint8_t droneIdx, const uint8_t weaponIdx, const char *name) { - snprintf(buf, _LOG_BUF_SIZE, "drone_%d_%s_%s", droneIdx, weaponNames[weaponIdx], name); - return buf; -} - -static int my_log(PyObject *dict, Log *log) { - assign_to_dict(dict, "episode_length", log->length); - assign_to_dict(dict, "ties", log->ties); - - assign_to_dict(dict, "perf", log->stats[0].wins); - assign_to_dict(dict, "score", log->stats[0].wins); - - char buf[_LOG_BUF_SIZE] = {0}; - for (uint8_t i = 0; i < MAX_DRONES; i++) { - assign_to_dict(dict, droneLog(buf, i, "returns"), log->stats[i].returns); - assign_to_dict(dict, droneLog(buf, i, "distance_traveled"), log->stats[i].distanceTraveled); - assign_to_dict(dict, droneLog(buf, i, "abs_distance_traveled"), log->stats[i].absDistanceTraveled); - assign_to_dict(dict, droneLog(buf, i, "brake_time"), log->stats[i].brakeTime); - assign_to_dict(dict, droneLog(buf, i, "total_bursts"), log->stats[i].totalBursts); - assign_to_dict(dict, droneLog(buf, i, "bursts_hit"), log->stats[i].burstsHit); - assign_to_dict(dict, droneLog(buf, i, "energy_emptied"), log->stats[i].energyEmptied); - assign_to_dict(dict, droneLog(buf, i, "wins"), log->stats[i].wins); - - // useful for debugging weapon balance, but really slows down - // sweeps due to adding a ton of extra logging data - // - // for (uint8_t j = 0; j < _NUM_WEAPONS; j++) { - // assign_to_dict(dict, weaponLog(buf, i, j, "shots_fired"), log->stats[i].shotsFired[j]); - // assign_to_dict(dict, weaponLog(buf, i, j, "shots_hit"), log->stats[i].shotsHit[j]); - // assign_to_dict(dict, weaponLog(buf, i, j, "shots_taken"), log->stats[i].shotsTaken[j]); - // assign_to_dict(dict, weaponLog(buf, i, j, "own_shots_taken"), log->stats[i].ownShotsTaken[j]); - // assign_to_dict(dict, weaponLog(buf, i, j, "picked_up"), log->stats[i].weaponsPickedUp[j]); - // assign_to_dict(dict, weaponLog(buf, i, j, "shot_distances"), log->stats[i].shotDistances[j]); - // } - - assign_to_dict(dict, droneLog(buf, i, "total_shots_fired"), log->stats[i].totalShotsFired); - assign_to_dict(dict, droneLog(buf, i, "total_shots_hit"), log->stats[i].totalShotsHit); - assign_to_dict(dict, droneLog(buf, i, "total_shots_taken"), log->stats[i].totalShotsTaken); - assign_to_dict(dict, droneLog(buf, i, "total_own_shots_taken"), log->stats[i].totalOwnShotsTaken); - assign_to_dict(dict, droneLog(buf, i, "total_picked_up"), log->stats[i].totalWeaponsPickedUp); - assign_to_dict(dict, droneLog(buf, i, "total_shot_distances"), log->stats[i].totalShotDistances); - } - - return 0; -} diff --git a/pufferlib/ocean/impulse_wars/env.h b/pufferlib/ocean/impulse_wars/env.h deleted file mode 100644 index 33a220e8c..000000000 --- a/pufferlib/ocean/impulse_wars/env.h +++ /dev/null @@ -1,1256 +0,0 @@ -#ifndef IMPULSE_WARS_ENV_H -#define IMPULSE_WARS_ENV_H - -#ifdef __EMSCRIPTEN__ -#include - -double lastFrameTime = 0.0; -double accumulator = 0.0; -#endif - -#include "game.h" -#include "map.h" -#include "render.h" -#include "scripted_agent.h" -#include "settings.h" -#include "types.h" - -const uint8_t THREE_BIT_MASK = 0x7; -const uint8_t FOUR_BIT_MASK = 0xf; - -// pufferlib compatibility -#define c_step stepEnv -#define c_reset resetEnv -#define c_render setupRayClient -#define c_close destroyEnv - -// returns a cell index that is closest to pos that isn't cellIdx -uint16_t findNearestCell(const iwEnv *e, const b2Vec2 pos, const uint16_t cellIdx) { - uint16_t closestCell = cellIdx; - float minDistance = FLT_MAX; - const uint8_t cellCol = cellIdx / e->map->columns; - const uint8_t cellRow = cellIdx % e->map->columns; - for (uint8_t i = 0; i < 8; i++) { - const int8_t newCellCol = cellCol + cellOffsets[i][0]; - if (newCellCol < 0 || newCellCol >= e->map->columns) { - continue; - } - const int8_t newCellRow = cellRow + cellOffsets[i][1]; - if (newCellRow < 0 || newCellRow >= e->map->rows) { - continue; - } - const int16_t newCellIdx = cellIndex(e, newCellCol, newCellRow); - const mapCell *cell = safe_array_get_at(e->cells, newCellIdx); - if (minDistance != min(minDistance, b2DistanceSquared(pos, cell->pos))) { - closestCell = newCellIdx; - } - } - - return closestCell; -} - -// normalize a drone's ammo count, setting infinite ammo as no ammo -static inline float scaleAmmo(const iwEnv *e, const droneEntity *drone) { - int8_t maxAmmo = weaponAmmo(e->defaultWeapon->type, drone->weaponInfo->type); - float scaledAmmo = 0; - if (drone->ammo != INFINITE) { - scaledAmmo = scaleValue(drone->ammo, maxAmmo, true); - } - return scaledAmmo; -} - -// fills a small 2D grid centered around the agent with discretized -// walls, floating walls, weapon pickups, and drone positions -void computeMapObs(iwEnv *e, const uint8_t agentIdx, const uint16_t obsStartOffset) { - droneEntity *drone = safe_array_get_at(e->drones, agentIdx); - const uint8_t droneCellCol = drone->mapCellIdx % e->map->columns; - const uint8_t droneCellRow = drone->mapCellIdx / e->map->columns; - - const int8_t obsStartCol = droneCellCol - (MAP_OBS_COLUMNS / 2); - const int8_t startCol = max(obsStartCol, 0); - const int8_t obsStartRow = droneCellRow - (MAP_OBS_ROWS / 2); - const int8_t startRow = max(obsStartRow, 0); - - const int8_t obsEndCol = droneCellCol + (MAP_OBS_COLUMNS / 2); - const int8_t endCol = min(obsEndCol, e->map->columns - 1); - const int8_t endRow = min(droneCellRow + (MAP_OBS_ROWS / 2), e->map->rows - 1); - - const int8_t obsColOffset = startCol - obsStartCol; - const int8_t obsRowOffset = startRow - obsStartRow; - uint16_t startOffset = obsStartOffset; - if (obsColOffset == 0 && obsRowOffset != 0) { - startOffset += obsRowOffset * MAP_OBS_COLUMNS; - } else if (obsColOffset != 0 && obsRowOffset == 0) { - startOffset += obsColOffset; - } else if (obsColOffset != 0 && obsRowOffset != 0) { - startOffset += obsColOffset + (obsRowOffset * MAP_OBS_COLUMNS); - } - uint16_t offset = startOffset; - - // compute map layout, and discretized positions of weapon pickups - if (!e->suddenDeathWallsPlaced) { - // copy precomputed map layout if sudden death walls haven't been placed - const int8_t numCols = endCol - startCol + 1; - for (int8_t row = startRow; row <= endRow; row++) { - const int16_t cellIdx = cellIndex(e, startCol, row); - memcpy(e->observations + offset, e->map->packedLayout + cellIdx, numCols * sizeof(uint8_t)); - offset += MAP_OBS_COLUMNS; - } - - // compute discretized location of weapon pickups on grid - for (size_t i = 0; i < cc_array_size(e->pickups); i++) { - const weaponPickupEntity *pickup = safe_array_get_at(e->pickups, i); - const uint8_t cellCol = pickup->mapCellIdx % e->map->columns; - if (cellCol < startCol || cellCol > endCol) { - continue; - } - const uint8_t cellRow = pickup->mapCellIdx / e->map->columns; - if (cellRow < startRow || cellRow > endRow) { - continue; - } - - offset = startOffset + ((cellCol - startCol) + ((cellRow - startRow) * MAP_OBS_COLUMNS)); - ASSERTF(offset <= startOffset + MAP_OBS_SIZE, "offset: %d", offset); - e->observations[offset] |= 1 << 3; - } - } else { - // sudden death walls have been placed so compute may layout manually - const int8_t colPadding = obsColOffset + (obsEndCol - endCol); - for (int8_t row = startRow; row <= endRow; row++) { - for (int8_t col = startCol; col <= endCol; col++) { - const int16_t cellIdx = cellIndex(e, col, row); - const mapCell *cell = safe_array_get_at(e->cells, cellIdx); - if (cell->ent == NULL) { - offset++; - continue; - } - - if (entityTypeIsWall(cell->ent->type)) { - e->observations[offset] = ((cell->ent->type + 1) & TWO_BIT_MASK) << 5; - } else if (cell->ent->type == WEAPON_PICKUP_ENTITY) { - e->observations[offset] |= 1 << 3; - } - - offset++; - } - offset += colPadding; - } - ASSERTF(offset <= startOffset + MAP_OBS_SIZE, "offset %u startOffset %u", offset, startOffset); - } - - // compute discretized locations of floating walls on grid - for (size_t i = 0; i < cc_array_size(e->floatingWalls); i++) { - const wallEntity *wall = safe_array_get_at(e->floatingWalls, i); - const uint8_t cellCol = wall->mapCellIdx % e->map->columns; - if (cellCol < startCol || cellCol > endCol) { - continue; - } - const uint8_t cellRow = wall->mapCellIdx / e->map->columns; - if (cellRow < startRow || cellRow > endRow) { - continue; - } - - offset = startOffset + ((cellCol - startCol) + ((cellRow - startRow) * MAP_OBS_COLUMNS)); - ASSERTF(offset <= startOffset + MAP_OBS_SIZE, "offset: %d", offset); - e->observations[offset] = ((wall->type + 1) & TWO_BIT_MASK) << 5; - e->observations[offset] |= 1 << 4; - } - - // compute discretized location and index of drones on grid - uint8_t newDroneIdx = 1; - uint16_t droneCells[e->numDrones]; - memset(droneCells, 0x0, sizeof(droneCells)); - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - if (i == agentIdx) { - continue; - } - - // ensure drones do not share cells in the observation - droneEntity *otherDrone = safe_array_get_at(e->drones, i); - if (i != 0) { - for (uint8_t j = 0; j < i; j++) { - if (droneCells[j] == otherDrone->mapCellIdx) { - otherDrone->mapCellIdx = findNearestCell(e, otherDrone->pos, otherDrone->mapCellIdx); - break; - } - } - } - const uint8_t cellCol = otherDrone->mapCellIdx % e->map->columns; - if (cellCol < startCol || cellCol > endCol) { - continue; - } - const uint8_t cellRow = otherDrone->mapCellIdx / e->map->columns; - if (cellRow < startRow || cellRow > endRow) { - continue; - } - droneCells[i] = otherDrone->mapCellIdx; - - offset = startOffset + ((cellCol - startCol) + ((cellRow - startRow) * MAP_OBS_COLUMNS)); - ASSERTF(offset <= startOffset + MAP_OBS_SIZE, "offset: %d", offset); - e->observations[offset] |= (newDroneIdx++ & THREE_BIT_MASK); - } -} - -// computes observations for N nearest walls, floating walls, and weapon pickups -void computeNearObs(iwEnv *e, const droneEntity *drone, const uint16_t discreteObsStart, float *continuousObs) { - nearEntity nearWalls[NUM_NEAR_WALL_OBS]; - findNearWalls(e, drone, nearWalls, NUM_NEAR_WALL_OBS); - - uint16_t offset; - - // compute type and position of N nearest walls - for (uint8_t i = 0; i < NUM_NEAR_WALL_OBS; i++) { - const wallEntity *wall = nearWalls[i].entity; - - offset = discreteObsStart + NEAR_WALL_TYPES_OBS_OFFSET + i; - ASSERTF(offset <= discreteObsStart + FLOATING_WALL_TYPES_OBS_OFFSET, "offset: %d", offset); - e->observations[offset] = wall->type; - - // DEBUG_LOGF("wall %d cell %d", i, wall->mapCellIdx); - - offset = NEAR_WALL_POS_OBS_OFFSET + (i * NEAR_WALL_POS_OBS_SIZE); - ASSERTF(offset <= FLOATING_WALL_INFO_OBS_OFFSET, "offset: %d", offset); - const b2Vec2 wallRelPos = b2Sub(wall->pos, drone->pos); - - continuousObs[offset++] = scaleValue(wallRelPos.x, MAX_X_POS, false); - continuousObs[offset] = scaleValue(wallRelPos.y, MAX_Y_POS, false); - } - - if (cc_array_size(e->floatingWalls) != 0) { - // find N nearest floating walls - nearEntity nearFloatingWalls[MAX_FLOATING_WALLS] = {0}; - for (uint8_t i = 0; i < cc_array_size(e->floatingWalls); i++) { - wallEntity *wall = safe_array_get_at(e->floatingWalls, i); - const nearEntity nearEnt = { - .entity = wall, - .distanceSquared = b2DistanceSquared(wall->pos, drone->pos), - }; - nearFloatingWalls[i] = nearEnt; - } - insertionSort(nearFloatingWalls, cc_array_size(e->floatingWalls)); - - // compute type, position, angle and velocity of N nearest floating walls - for (uint8_t i = 0; i < cc_array_size(e->floatingWalls); i++) { - if (i == NUM_FLOATING_WALL_OBS) { - break; - } - const wallEntity *wall = nearFloatingWalls[i].entity; - - const b2Transform wallTransform = b2Body_GetTransform(wall->bodyID); - const b2Vec2 wallRelPos = b2Sub(wallTransform.p, drone->pos); - const float angle = b2Rot_GetAngle(wallTransform.q); - - offset = discreteObsStart + FLOATING_WALL_TYPES_OBS_OFFSET + i; - ASSERTF(offset <= discreteObsStart + PROJECTILE_DRONE_OBS_OFFSET, "offset: %d", offset); - e->observations[offset] = wall->type + 1; - - // DEBUG_LOGF("floating wall %d cell %d", i, wall->mapCellIdx); - - offset = FLOATING_WALL_INFO_OBS_OFFSET + (i * FLOATING_WALL_INFO_OBS_SIZE); - ASSERTF(offset <= WEAPON_PICKUP_POS_OBS_OFFSET, "offset: %d", offset); - continuousObs[offset++] = scaleValue(wallRelPos.x, MAX_X_POS, false); - continuousObs[offset++] = scaleValue(wallRelPos.y, MAX_Y_POS, false); - continuousObs[offset++] = scaleValue(angle, MAX_ANGLE, false); - continuousObs[offset++] = scaleValue(wall->velocity.x, MAX_SPEED, false); - continuousObs[offset] = scaleValue(wall->velocity.y, MAX_SPEED, false); - } - } - - if (cc_array_size(e->pickups) != 0) { - // find N nearest weapon pickups - nearEntity nearPickups[MAX_WEAPON_PICKUPS] = {0}; - for (uint8_t i = 0; i < cc_array_size(e->pickups); i++) { - weaponPickupEntity *pickup = safe_array_get_at(e->pickups, i); - const nearEntity nearEnt = { - .entity = pickup, - .distanceSquared = b2DistanceSquared(pickup->pos, drone->pos), - }; - nearPickups[i] = nearEnt; - } - insertionSort(nearPickups, cc_array_size(e->pickups)); - - // compute type and location of N nearest weapon pickups - for (uint8_t i = 0; i < cc_array_size(e->pickups); i++) { - if (i == NUM_WEAPON_PICKUP_OBS) { - break; - } - const weaponPickupEntity *pickup = nearPickups[i].entity; - - offset = discreteObsStart + WEAPON_PICKUP_WEAPONS_OBS_OFFSET + i; - ASSERTF(offset <= discreteObsStart + ENEMY_DRONE_WEAPONS_OBS_OFFSET, "offset: %d", offset); - e->observations[offset] = pickup->weapon + 1; - - // DEBUG_LOGF("pickup %d cell %d", i, pickup->mapCellIdx); - - offset = WEAPON_PICKUP_POS_OBS_OFFSET + (i * WEAPON_PICKUP_POS_OBS_SIZE); - ASSERTF(offset <= PROJECTILE_INFO_OBS_OFFSET, "offset: %d", offset); - const b2Vec2 pickupRelPos = b2Sub(pickup->pos, drone->pos); - continuousObs[offset++] = scaleValue(pickupRelPos.x, MAX_X_POS, false); - continuousObs[offset] = scaleValue(pickupRelPos.y, MAX_Y_POS, false); - } - } -} - -void computeObs(iwEnv *e) { - for (uint8_t agentIdx = 0; agentIdx < e->numAgents; agentIdx++) { - droneEntity *agentDrone = safe_array_get_at(e->drones, agentIdx); - // if the drone is dead, only compute observations if it died - // this step and it isn't out of bounds - if (agentDrone->livesLeft == 0 && (!agentDrone->diedThisStep || agentDrone->mapCellIdx == -1)) { - continue; - } - - // compute discrete map observations - const uint16_t discreteObsStart = e->obsBytes * agentIdx; - memset(e->observations + discreteObsStart, 0x0, e->obsBytes); - computeMapObs(e, agentIdx, discreteObsStart); - - // compute continuous observations - uint16_t discreteObsOffset; - uint16_t continuousObsOffset; - const uint16_t continuousObsStart = discreteObsStart + e->discreteObsBytes; - float *continuousObs = (float *)(e->observations + continuousObsStart); - - computeNearObs(e, agentDrone, discreteObsStart, continuousObs); - - // sort projectiles by distance to the current agent - const b2Vec2 agentPos = agentDrone->pos; - const size_t numProjectiles = cc_array_size(e->projectiles); - if (numProjectiles > 0) { - projectileEntity *sortedProjectiles[numProjectiles]; - memcpy(sortedProjectiles, e->projectiles->buffer, numProjectiles * sizeof(projectileEntity *)); - - for (int16_t i = 1; i < (int64_t)numProjectiles; i++) { - projectileEntity *key = sortedProjectiles[i]; - const float keyDistance = b2DistanceSquared(agentPos, key->pos); - int16_t j = i - 1; - - while (j >= 0 && b2DistanceSquared(agentPos, sortedProjectiles[j]->pos) > keyDistance) { - sortedProjectiles[j + 1] = sortedProjectiles[j]; - j = j - 1; - } - - sortedProjectiles[j + 1] = key; - } - - // compute type and location of N projectiles - for (size_t i = 0; i < numProjectiles; i++) { - if (i == NUM_PROJECTILE_OBS) { - break; - } - const projectileEntity *projectile = sortedProjectiles[i]; - - discreteObsOffset = discreteObsStart + PROJECTILE_DRONE_OBS_OFFSET + i; - ASSERTF(discreteObsOffset <= discreteObsStart + PROJECTILE_WEAPONS_OBS_OFFSET, "offset: %d", discreteObsOffset); - e->observations[discreteObsOffset] = projectile->droneIdx + 1; - - discreteObsOffset = discreteObsStart + PROJECTILE_WEAPONS_OBS_OFFSET + i; - ASSERTF(discreteObsOffset <= discreteObsStart + WEAPON_PICKUP_WEAPONS_OBS_OFFSET, "offset: %d", discreteObsOffset); - e->observations[discreteObsOffset] = projectile->weaponInfo->type + 1; - - continuousObsOffset = PROJECTILE_INFO_OBS_OFFSET + (i * PROJECTILE_INFO_OBS_SIZE); - ASSERTF(continuousObsOffset <= ENEMY_DRONE_OBS_OFFSET, "offset: %d", continuousObsOffset); - const b2Vec2 projectileRelPos = b2Sub(projectile->pos, agentDrone->pos); - continuousObs[continuousObsOffset++] = scaleValue(projectileRelPos.x, MAX_X_POS, false); - continuousObs[continuousObsOffset++] = scaleValue(projectileRelPos.y, MAX_Y_POS, false); - continuousObs[continuousObsOffset++] = scaleValue(projectile->velocity.x, MAX_SPEED, false); - continuousObs[continuousObsOffset] = scaleValue(projectile->velocity.y, MAX_SPEED, false); - } - } - - // compute enemy drone observations - bool hitShot = false; - bool tookShot = false; - uint8_t processedDrones = 0; - for (uint8_t i = 0; i < e->numDrones; i++) { - if (i == agentIdx) { - continue; - } - - if (agentDrone->stepInfo.shotHit[i]) { - hitShot = true; - } - if (agentDrone->stepInfo.shotTaken[i]) { - tookShot = true; - } - - droneEntity *enemyDrone = safe_array_get_at(e->drones, i); - if (enemyDrone->livesLeft == 0) { - processedDrones++; - continue; - } - - const b2Vec2 enemyDroneRelPos = b2Sub(enemyDrone->pos, agentDrone->pos); - const float enemyDroneDistance = b2Distance(enemyDrone->pos, agentDrone->pos); - const b2Vec2 enemyDroneAccel = b2Sub(enemyDrone->velocity, enemyDrone->lastVelocity); - const b2Vec2 enemyDroneRelNormPos = b2Normalize(b2Sub(enemyDrone->pos, agentDrone->pos)); - const float enemyDroneAimAngle = atan2f(enemyDrone->lastAim.y, enemyDrone->lastAim.x); - float enemyDroneBraking = 0.0f; - if (enemyDrone->braking) { - enemyDroneBraking = 1.0f; - } - - discreteObsOffset = discreteObsStart + ENEMY_DRONE_WEAPONS_OBS_OFFSET + processedDrones; - e->observations[discreteObsOffset] = enemyDrone->weaponInfo->type + 1; - - continuousObsOffset = ENEMY_DRONE_OBS_OFFSET + (e->numDrones - 1) + (processedDrones * ENEMY_DRONE_OBS_SIZE); - continuousObs[continuousObsOffset++] = enemyDrone->team == agentDrone->team; - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneRelPos.x, MAX_X_POS, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneRelPos.y, MAX_Y_POS, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneDistance, MAX_DISTANCE, true); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->velocity.x, MAX_SPEED, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->velocity.y, MAX_SPEED, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneAccel.x, MAX_ACCEL, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneAccel.y, MAX_ACCEL, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneRelNormPos.x, 1.0f, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneRelNormPos.y, 1.0f, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->lastAim.x, 1.0f, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->lastAim.y, 1.0f, false); - continuousObs[continuousObsOffset++] = scaleValue(enemyDroneAimAngle, PI, false); - continuousObs[continuousObsOffset++] = scaleAmmo(e, enemyDrone); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->weaponCooldown, enemyDrone->weaponInfo->coolDown, true); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->weaponCharge, enemyDrone->weaponInfo->charge, true); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->energyLeft, DRONE_ENERGY_MAX, true); - continuousObs[continuousObsOffset++] = (float)enemyDrone->energyFullyDepleted; - continuousObs[continuousObsOffset++] = enemyDroneBraking; - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->burstCooldown, DRONE_BURST_COOLDOWN, true); - continuousObs[continuousObsOffset++] = (float)enemyDrone->chargingBurst; - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->burstCharge, DRONE_ENERGY_MAX, true); - continuousObs[continuousObsOffset++] = scaleValue(enemyDrone->livesLeft, DRONE_LIVES, true); - continuousObs[continuousObsOffset++] = !enemyDrone->dead; - - processedDrones++; - ASSERTF(continuousObsOffset == ENEMY_DRONE_OBS_OFFSET + (e->numDrones - 1) + (processedDrones * ENEMY_DRONE_OBS_SIZE), "offset: %d", continuousObsOffset); - } - - // compute active drone observations - continuousObsOffset = ENEMY_DRONE_OBS_OFFSET + ((e->numDrones - 1) * ENEMY_DRONE_OBS_SIZE); - const b2Vec2 agentDroneAccel = b2Sub(agentDrone->velocity, agentDrone->lastVelocity); - float agentDroneBraking = 0.0f; - if (agentDrone->braking) { - agentDroneBraking = 1.0f; - } - - discreteObsOffset = discreteObsStart + ENEMY_DRONE_WEAPONS_OBS_OFFSET + e->numDrones - 1; - e->observations[discreteObsOffset] = agentDrone->weaponInfo->type + 1; - - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->pos.x, MAX_X_POS, false); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->pos.y, MAX_Y_POS, false); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->velocity.x, MAX_SPEED, false); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->velocity.y, MAX_SPEED, false); - continuousObs[continuousObsOffset++] = scaleValue(agentDroneAccel.x, MAX_ACCEL, false); - continuousObs[continuousObsOffset++] = scaleValue(agentDroneAccel.y, MAX_ACCEL, false); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->lastAim.x, 1.0f, false); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->lastAim.y, 1.0f, false); - continuousObs[continuousObsOffset++] = scaleAmmo(e, agentDrone); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->weaponCooldown, agentDrone->weaponInfo->coolDown, true); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->weaponCharge, agentDrone->weaponInfo->charge, true); - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->energyLeft, DRONE_ENERGY_MAX, true); - continuousObs[continuousObsOffset++] = (float)agentDrone->energyFullyDepleted; - continuousObs[continuousObsOffset++] = agentDroneBraking; - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->burstCooldown, DRONE_BURST_COOLDOWN, true); - continuousObs[continuousObsOffset++] = (float)agentDrone->chargingBurst; - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->burstCharge, DRONE_ENERGY_MAX, true); - continuousObs[continuousObsOffset++] = hitShot; - continuousObs[continuousObsOffset++] = tookShot; - continuousObs[continuousObsOffset++] = agentDrone->stepInfo.ownShotTaken; - continuousObs[continuousObsOffset++] = scaleValue(agentDrone->livesLeft, DRONE_LIVES, true); - continuousObs[continuousObsOffset++] = !agentDrone->dead; - - ASSERTF(continuousObsOffset == ENEMY_DRONE_OBS_OFFSET + ((e->numDrones - 1) * ENEMY_DRONE_OBS_SIZE) + DRONE_OBS_SIZE, "offset: %d", continuousObsOffset); - continuousObs[continuousObsOffset] = scaleValue(e->stepsLeft, e->totalSteps, true); - } -} - -void setupEnv(iwEnv *e) { - e->needsReset = false; - - e->stepsLeft = e->totalSteps; - e->suddenDeathSteps = e->totalSuddenDeathSteps; - e->suddenDeathWallCounter = 0; - - e->lastSpawnQuad = -1; - - int8_t mapIdx = e->pinnedMapIdx; - if (e->pinnedMapIdx == -1) { - uint8_t firstMap = 0; - // don't evaluate on the boring empty map - if (!e->isTraining) { - firstMap = 1; - } - mapIdx = randInt(&e->randState, firstMap, NUM_MAPS - 1); - } - DEBUG_LOGF("setting up map %d", mapIdx); - setupMap(e, mapIdx); - - DEBUG_LOG("creating drones"); - for (uint8_t i = 0; i < e->numDrones; i++) { - createDrone(e, i); - } - - DEBUG_LOG("placing floating walls"); - placeRandFloatingWalls(e, mapIdx); - - DEBUG_LOG("creating weapon pickups"); - // start spawning pickups in a random quadrant - e->lastSpawnQuad = randInt(&e->randState, 0, 3); - for (uint8_t i = 0; i < maps[mapIdx]->weaponPickups; i++) { - createWeaponPickup(e); - } - - if (e->client != NULL) { - setupEnvCamera(e); - renderEnv(e, true, false, -1, -1); - } - - computeObs(e); -} - -// sets the timing related variables for the environment depending on -// the frame rate -void setEnvFrameRate(iwEnv *e) { - float frameRate = TRAINING_FRAME_RATE; - e->box2dSubSteps = TRAINING_BOX2D_SUBSTEPS; - // set a higher frame rate and physics substeps when evaluating - // to make it more enjoyable to play - if (!e->isTraining) { - frameRate = EVAL_FRAME_RATE; - e->box2dSubSteps = EVAL_BOX2D_SUBSTEPS; - } - - e->frameRate = frameRate; - e->deltaTime = 1.0f / (float)frameRate; - e->frameSkip = frameRate / TRAINING_ACTIONS_PER_SECOND; - - e->totalSteps = ROUND_STEPS * frameRate; - e->totalSuddenDeathSteps = SUDDEN_DEATH_STEPS * frameRate; -} - -iwEnv *initEnv(iwEnv *e, uint8_t numDrones, uint8_t numAgents, int8_t mapIdx, uint64_t seed, bool enableTeams, bool sittingDuck, bool isTraining, bool continuousActions) { - DEBUG_LOGF("seed: %lu", seed); - - e->numDrones = numDrones; - e->numAgents = numAgents; - e->teamsEnabled = enableTeams; - e->numTeams = numDrones; - if (e->teamsEnabled) { - e->numTeams = 2; - } - e->sittingDuck = sittingDuck; - e->isTraining = isTraining; - - e->obsBytes = obsBytes(e->numDrones); - e->discreteObsBytes = alignedSize(discreteObsSize(e->numDrones) * sizeof(uint8_t), sizeof(float)); - - e->continuousActions = continuousActions; - - // TODO: remove when puffer bindings add truncations - e->truncations = fastCalloc(numDrones, sizeof(uint8_t)); - - setEnvFrameRate(e); - e->randState = seed; - e->needsReset = false; - - b2WorldDef worldDef = b2DefaultWorldDef(); - worldDef.gravity = (b2Vec2){.x = 0.0f, .y = 0.0f}; - e->worldID = b2CreateWorld(&worldDef); - e->pinnedMapIdx = mapIdx; - e->mapIdx = -1; - - e->idPool = b2CreateIdPool(); - create_array(&e->entities, 128); - - create_array(&e->cells, 512); - create_array(&e->walls, 128); - create_array(&e->floatingWalls, MAX_FLOATING_WALLS); - create_array(&e->drones, e->numDrones); - create_array(&e->pickups, MAX_WEAPON_PICKUPS); - create_array(&e->projectiles, 64); - create_array(&e->explosions, 8); - create_array(&e->explodingProjectiles, 8); - create_array(&e->dronePieces, 16); - - e->mapPathing = fastCalloc(NUM_MAPS, sizeof(pathingInfo)); - for (uint8_t i = 0; i < NUM_MAPS; i++) { - const mapEntry *map = maps[i]; - pathingInfo *info = &e->mapPathing[i]; - info->paths = fastMalloc(map->rows * map->columns * map->rows * map->columns * sizeof(uint8_t)); - memset(info->paths, UINT8_MAX, map->rows * map->columns * map->rows * map->columns * sizeof(uint8_t)); - info->pathBuffer = fastCalloc(3 * 8 * map->rows * map->columns, sizeof(int8_t)); - } - - e->humanInput = false; - e->humanDroneInput = 0; - e->connectedControllers = 0; - - return e; -} - -void clearEnv(iwEnv *e) { - // rewards get cleared in stepEnv every step - // memset(e->masks, 1, e->numAgents * sizeof(uint8_t)); - memset(e->terminals, 0x0, e->numAgents * sizeof(uint8_t)); - memset(e->truncations, 0x0, e->numAgents * sizeof(uint8_t)); - - e->episodeLength = 0; - memset(e->stats, 0x0, sizeof(e->stats)); - - for (uint8_t i = 0; i < e->numDrones; i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - destroyDrone(e, drone); - } - - for (size_t i = 0; i < cc_array_size(e->floatingWalls); i++) { - wallEntity *wall = safe_array_get_at(e->floatingWalls, i); - destroyWall(e, wall, false); - } - - for (size_t i = 0; i < cc_array_size(e->pickups); i++) { - weaponPickupEntity *pickup = safe_array_get_at(e->pickups, i); - destroyWeaponPickup(e, pickup); - } - - for (size_t i = 0; i < cc_array_size(e->projectiles); i++) { - projectileEntity *p = safe_array_get_at(e->projectiles, i); - destroyProjectile(e, p, false, false); - } - - for (size_t i = 0; i < cc_array_size(e->explosions); i++) { - explosionInfo *explosion = safe_array_get_at(e->explosions, i); - fastFree(explosion); - } - - for (size_t i = 0; i < cc_array_size(e->dronePieces); i++) { - dronePieceEntity *piece = safe_array_get_at(e->dronePieces, i); - destroyDronePiece(e, piece); - } - - cc_array_remove_all(e->drones); - cc_array_remove_all(e->floatingWalls); - cc_array_remove_all(e->pickups); - cc_array_remove_all(e->projectiles); - cc_array_remove_all(e->explodingProjectiles); - cc_array_remove_all(e->explosions); - cc_array_remove_all(e->dronePieces); -} - -void destroyEnv(iwEnv *e) { - clearEnv(e); - - for (uint8_t i = 0; i < NUM_MAPS; i++) { - pathingInfo *info = &e->mapPathing[i]; - fastFree(info->paths); - fastFree(info->pathBuffer); - } - fastFree(e->mapPathing); - - for (size_t i = 0; i < cc_array_size(e->walls); i++) { - wallEntity *wall = safe_array_get_at(e->walls, i); - destroyWall(e, wall, false); - } - - for (size_t i = 0; i < cc_array_size(e->cells); i++) { - mapCell *cell = safe_array_get_at(e->cells, i); - fastFree(cell); - } - - for (size_t i = 0; i < cc_array_size(e->entities); i++) { - entity *ent = safe_array_get_at(e->entities, i); - fastFree(ent->id); - fastFree(ent); - } - b2DestroyIdPool(&e->idPool); - - cc_array_destroy(e->entities); - cc_array_destroy(e->cells); - cc_array_destroy(e->walls); - cc_array_destroy(e->drones); - cc_array_destroy(e->floatingWalls); - cc_array_destroy(e->pickups); - cc_array_destroy(e->projectiles); - cc_array_destroy(e->explosions); - cc_array_destroy(e->explodingProjectiles); - cc_array_destroy(e->dronePieces); - - b2DestroyWorld(e->worldID); -} - -void resetEnv(iwEnv *e) { - clearEnv(e); - setupEnv(e); -} - -float computeShotReward(const droneEntity *drone, const weaponInformation *weaponInfo) { - const float weaponForce = weaponInfo->fireMagnitude * weaponInfo->invMass; - const float scaledForce = (weaponForce * (weaponForce * SHOT_HIT_REWARD_COEF)) + 0.25f; - return scaledForce + computeHitStrength(drone); -} - -float computeExplosionReward(const droneEntity *drone) { - return computeHitStrength(drone) * EXPLOSION_HIT_REWARD_COEF; -} - -float computeReward(iwEnv *e, droneEntity *drone) { - float reward = 0.0f; - - if (drone->energyFullyDepleted && drone->energyRefillWait == DRONE_ENERGY_REFILL_EMPTY_WAIT) { - reward += ENERGY_EMPTY_PUNISHMENT; - } - - // only reward picking up a weapon if the standard weapon was - // previously held; every weapon is better than the standard - // weapon, but other weapons are situational better so don't - // reward switching a non-standard weapon - if (drone->stepInfo.pickedUpWeapon && drone->stepInfo.prevWeapon == STANDARD_WEAPON) { - reward += WEAPON_PICKUP_REWARD; - } - - for (uint8_t i = 0; i < e->numDrones; i++) { - if (i == drone->idx) { - continue; - } - droneEntity *enemyDrone = safe_array_get_at(e->drones, i); - const bool onTeam = drone->team == enemyDrone->team; - - if (drone->stepInfo.shotHit[i] != 0 && !onTeam) { - // subtract 1 from the weapon type because 1 is added so we - // can use 0 as no shot was hit - const weaponInformation *weaponInfo = weaponInfos[drone->stepInfo.shotHit[i] - 1]; - reward += computeShotReward(enemyDrone, weaponInfo); - } - if (drone->stepInfo.explosionHit[i] && !onTeam) { - reward += computeExplosionReward(enemyDrone); - } - - if (e->numAgents == e->numDrones) { - if (drone->stepInfo.shotTaken[i] != 0 && !onTeam) { - const weaponInformation *weaponInfo = weaponInfos[drone->stepInfo.shotTaken[i] - 1]; - reward -= computeShotReward(drone, weaponInfo) * 0.5f; - } - if (drone->stepInfo.explosionTaken[i] && !onTeam) { - reward -= computeExplosionReward(drone) * 0.5f; - } - } - - if (enemyDrone->dead && enemyDrone->diedThisStep) { - if (!onTeam) { - reward += ENEMY_DEATH_REWARD; - if (drone->killed[i]) { - reward += ENEMY_KILL_REWARD; - } - } else { - reward += TEAMMATE_DEATH_PUNISHMENT; - if (drone->killed[i]) { - reward += TEAMMATE_KILL_PUNISHMENT; - } - } - continue; - } - - const b2Vec2 enemyDirection = b2Normalize(b2Sub(enemyDrone->pos, drone->pos)); - const float velocityToEnemy = b2Dot(drone->lastVelocity, enemyDirection); - const float enemyDistance = b2Distance(enemyDrone->pos, drone->pos); - // stop rewarding approaching an enemy if they're very close - // to avoid constant clashing; always reward approaching when - // the current weapon is the shotgun, it greatly benefits from - // being close to enemies - if (velocityToEnemy > 0.1f && (drone->weaponInfo->type == SHOTGUN_WEAPON || enemyDistance > DISTANCE_CUTOFF)) { - reward += APPROACH_REWARD; - } - } - - return reward; -} - -const float REWARD_EPS = 1.0e-6f; - -void computeRewards(iwEnv *e, const bool roundOver, const int8_t winner, const int8_t winningTeam) { - if (roundOver && winner != -1 && winner < e->numAgents) { - e->rewards[winner] += WIN_REWARD; - } - - for (uint8_t i = 0; i < e->numDrones; i++) { - float reward = 0.0f; - droneEntity *drone = safe_array_get_at(e->drones, i); - if (!drone->dead) { - reward = computeReward(e, drone); - if (roundOver && winningTeam == drone->team) { - reward += WIN_REWARD; - } - } else if (drone->diedThisStep) { - reward = DEATH_PUNISHMENT; - if (drone->killedBy == drone->idx) { - reward += SELF_KILL_PUNISHMENT; - } - } - if (i < e->numAgents) { - e->rewards[i] += reward; - } - e->stats[i].returns += reward; - } -} - -static inline bool isActionNoop(const b2Vec2 action) { - return b2Length(action) < ACTION_NOOP_MAGNITUDE; -} - -agentActions _computeActions(iwEnv *e, droneEntity *drone, const agentActions *manualActions) { - agentActions actions = {0}; - - const uint8_t offset = drone->idx * CONTINUOUS_ACTION_SIZE; - if (manualActions == NULL) { - actions.move = (b2Vec2){.x = e->actions[offset + 0], .y = e->actions[offset + 1]}; - actions.aim = (b2Vec2){.x = e->actions[offset + 2], .y = e->actions[offset + 3]}; - if (e->continuousActions) { - actions.move.x = tanhf(actions.move.x); - actions.move.y = tanhf(actions.move.y); - actions.aim.x = tanhf(actions.aim.x); - actions.aim.y = tanhf(actions.aim.y); - } - actions.chargingWeapon = e->actions[offset + 4] > 0.0f; - actions.shoot = actions.chargingWeapon; - if (!actions.chargingWeapon && drone->chargingWeapon) { - actions.shoot = true; - } - actions.brake = e->actions[offset + 5] > 0.0f; - actions.chargingBurst = e->actions[offset + 6] > 0.0f; - } else { - actions.move = manualActions->move; - actions.aim = manualActions->aim; - actions.chargingWeapon = manualActions->chargingWeapon; - actions.shoot = manualActions->shoot; - actions.brake = manualActions->brake; - actions.chargingBurst = manualActions->chargingBurst; - actions.discardWeapon = manualActions->discardWeapon; - } - - // cap movement magnitude to 1.0 - if (b2Length(actions.move) > 1.0f) { - actions.move = b2Normalize(actions.move); - } else if (isActionNoop(actions.move)) { - actions.move = b2Vec2_zero; - } - - if (isActionNoop(actions.aim)) { - actions.aim = b2Vec2_zero; - } else { - actions.aim = b2Normalize(actions.aim); - } - - return actions; -} - -agentActions computeActions(iwEnv *e, droneEntity *drone, const agentActions *manualActions) { - const agentActions actions = _computeActions(e, drone, manualActions); - drone->lastMove = actions.move; - if (!b2VecEqual(actions.aim, b2Vec2_zero)) { - drone->lastAim = actions.aim; - } - return actions; -} - -void updateConnectedControllers(iwEnv *e) { - for (uint8_t i = 0; i < e->numDrones; i++) { - if (IsGamepadAvailable(i)) { - e->connectedControllers++; - } - } -} - -void updateHumanInputToggle(iwEnv *e) { - if (IsKeyPressed(KEY_LEFT_CONTROL)) { - e->humanInput = !e->humanInput; - if (!e->humanInput) { - e->connectedControllers = 0; - } - } - if (e->humanInput && e->connectedControllers == 0) { - updateConnectedControllers(e); - } - if (e->connectedControllers > 1) { - e->humanDroneInput = e->numDrones - e->connectedControllers; - return; - } - - if (IsKeyPressed(KEY_ONE) || IsKeyPressed(KEY_KP_1)) { - e->humanDroneInput = 0; - } - if (IsKeyPressed(KEY_TWO) || IsKeyPressed(KEY_KP_2)) { - e->humanDroneInput = 1; - } - if (e->numDrones >= 3 && (IsKeyPressed(KEY_THREE) || IsKeyPressed(KEY_KP_3))) { - e->humanDroneInput = 2; - } - if (e->numDrones >= 4 && (IsKeyPressed(KEY_FOUR) || IsKeyPressed(KEY_KP_4))) { - e->humanDroneInput = 3; - } -} - -agentActions getPlayerInputs(iwEnv *e, droneEntity *drone, uint8_t gamepadIdx) { - if (IsKeyPressed(KEY_R)) { - e->needsReset = true; - } - - agentActions actions = {0}; - - bool controllerConnected = false; - if (IsGamepadAvailable(gamepadIdx)) { - controllerConnected = true; - } - if (controllerConnected) { - float lStickX = GetGamepadAxisMovement(gamepadIdx, GAMEPAD_AXIS_LEFT_X); - float lStickY = GetGamepadAxisMovement(gamepadIdx, GAMEPAD_AXIS_LEFT_Y); - float rStickX = GetGamepadAxisMovement(gamepadIdx, GAMEPAD_AXIS_RIGHT_X); - float rStickY = GetGamepadAxisMovement(gamepadIdx, GAMEPAD_AXIS_RIGHT_Y); - - if (IsGamepadButtonDown(gamepadIdx, GAMEPAD_BUTTON_RIGHT_TRIGGER_2)) { - actions.chargingWeapon = true; - actions.shoot = true; - } else if (drone->chargingWeapon && IsGamepadButtonUp(gamepadIdx, GAMEPAD_BUTTON_RIGHT_TRIGGER_2)) { - actions.shoot = true; - } - - if (IsGamepadButtonDown(gamepadIdx, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) { - actions.brake = true; - } - - if (IsGamepadButtonDown(gamepadIdx, GAMEPAD_BUTTON_RIGHT_TRIGGER_1) || IsGamepadButtonDown(gamepadIdx, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) { - actions.chargingBurst = true; - } - - if (IsGamepadButtonPressed(gamepadIdx, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) { - actions.discardWeapon = true; - } - - actions.move = (b2Vec2){.x = lStickX, .y = lStickY}; - actions.aim = (b2Vec2){.x = rStickX, .y = rStickY}; - return computeActions(e, drone, &actions); - } - if (!controllerConnected && drone->idx != e->humanDroneInput) { - return actions; - } - - b2Vec2 move = b2Vec2_zero; - if (IsKeyDown(KEY_W)) { - move.y += -1.0f; - } - if (IsKeyDown(KEY_S)) { - move.y += 1.0f; - } - if (IsKeyDown(KEY_A)) { - move.x += -1.0f; - } - if (IsKeyDown(KEY_D)) { - move.x += 1.0f; - } - actions.move = b2Normalize(move); - - Vector2 mousePos = (Vector2){.x = (float)GetMouseX(), .y = (float)GetMouseY()}; - actions.aim = b2Normalize(b2Sub(rayVecToB2Vec(e, mousePos), drone->pos)); - - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - actions.chargingWeapon = true; - actions.shoot = true; - } else if (drone->chargingWeapon && IsMouseButtonUp(MOUSE_BUTTON_LEFT)) { - actions.shoot = true; - } - if (IsKeyDown(KEY_SPACE)) { - actions.brake = true; - } - if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) { - actions.chargingBurst = true; - } - - return computeActions(e, drone, &actions); -} - -bool droneControlledByHuman(const iwEnv *e, uint8_t i) { - if (!e->humanInput) { - return false; - } - return (e->connectedControllers > 1 && i >= e->humanDroneInput) || (e->connectedControllers <= 1 && i == e->humanDroneInput); -} - -void addLog(iwEnv *e, Log *log) { - e->log.length += log->length; - e->log.ties += log->ties; - - for (uint8_t j = 0; j < e->numDrones; j++) { - e->log.stats[j].returns += log->stats[j].returns; - e->log.stats[j].wins += log->stats[j].wins; - - e->log.stats[j].distanceTraveled += log->stats[j].distanceTraveled; - e->log.stats[j].absDistanceTraveled += log->stats[j].absDistanceTraveled; - e->log.stats[j].brakeTime += log->stats[j].brakeTime; - e->log.stats[j].totalBursts += log->stats[j].totalBursts; - e->log.stats[j].burstsHit += log->stats[j].burstsHit; - e->log.stats[j].energyEmptied += log->stats[j].energyEmptied; - - for (uint8_t k = 0; k < NUM_WEAPONS; k++) { - e->log.stats[j].shotsFired[k] += log->stats[j].shotsFired[k]; - e->log.stats[j].shotsHit[k] += log->stats[j].shotsHit[k]; - e->log.stats[j].shotsTaken[k] += log->stats[j].shotsTaken[k]; - e->log.stats[j].ownShotsTaken[k] += log->stats[j].ownShotsTaken[k]; - e->log.stats[j].weaponsPickedUp[k] += log->stats[j].weaponsPickedUp[k]; - e->log.stats[j].shotDistances[k] += log->stats[j].shotDistances[k]; - } - - e->log.stats[j].totalShotsFired += log->stats[j].totalShotsFired; - e->log.stats[j].totalShotsHit += log->stats[j].totalShotsHit; - e->log.stats[j].totalShotsTaken += log->stats[j].totalShotsTaken; - e->log.stats[j].totalOwnShotsTaken += log->stats[j].totalOwnShotsTaken; - e->log.stats[j].totalWeaponsPickedUp += log->stats[j].totalWeaponsPickedUp; - e->log.stats[j].totalShotDistances += log->stats[j].totalShotDistances; - } - - e->log.n += 1.0f; -} - -void stepEnv(iwEnv *e) { - if (e->needsReset) { - DEBUG_LOG("Resetting environment"); - resetEnv(e); - -#ifdef __EMSCRIPTEN__ - lastFrameTime = emscripten_get_now(); - accumulator = 0.0; -#endif - } - - agentActions stepActions[e->numDrones]; - memset(stepActions, 0x0, e->numDrones * sizeof(agentActions)); - - // preprocess agent actions for the next frameSkip steps - for (uint8_t i = 0; i < e->numDrones; i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead || droneControlledByHuman(e, i)) { - continue; - } - - if (i < e->numAgents) { - stepActions[i] = computeActions(e, drone, NULL); - } else { - const agentActions scriptedActions = scriptedAgentActions(e, drone); - stepActions[i] = computeActions(e, drone, &scriptedActions); - } - } - - // reset reward buffer - memset(e->rewards, 0x0, e->numAgents * sizeof(float)); - - for (int i = 0; i < e->frameSkip; i++) { -#ifdef __EMSCRIPTEN__ - // running at a fixed frame rate doesn't seem to work well in - // the browser, so we need to adjust to handle a variable frame - // rate; see https://www.gafferongames.com/post/fix_your_timestep/ - const double curTime = emscripten_get_now(); - const double deltaTime = (curTime - lastFrameTime) / 1000.0; - lastFrameTime = curTime; - - accumulator += deltaTime; - while (accumulator >= e->deltaTime) { - if (e->needsReset) { - break; - } -#endif - e->episodeLength++; - - // handle actions - if (e->client != NULL) { - updateHumanInputToggle(e); - } - - for (uint8_t i = 0; i < e->numDrones; i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - memset(&drone->stepInfo, 0x0, sizeof(droneStepInfo)); - if (drone->dead) { - drone->diedThisStep = false; - } - memset(&drone->killed, 0x0, sizeof(drone->killed)); - } - - for (uint8_t i = 0; i < e->numDrones; i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - - agentActions actions; - // take inputs from humans every frame - if (droneControlledByHuman(e, i)) { - actions = getPlayerInputs(e, drone, i - e->humanDroneInput); - } else { - actions = stepActions[i]; - } - - if (actions.discardWeapon) { - droneDiscardWeapon(e, drone); - } - if (actions.shoot) { - droneShoot(e, drone, actions.aim, actions.chargingWeapon); - } - if (actions.chargingBurst) { - droneChargeBurst(e, drone); - } else if (drone->chargingBurst) { - droneBurst(e, drone); - } - if (!b2VecEqual(actions.move, b2Vec2_zero)) { - droneMove(e, drone, actions.move); - } - droneBrake(e, drone, actions.brake); - - // update shield velocity if its active - if (drone->shield != NULL) { - b2Body_SetLinearVelocity(drone->shield->bodyID, b2Body_GetLinearVelocity(drone->bodyID)); - } - } - - b2World_Step(e->worldID, e->deltaTime, e->box2dSubSteps); - - // update dynamic body positions and velocities - handleBodyMoveEvents(e); - - // handle collisions - handleContactEvents(e); - handleSensorEvents(e); - - // handle sudden death - e->stepsLeft = max(e->stepsLeft - 1, 0); - if ((!e->isTraining || e->numDrones == e->numAgents) && e->stepsLeft == 0) { - e->suddenDeathSteps = max(e->suddenDeathSteps - 1, 0); - if (e->suddenDeathSteps == 0) { - DEBUG_LOG("placing sudden death walls"); - handleSuddenDeath(e); - e->suddenDeathSteps = e->totalSuddenDeathSteps; - } - } - - projectilesStep(e); - - int8_t lastAlive = -1; - int8_t lastAliveTeam = -1; - bool allAliveOnSameTeam = false; - bool roundOver = false; - uint8_t deadDrones = 0; - for (uint8_t i = 0; i < e->numDrones; i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->livesLeft != 0) { - if (!droneStep(e, drone)) { - // couldn't find a respawn position, end the round - deadDrones++; - roundOver = true; - } - lastAlive = i; - - if (e->teamsEnabled) { - if (lastAliveTeam == -1) { - lastAliveTeam = drone->team; - allAliveOnSameTeam = true; - } else if (drone->team != lastAliveTeam) { - allAliveOnSameTeam = false; - } - } - } else { - deadDrones++; - if (i < e->numAgents) { - if (drone->diedThisStep) { - e->terminals[i] = 1; - } - // else { - // e->masks[i] = 0; - // } - } - } - } - - weaponPickupsStep(e); - - if (!roundOver) { - roundOver = deadDrones >= e->numDrones - 1; - } - if (e->teamsEnabled && allAliveOnSameTeam) { - roundOver = true; - lastAlive = -1; - } - // if the enemy drone(s) are scripted don't enable sudden death - // so that the agent has to work for victories - if (e->isTraining && e->numDrones != e->numAgents && e->stepsLeft == 0) { - roundOver = true; - lastAliveTeam = -1; - } - if (roundOver && deadDrones < e->numDrones - 1) { - lastAlive = -1; - } - computeRewards(e, roundOver, lastAlive, lastAliveTeam); - - if (e->client != NULL) { - renderEnv(e, false, roundOver, lastAlive, lastAliveTeam); - } - - if (roundOver) { - if (e->numDrones != e->numAgents && e->stepsLeft == 0) { - DEBUG_LOG("truncating episode"); - memset(e->truncations, 1, e->numAgents * sizeof(uint8_t)); - } else { - DEBUG_LOG("terminating episode"); - memset(e->terminals, 1, e->numAgents * sizeof(uint8_t)); - } - - Log log = {0}; - log.length = e->episodeLength; - if (lastAlive != -1) { - e->stats[lastAlive].wins = 1.0f; - } else if (!e->teamsEnabled || (e->teamsEnabled && lastAliveTeam == -1)) { - log.ties = 1.0f; - } - - for (uint8_t i = 0; i < e->numDrones; i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (!drone->dead && e->teamsEnabled && drone->team == lastAliveTeam) { - e->stats[i].wins = 1.0f; - } - // set absolute distance traveled of agent drones - e->stats[i].absDistanceTraveled = b2Distance(drone->initalPos, drone->pos); - } - - memcpy(log.stats, e->stats, sizeof(e->stats)); - addLog(e, &log); - - e->needsReset = true; - break; - } -#ifdef __EMSCRIPTEN__ - accumulator -= e->deltaTime; - } - - if (e->needsReset) { - break; - } -#endif - } - -#ifndef NDEBUG - bool gotReward = false; - for (uint8_t i = 0; i < e->numDrones; i++) { - if (e->rewards[i] > REWARD_EPS || e->rewards[i] < -REWARD_EPS) { - gotReward = true; - break; - } - } - if (gotReward) { - DEBUG_RAW_LOG("!!! rewards: ["); - for (uint8_t i = 0; i < e->numDrones; i++) { - const float reward = e->rewards[i]; - DEBUG_RAW_LOGF("%f", reward); - if (i < e->numDrones - 1) { - DEBUG_RAW_LOG(", "); - } - } - DEBUG_RAW_LOGF("] step %d\n", e->totalSteps - e->stepsLeft); - } -#endif - - computeObs(e); -} - -#endif diff --git a/pufferlib/ocean/impulse_wars/game.h b/pufferlib/ocean/impulse_wars/game.h deleted file mode 100644 index 7a8260d23..000000000 --- a/pufferlib/ocean/impulse_wars/game.h +++ /dev/null @@ -1,2854 +0,0 @@ -#ifndef IMPULSE_WARS_GAME_H -#define IMPULSE_WARS_GAME_H - -#include "helpers.h" -#include "settings.h" -#include "types.h" - -// these functions call each other so need to be forward declared -void destroyProjectile(iwEnv *e, projectileEntity *projectile, const bool processExplosions, const bool full); -void createExplosion(iwEnv *e, droneEntity *drone, const projectileEntity *projectile, const b2ExplosionDef *def); - -void updateTrailPoints(trailPoints *tp, const uint8_t maxLen, const b2Vec2 pos); - -entity *createEntity(iwEnv *e, enum entityType type, void *entityData) { - int32_t id = b2AllocId(&e->idPool); - entity *ent = NULL; - if (id == (int64_t)cc_array_size(e->entities)) { - ent = fastCalloc(1, sizeof(entity)); - ent->id = fastCalloc(1, sizeof(entityID)); - cc_array_add(e->entities, ent); - } else { - ent = safe_array_get_at(e->entities, id); - } - - ent->generation += 1; - ent->type = type; - ent->entity = entityData; - ent->id->id = id + 1, - ent->id->generation = ent->generation; - - return ent; -} - -void destroyEntity(iwEnv *e, entity *ent) { - b2FreeId(&e->idPool, ent->id->id - 1); - ent->id->id = 0; -} - -// will return a pointer to an entity or NULL if the given entity ID is -// invalid or orphaned -entity *getEntityByID(const iwEnv *e, const entityID *id) { - if (id->id < 1 || (int64_t)cc_array_size(e->entities) < id->id) { - // invalid index - return NULL; - } - entity *ent = safe_array_get_at(e->entities, id->id - 1); - if (ent->id->id == 0 || ent->generation != id->generation) { - // orphaned entity - return NULL; - } - return ent; -} - -static inline bool entityTypeIsWall(const enum entityType type) { - // walls are the first 3 entity types - return type <= DEATH_WALL_ENTITY; -} - -static inline int16_t cellIndex(const iwEnv *e, const int8_t col, const int8_t row) { - return col + (row * e->map->columns); -} - -// discretizes an entity's position into a cell index; -1 is returned if -// the position is out of bounds of the map -static inline int16_t entityPosToCellIdx(const iwEnv *e, const b2Vec2 pos) { - const float cellX = pos.x + (((float)e->map->columns * WALL_THICKNESS) / 2.0f); - const float cellY = pos.y + (((float)e->map->rows * WALL_THICKNESS) / 2.0f); - const int8_t cellCol = cellX / WALL_THICKNESS; - const int8_t cellRow = cellY / WALL_THICKNESS; - const int16_t cellIdx = cellIndex(e, cellCol, cellRow); - // set the cell to -1 if it's out of bounds - if (cellIdx < 0 || (uint16_t)cellIdx >= cc_array_size(e->cells)) { - DEBUG_LOGF("invalid cell index: %d from position: (%f, %f)", cellIdx, pos.x, pos.y); - return -1; - } - return cellIdx; -} - -typedef struct overlapAABBCtx { - bool overlaps; -} overlapAABBCtx; - -bool overlapAABBCallback(b2ShapeId shapeID, void *context) { - if (!b2Shape_IsValid(shapeID)) { - return true; - } - - overlapAABBCtx *ctx = context; - ctx->overlaps = true; - return true; -} - -// returns true if the given position overlaps with shapes in a bounding -// box with a height and width of distance -bool isOverlappingAABB(const iwEnv *e, const b2Vec2 pos, const float distance, const b2QueryFilter filter) { - b2AABB bounds = { - .lowerBound = {.x = pos.x - distance, .y = pos.y - distance}, - .upperBound = {.x = pos.x + distance, .y = pos.y + distance}, - }; - overlapAABBCtx ctx = {.overlaps = false}; - b2World_OverlapAABB(e->worldID, bounds, filter, overlapAABBCallback, &ctx); - return ctx.overlaps; -} - -// TODO: store a shape proxy in entities? -b2ShapeProxy makeDistanceProxyFromType(const enum entityType type, bool *isCircle) { - b2ShapeProxy proxy = {0}; - switch (type) { - case DRONE_ENTITY: - *isCircle = true; - proxy.count = 1; - proxy.radius = DRONE_RADIUS; - break; - case SHIELD_ENTITY: - *isCircle = true; - proxy.count = 1; - proxy.radius = DRONE_SHIELD_RADIUS; - break; - case WEAPON_PICKUP_ENTITY: - proxy.count = 4; - proxy.points[0] = (b2Vec2){.x = -PICKUP_THICKNESS / 2.0f, .y = -PICKUP_THICKNESS / 2.0f}; - proxy.points[1] = (b2Vec2){.x = -PICKUP_THICKNESS / 2.0f, .y = +PICKUP_THICKNESS / 2.0f}; - proxy.points[2] = (b2Vec2){.x = +PICKUP_THICKNESS / 2.0f, .y = -PICKUP_THICKNESS / 2.0f}; - proxy.points[3] = (b2Vec2){.x = +PICKUP_THICKNESS / 2.0f, .y = +PICKUP_THICKNESS / 2.0f}; - break; - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - proxy.count = 4; - proxy.points[0] = (b2Vec2){.x = -FLOATING_WALL_THICKNESS / 2.0f, .y = -FLOATING_WALL_THICKNESS / 2.0f}; - proxy.points[1] = (b2Vec2){.x = -FLOATING_WALL_THICKNESS / 2.0f, .y = +FLOATING_WALL_THICKNESS / 2.0f}; - proxy.points[2] = (b2Vec2){.x = +FLOATING_WALL_THICKNESS / 2.0f, .y = -FLOATING_WALL_THICKNESS / 2.0f}; - proxy.points[3] = (b2Vec2){.x = +FLOATING_WALL_THICKNESS / 2.0f, .y = +FLOATING_WALL_THICKNESS / 2.0f}; - break; - default: - ERRORF("unknown entity type for shape distance: %d", type); - } - - return proxy; -} - -b2ShapeProxy makeDistanceProxy(const entity *ent, bool *isCircle) { - if (ent->type == PROJECTILE_ENTITY) { - *isCircle = true; - b2ShapeProxy proxy = {0}; - const projectileEntity *proj = ent->entity; - proxy.count = 1; - proxy.radius = proj->weaponInfo->radius; - return proxy; - } - - return makeDistanceProxyFromType(ent->type, isCircle); -} - -b2Transform entityTransform(const entity *ent) { - b2Transform transform; - wallEntity *wall; - projectileEntity *proj; - droneEntity *drone; - - switch (ent->type) { - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - wall = ent->entity; - transform.p = wall->pos; - transform.q = wall->rot; - return transform; - case PROJECTILE_ENTITY: - proj = ent->entity; - transform.p = proj->pos; - transform.q = b2Rot_identity; - return transform; - case DRONE_ENTITY: - drone = ent->entity; - transform.p = drone->pos; - transform.q = b2Rot_identity; - return transform; - default: - ERRORF("unknown entity type: %d", ent->type); - } -} - -// returns the closest points between two entities -b2DistanceOutput closestPoint(const entity *srcEnt, const entity *dstEnt) { - bool isCircle = false; - b2DistanceInput input; - input.proxyA = makeDistanceProxy(srcEnt, &isCircle); - input.proxyB = makeDistanceProxy(dstEnt, &isCircle); - input.transformA = entityTransform(srcEnt); - input.transformB = entityTransform(dstEnt); - if (input.proxyA.radius != 0.0f && input.proxyB.radius != 0.0f) { - b2DistanceOutput output = {0}; - output.distance = b2Distance(input.transformB.p, input.transformA.p) - input.proxyA.radius - input.proxyB.radius; - output.normal = b2Normalize(b2Sub(input.transformB.p, input.transformA.p)); - output.pointA = b2MulAdd(input.transformA.p, input.proxyA.radius, output.normal); - output.pointB = b2MulAdd(input.transformB.p, input.proxyB.radius, output.normal); - return output; - } - - input.useRadii = isCircle; - - b2SimplexCache cache = {0}; - return b2ShapeDistance(&input, &cache, NULL, 0); -} - -typedef struct behindWallContext { - const entity *dstEnt; - const enum entityType *targetType; - bool hit; -} behindWallContext; - -float posBehindWallCallback(b2ShapeId shapeID, b2Vec2 point, b2Vec2 normal, float fraction, void *context) { - // these are unused but required by the b2CastResultFcn callback prototype - MAYBE_UNUSED(point); - MAYBE_UNUSED(normal); - MAYBE_UNUSED(fraction); - - behindWallContext *ctx = context; - const entity *ent = b2Shape_GetUserData(shapeID); - if (ent == ctx->dstEnt || (ctx->targetType != NULL && ent->type == *ctx->targetType)) { - return -1; - } - ctx->hit = true; - return 0; -} - -// returns true if there are shapes that match filter between startPos and endPos -bool posBehindWall(const iwEnv *e, const b2Vec2 srcPos, const b2Vec2 dstPos, const entity *dstEnt, const b2QueryFilter filter, const enum entityType *targetType) { - const float rayDistance = b2Distance(srcPos, dstPos); - // if the two points are extremely close we can safely assume the - // entity isn't behind a wall - if (rayDistance <= 1.0f) { - return false; - } - - const b2Vec2 translation = b2Sub(dstPos, srcPos); - behindWallContext ctx = { - .dstEnt = dstEnt, - .targetType = targetType, - .hit = false, - }; - b2World_CastRay(e->worldID, srcPos, translation, filter, posBehindWallCallback, &ctx); - return ctx.hit; -} - -typedef struct overlapCircleCtx { - const iwEnv *e; - const entity *ent; - const enum entityType *targetType; - const b2QueryFilter filter; - bool overlaps; -} overlapCircleCtx; - -bool isOverlappingCircleCallback(b2ShapeId shapeID, void *context) { - if (!b2Shape_IsValid(shapeID)) { - return true; - } - - overlapCircleCtx *ctx = context; - const entity *overlappingEnt = b2Shape_GetUserData(shapeID); - if (ctx->targetType != NULL && overlappingEnt->type != *ctx->targetType) { - return true; - } - - const b2DistanceOutput output = closestPoint(ctx->ent, overlappingEnt); - const bool behind = posBehindWall(ctx->e, output.pointA, output.pointB, ctx->ent, ctx->filter, ctx->targetType); - if (!behind) { - ctx->overlaps = true; - } else if (ctx->ent->type == PROJECTILE_ENTITY) { - projectileEntity *proj = ctx->ent->entity; - droneEntity *drone = overlappingEnt->entity; - proj->dronesBehindWalls[proj->numDronesBehindWalls++] = drone->idx; - } - return behind; -} - -bool isOverlappingCircleInLineOfSight(const iwEnv *e, const entity *ent, const b2Vec2 startPos, const float radius, const b2QueryFilter filter, const enum entityType *targetType) { - const b2ShapeProxy cirProxy = b2MakeProxy(&startPos, 1, radius); - overlapCircleCtx ctx = { - .e = e, - .ent = ent, - .targetType = targetType, - .filter = (b2QueryFilter){ - .categoryBits = filter.categoryBits, - .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE, - }, - .overlaps = false, - }; - b2World_OverlapShape(e->worldID, &cirProxy, filter, isOverlappingCircleCallback, &ctx); - return ctx.overlaps; -} - -uint8_t cellOffsets[8][2] = { - {-1, 0}, // left - {1, 0}, // right - {0, -1}, // up - {0, 1}, // down - {-1, -1}, // top-left - {1, -1}, // top-right - {-1, 1}, // bottom-left - {1, 1}, // bottom-right -}; - -// returns true and sets emptyPos to the position of an empty cell -// that is an appropriate distance away from other entities if one exists; -// if quad is set to -1 a random valid position from anywhere on the map -// will be returned, otherwise a position within the specified quadrant -// will be returned -bool findOpenPos(iwEnv *e, const enum shapeCategory shapeType, b2Vec2 *emptyPos, int8_t quad) { - uint8_t checkedCells[BITNSLOTS(MAX_CELLS)] = {0}; - const size_t nCells = cc_array_size(e->cells) - 1; - uint16_t attempts = 0; - bool laxDroneDistanceChecks = false; - float minSpawnDistance = MIN_SPAWN_DISTANCE; - - while (true) { - if (attempts == nCells) { - // if we're trying to find a position for a drone and sudden - // death walls have been placed, try again this time ignoring - // distance checks; the drone must be spawned next - // to a death wall in this case - if (shapeType == DRONE_SHAPE && e->suddenDeathWallsPlaced && !laxDroneDistanceChecks) { - attempts = 0; - memset(checkedCells, 0x0, BITNSLOTS(MAX_CELLS)); - laxDroneDistanceChecks = true; - minSpawnDistance = MIN_SD_SPAWN_DISTANCE; - continue; - } - return false; - } - - uint16_t cellIdx; - if (quad == -1) { - cellIdx = randInt(&e->randState, 0, nCells); - } else { - const float minX = e->map->spawnQuads[quad].min.x; - const float minY = e->map->spawnQuads[quad].min.y; - const float maxX = e->map->spawnQuads[quad].max.x; - const float maxY = e->map->spawnQuads[quad].max.y; - - b2Vec2 randPos = {.x = randFloat(&e->randState, minX, maxX), .y = randFloat(&e->randState, minY, maxY)}; - cellIdx = entityPosToCellIdx(e, randPos); - } - if (bitTest(checkedCells, cellIdx)) { - continue; - } - bitSet(checkedCells, cellIdx); - attempts++; - - const mapCell *cell = safe_array_get_at(e->cells, cellIdx); - if (cell->ent != NULL) { - continue; - } - - bool tooClose = false; - switch (shapeType) { - case WEAPON_PICKUP_SHAPE: - // ensure pickups don't spawn too close to other pickups - for (uint8_t i = 0; i < cc_array_size(e->pickups); i++) { - const weaponPickupEntity *pickup = safe_array_get_at(e->pickups, i); - if (b2DistanceSquared(cell->pos, pickup->pos) < PICKUP_SPAWN_DISTANCE_SQUARED) { - tooClose = true; - break; - } - } - if (tooClose) { - continue; - } - break; - case DRONE_SHAPE: - if (e->suddenDeathWallsPlaced) { - if (!laxDroneDistanceChecks) { - // if sudden death walls have been placed, ignore the - // spawn points as they may be covered by death walls; - // instead just try and find a cell that doesn't neighbor - // a death wall - const uint8_t cellCol = cellIdx / e->map->columns; - const uint8_t cellRow = cellIdx % e->map->columns; - bool deathWallNeighboring = false; - for (uint8_t i = 0; i < 8; i++) { - const int8_t col = cellCol + cellOffsets[i][0]; - const int8_t row = cellRow + cellOffsets[i][1]; - if (row < 0 || row >= e->map->rows || col < 0 || col >= e->map->columns) { - continue; - } - const int16_t testCellIdx = cellIndex(e, col, row); - const mapCell *testCell = safe_array_get_at(e->cells, testCellIdx); - if (testCell->ent != NULL && testCell->ent->type == DEATH_WALL_ENTITY) { - deathWallNeighboring = true; - break; - } - } - if (deathWallNeighboring) { - continue; - } - } - - // ensure drones aren't spawning on top of each other - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - if (b2DistanceSquared(cell->pos, drone->pos) == 0.0f) { - tooClose = true; - break; - } - } - if (tooClose) { - continue; - } - } else { - if (!e->map->droneSpawns[cellIdx]) { - continue; - } - - // ensure drones don't spawn too close to other drones - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - if (b2DistanceSquared(cell->pos, drone->pos) < DRONE_DRONE_SPAWN_DISTANCE_SQUARED) { - tooClose = true; - break; - } - } - if (tooClose) { - continue; - } - } - break; - default: - break; - } - - uint64_t maskBits = FLOATING_WALL_SHAPE | WEAPON_PICKUP_SHAPE | DRONE_SHAPE; - if (shapeType != FLOATING_WALL_SHAPE && !laxDroneDistanceChecks) { - maskBits &= ~shapeType; - } - const b2QueryFilter filter = { - .categoryBits = shapeType, - .maskBits = maskBits, - }; - if (!isOverlappingAABB(e, cell->pos, minSpawnDistance, filter)) { - *emptyPos = cell->pos; - return true; - } - } -} - -entity *createWall(iwEnv *e, const b2Vec2 pos, const float width, const float height, int16_t cellIdx, const enum entityType type, const bool floating) { - ASSERT(cellIdx != -1); - ASSERT(entityTypeIsWall(type)); - - b2BodyDef wallBodyDef = b2DefaultBodyDef(); - wallBodyDef.position = pos; - if (floating) { - wallBodyDef.type = b2_dynamicBody; - wallBodyDef.linearDamping = FLOATING_WALL_DAMPING; - wallBodyDef.angularDamping = FLOATING_WALL_DAMPING; - wallBodyDef.isAwake = false; - } - b2BodyId wallBodyID = b2CreateBody(e->worldID, &wallBodyDef); - - b2Vec2 extent = {.x = width / 2.0f, .y = height / 2.0f}; - b2ShapeDef wallShapeDef = b2DefaultShapeDef(); - wallShapeDef.invokeContactCreation = false; - wallShapeDef.density = WALL_DENSITY; - wallShapeDef.material.restitution = STANDARD_WALL_RESTITUTION; - wallShapeDef.material.friction = STANDARD_WALL_FRICTION; - wallShapeDef.filter.categoryBits = WALL_SHAPE; - wallShapeDef.filter.maskBits = FLOATING_WALL_SHAPE | PROJECTILE_SHAPE | DRONE_SHAPE | SHIELD_SHAPE | DRONE_PIECE_SHAPE; - if (floating) { - wallShapeDef.filter.categoryBits = FLOATING_WALL_SHAPE; - wallShapeDef.filter.maskBits |= WALL_SHAPE | WEAPON_PICKUP_SHAPE; - wallShapeDef.enableSensorEvents = true; - } - - if (type == BOUNCY_WALL_ENTITY) { - wallShapeDef.material.restitution = BOUNCY_WALL_RESTITUTION; - wallShapeDef.material.friction = 0.0f; - } else if (type == DEATH_WALL_ENTITY) { - wallShapeDef.enableContactEvents = true; - } - - wallEntity *wall = fastCalloc(1, sizeof(wallEntity)); - wall->bodyID = wallBodyID; - wall->pos = pos; - wall->rot = b2Rot_identity; - wall->velocity = b2Vec2_zero; - wall->extent = extent; - wall->mapCellIdx = cellIdx; - wall->isFloating = floating; - wall->type = type; - wall->isSuddenDeath = e->suddenDeathWallsPlaced; - - entity *ent = createEntity(e, type, wall); - wall->ent = ent; - - wallShapeDef.userData = ent; - const b2Polygon wallPolygon = b2MakeBox(extent.x, extent.y); - wall->shapeID = b2CreatePolygonShape(wallBodyID, &wallShapeDef, &wallPolygon); - b2Body_SetUserData(wall->bodyID, ent); - - if (floating) { - cc_array_add(e->floatingWalls, wall); - } else { - cc_array_add(e->walls, wall); - } - - return ent; -} - -void destroyWall(iwEnv *e, wallEntity *wall, const bool full) { - destroyEntity(e, wall->ent); - - if (full) { - mapCell *cell = safe_array_get_at(e->cells, wall->mapCellIdx); - cell->ent = NULL; - } - - b2DestroyBody(wall->bodyID); - fastFree(wall); -} - -enum weaponType randWeaponPickupType(iwEnv *e) { - // spawn weapon pickups according to their spawn weights and how many - // pickups are currently spawned with different weapons - float totalWeight = 0.0f; - float spawnWeights[_NUM_WEAPONS - 1] = {0}; - for (uint8_t i = 1; i < NUM_WEAPONS; i++) { - if (i == e->defaultWeapon->type) { - continue; - } - spawnWeights[i - 1] = weaponInfos[i]->spawnWeight / ((e->spawnedWeaponPickups[i] + 1) * 2.0f); - totalWeight += spawnWeights[i - 1]; - } - - const float randPick = randFloat(&e->randState, 0.0f, totalWeight); - float cumulativeWeight = 0.0f; - enum weaponType type = STANDARD_WEAPON; - for (uint8_t i = 1; i < NUM_WEAPONS; i++) { - if (i == e->defaultWeapon->type) { - continue; - } - cumulativeWeight += spawnWeights[i - 1]; - if (randPick < cumulativeWeight) { - type = i; - break; - } - } - ASSERT(type != STANDARD_WEAPON && type != e->defaultWeapon->type); - e->spawnedWeaponPickups[type]++; - - return type; -} - -void createWeaponPickupBodyShape(const iwEnv *e, weaponPickupEntity *pickup) { - pickup->bodyDestroyed = false; - - b2BodyDef pickupBodyDef = b2DefaultBodyDef(); - pickupBodyDef.position = pickup->pos; - pickupBodyDef.userData = pickup->ent; - pickup->bodyID = b2CreateBody(e->worldID, &pickupBodyDef); - - b2ShapeDef pickupShapeDef = b2DefaultShapeDef(); - pickupShapeDef.filter.categoryBits = WEAPON_PICKUP_SHAPE; - pickupShapeDef.filter.maskBits = FLOATING_WALL_SHAPE | DRONE_SHAPE; - pickupShapeDef.isSensor = true; - pickupShapeDef.enableSensorEvents = true; - pickupShapeDef.userData = pickup->ent; - const b2Polygon pickupPolygon = b2MakeBox(PICKUP_THICKNESS / 2.0f, PICKUP_THICKNESS / 2.0f); - pickup->shapeID = b2CreatePolygonShape(pickup->bodyID, &pickupShapeDef, &pickupPolygon); -} - -void createWeaponPickup(iwEnv *e) { - // ensure weapon pickups are initially spawned somewhat uniformly - b2Vec2 pos; - e->lastSpawnQuad = (e->lastSpawnQuad + 1) % 4; - if (!findOpenPos(e, WEAPON_PICKUP_SHAPE, &pos, e->lastSpawnQuad)) { - ERROR("no open position for weapon pickup"); - } - - weaponPickupEntity *pickup = fastCalloc(1, sizeof(weaponPickupEntity)); - pickup->weapon = randWeaponPickupType(e); - pickup->respawnWait = 0.0f; - pickup->floatingWallsTouching = 0; - pickup->pos = pos; - - entity *ent = createEntity(e, WEAPON_PICKUP_ENTITY, pickup); - pickup->ent = ent; - - const int16_t cellIdx = entityPosToCellIdx(e, pos); - if (cellIdx == -1) { - ERRORF("invalid position for weapon pickup spawn: (%f, %f)", pos.x, pos.y); - } - pickup->mapCellIdx = cellIdx; - mapCell *cell = safe_array_get_at(e->cells, cellIdx); - cell->ent = ent; - - createWeaponPickupBodyShape(e, pickup); - - cc_array_add(e->pickups, pickup); -} - -void destroyWeaponPickup(iwEnv *e, weaponPickupEntity *pickup) { - destroyEntity(e, pickup->ent); - - mapCell *cell = safe_array_get_at(e->cells, pickup->mapCellIdx); - cell->ent = NULL; - - if (!pickup->bodyDestroyed) { - b2DestroyBody(pickup->bodyID); - } - - fastFree(pickup); -} - -// destroys the pickup body and shape while the pickup is waiting to -// respawn to avoid spurious sensor overlap checks; enabling/disabling -// a body is almost as expensive and creating a new body in box2d, and -// manually moving (teleporting) it is expensive as well so destroying -// the body now and re-creating it later is the fastest -void disableWeaponPickup(iwEnv *e, weaponPickupEntity *pickup) { - DEBUG_LOGF("disabling weapon pickup at cell %d (%f, %f)", pickup->mapCellIdx, pickup->pos.x, pickup->pos.y); - - pickup->respawnWait = PICKUP_RESPAWN_WAIT; - if (e->suddenDeathWallsPlaced) { - pickup->respawnWait = SUDDEN_DEATH_PICKUP_RESPAWN_WAIT; - } - b2DestroyBody(pickup->bodyID); - pickup->bodyDestroyed = true; - - mapCell *cell = safe_array_get_at(e->cells, pickup->mapCellIdx); - ASSERT(cell->ent != NULL); - cell->ent = NULL; - - e->spawnedWeaponPickups[pickup->weapon]--; -} - -void createDroneShield(iwEnv *e, droneEntity *drone, const int8_t groupIdx) { - // the shield is comprised of 2 shapes over 2 bodies: - // 1. a kinematic body that allows the parent drone to be unaffected - // by collisions since kinematic bodies have essentially infinite mass - // 2. a shape on the drone body that collides with walls and other - // shields, since kinematic bodies don't collide with other kinematic - // bodies or static bodies - - b2BodyDef shieldBodyDef = b2DefaultBodyDef(); - shieldBodyDef.type = b2_kinematicBody; - shieldBodyDef.motionLocks = (b2MotionLocks){.angularZ = true}; - shieldBodyDef.position = drone->pos; - b2BodyId shieldBodyID = b2CreateBody(e->worldID, &shieldBodyDef); - - b2ShapeDef shieldShapeDef = b2DefaultShapeDef(); - shieldShapeDef.filter.categoryBits = SHIELD_SHAPE; - shieldShapeDef.filter.maskBits = PROJECTILE_SHAPE | DRONE_SHAPE; - shieldShapeDef.filter.groupIndex = groupIdx; - shieldShapeDef.enableContactEvents = true; - - b2ShapeDef shieldBufferShapeDef = b2DefaultShapeDef(); - shieldBufferShapeDef.density = 0.0f; - shieldBufferShapeDef.material.friction = DRONE_FRICTION; - shieldBufferShapeDef.material.restitution = DRONE_RESTITUTION; - shieldBufferShapeDef.filter.categoryBits = SHIELD_SHAPE; - shieldBufferShapeDef.filter.maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | SHIELD_SHAPE; - shieldBufferShapeDef.filter.groupIndex = groupIdx; - - shieldEntity *shield = fastCalloc(1, sizeof(shieldEntity)); - shield->drone = drone; - shield->bodyID = shieldBodyID; - shield->pos = drone->pos; - shield->health = DRONE_SHIELD_HEALTH; - float duration = DRONE_SHIELD_START_DURATION; - if (drone->livesLeft != DRONE_LIVES) { - duration = DRONE_SHIELD_RESPAWN_DURATION; - } - shield->duration = duration; - - entity *ent = createEntity(e, SHIELD_ENTITY, shield); - shield->ent = ent; - - shieldShapeDef.userData = ent; - const b2Circle shieldCircle = {.center = b2Vec2_zero, .radius = DRONE_SHIELD_RADIUS}; - shield->shapeID = b2CreateCircleShape(shieldBodyID, &shieldShapeDef, &shieldCircle); - b2Body_SetUserData(shield->bodyID, ent); - - shieldBufferShapeDef.userData = ent; - shield->bufferShapeID = b2CreateCircleShape(drone->bodyID, &shieldBufferShapeDef, &shieldCircle); - - drone->shield = shield; -} - -void createDrone(iwEnv *e, const uint8_t idx) { - const int8_t groupIdx = -(idx + 1); - b2BodyDef droneBodyDef = b2DefaultBodyDef(); - droneBodyDef.type = b2_dynamicBody; - - int8_t spawnQuad = -1; - if (!e->isTraining) { - // spawn drones in diagonal quadrants from each other so that - // they're more likely to be further apart if we're not training; - // doing this while training will result in much slower learning - // due to drones starting much farther apart - if (e->lastSpawnQuad == -1) { - spawnQuad = randInt(&e->randState, 0, 3); - } else if (e->numDrones == 2) { - spawnQuad = 3 - e->lastSpawnQuad; - } else { - spawnQuad = (e->lastSpawnQuad + 1) % 4; - } - e->lastSpawnQuad = spawnQuad; - } - if (!findOpenPos(e, DRONE_SHAPE, &droneBodyDef.position, spawnQuad)) { - ERROR("no open position for drone"); - } - - droneBodyDef.motionLocks = (b2MotionLocks){.angularZ = true}; - droneBodyDef.linearDamping = DRONE_LINEAR_DAMPING; - b2BodyId droneBodyID = b2CreateBody(e->worldID, &droneBodyDef); - b2ShapeDef droneShapeDef = b2DefaultShapeDef(); - droneShapeDef.density = DRONE_DENSITY; - droneShapeDef.material.friction = DRONE_FRICTION; - droneShapeDef.material.restitution = DRONE_RESTITUTION; - droneShapeDef.filter.categoryBits = DRONE_SHAPE; - droneShapeDef.filter.maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | WEAPON_PICKUP_SHAPE | PROJECTILE_SHAPE | DRONE_SHAPE | SHIELD_SHAPE; - droneShapeDef.filter.groupIndex = groupIdx; - droneShapeDef.enableContactEvents = true; - droneShapeDef.enableSensorEvents = true; - const b2Circle droneCircle = {.center = b2Vec2_zero, .radius = DRONE_RADIUS}; - - droneEntity *drone = fastCalloc(1, sizeof(droneEntity)); - drone->bodyID = droneBodyID; - drone->weaponInfo = e->defaultWeapon; - drone->ammo = weaponAmmo(e->defaultWeapon->type, drone->weaponInfo->type); - drone->energyLeft = DRONE_ENERGY_MAX; - drone->idx = idx; - drone->team = idx; - if (e->teamsEnabled) { - drone->team = idx / (e->numDrones / 2); - } - drone->initalPos = droneBodyDef.position; - drone->pos = droneBodyDef.position; - drone->mapCellIdx = entityPosToCellIdx(e, droneBodyDef.position); - drone->lastAim = (b2Vec2){.x = 0.0f, .y = -1.0f}; - drone->livesLeft = DRONE_LIVES; - create_array(&drone->brakeTrailPoints, 64); - drone->respawnGuideLifetime = UINT16_MAX; - memset(&drone->stepInfo, 0x0, sizeof(droneStepInfo)); - create_array(&drone->physicsTracking, 128); - drone->killedBy = -1; - - entity *ent = createEntity(e, DRONE_ENTITY, drone); - drone->ent = ent; - - droneShapeDef.userData = ent; - drone->shapeID = b2CreateCircleShape(droneBodyID, &droneShapeDef, &droneCircle); - b2Body_SetUserData(drone->bodyID, ent); - - cc_array_add(e->drones, drone); - - createDroneShield(e, drone, groupIdx); -} - -void droneAddEnergy(droneEntity *drone, float energy) { - // if a burst is charging, add the energy to the burst charge - if (drone->chargingBurst) { - drone->burstCharge = clamp(drone->burstCharge + energy); - } else { - drone->energyLeft = clamp(drone->energyLeft + energy); - } -} - -void createDronePiece(iwEnv *e, droneEntity *drone, const bool fromShield) { - const float distance = randFloat(&e->randState, DRONE_PIECE_MIN_DISTANCE, DRONE_PIECE_MAX_DISTANCE); - const b2Vec2 direction = {.x = randFloat(&e->randState, -1.0f, 1.0f), .y = randFloat(&e->randState, -1.0f, 1.0f)}; - const b2Vec2 pos = b2MulAdd(drone->pos, distance, direction); - const b2Rot rot = b2MakeRot(randFloat(&e->randState, -PI, PI)); - - dronePieceEntity *piece = fastCalloc(1, sizeof(dronePieceEntity)); - piece->droneIdx = drone->idx; - piece->pos = pos; - piece->rot = rot; - piece->isShieldPiece = fromShield; - piece->lifetime = UINT16_MAX; - - entity *ent = createEntity(e, DRONE_PIECE_ENTITY, piece); - piece->ent = ent; - - b2BodyDef pieceBodyDef = b2DefaultBodyDef(); - pieceBodyDef.type = b2_dynamicBody; - - pieceBodyDef.position = pos; - pieceBodyDef.rotation = rot; - pieceBodyDef.linearDamping = DRONE_PIECE_LINEAR_DAMPING; - pieceBodyDef.angularDamping = DRONE_PIECE_ANGULAR_DAMPING; - const float bonus = 1.0f + min(b2Length(drone->velocity) / 15.0f, 5.0f); - const float speed = randFloat(&e->randState, DRONE_PIECE_MIN_SPEED, DRONE_PIECE_MAX_SPEED) * bonus; - pieceBodyDef.linearVelocity = b2MulSV(speed, direction); - pieceBodyDef.angularVelocity = randFloat(&e->randState, -PI, PI); - pieceBodyDef.userData = ent; - piece->bodyID = b2CreateBody(e->worldID, &pieceBodyDef); - - b2ShapeDef pieceShapeDef = b2DefaultShapeDef(); - pieceShapeDef.filter.categoryBits = DRONE_PIECE_SHAPE; - pieceShapeDef.filter.maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | DRONE_PIECE_SHAPE; - pieceShapeDef.density = 1.0f; - pieceShapeDef.material.friction = 0.5f; - pieceShapeDef.userData = ent; - - // make pieces from the shield a bit smaller - if (fromShield) { - piece->vertices[0] = (b2Vec2){.x = 0.0f, .y = -1.0f}; - piece->vertices[1] = (b2Vec2){.x = -0.5f, .y = 0.0f}; - piece->vertices[2] = (b2Vec2){.x = 0.5f, .y = 0.0f}; - } else { - piece->vertices[0] = (b2Vec2){.x = 0.0f, .y = -1.5f}; - piece->vertices[1] = (b2Vec2){.x = -0.75f, .y = 0.0f}; - piece->vertices[2] = (b2Vec2){.x = 0.75f, .y = 0.0f}; - } - - const b2Hull pieceHull = b2ComputeHull(piece->vertices, 3); - const b2Polygon piecePolygon = b2MakePolygon(&pieceHull, 0.0f); - piece->shapeID = b2CreatePolygonShape(piece->bodyID, &pieceShapeDef, &piecePolygon); - - cc_array_add(e->dronePieces, piece); -} - -void destroyDronePiece(iwEnv *e, dronePieceEntity *piece) { - b2DestroyBody(piece->bodyID); - destroyEntity(e, piece->ent); - fastFree(piece); -} - -void destroyDroneShield(iwEnv *e, shieldEntity *shield, const bool createPieces) { - droneEntity *drone = shield->drone; - const float health = shield->health; - if (health <= 0.0f) { - droneAddEnergy(drone, DRONE_SHIELD_BREAK_ENERGY_COST); - } - drone->shield = NULL; - - b2DestroyBody(shield->bodyID); - b2DestroyShape(shield->bufferShapeID, false); - destroyEntity(e, shield->ent); - fastFree(shield); - - if (!createPieces || health > 0.0f) { - return; - } - - // only create pieces if the shield was broken early - for (uint8_t i = 0; i < DRONE_PIECE_COUNT; i++) { - createDronePiece(e, drone, true); - } -} - -void destroyDrone(iwEnv *e, droneEntity *drone) { - for (size_t i = 0; i < cc_array_size(drone->brakeTrailPoints); i++) { - brakeTrailPoint *trailPoint = safe_array_get_at(drone->brakeTrailPoints, i); - fastFree(trailPoint); - } - cc_array_destroy(drone->brakeTrailPoints); - for (size_t i = 0; i < cc_array_size(drone->physicsTracking); i++) { - physicsStepInfo *physicsStep = safe_array_get_at(drone->physicsTracking, i); - fastFree(physicsStep); - } - cc_array_destroy(drone->physicsTracking); - - destroyEntity(e, drone->ent); - - shieldEntity *shield = drone->shield; - if (shield != NULL) { - destroyDroneShield(e, shield, false); - } - - b2DestroyBody(drone->bodyID); - fastFree(drone); -} - -void droneApplyForce(const iwEnv *e, const droneEntity *drone, const b2Vec2 force, const uint8_t srcIdx) { - b2Body_ApplyForceToCenter(drone->bodyID, force, true); - - physicsStepInfo *physicsStep = fastCalloc(1, sizeof(physicsStepInfo)); - physicsStep->srcIdx = srcIdx; - physicsStep->force = force; - physicsStep->step = e->episodeLength; - cc_array_add(drone->physicsTracking, physicsStep); -} - -void droneTrackImpulse(const iwEnv *e, const droneEntity *drone, const b2Vec2 impulse, const uint8_t srcIdx) { - physicsStepInfo *physicsStep = fastCalloc(1, sizeof(physicsStepInfo)); - physicsStep->srcIdx = srcIdx; - physicsStep->impulse = impulse; - physicsStep->step = e->episodeLength; - cc_array_add(drone->physicsTracking, physicsStep); -} - -void droneApplyImpulse(const iwEnv *e, const droneEntity *drone, const b2Vec2 impulse, const uint8_t srcIdx) { - b2Body_ApplyLinearImpulseToCenter(drone->bodyID, impulse, true); - droneTrackImpulse(e, drone, impulse, srcIdx); -} - -void droneTrackBrake(const iwEnv *e, const droneEntity *drone) { - physicsStepInfo *physicsStep = fastCalloc(1, sizeof(physicsStepInfo)); - physicsStep->brakeToggled = true; - physicsStep->step = e->episodeLength; - cc_array_add(drone->physicsTracking, physicsStep); -} - -void droneChangeWeapon(const iwEnv *e, droneEntity *drone, const enum weaponType newWeapon) { - // top up ammo but change nothing else if the weapon is the same - if (drone->weaponInfo->type != newWeapon || drone->dead) { - drone->weaponInfo = weaponInfos[newWeapon]; - drone->weaponCooldown = 0.0f; - drone->weaponCharge = 0.0f; - drone->heat = 0; - } - drone->ammo = weaponAmmo(e->defaultWeapon->type, drone->weaponInfo->type); -} - -void findDroneKiller(const iwEnv *e, droneEntity *drone) { - b2Vec2 contrib[e->numDrones]; - memset(contrib, 0x0, e->numDrones * sizeof(b2Vec2)); - uint16_t step = 0; - bool braking = false; - float droneDamping = DRONE_LINEAR_DAMPING; - - // calculate the contribution of forces and impulses of each drone - for (size_t i = 0; i < cc_array_size(drone->physicsTracking); i++) { - physicsStepInfo *physicsStep = safe_array_get_at(drone->physicsTracking, i); - // if the step has changed, apply damping to contributions - if (physicsStep->step != step) { - const uint16_t stepDiff = physicsStep->step - step; - const float damp = 1.0f / (1.0f + (droneDamping * (e->deltaTime * stepDiff))); - for (uint8_t k = 0; k < e->numDrones; k++) { - contrib[k] = b2MulSV(damp, contrib[k]); - } - - step = physicsStep->step; - } - - if (physicsStep->brakeToggled) { - braking = !braking; - if (braking) { - droneDamping = DRONE_BRAKE_DAMPING_COEF; - } else { - droneDamping = DRONE_LINEAR_DAMPING; - } - } - - const b2Vec2 invForce = b2MulSV(DRONE_INV_MASS, physicsStep->force); - contrib[physicsStep->srcIdx] = b2MulAdd(contrib[physicsStep->srcIdx], e->deltaTime, invForce); - contrib[physicsStep->srcIdx] = b2MulAdd(contrib[physicsStep->srcIdx], DRONE_INV_MASS, physicsStep->impulse); - - // DEBUG_LOGF("# step %d drone %d contrib %f (%f, %f)", physicsStep->step, physicsStep->srcIdx, b2Length(contrib[physicsStep->srcIdx]), contrib[physicsStep->srcIdx].x, contrib[physicsStep->srcIdx].y); - } - - // determine the killer by finding the drone that pushed the dead - // drone towards the wall that killed it the most - const b2Vec2 deathNormal = b2Normalize(drone->lastVelocity); - DEBUG_LOGF("> death normal (%f, %f) velocity (%f, %f)", deathNormal.x, deathNormal.y, drone->lastVelocity.x, drone->lastVelocity.y); - float maxContrib = -FLT_MAX; - int8_t killer = -1; - for (uint8_t i = 0; i < e->numDrones; i++) { - DEBUG_LOGF("---\n> src drone contrib %d %f (%f, %f)", i, b2Length(contrib[i]), contrib[i].x, contrib[i].y); - if (b2VecEqual(contrib[i], b2Vec2_zero)) { - continue; - } - - const b2Vec2 normContrib = b2Normalize(contrib[i]); - const float dot = b2Dot(normContrib, deathNormal); - DEBUG_LOGF("> > src drone %d norm contrib (%f, %f)", i, normContrib.x, normContrib.y); - DEBUG_LOGF("> src drone %d death normal dot %f", i, dot); - const float totalContrib = b2Length(contrib[i]) * dot; - DEBUG_LOGF("> src drone %d total contrib %f", i, totalContrib); - if (totalContrib > maxContrib) { - maxContrib = totalContrib; - killer = i; - } - } - if (killer == -1) { - return; - } - - DEBUG_LOGF(">>> drone %d killed by drone %d", drone->idx, killer); - - drone->killedBy = killer; - droneEntity *killerDrone = safe_array_get_at(e->drones, killer); - killerDrone->killed[drone->idx] = true; -} - -void killDrone(iwEnv *e, droneEntity *drone) { - if (drone->dead || drone->livesLeft == 0) { - return; - } - DEBUG_LOGF("drone %d died", drone->idx); - - findDroneKiller(e, drone); - - drone->livesLeft--; - drone->dead = true; - drone->diedThisStep = true; - drone->respawnWait = DRONE_RESPAWN_WAIT; - - for (uint8_t i = 0; i < DRONE_PIECE_COUNT; i++) { - createDronePiece(e, drone, false); - } - - b2Body_Disable(drone->bodyID); - droneChangeWeapon(e, drone, e->defaultWeapon->type); - drone->braking = false; - drone->chargingBurst = false; - drone->energyFullyDepleted = false; - drone->shotThisStep = false; - drone->velocity = b2Vec2_zero; - drone->lastVelocity = b2Vec2_zero; -} - -bool respawnDrone(iwEnv *e, droneEntity *drone) { - b2Vec2 pos; - if (!findOpenPos(e, DRONE_SHAPE, &pos, -1)) { - return false; - } - b2Body_SetTransform(drone->bodyID, pos, b2Rot_identity); - b2Body_Enable(drone->bodyID); - b2Body_SetLinearDamping(drone->bodyID, DRONE_LINEAR_DAMPING); - - drone->dead = false; - drone->pos = pos; - drone->respawnGuideLifetime = UINT16_MAX; - drone->killedBy = -1; - - droneAddEnergy(drone, DRONE_ENERGY_RESPAWN_REFILL); - - createDroneShield(e, drone, -(drone->idx + 1)); - - if (e->client != NULL) { - drone->trailPoints.length = 0; - } - - return true; -} - -void createProjectile(iwEnv *e, droneEntity *drone, const b2Vec2 normAim) { - ASSERT_VEC_NORMALIZED(normAim); - - const float radius = drone->weaponInfo->radius; - float droneRadius = DRONE_RADIUS; - if (drone->shield != NULL) { - droneRadius = DRONE_SHIELD_RADIUS; - } - // spawn the projectile just outside the drone so they don't - // immediately collide - b2Vec2 pos = b2MulAdd(drone->pos, droneRadius + (radius * 1.5f), normAim); - // if the projectile is inside a wall or out of the map, move the - // projectile to be just outside the wall - bool projectileInWall = false; - int16_t cellIdx = entityPosToCellIdx(e, pos); - if (cellIdx == -1) { - projectileInWall = true; - } else { - const mapCell *cell = safe_array_get_at(e->cells, cellIdx); - if (cell->ent != NULL && entityTypeIsWall(cell->ent->type)) { - projectileInWall = true; - } - } - if (projectileInWall) { - const b2Vec2 rayEnd = b2MulAdd(drone->pos, droneRadius + (radius * 2.5f), normAim); - const b2Vec2 translation = b2Sub(rayEnd, drone->pos); - const b2QueryFilter filter = {.categoryBits = PROJECTILE_SHAPE, .maskBits = WALL_SHAPE}; - const b2RayResult rayRes = b2World_CastRayClosest(e->worldID, drone->pos, translation, filter); - if (rayRes.hit) { - const b2Vec2 invNormAim = b2MulSV(-1.0f, normAim); - pos = b2MulAdd(rayRes.point, radius * 1.5f, invNormAim); - } - } - - b2BodyDef projectileBodyDef = b2DefaultBodyDef(); - projectileBodyDef.type = b2_dynamicBody; - projectileBodyDef.isBullet = drone->weaponInfo->isPhysicsBullet; - projectileBodyDef.linearDamping = drone->weaponInfo->damping; - projectileBodyDef.enableSleep = drone->weaponInfo->canSleep; - projectileBodyDef.position = pos; - b2BodyId projectileBodyID = b2CreateBody(e->worldID, &projectileBodyDef); - b2ShapeDef projectileShapeDef = b2DefaultShapeDef(); - projectileShapeDef.enableContactEvents = true; - projectileShapeDef.density = drone->weaponInfo->density; - projectileShapeDef.material.restitution = 1.0f; - projectileShapeDef.material.friction = 0.0f; - projectileShapeDef.filter.categoryBits = PROJECTILE_SHAPE; - projectileShapeDef.filter.maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | PROJECTILE_SHAPE | DRONE_SHAPE | SHIELD_SHAPE; - const b2Circle projectileCircle = {.center = b2Vec2_zero, .radius = radius}; - - b2ShapeId projectileShapeID = b2CreateCircleShape(projectileBodyID, &projectileShapeDef, &projectileCircle); - - // add a bit of lateral drone velocity to projectile - b2Vec2 forwardVel = b2MulSV(b2Dot(drone->velocity, normAim), normAim); - b2Vec2 lateralVel = b2Sub(drone->velocity, forwardVel); - lateralVel = b2MulSV(projectileShapeDef.density * DRONE_MOVE_AIM_COEF, lateralVel); - b2Vec2 aim = weaponAdjustAim(&e->randState, drone->weaponInfo->type, drone->heat, normAim); - b2Vec2 fire = b2MulAdd(lateralVel, weaponFire(&e->randState, drone->weaponInfo->type), aim); - b2Body_ApplyLinearImpulseToCenter(projectileBodyID, fire, true); - - projectileEntity *projectile = fastCalloc(1, sizeof(projectileEntity)); - projectile->droneIdx = drone->idx; - projectile->bodyID = projectileBodyID; - projectile->shapeID = projectileShapeID; - projectile->weaponInfo = drone->weaponInfo; - projectile->pos = projectileBodyDef.position; - projectile->lastPos = projectileBodyDef.position; - projectile->velocity = b2Body_GetLinearVelocity(projectileBodyID); - projectile->lastVelocity = projectile->velocity; - projectile->speed = b2Length(projectile->velocity); - projectile->lastSpeed = projectile->speed; - if (projectile->weaponInfo->type == BLACK_HOLE_WEAPON) { - create_array(&projectile->entsInBlackHole, 4); - } - cc_array_add(e->projectiles, projectile); - - entity *ent = createEntity(e, PROJECTILE_ENTITY, projectile); - projectile->ent = ent; - b2Body_SetUserData(projectile->bodyID, ent); - b2Shape_SetUserData(projectile->shapeID, ent); - - // create a sensor shape if needed - if (projectile->weaponInfo->hasSensor) { - projectile->sensorID = weaponSensor(projectile->bodyID, projectile->weaponInfo->type); - b2Shape_SetUserData(projectile->sensorID, ent); - } -} - -// compute value generally from 0-1 based off of how much a projectile(s) -// or explosion(s) caused the hit drone to change velocity -float computeHitStrength(const droneEntity *hitDrone) { - const float prevSpeed = b2Length(hitDrone->lastVelocity); - const float curSpeed = b2Length(hitDrone->velocity); - return fabsf(curSpeed - prevSpeed) / MAX_SPEED; -} - -// simplified and copied from box2d/src/shape.c -float getShapeProjectedPerimeter(const b2ShapeId shapeID, const b2Vec2 line) { - if (b2Shape_GetType(shapeID) == b2_circleShape) { - const b2Circle circle = b2Shape_GetCircle(shapeID); - return circle.radius * 2.0f; - } - - const b2Polygon polygon = b2Shape_GetPolygon(shapeID); - const b2Vec2 *points = polygon.vertices; - int count = polygon.count; - B2_ASSERT(count > 0); - float value = b2Dot(points[0], line); - float lower = value; - float upper = value; - for (int i = 1; i < count; ++i) { - value = b2Dot(points[i], line); - lower = b2MinFloat(lower, value); - upper = b2MaxFloat(upper, value); - } - - return upper - lower; -} - -// explodes projectile and ensures any other projectiles that are caught -// in the explosion are also destroyed if necessary -void createProjectileExplosion(iwEnv *e, projectileEntity *projectile, const bool initalProjectile) { - if (projectile->needsToBeDestroyed) { - return; - } - projectile->needsToBeDestroyed = true; - cc_array_add(e->explodingProjectiles, projectile); - - b2ExplosionDef explosion; - weaponExplosion(projectile->weaponInfo->type, &explosion); - explosion.position = projectile->pos; - explosion.maskBits = FLOATING_WALL_SHAPE | PROJECTILE_SHAPE | DRONE_SHAPE; - droneEntity *parentDrone = safe_array_get_at(e->drones, projectile->droneIdx); - createExplosion(e, parentDrone, projectile, &explosion); - - if (e->client != NULL) { - explosionInfo *explInfo = fastCalloc(1, sizeof(explosionInfo)); - explInfo->def = explosion; - explInfo->renderSteps = UINT16_MAX; - cc_array_add(e->explosions, explInfo); - } - if (!initalProjectile) { - return; - } - - // if we're not destroying the projectiles now, we need to remove the initial projectile - // from the list of exploding projectiles so it's not destroyed twice - const enum cc_stat res = cc_array_remove_fast(e->explodingProjectiles, projectile, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); -} - -// ensures projectiles don't slow down when an impluse is applied to them -void fixProjectileSpeed(projectileEntity *projectile) { - b2Vec2 newVel = b2Body_GetLinearVelocity(projectile->bodyID); - float newSpeed = b2Length(newVel); - if (newSpeed < projectile->lastSpeed) { - newSpeed = projectile->lastSpeed; - newVel = b2MulSV(newSpeed, b2Normalize(newVel)); - b2Body_SetLinearVelocity(projectile->bodyID, newVel); - } - - projectile->velocity = newVel; - projectile->lastSpeed = newSpeed; - projectile->speed = newSpeed; -} - -#define MAX_WALL_HITS 8 - -typedef struct wallBurstImpulse { - float distance; - b2Vec2 direction; - float magnitude; - uint16_t wallCellIdx; -} wallBurstImpulse; - -typedef struct explosionCtx { - iwEnv *e; - const bool isBurst; - droneEntity *parentDrone; - const projectileEntity *projectile; - const b2ExplosionDef *def; - - wallBurstImpulse wallImpulses[MAX_WALL_HITS]; - int8_t closestWallIdx; - uint8_t wallsHit; -} explosionCtx; - -const float COS_85_DEGREES = 0.087155743f; - -// b2World_Explode doesn't support filtering on shapes of the same category, -// so we have to do it manually -// mostly copied from box2d/src/world.c -bool explodeCallback(b2ShapeId shapeID, void *context) { - if (!b2Shape_IsValid(shapeID)) { - return true; - } - - explosionCtx *ctx = context; - const entity *entity = b2Shape_GetUserData(shapeID); - projectileEntity *projectile = NULL; - droneEntity *drone = NULL; - wallEntity *wall = NULL; - bool isStaticWall = false; - b2Transform transform; - - switch (entity->type) { - case PROJECTILE_ENTITY: - // don't explode the parent projectile - projectile = entity->entity; - if (ctx->projectile != NULL && (ctx->projectile == projectile || projectile->needsToBeDestroyed)) { - return true; - } - transform.p = projectile->pos; - transform.q = b2Rot_identity; - break; - case DRONE_ENTITY: - drone = entity->entity; - // the explosion shouldn't affect the parent drone if this is a burst - if (drone->idx == ctx->parentDrone->idx) { - if (ctx->isBurst) { - return true; - } - - drone->stepInfo.ownShotTaken = true; - ctx->e->stats[drone->idx].ownShotsTaken[ctx->projectile->weaponInfo->type]++; - ctx->e->stats[drone->idx].totalOwnShotsTaken++; - DEBUG_LOGF("drone %d hit itself with explosion from weapon %d", drone->idx, ctx->projectile->weaponInfo->type); - } - ctx->parentDrone->stepInfo.explosionHit[drone->idx] = true; - if (ctx->isBurst) { - DEBUG_LOGF("drone %d hit drone %d with burst", ctx->parentDrone->idx, drone->idx); - ctx->e->stats[ctx->parentDrone->idx].burstsHit++; - DEBUG_LOGF("drone %d hit by burst from drone %d", drone->idx, ctx->parentDrone->idx); - } else { - DEBUG_LOGF("drone %d hit drone %d with explosion from weapon %d", ctx->parentDrone->idx, drone->idx, ctx->projectile->weaponInfo->type); - ctx->e->stats[ctx->parentDrone->idx].shotsHit[ctx->projectile->weaponInfo->type]++; - ctx->e->stats[ctx->parentDrone->idx].totalShotsHit++; - DEBUG_LOGF("drone %d hit by explosion from weapon %d from drone %d", drone->idx, ctx->projectile->weaponInfo->type, ctx->parentDrone->idx); - } - drone->stepInfo.explosionTaken[ctx->parentDrone->idx] = true; - transform.p = drone->pos; - transform.q = b2Rot_identity; - break; - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - wall = entity->entity; - isStaticWall = !wall->isFloating; - // normal explosions don't affect static walls - if (!ctx->isBurst && isStaticWall) { - return true; - } - transform.p = wall->pos; - transform.q = wall->rot; - break; - default: - ERRORF("invalid entity type %d to explode", entity->type); - } - - // find the closest point from the entity to the explosion center - const b2BodyId bodyID = b2Shape_GetBody(shapeID); - ASSERT(b2Body_IsValid(bodyID)); - - bool isCircle = false; - b2DistanceInput input; - input.proxyA = makeDistanceProxy(entity, &isCircle); - input.proxyB = b2MakeProxy(&ctx->def->position, 1, 0.0f); - input.transformA = transform; - input.transformB = b2Transform_identity; - input.useRadii = isCircle; - - b2SimplexCache cache = {0}; - const b2DistanceOutput output = b2ShapeDistance(&input, &cache, NULL, 0); - if (output.distance > ctx->def->radius) { - return true; - } - - // don't explode the entity if it's behind a static or floating wall, - // but always consider floating walls for implosions - const bool isImplosion = ctx->def->impulsePerLength < 0.0f; - b2QueryFilter filter = {.categoryBits = PROJECTILE_SHAPE, .maskBits = WALL_SHAPE}; - if (!isImplosion) { - filter.maskBits |= FLOATING_WALL_SHAPE; - } - if (posBehindWall(ctx->e, ctx->def->position, output.pointA, entity, filter, NULL)) { - return true; - } - - const b2Vec2 closestPoint = output.pointA; - b2Vec2 direction; - if (isStaticWall) { - direction = b2Normalize(b2Sub(ctx->def->position, closestPoint)); - } else { - direction = b2Normalize(b2Sub(closestPoint, ctx->def->position)); - } - // if the direction is zero, the magnitude cannot be calculated - // correctly so set the direction randomly - if (b2VecEqual(direction, b2Vec2_zero)) { - direction.x = randFloat(&ctx->e->randState, -1.0f, 1.0f); - direction.y = randFloat(&ctx->e->randState, -1.0f, 1.0f); - direction = b2Normalize(direction); - } - - b2Vec2 localLine = b2Vec2_zero; - if (entityTypeIsWall(entity->type)) { - // the localLine isn't used in perimeter calculations for circles - localLine = b2InvRotateVector(transform.q, b2LeftPerp(direction)); - } - float perimeter = getShapeProjectedPerimeter(shapeID, localLine); - const float relDistance = output.distance / ctx->def->radius; - const float scale = 1.0f - (SQUARED(relDistance) * relDistance); - - // the parent drone or projecile's velocity affects the direction - // and magnitude of the explosion - b2Vec2 parentVelocity; - float parentSpeed; - if (ctx->projectile != NULL) { - // use the projectile's last velocity and speed if it is in - // contact with another body, as the current velocity will be - // the velocity after the projectile rebounds which is not - // what we want - if (ctx->projectile->contacts != 0) { - parentVelocity = ctx->projectile->lastVelocity; - parentSpeed = ctx->projectile->lastSpeed; - } else { - parentVelocity = ctx->projectile->velocity; - parentSpeed = ctx->projectile->speed; - } - if (isImplosion) { - parentSpeed *= -1.0f; - } - } else { - parentVelocity = ctx->parentDrone->lastVelocity; - parentSpeed = b2Length(parentVelocity); - } - const b2Vec2 parentDirection = b2Normalize(parentVelocity); - - // scale the parent speed by how close the movement direction of - // the parent is to where the entity is to the parent, except if - // we're bursting off of a wall to make it more predictable and - // to prevent taking a log of a negative number - if (!isStaticWall) { - parentSpeed *= b2Dot(direction, parentDirection); - } - // the parent entity's velocity affects the direction of the impulse - // depending on the speed - const b2Vec2 baseImpulse = b2MulSV(fabsf(ctx->def->impulsePerLength), direction); - direction = b2Normalize(b2Add(baseImpulse, parentVelocity)); - - float shieldReduction = 1.0f; - if (drone != NULL && drone->shield != NULL) { - shieldReduction = DRONE_SHIELD_EXPLOSION_REDUCTION; - } - - float magnitude = (ctx->def->impulsePerLength + parentSpeed) * perimeter * scale * shieldReduction; - if (isStaticWall) { - // ensure this wall faces at least 85 degrees away from the - // closest hit wall to prevent multiple walls facing roughly - // the same direction greatly increasing the impulse magnitude - if (ctx->closestWallIdx == -1) { - ctx->closestWallIdx = 0; - } else { - const float closestWallDistance = ctx->wallImpulses[ctx->closestWallIdx].distance; - if (output.distance < closestWallDistance) { - ctx->closestWallIdx = ctx->wallsHit; - - for (int8_t i = 0; i < ctx->wallsHit; i++) { - const wallBurstImpulse impulse = ctx->wallImpulses[i]; - if (impulse.distance != output.distance && b2Dot(impulse.direction, direction) > COS_85_DEGREES) { - ctx->wallImpulses[i] = ctx->wallImpulses[--ctx->wallsHit]; - i--; - } - } - } - } - - // reduce the magnitude when pushing a drone away from a wall - magnitude = log2f(magnitude) * (5.0f + (25.0f * ctx->parentDrone->burstCharge)); - - ctx->wallImpulses[ctx->wallsHit++] = (wallBurstImpulse){ - .distance = output.distance, - .direction = direction, - .magnitude = magnitude, - .wallCellIdx = wall->mapCellIdx, - }; - return true; - } - const b2Vec2 impulse = b2MulSV(magnitude, direction); - ASSERT(b2IsValidVec2(impulse)); - - switch (entity->type) { - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - b2Body_ApplyLinearImpulse(bodyID, impulse, output.pointA, true); - wall->velocity = b2Body_GetLinearVelocity(wall->bodyID); - break; - case PROJECTILE_ENTITY: - b2Body_ApplyLinearImpulseToCenter(bodyID, impulse, true); - // mine launcher projectiles explode when caught in another - // explosion, explode this mine only once - if (projectile->weaponInfo->type == MINE_LAUNCHER_WEAPON && ctx->def->impulsePerLength > 0.0f) { - createProjectileExplosion(ctx->e, projectile, false); - break; - } - - fixProjectileSpeed(projectile); - - break; - case DRONE_ENTITY: - droneApplyImpulse(ctx->e, drone, impulse, ctx->parentDrone->idx); - drone->lastVelocity = drone->velocity; - drone->velocity = b2Body_GetLinearVelocity(drone->bodyID); - - shieldEntity *shield = drone->shield; - - // add energy to the drone that fired the projectile that is - // currently exploding if it hit another drone - if (!ctx->isBurst && drone->team != ctx->parentDrone->team && shield == NULL) { - const float energyRefill = computeHitStrength(drone) * EXPLOSION_ENERGY_REFILL_COEF; - droneAddEnergy(ctx->parentDrone, energyRefill); - } else if (shield != NULL && shield->health > 0.0f) { - const float damage = fabsf(magnitude) * DRONE_SHIELD_HEALTH_EXPLOSION_COEF; - DEBUG_LOGF("shield explosion damage: %f", damage); - shield->health -= damage; - if (shield->health <= 0.0f) { - droneAddEnergy(ctx->parentDrone, DRONE_SHIELD_BREAK_ENERGY_REFILL); - } - } - - break; - default: - ERRORF("unknown entity type for burst impulse %d", entity->type); - } - - return true; -} - -void applyDroneBurstImpulse(iwEnv *e, explosionCtx *ctx, const droneEntity *drone) { - wallBurstImpulse *hitWalls = ctx->wallImpulses; - uint8_t wallsHit = ctx->wallsHit; - - // sort by distance - for (int8_t i = 1; i < wallsHit; i++) { - wallBurstImpulse key = hitWalls[i]; - int j = i - 1; - - while (j >= 0 && hitWalls[j].distance > key.distance) { - hitWalls[j + 1] = hitWalls[j]; - j = j - 1; - } - - hitWalls[j + 1] = key; - } - - for (uint8_t i = 0; i < wallsHit; i++) { - const wallBurstImpulse impulseA = hitWalls[i]; - if (impulseA.distance == FLT_MAX) { - continue; - } - for (uint8_t j = 0; j < wallsHit; j++) { - if (j == i) { - continue; - } - wallBurstImpulse impulseB = hitWalls[j]; - if (impulseB.distance == FLT_MAX) { - continue; - } - const uint8_t cellIdxDiff = abs(impulseB.wallCellIdx - impulseA.wallCellIdx); - if (cellIdxDiff == e->map->columns) { - hitWalls[j].distance = FLT_MAX; - continue; - } - if (cellIdxDiff == 1) { - int8_t colA = impulseA.wallCellIdx / e->map->columns; - int8_t colB = impulseB.wallCellIdx / e->map->columns; - if (colA == colB) { - hitWalls[j].distance = FLT_MAX; - } - } - } - } - - uint8_t wallsUsed = 0; - float magnitude = 0.0f; - b2Vec2 direction = b2Vec2_zero; - for (uint8_t i = 0; i < wallsHit; i++) { - const wallBurstImpulse impulse = hitWalls[i]; - if (impulse.distance == FLT_MAX) { - continue; - } - - wallsUsed++; - magnitude += impulse.magnitude; - direction = b2Add(direction, impulse.direction); - } - - if (wallsUsed > 2) { - DEBUG_LOG("more than 2 walls used"); - } - - DEBUG_LOGF("walls used: %d magnitude: %f final: %f", wallsUsed, magnitude, magnitude / (float)wallsUsed); - const b2Vec2 finalImpulse = b2MulSV(magnitude / (float)wallsUsed, b2Normalize(direction)); - ASSERT(b2IsValidVec2(finalImpulse)); - droneApplyImpulse(e, drone, finalImpulse, drone->idx); -} - -void createExplosion(iwEnv *e, droneEntity *drone, const projectileEntity *projectile, const b2ExplosionDef *def) { - b2AABB aabb = { - .lowerBound.x = def->position.x - def->radius, - .lowerBound.y = def->position.y - def->radius, - .upperBound.x = def->position.x + def->radius, - .upperBound.y = def->position.y + def->radius, - }; - - b2QueryFilter filter = b2DefaultQueryFilter(); - filter.categoryBits = PROJECTILE_SHAPE; - filter.maskBits = def->maskBits; - - const bool isBurst = projectile == NULL; - explosionCtx ctx = { - .e = e, - .isBurst = isBurst, - .parentDrone = drone, - .projectile = projectile, - .def = def, - .wallImpulses = {{0}}, - .closestWallIdx = -1, - .wallsHit = 0, - }; - b2World_OverlapAABB(e->worldID, aabb, filter, explodeCallback, &ctx); - - uint8_t wallsHit = ctx.wallsHit; - if (isBurst && wallsHit != 0) { - applyDroneBurstImpulse(e, &ctx, drone); - } -} - -void destroyProjectile(iwEnv *e, projectileEntity *projectile, const bool processExplosions, const bool full) { - // explode projectile if necessary - if (processExplosions && projectile->weaponInfo->explosive) { - createProjectileExplosion(e, projectile, true); - } - - destroyEntity(e, projectile->ent); - - b2DestroyBody(projectile->bodyID); - - if (full) { - enum cc_stat res = cc_array_remove_fast(e->projectiles, projectile, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); - } - - e->stats[projectile->droneIdx].shotDistances[projectile->weaponInfo->type] += projectile->distance; - e->stats[projectile->droneIdx].totalShotDistances += projectile->distance; - - if (projectile->entsInBlackHole != NULL) { - for (uint8_t i = 0; i < cc_array_size(projectile->entsInBlackHole); i++) { - entityID *id = safe_array_get_at(projectile->entsInBlackHole, i); - fastFree(id); - } - cc_array_destroy(projectile->entsInBlackHole); - } - - fastFree(projectile); -} - -// destroy projectiles that were caught in an explosion; projectiles -// can't be destroyed in explodeCallback because box2d assumes all shapes -// and bodies are valid for the lifetime of an AABB query -static inline void destroyExplodedProjectiles(iwEnv *e) { - if (cc_array_size(e->explodingProjectiles) == 0) { - return; - } - - CC_ArrayIter iter; - cc_array_iter_init(&iter, e->explodingProjectiles); - projectileEntity *projectile; - while (cc_array_iter_next(&iter, (void **)&projectile) != CC_ITER_END) { - destroyProjectile(e, projectile, false, false); - const enum cc_stat res = cc_array_remove_fast(e->projectiles, projectile, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); - } - cc_array_remove_all(e->explodingProjectiles); -} - -void createSuddenDeathWalls(iwEnv *e, const b2Vec2 startPos, const b2Vec2 size) { - int16_t endIdx; - uint8_t indexIncrement; - if (size.y == WALL_THICKNESS) { - // horizontal walls - const b2Vec2 endPos = (b2Vec2){.x = startPos.x + size.x, .y = startPos.y}; - endIdx = entityPosToCellIdx(e, endPos); - if (endIdx == -1) { - ERRORF("invalid position for sudden death wall: (%f, %f)", endPos.x, endPos.y); - } - indexIncrement = 1; - } else { - // vertical walls - const b2Vec2 endPos = (b2Vec2){.x = startPos.x, .y = startPos.y + size.y}; - endIdx = entityPosToCellIdx(e, endPos); - if (endIdx == -1) { - ERRORF("invalid position for sudden death wall: (%f, %f)", endPos.x, endPos.y); - } - indexIncrement = e->map->columns; - } - const int16_t startIdx = entityPosToCellIdx(e, startPos); - if (startIdx == -1) { - ERRORF("invalid position for sudden death wall: (%f, %f)", startPos.x, startPos.y); - } - for (uint16_t i = startIdx; i <= endIdx; i += indexIncrement) { - mapCell *cell = safe_array_get_at(e->cells, i); - if (cell->ent != NULL) { - if (cell->ent->type == WEAPON_PICKUP_ENTITY) { - weaponPickupEntity *pickup = cell->ent->entity; - disableWeaponPickup(e, pickup); - } else { - continue; - } - } - entity *ent = createWall(e, cell->pos, WALL_THICKNESS, WALL_THICKNESS, i, DEATH_WALL_ENTITY, false); - cell->ent = ent; - } -} - -void handleSuddenDeath(iwEnv *e) { - ASSERT(e->suddenDeathSteps == 0); - if (e->suddenDeathWallCounter >= e->map->maxSuddenDeathWalls) { - return; - } - - // create new walls that will close in on the arena - e->suddenDeathWallCounter++; - e->suddenDeathWallsPlaced = true; - - const float leftX = (e->suddenDeathWallCounter - 1) * WALL_THICKNESS; - const float yOffset = (WALL_THICKNESS * (e->suddenDeathWallCounter - 1)) + (WALL_THICKNESS / 2); - const float xWidth = WALL_THICKNESS * (e->map->columns - (e->suddenDeathWallCounter * 2) - 1); - - // top walls - createSuddenDeathWalls( - e, - (b2Vec2){ - .x = e->map->bounds.min.x + leftX, - .y = e->map->bounds.min.y + yOffset, - }, - (b2Vec2){ - .x = xWidth, - .y = WALL_THICKNESS, - } - ); - // bottom walls - createSuddenDeathWalls( - e, - (b2Vec2){ - .x = e->map->bounds.min.x + leftX, - .y = e->map->bounds.max.y - yOffset, - }, - (b2Vec2){ - .x = xWidth, - .y = WALL_THICKNESS, - } - ); - // left walls - createSuddenDeathWalls( - e, - (b2Vec2){ - .x = e->map->bounds.min.x + leftX, - .y = e->map->bounds.min.y + (e->suddenDeathWallCounter * WALL_THICKNESS), - }, - (b2Vec2){ - .x = WALL_THICKNESS, - .y = WALL_THICKNESS * (e->map->rows - (e->suddenDeathWallCounter * 2) - 2), - } - ); - // right walls - createSuddenDeathWalls( - e, - (b2Vec2){ - .x = e->map->bounds.min.x + ((e->map->columns - e->suddenDeathWallCounter - 2) * WALL_THICKNESS), - .y = e->map->bounds.min.y + (e->suddenDeathWallCounter * WALL_THICKNESS), - }, - (b2Vec2){ - .x = WALL_THICKNESS, - .y = WALL_THICKNESS * (e->map->rows - (e->suddenDeathWallCounter * 2) - 2), - } - ); - - // mark drones as dead if they touch a newly placed wall - for (uint8_t i = 0; i < e->numDrones; i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - const b2QueryFilter filter = { - .categoryBits = DRONE_SHAPE, - .maskBits = WALL_SHAPE, - }; - if (isOverlappingAABB(e, drone->pos, DRONE_RADIUS, filter)) { - killDrone(e, drone); - } - } - - // make floating walls static bodies if they are now overlapping with - // a newly placed wall, but destroy them if they are fully inside a wall - CC_ArrayIter floatingWallIter; - cc_array_iter_init(&floatingWallIter, e->floatingWalls); - wallEntity *wall; - while (cc_array_iter_next(&floatingWallIter, (void **)&wall) != CC_ITER_END) { - const mapCell *cell = safe_array_get_at(e->cells, wall->mapCellIdx); - if (cell->ent != NULL && entityTypeIsWall(cell->ent->type)) { - // floating wall is overlapping with a wall, destroy it - const enum cc_stat res = cc_array_iter_remove_fast(&floatingWallIter, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); - - const b2Vec2 wallPos = wall->pos; - MAYBE_UNUSED(wallPos); - destroyWall(e, wall, false); - DEBUG_LOGF("destroyed floating wall at %f, %f", wallPos.x, wallPos.y); - continue; - } - } - - // detroy all projectiles that are now overlapping with a newly placed wall - CC_ArrayIter projectileIter; - cc_array_iter_init(&projectileIter, e->projectiles); - projectileEntity *projectile; - while (cc_array_iter_next(&projectileIter, (void **)&projectile) != CC_ITER_END) { - const mapCell *cell = safe_array_get_at(e->cells, projectile->mapCellIdx); - if (cell->ent != NULL && entityTypeIsWall(cell->ent->type)) { - cc_array_iter_remove_fast(&projectileIter, NULL); - destroyProjectile(e, projectile, false, false); - } - } -} - -void droneMove(const iwEnv *e, droneEntity *drone, b2Vec2 direction) { - ASSERT_VEC_BOUNDED(direction); - - // if energy is fully depleted halve movement until energy starts - // to refill again - if (drone->energyFullyDepleted && drone->energyRefillWait != 0.0f) { - direction = b2MulSV(0.5f, direction); - drone->lastMove = direction; - } - const b2Vec2 force = b2MulSV(DRONE_MOVE_MAGNITUDE, direction); - droneApplyForce(e, drone, force, drone->idx); -} - -void droneShoot(iwEnv *e, droneEntity *drone, const b2Vec2 aim, const bool chargingWeapon) { - ASSERT(drone->ammo != 0); - - drone->shotThisStep = true; - // TODO: rework heat to only increase when projectiles are fired, - // and only cool down after the next shot was skipped - drone->heat++; - if (drone->weaponCooldown != 0.0f) { - return; - } - const bool weaponNeedsCharge = drone->weaponInfo->charge != 0.0f; - if (weaponNeedsCharge) { - if (chargingWeapon) { - drone->chargingWeapon = true; - drone->weaponCharge = min(drone->weaponCharge + e->deltaTime, drone->weaponInfo->charge); - } else if (drone->weaponCharge < drone->weaponInfo->charge) { - drone->chargingWeapon = false; - drone->weaponCharge = max(drone->weaponCharge - e->deltaTime, 0.0f); - } - } - // if the weapon needs to be charged, only fire the weapon if it's - // fully charged and the agent released the trigger - if (weaponNeedsCharge && (chargingWeapon || drone->weaponCharge < drone->weaponInfo->charge)) { - return; - } - - if (drone->ammo != INFINITE) { - drone->ammo--; - } - drone->weaponCooldown = drone->weaponInfo->coolDown; - drone->chargingWeapon = false; - drone->weaponCharge = 0.0f; - - b2Vec2 normAim = drone->lastAim; - if (!b2VecEqual(aim, b2Vec2_zero)) { - normAim = b2Normalize(aim); - } - ASSERT_VEC_NORMALIZED(normAim); - b2Vec2 recoil = b2MulSV(-drone->weaponInfo->recoilMagnitude, normAim); - droneApplyImpulse(e, drone, recoil, drone->idx); - - for (int i = 0; i < drone->weaponInfo->numProjectiles; i++) { - createProjectile(e, drone, normAim); - - e->stats[drone->idx].shotsFired[drone->weaponInfo->type]++; - e->stats[drone->idx].totalShotsFired++; - } - drone->stepInfo.firedShot = true; - - if (drone->ammo == 0) { - droneChangeWeapon(e, drone, e->defaultWeapon->type); - drone->weaponCooldown = drone->weaponInfo->coolDown; - } -} - -void droneBrake(iwEnv *e, droneEntity *drone, const bool brake) { - if (drone->shield != NULL) { - return; - } - // if the drone isn't braking or energy is fully depleted, return - // unless the drone was braking during the last step - if (!brake || drone->energyFullyDepleted) { - if (drone->braking) { - drone->braking = false; - b2Body_SetLinearDamping(drone->bodyID, DRONE_LINEAR_DAMPING); - if (drone->energyRefillWait == 0.0f && !drone->chargingBurst) { - drone->energyRefillWait = DRONE_ENERGY_REFILL_WAIT; - } - droneTrackBrake(e, drone); - - if (e->client != NULL) { - brakeTrailPoint *trailPoint = fastCalloc(1, sizeof(brakeTrailPoint)); - trailPoint->pos = drone->pos; - trailPoint->lifetime = UINT16_MAX; - trailPoint->isEnd = true; - cc_array_add(drone->brakeTrailPoints, trailPoint); - } - } - return; - } - ASSERT(!drone->energyFullyDepleted); - - // apply additional brake damping and decrease energy - if (brake) { - if (!drone->braking) { - drone->braking = true; - b2Body_SetLinearDamping(drone->bodyID, DRONE_LINEAR_DAMPING * DRONE_BRAKE_DAMPING_COEF); - droneTrackBrake(e, drone); - } - drone->energyLeft = max(drone->energyLeft - (DRONE_BRAKE_DRAIN_RATE * e->deltaTime), 0.0f); - e->stats[drone->idx].brakeTime += e->deltaTime; - } - - // if energy is empty but burst is being charged, let burst functions - // handle energy refill - if (drone->energyLeft == 0.0f && !drone->chargingBurst) { - drone->energyFullyDepleted = true; - drone->energyFullyDepletedThisStep = true; - drone->energyRefillWait = DRONE_ENERGY_REFILL_EMPTY_WAIT; - e->stats[drone->idx].energyEmptied++; - } - - if (e->client != NULL) { - brakeTrailPoint *trailPoint = fastCalloc(1, sizeof(brakeTrailPoint)); - trailPoint->pos = drone->pos; - trailPoint->lifetime = UINT16_MAX; - cc_array_add(drone->brakeTrailPoints, trailPoint); - } -} - -void droneChargeBurst(iwEnv *e, droneEntity *drone) { - if (drone->energyFullyDepleted || drone->burstCooldown != 0.0f || drone->shield != NULL || (!drone->chargingBurst && drone->energyLeft < DRONE_BURST_BASE_COST)) { - return; - } - - // take energy and put it into burst charge - if (drone->chargingBurst) { - drone->burstCharge = min(drone->burstCharge + (DRONE_BURST_CHARGE_RATE * e->deltaTime), DRONE_ENERGY_MAX); - drone->energyLeft = max(drone->energyLeft - (DRONE_BURST_CHARGE_RATE * e->deltaTime), 0.0f); - } else { - drone->burstCharge = min(drone->burstCharge + DRONE_BURST_BASE_COST, DRONE_ENERGY_MAX); - drone->energyLeft = max(drone->energyLeft - DRONE_BURST_BASE_COST, 0.0f); - drone->chargingBurst = true; - } - - if (drone->energyLeft == 0.0f) { - drone->energyFullyDepleted = true; - e->stats[drone->idx].energyEmptied++; - } -} - -void droneBurst(iwEnv *e, droneEntity *drone) { - if (!drone->chargingBurst) { - return; - } - - const float radius = (DRONE_BURST_RADIUS_BASE * drone->burstCharge) + DRONE_BURST_RADIUS_MIN; - b2ExplosionDef explosion = { - .position = drone->pos, - .radius = radius, - .impulsePerLength = (DRONE_BURST_IMPACT_BASE * drone->burstCharge) + DRONE_BURST_IMPACT_MIN, - .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | PROJECTILE_SHAPE | DRONE_SHAPE, - }; - createExplosion(e, drone, NULL, &explosion); - destroyExplodedProjectiles(e); - - drone->chargingBurst = false; - drone->burstCharge = 0.0f; - drone->burstCooldown = DRONE_BURST_COOLDOWN; - if (drone->energyLeft == 0.0f) { - drone->energyFullyDepletedThisStep = true; - drone->energyRefillWait = DRONE_ENERGY_REFILL_EMPTY_WAIT; - } else { - drone->energyRefillWait = DRONE_ENERGY_REFILL_WAIT; - } - e->stats[drone->idx].totalBursts++; - - if (e->client != NULL) { - explosionInfo *explInfo = fastCalloc(1, sizeof(explosionInfo)); - explInfo->def = explosion; - explInfo->isBurst = true; - explInfo->droneIdx = drone->idx; - explInfo->renderSteps = UINT16_MAX; - cc_array_add(e->explosions, explInfo); - } -} - -void droneDiscardWeapon(iwEnv *e, droneEntity *drone) { - if (drone->weaponInfo->type == e->defaultWeapon->type || (drone->energyFullyDepleted && !drone->chargingBurst)) { - return; - } - - droneChangeWeapon(e, drone, e->defaultWeapon->type); - droneAddEnergy(drone, -WEAPON_DISCARD_COST); - if (drone->chargingBurst) { - return; - } - - if (drone->energyLeft == 0.0f) { - drone->energyFullyDepleted = true; - drone->energyFullyDepletedThisStep = true; - drone->energyRefillWait = DRONE_ENERGY_REFILL_EMPTY_WAIT; - e->stats[drone->idx].energyEmptied++; - } else { - drone->energyRefillWait = DRONE_ENERGY_REFILL_WAIT; - } -} - -// update drone state, respawn the drone if necessary; false is returned -// if no position could be found to respawn the drone at, and true otherwise -bool droneStep(iwEnv *e, droneEntity *drone) { - if (drone->dead) { - drone->respawnWait -= e->deltaTime; - if (drone->respawnWait <= 0.0f) { - const bool foundPos = respawnDrone(e, drone); - if (!foundPos) { - return false; - } - } - return true; - } - - // manage weapon charge and heat - if (drone->weaponCooldown != 0.0f) { - drone->weaponCooldown = max(drone->weaponCooldown - e->deltaTime, 0.0f); - } - if (!drone->shotThisStep) { - drone->weaponCharge = max(drone->weaponCharge - e->deltaTime, 0); - drone->heat = max(drone->heat - 1, 0); - } else { - drone->shotThisStep = false; - } - ASSERT(!drone->shotThisStep); - - // manage drone energy - if (drone->burstCooldown != 0.0f) { - drone->burstCooldown = max(drone->burstCooldown - e->deltaTime, 0.0f); - } - if (drone->energyFullyDepletedThisStep) { - drone->energyFullyDepletedThisStep = false; - } else if (drone->energyRefillWait != 0.0f) { - drone->energyRefillWait = max(drone->energyRefillWait - e->deltaTime, 0.0f); - } else if (drone->energyLeft != DRONE_ENERGY_MAX && !drone->chargingBurst) { - // don't start recharging energy until the burst charge is used - drone->energyLeft = min(drone->energyLeft + (DRONE_ENERGY_REFILL_RATE * e->deltaTime), DRONE_ENERGY_MAX); - } - if (drone->energyLeft == DRONE_ENERGY_MAX) { - drone->energyFullyDepleted = false; - } - - const float distance = b2Distance(drone->lastPos, drone->pos); - e->stats[drone->idx].distanceTraveled += distance; - - shieldEntity *shield = drone->shield; - if (shield != NULL) { - shield->duration -= e->deltaTime; - if (shield->duration <= 0.0f || shield->health <= 0.0f) { - destroyDroneShield(e, shield, true); - } - } - - return true; -} - -void handleBlackHolePull(iwEnv *e, projectileEntity *projectile) { - ASSERT(projectile->weaponInfo->type == BLACK_HOLE_WEAPON); - - CC_ArrayIter entIter; - cc_array_iter_init(&entIter, projectile->entsInBlackHole); - entityID *id; - while (cc_array_iter_next(&entIter, (void **)&id) != CC_ITER_END) { - // check if the entity is still valid - const entity *ent = getEntityByID(e, id); - if (ent == NULL) { - fastFree(id); - enum cc_stat res = cc_array_iter_remove_fast(&entIter, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); - continue; - } - const b2DistanceOutput output = closestPoint(projectile->ent, ent); - const b2QueryFilter filter = {.categoryBits = PROJECTILE_SHAPE, .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE}; - if (posBehindWall(e, projectile->pos, output.pointB, ent, filter, NULL)) { - continue; - } - - b2BodyId bodyID; - b2ShapeId shapeID; - b2Rot rot; - - wallEntity *wall; - droneEntity *drone; - projectileEntity *proj; - bool hasShield = false; - - switch (ent->type) { - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - wall = ent->entity; - bodyID = wall->bodyID; - shapeID = wall->shapeID; - rot = wall->rot; - break; - case DRONE_ENTITY: - drone = ent->entity; - bodyID = drone->bodyID; - shapeID = drone->shapeID; - hasShield = drone->shield != NULL; - break; - case PROJECTILE_ENTITY: - proj = ent->entity; - bodyID = proj->bodyID; - shapeID = proj->shapeID; - break; - default: - ERRORF("unknown entity type %d for black hole suck", ent->type); - } - - b2Vec2 direction = b2Normalize(b2Sub(output.pointB, projectile->pos)); - - b2Vec2 localLine = b2Vec2_zero; - if (entityTypeIsWall(ent->type)) { - // the localLine isn't used in perimeter calculations for circles - localLine = b2InvRotateVector(rot, b2LeftPerp(direction)); - } - const float perimeter = getShapeProjectedPerimeter(shapeID, localLine); - - const float scale = 1.0f - (output.distance / BLACK_HOLE_PROXIMITY_RADIUS); - - // the projecile's velocity affects the direction - // and magnitude of the force - b2Vec2 parentVelocity = projectile->velocity; - float parentSpeed = projectile->speed; - if (projectile->contacts != 0) { - parentVelocity = projectile->lastVelocity; - parentSpeed = projectile->lastSpeed; - } - const b2Vec2 parentDirection = b2Normalize(parentVelocity); - parentSpeed *= b2Dot(direction, parentDirection); - // the parent entity's velocity affects the direction of the force - // depending on the speed - const b2Vec2 baseForce = b2MulSV(-1.0f * BLACK_HOLE_PULL_MAGNITUDE, direction); - direction = b2Normalize(b2Add(baseForce, parentVelocity)); - - float magnitude = (BLACK_HOLE_PULL_MAGNITUDE + (-1.0f * parentSpeed)) * perimeter * scale; - if (hasShield) { - magnitude *= DRONE_SHIELD_EXPLOSION_REDUCTION; - } - const b2Vec2 force = b2MulSV(magnitude, direction); - - if (entityTypeIsWall(ent->type)) { - b2Body_ApplyForce(bodyID, force, output.pointB, true); - } else if (ent->type == DRONE_ENTITY) { - droneEntity *drone = ent->entity; - droneApplyForce(e, drone, force, projectile->droneIdx); - } else { - b2Body_ApplyForceToCenter(bodyID, force, true); - } - } -} - -void projectilesStep(iwEnv *e) { - CC_ArrayIter projIter; - cc_array_iter_init(&projIter, e->projectiles); - projectileEntity *projectile; - while (cc_array_iter_next(&projIter, (void **)&projectile) != CC_ITER_END) { - if (projectile->needsToBeDestroyed) { - continue; - } - const float maxDistance = projectile->weaponInfo->maxDistance; - const float distance = b2Distance(projectile->pos, projectile->lastPos); - projectile->distance += distance; - - // if a drone is in a set mine's sensor range but behind a wall, - // we need to check until the drone leaves the sensor range if - // it's not behind the wall anymore as we normally only check if - // we need to explode the mine when a drone touches the sensor - if (projectile->numDronesBehindWalls != 0) { - bool destroyed = false; - for (uint8_t i = 0; i < projectile->numDronesBehindWalls; i++) { - const uint8_t droneIdx = projectile->dronesBehindWalls[i]; - const droneEntity *drone = safe_array_get_at(e->drones, droneIdx); - // TODO: do we need the closest point here? - const b2DistanceOutput output = closestPoint(projectile->ent, drone->ent); - const b2QueryFilter filter = {.categoryBits = PROJECTILE_SHAPE, .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE}; - if (posBehindWall(e, projectile->pos, output.pointB, NULL, filter, NULL)) { - continue; - } - - // we have to destroy the projectile using the iterator so - // we can continue to iterate correctly - destroyProjectile(e, projectile, true, false); - enum cc_stat res = cc_array_iter_remove_fast(&projIter, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); - destroyed = true; - break; - } - if (destroyed) { - continue; - } - } - - if (projectile->entsInBlackHole != NULL) { - handleBlackHolePull(e, projectile); - } - - if (maxDistance == INFINITE) { - continue; - } - if (projectile->distance >= maxDistance) { - // we have to destroy the projectile using the iterator so - // we can continue to iterate correctly - destroyProjectile(e, projectile, true, false); - enum cc_stat res = cc_array_iter_remove_fast(&projIter, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); - continue; - } - } - - destroyExplodedProjectiles(e); -} - -void weaponPickupsStep(iwEnv *e) { - CC_ArrayIter iter; - cc_array_iter_init(&iter, e->pickups); - weaponPickupEntity *pickup; - - // respawn weapon pickups at a random location as a random weapon type - // once the respawn wait has elapsed - while (cc_array_iter_next(&iter, (void **)&pickup) != CC_ITER_END) { - if (pickup->respawnWait == 0.0f) { - continue; - } - pickup->respawnWait = max(pickup->respawnWait - e->deltaTime, 0.0f); - if (pickup->respawnWait != 0.0f) { - continue; - } - - b2Vec2 pos; - if (!findOpenPos(e, WEAPON_PICKUP_SHAPE, &pos, -1)) { - const enum cc_stat res = cc_array_iter_remove_fast(&iter, NULL); - MAYBE_UNUSED(res); - ASSERT(res == CC_OK); - DEBUG_LOG("destroying weapon pickup"); - destroyWeaponPickup(e, pickup); - continue; - } - pickup->pos = pos; - pickup->weapon = randWeaponPickupType(e); - - const int16_t cellIdx = entityPosToCellIdx(e, pos); - if (cellIdx == -1) { - ERRORF("invalid position for weapon pickup spawn: (%f, %f)", pos.x, pos.y); - } - DEBUG_LOGF("respawned weapon pickup at cell %d (%f, %f)", cellIdx, pos.x, pos.y); - pickup->mapCellIdx = cellIdx; - createWeaponPickupBodyShape(e, pickup); - - mapCell *cell = safe_array_get_at(e->cells, cellIdx); - cell->ent = pickup->ent; - } -} - -// only update positions and velocities of dynamic bodies if they moved -// this step -void handleBodyMoveEvents(iwEnv *e) { - b2BodyEvents events = b2World_GetBodyEvents(e->worldID); - for (int i = 0; i < events.moveCount; i++) { - const b2BodyMoveEvent *event = events.moveEvents + i; - if (!b2Body_IsValid(event->bodyId)) { - continue; - } - ASSERT(b2IsValidVec2(event->transform.p)); - const b2Vec2 newPos = event->transform.p; - entity *ent = event->userData; - if (ent == NULL) { - continue; - } - - wallEntity *wall; - projectileEntity *proj; - droneEntity *drone; - shieldEntity *shield; - dronePieceEntity *piece; - int16_t mapIdx; - - // if the new position is out of bounds, destroy the entity unless - // a drone is out of bounds, then just kill it - switch (ent->type) { - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - wall = ent->entity; - mapIdx = entityPosToCellIdx(e, newPos); - if (mapIdx == -1) { - DEBUG_LOGF("invalid position for floating wall: (%f, %f) destroying", newPos.x, newPos.y); - cc_array_remove_fast(e->floatingWalls, wall, NULL); - destroyWall(e, wall, false); - continue; - } - wall->mapCellIdx = mapIdx; - wall->pos = newPos; - wall->rot = event->transform.q; - wall->velocity = b2Body_GetLinearVelocity(wall->bodyID); - break; - case PROJECTILE_ENTITY: - proj = ent->entity; - mapIdx = entityPosToCellIdx(e, newPos); - if (mapIdx == -1) { - DEBUG_LOGF("invalid position for projectile: (%f, %f) destroying", newPos.x, newPos.y); - destroyProjectile(e, proj, false, true); - continue; - } - proj->mapCellIdx = mapIdx; - proj->lastPos = proj->pos; - proj->pos = newPos; - proj->lastVelocity = proj->velocity; - proj->velocity = b2Body_GetLinearVelocity(proj->bodyID); - // if the projectile doesn't have damping its speed will - // only change when colliding with a dynamic body or getting - // hit by an explosion, and if it's currently colliding with - // something we don't care about the current speed - if (proj->weaponInfo->damping != 0.0f && proj->contacts == 0) { - proj->lastSpeed = proj->speed; - proj->speed = b2Length(proj->velocity); - } - - if (e->client != NULL) { - updateTrailPoints(&proj->trailPoints, MAX_PROJECTLE_TRAIL_POINTS, newPos); - } - break; - case DRONE_ENTITY: - drone = ent->entity; - mapIdx = entityPosToCellIdx(e, newPos); - if (mapIdx == -1) { - DEBUG_LOGF("invalid position for drone: (%f, %f) killing it", newPos.x, newPos.y); - killDrone(e, drone); - continue; - } - drone->mapCellIdx = mapIdx; - drone->lastPos = drone->pos; - drone->pos = newPos; - drone->lastVelocity = drone->velocity; - drone->velocity = b2Body_GetLinearVelocity(drone->bodyID); - - if (e->client != NULL) { - updateTrailPoints(&drone->trailPoints, MAX_DRONE_TRAIL_POINTS, newPos); - } - break; - case SHIELD_ENTITY: - shield = ent->entity; - shield->pos = newPos; - break; - case DRONE_PIECE_ENTITY: - piece = ent->entity; - piece->pos = newPos; - piece->rot = event->transform.q; - break; - default: - ERRORF("unknown entity type for move event %d", ent->type); - } - } -} - -// destroy the projectile if it has traveled enough or has bounced enough -// times, and update drone stats if a drone was hit -uint8_t handleProjectileBeginContact(iwEnv *e, const entity *proj, const entity *ent, const b2ContactId contactID, const bool projIsShapeA) { - projectileEntity *projectile = proj->entity; - projectile->contacts++; - - // e (shape B in the collision) will be NULL if it's another - // projectile that was just destroyed - if (ent == NULL || ent->type == PROJECTILE_ENTITY) { - // explode mines when hit by a projectile - if (projectile->weaponInfo->type == MINE_LAUNCHER_WEAPON) { - uint8_t numDestroyed = 1; - if (ent != NULL) { - const projectileEntity *projectile2 = ent->entity; - // if both entities are mines both will be destroyed - if (projectile2->weaponInfo->type == MINE_LAUNCHER_WEAPON) { - numDestroyed = 2; - } - } - destroyProjectile(e, projectile, true, true); - destroyExplodedProjectiles(e); - return numDestroyed; - } - - // always allow all other projectiles to bounce off each other - return false; - } else if (ent->type == BOUNCY_WALL_ENTITY) { - // always allow projectiles to bounce off bouncy walls - return false; - } else if (ent->type == SHIELD_ENTITY) { - // always allow projectiles to bounce off shields, and update shield health - shieldEntity *shield = ent->entity; - if (shield->health <= 0.0f) { - return false; - } - - const float damage = projectile->lastSpeed * projectile->weaponInfo->mass * DRONE_SHIELD_HEALTH_IMPULSE_COEF; - DEBUG_LOGF("shield projectile damage: %f", damage); - shield->health -= damage; - if (shield->health <= 0.0f) { - droneEntity *parentDrone = safe_array_get_at(e->drones, projectile->droneIdx); - droneAddEnergy(parentDrone, DRONE_SHIELD_BREAK_ENERGY_REFILL); - } - - return false; - } - - if (projectile->weaponInfo->type != BLACK_HOLE_WEAPON || ent->type != DRONE_ENTITY) { - projectile->bounces++; - } - if (ent->type == DRONE_ENTITY) { - droneEntity *hitDrone = ent->entity; - if (b2Contact_IsValid(contactID)) { - const b2Manifold manifold = b2Contact_GetData(contactID).manifold; - ASSERT(manifold.pointCount == 1); - b2Vec2 hitImpulse = b2MulSV(manifold.points[0].normalImpulse, manifold.normal); - if (!projIsShapeA) { - hitImpulse = b2Neg(hitImpulse); - } - droneTrackImpulse(e, hitDrone, hitImpulse, projectile->droneIdx); - } - - if (projectile->droneIdx != hitDrone->idx) { - droneEntity *shooterDrone = safe_array_get_at(e->drones, projectile->droneIdx); - - if (shooterDrone->team != hitDrone->team) { - const float impulseEnergy = projectile->lastSpeed * projectile->weaponInfo->mass * projectile->weaponInfo->energyRefillCoef; - droneAddEnergy(shooterDrone, impulseEnergy); - } - // add 1 so we can differentiate between no weapon and weapon 0 - shooterDrone->stepInfo.shotHit[hitDrone->idx] = projectile->weaponInfo->type + 1; - e->stats[shooterDrone->idx].shotsHit[projectile->weaponInfo->type]++; - e->stats[shooterDrone->idx].totalShotsHit++; - DEBUG_LOGF("drone %d hit drone %d with weapon %d", shooterDrone->idx, hitDrone->idx, projectile->weaponInfo->type); - hitDrone->stepInfo.shotTaken[shooterDrone->idx] = projectile->weaponInfo->type + 1; - e->stats[hitDrone->idx].shotsTaken[projectile->weaponInfo->type]++; - e->stats[hitDrone->idx].totalShotsTaken++; - DEBUG_LOGF("drone %d hit by drone %d with weapon %d", hitDrone->idx, shooterDrone->idx, projectile->weaponInfo->type); - } else { - hitDrone->stepInfo.ownShotTaken = true; - e->stats[hitDrone->idx].ownShotsTaken[projectile->weaponInfo->type]++; - e->stats[hitDrone->idx].totalOwnShotsTaken++; - DEBUG_LOGF("drone %d hit by own weapon %d", hitDrone->idx, projectile->weaponInfo->type); - } - - if (projectile->weaponInfo->destroyedOnDroneHit) { - destroyProjectile(e, projectile, projectile->weaponInfo->explodesOnDroneHit, true); - destroyExplodedProjectiles(e); - return 1; - } - } else if (projectile->weaponInfo->type == MINE_LAUNCHER_WEAPON && !projectile->setMine) { - // if the mine is in explosion proximity of a drone now, - // destroy it - const b2QueryFilter filter = { - .categoryBits = PROJECTILE_SHAPE, - .maskBits = DRONE_SHAPE, - }; - if (isOverlappingCircleInLineOfSight(e, projectile->ent, projectile->pos, MINE_LAUNCHER_PROXIMITY_RADIUS, filter, NULL)) { - destroyProjectile(e, projectile, true, true); - destroyExplodedProjectiles(e); - return 1; - } - - // create a weld joint to stick the mine to the wall - ASSERT(entityTypeIsWall(ent->type)); - wallEntity *wall = ent->entity; - ASSERT(b2Contact_IsValid(contactID)); - const b2Manifold manifold = b2Contact_GetData(contactID).manifold; - ASSERT(manifold.pointCount == 1); - - b2WeldJointDef jointDef = b2DefaultWeldJointDef(); - const b2Rot projRot = b2Body_GetRotation(projectile->bodyID); - if (projIsShapeA) { - jointDef.base.bodyIdA = projectile->bodyID; - jointDef.base.bodyIdB = wall->bodyID; - const b2Rot relativeRot = b2MakeRot(b2RelativeAngle(wall->rot, projRot)); - jointDef.base.localFrameA.p = b2InvRotateVector(projRot, manifold.points[0].anchorB); - jointDef.base.localFrameA.q = relativeRot; - jointDef.base.localFrameB.p = b2InvRotateVector(wall->rot, manifold.points[0].anchorA); - jointDef.base.localFrameB.q = relativeRot; - } else { - jointDef.base.bodyIdA = wall->bodyID; - jointDef.base.bodyIdB = projectile->bodyID; - const b2Rot relativeRot = b2MakeRot(b2RelativeAngle(projRot, wall->rot)); - jointDef.base.localFrameA.p = b2InvRotateVector(wall->rot, manifold.points[0].anchorA); - jointDef.base.localFrameA.q = relativeRot; - jointDef.base.localFrameB.p = b2InvRotateVector(projRot, manifold.points[0].anchorB); - jointDef.base.localFrameB.q = relativeRot; - } - b2CreateWeldJoint(e->worldID, &jointDef); - projectile->velocity = b2Vec2_zero; - projectile->lastVelocity = b2Vec2_zero; - projectile->speed = 0.0f; - projectile->lastSpeed = 0.0f; - projectile->setMine = true; - } - - const uint8_t maxBounces = projectile->weaponInfo->maxBounces; - if (projectile->bounces == maxBounces) { - destroyProjectile(e, projectile, true, true); - destroyExplodedProjectiles(e); - return 1; - } - - return 0; -} - -// ensure speed is maintained when a projectile hits a dynamic body -void handleProjectileEndContact(const entity *proj, const entity *ent) { - projectileEntity *projectile = proj->entity; - projectile->contacts--; - - // mines stick to walls, explode when hitting another projectile - // and are destroyed when hitting a drone so no matter what we don't - // need to do anything here - if (projectile->weaponInfo->type == MINE_LAUNCHER_WEAPON) { - return; - } - - if (ent != NULL) { - if (ent->type == PROJECTILE_ENTITY) { - const projectileEntity *projectile2 = ent->entity; - // allow projectile speeds to increase when two different - // projectile types collide - if (projectile->weaponInfo->type != projectile2->weaponInfo->type) { - b2Vec2 newVel = b2Body_GetLinearVelocity(projectile->bodyID); - float newSpeed = b2Length(newVel); - if (newSpeed < projectile->lastSpeed) { - newSpeed = projectile->lastSpeed; - newVel = b2MulSV(newSpeed, b2Normalize(newVel)); - b2Body_SetLinearVelocity(projectile->bodyID, newVel); - } - projectile->velocity = newVel; - projectile->speed = newSpeed; - projectile->lastSpeed = newSpeed; - return; - } - } - } - - // the last speed is used here instead of the current speed because - // the current speed will be the speed box2d set the projectile to - // after a collision and we want to keep the speed consistent - float newSpeed = projectile->lastSpeed; - if (projectile->weaponInfo->type == ACCELERATOR_WEAPON) { - newSpeed = min(projectile->lastSpeed * ACCELERATOR_BOUNCE_SPEED_COEF, ACCELERATOR_MAX_SPEED); - projectile->speed = newSpeed; - } - - // ensure the projectile's speed doesn't change after bouncing off - // something - b2Vec2 newVel = b2Body_GetLinearVelocity(projectile->bodyID); - newVel = b2MulSV(newSpeed, b2Normalize(newVel)); - b2Body_SetLinearVelocity(projectile->bodyID, newVel); - projectile->velocity = newVel; - projectile->speed = newSpeed; - projectile->lastSpeed = newSpeed; -} - -// TODO: drone on drone collisions should reduce shield health -void handleContactEvents(iwEnv *e) { - b2ContactEvents events = b2World_GetContactEvents(e->worldID); - for (int i = 0; i < events.beginCount; ++i) { - const b2ContactBeginTouchEvent *event = events.beginEvents + i; - entity *e1 = NULL; - entity *e2 = NULL; - - if (b2Shape_IsValid(event->shapeIdA)) { - e1 = b2Shape_GetUserData(event->shapeIdA); - ASSERT(e1 != NULL); - } - if (b2Shape_IsValid(event->shapeIdB)) { - e2 = b2Shape_GetUserData(event->shapeIdB); - ASSERT(e2 != NULL); - } - - if (e1 != NULL && e1->type == DRONE_ENTITY && e2 != NULL && e2->type == DRONE_ENTITY) { - droneEntity *drone1 = e1->entity; - droneEntity *drone2 = e2->entity; - - if (b2Contact_IsValid(event->contactId)) { - const b2Manifold manifold = b2Contact_GetData(event->contactId).manifold; - ASSERT(manifold.pointCount == 1); - b2Vec2 hitImpulse = b2MulSV(manifold.points[0].normalImpulse, manifold.normal); - droneTrackImpulse(e, drone2, hitImpulse, drone1->idx); - droneTrackImpulse(e, drone1, b2Neg(hitImpulse), drone2->idx); - } - } - - if (e1 != NULL) { - if (e1->type == PROJECTILE_ENTITY) { - uint8_t numDestroyed = handleProjectileBeginContact(e, e1, e2, event->contactId, true); - if (numDestroyed == 2) { - continue; - } else if (numDestroyed == 1) { - e1 = NULL; - } - - } else if (entityTypeIsWall(e1->type) && e2 != NULL) { - if (e1->type == DEATH_WALL_ENTITY) { - if (e2->type == DRONE_ENTITY) { - droneEntity *drone = e2->entity; - killDrone(e, drone); - } else if (e2->type == SHIELD_ENTITY) { - shieldEntity *shield = e2->entity; - shield->health = 0.0f; - destroyDroneShield(e, shield, true); - e2 = NULL; - } - } - } - } - if (e2 != NULL) { - if (e2->type == PROJECTILE_ENTITY) { - handleProjectileBeginContact(e, e2, e1, event->contactId, false); - } else if (entityTypeIsWall(e2->type) && e1 != NULL) { - if (e2->type == DEATH_WALL_ENTITY) { - if (e1->type == DRONE_ENTITY) { - droneEntity *drone = e1->entity; - killDrone(e, drone); - } else if (e1->type == SHIELD_ENTITY) { - shieldEntity *shield = e1->entity; - shield->health = 0.0f; - destroyDroneShield(e, shield, true); - } - } - } - } - } - - for (int i = 0; i < events.endCount; ++i) { - const b2ContactEndTouchEvent *event = events.endEvents + i; - entity *e1 = NULL; - entity *e2 = NULL; - if (b2Shape_IsValid(event->shapeIdA)) { - e1 = b2Shape_GetUserData(event->shapeIdA); - ASSERT(e1 != NULL); - } - if (b2Shape_IsValid(event->shapeIdB)) { - e2 = b2Shape_GetUserData(event->shapeIdB); - ASSERT(e2 != NULL); - } - if (e1 != NULL && e1->type == PROJECTILE_ENTITY) { - handleProjectileEndContact(e1, e2); - } - if (e2 != NULL && e2->type == PROJECTILE_ENTITY) { - handleProjectileEndContact(e2, e1); - } - } -} - -// set pickup to respawn somewhere else randomly if a drone touched it, -// mark the pickup as disabled if a floating wall is touching it -void handleWeaponPickupBeginTouch(iwEnv *e, const entity *sensor, entity *visitor) { - weaponPickupEntity *pickup = sensor->entity; - if (pickup->floatingWallsTouching != 0) { - return; - } - - wallEntity *wall; - - switch (visitor->type) { - case DRONE_ENTITY: - disableWeaponPickup(e, pickup); - - droneEntity *drone = visitor->entity; - drone->stepInfo.pickedUpWeapon = true; - drone->stepInfo.prevWeapon = drone->weaponInfo->type; - droneChangeWeapon(e, drone, pickup->weapon); - - e->stats[drone->idx].weaponsPickedUp[pickup->weapon]++; - e->stats[drone->idx].totalWeaponsPickedUp++; - DEBUG_LOGF("drone %d picked up weapon %d", drone->idx, pickup->weapon); - break; - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - wall = visitor->entity; - if (!wall->isFloating) { - if (!wall->isSuddenDeath) { - ERRORF("non sudden death wall type %d at cell %d touched weapon pickup", visitor->type, wall->mapCellIdx); - } - return; - } - - pickup->floatingWallsTouching++; - break; - default: - ERRORF("invalid weapon pickup begin touch visitor %d", visitor->type); - } -} - -// explode proximity detonating projectiles -void handleProjectileBeginTouch(iwEnv *e, const entity *sensor, entity *visitor) { - projectileEntity *projectile = sensor->entity; - - switch (projectile->weaponInfo->type) { - case FLAK_CANNON_WEAPON: - if (projectile->distance < FLAK_CANNON_SAFE_DISTANCE) { - return; - } - destroyProjectile(e, projectile, true, true); - destroyExplodedProjectiles(e); - break; - case MINE_LAUNCHER_WEAPON: - if (!projectile->setMine) { - return; - } - - ASSERT(visitor->type == DRONE_ENTITY); - const b2DistanceOutput output = closestPoint(sensor, visitor); - const b2QueryFilter filter = {.categoryBits = PROJECTILE_SHAPE, .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE}; - if (posBehindWall(e, projectile->pos, output.pointB, NULL, filter, NULL)) { - const droneEntity *drone = visitor->entity; - projectile->dronesBehindWalls[projectile->numDronesBehindWalls++] = drone->idx; - return; - } - - destroyProjectile(e, projectile, true, true); - destroyExplodedProjectiles(e); - break; - case BLACK_HOLE_WEAPON: - if (visitor->type == DRONE_ENTITY) { - const droneEntity *drone = visitor->entity; - if (projectile->droneIdx == drone->idx && projectile->distance < BLACK_HOLE_PARENT_IGNORE_DISTANCE) { - return; - } - } - - // copy the entity ID so it won't be changed if the entity is - // destroyed and reused later - const entityID *visitorID = visitor->id; - entityID *id = fastCalloc(1, sizeof(entityID)); - id->id = visitorID->id; - id->generation = visitorID->generation; - cc_array_add(projectile->entsInBlackHole, id); - break; - default: - ERRORF("invalid projectile type %d for begin touch event", sensor->type); - } -} - -// mark the pickup as enabled if no floating walls are touching it -void handleWeaponPickupEndTouch(const entity *sensor, entity *visitor) { - weaponPickupEntity *pickup = sensor->entity; - if (pickup->respawnWait != 0.0f) { - return; - } - - wallEntity *wall; - - switch (visitor->type) { - case DRONE_ENTITY: - break; - case STANDARD_WALL_ENTITY: - case BOUNCY_WALL_ENTITY: - case DEATH_WALL_ENTITY: - wall = visitor->entity; - if (!wall->isFloating) { - return; - } - - pickup->floatingWallsTouching--; - break; - default: - ERRORF("invalid weapon pickup end touch visitor %d", visitor->type); - } -} - -void handleProjectileEndTouch(const entity *sensor, entity *visitor) { - projectileEntity *projectile = sensor->entity; - - switch (projectile->weaponInfo->type) { - case FLAK_CANNON_WEAPON: - break; - case MINE_LAUNCHER_WEAPON: - if (projectile->numDronesBehindWalls == 0) { - return; - } - projectile->numDronesBehindWalls--; - break; - case BLACK_HOLE_WEAPON: - if (visitor == NULL) { - return; - } - - const entityID *visitorID = visitor->id; - for (uint8_t i = 0; i < cc_array_size(projectile->entsInBlackHole); ++i) { - entityID *id = safe_array_get_at(projectile->entsInBlackHole, i); - if (id->id == visitorID->id) { - fastFree(id); - cc_array_remove_fast_at(projectile->entsInBlackHole, i, NULL); - return; - } - } - break; - default: - ERRORF("invalid projectile type %d for end touch event", projectile->weaponInfo->type); - } -} - -void handleSensorEvents(iwEnv *e) { - b2SensorEvents events = b2World_GetSensorEvents(e->worldID); - for (int i = 0; i < events.beginCount; ++i) { - const b2SensorBeginTouchEvent *event = events.beginEvents + i; - if (!b2Shape_IsValid(event->sensorShapeId)) { - DEBUG_LOG("could not find sensor shape for begin touch event"); - continue; - } - entity *s = b2Shape_GetUserData(event->sensorShapeId); - ASSERT(s != NULL); - - if (!b2Shape_IsValid(event->visitorShapeId)) { - DEBUG_LOG("could not find visitor shape for begin touch event"); - continue; - } - entity *v = b2Shape_GetUserData(event->visitorShapeId); - ASSERT(v != NULL); - - switch (s->type) { - case WEAPON_PICKUP_ENTITY: - handleWeaponPickupBeginTouch(e, s, v); - break; - case PROJECTILE_ENTITY: - handleProjectileBeginTouch(e, s, v); - break; - default: - ERRORF("unknown entity type %d for sensor begin touch event", s->type); - } - } - - for (int i = 0; i < events.endCount; ++i) { - const b2SensorEndTouchEvent *event = events.endEvents + i; - if (!b2Shape_IsValid(event->sensorShapeId)) { - DEBUG_LOG("could not find sensor shape for end touch event"); - continue; - } - entity *s = b2Shape_GetUserData(event->sensorShapeId); - ASSERT(s != NULL); - entity *v = NULL; - if (b2Shape_IsValid(event->visitorShapeId)) { - v = b2Shape_GetUserData(event->visitorShapeId); - ASSERT(v != NULL); - } - - if (s->type == PROJECTILE_ENTITY) { - handleProjectileEndTouch(s, v); - continue; - } - - if (v != NULL) { - handleWeaponPickupEndTouch(s, v); - } - } -} - -void findNearWalls(const iwEnv *e, const droneEntity *drone, nearEntity nearestWalls[], const uint8_t nWalls) { - nearEntity nearWalls[MAX_NEAREST_WALLS]; - - for (uint8_t i = 0; i < MAX_NEAREST_WALLS; ++i) { - const uint32_t idx = (MAX_NEAREST_WALLS * drone->mapCellIdx) + i; - const uint16_t wallIdx = e->map->nearestWalls[idx].idx; - wallEntity *wall = safe_array_get_at(e->walls, wallIdx); - nearWalls[i].entity = wall; - nearWalls[i].distanceSquared = b2DistanceSquared(drone->pos, wall->pos); - } - insertionSort(nearWalls, MAX_NEAREST_WALLS); - memcpy(nearestWalls, nearWalls, nWalls * sizeof(nearEntity)); -} - -#endif diff --git a/pufferlib/ocean/impulse_wars/helpers.h b/pufferlib/ocean/impulse_wars/helpers.h deleted file mode 100644 index 1692d6b9b..000000000 --- a/pufferlib/ocean/impulse_wars/helpers.h +++ /dev/null @@ -1,246 +0,0 @@ -#ifndef IMPULSE_WARS_HELPERS_H -#define IMPULSE_WARS_HELPERS_H - -#include -#include -#include -#include - -#include "box2d/box2d.h" - -#include "include/cc_array.h" - -#ifndef NDEBUG -#define ON_ERROR __builtin_trap() -#define _DEBUG_GET_TIMEINFO() \ - time_t _t = time(NULL); \ - struct tm *_timeinfo; \ - _timeinfo = localtime(&_t) -#define DEBUG_RAW_LOG(msg) \ - do { \ - printf(msg); \ - fflush(stdout); \ - } while (0) -#define DEBUG_RAW_LOGF(fmt, args...) \ - do { \ - printf(fmt, args); \ - fflush(stdout); \ - } while (0) -#define DEBUG_LOGF(fmt, args...) \ - do { \ - _DEBUG_GET_TIMEINFO(); \ - printf(fmt " %d:%d:%d %s:%d\n", args, _timeinfo->tm_hour, _timeinfo->tm_min, _timeinfo->tm_sec, __FILE__, __LINE__); \ - fflush(stdout); \ - } while (0) -#define DEBUG_LOG(msg) \ - do { \ - _DEBUG_GET_TIMEINFO(); \ - printf(msg " %d:%d:%d %s:%d\n", _timeinfo->tm_hour, _timeinfo->tm_min, _timeinfo->tm_sec, __FILE__, __LINE__); \ - fflush(stdout); \ - } while (0) - -#define ASSERT(condition) \ - do { \ - if (!(condition)) { \ - printf("\nASSERTION FAILED: %s at %s:%d\n\n", #condition, __FILE__, __LINE__); \ - fflush(stdout); \ - ON_ERROR; \ - } \ - } while (0) -#define ASSERTF(condition, fmt, args...) \ - do { \ - if (!(condition)) { \ - printf("\nASSERTION FAILED: %s; " fmt "; at %s:%d\n\n", #condition, args, __FILE__, __LINE__); \ - fflush(stdout); \ - ON_ERROR; \ - } \ - } while (0) -#else -#define ON_ERROR abort() -#define DEBUG_RAW_LOG(msg) -#define DEBUG_RAW_LOGF(fmt, args...) -#define DEBUG_LOGF(fmt, args...) -#define DEBUG_LOG(msg) -#define ASSERT(condition) -#define ASSERTF(condition, fmt, args...) -#endif - -#define ERRORF(fmt, args...) \ - fprintf(stderr, "FATAL: " fmt " %s:%d\n", args, __FILE__, __LINE__); \ - fflush(stderr); \ - ON_ERROR -#define ERROR(msg) \ - fprintf(stderr, "FATAL: " msg " %s:%d\n", __FILE__, __LINE__); \ - fflush(stderr); \ - ON_ERROR - -// ignore compiler warnings about unused variables for variables that are -// only used in debug builds -#define MAYBE_UNUSED(x) (void)x - -#define SQUARED(x) ((x) * (x)) - -#ifndef PI -#define PI 3.14159265358979323846f -#endif - -#ifndef RAD2DEG -#define RAD2DEG (180.0f / PI) -#endif - -#define MASS(density, radius) ((density) * PI * (radius) * (radius)) -#define INV_MASS(mass) (1.0f / (mass)) -// given a mass and radius, calculate the density such that the density -// and radius will result in the same mass as what's given -#define MATCHING_DENSITY(mass, radius) ((mass) / (PI * (radius) * (radius))) - -#define ASSERT_VEC_BOUNDED(vec) \ - ASSERTF(vec.x <= 1.0f, "vec.x: %f", vec.x); \ - ASSERTF(vec.x >= -1.0f, "vec.x: %f", vec.x); \ - ASSERTF(vec.y <= 1.0f, "vec.y: %f", vec.y); \ - ASSERTF(vec.y >= -1.0f, "vec.y: %f", vec.y) - -#define ASSERT_VEC_NORMALIZED(vec) \ - ASSERT_VEC_BOUNDED(vec); \ - do { \ - const b2Vec2 norm = b2Normalize(vec); \ - MAYBE_UNUSED(norm); \ - ASSERTF(fabs(vec.x - norm.x) < 0.000001f, "vec: %f, %f norm: %f, %f", vec.x, vec.y, norm.x, norm.y); \ - ASSERTF(fabs(vec.y - norm.y) < 0.000001f, "vec: %f, %f norm: %f, %f", vec.x, vec.y, norm.x, norm.y); \ - } while (0) - -// use malloc when debugging so the address sanitizer can find issues with -// heap memory, use dlmalloc in release mode for performance; emscripten -// uses dlmalloc by default so no need to change anything here; dlmalloc -// sometimes won't compile on macOS so just use malloc and friends -#if !defined(NDEBUG) || defined(__EMSCRIPTEN__) || defined(__APPLE__) -#define fastMalloc(size) malloc(size) -#define fastMallocFn malloc -#define fastCalloc(nmemb, size) calloc(nmemb, size) -#define fastCallocFn calloc -#define fastFree(ptr) free(ptr) -#define fastFreeFn free -#else -#include "include/dlmalloc.h" -#define fastMalloc(size) dlmalloc(size) -#define fastMallocFn dlmalloc -#define fastCalloc(nmemb, size) dlcalloc(nmemb, size) -#define fastCallocFn dlcalloc -#define fastFree(ptr) dlfree(ptr) -#define fastFreeFn dlfree -#endif - -static inline void create_array(CC_Array **array, size_t initialCap) { - CC_ArrayConf conf; - cc_array_conf_init(&conf); - conf.capacity = initialCap; - conf.mem_alloc = fastMallocFn; - conf.mem_calloc = fastCallocFn; - conf.mem_free = fastFreeFn; - - cc_array_new_conf(&conf, array); -} - -// automatically checks that the index is valid and returns the value -// so callers can use it as a constant expression -static inline void *safe_array_get_at(const CC_Array *const array, size_t index) { - void *val; - const enum cc_stat res = cc_array_get_at(array, index, &val); - ASSERT(res == CC_OK); - MAYBE_UNUSED(res); - return val; -} - -static inline bool b2VecEqual(const b2Vec2 v1, const b2Vec2 v2) { - return v1.x == v2.x && v1.y == v2.y; -} - -// from https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/ -// see also https://github.com/lemire/testingRNG -uint64_t wyhash64(uint64_t *state) { - *state += 0x60bee2bee120fc15; - __uint128_t tmp; - tmp = (__uint128_t)(*state) * 0xa3b195354a39b70d; - uint64_t m1 = (tmp >> 64) ^ tmp; - tmp = (__uint128_t)m1 * 0x1b03738712fad5c9; - uint64_t m2 = (tmp >> 64) ^ tmp; - return m2; -} - -static inline float randFloat(uint64_t *state, const float min, const float max) { - float n = wyhash64(state) / (float)UINT64_MAX; - return min + n * (max - min); -} - -static inline int randInt(uint64_t *state, const int min, const int max) { - return min + wyhash64(state) % (max - min + 1); -} - -static inline float logBasef(const float v, const float b) { - return log2f(v) / log2(b); -} - -#define min(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a < _b ? _a : _b; \ - }) - -#define max(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ - }) - -// clamps between 0 and 1 -static inline float clamp(float f) { - return min(max(f, 0.0f), 1.0f); -} - -// normalize value to be between 0 and max, or -max and max; -// minIsZero determines if the min value is 0 or -max -static inline float scaleValue(const float v, const float max, const bool minIsZero) { - ASSERTF(v <= max, "v: %f, max: %f", v, max); - ASSERTF(!minIsZero || v >= 0, "v: %f", v); - ASSERTF(minIsZero || v >= -max, "v: %f, -max: %f", v, -max); - - float scaled = v / max; - if (minIsZero) { - return max(min(scaled, max), 0.0f); - } else { - return max(min(scaled, max), -1.0f); - } -} - -static inline uint8_t oneHotEncode(float *obs, const uint16_t offset, const uint8_t val, const uint8_t max) { - ASSERTF(val < max, "val: %d, max: %d", val, max); - memset(obs + offset, 0x0, max * sizeof(float)); - obs[offset + val] = 1; - return max; -} - -static inline uint16_t alignedSize(const uint16_t size, const uint8_t align) { - return (size + align - 1) & ~(align - 1); -} - -#define BITNSLOTS(nb) ((nb + sizeof(uint8_t) - 1) / sizeof(uint8_t)) - -static inline uint16_t bitMask(const uint16_t n) { - return 1 << (n % sizeof(uint8_t)); -} - -static inline uint16_t bitSlot(const uint16_t n) { - return n / sizeof(uint8_t); -} - -static inline void bitSet(uint8_t *b, const uint16_t n) { - b[bitSlot(n)] |= bitMask(n); -} - -static inline bool bitTest(const uint8_t *b, const uint16_t n) { - return b[bitSlot(n)] & bitMask(n); -} - -#endif diff --git a/pufferlib/ocean/impulse_wars/impulse_wars.c b/pufferlib/ocean/impulse_wars/impulse_wars.c deleted file mode 100644 index 1dfbd1d5c..000000000 --- a/pufferlib/ocean/impulse_wars/impulse_wars.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "env.h" -#include "render.h" - -#ifdef __EMSCRIPTEN__ -void emscriptenStep(void *e) { - stepEnv((iwEnv *)e); - return; -} -#endif - -int main(void) { - const int NUM_DRONES = 2; - - iwEnv *e = fastCalloc(1, sizeof(iwEnv)); - - posix_memalign((void **)&e->observations, sizeof(void *), alignedSize(NUM_DRONES * obsBytes(NUM_DRONES), sizeof(float))); - e->rewards = fastCalloc(NUM_DRONES, sizeof(float)); - e->actions = fastCalloc(NUM_DRONES * CONTINUOUS_ACTION_SIZE, sizeof(float)); - e->masks = fastCalloc(NUM_DRONES, sizeof(uint8_t)); - e->terminals = fastCalloc(NUM_DRONES, sizeof(uint8_t)); - e->truncations = fastCalloc(NUM_DRONES, sizeof(uint8_t)); - - rayClient *client = createRayClient(); - e->client = client; - - initEnv(e, NUM_DRONES, 0, -1, time(NULL), false, false, false, false); - initMaps(e); - setupEnv(e); - // e->humanInput = true; - -#ifdef __EMSCRIPTEN__ - lastFrameTime = emscripten_get_now(); - emscripten_set_main_loop_arg(emscriptenStep, e, 0, true); -#else - while (!WindowShouldClose()) { - stepEnv(e); - } - - destroyEnv(e); - destroyMaps(); - free(e->observations); - fastFree(e->actions); - fastFree(e->rewards); - fastFree(e->masks); - fastFree(e->terminals); - fastFree(e->truncations); - fastFree(e); - destroyRayClient(client); -#endif - return 0; -} diff --git a/pufferlib/ocean/impulse_wars/impulse_wars.py b/pufferlib/ocean/impulse_wars/impulse_wars.py deleted file mode 100644 index 0f958b8dc..000000000 --- a/pufferlib/ocean/impulse_wars/impulse_wars.py +++ /dev/null @@ -1,161 +0,0 @@ -from types import SimpleNamespace - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.impulse_wars import binding - - -discMoveToContMove = np.array([ - [1.0, 0.707107, 0.0, -0.707107, -1.0, -0.707107, 0.0, 0.707107, 0.0], - [0.0, 0.707107, 1.0, 0.707107, 0.0, -0.707107, -1.0, -0.707107, 0.0], -], dtype=np.float32) -discAimToContAim = np.array([ - [1.0, 0.92388, 0.707107, 0.382683, 0.0, -0.382683, -0.707107, -0.92388, -1.0, -0.92388, -0.707107, -0.382683, 0.0, 0.382683, 0.707107, 0.92388, 0.0], - [0.0, 0.382683, 0.707107, 0.92388, 1.0, 0.92388, 0.707107, 0.382683, 0.0, -0.382683, -0.707107, -0.92388, -1.0, -0.92388, -0.707107, -0.382683, 0.0], -], dtype=np.float32) - - -class ImpulseWars(pufferlib.PufferEnv): - def __init__( - self, - num_envs: int = 1, - num_drones: int = 2, - num_agents: int = 1, - enable_teams: bool = False, - sitting_duck: bool = False, - continuous: bool = False, - is_training: bool = True, - human_control: bool = False, - seed: int = 0, - render: bool = False, - report_interval: int = 64, - buf = None, - ): - self.obsInfo = SimpleNamespace(**binding.get_consts(num_drones)) - - if num_envs <= 0: - raise ValueError("num_envs must be greater than 0") - if num_drones > self.obsInfo.maxDrones or num_drones <= 0: - raise ValueError(f"num_drones must greater than 0 and less than or equal to {self.obsInfo.maxDrones}") - if num_agents > num_drones or num_agents <= 0: - raise ValueError("num_agents must greater than 0 and less than or equal to num_drones") - if enable_teams and (num_drones % 2 != 0 or num_drones <= 2): - raise ValueError("enable_teams is only supported for even numbers of drones greater than 2") - - self.numDrones = num_drones - self.continuous = continuous - - self.num_agents = num_agents * num_envs - self.tick = 0 - - # map observations are bit packed to save space, and scalar - # observations need to be floats - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=255, shape=(self.obsInfo.obsBytes,), dtype=np.uint8 - ) - - if self.continuous: - # action space is actually bounded by (-1, 1) but pufferlib - # will check that actions are within the bounds of the action - # space before actions get to the env, and we ensure the actions - # are bounded there; so set bounds to (-inf, inf) here so - # action bounds checks pass - self.single_action_space = gymnasium.spaces.Box( - low=float("-inf"), high=float("inf"), shape=(self.obsInfo.contActionsSize,), dtype=np.float32 - ) - else: - self.single_action_space = gymnasium.spaces.MultiDiscrete( - [ - 9, # move, noop + 8 directions - 17, # aim, noop + 16 directions - 2, # shoot or not - 2, # brake or not - 2, # burst - ] - ) - - self.report_interval = report_interval - self.render_mode = "human" if render else None - - super().__init__(buf) - if not self.continuous: - self.actions = np.zeros((self.num_agents, self.obsInfo.contActionsSize), dtype=np.float32) - - self.c_envs = binding.vec_init( - self.observations, - self.actions, - self.rewards, - self.terminals, - self.truncations, - num_envs, - seed, - num_drones=num_drones, - num_agents=num_agents, - map_idx=-1, - enable_teams=enable_teams, - sitting_duck=sitting_duck, - is_training=is_training, - continuous=continuous, - ) - - binding.shared(self.c_envs) - - def reset(self, seed=None): - self.tick = 0 - if seed is None: - binding.vec_reset(self.c_envs, 0) - else: - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - if self.continuous: - self.actions[:] = actions - else: - contMove = discMoveToContMove[:, actions[:, 0]].T - contAim = discAimToContAim[:, actions[:, 1]].T - contRest = actions[:, 2:].astype(np.float32) - self.actions[:] = np.concatenate([contMove, contAim, contRest], axis=1) - - self.tick += 1 - binding.vec_step(self.c_envs) - - infos = [] - if self.tick % self.report_interval == 0: - infos.append(binding.vec_log(self.c_envs)) - - return self.observations, self.rewards, self.terminals, self.truncations, infos - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - - -def testPerf(timeout, actionCache, numEnvs): - env = ImpulseWars(numEnvs) - - import time - - np.random.seed(int(time.time())) - actions = np.random.uniform(-1, 1, (actionCache, env.num_agents, 7)) - - tick = 0 - start = time.time() - while time.time() - start < timeout: - action = actions[tick % actionCache] - env.step(action) - tick += 1 - - sps = numEnvs * (tick / (time.time() - start)) - print(f"SPS: {sps:,}") - print(f"Steps: {numEnvs * tick}") - - env.close() - - -if __name__ == "__main__": - testPerf(timeout=5, actionCache=1024, numEnvs=1) diff --git a/pufferlib/ocean/impulse_wars/include/cc_array.h b/pufferlib/ocean/impulse_wars/include/cc_array.h deleted file mode 100644 index 311f99122..000000000 --- a/pufferlib/ocean/impulse_wars/include/cc_array.h +++ /dev/null @@ -1,1410 +0,0 @@ -/* - * Collections-C - * Copyright (C) 2013-2015 Srđan Panić - * - * This file is part of Collections-C. - * - * Collections-C is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Collections-C is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Collections-C. If not, see . - */ - -#ifndef CC_ARRAY_H -#define CC_ARRAY_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "cc_common.h" - -/** - * A dynamic array that expands automatically as elements are - * added. The array supports amortized constant time insertion - * and removal of elements at the end of the array, as well as - * constant time access. - */ -typedef struct cc_array_s CC_Array; - -/** - * Array configuration structure. Used to initialize a new Array - * with specific values. - */ -typedef struct cc_array_conf_s { - /** - * The initial capacity of the array */ - size_t capacity; - - /** - * The rate at which the buffer expands (capacity * exp_factor). */ - float exp_factor; - - /** - * Memory allocators used to allocate the Array structure and the - * underlying data buffers. */ - void *(*mem_alloc)(size_t size); - void *(*mem_calloc)(size_t blocks, size_t size); - void (*mem_free)(void *block); -} CC_ArrayConf; - -/** - * Array iterator structure. Used to iterate over the elements of - * the array in an ascending order. The iterator also supports - * operations for safely adding and removing elements during - * iteration. - */ -typedef struct cc_array_iter_s { - /** - * The array associated with this iterator */ - CC_Array *ar; - - /** - * The current position of the iterator.*/ - size_t index; - - /** - * Set to true if the last returned element was removed. */ - bool last_removed; -} CC_ArrayIter; - -/** - * Array zip iterator structure. Used to iterate over the elements of two - * arrays in lockstep in an ascending order until one of the Arrays is - * exhausted. The iterator also supports operations for safely adding - * and removing elements during iteration. - */ -typedef struct array_zip_iter_s { - CC_Array *ar1; - CC_Array *ar2; - size_t index; - bool last_removed; -} CC_ArrayZipIter; - -enum cc_stat cc_array_new(CC_Array **out); -enum cc_stat cc_array_new_conf(CC_ArrayConf const *const conf, CC_Array **out); -void cc_array_conf_init(CC_ArrayConf *conf); -size_t cc_array_struct_size(); - -void cc_array_destroy(CC_Array *ar); -void cc_array_destroy_cb(CC_Array *ar, void (*cb)(void *)); - -enum cc_stat cc_array_add(CC_Array *ar, void *element); -enum cc_stat cc_array_add_at(CC_Array *ar, void *element, size_t index); -enum cc_stat cc_array_replace_at(CC_Array *ar, void *element, size_t index, void **out); -enum cc_stat cc_array_swap_at(CC_Array *ar, size_t index1, size_t index2); - -enum cc_stat cc_array_remove(CC_Array *ar, void *element, void **out); -enum cc_stat cc_array_remove_fast(CC_Array *ar, void *element, void **out); -enum cc_stat cc_array_remove_at(CC_Array *ar, size_t index, void **out); -enum cc_stat cc_array_remove_fast_at(CC_Array *ar, size_t index, void **out); -enum cc_stat cc_array_remove_last(CC_Array *ar, void **out); -void cc_array_remove_all(CC_Array *ar); -void cc_array_remove_all_free(CC_Array *ar); - -enum cc_stat cc_array_get_at(const CC_Array *ar, size_t index, void **out); -enum cc_stat cc_array_get_last(const CC_Array *ar, void **out); - -enum cc_stat cc_array_subarray(CC_Array *ar, size_t from, size_t to, CC_Array **out); -enum cc_stat cc_array_copy_shallow(CC_Array *ar, CC_Array **out); -enum cc_stat cc_array_copy_deep(CC_Array *ar, void *(*cp)(void *), CC_Array **out); - -void cc_array_reverse(CC_Array *ar); -enum cc_stat cc_array_trim_capacity(CC_Array *ar); - -size_t cc_array_contains(const CC_Array *ar, void *element); -size_t cc_array_contains_value(const CC_Array *ar, void *element, int (*cmp)(const void *, const void *)); -size_t cc_array_size(const CC_Array *ar); -size_t cc_array_capacity(const CC_Array *ar); - -enum cc_stat cc_array_index_of(const CC_Array *ar, void *element, size_t *index); -void cc_array_sort(CC_Array *ar, int (*cmp)(const void *, const void *)); - -void cc_array_map(CC_Array *ar, void (*fn)(void *)); -void cc_array_reduce(CC_Array *ar, void (*fn)(void *, void *, void *), void *result); - -enum cc_stat cc_array_filter_mut(CC_Array *ar, bool (*predicate)(const void *)); -enum cc_stat cc_array_filter(CC_Array *ar, bool (*predicate)(const void *), CC_Array **out); - -void cc_array_iter_init(CC_ArrayIter *iter, CC_Array *ar); -enum cc_stat cc_array_iter_next(CC_ArrayIter *iter, void **out); -enum cc_stat cc_array_iter_remove(CC_ArrayIter *iter, void **out); -enum cc_stat cc_array_iter_remove_fast(CC_ArrayIter *iter, void **out); -enum cc_stat cc_array_iter_add(CC_ArrayIter *iter, void *element); -enum cc_stat cc_array_iter_replace(CC_ArrayIter *iter, void *element, void **out); -size_t cc_array_iter_index(CC_ArrayIter *iter); - -void cc_array_zip_iter_init(CC_ArrayZipIter *iter, CC_Array *a1, CC_Array *a2); -enum cc_stat cc_array_zip_iter_next(CC_ArrayZipIter *iter, void **out1, void **out2); -enum cc_stat cc_array_zip_iter_add(CC_ArrayZipIter *iter, void *e1, void *e2); -enum cc_stat cc_array_zip_iter_remove(CC_ArrayZipIter *iter, void **out1, void **out2); -enum cc_stat cc_array_zip_iter_replace(CC_ArrayZipIter *iter, void *e1, void *e2, void **out1, void **out2); -size_t cc_array_zip_iter_index(CC_ArrayZipIter *iter); - -const void *const *cc_array_get_buffer(CC_Array *ar); - -#define CC_ARRAY_FOREACH(val, array, body) \ - { \ - CC_ArrayIter cc_array_iter_53d46d2a04458e7b; \ - cc_array_iter_init(&cc_array_iter_53d46d2a04458e7b, array); \ - void *val; \ - while (cc_array_iter_next(&cc_array_iter_53d46d2a04458e7b, &val) != CC_ITER_END) \ - body \ - } - -#define CC_ARRAY_FOREACH_ZIP(val1, val2, array1, array2, body) \ - { \ - CC_ArrayZipIter cc_array_zip_iter_ea08d3e52f25883b3; \ - cc_array_zip_iter_init(&cc_array_zip_iter_ea08d3e52f25883b3, array1, array2); \ - void *val1; \ - void *val2; \ - while (cc_array_zip_iter_next(&cc_array_zip_iter_ea08d3e52f25883b3, &val1, &val2) != CC_ITER_END) \ - body \ - } - -#define DEFAULT_CAPACITY 8 -#define DEFAULT_EXPANSION_FACTOR 2 - -struct cc_array_s { - size_t size; - size_t capacity; - float exp_factor; - void **buffer; - - void *(*mem_alloc)(size_t size); - void *(*mem_calloc)(size_t blocks, size_t size); - void (*mem_free)(void *block); -}; - -static enum cc_stat expand_array_capacity(CC_Array *ar); - -/** - * Creates a new empty array and returns a status code. - * - * @param[out] out pointer to where the newly created CC_Array is to be stored - * - * @return CC_OK if the creation was successful, or CC_ERR_ALLOC if the - * memory allocation for the new CC_Array structure failed. - */ -enum cc_stat cc_array_new(CC_Array **out) { - CC_ArrayConf c; - cc_array_conf_init(&c); - return cc_array_new_conf(&c, out); -} - -/** - * Creates a new empty CC_Array based on the specified CC_ArrayConf struct and - * returns a status code. - * - * The CC_Array is allocated using the allocators specified in the CC_ArrayConf - * struct. The allocation may fail if underlying allocator fails. It may also - * fail if the values of exp_factor and capacity in the CC_ArrayConf do not meet - * the following condition: exp_factor < (CC_MAX_ELEMENTS / capacity). - * - * @param[in] conf array configuration structure - * @param[out] out pointer to where the newly created CC_Array is to be stored - * - * @return CC_OK if the creation was successful, CC_ERR_INVALID_CAPACITY if - * the above mentioned condition is not met, or CC_ERR_ALLOC if the memory - * allocation for the new CC_Array structure failed. - */ -enum cc_stat cc_array_new_conf(CC_ArrayConf const *const conf, CC_Array **out) { - float ex; - - /* The expansion factor must be greater than one for the - * array to grow */ - if (conf->exp_factor <= 1) { - ex = DEFAULT_EXPANSION_FACTOR; - } else { - ex = conf->exp_factor; - } - - /* Needed to avoid an integer overflow on the first resize and - * to easily check for any future overflows. */ - if (!conf->capacity || ex >= CC_MAX_ELEMENTS / conf->capacity) { - return CC_ERR_INVALID_CAPACITY; - } - - CC_Array *ar = (CC_Array *)conf->mem_calloc(1, sizeof(CC_Array)); - - if (!ar) { - return CC_ERR_ALLOC; - } - - void **buff = (void **)conf->mem_alloc(conf->capacity * sizeof(void *)); - - if (!buff) { - conf->mem_free(ar); - return CC_ERR_ALLOC; - } - - ar->buffer = buff; - ar->exp_factor = ex; - ar->capacity = conf->capacity; - ar->mem_alloc = conf->mem_alloc; - ar->mem_calloc = conf->mem_calloc; - ar->mem_free = conf->mem_free; - - *out = ar; - return CC_OK; -} - -/** - * Initializes the fields of the CC_ArrayConf struct to default values. - * - * @param[in, out] conf CC_ArrayConf structure that is being initialized - */ -void cc_array_conf_init(CC_ArrayConf *conf) { - conf->exp_factor = DEFAULT_EXPANSION_FACTOR; - conf->capacity = DEFAULT_CAPACITY; - conf->mem_alloc = malloc; - conf->mem_calloc = calloc; - conf->mem_free = free; -} - -/** - * Destroys the CC_Array structure, but leaves the data it used to hold intact. - * - * @param[in] ar the array that is to be destroyed - */ -void cc_array_destroy(CC_Array *ar) { - ar->mem_free(ar->buffer); - ar->mem_free(ar); -} - -/** - * Destroys the CC_Array structure along with all the data it holds. - * - * @note - * This function should not be called on a array that has some of its elements - * allocated on the stack. - * - * @param[in] ar the array that is being destroyed - */ -void cc_array_destroy_cb(CC_Array *ar, void (*cb)(void *)) { - size_t i; - for (i = 0; i < ar->size; i++) { - cb(ar->buffer[i]); - } - - cc_array_destroy(ar); -} - -/** - * Adds a new element to the CC_Array. The element is appended to the array making - * it the last element (the one with the highest index) of the CC_Array. - * - * @param[in] ar the array to which the element is being added - * @param[in] element the element that is being added - * - * @return CC_OK if the element was successfully added, CC_ERR_ALLOC if the - * memory allocation for the new element failed, or CC_ERR_MAX_CAPACITY if the - * array is already at maximum capacity. - */ -enum cc_stat cc_array_add(CC_Array *ar, void *element) { - if (ar->size >= ar->capacity) { - enum cc_stat status = expand_array_capacity(ar); - if (status != CC_OK) { - return status; - } - } - - ar->buffer[ar->size] = element; - ar->size++; - - return CC_OK; -} - -/** - * Adds a new element to the array at a specified position by shifting all - * subsequent elements by one. The specified index must be within the bounds - * of the array. This function may also fail if the memory allocation for - * the new element was unsuccessful. - * - * @param[in] ar the array to which the element is being added - * @param[in] element the element that is being added - * @param[in] index the position in the array at which the element is being - * added - * - * @return CC_OK if the element was successfully added, CC_ERR_OUT_OF_RANGE if - * the specified index was not in range, CC_ERR_ALLOC if the memory - * allocation for the new element failed, or CC_ERR_MAX_CAPACITY if the - * array is already at maximum capacity. - */ -enum cc_stat cc_array_add_at(CC_Array *ar, void *element, size_t index) { - if (index == ar->size) { - return cc_array_add(ar, element); - } - - if ((ar->size == 0 && index != 0) || index > (ar->size - 1)) { - return CC_ERR_OUT_OF_RANGE; - } - - if (ar->size >= ar->capacity) { - enum cc_stat status = expand_array_capacity(ar); - if (status != CC_OK) { - return status; - } - } - - size_t shift = (ar->size - index) * sizeof(void *); - - memmove(&(ar->buffer[index + 1]), - &(ar->buffer[index]), - shift); - - ar->buffer[index] = element; - ar->size++; - - return CC_OK; -} - -/** - * Replaces an array element at the specified index and optionally sets the out - * parameter to the value of the replaced element. The specified index must be - * within the bounds of the CC_Array. - * - * @param[in] ar array whose element is being replaced - * @param[in] element replacement element - * @param[in] index index at which the replacement element should be inserted - * @param[out] out pointer to where the replaced element is stored, or NULL if - * it is to be ignored - * - * @return CC_OK if the element was successfully replaced, or CC_ERR_OUT_OF_RANGE - * if the index was out of range. - */ -enum cc_stat cc_array_replace_at(CC_Array *ar, void *element, size_t index, void **out) { - if (index >= ar->size) { - return CC_ERR_OUT_OF_RANGE; - } - - if (out) { - *out = ar->buffer[index]; - } - - ar->buffer[index] = element; - - return CC_OK; -} - -enum cc_stat cc_array_swap_at(CC_Array *ar, size_t index1, size_t index2) { - void *tmp; - - if (index1 >= ar->size || index2 >= ar->size) { - return CC_ERR_OUT_OF_RANGE; - } - - tmp = ar->buffer[index1]; - - ar->buffer[index1] = ar->buffer[index2]; - ar->buffer[index2] = tmp; - return CC_OK; -} - -/** - * Removes the specified element from the CC_Array if such element exists and - * optionally sets the out parameter to the value of the removed element. - * - * @param[in] ar array from which the element is being removed - * @param[in] element element being removed - * @param[out] out pointer to where the removed value is stored, or NULL - * if it is to be ignored - * - * @return CC_OK if the element was successfully removed, or - * CC_ERR_VALUE_NOT_FOUND if the element was not found. - */ -enum cc_stat cc_array_remove(CC_Array *ar, void *element, void **out) { - size_t index; - enum cc_stat status = cc_array_index_of(ar, element, &index); - - if (status == CC_ERR_OUT_OF_RANGE) { - return CC_ERR_VALUE_NOT_FOUND; - } - - if (index != ar->size - 1) { - size_t block_size = (ar->size - 1 - index) * sizeof(void *); - - memmove(&(ar->buffer[index]), - &(ar->buffer[index + 1]), - block_size); - } - ar->size--; - - if (out) { - *out = element; - } - - return CC_OK; -} - -/** - * Removes a CC_Array element without preserving order and optionally sets the - * out parameter to the value of the removed element. The last element of the - * array is moved to the index of the element being removed, and the last - * element is removed. - * - * @param[in] ar the array whose last element is being removed - * @param[out] out pointer to where the removed value is stored, or NULL if it is - * to be ignored - * - * @return CC_OK if the element was successfully removed, or CC_ERR_OUT_OF_RANGE - * if the CC_Array is already empty. - */ -enum cc_stat cc_array_remove_fast(CC_Array *ar, void *element, void **out) { - size_t index = 0; - const enum cc_stat status = cc_array_index_of(ar, element, &index); - if (status != CC_OK) { - return status; - } - - if (out) { - *out = ar->buffer[index]; - } - - ar->buffer[index] = ar->buffer[ar->size - 1]; - ar->size--; - - return CC_OK; -} - -/** - * Removes an CC_Array element from the specified index and optionally sets the - * out parameter to the value of the removed element. The index must be within - * the bounds of the array. - * - * @param[in] ar the array from which the element is being removed - * @param[in] index the index of the element being removed. - * @param[out] out pointer to where the removed value is stored, - * or NULL if it is to be ignored - * - * @return CC_OK if the element was successfully removed, or CC_ERR_OUT_OF_RANGE - * if the index was out of range. - */ -enum cc_stat cc_array_remove_at(CC_Array *ar, size_t index, void **out) { - if (index >= ar->size) { - return CC_ERR_OUT_OF_RANGE; - } - - if (out) { - *out = ar->buffer[index]; - } - - if (index != ar->size - 1) { - size_t block_size = (ar->size - 1 - index) * sizeof(void *); - - memmove(&(ar->buffer[index]), - &(ar->buffer[index + 1]), - block_size); - } - ar->size--; - - return CC_OK; -} - -/** - * Removes a CC_Array element from the specified index and optionally sets the - * out parameter to the value of the removed element without preserving ordering. - * The last element of the array is moved to the index of the element being removed, - * and the last element is removed. The index must be within the bounds of the array. - * - * @param[in] ar the array from which the element is being removed - * @param[in] index the index of the element being removed. - * @param[out] out pointer to where the removed value is stored, - * or NULL if it is to be ignored - * - * @return CC_OK if the element was successfully removed, or CC_ERR_OUT_OF_RANGE - * if the index was out of range. - */ -enum cc_stat cc_array_remove_fast_at(CC_Array *ar, size_t index, void **out) { - if (index >= ar->size) { - return CC_ERR_OUT_OF_RANGE; - } - - if (out) { - *out = ar->buffer[index]; - } - - ar->buffer[index] = ar->buffer[ar->size - 1]; - ar->size--; - - return CC_OK; -} - -/** - * Removes an CC_Array element from the end of the array and optionally sets the - * out parameter to the value of the removed element. - * - * @param[in] ar the array whose last element is being removed - * @param[out] out pointer to where the removed value is stored, or NULL if it is - * to be ignored - * - * @return CC_OK if the element was successfully removed, or CC_ERR_OUT_OF_RANGE - * if the CC_Array is already empty. - */ -enum cc_stat cc_array_remove_last(CC_Array *ar, void **out) { - return cc_array_remove_at(ar, ar->size - 1, out); -} - -/** - * Removes all elements from the specified array. This function does not shrink - * the array capacity. - * - * @param[in] ar array from which all elements are to be removed - */ -void cc_array_remove_all(CC_Array *ar) { - ar->size = 0; -} - -/** - * Removes and frees all elements from the specified array. This function does - * not shrink the array capacity. - * - * @param[in] ar array from which all elements are to be removed - */ -void cc_array_remove_all_free(CC_Array *ar) { - size_t i; - for (i = 0; i < ar->size; i++) { - free(ar->buffer[i]); - } - - cc_array_remove_all(ar); -} - -/** - * Gets an CC_Array element from the specified index and sets the out parameter to - * its value. The specified index must be within the bounds of the array. - * - * @param[in] ar the array from which the element is being retrieved - * @param[in] index the index of the array element - * @param[out] out pointer to where the element is stored - * - * @return CC_OK if the element was found, or CC_ERR_OUT_OF_RANGE if the index - * was out of range. - */ -enum cc_stat cc_array_get_at(const CC_Array *ar, size_t index, void **out) { - if (index >= ar->size) { - return CC_ERR_OUT_OF_RANGE; - } - - *out = ar->buffer[index]; - return CC_OK; -} - -/** - * Gets the last element of the array or the element at the highest index - * and sets the out parameter to its value. - * - * @param[in] ar the array whose last element is being returned - * @param[out] out pointer to where the element is stored - * - * @return CC_OK if the element was found, or CC_ERR_VALUE_NOT_FOUND if the - * CC_Array is empty. - */ -enum cc_stat cc_array_get_last(const CC_Array *ar, void **out) { - if (ar->size == 0) { - return CC_ERR_VALUE_NOT_FOUND; - } - - return cc_array_get_at(ar, ar->size - 1, out); -} - -/** - * Returns the underlying array buffer. - * - * @note Any direct modification of the buffer may invalidate the CC_Array. - * - * @param[in] ar array whose underlying buffer is being returned - * - * @return array's internal buffer. - */ -const void *const *cc_array_get_buffer(CC_Array *ar) { - return (const void *const *)ar->buffer; -} - -/** - * Gets the index of the specified element. The returned index is the index - * of the first occurrence of the element starting from the beginning of the - * CC_Array. - * - * @param[in] ar array being searched - * @param[in] element the element whose index is being looked up - * @param[out] index pointer to where the index is stored - * - * @return CC_OK if the index was found, or CC_OUT_OF_RANGE if not. - */ -enum cc_stat cc_array_index_of(const CC_Array *ar, void *element, size_t *index) { - size_t i; - for (i = 0; i < ar->size; i++) { - if (ar->buffer[i] == element) { - *index = i; - return CC_OK; - } - } - return CC_ERR_OUT_OF_RANGE; -} - -/** - * Creates a subarray of the specified CC_Array, ranging from b - * index (inclusive) to e index (inclusive). The range indices - * must be within the bounds of the CC_Array, while the e index - * must be greater or equal to the b index. - * - * @note The new CC_Array is allocated using the original CC_Array's allocators - * and it also inherits the configuration of the original CC_Array. - * - * @param[in] ar array from which the subarray is being created - * @param[in] b the beginning index (inclusive) of the subarray that must be - * within the bounds of the array and must not exceed the - * the end index - * @param[in] e the end index (inclusive) of the subarray that must be within - * the bounds of the array and must be greater or equal to the - * beginning index - * @param[out] out pointer to where the new sublist is stored - * - * @return CC_OK if the subarray was successfully created, CC_ERR_INVALID_RANGE - * if the specified index range is invalid, or CC_ERR_ALLOC if the memory allocation - * for the new subarray failed. - */ -enum cc_stat cc_array_subarray(CC_Array *ar, size_t b, size_t e, CC_Array **out) { - if (b > e || e >= ar->size) { - return CC_ERR_INVALID_RANGE; - } - - CC_Array *sub_ar = (CC_Array *)ar->mem_calloc(1, sizeof(CC_Array)); - - if (!sub_ar) { - return CC_ERR_ALLOC; - } - - /* Try to allocate the buffer */ - if (!(sub_ar->buffer = (void **)ar->mem_alloc(ar->capacity * sizeof(void *)))) { - ar->mem_free(sub_ar); - return CC_ERR_ALLOC; - } - - sub_ar->mem_alloc = ar->mem_alloc; - sub_ar->mem_calloc = ar->mem_calloc; - sub_ar->mem_free = ar->mem_free; - sub_ar->size = e - b + 1; - sub_ar->capacity = sub_ar->size; - - memcpy(sub_ar->buffer, - &(ar->buffer[b]), - sub_ar->size * sizeof(void *)); - - *out = sub_ar; - return CC_OK; -} - -/** - * Creates a shallow copy of the specified CC_Array. A shallow copy is a copy of - * the CC_Array structure, but not the elements it holds. - * - * @note The new CC_Array is allocated using the original CC_Array's allocators - * and it also inherits the configuration of the original array. - * - * @param[in] ar the array to be copied - * @param[out] out pointer to where the newly created copy is stored - * - * @return CC_OK if the copy was successfully created, or CC_ERR_ALLOC if the - * memory allocation for the copy failed. - */ -enum cc_stat cc_array_copy_shallow(CC_Array *ar, CC_Array **out) { - CC_Array *copy = (CC_Array *)ar->mem_alloc(sizeof(CC_Array)); - - if (!copy) { - return CC_ERR_ALLOC; - } - - if (!(copy->buffer = (void **)ar->mem_calloc(ar->capacity, sizeof(void *)))) { - ar->mem_free(copy); - return CC_ERR_ALLOC; - } - copy->exp_factor = ar->exp_factor; - copy->size = ar->size; - copy->capacity = ar->capacity; - copy->mem_alloc = ar->mem_alloc; - copy->mem_calloc = ar->mem_calloc; - copy->mem_free = ar->mem_free; - - memcpy(copy->buffer, - ar->buffer, - copy->size * sizeof(void *)); - - *out = copy; - return CC_OK; -} - -/** - * Creates a deep copy of the specified CC_Array. A deep copy is a copy of - * both the CC_Array structure and the data it holds. - * - * @note The new CC_Array is allocated using the original CC_Array's allocators - * and it also inherits the configuration of the original CC_Array. - * - * @param[in] ar array to be copied - * @param[in] cp the copy function that should return a pointer to the copy of - * the data - * @param[out] out pointer to where the newly created copy is stored - * - * @return CC_OK if the copy was successfully created, or CC_ERR_ALLOC if the - * memory allocation for the copy failed. - */ -enum cc_stat cc_array_copy_deep(CC_Array *ar, void *(*cp)(void *), CC_Array **out) { - CC_Array *copy = (CC_Array *)ar->mem_alloc(sizeof(CC_Array)); - - if (!copy) { - return CC_ERR_ALLOC; - } - - if (!(copy->buffer = (void **)ar->mem_calloc(ar->capacity, sizeof(void *)))) { - ar->mem_free(copy); - return CC_ERR_ALLOC; - } - - copy->exp_factor = ar->exp_factor; - copy->size = ar->size; - copy->capacity = ar->capacity; - copy->mem_alloc = ar->mem_alloc; - copy->mem_calloc = ar->mem_calloc; - copy->mem_free = ar->mem_free; - - size_t i; - for (i = 0; i < copy->size; i++) { - copy->buffer[i] = cp(ar->buffer[i]); - } - - *out = copy; - - return CC_OK; -} - -/** - * Filters the CC_Array by modifying it. It removes all elements that don't - * return true on pred(element). - * - * @param[in] ar array that is to be filtered - * @param[in] pred predicate function which returns true if the element should - * be kept in the CC_Array - * - * @return CC_OK if the CC_Array was filtered successfully, or CC_ERR_OUT_OF_RANGE - * if the CC_Array is empty. - */ -enum cc_stat cc_array_filter_mut(CC_Array *ar, bool (*pred)(const void *)) { - if (ar->size == 0) { - return CC_ERR_OUT_OF_RANGE; - } - - size_t rm = 0; - size_t keep = 0; - - /* Look for clusters of non matching elements before moving - * in order to minimize the number of memmoves */ - for (size_t i = ar->size - 1; i != ((size_t)-1); i--) { - if (!pred(ar->buffer[i])) { - rm++; - continue; - } - if (rm > 0) { - if (keep > 0) { - size_t block_size = keep * sizeof(void *); - memmove(&(ar->buffer[i + 1]), - &(ar->buffer[i + 1 + rm]), - block_size); - } - ar->size -= rm; - rm = 0; - } - keep++; - } - /* Remove any remaining elements*/ - if (rm > 0) { - size_t block_size = keep * sizeof(void *); - memmove(&(ar->buffer[0]), - &(ar->buffer[rm]), - block_size); - - ar->size -= rm; - } - return CC_OK; -} - -/** - * Filters the CC_Array by creating a new CC_Array that contains all elements from the - * original CC_Array that return true on pred(element) without modifying the original - * CC_Array. - * - * @param[in] ar array that is to be filtered - * @param[in] pred predicate function which returns true if the element should - * be kept in the filtered array - * @param[out] out pointer to where the new filtered CC_Array is to be stored - * - * @return CC_OK if the CC_Array was filtered successfully, CC_ERR_OUT_OF_RANGE - * if the CC_Array is empty, or CC_ERR_ALLOC if the memory allocation for the - * new CC_Array failed. - */ -enum cc_stat cc_array_filter(CC_Array *ar, bool (*pred)(const void *), CC_Array **out) { - if (ar->size == 0) { - return CC_ERR_OUT_OF_RANGE; - } - - CC_Array *filtered = (CC_Array *)ar->mem_alloc(sizeof(CC_Array)); - - if (!filtered) { - return CC_ERR_ALLOC; - } - - if (!(filtered->buffer = (void **)ar->mem_calloc(ar->capacity, sizeof(void *)))) { - ar->mem_free(filtered); - return CC_ERR_ALLOC; - } - - filtered->exp_factor = ar->exp_factor; - filtered->size = 0; - filtered->capacity = ar->capacity; - filtered->mem_alloc = ar->mem_alloc; - filtered->mem_calloc = ar->mem_calloc; - filtered->mem_free = ar->mem_free; - - size_t f = 0; - for (size_t i = 0; i < ar->size; i++) { - if (pred(ar->buffer[i])) { - filtered->buffer[f++] = ar->buffer[i]; - filtered->size++; - } - } - *out = filtered; - - return CC_OK; -} - -/** - * Reverses the order of elements in the specified array. - * - * @param[in] ar array that is being reversed - */ -void cc_array_reverse(CC_Array *ar) { - if (ar->size == 0) { - return; - } - - size_t i; - size_t j; - for (i = 0, j = ar->size - 1; i < ar->size / 2; i++, j--) { - void *tmp = ar->buffer[i]; - ar->buffer[i] = ar->buffer[j]; - ar->buffer[j] = tmp; - } -} - -/** - * Trims the array's capacity, in other words, it shrinks the capacity to match - * the number of elements in the CC_Array, however the capacity will never shrink - * below 1. - * - * @param[in] ar array whose capacity is being trimmed - * - * @return CC_OK if the capacity was trimmed successfully, or CC_ERR_ALLOC if - * the reallocation failed. - */ -enum cc_stat cc_array_trim_capacity(CC_Array *ar) { - if (ar->size == ar->capacity) { - return CC_OK; - } - - void **new_buff = (void **)ar->mem_calloc(ar->size, sizeof(void *)); - - if (!new_buff) { - return CC_ERR_ALLOC; - } - - size_t size = ar->size < 1 ? 1 : ar->size; - - memcpy(new_buff, ar->buffer, size * sizeof(void *)); - ar->mem_free(ar->buffer); - - ar->buffer = new_buff; - ar->capacity = ar->size; - - return CC_OK; -} - -/** - * Returns the number of occurrences of the element within the specified CC_Array. - * - * @param[in] ar array that is being searched - * @param[in] element the element that is being searched for - * - * @return the number of occurrences of the element. - */ -size_t cc_array_contains(const CC_Array *ar, void *element) { - size_t o = 0; - size_t i; - for (i = 0; i < ar->size; i++) { - if (ar->buffer[i] == element) { - o++; - } - } - return o; -} - -/** - * Returns the number of occurrences of the value pointed to by e - * within the specified CC_Array. - * - * @param[in] ar array that is being searched - * @param[in] element the element that is being searched for - * @param[in] cmp comparator function which returns 0 if the values passed to it are equal - * - * @return the number of occurrences of the value. - */ -size_t cc_array_contains_value(const CC_Array *ar, void *element, int (*cmp)(const void *, const void *)) { - size_t o = 0; - size_t i; - for (i = 0; i < ar->size; i++) { - if (cmp(element, ar->buffer[i]) == 0) { - o++; - } - } - return o; -} - -/** - * Returns the size of the specified CC_Array. The size of the array is the - * number of elements contained within the CC_Array. - * - * @param[in] ar array whose size is being returned - * - * @return the the number of element within the CC_Array. - */ -size_t cc_array_size(const CC_Array *ar) { - return ar->size; -} - -/** - * Returns the capacity of the specified CC_Array. The capacity of the CC_Array is - * the maximum number of elements an CC_Array can hold before it has to be resized. - * - * @param[in] ar array whose capacity is being returned - * - * @return the capacity of the CC_Array. - */ -size_t cc_array_capacity(const CC_Array *ar) { - return ar->capacity; -} - -/** - * Sorts the specified array. - * - * @note - * Pointers passed to the comparator function will be pointers to the array - * elements that are of type (void*) ie. void**. So an extra step of - * dereferencing will be required before the data can be used for comparison: - * eg. my_type e = *(*((my_type**) ptr));. - * - * @code - * enum cc_stat mycmp(const void *e1, const void *e2) { - * MyType el1 = *(*((enum cc_stat**) e1)); - * MyType el2 = *(*((enum cc_stat**) e2)); - * - * if (el1 < el2) return -1; - * if (el1 > el2) return 1; - * return 0; - * } - * - * ... - * - * cc_array_sort(array, mycmp); - * @endcode - * - * @param[in] ar array to be sorted - * @param[in] cmp the comparator function that must be of type - * enum cc_stat cmp(const void e1*, const void e2*) that - * returns < 0 if the first element goes before the second, - * 0 if the elements are equal and > 0 if the second goes - * before the first - */ -void cc_array_sort(CC_Array *ar, int (*cmp)(const void *, const void *)) { - qsort(ar->buffer, ar->size, sizeof(void *), cmp); -} - -/** - * Expands the CC_Array capacity. This might fail if the the new buffer - * cannot be allocated. In case the expansion would overflow the index - * range, a maximum capacity buffer is allocated instead. If the capacity - * is already at the maximum capacity, no new buffer is allocated. - * - * @param[in] ar array whose capacity is being expanded - * - * @return CC_OK if the buffer was expanded successfully, CC_ERR_ALLOC if - * the memory allocation for the new buffer failed, or CC_ERR_MAX_CAPACITY - * if the array is already at maximum capacity. - */ -static enum cc_stat expand_array_capacity(CC_Array *ar) { - if (ar->capacity == CC_MAX_ELEMENTS) { - return CC_ERR_MAX_CAPACITY; - } - - size_t new_capacity = (size_t)(ar->capacity * ar->exp_factor); - - /* As long as the capacity is greater that the expansion factor - * at the point of overflow, this is check is valid. */ - if (new_capacity <= ar->capacity) { - ar->capacity = CC_MAX_ELEMENTS; - } else { - ar->capacity = new_capacity; - } - - void **new_buff = (void **)ar->mem_alloc(ar->capacity * sizeof(void *)); - - if (!new_buff) { - return CC_ERR_ALLOC; - } - - memcpy(new_buff, ar->buffer, ar->size * sizeof(void *)); - - ar->mem_free(ar->buffer); - ar->buffer = new_buff; - - return CC_OK; -} - -/** - * Applies the function fn to each element of the CC_Array. - * - * @param[in] ar array on which this operation is performed - * @param[in] fn operation function that is to be invoked on each CC_Array - * element - */ -void cc_array_map(CC_Array *ar, void (*fn)(void *e)) { - size_t i; - for (i = 0; i < ar->size; i++) { - fn(ar->buffer[i]); - } -} - -/** - * A fold/reduce function that collects all of the elements in the array - * together. For example, if we have an array of [a,b,c...] the end result - * will be (...((a+b)+c)+...). - * - * @param[in] ar the array on which this operation is performed - * @param[in] fn the operation function that is to be invoked on each array - * element - * @param[in] result the pointer which will collect the end result - */ -void cc_array_reduce(CC_Array *ar, void (*fn)(void *, void *, void *), void *result) { - if (ar->size == 1) { - fn(ar->buffer[0], NULL, result); - return; - } - if (ar->size > 1) { - fn(ar->buffer[0], ar->buffer[1], result); - } - - for (size_t i = 2; i < ar->size; i++) { - fn(result, ar->buffer[i], result); - } -} - -/** - * Initializes the iterator. - * - * @param[in] iter the iterator that is being initialized - * @param[in] ar the array to iterate over - */ -void cc_array_iter_init(CC_ArrayIter *iter, CC_Array *ar) { - iter->ar = ar; - iter->index = 0; - iter->last_removed = false; -} - -/** - * Advances the iterator and sets the out parameter to the value of the - * next element in the sequence. - * - * @param[in] iter the iterator that is being advanced - * @param[out] out pointer to where the next element is set - * - * @return CC_OK if the iterator was advanced, or CC_ITER_END if the - * end of the CC_Array has been reached. - */ -enum cc_stat cc_array_iter_next(CC_ArrayIter *iter, void **out) { - if (iter->index >= iter->ar->size) { - return CC_ITER_END; - } - - *out = iter->ar->buffer[iter->index]; - - iter->index++; - iter->last_removed = false; - - return CC_OK; -} - -/** - * Removes the last returned element by cc_array_iter_next() - * function without invalidating the iterator and optionally sets the out - * parameter to the value of the removed element. - * - * @note This function should only ever be called after a call to - * cc_array_iter_next(). - - * @param[in] iter the iterator on which this operation is being performed - * @param[out] out pointer to where the removed element is stored, or NULL - * if it is to be ignored - * - * @return CC_OK if the element was successfully removed, or - * CC_ERR_VALUE_NOT_FOUND. - */ -enum cc_stat cc_array_iter_remove(CC_ArrayIter *iter, void **out) { - enum cc_stat status = CC_ERR_VALUE_NOT_FOUND; - - if (!iter->last_removed) { - status = cc_array_remove_at(iter->ar, iter->index - 1, out); - if (status != CC_OK) { - return status; - } - - iter->last_removed = true; - if (iter->index > 0) { - iter->index--; - } - } - return status; -} - -/** - * Removes the last returned element by cc_array_iter_next() - * function without invalidating the iterator and optionally sets the out - * parameter to the value of the removed element. The order of the array - * is not preserved, the last element of the array is moved to the index - * of the last returned element and the last element is removed. - * - * @note This function should only ever be called after a call to - * cc_array_iter_next(). - - * @param[in] iter the iterator on which this operation is being performed - * @param[out] out pointer to where the removed element is stored, or NULL - * if it is to be ignored - * - * @return CC_OK if the element was successfully removed, or - * CC_ERR_VALUE_NOT_FOUND. - */ -enum cc_stat cc_array_iter_remove_fast(CC_ArrayIter *iter, void **out) { - enum cc_stat status = CC_ERR_VALUE_NOT_FOUND; - - if (!iter->last_removed) { - status = cc_array_remove_fast_at(iter->ar, iter->index - 1, out); - if (status != CC_OK) { - return status; - } - - iter->last_removed = true; - if (iter->index > 0) { - iter->index--; - } - } - return status; -} - -/** - * Adds a new element to the CC_Array after the last returned element by - * cc_array_iter_next() function without invalidating the - * iterator. - * - * @note This function should only ever be called after a call to - * cc_array_iter_next(). - * - * @param[in] iter the iterator on which this operation is being performed - * @param[in] element the element being added - * - * @return CC_OK if the element was successfully added, CC_ERR_ALLOC if the - * memory allocation for the new element failed, or CC_ERR_MAX_CAPACITY if - * the array is already at maximum capacity. - */ -enum cc_stat cc_array_iter_add(CC_ArrayIter *iter, void *element) { - return cc_array_add_at(iter->ar, element, iter->index++); -} - -/** - * Replaces the last returned element by cc_array_iter_next() - * with the specified element and optionally sets the out parameter to - * the value of the replaced element. - * - * @note This function should only ever be called after a call to - * cc_array_iter_next(). - * - * @param[in] iter the iterator on which this operation is being performed - * @param[in] element the replacement element - * @param[out] out pointer to where the replaced element is stored, or NULL - * if it is to be ignored - * - * @return CC_OK if the element was replaced successfully, or - * CC_ERR_OUT_OF_RANGE. - */ -enum cc_stat cc_array_iter_replace(CC_ArrayIter *iter, void *element, void **out) { - return cc_array_replace_at(iter->ar, element, iter->index - 1, out); -} - -/** - * Returns the index of the last returned element by cc_array_iter_next() - * . - * - * @note - * This function should not be called before a call to cc_array_iter_next() - * . - * - * @param[in] iter the iterator on which this operation is being performed - * - * @return the index. - */ -size_t cc_array_iter_index(CC_ArrayIter *iter) { - return iter->index - 1; -} - -/** - * Initializes the zip iterator. - * - * @param[in] iter iterator that is being initialized - * @param[in] ar1 first array - * @param[in] ar2 second array - */ -void cc_array_zip_iter_init(CC_ArrayZipIter *iter, CC_Array *ar1, CC_Array *ar2) { - iter->ar1 = ar1; - iter->ar2 = ar2; - iter->index = 0; - iter->last_removed = false; -} - -/** - * Outputs the next element pair in the sequence and advances the iterator. - * - * @param[in] iter iterator that is being advanced - * @param[out] out1 output of the first array element - * @param[out] out2 output of the second array element - * - * @return CC_OK if a next element pair is returned, or CC_ITER_END if the end of one - * of the arrays has been reached. - */ -enum cc_stat cc_array_zip_iter_next(CC_ArrayZipIter *iter, void **out1, void **out2) { - if (iter->index >= iter->ar1->size || iter->index >= iter->ar2->size) { - return CC_ITER_END; - } - - *out1 = iter->ar1->buffer[iter->index]; - *out2 = iter->ar2->buffer[iter->index]; - - iter->index++; - iter->last_removed = false; - - return CC_OK; -} - -/** - * Removes and outputs the last returned element pair by cc_array_zip_iter_next() - * without invalidating the iterator. - * - * @param[in] iter iterator on which this operation is being performed - * @param[out] out1 output of the removed element from the first array - * @param[out] out2 output of the removed element from the second array - * - * @return CC_OK if the element was successfully removed, CC_ERR_OUT_OF_RANGE if the - * state of the iterator is invalid, or CC_ERR_VALUE_NOT_FOUND if the element was - * already removed. - */ -enum cc_stat cc_array_zip_iter_remove(CC_ArrayZipIter *iter, void **out1, void **out2) { - if ((iter->index - 1) >= iter->ar1->size || (iter->index - 1) >= iter->ar2->size) { - return CC_ERR_OUT_OF_RANGE; - } - - if (!iter->last_removed) { - cc_array_remove_at(iter->ar1, iter->index - 1, out1); - cc_array_remove_at(iter->ar2, iter->index - 1, out2); - iter->last_removed = true; - return CC_OK; - } - return CC_ERR_VALUE_NOT_FOUND; -} - -/** - * Adds a new element pair to the arrays after the last returned element pair by - * cc_array_zip_iter_next() and immediately before an element pair - * that would be returned by a subsequent call to cc_array_zip_iter_next() - * without invalidating the iterator. - * - * @param[in] iter iterator on which this operation is being performed - * @param[in] e1 element added to the first array - * @param[in] e2 element added to the second array - * - * @return CC_OK if the element pair was successfully added to the arrays, or - * CC_ERR_ALLOC if the memory allocation for the new elements failed. - */ -enum cc_stat cc_array_zip_iter_add(CC_ArrayZipIter *iter, void *e1, void *e2) { - size_t index = iter->index++; - CC_Array *ar1 = iter->ar1; - CC_Array *ar2 = iter->ar2; - - /* Make sure both array buffers have room */ - if ((ar1->size == ar1->capacity && (expand_array_capacity(ar1) != CC_OK)) || - (ar2->size == ar2->capacity && (expand_array_capacity(ar2) != CC_OK))) { - return CC_ERR_ALLOC; - } - - cc_array_add_at(ar1, e1, index); - cc_array_add_at(ar2, e2, index); - - return CC_OK; -} - -/** - * Replaces the last returned element pair by cc_array_zip_iter_next() - * with the specified replacement element pair. - * - * @param[in] iter iterator on which this operation is being performed - * @param[in] e1 first array's replacement element - * @param[in] e2 second array's replacement element - * @param[out] out1 output of the replaced element from the first array - * @param[out] out2 output of the replaced element from the second array - * - * @return CC_OK if the element was successfully replaced, or CC_ERR_OUT_OF_RANGE. - */ -enum cc_stat cc_array_zip_iter_replace(CC_ArrayZipIter *iter, void *e1, void *e2, void **out1, void **out2) { - if ((iter->index - 1) >= iter->ar1->size || (iter->index - 1) >= iter->ar2->size) { - return CC_ERR_OUT_OF_RANGE; - } - - cc_array_replace_at(iter->ar1, e1, iter->index - 1, out1); - cc_array_replace_at(iter->ar2, e2, iter->index - 1, out2); - - return CC_OK; -} - -/** - * Returns the index of the last returned element pair by cc_array_zip_iter_next(). - * - * @param[in] iter iterator on which this operation is being performed - * - * @return current iterator index. - */ -size_t cc_array_zip_iter_index(CC_ArrayZipIter *iter) { - return iter->index - 1; -} - -size_t cc_array_struct_size() { - return sizeof(CC_Array); -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/pufferlib/ocean/impulse_wars/include/cc_common.h b/pufferlib/ocean/impulse_wars/include/cc_common.h deleted file mode 100644 index 174046064..000000000 --- a/pufferlib/ocean/impulse_wars/include/cc_common.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Collections-C - * Copyright (C) 2013-2014 Srđan Panić - * - * This file is part of Collections-C. - * - * Collections-C is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Collections-C is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Collections-C. If not, see . - */ - -#ifndef CC_COMMON_H -#define CC_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include - -#ifdef ARCH_64 -#define MAX_POW_TWO (((size_t)1) << 63) -#else -#define MAX_POW_TWO (((size_t)1) << 31) -#endif /* ARCH_64 */ - -enum cc_stat { - CC_OK = 0, - - CC_ERR_ALLOC = 1, - CC_ERR_INVALID_CAPACITY = 2, - CC_ERR_INVALID_RANGE = 3, - CC_ERR_MAX_CAPACITY = 4, - CC_ERR_KEY_NOT_FOUND = 6, - CC_ERR_VALUE_NOT_FOUND = 7, - CC_ERR_OUT_OF_RANGE = 8, - - CC_ITER_END = 9, -}; - -#define CC_MAX_ELEMENTS ((size_t) - 2) - -#if defined(_MSC_VER) - -#define INLINE __inline -#define FORCE_INLINE __forceinline - -#else - -#define INLINE inline -#define FORCE_INLINE inline __attribute__((always_inline)) - -#endif /* _MSC_VER */ - -int cc_common_cmp_str(const void *key1, const void *key2); - -#define CC_CMP_STRING cc_common_cmp_str - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/pufferlib/ocean/impulse_wars/include/dlmalloc.h b/pufferlib/ocean/impulse_wars/include/dlmalloc.h deleted file mode 100644 index 4ef7c9cfd..000000000 --- a/pufferlib/ocean/impulse_wars/include/dlmalloc.h +++ /dev/null @@ -1,6264 +0,0 @@ -/* - Default header file for malloc-2.7.2, written by Doug Lea - and released to the public domain. Use, modify, and redistribute - this code without permission or acknowledgement in any way you wish. - Send questions, comments, complaints, performance data, etc to - dl@cs.oswego.edu. - - last update: Sun Feb 25 18:38:11 2001 Doug Lea (dl at gee) - - This header is for ANSI C/C++ only. You can set either of - the following #defines before including: - - * If USE_DL_PREFIX is defined, it is assumed that malloc.c - was also compiled with this option, so all routines - have names starting with "dl". - - * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this - file will be #included AFTER . This is needed only if - your system defines a struct mallinfo that is incompatible with the - standard one declared here. Otherwise, you can include this file - INSTEAD of your system system . At least on ANSI, all - declarations should be compatible with system versions -*/ - -#ifndef MALLOC_270_H -#define MALLOC_270_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include /* for size_t */ - -#define USE_DL_PREFIX - -/* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available. Additionally, on failure, errno is - set to ENOMEM on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. The minimum size - is 16 bytes on most 32bit systems, and either 24 or 32 bytes on - 64bit systems, depending on internal size and alignment restrictions. - - On most systems, size_t is an unsigned type. Calls with values of n - that appear "negative" when signed are interpreted as requests for - huge amounts of space, which will most often fail. - - The maximum allowed value of n differs across systems, but is in all - cases less (typically by 8K) than the maximum representable value of - a size_t. Requests greater than this value result in failure. -*/ - -#ifndef USE_DL_PREFIX -void *malloc(size_t); -#else -void *dlmalloc(size_t); -#endif - -/* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. It can have arbitrary (and bad!) - effects if p has already been freed or was not obtained via malloc. - - Unless disabled using mallopt, freeing very large spaces will, - when possible, automatically trigger operations that give - back unused memory to the system, thus reducing program footprint. -*/ -#ifndef USE_DL_PREFIX -void free(void *); -#else -void dlfree(void *); -#endif - -/* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. -*/ -#ifndef USE_DL_PREFIX -void *calloc(size_t, size_t); -#else -void *dlcalloc(size_t, size_t); -#endif - -/* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p when possible, otherwise it employs the - equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. Unless the #define - REALLOC_ZERO_BYTES_FREES is set, realloc with a size argument of - zero (re)allocates a minimum-sized chunk. - - Large chunks that were internally obtained via mmap will always - be reallocated using malloc-copy-free sequences unless - the system supports MREMAP (currently only linux). - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. -*/ - -#ifndef USE_DL_PREFIX -void *realloc(void *, size_t); -#else -void *dlrealloc(void *, size_t); -#endif - -/* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. -*/ - -#ifndef USE_DL_PREFIX -void *memalign(size_t, size_t); -#else -void *dlmemalign(size_t, size_t); -#endif - -/* - valloc(size_t n); - Allocates a page-aligned chunk of at least n bytes. - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. -*/ - -#ifndef USE_DL_PREFIX -void *valloc(size_t); -#else -void *dlvalloc(size_t); -#endif - -/* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements, each of which can hold contents of size - elem_size. Each element starts out cleared, and can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use regular calloc and assign pointers into this - space to represent elements. (In this case though, you cannot - independently free elements.) - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) return 0; // failure - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } -*/ - -#ifndef USE_DL_PREFIX -void **independent_calloc(size_t, size_t, void **); -#else -void **dlindependent_calloc(size_t, size_t, void **); -#endif - -/* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use a single regular malloc, and assign pointers at - particular offsets in the aggregate space. (In this case though, you - cannot independently free elements.) - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. -*/ - -#ifndef USE_DL_PREFIX -void **independent_comalloc(size_t, size_t *, void **); -#else -void **dlindependent_comalloc(size_t, size_t *, void **); -#endif - -/* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ - -#ifndef USE_DL_PREFIX -void *pvalloc(size_t); -#else -void *dlpvalloc(size_t); -#endif - -/* - cfree(void* p); - Equivalent to free(p). - - cfree is needed/defined on some systems that pair it with calloc, - for odd historical reasons (such as: cfree is used in example - code in the first edition of K&R). -*/ - -#ifndef USE_DL_PREFIX -void cfree(void *); -#else -void dlcfree(void *); -#endif - -/* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative - arguments to sbrk) if there is unused memory at the `high' end of - the malloc pool. You can call this after freeing large blocks of - memory to potentially reduce the system-level memory requirements - of a program. However, it cannot guarantee to reduce memory. Under - some allocation patterns, some large free blocks of memory will be - locked between two used chunks, so they cannot be given back to - the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, - only the minimum amount of memory to maintain internal data - structures will be left (one page or less). Non-zero arguments - can be supplied to maintain enough trailing space to service - future expected allocations without having to re-obtain memory - from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. - On systems that do not support "negative sbrks", it will always - return 0. -*/ - -#ifndef USE_DL_PREFIX -int malloc_trim(size_t); -#else -int dlmalloc_trim(size_t); -#endif - -/* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in an allocated - chunk, which may be more than you requested (although often not) due - to alignment and minimum size constraints. You can use this many - bytes without worrying about overwriting other allocated - objects. This is not a particularly great programming practice. But - malloc_usable_size can be more useful in debugging and assertions, - for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); -*/ - -#ifndef USE_DL_PREFIX -size_t malloc_usable_size(void *); -#else -size_t dlmalloc_usable_size(void *); -#endif - -/* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. -*/ - -#ifndef USE_DL_PREFIX -void malloc_stats(void); -#else -void dlmalloc_stats(void); -#endif - -/* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: the number of fastbin blocks (i.e., small chunks that - have been freed but not use resused or consolidated) - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: total bytes held in fastbin blocks - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - The names of some of these fields don't bear much relation with - their contents because this struct was defined as standard in - SVID/XPG so reflects the malloc implementation that was then used - in SystemV Unix. - - The original SVID version of this struct, defined on most systems - with mallinfo, declares all fields as ints. But some others define - as unsigned long. If your system defines the fields using a type of - different width than listed here, you should #include your system - version before including this file. The struct declaration is - suppressed if _MALLOC_H is defined (which is done in most system - malloc.h files). You can also suppress it by defining - HAVE_USR_INCLUDE_MALLOC_H. - - Because these fields are ints, but internal bookkeeping is done with - unsigned longs, the reported values may appear as negative, and may - wrap around zero and thus be inaccurate. -*/ - -#ifndef HAVE_USR_INCLUDE_MALLOC_H -#ifndef _MALLOC_H -struct mallinfo { - int arena; - int ordblks; - int smblks; - int hblks; - int hblkhd; - int usmblks; - int fsmblks; - int uordblks; - int fordblks; - int keepcost; -}; -#endif -#endif - -#ifndef USE_DL_PREFIX -struct mallinfo mallinfo(void); -#else -struct mallinfo mallinfo(void); -#endif - -/* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. SVID/XPG defines four standard param numbers for mallopt, - normally defined in malloc.h. Only one of these (M_MXFAST) is used - in this malloc. The others (M_NLBLKS, M_GRAIN, M_KEEP) don't apply, - so setting them has no effect. But this malloc also supports four - other options in mallopt. See below for details. Briefly, supported - parameters are as follows (listed defaults are for "typical" - configurations). - - Symbol param # default allowed param values - M_MXFAST 1 64 0-80 (0 disables fastbins) - M_TRIM_THRESHOLD -1 128*1024 any (-1U disables trimming) - M_TOP_PAD -2 0 any - M_MMAP_THRESHOLD -3 128*1024 any (or 0 if no MMAP support) - M_MMAP_MAX -4 65536 any (0 disables use of mmap) -*/ - -#ifndef USE_DL_PREFIX -int mallopt(int, int); -#else -int dlmallopt(int, int); -#endif - -/* Descriptions of tuning options */ - -/* - M_MXFAST is the maximum request size used for "fastbins", special bins - that hold returned chunks without consolidating their spaces. This - enables future requests for chunks of the same size to be handled - very quickly, but can increase fragmentation, and thus increase the - overall memory footprint of a program. - - This malloc manages fastbins very conservatively yet still - efficiently, so fragmentation is rarely a problem for values less - than or equal to the default. The maximum supported value of MXFAST - is 80. You wouldn't want it any higher than this anyway. Fastbins - are designed especially for use with many small structs, objects or - strings -- the default handles structs/objects/arrays with sizes up - to 8 4byte fields, or small strings representing words, tokens, - etc. Using fastbins for larger objects normally worsens - fragmentation without improving speed. - - You can reduce M_MXFAST to 0 to disable all use of fastbins. This - causes the malloc algorithm to be a closer approximation of - fifo-best-fit in all cases, not just for larger requests, but will - generally cause it to be slower. -*/ - -#ifndef M_MXFAST -#define M_MXFAST 1 -#endif - -/* - M_TRIM_THRESHOLD is the maximum amount of unused top-most memory - to keep before releasing via malloc_trim in free(). - - Automatic trimming is mainly useful in long-lived programs. - Because trimming via sbrk can be slow on some systems, and can - sometimes be wasteful (in cases where programs immediately - afterward allocate more large chunks) the value should be high - enough so that your overall system performance would improve by - releasing this much memory. - - The trim threshold and the mmap control parameters (see below) - can be traded off with one another. Trimming and mmapping are - two different ways of releasing unused memory back to the - system. Between these two, it is often possible to keep - system-level demands of a long-lived program down to a bare - minimum. For example, in one test suite of sessions measuring - the XF86 X server on Linux, using a trim threshold of 128K and a - mmap threshold of 192K led to near-minimal long term resource - consumption. - - If you are using this malloc in a long-lived program, it should - pay to experiment with these values. As a rough guide, you - might set to a value close to the average size of a process - (program) running on your system. Releasing this much memory - would allow such a process to run in memory. Generally, it's - worth it to tune for trimming rather tham memory mapping when a - program undergoes phases where several large chunks are - allocated and released in ways that can reuse each other's - storage, perhaps mixed with phases where there are no such - chunks at all. And in well-behaved long-lived programs, - controlling release of large blocks via trimming versus mapping - is usually faster. - - However, in most programs, these parameters serve mainly as - protection against the system-level effects of carrying around - massive amounts of unneeded memory. Since frequent calls to - sbrk, mmap, and munmap otherwise degrade performance, the default - parameters are set to relatively high values that serve only as - safeguards. - - The trim value It must be greater than page size to have any useful - effect. To disable trimming completely, you can set to - (unsigned long)(-1) - - Trim settings interact with fastbin (MXFAST) settings: Unless - compiled with TRIM_FASTBINS defined, automatic trimming never takes - place upon freeing a chunk with size less than or equal to - MXFAST. Trimming is instead delayed until subsequent freeing of - larger chunks. However, you can still force an attempted trim by - calling malloc_trim. - - Also, trimming is not generally possible in cases where - the main arena is obtained via mmap. - - Note that the trick some people use of mallocing a huge space and - then freeing it at program startup, in an attempt to reserve system - memory, doesn't have the intended effect under automatic trimming, - since that memory will immediately be returned to the system. -*/ - -#define M_TRIM_THRESHOLD -1 - -/* - M_TOP_PAD is the amount of extra `padding' space to allocate or - retain whenever sbrk is called. It is used in two ways internally: - - * When sbrk is called to extend the top of the arena to satisfy - a new malloc request, this much padding is added to the sbrk - request. - - * When malloc_trim is called automatically from free(), - it is used as the `pad' argument. - - In both cases, the actual amount of padding is rounded - so that the end of the arena is always a system page boundary. - - The main reason for using padding is to avoid calling sbrk so - often. Having even a small pad greatly reduces the likelihood - that nearly every malloc request during program start-up (or - after trimming) will invoke sbrk, which needlessly wastes - time. - - Automatic rounding-up to page-size units is normally sufficient - to avoid measurable overhead, so the default is 0. However, in - systems where sbrk is relatively slow, it can pay to increase - this value, at the expense of carrying around more memory than - the program needs. -*/ - -#define M_TOP_PAD -2 - -/* - M_MMAP_THRESHOLD is the request size threshold for using mmap() - to service a request. Requests of at least this size that cannot - be allocated using already-existing space will be serviced via mmap. - (If enough normal freed space already exists it is used instead.) - - Using mmap segregates relatively large chunks of memory so that - they can be individually obtained and released from the host - system. A request serviced through mmap is never reused by any - other request (at least not directly; the system may just so - happen to remap successive requests to the same locations). - - Segregating space in this way has the benefits that: - - 1. Mmapped space can ALWAYS be individually released back - to the system, which helps keep the system level memory - demands of a long-lived program low. - 2. Mapped memory can never become `locked' between - other chunks, as can happen with normally allocated chunks, which - means that even trimming via malloc_trim would not release them. - 3. On some systems with "holes" in address spaces, mmap can obtain - memory that sbrk cannot. - - However, it has the disadvantages that: - - 1. The space cannot be reclaimed, consolidated, and then - used to service later requests, as happens with normal chunks. - 2. It can lead to more wastage because of mmap page alignment - requirements - 3. It causes malloc performance to be more dependent on host - system memory management support routines. - - The advantages of mmap nearly always outweigh disadvantages for - "large" chunks, but the value of "large" varies across systems. The - default is an empirically derived value that works well in most - systems. -*/ - -#define M_MMAP_THRESHOLD -3 - -/* - M_MMAP_MAX is the maximum number of requests to simultaneously - service using mmap. This parameter exists because - some systems have a limited number of internal tables for - use by mmap, and using more than a few of them may degrade - performance. - - The default is set to a value that serves only as a safeguard. - Setting to 0 disables use of mmap for servicing large requests. If - mmap is not supported on a system, the default value is 0, and - attempts to set it to non-zero values in mallopt will fail. -*/ - -#define M_MMAP_MAX -4 - -/* Unused SVID2/XPG mallopt options, listed for completeness */ - -#ifndef M_NBLKS -#define M_NLBLKS 2 /* UNUSED in this malloc */ -#endif -#ifndef M_GRAIN -#define M_GRAIN 3 /* UNUSED in this malloc */ -#endif -#ifndef M_KEEP -#define M_KEEP 4 /* UNUSED in this malloc */ -#endif - -/* - Some malloc.h's declare alloca, even though it is not part of malloc. -*/ - -#ifndef _ALLOCA_H -extern void *alloca(size_t); -#endif - -/* - This is a version (aka dlmalloc) of malloc/free/realloc written by - Doug Lea and released to the public domain. Use, modify, and - redistribute this code without permission or acknowledgement in any - way you wish. Send questions, comments, complaints, performance - data, etc to dl@cs.oswego.edu - -* VERSION 2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) - - Note: There may be an updated version of this malloc obtainable at - ftp://gee.cs.oswego.edu/pub/misc/malloc.c - Check before installing! - -* Quickstart - - This library is all in one file to simplify the most common usage: - ftp it, compile it (-O), and link it into another program. All - of the compile-time options default to reasonable values for use on - most unix platforms. Compile -DWIN32 for reasonable defaults on windows. - You might later want to step through various compile-time and dynamic - tuning options. - - For convenience, an include file for code using this malloc is at: - ftp://gee.cs.oswego.edu/pub/misc/malloc-2.7.1.h - You don't really need this .h file unless you call functions not - defined in your system include files. The .h file contains only the - excerpts from this file needed for using this malloc on ANSI C/C++ - systems, so long as you haven't changed compile-time options about - naming and tuning parameters. If you do, then you can create your - own malloc.h that does include all settings by cutting at the point - indicated below. - -* Why use this malloc? - - This is not the fastest, most space-conserving, most portable, or - most tunable malloc ever written. However it is among the fastest - while also being among the most space-conserving, portable and tunable. - Consistent balance across these factors results in a good general-purpose - allocator for malloc-intensive programs. - - The main properties of the algorithms are: - * For large (>= 512 bytes) requests, it is a pure best-fit allocator, - with ties normally decided via FIFO (i.e. least recently used). - * For small (<= 64 bytes by default) requests, it is a caching - allocator, that maintains pools of quickly recycled chunks. - * In between, and for combinations of large and small requests, it does - the best it can trying to meet both goals at once. - * For very large requests (>= 128KB by default), it relies on system - memory mapping facilities, if supported. - - For a longer but slightly out of date high-level description, see - http://gee.cs.oswego.edu/dl/html/malloc.html - - You may already by default be using a C library containing a malloc - that is based on some version of this malloc (for example in - linux). You might still want to use the one in this file in order to - customize settings or to avoid overheads associated with library - versions. - -* Contents, described in more detail in "description of public routines" below. - - Standard (ANSI/SVID/...) functions: - malloc(size_t n); - calloc(size_t n_elements, size_t element_size); - free(Void_t* p); - realloc(Void_t* p, size_t n); - memalign(size_t alignment, size_t n); - valloc(size_t n); - mallinfo() - mallopt(int parameter_number, int parameter_value) - - Additional functions: - independent_calloc(size_t n_elements, size_t size, Void_t* chunks[]); - independent_comalloc(size_t n_elements, size_t sizes[], Void_t* chunks[]); - pvalloc(size_t n); - cfree(Void_t* p); - malloc_trim(size_t pad); - malloc_usable_size(Void_t* p); - malloc_stats(); - -* Vital statistics: - - Supported pointer representation: 4 or 8 bytes - Supported size_t representation: 4 or 8 bytes - Note that size_t is allowed to be 4 bytes even if pointers are 8. - You can adjust this by defining INTERNAL_SIZE_T - - Alignment: 2 * sizeof(size_t) (default) - (i.e., 8 byte alignment with 4byte size_t). This suffices for - nearly all current machines and C compilers. However, you can - define MALLOC_ALIGNMENT to be wider than this if necessary. - - Minimum overhead per allocated chunk: 4 or 8 bytes - Each malloced chunk has a hidden word of overhead holding size - and status information. - - Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead) - 8-byte ptrs: 24/32 bytes (including, 4/8 overhead) - - When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte - ptrs but 4 byte size) or 24 (for 8/8) additional bytes are - needed; 4 (8) for a trailing size field and 8 (16) bytes for - free list pointers. Thus, the minimum allocatable size is - 16/24/32 bytes. - - Even a request for zero bytes (i.e., malloc(0)) returns a - pointer to something of the minimum allocatable size. - - The maximum overhead wastage (i.e., number of extra bytes - allocated than were requested in malloc) is less than or equal - to the minimum size, except for requests >= mmap_threshold that - are serviced via mmap(), where the worst case wastage is 2 * - sizeof(size_t) bytes plus the remainder from a system page (the - minimal mmap unit); typically 4096 or 8192 bytes. - - Maximum allocated size: 4-byte size_t: 2^32 minus about two pages - 8-byte size_t: 2^64 minus about two pages - - It is assumed that (possibly signed) size_t values suffice to - represent chunk sizes. `Possibly signed' is due to the fact - that `size_t' may be defined on a system as either a signed or - an unsigned type. The ISO C standard says that it must be - unsigned, but a few systems are known not to adhere to this. - Additionally, even when size_t is unsigned, sbrk (which is by - default used to obtain memory from system) accepts signed - arguments, and may not be able to handle size_t-wide arguments - with negative sign bit. Generally, values that would - appear as negative after accounting for overhead and alignment - are supported only via mmap(), which does not have this - limitation. - - Requests for sizes outside the allowed range will perform an optional - failure action and then return null. (Requests may also - also fail because a system is out of memory.) - - Thread-safety: NOT thread-safe unless USE_MALLOC_LOCK defined - - When USE_MALLOC_LOCK is defined, wrappers are created to - surround every public call with either a pthread mutex or - a win32 spinlock (depending on WIN32). This is not - especially fast, and can be a major bottleneck. - It is designed only to provide minimal protection - in concurrent environments, and to provide a basis for - extensions. If you are using malloc in a concurrent program, - you would be far better off obtaining ptmalloc, which is - derived from a version of this malloc, and is well-tuned for - concurrent programs. (See http://www.malloc.de) Note that - even when USE_MALLOC_LOCK is defined, you can can guarantee - full thread-safety only if no threads acquire memory through - direct calls to MORECORE or other system-level allocators. - - Compliance: I believe it is compliant with the 1997 Single Unix Specification - (See http://www.opennc.org). Also SVID/XPG, ANSI C, and probably - others as well. - -* Synopsis of compile-time options: - - People have reported using previous versions of this malloc on all - versions of Unix, sometimes by tweaking some of the defines - below. It has been tested most extensively on Solaris and - Linux. It is also reported to work on WIN32 platforms. - People also report using it in stand-alone embedded systems. - - The implementation is in straight, hand-tuned ANSI C. It is not - at all modular. (Sorry!) It uses a lot of macros. To be at all - usable, this code should be compiled using an optimizing compiler - (for example gcc -O3) that can simplify expressions and control - paths. (FAQ: some macros import variables as arguments rather than - declare locals because people reported that some debuggers - otherwise get confused.) - - OPTION DEFAULT VALUE - - Compilation Environment options: - - __STD_C derived from C compiler defines - WIN32 NOT defined - HAVE_MEMCPY defined - USE_MEMCPY 1 if HAVE_MEMCPY is defined - HAVE_MMAP defined as 1 - MMAP_CLEARS 1 - HAVE_MREMAP 0 unless linux defined - malloc_getpagesize derived from system #includes, or 4096 if not - HAVE_USR_INCLUDE_MALLOC_H NOT defined - LACKS_UNISTD_H NOT defined unless WIN32 - LACKS_SYS_PARAM_H NOT defined unless WIN32 - LACKS_SYS_MMAN_H NOT defined unless WIN32 - LACKS_FCNTL_H NOT defined - - Changing default word sizes: - - INTERNAL_SIZE_T size_t - MALLOC_ALIGNMENT 2 * sizeof(INTERNAL_SIZE_T) - PTR_UINT unsigned long - CHUNK_SIZE_T unsigned long - - Configuration and functionality options: - - USE_DL_PREFIX NOT defined - USE_PUBLIC_MALLOC_WRAPPERS NOT defined - USE_MALLOC_LOCK NOT defined - DL_DEBUG NOT defined - REALLOC_ZERO_BYTES_FREES NOT defined - MALLOC_FAILURE_ACTION errno = ENOMEM, if __STD_C defined, else no-op - TRIM_FASTBINS 0 - FIRST_SORTED_BIN_SIZE 512 - - Options for customizing MORECORE: - - MORECORE sbrk - MORECORE_CONTIGUOUS 1 - MORECORE_CANNOT_TRIM NOT defined - MMAP_AS_MORECORE_SIZE (1024 * 1024) - - Tuning options that are also dynamically changeable via mallopt: - - DEFAULT_MXFAST 64 - DEFAULT_TRIM_THRESHOLD 256 * 1024 - DEFAULT_TOP_PAD 0 - DEFAULT_MMAP_THRESHOLD 256 * 1024 - DEFAULT_MMAP_MAX 65536 - - There are several other #defined constants and macros that you - probably don't want to touch unless you are extending or adapting malloc. -*/ - -/* - WIN32 sets up defaults for MS environment and compilers. - Otherwise defaults are for unix. -*/ - -/* #define WIN32 */ - -#ifdef WIN32 - -#define WIN32_LEAN_AND_MEAN -#include - -/* Win32 doesn't supply or need the following headers */ -#define LACKS_UNISTD_H -#define LACKS_SYS_PARAM_H -#define LACKS_SYS_MMAN_H - -/* Use the supplied emulation of sbrk */ -#define MORECORE sbrk -#define MORECORE_CONTIGUOUS 1 -#define MORECORE_FAILURE ((void *)(-1)) - -/* Use the supplied emulation of mmap and munmap */ -#define HAVE_MMAP 1 -#define MUNMAP_FAILURE (-1) -#define MMAP_CLEARS 1 - -/* These values don't really matter in windows mmap emulation */ -#define MAP_PRIVATE 1 -#define MAP_ANONYMOUS 2 -#define PROT_READ 1 -#define PROT_WRITE 2 - -/* Emulation functions defined at the end of this file */ - -/* If USE_MALLOC_LOCK, use supplied critical-section-based lock functions */ -#ifdef USE_MALLOC_LOCK -static int slwait(int *sl); -static int slrelease(int *sl); -#endif - -static long getpagesize(void); -static long getregionsize(void); -static void *sbrk(long size); -static void *mmap(void *ptr, long size, long prot, long type, long handle, long arg); -static long munmap(void *ptr, long size); - -static void vminfo(unsigned long *free, unsigned long *reserved, unsigned long *committed); -static int cpuinfo(int whole, unsigned long *kernel, unsigned long *user); - -#endif - -/* - __STD_C should be nonzero if using ANSI-standard C compiler, a C++ - compiler, or a C compiler sufficiently close to ANSI to get away - with it. -*/ - -#ifndef __STD_C -#if defined(__STDC__) || defined(_cplusplus) -#define __STD_C 1 -#else -#define __STD_C 0 -#endif -#endif /*__STD_C*/ - -/* - Void_t* is the pointer type that malloc should say it returns -*/ - -#ifndef Void_t -#if (__STD_C || defined(WIN32)) -#define Void_t void -#else -#define Void_t char -#endif -#endif /*Void_t*/ - -#if __STD_C -#include /* for size_t */ -#else -#include -#endif - -/* define LACKS_UNISTD_H if your system does not have a . */ - -/* #define LACKS_UNISTD_H */ - -#ifndef LACKS_UNISTD_H -#include -#endif - -/* define LACKS_SYS_PARAM_H if your system does not have a . */ - -/* #define LACKS_SYS_PARAM_H */ - -#include /* needed for optional MALLOC_FAILURE_ACTION */ -#include /* needed for malloc_stats */ - -/* - Debugging: - - Because freed chunks may be overwritten with bookkeeping fields, this - malloc will often die when freed memory is overwritten by user - programs. This can be very effective (albeit in an annoying way) - in helping track down dangling pointers. - - If you compile with -DDL_DEBUG, a number of assertion checks are - enabled that will catch more memory errors. You probably won't be - able to make much sense of the actual assertion errors, but they - should help you locate incorrectly overwritten memory. The - checking is fairly extensive, and will slow down execution - noticeably. Calling malloc_stats or mallinfo with DL_DEBUG set will - attempt to check every non-mmapped allocated and free chunk in the - course of computing the summmaries. (By nature, mmapped regions - cannot be checked very much automatically.) - - Setting DL_DEBUG may also be helpful if you are trying to modify - this code. The assertions in the check routines spell out in more - detail the assumptions and invariants underlying the algorithms. - - Setting DL_DEBUG does NOT provide an automated mechanism for checking - that all accesses to malloced memory stay within their - bounds. However, there are several add-ons and adaptations of this - or other mallocs available that do this. -*/ - -#include - -/* - The unsigned integer type used for comparing any two chunk sizes. - This should be at least as wide as size_t, but should not be signed. -*/ - -#ifndef CHUNK_SIZE_T -#define CHUNK_SIZE_T unsigned long -#endif - -/* - The unsigned integer type used to hold addresses when they are are - manipulated as integers. Except that it is not defined on all - systems, intptr_t would suffice. -*/ -#ifndef PTR_UINT -#define PTR_UINT unsigned long -#endif - -/* - INTERNAL_SIZE_T is the word-size used for internal bookkeeping - of chunk sizes. - - The default version is the same as size_t. - - While not strictly necessary, it is best to define this as an - unsigned type, even if size_t is a signed type. This may avoid some - artificial size limitations on some systems. - - On a 64-bit machine, you may be able to reduce malloc overhead by - defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' at the - expense of not being able to handle more than 2^32 of malloced - space. If this limitation is acceptable, you are encouraged to set - this unless you are on a platform requiring 16byte alignments. In - this case the alignment requirements turn out to negate any - potential advantages of decreasing size_t word size. - - Implementors: Beware of the possible combinations of: - - INTERNAL_SIZE_T might be signed or unsigned, might be 32 or 64 bits, - and might be the same width as int or as long - - size_t might have different width and signedness as INTERNAL_SIZE_T - - int and long might be 32 or 64 bits, and might be the same width - To deal with this, most comparisons and difference computations - among INTERNAL_SIZE_Ts should cast them to CHUNK_SIZE_T, being - aware of the fact that casting an unsigned int to a wider long does - not sign-extend. (This also makes checking for negative numbers - awkward.) Some of these casts result in harmless compiler warnings - on some systems. -*/ - -#ifndef INTERNAL_SIZE_T -#define INTERNAL_SIZE_T size_t -#endif - -/* The corresponding word size */ -#define SIZE_SZ (sizeof(INTERNAL_SIZE_T)) - -/* - MALLOC_ALIGNMENT is the minimum alignment for malloc'ed chunks. - It must be a power of two at least 2 * SIZE_SZ, even on machines - for which smaller alignments would suffice. It may be defined as - larger than this though. Note however that code and data structures - are optimized for the case of 8-byte alignment. -*/ - -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT (2 * SIZE_SZ) -#endif - -/* The corresponding bit mask value */ -#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) - -/* - REALLOC_ZERO_BYTES_FREES should be set if a call to - realloc with zero bytes should be the same as a call to free. - Some people think it should. Otherwise, since this malloc - returns a unique pointer for malloc(0), so does realloc(p, 0). -*/ - -/* #define REALLOC_ZERO_BYTES_FREES */ - -/* - TRIM_FASTBINS controls whether free() of a very small chunk can - immediately lead to trimming. Setting to true (1) can reduce memory - footprint, but will almost always slow down programs that use a lot - of small chunks. - - Define this only if you are willing to give up some speed to more - aggressively reduce system-level memory footprint when releasing - memory in programs that use many small chunks. You can get - essentially the same effect by setting MXFAST to 0, but this can - lead to even greater slowdowns in programs using many small chunks. - TRIM_FASTBINS is an in-between compile-time option, that disables - only those chunks bordering topmost memory from being placed in - fastbins. -*/ - -#ifndef TRIM_FASTBINS -#define TRIM_FASTBINS 0 -#endif - -/* - USE_DL_PREFIX will prefix all public routines with the string 'dl'. - This is necessary when you only want to use this malloc in one part - of a program, using your regular system malloc elsewhere. -*/ - -/* #define USE_DL_PREFIX */ - -/* - USE_MALLOC_LOCK causes wrapper functions to surround each - callable routine with pthread mutex lock/unlock. - - USE_MALLOC_LOCK forces USE_PUBLIC_MALLOC_WRAPPERS to be defined -*/ - -/* #define USE_MALLOC_LOCK */ - -/* - If USE_PUBLIC_MALLOC_WRAPPERS is defined, every public routine is - actually a wrapper function that first calls MALLOC_PREACTION, then - calls the internal routine, and follows it with - MALLOC_POSTACTION. This is needed for locking, but you can also use - this, without USE_MALLOC_LOCK, for purposes of interception, - instrumentation, etc. It is a sad fact that using wrappers often - noticeably degrades performance of malloc-intensive programs. -*/ - -#ifdef USE_MALLOC_LOCK -#define USE_PUBLIC_MALLOC_WRAPPERS -#else -/* #define USE_PUBLIC_MALLOC_WRAPPERS */ -#endif - -/* - Two-phase name translation. - All of the actual routines are given mangled names. - When wrappers are used, they become the public callable versions. - When DL_PREFIX is used, the callable names are prefixed. -*/ - -#ifndef USE_PUBLIC_MALLOC_WRAPPERS -#define cALLOc public_cALLOc -#define fREe public_fREe -#define cFREe public_cFREe -#define mALLOc public_mALLOc -#define mEMALIGn public_mEMALIGn -#define rEALLOc public_rEALLOc -#define vALLOc public_vALLOc -#define pVALLOc public_pVALLOc -#define mALLINFo public_mALLINFo -#define mALLOPt public_mALLOPt -#define mTRIm public_mTRIm -#define mSTATs public_mSTATs -#define mUSABLe public_mUSABLe -#define iCALLOc public_iCALLOc -#define iCOMALLOc public_iCOMALLOc -#endif - -#ifdef USE_DL_PREFIX -#define public_cALLOc dlcalloc -#define public_fREe dlfree -#define public_cFREe dlcfree -#define public_mALLOc dlmalloc -#define public_mEMALIGn dlmemalign -#define public_rEALLOc dlrealloc -#define public_vALLOc dlvalloc -#define public_pVALLOc dlpvalloc -#define public_mALLINFo dlmallinfo -#define public_mALLOPt dlmallopt -#define public_mTRIm dlmalloc_trim -#define public_mSTATs dlmalloc_stats -#define public_mUSABLe dlmalloc_usable_size -#define public_iCALLOc dlindependent_calloc -#define public_iCOMALLOc dlindependent_comalloc -#else /* USE_DL_PREFIX */ -#define public_cALLOc calloc -#define public_fREe free -#define public_cFREe cfree -#define public_mALLOc malloc -#define public_mEMALIGn memalign -#define public_rEALLOc realloc -#define public_vALLOc valloc -#define public_pVALLOc pvalloc -#define public_mALLINFo mallinfo -#define public_mALLOPt mallopt -#define public_mTRIm malloc_trim -#define public_mSTATs malloc_stats -#define public_mUSABLe malloc_usable_size -#define public_iCALLOc independent_calloc -#define public_iCOMALLOc independent_comalloc -#endif /* USE_DL_PREFIX */ - -/* - HAVE_MEMCPY should be defined if you are not otherwise using - ANSI STD C, but still have memcpy and memset in your C library - and want to use them in calloc and realloc. Otherwise simple - macro versions are defined below. - - USE_MEMCPY should be defined as 1 if you actually want to - have memset and memcpy called. People report that the macro - versions are faster than libc versions on some systems. - - Even if USE_MEMCPY is set to 1, loops to copy/clear small chunks - (of <= 36 bytes) are manually unrolled in realloc and calloc. -*/ - -#define HAVE_MEMCPY - -#ifndef USE_MEMCPY -#ifdef HAVE_MEMCPY -#define USE_MEMCPY 1 -#else -#define USE_MEMCPY 0 -#endif -#endif - -#if (__STD_C || defined(HAVE_MEMCPY)) - -#ifdef WIN32 -/* On Win32 memset and memcpy are already declared in windows.h */ -#else -#if __STD_C -void *memset(void *, int, size_t); -void *memcpy(void *, const void *, size_t); -#else -Void_t *memset(); -Void_t *memcpy(); -#endif -#endif -#endif - -/* - MALLOC_FAILURE_ACTION is the action to take before "return 0" when - malloc fails to be able to return memory, either because memory is - exhausted or because of illegal arguments. - - By default, sets errno if running on STD_C platform, else does nothing. -*/ - -#ifndef MALLOC_FAILURE_ACTION -#if __STD_C -#define MALLOC_FAILURE_ACTION \ - errno = ENOMEM; - -#else -#define MALLOC_FAILURE_ACTION -#endif -#endif - -/* - MORECORE-related declarations. By default, rely on sbrk -*/ - -#ifdef LACKS_UNISTD_H -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) -#if __STD_C -extern Void_t *sbrk(ptrdiff_t); -#else -extern Void_t *sbrk(); -#endif -#endif -#endif - -/* - MORECORE is the name of the routine to call to obtain more memory - from the system. See below for general guidance on writing - alternative MORECORE functions, as well as a version for WIN32 and a - sample version for pre-OSX macos. -*/ - -// #define _GNU_SOURCE -// #include -extern void *sbrk(intptr_t __delta) __THROW; -#define MORECORE sbrk - -/* - MORECORE_FAILURE is the value returned upon failure of MORECORE - as well as mmap. Since it cannot be an otherwise valid memory address, - and must reflect values of standard sys calls, you probably ought not - try to redefine it. -*/ - -#ifndef MORECORE_FAILURE -#define MORECORE_FAILURE (-1) -#endif - -/* - If MORECORE_CONTIGUOUS is true, take advantage of fact that - consecutive calls to MORECORE with positive arguments always return - contiguous increasing addresses. This is true of unix sbrk. Even - if not defined, when regions happen to be contiguous, malloc will - permit allocations spanning regions obtained from different - calls. But defining this when applicable enables some stronger - consistency checks and space efficiencies. -*/ - -#ifndef MORECORE_CONTIGUOUS -#define MORECORE_CONTIGUOUS 1 -#endif - -/* - Define MORECORE_CANNOT_TRIM if your version of MORECORE - cannot release space back to the system when given negative - arguments. This is generally necessary only if you are using - a hand-crafted MORECORE function that cannot handle negative arguments. -*/ - -/* #define MORECORE_CANNOT_TRIM */ - -/* - Define HAVE_MMAP as true to optionally make malloc() use mmap() to - allocate very large blocks. These will be returned to the - operating system immediately after a free(). Also, if mmap - is available, it is used as a backup strategy in cases where - MORECORE fails to provide space from system. - - This malloc is best tuned to work with mmap for large requests. - If you do not have mmap, operations involving very large chunks (1MB - or so) may be slower than you'd like. -*/ - -#ifndef HAVE_MMAP -#define HAVE_MMAP 1 -#endif - -#if HAVE_MMAP -/* - Standard unix mmap using /dev/zero clears memory so calloc doesn't - need to. -*/ - -#ifndef MMAP_CLEARS -#define MMAP_CLEARS 1 -#endif - -#else /* no mmap */ -#ifndef MMAP_CLEARS -#define MMAP_CLEARS 0 -#endif -#endif - -/* - MMAP_AS_MORECORE_SIZE is the minimum mmap size argument to use if - sbrk fails, and mmap is used as a backup (which is done only if - HAVE_MMAP). The value must be a multiple of page size. This - backup strategy generally applies only when systems have "holes" in - address space, so sbrk cannot perform contiguous expansion, but - there is still space available on system. On systems for which - this is known to be useful (i.e. most linux kernels), this occurs - only when programs allocate huge amounts of memory. Between this, - and the fact that mmap regions tend to be limited, the size should - be large, to avoid too many mmap calls and thus avoid running out - of kernel resources. -*/ - -#ifndef MMAP_AS_MORECORE_SIZE -#define MMAP_AS_MORECORE_SIZE (1024 * 1024) -#endif - -/* - Define HAVE_MREMAP to make realloc() use mremap() to re-allocate - large blocks. This is currently only possible on Linux with - kernel versions newer than 1.3.77. -*/ - -#ifndef HAVE_MREMAP -#if defined(linux) && defined(__USE_GNU) -#define HAVE_MREMAP 1 -#else -#define HAVE_MREMAP 0 -#endif - -#endif /* HAVE_MMAP */ - -/* - The system page size. To the extent possible, this malloc manages - memory from the system in page-size units. Note that this value is - cached during initialization into a field of malloc_state. So even - if malloc_getpagesize is a function, it is only called once. - - The following mechanics for getpagesize were adapted from bsd/gnu - getpagesize.h. If none of the system-probes here apply, a value of - 4096 is used, which should be OK: If they don't apply, then using - the actual value probably doesn't impact performance. -*/ - -#ifndef malloc_getpagesize - -#ifndef LACKS_UNISTD_H -#include -#endif - -#ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ -#ifndef _SC_PAGE_SIZE -#define _SC_PAGE_SIZE _SC_PAGESIZE -#endif -#endif - -#ifdef _SC_PAGE_SIZE -#define malloc_getpagesize sysconf(_SC_PAGE_SIZE) -#else -#if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) -extern size_t getpagesize(); -#define malloc_getpagesize getpagesize() -#else -#ifdef WIN32 /* use supplied emulation of getpagesize */ -#define malloc_getpagesize getpagesize() -#else -#ifndef LACKS_SYS_PARAM_H -#include -#endif -#ifdef EXEC_PAGESIZE -#define malloc_getpagesize EXEC_PAGESIZE -#else -#ifdef NBPG -#ifndef CLSIZE -#define malloc_getpagesize NBPG -#else -#define malloc_getpagesize (NBPG * CLSIZE) -#endif -#else -#ifdef NBPC -#define malloc_getpagesize NBPC -#else -#ifdef PAGESIZE -#define malloc_getpagesize PAGESIZE -#else /* just guess */ -#define malloc_getpagesize (4096) -#endif -#endif -#endif -#endif -#endif -#endif -#endif -#endif - -/* - This version of malloc supports the standard SVID/XPG mallinfo - routine that returns a struct containing usage properties and - statistics. It should work on any SVID/XPG compliant system that has - a /usr/include/malloc.h defining struct mallinfo. (If you'd like to - install such a thing yourself, cut out the preliminary declarations - as described above and below and save them in a malloc.h file. But - there's no compelling reason to bother to do this.) - - The main declaration needed is the mallinfo struct that is returned - (by-copy) by mallinfo(). The SVID/XPG malloinfo struct contains a - bunch of fields that are not even meaningful in this version of - malloc. These fields are are instead filled by mallinfo() with - other numbers that might be of interest. - - HAVE_USR_INCLUDE_MALLOC_H should be set if you have a - /usr/include/malloc.h file that includes a declaration of struct - mallinfo. If so, it is included; else an SVID2/XPG2 compliant - version is declared below. These must be precisely the same for - mallinfo() to work. The original SVID version of this struct, - defined on most systems with mallinfo, declares all fields as - ints. But some others define as unsigned long. If your system - defines the fields using a type of different width than listed here, - you must #include your system version and #define - HAVE_USR_INCLUDE_MALLOC_H. -*/ - -/* #define HAVE_USR_INCLUDE_MALLOC_H */ - -#ifdef HAVE_USR_INCLUDE_MALLOC_H -#include "/usr/include/malloc.h" -#else - -/* SVID2/XPG mallinfo structure */ - -/* - SVID/XPG defines four standard parameter numbers for mallopt, - normally defined in malloc.h. Only one of these (M_MXFAST) is used - in this malloc. The others (M_NLBLKS, M_GRAIN, M_KEEP) don't apply, - so setting them has no effect. But this malloc also supports other - options in mallopt described below. -*/ -#endif - -/* ---------- description of public routines ------------ */ - -/* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or null - if no space is available. Additionally, on failure, errno is - set to ENOMEM on ANSI C systems. - - If n is zero, malloc returns a minumum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 24 or 32 bytes on 64bit - systems.) On most systems, size_t is an unsigned type, so calls - with negative arguments are interpreted as requests for huge amounts - of space, which will often fail. The maximum supported value of n - differs across systems, but is in all cases less than the maximum - representable value of a size_t. -*/ -#if __STD_C -Void_t *public_mALLOc(size_t); -#else -Void_t *public_mALLOc(); -#endif - -/* - free(Void_t* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. It can have arbitrary (i.e., bad!) - effects if p has already been freed. - - Unless disabled (using mallopt), freeing very large spaces will - when possible, automatically trigger operations that give - back unused memory to the system, thus reducing program footprint. -*/ -#if __STD_C -void public_fREe(Void_t *); -#else -void public_fREe(); -#endif - -/* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. -*/ -#if __STD_C -Void_t *public_cALLOc(size_t, size_t); -#else -Void_t *public_cALLOc(); -#endif - -/* - realloc(Void_t* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p when possible, otherwise it employs the - equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. Unless the #define - REALLOC_ZERO_BYTES_FREES is set, realloc with a size argument of - zero (re)allocates a minimum-sized chunk. - - Large chunks that were internally obtained via mmap will always - be reallocated using malloc-copy-free sequences unless - the system supports MREMAP (currently only linux). - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. -*/ -#if __STD_C -Void_t *public_rEALLOc(Void_t *, size_t); -#else -Void_t *public_rEALLOc(); -#endif - -/* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. -*/ -#if __STD_C -Void_t *public_mEMALIGn(size_t, size_t); -#else -Void_t *public_mEMALIGn(); -#endif - -/* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. -*/ -#if __STD_C -Void_t *public_vALLOc(size_t); -#else -Void_t *public_vALLOc(); -#endif - -/* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. Only one of these (M_MXFAST) is used - in this malloc. The others (M_NLBLKS, M_GRAIN, M_KEEP) don't apply, - so setting them has no effect. But this malloc also supports four - other options in mallopt. See below for details. Briefly, supported - parameters are as follows (listed defaults are for "typical" - configurations). - - Symbol param # default allowed param values - M_MXFAST 1 64 0-80 (0 disables fastbins) - M_TRIM_THRESHOLD -1 256*1024 any (-1U disables trimming) - M_TOP_PAD -2 0 any - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) - M_MMAP_MAX -4 65536 any (0 disables use of mmap) -*/ -#if __STD_C -int public_mALLOPt(int, int); -#else -int public_mALLOPt(); -#endif - -/* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: the number of fastbin blocks (i.e., small chunks that - have been freed but not use resused or consolidated) - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: total bytes held in fastbin blocks - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. -*/ -#if __STD_C -struct mallinfo public_mALLINFo(void); -#else -struct mallinfo public_mALLINFo(); -#endif - -/* - independent_calloc(size_t n_elements, size_t element_size, Void_t* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use regular calloc and assign pointers into this - space to represent elements. (In this case though, you cannot - independently free elements.) - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } -*/ -#if __STD_C -Void_t **public_iCALLOc(size_t, size_t, Void_t **); -#else -Void_t **public_iCALLOc(); -#endif - -/* - independent_comalloc(size_t n_elements, size_t sizes[], Void_t* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use a single regular malloc, and assign pointers at - particular offsets in the aggregate space. (In this case though, you - cannot independently free elements.) - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. -*/ -#if __STD_C -Void_t **public_iCOMALLOc(size_t, size_t *, Void_t **); -#else -Void_t **public_iCOMALLOc(); -#endif - -/* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ -#if __STD_C -Void_t *public_pVALLOc(size_t); -#else -Void_t *public_pVALLOc(); -#endif - -/* - cfree(Void_t* p); - Equivalent to free(p). - - cfree is needed/defined on some systems that pair it with calloc, - for odd historical reasons (such as: cfree is used in example - code in the first edition of K&R). -*/ -#if __STD_C -void public_cFREe(Void_t *); -#else -void public_cFREe(); -#endif - -/* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative - arguments to sbrk) if there is unused memory at the `high' end of - the malloc pool. You can call this after freeing large blocks of - memory to potentially reduce the system-level memory requirements - of a program. However, it cannot guarantee to reduce memory. Under - some allocation patterns, some large free blocks of memory will be - locked between two used chunks, so they cannot be given back to - the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, - only the minimum amount of memory to maintain internal data - structures will be left (one page or less). Non-zero arguments - can be supplied to maintain enough trailing space to service - future expected allocations without having to re-obtain memory - from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. - On systems that do not support "negative sbrks", it will always - rreturn 0. -*/ -#if __STD_C -int public_mTRIm(size_t); -#else -int public_mTRIm(); -#endif - -/* - malloc_usable_size(Void_t* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); - -*/ -#if __STD_C -size_t public_mUSABLe(Void_t *); -#else -size_t public_mUSABLe(); -#endif - -/* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. - -*/ -#if __STD_C -void public_mSTATs(void); -#else -void public_mSTATs(void); -#endif - -/* mallopt tuning options */ - -/* - M_MXFAST is the maximum request size used for "fastbins", special bins - that hold returned chunks without consolidating their spaces. This - enables future requests for chunks of the same size to be handled - very quickly, but can increase fragmentation, and thus increase the - overall memory footprint of a program. - - This malloc manages fastbins very conservatively yet still - efficiently, so fragmentation is rarely a problem for values less - than or equal to the default. The maximum supported value of MXFAST - is 80. You wouldn't want it any higher than this anyway. Fastbins - are designed especially for use with many small structs, objects or - strings -- the default handles structs/objects/arrays with sizes up - to 16 4byte fields, or small strings representing words, tokens, - etc. Using fastbins for larger objects normally worsens - fragmentation without improving speed. - - M_MXFAST is set in REQUEST size units. It is internally used in - chunksize units, which adds padding and alignment. You can reduce - M_MXFAST to 0 to disable all use of fastbins. This causes the malloc - algorithm to be a closer approximation of fifo-best-fit in all cases, - not just for larger requests, but will generally cause it to be - slower. -*/ - -/* M_MXFAST is a standard SVID/XPG tuning option, usually listed in malloc.h */ -#ifndef M_MXFAST -#define M_MXFAST 1 -#endif - -#ifndef DEFAULT_MXFAST -#define DEFAULT_MXFAST 64 -#endif - -/* - M_TRIM_THRESHOLD is the maximum amount of unused top-most memory - to keep before releasing via malloc_trim in free(). - - Automatic trimming is mainly useful in long-lived programs. - Because trimming via sbrk can be slow on some systems, and can - sometimes be wasteful (in cases where programs immediately - afterward allocate more large chunks) the value should be high - enough so that your overall system performance would improve by - releasing this much memory. - - The trim threshold and the mmap control parameters (see below) - can be traded off with one another. Trimming and mmapping are - two different ways of releasing unused memory back to the - system. Between these two, it is often possible to keep - system-level demands of a long-lived program down to a bare - minimum. For example, in one test suite of sessions measuring - the XF86 X server on Linux, using a trim threshold of 128K and a - mmap threshold of 192K led to near-minimal long term resource - consumption. - - If you are using this malloc in a long-lived program, it should - pay to experiment with these values. As a rough guide, you - might set to a value close to the average size of a process - (program) running on your system. Releasing this much memory - would allow such a process to run in memory. Generally, it's - worth it to tune for trimming rather tham memory mapping when a - program undergoes phases where several large chunks are - allocated and released in ways that can reuse each other's - storage, perhaps mixed with phases where there are no such - chunks at all. And in well-behaved long-lived programs, - controlling release of large blocks via trimming versus mapping - is usually faster. - - However, in most programs, these parameters serve mainly as - protection against the system-level effects of carrying around - massive amounts of unneeded memory. Since frequent calls to - sbrk, mmap, and munmap otherwise degrade performance, the default - parameters are set to relatively high values that serve only as - safeguards. - - The trim value must be greater than page size to have any useful - effect. To disable trimming completely, you can set to - (unsigned long)(-1) - - Trim settings interact with fastbin (MXFAST) settings: Unless - TRIM_FASTBINS is defined, automatic trimming never takes place upon - freeing a chunk with size less than or equal to MXFAST. Trimming is - instead delayed until subsequent freeing of larger chunks. However, - you can still force an attempted trim by calling malloc_trim. - - Also, trimming is not generally possible in cases where - the main arena is obtained via mmap. - - Note that the trick some people use of mallocing a huge space and - then freeing it at program startup, in an attempt to reserve system - memory, doesn't have the intended effect under automatic trimming, - since that memory will immediately be returned to the system. -*/ - -#define M_TRIM_THRESHOLD -1 - -#ifndef DEFAULT_TRIM_THRESHOLD -#define DEFAULT_TRIM_THRESHOLD (256 * 1024) -#endif - -/* - M_TOP_PAD is the amount of extra `padding' space to allocate or - retain whenever sbrk is called. It is used in two ways internally: - - * When sbrk is called to extend the top of the arena to satisfy - a new malloc request, this much padding is added to the sbrk - request. - - * When malloc_trim is called automatically from free(), - it is used as the `pad' argument. - - In both cases, the actual amount of padding is rounded - so that the end of the arena is always a system page boundary. - - The main reason for using padding is to avoid calling sbrk so - often. Having even a small pad greatly reduces the likelihood - that nearly every malloc request during program start-up (or - after trimming) will invoke sbrk, which needlessly wastes - time. - - Automatic rounding-up to page-size units is normally sufficient - to avoid measurable overhead, so the default is 0. However, in - systems where sbrk is relatively slow, it can pay to increase - this value, at the expense of carrying around more memory than - the program needs. -*/ - -#define M_TOP_PAD -2 - -#ifndef DEFAULT_TOP_PAD -#define DEFAULT_TOP_PAD (0) -#endif - -/* - M_MMAP_THRESHOLD is the request size threshold for using mmap() - to service a request. Requests of at least this size that cannot - be allocated using already-existing space will be serviced via mmap. - (If enough normal freed space already exists it is used instead.) - - Using mmap segregates relatively large chunks of memory so that - they can be individually obtained and released from the host - system. A request serviced through mmap is never reused by any - other request (at least not directly; the system may just so - happen to remap successive requests to the same locations). - - Segregating space in this way has the benefits that: - - 1. Mmapped space can ALWAYS be individually released back - to the system, which helps keep the system level memory - demands of a long-lived program low. - 2. Mapped memory can never become `locked' between - other chunks, as can happen with normally allocated chunks, which - means that even trimming via malloc_trim would not release them. - 3. On some systems with "holes" in address spaces, mmap can obtain - memory that sbrk cannot. - - However, it has the disadvantages that: - - 1. The space cannot be reclaimed, consolidated, and then - used to service later requests, as happens with normal chunks. - 2. It can lead to more wastage because of mmap page alignment - requirements - 3. It causes malloc performance to be more dependent on host - system memory management support routines which may vary in - implementation quality and may impose arbitrary - limitations. Generally, servicing a request via normal - malloc steps is faster than going through a system's mmap. - - The advantages of mmap nearly always outweigh disadvantages for - "large" chunks, but the value of "large" varies across systems. The - default is an empirically derived value that works well in most - systems. -*/ - -#define M_MMAP_THRESHOLD -3 - -#ifndef DEFAULT_MMAP_THRESHOLD -#define DEFAULT_MMAP_THRESHOLD (256 * 1024) -#endif - -/* - M_MMAP_MAX is the maximum number of requests to simultaneously - service using mmap. This parameter exists because -. Some systems have a limited number of internal tables for - use by mmap, and using more than a few of them may degrade - performance. - - The default is set to a value that serves only as a safeguard. - Setting to 0 disables use of mmap for servicing large requests. If - HAVE_MMAP is not set, the default value is 0, and attempts to set it - to non-zero values in mallopt will fail. -*/ - -#define M_MMAP_MAX -4 - -#ifndef DEFAULT_MMAP_MAX -#if HAVE_MMAP -#define DEFAULT_MMAP_MAX (65536) -#else -#define DEFAULT_MMAP_MAX (0) -#endif -#endif - -/* - ======================================================================== - To make a fully customizable malloc.h header file, cut everything - above this line, put into file malloc.h, edit to suit, and #include it - on the next line, as well as in programs that use this malloc. - ======================================================================== -*/ - -/* #include "malloc.h" */ - -/* --------------------- public wrappers ---------------------- */ - -#ifdef USE_PUBLIC_MALLOC_WRAPPERS - -/* Declare all routines as internal */ -#if __STD_C -static Void_t *mALLOc(size_t); -static void fREe(Void_t *); -static Void_t *rEALLOc(Void_t *, size_t); -static Void_t *mEMALIGn(size_t, size_t); -static Void_t *vALLOc(size_t); -static Void_t *pVALLOc(size_t); -static Void_t *cALLOc(size_t, size_t); -static Void_t **iCALLOc(size_t, size_t, Void_t **); -static Void_t **iCOMALLOc(size_t, size_t *, Void_t **); -static void cFREe(Void_t *); -static int mTRIm(size_t); -static size_t mUSABLe(Void_t *); -static void mSTATs(); -static int mALLOPt(int, int); -static struct mallinfo mALLINFo(void); -#else -static Void_t *mALLOc(); -static void fREe(); -static Void_t *rEALLOc(); -static Void_t *mEMALIGn(); -static Void_t *vALLOc(); -static Void_t *pVALLOc(); -static Void_t *cALLOc(); -static Void_t **iCALLOc(); -static Void_t **iCOMALLOc(); -static void cFREe(); -static int mTRIm(); -static size_t mUSABLe(); -static void mSTATs(); -static int mALLOPt(); -static struct mallinfo mALLINFo(); -#endif - -/* - MALLOC_PREACTION and MALLOC_POSTACTION should be - defined to return 0 on success, and nonzero on failure. - The return value of MALLOC_POSTACTION is currently ignored - in wrapper functions since there is no reasonable default - action to take on failure. -*/ - -#ifdef USE_MALLOC_LOCK - -#ifdef WIN32 - -static int mALLOC_MUTEx; -#define MALLOC_PREACTION slwait(&mALLOC_MUTEx) -#define MALLOC_POSTACTION slrelease(&mALLOC_MUTEx) - -#else - -#include - -static pthread_mutex_t mALLOC_MUTEx = PTHREAD_MUTEX_INITIALIZER; - -#define MALLOC_PREACTION pthread_mutex_lock(&mALLOC_MUTEx) -#define MALLOC_POSTACTION pthread_mutex_unlock(&mALLOC_MUTEx) - -#endif /* USE_MALLOC_LOCK */ - -#else - -/* Substitute anything you like for these */ - -#define MALLOC_PREACTION (0) -#define MALLOC_POSTACTION (0) - -#endif - -Void_t *public_mALLOc(size_t bytes) { - Void_t *m; - if (MALLOC_PREACTION != 0) { - return 0; - } - m = mALLOc(bytes); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -void public_fREe(Void_t *m) { - if (MALLOC_PREACTION != 0) { - return; - } - fREe(m); - if (MALLOC_POSTACTION != 0) { - } -} - -Void_t *public_rEALLOc(Void_t *m, size_t bytes) { - if (MALLOC_PREACTION != 0) { - return 0; - } - m = rEALLOc(m, bytes); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -Void_t *public_mEMALIGn(size_t alignment, size_t bytes) { - Void_t *m; - if (MALLOC_PREACTION != 0) { - return 0; - } - m = mEMALIGn(alignment, bytes); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -Void_t *public_vALLOc(size_t bytes) { - Void_t *m; - if (MALLOC_PREACTION != 0) { - return 0; - } - m = vALLOc(bytes); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -Void_t *public_pVALLOc(size_t bytes) { - Void_t *m; - if (MALLOC_PREACTION != 0) { - return 0; - } - m = pVALLOc(bytes); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -Void_t *public_cALLOc(size_t n, size_t elem_size) { - Void_t *m; - if (MALLOC_PREACTION != 0) { - return 0; - } - m = cALLOc(n, elem_size); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -Void_t **public_iCALLOc(size_t n, size_t elem_size, Void_t **chunks) { - Void_t **m; - if (MALLOC_PREACTION != 0) { - return 0; - } - m = iCALLOc(n, elem_size, chunks); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -Void_t **public_iCOMALLOc(size_t n, size_t sizes[], Void_t **chunks) { - Void_t **m; - if (MALLOC_PREACTION != 0) { - return 0; - } - m = iCOMALLOc(n, sizes, chunks); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -void public_cFREe(Void_t *m) { - if (MALLOC_PREACTION != 0) { - return; - } - cFREe(m); - if (MALLOC_POSTACTION != 0) { - } -} - -int public_mTRIm(size_t s) { - int result; - if (MALLOC_PREACTION != 0) { - return 0; - } - result = mTRIm(s); - if (MALLOC_POSTACTION != 0) { - } - return result; -} - -size_t public_mUSABLe(Void_t *m) { - size_t result; - if (MALLOC_PREACTION != 0) { - return 0; - } - result = mUSABLe(m); - if (MALLOC_POSTACTION != 0) { - } - return result; -} - -void public_mSTATs() { - if (MALLOC_PREACTION != 0) { - return; - } - mSTATs(); - if (MALLOC_POSTACTION != 0) { - } -} - -struct mallinfo public_mALLINFo() { - struct mallinfo m; - if (MALLOC_PREACTION != 0) { - struct mallinfo nm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - return nm; - } - m = mALLINFo(); - if (MALLOC_POSTACTION != 0) { - } - return m; -} - -int public_mALLOPt(int p, int v) { - int result; - if (MALLOC_PREACTION != 0) { - return 0; - } - result = mALLOPt(p, v); - if (MALLOC_POSTACTION != 0) { - } - return result; -} - -#endif - -/* ------------- Optional versions of memcopy ---------------- */ - -#if USE_MEMCPY - -/* - Note: memcpy is ONLY invoked with non-overlapping regions, - so the (usually slower) memmove is not needed. -*/ - -#define MALLOC_COPY(dest, src, nbytes) memcpy(dest, src, nbytes) -#define MALLOC_ZERO(dest, nbytes) memset(dest, 0, nbytes) - -#else /* !USE_MEMCPY */ - -/* Use Duff's device for good zeroing/copying performance. */ - -#define MALLOC_ZERO(charp, nbytes) \ - do { \ - INTERNAL_SIZE_T *mzp = (INTERNAL_SIZE_T *)(charp); \ - CHUNK_SIZE_T mctmp = (nbytes) / sizeof(INTERNAL_SIZE_T); \ - long mcn; \ - if (mctmp < 8) \ - mcn = 0; \ - else { \ - mcn = (mctmp - 1) / 8; \ - mctmp %= 8; \ - } \ - switch (mctmp) { \ - case 0: \ - for (;;) { \ - *mzp++ = 0; \ - case 7: \ - *mzp++ = 0; \ - case 6: \ - *mzp++ = 0; \ - case 5: \ - *mzp++ = 0; \ - case 4: \ - *mzp++ = 0; \ - case 3: \ - *mzp++ = 0; \ - case 2: \ - *mzp++ = 0; \ - case 1: \ - *mzp++ = 0; \ - if (mcn <= 0) \ - break; \ - mcn--; \ - } \ - } \ - } while (0) - -#define MALLOC_COPY(dest, src, nbytes) \ - do { \ - INTERNAL_SIZE_T *mcsrc = (INTERNAL_SIZE_T *)src; \ - INTERNAL_SIZE_T *mcdst = (INTERNAL_SIZE_T *)dest; \ - CHUNK_SIZE_T mctmp = (nbytes) / sizeof(INTERNAL_SIZE_T); \ - long mcn; \ - if (mctmp < 8) \ - mcn = 0; \ - else { \ - mcn = (mctmp - 1) / 8; \ - mctmp %= 8; \ - } \ - switch (mctmp) { \ - case 0: \ - for (;;) { \ - *mcdst++ = *mcsrc++; \ - case 7: \ - *mcdst++ = *mcsrc++; \ - case 6: \ - *mcdst++ = *mcsrc++; \ - case 5: \ - *mcdst++ = *mcsrc++; \ - case 4: \ - *mcdst++ = *mcsrc++; \ - case 3: \ - *mcdst++ = *mcsrc++; \ - case 2: \ - *mcdst++ = *mcsrc++; \ - case 1: \ - *mcdst++ = *mcsrc++; \ - if (mcn <= 0) \ - break; \ - mcn--; \ - } \ - } \ - } while (0) - -#endif - -/* ------------------ MMAP support ------------------ */ - -#if HAVE_MMAP - -#ifndef LACKS_FCNTL_H -#include -#endif - -#ifndef LACKS_SYS_MMAN_H -#include -#endif - -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif - -/* - Nearly all versions of mmap support MAP_ANONYMOUS, - so the following is unlikely to be needed, but is - supplied just in case. -*/ - -#ifndef MAP_ANONYMOUS - -static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ - -#define MMAP(addr, size, prot, flags) ((dev_zero_fd < 0) ? (dev_zero_fd = open("/dev/zero", O_RDWR), \ - mmap((addr), (size), (prot), (flags), dev_zero_fd, 0)) \ - : mmap((addr), (size), (prot), (flags), dev_zero_fd, 0)) - -#else - -#define MMAP(addr, size, prot, flags) \ - (mmap((addr), (size), (prot), (flags) | MAP_ANONYMOUS, -1, 0)) - -#endif - -#endif /* HAVE_MMAP */ - -/* - ----------------------- Chunk representations ----------------------- -*/ - -/* - This struct declaration is misleading (but accurate and necessary). - It declares a "view" into memory allowing access to necessary - fields at known offsets from a given base. See explanation below. -*/ - -struct malloc_chunk { - - INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ - INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ - - struct malloc_chunk *fd; /* double links -- used only if free. */ - struct malloc_chunk *bk; -}; - -typedef struct malloc_chunk *mchunkptr; - -/* - malloc_chunk details: - - (The following includes lightly edited explanations by Colin Plumb.) - - Chunks of memory are maintained using a `boundary tag' method as - described in e.g., Knuth or Standish. (See the paper by Paul - Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a - survey of such techniques.) Sizes of free chunks are stored both - in the front of each chunk and at the end. This makes - consolidating fragmented chunks into bigger chunks very fast. The - size fields also hold bits representing whether chunks are free or - in use. - - An allocated chunk looks like this: - - - chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk, if allocated | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of chunk, in bytes |P| - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | User data starts here... . - . . - . (malloc_usable_space() bytes) . - . | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - Where "chunk" is the front of the chunk for the purpose of most of - the malloc code, but "mem" is the pointer that is returned to the - user. "Nextchunk" is the beginning of the next contiguous chunk. - - Chunks always begin on even word boundries, so the mem portion - (which is returned to the user) is also on an even word boundary, and - thus at least double-word aligned. - - Free chunks are stored in circular doubly-linked lists, and look like this: - - chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - `head:' | Size of chunk, in bytes |P| - mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Forward pointer to next chunk in list | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Back pointer to previous chunk in list | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Unused space (may be 0 bytes long) . - . . - . | -nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - `foot:' | Size of chunk, in bytes | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - The P (PREV_INUSE) bit, stored in the unused low-order bit of the - chunk size (which is always a multiple of two words), is an in-use - bit for the *previous* chunk. If that bit is *clear*, then the - word before the current chunk size contains the previous chunk - size, and can be used to find the front of the previous chunk. - The very first chunk allocated always has this bit set, - preventing access to non-existent (or non-owned) memory. If - prev_inuse is set for any given chunk, then you CANNOT determine - the size of the previous chunk, and might even get a memory - addressing fault when trying to do so. - - Note that the `foot' of the current chunk is actually represented - as the prev_size of the NEXT chunk. This makes it easier to - deal with alignments etc but can be very confusing when trying - to extend or adapt this code. - - The two exceptions to all this are - - 1. The special chunk `top' doesn't bother using the - trailing size field since there is no next contiguous chunk - that would have to index off it. After initialization, `top' - is forced to always exist. If it would become less than - MINSIZE bytes long, it is replenished. - - 2. Chunks allocated via mmap, which have the second-lowest-order - bit (IS_MMAPPED) set in their size fields. Because they are - allocated one-by-one, each must contain its own trailing size field. - -*/ - -/* - ---------- Size and alignment checks and conversions ---------- -*/ - -/* conversion from malloc headers to user pointers, and back */ - -#define chunk2mem(p) ((Void_t *)((char *)(p) + 2 * SIZE_SZ)) -#define mem2chunk(mem) ((mchunkptr)((char *)(mem) - 2 * SIZE_SZ)) - -/* The smallest possible chunk */ -#define MIN_CHUNK_SIZE (sizeof(struct malloc_chunk)) - -/* The smallest size we can malloc is an aligned minimal chunk */ - -#define MINSIZE \ - (CHUNK_SIZE_T)(((MIN_CHUNK_SIZE + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)) - -/* Check if m has acceptable alignment */ - -#define aligned_OK(m) (((PTR_UINT)((m)) & (MALLOC_ALIGN_MASK)) == 0) - -/* - Check if a request is so large that it would wrap around zero when - padded and aligned. To simplify some other code, the bound is made - low enough so that adding MINSIZE will also not wrap around sero. -*/ - -#define REQUEST_OUT_OF_RANGE(req) \ - ((CHUNK_SIZE_T)(req) >= \ - (CHUNK_SIZE_T)(INTERNAL_SIZE_T)(-2 * MINSIZE)) - -/* pad request bytes into a usable size -- internal version */ - -#define request2size(req) \ - (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? MINSIZE : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) - -/* Same, except also perform argument check */ - -#define checked_request2size(req, sz) \ - if (REQUEST_OUT_OF_RANGE(req)) { \ - MALLOC_FAILURE_ACTION; \ - return 0; \ - } \ - (sz) = request2size(req); - -/* - --------------- Physical chunk operations --------------- -*/ - -/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ -#define PREV_INUSE 0x1 - -/* extract inuse bit of previous chunk */ -#define prev_inuse(p) ((p)->size & PREV_INUSE) - -/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ -#define IS_MMAPPED 0x2 - -/* check for mmap()'ed chunk */ -#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) - -/* - Bits to mask off when extracting size - - Note: IS_MMAPPED is intentionally not masked off from size field in - macros for which mmapped chunks should never be seen. This should - cause helpful core dumps to occur if it is tried by accident by - people extending or adapting this malloc. -*/ -#define SIZE_BITS (PREV_INUSE | IS_MMAPPED) - -/* Get size, ignoring use bits */ -#define chunksize(p) ((p)->size & ~(SIZE_BITS)) - -/* Ptr to next physical malloc_chunk. */ -#define next_chunk(p) ((mchunkptr)(((char *)(p)) + ((p)->size & ~PREV_INUSE))) - -/* Ptr to previous physical malloc_chunk */ -#define prev_chunk(p) ((mchunkptr)(((char *)(p)) - ((p)->prev_size))) - -/* Treat space at ptr + offset as a chunk */ -#define chunk_at_offset(p, s) ((mchunkptr)(((char *)(p)) + (s))) - -/* extract p's inuse bit */ -#define inuse(p) \ - ((((mchunkptr)(((char *)(p)) + ((p)->size & ~PREV_INUSE)))->size) & PREV_INUSE) - -/* set/clear chunk as being inuse without otherwise disturbing */ -#define set_inuse(p) \ - ((mchunkptr)(((char *)(p)) + ((p)->size & ~PREV_INUSE)))->size |= PREV_INUSE - -#define clear_inuse(p) \ - ((mchunkptr)(((char *)(p)) + ((p)->size & ~PREV_INUSE)))->size &= ~(PREV_INUSE) - -/* check/set/clear inuse bits in known places */ -#define inuse_bit_at_offset(p, s) \ - (((mchunkptr)(((char *)(p)) + (s)))->size & PREV_INUSE) - -#define set_inuse_bit_at_offset(p, s) \ - (((mchunkptr)(((char *)(p)) + (s)))->size |= PREV_INUSE) - -#define clear_inuse_bit_at_offset(p, s) \ - (((mchunkptr)(((char *)(p)) + (s)))->size &= ~(PREV_INUSE)) - -/* Set size at head, without disturbing its use bit */ -#define set_head_size(p, s) ((p)->size = (((p)->size & PREV_INUSE) | (s))) - -/* Set size/use field */ -#define set_head(p, s) ((p)->size = (s)) - -/* Set size at footer (only when chunk is not in use) */ -#define set_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_size = (s)) - -/* - -------------------- Internal data structures -------------------- - - All internal state is held in an instance of malloc_state defined - below. There are no other static variables, except in two optional - cases: - * If USE_MALLOC_LOCK is defined, the mALLOC_MUTEx declared above. - * If HAVE_MMAP is true, but mmap doesn't support - MAP_ANONYMOUS, a dummy file descriptor for mmap. - - Beware of lots of tricks that minimize the total bookkeeping space - requirements. The result is a little over 1K bytes (for 4byte - pointers and size_t.) -*/ - -/* - Bins - - An array of bin headers for free chunks. Each bin is doubly - linked. The bins are approximately proportionally (log) spaced. - There are a lot of these bins (128). This may look excessive, but - works very well in practice. Most bins hold sizes that are - unusual as malloc request sizes, but are more usual for fragments - and consolidated sets of chunks, which is what these bins hold, so - they can be found quickly. All procedures maintain the invariant - that no consolidated chunk physically borders another one, so each - chunk in a list is known to be preceeded and followed by either - inuse chunks or the ends of memory. - - Chunks in bins are kept in size order, with ties going to the - approximately least recently used chunk. Ordering isn't needed - for the small bins, which all contain the same-sized chunks, but - facilitates best-fit allocation for larger chunks. These lists - are just sequential. Keeping them in order almost never requires - enough traversal to warrant using fancier ordered data - structures. - - Chunks of the same size are linked with the most - recently freed at the front, and allocations are taken from the - back. This results in LRU (FIFO) allocation order, which tends - to give each chunk an equal opportunity to be consolidated with - adjacent freed chunks, resulting in larger free chunks and less - fragmentation. - - To simplify use in double-linked lists, each bin header acts - as a malloc_chunk. This avoids special-casing for headers. - But to conserve space and improve locality, we allocate - only the fd/bk pointers of bins, and then use repositioning tricks - to treat these as the fields of a malloc_chunk*. -*/ - -typedef struct malloc_chunk *mbinptr; - -/* addressing -- note that bin_at(0) does not exist */ -#define bin_at(m, i) ((mbinptr)((char *)&((m)->bins[(i) << 1]) - (SIZE_SZ << 1))) - -/* analog of ++bin */ -#define next_bin(b) ((mbinptr)((char *)(b) + (sizeof(mchunkptr) << 1))) - -/* Reminders about list directionality within bins */ -#define first(b) ((b)->fd) -#define last(b) ((b)->bk) - -/* Take a chunk off a bin list */ -#define unlink(P, BK, FD) \ - { \ - FD = P->fd; \ - BK = P->bk; \ - FD->bk = BK; \ - BK->fd = FD; \ - } - -/* - Indexing - - Bins for sizes < 512 bytes contain chunks of all the same size, spaced - 8 bytes apart. Larger bins are approximately logarithmically spaced: - - 64 bins of size 8 - 32 bins of size 64 - 16 bins of size 512 - 8 bins of size 4096 - 4 bins of size 32768 - 2 bins of size 262144 - 1 bin of size what's left - - The bins top out around 1MB because we expect to service large - requests via mmap. -*/ - -#define NBINS 96 -#define NSMALLBINS 32 -#define SMALLBIN_WIDTH 8 -#define MIN_LARGE_SIZE 256 - -#define in_smallbin_range(sz) \ - ((CHUNK_SIZE_T)(sz) < (CHUNK_SIZE_T)MIN_LARGE_SIZE) - -#define smallbin_index(sz) (((unsigned)(sz)) >> 3) - -/* - Compute index for size. We expect this to be inlined when - compiled with optimization, else not, which works out well. -*/ -static int largebin_index(unsigned int sz) { - unsigned int x = sz >> SMALLBIN_WIDTH; - unsigned int m; /* bit position of highest set bit of m */ - - if (x >= 0x10000) { - return NBINS - 1; - } - - /* On intel, use BSRL instruction to find highest bit */ -#if defined(__GNUC__) && defined(i386) - - __asm__("bsrl %1,%0\n\t" - : "=r"(m) - : "g"(x)); - -#else - { - /* - Based on branch-free nlz algorithm in chapter 5 of Henry - S. Warren Jr's book "Hacker's Delight". - */ - - unsigned int n = ((x - 0x100) >> 16) & 8; - x <<= n; - m = ((x - 0x1000) >> 16) & 4; - n += m; - x <<= m; - m = ((x - 0x4000) >> 16) & 2; - n += m; - x = (x << m) >> 14; - m = 13 - n + (x & ~(x >> 1)); - } -#endif - - /* Use next 2 bits to create finer-granularity bins */ - return NSMALLBINS + (m << 2) + ((sz >> (m + 6)) & 3); -} - -#define bin_index(sz) \ - ((in_smallbin_range(sz)) ? smallbin_index(sz) : largebin_index(sz)) - -/* - FIRST_SORTED_BIN_SIZE is the chunk size corresponding to the - first bin that is maintained in sorted order. This must - be the smallest size corresponding to a given bin. - - Normally, this should be MIN_LARGE_SIZE. But you can weaken - best fit guarantees to sometimes speed up malloc by increasing value. - Doing this means that malloc may choose a chunk that is - non-best-fitting by up to the width of the bin. - - Some useful cutoff values: - 512 - all bins sorted - 2560 - leaves bins <= 64 bytes wide unsorted - 12288 - leaves bins <= 512 bytes wide unsorted - 65536 - leaves bins <= 4096 bytes wide unsorted - 262144 - leaves bins <= 32768 bytes wide unsorted - -1 - no bins sorted (not recommended!) -*/ - -#define FIRST_SORTED_BIN_SIZE MIN_LARGE_SIZE -/* #define FIRST_SORTED_BIN_SIZE 65536 */ - -/* - Unsorted chunks - - All remainders from chunk splits, as well as all returned chunks, - are first placed in the "unsorted" bin. They are then placed - in regular bins after malloc gives them ONE chance to be used before - binning. So, basically, the unsorted_chunks list acts as a queue, - with chunks being placed on it in free (and malloc_consolidate), - and taken off (to be either used or placed in bins) in malloc. -*/ - -/* The otherwise unindexable 1-bin is used to hold unsorted chunks. */ -#define unsorted_chunks(M) (bin_at(M, 1)) - -/* - Top - - The top-most available chunk (i.e., the one bordering the end of - available memory) is treated specially. It is never included in - any bin, is used only if no other chunk is available, and is - released back to the system if it is very large (see - M_TRIM_THRESHOLD). Because top initially - points to its own bin with initial zero size, thus forcing - extension on the first malloc request, we avoid having any special - code in malloc to check whether it even exists yet. But we still - need to do so when getting memory from system, so we make - initial_top treat the bin as a legal but unusable chunk during the - interval between initialization and the first call to - sYSMALLOc. (This is somewhat delicate, since it relies on - the 2 preceding words to be zero during this interval as well.) -*/ - -/* Conveniently, the unsorted bin can be used as dummy top on first call */ -#define initial_top(M) (unsorted_chunks(M)) - -/* - Binmap - - To help compensate for the large number of bins, a one-level index - structure is used for bin-by-bin searching. `binmap' is a - bitvector recording whether bins are definitely empty so they can - be skipped over during during traversals. The bits are NOT always - cleared as soon as bins are empty, but instead only - when they are noticed to be empty during traversal in malloc. -*/ - -/* Conservatively use 32 bits per map word, even if on 64bit system */ -#define BINMAPSHIFT 5 -#define BITSPERMAP (1U << BINMAPSHIFT) -#define BINMAPSIZE (NBINS / BITSPERMAP) - -#define idx2block(i) ((i) >> BINMAPSHIFT) -#define idx2bit(i) ((1U << ((i) & ((1U << BINMAPSHIFT) - 1)))) - -#define mark_bin(m, i) ((m)->binmap[idx2block(i)] |= idx2bit(i)) -#define unmark_bin(m, i) ((m)->binmap[idx2block(i)] &= ~(idx2bit(i))) -#define get_binmap(m, i) ((m)->binmap[idx2block(i)] & idx2bit(i)) - -/* - Fastbins - - An array of lists holding recently freed small chunks. Fastbins - are not doubly linked. It is faster to single-link them, and - since chunks are never removed from the middles of these lists, - double linking is not necessary. Also, unlike regular bins, they - are not even processed in FIFO order (they use faster LIFO) since - ordering doesn't much matter in the transient contexts in which - fastbins are normally used. - - Chunks in fastbins keep their inuse bit set, so they cannot - be consolidated with other free chunks. malloc_consolidate - releases all chunks in fastbins and consolidates them with - other free chunks. -*/ - -typedef struct malloc_chunk *mfastbinptr; - -/* offset 2 to use otherwise unindexable first 2 bins */ -#define fastbin_index(sz) ((((unsigned int)(sz)) >> 3) - 2) - -/* The maximum fastbin request size we support */ -#define MAX_FAST_SIZE 80 - -#define NFASTBINS (fastbin_index(request2size(MAX_FAST_SIZE)) + 1) - -/* - FASTBIN_CONSOLIDATION_THRESHOLD is the size of a chunk in free() - that triggers automatic consolidation of possibly-surrounding - fastbin chunks. This is a heuristic, so the exact value should not - matter too much. It is defined at half the default trim threshold as a - compromise heuristic to only attempt consolidation if it is likely - to lead to trimming. However, it is not dynamically tunable, since - consolidation reduces fragmentation surrounding loarge chunks even - if trimming is not used. -*/ - -#define FASTBIN_CONSOLIDATION_THRESHOLD \ - ((unsigned long)(DEFAULT_TRIM_THRESHOLD) >> 1) - -/* - Since the lowest 2 bits in max_fast don't matter in size comparisons, - they are used as flags. -*/ - -/* - ANYCHUNKS_BIT held in max_fast indicates that there may be any - freed chunks at all. It is set true when entering a chunk into any - bin. -*/ - -#define ANYCHUNKS_BIT (1U) - -#define have_anychunks(M) (((M)->max_fast & ANYCHUNKS_BIT)) -#define set_anychunks(M) ((M)->max_fast |= ANYCHUNKS_BIT) -#define clear_anychunks(M) ((M)->max_fast &= ~ANYCHUNKS_BIT) - -/* - FASTCHUNKS_BIT held in max_fast indicates that there are probably - some fastbin chunks. It is set true on entering a chunk into any - fastbin, and cleared only in malloc_consolidate. -*/ - -#define FASTCHUNKS_BIT (2U) - -#define have_fastchunks(M) (((M)->max_fast & FASTCHUNKS_BIT)) -#define set_fastchunks(M) ((M)->max_fast |= (FASTCHUNKS_BIT | ANYCHUNKS_BIT)) -#define clear_fastchunks(M) ((M)->max_fast &= ~(FASTCHUNKS_BIT)) - -/* - Set value of max_fast. - Use impossibly small value if 0. -*/ - -#define set_max_fast(M, s) \ - (M)->max_fast = (((s) == 0) ? SMALLBIN_WIDTH : request2size(s)) | \ - ((M)->max_fast & (FASTCHUNKS_BIT | ANYCHUNKS_BIT)) - -#define get_max_fast(M) \ - ((M)->max_fast & ~(FASTCHUNKS_BIT | ANYCHUNKS_BIT)) - -/* - morecore_properties is a status word holding dynamically discovered - or controlled properties of the morecore function -*/ - -#define MORECORE_CONTIGUOUS_BIT (1U) - -#define contiguous(M) \ - (((M)->morecore_properties & MORECORE_CONTIGUOUS_BIT)) -#define noncontiguous(M) \ - (((M)->morecore_properties & MORECORE_CONTIGUOUS_BIT) == 0) -#define set_contiguous(M) \ - ((M)->morecore_properties |= MORECORE_CONTIGUOUS_BIT) -#define set_noncontiguous(M) \ - ((M)->morecore_properties &= ~MORECORE_CONTIGUOUS_BIT) - -/* - ----------- Internal state representation and initialization ----------- -*/ - -struct malloc_state { - - /* The maximum chunk size to be eligible for fastbin */ - INTERNAL_SIZE_T max_fast; /* low 2 bits used as flags */ - - /* Fastbins */ - mfastbinptr fastbins[NFASTBINS]; - - /* Base of the topmost chunk -- not otherwise kept in a bin */ - mchunkptr top; - - /* The remainder from the most recent split of a small request */ - mchunkptr last_remainder; - - /* Normal bins packed as described above */ - mchunkptr bins[NBINS * 2]; - - /* Bitmap of bins. Trailing zero map handles cases of largest binned size */ - unsigned int binmap[BINMAPSIZE + 1]; - - /* Tunable parameters */ - CHUNK_SIZE_T trim_threshold; - INTERNAL_SIZE_T top_pad; - INTERNAL_SIZE_T mmap_threshold; - - /* Memory map support */ - int n_mmaps; - int n_mmaps_max; - int max_n_mmaps; - - /* Cache malloc_getpagesize */ - unsigned int pagesize; - - /* Track properties of MORECORE */ - unsigned int morecore_properties; - - /* Statistics */ - INTERNAL_SIZE_T mmapped_mem; - INTERNAL_SIZE_T sbrked_mem; - INTERNAL_SIZE_T max_sbrked_mem; - INTERNAL_SIZE_T max_mmapped_mem; - INTERNAL_SIZE_T max_total_mem; -}; - -typedef struct malloc_state *mstate; - -/* - There is exactly one instance of this struct in this malloc. - If you are adapting this malloc in a way that does NOT use a static - malloc_state, you MUST explicitly zero-fill it before using. This - malloc relies on the property that malloc_state is initialized to - all zeroes (as is true of C statics). -*/ - -static struct malloc_state av_; /* never directly referenced */ - -/* - All uses of av_ are via get_malloc_state(). - At most one "call" to get_malloc_state is made per invocation of - the public versions of malloc and free, but other routines - that in turn invoke malloc and/or free may call more then once. - Also, it is called in check* routines if DL_DEBUG is set. -*/ - -#define get_malloc_state() (&(av_)) - -/* - Initialize a malloc_state struct. - - This is called only from within malloc_consolidate, which needs - be called in the same contexts anyway. It is never called directly - outside of malloc_consolidate because some optimizing compilers try - to inline it at all call points, which turns out not to be an - optimization at all. (Inlining it in malloc_consolidate is fine though.) -*/ - -#if __STD_C -static void malloc_init_state(mstate av) -#else -static void malloc_init_state(av) mstate av; -#endif -{ - int i; - mbinptr bin; - - /* Establish circular links for normal bins */ - for (i = 1; i < NBINS; ++i) { - bin = bin_at(av, i); - bin->fd = bin->bk = bin; - } - - av->top_pad = DEFAULT_TOP_PAD; - av->n_mmaps_max = DEFAULT_MMAP_MAX; - av->mmap_threshold = DEFAULT_MMAP_THRESHOLD; - av->trim_threshold = DEFAULT_TRIM_THRESHOLD; - -#if MORECORE_CONTIGUOUS - set_contiguous(av); -#else - set_noncontiguous(av); -#endif - - set_max_fast(av, DEFAULT_MXFAST); - - av->top = initial_top(av); - av->pagesize = malloc_getpagesize; -} - -/* - Other internal utilities operating on mstates -*/ - -#if __STD_C -static Void_t *sYSMALLOc(INTERNAL_SIZE_T, mstate); -static int sYSTRIm(size_t, mstate); -static void malloc_consolidate(mstate); -static Void_t **iALLOc(size_t, size_t *, int, Void_t **); -#else -static Void_t *sYSMALLOc(); -static int sYSTRIm(); -static void malloc_consolidate(); -static Void_t **iALLOc(); -#endif - -/* - Debugging support - - These routines make a number of assertions about the states - of data structures that should be true at all times. If any - are not true, it's very likely that a user program has somehow - trashed memory. (It's also possible that there is a coding error - in malloc. In which case, please report it!) -*/ - -#if !DL_DEBUG - -#define check_chunk(P) -#define check_free_chunk(P) -#define check_inuse_chunk(P) -#define check_remalloced_chunk(P, N) -#define check_malloced_chunk(P, N) -#define check_malloc_state() - -#else -#define check_chunk(P) do_check_chunk(P) -#define check_free_chunk(P) do_check_free_chunk(P) -#define check_inuse_chunk(P) do_check_inuse_chunk(P) -#define check_remalloced_chunk(P, N) do_check_remalloced_chunk(P, N) -#define check_malloced_chunk(P, N) do_check_malloced_chunk(P, N) -#define check_malloc_state() do_check_malloc_state() - -/* - Properties of all chunks -*/ - -#if __STD_C -static void do_check_chunk(mchunkptr p) -#else -static void do_check_chunk(p) mchunkptr p; -#endif -{ - mstate av = get_malloc_state(); - CHUNK_SIZE_T sz = chunksize(p); - /* min and max possible addresses assuming contiguous allocation */ - char *max_address = (char *)(av->top) + chunksize(av->top); - char *min_address = max_address - av->sbrked_mem; - - if (!chunk_is_mmapped(p)) { - - /* Has legal address ... */ - if (p != av->top) { - if (contiguous(av)) { - assert(((char *)p) >= min_address); - assert(((char *)p + sz) <= ((char *)(av->top))); - } - } else { - /* top size is always at least MINSIZE */ - assert((CHUNK_SIZE_T)(sz) >= MINSIZE); - /* top predecessor always marked inuse */ - assert(prev_inuse(p)); - } - } else { -#if HAVE_MMAP - /* address is outside main heap */ - if (contiguous(av) && av->top != initial_top(av)) { - assert(((char *)p) < min_address || ((char *)p) > max_address); - } - /* chunk is page-aligned */ - assert(((p->prev_size + sz) & (av->pagesize - 1)) == 0); - /* mem is aligned */ - assert(aligned_OK(chunk2mem(p))); -#else - /* force an appropriate assert violation if debug set */ - assert(!chunk_is_mmapped(p)); -#endif - } -} - -/* - Properties of free chunks -*/ - -#if __STD_C -static void do_check_free_chunk(mchunkptr p) -#else -static void do_check_free_chunk(p) mchunkptr p; -#endif -{ - mstate av = get_malloc_state(); - - INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; - mchunkptr next = chunk_at_offset(p, sz); - - do_check_chunk(p); - - /* Chunk must claim to be free ... */ - assert(!inuse(p)); - assert(!chunk_is_mmapped(p)); - - /* Unless a special marker, must have OK fields */ - if ((CHUNK_SIZE_T)(sz) >= MINSIZE) { - assert((sz & MALLOC_ALIGN_MASK) == 0); - assert(aligned_OK(chunk2mem(p))); - /* ... matching footer field */ - assert(next->prev_size == sz); - /* ... and is fully consolidated */ - assert(prev_inuse(p)); - assert(next == av->top || inuse(next)); - - /* ... and has minimally sane links */ - assert(p->fd->bk == p); - assert(p->bk->fd == p); - } else { /* markers are always of size SIZE_SZ */ - assert(sz == SIZE_SZ); - } -} - -/* - Properties of inuse chunks -*/ - -#if __STD_C -static void do_check_inuse_chunk(mchunkptr p) -#else -static void do_check_inuse_chunk(p) mchunkptr p; -#endif -{ - mstate av = get_malloc_state(); - mchunkptr next; - do_check_chunk(p); - - if (chunk_is_mmapped(p)) { - return; /* mmapped chunks have no next/prev */ - } - - /* Check whether it claims to be in use ... */ - assert(inuse(p)); - - next = next_chunk(p); - - /* ... and is surrounded by OK chunks. - Since more things can be checked with free chunks than inuse ones, - if an inuse chunk borders them and debug is on, it's worth doing them. - */ - if (!prev_inuse(p)) { - /* Note that we cannot even look at prev unless it is not inuse */ - mchunkptr prv = prev_chunk(p); - assert(next_chunk(prv) == p); - do_check_free_chunk(prv); - } - - if (next == av->top) { - assert(prev_inuse(next)); - assert(chunksize(next) >= MINSIZE); - } else if (!inuse(next)) { - do_check_free_chunk(next); - } -} - -/* - Properties of chunks recycled from fastbins -*/ - -#if __STD_C -static void do_check_remalloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) -#else -static void do_check_remalloced_chunk(p, s) mchunkptr p; -INTERNAL_SIZE_T s; -#endif -{ - INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; - - do_check_inuse_chunk(p); - - /* Legal size ... */ - assert((sz & MALLOC_ALIGN_MASK) == 0); - assert((CHUNK_SIZE_T)(sz) >= MINSIZE); - /* ... and alignment */ - assert(aligned_OK(chunk2mem(p))); - /* chunk is less than MINSIZE more than request */ - assert((long)(sz) - (long)(s) >= 0); - assert((long)(sz) - (long)(s + MINSIZE) < 0); -} - -/* - Properties of nonrecycled chunks at the point they are malloced -*/ - -#if __STD_C -static void do_check_malloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) -#else -static void do_check_malloced_chunk(p, s) mchunkptr p; -INTERNAL_SIZE_T s; -#endif -{ - /* same as recycled case ... */ - do_check_remalloced_chunk(p, s); - - /* - ... plus, must obey implementation invariant that prev_inuse is - always true of any allocated chunk; i.e., that each allocated - chunk borders either a previously allocated and still in-use - chunk, or the base of its memory arena. This is ensured - by making all allocations from the the `lowest' part of any found - chunk. This does not necessarily hold however for chunks - recycled via fastbins. - */ - - assert(prev_inuse(p)); -} - -/* - Properties of malloc_state. - - This may be useful for debugging malloc, as well as detecting user - programmer errors that somehow write into malloc_state. - - If you are extending or experimenting with this malloc, you can - probably figure out how to hack this routine to print out or - display chunk addresses, sizes, bins, and other instrumentation. -*/ - -static void do_check_malloc_state(void) { - mstate av = get_malloc_state(); - int i; - mchunkptr p; - mchunkptr q; - mbinptr b; - unsigned int binbit; - int empty; - unsigned int idx; - INTERNAL_SIZE_T size; - CHUNK_SIZE_T total = 0; - int max_fast_bin; - - /* internal size_t must be no wider than pointer type */ - assert(sizeof(INTERNAL_SIZE_T) <= sizeof(char *)); - - /* alignment is a power of 2 */ - assert((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT - 1)) == 0); - - /* cannot run remaining checks until fully initialized */ - if (av->top == 0 || av->top == initial_top(av)) { - return; - } - - /* pagesize is a power of 2 */ - assert((av->pagesize & (av->pagesize - 1)) == 0); - - /* properties of fastbins */ - - /* max_fast is in allowed range */ - assert(get_max_fast(av) <= request2size(MAX_FAST_SIZE)); - - max_fast_bin = fastbin_index(av->max_fast); - - for (i = 0; NFASTBINS - i > 0; ++i) { - p = av->fastbins[i]; - - /* all bins past max_fast are empty */ - if (i > max_fast_bin) { - assert(p == 0); - } - - while (p != 0) { - /* each chunk claims to be inuse */ - do_check_inuse_chunk(p); - total += chunksize(p); - /* chunk belongs in this bin */ - assert(fastbin_index(chunksize(p)) == i); - p = p->fd; - } - } - - if (total != 0) { - assert(have_fastchunks(av)); - } else if (!have_fastchunks(av)) { - assert(total == 0); - } - - /* check normal bins */ - for (i = 1; i < NBINS; ++i) { - b = bin_at(av, i); - - /* binmap is accurate (except for bin 1 == unsorted_chunks) */ - if (i >= 2) { - binbit = get_binmap(av, i); - empty = last(b) == b; - if (!binbit) { - assert(empty); - } else if (!empty) { - assert(binbit); - } - } - - for (p = last(b); p != b; p = p->bk) { - /* each chunk claims to be free */ - do_check_free_chunk(p); - size = chunksize(p); - total += size; - if (i >= 2) { - /* chunk belongs in bin */ - idx = bin_index(size); - assert(idx == i); - /* lists are sorted */ - if ((CHUNK_SIZE_T)size >= (CHUNK_SIZE_T)(FIRST_SORTED_BIN_SIZE)) { - assert(p->bk == b || - (CHUNK_SIZE_T)chunksize(p->bk) >= - (CHUNK_SIZE_T)chunksize(p)); - } - } - /* chunk is followed by a legal chain of inuse chunks */ - for (q = next_chunk(p); - (q != av->top && inuse(q) && - (CHUNK_SIZE_T)(chunksize(q)) >= MINSIZE); - q = next_chunk(q)) { - do_check_inuse_chunk(q); - } - } - } - - /* top chunk is OK */ - check_chunk(av->top); - - /* sanity checks for statistics */ - - assert(total <= (CHUNK_SIZE_T)(av->max_total_mem)); - assert(av->n_mmaps >= 0); - assert(av->n_mmaps <= av->max_n_mmaps); - - assert((CHUNK_SIZE_T)(av->sbrked_mem) <= - (CHUNK_SIZE_T)(av->max_sbrked_mem)); - - assert((CHUNK_SIZE_T)(av->mmapped_mem) <= - (CHUNK_SIZE_T)(av->max_mmapped_mem)); - - assert((CHUNK_SIZE_T)(av->max_total_mem) >= - (CHUNK_SIZE_T)(av->mmapped_mem) + (CHUNK_SIZE_T)(av->sbrked_mem)); -} -#endif - -/* ----------- Routines dealing with system allocation -------------- */ - -/* - sysmalloc handles malloc cases requiring more memory from the system. - On entry, it is assumed that av->top does not have enough - space to service request for nb bytes, thus requiring that av->top - be extended or replaced. -*/ - -#if __STD_C -static Void_t *sYSMALLOc(INTERNAL_SIZE_T nb, mstate av) -#else -static Void_t *sYSMALLOc(nb, av) -INTERNAL_SIZE_T nb; -mstate av; -#endif -{ - mchunkptr old_top; /* incoming value of av->top */ - INTERNAL_SIZE_T old_size; /* its size */ - char *old_end; /* its end address */ - - long size; /* arg to first MORECORE or mmap call */ - char *brk; /* return value from MORECORE */ - - long correction; /* arg to 2nd MORECORE call */ - char *snd_brk; /* 2nd return val */ - - INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of new space */ - INTERNAL_SIZE_T end_misalign; /* partial page left at end of new space */ - char *aligned_brk; /* aligned offset into brk */ - - mchunkptr p; /* the allocated/returned chunk */ - mchunkptr remainder; /* remainder from allocation */ - CHUNK_SIZE_T remainder_size; /* its size */ - - CHUNK_SIZE_T sum; /* for updating stats */ - - size_t pagemask = av->pagesize - 1; - - /* - If there is space available in fastbins, consolidate and retry - malloc from scratch rather than getting memory from system. This - can occur only if nb is in smallbin range so we didn't consolidate - upon entry to malloc. It is much easier to handle this case here - than in malloc proper. - */ - - if (have_fastchunks(av)) { - assert(in_smallbin_range(nb)); - malloc_consolidate(av); - return mALLOc(nb - MALLOC_ALIGN_MASK); - } - -#if HAVE_MMAP - - /* - If have mmap, and the request size meets the mmap threshold, and - the system supports mmap, and there are few enough currently - allocated mmapped regions, try to directly map this request - rather than expanding top. - */ - - if ((CHUNK_SIZE_T)(nb) >= (CHUNK_SIZE_T)(av->mmap_threshold) && - (av->n_mmaps < av->n_mmaps_max)) { - - char *mm; /* return value from mmap call*/ - - /* - Round up size to nearest page. For mmapped chunks, the overhead - is one SIZE_SZ unit larger than for normal chunks, because there - is no following chunk whose prev_size field could be used. - */ - size = (nb + SIZE_SZ + MALLOC_ALIGN_MASK + pagemask) & ~pagemask; - - /* Don't try if size wraps around 0 */ - if ((CHUNK_SIZE_T)(size) > (CHUNK_SIZE_T)(nb)) { - - mm = (char *)(MMAP(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - - if (mm != (char *)(MORECORE_FAILURE)) { - - /* - The offset to the start of the mmapped region is stored - in the prev_size field of the chunk. This allows us to adjust - returned start address to meet alignment requirements here - and in memalign(), and still be able to compute proper - address argument for later munmap in free() and realloc(). - */ - - front_misalign = (INTERNAL_SIZE_T)chunk2mem(mm) & MALLOC_ALIGN_MASK; - if (front_misalign > 0) { - correction = MALLOC_ALIGNMENT - front_misalign; - p = (mchunkptr)(mm + correction); - p->prev_size = correction; - set_head(p, (size - correction) | IS_MMAPPED); - } else { - p = (mchunkptr)mm; - p->prev_size = 0; - set_head(p, size | IS_MMAPPED); - } - - /* update statistics */ - - if (++av->n_mmaps > av->max_n_mmaps) { - av->max_n_mmaps = av->n_mmaps; - } - - sum = av->mmapped_mem += size; - if (sum > (CHUNK_SIZE_T)(av->max_mmapped_mem)) { - av->max_mmapped_mem = sum; - } - sum += av->sbrked_mem; - if (sum > (CHUNK_SIZE_T)(av->max_total_mem)) { - av->max_total_mem = sum; - } - - check_chunk(p); - - return chunk2mem(p); - } - } - } -#endif - - /* Record incoming configuration of top */ - - old_top = av->top; - old_size = chunksize(old_top); - old_end = (char *)(chunk_at_offset(old_top, old_size)); - - brk = snd_brk = (char *)(MORECORE_FAILURE); - - /* - If not the first time through, we require old_size to be - at least MINSIZE and to have prev_inuse set. - */ - - assert((old_top == initial_top(av) && old_size == 0) || - ((CHUNK_SIZE_T)(old_size) >= MINSIZE && - prev_inuse(old_top))); - - /* Precondition: not enough current space to satisfy nb request */ - assert((CHUNK_SIZE_T)(old_size) < (CHUNK_SIZE_T)(nb + MINSIZE)); - - /* Precondition: all fastbins are consolidated */ - assert(!have_fastchunks(av)); - - /* Request enough space for nb + pad + overhead */ - - size = nb + av->top_pad + MINSIZE; - - /* - If contiguous, we can subtract out existing space that we hope to - combine with new space. We add it back later only if - we don't actually get contiguous space. - */ - - if (contiguous(av)) { - size -= old_size; - } - - /* - Round to a multiple of page size. - If MORECORE is not contiguous, this ensures that we only call it - with whole-page arguments. And if MORECORE is contiguous and - this is not first time through, this preserves page-alignment of - previous calls. Otherwise, we correct to page-align below. - */ - - size = (size + pagemask) & ~pagemask; - - /* - Don't try to call MORECORE if argument is so big as to appear - negative. Note that since mmap takes size_t arg, it may succeed - below even if we cannot call MORECORE. - */ - - if (size > 0) { - brk = (char *)(MORECORE(size)); - } - - /* - If have mmap, try using it as a backup when MORECORE fails or - cannot be used. This is worth doing on systems that have "holes" in - address space, so sbrk cannot extend to give contiguous space, but - space is available elsewhere. Note that we ignore mmap max count - and threshold limits, since the space will not be used as a - segregated mmap region. - */ - -#if HAVE_MMAP - if (brk == (char *)(MORECORE_FAILURE)) { - - /* Cannot merge with old top, so add its size back in */ - if (contiguous(av)) { - size = (size + old_size + pagemask) & ~pagemask; - } - - /* If we are relying on mmap as backup, then use larger units */ - if ((CHUNK_SIZE_T)(size) < (CHUNK_SIZE_T)(MMAP_AS_MORECORE_SIZE)) { - size = MMAP_AS_MORECORE_SIZE; - } - - /* Don't try if size wraps around 0 */ - if ((CHUNK_SIZE_T)(size) > (CHUNK_SIZE_T)(nb)) { - - brk = (char *)(MMAP(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - - if (brk != (char *)(MORECORE_FAILURE)) { - - /* We do not need, and cannot use, another sbrk call to find end */ - snd_brk = brk + size; - - /* - Record that we no longer have a contiguous sbrk region. - After the first time mmap is used as backup, we do not - ever rely on contiguous space since this could incorrectly - bridge regions. - */ - set_noncontiguous(av); - } - } - } -#endif - - if (brk != (char *)(MORECORE_FAILURE)) { - av->sbrked_mem += size; - - /* - If MORECORE extends previous space, we can likewise extend top size. - */ - - if (brk == old_end && snd_brk == (char *)(MORECORE_FAILURE)) { - set_head(old_top, (size + old_size) | PREV_INUSE); - } - - /* - Otherwise, make adjustments: - - * If the first time through or noncontiguous, we need to call sbrk - just to find out where the end of memory lies. - - * We need to ensure that all returned chunks from malloc will meet - MALLOC_ALIGNMENT - - * If there was an intervening foreign sbrk, we need to adjust sbrk - request size to account for fact that we will not be able to - combine new space with existing space in old_top. - - * Almost all systems internally allocate whole pages at a time, in - which case we might as well use the whole last page of request. - So we allocate enough more memory to hit a page boundary now, - which in turn causes future contiguous calls to page-align. - */ - - else { - front_misalign = 0; - end_misalign = 0; - correction = 0; - aligned_brk = brk; - - /* - If MORECORE returns an address lower than we have seen before, - we know it isn't really contiguous. This and some subsequent - checks help cope with non-conforming MORECORE functions and - the presence of "foreign" calls to MORECORE from outside of - malloc or by other threads. We cannot guarantee to detect - these in all cases, but cope with the ones we do detect. - */ - if (contiguous(av) && old_size != 0 && brk < old_end) { - set_noncontiguous(av); - } - - /* handle contiguous cases */ - if (contiguous(av)) { - - /* - We can tolerate forward non-contiguities here (usually due - to foreign calls) but treat them as part of our space for - stats reporting. - */ - if (old_size != 0) { - av->sbrked_mem += brk - old_end; - } - - /* Guarantee alignment of first new chunk made from this space */ - - front_misalign = (INTERNAL_SIZE_T)chunk2mem(brk) & MALLOC_ALIGN_MASK; - if (front_misalign > 0) { - - /* - Skip over some bytes to arrive at an aligned position. - We don't need to specially mark these wasted front bytes. - They will never be accessed anyway because - prev_inuse of av->top (and any chunk created from its start) - is always true after initialization. - */ - - correction = MALLOC_ALIGNMENT - front_misalign; - aligned_brk += correction; - } - - /* - If this isn't adjacent to existing space, then we will not - be able to merge with old_top space, so must add to 2nd request. - */ - - correction += old_size; - - /* Extend the end address to hit a page boundary */ - end_misalign = (INTERNAL_SIZE_T)(brk + size + correction); - correction += ((end_misalign + pagemask) & ~pagemask) - end_misalign; - - assert(correction >= 0); - snd_brk = (char *)(MORECORE(correction)); - - if (snd_brk == (char *)(MORECORE_FAILURE)) { - /* - If can't allocate correction, try to at least find out current - brk. It might be enough to proceed without failing. - */ - correction = 0; - snd_brk = (char *)(MORECORE(0)); - } else if (snd_brk < brk) { - /* - If the second call gives noncontiguous space even though - it says it won't, the only course of action is to ignore - results of second call, and conservatively estimate where - the first call left us. Also set noncontiguous, so this - won't happen again, leaving at most one hole. - - Note that this check is intrinsically incomplete. Because - MORECORE is allowed to give more space than we ask for, - there is no reliable way to detect a noncontiguity - producing a forward gap for the second call. - */ - snd_brk = brk + size; - correction = 0; - set_noncontiguous(av); - } - } - - /* handle non-contiguous cases */ - else { - /* MORECORE/mmap must correctly align */ - assert(aligned_OK(chunk2mem(brk))); - - /* Find out current end of memory */ - if (snd_brk == (char *)(MORECORE_FAILURE)) { - snd_brk = (char *)(MORECORE(0)); - av->sbrked_mem += snd_brk - brk - size; - } - } - - /* Adjust top based on results of second sbrk */ - if (snd_brk != (char *)(MORECORE_FAILURE)) { - av->top = (mchunkptr)aligned_brk; - set_head(av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE); - av->sbrked_mem += correction; - - /* - If not the first time through, we either have a - gap due to foreign sbrk or a non-contiguous region. Insert a - double fencepost at old_top to prevent consolidation with space - we don't own. These fenceposts are artificial chunks that are - marked as inuse and are in any case too small to use. We need - two to make sizes and alignments work out. - */ - - if (old_size != 0) { - /* - Shrink old_top to insert fenceposts, keeping size a - multiple of MALLOC_ALIGNMENT. We know there is at least - enough space in old_top to do this. - */ - old_size = (old_size - 3 * SIZE_SZ) & ~MALLOC_ALIGN_MASK; - set_head(old_top, old_size | PREV_INUSE); - - /* - Note that the following assignments completely overwrite - old_top when old_size was previously MINSIZE. This is - intentional. We need the fencepost, even if old_top otherwise gets - lost. - */ - chunk_at_offset(old_top, old_size)->size = - SIZE_SZ | PREV_INUSE; - - chunk_at_offset(old_top, old_size + SIZE_SZ)->size = - SIZE_SZ | PREV_INUSE; - - /* - If possible, release the rest, suppressing trimming. - */ - if (old_size >= MINSIZE) { - INTERNAL_SIZE_T tt = av->trim_threshold; - av->trim_threshold = (INTERNAL_SIZE_T)(-1); - fREe(chunk2mem(old_top)); - av->trim_threshold = tt; - } - } - } - } - - /* Update statistics */ - sum = av->sbrked_mem; - if (sum > (CHUNK_SIZE_T)(av->max_sbrked_mem)) { - av->max_sbrked_mem = sum; - } - - sum += av->mmapped_mem; - if (sum > (CHUNK_SIZE_T)(av->max_total_mem)) { - av->max_total_mem = sum; - } - - check_malloc_state(); - - /* finally, do the allocation */ - - p = av->top; - size = chunksize(p); - - /* check that one of the above allocation paths succeeded */ - if ((CHUNK_SIZE_T)(size) >= (CHUNK_SIZE_T)(nb + MINSIZE)) { - remainder_size = size - nb; - remainder = chunk_at_offset(p, nb); - av->top = remainder; - set_head(p, nb | PREV_INUSE); - set_head(remainder, remainder_size | PREV_INUSE); - check_malloced_chunk(p, nb); - return chunk2mem(p); - } - } - - /* catch all failure paths */ - MALLOC_FAILURE_ACTION; - return 0; -} - -/* - sYSTRIm is an inverse of sorts to sYSMALLOc. It gives memory back - to the system (via negative arguments to sbrk) if there is unused - memory at the `high' end of the malloc pool. It is called - automatically by free() when top space exceeds the trim - threshold. It is also called by the public malloc_trim routine. It - returns 1 if it actually released any memory, else 0. -*/ - -#if __STD_C -static int sYSTRIm(size_t pad, mstate av) -#else -static int sYSTRIm(pad, av) -size_t pad; -mstate av; -#endif -{ - long top_size; /* Amount of top-most memory */ - long extra; /* Amount to release */ - long released; /* Amount actually released */ - char *current_brk; /* address returned by pre-check sbrk call */ - char *new_brk; /* address returned by post-check sbrk call */ - size_t pagesz; - - pagesz = av->pagesize; - top_size = chunksize(av->top); - - /* Release in pagesize units, keeping at least one page */ - extra = ((top_size - pad - MINSIZE + (pagesz - 1)) / pagesz - 1) * pagesz; - - if (extra > 0) { - - /* - Only proceed if end of memory is where we last set it. - This avoids problems if there were foreign sbrk calls. - */ - current_brk = (char *)(MORECORE(0)); - if (current_brk == (char *)(av->top) + top_size) { - - /* - Attempt to release memory. We ignore MORECORE return value, - and instead call again to find out where new end of memory is. - This avoids problems if first call releases less than we asked, - of if failure somehow altered brk value. (We could still - encounter problems if it altered brk in some very bad way, - but the only thing we can do is adjust anyway, which will cause - some downstream failure.) - */ - - MORECORE(-extra); - new_brk = (char *)(MORECORE(0)); - - if (new_brk != (char *)MORECORE_FAILURE) { - released = (long)(current_brk - new_brk); - - if (released != 0) { - /* Success. Adjust top. */ - av->sbrked_mem -= released; - set_head(av->top, (top_size - released) | PREV_INUSE); - check_malloc_state(); - return 1; - } - } - } - } - return 0; -} - -/* - ------------------------------ malloc ------------------------------ -*/ - -#if __STD_C -Void_t *mALLOc(size_t bytes) -#else -Void_t *mALLOc(bytes) -size_t bytes; -#endif -{ - mstate av = get_malloc_state(); - - INTERNAL_SIZE_T nb; /* normalized request size */ - unsigned int idx; /* associated bin index */ - mbinptr bin; /* associated bin */ - mfastbinptr *fb; /* associated fastbin */ - - mchunkptr victim; /* inspected/selected chunk */ - INTERNAL_SIZE_T size; /* its size */ - int victim_index; /* its bin index */ - - mchunkptr remainder; /* remainder from a split */ - CHUNK_SIZE_T remainder_size; /* its size */ - - unsigned int block; /* bit map traverser */ - unsigned int bit; /* bit map traverser */ - unsigned int map; /* current word of binmap */ - - mchunkptr fwd; /* misc temp for linking */ - mchunkptr bck; /* misc temp for linking */ - - /* - Convert request size to internal form by adding SIZE_SZ bytes - overhead plus possibly more to obtain necessary alignment and/or - to obtain a size of at least MINSIZE, the smallest allocatable - size. Also, checked_request2size traps (returning 0) request sizes - that are so large that they wrap around zero when padded and - aligned. - */ - - checked_request2size(bytes, nb); - - /* - Bypass search if no frees yet - */ - if (!have_anychunks(av)) { - if (av->max_fast == 0) { /* initialization check */ - malloc_consolidate(av); - } - goto use_top; - } - - /* - If the size qualifies as a fastbin, first check corresponding bin. - */ - - if ((CHUNK_SIZE_T)(nb) <= (CHUNK_SIZE_T)(av->max_fast)) { - fb = &(av->fastbins[(fastbin_index(nb))]); - if ((victim = *fb) != 0) { - *fb = victim->fd; - check_remalloced_chunk(victim, nb); - return chunk2mem(victim); - } - } - - /* - If a small request, check regular bin. Since these "smallbins" - hold one size each, no searching within bins is necessary. - (For a large request, we need to wait until unsorted chunks are - processed to find best fit. But for small ones, fits are exact - anyway, so we can check now, which is faster.) - */ - - if (in_smallbin_range(nb)) { - idx = smallbin_index(nb); - bin = bin_at(av, idx); - - if ((victim = last(bin)) != bin) { - bck = victim->bk; - set_inuse_bit_at_offset(victim, nb); - bin->bk = bck; - bck->fd = bin; - - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - } - - /* - If this is a large request, consolidate fastbins before continuing. - While it might look excessive to kill all fastbins before - even seeing if there is space available, this avoids - fragmentation problems normally associated with fastbins. - Also, in practice, programs tend to have runs of either small or - large requests, but less often mixtures, so consolidation is not - invoked all that often in most programs. And the programs that - it is called frequently in otherwise tend to fragment. - */ - - else { - idx = largebin_index(nb); - if (have_fastchunks(av)) { - malloc_consolidate(av); - } - } - - /* - Process recently freed or remaindered chunks, taking one only if - it is exact fit, or, if this a small request, the chunk is remainder from - the most recent non-exact fit. Place other traversed chunks in - bins. Note that this step is the only place in any routine where - chunks are placed in bins. - */ - - while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) { - bck = victim->bk; - size = chunksize(victim); - - /* - If a small request, try to use last remainder if it is the - only chunk in unsorted bin. This helps promote locality for - runs of consecutive small requests. This is the only - exception to best-fit, and applies only when there is - no exact fit for a small chunk. - */ - - if (in_smallbin_range(nb) && - bck == unsorted_chunks(av) && - victim == av->last_remainder && - (CHUNK_SIZE_T)(size) > (CHUNK_SIZE_T)(nb + MINSIZE)) { - - /* split and reattach remainder */ - remainder_size = size - nb; - remainder = chunk_at_offset(victim, nb); - unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder; - av->last_remainder = remainder; - remainder->bk = remainder->fd = unsorted_chunks(av); - - set_head(victim, nb | PREV_INUSE); - set_head(remainder, remainder_size | PREV_INUSE); - set_foot(remainder, remainder_size); - - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - - /* remove from unsorted list */ - unsorted_chunks(av)->bk = bck; - bck->fd = unsorted_chunks(av); - - /* Take now instead of binning if exact fit */ - - if (size == nb) { - set_inuse_bit_at_offset(victim, size); - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - - /* place chunk in bin */ - - if (in_smallbin_range(size)) { - victim_index = smallbin_index(size); - bck = bin_at(av, victim_index); - fwd = bck->fd; - } else { - victim_index = largebin_index(size); - bck = bin_at(av, victim_index); - fwd = bck->fd; - - if (fwd != bck) { - /* if smaller than smallest, place first */ - if ((CHUNK_SIZE_T)(size) < (CHUNK_SIZE_T)(bck->bk->size)) { - fwd = bck; - bck = bck->bk; - } else if ((CHUNK_SIZE_T)(size) >= - (CHUNK_SIZE_T)(FIRST_SORTED_BIN_SIZE)) { - - /* maintain large bins in sorted order */ - size |= PREV_INUSE; /* Or with inuse bit to speed comparisons */ - while ((CHUNK_SIZE_T)(size) < (CHUNK_SIZE_T)(fwd->size)) { - fwd = fwd->fd; - } - bck = fwd->bk; - } - } - } - - mark_bin(av, victim_index); - victim->bk = bck; - victim->fd = fwd; - fwd->bk = victim; - bck->fd = victim; - } - - /* - If a large request, scan through the chunks of current bin to - find one that fits. (This will be the smallest that fits unless - FIRST_SORTED_BIN_SIZE has been changed from default.) This is - the only step where an unbounded number of chunks might be - scanned without doing anything useful with them. However the - lists tend to be short. - */ - - if (!in_smallbin_range(nb)) { - bin = bin_at(av, idx); - - for (victim = last(bin); victim != bin; victim = victim->bk) { - size = chunksize(victim); - - if ((CHUNK_SIZE_T)(size) >= (CHUNK_SIZE_T)(nb)) { - remainder_size = size - nb; - unlink(victim, bck, fwd); - - /* Exhaust */ - if (remainder_size < MINSIZE) { - set_inuse_bit_at_offset(victim, size); - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - /* Split */ - else { - remainder = chunk_at_offset(victim, nb); - unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder; - remainder->bk = remainder->fd = unsorted_chunks(av); - set_head(victim, nb | PREV_INUSE); - set_head(remainder, remainder_size | PREV_INUSE); - set_foot(remainder, remainder_size); - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - } - } - } - - /* - Search for a chunk by scanning bins, starting with next largest - bin. This search is strictly by best-fit; i.e., the smallest - (with ties going to approximately the least recently used) chunk - that fits is selected. - - The bitmap avoids needing to check that most blocks are nonempty. - */ - - ++idx; - bin = bin_at(av, idx); - block = idx2block(idx); - map = av->binmap[block]; - bit = idx2bit(idx); - - for (;;) { - - /* Skip rest of block if there are no more set bits in this block. */ - if (bit > map || bit == 0) { - do { - if (++block >= BINMAPSIZE) { /* out of bins */ - goto use_top; - } - } while ((map = av->binmap[block]) == 0); - - bin = bin_at(av, (block << BINMAPSHIFT)); - bit = 1; - } - - /* Advance to bin with set bit. There must be one. */ - while ((bit & map) == 0) { - bin = next_bin(bin); - bit <<= 1; - assert(bit != 0); - } - - /* Inspect the bin. It is likely to be non-empty */ - victim = last(bin); - - /* If a false alarm (empty bin), clear the bit. */ - if (victim == bin) { - av->binmap[block] = map &= ~bit; /* Write through */ - bin = next_bin(bin); - bit <<= 1; - } - - else { - size = chunksize(victim); - - /* We know the first chunk in this bin is big enough to use. */ - assert((CHUNK_SIZE_T)(size) >= (CHUNK_SIZE_T)(nb)); - - remainder_size = size - nb; - - /* unlink */ - bck = victim->bk; - bin->bk = bck; - bck->fd = bin; - - /* Exhaust */ - if (remainder_size < MINSIZE) { - set_inuse_bit_at_offset(victim, size); - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - - /* Split */ - else { - remainder = chunk_at_offset(victim, nb); - - unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder; - remainder->bk = remainder->fd = unsorted_chunks(av); - /* advertise as last remainder */ - if (in_smallbin_range(nb)) { - av->last_remainder = remainder; - } - - set_head(victim, nb | PREV_INUSE); - set_head(remainder, remainder_size | PREV_INUSE); - set_foot(remainder, remainder_size); - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - } - } - -use_top: - /* - If large enough, split off the chunk bordering the end of memory - (held in av->top). Note that this is in accord with the best-fit - search rule. In effect, av->top is treated as larger (and thus - less well fitting) than any other available chunk since it can - be extended to be as large as necessary (up to system - limitations). - - We require that av->top always exists (i.e., has size >= - MINSIZE) after initialization, so if it would otherwise be - exhuasted by current request, it is replenished. (The main - reason for ensuring it exists is that we may need MINSIZE space - to put in fenceposts in sysmalloc.) - */ - - victim = av->top; - size = chunksize(victim); - - if ((CHUNK_SIZE_T)(size) >= (CHUNK_SIZE_T)(nb + MINSIZE)) { - remainder_size = size - nb; - remainder = chunk_at_offset(victim, nb); - av->top = remainder; - set_head(victim, nb | PREV_INUSE); - set_head(remainder, remainder_size | PREV_INUSE); - - check_malloced_chunk(victim, nb); - return chunk2mem(victim); - } - - /* - If no space in top, relay to handle system-dependent cases - */ - return sYSMALLOc(nb, av); -} - -/* - ------------------------------ free ------------------------------ -*/ - -#if __STD_C -void fREe(Void_t *mem) -#else -void fREe(mem) Void_t *mem; -#endif -{ - mstate av = get_malloc_state(); - - mchunkptr p; /* chunk corresponding to mem */ - INTERNAL_SIZE_T size; /* its size */ - mfastbinptr *fb; /* associated fastbin */ - mchunkptr nextchunk; /* next contiguous chunk */ - INTERNAL_SIZE_T nextsize; /* its size */ - int nextinuse; /* true if nextchunk is used */ - INTERNAL_SIZE_T prevsize; /* size of previous contiguous chunk */ - mchunkptr bck; /* misc temp for linking */ - mchunkptr fwd; /* misc temp for linking */ - - /* free(0) has no effect */ - if (mem != 0) { - p = mem2chunk(mem); - size = chunksize(p); - - check_inuse_chunk(p); - - /* - If eligible, place chunk on a fastbin so it can be found - and used quickly in malloc. - */ - - if ((CHUNK_SIZE_T)(size) <= (CHUNK_SIZE_T)(av->max_fast) - -#if TRIM_FASTBINS - /* - If TRIM_FASTBINS set, don't place chunks - bordering top into fastbins - */ - && (chunk_at_offset(p, size) != av->top) -#endif - ) { - - set_fastchunks(av); - fb = &(av->fastbins[fastbin_index(size)]); - p->fd = *fb; - *fb = p; - } - - /* - Consolidate other non-mmapped chunks as they arrive. - */ - - else if (!chunk_is_mmapped(p)) { - set_anychunks(av); - - nextchunk = chunk_at_offset(p, size); - nextsize = chunksize(nextchunk); - - /* consolidate backward */ - if (!prev_inuse(p)) { - prevsize = p->prev_size; - size += prevsize; - p = chunk_at_offset(p, -((long)prevsize)); - unlink(p, bck, fwd); - } - - if (nextchunk != av->top) { - /* get and clear inuse bit */ - nextinuse = inuse_bit_at_offset(nextchunk, nextsize); - set_head(nextchunk, nextsize); - - /* consolidate forward */ - if (!nextinuse) { - unlink(nextchunk, bck, fwd); - size += nextsize; - } - - /* - Place the chunk in unsorted chunk list. Chunks are - not placed into regular bins until after they have - been given one chance to be used in malloc. - */ - - bck = unsorted_chunks(av); - fwd = bck->fd; - p->bk = bck; - p->fd = fwd; - bck->fd = p; - fwd->bk = p; - - set_head(p, size | PREV_INUSE); - set_foot(p, size); - - check_free_chunk(p); - } - - /* - If the chunk borders the current high end of memory, - consolidate into top - */ - - else { - size += nextsize; - set_head(p, size | PREV_INUSE); - av->top = p; - check_chunk(p); - } - - /* - If freeing a large space, consolidate possibly-surrounding - chunks. Then, if the total unused topmost memory exceeds trim - threshold, ask malloc_trim to reduce top. - - Unless max_fast is 0, we don't know if there are fastbins - bordering top, so we cannot tell for sure whether threshold - has been reached unless fastbins are consolidated. But we - don't want to consolidate on each free. As a compromise, - consolidation is performed if FASTBIN_CONSOLIDATION_THRESHOLD - is reached. - */ - - if ((CHUNK_SIZE_T)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) { - if (have_fastchunks(av)) { - malloc_consolidate(av); - } - -#ifndef MORECORE_CANNOT_TRIM - if ((CHUNK_SIZE_T)(chunksize(av->top)) >= - (CHUNK_SIZE_T)(av->trim_threshold)) { - sYSTRIm(av->top_pad, av); - } -#endif - } - } - /* - If the chunk was allocated via mmap, release via munmap() - Note that if HAVE_MMAP is false but chunk_is_mmapped is - true, then user must have overwritten memory. There's nothing - we can do to catch this error unless DL_DEBUG is set, in which case - check_inuse_chunk (above) will have triggered error. - */ - - else { -#if HAVE_MMAP - INTERNAL_SIZE_T offset = p->prev_size; - av->n_mmaps--; - av->mmapped_mem -= (size + offset); - munmap((char *)p - offset, size + offset); -#endif - } - } -} - -/* - ------------------------- malloc_consolidate ------------------------- - - malloc_consolidate is a specialized version of free() that tears - down chunks held in fastbins. Free itself cannot be used for this - purpose since, among other things, it might place chunks back onto - fastbins. So, instead, we need to use a minor variant of the same - code. - - Also, because this routine needs to be called the first time through - malloc anyway, it turns out to be the perfect place to trigger - initialization code. -*/ - -#if __STD_C -static void malloc_consolidate(mstate av) -#else -static void malloc_consolidate(av) mstate av; -#endif -{ - mfastbinptr *fb; /* current fastbin being consolidated */ - mfastbinptr *maxfb; /* last fastbin (for loop control) */ - mchunkptr p; /* current chunk being consolidated */ - mchunkptr nextp; /* next chunk to consolidate */ - mchunkptr unsorted_bin; /* bin header */ - mchunkptr first_unsorted; /* chunk to link to */ - - /* These have same use as in free() */ - mchunkptr nextchunk; - INTERNAL_SIZE_T size; - INTERNAL_SIZE_T nextsize; - INTERNAL_SIZE_T prevsize; - int nextinuse; - mchunkptr bck; - mchunkptr fwd; - - /* - If max_fast is 0, we know that av hasn't - yet been initialized, in which case do so below - */ - - if (av->max_fast != 0) { - clear_fastchunks(av); - - unsorted_bin = unsorted_chunks(av); - - /* - Remove each chunk from fast bin and consolidate it, placing it - then in unsorted bin. Among other reasons for doing this, - placing in unsorted bin avoids needing to calculate actual bins - until malloc is sure that chunks aren't immediately going to be - reused anyway. - */ - - maxfb = &(av->fastbins[fastbin_index(av->max_fast)]); - fb = &(av->fastbins[0]); - do { - if ((p = *fb) != 0) { - *fb = 0; - - do { - check_inuse_chunk(p); - nextp = p->fd; - - /* Slightly streamlined version of consolidation code in free() */ - size = p->size & ~PREV_INUSE; - nextchunk = chunk_at_offset(p, size); - nextsize = chunksize(nextchunk); - - if (!prev_inuse(p)) { - prevsize = p->prev_size; - size += prevsize; - p = chunk_at_offset(p, -((long)prevsize)); - unlink(p, bck, fwd); - } - - if (nextchunk != av->top) { - nextinuse = inuse_bit_at_offset(nextchunk, nextsize); - set_head(nextchunk, nextsize); - - if (!nextinuse) { - size += nextsize; - unlink(nextchunk, bck, fwd); - } - - first_unsorted = unsorted_bin->fd; - unsorted_bin->fd = p; - first_unsorted->bk = p; - - set_head(p, size | PREV_INUSE); - p->bk = unsorted_bin; - p->fd = first_unsorted; - set_foot(p, size); - } - - else { - size += nextsize; - set_head(p, size | PREV_INUSE); - av->top = p; - } - - } while ((p = nextp) != 0); - } - } while (fb++ != maxfb); - } else { - malloc_init_state(av); - check_malloc_state(); - } -} - -/* - ------------------------------ realloc ------------------------------ -*/ - -#if __STD_C -Void_t *rEALLOc(Void_t *oldmem, size_t bytes) -#else -Void_t *rEALLOc(oldmem, bytes) -Void_t *oldmem; -size_t bytes; -#endif -{ - mstate av = get_malloc_state(); - - INTERNAL_SIZE_T nb; /* padded request size */ - - mchunkptr oldp; /* chunk corresponding to oldmem */ - INTERNAL_SIZE_T oldsize; /* its size */ - - mchunkptr newp; /* chunk to return */ - INTERNAL_SIZE_T newsize; /* its size */ - Void_t *newmem; /* corresponding user mem */ - - mchunkptr next; /* next contiguous chunk after oldp */ - - mchunkptr remainder; /* extra space at end of newp */ - CHUNK_SIZE_T remainder_size; /* its size */ - - mchunkptr bck; /* misc temp for linking */ - mchunkptr fwd; /* misc temp for linking */ - - CHUNK_SIZE_T copysize; /* bytes to copy */ - unsigned int ncopies; /* INTERNAL_SIZE_T words to copy */ - INTERNAL_SIZE_T *s; /* copy source */ - INTERNAL_SIZE_T *d; /* copy destination */ - -#ifdef REALLOC_ZERO_BYTES_FREES - if (bytes == 0) { - fREe(oldmem); - return 0; - } -#endif - - /* realloc of null is supposed to be same as malloc */ - if (oldmem == 0) { - return mALLOc(bytes); - } - - checked_request2size(bytes, nb); - - oldp = mem2chunk(oldmem); - oldsize = chunksize(oldp); - - check_inuse_chunk(oldp); - - if (!chunk_is_mmapped(oldp)) { - - if ((CHUNK_SIZE_T)(oldsize) >= (CHUNK_SIZE_T)(nb)) { - /* already big enough; split below */ - newp = oldp; - newsize = oldsize; - } - - else { - next = chunk_at_offset(oldp, oldsize); - - /* Try to expand forward into top */ - if (next == av->top && - (CHUNK_SIZE_T)(newsize = oldsize + chunksize(next)) >= - (CHUNK_SIZE_T)(nb + MINSIZE)) { - set_head_size(oldp, nb); - av->top = chunk_at_offset(oldp, nb); - set_head(av->top, (newsize - nb) | PREV_INUSE); - return chunk2mem(oldp); - } - - /* Try to expand forward into next chunk; split off remainder below */ - else if (next != av->top && - !inuse(next) && - (CHUNK_SIZE_T)(newsize = oldsize + chunksize(next)) >= - (CHUNK_SIZE_T)(nb)) { - newp = oldp; - unlink(next, bck, fwd); - } - - /* allocate, copy, free */ - else { - newmem = mALLOc(nb - MALLOC_ALIGN_MASK); - if (newmem == 0) { - return 0; /* propagate failure */ - } - - newp = mem2chunk(newmem); - newsize = chunksize(newp); - - /* - Avoid copy if newp is next chunk after oldp. - */ - if (newp == next) { - newsize += oldsize; - newp = oldp; - } else { - /* - Unroll copy of <= 36 bytes (72 if 8byte sizes) - We know that contents have an odd number of - INTERNAL_SIZE_T-sized words; minimally 3. - */ - - copysize = oldsize - SIZE_SZ; - s = (INTERNAL_SIZE_T *)(oldmem); - d = (INTERNAL_SIZE_T *)(newmem); - ncopies = copysize / sizeof(INTERNAL_SIZE_T); - assert(ncopies >= 3); - - if (ncopies > 9) { - MALLOC_COPY(d, s, copysize); - } - - else { - *(d + 0) = *(s + 0); - *(d + 1) = *(s + 1); - *(d + 2) = *(s + 2); - if (ncopies > 4) { - *(d + 3) = *(s + 3); - *(d + 4) = *(s + 4); - if (ncopies > 6) { - *(d + 5) = *(s + 5); - *(d + 6) = *(s + 6); - if (ncopies > 8) { - *(d + 7) = *(s + 7); - *(d + 8) = *(s + 8); - } - } - } - } - - fREe(oldmem); - check_inuse_chunk(newp); - return chunk2mem(newp); - } - } - } - - /* If possible, free extra space in old or extended chunk */ - - assert((CHUNK_SIZE_T)(newsize) >= (CHUNK_SIZE_T)(nb)); - - remainder_size = newsize - nb; - - if (remainder_size < MINSIZE) { /* not enough extra to split off */ - set_head_size(newp, newsize); - set_inuse_bit_at_offset(newp, newsize); - } else { /* split remainder */ - remainder = chunk_at_offset(newp, nb); - set_head_size(newp, nb); - set_head(remainder, remainder_size | PREV_INUSE); - /* Mark remainder as inuse so free() won't complain */ - set_inuse_bit_at_offset(remainder, remainder_size); - fREe(chunk2mem(remainder)); - } - - check_inuse_chunk(newp); - return chunk2mem(newp); - } - - /* - Handle mmap cases - */ - - else { -#if HAVE_MMAP - -#if HAVE_MREMAP - INTERNAL_SIZE_T offset = oldp->prev_size; - size_t pagemask = av->pagesize - 1; - char *cp; - CHUNK_SIZE_T sum; - - /* Note the extra SIZE_SZ overhead */ - newsize = (nb + offset + SIZE_SZ + pagemask) & ~pagemask; - - /* don't need to remap if still within same page */ - if (oldsize == newsize - offset) { - return oldmem; - } - - cp = (char *)mremap((char *)oldp - offset, oldsize + offset, newsize, 1); - - if (cp != (char *)MORECORE_FAILURE) { - - newp = (mchunkptr)(cp + offset); - set_head(newp, (newsize - offset) | IS_MMAPPED); - - assert(aligned_OK(chunk2mem(newp))); - assert((newp->prev_size == offset)); - - /* update statistics */ - sum = av->mmapped_mem += newsize - oldsize; - if (sum > (CHUNK_SIZE_T)(av->max_mmapped_mem)) { - av->max_mmapped_mem = sum; - } - sum += av->sbrked_mem; - if (sum > (CHUNK_SIZE_T)(av->max_total_mem)) { - av->max_total_mem = sum; - } - - return chunk2mem(newp); - } -#endif - - /* Note the extra SIZE_SZ overhead. */ - if ((CHUNK_SIZE_T)(oldsize) >= (CHUNK_SIZE_T)(nb + SIZE_SZ)) { - newmem = oldmem; /* do nothing */ - } else { - /* Must alloc, copy, free. */ - newmem = mALLOc(nb - MALLOC_ALIGN_MASK); - if (newmem != 0) { - MALLOC_COPY(newmem, oldmem, oldsize - 2 * SIZE_SZ); - fREe(oldmem); - } - } - return newmem; - -#else - /* If !HAVE_MMAP, but chunk_is_mmapped, user must have overwritten mem */ - check_malloc_state(); - MALLOC_FAILURE_ACTION; - return 0; -#endif - } -} - -/* - ------------------------------ memalign ------------------------------ -*/ - -#if __STD_C -Void_t *mEMALIGn(size_t alignment, size_t bytes) -#else -Void_t *mEMALIGn(alignment, bytes) -size_t alignment; -size_t bytes; -#endif -{ - INTERNAL_SIZE_T nb; /* padded request size */ - char *m; /* memory returned by malloc call */ - mchunkptr p; /* corresponding chunk */ - char *brk; /* alignment point within p */ - mchunkptr newp; /* chunk to return */ - INTERNAL_SIZE_T newsize; /* its size */ - INTERNAL_SIZE_T leadsize; /* leading space before alignment point */ - mchunkptr remainder; /* spare room at end to split off */ - CHUNK_SIZE_T remainder_size; /* its size */ - INTERNAL_SIZE_T size; - - /* If need less alignment than we give anyway, just relay to malloc */ - - if (alignment <= MALLOC_ALIGNMENT) { - return mALLOc(bytes); - } - - /* Otherwise, ensure that it is at least a minimum chunk size */ - - if (alignment < MINSIZE) { - alignment = MINSIZE; - } - - /* Make sure alignment is power of 2 (in case MINSIZE is not). */ - if ((alignment & (alignment - 1)) != 0) { - size_t a = MALLOC_ALIGNMENT * 2; - while ((CHUNK_SIZE_T)a < (CHUNK_SIZE_T)alignment) { - a <<= 1; - } - alignment = a; - } - - checked_request2size(bytes, nb); - - /* - Strategy: find a spot within that chunk that meets the alignment - request, and then possibly free the leading and trailing space. - */ - - /* Call malloc with worst case padding to hit alignment. */ - - m = (char *)(mALLOc(nb + alignment + MINSIZE)); - - if (m == 0) { - return 0; /* propagate failure */ - } - - p = mem2chunk(m); - - if ((((PTR_UINT)(m)) % alignment) != 0) { /* misaligned */ - - /* - Find an aligned spot inside chunk. Since we need to give back - leading space in a chunk of at least MINSIZE, if the first - calculation places us at a spot with less than MINSIZE leader, - we can move to the next aligned spot -- we've allocated enough - total room so that this is always possible. - */ - - brk = (char *)mem2chunk((PTR_UINT)(((PTR_UINT)(m + alignment - 1)) & - -((signed long)alignment))); - if ((CHUNK_SIZE_T)(brk - (char *)(p)) < MINSIZE) { - brk += alignment; - } - - newp = (mchunkptr)brk; - leadsize = brk - (char *)(p); - newsize = chunksize(p) - leadsize; - - /* For mmapped chunks, just adjust offset */ - if (chunk_is_mmapped(p)) { - newp->prev_size = p->prev_size + leadsize; - set_head(newp, newsize | IS_MMAPPED); - return chunk2mem(newp); - } - - /* Otherwise, give back leader, use the rest */ - set_head(newp, newsize | PREV_INUSE); - set_inuse_bit_at_offset(newp, newsize); - set_head_size(p, leadsize); - fREe(chunk2mem(p)); - p = newp; - - assert(newsize >= nb && - (((PTR_UINT)(chunk2mem(p))) % alignment) == 0); - } - - /* Also give back spare room at the end */ - if (!chunk_is_mmapped(p)) { - size = chunksize(p); - if ((CHUNK_SIZE_T)(size) > (CHUNK_SIZE_T)(nb + MINSIZE)) { - remainder_size = size - nb; - remainder = chunk_at_offset(p, nb); - set_head(remainder, remainder_size | PREV_INUSE); - set_head_size(p, nb); - fREe(chunk2mem(remainder)); - } - } - - check_inuse_chunk(p); - return chunk2mem(p); -} - -/* - ------------------------------ calloc ------------------------------ -*/ - -#if __STD_C -Void_t *cALLOc(size_t n_elements, size_t elem_size) -#else -Void_t *cALLOc(n_elements, elem_size) -size_t n_elements; -size_t elem_size; -#endif -{ - mchunkptr p; - CHUNK_SIZE_T clearsize; - CHUNK_SIZE_T nclears; - INTERNAL_SIZE_T *d; - - Void_t *mem = mALLOc(n_elements * elem_size); - - if (mem != 0) { - p = mem2chunk(mem); - - if (!chunk_is_mmapped(p)) { - /* - Unroll clear of <= 36 bytes (72 if 8byte sizes) - We know that contents have an odd number of - INTERNAL_SIZE_T-sized words; minimally 3. - */ - - d = (INTERNAL_SIZE_T *)mem; - clearsize = chunksize(p) - SIZE_SZ; - nclears = clearsize / sizeof(INTERNAL_SIZE_T); - assert(nclears >= 3); - - if (nclears > 9) { - MALLOC_ZERO(d, clearsize); - } - - else { - *(d + 0) = 0; - *(d + 1) = 0; - *(d + 2) = 0; - if (nclears > 4) { - *(d + 3) = 0; - *(d + 4) = 0; - if (nclears > 6) { - *(d + 5) = 0; - *(d + 6) = 0; - if (nclears > 8) { - *(d + 7) = 0; - *(d + 8) = 0; - } - } - } - } - } -#if !MMAP_CLEARS - else { - d = (INTERNAL_SIZE_T *)mem; - /* - Note the additional SIZE_SZ - */ - clearsize = chunksize(p) - 2 * SIZE_SZ; - MALLOC_ZERO(d, clearsize); - } -#endif - } - return mem; -} - -/* - ------------------------------ cfree ------------------------------ -*/ - -#if __STD_C -void cFREe(Void_t *mem) -#else -void cFREe(mem) Void_t *mem; -#endif -{ - fREe(mem); -} - -/* - ------------------------- independent_calloc ------------------------- -*/ - -#if __STD_C -Void_t **iCALLOc(size_t n_elements, size_t elem_size, Void_t *chunks[]) -#else -Void_t **iCALLOc(n_elements, elem_size, chunks) -size_t n_elements; -size_t elem_size; -Void_t *chunks[]; -#endif -{ - size_t sz = elem_size; /* serves as 1-element array */ - /* opts arg of 3 means all elements are same size, and should be cleared */ - return iALLOc(n_elements, &sz, 3, chunks); -} - -/* - ------------------------- independent_comalloc ------------------------- -*/ - -#if __STD_C -Void_t **iCOMALLOc(size_t n_elements, size_t sizes[], Void_t *chunks[]) -#else -Void_t **iCOMALLOc(n_elements, sizes, chunks) -size_t n_elements; -size_t sizes[]; -Void_t *chunks[]; -#endif -{ - return iALLOc(n_elements, sizes, 0, chunks); -} - -/* - ------------------------------ ialloc ------------------------------ - ialloc provides common support for independent_X routines, handling all of - the combinations that can result. - - The opts arg has: - bit 0 set if all elements are same size (using sizes[0]) - bit 1 set if elements should be zeroed -*/ - -#if __STD_C -static Void_t **iALLOc(size_t n_elements, - size_t *sizes, - int opts, - Void_t *chunks[]) -#else -static Void_t **iALLOc(n_elements, sizes, opts, chunks) -size_t n_elements; -size_t *sizes; -int opts; -Void_t *chunks[]; -#endif -{ - mstate av = get_malloc_state(); - INTERNAL_SIZE_T element_size; /* chunksize of each element, if all same */ - INTERNAL_SIZE_T contents_size; /* total size of elements */ - INTERNAL_SIZE_T array_size; /* request size of pointer array */ - Void_t *mem; /* malloced aggregate space */ - mchunkptr p; /* corresponding chunk */ - INTERNAL_SIZE_T remainder_size; /* remaining bytes while splitting */ - Void_t **marray; /* either "chunks" or malloced ptr array */ - mchunkptr array_chunk; /* chunk for malloced ptr array */ - int mmx; /* to disable mmap */ - INTERNAL_SIZE_T size; - size_t i; - - /* Ensure initialization */ - if (av->max_fast == 0) { - malloc_consolidate(av); - } - - /* compute array length, if needed */ - if (chunks != 0) { - if (n_elements == 0) { - return chunks; /* nothing to do */ - } - marray = chunks; - array_size = 0; - } else { - /* if empty req, must still return chunk representing empty array */ - if (n_elements == 0) { - return (Void_t **)mALLOc(0); - } - marray = 0; - array_size = request2size(n_elements * (sizeof(Void_t *))); - } - - /* compute total element size */ - if (opts & 0x1) { /* all-same-size */ - element_size = request2size(*sizes); - contents_size = n_elements * element_size; - } else { /* add up all the sizes */ - element_size = 0; - contents_size = 0; - for (i = 0; i != n_elements; ++i) { - contents_size += request2size(sizes[i]); - } - } - - /* subtract out alignment bytes from total to minimize overallocation */ - size = contents_size + array_size - MALLOC_ALIGN_MASK; - - /* - Allocate the aggregate chunk. - But first disable mmap so malloc won't use it, since - we would not be able to later free/realloc space internal - to a segregated mmap region. - */ - mmx = av->n_mmaps_max; /* disable mmap */ - av->n_mmaps_max = 0; - mem = mALLOc(size); - av->n_mmaps_max = mmx; /* reset mmap */ - if (mem == 0) { - return 0; - } - - p = mem2chunk(mem); - assert(!chunk_is_mmapped(p)); - remainder_size = chunksize(p); - - if (opts & 0x2) { /* optionally clear the elements */ - MALLOC_ZERO(mem, remainder_size - SIZE_SZ - array_size); - } - - /* If not provided, allocate the pointer array as final part of chunk */ - if (marray == 0) { - array_chunk = chunk_at_offset(p, contents_size); - marray = (Void_t **)(chunk2mem(array_chunk)); - set_head(array_chunk, (remainder_size - contents_size) | PREV_INUSE); - remainder_size = contents_size; - } - - /* split out elements */ - for (i = 0;; ++i) { - marray[i] = chunk2mem(p); - if (i != n_elements - 1) { - if (element_size != 0) { - size = element_size; - } else { - size = request2size(sizes[i]); - } - remainder_size -= size; - set_head(p, size | PREV_INUSE); - p = chunk_at_offset(p, size); - } else { /* the final element absorbs any overallocation slop */ - set_head(p, remainder_size | PREV_INUSE); - break; - } - } - -#if DL_DEBUG - if (marray != chunks) { - /* final element must have exactly exhausted chunk */ - if (element_size != 0) { - assert(remainder_size == element_size); - } else { - assert(remainder_size == request2size(sizes[i])); - } - check_inuse_chunk(mem2chunk(marray)); - } - - for (i = 0; i != n_elements; ++i) { - check_inuse_chunk(mem2chunk(marray[i])); - } -#endif - - return marray; -} - -/* - ------------------------------ valloc ------------------------------ -*/ - -#if __STD_C -Void_t *vALLOc(size_t bytes) -#else -Void_t *vALLOc(bytes) -size_t bytes; -#endif -{ - /* Ensure initialization */ - mstate av = get_malloc_state(); - if (av->max_fast == 0) { - malloc_consolidate(av); - } - return mEMALIGn(av->pagesize, bytes); -} - -/* - ------------------------------ pvalloc ------------------------------ -*/ - -#if __STD_C -Void_t *pVALLOc(size_t bytes) -#else -Void_t *pVALLOc(bytes) -size_t bytes; -#endif -{ - mstate av = get_malloc_state(); - size_t pagesz; - - /* Ensure initialization */ - if (av->max_fast == 0) { - malloc_consolidate(av); - } - pagesz = av->pagesize; - return mEMALIGn(pagesz, (bytes + pagesz - 1) & ~(pagesz - 1)); -} - -/* - ------------------------------ malloc_trim ------------------------------ -*/ - -#if __STD_C -int mTRIm(size_t pad) -#else -int mTRIm(pad) -size_t pad; -#endif -{ - mstate av = get_malloc_state(); - /* Ensure initialization/consolidation */ - malloc_consolidate(av); - -#ifndef MORECORE_CANNOT_TRIM - return sYSTRIm(pad, av); -#else - return 0; -#endif -} - -/* - ------------------------- malloc_usable_size ------------------------- -*/ - -#if __STD_C -size_t mUSABLe(Void_t *mem) -#else -size_t mUSABLe(mem) -Void_t *mem; -#endif -{ - mchunkptr p; - if (mem != 0) { - p = mem2chunk(mem); - if (chunk_is_mmapped(p)) { - return chunksize(p) - 2 * SIZE_SZ; - } else if (inuse(p)) { - return chunksize(p) - SIZE_SZ; - } - } - return 0; -} - -/* - ------------------------------ mallinfo ------------------------------ -*/ - -struct mallinfo mALLINFo() { - mstate av = get_malloc_state(); - struct mallinfo mi; - int i; - mbinptr b; - mchunkptr p; - INTERNAL_SIZE_T avail; - INTERNAL_SIZE_T fastavail; - int nblocks; - int nfastblocks; - - /* Ensure initialization */ - if (av->top == 0) { - malloc_consolidate(av); - } - - check_malloc_state(); - - /* Account for top */ - avail = chunksize(av->top); - nblocks = 1; /* top always exists */ - - /* traverse fastbins */ - nfastblocks = 0; - fastavail = 0; - - for (i = 0; NFASTBINS - i > 0; ++i) { - for (p = av->fastbins[i]; p != 0; p = p->fd) { - ++nfastblocks; - fastavail += chunksize(p); - } - } - - avail += fastavail; - - /* traverse regular bins */ - for (i = 1; i < NBINS; ++i) { - b = bin_at(av, i); - for (p = last(b); p != b; p = p->bk) { - ++nblocks; - avail += chunksize(p); - } - } - - mi.smblks = nfastblocks; - mi.ordblks = nblocks; - mi.fordblks = avail; - mi.uordblks = av->sbrked_mem - avail; - mi.arena = av->sbrked_mem; - mi.hblks = av->n_mmaps; - mi.hblkhd = av->mmapped_mem; - mi.fsmblks = fastavail; - mi.keepcost = chunksize(av->top); - mi.usmblks = av->max_total_mem; - return mi; -} - -/* - ------------------------------ malloc_stats ------------------------------ -*/ - -void mSTATs(void) { - struct mallinfo mi = mALLINFo(); - -#ifdef WIN32 - { - CHUNK_SIZE_T free, reserved, committed; - vminfo(&free, &reserved, &committed); - fprintf(stderr, "free bytes = %10lu\n", - free); - fprintf(stderr, "reserved bytes = %10lu\n", - reserved); - fprintf(stderr, "committed bytes = %10lu\n", - committed); - } -#endif - - fprintf(stderr, "max system bytes = %10lu\n", - (CHUNK_SIZE_T)(mi.usmblks)); - fprintf(stderr, "system bytes = %10lu\n", - (CHUNK_SIZE_T)(mi.arena + mi.hblkhd)); - fprintf(stderr, "in use bytes = %10lu\n", - (CHUNK_SIZE_T)(mi.uordblks + mi.hblkhd)); - -#ifdef WIN32 - { - CHUNK_SIZE_T kernel, user; - if (cpuinfo(TRUE, &kernel, &user)) { - fprintf(stderr, "kernel ms = %10lu\n", - kernel); - fprintf(stderr, "user ms = %10lu\n", - user); - } - } -#endif -} - -/* - ------------------------------ mallopt ------------------------------ -*/ - -#if __STD_C -int mALLOPt(int param_number, int value) -#else -int mALLOPt(param_number, value) -int param_number; -int value; -#endif -{ - mstate av = get_malloc_state(); - /* Ensure initialization/consolidation */ - malloc_consolidate(av); - - switch (param_number) { - case M_MXFAST: - if (value >= 0 && value <= MAX_FAST_SIZE) { - set_max_fast(av, value); - return 1; - } else { - return 0; - } - - case M_TRIM_THRESHOLD: - av->trim_threshold = value; - return 1; - - case M_TOP_PAD: - av->top_pad = value; - return 1; - - case M_MMAP_THRESHOLD: - av->mmap_threshold = value; - return 1; - - case M_MMAP_MAX: -#if !HAVE_MMAP - if (value != 0) { - return 0; - } -#endif - av->n_mmaps_max = value; - return 1; - - default: - return 0; - } -} - -/* - -------------------- Alternative MORECORE functions -------------------- -*/ - -/* - General Requirements for MORECORE. - - The MORECORE function must have the following properties: - - If MORECORE_CONTIGUOUS is false: - - * MORECORE must allocate in multiples of pagesize. It will - only be called with arguments that are multiples of pagesize. - - * MORECORE(0) must return an address that is at least - MALLOC_ALIGNMENT aligned. (Page-aligning always suffices.) - - else (i.e. If MORECORE_CONTIGUOUS is true): - - * Consecutive calls to MORECORE with positive arguments - return increasing addresses, indicating that space has been - contiguously extended. - - * MORECORE need not allocate in multiples of pagesize. - Calls to MORECORE need not have args of multiples of pagesize. - - * MORECORE need not page-align. - - In either case: - - * MORECORE may allocate more memory than requested. (Or even less, - but this will generally result in a malloc failure.) - - * MORECORE must not allocate memory when given argument zero, but - instead return one past the end address of memory from previous - nonzero call. This malloc does NOT call MORECORE(0) - until at least one call with positive arguments is made, so - the initial value returned is not important. - - * Even though consecutive calls to MORECORE need not return contiguous - addresses, it must be OK for malloc'ed chunks to span multiple - regions in those cases where they do happen to be contiguous. - - * MORECORE need not handle negative arguments -- it may instead - just return MORECORE_FAILURE when given negative arguments. - Negative arguments are always multiples of pagesize. MORECORE - must not misinterpret negative args as large positive unsigned - args. You can suppress all such calls from even occurring by defining - MORECORE_CANNOT_TRIM, - - There is some variation across systems about the type of the - argument to sbrk/MORECORE. If size_t is unsigned, then it cannot - actually be size_t, because sbrk supports negative args, so it is - normally the signed type of the same width as size_t (sometimes - declared as "intptr_t", and sometimes "ptrdiff_t"). It doesn't much - matter though. Internally, we use "long" as arguments, which should - work across all reasonable possibilities. - - Additionally, if MORECORE ever returns failure for a positive - request, and HAVE_MMAP is true, then mmap is used as a noncontiguous - system allocator. This is a useful backup strategy for systems with - holes in address spaces -- in this case sbrk cannot contiguously - expand the heap, but mmap may be able to map noncontiguous space. - - If you'd like mmap to ALWAYS be used, you can define MORECORE to be - a function that always returns MORECORE_FAILURE. - - Malloc only has limited ability to detect failures of MORECORE - to supply contiguous space when it says it can. In particular, - multithreaded programs that do not use locks may result in - rece conditions across calls to MORECORE that result in gaps - that cannot be detected as such, and subsequent corruption. - - If you are using this malloc with something other than sbrk (or its - emulation) to supply memory regions, you probably want to set - MORECORE_CONTIGUOUS as false. As an example, here is a custom - allocator kindly contributed for pre-OSX macOS. It uses virtually - but not necessarily physically contiguous non-paged memory (locked - in, present and won't get swapped out). You can use it by - uncommenting this section, adding some #includes, and setting up the - appropriate defines above: - - #define MORECORE osMoreCore - #define MORECORE_CONTIGUOUS 0 - - There is also a shutdown routine that should somehow be called for - cleanup upon program exit. - - #define MAX_POOL_ENTRIES 100 - #define MINIMUM_MORECORE_SIZE (64 * 1024) - static int next_os_pool; - void *our_os_pools[MAX_POOL_ENTRIES]; - - void *osMoreCore(int size) - { - void *ptr = 0; - static void *sbrk_top = 0; - - if (size > 0) - { - if (size < MINIMUM_MORECORE_SIZE) - size = MINIMUM_MORECORE_SIZE; - if (CurrentExecutionLevel() == kTaskLevel) - ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); - if (ptr == 0) - { - return (void *) MORECORE_FAILURE; - } - // save ptrs so they can be freed during cleanup - our_os_pools[next_os_pool] = ptr; - next_os_pool++; - ptr = (void *) ((((CHUNK_SIZE_T) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); - sbrk_top = (char *) ptr + size; - return ptr; - } - else if (size < 0) - { - // we don't currently support shrink behavior - return (void *) MORECORE_FAILURE; - } - else - { - return sbrk_top; - } - } - - // cleanup any allocated memory pools - // called as last thing before shutting down driver - - void osCleanupMem(void) - { - void **ptr; - - for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) - if (*ptr) - { - PoolDeallocate(*ptr); - *ptr = 0; - } - } - -*/ - -/* - -------------------------------------------------------------- - - Emulation of sbrk for win32. - Donated by J. Walter . - For additional information about this code, and malloc on Win32, see - http://www.genesys-e.de/jwalter/ -*/ - -#ifdef WIN32 - -#ifdef _DEBUG -/* #define TRACE */ -#endif - -/* Support for USE_MALLOC_LOCK */ -#ifdef USE_MALLOC_LOCK - -/* Wait for spin lock */ -static int slwait(int *sl) { - while (InterlockedCompareExchange((void **)sl, (void *)1, (void *)0) != 0) { - Sleep(0); - } - return 0; -} - -/* Release spin lock */ -static int slrelease(int *sl) { - InterlockedExchange(sl, 0); - return 0; -} - -#ifdef NEEDED -/* Spin lock for emulation code */ -static int g_sl; -#endif - -#endif /* USE_MALLOC_LOCK */ - -/* getpagesize for windows */ -static long getpagesize(void) { - static long g_pagesize = 0; - if (!g_pagesize) { - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - g_pagesize = system_info.dwPageSize; - } - return g_pagesize; -} -static long getregionsize(void) { - static long g_regionsize = 0; - if (!g_regionsize) { - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - g_regionsize = system_info.dwAllocationGranularity; - } - return g_regionsize; -} - -/* A region list entry */ -typedef struct _region_list_entry { - void *top_allocated; - void *top_committed; - void *top_reserved; - long reserve_size; - struct _region_list_entry *previous; -} region_list_entry; - -/* Allocate and link a region entry in the region list */ -static int region_list_append(region_list_entry **last, void *base_reserved, long reserve_size) { - region_list_entry *next = HeapAlloc(GetProcessHeap(), 0, sizeof(region_list_entry)); - if (!next) { - return FALSE; - } - next->top_allocated = (char *)base_reserved; - next->top_committed = (char *)base_reserved; - next->top_reserved = (char *)base_reserved + reserve_size; - next->reserve_size = reserve_size; - next->previous = *last; - *last = next; - return TRUE; -} -/* Free and unlink the last region entry from the region list */ -static int region_list_remove(region_list_entry **last) { - region_list_entry *previous = (*last)->previous; - if (!HeapFree(GetProcessHeap(), sizeof(region_list_entry), *last)) { - return FALSE; - } - *last = previous; - return TRUE; -} - -#define CEIL(size, to) (((size) + (to) - 1) & ~((to) - 1)) -#define FLOOR(size, to) ((size) & ~((to) - 1)) - -#define SBRK_SCALE 0 -/* #define SBRK_SCALE 1 */ -/* #define SBRK_SCALE 2 */ -/* #define SBRK_SCALE 4 */ - -/* sbrk for windows */ -static void *sbrk(long size) { - static long g_pagesize, g_my_pagesize; - static long g_regionsize, g_my_regionsize; - static region_list_entry *g_last; - void *result = (void *)MORECORE_FAILURE; -#ifdef TRACE - printf("sbrk %d\n", size); -#endif -#if defined(USE_MALLOC_LOCK) && defined(NEEDED) - /* Wait for spin lock */ - slwait(&g_sl); -#endif - /* First time initialization */ - if (!g_pagesize) { - g_pagesize = getpagesize(); - g_my_pagesize = g_pagesize << SBRK_SCALE; - } - if (!g_regionsize) { - g_regionsize = getregionsize(); - g_my_regionsize = g_regionsize << SBRK_SCALE; - } - if (!g_last) { - if (!region_list_append(&g_last, 0, 0)) { - goto sbrk_exit; - } - } - /* Assert invariants */ - assert(g_last); - assert((char *)g_last->top_reserved - g_last->reserve_size <= (char *)g_last->top_allocated && - g_last->top_allocated <= g_last->top_committed); - assert((char *)g_last->top_reserved - g_last->reserve_size <= (char *)g_last->top_committed && - g_last->top_committed <= g_last->top_reserved && - (unsigned)g_last->top_committed % g_pagesize == 0); - assert((unsigned)g_last->top_reserved % g_regionsize == 0); - assert((unsigned)g_last->reserve_size % g_regionsize == 0); - /* Allocation requested? */ - if (size >= 0) { - /* Allocation size is the requested size */ - long allocate_size = size; - /* Compute the size to commit */ - long to_commit = (char *)g_last->top_allocated + allocate_size - (char *)g_last->top_committed; - /* Do we reach the commit limit? */ - if (to_commit > 0) { - /* Round size to commit */ - long commit_size = CEIL(to_commit, g_my_pagesize); - /* Compute the size to reserve */ - long to_reserve = (char *)g_last->top_committed + commit_size - (char *)g_last->top_reserved; - /* Do we reach the reserve limit? */ - if (to_reserve > 0) { - /* Compute the remaining size to commit in the current region */ - long remaining_commit_size = (char *)g_last->top_reserved - (char *)g_last->top_committed; - if (remaining_commit_size > 0) { - /* Assert preconditions */ - assert((unsigned)g_last->top_committed % g_pagesize == 0); - assert(0 < remaining_commit_size && remaining_commit_size % g_pagesize == 0); - { - /* Commit this */ - void *base_committed = VirtualAlloc(g_last->top_committed, remaining_commit_size, - MEM_COMMIT, PAGE_READWRITE); - /* Check returned pointer for consistency */ - if (base_committed != g_last->top_committed) { - goto sbrk_exit; - } - /* Assert postconditions */ - assert((unsigned)base_committed % g_pagesize == 0); -#ifdef TRACE - printf("Commit %p %d\n", base_committed, remaining_commit_size); -#endif - /* Adjust the regions commit top */ - g_last->top_committed = (char *)base_committed + remaining_commit_size; - } - } - { - /* Now we are going to search and reserve. */ - int contiguous = -1; - int found = FALSE; - MEMORY_BASIC_INFORMATION memory_info; - void *base_reserved; - long reserve_size; - do { - /* Assume contiguous memory */ - contiguous = TRUE; - /* Round size to reserve */ - reserve_size = CEIL(to_reserve, g_my_regionsize); - /* Start with the current region's top */ - memory_info.BaseAddress = g_last->top_reserved; - /* Assert preconditions */ - assert((unsigned)memory_info.BaseAddress % g_pagesize == 0); - assert(0 < reserve_size && reserve_size % g_regionsize == 0); - while (VirtualQuery(memory_info.BaseAddress, &memory_info, sizeof(memory_info))) { - /* Assert postconditions */ - assert((unsigned)memory_info.BaseAddress % g_pagesize == 0); -#ifdef TRACE - printf("Query %p %d %s\n", memory_info.BaseAddress, memory_info.RegionSize, - memory_info.State == MEM_FREE ? "FREE" : (memory_info.State == MEM_RESERVE ? "RESERVED" : (memory_info.State == MEM_COMMIT ? "COMMITTED" : "?"))); -#endif - /* Region is free, well aligned and big enough: we are done */ - if (memory_info.State == MEM_FREE && - (unsigned)memory_info.BaseAddress % g_regionsize == 0 && - memory_info.RegionSize >= (unsigned)reserve_size) { - found = TRUE; - break; - } - /* From now on we can't get contiguous memory! */ - contiguous = FALSE; - /* Recompute size to reserve */ - reserve_size = CEIL(allocate_size, g_my_regionsize); - memory_info.BaseAddress = (char *)memory_info.BaseAddress + memory_info.RegionSize; - /* Assert preconditions */ - assert((unsigned)memory_info.BaseAddress % g_pagesize == 0); - assert(0 < reserve_size && reserve_size % g_regionsize == 0); - } - /* Search failed? */ - if (!found) { - goto sbrk_exit; - } - /* Assert preconditions */ - assert((unsigned)memory_info.BaseAddress % g_regionsize == 0); - assert(0 < reserve_size && reserve_size % g_regionsize == 0); - /* Try to reserve this */ - base_reserved = VirtualAlloc(memory_info.BaseAddress, reserve_size, - MEM_RESERVE, PAGE_NOACCESS); - if (!base_reserved) { - int rc = GetLastError(); - if (rc != ERROR_INVALID_ADDRESS) { - goto sbrk_exit; - } - } - /* A null pointer signals (hopefully) a race condition with another thread. */ - /* In this case, we try again. */ - } while (!base_reserved); - /* Check returned pointer for consistency */ - if (memory_info.BaseAddress && base_reserved != memory_info.BaseAddress) { - goto sbrk_exit; - } - /* Assert postconditions */ - assert((unsigned)base_reserved % g_regionsize == 0); -#ifdef TRACE - printf("Reserve %p %d\n", base_reserved, reserve_size); -#endif - /* Did we get contiguous memory? */ - if (contiguous) { - long start_size = (char *)g_last->top_committed - (char *)g_last->top_allocated; - /* Adjust allocation size */ - allocate_size -= start_size; - /* Adjust the regions allocation top */ - g_last->top_allocated = g_last->top_committed; - /* Recompute the size to commit */ - to_commit = (char *)g_last->top_allocated + allocate_size - (char *)g_last->top_committed; - /* Round size to commit */ - commit_size = CEIL(to_commit, g_my_pagesize); - } - /* Append the new region to the list */ - if (!region_list_append(&g_last, base_reserved, reserve_size)) { - goto sbrk_exit; - } - /* Didn't we get contiguous memory? */ - if (!contiguous) { - /* Recompute the size to commit */ - to_commit = (char *)g_last->top_allocated + allocate_size - (char *)g_last->top_committed; - /* Round size to commit */ - commit_size = CEIL(to_commit, g_my_pagesize); - } - } - } - /* Assert preconditions */ - assert((unsigned)g_last->top_committed % g_pagesize == 0); - assert(0 < commit_size && commit_size % g_pagesize == 0); - { - /* Commit this */ - void *base_committed = VirtualAlloc(g_last->top_committed, commit_size, - MEM_COMMIT, PAGE_READWRITE); - /* Check returned pointer for consistency */ - if (base_committed != g_last->top_committed) { - goto sbrk_exit; - } - /* Assert postconditions */ - assert((unsigned)base_committed % g_pagesize == 0); -#ifdef TRACE - printf("Commit %p %d\n", base_committed, commit_size); -#endif - /* Adjust the regions commit top */ - g_last->top_committed = (char *)base_committed + commit_size; - } - } - /* Adjust the regions allocation top */ - g_last->top_allocated = (char *)g_last->top_allocated + allocate_size; - result = (char *)g_last->top_allocated - size; - /* Deallocation requested? */ - } else if (size < 0) { - long deallocate_size = -size; - /* As long as we have a region to release */ - while ((char *)g_last->top_allocated - deallocate_size < (char *)g_last->top_reserved - g_last->reserve_size) { - /* Get the size to release */ - long release_size = g_last->reserve_size; - /* Get the base address */ - void *base_reserved = (char *)g_last->top_reserved - release_size; - /* Assert preconditions */ - assert((unsigned)base_reserved % g_regionsize == 0); - assert(0 < release_size && release_size % g_regionsize == 0); - { - /* Release this */ - int rc = VirtualFree(base_reserved, 0, - MEM_RELEASE); - /* Check returned code for consistency */ - if (!rc) { - goto sbrk_exit; - } -#ifdef TRACE - printf("Release %p %d\n", base_reserved, release_size); -#endif - } - /* Adjust deallocation size */ - deallocate_size -= (char *)g_last->top_allocated - (char *)base_reserved; - /* Remove the old region from the list */ - if (!region_list_remove(&g_last)) { - goto sbrk_exit; - } - } - { - /* Compute the size to decommit */ - long to_decommit = (char *)g_last->top_committed - ((char *)g_last->top_allocated - deallocate_size); - if (to_decommit >= g_my_pagesize) { - /* Compute the size to decommit */ - long decommit_size = FLOOR(to_decommit, g_my_pagesize); - /* Compute the base address */ - void *base_committed = (char *)g_last->top_committed - decommit_size; - /* Assert preconditions */ - assert((unsigned)base_committed % g_pagesize == 0); - assert(0 < decommit_size && decommit_size % g_pagesize == 0); - { - /* Decommit this */ - int rc = VirtualFree((char *)base_committed, decommit_size, - MEM_DECOMMIT); - /* Check returned code for consistency */ - if (!rc) { - goto sbrk_exit; - } -#ifdef TRACE - printf("Decommit %p %d\n", base_committed, decommit_size); -#endif - } - /* Adjust deallocation size and regions commit and allocate top */ - deallocate_size -= (char *)g_last->top_allocated - (char *)base_committed; - g_last->top_committed = base_committed; - g_last->top_allocated = base_committed; - } - } - /* Adjust regions allocate top */ - g_last->top_allocated = (char *)g_last->top_allocated - deallocate_size; - /* Check for underflow */ - if ((char *)g_last->top_reserved - g_last->reserve_size > (char *)g_last->top_allocated || - g_last->top_allocated > g_last->top_committed) { - /* Adjust regions allocate top */ - g_last->top_allocated = (char *)g_last->top_reserved - g_last->reserve_size; - goto sbrk_exit; - } - result = g_last->top_allocated; - } - /* Assert invariants */ - assert(g_last); - assert((char *)g_last->top_reserved - g_last->reserve_size <= (char *)g_last->top_allocated && - g_last->top_allocated <= g_last->top_committed); - assert((char *)g_last->top_reserved - g_last->reserve_size <= (char *)g_last->top_committed && - g_last->top_committed <= g_last->top_reserved && - (unsigned)g_last->top_committed % g_pagesize == 0); - assert((unsigned)g_last->top_reserved % g_regionsize == 0); - assert((unsigned)g_last->reserve_size % g_regionsize == 0); - -sbrk_exit: -#if defined(USE_MALLOC_LOCK) && defined(NEEDED) - /* Release spin lock */ - slrelease(&g_sl); -#endif - return result; -} - -/* mmap for windows */ -static void *mmap(void *ptr, long size, long prot, long type, long handle, long arg) { - static long g_pagesize; - static long g_regionsize; -#ifdef TRACE - printf("mmap %d\n", size); -#endif -#if defined(USE_MALLOC_LOCK) && defined(NEEDED) - /* Wait for spin lock */ - slwait(&g_sl); -#endif - /* First time initialization */ - if (!g_pagesize) { - g_pagesize = getpagesize(); - } - if (!g_regionsize) { - g_regionsize = getregionsize(); - } - /* Assert preconditions */ - assert((unsigned)ptr % g_regionsize == 0); - assert(size % g_pagesize == 0); - /* Allocate this */ - ptr = VirtualAlloc(ptr, size, - MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE); - if (!ptr) { - ptr = (void *)MORECORE_FAILURE; - goto mmap_exit; - } - /* Assert postconditions */ - assert((unsigned)ptr % g_regionsize == 0); -#ifdef TRACE - printf("Commit %p %d\n", ptr, size); -#endif -mmap_exit: -#if defined(USE_MALLOC_LOCK) && defined(NEEDED) - /* Release spin lock */ - slrelease(&g_sl); -#endif - return ptr; -} - -/* munmap for windows */ -static long munmap(void *ptr, long size) { - static long g_pagesize; - static long g_regionsize; - int rc = MUNMAP_FAILURE; -#ifdef TRACE - printf("munmap %p %d\n", ptr, size); -#endif -#if defined(USE_MALLOC_LOCK) && defined(NEEDED) - /* Wait for spin lock */ - slwait(&g_sl); -#endif - /* First time initialization */ - if (!g_pagesize) { - g_pagesize = getpagesize(); - } - if (!g_regionsize) { - g_regionsize = getregionsize(); - } - /* Assert preconditions */ - assert((unsigned)ptr % g_regionsize == 0); - assert(size % g_pagesize == 0); - /* Free this */ - if (!VirtualFree(ptr, 0, - MEM_RELEASE)) { - goto munmap_exit; - } - rc = 0; -#ifdef TRACE - printf("Release %p %d\n", ptr, size); -#endif -munmap_exit: -#if defined(USE_MALLOC_LOCK) && defined(NEEDED) - /* Release spin lock */ - slrelease(&g_sl); -#endif - return rc; -} - -static void vminfo(CHUNK_SIZE_T *free, CHUNK_SIZE_T *reserved, CHUNK_SIZE_T *committed) { - MEMORY_BASIC_INFORMATION memory_info; - memory_info.BaseAddress = 0; - *free = *reserved = *committed = 0; - while (VirtualQuery(memory_info.BaseAddress, &memory_info, sizeof(memory_info))) { - switch (memory_info.State) { - case MEM_FREE: - *free += memory_info.RegionSize; - break; - case MEM_RESERVE: - *reserved += memory_info.RegionSize; - break; - case MEM_COMMIT: - *committed += memory_info.RegionSize; - break; - } - memory_info.BaseAddress = (char *)memory_info.BaseAddress + memory_info.RegionSize; - } -} - -static int cpuinfo(int whole, CHUNK_SIZE_T *kernel, CHUNK_SIZE_T *user) { - if (whole) { - __int64 creation64, exit64, kernel64, user64; - int rc = GetProcessTimes(GetCurrentProcess(), - (FILETIME *)&creation64, - (FILETIME *)&exit64, - (FILETIME *)&kernel64, - (FILETIME *)&user64); - if (!rc) { - *kernel = 0; - *user = 0; - return FALSE; - } - *kernel = (CHUNK_SIZE_T)(kernel64 / 10000); - *user = (CHUNK_SIZE_T)(user64 / 10000); - return TRUE; - } else { - __int64 creation64, exit64, kernel64, user64; - int rc = GetThreadTimes(GetCurrentThread(), - (FILETIME *)&creation64, - (FILETIME *)&exit64, - (FILETIME *)&kernel64, - (FILETIME *)&user64); - if (!rc) { - *kernel = 0; - *user = 0; - return FALSE; - } - *kernel = (CHUNK_SIZE_T)(kernel64 / 10000); - *user = (CHUNK_SIZE_T)(user64 / 10000); - return TRUE; - } -} - -#endif /* WIN32 */ - -/* ------------------------------------------------------------ -History: - V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) - * Fix malloc_state bitmap array misdeclaration - - V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) - * Allow tuning of FIRST_SORTED_BIN_SIZE - * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. - * Better detection and support for non-contiguousness of MORECORE. - Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger - * Bypass most of malloc if no frees. Thanks To Emery Berger. - * Fix freeing of old top non-contiguous chunk im sysmalloc. - * Raised default trim and map thresholds to 256K. - * Fix mmap-related #defines. Thanks to Lubos Lunak. - * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. - * Branch-free bin calculation - * Default trim and mmap thresholds now 256K. - - V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) - * Introduce independent_comalloc and independent_calloc. - Thanks to Michael Pachos for motivation and help. - * Make optional .h file available - * Allow > 2GB requests on 32bit systems. - * new WIN32 sbrk, mmap, munmap, lock code from . - Thanks also to Andreas Mueller , - and Anonymous. - * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for - helping test this.) - * memalign: check alignment arg - * realloc: don't try to shift chunks backwards, since this - leads to more fragmentation in some programs and doesn't - seem to help in any others. - * Collect all cases in malloc requiring system memory into sYSMALLOc - * Use mmap as backup to sbrk - * Place all internal state in malloc_state - * Introduce fastbins (although similar to 2.5.1) - * Many minor tunings and cosmetic improvements - * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK - * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS - Thanks to Tony E. Bennett and others. - * Include errno.h to support default failure action. - - V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) - * return null for negative arguments - * Added Several WIN32 cleanups from Martin C. Fong - * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' - (e.g. WIN32 platforms) - * Cleanup header file inclusion for WIN32 platforms - * Cleanup code to avoid Microsoft Visual C++ compiler complaints - * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing - memory allocation routines - * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) - * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to - usage of 'assert' in non-WIN32 code - * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to - avoid infinite loop - * Always call 'fREe()' rather than 'free()' - - V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) - * Fixed ordering problem with boundary-stamping - - V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) - * Added pvalloc, as recommended by H.J. Liu - * Added 64bit pointer support mainly from Wolfram Gloger - * Added anonymously donated WIN32 sbrk emulation - * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen - * malloc_extend_top: fix mask error that caused wastage after - foreign sbrks - * Add linux mremap support code from HJ Liu - - V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) - * Integrated most documentation with the code. - * Add support for mmap, with help from - Wolfram Gloger (Gloger@lrz.uni-muenchen.de). - * Use last_remainder in more cases. - * Pack bins using idea from colin@nyx10.cs.du.edu - * Use ordered bins instead of best-fit threshhold - * Eliminate block-local decls to simplify tracing and debugging. - * Support another case of realloc via move into top - * Fix error occuring when initial sbrk_base not word-aligned. - * Rely on page size for units instead of SBRK_UNIT to - avoid surprises about sbrk alignment conventions. - * Add mallinfo, mallopt. Thanks to Raymond Nijssen - (raymond@es.ele.tue.nl) for the suggestion. - * Add `pad' argument to malloc_trim and top_pad mallopt parameter. - * More precautions for cases where other routines call sbrk, - courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). - * Added macros etc., allowing use in linux libc from - H.J. Lu (hjl@gnu.ai.mit.edu) - * Inverted this history list - - V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) - * Re-tuned and fixed to behave more nicely with V2.6.0 changes. - * Removed all preallocation code since under current scheme - the work required to undo bad preallocations exceeds - the work saved in good cases for most test programs. - * No longer use return list or unconsolidated bins since - no scheme using them consistently outperforms those that don't - given above changes. - * Use best fit for very large chunks to prevent some worst-cases. - * Added some support for debugging - - V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) - * Removed footers when chunks are in use. Thanks to - Paul Wilson (wilson@cs.texas.edu) for the suggestion. - - V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) - * Added malloc_trim, with help from Wolfram Gloger - (wmglo@Dent.MED.Uni-Muenchen.DE). - - V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) - - V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) - * realloc: try to expand in both directions - * malloc: swap order of clean-bin strategy; - * realloc: only conditionally expand backwards - * Try not to scavenge used bins - * Use bin counts as a guide to preallocation - * Occasionally bin return list chunks in first scan - * Add a few optimizations from colin@nyx10.cs.du.edu - - V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) - * faster bin computation & slightly different binning - * merged all consolidations to one part of malloc proper - (eliminating old malloc_find_space & malloc_clean_bin) - * Scan 2 returns chunks (not just 1) - * Propagate failure in realloc if malloc returns 0 - * Add stuff to allow compilation on non-ANSI compilers - from kpv@research.att.com - - V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) - * removed potential for odd address access in prev_chunk - * removed dependency on getpagesize.h - * misc cosmetics and a bit more internal documentation - * anticosmetics: mangled names in macros to evade debugger strangeness - * tested on sparc, hp-700, dec-mips, rs6000 - with gcc & native cc (hp, dec only) allowing - Detlefs & Zorn comparison study (in SIGPLAN Notices.) - - Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) - * Based loosely on libg++-1.2X malloc. (It retains some of the overall - structure of old version, but most details differ.) - -*/ - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif - -#endif /* MALLOC_270_H */ diff --git a/pufferlib/ocean/impulse_wars/include/rlights.h b/pufferlib/ocean/impulse_wars/include/rlights.h deleted file mode 100644 index e84009bf6..000000000 --- a/pufferlib/ocean/impulse_wars/include/rlights.h +++ /dev/null @@ -1,170 +0,0 @@ -/********************************************************************************************** -* -* raylib.lights - Some useful functions to deal with lights data -* -* CONFIGURATION: -* -* #define RLIGHTS_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2017-2024 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RLIGHTS_H -#define RLIGHTS_H - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define MAX_LIGHTS 64 // Max dynamic lights supported by shader - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -// Light data -typedef struct { - int type; - bool enabled; - Vector3 position; - Vector3 target; - Color color; - float attenuation; - - // Shader locations - int enabledLoc; - int typeLoc; - int positionLoc; - int targetLoc; - int colorLoc; - int attenuationLoc; -} Light; - -// Light type -typedef enum { - LIGHT_DIRECTIONAL = 0, - LIGHT_POINT -} LightType; - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader); // Create a light and get shader locations -void UpdateLightValues(Shader shader, Light light); // Send light properties to shader - -#ifdef __cplusplus -} -#endif - -#endif // RLIGHTS_H - - -/*********************************************************************************** -* -* RLIGHTS IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RLIGHTS_IMPLEMENTATION) - -#include "raylib.h" - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -// ... - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -// ... - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static int lightsCount = 0; // Current amount of created lights - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -// ... - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- - -// Create a light and get shader locations -Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader) -{ - Light light = { 0 }; - - if (lightsCount < MAX_LIGHTS) - { - light.enabled = true; - light.type = type; - light.position = position; - light.target = target; - light.color = color; - - // NOTE: Lighting shader naming must be the provided ones - light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightsCount)); - light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightsCount)); - light.positionLoc = GetShaderLocation(shader, TextFormat("lights[%i].position", lightsCount)); - light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightsCount)); - light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightsCount)); - - UpdateLightValues(shader, light); - - lightsCount++; - } - - return light; -} - -// Send light properties to shader -// NOTE: Light shader locations should be available -void UpdateLightValues(Shader shader, Light light) -{ - // Send to shader light enabled state and type - SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT); - SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT); - - // Send to shader light position values - float position[3] = { light.position.x, light.position.y, light.position.z }; - SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3); - - // Send to shader light target position values - float target[3] = { light.target.x, light.target.y, light.target.z }; - SetShaderValue(shader, light.targetLoc, target, SHADER_UNIFORM_VEC3); - - // Send to shader light color values - float color[4] = { (float)light.color.r/(float)255, (float)light.color.g/(float)255, - (float)light.color.b/(float)255, (float)light.color.a/(float)255 }; - SetShaderValue(shader, light.colorLoc, color, SHADER_UNIFORM_VEC4); -} - -#endif // RLIGHTS_IMPLEMENTATION diff --git a/pufferlib/ocean/impulse_wars/map.h b/pufferlib/ocean/impulse_wars/map.h deleted file mode 100644 index eb5f2c702..000000000 --- a/pufferlib/ocean/impulse_wars/map.h +++ /dev/null @@ -1,666 +0,0 @@ -#ifndef IMPULSE_WARS_MAP_H -#define IMPULSE_WARS_MAP_H - -#include -#include - -#include "env.h" -#include "settings.h" - -// clang-format off - -const char boringLayout[] = { - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', -}; - -mapEntry boringMap = { - .layout = boringLayout, - .columns = 21, - .rows = 21, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = false, - .weaponPickups = 8, - .defaultWeapon = STANDARD_WEAPON, - .maxSuddenDeathWalls = 5, -}; - -const char prototypeArenaLayout[] = { - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', - 'D','O','O','O','O','O','O','O','d','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','w','O','O','O','O','O','O','O','O','O','O','O','O','O','O','d','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','w','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','W','W','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','D','D','W','W','D','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','d','O','D','D','D','D','D','O','O','O','O','O','O','O','D', - 'D','O','w','O','O','O','O','D','D','D','D','D','O','O','O','O','w','O','O','D', - 'D','O','O','O','O','O','O','D','D','D','D','D','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','W','W','O','O','O','d','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','w','O','O','D', - 'D','O','O','O','w','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','d','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','d','D', - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', -}; - -mapEntry prototypeArenaMap = { - .layout = prototypeArenaLayout, - .columns = 20, - .rows = 20, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = true, - .weaponPickups = 6, - .defaultWeapon = STANDARD_WEAPON, - .maxSuddenDeathWalls = 4, -}; - -const char snipersLayout[] = { - 'B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B', - 'B','D','D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D','D','B', - 'B','D','D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D','D','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','D','D','B','O','B','D','D','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','D','D','D','B','O','B','D','D','D','O','O','O','O','O','B', - 'B','O','O','O','O','O','D','D','D','B','O','B','D','D','D','O','O','O','O','O','B', - 'B','O','O','O','O','O','B','B','B','B','O','B','B','B','B','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','B','B','B','B','O','B','B','B','B','O','O','O','O','O','B', - 'B','O','O','O','O','O','D','D','D','B','O','B','D','D','D','O','O','O','O','O','B', - 'B','O','O','O','O','O','D','D','D','B','O','B','D','D','D','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','D','D','B','O','B','D','D','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','D','D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D','D','B', - 'B','D','D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D','D','B', - 'B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B', -}; - -mapEntry snipersMap = { - .layout = snipersLayout, - .columns = 21, - .rows = 21, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = false, - .weaponPickups = 6, - .defaultWeapon = SNIPER_WEAPON, - .maxSuddenDeathWalls = 4, -}; - -const char roomsLayout[] = { - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','W','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','W','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','D','D','W','O','O','O','W','D','D','D','D','D','W','O','O','O','W','D','D','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','W','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','W','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D', - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', -}; - -mapEntry roomsMap = { - .layout = roomsLayout, - .columns = 21, - .rows = 21, - .randFloatingStandardWalls = 3, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 3, - .hasSetFloatingWalls = false, - .weaponPickups = 10, - .defaultWeapon = STANDARD_WEAPON, - .maxSuddenDeathWalls = 5, -}; - -const char xArenaLayout[] = { - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','d','O','O','O','O','O','O','O','O','O','d','O','D', - 'D','O','w','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','d','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','W','W','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','D','W','W','D','D','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','D','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','D', - 'D','O','O','O','O','O','D','D','O','O','w','O','O','O','O','D','W','W','O','O','O','O','D', - 'D','O','O','O','O','W','W','D','D','O','O','O','O','O','D','D','W','W','O','O','w','O','D', - 'D','O','w','O','O','W','W','D','O','O','O','O','d','O','O','D','D','O','O','O','O','O','D', - 'D','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','D','D','D','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','D','D','W','W','D','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','W','W','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','w','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','w','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','d','D', - 'D','O','d','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', -}; - -mapEntry xArena = { - .layout = xArenaLayout, - .columns = 23, - .rows = 23, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = true, - .weaponPickups = 8, - .defaultWeapon = STANDARD_WEAPON, - .maxSuddenDeathWalls = 6, -}; - -const char crossBounceLayout[] = { - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', - 'D','B','B','B','B','O','O','O','O','B','D','D','D','D','B','O','O','O','O','B','B','B','B','D', - 'D','B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B','D', - 'D','B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B','D', - 'D','B','O','O','B','B','O','O','O','O','O','w','d','O','O','O','O','O','B','B','O','O','B','D', - 'D','O','O','O','B','D','D','O','O','O','O','O','O','O','O','O','O','D','D','B','O','O','O','D', - 'D','O','O','O','O','D','O','O','O','O','O','O','O','O','O','O','O','O','D','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','D','B','O','O','B','D','O','O','O','O','O','O','O','O','D', - 'D','B','O','O','O','O','O','O','D','D','B','O','O','B','D','D','O','O','O','O','O','O','B','D', - 'D','D','O','O','O','O','O','O','B','B','B','O','O','B','B','B','O','O','O','O','O','O','D','D', - 'D','D','O','O','d','O','O','O','O','O','O','O','O','O','O','O','O','O','O','w','O','O','D','D', - 'D','D','O','O','w','O','O','O','O','O','O','O','O','O','O','O','O','O','O','d','O','O','D','D', - 'D','D','O','O','O','O','O','O','B','B','B','O','O','B','B','B','O','O','O','O','O','O','D','D', - 'D','B','O','O','O','O','O','O','D','D','B','O','O','B','D','D','O','O','O','O','O','O','B','D', - 'D','O','O','O','O','O','O','O','O','D','B','O','O','B','D','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','D','O','O','O','O','O','O','O','O','O','O','O','O','D','O','O','O','O','D', - 'D','O','O','O','B','D','D','O','O','O','O','O','O','O','O','O','O','D','D','B','O','O','O','D', - 'D','B','O','O','B','B','O','O','O','O','O','d','w','O','O','O','O','O','B','B','O','O','B','D', - 'D','B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B','D', - 'D','B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B','D', - 'D','B','B','B','B','O','O','O','O','B','D','D','D','D','B','O','O','O','O','B','B','B','B','D', - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', -}; - -mapEntry crossBounce = { - .layout = crossBounceLayout, - .columns = 24, - .rows = 24, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = true, - .weaponPickups = 8, - .defaultWeapon = STANDARD_WEAPON,// TODO: make this exploding weapon - .maxSuddenDeathWalls = 6, -}; - -const char asteriskArenaLayout[]= { - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','D','W','O','O','O','W','D','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','D','O','O','O','D','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','D','D','O','O','O','O','O','O','O','O','O','D','D','O','O','O','O','D', - 'D','O','O','O','O','W','W','D','O','O','O','O','O','O','O','D','W','W','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','W','W','D','O','O','O','O','O','O','O','D','W','W','O','O','O','O','D', - 'D','O','O','O','O','D','D','O','O','O','O','O','O','O','O','O','D','D','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','D','O','O','O','D','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','D','W','O','O','O','W','D','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D','D', -}; - -mapEntry asteriskArena = { - .layout = asteriskArenaLayout, - .columns = 23, - .rows = 23, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = false, - .weaponPickups = 8, - .defaultWeapon = STANDARD_WEAPON, - .maxSuddenDeathWalls = 7, -}; - -const char foamPitLayout[] = { - 'B','B','B','W','W','W','D','D','D','B','B','D','D','D','W','W','W','B','B','B', - 'B','O','O','O','O','O','O','O','D','B','B','D','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','O','B','B','O','O','O','O','O','O','O','O','B', - 'W','O','O','d','O','O','O','O','O','O','O','O','O','O','O','O','d','O','O','W', - 'W','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','W', - 'W','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','W', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','d','O','O','O','O','d','O','O','O','O','O','O','D', - 'D','D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D','D', - 'B','B','B','O','O','O','O','O','O','d','d','O','O','O','O','O','O','B','B','B', - 'B','B','B','O','O','O','O','O','O','d','d','O','O','O','O','O','O','B','B','B', - 'D','D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D','D', - 'D','O','O','O','O','O','O','d','O','O','O','O','d','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'W','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','W', - 'W','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','W', - 'W','O','O','d','O','O','O','O','O','O','O','O','O','O','O','O','d','O','O','W', - 'B','O','O','O','O','O','O','O','O','B','B','O','O','O','O','O','O','O','O','B', - 'B','O','O','O','O','O','O','O','D','B','B','D','O','O','O','O','O','O','O','B', - 'B','B','B','W','W','W','D','D','D','B','B','D','D','D','W','W','W','B','B','B', -}; - -mapEntry foamPitMap = { - .layout = foamPitLayout, - .columns = 20, - .rows = 20, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = true, - .weaponPickups = 6, - .defaultWeapon = STANDARD_WEAPON, - .maxSuddenDeathWalls = 5, -}; - -const char siegeLayout[] = { - 'B','B','B','W','W','W','W','W','D','D','D','D','D','D','D','D','D','W','W','W','W','W','B','B','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','O','d','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','d','O','B', - 'W','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','W', - 'W','W','O','O','O','W','D','D','W','W','O','O','O','O','O','W','W','D','D','W','O','O','O','W','W', - 'W','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','W', - 'W','O','O','O','b','O','W','O','b','O','O','O','O','O','O','O','b','O','W','O','b','O','O','O','W', - 'W','O','b','O','O','O','W','O','O','O','O','O','O','O','O','O','O','O','W','O','O','O','b','O','W', - 'W','O','O','O','O','O','W','O','O','O','O','B','B','B','O','O','O','O','W','O','O','O','O','O','W', - 'W','W','O','O','O','W','W','O','O','O','O','O','O','O','O','O','O','O','W','W','O','O','O','W','W', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','b','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'D','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','D', - 'W','W','O','O','O','W','W','O','O','O','O','O','O','O','O','O','O','O','W','W','O','O','O','W','W', - 'W','O','O','O','O','O','W','O','O','O','O','B','B','B','O','O','O','O','W','O','O','O','O','O','W', - 'W','O','b','O','O','O','W','O','O','O','O','O','O','O','O','O','O','O','W','O','O','O','b','O','W', - 'W','O','O','O','b','O','W','O','b','O','O','O','O','O','O','O','b','O','W','O','b','O','O','O','W', - 'W','O','O','O','O','O','D','O','O','O','O','O','O','O','O','O','O','O','D','O','O','O','O','O','W', - 'W','W','O','O','O','W','D','D','W','W','O','O','O','O','O','W','W','D','D','W','O','O','O','W','W', - 'W','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','W', - 'B','O','d','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','d','O','B', - 'B','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','O','B', - 'B','B','B','W','W','W','W','W','D','D','D','D','D','D','D','D','D','W','W','W','W','W','B','B','B', -}; - -mapEntry siegeMap = { - .layout = siegeLayout, - .columns = 25, - .rows = 24, - .randFloatingStandardWalls = 0, - .randFloatingBouncyWalls = 0, - .randFloatingDeathWalls = 0, - .hasSetFloatingWalls = true, - .weaponPickups = 5, - .defaultWeapon = STANDARD_WEAPON, - .maxSuddenDeathWalls = 8, -}; - -// clang-format on - -mapEntry *maps[] = { - &boringMap, - &prototypeArenaMap, - &snipersMap, - &roomsMap, - &xArena, - &crossBounce, - &asteriskArena, - &foamPitMap, - &siegeMap, -}; - -void resetMap(iwEnv *e) { - // if sudden death walls were placed, remove them - if (e->suddenDeathWallsPlaced) { - e->suddenDeathWallsPlaced = false; - DEBUG_LOG("removing sudden death walls"); - // remove walls from the end of the array, sudden death walls - // are added last - for (int16_t i = cc_array_size(e->walls) - 1; i >= 0; i--) { - wallEntity *wall = safe_array_get_at(e->walls, i); - if (!wall->isSuddenDeath) { - // if we reached the first non sudden death wall, we're done - break; - } - cc_array_remove_last(e->walls, NULL); - destroyWall(e, wall, true); - } - } - - // place floating walls with a set position if there are any - const mapEntry *map = maps[e->mapIdx]; - if (!map->hasSetFloatingWalls) { - return; - } - DEBUG_LOG("placing set floating walls"); - - const uint8_t columns = map->columns; - const uint8_t rows = map->rows; - const char *layout = map->layout; - uint16_t cellIdx = 0; - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - char cellType = layout[col + (row * columns)]; - - enum entityType wallType; - switch (cellType) { - case 'w': - wallType = STANDARD_WALL_ENTITY; - break; - case 'b': - wallType = BOUNCY_WALL_ENTITY; - break; - case 'd': - wallType = DEATH_WALL_ENTITY; - break; - default: - cellIdx++; - continue; - } - - const mapCell *cell = safe_array_get_at(e->cells, cellIdx); - createWall(e, cell->pos, FLOATING_WALL_THICKNESS, FLOATING_WALL_THICKNESS, cellIdx, wallType, true); - cellIdx++; - } - } -} - -void setupMap(iwEnv *e, const uint8_t mapIdx) { - // reset the map if we're switching to the same map - if (e->mapIdx == mapIdx) { - resetMap(e); - return; - } - - // clear the old map - for (size_t i = 0; i < cc_array_size(e->walls); i++) { - wallEntity *wall = safe_array_get_at(e->walls, i); - destroyWall(e, wall, false); - } - - for (size_t i = 0; i < cc_array_size(e->cells); i++) { - mapCell *cell = safe_array_get_at(e->cells, i); - fastFree(cell); - } - - cc_array_remove_all(e->walls); - cc_array_remove_all(e->cells); - e->suddenDeathWallsPlaced = false; - - const uint8_t columns = maps[mapIdx]->columns; - const uint8_t rows = maps[mapIdx]->rows; - const char *layout = maps[mapIdx]->layout; - - e->mapIdx = mapIdx; - e->map = maps[mapIdx]; - e->defaultWeapon = weaponInfos[maps[mapIdx]->defaultWeapon]; - if (e->isTraining && randFloat(&e->randState, 0.0f, 1.0f) < 0.25f) { - e->defaultWeapon = weaponInfos[randInt(&e->randState, 0, NUM_WEAPONS - 1)]; - } - - uint16_t cellIdx = 0; - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - char cellType = layout[col + (row * columns)]; - enum entityType wallType; - const float x = (col - ((columns - 1) * 0.5f)) * WALL_THICKNESS; - const float y = (row - (rows - 1) * 0.5f) * WALL_THICKNESS; - - b2Vec2 pos = {.x = x, .y = y}; - mapCell *cell = fastCalloc(1, sizeof(mapCell)); - cell->ent = NULL; - cell->pos = pos; - cc_array_add(e->cells, cell); - - bool floating = false; - float thickness = WALL_THICKNESS; - switch (cellType) { - case 'O': - cellIdx++; - continue; - case 'w': - thickness = FLOATING_WALL_THICKNESS; - floating = true; - case 'W': - wallType = STANDARD_WALL_ENTITY; - break; - case 'b': - thickness = FLOATING_WALL_THICKNESS; - floating = true; - case 'B': - wallType = BOUNCY_WALL_ENTITY; - break; - case 'd': - thickness = FLOATING_WALL_THICKNESS; - floating = true; - case 'D': - wallType = DEATH_WALL_ENTITY; - break; - default: - ERRORF("unknown map layout cell %c", cellType); - } - - entity *ent = createWall(e, pos, thickness, thickness, cellIdx, wallType, floating); - if (!floating) { - cell->ent = ent; - } - cellIdx++; - } - } -} - -void computeMapBoundsAndQuadrants(iwEnv *e, mapEntry *map) { - mapBounds bounds = {.min = {.x = FLT_MAX, .y = FLT_MAX}, .max = {.x = FLT_MIN, .y = FLT_MIN}}; - for (size_t i = 0; i < cc_array_size(e->walls); i++) { - const wallEntity *wall = safe_array_get_at(e->walls, i); - bounds.min.x = min(wall->pos.x - wall->extent.x + WALL_THICKNESS, bounds.min.x); - bounds.min.y = min(wall->pos.y - wall->extent.y + WALL_THICKNESS, bounds.min.y); - bounds.max.x = max(wall->pos.x + wall->extent.x - WALL_THICKNESS, bounds.max.x); - bounds.max.y = max(wall->pos.y + wall->extent.y - WALL_THICKNESS, bounds.max.y); - } - map->bounds = bounds; - map->spawnQuads[0] = (mapBounds){ - .min = (b2Vec2){ - .x = map->bounds.min.x + WALL_THICKNESS, - .y = map->bounds.min.y + WALL_THICKNESS, - }, - .max = (b2Vec2){ - .x = 0.0f, - .y = 0.0f, - } - }; - map->spawnQuads[1] = (mapBounds){ - .min = (b2Vec2){ - .x = 0.0f, - .y = map->bounds.min.y + WALL_THICKNESS, - }, - .max = (b2Vec2){ - .x = map->bounds.max.x - WALL_THICKNESS, - .y = 0.0f, - } - }; - map->spawnQuads[2] = (mapBounds){ - .min = (b2Vec2){ - .x = map->bounds.min.x + WALL_THICKNESS, - .y = 0.0f, - }, - .max = (b2Vec2){ - .x = 0.0f, - .y = map->bounds.max.y - WALL_THICKNESS, - } - }; - map->spawnQuads[3] = (mapBounds){ - .min = (b2Vec2){ - .x = 0.0f, - .y = 0.0f, - }, - .max = (b2Vec2){ - .x = map->bounds.max.x - WALL_THICKNESS, - .y = map->bounds.max.y - WALL_THICKNESS, - } - }; -} - -bool posValidDroneSpawnPoint(const iwEnv *e, const b2Vec2 pos) { - const b2QueryFilter filter = { - .categoryBits = DRONE_SHAPE, - .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE, - }; - droneEntity dummyDrone = {.pos = pos}; - const entity ent = {.type = DRONE_ENTITY, .entity = &dummyDrone}; - const enum entityType deathWallType = DEATH_WALL_ENTITY; - - if (isOverlappingCircleInLineOfSight(e, &ent, pos, DRONE_DEATH_WALL_SPAWN_DISTANCE, filter, &deathWallType)) { - return false; - } - if (isOverlappingAABB(e, pos, DRONE_WALL_SPAWN_DISTANCE, filter)) { - return false; - } - - return true; -} - -void initMaps(iwEnv *e) { - for (uint8_t i = 0; i < NUM_MAPS; i++) { - setupMap(e, i); - mapEntry *map = maps[i]; - - computeMapBoundsAndQuadrants(e, map); - - bool *droneSpawns = fastCalloc(map->columns * map->rows, sizeof(bool)); - uint8_t *packedLayout = fastCalloc(map->columns * map->rows, sizeof(uint8_t)); - nearEntity *nearestWalls = fastCalloc(MAX_NEAREST_WALLS * map->columns * map->rows, sizeof(nearEntity)); - - for (uint16_t i = 0; i < cc_array_size(e->cells); i++) { - const mapCell *cell = safe_array_get_at(e->cells, i); - - // precompute packed map layout - if (cell->ent != NULL) { - packedLayout[i] = ((cell->ent->type + 1) & TWO_BIT_MASK) << 5; - continue; - } else { - // precompute valid cells for drones to spawn - droneSpawns[i] = posValidDroneSpawnPoint(e, cell->pos); - } - - // find nearest walls for each empty cell - uint16_t wallIdx = 0; - nearEntity walls[map->columns * map->rows]; - memset(walls, 0x0, map->columns * map->rows * sizeof(nearEntity)); - for (uint16_t j = 0; j < cc_array_size(e->cells); j++) { - const mapCell *c = safe_array_get_at(e->cells, j); - if (c->ent == NULL) { - continue; - } - - walls[wallIdx].idx = wallIdx; - walls[wallIdx].distanceSquared = b2DistanceSquared(cell->pos, c->pos); - wallIdx++; - } - insertionSort(walls, wallIdx); - - const uint32_t startIdx = i * MAX_NEAREST_WALLS; - memcpy(nearestWalls + startIdx, walls, MAX_NEAREST_WALLS * sizeof(nearEntity)); - } - map->droneSpawns = droneSpawns; - map->packedLayout = packedLayout; - map->nearestWalls = nearestWalls; - - // clear floating walls from the map - for (uint8_t i = 0; i < cc_array_size(e->floatingWalls); i++) { - wallEntity *wall = safe_array_get_at(e->floatingWalls, i); - destroyWall(e, wall, false); - } - cc_array_remove_all(e->floatingWalls); - } - - e->mapIdx = -1; -} - -void destroyMaps() { - for (uint8_t i = 0; i < NUM_MAPS; i++) { - mapEntry *map = maps[i]; - fastFree(map->droneSpawns); - fastFree(map->packedLayout); - fastFree(map->nearestWalls); - } -} - -void placeRandFloatingWall(iwEnv *e, const enum entityType wallType) { - b2Vec2 pos; - if (!findOpenPos(e, FLOATING_WALL_SHAPE, &pos, -1)) { - ERROR("failed to find open position for floating wall"); - } - int16_t cellIdx = entityPosToCellIdx(e, pos); - createWall(e, pos, FLOATING_WALL_THICKNESS, FLOATING_WALL_THICKNESS, cellIdx, wallType, true); -} - -void placeRandFloatingWalls(iwEnv *e, const int mapIdx) { - for (int i = 0; i < maps[mapIdx]->randFloatingStandardWalls; i++) { - placeRandFloatingWall(e, STANDARD_WALL_ENTITY); - } - for (int i = 0; i < maps[mapIdx]->randFloatingBouncyWalls; i++) { - placeRandFloatingWall(e, BOUNCY_WALL_ENTITY); - } - for (int i = 0; i < maps[mapIdx]->randFloatingDeathWalls; i++) { - placeRandFloatingWall(e, DEATH_WALL_ENTITY); - } -} - -#endif diff --git a/pufferlib/ocean/impulse_wars/pyproject.toml b/pufferlib/ocean/impulse_wars/pyproject.toml deleted file mode 100644 index df67b2bd1..000000000 --- a/pufferlib/ocean/impulse_wars/pyproject.toml +++ /dev/null @@ -1,25 +0,0 @@ -[build-system] -requires = ["scikit-build-core>=0.10", "autopxd2>=2.5.0", "cython>=3.0.11"] -build-backend = "scikit_build_core.build" - -[project] -name = "binding" -version = "1.0.0" -requires-python = ">=3.11" - -[tool.scikit-build] -minimum-version = "build-system.requires" -cmake.build-type = "Release" -build.verbose = true -logging.level = "INFO" - -[tool.scikit-build.cmake.define] -BUILD_PYTHON_MODULE = true -CMAKE_C_COMPILER = "clang-20" - -[tool.ruff] -line-length = 110 - -[tool.ruff.lint] -# skip "Module level import not at top of file" -ignore = ["E402"] diff --git a/pufferlib/ocean/impulse_wars/render.h b/pufferlib/ocean/impulse_wars/render.h deleted file mode 100644 index ca7b94c32..000000000 --- a/pufferlib/ocean/impulse_wars/render.h +++ /dev/null @@ -1,1777 +0,0 @@ -#ifndef IMPULSE_WARS_RENDER_H -#define IMPULSE_WARS_RENDER_H - -#include "raymath.h" -#include "rlgl.h" - -#define RLIGHTS_IMPLEMENTATION -#include "include/rlights.h" - -#include "helpers.h" - -#if defined(PLATFORM_DESKTOP) -#define GLSL_VERSION 330 -#else // PLATFORM_ANDROID, PLATFORM_WEB -#define GLSL_VERSION 100 -#endif - -#define LETTER_BOUNDRY_SIZE 0.25f -#define TEXT_MAX_LAYERS 32 -#define LETTER_BOUNDRY_COLOR VIOLET - -bool SHOW_LETTER_BOUNDRY = false; - -const Color STONE_GRAY = (Color){80, 80, 80, 255}; -const Color PUFF_RED = RED; -const Color PUFF_GREEN = GREEN; -const Color PUFF_YELLOW = YELLOW; -const Color PUFF_CYAN = BLUE; -const Color PUFF_WHITE = RAYWHITE; -const Color PUFF_BACKGROUND = BLACK; -const Color PUFF_BACKGROUND2 = BLACK; - -void setEnvFrameRate(iwEnv *e); -bool droneControlledByHuman(const iwEnv *e, uint8_t i); - -const float DEFAULT_SCALE = 11.0f; -const uint16_t DEFAULT_WIDTH = 1280; -const uint16_t DEFAULT_HEIGHT = 720; -const uint16_t HEIGHT_LEEWAY = 75; - -const float START_READY_TIME = 1.5f; -const float END_WAIT_TIME = 2.0f; - -const float EXPLOSION_TIME = 0.5f; - -const float DRONE_RESPAWN_GUIDE_SHRINK_TIME = 0.75f; -const float DRONE_RESPAWN_GUIDE_HOLD_TIME = 0.75f; -const float DRONE_RESPAWN_GUIDE_MAX_RADIUS = DRONE_RADIUS * 5.5f; -const float DRONE_RESPAWN_GUIDE_MIN_RADIUS = DRONE_RADIUS * 2.5f; - -const float DRONE_PIECE_LIFETIME = 2.0f; - -const Color barolo = {.r = 165, .g = 37, .b = 8, .a = 255}; -const Color bambooBrown = {.r = 204, .g = 129, .b = 0, .a = 255}; - -const float droneLightRadius = 0.1f; -const float halfDroneRadius = DRONE_RADIUS / 2.0f; -const float droneThrusterLength = 1.5f * DRONE_RADIUS; -const float aimGuideLength = 0.3f * DRONE_RADIUS; -const float chargedAimGuideLength = DRONE_RADIUS; - -static inline b2Vec2 rayVecToB2Vec(const iwEnv *e, const Vector2 v) { - return (b2Vec2){.x = (v.x - e->client->halfWidth) / e->renderScale, .y = ((v.y - e->client->halfHeight - (2 * e->renderScale)) / e->renderScale)}; -} - -void updateTrailPoints(trailPoints *tp, const uint8_t maxLen, const b2Vec2 pos) { - const Vector2 v = (Vector2){.x = pos.x, .y = pos.y}; - if (tp->length < maxLen) { - tp->points[tp->length++] = v; - return; - } - - for (uint8_t i = 0; i < maxLen - 1; i++) { - tp->points[i] = tp->points[i + 1]; - } - tp->points[maxLen - 1] = v; -} - -rayClient *createRayClient() { - SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, "Impulse Wars"); - - rayClient *client = fastCalloc(1, sizeof(rayClient)); - - if (client->height == 0) { -#ifndef __EMSCRIPTEN__ - const int monitor = GetCurrentMonitor(); - client->height = GetMonitorHeight(monitor) - HEIGHT_LEEWAY; - client->width = ((float)client->height * ((float)DEFAULT_WIDTH / (float)DEFAULT_HEIGHT)); -#else - client->width = DEFAULT_WIDTH; - client->height = DEFAULT_HEIGHT; -#endif - } - client->scale = (float)client->height * (float)(DEFAULT_SCALE / DEFAULT_HEIGHT); - - client->halfWidth = client->width / 2.0f; - client->halfHeight = client->height / 2.0f; - - SetWindowSize(client->width, client->height); - -#ifndef __EMSCRIPTEN__ - SetTargetFPS(EVAL_FRAME_RATE); -#endif - - client->camera = fastCalloc(1, sizeof(gameCamera)); - client->camera->camera3D = (Camera3D){ - .position = (Vector3){.x = 0.0f, .y = 100.0f, .z = 0.0f}, - .target = (Vector3){.x = 0.0f, .y = 0.0f, .z = 0.0f}, - .up = (Vector3){.x = 0.0f, .y = 0.0f, .z = -1.0f}, - .fovy = 45.0f, - .projection = CAMERA_PERSPECTIVE, - }; - client->camera->camera2D = (Camera2D){ - .offset = (Vector2){.x = client->width / 2.0f, .y = client->height / 2.0f}, - .target = (Vector2){.x = 0.0f, .y = 0.0f}, - .zoom = 1.0f, - }; - client->camera->orthographic = false; - - client->wallTexture = LoadTexture("resources/impulse_wars/wall_texture_map.png"); - client->blurSrcTexture = LoadRenderTexture(client->width, client->height); - client->blurDstTexture = LoadRenderTexture(client->width, client->height); - client->droneRawTex = LoadRenderTexture(client->width, client->height); - client->droneBloomTex = LoadRenderTexture(client->width, client->height); - client->projRawTex = LoadRenderTexture(client->width, client->height); - client->projBloomTex = LoadRenderTexture(client->width, client->height); - - const char *gridVSPath = TextFormat("resources/impulse_wars/shaders/gls%i/shader.vs", GLSL_VERSION); - const char *gridFSPath = TextFormat("resources/impulse_wars/shaders/gls%i/grid.fs", GLSL_VERSION); - client->gridShader = LoadShader(gridVSPath, gridFSPath); - for (int i = 0; i < 4; i++) { - client->gridShaderPosLoc[i] = GetShaderLocation(client->gridShader, TextFormat("pos[%i]", i)); - client->gridShaderColorLoc[i] = GetShaderLocation(client->gridShader, TextFormat("color[%i]", i)); - } - - const char *blurVSPath = TextFormat("resources/impulse_wars/shaders/gls%i/shader.vs", GLSL_VERSION); - const char *blurFSPath = TextFormat("resources/impulse_wars/shaders/gls%i/blur.fs", GLSL_VERSION); - client->blurShader = LoadShader(blurVSPath, blurFSPath); - client->blurShaderDirLoc = GetShaderLocation(client->blurShader, "uTexelDir"); - - const char *bloomVSPath = TextFormat("resources/impulse_wars/shaders/gls%i/shader.vs", GLSL_VERSION); - const char *bloomFSPath = TextFormat("resources/impulse_wars/shaders/gls%i/bloom.fs", GLSL_VERSION); - client->bloomShader = LoadShader(bloomVSPath, bloomFSPath); - int32_t bloomModeLoc = GetShaderLocation(client->bloomShader, "uBloomMode"); - const int32_t bloomMode = 1; - SetShaderValue(client->bloomShader, bloomModeLoc, &bloomMode, SHADER_UNIFORM_INT); - client->bloomIntensityLoc = GetShaderLocation(client->bloomShader, "uBloomIntensity"); - client->bloomTexColorLoc = GetShaderLocation(client->bloomShader, "uTexColor"); - client->bloomTexBloomBlurLoc = GetShaderLocation(client->bloomShader, "uTexBloomBlur"); - - return client; -} - -void setupRayClient(iwEnv *e) { - if (e->client != NULL) { - return; - } - // create a rendering client, change the env to eval mode and ensure - // it's reset so training only maps and behaviors aren't evaluated - e->client = createRayClient(); - e->isTraining = false; - setEnvFrameRate(e); - e->needsReset = true; -} - -void destroyRayClient(rayClient *client) { - UnloadTexture(client->wallTexture); - UnloadRenderTexture(client->blurSrcTexture); - UnloadRenderTexture(client->blurDstTexture); - UnloadRenderTexture(client->droneRawTex); - UnloadRenderTexture(client->droneBloomTex); - UnloadRenderTexture(client->projRawTex); - UnloadRenderTexture(client->projBloomTex); - - UnloadShader(client->gridShader); - UnloadShader(client->blurShader); - UnloadShader(client->bloomShader); - - CloseWindow(); - fastFree(client->camera); - fastFree(client); -} - -const float ZOOM_SPEED = 0.04f; -const float PAN_SPEED = 0.04f; -const float MAX_CAMERA_HEIGHT = 130.0f; -const float MIN_CAMERA_HEIGHT = 60.0f; -const float BOUNDS_PADDING = 4.0f; -const float MAP_MAX_X_OFFSET = 25.0f; -const float MAP_MAX_Y_OFFSET = 10.0f; - -void setCamera2DZoom(iwEnv *e) { - gameCamera *camera = e->client->camera; - - float screenHeightInWorldUnits = 2.0f * tanf((camera->camera3D.fovy * DEG2RAD) / 2.0f) * camera->camera3D.position.y; - camera->camera2D.zoom = e->client->height / screenHeightInWorldUnits; -} - -void setupEnvCamera(iwEnv *e) { - const float BASE_ROWS = 21.0f; - const float scale = e->client->scale * (BASE_ROWS / e->map->rows); - // TODO: remove this field? - e->renderScale = scale; - - gameCamera *camera = e->client->camera; - if (camera->orthographic) { - camera->camera3D.projection = CAMERA_ORTHOGRAPHIC; - camera->camera3D.position.x = 0.0f; - camera->camera3D.position.y = 25.0f; - camera->camera3D.position.z = -2.0f; - camera->camera3D.target.x = 0.0f; - camera->camera3D.target.y = 0.0f; - camera->camera3D.target.z = -2.0f; - camera->camera3D.fovy = (5.0f * e->map->rows) - 15.0f; - - camera->camera2D.target.x = 0.0f; - camera->camera2D.target.y = -2.0f; - camera->camera2D.zoom = (float)e->client->height / camera->camera3D.fovy; - } else { - camera->targetPos = Vector2Zero(); - - camera->camera3D.projection = CAMERA_PERSPECTIVE; - camera->maxZoom = (5 * e->map->rows) + 10; - const float startingZoom = max((camera->maxZoom + MIN_CAMERA_HEIGHT) / 2.0f, MIN_CAMERA_HEIGHT); - camera->camera3D.position = (Vector3){.x = 0.0f, .y = startingZoom, .z = 0.0f}; - camera->camera3D.target = (Vector3){.x = 0.0f, .y = 0.0f, .z = 0.0f}; - camera->camera3D.fovy = 45.0f; - - camera->camera2D.target.x = 0.0f; - camera->camera2D.target.y = 0.0f; - setCamera2DZoom(e); - } -} - -Rectangle calculatePlayersBoundingBox(const iwEnv *e) { - float minX = FLT_MAX; - float minY = FLT_MAX; - float maxX = -FLT_MAX; - float maxY = -FLT_MAX; - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead && drone->livesLeft == 0 && !drone->diedThisStep) { - continue; - } - - minX = min(drone->pos.x, minX); - minY = min(drone->pos.y, minY); - maxX = max(drone->pos.x, maxX); - maxY = max(drone->pos.y, maxY); - } - - Rectangle bounds = { - .x = (minX + maxX) / 2.0f, - .y = (minY + maxY) / 2.0f, - .width = (maxX - minX) + BOUNDS_PADDING, - .height = (maxY - minY) + BOUNDS_PADDING, - }; - return bounds; -} - -float calculateZoom(const iwEnv *e, Rectangle droneBounds) { - float boundsZoom = (droneBounds.width + droneBounds.height) * 0.5f; - boundsZoom += max(droneBounds.width, droneBounds.height) * 0.5f; - - float fov = tanf((e->client->camera->camera3D.fovy * DEG2RAD) / 2.0f); - float zoom = boundsZoom / fov; - zoom = (MIN_CAMERA_HEIGHT + zoom) * 0.45f; - zoom = Clamp(zoom, MIN_CAMERA_HEIGHT, e->client->camera->maxZoom); - - return zoom; -} - -void updateCamera(iwEnv *e) { - gameCamera *camera = e->client->camera; - - if (IsKeyPressed(KEY_TAB)) { - camera->orthographic = !camera->orthographic; - setupEnvCamera(e); - } - if (camera->orthographic) { - return; - } - - const Rectangle droneBounds = calculatePlayersBoundingBox(e); - if (droneBounds.width == 0 && droneBounds.height == 0) { - return; - } - - // smoothly move towards the center of players - const Vector2 centerPoint = {.x = droneBounds.x, .y = droneBounds.y}; - camera->targetPos = Vector2Lerp(camera->targetPos, centerPoint, PAN_SPEED); - camera->targetPos.x = Clamp(camera->targetPos.x, e->map->bounds.min.x + MAP_MAX_X_OFFSET, e->map->bounds.max.x - MAP_MAX_X_OFFSET); - camera->targetPos.y = Clamp(camera->targetPos.y, e->map->bounds.min.y + MAP_MAX_Y_OFFSET, e->map->bounds.max.y - MAP_MAX_Y_OFFSET); - - camera->camera3D.target.x = camera->targetPos.x; - camera->camera3D.target.z = camera->targetPos.y; - - camera->camera3D.position.x = camera->targetPos.x; - camera->camera3D.position.z = camera->targetPos.y; - - // smoothly zoom in or out - const float zoom = calculateZoom(e, droneBounds); - camera->camera3D.position.y = Lerp(camera->camera3D.position.y, zoom, ZOOM_SPEED); - setCamera2DZoom(e); - - // update 2D camera - camera->camera2D.target = camera->targetPos; -} - -Color getDroneColor(const uint8_t droneIdx) { - switch (droneIdx) { - case 0: - return barolo; - case 1: - return PUFF_GREEN; - case 2: - return PUFF_CYAN; - case 3: - return PUFF_YELLOW; - default: - ERRORF("unsupported number of drones %d", droneIdx + 1); - } -} - -char *getWeaponAbreviation(const enum weaponType type) { - char *name = ""; - switch (type) { - case MACHINEGUN_WEAPON: - name = "MCGN"; - break; - case SNIPER_WEAPON: - // TODO: rename to railgun everywhere - name = "RAIL"; - break; - case SHOTGUN_WEAPON: - name = "SHGN"; - break; - case IMPLODER_WEAPON: - name = "IMPL"; - break; - case ACCELERATOR_WEAPON: - name = "ACCL"; - break; - case FLAK_CANNON_WEAPON: - name = "FLAK"; - break; - case MINE_LAUNCHER_WEAPON: - name = "MINE"; - break; - case BLACK_HOLE_WEAPON: - name = "BLKH"; - break; - case NUKE_WEAPON: - name = "NUKE"; - break; - default: - ERRORF("unknown weapon pickup type %d", type); - } - return name; -} - -char *getWeaponName(const enum weaponType type) { - char *name = ""; - switch (type) { - case STANDARD_WEAPON: - name = "Standard"; - break; - case MACHINEGUN_WEAPON: - name = "Machine Gun"; - break; - case SNIPER_WEAPON: - name = "Railgun"; - break; - case SHOTGUN_WEAPON: - name = "Shotgun"; - break; - case IMPLODER_WEAPON: - name = "Imploder"; - break; - case ACCELERATOR_WEAPON: - name = "Accelerator"; - break; - case FLAK_CANNON_WEAPON: - name = "Flak Cannon"; - break; - case MINE_LAUNCHER_WEAPON: - name = "Mine Launcher"; - break; - case BLACK_HOLE_WEAPON: - name = "Black Hole"; - break; - case NUKE_WEAPON: - name = "Tactical Nuke"; - break; - default: - ERRORF("unknown weapon pickup type %d", type); - } - return name; -} - -float getWeaponAimGuideWidth(const enum weaponType type) { - switch (type) { - case STANDARD_WEAPON: - case IMPLODER_WEAPON: - case ACCELERATOR_WEAPON: - case NUKE_WEAPON: - return 5.0f; - case FLAK_CANNON_WEAPON: - case BLACK_HOLE_WEAPON: - return 7.5f; - case MACHINEGUN_WEAPON: - case MINE_LAUNCHER_WEAPON: - return 10.0f; - case SNIPER_WEAPON: - return 150.0f; - case SHOTGUN_WEAPON: - return 3.0f; - default: - ERRORF("unknown weapon when getting aim guide width %d", type); - } -} - -Color getProjectileColor(const enum weaponType type) { - Color color; - switch (type) { - case STANDARD_WEAPON: - color = PURPLE; - break; - case IMPLODER_WEAPON: - case ACCELERATOR_WEAPON: - color = DARKBLUE; - break; - case FLAK_CANNON_WEAPON: - color = MAROON; - break; - case MACHINEGUN_WEAPON: - case SNIPER_WEAPON: - case SHOTGUN_WEAPON: - color = ORANGE; - break; - case MINE_LAUNCHER_WEAPON: - color = BROWN; - break; - case BLACK_HOLE_WEAPON: - color = DARKGRAY; - break; - case NUKE_WEAPON: - color = MAROON; - break; - default: - ERRORF("unknown weapon when getting projectile color %d", type); - } - - color.r *= 0.5f; - color.g *= 0.5f; - color.b *= 0.5f; - return color; -} - -void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color) { - float x = position.x; - float y = position.y; - float z = position.z; - - // Set desired texture to be enabled while drawing following vertex data - rlSetTexture(texture.id); - - rlBegin(RL_QUADS); - rlColor4ub(color.r, color.g, color.b, color.a); - // Front Face - rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer - rlTexCoord2f(0.0f, 0.0f); - rlVertex3f(x - width / 2, y - height / 2, z + length / 2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); - rlVertex3f(x + width / 2, y - height / 2, z + length / 2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); - rlVertex3f(x + width / 2, y + height / 2, z + length / 2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); - rlVertex3f(x - width / 2, y + height / 2, z + length / 2); // Top Left Of The Texture and Quad - // Back Face - rlNormal3f(0.0f, 0.0f, -1.0f); // Normal Pointing Away From Viewer - rlTexCoord2f(1.0f, 0.0f); - rlVertex3f(x - width / 2, y - height / 2, z - length / 2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); - rlVertex3f(x - width / 2, y + height / 2, z - length / 2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); - rlVertex3f(x + width / 2, y + height / 2, z - length / 2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); - rlVertex3f(x + width / 2, y - height / 2, z - length / 2); // Bottom Left Of The Texture and Quad - // Top Face - rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up - rlTexCoord2f(0.0f, 1.0f); - rlVertex3f(x - width / 2, y + height / 2, z - length / 2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); - rlVertex3f(x - width / 2, y + height / 2, z + length / 2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); - rlVertex3f(x + width / 2, y + height / 2, z + length / 2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); - rlVertex3f(x + width / 2, y + height / 2, z - length / 2); // Top Right Of The Texture and Quad - // Bottom Face - rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down - rlTexCoord2f(1.0f, 1.0f); - rlVertex3f(x - width / 2, y - height / 2, z - length / 2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); - rlVertex3f(x + width / 2, y - height / 2, z - length / 2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); - rlVertex3f(x + width / 2, y - height / 2, z + length / 2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); - rlVertex3f(x - width / 2, y - height / 2, z + length / 2); // Bottom Right Of The Texture and Quad - // Right face - rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right - rlTexCoord2f(1.0f, 0.0f); - rlVertex3f(x + width / 2, y - height / 2, z - length / 2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); - rlVertex3f(x + width / 2, y + height / 2, z - length / 2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); - rlVertex3f(x + width / 2, y + height / 2, z + length / 2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); - rlVertex3f(x + width / 2, y - height / 2, z + length / 2); // Bottom Left Of The Texture and Quad - // Left Face - rlNormal3f(-1.0f, 0.0f, 0.0f); // Normal Pointing Left - rlTexCoord2f(0.0f, 0.0f); - rlVertex3f(x - width / 2, y - height / 2, z - length / 2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); - rlVertex3f(x - width / 2, y - height / 2, z + length / 2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); - rlVertex3f(x - width / 2, y + height / 2, z + length / 2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); - rlVertex3f(x - width / 2, y + height / 2, z - length / 2); // Top Left Of The Texture and Quad - rlEnd(); - // rlPopMatrix(); - - rlSetTexture(0); -} - -// Draw cube with texture piece applied to all faces -void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, float width, float height, float length, Color color) { - float x = position.x; - float y = position.y; - float z = position.z; - float texWidth = (float)texture.width; - float texHeight = (float)texture.height; - - // Set desired texture to be enabled while drawing following vertex data - rlSetTexture(texture.id); - - // We calculate the normalized texture coordinates for the desired texture-source-rectangle - // It means converting from (tex.width, tex.height) coordinates to [0.0f, 1.0f] equivalent - rlBegin(RL_QUADS); - rlColor4ub(color.r, color.g, color.b, color.a); - - // Front face - rlNormal3f(0.0f, 0.0f, 1.0f); - rlTexCoord2f(source.x / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x - width / 2, y - height / 2, z + length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x + width / 2, y - height / 2, z + length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, source.y / texHeight); - rlVertex3f(x + width / 2, y + height / 2, z + length / 2); - rlTexCoord2f(source.x / texWidth, source.y / texHeight); - rlVertex3f(x - width / 2, y + height / 2, z + length / 2); - - // Back face - rlNormal3f(0.0f, 0.0f, -1.0f); - rlTexCoord2f((source.x + source.width) / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x - width / 2, y - height / 2, z - length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, source.y / texHeight); - rlVertex3f(x - width / 2, y + height / 2, z - length / 2); - rlTexCoord2f(source.x / texWidth, source.y / texHeight); - rlVertex3f(x + width / 2, y + height / 2, z - length / 2); - rlTexCoord2f(source.x / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x + width / 2, y - height / 2, z - length / 2); - - // Top face - rlNormal3f(0.0f, 1.0f, 0.0f); - rlTexCoord2f(source.x / texWidth, source.y / texHeight); - rlVertex3f(x - width / 2, y + height / 2, z - length / 2); - rlTexCoord2f(source.x / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x - width / 2, y + height / 2, z + length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x + width / 2, y + height / 2, z + length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, source.y / texHeight); - rlVertex3f(x + width / 2, y + height / 2, z - length / 2); - - // Bottom face - rlNormal3f(0.0f, -1.0f, 0.0f); - rlTexCoord2f((source.x + source.width) / texWidth, source.y / texHeight); - rlVertex3f(x - width / 2, y - height / 2, z - length / 2); - rlTexCoord2f(source.x / texWidth, source.y / texHeight); - rlVertex3f(x + width / 2, y - height / 2, z - length / 2); - rlTexCoord2f(source.x / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x + width / 2, y - height / 2, z + length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x - width / 2, y - height / 2, z + length / 2); - - // Right face - rlNormal3f(1.0f, 0.0f, 0.0f); - rlTexCoord2f((source.x + source.width) / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x + width / 2, y - height / 2, z - length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, source.y / texHeight); - rlVertex3f(x + width / 2, y + height / 2, z - length / 2); - rlTexCoord2f(source.x / texWidth, source.y / texHeight); - rlVertex3f(x + width / 2, y + height / 2, z + length / 2); - rlTexCoord2f(source.x / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x + width / 2, y - height / 2, z + length / 2); - - // Left face - rlNormal3f(-1.0f, 0.0f, 0.0f); - rlTexCoord2f(source.x / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x - width / 2, y - height / 2, z - length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, (source.y + source.height) / texHeight); - rlVertex3f(x - width / 2, y - height / 2, z + length / 2); - rlTexCoord2f((source.x + source.width) / texWidth, source.y / texHeight); - rlVertex3f(x - width / 2, y + height / 2, z + length / 2); - rlTexCoord2f(source.x / texWidth, source.y / texHeight); - rlVertex3f(x - width / 2, y + height / 2, z - length / 2); - - rlEnd(); - - rlSetTexture(0); -} - -static void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint) { - // Character index position in sprite font - // NOTE: In case a codepoint is not available in the font, index returned points to '?' - int index = GetGlyphIndex(font, codepoint); - float scale = fontSize / (float)font.baseSize; - - // Character destination rectangle on screen - // NOTE: We consider charsPadding on drawing - position.x += (float)(font.glyphs[index].offsetX - font.glyphPadding) / (float)font.baseSize * scale; - position.z += (float)(font.glyphs[index].offsetY - font.glyphPadding) / (float)font.baseSize * scale; - - // Character source rectangle from font texture atlas - // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects - Rectangle srcRec = { - .x = font.recs[index].x - (float)font.glyphPadding, - .y = font.recs[index].y - (float)font.glyphPadding, - .width = font.recs[index].width + 2.0f * font.glyphPadding, - .height = font.recs[index].height + 2.0f * font.glyphPadding, - }; - - float width = (float)(font.recs[index].width + 2.0f * font.glyphPadding) / (float)font.baseSize * scale; - float height = (float)(font.recs[index].height + 2.0f * font.glyphPadding) / (float)font.baseSize * scale; - - if (font.texture.id > 0) { - const float x = 0.0f; - const float y = 0.0f; - const float z = 0.0f; - - // normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f) - const float tx = srcRec.x / font.texture.width; - const float ty = srcRec.y / font.texture.height; - const float tw = (srcRec.x + srcRec.width) / font.texture.width; - const float th = (srcRec.y + srcRec.height) / font.texture.height; - - if (SHOW_LETTER_BOUNDRY) { - DrawCubeWiresV((Vector3){position.x + width / 2, position.y, position.z + height / 2}, (Vector3){width, LETTER_BOUNDRY_SIZE, height}, LETTER_BOUNDRY_COLOR); - } - - rlCheckRenderBatchLimit(4 + 4 * backface); - rlSetTexture(font.texture.id); - - rlPushMatrix(); - rlTranslatef(position.x, position.y, position.z); - - rlBegin(RL_QUADS); - rlColor4ub(tint.r, tint.g, tint.b, tint.a); - - // Front Face - rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up - rlTexCoord2f(tx, ty); - rlVertex3f(x, y, z); // Top Left Of The Texture and Quad - rlTexCoord2f(tx, th); - rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad - rlTexCoord2f(tw, th); - rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad - rlTexCoord2f(tw, ty); - rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad - - if (backface) { - // Back Face - rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down - rlTexCoord2f(tx, ty); - rlVertex3f(x, y, z); // Top Right Of The Texture and Quad - rlTexCoord2f(tw, ty); - rlVertex3f(x + width, y, z); // Top Left Of The Texture and Quad - rlTexCoord2f(tw, th); - rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad - rlTexCoord2f(tx, th); - rlVertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad - } - rlEnd(); - rlPopMatrix(); - - rlSetTexture(0); - } -} - -static void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint) { - int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop - - float textOffsetY = 0.0f; // Offset between lines (on line break '\n') - float textOffsetX = 0.0f; // Offset X to next character to draw - - const float scale = fontSize / (float)font.baseSize; - - for (int i = 0; i < length;) { - // Get next codepoint from byte string and glyph index in font - int codepointByteCount = 0; - int codepoint = GetCodepoint(&text[i], &codepointByteCount); - int index = GetGlyphIndex(font, codepoint); - - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all of the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) { - codepointByteCount = 1; - } - - if (codepoint == '\n') { - // NOTE: Fixed line spacing of 1.5 line-height - // TODO: Support custom line spacing defined by user - textOffsetY += scale + lineSpacing / (float)font.baseSize * scale; - textOffsetX = 0.0f; - } else { - if ((codepoint != ' ') && (codepoint != '\t')) { - DrawTextCodepoint3D(font, codepoint, (Vector3){position.x + textOffsetX, position.y, position.z + textOffsetY}, fontSize, backface, tint); - } - - if (font.glyphs[index].advanceX == 0) { - textOffsetX += (float)(font.recs[index].width + fontSpacing) / (float)font.baseSize * scale; - } else { - textOffsetX += (float)(font.glyphs[index].advanceX + fontSpacing) / (float)font.baseSize * scale; - } - } - - i += codepointByteCount; // Move text bytes counter to next codepoint - } -} - -void renderTimer(const iwEnv *e, const char *timerStr, const Color color) { - int fontSize = 2.5 * e->client->scale; - int textWidth = MeasureText(timerStr, fontSize); - int posX = (e->client->width - textWidth) / 2; - DrawText(timerStr, posX, e->client->scale, fontSize, color); -} - -void renderUI(const iwEnv *e, const bool starting) { - // render drone info - const uint8_t fontSize = 2 * e->client->scale; - const uint8_t xMargin = 5 * e->client->scale; - const uint8_t yMargin = 12 * e->client->scale; - - for (int i = 0; i < e->numDrones; i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - - const char *droneNum = TextFormat("Drone %d", drone->idx + 1); - const Vector2 textSize = MeasureTextEx(GetFontDefault(), droneNum, fontSize, fontSize / 10); - const uint16_t lineWidth = textSize.x + (3 * (e->client->scale * 2.5f)); - - uint16_t x = 0; - uint16_t y = 0; - switch (drone->idx) { - case 0: - x = xMargin; - y = yMargin; - break; - case 1: - x = e->client->width - lineWidth - xMargin; - y = yMargin; - break; - case 2: - x = xMargin; - y = e->client->height - yMargin - (6 * e->client->scale); - break; - case 3: - x = e->client->width - lineWidth - xMargin; - y = e->client->height - yMargin - (6 * e->client->scale); - break; - } - - Color textColor = PUFF_WHITE; - if (drone->livesLeft == 0) { - textColor = Fade(PUFF_WHITE, 0.5f); - } - DrawText(droneNum, x, y, fontSize, textColor); - - uint16_t lifeX = x + textSize.x; - uint16_t lifeY = y + (textSize.y / 2); - const Color droneColor = getDroneColor(drone->idx); - for (uint8_t i = 0; i < drone->livesLeft; i++) { - lifeX += e->client->scale * 2.5f; - DrawCircleLines(lifeX, lifeY, e->client->scale, droneColor); - } - - y += textSize.y + e->client->scale; - DrawLine(x, y, x + textSize.x, y, droneColor); - - y += e->client->scale; - const char *weaponName = getWeaponName(drone->weaponInfo->type); - DrawText(weaponName, x, y, fontSize, textColor); - - y += textSize.y + e->client->scale; - DrawLine(x, y, x + MeasureText(weaponName, fontSize), y, droneColor); - - y += e->client->scale; - char *playerType = ""; - if (droneControlledByHuman(e, drone->idx)) { - playerType = "Human"; - } else if (drone->idx < e->numAgents) { - playerType = "NN"; - } else { - if (e->sittingDuck) { - playerType = "Sitting Duck"; - } else { - playerType = "Scripted"; - } - } - char *droneInfo; - if (e->teamsEnabled) { - droneInfo = (char *)TextFormat("%s | Team %d", playerType, drone->team + 1); - } else { - droneInfo = playerType; - } - - DrawText(droneInfo, x, y, fontSize, textColor); - - if (drone->killedBy != -1) { - y += textSize.y + e->client->scale; - const char *killedBy = TextFormat("Killed by Player %d", drone->killedBy + 1); - - DrawText(killedBy, x, y, fontSize, getDroneColor(drone->killedBy)); - } - } - - // render timer - if (starting) { - renderTimer(e, "READY", PUFF_WHITE); - return; - } else if (e->stepsLeft > (ROUND_STEPS - 1) * e->frameRate) { - renderTimer(e, "GO!", PUFF_WHITE); - return; - } else if (e->stepsLeft == 0) { - renderTimer(e, "SUDDEN DEATH", PUFF_WHITE); - return; - } - - char *timerStr; - if (e->stepsLeft >= 10 * e->frameRate) { - timerStr = (char *)TextFormat("%d", (uint16_t)(e->stepsLeft / e->frameRate)); - } else { - timerStr = (char *)TextFormat("0%d", (uint16_t)(e->stepsLeft / e->frameRate)); - } - renderTimer(e, timerStr, PUFF_WHITE); -} - -void renderBrakeTrails(iwEnv *e, const droneEntity *drone) { - const float maxLifetime = 3.0f * e->frameRate; - const float maxAlpha = 0.33f; - const float trailWidth = 0.33f; - - // update lifetimes and prune expired points - CC_ArrayIter iter; - cc_array_iter_init(&iter, drone->brakeTrailPoints); - brakeTrailPoint *pt; - while (cc_array_iter_next(&iter, (void **)&pt) != CC_ITER_END) { - if (pt->lifetime == UINT16_MAX) { - pt->lifetime = maxLifetime; - } else if (pt->lifetime == 0) { - fastFree(pt); - cc_array_iter_remove(&iter, NULL); - continue; - } else { - pt->lifetime--; - } - } - - size_t count = cc_array_size(drone->brakeTrailPoints); - if (count < 2) { - return; - } - - for (size_t i = 0; i + 1 < count; i++) { - const brakeTrailPoint *trailPoint0 = safe_array_get_at(drone->brakeTrailPoints, i); - if (trailPoint0->isEnd) { - continue; - } - const brakeTrailPoint *trailPoint1 = safe_array_get_at(drone->brakeTrailPoints, i + 1); - const Vector2 p0 = (Vector2){.x = trailPoint0->pos.x, .y = trailPoint0->pos.y}; - const Vector2 p1 = (Vector2){.x = trailPoint1->pos.x, .y = trailPoint1->pos.y}; - - // compute direction and a perpendicular vector - Vector2 segment = Vector2Subtract(p1, p0); - if (Vector2Length(segment) == 0) { - continue; - } - segment = Vector2Normalize(segment); - const Vector2 perp = {-segment.y, segment.x}; - - // compute four vertices for the quad segment - const Vector2 v0 = Vector2Add(p0, Vector2Scale(perp, trailWidth)); - const Vector2 v1 = Vector2Subtract(p0, Vector2Scale(perp, trailWidth)); - const Vector2 v2 = Vector2Add(p1, Vector2Scale(perp, trailWidth)); - const Vector2 v3 = Vector2Subtract(p1, Vector2Scale(perp, trailWidth)); - - // draw the quad as two triangles - const float alpha0 = maxAlpha * (trailPoint0->lifetime / maxLifetime); - const float alpha1 = maxAlpha * (trailPoint1->lifetime / maxLifetime); - DrawTriangle3D( - (Vector3){.x = v0.x, .y = 0.0f, .z = v0.y}, - (Vector3){.x = v2.x, .y = 0.0f, .z = v2.y}, - (Vector3){.x = v1.x, .y = 0.0f, .z = v1.y}, - Fade(GRAY, alpha0) - ); - DrawTriangle3D( - (Vector3){.x = v1.x, .y = 0.0f, .z = v1.y}, - (Vector3){.x = v2.x, .y = 0.0f, .z = v2.y}, - (Vector3){.x = v3.x, .y = 0.0f, .z = v3.y}, - Fade(GRAY, alpha1) - ); - } -} - -// TODO: improve -void renderExplosions(const iwEnv *e) { - const uint16_t maxRenderSteps = EXPLOSION_TIME * e->frameRate; - - CC_ArrayIter iter; - cc_array_iter_init(&iter, e->explosions); - explosionInfo *explosion; - - while (cc_array_iter_next(&iter, (void **)&explosion) != CC_ITER_END) { - if (explosion->renderSteps == UINT16_MAX) { - explosion->renderSteps = maxRenderSteps; - } else if (explosion->renderSteps == 0) { - fastFree(explosion); - cc_array_iter_remove(&iter, NULL); - continue; - } - - // color bursts with a bit of the parent drone's color' - const float alpha = (float)explosion->renderSteps / maxRenderSteps; - BeginBlendMode(BLEND_ALPHA); - if (false && explosion->isBurst) { - const Color droneColor = Fade(getDroneColor(explosion->droneIdx), alpha); - DrawSphereEx( - (Vector3){.x = explosion->def.position.x, .y = 0.5f, .z = explosion->def.position.y}, - explosion->def.radius + explosion->def.falloff, - 20, - 50, - DARKGRAY - ); - DrawSphereEx( - (Vector3){.x = explosion->def.position.x, .y = 0.5f, .z = explosion->def.position.y}, - explosion->def.radius, - 20, - 50, - droneColor - ); - } else { - const Color falloffColor = Fade(GRAY, alpha); - const Color explosionColor = Fade(RAYWHITE, alpha); - - DrawSphereEx( - (Vector3){.x = explosion->def.position.x, .y = 0.5f, .z = explosion->def.position.y}, - explosion->def.radius + explosion->def.falloff, - 20, - 50, - falloffColor - ); - DrawSphereEx( - (Vector3){.x = explosion->def.position.x, .y = 0.5f, .z = explosion->def.position.y}, - explosion->def.radius, - 20, - 50, - explosionColor - ); - } - EndBlendMode(); - - explosion->renderSteps = max(explosion->renderSteps - 1, 0); - } -} - -// TODO: add bloom lines at drone level -void renderWall(const iwEnv *e, const wallEntity *wall) { - Color color = {0}; - Rectangle textureRec; - switch (wall->type) { - case STANDARD_WALL_ENTITY: - color = PUFF_CYAN; - textureRec = (Rectangle){ - 0.0f, - 0.0f, - e->client->wallTexture.width / 2.0f, - e->client->wallTexture.height / 2.0f, - }; - break; - case BOUNCY_WALL_ENTITY: - color = PUFF_YELLOW; - textureRec = (Rectangle){ - 0.0f, - e->client->wallTexture.height / 2.0f, - e->client->wallTexture.width / 2.0f, - e->client->wallTexture.height / 2.0f, - }; - break; - case DEATH_WALL_ENTITY: - color = PUFF_RED; - textureRec = (Rectangle){ - e->client->wallTexture.width / 2.0f, - 0.0f, - e->client->wallTexture.width / 2.0f, - e->client->wallTexture.height / 2.0f, - }; - break; - default: - ERRORF("unknown wall type %d", wall->type); - } - - float angle = 0.0f; - if (wall->isFloating) { - angle = b2Rot_GetAngle(wall->rot); - angle *= RAD2DEG; - } - - float y; - float y_size; - if (wall->isFloating) { - y = (FLOATING_WALL_THICKNESS / 2.0f) - 1.0f; - y_size = FLOATING_WALL_THICKNESS; - } else { - y = (WALL_THICKNESS / 2.0f) - 1.0f; - y_size = WALL_THICKNESS; - } - - float x = 2.0f * wall->extent.x; - float z = 2.0f * wall->extent.y; - - rlPushMatrix(); - rlTranslatef(wall->pos.x, 0.0f, wall->pos.y); - rlRotatef(-angle, 0.0f, 1.0f, 0.0f); - - DrawCubeTextureRec( - e->client->wallTexture, - textureRec, - (Vector3){.x = 0.0f, .y = y, .z = 0.0f}, - x, - y_size, - z, - color - ); - - if (!wall->isFloating) { - for (uint8_t i = 0; i < 3; i++) { - y -= WALL_THICKNESS; - DrawCubeTextureRec( - e->client->wallTexture, - textureRec, - (Vector3){.x = 0.0f, .y = y, .z = 0.0f}, - x, - y_size, - z, - color - ); - } - } - - rlPopMatrix(); -} - -void renderWeaponPickup(const iwEnv *e, const weaponPickupEntity *pickup) { - if (pickup->respawnWait != 0.0f || pickup->floatingWallsTouching != 0) { - return; - } - Rectangle textureRec = (Rectangle){ - e->client->wallTexture.width / 2.0f, - e->client->wallTexture.height / 2.0f, - e->client->wallTexture.width / 2.0f, - e->client->wallTexture.height / 2.0f, - }; - - DrawCubeTextureRec( - e->client->wallTexture, - textureRec, - (Vector3){.x = pickup->pos.x, .y = 0.5f, .z = pickup->pos.y}, - PICKUP_THICKNESS, - 0.0f, - PICKUP_THICKNESS, - WHITE - ); - - const char *weaponName = getWeaponAbreviation(pickup->weapon); - Vector3 textPos = (Vector3){ - .x = pickup->pos.x - PICKUP_THICKNESS / 2.0f, - .y = 1.0f, - .z = pickup->pos.y - PICKUP_THICKNESS / 2.0f - }; - - DrawText3D(GetFontDefault(), weaponName, textPos, 12, 0.5f, -1.0f, false, PUFF_WHITE); -} - -void renderDronePieces(iwEnv *e) { - const float maxLifetime = e->frameRate * DRONE_PIECE_LIFETIME; - - CC_ArrayIter iter; - cc_array_iter_init(&iter, e->dronePieces); - dronePieceEntity *piece; - - while (cc_array_iter_next(&iter, (void **)&piece) != CC_ITER_END) { - if (piece->lifetime == UINT16_MAX) { - piece->lifetime = maxLifetime; - } - - float baseAlpha = 1.0f; - if (piece->isShieldPiece) { - baseAlpha = 0.5f; - } - const float alpha = 1.0f - (baseAlpha * ((float)piece->lifetime / maxLifetime)); - const float finalAlpha = 1.0f - (SQUARED(alpha) * alpha); - const Color color = Fade(getDroneColor(piece->droneIdx), finalAlpha); - const float angle = RAD2DEG * b2Rot_GetAngle(piece->rot); - - // Draw edges of the triangle - rlPushMatrix(); - rlTranslatef(piece->pos.x, 0.0f, piece->pos.y); - rlRotatef(-angle, 0.0f, 1.0f, 0.0f); - - rlBegin(RL_LINES); - rlColor4ub(color.r, color.g, color.b, color.a); - - rlVertex3f(piece->vertices[0].x, 0.5f, piece->vertices[0].y); - rlVertex3f(piece->vertices[1].x, 0.5f, piece->vertices[1].y); - - rlVertex3f(piece->vertices[1].x, 0.5f, piece->vertices[1].y); - rlVertex3f(piece->vertices[2].x, 0.5f, piece->vertices[2].y); - - rlVertex3f(piece->vertices[2].x, 0.5f, piece->vertices[2].y); - rlVertex3f(piece->vertices[0].x, 0.5f, piece->vertices[0].y); - - rlEnd(); - - rlPopMatrix(); - - piece->lifetime--; - if (piece->lifetime == 0) { - destroyDronePiece(e, piece); - cc_array_iter_remove_fast(&iter, NULL); - } - } -} - -void renderDroneRespawnGuides(const iwEnv *e, droneEntity *drone) { - if (drone->respawnGuideLifetime == 0) { - return; - } - - const float maxLifetime = e->frameRate * (DRONE_RESPAWN_GUIDE_SHRINK_TIME + DRONE_RESPAWN_GUIDE_HOLD_TIME); - const uint16_t shrinkTime = e->frameRate * DRONE_RESPAWN_GUIDE_SHRINK_TIME; - if (drone->respawnGuideLifetime == UINT16_MAX) { - drone->respawnGuideLifetime = maxLifetime; - } - - float radius = DRONE_RESPAWN_GUIDE_MIN_RADIUS; - if (drone->respawnGuideLifetime >= maxLifetime - shrinkTime) { - radius += DRONE_RESPAWN_GUIDE_MAX_RADIUS * ((drone->respawnGuideLifetime - (e->frameRate * DRONE_RESPAWN_GUIDE_HOLD_TIME)) / shrinkTime); - } - - Vector2 dronePos = (Vector2){.x = drone->pos.x, .y = drone->pos.y}; - DrawCircleLinesV(dronePos, radius, getDroneColor(drone->idx)); - - drone->respawnGuideLifetime--; -} - -b2RayResult droneAimingAt(const iwEnv *e, const droneEntity *drone) { - const b2Vec2 rayEnd = b2MulAdd(drone->pos, 150.0f, drone->lastAim); - const b2Vec2 translation = b2Sub(rayEnd, drone->pos); - const b2QueryFilter filter = {.categoryBits = PROJECTILE_SHAPE, .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | DRONE_SHAPE}; - return b2World_CastRayClosest(e->worldID, drone->pos, translation, filter); -} - -void renderDroneAimGuide(const iwEnv *e, const droneEntity *drone) { - // find length of laser aiming guide by where it touches the nearest shape - const b2RayResult rayRes = droneAimingAt(e, drone); - ASSERT(b2Shape_IsValid(rayRes.shapeId)); - const entity *ent = b2Shape_GetUserData(rayRes.shapeId); - - const b2DistanceOutput output = closestPoint(drone->ent, ent); - float aimGuideWidth = getWeaponAimGuideWidth(drone->weaponInfo->type); - aimGuideWidth = min(aimGuideWidth, output.distance + 0.1f) + (DRONE_RADIUS * 2.0f); - - // render laser aim guide - const b2Vec2 pos = b2MulAdd(drone->pos, aimGuideWidth / 2.0f, drone->lastAim); - const float aimAngle = RAD2DEG * b2Atan2(drone->lastAim.y, drone->lastAim.x); - - rlPushMatrix(); - rlTranslatef(pos.x, 0.0f, pos.y); - rlRotatef(-aimAngle, 0.0f, 1.0f, 0.0f); - - const Color droneColor = getDroneColor(drone->idx); - DrawCube((Vector3){.x = 0.0f, .y = 0.0f, .z = 0.0f}, aimGuideWidth, 0.0f, aimGuideLength, droneColor); - - rlPopMatrix(); -} - -void renderDroneGuides(iwEnv *e, const droneEntity *drone, const bool ending) { - // render thruster move guide - if (!b2VecEqual(drone->lastMove, b2Vec2_zero) && !ending) { - const float moveMagnitude = b2Length(drone->lastMove); - const float thrusterAngle = RAD2DEG * b2Atan2(-drone->lastMove.y, -drone->lastMove.x); - const float flickerWidth = randFloat(&e->randState, -0.05f, 0.05f); - const float thrusterWidth = 2.5f * ((halfDroneRadius * moveMagnitude) + halfDroneRadius + flickerWidth); - const b2Vec2 thrusterPos = b2MulAdd(drone->pos, -thrusterWidth / 2.0f, drone->lastMove); - const Color thrusterColor = Fade(getDroneColor(drone->idx), 0.9); - - rlPushMatrix(); - rlTranslatef(thrusterPos.x, 0.0f, thrusterPos.y); - rlRotatef(-thrusterAngle, 0.0f, 1.0f, 0.0f); - - DrawCube((Vector3){.x = 0.0f, .y = 0.0f, .z = 0.0f}, thrusterWidth, 0.0f, droneThrusterLength, thrusterColor); - - rlPopMatrix(); - } - - renderDroneAimGuide(e, drone); -} - -void renderDroneTrail(const droneEntity *drone) { - if (drone->trailPoints.length < 2) { - return; - } - - const float trailWidth = DRONE_RADIUS; - const float numPoints = drone->trailPoints.length; - const Color droneColor = getDroneColor(drone->idx); - - for (uint8_t i = 0; i < drone->trailPoints.length - 1; i++) { - const Vector2 p0 = drone->trailPoints.points[i]; - const Vector2 p1 = drone->trailPoints.points[i + 1]; - - // compute direction and a perpendicular vector - Vector2 segment = Vector2Subtract(p1, p0); - if (Vector2Length(segment) == 0) { - continue; - } - segment = Vector2Normalize(segment); - const Vector2 perp = {-segment.y, segment.x}; - - // compute four vertices for the quad segment - const Vector2 v0 = Vector2Add(p0, Vector2Scale(perp, trailWidth)); - const Vector2 v1 = Vector2Subtract(p0, Vector2Scale(perp, trailWidth)); - const Vector2 v2 = Vector2Add(p1, Vector2Scale(perp, trailWidth)); - const Vector2 v3 = Vector2Subtract(p1, Vector2Scale(perp, trailWidth)); - - // draw the quad as two triangles - const float alpha0 = 0.7f * ((float)(i + 1) / numPoints); - const float alpha1 = 0.7f * ((float)(i + 2) / numPoints); - DrawTriangle3D( - (Vector3){.x = v0.x, .y = 0.0f, .z = v0.y}, - (Vector3){.x = v2.x, .y = 0.0f, .z = v2.y}, - (Vector3){.x = v1.x, .y = 0.0f, .z = v1.y}, - Fade(droneColor, alpha0) - ); - DrawTriangle3D( - (Vector3){.x = v1.x, .y = 0.0f, .z = v1.y}, - (Vector3){.x = v2.x, .y = 0.0f, .z = v2.y}, - (Vector3){.x = v3.x, .y = 0.0f, .z = v3.y}, - Fade(droneColor, alpha1) - ); - } -} - -void renderDroneLight(const droneEntity *drone) { - const Color droneColor = getDroneColor(drone->idx); - - DrawCylinder( - (Vector3){.x = drone->pos.x, .y = 0.0, .z = drone->pos.y}, - DRONE_RADIUS, - DRONE_RADIUS, - 0.0f, - 32, - droneColor - ); -} - -void renderDrone(const droneEntity *drone) { - renderDroneLight(drone); - - DrawSphere( - (Vector3){.x = drone->pos.x, .y = 0.0f, .z = drone->pos.y}, - DRONE_RADIUS - droneLightRadius, - BLACK - ); - - if (drone->shield != NULL) { - DrawSphereWires( - (Vector3){.x = drone->shield->pos.x, .y = 0.0f, .z = drone->shield->pos.y}, - DRONE_SHIELD_RADIUS, - 6, - 12, - Fade(getDroneColor(drone->idx), 0.5f) - ); - } -} - -void renderDroneAmmo(const iwEnv *e, const droneEntity *drone) { - Vector2 worldPos = {.x = drone->pos.x, .y = drone->pos.y}; - Vector2 screenPos = GetWorldToScreen2D(worldPos, e->client->camera->camera2D); - - // draw ammo count - const float fontSize = 1.5f * e->client->camera->camera2D.zoom; - const char *ammoStr = TextFormat("%d", drone->ammo); - const Vector2 textSize = MeasureTextEx(GetFontDefault(), ammoStr, fontSize, fontSize / 10.0f); - const Vector2 textOrigin = {.x = screenPos.x - (textSize.x / 2), .y = screenPos.y + (1.3f * textSize.y)}; - DrawTextEx(GetFontDefault(), ammoStr, textOrigin, fontSize, fontSize / 10.f, RAYWHITE); -} - -void renderDroneUI(const droneEntity *drone) { - // draw energy meter - const float energyMeterInnerRadius = 0.6f; - const float energyMeterOuterRadius = 0.3f; - const Vector2 energyMeterOrigin = {.x = drone->pos.x, .y = drone->pos.y}; - float energyMeterEndAngle = 360.f * drone->energyLeft; - Color energyMeterColor = RAYWHITE; - if (drone->shield != NULL) { - energyMeterColor = bambooBrown; - } else if (drone->energyFullyDepleted && drone->energyRefillWait != 0.0f) { - energyMeterColor = bambooBrown; - energyMeterEndAngle = 360.0f * (1.0f - (drone->energyRefillWait / (DRONE_ENERGY_REFILL_EMPTY_WAIT))); - } else if (drone->energyFullyDepleted) { - energyMeterColor = GRAY; - } - DrawRing(energyMeterOrigin, energyMeterInnerRadius, energyMeterOuterRadius, 0.0f, energyMeterEndAngle, 32, energyMeterColor); - - // draw burst charge indicator - if (drone->chargingBurst) { - const float alpha = min(drone->burstCharge + (50.0f / 255.0f), 1.0f); - const Color burstChargeColor = Fade(RAYWHITE, alpha); - const float burstChargeOuterRadius = (DRONE_BURST_RADIUS_BASE * drone->burstCharge) + DRONE_BURST_RADIUS_MIN; - const float burstChargeInnerRadius = burstChargeOuterRadius - 0.15f; - DrawRing(energyMeterOrigin, burstChargeInnerRadius, burstChargeOuterRadius, 0.0f, 360.0f, 50, burstChargeColor); - } - - const float maxCharge = drone->weaponInfo->charge; - if (maxCharge == 0) { - return; - } - - // draw charge meter - const Vector2 chargeMeterOrigin = {.x = drone->pos.x, .y = drone->pos.y + 3.3f}; - const float chargeMeterInnerRadius = 1.0f; - const float chargeMeterOuterRadius = 0.5f; - const float chargeMeterStartAngle = 157.5f; - const float chargeMeterEndAngle = chargeMeterStartAngle - (135.0f * (drone->weaponCharge / drone->weaponInfo->charge)); - - rlPushMatrix(); - rlTranslatef(chargeMeterOrigin.x, chargeMeterOrigin.y, 0.0f); - rlScalef(1.7f, 1.0f, 1.0f); - - DrawRing(Vector2Zero(), chargeMeterInnerRadius, chargeMeterOuterRadius, chargeMeterStartAngle, chargeMeterEndAngle, 32, RAYWHITE); - DrawRingLines(Vector2Zero(), chargeMeterInnerRadius, chargeMeterOuterRadius, chargeMeterStartAngle, chargeMeterStartAngle - 135.0f, 10, RAYWHITE); - - rlPopMatrix(); -} - -void renderProjectileTrail(const projectileEntity *proj) { - if (proj->trailPoints.length < 2) { - return; // need at least two points - } - - const float maxWidth = proj->weaponInfo->radius; - const float numPoints = proj->trailPoints.length; - - for (uint8_t i = 0; i < proj->trailPoints.length - 1; i++) { - const Vector2 p0 = proj->trailPoints.points[i]; - const Vector2 p1 = proj->trailPoints.points[i + 1]; - - // Compute a perpendicular vector for the segment - Vector2 dir = Vector2Subtract(p1, p0); - if (Vector2Length(dir) == 0) { - continue; // Avoid division by zero - } - dir = Vector2Normalize(dir); - const Vector2 perp = {-dir.y, dir.x}; - - // Compute widths for the start and end of the segment. - // Taper so that older segments are narrower. - const float taper0 = (float)(i + 1) / numPoints; - const float taper1 = (float)(i + 2) / numPoints; - const float width0 = maxWidth * taper0; - const float width1 = maxWidth * taper1; - - // Calculate two vertices on each side of the segment. - const Vector2 v0 = Vector2Add(p0, Vector2Scale(perp, width0)); - const Vector2 v1 = Vector2Subtract(p0, Vector2Scale(perp, width0)); - const Vector2 v2 = Vector2Add(p1, Vector2Scale(perp, width1)); - const Vector2 v3 = Vector2Subtract(p1, Vector2Scale(perp, width1)); - - // Draw two triangles for the quad and fade the color with distance so older parts are more transparent. - const Color color = getProjectileColor(proj->weaponInfo->type); - DrawTriangle3D( - (Vector3){.x = v0.x, .y = 0.5f, .z = v0.y}, - (Vector3){.x = v2.x, .y = 0.5f, .z = v2.y}, - (Vector3){.x = v1.x, .y = 0.5f, .z = v1.y}, - Fade(color, taper0) - ); - DrawTriangle3D( - (Vector3){.x = v1.x, .y = 0.5f, .z = v1.y}, - (Vector3){.x = v2.x, .y = 0.5f, .z = v2.y}, - (Vector3){.x = v3.x, .y = 0.5f, .z = v3.y}, - Fade(color, taper1) - ); - } -} - -void renderProjectile(const projectileEntity *projectile) { - DrawSphere( - (Vector3){.x = projectile->pos.x, .y = 0.5f, .z = projectile->pos.y}, - projectile->weaponInfo->radius, - getProjectileColor(projectile->weaponInfo->type) - ); -} - -void renderBannerText(iwEnv *e, const bool starting, const int8_t winner, const int8_t winningTeam) { - const uint16_t fontSize = 5 * e->client->scale; - - char *bannerStr = NULL; - Color winColor = PUFF_WHITE; - - if (starting) { - bannerStr = "Ready?"; - } else if (winner == -1 && winningTeam == -1) { - bannerStr = "Tie"; - } else if (e->teamsEnabled) { - bannerStr = (char *)TextFormat("Team %d wins!", winningTeam + 1); - } else { - bannerStr = (char *)TextFormat("Player %d wins!", winner + 1); - winColor = getDroneColor(winner); - } - - uint16_t textWidth = MeasureText(bannerStr, fontSize); - const uint16_t posX = (e->client->halfWidth - (textWidth / 2)); - DrawText(bannerStr, posX, e->client->halfHeight, fontSize, winColor); -} - -void applyBloom(const iwEnv *e, RenderTexture2D srcTex, RenderTexture2D dstTex, const float bloomIntensity) { - BeginTextureMode(e->client->blurSrcTexture); - ClearBackground(BLANK); - DrawTextureRec(srcTex.texture, (Rectangle){0.0f, 0.0f, e->client->width, -e->client->height}, Vector2Zero(), WHITE); - EndTextureMode(); - - // apply horizontal and vertical blurring - RenderTexture2D blurSrcTex = e->client->blurDstTexture; - RenderTexture2D blurDstTex = e->client->blurSrcTexture; - for (uint8_t i = 0, horizontal = true; i < 10; i++, horizontal = !horizontal) { - RenderTexture2D temp = blurSrcTex; - blurSrcTex = blurDstTex; - blurDstTex = temp; - - Vector2 blurDir; - if (horizontal) { - blurDir = (Vector2){1.0f / e->client->width, 0.0f}; - } else { - blurDir = (Vector2){0.0f, 1.0f / e->client->height}; - } - - BeginTextureMode(blurDstTex); - BeginShaderMode(e->client->blurShader); - SetShaderValue(e->client->blurShader, e->client->blurShaderDirLoc, &blurDir, SHADER_UNIFORM_VEC2); - DrawTextureRec(blurSrcTex.texture, (Rectangle){0.0f, 0.0f, e->client->width, -e->client->height}, Vector2Zero(), WHITE); - EndShaderMode(); - EndTextureMode(); - } - - // bloom - BeginTextureMode(dstTex); - BeginShaderMode(e->client->bloomShader); - - SetShaderValue(e->client->bloomShader, e->client->bloomIntensityLoc, &bloomIntensity, SHADER_UNIFORM_FLOAT); - SetShaderValueTexture(e->client->bloomShader, e->client->bloomTexColorLoc, srcTex.texture); - SetShaderValueTexture(e->client->bloomShader, e->client->bloomTexBloomBlurLoc, e->client->blurDstTexture.texture); - DrawTextureRec(e->client->blurDstTexture.texture, (Rectangle){0.0f, 0.0f, e->client->width, -e->client->height}, Vector2Zero(), WHITE); - - EndShaderMode(); - EndTextureMode(); -} - -void minimalStepEnv(iwEnv *e) { - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead || drone->shield == NULL) { - continue; - } - - // update shield velocity if its active - b2Body_SetLinearVelocity(drone->shield->bodyID, b2Body_GetLinearVelocity(drone->bodyID)); - }; - - b2World_Step(e->worldID, e->deltaTime, e->box2dSubSteps); - - handleBodyMoveEvents(e); - handleContactEvents(e); - handleSensorEvents(e); - - projectilesStep(e); - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - droneStep(e, drone); - } -} - -void _renderEnv(iwEnv *e, const bool starting, const bool ending, const int8_t winner, const int8_t winningTeam) { - if (ending) { - minimalStepEnv(e); - } - - // UpdateCamera(&e->client->camera3D, CAMERA_ORBITAL); - - updateCamera(e); - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - // TODO: is there a better way to do this? - float gridPos[2] = {-1000, -1000}; - SetShaderValue(e->client->gridShader, e->client->gridShaderPosLoc[drone->idx], gridPos, SHADER_UNIFORM_VEC2); - continue; - } - - float gridPos[2] = {drone->pos.x, drone->pos.y}; - SetShaderValue(e->client->gridShader, e->client->gridShaderPosLoc[drone->idx], gridPos, SHADER_UNIFORM_VEC2); - const Color droneColor = getDroneColor(drone->idx); - float gridColor[4] = {droneColor.r, droneColor.g, droneColor.b, droneColor.a}; - SetShaderValue(e->client->gridShader, e->client->gridShaderColorLoc[drone->idx], gridColor, SHADER_UNIFORM_VEC4); - } - - // apply bloom to parts of drones - BeginTextureMode(e->client->droneRawTex); - ClearBackground(BLACK); - BeginMode3D(e->client->camera->camera3D); - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - - // light up the laser aim guide if the drone's weapon is fully charged - if (drone->weaponInfo->charge != 0.0f && drone->weaponCharge == drone->weaponInfo->charge) { - renderDroneAimGuide(e, drone); - } - - renderDroneLight(drone); - } - - EndMode3D(); - EndTextureMode(); - - applyBloom(e, e->client->droneRawTex, e->client->droneBloomTex, 2.0f); - - // apply bloom to projectiles - BeginTextureMode(e->client->projRawTex); - ClearBackground(BLACK); - BeginMode3D(e->client->camera->camera3D); - - BeginBlendMode(BLEND_ALPHA); - for (size_t i = 0; i < cc_array_size(e->projectiles); i++) { - const projectileEntity *projectile = safe_array_get_at(e->projectiles, i); - renderProjectileTrail(projectile); - } - EndBlendMode(); - - for (size_t i = 0; i < cc_array_size(e->projectiles); i++) { - const projectileEntity *projectile = safe_array_get_at(e->projectiles, i); - renderProjectile(projectile); - } - - EndMode3D(); - EndTextureMode(); - - applyBloom(e, e->client->projRawTex, e->client->projBloomTex, 3.0f); - - BeginDrawing(); - ClearBackground(BLACK); - -#ifndef __EMSCRIPTEN__ - DrawFPS(e->client->scale, e->client->scale); -#endif - - BeginMode3D(e->client->camera->camera3D); - - // TODO: fix for maps with different rows and columns - // draw a thicker grid below - float y = (-3.0f * WALL_THICKNESS) - 1.0f; - Color color = (Color){.r = 94, .g = 59, .b = 136, .a = 128}; - for (int i = 0; i < e->map->columns; i++) { - const float d = WALL_THICKNESS * e->map->columns; - const Vector2 start = {.x = -d / 2.0f, .y = WALL_THICKNESS * i - d / 2.0f}; - const Vector2 end = {.x = d / 2.0f, .y = WALL_THICKNESS * i - d / 2.0f}; - const Vector3 pos = {.x = start.x, .y = y, .z = start.y}; - - rlPushMatrix(); - rlTranslatef(pos.x, pos.y, pos.z); - rlRotatef(-90.0f, 0.0f, 0.0f, 1.0f); - - DrawCylinder(Vector3Zero(), 0.1f, 0.1f, Vector2Distance(start, end), 1, color); - - rlPopMatrix(); - } - for (int i = 0; i < e->map->rows; i++) { - const float d = WALL_THICKNESS * e->map->rows; - const Vector2 start = {.x = WALL_THICKNESS * i - d / 2.0f, .y = -d / 2.0f}; - const Vector2 end = {.x = WALL_THICKNESS * i - d / 2.0f, .y = d / 2.0f}; - const Vector3 pos = {.x = start.x, .y = y, .z = start.y}; - - rlPushMatrix(); - rlTranslatef(pos.x, pos.y, pos.z); - rlRotatef(90.0f, 1.0f, 0.0f, 0.0f); - - DrawCylinder(Vector3Zero(), 0.1f, 0.1f, Vector2Distance(start, end), 1, color); - - rlPopMatrix(); - } - - // render smaller higher grid - BeginBlendMode(BLEND_ALPHA); - BeginShaderMode(e->client->gridShader); - y = -1.0f; - color = PUFF_BACKGROUND; - for (int i = 0; i < 2 * e->map->columns; i++) { - const float d = WALL_THICKNESS * e->map->columns; - const Vector3 start = {.x = -d / 2.0f, .y = y, .z = WALL_THICKNESS / 2.0f * i - d / 2.0f}; - const Vector3 end = {.x = (d - WALL_THICKNESS / 2.0f) / 2.0f, .y = y, .z = WALL_THICKNESS / 2.0f * i - d / 2.0f}; - - DrawLine3D(start, end, color); - } - for (int i = 0; i < 2 * e->map->rows; i++) { - float d = WALL_THICKNESS * e->map->rows; - const Vector3 start = {.x = WALL_THICKNESS * i / 2.0f - d / 2.0f, .y = y, .z = -d / 2.0f}; - const Vector3 end = {.x = WALL_THICKNESS * i / 2.0f - d / 2.0f, .y = y, .z = (d - WALL_THICKNESS / 2.0f) / 2.0f}; - - DrawLine3D(start, end, color); - } - EndShaderMode(); - EndBlendMode(); - - for (size_t i = 0; i < cc_array_size(e->pickups); i++) { - const weaponPickupEntity *pickup = safe_array_get_at(e->pickups, i); - renderWeaponPickup(e, pickup); - } - - BeginBlendMode(BLEND_ALPHA); - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - renderBrakeTrails(e, drone); - } - EndBlendMode(); - renderDronePieces(e); - - EndMode3D(); - - BeginBlendMode(BLEND_ADDITIVE); - DrawTextureRec(e->client->droneBloomTex.texture, (Rectangle){0.0f, 0.0f, e->client->width, -e->client->height}, Vector2Zero(), WHITE); - EndBlendMode(); - - BeginMode3D(e->client->camera->camera3D); - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - renderDroneTrail(drone); - } - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - renderDroneGuides(e, drone, ending); - } - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - const droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - renderDrone(drone); - } - - for (size_t i = 0; i < cc_array_size(e->walls); i++) { - const wallEntity *wall = safe_array_get_at(e->walls, i); - renderWall(e, wall); - } - - for (size_t i = 0; i < cc_array_size(e->floatingWalls); i++) { - const wallEntity *wall = safe_array_get_at(e->floatingWalls, i); - renderWall(e, wall); - } - - renderExplosions(e); - EndMode3D(); - - BeginBlendMode(BLEND_ADDITIVE); - DrawTextureRec(e->client->projRawTex.texture, (Rectangle){0.0f, 0.0f, e->client->width, -e->client->height}, Vector2Zero(), WHITE); - DrawTextureRec(e->client->projBloomTex.texture, (Rectangle){0.0f, 0.0f, e->client->width, -e->client->height}, Vector2Zero(), WHITE); - EndBlendMode(); - - BeginMode2D(e->client->camera->camera2D); - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - renderDroneRespawnGuides(e, drone); - renderDroneUI(drone); - } - - if (!b2VecEqual(e->debugPoint, b2Vec2_zero)) { - const Vector2 pos = {.x = e->debugPoint.x, .y = e->debugPoint.y}; - DrawCircleV(pos, DRONE_RADIUS * 0.5f, WHITE); - } - - EndMode2D(); - - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - droneEntity *drone = safe_array_get_at(e->drones, i); - if (drone->dead) { - continue; - } - renderDroneAmmo(e, drone); - } - - renderUI(e, starting); - - if (starting || ending) { - renderBannerText(e, starting, winner, winningTeam); - } - - EndDrawing(); -} - -void renderWait(iwEnv *e, const bool starting, const bool ending, const int8_t winner, const int8_t winningTeam, const float time) { -#ifdef __EMSCRIPTEN__ - const double startTime = emscripten_get_now(); - while (time > (emscripten_get_now() - startTime) / 1000.0) { - _renderEnv(e, starting, ending, winner, winningTeam); - emscripten_sleep(e->deltaTime * 1000.0); - } -#else - for (uint16_t i = 0; i < (uint16_t)(time * e->frameRate); i++) { - _renderEnv(e, starting, ending, winner, winningTeam); - } -#endif -} - -void renderEnv(iwEnv *e, const bool starting, const bool ending, const int8_t winner, const int8_t winningTeam) { - if (starting) { - renderWait(e, starting, ending, winner, winningTeam, START_READY_TIME); - } else if (ending) { - renderWait(e, starting, ending, winner, winningTeam, END_WAIT_TIME); - } else { - _renderEnv(e, starting, ending, winner, winningTeam); - } -} - -#endif diff --git a/pufferlib/ocean/impulse_wars/scripted_agent.h b/pufferlib/ocean/impulse_wars/scripted_agent.h deleted file mode 100644 index eaa4c096f..000000000 --- a/pufferlib/ocean/impulse_wars/scripted_agent.h +++ /dev/null @@ -1,436 +0,0 @@ -#ifndef IMPULSE_WARS_SCRIPTED_BOT_H -#define IMPULSE_WARS_SCRIPTED_BOT_H - -#include "game.h" -#include "types.h" - -const uint8_t NUM_NEAR_WALLS = 3; -const uint8_t NUM_NEAR_PICKUPS = 1; - -const float WALL_CHECK_DISTANCE_SQUARED = SQUARED(6.0f); -const float WALL_AVOID_DISTANCE = 4.0f; -const float WALL_DANGER_DISTANCE = 3.0f; -const float WALL_BRAKE_DISTANCE = 20.0f; -const float WALL_BRAKE_SPEED = 12.5f; -const float WALL_BRAKE_TIME = 0.5f; -const float BURST_MIN_RADIUS_SQUARED = SQUARED(DRONE_BURST_RADIUS_MIN); -const float MOVE_SPEED_SQUARED = SQUARED(5.0f); - -typedef struct castCircleCtx { - bool hit; - b2ShapeId shapeID; -} castCircleCtx; - -float castCircleCallback(b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void *context) { - // these parameters are required by the callback signature - MAYBE_UNUSED(point); - MAYBE_UNUSED(normal); - if (!b2Shape_IsValid(shapeId) || (fraction == 0.0f && b2VecEqual(normal, b2Vec2_zero))) { - // skip this shape if it isn't valid or this is an initial overlap - return -1.0f; - } - - castCircleCtx *ctx = context; - ctx->hit = true; - ctx->shapeID = shapeId; - - return 0.0f; -} - -static inline uint32_t pathOffset(const iwEnv *e, uint16_t srcCellIdx, uint16_t destCellIdx) { - const uint8_t srcCol = srcCellIdx % e->map->columns; - const uint8_t srcRow = srcCellIdx / e->map->columns; - const uint8_t destCol = destCellIdx % e->map->columns; - const uint8_t destRow = destCellIdx / e->map->columns; - return (destRow * e->map->rows * e->map->columns * e->map->rows) + (destCol * e->map->rows * e->map->columns) + (srcRow * e->map->columns) + srcCol; -} - -void pathfindBFS(const iwEnv *e, uint8_t *flatPaths, uint16_t destCellIdx) { - uint8_t (*paths)[e->map->columns] = (uint8_t (*)[e->map->columns])flatPaths; - int8_t (*buffer)[3] = (int8_t (*)[3])e->mapPathing[e->mapIdx].pathBuffer; - - uint16_t start = 0; - uint16_t end = 1; - - const mapCell *cell = safe_array_get_at(e->cells, destCellIdx); - if (cell->ent != NULL && entityTypeIsWall(cell->ent->type)) { - return; - } - const int8_t destCol = destCellIdx % e->map->columns; - const int8_t destRow = destCellIdx / e->map->columns; - - buffer[start][0] = 8; - buffer[start][1] = destCol; - buffer[start][2] = destRow; - while (start < end) { - const int8_t direction = buffer[start][0]; - const int8_t startCol = buffer[start][1]; - const int8_t startRow = buffer[start][2]; - start++; - - if (startCol < 0 || startCol >= e->map->columns || startRow < 0 || startRow >= e->map->rows || paths[startRow][startCol] != UINT8_MAX) { - continue; - } - int16_t cellIdx = cellIndex(e, startCol, startRow); - const mapCell *cell = safe_array_get_at(e->cells, cellIdx); - if (cell->ent != NULL && entityTypeIsWall(cell->ent->type)) { - paths[startRow][startCol] = 8; - continue; - } - - paths[startRow][startCol] = direction; - - buffer[end][0] = 6; // up - buffer[end][1] = startCol; - buffer[end][2] = startRow + 1; - end++; - - buffer[end][0] = 2; // down - buffer[end][1] = startCol; - buffer[end][2] = startRow - 1; - end++; - - buffer[end][0] = 0; // right - buffer[end][1] = startCol - 1; - buffer[end][2] = startRow; - end++; - - buffer[end][0] = 4; // left - buffer[end][1] = startCol + 1; - buffer[end][2] = startRow; - end++; - - buffer[end][0] = 5; // up left - buffer[end][1] = startCol + 1; - buffer[end][2] = startRow + 1; - end++; - - buffer[end][0] = 3; // down left - buffer[end][1] = startCol + 1; - buffer[end][2] = startRow - 1; - end++; - - buffer[end][0] = 1; // down right - buffer[end][1] = startCol - 1; - buffer[end][2] = startRow - 1; - end++; - - buffer[end][0] = 7; // up right - buffer[end][1] = startCol - 1; - buffer[end][2] = startRow + 1; - end++; - } -} - -float distanceWithDamping(const iwEnv *e, const droneEntity *drone, const b2Vec2 direction, const float linearDamping, const float steps) { - float speed = drone->weaponInfo->recoilMagnitude * DRONE_INV_MASS; - if (!b2VecEqual(drone->velocity, b2Vec2_zero)) { - speed = b2Length(b2MulAdd(drone->velocity, speed, direction)); - } - - const float damping = 1.0f + linearDamping * e->deltaTime; - return speed * (damping / linearDamping) * (1.0f - powf(1.0f / damping, steps)); -} - -bool safeToFire(iwEnv *e, const droneEntity *drone, const b2Vec2 direction) { - float shotWait; - if (drone->ammo > 1) { - shotWait = ((drone->weaponInfo->coolDown + drone->weaponInfo->charge) / e->deltaTime) * 1.5f; - } else { - shotWait = ((e->defaultWeapon->coolDown + e->defaultWeapon->charge) / e->deltaTime) * 1.5f; - } - const b2Vec2 invDirection = b2MulSV(-1.0f, direction); - const float recoilDistance = distanceWithDamping(e, drone, invDirection, DRONE_LINEAR_DAMPING, shotWait); - - // e->debugPoint = b2MulAdd(drone->pos, recoilDistance, invDirection); - - const b2Vec2 pos = drone->pos; - const b2Vec2 rayEnd = b2MulAdd(pos, recoilDistance, invDirection); - const b2Vec2 translation = b2Sub(rayEnd, pos); - const b2ShapeProxy cirProxy = b2MakeProxy(&pos, 1, DRONE_RADIUS); - const b2QueryFilter filter = {.categoryBits = DRONE_SHAPE, .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | DRONE_SHAPE}; - - castCircleCtx ctx = {0}; - b2World_CastShape(e->worldID, &cirProxy, translation, filter, castCircleCallback, &ctx); - if (!ctx.hit) { - return true; - } else { - const entity *ent = b2Shape_GetUserData(ctx.shapeID); - if (ent->type == STANDARD_WALL_ENTITY || ent->type == BOUNCY_WALL_ENTITY || ent->type == DRONE_ENTITY) { - return true; - } - } - - return false; -} - -bool weaponSafeForMovement(const droneEntity *drone) { - switch (drone->weaponInfo->type) { - case IMPLODER_WEAPON: - case MINE_LAUNCHER_WEAPON: - return false; - default: - return true; - } -} - -void scriptedAgentShoot(const droneEntity *drone, agentActions *actions) { - actions->shoot = true; - if (drone->chargingWeapon && drone->weaponCharge == drone->weaponInfo->charge) { - actions->chargingWeapon = false; - } -} - -void moveTo(iwEnv *e, const droneEntity *drone, agentActions *actions, const b2Vec2 dstPos) { - ASSERT(drone->mapCellIdx != -1); - int16_t dstIdx = entityPosToCellIdx(e, dstPos); - if (dstIdx == -1) { - return; - } - - uint32_t pathIdx = pathOffset(e, drone->mapCellIdx, dstIdx); - uint8_t *paths = e->mapPathing[e->mapIdx].paths; - uint8_t direction = paths[pathIdx]; - if (direction == UINT8_MAX) { - uint32_t bfsIdx = pathOffset(e, 0, dstIdx); - pathfindBFS(e, &paths[bfsIdx], dstIdx); - direction = paths[pathIdx]; - } - if (direction >= 8) { - return; - } - actions->move.x += discMoveToContMoveMap[0][direction]; - actions->move.y += discMoveToContMoveMap[1][direction]; - actions->move = b2Normalize(actions->move); - - const b2Vec2 invDirection = b2MulSV(-1.0f, actions->move); - if (!weaponSafeForMovement(drone) || !safeToFire(e, drone, invDirection)) { - return; - } - actions->aim = invDirection; - scriptedAgentShoot(drone, actions); -} - -float weaponIdealRangeSquared(const droneEntity *drone) { - switch (drone->weaponInfo->type) { - case STANDARD_WEAPON: - return SQUARED(20.0f); - case MACHINEGUN_WEAPON: - return SQUARED(30.0f); - case SHOTGUN_WEAPON: - return SQUARED(20.0f); - case IMPLODER_WEAPON: - return SQUARED(30.0f); - case FLAK_CANNON_WEAPON: - return SQUARED(FLAK_CANNON_SAFE_DISTANCE + 5.0f); - case SNIPER_WEAPON: - case ACCELERATOR_WEAPON: - case MINE_LAUNCHER_WEAPON: - case BLACK_HOLE_WEAPON: - case NUKE_WEAPON: - return FLT_MAX; - - default: - ERRORF("unknown weapon type %d", drone->weaponInfo->type); - } -} - -bool shouldShootAtEnemy(iwEnv *e, const droneEntity *drone, const droneEntity *enemyDrone, const b2Vec2 enemyDroneDirection) { - if (!safeToFire(e, drone, enemyDroneDirection)) { - return false; - } - - // cast a circle that's the size of a projectile of the current weapon - const float enemyDroneDistance = b2Distance(enemyDrone->pos, drone->pos); - const b2Vec2 castEnd = b2MulAdd(drone->pos, enemyDroneDistance, enemyDroneDirection); - const b2Vec2 translation = b2Sub(castEnd, drone->pos); - const b2ShapeProxy cirProxy = b2MakeProxy(&drone->pos, 1, drone->weaponInfo->radius); - const b2QueryFilter filter = {.categoryBits = PROJECTILE_SHAPE, .maskBits = WALL_SHAPE | FLOATING_WALL_SHAPE | DRONE_SHAPE}; - - castCircleCtx ctx = {0}; - b2World_CastShape(e->worldID, &cirProxy, translation, filter, castCircleCallback, &ctx); - if (!ctx.hit) { - return false; - } - ASSERT(b2Shape_IsValid(ctx.shapeID)); - const entity *ent = b2Shape_GetUserData(ctx.shapeID); - if (ent == NULL || ent->type != DRONE_ENTITY) { - return false; - } - - return true; -} - -b2Vec2 predictiveAim(const droneEntity *drone, const droneEntity *enemyDrone, const float distanceSquared) { - const float timeToImpact = sqrtf(distanceSquared) / drone->weaponInfo->initialSpeed; - const b2Vec2 predictedPos = b2MulAdd(enemyDrone->pos, timeToImpact, enemyDrone->velocity); - return b2Normalize(b2Sub(predictedPos, drone->pos)); -} - -void handleWallProximity(iwEnv *e, const droneEntity *drone, const wallEntity *wall, const float distance, agentActions *actions) { - if (distance > WALL_BRAKE_DISTANCE) { - return; - } - - const b2Vec2 wallDirection = b2Normalize(b2Sub(wall->pos, drone->pos)); - const float speedToWall = b2Dot(drone->velocity, wallDirection); - if (speedToWall > WALL_BRAKE_SPEED) { - float damping = DRONE_LINEAR_DAMPING; - if (drone->braking) { - damping *= DRONE_BRAKE_DAMPING_COEF; - } - const float travelDistance = distanceWithDamping(e, drone, wallDirection, damping, WALL_BRAKE_TIME / e->deltaTime); - if (travelDistance >= distance) { - actions->brake = true; - } - } - if (actions->brake || distance <= WALL_AVOID_DISTANCE) { - const b2Vec2 invWallDirection = b2MulSV(-1.0f, wallDirection); - actions->move = b2MulAdd(actions->move, distance, invWallDirection); - } - - if (distance > WALL_DANGER_DISTANCE) { - return; - } - - // shoot to move away faster from a death wall if we're too close and it's safe - if (weaponSafeForMovement(drone) && safeToFire(e, drone, wallDirection)) { - actions->aim = wallDirection; - scriptedAgentShoot(drone, actions); - } -} - -void scriptedAgentBurst(const droneEntity *drone, agentActions *actions) { - if (drone->chargingBurst) { - return; - } else { - actions->chargingBurst = true; - } -} - -agentActions scriptedAgentActions(iwEnv *e, droneEntity *drone) { - agentActions actions = {0}; - if (e->sittingDuck) { - return actions; - } - - // keep the weapon charged and ready if it needs it - if (drone->weaponInfo->charge != 0.0f) { - actions.chargingWeapon = true; - actions.shoot = true; - } - - // find the nearest death wall or floating wall - nearEntity nearWalls[MAX_NEAREST_WALLS] = {0}; - findNearWalls(e, drone, nearWalls, NUM_NEAR_WALLS); - - // find the distance between the closest points on the drone and the nearest wall - for (uint8_t i = 0; i < NUM_NEAR_WALLS; i++) { - const wallEntity *wall = nearWalls[i].entity; - if (wall->type != DEATH_WALL_ENTITY) { - continue; - } - - const b2DistanceOutput output = closestPoint(drone->ent, wall->ent); - handleWallProximity(e, drone, wall, output.distance, &actions); - } - - for (uint8_t i = 0; i < cc_array_size(e->floatingWalls); i++) { - wallEntity *floatingWall = safe_array_get_at(e->floatingWalls, i); - if (floatingWall->type != DEATH_WALL_ENTITY) { - continue; - } - if (b2DistanceSquared(floatingWall->pos, drone->pos) > WALL_CHECK_DISTANCE_SQUARED) { - continue; - } - - const b2DistanceOutput output = closestPoint(drone->ent, floatingWall->ent); - handleWallProximity(e, drone, floatingWall, output.distance, &actions); - } - - // get a weapon if the standard weapon is active - if (drone->weaponInfo->type == STANDARD_WEAPON && cc_array_size(e->pickups) != 0) { - nearEntity nearPickups[MAX_WEAPON_PICKUPS] = {0}; - uint8_t numActivePickups = 0; - for (uint8_t i = 0; i < cc_array_size(e->pickups); i++) { - weaponPickupEntity *pickup = safe_array_get_at(e->pickups, i); - if (pickup->floatingWallsTouching > 0) { - continue; - } - const nearEntity nearEnt = { - .entity = pickup, - .distanceSquared = b2DistanceSquared(pickup->pos, drone->pos), - }; - nearPickups[numActivePickups++] = nearEnt; - } - if (numActivePickups > 0) { - insertionSort(nearPickups, numActivePickups); - const weaponPickupEntity *pickup = nearPickups[0].entity; - moveTo(e, drone, &actions, pickup->pos); - return actions; - } - } - - // find closest enemy drone - droneEntity *enemyDrone = NULL; - float closestDistanceSquared = FLT_MAX; - for (uint8_t i = 0; i < cc_array_size(e->drones); i++) { - if (i == drone->idx) { - continue; - } - droneEntity *otherDrone = safe_array_get_at(e->drones, i); - if (otherDrone->dead || otherDrone->team == drone->team) { - continue; - } - const float distanceSquared = b2DistanceSquared(otherDrone->pos, drone->pos); - if (distanceSquared < closestDistanceSquared) { - closestDistanceSquared = distanceSquared; - enemyDrone = otherDrone; - } - } - if (enemyDrone == NULL) { - return actions; - } - - // if we're close enough to a wall to need to shoot at it, don't - // worry about enemies - if (!b2VecEqual(actions.aim, b2Vec2_zero)) { - return actions; - } - - // burst if the enemy drone is very close - if (closestDistanceSquared <= BURST_MIN_RADIUS_SQUARED) { - scriptedAgentBurst(drone, &actions); - } - // move into ideal range for the current weapon - if (closestDistanceSquared > weaponIdealRangeSquared(drone)) { - moveTo(e, drone, &actions, enemyDrone->pos); - return actions; - } - - // shoot at enemy drone if it's in line of sight and safe, otherwise move towards it - const b2Vec2 enemyDroneDirection = b2Normalize(b2Sub(enemyDrone->pos, drone->pos)); - if (shouldShootAtEnemy(e, drone, enemyDrone, enemyDroneDirection)) { - if (drone->weaponCooldown == 0.0f && drone->weaponCharge >= drone->weaponInfo->charge - e->deltaTime) { - actions.move.x += enemyDroneDirection.x; - actions.move.y += enemyDroneDirection.y; - actions.move = b2Normalize(actions.move); - } - actions.aim = predictiveAim(drone, enemyDrone, closestDistanceSquared); - scriptedAgentShoot(drone, &actions); - } else { - moveTo(e, drone, &actions, enemyDrone->pos); - } - - // fight recoil if we're not otherwise moving - if (b2VecEqual(actions.move, b2Vec2_zero)) { - const float speedSquared = b2LengthSquared(drone->velocity); - if (speedSquared > MOVE_SPEED_SQUARED) { - actions.move = b2MulSV(-1.0f, b2Normalize(drone->velocity)); - } - } - - return actions; -} - -#endif diff --git a/pufferlib/ocean/impulse_wars/settings.h b/pufferlib/ocean/impulse_wars/settings.h deleted file mode 100644 index 3a20f4652..000000000 --- a/pufferlib/ocean/impulse_wars/settings.h +++ /dev/null @@ -1,805 +0,0 @@ -#ifndef IMPULSE_WARS_SETTINGS_H -#define IMPULSE_WARS_SETTINGS_H - -#include "helpers.h" -#include "types.h" - -#define INFINITE -1 -const uint8_t TWO_BIT_MASK = 0x3; - -// general settings -const uint8_t TRAINING_ACTIONS_PER_SECOND = 10; -const uint8_t TRAINING_FRAME_RATE = 30; -const uint8_t TRAINING_BOX2D_SUBSTEPS = 2; - -const uint8_t EVAL_FRAME_RATE = 120; -const uint8_t EVAL_BOX2D_SUBSTEPS = 4; - -const uint8_t NUM_MAPS = 9; -#define _MAX_MAP_COLUMNS 25 -#define _MAX_MAP_ROWS 25 -#define MAX_CELLS _MAX_MAP_COLUMNS *_MAX_MAP_ROWS + 1 -#define MAX_FLOATING_WALLS 18 -#define MAX_WEAPON_PICKUPS 12 - -#define MAX_NEAREST_WALLS 8 - -const uint8_t DRONE_LIVES = 1; -const float DRONE_RESPAWN_WAIT = 2.0f; -const uint8_t ROUND_STEPS = 90; -const uint8_t SUDDEN_DEATH_STEPS = 5; - -const uint8_t MAX_DRONES = _MAX_DRONES; - -const uint16_t LOG_BUFFER_SIZE = 1024; - -// reward settings -const float WIN_REWARD = 1.5f; -const float SELF_KILL_PUNISHMENT = -2.0f; -const float ENEMY_DEATH_REWARD = 1.0f; -const float ENEMY_KILL_REWARD = 1.0f; -const float TEAMMATE_DEATH_PUNISHMENT = -0.5f; -const float TEAMMATE_KILL_PUNISHMENT = -1.0f; -const float DEATH_PUNISHMENT = 0.0f; -const float ENERGY_EMPTY_PUNISHMENT = -0.75f; -const float WEAPON_PICKUP_REWARD = 0.5f; -const float SHOT_HIT_REWARD_COEF = 0.000013333f; -const float EXPLOSION_HIT_REWARD_COEF = 5.0f; -const float APPROACH_REWARD = 0.0f; - -// approach reward doesn't apply within the cutoff to avoid constant clashing -const uint8_t DISTANCE_CUTOFF = 15.0f; - -// observation constants - -// map layout observations -const uint8_t MAP_OBS_ROWS = 11; -const uint8_t MAP_OBS_COLUMNS = 11; -const uint16_t MAP_OBS_SIZE = MAP_OBS_ROWS * MAP_OBS_COLUMNS; - -// discrete observations -#define _NUM_NEAR_WALL_OBS 4 -const uint8_t NUM_NEAR_WALL_OBS = _NUM_NEAR_WALL_OBS; -const uint16_t NEAR_WALL_TYPES_OBS_OFFSET = MAP_OBS_SIZE; - -#define _NUM_FLOATING_WALL_OBS 4 -const uint8_t NUM_FLOATING_WALL_OBS = _NUM_FLOATING_WALL_OBS; -const uint16_t FLOATING_WALL_TYPES_OBS_OFFSET = NEAR_WALL_TYPES_OBS_OFFSET + NUM_NEAR_WALL_OBS; - -const uint8_t NUM_PROJECTILE_OBS = 30; -const uint16_t PROJECTILE_DRONE_OBS_OFFSET = FLOATING_WALL_TYPES_OBS_OFFSET + NUM_FLOATING_WALL_OBS; -const uint16_t PROJECTILE_WEAPONS_OBS_OFFSET = PROJECTILE_DRONE_OBS_OFFSET + NUM_PROJECTILE_OBS; - -#define _NUM_WEAPON_PICKUP_OBS 3 -const uint8_t NUM_WEAPON_PICKUP_OBS = _NUM_WEAPON_PICKUP_OBS; -const uint16_t WEAPON_PICKUP_WEAPONS_OBS_OFFSET = PROJECTILE_WEAPONS_OBS_OFFSET + NUM_PROJECTILE_OBS; - -const uint16_t ENEMY_DRONE_WEAPONS_OBS_OFFSET = WEAPON_PICKUP_WEAPONS_OBS_OFFSET + NUM_WEAPON_PICKUP_OBS; - -// continuous observations -const uint8_t NEAR_WALL_POS_OBS_SIZE = 2; -const uint8_t NEAR_WALL_OBS_SIZE = NUM_NEAR_WALL_OBS * NEAR_WALL_POS_OBS_SIZE; -const uint16_t NEAR_WALL_POS_OBS_OFFSET = 0; - -const uint8_t FLOATING_WALL_INFO_OBS_SIZE = 5; -const uint8_t FLOATING_WALL_OBS_SIZE = NUM_FLOATING_WALL_OBS * FLOATING_WALL_INFO_OBS_SIZE; -const uint16_t FLOATING_WALL_INFO_OBS_OFFSET = NEAR_WALL_POS_OBS_OFFSET + NEAR_WALL_OBS_SIZE; - -const uint8_t WEAPON_PICKUP_POS_OBS_SIZE = 2; -const uint8_t WEAPON_PICKUP_OBS_SIZE = NUM_WEAPON_PICKUP_OBS * WEAPON_PICKUP_POS_OBS_SIZE; -const uint16_t WEAPON_PICKUP_POS_OBS_OFFSET = FLOATING_WALL_INFO_OBS_OFFSET + FLOATING_WALL_OBS_SIZE; - -const uint8_t PROJECTILE_INFO_OBS_SIZE = 4; -const uint8_t PROJECTILE_OBS_SIZE = NUM_PROJECTILE_OBS * PROJECTILE_INFO_OBS_SIZE; -const uint16_t PROJECTILE_INFO_OBS_OFFSET = WEAPON_PICKUP_POS_OBS_OFFSET + WEAPON_PICKUP_OBS_SIZE; - -const uint16_t ENEMY_DRONE_OBS_OFFSET = PROJECTILE_INFO_OBS_OFFSET + PROJECTILE_OBS_SIZE; -const uint8_t ENEMY_DRONE_OBS_SIZE = 24; - -const uint8_t DRONE_OBS_SIZE = 22; - -const uint8_t MISC_OBS_SIZE = 1; - -const uint16_t _DISCRETE_OBS_SIZE = MAP_OBS_SIZE + NUM_NEAR_WALL_OBS + NUM_FLOATING_WALL_OBS + (NUM_PROJECTILE_OBS * 2) + NUM_WEAPON_PICKUP_OBS + 1; -const uint16_t _CONTINUOUS_OBS_SIZE = NEAR_WALL_OBS_SIZE + FLOATING_WALL_OBS_SIZE + WEAPON_PICKUP_OBS_SIZE + PROJECTILE_OBS_SIZE + DRONE_OBS_SIZE + MISC_OBS_SIZE; - -uint16_t discreteObsSize(uint8_t numDrones) { - return _DISCRETE_OBS_SIZE + ((numDrones - 1)); -} - -uint16_t continuousObsSize(uint8_t numDrones) { - return _CONTINUOUS_OBS_SIZE + ((numDrones - 1) * ENEMY_DRONE_OBS_SIZE); -} - -uint16_t obsBytes(uint8_t numDrones) { - return alignedSize((discreteObsSize(numDrones) * sizeof(uint8_t)) + (continuousObsSize(numDrones) * sizeof(float)), sizeof(float)); -} - -const float MAX_X_POS = 150.0f; -const float MAX_Y_POS = 150.0f; -const float MAX_DISTANCE = 200.0f; -const float MAX_SPEED = 500.0f; -const float MAX_ACCEL = 1000.0f; -const float MAX_ANGLE = PI; - -// action constants -const uint8_t CONTINUOUS_ACTION_SIZE = 7; -const uint8_t DISCRETE_ACTION_SIZE = 5; -const float ACTION_NOOP_MAGNITUDE = 0.1f; - -const float discMoveToContMoveMap[2][8] = { - {1.0f, 0.707107f, 0.0f, -0.707107f, -1.0f, -0.707107f, 0.0f, 0.707107f}, - {0.0f, 0.707107f, 1.0f, 0.707107f, 0.0f, -0.707107f, -1.0f, -0.707107f}, -}; -const float discAimToContAimMap[2][16] = { - {1.0f, 0.92388f, 0.707107f, 0.382683f, 0.0f, -0.382683f, -0.707107f, -0.92388f, -1.0f, -0.92388f, -0.707107f, -0.382683f, 0.0f, 0.382683f, 0.707107f, 0.92388f}, - {0.0f, 0.382683f, 0.707107f, 0.92388f, 1.0f, 0.92388f, 0.707107f, 0.382683f, 0.0f, -0.382683f, -0.707107f, -0.92388f, -1.0f, -0.92388f, -0.707107f, -0.382683f}, -}; - -const float MIN_SPAWN_DISTANCE = 6.0f; -const float MIN_SD_SPAWN_DISTANCE = 3.0f; - -// wall settings -const float WALL_THICKNESS = 4.0f; -const float FLOATING_WALL_THICKNESS = 3.0f; -const float FLOATING_WALL_DAMPING = 0.75f; -const float STANDARD_WALL_RESTITUTION = 0.01f; -const float STANDARD_WALL_FRICTION = 0.3f; -const float BOUNCY_WALL_RESTITUTION = 1.0f; -const float WALL_DENSITY = 4.0f; - -// weapon pickup settings -const float PICKUP_THICKNESS = 3.0f; -const float PICKUP_SPAWN_DISTANCE_SQUARED = SQUARED(10.0f); -const float PICKUP_RESPAWN_WAIT = 3.0f; -const float SUDDEN_DEATH_PICKUP_RESPAWN_WAIT = 2.0f; - -// drone settings -const float DRONE_WALL_SPAWN_DISTANCE = 2.0f; -const float DRONE_DEATH_WALL_SPAWN_DISTANCE = 7.5f; -const float DRONE_DRONE_SPAWN_DISTANCE_SQUARED = SQUARED(10.0f); - -#define DRONE_RADIUS 1.0f -#define DRONE_DENSITY 1.25f -const float DRONE_RESTITUTION = 0.3f; -const float DRONE_FRICTION = 0.1f; -#define DRONE_INV_MASS INV_MASS(MASS(DRONE_DENSITY, DRONE_RADIUS)) -const float DRONE_MOVE_MAGNITUDE = 35.0f; -const float DRONE_LINEAR_DAMPING = 1.0f; -const float DRONE_MOVE_AIM_COEF = 0.1f; - -const float DRONE_ENERGY_MAX = 1.0f; -const float DRONE_BRAKE_DAMPING_COEF = 2.5f; -const float DRONE_BRAKE_DRAIN_RATE = 0.5f; -const float DRONE_ENERGY_REFILL_WAIT = 1.0f; -const float DRONE_ENERGY_REFILL_EMPTY_WAIT = 3.0f; -const float DRONE_ENERGY_REFILL_RATE = 0.03f; -const float DRONE_ENERGY_RESPAWN_REFILL = 0.5f; - -const float DRONE_BURST_BASE_COST = 0.1f; -const float DRONE_BURST_CHARGE_RATE = 0.6f; -const float DRONE_BURST_RADIUS_BASE = 5.0f; -const float DRONE_BURST_RADIUS_MIN = 3.5f; -const float DRONE_BURST_IMPACT_BASE = 140.0f; -const float DRONE_BURST_IMPACT_MIN = 35.0f; -const float DRONE_BURST_COOLDOWN = 0.5f; - -const float DRONE_SHIELD_RADIUS = DRONE_RADIUS * 1.5f; -const float DRONE_SHIELD_HEALTH = 100.0f; -const float DRONE_SHIELD_START_DURATION = 1.5f; -const float DRONE_SHIELD_RESPAWN_DURATION = 3.0f; -const float DRONE_SHIELD_EXPLOSION_REDUCTION = 0.5f; -const float DRONE_SHIELD_HEALTH_IMPULSE_COEF = 0.5f; -const float DRONE_SHIELD_HEALTH_EXPLOSION_COEF = 0.8; -const float DRONE_SHIELD_BREAK_ENERGY_COST = -0.33f; -const float DRONE_SHIELD_BREAK_ENERGY_REFILL = 0.25f; - -#define PROJECTILE_ENERGY_REFILL_COEF 0.001f -const float EXPLOSION_ENERGY_REFILL_COEF = 1.75f; -const float WEAPON_DISCARD_COST = 0.2f; - -const uint8_t DRONE_PIECE_COUNT = 5; -const float DRONE_PIECE_MIN_DISTANCE = 1.5f; -const float DRONE_PIECE_MAX_DISTANCE = 3.0f; -const float DRONE_PIECE_LINEAR_DAMPING = 1.0f; -const float DRONE_PIECE_ANGULAR_DAMPING = 1.0f; -const float DRONE_PIECE_MIN_SPEED = 5.0f; -const float DRONE_PIECE_MAX_SPEED = 10.0f; - -// TODO: increase impulse of imploder of entity it directly hits? -// weapon projectile settings -#define STANDARD_AMMO INFINITE -#define STANDARD_PROJECTILES 1 -#define STANDARD_RECOIL_MAGNITUDE 20.0f -#define STANDARD_FIRE_MAGNITUDE 17.0f -#define STANDARD_DAMPING 0.0f -#define STANDARD_CHARGE 0.0f -#define STANDARD_COOL_DOWN 0.37f -#define STANDARD_MAX_DISTANCE 80.0f -#define STANDARD_RADIUS 0.2 -#define STANDARD_DENSITY 3.25f -#define STANDARD_MASS MASS(STANDARD_DENSITY, STANDARD_RADIUS) -#define STANDARD_INV_MASS INV_MASS(STANDARD_MASS) -#define STANDARD_BOUNCE 2 -#define STANDARD_SPAWN_WEIGHT 0 - -#define MACHINEGUN_AMMO 50 -#define MACHINEGUN_PROJECTILES 1 -#define MACHINEGUN_RECOIL_MAGNITUDE 12.8f -#define MACHINEGUN_FIRE_MAGNITUDE 30.0f -#define MACHINEGUN_DAMPING 0.1f -#define MACHINEGUN_CHARGE 0.0f -#define MACHINEGUN_COOL_DOWN 0.07f -#define MACHINEGUN_MAX_DISTANCE 225.0f -#define MACHINEGUN_RADIUS 0.15f -#define MACHINEGUN_DENSITY 3.2f -#define MACHINEGUN_MASS MASS(MACHINEGUN_DENSITY, MACHINEGUN_RADIUS) -#define MACHINEGUN_INV_MASS INV_MASS(MACHINEGUN_MASS) -#define MACHINEGUN_BOUNCE 1 -#define MACHINEGUN_ENERGY_REFILL_COEF 0.2f -#define MACHINEGUN_SPAWN_WEIGHT 3.0f - -#define SNIPER_AMMO 3 -#define SNIPER_PROJECTILES 1 -#define SNIPER_RECOIL_MAGNITUDE 96.0f -#define SNIPER_FIRE_MAGNITUDE 300.0f -#define SNIPER_DAMPING 0.05f -#define SNIPER_CHARGE 1.0f -#define SNIPER_COOL_DOWN 1.5f -#define SNIPER_MAX_DISTANCE INFINITE -#define SNIPER_RADIUS 0.5f -#define SNIPER_DENSITY 2.0f -#define SNIPER_MASS MASS(SNIPER_DENSITY, SNIPER_RADIUS) -#define SNIPER_INV_MASS INV_MASS(SNIPER_MASS) -#define SNIPER_BOUNCE 0 -#define SNIPER_ENERGY_REFILL_COEF 1.2f -#define SNIPER_SPAWN_WEIGHT 3.0f - -#define SHOTGUN_AMMO 8 -#define SHOTGUN_PROJECTILES 8 -#define SHOTGUN_RECOIL_MAGNITUDE 100.0f -#define SHOTGUN_FIRE_MAGNITUDE 25.0f -#define SHOTGUN_DAMPING 0.3f -#define SHOTGUN_CHARGE 0.0f -#define SHOTGUN_COOL_DOWN 0.8f -#define SHOTGUN_MAX_DISTANCE 100.0f -#define SHOTGUN_RADIUS 0.15f -#define SHOTGUN_DENSITY 2.5f -#define SHOTGUN_MASS MASS(SHOTGUN_DENSITY, SHOTGUN_RADIUS) -#define SHOTGUN_INV_MASS INV_MASS(SHOTGUN_MASS) -#define SHOTGUN_BOUNCE 1 -#define SHOTGUN_ENERGY_REFILL_COEF 0.5f -#define SHOTGUN_SPAWN_WEIGHT 3.0f - -#define IMPLODER_AMMO 1 -#define IMPLODER_PROJECTILES 1 -#define IMPLODER_RECOIL_MAGNITUDE 65.0f -#define IMPLODER_FIRE_MAGNITUDE 60.0f -#define IMPLODER_DAMPING 0.0f -#define IMPLODER_CHARGE 2.0f -#define IMPLODER_COOL_DOWN 1.5f -#define IMPLODER_MAX_DISTANCE INFINITE -#define IMPLODER_RADIUS 0.8f -#define IMPLODER_DENSITY 1.0f -#define IMPLODER_MASS MASS(IMPLODER_DENSITY, IMPLODER_RADIUS) -#define IMPLODER_INV_MASS INV_MASS(IMPLODER_MASS) -#define IMPLODER_BOUNCE 0 -#define IMPLODER_SPAWN_WEIGHT 1.0f - -#define ACCELERATOR_AMMO 1 -#define ACCELERATOR_PROJECTILES 1 -#define ACCELERATOR_RECOIL_MAGNITUDE 100.0f -#define ACCELERATOR_FIRE_MAGNITUDE 35.0f -#define ACCELERATOR_DAMPING 0.0f -#define ACCELERATOR_CHARGE 0.0f -#define ACCELERATOR_COOL_DOWN 2.0f -#define ACCELERATOR_MAX_DISTANCE INFINITE -#define ACCELERATOR_RADIUS 0.5f -#define ACCELERATOR_DENSITY 2.0f -#define ACCELERATOR_MASS MASS(ACCELERATOR_DENSITY, ACCELERATOR_RADIUS) -#define ACCELERATOR_INV_MASS INV_MASS(ACCELERATOR_MASS) -#define ACCELERATOR_BOUNCE 100 -#define ACCELERATOR_BOUNCE_SPEED_COEF 1.07f -#define ACCELERATOR_MAX_SPEED 500.f -#define ACCELERATOR_SPAWN_WEIGHT 1.0f - -#define FLAK_CANNON_AMMO 12 -#define FLAK_CANNON_PROJECTILES 1 -#define FLAK_CANNON_RECOIL_MAGNITUDE 30.0f -#define FLAK_CANNON_FIRE_MAGNITUDE 14.0f -#define FLAK_CANNON_DAMPING 0.15f -#define FLAK_CANNON_CHARGE 0.0f -#define FLAK_CANNON_COOL_DOWN 0.4f -#define FLAK_CANNON_MAX_DISTANCE 100.0f -#define FLAK_CANNON_RADIUS 0.3f -#define FLAK_CANNON_DENSITY 1.0f -#define FLAK_CANNON_MASS MASS(FLAK_CANNON_DENSITY, FLAK_CANNON_RADIUS) -#define FLAK_CANNON_INV_MASS INV_MASS(FLAK_CANNON_MASS) -#define FLAK_CANNON_BOUNCE INFINITE -#define FLAK_CANNON_SAFE_DISTANCE 25.0f -#define FLAK_CANNON_PROXIMITY_RADIUS 2.0f -#define FLAK_CANNON_SPAWN_WEIGHT 2.0f - -#define MINE_LAUNCHER_AMMO 3 -#define MINE_LAUNCHER_PROJECTILES 1 -#define MINE_LAUNCHER_RECOIL_MAGNITUDE 20.0f -#define MINE_LAUNCHER_FIRE_MAGNITUDE 25.0f -#define MINE_LAUNCHER_DAMPING 0.2f -#define MINE_LAUNCHER_CHARGE 0.0f -#define MINE_LAUNCHER_COOL_DOWN 0.6f -#define MINE_LAUNCHER_MAX_DISTANCE INFINITE -#define MINE_LAUNCHER_RADIUS 0.5f -#define MINE_LAUNCHER_DENSITY 0.5f -#define MINE_LAUNCHER_MASS MASS(MINE_LAUNCHER_DENSITY, MINE_LAUNCHER_RADIUS) -#define MINE_LAUNCHER_INV_MASS INV_MASS(MINE_LAUNCHER_MASS) -#define MINE_LAUNCHER_BOUNCE INFINITE // this is to avoid mines sometimes exploding when hitting walls -#define MINE_LAUNCHER_SPAWN_WEIGHT 2.0f -#define MINE_LAUNCHER_PROXIMITY_RADIUS 7.5f - -#define BLACK_HOLE_AMMO 3 -#define BLACK_HOLE_PROJECTILES 1 -#define BLACK_HOLE_RECOIL_MAGNITUDE 75.0f -#define BLACK_HOLE_FIRE_MAGNITUDE 125.0f -#define BLACK_HOLE_DAMPING 0.0f -#define BLACK_HOLE_CHARGE 0.75f -#define BLACK_HOLE_COOL_DOWN 1.0f -#define BLACK_HOLE_MAX_DISTANCE INFINITE -#define BLACK_HOLE_RADIUS 0.5f -#define BLACK_HOLE_DENSITY 10.0f -#define BLACK_HOLE_MASS MASS(BLACK_HOLE_DENSITY, BLACK_HOLE_RADIUS) -#define BLACK_HOLE_INV_MASS INV_MASS(BLACK_HOLE_MASS) -#define BLACK_HOLE_BOUNCE 0 -#define BLACK_HOLE_SPAWN_WEIGHT 2.0f -#define BLACK_HOLE_PROXIMITY_RADIUS 10.0f -#define BLACK_HOLE_PARENT_IGNORE_DISTANCE BLACK_HOLE_PROXIMITY_RADIUS * 1.5f -#define BLACK_HOLE_PULL_MAGNITUDE -300.0f - -#define NUKE_AMMO 1 -#define NUKE_PROJECTILES 1 -#define NUKE_RECOIL_MAGNITUDE 150.0f -#define NUKE_FIRE_MAGNITUDE 85.0f -#define NUKE_DAMPING 0.0f -#define NUKE_CHARGE 5.0f -#define NUKE_COOL_DOWN 3.0f -#define NUKE_MAX_DISTANCE INFINITE -#define NUKE_RADIUS 0.8f -#define NUKE_DENSITY 3.0f -#define NUKE_MASS MASS(NUKE_DENSITY, NUKE_RADIUS) -#define NUKE_INV_MASS INV_MASS(NUKE_MASS) -#define NUKE_BOUNCE 0 -#define NUKE_SPAWN_WEIGHT 0.5f - -const weaponInformation standard = { - .type = STANDARD_WEAPON, - .isPhysicsBullet = true, - .canSleep = false, - .numProjectiles = STANDARD_PROJECTILES, - .fireMagnitude = STANDARD_FIRE_MAGNITUDE, - .recoilMagnitude = STANDARD_RECOIL_MAGNITUDE, - .damping = STANDARD_DAMPING, - .charge = STANDARD_CHARGE, - .coolDown = STANDARD_COOL_DOWN, - .maxDistance = STANDARD_MAX_DISTANCE, - .radius = STANDARD_RADIUS, - .density = STANDARD_DENSITY, - .mass = STANDARD_MASS, - .invMass = STANDARD_INV_MASS, - .initialSpeed = STANDARD_FIRE_MAGNITUDE * STANDARD_INV_MASS, - .maxBounces = STANDARD_BOUNCE + 1, - .explosive = false, - .destroyedOnDroneHit = false, - .explodesOnDroneHit = false, - .hasSensor = false, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF, - .spawnWeight = STANDARD_SPAWN_WEIGHT, -}; - -const weaponInformation machineGun = { - .type = MACHINEGUN_WEAPON, - .isPhysicsBullet = true, - .canSleep = false, - .numProjectiles = MACHINEGUN_PROJECTILES, - .fireMagnitude = MACHINEGUN_FIRE_MAGNITUDE, - .recoilMagnitude = MACHINEGUN_RECOIL_MAGNITUDE, - .damping = MACHINEGUN_DAMPING, - .charge = MACHINEGUN_CHARGE, - .coolDown = MACHINEGUN_COOL_DOWN, - .maxDistance = MACHINEGUN_MAX_DISTANCE, - .radius = MACHINEGUN_RADIUS, - .density = MACHINEGUN_DENSITY, - .mass = MACHINEGUN_MASS, - .invMass = MACHINEGUN_INV_MASS, - .initialSpeed = MACHINEGUN_FIRE_MAGNITUDE * MACHINEGUN_INV_MASS, - .maxBounces = MACHINEGUN_BOUNCE + 1, - .explosive = false, - .destroyedOnDroneHit = false, - .explodesOnDroneHit = false, - .hasSensor = false, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF * MACHINEGUN_ENERGY_REFILL_COEF, - .spawnWeight = MACHINEGUN_SPAWN_WEIGHT, -}; - -const weaponInformation sniper = { - .type = SNIPER_WEAPON, - .isPhysicsBullet = true, - .canSleep = false, - .numProjectiles = SNIPER_PROJECTILES, - .fireMagnitude = SNIPER_FIRE_MAGNITUDE, - .recoilMagnitude = SNIPER_RECOIL_MAGNITUDE, - .damping = SNIPER_DAMPING, - .charge = SNIPER_CHARGE, - .coolDown = SNIPER_COOL_DOWN, - .maxDistance = SNIPER_MAX_DISTANCE, - .radius = SNIPER_RADIUS, - .density = SNIPER_DENSITY, - .mass = SNIPER_MASS, - .invMass = SNIPER_INV_MASS, - .initialSpeed = SNIPER_FIRE_MAGNITUDE * SNIPER_INV_MASS, - .maxBounces = SNIPER_BOUNCE + 1, - .explosive = false, - .destroyedOnDroneHit = true, - .explodesOnDroneHit = false, - .hasSensor = false, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF * SNIPER_ENERGY_REFILL_COEF, - .spawnWeight = SNIPER_SPAWN_WEIGHT, -}; - -const weaponInformation shotgun = { - .type = SHOTGUN_WEAPON, - .isPhysicsBullet = true, - .canSleep = false, - .numProjectiles = SHOTGUN_PROJECTILES, - .fireMagnitude = SHOTGUN_FIRE_MAGNITUDE, - .recoilMagnitude = SHOTGUN_RECOIL_MAGNITUDE, - .damping = SHOTGUN_DAMPING, - .charge = SHOTGUN_CHARGE, - .coolDown = SHOTGUN_COOL_DOWN, - .maxDistance = SHOTGUN_MAX_DISTANCE, - .radius = SHOTGUN_RADIUS, - .density = SHOTGUN_DENSITY, - .mass = SHOTGUN_MASS, - .invMass = SHOTGUN_INV_MASS, - .initialSpeed = SHOTGUN_FIRE_MAGNITUDE * SHOTGUN_INV_MASS, - .maxBounces = SHOTGUN_BOUNCE + 1, - .explosive = false, - .destroyedOnDroneHit = false, - .explodesOnDroneHit = false, - .hasSensor = false, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF * SHOTGUN_ENERGY_REFILL_COEF, - .spawnWeight = SHOTGUN_SPAWN_WEIGHT, -}; - -const weaponInformation imploder = { - .type = IMPLODER_WEAPON, - .isPhysicsBullet = false, - .canSleep = false, - .numProjectiles = IMPLODER_PROJECTILES, - .fireMagnitude = IMPLODER_FIRE_MAGNITUDE, - .recoilMagnitude = IMPLODER_RECOIL_MAGNITUDE, - .damping = IMPLODER_DAMPING, - .charge = IMPLODER_CHARGE, - .coolDown = IMPLODER_COOL_DOWN, - .maxDistance = IMPLODER_MAX_DISTANCE, - .radius = IMPLODER_RADIUS, - .density = IMPLODER_DENSITY, - .mass = IMPLODER_MASS, - .invMass = IMPLODER_INV_MASS, - .initialSpeed = IMPLODER_FIRE_MAGNITUDE * IMPLODER_INV_MASS, - .maxBounces = IMPLODER_BOUNCE + 1, - .explosive = true, - .destroyedOnDroneHit = true, - .explodesOnDroneHit = true, - .hasSensor = false, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF, - .spawnWeight = IMPLODER_SPAWN_WEIGHT, -}; - -const weaponInformation accelerator = { - .type = ACCELERATOR_WEAPON, - .isPhysicsBullet = true, - .canSleep = false, - .numProjectiles = ACCELERATOR_PROJECTILES, - .fireMagnitude = ACCELERATOR_FIRE_MAGNITUDE, - .recoilMagnitude = ACCELERATOR_RECOIL_MAGNITUDE, - .damping = ACCELERATOR_DAMPING, - .charge = ACCELERATOR_CHARGE, - .coolDown = ACCELERATOR_COOL_DOWN, - .maxDistance = ACCELERATOR_MAX_DISTANCE, - .radius = ACCELERATOR_RADIUS, - .density = ACCELERATOR_DENSITY, - .mass = ACCELERATOR_MASS, - .invMass = ACCELERATOR_INV_MASS, - .initialSpeed = ACCELERATOR_FIRE_MAGNITUDE * ACCELERATOR_INV_MASS, - .maxBounces = ACCELERATOR_BOUNCE + 1, - .explosive = false, - .destroyedOnDroneHit = true, - .explodesOnDroneHit = false, - .hasSensor = false, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF, - .spawnWeight = ACCELERATOR_SPAWN_WEIGHT, -}; - -const weaponInformation flakCannon = { - .type = FLAK_CANNON_WEAPON, - .isPhysicsBullet = true, - .canSleep = false, - .numProjectiles = FLAK_CANNON_PROJECTILES, - .fireMagnitude = FLAK_CANNON_FIRE_MAGNITUDE, - .recoilMagnitude = FLAK_CANNON_RECOIL_MAGNITUDE, - .damping = FLAK_CANNON_DAMPING, - .charge = FLAK_CANNON_CHARGE, - .coolDown = FLAK_CANNON_COOL_DOWN, - .maxDistance = FLAK_CANNON_MAX_DISTANCE, - .radius = FLAK_CANNON_RADIUS, - .density = FLAK_CANNON_DENSITY, - .mass = FLAK_CANNON_MASS, - .invMass = FLAK_CANNON_INV_MASS, - .initialSpeed = FLAK_CANNON_FIRE_MAGNITUDE * FLAK_CANNON_INV_MASS, - .maxBounces = FLAK_CANNON_BOUNCE + 1, - .explosive = true, - .destroyedOnDroneHit = false, - .explodesOnDroneHit = false, - .hasSensor = true, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF, - .spawnWeight = FLAK_CANNON_SPAWN_WEIGHT, -}; - -const weaponInformation mineLauncher = { - .type = MINE_LAUNCHER_WEAPON, - .isPhysicsBullet = false, - .canSleep = true, - .numProjectiles = MINE_LAUNCHER_PROJECTILES, - .fireMagnitude = MINE_LAUNCHER_FIRE_MAGNITUDE, - .recoilMagnitude = MINE_LAUNCHER_RECOIL_MAGNITUDE, - .damping = MINE_LAUNCHER_DAMPING, - .charge = MINE_LAUNCHER_CHARGE, - .coolDown = MINE_LAUNCHER_COOL_DOWN, - .maxDistance = MINE_LAUNCHER_MAX_DISTANCE, - .radius = MINE_LAUNCHER_RADIUS, - .density = MINE_LAUNCHER_DENSITY, - .mass = MINE_LAUNCHER_MASS, - .invMass = MINE_LAUNCHER_INV_MASS, - .initialSpeed = MINE_LAUNCHER_FIRE_MAGNITUDE * MINE_LAUNCHER_INV_MASS, - .maxBounces = MINE_LAUNCHER_BOUNCE + 1, - .explosive = true, - .destroyedOnDroneHit = true, - .explodesOnDroneHit = false, - .hasSensor = true, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF, - .spawnWeight = MINE_LAUNCHER_SPAWN_WEIGHT, -}; - -const weaponInformation blackHole = { - .type = BLACK_HOLE_WEAPON, - .isPhysicsBullet = false, - .canSleep = false, - .numProjectiles = BLACK_HOLE_PROJECTILES, - .fireMagnitude = BLACK_HOLE_FIRE_MAGNITUDE, - .recoilMagnitude = BLACK_HOLE_RECOIL_MAGNITUDE, - .damping = BLACK_HOLE_DAMPING, - .charge = BLACK_HOLE_CHARGE, - .coolDown = BLACK_HOLE_COOL_DOWN, - .maxDistance = BLACK_HOLE_MAX_DISTANCE, - .radius = BLACK_HOLE_RADIUS, - .density = BLACK_HOLE_DENSITY, - .mass = BLACK_HOLE_MASS, - .invMass = BLACK_HOLE_INV_MASS, - .initialSpeed = BLACK_HOLE_FIRE_MAGNITUDE * BLACK_HOLE_INV_MASS, - .maxBounces = BLACK_HOLE_BOUNCE + 1, - .explosive = false, - .destroyedOnDroneHit = false, - .explodesOnDroneHit = false, - .hasSensor = true, - .energyRefillCoef = 0.0f, - .spawnWeight = BLACK_HOLE_SPAWN_WEIGHT, -}; - -const weaponInformation nuke = { - .type = NUKE_WEAPON, - .isPhysicsBullet = false, - .canSleep = false, - .numProjectiles = NUKE_PROJECTILES, - .fireMagnitude = NUKE_FIRE_MAGNITUDE, - .recoilMagnitude = NUKE_RECOIL_MAGNITUDE, - .damping = NUKE_DAMPING, - .charge = NUKE_CHARGE, - .coolDown = NUKE_COOL_DOWN, - .maxDistance = NUKE_MAX_DISTANCE, - .radius = NUKE_RADIUS, - .density = NUKE_DENSITY, - .mass = NUKE_MASS, - .invMass = NUKE_INV_MASS, - .initialSpeed = NUKE_FIRE_MAGNITUDE * NUKE_INV_MASS, - .maxBounces = NUKE_BOUNCE + 1, - .explosive = true, - .destroyedOnDroneHit = true, - .explodesOnDroneHit = true, - .hasSensor = false, - .energyRefillCoef = PROJECTILE_ENERGY_REFILL_COEF, - .spawnWeight = NUKE_SPAWN_WEIGHT, -}; - -weaponInformation *weaponInfos[] = { - (weaponInformation *)&standard, - (weaponInformation *)&machineGun, - (weaponInformation *)&sniper, - (weaponInformation *)&shotgun, - (weaponInformation *)&imploder, - (weaponInformation *)&accelerator, - (weaponInformation *)&flakCannon, - (weaponInformation *)&mineLauncher, - (weaponInformation *)&blackHole, - (weaponInformation *)&nuke, -}; - -const char *weaponNames[] = { - "standard", - "machine_gun", - "sniper", - "shotgun", - "imploder", - "accelerator", - "flak_cannon", - "mine_launcher", - "black_hole", - "tactical_nuke", -}; - -// max ammo of weapon -int8_t weaponAmmo(const enum weaponType defaultWep, const enum weaponType type) { - if (type == defaultWep) { - return INFINITE; - } - switch (type) { - case STANDARD_WEAPON: - return STANDARD_AMMO; - case MACHINEGUN_WEAPON: - return MACHINEGUN_AMMO; - case SNIPER_WEAPON: - return SNIPER_AMMO; - case SHOTGUN_WEAPON: - return SHOTGUN_AMMO; - case IMPLODER_WEAPON: - return IMPLODER_AMMO; - case ACCELERATOR_WEAPON: - return ACCELERATOR_AMMO; - case FLAK_CANNON_WEAPON: - return FLAK_CANNON_AMMO; - case MINE_LAUNCHER_WEAPON: - return MINE_LAUNCHER_AMMO; - case BLACK_HOLE_WEAPON: - return BLACK_HOLE_AMMO; - case NUKE_WEAPON: - return NUKE_AMMO; - default: - ERRORF("unknown weapon type %d", type); - } -} - -b2ShapeId weaponSensor(const b2BodyId bodyID, const enum weaponType type) { - b2ShapeDef sensorShapeDef = b2DefaultShapeDef(); - sensorShapeDef.density = 0.0f; - sensorShapeDef.isSensor = true; - sensorShapeDef.enableSensorEvents = true; - b2Circle sensorCircle = {.center = b2Vec2_zero}; - - switch (type) { - case FLAK_CANNON_WEAPON: - sensorShapeDef.filter.categoryBits = PROJECTILE_SHAPE; - sensorShapeDef.filter.maskBits = DRONE_SHAPE; - sensorCircle.radius = FLAK_CANNON_PROXIMITY_RADIUS; - break; - case MINE_LAUNCHER_WEAPON: - sensorShapeDef.filter.categoryBits = PROJECTILE_SHAPE; - sensorShapeDef.filter.maskBits = DRONE_SHAPE; - sensorCircle.radius = MINE_LAUNCHER_PROXIMITY_RADIUS; - break; - case BLACK_HOLE_WEAPON: - sensorShapeDef.filter.categoryBits = PROJECTILE_SHAPE; - sensorShapeDef.filter.maskBits = FLOATING_WALL_SHAPE | PROJECTILE_SHAPE | DRONE_SHAPE; - sensorCircle.radius = BLACK_HOLE_PROXIMITY_RADIUS; - break; - default: - ERRORF("unknown weapon type with sensor %d", type); - } - - return b2CreateCircleShape(bodyID, &sensorShapeDef, &sensorCircle); -} - -// amount of force to apply to projectile -float weaponFire(uint64_t *seed, const enum weaponType type) { - switch (type) { - case STANDARD_WEAPON: - return STANDARD_FIRE_MAGNITUDE; - case MACHINEGUN_WEAPON: - return MACHINEGUN_FIRE_MAGNITUDE; - case SNIPER_WEAPON: - return SNIPER_FIRE_MAGNITUDE; - case SHOTGUN_WEAPON: { - const int maxOffset = 3; - const int fireOffset = randInt(seed, -maxOffset, maxOffset); - return SHOTGUN_FIRE_MAGNITUDE + fireOffset; - } - case IMPLODER_WEAPON: - return IMPLODER_FIRE_MAGNITUDE; - case ACCELERATOR_WEAPON: - return ACCELERATOR_FIRE_MAGNITUDE; - case FLAK_CANNON_WEAPON: - return FLAK_CANNON_FIRE_MAGNITUDE; - case MINE_LAUNCHER_WEAPON: - return MINE_LAUNCHER_FIRE_MAGNITUDE; - case BLACK_HOLE_WEAPON: - return BLACK_HOLE_FIRE_MAGNITUDE; - case NUKE_WEAPON: - return NUKE_FIRE_MAGNITUDE; - default: - ERRORF("unknown weapon type %d", type); - return 0; - } -} - -b2Vec2 weaponAdjustAim(uint64_t *seed, const enum weaponType type, const uint16_t heat, const b2Vec2 normAim) { - switch (type) { - case MACHINEGUN_WEAPON: { - const float swayCoef = logBasef((heat / 5.0f) + 1, 180); - const float maxSway = 0.1f; - const float swayX = randFloat(seed, maxSway * -swayCoef, maxSway * swayCoef); - const float swayY = randFloat(seed, maxSway * -swayCoef, maxSway * swayCoef); - b2Vec2 machinegunAim = {.x = normAim.x + swayX, .y = normAim.y + swayY}; - return b2Normalize(machinegunAim); - } - case SHOTGUN_WEAPON: { - const float maxOffset = 0.11f; - const float offsetX = randFloat(seed, -maxOffset, maxOffset); - const float offsetY = randFloat(seed, -maxOffset, maxOffset); - b2Vec2 shotgunAim = {.x = normAim.x + offsetX, .y = normAim.y + offsetY}; - return b2Normalize(shotgunAim); - } - default: - return normAim; - } -} - -// sets explosion parameters and returns true if an explosion should be created -// when a projectile is destroyed -void weaponExplosion(const enum weaponType type, b2ExplosionDef *explosionDef) { - switch (type) { - case IMPLODER_WEAPON: - explosionDef->radius = 15.0f; - explosionDef->impulsePerLength = -200.0f; - return; - case FLAK_CANNON_WEAPON: - explosionDef->radius = 7.5f; - // will typically be closer to 45 with projectile velocity - // factored in - explosionDef->impulsePerLength = 10.0f; - return; - case MINE_LAUNCHER_WEAPON: - explosionDef->radius = 15.0f; - explosionDef->impulsePerLength = 200.0f; - return; - case NUKE_WEAPON: - explosionDef->radius = 35.0f; - explosionDef->impulsePerLength = 300.0f; - return; - default: - ERRORF("unknown weapon type %d for projectile explosion", type); - } -} - -// insertion sort should be faster than quicksort for small arrays -void insertionSort(nearEntity arr[], uint8_t size) { - for (int16_t i = 1; i < size; i++) { - nearEntity key = arr[i]; - int16_t j = i - 1; - - while (j >= 0 && arr[j].distanceSquared > key.distanceSquared) { - arr[j + 1] = arr[j]; - j = j - 1; - } - - arr[j + 1] = key; - } -} - -#endif diff --git a/pufferlib/ocean/impulse_wars/types.h b/pufferlib/ocean/impulse_wars/types.h deleted file mode 100644 index 3fdb3a6d7..000000000 --- a/pufferlib/ocean/impulse_wars/types.h +++ /dev/null @@ -1,472 +0,0 @@ -#ifndef IMPULSE_WARS_TYPES_H -#define IMPULSE_WARS_TYPES_H - -#include "box2d/box2d.h" -#include "id_pool.h" -#include "raylib.h" -#include "rlights.h" - -#include "include/cc_array.h" - -#include "settings.h" - -#define _MAX_DRONES 4 - -const uint8_t NUM_WALL_TYPES = 3; - -#define MAX_TRAIL_POINTS 20 -#define MAX_DRONE_TRAIL_POINTS 20 -#define MAX_PROJECTLE_TRAIL_POINTS 10 - -enum entityType { - STANDARD_WALL_ENTITY, - BOUNCY_WALL_ENTITY, - DEATH_WALL_ENTITY, - WEAPON_PICKUP_ENTITY, - PROJECTILE_ENTITY, - DRONE_ENTITY, - SHIELD_ENTITY, - DRONE_PIECE_ENTITY, -}; - -// the category bit that will be set on each entity's shape; this is -// used to control what entities can collide with each other -enum shapeCategory { - WALL_SHAPE = 1, - FLOATING_WALL_SHAPE = 2, - PROJECTILE_SHAPE = 4, - WEAPON_PICKUP_SHAPE = 8, - DRONE_SHAPE = 16, - SHIELD_SHAPE = 32, - DRONE_PIECE_SHAPE = 64, -}; - -typedef struct entityID { - int32_t id; - uint16_t generation; -} entityID; - -// general purpose entity object -typedef struct entity { - entityID *id; - uint32_t generation; - enum entityType type; - void *entity; -} entity; - -#define _NUM_WEAPONS 10 -const uint8_t NUM_WEAPONS = _NUM_WEAPONS; - -enum weaponType { - STANDARD_WEAPON, - MACHINEGUN_WEAPON, - SNIPER_WEAPON, - SHOTGUN_WEAPON, - IMPLODER_WEAPON, - ACCELERATOR_WEAPON, - FLAK_CANNON_WEAPON, - MINE_LAUNCHER_WEAPON, - BLACK_HOLE_WEAPON, - NUKE_WEAPON, -}; - -typedef struct mapBounds { - b2Vec2 min; - b2Vec2 max; -} mapBounds; - -// used for N near entities observations -typedef struct nearEntity { - uint16_t idx; - void *entity; - float distanceSquared; -} nearEntity; - -typedef struct mapEntry { - const char *layout; - const uint8_t columns; - const uint8_t rows; - const uint8_t randFloatingStandardWalls; - const uint8_t randFloatingBouncyWalls; - const uint8_t randFloatingDeathWalls; - // are there any floating walls that have consistent starting positions - const bool hasSetFloatingWalls; - const uint16_t weaponPickups; - const enum weaponType defaultWeapon; - const uint8_t maxSuddenDeathWalls; - - mapBounds bounds; - mapBounds spawnQuads[4]; - bool *droneSpawns; - uint8_t *packedLayout; - nearEntity *nearestWalls; -} mapEntry; - -// a cell in the map; ent will be NULL if the cell is empty -typedef struct mapCell { - entity *ent; - b2Vec2 pos; -} mapCell; - -typedef struct wallEntity { - b2BodyId bodyID; - b2ShapeId shapeID; - b2Vec2 pos; - b2Rot rot; - b2Vec2 velocity; - b2Vec2 extent; - int16_t mapCellIdx; - bool isFloating; - enum entityType type; - bool isSuddenDeath; - - entity *ent; -} wallEntity; - -typedef struct weaponInformation { - const enum weaponType type; - // should the body be treated as a bullet by box2d; if so CCD - // (continuous collision detection) will be enabled to prevent - // tunneling through static bodies which is expensive so it's - // only enabled for fast moving projectiles - const bool isPhysicsBullet; - // can the projectile ever be stationary? if so, it should be - // allowed to sleep to save on physics updates - const bool canSleep; - const uint8_t numProjectiles; - const float fireMagnitude; - const float recoilMagnitude; - const float damping; - const float charge; - const float coolDown; - const float maxDistance; - const float radius; - const float density; - const float mass; - const float invMass; - const float initialSpeed; - const uint8_t maxBounces; - const bool explosive; - const bool destroyedOnDroneHit; - const bool explodesOnDroneHit; - const bool hasSensor; - const float energyRefillCoef; - const float spawnWeight; -} weaponInformation; - -typedef struct weaponPickupEntity { - b2BodyId bodyID; - b2ShapeId shapeID; - enum weaponType weapon; - float respawnWait; - // how many floating walls are touching this pickup - uint8_t floatingWallsTouching; - b2Vec2 pos; - int16_t mapCellIdx; - - entity *ent; - bool bodyDestroyed; -} weaponPickupEntity; - -typedef struct droneEntity droneEntity; - -typedef struct trailPoints { - Vector2 points[MAX_TRAIL_POINTS]; - uint8_t length; -} trailPoints; - -typedef struct projectileEntity { - uint8_t droneIdx; - - b2BodyId bodyID; - b2ShapeId shapeID; - // used for proximity explosive projectiles - b2ShapeId sensorID; - weaponInformation *weaponInfo; - b2Vec2 pos; - int16_t mapCellIdx; - b2Vec2 lastPos; - b2Vec2 velocity; - b2Vec2 lastVelocity; - float speed; - float lastSpeed; - float distance; - uint8_t bounces; - uint8_t contacts; - bool setMine; - uint8_t numDronesBehindWalls; - uint8_t dronesBehindWalls[_MAX_DRONES]; - CC_Array *entsInBlackHole; - bool needsToBeDestroyed; - - entity *ent; - - // for rendering - trailPoints trailPoints; -} projectileEntity; - -// used to keep track of what happened each step for reward purposes -typedef struct droneStepInfo { - bool firedShot; - bool pickedUpWeapon; - enum weaponType prevWeapon; - uint8_t shotHit[_MAX_DRONES]; - bool explosionHit[_MAX_DRONES]; - uint8_t shotTaken[_MAX_DRONES]; - bool explosionTaken[_MAX_DRONES]; - bool ownShotTaken; -} droneStepInfo; - -typedef struct shieldEntity { - droneEntity *drone; - - b2BodyId bodyID; - b2ShapeId shapeID; - b2ShapeId bufferShapeID; - b2Vec2 pos; - float health; - float duration; - - entity *ent; -} shieldEntity; - -typedef struct dronePieceEntity { - uint8_t droneIdx; - - b2BodyId bodyID; - b2ShapeId shapeID; - b2Vec2 pos; - b2Rot rot; - b2Vec2 vertices[3]; - bool isShieldPiece; - - entity *ent; - - uint16_t lifetime; -} dronePieceEntity; - -typedef struct physicsStepInfo { - uint8_t srcIdx; - b2Vec2 impulse; - b2Vec2 force; - bool brakeToggled; - uint16_t step; -} physicsStepInfo; - -typedef struct droneEntity { - b2BodyId bodyID; - b2ShapeId shapeID; - weaponInformation *weaponInfo; - int8_t ammo; - float weaponCooldown; - uint16_t heat; - bool chargingWeapon; - float weaponCharge; - float energyLeft; - bool braking; - bool chargingBurst; - float burstCharge; - float burstCooldown; - bool energyFullyDepleted; - bool energyFullyDepletedThisStep; - float energyRefillWait; - bool shotThisStep; - bool diedThisStep; - - uint8_t idx; - uint8_t team; - b2Vec2 initalPos; - b2Vec2 pos; - int16_t mapCellIdx; - b2Vec2 lastPos; - b2Vec2 lastMove; - b2Vec2 lastAim; - b2Vec2 velocity; - b2Vec2 lastVelocity; - droneStepInfo stepInfo; - float respawnWait; - uint8_t livesLeft; - bool dead; - - CC_Array *physicsTracking; - int8_t killedBy; - bool killed[_MAX_DRONES]; - - shieldEntity *shield; - entity *ent; - - // for rendering - trailPoints trailPoints; - CC_Array *brakeTrailPoints; - uint16_t respawnGuideLifetime; -} droneEntity; - -// stats for the whole episode -typedef struct droneStats { - float returns; - float distanceTraveled; - float absDistanceTraveled; - float brakeTime; - float totalBursts; - float burstsHit; - float energyEmptied; - float wins; - - float shotsFired[_NUM_WEAPONS]; - float shotsHit[_NUM_WEAPONS]; - float shotsTaken[_NUM_WEAPONS]; - float ownShotsTaken[_NUM_WEAPONS]; - float weaponsPickedUp[_NUM_WEAPONS]; - float shotDistances[_NUM_WEAPONS]; - - float totalShotsFired; - float totalShotsHit; - float totalShotsTaken; - float totalOwnShotsTaken; - float totalWeaponsPickedUp; - float totalShotDistances; -} droneStats; - -typedef struct Log { - float length; - float ties; - droneStats stats[_MAX_DRONES]; - - float n; -} Log; - -typedef struct gameCamera { - Camera3D camera3D; - Camera2D camera2D; - Vector2 targetPos; - float maxZoom; - bool orthographic; -} gameCamera; - -typedef struct rayClient { - float scale; - uint16_t width; - uint16_t height; - uint16_t halfWidth; - uint16_t halfHeight; - - gameCamera *camera; - - Shader blurShader; - int32_t blurShaderDirLoc; - Shader bloomShader; - int32_t bloomIntensityLoc; - int32_t bloomTexColorLoc; - int32_t bloomTexBloomBlurLoc; - Shader gridShader; - int32_t gridShaderPosLoc[4]; - int32_t gridShaderColorLoc[4]; - Texture2D wallTexture; - RenderTexture2D blurSrcTexture; - RenderTexture2D blurDstTexture; - RenderTexture2D projRawTex; - RenderTexture2D projBloomTex; - RenderTexture2D droneRawTex; - RenderTexture2D droneBloomTex; -} rayClient; - -typedef struct brakeTrailPoint { - b2Vec2 pos; - uint16_t lifetime; - bool isEnd; -} brakeTrailPoint; - -typedef struct explosionInfo { - b2ExplosionDef def; - bool isBurst; - uint8_t droneIdx; - uint16_t renderSteps; -} explosionInfo; - -typedef struct agentActions { - b2Vec2 move; - b2Vec2 aim; - bool chargingWeapon; - bool shoot; - bool brake; - bool chargingBurst; - bool discardWeapon; -} agentActions; - -typedef struct pathingInfo { - uint8_t *paths; - int8_t *pathBuffer; -} pathingInfo; - -typedef struct iwEnv { - uint8_t numDrones; - uint8_t numAgents; - uint8_t numTeams; - bool teamsEnabled; - bool sittingDuck; - bool isTraining; - - uint16_t obsBytes; - uint16_t discreteObsBytes; - bool continuousActions; - - uint8_t *observations; - float *rewards; - float *actions; - uint8_t *masks; - uint8_t *terminals; - uint8_t *truncations; - - uint8_t frameRate; - float deltaTime; - uint8_t frameSkip; - uint8_t box2dSubSteps; - uint64_t randState; - bool needsReset; - - uint16_t episodeLength; - Log log; - droneStats stats[_MAX_DRONES]; - - b2WorldId worldID; - int8_t pinnedMapIdx; - int8_t mapIdx; - mapEntry *map; - int8_t lastSpawnQuad; - uint8_t spawnedWeaponPickups[_NUM_WEAPONS]; - weaponInformation *defaultWeapon; - b2IdPool idPool; - CC_Array *entities; - CC_Array *cells; - CC_Array *walls; - CC_Array *floatingWalls; - CC_Array *drones; - CC_Array *pickups; - CC_Array *projectiles; - CC_Array *explodingProjectiles; - CC_Array *dronePieces; - - pathingInfo *mapPathing; - - uint16_t totalSteps; - uint16_t totalSuddenDeathSteps; - // steps left until sudden death - uint16_t stepsLeft; - // steps left until the next set of sudden death walls are spawned - uint16_t suddenDeathSteps; - // the amount of sudden death walls that have been spawned - uint8_t suddenDeathWallCounter; - bool suddenDeathWallsPlaced; - - bool humanInput; - uint8_t humanDroneInput; - uint8_t connectedControllers; - - // used for rendering - rayClient *client; - float renderScale; - CC_Array *explosions; - b2Vec2 debugPoint; -} iwEnv; - -#endif diff --git a/pufferlib/ocean/matsci/binding.c b/pufferlib/ocean/matsci/binding.c deleted file mode 100644 index 1be426be8..000000000 --- a/pufferlib/ocean/matsci/binding.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "matsci.h" - -#define Env Matsci -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->num_agents = unpack(kwargs, "num_agents"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "score", log->score); - return 0; -} diff --git a/pufferlib/ocean/matsci/matsci.c b/pufferlib/ocean/matsci/matsci.c deleted file mode 100644 index 950092008..000000000 --- a/pufferlib/ocean/matsci/matsci.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "matsci.h" - -int main() { - int num_agents = 16; - Matsci env = {.num_agents=num_agents}; - env.observations = (float*)calloc(3*num_agents, sizeof(float)); - env.actions = (float*)calloc(3*num_agents, sizeof(float)); - env.rewards = (float*)calloc(num_agents, sizeof(float)); - env.terminals = (unsigned char*)calloc(num_agents, sizeof(unsigned char)); - init(&env); - - c_reset(&env); - c_render(&env); - while (!WindowShouldClose()) { - for (int i=0; i<3*num_agents; i++) { - env.actions[i] = rndf(-1.0f, 1.0f); - } - c_step(&env); - c_render(&env); - } - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} - diff --git a/pufferlib/ocean/matsci/matsci.h b/pufferlib/ocean/matsci/matsci.h deleted file mode 100644 index 5b19b0e01..000000000 --- a/pufferlib/ocean/matsci/matsci.h +++ /dev/null @@ -1,315 +0,0 @@ -#include -#include -#include -#include -#include -#include "raylib.h" - -#define WIDTH 1080 -#define HEIGHT 720 - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -typedef struct { - float x, y, z; -} Vec3; - -static inline float clampf(float v, float min, float max) { - if (v < min) - return min; - if (v > max) - return max; - return v; -} - -static inline float rndf(float a, float b) { - return a + ((float)rand() / (float)RAND_MAX) * (b - a); -} - -static inline Vec3 add3(Vec3 a, Vec3 b) { return (Vec3){a.x + b.x, a.y + b.y, a.z + b.z}; } - -static inline Vec3 sub3(Vec3 a, Vec3 b) { return (Vec3){a.x - b.x, a.y - b.y, a.z - b.z}; } - -static inline Vec3 scalmul3(Vec3 a, float b) { return (Vec3){a.x * b, a.y * b, a.z * b}; } - -static inline float dot3(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } - -static inline float norm3(Vec3 a) { return sqrtf(dot3(a, a)); } - -static inline void clamp3(Vec3 *vec, float min, float max) { - vec->x = clampf(vec->x, min, max); - vec->y = clampf(vec->y, min, max); - vec->z = clampf(vec->z, min, max); -} - - -// Only use floats! -typedef struct { - float score; - float n; // Required as the last field -} Log; - -typedef struct { - Camera3D camera; - float width; - float height; - - float camera_distance; - float camera_azimuth; - float camera_elevation; - bool is_dragging; - Vector2 last_mouse_pos; -} Client; - -typedef struct { - Log log; // Required field - float* observations; // Required field. Ensure type matches in .py and .c - float* actions; // Required field. Ensure type matches in .py and .c - float* rewards; // Required field - unsigned char* terminals; // Required field - int num_agents; - Vec3 goal; - int tick; - Client* client; - void* handle; -} Matsci; - -void init(Matsci* env) { - void *handle; - const char *lmpargv[] = { "liblammps", "-log", "none", "-screen", "none"}; - int lmpargc = sizeof(lmpargv)/sizeof(const char *); - - /* create LAMMPS instance */ - handle = lammps_open_no_mpi(lmpargc, (char **)lmpargv, NULL); - if (handle == NULL) { - printf("LAMMPS initialization failed\n"); - lammps_mpi_finalize(); - } - - // Setup the basic simulation parameters via string commands - lammps_command(handle, "units lj"); - lammps_command(handle, "dimension 3"); - lammps_command(handle, "boundary p p p"); - lammps_command(handle, "atom_style atomic"); - lammps_command(handle, "pair_style zero 1.0 nocoeff"); // Dummy pair style for no interactions - lammps_command(handle, "region box block -10 10 -10 10 -10 10"); - lammps_command(handle, "create_box 1 box"); - lammps_command(handle, "mass 1 1.0"); - lammps_command(handle, "pair_coeff 1 1 1.0 1.0"); - - lammps_command(handle, "region randbox block -10 10 -10 10 -10 10"); - char cmd[256]; - int seed = 123; - snprintf(cmd, sizeof(cmd), "create_atoms 1 random %d %d randbox overlap 0.8", env->num_agents, seed); - lammps_command(handle, cmd); - - // Setup for running simulations (timestep and integrator) - lammps_command(handle, "timestep 0.5"); - lammps_command(handle, "fix 1 all nve"); - - // Initialize - lammps_command(handle, "run 0"); - env->handle = handle; -} - -void compute_observations(Matsci* env) { - double** x = (double **) lammps_extract_atom(env->handle, "x"); - for (int i=0; inum_agents; i++) { - env->observations[3*i] = x[i][0] - env->goal.x; - env->observations[3*i + 1] = x[i][1] - env->goal.y; - env->observations[3*i + 2] = x[i][2] - env->goal.z; - } -} - -void reset_atom(Matsci* env, double** x, int i) { - x[i][0] = rndf(-10.0f, 10.0f); - x[i][1] = rndf(-10.0f, 10.0f); - x[i][2] = rndf(-10.0f, 10.0f); -} - -void c_reset(Matsci* env) { - void* handle = env->handle; - double** x = (double **) lammps_extract_atom(handle, "x"); - for (int i=0; inum_agents; i++) { - reset_atom(env, x, i); - } - env->goal.x = rndf(-10.0f, 10.0f); - env->goal.y = rndf(-10.0f, 10.0f); - env->goal.z = rndf(-10.0f, 10.0f); - env->tick = 0; -} - -void c_step(Matsci* env) { - void* handle = env->handle; - env->tick++; - - if (env->tick >= 1024) { - c_reset(env); - for (int i=0; inum_agents; i++) { - env->rewards[i] = -1; - env->terminals[i] = 1; - env->log.n += 1; - } - return; - } - - double **v = (double **) lammps_extract_atom(handle, "v"); - for (int i=0; inum_agents; i++) { - env->rewards[i] = 0; - env->terminals[i] = 0; - - v[i][0] = env->actions[3*i]; - v[i][1] = env->actions[3*i + 1]; - v[i][2] = env->actions[3*i + 2]; - } - - lammps_command(handle, "run 1"); - - double** x = (double **) lammps_extract_atom(handle, "x"); - for (int i=0; inum_agents; i++) { - Vec3 pos = (Vec3){x[i][0], x[i][1], x[i][2]}; - float dist = norm3(sub3(pos, env->goal)); - - if (dist > 20.0f) { - reset_atom(env, x, i); - env->rewards[i] = -1; - env->terminals[i] = 1; - env->log.n += 1; - } - - if (dist < 1.0f) { - reset_atom(env, x, i); - env->rewards[i] = 1; - env->terminals[i] = 1; - env->log.score += 1; - env->log.n += 1; - } - } - - compute_observations(env); -} - -static void update_camera_position(Client *c) { - float r = c->camera_distance; - float az = c->camera_azimuth; - float el = c->camera_elevation; - - float x = r * cosf(el) * cosf(az); - float y = r * cosf(el) * sinf(az); - float z = r * sinf(el); - - c->camera.position = (Vector3){x, y, z}; - c->camera.target = (Vector3){0, 0, 0}; -} - -void handle_camera_controls(Client *client) { - Vector2 mouse_pos = GetMousePosition(); - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - client->is_dragging = true; - client->last_mouse_pos = mouse_pos; - } - - if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - client->is_dragging = false; - } - - if (client->is_dragging && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - Vector2 mouse_delta = {mouse_pos.x - client->last_mouse_pos.x, - mouse_pos.y - client->last_mouse_pos.y}; - - float sensitivity = 0.005f; - - client->camera_azimuth -= mouse_delta.x * sensitivity; - - client->camera_elevation += mouse_delta.y * sensitivity; - client->camera_elevation = - clampf(client->camera_elevation, -PI / 2.0f + 0.1f, PI / 2.0f - 0.1f); - - client->last_mouse_pos = mouse_pos; - - update_camera_position(client); - } - - float wheel = GetMouseWheelMove(); - if (wheel != 0) { - client->camera_distance -= wheel * 2.0f; - client->camera_distance = clampf(client->camera_distance, 5.0f, 50.0f); - update_camera_position(client); - } -} - -void c_close(Matsci* env) { - /* - if (IsWindowReady()) { - CloseWindow(); - } - */ -} - -void c_render(Matsci* env) { - if (!IsWindowReady()) { - Client *client = (Client *)calloc(1, sizeof(Client)); - - client->width = WIDTH; - client->height = HEIGHT; - - SetConfigFlags(FLAG_MSAA_4X_HINT); // antialiasing - InitWindow(WIDTH, HEIGHT, "PufferLib DroneSwarm"); - - #ifndef __EMSCRIPTEN__ - SetTargetFPS(60); - #endif - - client->camera_distance = 40.0f; - client->camera_azimuth = 0.0f; - client->camera_elevation = PI / 10.0f; - client->is_dragging = false; - client->last_mouse_pos = (Vector2){0.0f, 0.0f}; - - client->camera.up = (Vector3){0.0f, 0.0f, 1.0f}; - client->camera.fovy = 45.0f; - client->camera.projection = CAMERA_PERSPECTIVE; - env->client = client; - - update_camera_position(client); - } - - if (WindowShouldClose()) { - c_close(env); - exit(0); - } - - if (IsKeyDown(KEY_ESCAPE)) { - - c_close(env); - exit(0); - } - - handle_camera_controls(env->client); - - Client *client = env->client; - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - BeginMode3D(client->camera); - DrawCubeWires((Vector3){0.0f, 0.0f, 0.0f}, 20.0f, 20.0f, 20.0f, WHITE); - - double** x = (double **) lammps_extract_atom(env->handle, "x"); - for (int i=0; inum_agents; i++) { - DrawSphere((Vector3){x[i][0], x[i][1], x[i][2]}, 0.1f, PUFF_CYAN); - } - - DrawSphere((Vector3){env->goal.x, env->goal.y, env->goal.z}, 0.1f, PUFF_RED); - EndMode3D(); - - DrawText("Left click + drag: Rotate camera", 10, 10, 16, PUFF_WHITE); - DrawText("Mouse wheel: Zoom in/out", 10, 30, 16, PUFF_WHITE); - - EndDrawing(); -} - - diff --git a/pufferlib/ocean/matsci/matsci.py b/pufferlib/ocean/matsci/matsci.py deleted file mode 100644 index eda5df1a4..000000000 --- a/pufferlib/ocean/matsci/matsci.py +++ /dev/null @@ -1,66 +0,0 @@ -'''A minimal matsci for your own envs.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.matsci import binding - -class Matsci(pufferlib.PufferEnv): - def __init__(self, num_envs=1, num_atoms=2, render_mode=None, log_interval=128, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(3,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Box( - low=-1, high=1, shape=(3,), dtype=np.float32 - ) - self.render_mode = render_mode - self.num_agents = num_envs*num_atoms - - super().__init__(buf) - c_envs = [] - for i in range(num_envs): - c_envs.append(binding.env_init( - self.observations[i*num_atoms:(i+1)*num_atoms], - self.actions[i*num_atoms:(i+1)*num_atoms], - self.rewards[i*num_atoms:(i+1)*num_atoms], - self.terminals[i*num_atoms:(i+1)*num_atoms], - self.truncations[i*num_atoms:(i+1)*num_atoms], - i, - num_agents=num_atoms, - )) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - info = [binding.vec_log(self.c_envs)] - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 4096 - env = Matsci(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, 5, (CACHE, N)) - - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[steps % CACHE]) - steps += 1 - - print('Squared SPS:', int(env.num_agents*steps / (time.time() - start))) diff --git a/pufferlib/ocean/memory/binding.c b/pufferlib/ocean/memory/binding.c deleted file mode 100644 index 4475895ef..000000000 --- a/pufferlib/ocean/memory/binding.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "memory.h" - -#define Env Memory -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->length = unpack(kwargs, "length"); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "score", log->score); - return 0; -} diff --git a/pufferlib/ocean/memory/memory.c b/pufferlib/ocean/memory/memory.c deleted file mode 100644 index dc1f3fea3..000000000 --- a/pufferlib/ocean/memory/memory.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "memory.h" - -int main() { - Memory env = {.length = 16}; - env.observations = (float*)calloc(1, sizeof(unsigned char)); - env.actions = (int*)calloc(1, sizeof(int)); - env.rewards = (float*)calloc(1, sizeof(float)); - env.terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); - - c_reset(&env); - c_render(&env); - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) { - env.actions[0] = 0; - } else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) { - env.actions[0] = 1; - } else { - env.actions[0] = -1; - } - } else { - env.actions[0] = rand() % 2; - } - c_step(&env); - c_render(&env); - } - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} - diff --git a/pufferlib/ocean/memory/memory.h b/pufferlib/ocean/memory/memory.h deleted file mode 100644 index 654ac61c3..000000000 --- a/pufferlib/ocean/memory/memory.h +++ /dev/null @@ -1,81 +0,0 @@ -#include -#include -#include "raylib.h" - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -// Only use floats! -typedef struct { - float score; - float n; // Required as the last field -} Log; - -typedef struct { - Log log; // Required field - float* observations; // Required field. Ensure type matches in .py and .c - int* actions; // Required field. Ensure type matches in .py and .c - float* rewards; // Required field - unsigned char* terminals; // Required field - int length; - int goal; - int tick; -} Memory; - -void c_reset(Memory* env) { - env->goal = (rand()%2 == 0) ? -1 : 1; - env->observations[0] = env->goal; - env->tick = 0; -} - -void c_step(Memory* env) { - env->rewards[0] = 0; - env->terminals[0] = 0; - env->observations[0] = 0; - env->tick++; - - if (env->tick < env->length) { - return; - } - - float val = 0.0f; - if (env->actions[0] == 0 && env->goal == -1) { - val = 1.0f; - } - if (env->actions[0] == 1 && env->goal == 1) { - val = 1.0f; - } - - c_reset(env); - env->rewards[0] = val; - env->terminals[0] = 1; - env->log.score += val; - env->log.n += 1; -} - -void c_render(Memory* env) { - if (!IsWindowReady()) { - InitWindow(960, 480, "PufferLib Memory"); - SetTargetFPS(5); - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - DrawRectangle(0, 0, 480, 480, (env->goal == -1 ? PUFF_CYAN : PUFF_RED)); - DrawRectangle(480, 0, 480, 480, (env->rewards[0] == 0 ? PUFF_RED: GREEN)); - DrawText(TextFormat("Tick %.0d. Simon says...", env->tick), 20, 20, 20, PUFF_WHITE); - - ClearBackground(PUFF_BACKGROUND); - EndDrawing(); -} - -void c_close(Memory* env) { - if (IsWindowReady()) { - CloseWindow(); - } -} diff --git a/pufferlib/ocean/memory/memory.py b/pufferlib/ocean/memory/memory.py deleted file mode 100644 index ca7786786..000000000 --- a/pufferlib/ocean/memory/memory.py +++ /dev/null @@ -1,53 +0,0 @@ -'''A minimal test env for memory (note: requires credit assignment too because RL)''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.memory import binding - -class Memory(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, log_interval=128, length=4, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(1,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(2) - self.render_mode = render_mode - self.num_agents = num_envs - - super().__init__(buf) - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, length=length) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - info = [binding.vec_log(self.c_envs)] - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 4096 - env = Memory(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, 5, (CACHE, N)) - - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[steps % CACHE]) - steps += 1 - - print('Squared SPS:', int(env.num_agents*steps / (time.time() - start))) diff --git a/pufferlib/ocean/moba/__init__.py b/pufferlib/ocean/moba/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pufferlib/ocean/moba/binding.c b/pufferlib/ocean/moba/binding.c deleted file mode 100644 index c48c5fc9e..000000000 --- a/pufferlib/ocean/moba/binding.c +++ /dev/null @@ -1,150 +0,0 @@ -#include "moba.h" - -#define Env MOBA -#define MY_SHARED -#include "../env_binding.h" - -static PyObject* my_shared(PyObject* self, PyObject* args, PyObject* kwargs) { - unsigned char* game_map_npy = read_file("resources/moba/game_map.npy"); - int* ai_path_buffer = calloc(3*8*128*128, sizeof(int)); - unsigned char* ai_paths = calloc(128*128*128*128, sizeof(unsigned char)); - for (int i = 0; i < 128*128*128*128; i++) { - ai_paths[i] = 255; - } - - PyObject* ai_path_buffer_handle = PyLong_FromVoidPtr(ai_path_buffer); - PyObject* ai_paths_handle = PyLong_FromVoidPtr(ai_paths); - PyObject* game_map_handle = PyLong_FromVoidPtr(game_map_npy); - PyObject* state = PyDict_New(); - PyDict_SetItemString(state, "ai_path_buffer", ai_path_buffer_handle); - PyDict_SetItemString(state, "ai_paths", ai_paths_handle); - PyDict_SetItemString(state, "game_map", game_map_handle); - return PyLong_FromVoidPtr(state); -} - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->vision_range = unpack(kwargs, "vision_range"); - env->agent_speed = unpack(kwargs, "agent_speed"); - env->discretize = unpack(kwargs, "discretize"); - env->reward_death = unpack(kwargs, "reward_death"); - env->reward_xp = unpack(kwargs, "reward_xp"); - env->reward_distance = unpack(kwargs, "reward_distance"); - env->reward_tower = unpack(kwargs, "reward_tower"); - env->script_opponents = unpack(kwargs, "script_opponents"); - - PyObject* handle_obj = PyDict_GetItemString(kwargs, "state"); - if (handle_obj == NULL) { - PyErr_SetString(PyExc_KeyError, "Key 'state' not found in kwargs"); - return 1; - } - - // Check if handle_obj is a PyLong - if (!PyLong_Check(handle_obj)) { - PyErr_SetString(PyExc_TypeError, "state handle must be an integer"); - return 1; - } - - // Convert PyLong to PyObject* (state dictionary) - PyObject* state_dict = (PyObject*)PyLong_AsVoidPtr(handle_obj); - if (state_dict == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid state dictionary pointer"); - return 1; - } - - // Verify it’s a dictionary - if (!PyDict_Check(state_dict)) { - PyErr_SetString(PyExc_TypeError, "State pointer does not point to a dictionary"); - return 1; - } - - // Basic validation: check reference count - if (state_dict->ob_refcnt <= 0) { - PyErr_SetString(PyExc_RuntimeError, "State dictionary has invalid reference count"); - return 1; - } - - // Extract ai_path_buffer - PyObject* ai_path_buffer_obj = PyDict_GetItemString(state_dict, "ai_path_buffer"); - if (ai_path_buffer_obj == NULL) { - PyErr_SetString(PyExc_KeyError, "Key 'ai_path_buffer' not found in state"); - return 1; - } - if (!PyLong_Check(ai_path_buffer_obj)) { - PyErr_SetString(PyExc_TypeError, "ai_path_buffer must be an integer"); - return 1; - } - env->ai_path_buffer = (int*)PyLong_AsVoidPtr(ai_path_buffer_obj); - if (env->ai_path_buffer == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid ai_path_buffer pointer"); - return 1; - } - - // Extract ai_paths - PyObject* ai_paths_obj = PyDict_GetItemString(state_dict, "ai_paths"); - if (ai_paths_obj == NULL) { - PyErr_SetString(PyExc_KeyError, "Key 'ai_paths' not found in state"); - return 1; - } - if (!PyLong_Check(ai_paths_obj)) { - PyErr_SetString(PyExc_TypeError, "ai_paths must be an integer"); - return 1; - } - env->ai_paths = (unsigned char*)PyLong_AsVoidPtr(ai_paths_obj); - if (env->ai_paths == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid ai_paths pointer"); - return 1; - } - - // Extract game_map - PyObject* game_map_obj = PyDict_GetItemString(state_dict, "game_map"); - if (game_map_obj == NULL) { - PyErr_SetString(PyExc_KeyError, "Key 'game_map' not found in state"); - return 1; - } - if (!PyLong_Check(game_map_obj)) { - PyErr_SetString(PyExc_TypeError, "game_map must be an integer"); - return 1; - } - unsigned char* game_map = (unsigned char*)PyLong_AsVoidPtr(game_map_obj); - if (game_map == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid game_map pointer"); - return 1; - } - - init_moba(env, game_map); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "radiant_victory", log->radiant_victory); - assign_to_dict(dict, "dire_victory", log->dire_victory); - assign_to_dict(dict, "radiant_level", log->radiant_level); - assign_to_dict(dict, "dire_level", log->dire_level); - assign_to_dict(dict, "radiant_towers_alive", log->radiant_towers_alive); - assign_to_dict(dict, "dire_towers_alive", log->dire_towers_alive); - - assign_to_dict(dict, "radiant_support_episode_return", log->radiant_support_episode_return); - assign_to_dict(dict, "radiant_support_reward_death", log->radiant_support_reward_death); - assign_to_dict(dict, "radiant_support_reward_xp", log->radiant_support_reward_xp); - assign_to_dict(dict, "radiant_support_reward_distance", log->radiant_support_reward_distance); - assign_to_dict(dict, "radiant_support_reward_tower", log->radiant_support_reward_tower); - assign_to_dict(dict, "radiant_support_level", log->radiant_support_level); - assign_to_dict(dict, "radiant_support_kills", log->radiant_support_kills); - assign_to_dict(dict, "radiant_support_deaths", log->radiant_support_deaths); - assign_to_dict(dict, "radiant_support_damage_dealt", log->radiant_support_damage_dealt); - assign_to_dict(dict, "radiant_support_damage_received", log->radiant_support_damage_received); - assign_to_dict(dict, "radiant_support_healing_dealt", log->radiant_support_healing_dealt); - assign_to_dict(dict, "radiant_support_healing_received", log->radiant_support_healing_received); - assign_to_dict(dict, "radiant_support_creeps_killed", log->radiant_support_creeps_killed); - assign_to_dict(dict, "radiant_support_neutrals_killed", log->radiant_support_neutrals_killed); - assign_to_dict(dict, "radiant_support_towers_killed", log->radiant_support_towers_killed); - assign_to_dict(dict, "radiant_support_usage_auto", log->radiant_support_usage_auto); - assign_to_dict(dict, "radiant_support_usage_q", log->radiant_support_usage_q); - assign_to_dict(dict, "radiant_support_usage_w", log->radiant_support_usage_w); - assign_to_dict(dict, "radiant_support_usage_e", log->radiant_support_usage_e); - return 0; -} diff --git a/pufferlib/ocean/moba/cy_moba.pyx b/pufferlib/ocean/moba/cy_moba.pyx deleted file mode 100644 index 51725d108..000000000 --- a/pufferlib/ocean/moba/cy_moba.pyx +++ /dev/null @@ -1,299 +0,0 @@ -from libc.stdlib cimport calloc, free -import numpy as np - -cdef extern from "moba.h": - int EMPTY - int WALL - int TOWER - int RADIANT_CREEP - int DIRE_CREEP - int NEUTRAL - int RADIANT_SUPPORT - int RADIANT_ASSASSIN - int RADIANT_BURST - int RADIANT_TANK - int RADIANT_CARRY - int DIRE_SUPPORT - int DIRE_ASSASSIN - int DIRE_BURST - int DIRE_TANK - int DIRE_CARRY - - int TOWER_VISION - int CREEP_VISION - int NEUTRAL_VISION - - int ENTITY_PLAYER - int ENTITY_CREEP - int ENTITY_NEUTRAL - int ENTITY_TOWER - - int XP_RANGE - int MAX_USES - - int LOG_BUFFER_SIZE - - ctypedef struct PlayerLog: - float episode_return - float reward_death - float reward_xp - float reward_distance - float reward_tower - float level - float kills - float deaths - float damage_dealt - float damage_received - float healing_dealt - float healing_received - float creeps_killed - float neutrals_killed - float towers_killed - float usage_auto - float usage_q - float usage_w - float usage_e - - ctypedef struct Log: - float perf - float score - float episode_return - float episode_length - float reward_death - float reward_xp - float reward_distance - float reward_tower - - float radiant_victory - float radiant_level - float radiant_towers_alive - - float dire_victory - float dire_level - float dire_towers_alive - - PlayerLog radiant_support - PlayerLog radiant_assassin - PlayerLog radiant_burst - PlayerLog radiant_tank - PlayerLog radiant_carry - - PlayerLog dire_support - PlayerLog dire_assassin - PlayerLog dire_burst - PlayerLog dire_tank - PlayerLog dire_carry - - ctypedef struct LogBuffer - LogBuffer* allocate_logbuffer(int) - void free_logbuffer(LogBuffer*) - Log aggregate_and_clear(LogBuffer*) - - ctypedef int (*skill)(MOBA*, Entity*, Entity*) noexcept - - ctypedef struct Map: - unsigned char* grid; - int* pids - int width - int height - - ctypedef struct CachedRNG: - float* rng - int rng_n - int rng_idx - - ctypedef struct Reward: - float death; - float xp; - float distance; - float tower; - - ctypedef struct Entity: - int pid; - int entity_type; - int hero_type; - int grid_id; - int team; - float health; - float max_health; - float mana; - float max_mana; - float y; - float x; - float spawn_y; - float spawn_x; - float damage; - int lane; - int waypoint; - float move_speed; - float move_modifier; - int stun_timer; - int move_timer; - int q_timer; - int w_timer; - int e_timer; - int basic_attack_timer; - int basic_attack_cd; - int is_hit; - int level; - int xp; - int xp_on_kill; - float reward; - int tier; - float base_health; - float base_mana; - float base_damage; - int hp_gain_per_level; - int mana_gain_per_level; - int damage_gain_per_level; - float last_x; - float last_y; - int target_pid; - int attack_aoe; - - ctypedef struct MOBA: - int vision_range; - float agent_speed; - unsigned char discretize; - unsigned char script_opponents; - int obs_size; - int creep_idx; - int tick; - - Map* map; - unsigned char* ai_paths; - int* ai_path_buffer; - unsigned char* observations; - int* actions; - float* rewards; - unsigned char* terminals; - unsigned char* truncations; - Entity* entities; - Reward* reward_components; - LogBuffer* log_buffer; - PlayerLog log[10]; - - float reward_death; - float reward_xp; - float reward_distance; - float reward_tower; - - int total_towers_taken; - int total_levels_gained; - int radiant_victories; - int dire_victories; - - # MAX_ENTITIES x MAX_SCANNED_TARGETS - Entity* scanned_targets[256][121]; - skill skills[10][3]; - - CachedRNG *rng; - - ctypedef struct GameRenderer - GameRenderer* init_game_renderer(int cell_size, int width, int height) - int render_game(GameRenderer* renderer, MOBA* env, int frame) - void close_game_renderer(GameRenderer* renderer) - - ctypedef struct Reward - void init_moba(MOBA* env, unsigned char* game_map_npy) - void free_moba(MOBA* env) - - unsigned char* read_file(char* filename) - - void c_reset(MOBA* env) - void c_step(MOBA* env) - void randomize_tower_hp(MOBA* env) - -cpdef entity_dtype(): - '''Make a dummy entity to get the dtype''' - cdef Entity entity - return np.asarray(&entity).dtype - -cpdef reward_dtype(): - '''Make a dummy reward to get the dtype''' - cdef Reward reward - return np.asarray(&reward).dtype - -cdef class CyMOBA: - cdef MOBA* envs - cdef GameRenderer* client - cdef int num_envs - cdef LogBuffer* logs - - cdef int* ai_path_buffer - cdef unsigned char* ai_paths - - def __init__(self, unsigned char[:, :] observations, int[:, :] actions, - float[:] rewards, unsigned char[:] terminals, int num_envs, int vision_range, - float agent_speed, bint discretize, float reward_death, float reward_xp, - float reward_distance, float reward_tower, bint script_opponents): - - self.num_envs = num_envs - self.client = NULL - self.envs = calloc(num_envs, sizeof(MOBA)) - self.logs = allocate_logbuffer(LOG_BUFFER_SIZE) - - import os - cwd = os.getcwd() - os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) - cdef unsigned char* game_map_npy = read_file("resources/moba/game_map.npy"); - os.chdir(cwd) - - self.ai_path_buffer = calloc(3*8*128*128, sizeof(int)) - self.ai_paths = calloc(128*128*128*128, sizeof(unsigned char)) - cdef int i - for i in range(128*128*128*128): - self.ai_paths[i] = 255 - - cdef int inc = 5 if script_opponents else 10 - for i in range(num_envs): - self.envs[i] = MOBA( - observations=&observations[inc*i, 0], - actions=&actions[inc*i, 0], - rewards=&rewards[inc*i], - terminals=&terminals[inc*i], - ai_paths = self.ai_paths, - ai_path_buffer = self.ai_path_buffer, - log_buffer=self.logs, - vision_range=vision_range, - agent_speed=agent_speed, - discretize=discretize, - reward_death=reward_death, - reward_xp=reward_xp, - reward_distance=reward_distance, - reward_tower=reward_tower, - script_opponents=script_opponents, - ) - init_moba(&self.envs[i], game_map_npy) - - def reset(self): - cdef int i - for i in range(self.num_envs): - c_reset(&self.envs[i]) - - def step(self): - cdef int i - for i in range(self.num_envs): - c_step(&self.envs[i]) - - def render(self, int tick): - if self.client == NULL: - import os - cwd = os.getcwd() - os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) - self.client = init_game_renderer(32, 41, 23) - os.chdir(cwd) - - render_game(self.client, &self.envs[0], tick) - - def close(self): - if self.client != NULL: - close_game_renderer(self.client) - self.client = NULL - - # TODO: free - #free_moba(self.envs) - - def log(self): - cdef Log log = aggregate_and_clear(self.logs) - return log diff --git a/pufferlib/ocean/moba/game_map.h b/pufferlib/ocean/moba/game_map.h deleted file mode 100644 index df4403922..000000000 --- a/pufferlib/ocean/moba/game_map.h +++ /dev/null @@ -1,1369 +0,0 @@ -unsigned char game_map_npy[] = { - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01 -}; -unsigned int game_map_npy_len = 16384; diff --git a/pufferlib/ocean/moba/moba.c b/pufferlib/ocean/moba/moba.c deleted file mode 100644 index 29b50cca7..000000000 --- a/pufferlib/ocean/moba/moba.c +++ /dev/null @@ -1,215 +0,0 @@ -#include "moba.h" -#include "puffernet.h" - -typedef struct MOBANet MOBANet; -struct MOBANet { - int num_agents; - float* obs_2d; - float* obs_1d; - Conv2D* conv1; - ReLU* relu1; - Conv2D* conv2; - Linear* flat; - CatDim1* cat; - ReLU* relu2; - Linear* proj; - ReLU* relu3; - LSTM* lstm; - Linear* actor; - Linear* value_fn; - Multidiscrete* multidiscrete; -}; - -MOBANet* init_mobanet(Weights* weights, int num_agents) { - MOBANet* net = calloc(1, sizeof(MOBANet)); - int hidden = 128; - net->num_agents = num_agents; - net->obs_2d = calloc(num_agents*11*11*19, sizeof(float)); - net->obs_1d = calloc(num_agents*26, sizeof(float)); - net->conv1 = make_conv2d(weights, num_agents, 11, 11, 19, hidden, 5, 3); - net->relu1 = make_relu(num_agents, hidden*3*3); - net->conv2 = make_conv2d(weights, num_agents, 3, 3, hidden, hidden, 3, 1); - net->flat = make_linear(weights, num_agents, 26, hidden); - net->cat = make_cat_dim1(num_agents, hidden, hidden); - net->relu2 = make_relu(num_agents, 2*hidden); - net->proj = make_linear(weights, num_agents, 2*hidden, 128); - net->relu3 = make_relu(num_agents, 128); - net->actor = make_linear(weights, num_agents, 128, 23); - net->value_fn = make_linear(weights, num_agents, 128, 1); - net->lstm = make_lstm(weights, num_agents, 128, 128); - int logit_sizes[6] = {7, 7, 3, 2, 2, 2}; - net->multidiscrete = make_multidiscrete(num_agents, logit_sizes, 6); - return net; -} - -void free_mobanet(MOBANet* net) { - free(net->obs_2d); - free(net->obs_1d); - free(net->conv1); - free(net->relu1); - free(net->conv2); - free(net->flat); - free(net->cat); - free(net->relu2); - free(net->proj); - free(net->relu3); - free(net->actor); - free(net->value_fn); - free(net->lstm); - free(net->multidiscrete); - free(net); -} - -void forward(MOBANet* net, unsigned char* observations, int* actions) { - memset(net->obs_2d, 0, net->num_agents*11*11*19*sizeof(float)); - float (*obs_2d)[19][11][11] = (float (*)[19][11][11])net->obs_2d; - float (*obs_1d)[26] = (float (*)[26])net->obs_1d; - for (int b = 0; b < net->num_agents; b++) { - int b_offset = b*(11*11*4 + 26); - for (int i = 0; i < 11; i++) { - for (int j = 0; j < 11; j++) { - int elem_offset = 4*(i*11 + j); - int one_hot_idx = observations[b_offset + elem_offset]; - obs_2d[b][one_hot_idx][i][j] = 1; - obs_2d[b][16][i][j] = observations[b_offset + elem_offset+1] / 255.0f; - obs_2d[b][17][i][j] = observations[b_offset + elem_offset+2] / 255.0f; - obs_2d[b][18][i][j] = observations[b_offset + elem_offset+3] / 255.0f; - } - } - for (int i = 0; i < 26; i++) { - obs_1d[b][i] = observations[b_offset + 11*11*4 + i] / 255.0f; - } - } - - conv2d(net->conv1, net->obs_2d); - relu(net->relu1, net->conv1->output); - conv2d(net->conv2, net->relu1->output); - - linear(net->flat, net->obs_1d); - - cat_dim1(net->cat, net->conv2->output, net->flat->output); - relu(net->relu2, net->cat->output); - linear(net->proj, net->relu2->output); - relu(net->relu3, net->proj->output); - - lstm(net->lstm, net->relu3->output); - - linear(net->actor, net->lstm->state_h); - linear(net->value_fn, net->lstm->state_h); - - softmax_multidiscrete(net->multidiscrete, net->actor->output, actions); - for (int i = 0; i < net->num_agents; i++) { - actions[i*6] = 100*(actions[i*6] - 3); - actions[i*6 + 1] = 100*(actions[i*6 + 1] - 3); - } -} - -void demo() { - Weights* weights = load_weights("resources/moba/moba_weights.bin", 380056); - bool script_opponents = true; - - int num_agents = script_opponents ? 5 : 10; - MOBANet* net = init_mobanet(weights, num_agents); - - MOBA env = { - .vision_range = 5, - .agent_speed = 1.0, - .discretize = true, - .reward_death = -1.0, - .reward_xp = 0.006, - .reward_distance = 0.05, - .reward_tower = 3.0, - .script_opponents = script_opponents, - }; - allocate_moba(&env); - c_reset(&env); - c_render(&env); - int frame = 1; - while (!WindowShouldClose()) { - if (frame % 12 == 0) { - c_step(&env); - forward(net, env.observations, env.actions); - } - c_render(&env); - frame = (frame + 1) % 12; - } - free_mobanet(net); - free(weights); - free_allocated_moba(&env); - //close_game_renderer(renderer); -} - -void test_performance(float test_time) { - bool script_opponents = true; - int num_agents = script_opponents ? 5 : 10; - - MOBA env = { - .vision_range = 5, - .agent_speed = 1.0, - .discretize = true, - .reward_death = -1.0, - .reward_xp = 0.006, - .reward_distance = 0.05, - .reward_tower = 3.0, - .script_opponents = script_opponents, - }; - allocate_moba(&env); - - c_reset(&env); - int start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - for (int j = 0; j < num_agents; j++) { - env.actions[6*j] = rand()%600 - 300; - env.actions[6*j + 1] = rand()%600 - 300; - env.actions[6*j + 2] = rand()%3; - env.actions[6*j + 3] = rand()%2; - env.actions[6*j + 4] = rand()%2; - env.actions[6*j + 5] = rand()%2; - } - c_step(&env); - i++; - } - int end = time(NULL); - printf("SPS: %f\n", (float)num_agents*i / (end - start)); -} - -void test_bugs(float test_time) { - Weights* weights = load_weights("resources/moba/moba_weights.bin", 380056); - MOBANet* net = init_mobanet(weights, 10); - - MOBA env = { - .vision_range = 5, - .agent_speed = 1.0, - .discretize = true, - .reward_death = -1.0, - .reward_xp = 0.006, - .reward_distance = 0.05, - .reward_tower = 3.0, - .script_opponents = true, - }; - allocate_moba(&env); - - c_reset(&env); - int start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - c_step(&env); - forward(net, env.observations, env.actions); - i++; - } - int end = time(NULL); - printf("SPS: %f\n", 10.0f*i / (end - start)); - printf("Frames: %i\n", i); - free_mobanet(net); - free(weights); - free_allocated_moba(&env); -} - - -int main() { - //test_bugs(2.0f); - demo(); - //test_performance(30.0f); - return 0; -} diff --git a/pufferlib/ocean/moba/moba.h b/pufferlib/ocean/moba/moba.h deleted file mode 100644 index bfe399f83..000000000 --- a/pufferlib/ocean/moba/moba.h +++ /dev/null @@ -1,2444 +0,0 @@ -// Incremental port of Puffer Moba to C. Be careful to add semicolons and avoid leftover cython syntax -#include -#include -#include -#include -#include -#include // xxd -i game_map.npy > game_map.h #include "game_map.h" - -#include "raylib.h" - -#if defined(PLATFORM_DESKTOP) - #define GLSL_VERSION 330 -#else - #define GLSL_VERSION 100 -#endif - - -#define NUM_PLAYERS 10 -#define NUM_CREEPS 100 -#define NUM_NEUTRALS 72 -#define NUM_TOWERS 24 -#define NUM_ENTITIES (NUM_PLAYERS + NUM_CREEPS + NUM_NEUTRALS + NUM_TOWERS) -#define CREEP_OFFSET NUM_PLAYERS -#define NEUTRAL_OFFSET (NUM_PLAYERS + NUM_CREEPS) -#define TOWER_OFFSET (NUM_PLAYERS + NUM_CREEPS + NUM_NEUTRALS) - -#define EMPTY 0 -#define WALL 1 -#define TOWER 2 -#define RADIANT_CREEP 3 -#define DIRE_CREEP 4 -#define NEUTRAL 5 -#define RADIANT_SUPPORT 6 -#define RADIANT_ASSASSIN 7 -#define RADIANT_BURST 8 -#define RADIANT_TANK 9 -#define RADIANT_CARRY 10 -#define DIRE_SUPPORT 11 -#define DIRE_ASSASSIN 12 -#define DIRE_BURST 13 -#define DIRE_TANK 14 -#define DIRE_CARRY 15 - -#define TOWER_VISION 5 -#define CREEP_VISION 7 -#define NEUTRAL_VISION 3 - -#define ENTITY_PLAYER 0 -#define ENTITY_CREEP 1 -#define ENTITY_NEUTRAL 2 -#define ENTITY_TOWER 3 - -#define XP_RANGE 7 - -const float XP_FOR_LEVEL[] = {0, 240, 640, 1160, 1760, 2440, 3200, 4000, 4900, 4900, 7000, 8200, 9500, 10900, 12400, 14000, 15700, 17500, 19400, 21400, 23600, 26000, 28600, 31400, 34400, 38400, 43400, 49400, 56400, 63900}; - -const float ATN_MAP[][8] = { - {1, -1, 0, 0, 1, -1, -1, 1}, - {0, 0, 1, -1, -1, -1, 1, 1} -}; - -const float NEUTRAL_CAMP_X[] = {44.753846153846155, 74.41538461538462, 101.67692307692307, 89.92307692307692, 73.95384615384616, 64.38461538461539, 31.657692307692308, 95.67692307692307, 81.1076923076923, 34.99230769230769, 50.784615384615385, 63.646153846153844, 59.49230769230769, 44.69230769230769, 98.67307692307692, 28.642307692307693, 64.87692307692308, 51.46153846153846}; -const float NEUTRAL_CAMP_Y[] = {71.99230769230769, 108.15384615384616, 102.16923076923078, 102.78461538461539, 40.753846153846155, 39.92307692307692, 39.96923076923077, 70.78461538461538, 69.18461538461538, 59.52307692307692, 99.95384615384614, 93.97692307692307, 49.86153846153846, 31.353846153846156, 61.06153846153846, 69.92307692307692, 83.83076923076923, 33.98461538461538}; -const float TOWER_DAMAGE[] = {175.0, 175.0, 175.0, 175.0, 110.0, 175.0, 175.0, 110.0, 175.0, 175.0, 175.0, 110.0, 175.0, 110.0, 175.0, 175.0, 175.0, 175.0, 175.0, 175.0, 110.0, 110.0, 0, 0}; -const float TOWER_HEALTH[] = {2000, 2000, 2000, 2000, 1800, 2000, 2000, 1800, 2100, 2100, 2000, 1800, 2000, 1800, 2000, 2000, 2000, 2000, 2100, 2100, 1800, 1800, 4500, 4500}; -const int TOWER_TEAM[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0}; -const int TOWER_TIER[] = {3, 3, 3, 2, 1, 2, 2, 1, 4, 4, 2, 1, 2, 1, 2, 3, 3, 3, 4, 4, 1, 1, 5, 5}; -const float TOWER_X[] = {34.6, 29.307692307692307, 14.292307692307695, 64.2, 102.87692307692308, 38.29230769230769, 17.615384615384613, 16.876923076923077, 21.06153846153846, 103.03076923076924, 65.0, 29.06153846153846, 84.2, 69.03076923076924, 112.75384615384615, 113.73846153846154, 92.32307692307693, 97.86153846153846, 105.61538461538461, 23.52307692307692, 53.12307692307692, 113.22307692307692, 107.52307692307693, 19.46153846153846}; -const float TOWER_Y[] = {112.01538461538462, 96.87692307692308, 91.21538461538461, 113.61538461538461, 112.13846153846154, 85.43076923076923, 71.70769230769231, 51.03076923076923, 102.41538461538462, 28.261538461538464, 18.723076923076924, 18.723076923076924, 48.753846153846155, 59.98461538461538, 62.04615384615384, 41.676923076923075, 20.56923076923077, 36.08461538461539, 30.907692307692308, 104.93846153846154, 75.83076923076923, 78.3, 26.53846153846154, 106.16923076923078}; -const float WAYPOINTS[][20][2] = {{{96.26153846153846, 14.04615384615385}, {93.61538461538461, 14.292307692307695}, {58.23076923076923, 16.07692307692308}, {43.4, 16.01538461538462}, {32.10769230769231, 17.584615384615383}, {24.93846153846154, 19.123076923076923}, {22.96923076923077, 20.446153846153848}, {21.06153846153846, 25.307692307692307}, {20.200000000000003, 30.47692307692308}, {19.4, 35.892307692307696}, {18.47692307692308, 43.52307692307693}, {18.04615384615385, 50.723076923076924}, {20.50769230769231, 94.66153846153847}, {27.676923076923075, 105.94615384615385}}, {{99.52307692307693, 26.47692307692308}, {98.66153846153847, 27.646153846153844}, {86.04615384615384, 41.12307692307692}, {71.33846153846154, 57.49230769230769}, {66.96923076923076, 62.23076923076923}, {64.07692307692308, 66.35384615384615}, {60.2, 72.01538461538462}, {51.892307692307696, 84.87692307692308}, {34.66153846153846, 99.4}, {28.169230769230772, 105.94615384615385}}, {{112.01538461538462, 36.93846153846154}, {112.01538461538462, 32.50769230769231}, {116.2, 68.6923076923077}, {113.73846153846154, 80.75384615384615}, {113.55384615384615, 90.53846153846155}, {113.21538461538461, 95.98461538461538}, {111.49230769230769, 100.93846153846154}, {109.06153846153846, 108.07692307692308}, {107.18461538461538, 111.36923076923077}, {101.73846153846154, 114.66153846153847}, {90.53846153846155, 111.76923076923077}, {39.4, 113.73846153846154}, {28.169230769230772, 106.43846153846154}}, {{20.446153846153848, 89.36923076923077}, {20.630769230769232, 94.66153846153847}, {18.784615384615385, 50.6}, {22.292307692307695, 24.50769230769231}, {22.815384615384616, 18.815384615384616}, {27.52307692307692, 17.03076923076923}, {35.03076923076923, 15.95384615384615}, {93.61538461538461, 14.415384615384617}, {103.64615384615385, 21.99230769230769}}, {{37.43076923076923, 96.50769230769231}, {34.53846153846154, 99.27692307692308}, {51.707692307692305, 84.38461538461539}, {59.83076923076923, 71.64615384615385}, {63.83076923076923, 66.1076923076923}, {66.72307692307692, 61.98461538461538}, {70.84615384615384, 57.246153846153845}, {98.53846153846155, 27.52307692307692}, {103.64615384615385, 22.48461538461538}}, {{36.93846153846154, 113.24615384615385}, {39.4, 113.61538461538461}, {62.292307692307695, 114.6}, {83.64615384615385, 114.6}, {90.66153846153847, 109.8}, {94.87692307692308, 112.01538461538462}, {104.26153846153846, 112.2923076923077}, {109.3076923076923, 107.83076923076922}, {112.23076923076923, 105.86153846153846}, {114.44615384615385, 72.96923076923076}, {111.8923076923077, 32.50769230769231}, {104.13846153846154, 22.48461538461538}}}; -const int WAYPOINTS_N[6] = {14, 10, 13, 9, 9, 12}; - -#define LOG_BUFFER_SIZE 1024 - - -typedef struct PlayerLog PlayerLog; -struct PlayerLog { - float episode_return; - float reward_death; - float reward_xp; - float reward_distance; - float reward_tower; - float level; - float kills; - float deaths; - float damage_dealt; - float damage_received; - float healing_dealt; - float healing_received; - float creeps_killed; - float neutrals_killed; - float towers_killed; - float usage_auto; - float usage_q; - float usage_w; - float usage_e; -}; - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float reward_death; - float reward_xp; - float reward_distance; - float reward_tower; - - float radiant_victory; - float radiant_level; - float radiant_towers_alive; - - float dire_victory; - float dire_level; - float dire_towers_alive; - - float radiant_support_episode_return; - float radiant_support_reward_death; - float radiant_support_reward_xp; - float radiant_support_reward_distance; - float radiant_support_reward_tower; - float radiant_support_level; - float radiant_support_kills; - float radiant_support_deaths; - float radiant_support_damage_dealt; - float radiant_support_damage_received; - float radiant_support_healing_dealt; - float radiant_support_healing_received; - float radiant_support_creeps_killed; - float radiant_support_neutrals_killed; - float radiant_support_towers_killed; - float radiant_support_usage_auto; - float radiant_support_usage_q; - float radiant_support_usage_w; - float radiant_support_usage_e; - - // TODO: Only ported global and radiant support logs for now - /* - PlayerLog radiant_support; - PlayerLog radiant_assassin; - PlayerLog radiant_burst; - PlayerLog radiant_tank; - PlayerLog radiant_carry; - - PlayerLog dire_support; - PlayerLog dire_assassin; - PlayerLog dire_burst; - PlayerLog dire_tank; - PlayerLog dire_carry; - */ - float n; -}; - - -typedef struct MOBA MOBA; -typedef struct GameRenderer GameRenderer; -typedef struct Entity Entity; -typedef int (*skill)(MOBA*, Entity*, Entity*); - -struct Entity { - int pid; - int entity_type; - int hero_type; - int grid_id; - int team; - float health; - float max_health; - float mana; - float max_mana; - float y; - float x; - float spawn_y; - float spawn_x; - float damage; - int lane; - int waypoint; - float move_speed; - float move_modifier; - int stun_timer; - int move_timer; - int q_timer; - int w_timer; - int e_timer; - int basic_attack_timer; - int basic_attack_cd; - int is_hit; - int level; - int xp; - int xp_on_kill; - float reward; - int tier; - float base_health; - float base_mana; - float base_damage; - int hp_gain_per_level; - int mana_gain_per_level; - int damage_gain_per_level; - float last_x; - float last_y; - int target_pid; - int attack_aoe; -}; - -typedef struct { - unsigned char* grid; - int* pids; - int width; - int height; -} Map; - -static inline int map_offset(Map* map, int y, int x) { - return y*map->width + x; -} - -static inline int ai_offset(int y_dst, int x_dst, int y_src, int x_src) { - return y_dst*128*128*128 + x_dst*128*128 + y_src*128 + x_src; -} - -typedef struct { - float death; - float xp; - float distance; - float tower; -} Reward; - -typedef struct { - float* rng; - int rng_n; - int rng_idx; -} CachedRNG; - -float fast_rng(CachedRNG* rng) { - float val = rng->rng[rng->rng_idx]; - rng->rng_idx += 1; - if (rng->rng_idx >= rng->rng_n - 1) - rng->rng_idx = 0; - return val; -} - -int bfs(Map *map, unsigned char *flat_paths, int* flat_buffer, int dest_r, int dest_c) { - int N = map->width; - unsigned char (*paths)[N] = (unsigned char(*)[N])flat_paths; - int (*buffer)[3] = (int(*)[3])flat_buffer; - - int start = 0; - int end = 1; - - int adr = map_offset(map, dest_r, dest_c); - if (map->grid[adr] == 1) { - return 1; - } - - buffer[start][0] = 0; - buffer[start][1] = dest_r; - buffer[start][2] = dest_c; - while (start < end) { - int atn = buffer[start][0]; - int start_r = buffer[start][1]; - int start_c = buffer[start][2]; - start++; - - if (start_r < 0 || start_r >= N || start_c < 0 || start_c >= N) { - continue; - } - if (paths[start_r][start_c] != 255) { - continue; - } - int adr = map_offset(map, start_r, start_c); - if (map->grid[adr] == 1) { - paths[start_r][start_c] = 8; - continue; - } - - paths[start_r][start_c] = atn; - - buffer[end][0] = 0; - buffer[end][1] = start_r - 1; - buffer[end][2] = start_c; - end++; - - buffer[end][0] = 1; - buffer[end][1] = start_r + 1; - buffer[end][2] = start_c; - end++; - - buffer[end][0] = 2; - buffer[end][1] = start_r; - buffer[end][2] = start_c - 1; - end++; - - buffer[end][0] = 3; - buffer[end][1] = start_r; - buffer[end][2] = start_c + 1; - end++; - - buffer[end][0] = 4; - buffer[end][1] = start_r - 1; - buffer[end][2] = start_c + 1; - end++; - - buffer[end][0] = 5; - buffer[end][1] = start_r + 1; - buffer[end][2] = start_c + 1; - end++; - - buffer[end][0] = 6; - buffer[end][1] = start_r + 1; - buffer[end][2] = start_c - 1; - end++; - - buffer[end][0] = 7; - buffer[end][1] = start_r - 1; - buffer[end][2] = start_c - 1; - end++; - } - paths[dest_r][dest_c] = 8; - return 0; -} - -unsigned char* precompute_pathing(Map* map){ - int N = map->width; - unsigned char* paths = calloc(N*N*N*N, 1); - int* buffer = calloc(3*8*N*N, sizeof(int)); - for (int r = 0; r < N; r++) { - for (int c = 0; c < N; c++) { - for (int rr = 0; rr < N; rr++) { - for (int cc = 0; cc < N; cc++) { - int adr = ai_offset(r, c, rr, cc); - paths[adr] = 255; - } - } - int adr = ai_offset(r, c, 0, 0); - bfs(map, &paths[adr], buffer, r, c); - } - } - return paths; -} - -struct MOBA { - GameRenderer* client; - int vision_range; - float agent_speed; - bool discretize; - bool script_opponents; - int obs_size; - int creep_idx; - int tick; - bool should_reset; - - Map* map; - unsigned char* orig_grid; - unsigned char* ai_paths; - int* ai_path_buffer; - unsigned char* observations; - int* actions; - float* rewards; - unsigned char* terminals; - unsigned char* truncations; - Entity* entities; - Reward* reward_components; - Log log; - PlayerLog player_logs[10]; - - float reward_death; - float reward_xp; - float reward_distance; - float reward_tower; - - int total_towers_taken; - int total_levels_gained; - int radiant_victories; - int dire_victories; - - // MAX_ENTITIES x MAX_SCANNED_TARGETS - Entity* scanned_targets[256][121]; - skill skills[10][3]; - - CachedRNG *rng; -}; - -void add_log(MOBA* env, int radiant_victory, int dire_victory) { - Log* log = &env->log; - log->n += 1; - log->score += radiant_victory; - log->perf += radiant_victory; - log->episode_length = env->tick; - log->radiant_victory = radiant_victory; - log->dire_victory = dire_victory; - for (int i = 0; i < 5; i++) { - log->radiant_level += env->entities[i].level / 5.0f; - } - for (int i = 5; i < 10; i++) { - log->dire_level += env->entities[i].level / 5.0f; - } - for (int i = 0; i < NUM_TOWERS; i++) { - Entity* tower = &env->entities[TOWER_OFFSET + i]; - if (TOWER_TEAM[i] == 0) { - log->radiant_towers_alive += tower->health > 0; - } else { - log->dire_towers_alive += tower->health > 0; - } - } - - PlayerLog* radiant_support = &env->player_logs[0]; - log->radiant_support_episode_return = radiant_support->episode_return; - log->radiant_support_reward_death = radiant_support->reward_death; - log->radiant_support_reward_xp = radiant_support->reward_xp; - log->radiant_support_reward_distance = radiant_support->reward_distance; - log->radiant_support_reward_tower = radiant_support->reward_tower; - log->radiant_support_level = radiant_support->level; - log->radiant_support_kills = radiant_support->kills; - log->radiant_support_deaths = radiant_support->deaths; - log->radiant_support_damage_dealt = radiant_support->damage_dealt; - log->radiant_support_damage_received = radiant_support->damage_received; - log->radiant_support_healing_dealt = radiant_support->healing_dealt; - log->radiant_support_healing_received = radiant_support->healing_received; - log->radiant_support_creeps_killed = radiant_support->creeps_killed; - log->radiant_support_neutrals_killed = radiant_support->neutrals_killed; - log->radiant_support_towers_killed = radiant_support->towers_killed; - log->radiant_support_usage_auto = radiant_support->usage_auto; - log->radiant_support_usage_q = radiant_support->usage_q; - log->radiant_support_usage_w = radiant_support->usage_w; - log->radiant_support_usage_e = radiant_support->usage_e; -} - -void c_close(MOBA* env) { - free(env->reward_components); - free(env->map->grid); - free(env->map); - free(env->orig_grid); - free(env->rng->rng); - free(env->rng); -} - -void free_allocated_moba(MOBA* env) { - free(env->rewards); - free(env->map->pids); - free(env->ai_path_buffer); - free(env->ai_paths); - free(env->observations); - free(env->actions); - free(env->terminals); - free(env->truncations); - free(env->entities); - c_close(env); -} - -void compute_observations(MOBA* env) { - int agents = (env->script_opponents) ? NUM_PLAYERS/2 : NUM_PLAYERS; - memset(env->observations, 0, agents*(11*11*4 + 26)*sizeof(unsigned char)); - - int vis = env->vision_range; - Map* map = env->map; - - unsigned char (*observations)[11*11*4 + 26] = (unsigned char(*)[11*11*4 + 26])env->observations; - for (int pid = 0; pid < agents; pid++) { - // Does this copy? - unsigned char* obs_map = &observations[pid][0]; - unsigned char* obs_extra = &observations[pid][11*11*4]; - - Entity* player = &env->entities[pid]; - Reward* reward = &env->reward_components[pid]; - - int y = player->y; - int x = player->x; - - // TODO: Add bounds debug checks asserts - obs_extra[0] = 2*x; - obs_extra[1] = 2*y; - obs_extra[2] = 255*player->level/30.0; - obs_extra[3] = 255*player->health/player->max_health; - obs_extra[4] = 255*player->mana/player->max_mana; - obs_extra[5] = player->damage / 4.0; - obs_extra[6] = 100*player->move_speed; - obs_extra[7] = player->move_modifier*100; - obs_extra[8] = 2*player->stun_timer; - obs_extra[9] = 2*player->move_timer; - obs_extra[10] = 2*player->q_timer; - obs_extra[11] = 2*player->w_timer; - obs_extra[12] = 2*player->e_timer; - obs_extra[13] = 50*player->basic_attack_timer; - obs_extra[14] = 50*player->basic_attack_cd; - obs_extra[15] = 255*player->is_hit; - obs_extra[16] = 255*player->team; - obs_extra[17 + player->hero_type] = 255; - - // Assumes scaled between -1 and 1, else overflows - obs_extra[22] = (reward->death == 0) ? 0 : 255; - obs_extra[23] = (reward->xp == 0) ? 0 : 255; - obs_extra[24] = (reward->distance == 0) ? 0 : 255; - obs_extra[25] = (reward->tower == 0) ? 0 : 255; - - for (int dy = -vis; dy <= vis; dy++) { - for (int dx = -vis; dx <= vis; dx++) { - int xx = x + dx; - int yy = y + dy; - int ob_x = dx + vis; - int ob_y = dy + vis; - - int map_idx = ob_y*11 + ob_x; - - int adr = map_offset(map, yy, xx); - int tile = map->grid[adr]; - obs_map[map_idx] = tile; - if (tile > 15) { - printf("Invalid map value: %i at %i, %i\n", map->grid[adr], yy, xx); - } - int target_pid = env->map->pids[adr]; - if (target_pid == -1) - continue; - - Entity* target = &env->entities[target_pid]; - obs_map[map_idx+1] = 255*target->health/target->max_health; - if (target->max_mana > 0) { // Towers do not have mana - obs_map[map_idx+2] = 255*target->mana/target->max_mana; - } - obs_map[map_idx+3] = target->level/30.0; - } - } - } -} - -static inline int xp_for_player_kill(Entity* entity) { - return 100 + (int)(entity->xp / 7.69); -} - -static inline float clip(float x) { - return fmaxf(-1.0f, fminf(x, 1.0f)); -} - -static inline float l1_distance(float x1, float y1, float x2, float y2) { - return fabs(x1 - x2) + fabs(y1 - y2); -} - -// TODO: Should not take entire moba. Rename to min_greater_than or similar -int calc_level(MOBA* env, int xp) { - int i; - for (i = 0; i < 30; i++) { - if (xp < XP_FOR_LEVEL[i]) - return i + 1; - } - return i; -} - -Reward* get_reward(MOBA* env, int pid) { - return &env->reward_components[pid]; -} - -int move_to(Map* map, Entity* player, float dest_y, float dest_x) { - int src = map_offset(map, (int)player->y, (int)player->x); - int dst = map_offset(map, (int)dest_y, (int)dest_x); - - if (map->grid[dst] != EMPTY && map->pids[dst] != player->pid) - return 1; - - /* - int idest_y = dest_y; - int idest_x = dest_x; - if (idest_y == 106 && idest_x == 19 && player->pid != 205) { - printf("Moving player pid %i to %i, %i\n", player->pid, idest_y, idest_x); - exit(0); - } - */ - - map->grid[src] = EMPTY; - map->grid[dst] = player->grid_id; - - map->pids[src] = -1; - map->pids[dst] = player->pid; - - player->y = dest_y; - player->x = dest_x; - return 0; -} - -int move_near(Map* map, Entity* entity, Entity* target) { - for (int dy = -1; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++) { - if (move_to(map, entity, target->y + dy, target->x + dx) == 0) - return 0; - } - } - return 1; -} - -int move_towards(MOBA* env, Entity* entity, int y_dst, int x_dst, float speed) { - int y_src = entity->y; - int x_src = entity->x; - - int adr = ai_offset(y_dst, x_dst, y_src, x_src); - int atn = env->ai_paths[adr]; - - // Compute path if not cached - if (atn == 255) { - int bfs_adr = ai_offset(y_dst, x_dst, 0, 0); - bfs(env->map, &env->ai_paths[bfs_adr], env->ai_path_buffer, y_dst, x_dst); - atn = env->ai_paths[adr]; - } - - if (atn >= 8) - return 0; - - float modifier = speed * entity->move_modifier; - y_dst = y_src + modifier*ATN_MAP[0][atn]; - x_dst = x_src + modifier*ATN_MAP[1][atn]; - - if (move_to(env->map, entity, y_dst, x_dst) == 0) - return 0; - - float jitter_x = fast_rng(env->rng); - float jitter_y = fast_rng(env->rng); - return move_to(env->map, entity, entity->y + jitter_y, entity->x + jitter_x); -} - -void kill_entity(Map* map, Entity* entity) { - if (entity->pid == -1) { - return; - } - int adr = map_offset(map, (int)entity->y, (int)entity->x); - map->grid[adr] = EMPTY; - map->pids[adr] = -1; - entity->pid = -1; - entity->target_pid = -1; - entity->health = 0; - entity->last_x = 0; - entity->last_y = 0; - entity->x = 0; - entity->y = 0; -} - -void spawn_player(Map* map, Entity* entity) { - int pid = entity->pid; - kill_entity(map, entity); - entity->pid = pid; - - entity->max_health = entity->base_health + entity->level*entity->hp_gain_per_level; - entity->max_mana = entity->base_mana + entity->level*entity->mana_gain_per_level; - entity->damage = entity->base_damage + entity->level*entity->damage_gain_per_level; - - entity->health = entity->max_health; - entity->mana = entity->max_mana; - entity->basic_attack_timer = 0; - entity->move_modifier = 0; - entity->move_timer = 0; - entity->stun_timer = 0; - entity->q_timer = 0; - entity->w_timer = 0; - entity->e_timer = 0; - entity->target_pid = -1; - entity->waypoint = 1; - - // TODO: Cache noise? - // Also.. technically can infinite loop? - bool valid_pos = false; - int y, x; - while (!valid_pos) { - y = entity->spawn_y + rand()%15 - 7; - x = entity->spawn_x + rand()%15 - 7; - valid_pos = map->grid[map_offset(map, y, x)] == EMPTY; - } - entity->last_x = x; - entity->last_y = y; - int err = move_to(map, entity, y, x); - if (err) { - printf("Failed to move entity %i to %i, %i\n", entity->pid, y, x); - exit(0); - } -} - -int attack(MOBA* env, Entity* player, Entity* target, float damage) { - if (target->pid == -1 || target->team == player->team) - return 1; - - float dist_to_target = l1_distance(player->y, player->x, target->y, target->x); - if (dist_to_target > 12) { - return 1; - //printf("Attacker %i at %f, %f, target %i at %f, %f, dist %f\n", player->pid, player->y, player->x, target->pid, target->y, target->x, dist_to_target); - } - - // Dummy logs for non-player entities - // TODO: Improve this design - PlayerLog empty_log = {0}; - PlayerLog* player_log = &empty_log; - if (player->entity_type == ENTITY_PLAYER) { - player_log = &env->player_logs[player->pid]; - } - PlayerLog* target_log = &empty_log; - if (target->entity_type == ENTITY_PLAYER) { - target_log = &env->player_logs[target->pid]; - } - - if (damage < target->health) { - player_log->damage_dealt += damage; - target_log->damage_received += damage; - target->health -= damage; - player->target_pid = target->pid; - target->is_hit = 1; - return 0; - } - - player_log->damage_dealt += target->health; - target_log->damage_received += target->health; - target->health = 0; - - if (target->pid == 205) { - //printf("Killed the radiant ancient\n"); - env->should_reset = true; - } - - int target_type = target->entity_type; - if (target_type == ENTITY_PLAYER) { - env->reward_components[target->pid].death = env->reward_death; - target_log->reward_death = env->reward_death; - player_log->kills += 1; - target_log->deaths += 1; - spawn_player(env->map, target); - } else if (target_type == ENTITY_CREEP) { - player_log->creeps_killed += 1; - kill_entity(env->map, target); - } else if (target_type == ENTITY_NEUTRAL) { - player_log->neutrals_killed += 1; - kill_entity(env->map, target); - } else if (target_type == ENTITY_TOWER) { - player_log->towers_killed += 1; - kill_entity(env->map, target); - } - - if (player->entity_type != ENTITY_PLAYER) - return 0; - - int xp = 0; - if (target_type == ENTITY_PLAYER) - xp = xp_for_player_kill(target); - else - xp = target->xp_on_kill; - - // Share xp with allies in range - // TODO: You can replace this array with a pointer array - int first_player_on_team = (player->team == 0) ? 0 : 5; - bool in_range[5] = {false, false, false, false, false}; - float target_x = target->x; - float target_y = target->y; - int num_in_range = 0; - for (int i = 0; i < 5; i++) { - Entity* ally = &env->entities[first_player_on_team + i]; - if (ally->pid == player->pid) { - in_range[i] = true; - num_in_range += 1; - continue; - } - - if (l1_distance(ally->y, ally->x, target_y, target_x) <= XP_RANGE) { - in_range[i] = true; - num_in_range += 1; - } - } - - xp /= num_in_range; - - for (int i = 0; i < 5; i++) { - if (!in_range[i]) - continue; - - Entity* ally = &env->entities[first_player_on_team + i]; - if (ally->xp > 10000000) - continue; - - ally->xp += xp; - env->reward_components[first_player_on_team + i].xp = xp*env->reward_xp; - - int level = ally->level; - ally->level = calc_level(env, ally->xp); - if (ally->level > level) { - PlayerLog* ally_log = &env->player_logs[ally->pid]; - ally_log->level = ally->level; - env->total_levels_gained += 1; - } - - ally->max_health = ally->base_health + ally->level*ally->hp_gain_per_level; - ally->max_mana = ally->base_mana + ally->level*ally->mana_gain_per_level; - ally->damage = ally->base_damage + ally->level*ally->damage_gain_per_level; - } - - if (target->entity_type == ENTITY_TOWER) { - env->reward_components[player->pid].tower = env->reward_tower; - env->total_towers_taken += 1; - } - return 0; -} - -int basic_attack(MOBA* env, Entity* player, Entity* target) { - if (player->basic_attack_timer > 0) - return 1; - - player->basic_attack_timer = player->basic_attack_cd; - return attack(env, player, target, player->damage); -} - -int heal(MOBA* env, Entity* player, Entity* target, float amount) { - if (target->pid == -1 || target->team != player->team) - return 1; - - // Currently only allowed to heal players - if (target->entity_type != ENTITY_PLAYER) - return 1; - - PlayerLog* player_log = &env->player_logs[player->pid]; - PlayerLog* target_log = &env->player_logs[target->pid]; - int missing_health = target->max_health - target->health; - if (amount <= missing_health) { - target->health += amount; - player_log->healing_dealt += amount; - target_log->healing_received += amount; - return 0; - } - - target->health = target->max_health; - player_log->healing_dealt += missing_health; - target_log->healing_received += missing_health; - return 0; -} - -int spawn_creep(MOBA* env, int idx, int lane) { - int pid = CREEP_OFFSET + idx; - Entity* creep = &env->entities[pid]; - - if (lane < 3) { - creep->team = 0; - creep->grid_id = RADIANT_CREEP; - } else { - creep->team = 1; - creep->grid_id = DIRE_CREEP; - } - creep->pid = pid; - creep->entity_type = ENTITY_CREEP; - creep->health = 450; - creep->max_health = 450; - creep->lane = lane; - creep->waypoint = 0; - creep->xp_on_kill = 60; - creep->damage = 22; - creep->basic_attack_cd = 5; - - int spawn_y = WAYPOINTS[lane][0][0]; - int spawn_x = WAYPOINTS[lane][0][1]; - - Map* map = env->map; - int y, x; - for (int i = 0; i < 10; i++) { - y = spawn_y + rand() % 7 - 3; - x = spawn_x + rand() % 7 - 3; - int adr = map_offset(map, y, x); - if (map->grid[adr] == EMPTY) { - break; - } - } - creep->health = creep->max_health; - creep->target_pid = -1; - creep->waypoint = 1; - creep->last_x = x; - creep->last_y = y; - return move_to(env->map, creep, y, x); -} - -int spawn_neutral(MOBA* env, int idx) { - int pid = NEUTRAL_OFFSET + idx; - Entity* neutral = &env->entities[pid]; - neutral->pid = pid; - neutral->health = neutral->max_health; - neutral->basic_attack_timer = 0; - neutral->target_pid = -1; - - // TODO: Clean up spawn regions. Some might be offset and are obscured. - // Maybe check all valid spawn spots? - int y, x; - Map* map = env->map; - int spawn_y = (int)neutral->spawn_y; - int spawn_x = (int)neutral->spawn_x; - for (int i = 0; i < 100; i++) { - y = spawn_y + rand() % 7 - 3; - x = spawn_x + rand() % 7 - 3; - int adr = map_offset(map, y, x); - if (map->grid[adr] == EMPTY) { - break; - } - } - neutral->last_x = x; - neutral->last_y = y; - return move_to(env->map, neutral, y, x); -} - -// TODO: Rework spawn system -int spawn_at(Map* map, Entity* entity, float y, float x) { - int adr = map_offset(map, (int)y, (int)x); - - if (map->grid[adr] != EMPTY) - return 1; - - map->grid[adr] = entity->grid_id; - map->pids[adr] = entity->pid; - entity->y = y; - entity->x = x; - return 0; -} - -int scan_aoe(MOBA* env, Entity* player, int radius, - bool exclude_friendly, bool exclude_hostile, bool exclude_creeps, - bool exclude_neutrals, bool exclude_towers) { - - Map* map = env->map; - int player_y = player->y; - int player_x = player->x; - int player_team = player->team; - int pid = player->pid; - int idx = 0; - - for (int y = player_y-radius; y <= player_y+radius; y++) { - for (int x = player_x-radius; x <= player_x+radius; x++) { - if (y < 0 || y >= 128 || x < 0 || x >= 128) - continue; - - int adr = map_offset(map, y, x); - int target_pid = map->pids[adr]; - if (target_pid == -1) - continue; - - Entity* target = &env->entities[target_pid]; - - int target_team = target->team; - if (exclude_friendly && target_team == player_team) - continue; - if (exclude_hostile && target_team != player_team) - continue; - - int target_type = target->entity_type; - if (exclude_neutrals && target_type == ENTITY_NEUTRAL) - continue; - if (exclude_creeps && target_type == ENTITY_CREEP) - continue; - if (exclude_towers && target_type == ENTITY_TOWER) - continue; - - /*if (player->pid >= 5 && player->entity_type == ENTITY_PLAYER && target_type == ENTITY_TOWER && target->tier==5) { - printf("Player %i at %f, %f scanned tower %i at %f, %f\n", player->pid, player->y, player->x, target->pid, target->y, target->x); - } - */ - - env->scanned_targets[pid][idx] = target; - float dist_to_target = l1_distance(player->y, player->x, target->y, target->x); - if (dist_to_target > 20) { - printf("Invalid target at %f, %f\n", target->y, target->x); - printf("player x: %f, y: %f, target x: %f, y: %f, dist: %f\n", player->x, player->y, target->x, target->y, dist_to_target); - printf("player pid: %i, target pid: %i\n", player->pid, target->pid); - printf("Tick: %i\n", env->tick); - exit(0); - } - idx += 1; - } - } - env->scanned_targets[pid][idx] = NULL; - return (idx == 0) ? 1 : 0; -} - -Entity* nearest_scanned_target(MOBA* env, Entity* player){ - Entity* nearest_target = NULL; - float nearest_dist = 9999999; - float player_y = player->y; - float player_x = player->x; - int pid = player->pid; - - // TODO: Clean up - for (int idx = 0; idx < 121; idx++) { - Entity* target = env->scanned_targets[pid][idx]; - if (target == NULL) - break; - - float dist = l1_distance(player_y, player_x, target->y, target->x); - if (dist < nearest_dist) { - nearest_target = target; - nearest_dist = dist; - } - } - return nearest_target; -} - -void aoe_scanned(MOBA* env, Entity* player, Entity* target, float damage, int stun) { - int pid = player->pid; - for (int idx = 0; idx < 121; idx++) { - Entity* target = env->scanned_targets[pid][idx]; - - if (target == NULL) - break; - - // Negative damage is healing - if (damage < 0) { - heal(env, player, target, -damage); - continue; - } - - attack(env, player, target, damage); - if (stun > 0) - target->stun_timer = stun; - } -} - -int player_aoe_attack(MOBA* env, Entity* player, - Entity* target, int radius, float damage, int stun) { - bool exclude_hostile = damage < 0; - bool exclude_friendly = !exclude_hostile; - - int err = scan_aoe(env, player, radius, exclude_friendly, - exclude_hostile, false, false, false); - - if (err != 0) - return 1; - - aoe_scanned(env, player, target, damage, stun); - player->target_pid = target->pid; - player->attack_aoe = radius; - return 0; -} - -int push(MOBA* env, Entity* player, Entity* target, float amount) { - float dx = target->x - player->x; - float dy = target->y - player->y; - float dist = fabs(dx) + fabs(dy); - - if (dist == 0.0) - return 1; - - // Norm to unit vector - dx = amount * dx / dist; - dy = amount * dy / dist; - return move_to(env->map, target, target->y + dy, target->x + dx); -} - -int pull(MOBA* env, Entity* player, Entity* target, float amount) { - return push(env, player, target, -amount); -} - -int aoe_pull(MOBA* env, Entity* player, int radius, float amount) { - scan_aoe(env, player, radius, true, false, false, false, true); - int err = 1; - int pid = player->pid; - for (int idx = 0; idx < 121; idx++) { - Entity* target = env->scanned_targets[pid][idx]; - if (target == NULL) - break; - - pull(env, target, player, amount); - err = 0; - } - return err; -} - -void creep_ai(MOBA* env, Entity* creep) { - int waypoint = creep->waypoint; - int lane = creep->lane; - int pid = creep->pid; - - if (env->tick % 5 == 0) - scan_aoe(env, creep, CREEP_VISION, true, false, false, true, false); - - if (env->scanned_targets[pid][0] != NULL) { - Entity* target = nearest_scanned_target(env, creep); - float dest_y = target->y; - float dest_x = target->x; - float dist = l1_distance(creep->y, creep->x, dest_y, dest_x); - if (dist < 2) - basic_attack(env, creep, target); - - move_towards(env, creep, dest_y, dest_x, env->agent_speed); - } else { - float dest_y = WAYPOINTS[lane][waypoint][0]; - float dest_x = WAYPOINTS[lane][waypoint][1]; - move_towards(env, creep, dest_y, dest_x, env->agent_speed); - - float dist = l1_distance(creep->y, creep->x, dest_y, dest_x); - if (dist < 2 && creep->waypoint < WAYPOINTS_N[lane] - 1) - creep->waypoint += 1; - //if (creep->waypoint == WAYPOINTS_N[lane] - 1) - // printf("Creep %i at %f, %f, waypoint %i\n", creep->pid, creep->y, creep->x, creep->waypoint); - } -} - -void neutral_ai(MOBA* env, Entity* neutral) { - if (env->tick % 5 == 0) { - scan_aoe(env, neutral, NEUTRAL_VISION, true, false, true, true, true); - } - - int pid = neutral->pid; - if (env->scanned_targets[pid][0] != NULL) { - Entity* target = nearest_scanned_target(env, neutral); - if (l1_distance(neutral->y, neutral->x, target->y, target->x) < 2) - basic_attack(env, neutral, target); - else - move_towards(env, neutral, target->y, target->x, env->agent_speed); - - } else if (l1_distance(neutral->y, neutral->x, - neutral->spawn_y, neutral->spawn_x) > 2) { - move_towards(env, neutral, neutral->spawn_y, neutral->spawn_x, env->agent_speed); - } -} - -void randomize_tower_hp(MOBA* env) { - for (int i = 0; i < NUM_TOWERS; i++) { - int pid = TOWER_OFFSET + i; - Entity* tower = &env->entities[pid]; - tower->health = rand() % (int)tower->max_health + 1; - } -} - -void update_status(Entity* entity) { - if (entity->stun_timer > 0) - entity->stun_timer -= 1; - - if (entity->move_timer > 0) - entity->move_timer -= 1; - - if (entity->move_timer == 0) - entity->move_modifier = 1.0; -} - -void update_cooldowns(Entity* entity) { - if (entity->q_timer > 0) - entity->q_timer -= 1; - - if (entity->w_timer > 0) - entity->w_timer -= 1; - - if (entity->e_timer > 0) - entity->e_timer -= 1; - - if (entity->basic_attack_timer > 0) - entity->basic_attack_timer -= 1; -} - -// TODO: Fix -int skill_support_hook(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 100; - if (target == NULL || player->mana < mana_cost) - return 1; - - pull(env, target, player, 1.5 + 0.1*player->level); - player->mana -= mana_cost; - player->q_timer = 15; - return 0; -} - -int skill_support_aoe_heal(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 100; - if (player->mana < mana_cost) - return 1; - - if (player_aoe_attack(env, player, player, 5, -350 - 50*player->level, 0) == 0) { - player->mana -= mana_cost; - player->w_timer = 50; - return 0; - } - return 1; -} - -int skill_support_stun(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 75; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (attack(env, player, target, 50 + 20*player->level) == 0) { - target->stun_timer = 15 + 0.5*player->level; - player->mana -= mana_cost; - player->e_timer = 60; - return 0; - } - return 1; -} - -int skill_assassin_aoe_minions(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 100; - if (target == NULL || player->mana < mana_cost) - return 1; - - int target_type = target->entity_type; - if (target_type != ENTITY_CREEP && target_type != ENTITY_NEUTRAL) - return 1; - - if (player_aoe_attack(env, player, target, 3, 100 + 20*player->level, 0) == 0) { - player->mana -= mana_cost; - player->q_timer = 40; - return 0; - } - return 1; -} - -int skill_assassin_tp_damage(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 150; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (move_near(env->map, player, target) != 0) { - return 1; - } - - player->mana -= mana_cost; - if (attack(env, player, target, 250+50*player->level) == 0) { - player->w_timer = 60; - return 0; - } - return 1; -} - -int skill_assassin_move_buff(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 100; - if (player->mana < mana_cost) - return 1; - - player->move_modifier = 2.0; - player->move_timer = 25; - player->mana -= mana_cost; - player->e_timer = 100; - return 0; -} - -int skill_burst_nuke(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 200; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (attack(env, player, target, 250 + 40*player->level) == 0) { - player->mana -= mana_cost; - player->q_timer = 70; - return 0; - } - return 1; -} - -int skill_burst_aoe(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 100; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (player_aoe_attack(env, player, target, 2, 100 + 40*player->level, 0) == 0) { - player->mana -= mana_cost; - player->w_timer = 40; - return 0; - } - return 1; -} - -int skill_burst_aoe_stun(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 75; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (player_aoe_attack(env, player, target, 2, 0, 10 + 0.5*player->level) == 0) { - player->mana -= mana_cost; - player->e_timer = 50; - return 0; - } - return 1; -} - -int skill_tank_aoe_dot(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 5; - if (player->mana < mana_cost) - return 1; - - if (player_aoe_attack(env, player, player, 2, 25 + 2.0*player->level, 0) == 0) { - player->mana -= mana_cost; - return 0; - } - return 1; -} - -// TODO: Fix -int skill_tank_self_heal(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 100; - if (player->mana < mana_cost) - return 1; - - if (heal(env, player, player, 400 + 125*player->level) == 0) { - player->mana -= mana_cost; - player->w_timer = 70; - return 0; - } - return 1; -} - -//Engages but doesnt push -int skill_tank_engage_aoe(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 50; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (move_near(env->map, player, target) == 0) { - player->mana -= mana_cost; - player->e_timer = 40; - aoe_pull(env, player, 4, 2.0 + 0.1*player->level); - return 0; - } - return 1; -} - -int skill_carry_retreat_slow(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 25; - if (target == NULL || player->mana < mana_cost) - return 1; - - int err = 1; - for (int i = 0; i < 3; i++) { - if (target == NULL || player->mana < mana_cost) - return err; - - if (push(env, target, player, 3 + 0.1*player->level) == 0) { - target->move_timer = 15; - target->move_modifier = 0.5; - player->mana -= mana_cost; - player->q_timer = 40; - err = 0; - } - } - return err; -} - -int skill_carry_slow_damage(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 150; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (attack(env, player, target, 50 + 20*player->level) == 0) { - target->move_timer = 20 + player->level; - target->move_modifier = 0.5; - player->mana -= mana_cost; - player->w_timer = 40; - return 0; - } - return 1; -} - -int skill_carry_aoe(MOBA* env, Entity* player, Entity* target) { - int mana_cost = 100; - if (target == NULL || player->mana < mana_cost) - return 1; - - if (player_aoe_attack(env, player, target, 2, 100 + 20*player->level, 0) == 0) { - player->mana -= mana_cost; - player->e_timer = 40; - return 0; - } - return 1; -} - -void step_creeps(MOBA* env) { - // Spawn wave - if (env->tick % 150 == 0) { - for (int lane = 0; lane < 6; lane++) { - for (int i = 0; i < 5; i++) { - int creep_pid = CREEP_OFFSET + env->creep_idx; - kill_entity(env->map, &env->entities[creep_pid]); - spawn_creep(env, env->creep_idx, lane); - env->creep_idx = (env->creep_idx + 1) % NUM_CREEPS; - } - } - } - for (int idx = 0; idx < NUM_CREEPS; idx++) { - int pid = CREEP_OFFSET + idx; - Entity* creep = &env->entities[pid]; - if (creep->pid == -1) - continue; - - update_status(creep); - update_cooldowns(creep); - if (creep->stun_timer > 0) - continue; - - creep_ai(env, creep); - } -} - -void step_neutrals(MOBA* env) { - if (env->tick % 600 == 0) { - for (int camp = 0; camp < 18; camp++) { - for (int neut = 0; neut < 4; neut++) { - spawn_neutral(env, 4*camp + neut); - } - } - } - for (int idx = 0; idx < NUM_NEUTRALS; idx++) { - int pid = NEUTRAL_OFFSET + idx; - Entity* neutral = &env->entities[pid]; - if (neutral->pid == -1) - continue; - - update_status(neutral); - update_cooldowns(neutral); - if (neutral->stun_timer > 0) - continue; - - neutral_ai(env, neutral); - } -} - -void step_towers(MOBA* env) { - for (int idx = 0; idx < NUM_TOWERS; idx++) { - int pid = TOWER_OFFSET + idx; - Entity* tower = &env->entities[pid]; - if (tower->pid == -1) - continue; - - /* - if (tower->health < tower->max_health) - tower->health += 10; - if (tower->health > tower->max_health) - tower->health = tower->max_health; - */ - - - update_cooldowns(tower); - if (tower->basic_attack_timer > 0) - continue; - - if (env->tick % 3 == 0) { // Is this fast enough? - scan_aoe(env, tower, TOWER_VISION, true, false, false, true, true); - if (env->scanned_targets[tower->pid][0] != NULL) { - float distance_to_first_scanned = l1_distance(tower->y, tower->x, env->scanned_targets[tower->pid][0]->y, env->scanned_targets[tower->pid][0]->x); - if (distance_to_first_scanned > 12) { - printf("Tower %i at %f, %f, target %i at %f, %f, dist %f\n", tower->pid, tower->y, tower->x, env->scanned_targets[tower->pid][0]->pid, env->scanned_targets[tower->pid][0]->y, env->scanned_targets[tower->pid][0]->x, distance_to_first_scanned); - } - } - - } - - Entity* target = nearest_scanned_target(env, tower); - if (target != NULL) - basic_attack(env, tower, target); - } -} - -void step_players(MOBA* env) { - // Clear rewards - for (int pid = 0; pid < NUM_PLAYERS; pid++) { - Reward* reward = &env->reward_components[pid]; - reward->death = 0; - reward->xp = 0; - reward->distance = 0; - reward->tower = 0; - } - - for (int pid = 0; pid < NUM_PLAYERS; pid++) { - Entity* player = &env->entities[pid]; - PlayerLog* log = &env->player_logs[pid]; - Reward* reward = &env->reward_components[pid]; - // TODO: Is this needed? - //if (rand() % 1024 == 0) - // spawn_player(env->map, player); - - if (player->mana < player->max_mana) - player->mana += 2; - if (player->mana > player->max_mana) - player->mana = player->max_mana; - if (player->health < player->max_health) - player->health += 2; - if (player->health > player->max_health) - player->health = player->max_health; - - update_status(player); - update_cooldowns(player); - - if (player->stun_timer > 0) - continue; - - if (env->script_opponents && pid >= 5) { - creep_ai(env, player); - /* - if (env->scanned_targets[pid][0] != NULL) { - Entity* target = nearest_scanned_target(env, player); - float dest_y = target->y; - float dest_x = target->x; - float dist = l1_distance(player->y, player->x, dest_y, dest_x); - if (dist < 5) { - if (player->q_timer <= 0 && env->skills[pid][0](env, player, target) == 0) { - log->usage_q += 1; - } else if (player->w_timer <= 0 && env->skills[pid][1](env, player, target) == 0) { - log->usage_w += 1; - } else if (player->e_timer <= 0 && env->skills[pid][2](env, player, target) == 0) { - log->usage_e += 1; - } - } - } - */ - } else { - int (*actions)[6] = (int(*)[6])env->actions; - //float vel_y = (actions[pid][0] > 0) ? 1 : -1; - //float vel_x = (actions[pid][1] > 0) ? 1 : -1; - float vel_y = actions[pid][0] / 300.0f; - float vel_x = actions[pid][1] / 300.0f; - float mag = sqrtf(vel_y*vel_y + vel_x*vel_x); - if (mag > 1) { - vel_y /= mag; - vel_x /= mag; - } - - int attack_target = actions[pid][2]; - bool use_q = actions[pid][3]; - bool use_w = actions[pid][4]; - bool use_e = actions[pid][5]; - - if (attack_target == 1 || attack_target == 0) { - // Scan everything - scan_aoe(env, player, env->vision_range, true, false, false, false, false); - } else if (attack_target == 2) { - // Scan only heros and towers - scan_aoe(env, player, env->vision_range, true, false, true, true, false); - } - - Entity* target = NULL; - // TODO: What is this logic here? - if (env->scanned_targets[pid][0] != NULL) - target = nearest_scanned_target(env, player); - - // TODO: Clean this mess - if (use_q && player->q_timer <= 0 && env->skills[pid][0](env, player, target) == 0) { - log->usage_q += 1; - } else if (use_w && player->w_timer <= 0 && env->skills[pid][1](env, player, target) == 0) { - log->usage_w += 1; - } else if (use_e && player->e_timer <= 0 && env->skills[pid][2](env, player, target) == 0) { - log->usage_e += 1; - } else if (target != NULL && basic_attack(env, player, target)==0) { - log->usage_auto += 1; - } - - float dest_y = player->y + player->move_modifier*env->agent_speed*vel_y; - float dest_x = player->x + player->move_modifier*env->agent_speed*vel_x; - move_to(env->map, player, dest_y, dest_x); - } - - float sum_reward = ( - reward->death + - reward->xp + - reward->distance + - reward->tower - ); - - env->player_logs[pid].reward_death = reward->death; - env->player_logs[pid].reward_xp = reward->xp; - env->player_logs[pid].reward_distance = reward->distance; - env->player_logs[pid].reward_tower = reward->tower; - env->player_logs[pid].episode_return += sum_reward; - - if (!env->script_opponents || pid < 5) { - env->rewards[pid] = sum_reward; - } - } -} - -unsigned char* read_file(char* filename) { - FILE* file; - unsigned char* array; - file = fopen(filename, "rb"); - if (file == NULL) { - perror("Error opening file"); - return NULL; - } - fseek(file, 0, SEEK_END); - size_t file_bytes = ftell(file); - fseek(file, 0, SEEK_SET); - array = calloc(file_bytes, sizeof(unsigned char)); - if (array == NULL) { - perror("Memory allocation failed"); - fclose(file); - return NULL; - } - size_t num_read = fread(array, 1, file_bytes, file); - if (num_read != file_bytes) { - perror("Error reading file"); - free(array); - fclose(file); - return NULL; - } - return array; -} - -void init_moba(MOBA* env, unsigned char* game_map_npy) { - env->obs_size = 2*env->vision_range + 1; - env->creep_idx = 0; - env->total_towers_taken = 0; - env->total_levels_gained = 0; - env->radiant_victories = 0; - env->dire_victories = 0; - - env->entities = calloc(NUM_ENTITIES, sizeof(Entity)); - env->reward_components = calloc(NUM_PLAYERS, sizeof(Reward)); - - env->map = (Map*)calloc(1, sizeof(Map)); - env->map->grid = calloc(128*128, sizeof(unsigned char)); - env->orig_grid = calloc(128*128, sizeof(unsigned char)); - memcpy(env->map->grid, game_map_npy, 128*128); - memcpy(env->orig_grid, game_map_npy, 128*128); - if (env->map->grid == NULL) { - printf("Failed to load game map\n"); - exit(1); - } - env->map->width = 128; - env->map->height = 128; - env->map->pids = calloc(128*128, sizeof(int)); - for (int i = 0; i < 128*128; i++) - env->map->pids[i] = -1; - - // Zero out scanned targets - for (int i = 0; i < 256; i++) { - env->scanned_targets[i][0] = NULL; - env->scanned_targets[i][1] = NULL; - } - - env->rng = (CachedRNG*)calloc(1, sizeof(CachedRNG)); - env->rng->rng_n = 10000; - env->rng->rng_idx = 0; - env->rng->rng = calloc(env->rng->rng_n, sizeof(float)); - for (int i = 0; i < env->rng->rng_n; i++) - env->rng->rng[i] = -1+2*((float)rand())/(float)RAND_MAX; - - // Initialize Players - Entity *player; - for (int team = 0; team < 2; team++) { - int spawn_y, spawn_x; - if (team == 0) { - spawn_y = 128 - 15; - spawn_x = 12; - } else { - spawn_y = 15; - spawn_x = 128 - 12; - } - - for (int pid = team*5; pid < team*5 + 5; pid++) { - player = &env->entities[pid]; - player->pid = pid; - player->entity_type = ENTITY_PLAYER; - player->team = team; - player->spawn_y = spawn_y; - player->spawn_x = spawn_x; - player->x = 0; - player->y = 0; - player->move_speed = env->agent_speed; - player->basic_attack_cd = 8; - player->base_damage = 50; - player->last_x = 0; - player->last_y = 0; - } - - int pid = 5*team; - player = &env->entities[pid]; - player->pid = pid; - player->entity_type = ENTITY_PLAYER; - player->grid_id = RADIANT_SUPPORT + team*5; - player->hero_type = 0; - player->lane = 2 + 3*team; - env->skills[pid][0] = skill_support_hook; - env->skills[pid][1] = skill_support_aoe_heal; - env->skills[pid][2] = skill_support_stun; - player->base_health = 500; - player->base_mana = 250; - player->hp_gain_per_level = 100; - player->mana_gain_per_level = 50; - player->damage_gain_per_level = 10; - player->lane = 2 + 3*team; - - pid = 5*team + 1; - player = &env->entities[pid]; - player->pid = pid; - player->entity_type = ENTITY_PLAYER; - player->grid_id = RADIANT_ASSASSIN + team*5; - player->hero_type = 1; - player->lane = 2 + 3*team; - env->skills[pid][0] = skill_assassin_aoe_minions; - env->skills[pid][1] = skill_assassin_tp_damage; - env->skills[pid][2] = skill_assassin_move_buff; - player->base_health = 400; - player->base_mana = 300; - player->hp_gain_per_level = 100; - player->mana_gain_per_level = 65; - player->damage_gain_per_level = 10; - player->lane = 1 + 3*team; - - pid = 5*team + 2; - player = &env->entities[pid]; - player->pid = pid; - player->entity_type = ENTITY_PLAYER; - player->grid_id = RADIANT_BURST + team*5; - player->hero_type = 2; - player->lane = 1 + 3*team; - env->skills[pid][0] = skill_burst_nuke; - env->skills[pid][1] = skill_burst_aoe; - env->skills[pid][2] = skill_burst_aoe_stun; - player->base_health = 400; - player->base_mana = 300; - player->hp_gain_per_level = 75; - player->mana_gain_per_level = 90; - player->damage_gain_per_level = 10; - player->lane = 1 + 3*team; - - pid = 5*team + 3; - player = &env->entities[pid]; - player->pid = pid; - player->entity_type = ENTITY_PLAYER; - player->grid_id = RADIANT_TANK + team*5; - player->hero_type = 3; - player->lane = 3*team; - env->skills[pid][0] = skill_tank_aoe_dot; - env->skills[pid][1] = skill_tank_self_heal; - env->skills[pid][2] = skill_tank_engage_aoe; - player->base_health = 700; - player->base_mana = 200; - player->hp_gain_per_level = 150; - player->mana_gain_per_level = 50; - player->damage_gain_per_level = 15; - player->lane = 3*team; - - pid = 5*team + 4; - player = &env->entities[pid]; - player->pid = pid; - player->entity_type = ENTITY_PLAYER; - player->grid_id = RADIANT_CARRY + team*5; - player->hero_type = 4; - player->lane = 2 + 3*team; - env->skills[pid][0] = skill_carry_retreat_slow; - env->skills[pid][1] = skill_carry_slow_damage; - env->skills[pid][2] = skill_carry_aoe; - player->base_health = 300; - player->base_mana = 250; - player->hp_gain_per_level = 50; - player->mana_gain_per_level = 50; - player->damage_gain_per_level = 25; - player->lane = 2 + 3*team; - } - - Entity *tower; - for (int idx = 0; idx < NUM_TOWERS; idx++) { - int pid = TOWER_OFFSET + idx; - tower = &env->entities[pid]; - tower->pid = pid; - tower->entity_type = ENTITY_TOWER; - tower->grid_id = TOWER; - tower->basic_attack_cd = 5; - tower->team = TOWER_TEAM[idx]; - tower->spawn_y = TOWER_Y[idx]; - tower->spawn_x = TOWER_X[idx]; - tower->x = 0; - tower->y = 0; - tower->max_health = TOWER_HEALTH[idx]; - tower->damage = TOWER_DAMAGE[idx]; - tower->tier = TOWER_TIER[idx]; - tower->xp_on_kill = 0;//800 * tower->tier; - } - - int idx = 0; - Entity* neutral; - for (int camp = 0; camp < NUM_NEUTRALS/4; camp++) { - // 4 neutrals per camp - for (int i = 0; i < 4; i++) { - int pid = NEUTRAL_OFFSET + idx; - neutral = &env->entities[pid]; - // TODO: Consider initializing pid for start of game - neutral->entity_type = ENTITY_NEUTRAL; - neutral->grid_id = NEUTRAL; - neutral->max_health = 500; - neutral->team = 2; - neutral->spawn_y = NEUTRAL_CAMP_Y[camp]; - neutral->spawn_x = NEUTRAL_CAMP_X[camp]; - neutral->x = 0; - neutral->y = 0; - neutral->xp_on_kill = 35; - neutral->basic_attack_cd = 5; - neutral->damage = 22; - idx++; - } - } - - Entity* creep; - for (int i = 0; i < NUM_CREEPS; i++) { - creep = &env->entities[CREEP_OFFSET + i]; - creep->pid = -1; - creep->x = 0; - creep->y = 0; - } -} - -MOBA* allocate_moba(MOBA* env) { - // TODO: Don't hardcode sizes - int agents = (env->script_opponents) ? NUM_PLAYERS/2 : NUM_PLAYERS; - env->observations = calloc(agents*(11*11*4 + 26), sizeof(unsigned char)); - env->actions = calloc(agents*6, sizeof(int)); - env->rewards = calloc(agents, sizeof(float)); - env->terminals = calloc(agents, sizeof(unsigned char)); - env->truncations = calloc(agents, sizeof(unsigned char)); - - unsigned char* game_map_npy = read_file("resources/moba/game_map.npy"); - env->ai_path_buffer = calloc(3*8*128*128, sizeof(int)); - env->ai_paths = calloc(128*128*128*128, sizeof(unsigned char)); - for (int i = 0; i < 128*128*128*128; i++) { - env->ai_paths[i] = 255; - } - - init_moba(env, game_map_npy); - free(game_map_npy); - return env; -} - -void c_reset(MOBA* env) { - //map->pids[:] = -1 - //randomize_tower_hp(env); - - env->tick = 0; - - // Reset scanned targets - for (int i = 0; i < NUM_ENTITIES; i++) { - Entity* entity = &env->entities[i]; - entity->target_pid = -1; - env->scanned_targets[i][0] = NULL; - kill_entity(env->map, entity); - } - - Map* map = env->map; - memcpy(map->grid, env->orig_grid, 128*128); - for (int i = 0; i < 128*128; i++) { - map->pids[i] = -1; - } - - // Respawn towers - for (int idx = 0; idx < NUM_TOWERS; idx++) { - int pid = TOWER_OFFSET + idx; - Entity* tower = &env->entities[pid]; - tower->target_pid = -1; - tower->pid = pid; - tower->health = tower->max_health; - tower->basic_attack_timer = 0; - tower->x = 0; - tower->y = 0; - int adr = map_offset(map, tower->spawn_y, tower->spawn_x); - map->grid[adr] = EMPTY; - int success = move_to(env->map, tower, tower->spawn_y, tower->spawn_x); - if (success == 1) { - printf("Failed to move tower %i to %f, %f\n", pid, tower->spawn_y, tower->spawn_x); - printf("Currently occupied by %i\n", map->pids[adr]); - exit(1); - } - tower->last_x = tower->x; - tower->last_y = tower->y; - } - - Entity* rad = &env->entities[205]; - int adr = map_offset(env->map, rad->y, rad->x); - if (rad->health > 0 && env->map->pids[adr] != 205) { - printf("glitch state\n"); - } - - // Respawn agents - for (int i = 0; i < NUM_PLAYERS; i++) { - env->player_logs[i] = (PlayerLog){0}; - Entity* player = &env->entities[i]; - player->pid = i; - player->target_pid = -1; - player->xp = 0; - player->level = 1; - //player->x = 0; - //player->y = 0; - spawn_player(env->map, player); - } - - rad = &env->entities[205]; - adr = map_offset(env->map, rad->y, rad->x); - if (rad->health > 0 && env->map->pids[adr] != 205) { - printf("glitch state\n"); - } - - compute_observations(env); -} - -void c_step(MOBA* env) { - for (int pid = 0; pid < NUM_ENTITIES; pid++) { - Entity* entity = &env->entities[pid]; - entity->target_pid = -1; - entity->attack_aoe = 0; - entity->last_x = entity->x; - entity->last_y = entity->y; - entity->is_hit = 0; - } - - step_neutrals(env); - step_creeps(env); - step_towers(env); - step_players(env); - env->tick += 1; - - Entity* rad = &env->entities[205]; - int adr = map_offset(env->map, rad->y, rad->x); - if (rad->health > 0 && env->map->pids[adr] != 205) { - printf("glitch state\n"); - } - - int radiant_pid = TOWER_OFFSET + 23; - int dire_pid = TOWER_OFFSET + 22; - - bool do_reset = false; - float radiant_victory = 0; - float dire_victory = 0; - Entity* ancient = &env->entities[dire_pid]; - if (ancient->health <= 0 || ancient->pid == -1) { - do_reset = true; - radiant_victory = 1; - /* - printf("Radiant victory\n"); - //Print tower states - for (int i = 0; i < NUM_TOWERS; i++) { - Entity* tower = &env->entities[TOWER_OFFSET + i]; - printf("Tower %i (%i): %f, %f, %f, %i\n", i, tower->pid, tower->health, tower->y, tower->x, tower->team); - int y = tower->y; - int x = tower->x; - int adr = map_offset(env->map, y, x); - int tile = env->map->grid[adr]; - int pid = env->map->pids[adr]; - printf("\ttile %i, pid %i\n", tile, pid); - } - //Print levels and pos - for (int i = 0; i < NUM_PLAYERS; i++) { - Entity* player = &env->entities[i]; - printf("Player %i (%i): %i, %f, %f, %i\n", i, player->pid, player->level, player->y, player->x, player->team); - int y = player->y; - int x = player->x; - int adr = map_offset(env->map, y, x); - int tile = env->map->grid[adr]; - int pid = env->map->pids[adr]; - printf("\ttile %i, pid %i\n", tile, pid); - } - printf("Tick %i", env->tick); - exit(0); - */ - } - - ancient = &env->entities[radiant_pid]; - if (ancient->health <= 0 || ancient->pid == -1) { - do_reset = true; - dire_victory = 1; - } - - if (!do_reset && env->should_reset) { - printf("We should have reset but didn't\n"); - printf("Radiant pid: %i\n", radiant_pid); - Entity* radiant = &env->entities[radiant_pid]; - printf("Radiant %i: %f, %f, %f\n", radiant->pid, radiant->y, radiant->x, radiant->health); - exit(0); - } - - if (do_reset) { - env->should_reset = false; - add_log(env, radiant_victory, dire_victory); - c_reset(env); - } - compute_observations(env); -} - -// Raylib client -Color COLORS[] = { - (Color){6, 24, 24, 255}, // Empty - (Color){0, 178, 178, 255}, // Wall - (Color){255, 165, 0, 255}, // Tower - (Color){0, 0, 128, 255}, // Radiant Creep - (Color){128, 0, 0, 255}, // Dire Creep - (Color){128, 128, 128, 255}, // Neutral - (Color){0, 0, 255, 255}, // Radiant Support - (Color){0, 0, 255, 255}, // Radiant Assassin - (Color){0, 0, 255, 255}, // Radiant Burst - (Color){0, 0, 255, 255}, // Radiant Tank - (Color){0, 0, 255, 255}, // Radiant Carry - (Color){255, 0, 0, 255}, // Dire Support - (Color){255, 0, 0, 255}, // Dire Assassin - (Color){255, 0, 0, 255}, // Dire Burst - (Color){255, 0, 0, 255}, // Dire Tank - (Color){255, 0, 0, 255}, // Dire Carry -}; - -// High-level map overview -typedef struct { - int cell_size; - int width; - int height; -} MapRenderer; - -MapRenderer* init_map_renderer(int cell_size, int width, int height) { - MapRenderer* renderer = (MapRenderer*)malloc(sizeof(MapRenderer)); - renderer->cell_size = cell_size; - renderer->width = width; - renderer->height = height; - InitWindow(width*cell_size, height*cell_size, "Puffer MOBA"); - SetTargetFPS(10); - return renderer; -} - -void close_map_renderer(MapRenderer* renderer) { - CloseWindow(); - free(renderer); -} - -void render_map(MapRenderer* renderer, MOBA* env) { - BeginDrawing(); - ClearBackground(COLORS[0]); - int sz = renderer->cell_size; - for (int y = 0; y < renderer->height; y++) { - for (int x = 0; x < renderer->width; x++){ - int adr = map_offset(env->map, y, x); - int tile = env->map->grid[adr]; - if (tile != EMPTY) - DrawRectangle(x*sz, y*sz, sz, sz, COLORS[tile]); - } - } - DrawText("Reinforcement learned MOBA agents running in your browswer!", 10, 10, 20, COLORS[8]); - DrawText("Written in pure C by @jsuarez5341. Star it on GitHub/pufferai/pufferlib to support my work!", 10, 40, 20, COLORS[8]); - EndDrawing(); -} - -// Player client view -struct GameRenderer { - int frame; - int cell_size; - int width; - int height; - Camera2D camera; - Rectangle asset_map[16]; - Rectangle stun_uv; - Rectangle slow_uv; - Rectangle speed_uv; - Texture2D game_map; - Texture2D puffer; - Image shader_background; - Texture2D shader_canvas; - Shader shader; - float shader_x; - float shader_y; - double shader_start_seconds; - float shader_seconds; - int shader_resolution_loc; - float shader_resolution[3]; - Shader bloom_shader; - float shader_camera_x; - float shader_camera_y; - float shader_time; - int shader_texture1; - float last_click_x; - float last_click_y; - int render_entities[128*128]; - int human_player; -}; - -GameRenderer* init_game_renderer(int cell_size, int width, int height) { - GameRenderer* renderer = (GameRenderer*)calloc(1, sizeof(GameRenderer)); - renderer->cell_size = cell_size; - renderer->width = width; - renderer->height = height; - - InitWindow(width*cell_size, height*cell_size, "Puffer MOBA"); - SetTargetFPS(60); - - Rectangle asset_map[] = { - (Rectangle){0, 0, 0, 0}, - (Rectangle){0, 0, 0, 0}, - (Rectangle){384, 384, 128, 128}, - (Rectangle){384, 0, 128, 128}, - (Rectangle){256, 0, 128, 128}, - (Rectangle){384, 128, 128, 128}, - (Rectangle){256, 256, 128, 128}, - (Rectangle){384, 256, 128, 128}, - (Rectangle){128, 256, 128, 128}, - (Rectangle){0, 256, 128, 128}, - (Rectangle){0, 384, 128, 128}, - (Rectangle){256, 256, 128, 128}, - (Rectangle){384, 256, 128, 128}, - (Rectangle){128, 256, 128, 128}, - (Rectangle){0, 256, 128, 128}, - (Rectangle){0, 384, 128, 128}, - }; - memcpy(renderer->asset_map, asset_map, sizeof(asset_map)); - - renderer->stun_uv = (Rectangle){0, 128, 128, 128}; - renderer->slow_uv = (Rectangle){128, 128, 128, 128}; - renderer->speed_uv = (Rectangle){256, 128, 128, 128}; - - renderer->game_map = LoadTexture("resources/moba/dota_map.png"); - renderer->puffer = LoadTexture("resources/moba/moba_assets.png"); - renderer->shader_background = GenImageColor(2560, 1440, (Color){0, 0, 0, 255}); - renderer->shader_canvas = LoadTextureFromImage(renderer->shader_background); - renderer->shader = LoadShader(0, TextFormat("resources/moba/map_shader_%i.fs", GLSL_VERSION)); - renderer->bloom_shader = LoadShader(0, TextFormat("resources/moba/bloom_shader_%i.fs", GLSL_VERSION)); - - // TODO: These should be int locs? - renderer->shader_camera_x = GetShaderLocation(renderer->shader, "camera_x"); - renderer->shader_camera_y = GetShaderLocation(renderer->shader, "camera_y"); - renderer->shader_time = GetShaderLocation(renderer->shader, "time"); - renderer->shader_texture1 = GetShaderLocation(renderer->shader, "texture1"); - renderer->shader_resolution_loc = GetShaderLocation(renderer->shader, "resolution"); - struct timespec time_spec; - clock_gettime(CLOCK_REALTIME, &time_spec); - renderer->shader_start_seconds = time_spec.tv_sec; - - renderer->camera = (Camera2D){0}; - renderer->camera.target = (Vector2){0.0, 0.0}; - // TODO: Init this? - //renderer->camera.offset = (Vector2){GetScreenWidth()/2.0f, GetScreenHeight()/2.0f}; - renderer->camera.rotation = 0.0f; - renderer->camera.zoom = 1.0f; - - renderer->human_player = 1; - - // Init last clicks - renderer->last_click_x = -1; - renderer->last_click_y = -1; - return renderer; -} - -//def render(self, grid, pids, entities, obs_players, actions, discretize, frames): -#define FRAMES 12 - -void draw_bars(Entity* entity, int x, int y, int width, int height, bool draw_text) { - float health_bar = entity->health / entity->max_health; - float mana_bar = entity->mana / entity->max_mana; - if (entity->max_health == 0) { - health_bar = 2; - } - if (entity->max_mana == 0) { - mana_bar = 2; - } - DrawRectangle(x, y, width, height, RED); - DrawRectangle(x, y, width*health_bar, height, GREEN); - - if (entity->entity_type == 0) { - DrawRectangle(x, y - height - 2, width, height, RED); - DrawRectangle(x, y - height - 2, width*mana_bar, height, (Color){0, 255, 255, 255}); - } - - Color color = (entity->team == 0) ? (Color){0, 255, 255, 255} : (Color){255, 0, 0, 255}; - if (draw_text) { - int health = entity->health; - int mana = entity->mana; - int max_health = entity->max_health; - int max_mana = entity->max_mana; - DrawText(TextFormat("Health: %i/%i", health, max_health), x+8, y+2, 20, (Color){255, 255, 255, 255}); - DrawText(TextFormat("Mana: %i/%i", mana, max_mana), x+8, y+2 - height - 2, 20, (Color){255, 255, 255, 255}); - DrawText(TextFormat("Experience: %i", entity->xp), x+8, y - 2*height - 4, 20, (Color){255, 255, 255, 255}); - } else if (entity->entity_type == 0) { - DrawText(TextFormat("Level: %i", entity->level), x+4, y -2*height - 12, 12, color); - } -} - -int c_render(MOBA* env) { - if (env->client == NULL) { - env->client = init_game_renderer(32, 41, 23); - } - GameRenderer* renderer = env->client; - int frame = renderer->frame; - - Map* map = env->map; - Entity* my_player = &env->entities[renderer->human_player]; - int ts = renderer->cell_size; - - renderer->width = GetScreenWidth() / ts; - renderer->height = GetScreenHeight() / ts; - renderer->shader_resolution[0] = renderer->width; - renderer->shader_resolution[1] = renderer->height; - - float tick_frac = (float)frame / (float)FRAMES; - - float fmain_r = my_player->last_y + tick_frac*(my_player->y - my_player->last_y); - float fmain_c = my_player->last_x + tick_frac*(my_player->x - my_player->last_x); - - renderer->camera.target.x = (int)((fmain_c - renderer->width/2) * ts); - renderer->camera.target.y = (int)((fmain_r - renderer->height/2) * ts); - - int main_r = fmain_r; - int main_c = fmain_c; - - int r_min = main_r - renderer->height/2 - 1; - int r_max = main_r + renderer->height/2 + 1; - int c_min = main_c - renderer->width/2 - 1; - int c_max = main_c + renderer->width/2 + 1; - - Vector2 pos = GetMousePosition(); - float raw_mouse_x = pos.x + renderer->camera.target.x; - float raw_mouse_y = pos.y + renderer->camera.target.y; - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - renderer->last_click_x = raw_mouse_x / ts; - renderer->last_click_y = raw_mouse_y / ts; - } - - int human = renderer->human_player; - bool HUMAN_CONTROL = IsKeyDown(KEY_LEFT_SHIFT); - int (*actions)[6] = (int(*)[6])env->actions; - - // Clears so as to not let the nn spam actions - if (HUMAN_CONTROL && frame % 12 == 0) { - actions[human][0] = 0; - actions[human][1] = 0; - actions[human][2] = 0; - actions[human][3] = 0; - actions[human][4] = 0; - actions[human][5] = 0; - } - - // TODO: better way to null clicks? - if (renderer->last_click_x != -1 && renderer->last_click_y != -1) { - float dest_x = renderer->last_click_x; - float dest_y = renderer->last_click_y; - float dy = dest_y - my_player->y; - float dx = dest_x - my_player->x; - - float mag = sqrtf(dy*dy + dx*dx); - if (mag < 1) { - renderer->last_click_x = -1; - renderer->last_click_y = -1; - } - - if (HUMAN_CONTROL) { - actions[human][0] = 300*dy; - actions[human][1] = 300*dx; - } - } - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - if (HUMAN_CONTROL) { - if (IsKeyDown(KEY_Q) || IsKeyPressed(KEY_Q)) { - actions[human][3] = 1; - } - if (IsKeyDown(KEY_W) || IsKeyPressed(KEY_W)) { - actions[human][4] = 1; - } - if (IsKeyDown(KEY_E) || IsKeyPressed(KEY_E)) { - actions[human][5] = 1; - } - if (IsKeyDown(KEY_LEFT_SHIFT)) { - actions[human][2] = 2; // Target heroes - } - } - // Num keys toggle selected player - int num_pressed = GetKeyPressed(); - if (num_pressed > KEY_ZERO && num_pressed <= KEY_NINE) { - renderer->human_player = num_pressed - KEY_ZERO - 1; - } else if (num_pressed == KEY_ZERO) { - renderer->human_player = 9; - } - - BeginDrawing(); - ClearBackground(COLORS[0]); - - // Main environment shader - BeginShaderMode(renderer->shader); - renderer->shader_y = (fmain_r - renderer->height/2) / 128; - renderer->shader_x = (fmain_c - renderer->width/2) / 128; - struct timespec time_spec; - clock_gettime(CLOCK_REALTIME, &time_spec); - renderer->shader_seconds = time_spec.tv_sec - renderer->shader_start_seconds + time_spec.tv_nsec / 1e9; - SetShaderValue(renderer->shader, renderer->shader_camera_x, &renderer->shader_x, SHADER_UNIFORM_FLOAT); - SetShaderValue(renderer->shader, renderer->shader_camera_y, &renderer->shader_y, SHADER_UNIFORM_FLOAT); - SetShaderValue(renderer->shader, renderer->shader_time, &renderer->shader_seconds, SHADER_UNIFORM_FLOAT); - SetShaderValue(renderer->shader, renderer->shader_resolution_loc, renderer->shader_resolution, SHADER_UNIFORM_VEC3); - SetShaderValueTexture(renderer->shader, renderer->shader_texture1, renderer->game_map); - DrawTexture(renderer->shader_canvas, 0, 0, WHITE); - EndShaderMode(); - - BeginMode2D(renderer->camera); - - int render_idx = 0; - for (int y = r_min; y < r_max+1; y++) { - for (int x = c_min; x < c_max+1; x++) { - if (y < 0 || y >= 128 || x < 0 || x >= 128) { - continue; - } - - int adr = map_offset(map, y, x); - int pid = map->pids[adr]; - //if (pid != -1) { - // DrawRectangle(x*ts, y*ts, ts, ts, RED); - //} - - unsigned char tile = map->grid[adr]; - if (tile == EMPTY || tile == WALL) { - continue; - } - - pid = map->pids[adr]; - if (pid == -1) { - DrawRectangle(x*ts, y*ts, ts, ts, RED); - } - - renderer->render_entities[render_idx] = pid; - render_idx++; - } - } - - // Targeting overlays - for (int i = 0; i < render_idx; i++) { - int pid = renderer->render_entities[i]; - if (pid == -1) { - continue; - } - - Entity* entity = &env->entities[pid]; - int target_pid = entity->target_pid; - if (target_pid == -1) { - continue; - } - - Entity* target = &env->entities[target_pid]; - float entity_x = entity->last_x + tick_frac*(entity->x - entity->last_x); - float entity_y = entity->last_y + tick_frac*(entity->y - entity->last_y); - float target_x = target->last_x + tick_frac*(target->x - target->last_x); - float target_y = target->last_y + tick_frac*(target->y - target->last_y); - - Color base; - Color accent; - if (entity->team == 0) { - base = (Color){0, 128, 128, 255}; - accent = (Color){0, 255, 255, 255}; - } else if (entity->team == 1) { - base = (Color){128, 0, 0, 255}; - accent = (Color){255, 0, 0, 255}; - } else { - base = (Color){128, 128, 128, 255}; - accent = (Color){255, 255, 255, 255}; - } - - int target_px = target_x*ts + ts/2; - int target_py = target_y*ts + ts/2; - int entity_px = entity_x*ts + ts/2; - int entity_py = entity_y*ts + ts/2; - - if (entity->attack_aoe == 0) { - Vector2 line_start = (Vector2){entity_px, entity_py}; - Vector2 line_end = (Vector2){target_px, target_py}; - DrawLineEx(line_start, line_end, ts/16, accent); - } else { - int radius = entity->attack_aoe*ts; - DrawRectangle(target_px - radius, target_py - radius, - 2*radius, 2*radius, base); - Rectangle rec = (Rectangle){target_px - radius, - target_py - radius, 2*radius, 2*radius}; - DrawRectangleLinesEx(rec, ts/8, accent); - } - } - - // Entity renders - for (int i = 0; i < render_idx; i++) { - Color tint = (Color){255, 255, 255, 255}; - - int pid = renderer->render_entities[i]; - if (pid == -1) { - continue; - } - Entity* entity = &env->entities[pid]; - int y = entity->y; - int x = entity->x; - - float entity_x = entity->last_x + tick_frac*(entity->x - entity->last_x); - float entity_y = entity->last_y + tick_frac*(entity->y - entity->last_y); - int tx = entity_x*ts; - int ty = entity_y*ts; - draw_bars(entity, tx, ty-8, ts, 4, false); - - int adr = map_offset(map, y, x); - int tile = map->grid[adr]; - - // TODO: Might need a vector type - Rectangle source_rect = renderer->asset_map[tile]; - Rectangle dest_rect = (Rectangle){tx, ty, ts, ts}; - - if (entity->is_hit) { - BeginShaderMode(renderer->bloom_shader); - } - Vector2 origin = (Vector2){0, 0}; - DrawTexturePro(renderer->puffer, source_rect, dest_rect, origin, 0, tint); - if (entity->is_hit) { - EndShaderMode(); - } - - // Draw status icons - if (entity->stun_timer > 0) { - DrawTexturePro(renderer->puffer, renderer->stun_uv, dest_rect, origin, 0, tint); - } - if (entity->move_timer > 0) { - if (entity->move_modifier < 0) { - DrawTexturePro(renderer->puffer, renderer->slow_uv, dest_rect, origin, 0, tint); - } - if (entity->move_modifier > 0) { - DrawTexturePro(renderer->puffer, renderer->speed_uv, dest_rect, origin, 0, tint); - } - } - } - - //DrawCircle(ts*mouse_x + ts/2, ts*mouse_y + ts/8, ts/8, WHITE); - EndMode2D(); - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - DrawCircle(pos.x, pos.y, ts/8, RED); - } - - // Draw HUD - Entity* player = &env->entities[human]; - DrawFPS(10, 10); - - float hud_y = renderer->height*ts - 2*ts; - draw_bars(player, 2*ts, hud_y, 10*ts, 24, true); - - Color off_color = (Color){255, 255, 255, 255}; - Color on_color = (player->team == 0) ? (Color){0, 255, 255, 255} : (Color){255, 0, 0, 255}; - - Color q_color = (actions[human][3]) ? on_color : off_color; - Color w_color = (actions[human][4]) ? on_color : off_color; - Color e_color = (actions[human][5]) ? on_color : off_color; - - int q_cd = player->q_timer; - int w_cd = player->w_timer; - int e_cd = player->e_timer; - - DrawText(TextFormat("Q: %i", q_cd), 13*ts, hud_y - 20, 40, q_color); - DrawText(TextFormat("W: %i", w_cd), 17*ts, hud_y - 20, 40, w_color); - DrawText(TextFormat("E: %i", e_cd), 21*ts, hud_y - 20, 40, e_color); - DrawText(TextFormat("Stun: %i", player->stun_timer), 25*ts, hud_y - 20, 20, (player->stun_timer > 0) ? on_color : off_color); - DrawText(TextFormat("Move: %i", player->move_timer), 25*ts, hud_y, 20, (player->move_timer > 0) ? on_color : off_color); - - EndDrawing(); - renderer->frame += 1; - if (renderer->frame % FRAMES == 0) { - renderer->frame = 0; - } - return 0; -} - -void close_game_renderer(GameRenderer* renderer) { - UnloadImage(renderer->shader_background); - UnloadShader(renderer->shader); - UnloadShader(renderer->bloom_shader); - CloseWindow(); - free(renderer); -} - - diff --git a/pufferlib/ocean/moba/moba.py b/pufferlib/ocean/moba/moba.py deleted file mode 100644 index 4b4dd0be8..000000000 --- a/pufferlib/ocean/moba/moba.py +++ /dev/null @@ -1,113 +0,0 @@ -from pdb import set_trace as T -import numpy as np -import os - -import pettingzoo -import gymnasium - -import pufferlib -from pufferlib.ocean.moba import binding - -MAP_OBS_N = 11*11*4 -PLAYER_OBS_N = 26 - -class Moba(pufferlib.PufferEnv): - def __init__(self, num_envs=4, vision_range=5, agent_speed=1.0, - discretize=True, reward_death=-1.0, reward_xp=0.006, - reward_distance=0.05, reward_tower=3.0, report_interval=32, - script_opponents=True, render_mode='human', buf=None, seed=0): - - self.report_interval = report_interval - self.render_mode = render_mode - self.num_agents = 5*num_envs if script_opponents else 10*num_envs - - self.single_observation_space = gymnasium.spaces.Box(low=0, high=255, - shape=(MAP_OBS_N + PLAYER_OBS_N,), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.MultiDiscrete([7, 7, 3, 2, 2, 2]) - - super().__init__(buf=buf) - - c_envs = [] - players = 5 if script_opponents else 10 - self.c_state = binding.shared() - for i in range(num_envs): - env_id = binding.env_init( - self.observations[i*players:(i+1)*players], - self.actions[i*players:(i+1)*players], - self.rewards[i*players:(i+1)*players], - self.terminals[i*players:(i+1)*players], - self.truncations[i*players:(i+1)*players], - i + seed*num_envs, - vision_range=vision_range, - agent_speed=agent_speed, - discretize=discretize, - reward_death=reward_death, - reward_xp=reward_xp, - reward_distance=reward_distance, - reward_tower=reward_tower, - script_opponents=script_opponents, - state=self.c_state, - ) - c_envs.append(env_id) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - self.actions[:, 0] = 100*(self.actions[:, 0] - 3) - self.actions[:, 1] = 100*(self.actions[:, 1] - 3) - binding.vec_step(self.c_envs) - - infos = [] - self.tick += 1 - if self.tick % self.report_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - infos.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, infos) - - def render(self): - for frame in range(12): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - - -def test_performance(timeout=20, atn_cache=1024, num_envs=400): - tick = 0 - - import time - start = time.time() - while time.time() - start < timeout: - atns = actions[tick % atn_cache] - env.step(atns) - tick += 1 - - print(f'SPS: %f', 10*num_envs*tick / (time.time() - start)) - -if __name__ == '__main__': - # Run with c profile - from cProfile import run - num_envs = 400 - env = Moba(num_envs=num_envs, report_interval=10000000) - env.reset() - actions = np.random.randint(0, env.single_action_space.nvec, (1024, 10*num_envs, 6)) - test_performance(20, 1024, num_envs) - exit(0) - - run('test_performance(20)', 'stats.profile') - import pstats - from pstats import SortKey - p = pstats.Stats('stats.profile') - p.sort_stats(SortKey.TIME).print_stats(25) - exit(0) - - #test_performance(10) diff --git a/pufferlib/ocean/nmmo3/binding.c b/pufferlib/ocean/nmmo3/binding.c deleted file mode 100644 index d5c97cf1c..000000000 --- a/pufferlib/ocean/nmmo3/binding.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "nmmo3.h" - -#define Env MMO -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->num_players = unpack(kwargs, "num_players"); - env->num_enemies = unpack(kwargs, "num_enemies"); - env->num_resources = unpack(kwargs, "num_resources"); - env->num_weapons = unpack(kwargs, "num_weapons"); - env->num_gems = unpack(kwargs, "num_gems"); - env->tiers = unpack(kwargs, "tiers"); - env->levels = unpack(kwargs, "levels"); - env->teleportitis_prob = unpack(kwargs, "teleportitis_prob"); - env->enemy_respawn_ticks = unpack(kwargs, "enemy_respawn_ticks"); - env->item_respawn_ticks = unpack(kwargs, "item_respawn_ticks"); - env->x_window = unpack(kwargs, "x_window"); - env->y_window = unpack(kwargs, "y_window"); - env->reward_combat_level = unpack(kwargs, "reward_combat_level"); - env->reward_prof_level = unpack(kwargs, "reward_prof_level"); - env->reward_item_level = unpack(kwargs, "reward_item_level"); - env->reward_market = unpack(kwargs, "reward_market"); - env->reward_death = unpack(kwargs, "reward_death"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "return_comb_lvl", log->return_comb_lvl); - assign_to_dict(dict, "return_prof_lvl", log->return_prof_lvl); - assign_to_dict(dict, "return_item_atk_lvl", log->return_item_atk_lvl); - assign_to_dict(dict, "return_item_def_lvl", log->return_item_def_lvl); - assign_to_dict(dict, "return_market_buy", log->return_market_buy); - assign_to_dict(dict, "return_market_sell", log->return_market_sell); - assign_to_dict(dict, "return_death", log->return_death); - assign_to_dict(dict, "min_comb_prof", log->min_comb_prof); - assign_to_dict(dict, "purchases", log->purchases); - assign_to_dict(dict, "sales", log->sales); - assign_to_dict(dict, "equip_attack", log->equip_attack); - assign_to_dict(dict, "equip_defense", log->equip_defense); - assign_to_dict(dict, "r", log->r); - assign_to_dict(dict, "c", log->c); - return 0; -} diff --git a/pufferlib/ocean/nmmo3/cy_nmmo3.pyx b/pufferlib/ocean/nmmo3/cy_nmmo3.pyx deleted file mode 100644 index 65d037097..000000000 --- a/pufferlib/ocean/nmmo3/cy_nmmo3.pyx +++ /dev/null @@ -1,270 +0,0 @@ -# distutils: define_macros=NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION -# cython: language_level=3 -# cython: boundscheck=True -# cython: initializedcheck=True -# cython: wraparound=True -# cython: cdivision=False -# cython: nonecheck=True -# cython: profile=True - -from libc.stdlib cimport calloc, free -cimport numpy as cnp -import numpy as np - -cdef extern from "nmmo3.h": - int LOG_BUFFER_SIZE - - ctypedef struct Log: - float episode_return; - float episode_length; - float return_comb_lvl; - float return_prof_lvl; - float return_item_atk_lvl; - float return_item_def_lvl; - float return_market_buy; - float return_market_sell; - float return_death; - float min_comb_prof; - float purchases; - float sales; - float equip_attack; - float equip_defense; - float r; - float c; - - ctypedef struct LogBuffer - LogBuffer* allocate_logbuffer(int) - void free_logbuffer(LogBuffer*) - Log aggregate_and_clear(LogBuffer*) - - int ATN_NOOP - - ctypedef struct Entity: - int type - int comb_lvl - int element - int dir - int anim - int hp - int hp_max - int prof_lvl - int ui_mode - int market_tier - int sell_idx - int gold - int in_combat - int equipment[5] - int inventory[12] - int is_equipped[12] - int wander_range - int ranged - int goal - int equipment_attack - int equipment_defense - int r - int c - int spawn_r - int spawn_c - int min_comb_prof[500] - int min_comb_prof_idx - int time_alive; - int purchases; - int sales; - - ctypedef struct Reward: - float total - float death; - float pioneer; - float comb_lvl; - float prof_lvl; - float item_atk_lvl; - float item_def_lvl; - float item_tool_lvl; - float market_buy; - float market_sell; - - ctypedef struct ItemMarket: - int offer_idx - int max_offers - - ctypedef struct RespawnBuffer: - int* buffer - int ticks - int size - - ctypedef struct MMO: - int width - int height - int num_players - int num_enemies - int num_resources - int num_weapons - int num_gems - char* terrain - unsigned char* rendered - Entity* players - Entity* enemies - short* pids - unsigned char* items - Reward* rewards - unsigned char* counts - unsigned char* obs - int* actions - int tick - int tiers - int levels - float teleportitis_prob - int x_window - int y_window - int obs_size - int enemy_respawn_ticks - int item_respawn_ticks - ItemMarket* market - int market_buys - int market_sells - RespawnBuffer* resource_respawn_buffer - RespawnBuffer* enemy_respawn_buffer - Log* logs - LogBuffer* log_buffer - float reward_combat_level - float reward_prof_level - float reward_item_level - float reward_market - float reward_death - - ctypedef struct Client - Client* make_client(MMO* env) - #void close_client(Client* client) - int tick(Client* client, MMO* env, float delta) - - void init_mmo(MMO* env) - void c_reset(MMO* env, int seed) - void c_step(MMO* env) - -cpdef entity_dtype(): - '''Make a dummy entity to get the dtype''' - cdef Entity entity - return np.asarray(&entity).dtype - -cpdef reward_dtype(): - '''Make a dummy reward to get the dtype''' - cdef Reward reward - return np.asarray(&reward).dtype - -cdef class Environment: - cdef: - MMO* envs - Client* client - LogBuffer* logs - int num_envs - - def __init__(self, unsigned char[:, :] observations, int[:, :] players, - int[:, :] enemies, float[:, :] rewards, int[:] actions, - list width, list height, int num_envs, list num_players, - list num_enemies, list num_resources, list num_weapons, list num_gems, - list tiers, list levels, list teleportitis_prob, list enemy_respawn_ticks, - list item_respawn_ticks, float reward_combat_level, float reward_prof_level, - float reward_item_level, float reward_market, float reward_death, - int x_window=7, int y_window=5): - - cdef: - int total_players = 0 - int total_enemies = 0 - int n_players = 0 - int n_enemies = 0 - - self.num_envs = num_envs - self.envs = calloc(num_envs, sizeof(MMO)) - self.logs = allocate_logbuffer(LOG_BUFFER_SIZE) - for i in range(num_envs): - obs_i = observations[total_players:total_players+n_players] - rewards_i = rewards[total_players:total_players+n_players] - players_i = players[total_players:total_players+n_players] - enemies_i = enemies[total_enemies:total_enemies+n_enemies] - #counts_i = counts[total_players:total_players+n_players] - #terrain_i = terrain[total_players:total_players+n_players] - #rendered_i = rendered[total_players:total_players+n_players] - actions_i = actions[total_players:total_players+n_players] - - self.envs[i] = MMO( - obs=&observations[total_players, 0], - rewards= &rewards[total_players, 0], - players= &players[total_players, 0], - enemies= &enemies[total_enemies, 0], - actions=&actions[total_players], - width=width[i], - height=height[i], - num_players=num_players[i], - num_enemies=num_enemies[i], - num_resources=num_resources[i], - num_weapons=num_weapons[i], - num_gems=num_gems[i], - tiers=tiers[i], - levels=levels[i], - teleportitis_prob=teleportitis_prob[i], - enemy_respawn_ticks=enemy_respawn_ticks[i], - item_respawn_ticks=item_respawn_ticks[i], - x_window=x_window, - y_window=y_window, - log_buffer=self.logs, - reward_combat_level=reward_combat_level, - reward_prof_level=reward_prof_level, - reward_item_level=reward_item_level, - reward_market=reward_market, - reward_death=reward_death, - ) - n_players = num_players[i] - n_enemies = num_enemies[i] - - init_mmo(&self.envs[i]) - total_players += n_players - total_enemies += n_enemies - - self.client = NULL - - def reset(self): - cdef int i - for i in range(self.num_envs): - # TODO: Seed - c_reset(&self.envs[i], i+1) - # Do I need to reset terrain here? - - def step(self): - cdef int i - for i in range(self.num_envs): - c_step(&self.envs[i]) - - def pids(self): - ary = np.zeros((512, 512), dtype=np.intc) - cdef int i, j - for i in range(512): - for j in range(512): - ary[i, j] = self.envs[0].pids[512*i + j] - return ary - - def render(self): - if self.client == NULL: - import os - cwd = os.getcwd() - os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) - self.client = make_client(&self.envs[0]) - os.chdir(cwd) - - cdef int i, atn - cdef int action = ATN_NOOP; - for i in range(36): - atn = tick(self.client, &self.envs[0], i/36.0) - if atn != ATN_NOOP: - action = atn - - self.envs[0].actions[0] = action - - # TODO - def close(self): - if self.client != NULL: - #close_game_renderer(self.renderer) - self.client = NULL - - def log(self): - cdef Log log = aggregate_and_clear(self.logs) - return log diff --git a/pufferlib/ocean/nmmo3/make_sprite_sheets.py b/pufferlib/ocean/nmmo3/make_sprite_sheets.py deleted file mode 100644 index be4d52c33..000000000 --- a/pufferlib/ocean/nmmo3/make_sprite_sheets.py +++ /dev/null @@ -1,498 +0,0 @@ -''' -This script is used to generate scaled and combined sprite sheets for nmmo3 -You will need the to put the following folders into the same directory. They -can be purchased from ManaSeed on itch.io - -20.04c - Summer Forest 4.2 -20.05c - Spring Forest 4.1 -20.06a - Autumn Forest 4.1 -20.07a - Winter Forest 4.0a -20.01a - Character Base 2.5c -20.01c - Peasant Farmer Pants & Hat 2.1 (comp. v01) -20.01c - Peasant Farmer Pants & Hat 2.2 (optional combat animations) -20.08b - Bow Combat 3.2 -21.07b - Sword & Shield Combat 2.3 -21.10a - Forester Pointed Hat & Tunic 2.1a (comp. v01) -21.10a - Forester Pointed Hat & Tunic 2.2 (optional, combat animations) -''' - -from itertools import product -from PIL import Image -import pyray as ray -import numpy as np -import random -import sys -import os -import cv2 - - -SHEET_SIZE = 2048 -N_GENERATE = 10 - -ELEMENTS = ( - ('neutral', 1, ray.Color(255, 255, 255, 255)), - ('fire', 5, ray.Color(255, 128, 128, 255)), - ('water', 9, ray.Color(128, 128, 255, 255)), - ('earth', 11, ray.Color(128, 255, 128, 255)), - ('air', 3, ray.Color(255, 255, 128, 255)), -) - -BASE = list(range(8)) -HAIR = list(range(14)) -CLOTHES = list(range(1, 6)) -SWORD = list(range(1, 6)) -BOW = list(range(1, 6)) -QUIVER = list(range(1, 9)) - -# Hair colors, indices into files -''' -HAIR = { - ELEM_NEUTRAL: 1, - ELEM_FIRE: 5, - ELEM_WATER: 9, - ELEM_EARTH: 11, - ELEM_AIR: 3 -} -''' - - -# Character base -character = 'char_a_p1_0bas_humn_v{i:02}.png' -demon = 'char_a_p1_0bas_demn_v{i:02}.png' -goblin = 'char_a_p1_0bas_gbln_v{i:02}.png' -hair_dap = 'char_a_p1_4har_dap1_v{i:02}.png' -hair_bob = 'char_a_p1_4har_bob1_v{i:02}.png' - -# Combat animations -sword_character = 'char_a_pONE3_0bas_humn_v{i:02}.png' -sword_weapon = 'char_a_pONE3_6tla_sw01_v{i:02}.png' -sword_hair_bob = 'char_a_pONE3_4har_bob1_v{i:02}.png' -sword_hair_dap = 'char_a_pONE3_4har_dap1_v{i:02}.png' -bow_character = 'char_a_pBOW3_0bas_humn_v{i:02}.png' -bow_hair_dap = 'char_a_pBOW3_4har_dap1_v{i:02}.png' -bow_hair_bob = 'char_a_pBOW3_4har_bob1_v{i:02}.png' -bow_weapon = 'char_a_pBOW3_6tla_bo01_v{i:02}.png' -bow_quiver = 'char_a_pBOW3_7tlb_qv01_v{i:02}.png' -arrow = 'aro_comn_v{i:02}.png' - -# Peasant character alternative -peasant_clothes = 'char_a_p1_1out_pfpn_v{i:02}.png' -sword_peasant_clothes = 'char_a_pONE3_1out_pfpn_v{i:02}.png' -bow_peasant_clothes = 'char_a_pBOW3_1out_pfpn_v{i:02}.png' - -# Forester character alternative -forester_hat = 'char_a_p1_5hat_pnty_v{i:02}.png' -forester_clothes = 'char_a_p1_1out_fstr_v{i:02}.png' -sword_forester_hat = 'char_a_pONE3_5hat_pnty_v{i:02}.png' -sword_forester_clothes = 'char_a_pONE3_1out_fstr_v{i:02}.png' -bow_forester_hat = 'char_a_pBOW3_5hat_pnty_v{i:02}.png' -bow_forester_clothes = 'char_a_pBOW3_1out_fstr_v{i:02}.png' - -sword_mask = np.array(( - (1, 1, 1, 1, 1, 1, 1, 1), - (1, 1, 1, 1, 1, 1, 1, 1), - (1, 0, 1, 1, 1, 1, 1, 1), - (1, 0, 1, 1, 1, 1, 1, 1), - (0, 0, 1, 1, 1, 1, 1, 1), - (1, 1, 1, 1, 1, 1, 1, 1), - (0, 0, 1, 1, 0, 0, 0, 0), - (0, 0, 1, 1, 0, 0, 0, 0), -)) - -bow_mask = np.array(( - (0, 0, 0, 0, 0, 0, 0, 0), - (1, 1, 1, 1, 1, 1, 1, 1), - (1, 0, 0, 0, 0, 0, 0, 0), - (1, 0, 0, 0, 0, 0, 0, 0), - (0, 0, 0, 0, 0, 0, 0, 0), - (1, 1, 1, 1, 1, 1, 1, 1), - (1, 0, 0, 0, 0, 0, 0, 0), - (1, 0, 0, 0, 0, 0, 0, 0), -)) - -quiver_mask = np.array(( - (1, 1, 1, 1, 1, 1, 1, 1), - (0, 0, 0, 0, 0, 0, 0, 0), - (1, 1, 1, 1, 1, 1, 1, 1), - (1, 1, 1, 1, 1, 1, 1, 1), - (1, 1, 1, 1, 1, 1, 1, 1), - (0, 0, 0, 0, 0, 0, 0, 0), - (1, 1, 1, 1, 1, 1, 1, 1), - (1, 1, 1, 1, 1, 1, 1, 1), -)) - -def draw_tex(path, f_name, i, x, y, tint=None): - if tint is None: - tint = ray.WHITE - - path = os.path.join(path, f_name).format(i=i) - texture = ray.load_texture(path) - source_rect = ray.Rectangle(0, 0, texture.width, -texture.height) - dest_rect = ray.Rectangle(x, y, texture.width, texture.height) - ray.draw_texture_pro(texture, source_rect, dest_rect, (0, 0), 0, tint) - -def draw_masked_tex(path, f_name, i, x, y, mask, tint=None): - if tint is None: - tint = ray.WHITE - - path = os.path.join(path, f_name).format(i=i) - texture = ray.load_texture(path) - Y, X = mask.shape - for r, row in enumerate(mask): - for c, m in enumerate(row): - if m == 0: - continue - - src_x = c * 128 - src_y = r * 128 - source_rect = ray.Rectangle(src_x, src_y, 128, -128) - - dst_x = x + src_x - dst_y = y + (Y-r-1)*128 - dest_rect = ray.Rectangle(dst_x, dst_y, 128, 128) - - ray.draw_texture_pro(texture, source_rect, dest_rect, (0, 0), 0, tint) - -def draw_arrow(tex, src_x, src_y, dst_x, dst_y, offset_x, offset_y, rot): - source_rect = ray.Rectangle(src_x*32, src_y*32, 32, -32) - dest_rect = ray.Rectangle(dst_x*128 + offset_x, SHEET_SIZE-(dst_y+1)*128+ offset_y, 32, 32) - ray.draw_texture_pro(tex, source_rect, dest_rect, (0, 0), rot, ray.WHITE) - -def draw_sheet(src, hair_i, tint, seed=None): - if seed is not None: - random.seed(seed) - - base_i = random.choice(BASE) - if hair_i is None: - hair_i = random.choice(HAIR) - clothes_i = random.choice(CLOTHES) - sword_i = random.choice(SWORD) - bow_i = random.choice(BOW) - quiver_i = random.choice(QUIVER) - - hair_variant = random.randint(0, 1) - hair = [hair_dap, hair_bob][hair_variant] - sword_hair = [sword_hair_dap, sword_hair_bob][hair_variant] - bow_hair = [bow_hair_dap, bow_hair_bob][hair_variant] - - clothes_variant = random.randint(0, 1) - clothes = [peasant_clothes, forester_clothes][clothes_variant] - sword_clothes = [sword_peasant_clothes, sword_forester_clothes][clothes_variant] - bow_clothes = [bow_peasant_clothes, bow_forester_clothes][clothes_variant] - - x = 0 - y = 1024 - draw_tex(src, character, base_i, x, y) - draw_tex(src, hair, hair_i, x, y) - draw_tex(src, clothes, clothes_i, x, y) - - x = 0 - y = 0 - draw_masked_tex(src, sword_weapon, sword_i, x, y, sword_mask, tint=tint) - draw_tex(src, sword_character, base_i, x, y) - draw_tex(src, sword_hair, hair_i, x, y) - draw_tex(src, sword_clothes, clothes_i, x, y) - draw_masked_tex(src, sword_weapon, sword_i, x, y, 1-sword_mask, tint=tint) - - x = 1024 - y = 1024 - draw_masked_tex(src, bow_weapon, bow_i, x, y, bow_mask, tint=tint) - draw_masked_tex(src, bow_quiver, quiver_i, x, y, quiver_mask, tint=tint) - draw_tex(src, bow_character, base_i, x, y) - draw_tex(src, bow_hair, hair_i, x, y) - draw_tex(src, bow_clothes, clothes_i, x, y) - draw_masked_tex(src, bow_weapon, bow_i, x, y, 1-bow_mask, tint=tint) - draw_masked_tex(src, bow_quiver, quiver_i, x, y, 1-quiver_mask, tint=tint) - - arrow_path = os.path.join(src, arrow).format(i=quiver_i) - arrow_tex = ray.load_texture(arrow_path) - - ### Arrows are manually aligned - # Left facing arrows - draw_arrow(arrow_tex, 4, 1, 9, 3, 24, 40, 0) - draw_arrow(arrow_tex, 4, 1, 10, 3, 24, 40, 0) - draw_arrow(arrow_tex, 3, 1, 11, 3, 24, 52, 0) - draw_arrow(arrow_tex, 1, 1, 12, 3, 38, 64, 0) - - # Right facing arrows - draw_arrow(arrow_tex, 4, 1, 9, 2, 64+42, 48, 120) - draw_arrow(arrow_tex, 4, 1, 10, 2, 64+42, 48, 120) - draw_arrow(arrow_tex, 3, 1, 11, 2, 64+32, 82, 180) - draw_arrow(arrow_tex, 1, 1, 12, 2, 56, 98, 180+80) - - -def scale_image(image_array, scale_factor): - if scale_factor < 1: - # Scale down with exact interpolation - scaled_image_array = image_array[::int(1/scale_factor), ::int(1/scale_factor)] - elif scale_factor > 1: - # Scale up (duplicate pixels) - scaled_image_array = np.repeat( - np.repeat( - image_array, scale_factor, axis=0 - ), scale_factor, axis=1 - ) - else: - # No scaling - scaled_image_array = image_array - - return scaled_image_array - -def copy_and_scale_files(source_directory, target_directory, scale_factor): - for root, dirs, files in os.walk(source_directory): - relative_path = os.path.relpath(root, source_directory) - target_path = os.path.join(target_directory) - os.makedirs(target_path, exist_ok=True) - - for file in files: - src_file_path = os.path.join(root, file) - target_file_path = os.path.join(target_directory, file) - - path = src_file_path.lower() - if path.endswith('.ttf'): - os.copy(src_file_path, target_file_path) - continue - - if not src_file_path.lower().endswith(('.png', '.jpg', '.jpeg')): - continue - - image = Image.open(src_file_path) - image_array = np.array(image) - scaled_image_array = scale_image(image_array, scale_factor) - scaled_image = Image.fromarray(scaled_image_array) - scaled_image.save(target_file_path) - -if len(sys.argv) != 4: - print("Usage: script.py source_directory target_directory scale_factor") - sys.exit(1) - -source_directory = sys.argv[1] -target_directory = sys.argv[2] -scale_factor = float(sys.argv[3]) - -if not os.path.exists(source_directory): - print("Source directory does not exist.") - sys.exit(1) - -valid_scales = [0.125, 0.25, 0.5, 1, 2, 4] -if scale_factor not in valid_scales: - print(f'Scale factor must be one of {valid_scales}') - -intermediate_directory = os.path.join(target_directory, 'temp') -if not os.path.exists(intermediate_directory): - os.makedirs(intermediate_directory) - copy_and_scale_files(source_directory, intermediate_directory, scale_factor) - -ray.init_window(SHEET_SIZE, SHEET_SIZE, "NMMO3") -ray.set_target_fps(60) - -output_image = ray.load_render_texture(SHEET_SIZE, SHEET_SIZE) - -i = 0 -while not ray.window_should_close() and i < N_GENERATE: - ray.set_window_title(f'Generating sheet {i+1}/{N_GENERATE}') - - for elem in ELEMENTS: - elem_name, hair_i, tint = elem - ray.begin_drawing() - ray.begin_texture_mode(output_image) - ray.clear_background(ray.BLANK) - draw_sheet(intermediate_directory, hair_i, tint, seed=i) - ray.end_texture_mode() - - image = ray.load_image_from_texture(output_image.texture) - f_path = os.path.join(target_directory, f'{elem_name}_{i}.png') - ray.export_image(image, f_path) - - ray.clear_background(ray.GRAY) - ray.draw_texture(output_image.texture, 0, 0, ray.WHITE) - ray.end_drawing() - - i += 1 - -coords = (0, 1) -spring = cv2.imread(intermediate_directory + '/spring forest.png') -summer = cv2.imread(intermediate_directory + '/summer forest.png') -autumn = cv2.imread(intermediate_directory + '/autumn forest (bare).png') -winter = cv2.imread(intermediate_directory + '/winter forest (clean).png') - -spring = scale_image(spring, 2) -summer = scale_image(summer, 2) -autumn = scale_image(autumn, 2) -winter = scale_image(winter, 2) - -SEASONS = [spring, summer, autumn, winter] - -spring_sparkle = cv2.imread(intermediate_directory + '/spring water sparkles B.png') -summer_sparkle = cv2.imread(intermediate_directory + '/summer water sparkles B 16x16.png') -autumn_sparkle = cv2.imread(intermediate_directory + '/autumn water sparkles B 16x16.png') -winter_sparkle = cv2.imread(intermediate_directory + '/winter water sparkles B 16x16.png') - -spring_sparkle = scale_image(spring_sparkle, 2) -summer_sparkle = scale_image(summer_sparkle, 2) -autumn_sparkle = scale_image(autumn_sparkle, 2) -winter_sparkle = scale_image(winter_sparkle, 2) - -SPARKLES = [spring_sparkle, summer_sparkle, autumn_sparkle, winter_sparkle] - -GRASS_OFFSET = (0, 0) -DIRT_OFFSET = (5, 0) -STONE_OFFSET = (9, 0) -WATER_OFFSET = (29, 16) - -# Not compatible with water -GRASS_1 = (0, 1) -GRASS_2 = (0, 2) -GRASS_3 = (0, 3) -GRASS_4 = (0, 4) -GRASS_5 = (0, 5) - -DIRT_1 = (8, 0) -DIRT_2 = (8, 1) -DIRT_3 = (8, 2) -DIRT_4 = (8, 3) -DIRT_5 = (8, 4) - -STONE_1 = (12, 0) -STONE_2 = (12, 1) -STONE_3 = (12, 2) -STONE_4 = (12, 3) -STONE_5 = (12, 4) - -WATER_1 = (27, 14) -WATER_2 = (28, 13) -WATER_3 = (28, 14) -WATER_4 = (29, 13) -WATER_5 = (29, 14) - -GRASS_N = [GRASS_1, GRASS_2, GRASS_3, GRASS_4, GRASS_5] -DIRT_N = [DIRT_1, DIRT_2, DIRT_3, DIRT_4, DIRT_5] -STONE_N = [STONE_1, STONE_2, STONE_3, STONE_4, STONE_5] -WATER_N = [WATER_1, WATER_2, WATER_3, WATER_4, WATER_5] - -ALL_MATERIALS = [DIRT_N, STONE_N, WATER_N] -ALL_OFFSETS = [DIRT_OFFSET, STONE_OFFSET, WATER_OFFSET] - -# These values are just sentinels -# They will be mapped to GRASS/DIRT/STONE/WATER -FULL = (-1, 0) -EMPTY = (0, -1) - -TL_CORNER = (0, 0) -T_FLAT = (1, 0) -TR_CORNER = (2, 0) -L_FLAT = (0, 1) -CENTER = (1, 1) -R_FLAT = (2, 1) -BL_CORNER = (0, 2) -B_FLAT = (1, 2) -BR_CORNER = (2, 2) -TL_DIAG = (0, 3) -TR_DIAG = (1, 3) -BL_DIAG = (0, 4) -BR_DIAG = (1, 4) -TRR_DIAG = (2, 3) -BRR_DIAG = (2, 4) - -OFFSETS = [TL_CORNER, T_FLAT, TR_CORNER, L_FLAT, CENTER, R_FLAT, BL_CORNER, - B_FLAT, BR_CORNER, TL_DIAG, TR_DIAG, BL_DIAG, BR_DIAG, TRR_DIAG, BRR_DIAG] - -TILE_SIZE = int(32 * scale_factor) -SHEET_SIZE = 64 -SHEET_PX = TILE_SIZE * SHEET_SIZE -merged_sheet = np.zeros((SHEET_PX, SHEET_PX, 3), dtype=np.uint8) - -def gen_lerps(): - valid_combinations = [] - for combo in product(range(10), repeat=4): - if sum(combo) == 9 and any(weight > 0 for weight in combo): - valid_combinations.append(combo) - - return valid_combinations - -def gen_lerps(): - valid_combinations = [] - for total_sum in range(1, 10): # Loop through all possible sums from 1 to 9 - for combo in product(range(10), repeat=4): - if sum(combo) == total_sum: - valid_combinations.append(combo) - return valid_combinations - -def slice(r, c): - return np.s_[ - r*TILE_SIZE:(r+1)*TILE_SIZE, - c*TILE_SIZE:(c+1)*TILE_SIZE - ] - -idx = 0 -for sheet in SEASONS: - for offset, material in zip(ALL_OFFSETS, ALL_MATERIALS): - src_dx, src_dy = offset - - # Write full tile textures. These are irregularly - # arranged in the source sheet and require manual offsets. - for src_x, src_y in material: - dst_r, dst_c = divmod(idx, SHEET_SIZE) - idx += 1 - - src_pos = slice(src_y, src_x) - tile_tex = sheet[src_pos] - - dst_pos = slice(dst_r, dst_c) - merged_sheet[dst_pos] = tile_tex - - # Write partial tile textures. These have fixed offsets - for dx, dy in OFFSETS: - dst_r, dst_c = divmod(idx, SHEET_SIZE) - idx += 1 - - src_pos = slice(dy+src_dy, dx+src_dx) - tile_tex = sheet[src_pos] - - dst_pos = slice(dst_r, dst_c) - merged_sheet[dst_pos] = tile_tex - -for x, y in WATER_N: - # 3 animations - for anim_y in range(3): - for season, sparkle in zip(SEASONS, SPARKLES): - src_pos = slice(y, x) - tile_tex = season[src_pos] - - # 4 frame animation - for anim_x in range(4): - dst_r, dst_c = divmod(idx, SHEET_SIZE) - idx += 1 - - src_pos = slice(anim_y, anim_x) - sparkle_tex = sparkle[src_pos] - - dst_pos = slice(dst_r, dst_c) - merged_sheet[dst_pos] = tile_tex - mask = np.where(sparkle_tex != 0) - merged_sheet[dst_pos][mask] = sparkle_tex[mask] - - -for src in range(1, 5): - tex_src = slice(src, 0) - tiles = [spring[tex_src], summer[tex_src], autumn[tex_src], winter[tex_src]] - for combo in gen_lerps(): - tex = np.zeros((TILE_SIZE, TILE_SIZE, 3)) - total_weight = sum(combo) - for i, weight in enumerate(combo): - tex += weight/total_weight * tiles[i] - - tex = tex.astype(np.uint8) - - dst_r, dst_c = divmod(idx, SHEET_SIZE) - idx += 1 - - dst_pos = slice(dst_r, dst_c) - merged_sheet[dst_pos] = tex - - print(idx) - -# save image -cv2.imwrite('merged_sheet.png', merged_sheet) -cv2.imshow('merged_sheet', merged_sheet) -cv2.waitKey(0) diff --git a/pufferlib/ocean/nmmo3/nmmo3.c b/pufferlib/ocean/nmmo3/nmmo3.c deleted file mode 100644 index 4a3b08763..000000000 --- a/pufferlib/ocean/nmmo3/nmmo3.c +++ /dev/null @@ -1,485 +0,0 @@ -#include -#include -#include -#include "puffernet.h" -#include "nmmo3.h" - -// Only rens a few agents in the C -// version, and reduces for web. -// You can run the full 1024 on GPU -// with PyTorch. -#if defined(PLATFORM_WEB) - #define NUM_AGENTS 4 -#else - #define NUM_AGENTS 16 -#endif - - -typedef struct MMONet MMONet; -struct MMONet { - int num_agents; - float* ob_map; - int* ob_player_discrete; - float* ob_player_continuous; - float* ob_reward; - Conv2D* map_conv1; - ReLU* map_relu; - Conv2D* map_conv2; - Embedding* player_embed; - float* proj_buffer; - Linear* proj; - ReLU* proj_relu; - LayerNorm* layer_norm; - LSTM* lstm; - Linear* actor; - Linear* value_fn; - Multidiscrete* multidiscrete; -}; - -MMONet* init_mmonet(Weights* weights, int num_agents) { - MMONet* net = calloc(1, sizeof(MMONet)); - int hidden = 512; - net->num_agents = num_agents; - net->ob_map = calloc(num_agents*11*15*59, sizeof(float)); - net->ob_player_discrete = calloc(num_agents*47, sizeof(int)); - net->ob_player_continuous = calloc(num_agents*47, sizeof(float)); - net->ob_reward = calloc(num_agents*10, sizeof(float)); - net->map_conv1 = make_conv2d(weights, num_agents, 15, 11, 59, 128, 5, 3); - net->map_relu = make_relu(num_agents, 128*3*4); - net->map_conv2 = make_conv2d(weights, num_agents, 4, 3, 128, 128, 3, 1); - net->player_embed = make_embedding(weights, num_agents*47, 128, 32); - net->proj_buffer = calloc(num_agents*1817, sizeof(float)); - net->proj = make_linear(weights, num_agents, 1817, hidden); - net->proj_relu = make_relu(num_agents, hidden); - net->layer_norm = make_layernorm(weights, num_agents, hidden); - net->actor = make_linear(weights, num_agents, hidden, 26); - net->value_fn = make_linear(weights, num_agents, hidden, 1); - net->lstm = make_lstm(weights, num_agents, hidden, hidden); - int logit_sizes[1] = {26}; - net->multidiscrete = make_multidiscrete(num_agents, logit_sizes, 1); - return net; -} - -void free_mmonet(MMONet* net) { - free(net->ob_map); - free(net->ob_player_discrete); - free(net->ob_player_continuous); - free(net->ob_reward); - free(net->map_conv1); - free(net->map_relu); - free(net->map_conv2); - free(net->player_embed); - free(net->proj_buffer); - free(net->proj); - free(net->proj_relu); - free(net->layer_norm); - free(net->actor); - free(net->value_fn); - free(net->lstm); - free(net->multidiscrete); - free(net); -} - -void forward(MMONet* net, unsigned char* observations, int* actions) { - memset(net->ob_map, 0, net->num_agents*11*15*59*sizeof(float)); - - // DUMMY INPUT FOR TESTING - //for (int i = 0; i < 11*15*10 + 47 + 10; i++) { - // observations[i] = i % 4; - //} - - // CNN subnetwork - int factors[10] = {4, 4, 17, 5, 3, 5, 5, 5, 7, 4}; - float (*ob_map)[59][11][15] = (float (*)[59][11][15])net->ob_map; - for (int b = 0; b < net->num_agents; b++) { - int b_offset = b*(11*15*10 + 47 + 10); - for (int i = 0; i < 11; i++) { - for (int j = 0; j < 15; j++) { - int f_offset = 0; - for (int f = 0; f < 10; f++) { - int obs_idx = f_offset + observations[b_offset + i*15*10 + j*10 + f]; - ob_map[b][obs_idx][i][j] = 1; - f_offset += factors[f]; - } - } - } - } - conv2d(net->map_conv1, net->ob_map); - relu(net->map_relu, net->map_conv1->output); - conv2d(net->map_conv2, net->map_relu->output); - - // Player embedding subnetwork - for (int b = 0; b < net->num_agents; b++) { - for (int i = 0; i < 47; i++) { - unsigned char ob = observations[b*(11*15*10 + 47 + 10) + 11*15*10 + i]; - net->ob_player_discrete[b*47 + i] = ob; - net->ob_player_continuous[b*47 + i] = ob; - } - } - embedding(net->player_embed, net->ob_player_discrete); - - // Rewards - for (int b = 0; b < net->num_agents; b++) { - for (int i = 0; i < 10; i++) { - net->ob_reward[b*10 + i] = observations[b*(11*15*10 + 47 + 10) + 11*15*10 + 47 + i]; - } - } - - for (int b = 0; b < net->num_agents; b++) { - int b_offset = b*1817; - for (int i = 0; i < 256; i++) { - net->proj_buffer[b_offset + i] = net->map_conv2->output[b*256 + i]; - } - - b_offset += 256; - for (int i = 0; i < 47*32; i++) { - net->proj_buffer[b_offset + i] = net->player_embed->output[b*47*32 + i]; - } - - b_offset += 47*32; - for (int i = 0; i < 47; i++) { - net->proj_buffer[b_offset + i] = net->ob_player_continuous[b*47 + i]; - } - - b_offset += 47; - for (int i = 0; i < 10; i++) { - net->proj_buffer[b_offset + i] = net->ob_reward[b*10 + i]; - } - } - - linear(net->proj, net->proj_buffer); - relu(net->proj_relu, net->proj->output); - - lstm(net->lstm, net->proj_relu->output); - layernorm(net->layer_norm, net->lstm->state_h); - - linear(net->actor, net->layer_norm->output); - linear(net->value_fn, net->layer_norm->output); - - softmax_multidiscrete(net->multidiscrete, net->actor->output, actions); -} - -void demo(int num_players) { - srand(time(NULL)); - Weights* weights = load_weights("resources/nmmo3/nmmo3_weights.bin", 3387547); - MMONet* net = init_mmonet(weights, num_players); - - MMO env = { - .client = NULL, - .width = 512, - .height = 512, - .num_players = num_players, - .num_enemies = 2048, - .num_resources = 2048, - .num_weapons = 1024, - .num_gems = 512, - .tiers = 5, - .levels = 40, - .teleportitis_prob = 0.0, - .enemy_respawn_ticks = 2, - .item_respawn_ticks = 100, - .x_window = 7, - .y_window = 5, - }; - allocate_mmo(&env); - - c_reset(&env); - c_render(&env); - - int human_action = ATN_NOOP; - bool human_mode = false; - int i = 1; - while (!WindowShouldClose()) { - if (IsKeyPressed(KEY_LEFT_CONTROL)) { - human_mode = !human_mode; - } - if (i % 36 == 0) { - forward(net, env.observations, env.actions); - if (human_mode) { - env.actions[0] = human_action; - } - - c_step(&env); - human_action = ATN_NOOP; - } - int atn = c_render(&env); - if (atn != ATN_NOOP) { - human_action = atn; - } - i = (i + 1) % 36; - } - - free_mmonet(net); - free(weights); - free_allocated_mmo(&env); - //close_client(client); -} - -void test_mmonet_performance(int num_players, int timeout) { - Weights* weights = load_weights("nmmo3_weights.bin", 1101403); - MMONet* net = init_mmonet(weights, num_players); - - MMO env = { - .width = 512, - .height = 512, - .num_players = num_players, - .num_enemies = 128, - .num_resources = 32, - .num_weapons = 32, - .num_gems = 32, - .tiers = 5, - .levels = 7, - .teleportitis_prob = 0.001, - .enemy_respawn_ticks = 10, - .item_respawn_ticks = 200, - .x_window = 7, - .y_window = 5, - }; - allocate_mmo(&env); - c_reset(&env); - - int start = time(NULL); - int num_steps = 0; - while (time(NULL) - start < timeout) { - forward(net, env.observations, env.actions); - c_step(&env); - num_steps++; - } - - int end = time(NULL); - float sps = num_players * num_steps / (end - start); - printf("Test Environment Performance FPS: %f\n", sps); - free_allocated_mmo(&env); - free_mmonet(net); - free(weights); -} - -void copy_cast(float* input, unsigned char* output, int width, int height) { - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - output[adr] = 255*input[adr]; - } - } -} - -void raylib_grid(unsigned char* grid, int width, int height, int tile_size) { - InitWindow(width*tile_size, height*tile_size, "Raylib Grid"); - SetTargetFPS(1); - - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(BLACK); - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - unsigned char val = grid[adr]; - - Color color = (Color){val, val, val, 255}; - - int x = c*tile_size; - int y = r*tile_size; - DrawRectangle(x, y, tile_size, tile_size, color); - } } - EndDrawing(); - } - CloseWindow(); -} - -void raylib_grid_colored(unsigned char* grid, int width, int height, int tile_size) { - InitWindow(width*tile_size, height*tile_size, "Raylib Grid"); - SetTargetFPS(1); - - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(BLACK); - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = 3*(r*width + c); - unsigned char red = grid[adr]; - unsigned char green = grid[adr+1]; - unsigned char blue = grid[adr+2]; - - Color color = (Color){red, green, blue, 255}; - - int x = c*tile_size; - int y = r*tile_size; - DrawRectangle(x, y, tile_size, tile_size, color); - } - } - EndDrawing(); - } - CloseWindow(); -} - -void test_perlin_noise(int width, int height, - float base_frequency, int octaves, int seed) { - float terrain[width*height]; - perlin_noise((float*)terrain, width, height, base_frequency, octaves, seed, seed); - - unsigned char map[width*height]; - copy_cast((float*)terrain, (unsigned char*)map, width, height); - raylib_grid((unsigned char*)map, width, height, 1024.0/width); -} - -void test_flood_fill(int width, int height, int colors) { - unsigned char unfilled[width][height]; - memset(unfilled, 0, width*height); - - // Draw some squares - for (int i = 0; i < 32; i++) { - int w = rand() % width/4; - int h = rand() % height/4; - int start_r = rand() % (3*height/4); - int start_c = rand() % (3*width/4); - int end_r = start_r + h; - int end_c = start_c + w; - for (int r = start_r; r < end_r; r++) { - unfilled[r][start_c] = 1; - unfilled[r][end_c] = 1; - } - for (int c = start_c; c < end_c; c++) { - unfilled[start_r][c] = 1; - unfilled[end_r][c] = 1; - } - } - - char filled[width*height]; - flood_fill((unsigned char*)unfilled, (char*)filled, - width, height, colors, width*height); - - // Cast and colorize - unsigned char output[width*height]; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - int val = filled[adr]; - if (val == 0) { - output[adr] = 0; - } - output[adr] = 128 + (128/colors)*val; - } - } - - raylib_grid((unsigned char*)output, width, height, 1024.0/width); -} - -void test_cellular_automata(int width, int height, int colors, int max_fill) { - char grid[width][height]; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - grid[r][c] = -1; - } - } - - // Fill some squares - for (int i = 0; i < 32; i++) { - int w = rand() % width/4; - int h = rand() % height/4; - int start_r = rand() % (3*height/4); - int start_c = rand() % (3*width/4); - int end_r = start_r + h; - int end_c = start_c + w; - int color = rand() % colors; - for (int r = start_r; r < end_r; r++) { - for (int c = start_c; c < end_c; c++) { - grid[r][c] = color; - } - } - } - - cellular_automata((char*)grid, width, height, colors, max_fill); - - // Colorize - unsigned char output[width*height]; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int val = grid[r][c]; - int adr = r*width + c; - if (val == 0) { - output[adr] = 0; - } - output[adr] = (255/colors)*val; - } - } - - raylib_grid((unsigned char*)output, width, height, 1024.0/width); -} - -void test_generate_terrain(int width, int height, int x_border, int y_border) { - char terrain[width][height]; - unsigned char rendered[width][height][3]; - generate_terrain((char*)terrain, (unsigned char*)rendered, width, height, x_border, y_border); - - - // Colorize - /* - unsigned char output[width*height]; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int val = terrain[r][c]; - int adr = r*width + c; - if (val == 0) { - output[adr] = 0; - } - output[adr] = (255/4)*val; - } - } - */ - - raylib_grid_colored((unsigned char*)rendered, width, height, 1024.0/width); -} - -void test_performance(int num_players, int timeout) { - MMO env = { - .width = 512, - .height = 512, - .num_players = num_players, - .num_enemies = 128, - .num_resources = 32, - .num_weapons = 32, - .num_gems = 32, - .tiers = 5, - .levels = 7, - .teleportitis_prob = 0.001, - .enemy_respawn_ticks = 10, - .item_respawn_ticks = 200, - .x_window = 7, - .y_window = 5, - }; - allocate_mmo(&env); - c_reset(&env); - - int start = time(NULL); - int num_steps = 0; - while (time(NULL) - start < timeout) { - for (int i = 0; i < num_players; i++) { - env.actions[i] = rand() % 23; - } - c_step(&env); - num_steps++; - } - - int end = time(NULL); - float sps = num_players * num_steps / (end - start); - printf("Test Environment SPS: %f\n", sps); - free_allocated_mmo(&env); -} - -int main() { - - /* - int width = 512; - int height = 512; - float base_frequency = 1.0/64.0; - int octaves = 2; - int seed = 0; - test_perlin_noise(width, height, base_frequency, octaves, seed); - test_flood_fill(width, height, 4); - test_cellular_automata(width, height, 4, 4000); - test_generate_terrain(width, height, 8, 8); - */ - //test_performance(64, 10); - demo(NUM_AGENTS); - //test_mmonet_performance(1024, 10); -} diff --git a/pufferlib/ocean/nmmo3/nmmo3.h b/pufferlib/ocean/nmmo3/nmmo3.h deleted file mode 100644 index b1d5c22c4..000000000 --- a/pufferlib/ocean/nmmo3/nmmo3.h +++ /dev/null @@ -1,3170 +0,0 @@ -// Neural MMO 3 by Joseph Suarez -// This was the first new environment I started for Puffer Ocean. -// I started it in Cython and then ported it to C. This is why there -// are still some commented sections with features I didn't get to fully -// implement, like the command console. Feel free to add and PR! -// The assets get generated from a separate script. Message me if you need those, -// since they use ManaSeed assets. I've licensed them for the project but -// can't include the source files before they've gone through spritesheet gen. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "simplex.h" -#include "tile_atlas.h" -#include "raylib.h" - -#if defined(PLATFORM_DESKTOP) - #define GLSL_VERSION 330 -#else - #define GLSL_VERSION 100 -#endif - -// Play modes -#define MODE_PLAY 0 -#define MODE_BUY_TIER 1 -#define MODE_BUY_ITEM 2 -#define MODE_SELL_SELECT 3 -#define MODE_SELL_PRICE 4 - -// Animations -#define ANIM_IDLE 0 -#define ANIM_MOVE 1 -#define ANIM_ATTACK 2 -#define ANIM_SWORD 3 -#define ANIM_BOW 4 -#define ANIM_DEATH 5 -#define ANIM_RUN 6 - -// Actions - the order of these is used for offset math -#define ATN_DOWN 0 -#define ATN_UP 1 -#define ATN_RIGHT 2 -#define ATN_LEFT 3 -#define ATN_NOOP 4 -#define ATN_ATTACK 5 -#define ATN_UI 7 -#define ATN_ONE 8 -#define ATN_TWO 9 -#define ATN_THREE 10 -#define ATN_FOUR 11 -#define ATN_FIVE 12 -#define ATN_SIX 13 -#define ATN_SEVEN 14 -#define ATN_EIGHT 15 -#define ATN_NINE 16 -#define ATN_ZERO 17 -#define ATN_MINUS 18 -#define ATN_EQUALS 19 -#define ATN_BUY 20 -#define ATN_SELL 21 -#define ATN_DOWN_SHIFT 22 -#define ATN_UP_SHIFT 23 -#define ATN_RIGHT_SHIFT 24 -#define ATN_LEFT_SHIFT 25 - -// Entity types -#define ENTITY_NULL 0 -#define ENTITY_PLAYER 1 -#define ENTITY_ENEMY 2 - -// Elements -#define ELEM_NEUTRAL 0 -#define ELEM_FIRE 1 -#define ELEM_WATER 2 -#define ELEM_EARTH 3 -#define ELEM_AIR 4 - -// Tiles - -#define TILE_SPRING_GRASS 0 -#define TILE_SUMMER_GRASS 1 -#define TILE_AUTUMN_GRASS 2 -#define TILE_WINTER_GRASS 3 -#define TILE_SPRING_DIRT 4 -#define TILE_SUMMER_DIRT 5 -#define TILE_AUTUMN_DIRT 6 -#define TILE_WINTER_DIRT 7 -#define TILE_SPRING_STONE 8 -#define TILE_SUMMER_STONE 9 -#define TILE_AUTUMN_STONE 10 -#define TILE_WINTER_STONE 11 -#define TILE_SPRING_WATER 12 -#define TILE_SUMMER_WATER 13 -#define TILE_AUTUMN_WATER 14 -#define TILE_WINTER_WATER 15 - -// Entity -#define P_N 44 -#define P_TYPE 0 -#define P_COMB_LVL 1 -#define P_ELEMENT 2 -#define P_DIR 3 -#define P_ANIM 4 -#define P_HP 5 -#define P_HP_MAX 6 -#define P_PROF_LVL 7 -#define P_EQUIP_HELM 8 -#define P_EQUIP_CHEST 9 -#define P_EQUIP_LEGS 10 -#define P_EQUIP_WEAPON 11 -#define P_EQUIP_GEM 12 -#define P_UI_MODE 13 -#define P_MARKET_TIER 14 -#define P_SELL_IDX 15 -#define P_GOLD 16 -#define P_IN_COMBAT 17 -#define P_INVENTORY 18 -#define P_INVENTORY_SIZE 12 -#define P_EQUIP_BOOLS 30 -#define P_WANDER_RANGE 42 -#define P_RANGED 43 - -// Items -#define I_N 17 -#define I_NULL 0 -#define I_HELM 1 -#define I_CHEST 2 -#define I_LEGS 3 -#define I_SWORD 4 -#define I_BOW 5 -#define I_TOOL 6 -#define I_EARTH 7 -#define I_FIRE 8 -#define I_AIR 9 -#define I_WATER 10 -#define I_HERB 11 -#define I_ORE 12 -#define I_WOOD 13 -#define I_HILT 14 -#define I_SILVER 15 -#define I_GOLD 16 - -#define INVENTORY_SIZE 12 - -// Equipment -#define SLOT_HELM 0 -#define SLOT_CHEST 1 -#define SLOT_LEGS 2 -#define SLOT_HELD 3 -#define SLOT_GEM 4 - -// Map dims -#define D_N 2 -#define D_MAP 0 -#define D_ITEM 1 - -// Extra constants -#define IN_COMBAT_TICKS 5 -#define LEVEL_MUL 2.0 -#define EQUIP_MUL 1.0 -#define TIER_EXP_BASE 8 -#define MAX_TIERS 5 -#define NPC_AGGRO_RANGE 4 - -void range(int* array, int n) { - for (int i = 0; i < n; i++) { - array[i] = i; - } -} - -void shuffle(int* array, int n) { - for (int i = 0; i < n; i++) { - int j = rand() % n; - int temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } -} - -double sample_exponential(double halving_rate) { - double u = (double)rand() / RAND_MAX; // Random number u in [0, 1) - return 1 + halving_rate*(-log(1 - u) / log(2)); -} - -// Terrain gen -char ALL_GRASS[4] = {TILE_SPRING_GRASS, TILE_SUMMER_GRASS, TILE_AUTUMN_GRASS, TILE_WINTER_GRASS}; -char ALL_DIRT[4] = {TILE_SPRING_DIRT, TILE_SUMMER_DIRT, TILE_AUTUMN_DIRT, TILE_WINTER_DIRT}; -char ALL_STONE[4] = {TILE_SPRING_STONE, TILE_SUMMER_STONE, TILE_AUTUMN_STONE, TILE_WINTER_STONE}; -char ALL_WATER[4] = {TILE_SPRING_WATER, TILE_SUMMER_WATER, TILE_AUTUMN_WATER, TILE_WINTER_WATER}; - -unsigned char RENDER_COLORS[16][3] = { - {60, 220, 75}, // Spring grass - {20, 180, 40}, // Summer grass - {210, 180, 40}, // Autumn grass - {240, 250, 250}, // Winter grass - {130, 110, 70}, // Spring dirt - {160, 140, 70}, // Summer dirt - {140, 120, 90}, // Autumn dirt - {130, 120, 100}, // Winter dirt - {120, 120, 130}, // Spring stone - {110, 110, 120}, // Summer stone - {100, 100, 110}, // Autumn stone - {180, 180, 190}, // Winter stone - {70, 130, 180}, // Spring water - {0, 120, 200}, // Summer water - {50, 100, 160}, // Autumn water - {210, 240, 255}, // Winter water -}; - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; - float return_comb_lvl; - float return_prof_lvl; - float return_item_atk_lvl; - float return_item_def_lvl; - float return_market_buy; - float return_market_sell; - float return_death; - float min_comb_prof; - float purchases; - float sales; - float equip_attack; - float equip_defense; - float r; - float c; -}; - -// TODO: This is actually simplex and we should probably use the original impl -// ALSO: Not seeded correctly -void perlin_noise(float* map, int width, int height, - float base_frequency, int octaves, int offset_x, int offset_y) { - float frequencies[octaves]; - for (int i = 0; i < octaves; i++) { - frequencies[i] = base_frequency*pow(2, i); - } - - float min_value = FLT_MAX; - float max_value = FLT_MIN; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - for (int oct = 0; oct < octaves; oct++) { - float freq = frequencies[oct]; - map[adr] = noise2(freq*c + offset_x, freq*r + offset_y); - } - float val = map[adr]; - if (val < min_value) { - min_value = val; - } - if (val > max_value) { - max_value = val; - } - } - } - - // TODO: You scale wrong in the original code - float scale = 1.0/(max_value - min_value); - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - map[adr] = scale * (map[adr] - min_value); - } - } -} - -void flood_fill(unsigned char* input, char* output, - int width, int height, int n, int max_fill) { - - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - output[r*width + c] = -1; - } - } - - int* pos = calloc(width*height, sizeof(int)); - range((int*)pos, width*height); - shuffle((int*)pos, width*height); - - short queue[2*max_fill]; - for (int i = 0; i < 2*max_fill; i++) { - queue[i] = 0; - } - - for (int idx = 0; idx < width*height; idx++) { - int r = pos[idx] / width; - int c = pos[idx] % width; - int adr = r*width + c; - - if (input[adr] != 0 || output[adr] != -1) { - continue; - } - - int color = rand() % n; - output[adr] = color; - queue[0] = r; - queue[1] = c; - - int queue_idx = 0; - int queue_n = 1; - while (queue_idx < max_fill && queue_idx < queue_n) { - r = queue[2*queue_idx]; - c = queue[2*queue_idx + 1]; - - // These checks are done before adding, even though that is - // more verbose, to preserve the max q length. There are - // also some perf benefits with the bounds check - int dd = r-1; - adr = dd*width + c; - if (dd >= 0 && input[adr] == 0 && output[adr] == -1) { - output[adr] = color; - queue[2*queue_n] = dd; - queue[2*queue_n + 1] = c; - queue_n += 1; - if (queue_n == max_fill) { - break; - } - } - dd = c-1; - adr = r*width + dd; - if (dd >= 0 && input[adr] == 0 && output[adr] == -1) { - output[adr] = color; - queue[2*queue_n] = r; - queue[2*queue_n + 1] = dd; - queue_n += 1; - if (queue_n == max_fill) { - break; - } - } - dd = r+1; - adr = dd*width + c; - if (dd < width && input[adr] == 0 && output[adr] == -1) { - output[adr] = color; - queue[2*queue_n] = dd; - queue[2*queue_n + 1] = c; - queue_n += 1; - if (queue_n == max_fill) { - break; - } - } - dd = c+1; - adr = r*width + dd; - if (dd < width && input[adr] == 0 && output[adr] == -1) { - output[adr] = color; - queue[2*queue_n] = r; - queue[2*queue_n + 1] = dd; - queue_n += 1; - if (queue_n == max_fill) { - break; - } - } - queue_idx += 1; - } - } - free(pos); -} - -void cellular_automata(char* grid, - int width, int height, int colors, int max_fill) { - - int* pos = calloc(2*width*height, sizeof(int)); - int pos_sz = 0; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int inp_adr = r*width + c; - if (grid[inp_adr] != -1) { - continue; - } - pos[pos_sz] = r; - pos_sz++; - pos[pos_sz] = c; - pos_sz++; - } - } - - bool done = false; - while (!done) { - // In place shuffle on active buffer only - for (int i = 0; i < pos_sz; i+=2) { - int r = pos[i]; - int c = pos[i + 1]; - int adr = rand() % pos_sz; - if (adr % 2 == 1) { - adr--; - } - pos[i] = pos[adr]; - pos[i + 1] = pos[adr + 1]; - pos[adr] = r; - pos[adr + 1] = c; - } - - done = true; - int pos_adr = 0; - for (int i = 0; i < pos_sz; i+=2) { - int r = pos[i]; - int c = pos[i + 1]; - - int counts[colors]; - for (int i = 0; i < colors; i++) { - counts[i] = 0; - } - - bool no_neighbors = true; - for (int rr = r-1; rr <= r+1; rr++) { - for (int cc = c-1; cc <= c+1; cc++) { - if (rr < 0 || rr >= height || cc < 0 || cc >= width) { - continue; - } - int adr = rr*width + cc; - int val = grid[adr]; - if (val != -1) { - counts[val] += 1; - no_neighbors = false; - } - } - } - - if (no_neighbors) { - done = false; - pos[pos_adr] = r; - pos_adr++; - pos[pos_adr] = c; - pos_adr++; - continue; - } - - // Find maximum count and ties - int max_count = 0; - for (int i = 0; i < colors; i++) { - int val_count = counts[i]; - if (val_count > max_count) { - max_count = val_count; - } - } - int num_ties = 0; - for (int i = 0; i < colors; i++) { - if (counts[i] == max_count) { - num_ties += 1; - } - } - - int idx = 0; - int winner = rand() % num_ties; - for (int j = 0; j < colors; j++) { - if (counts[j] == max_count) { - if (idx == winner) { - int adr = r*width + c; - grid[adr] = j; - break; - } - idx += 1; - } - } - } - pos_sz = pos_adr; - pos_adr = 0; - } - free(pos); -} - -void generate_terrain(char* terrain, unsigned char* rendered, - int R, int C, int x_border, int y_border) { - // Perlin noise for the base terrain - // TODO: Not handling octaves correctly - float* perlin_map = calloc(R*C, sizeof(float)); - int offset_x = rand() % 100000; - int offset_y = rand() % 100000; - perlin_noise(perlin_map, C, R, 1.0/64.0, 2, offset_x, offset_y); - - // Flood fill connected components to determine biomes - unsigned char* ridges = calloc(R*C, sizeof(unsigned char)); - for (int r = 0; r < R; r++) { - for (int c = 0; c < C; c++) { - int adr = r*C + c; - ridges[adr] = (perlin_map[adr]>0.35) & (perlin_map[adr]<0.65); - } - } - char *biomes = calloc(R*C, sizeof(char)); - flood_fill(ridges, biomes, R, C, 4, 4000); - - // Cellular automata to cover unfilled ridges - cellular_automata(biomes, R, C, 4, 4000); - - unsigned char (*rendered_ary)[C][3] = (unsigned char(*)[C][3])rendered; - - for (int r = 0; r < R; r++) { - for (int c = 0; c < C; c++) { - int tile; - int adr = r*C + c; - if (r < y_border || r >= R-y_border || c < x_border || c >= C-x_border) { - tile = TILE_SPRING_WATER; - } else { - int season = biomes[adr]; - float val = perlin_map[adr]; - if (val > 0.75) { - tile = ALL_STONE[season]; - } else if (val < 0.25) { - tile = ALL_WATER[season]; - } else { - tile = ALL_GRASS[season]; - } - } - terrain[adr] = tile; - rendered_ary[r][c][0] = RENDER_COLORS[tile][0]; - rendered_ary[r][c][1] = RENDER_COLORS[tile][1]; - rendered_ary[r][c][2] = RENDER_COLORS[tile][2]; - } - } - free(perlin_map); - free(ridges); - free(biomes); -} - -typedef struct Entity Entity; -struct Entity { - int type; - int comb_lvl; - int element; - int dir; - int anim; - int hp; - int hp_max; - int prof_lvl; - int ui_mode; - int market_tier; - int sell_idx; - int gold; - int in_combat; - int equipment[5]; - int inventory[12]; - int is_equipped[12]; - int wander_range; - int ranged; - int goal; - int equipment_attack; - int equipment_defense; - int r; - int c; - int spawn_r; - int spawn_c; - int min_comb_prof[500]; - int min_comb_prof_idx; - int time_alive; - int purchases; - int sales; -}; - -typedef struct Item Item; -struct Item { - int id; - int type; - int tier; -}; - -Item ITEMS[(MAX_TIERS+1)*I_N + 1]; - -int item_index(int i, int tier) { - return (tier-1)*I_N + i; -} - -void init_items() { - Item* item_ptr; - for (int tier = 1; tier <= MAX_TIERS+1; tier++) { - for (int i = 1; i <= I_N; i++) { - int id = item_index(i, tier); - item_ptr = &ITEMS[id]; - item_ptr->id = id; - item_ptr->type = i; - item_ptr->tier = tier; - } - } -} - -#define MAX_MARKET_OFFERS 32 - -typedef struct MarketOffer MarketOffer; -struct MarketOffer { - int id; - int seller; - int price; -}; - -typedef struct ItemMarket ItemMarket; -struct ItemMarket { - MarketOffer offers[MAX_MARKET_OFFERS]; - int next_offer_id; - int item_id; - int stock; -}; - -// TODO: Redundant? -int peek_price(ItemMarket* market) { - int stock = market->stock; - if (stock == 0) { - return 0; - } - return market->offers[stock-1].price; -} - -typedef struct Reward Reward; -struct Reward{ - float death; - float pioneer; - float comb_lvl; - float prof_lvl; - float item_atk_lvl; - float item_def_lvl; - float item_tool_lvl; - float market_buy; - float market_sell; -}; - -typedef struct Respawnable Respawnable; -struct Respawnable { - int id; - int r; - int c; -}; - -typedef struct RespawnBuffer RespawnBuffer; -struct RespawnBuffer { - Respawnable* data; - int* lengths; - int ticks; - int size; -}; - -RespawnBuffer* make_respawn_buffer(int size, int ticks) { - RespawnBuffer* buffer = calloc(1, sizeof(RespawnBuffer)); - buffer->data = calloc(ticks*size, sizeof(Respawnable)); - buffer->lengths = calloc(ticks, sizeof(int)); - buffer->ticks = ticks; - buffer->size = size; - return buffer; -} - -bool has_elements(RespawnBuffer* buffer, int tick) { - return buffer->lengths[tick % buffer->ticks] > 0; -} - -void free_respawn_buffer(RespawnBuffer* buffer) { - free(buffer->data); - free(buffer->lengths); - free(buffer); -} - -void clear_respawn_buffer(RespawnBuffer* buffer) { - for (int i = 0; i < buffer->ticks; i++) { - buffer->lengths[i] = 0; - } -} - -void add_to_buffer(RespawnBuffer* buffer, Respawnable elem, int tick) { - tick = tick % buffer->ticks; - assert(buffer->lengths[tick] < buffer->size); - int offset = tick*buffer->size + buffer->lengths[tick]; - buffer->data[offset] = elem; buffer->lengths[tick] += 1; } - -Respawnable pop_from_buffer(RespawnBuffer* buffer, int tick) { - tick = tick % buffer->ticks; - assert(buffer->lengths[tick] > 0); - buffer->lengths[tick] -= 1; - int offset = tick*buffer->size + buffer->lengths[tick]; - return buffer->data[offset]; -} - -typedef struct Client Client; -typedef struct MMO MMO; -struct MMO { - Client* client; - int width; - int height; - int num_players; - int num_enemies; - int num_resources; - int num_weapons; - int num_gems; - char* terrain; // TODO: Unsigned? - unsigned char* rendered; - Entity* players; - Entity* enemies; - short* pids; - unsigned char* items; - unsigned char* counts; - unsigned char* observations; - float* rewards; - float* terminals; - Reward* reward_struct; - Reward* returns; - int* actions; - int tick; - int tiers; - int levels; - float teleportitis_prob; - int x_window; - int y_window; - int obs_size; - int enemy_respawn_ticks; - int item_respawn_ticks; - ItemMarket* market; - int market_buys; - int market_sells; - RespawnBuffer* resource_respawn_buffer; - RespawnBuffer* enemy_respawn_buffer; - RespawnBuffer* drop_respawn_buffer; - Log log; - float reward_combat_level; - float reward_prof_level; - float reward_item_level; - float reward_market; - float reward_death; -}; - -Entity* get_entity(MMO* env, int pid) { - if (pid < env->num_players) { - return &env->players[pid]; - } else { - return &env->enemies[pid - env->num_players]; - } -} - -void add_player_log(MMO* env, int pid) { - Reward * ret = &env->returns[pid]; - Entity* player = get_entity(env, pid); - Log* log = &env->log; - log->return_comb_lvl += ret->comb_lvl; - log->return_prof_lvl += ret->prof_lvl; - log->return_item_atk_lvl += ret->item_atk_lvl; - log->return_item_def_lvl += ret->item_def_lvl; - log->return_market_buy += ret->market_buy; - log->return_market_sell += ret->market_sell; - log->return_death += ret->death; - log->min_comb_prof += (player->prof_lvl > player->comb_lvl) ? player->comb_lvl : player->prof_lvl; - log->purchases += player->purchases; - log->sales += player->sales; - log->equip_attack += player->equipment_attack; - log->equip_defense += player->equipment_defense; - log->r += player->r; - log->c += player->c; - log->episode_return += ( - ret->comb_lvl + ret->prof_lvl - + ret->item_atk_lvl + ret->item_def_lvl - + ret->market_buy + ret->market_sell - + ret->death - ); - log->episode_length += player->time_alive; - log->score = log->min_comb_prof; - log->perf = log->min_comb_prof / (float)env->levels; - log->n++; - *ret = (Reward){0}; -} - -void init(MMO* env) { - init_items(); - - int sz = env->width*env->height; - env->counts = calloc(sz, sizeof(unsigned char)); - env->terrain = calloc(sz, sizeof(char)); - env->rendered = calloc(sz*3, sizeof(unsigned char)); - - env->pids = calloc(sz, sizeof(short)); - env->items = calloc(sz, sizeof(unsigned char)); - - // Circular buffers for respawning resources and enemies - env->resource_respawn_buffer = make_respawn_buffer(2*env->num_resources - + 2*env->num_weapons + 4*env->num_gems, env->item_respawn_ticks); - env->enemy_respawn_buffer = make_respawn_buffer( - env->num_enemies, env->enemy_respawn_ticks); - env->drop_respawn_buffer = make_respawn_buffer(2*env->num_enemies, 20); - - env->returns = calloc(env->num_players, sizeof(Reward)); - env->reward_struct = calloc(env->num_players, sizeof(Reward)); - env->players = calloc(env->num_players, sizeof(Entity)); - env->enemies = calloc(env->num_enemies, sizeof(Entity)); - - // TODO: Figure out how to cast to array. Size is static - int num_market = (MAX_TIERS+1)*(I_N+1); - env->market = (ItemMarket*)calloc(num_market, sizeof(ItemMarket)); -} - -void allocate_mmo(MMO* env) { - // TODO: Not hardcode - env->observations = calloc(env->num_players*(11*15*10+47+10), sizeof(unsigned char)); - env->rewards = calloc(env->num_players, sizeof(float)); - env->terminals = calloc(env->num_players, sizeof(float)); - env->actions = calloc(env->num_players, sizeof(int)); - init(env); -} - -void c_close(MMO* env) { - free(env->counts); - free(env->terrain); - free(env->rendered); - free(env->pids); - free(env->items); - free_respawn_buffer(env->resource_respawn_buffer); - free_respawn_buffer(env->enemy_respawn_buffer); - free_respawn_buffer(env->drop_respawn_buffer); - free(env->market); -} - -void free_allocated_mmo(MMO* env) { - free(env->observations); - free(env->rewards); - free(env->terminals); - free(env->returns); - free(env->reward_struct); - free(env->players); - free(env->enemies); - free(env->actions); - c_close(env); -} - -bool is_buy(int mode) { - return mode == MODE_BUY_TIER || mode == MODE_BUY_ITEM; -} - -bool is_sell(int mode) { - return mode == MODE_SELL_SELECT || mode == MODE_SELL_PRICE; -} - -bool is_move(int action) { - return action >= ATN_DOWN && action <= ATN_LEFT; -} - -bool is_run(int action) { - return action >= ATN_DOWN_SHIFT && action <= ATN_LEFT_SHIFT; -} - -bool is_num(int action) { - return action >= ATN_ONE && action <= ATN_NINE; -} - -int EFFECT_MATRIX[5][5] = { - {1, 1, 1, 1, 1}, - {1, 1, 0, 1, 2}, - {1, 2, 1, 0, 1}, - {1, 1, 2, 1, 0}, - {1, 0, 1, 2, 1}, -}; - -int DELTAS[4][2] = { - {1, 0}, - {-1, 0}, - {0, 1}, - {0, -1}, -}; - -int ATTACK_BASIC[4][1][2] = { - {{1, 0}}, - {{-1, 0}}, - {{0, 1}}, - {{0, -1}}, -}; - -int ATTACK_SWORD[4][3][2] = { - {{1, -1}, {1, 0}, {1, 1}}, - {{-1, -1}, {-1, 0}, {-1, 1}}, - {{-1, 1}, {0, 1}, {1, 1}}, - {{-1, -1}, {0, -1}, {1, -1}}, -}; - -int ATTACK_BOW[4][12][2] = { - {{1, 0}, {2, 0}, {3, 0}, {4, 0}}, - {{-1, 0}, {-2, 0}, {-3, 0}, {-4, 0}}, - {{0, 1}, {0, 2}, {0, 3}, {0, 4},}, - {{0, -1}, {0, -2}, {0, -3}, {0, -4}}, -}; - -float tier_level(float tier) { - return TIER_EXP_BASE*pow(2, tier-1); -} - -float level_tier(int level) { - if (level < TIER_EXP_BASE) { - return 1; - } - return 1 + ceil(log2(level/(float)TIER_EXP_BASE)); -} - -bool PASSABLE[16] = { - true, true, true, true, // Grass tiles - true, true, true, true, // Dirt tiles - false, false, false, false, // Stone tiles - false, false, false, false, // Water tiles -}; - -bool is_grass(int tile) { - return (tile >= TILE_SPRING_GRASS && tile <= TILE_WINTER_GRASS); -} - -bool is_dirt(int tile) { - return (tile >= TILE_SPRING_DIRT && tile <= TILE_WINTER_DIRT); -} - -bool is_stone(int tile) { - return (tile >= TILE_SPRING_STONE && tile <= TILE_WINTER_STONE); -} - -bool is_water(int tile) { - return (tile >= TILE_SPRING_WATER && tile <= TILE_WINTER_WATER); -} - -int map_offset(MMO* env, int r, int c) { - return r*env->width + c; -} - -float sell_price(int idx) { - return 0.5 + 0.1*idx; -} - -void compute_all_obs(MMO* env) { - for (int pid = 0; pid < env->num_players; pid++) { - Entity* player = get_entity(env, pid); - int r = player->r; - int c = player->c; - - int start_row = r - env->y_window; - int end_row = r + env->y_window + 1; - int start_col = c - env->x_window; - int end_col = c + env->x_window + 1; - - assert(start_row >= 0); - assert(end_row <= env->height); - assert(start_col >= 0); - assert(end_col <= env->width); - - int comb_lvl = player->comb_lvl; - int obs_adr = pid*(11*15*10+47+10); - for (int obs_r = start_row; obs_r < end_row; obs_r++) { - for (int obs_c = start_col; obs_c < end_col; obs_c++) { - int map_adr = map_offset(env, obs_r, obs_c); - - // Split by terrain type and season - unsigned char terrain = env->terrain[map_adr]; - env->observations[obs_adr] = terrain % 4; - env->observations[obs_adr+1] = terrain / 4; - - // Split by item type and tier - unsigned char item = env->items[map_adr]; - env->observations[obs_adr+2] = item % 17; - env->observations[obs_adr+3] = item / 17; - - int pid = env->pids[map_adr]; - if (pid != -1) { - Entity* seen = get_entity(env, pid); - env->observations[obs_adr+4] = seen->type; - env->observations[obs_adr+5] = seen->element; - int delta_comb_obs = (seen->comb_lvl - comb_lvl) / 2; - if (delta_comb_obs < 0) { - delta_comb_obs = 0; - } - if (delta_comb_obs > 4) { - delta_comb_obs = 4; - } - env->observations[obs_adr+6] = delta_comb_obs; - env->observations[obs_adr+7] = seen->hp / 20; // Bucketed for discrete - env->observations[obs_adr+8] = seen->anim; - env->observations[obs_adr+9] = seen->dir; - } - obs_adr += 10; - } - } - - // Player observation - env->observations[obs_adr] = player->type; - env->observations[obs_adr+1] = player->comb_lvl; - env->observations[obs_adr+2] = player->element; - env->observations[obs_adr+3] = player->dir; - env->observations[obs_adr+4] = player->anim; - env->observations[obs_adr+5] = player->hp; - env->observations[obs_adr+6] = player->hp_max; - env->observations[obs_adr+7] = player->prof_lvl; - env->observations[obs_adr+8] = player->ui_mode; - env->observations[obs_adr+9] = player->market_tier; - env->observations[obs_adr+10] = player->sell_idx; - env->observations[obs_adr+11] = player->gold; - env->observations[obs_adr+12] = player->in_combat; - for (int j = 0; j < 5; j++) { - env->observations[obs_adr+13+j] = player->equipment[j]; - } - for (int j = 0; j < 12; j++) { - env->observations[obs_adr+18+j] = player->inventory[j]; - } - for (int j = 0; j < 12; j++) { - env->observations[obs_adr+30+j] = player->is_equipped[j]; - } - env->observations[obs_adr+42] = player->wander_range; - env->observations[obs_adr+43] = player->ranged; - env->observations[obs_adr+44] = player->goal; - env->observations[obs_adr+45] = player->equipment_attack; - env->observations[obs_adr+46] = player->equipment_defense; - - // Reward observation - Reward* reward = &env->reward_struct[pid]; - env->observations[obs_adr+47] = (reward->death == 0) ? 0 : 1; - env->observations[obs_adr+48] = (reward->pioneer == 0) ? 0 : 1; - env->observations[obs_adr+49] = reward->comb_lvl / 20; - env->observations[obs_adr+50] = reward->prof_lvl / 20; - env->observations[obs_adr+51] = reward->item_atk_lvl / 20; - env->observations[obs_adr+52] = reward->item_def_lvl / 20; - env->observations[obs_adr+53] = reward->item_tool_lvl / 20; - env->observations[obs_adr+54] = reward->market_buy / 20; - env->observations[obs_adr+55] = reward->market_sell / 20; - } -} - -int safe_tile(MMO* env, int delta) { - bool valid = false; - int idx; - while (!valid) { - valid = true; - idx = rand() % (env->width * env->height); - char tile = env->terrain[idx]; - if (!is_grass(tile)) { - valid = false; - continue; - } - int r = idx / env->width; - int c = idx % env->width; - - for (int dr = -delta; dr <= delta; dr++) { - for (int dc = -delta; dc <= delta; dc++) { - int adr = map_offset(env, r+dr, c+dc); - if (env->pids[adr] != -1) { - valid = false; - break; - } - } - if (!valid) { - break; - } - } - } - return idx; -} - -// Spawns a player at the specified position. -// Can spawn on top of another player, but this will not corrupt the state. -// They will just move off of each other. -void spawn(MMO* env, Entity* entity) { - entity->hp = 99; - entity->time_alive = 0; - entity->purchases = 0; - entity->sales = 0; - - int idx = safe_tile(env, 5); - int r = idx / env->width; - int c = idx % env->width; - - //entity->r = entity->spawn_r; - //entity->c = entity->spawn_c; - entity->spawn_r = r; - entity->spawn_c = c; - entity->r = r; - entity->c = c; - - entity->anim = ANIM_IDLE; - entity->dir = ATN_DOWN; - entity->ui_mode = MODE_PLAY; - entity->gold = 0; - entity->in_combat = 0; - entity->equipment_attack = 0; - entity->equipment_defense = 0; - - // Try zeroing levels too - entity->prof_lvl = 1; - entity->comb_lvl = 1; - - entity->equipment[SLOT_HELM] = 0; - entity->equipment[SLOT_CHEST] = 0; - entity->equipment[SLOT_LEGS] = 0; - entity->equipment[SLOT_HELD] = 0; - entity->equipment[SLOT_GEM] = 0; - - int num_slots = sizeof(entity->inventory) / sizeof(entity->inventory[0]); - for (int idx = 0; idx < num_slots; idx++) { - entity->inventory[idx] = 0; - entity->is_equipped[idx] = 0; - } - - entity->goal = (rand() % 2) == 0; - memset(entity->min_comb_prof, 0, sizeof(entity->min_comb_prof)); - entity->min_comb_prof_idx = 0; -} - -void give_starter_gear(MMO* env, int pid, int tier) { - assert(tier >= 1); - assert(tier <= env->tiers); - - Entity* player = &env->players[pid]; - int idx = (rand() % 6) + 1; - tier = (rand() % tier) + 1; - player->inventory[0] = item_index(idx, tier); - player->gold += 50; -} - -int get_free_inventory_idx(MMO* env, int pid) { - Entity* player = &env->players[pid]; - // TODO: #define this - int num_slots = sizeof(player->inventory) / sizeof(player->inventory[0]); - for (int idx = 0; idx < num_slots; idx++) { - int item_type = player->inventory[idx]; - if (item_type == 0) { - return idx; - } - } - return -1; -} - -void pickup_item(MMO* env, int pid) { - Entity* player = &env->players[pid]; - if (player->type != ENTITY_PLAYER) { - return; - } - - int r = player->r; - int c = player->c; - int adr = map_offset(env, r, c); - int ground_id = env->items[adr]; - if (ground_id == 0) { - return; - } - - int inventory_idx = get_free_inventory_idx(env, pid); - if (inventory_idx == -1) { - return; - } - - Item* ground_item = &ITEMS[ground_id]; - int ground_type = ground_item->type; - - // This is the only item that can be picked up without a tool - if (ground_type == I_TOOL) { - player->inventory[inventory_idx] = ground_id; - env->items[adr] = 0; - return; - } - - int ground_tier = ground_item->tier; - int held_id = player->equipment[SLOT_HELD]; - Item* held_item = &ITEMS[held_id]; - int held_type = held_item->type; - int held_tier = held_item->tier; - if (held_type != I_TOOL) { - return; - } - if (held_tier < ground_tier) { - return; - } - - // Harvest resource - Respawnable respawnable = {.id = ground_id, .r = r, .c = c}; - add_to_buffer(env->resource_respawn_buffer, respawnable, env->tick); - Reward* reward = &env->reward_struct[pid]; - Reward* ret = &env->returns[pid]; - - // Level up for a worthy harvest - if (player->prof_lvl < env->levels && player->prof_lvl < tier_level(ground_tier)) { - player->prof_lvl += 1; - reward->prof_lvl = env->reward_prof_level; - ret->prof_lvl += env->reward_prof_level; - } - - // Some items are different on the ground and in inventory - if (ground_type == I_ORE) { - int armor_id = I_HELM + rand() % 3; - ground_id = item_index(armor_id, ground_tier); - } else if (ground_type == I_HILT) { - ground_id = item_index(I_SWORD, ground_tier); - } else if (ground_type == I_WOOD) { - ground_id = item_index(I_BOW, ground_tier); - } else { - ground_id = item_index(ground_type, ground_tier); - } - player->inventory[inventory_idx] = ground_id; - env->items[adr] = 0; -} - -bool dest_check(MMO* env, int r, int c); -inline bool dest_check(MMO* env, int r, int c) { - int adr = map_offset(env, r, c); - return PASSABLE[(int)env->terrain[adr]] & (env->pids[adr] == -1); -} - -void move(MMO* env, int pid, int direction, bool run) { - Entity* entity = get_entity(env, pid); - int r = entity->r; - int c = entity->c; - int dr = DELTAS[direction][0]; - int dc = DELTAS[direction][1]; - int rr = r + dr; - int cc = c + dc; - - entity->dir = direction; - - if (!dest_check(env, rr, cc)) { - return; - } - - if (run) { - rr += dr; - cc += dc; - if (!dest_check(env, rr, cc)) { - return; - } - } - - // Move to new pos. - entity->r = rr; - entity->c = cc; - entity->anim = (run ? ANIM_RUN : ANIM_MOVE); - env->pids[map_offset(env, rr, cc)] = pid; - - int old_adr = map_offset(env, r, c); - env->pids[old_adr] = -1; - - // Update visitation map. Skips run tiles - if (entity->type == ENTITY_PLAYER) { - if (env->counts[map_offset(env, rr, cc)] == 0) { - env->reward_struct[pid].pioneer = 1.0; - } - if (env->counts[map_offset(env, rr, cc)] < 255) { - env->counts[map_offset(env, rr, cc)] += 1; - } - pickup_item(env, pid); - } -} - -void wander(MMO* env, int pid) { - Entity* entity = get_entity(env, pid); - int wander_range = entity->wander_range; - int spawn_r = entity->spawn_r; - int spawn_c = entity->spawn_c; - int end_r = spawn_r; - int end_c = spawn_c; - - // Return entity to wander area - if (end_r - spawn_r > wander_range) { - move(env, pid, ATN_UP, false); - return; - } - if (end_r - spawn_r < -wander_range) { - move(env, pid, ATN_DOWN, false); - return; - } - if (end_c - spawn_c > wander_range) { - move(env, pid, ATN_LEFT, false); - return; - } - if (end_c - spawn_c < -wander_range) { - move(env, pid, ATN_RIGHT, false); - return; - } - - // Move randomly - int direction = rand() % 4; - if (direction == ATN_UP) { - end_r -= 1; - } else if (direction == ATN_DOWN) { - end_r += 1; - } else if (direction == ATN_LEFT) { - end_c -= 1; - } else if (direction == ATN_RIGHT) { - end_c += 1; - } - - move(env, pid, direction, false); -} - -// Agents gain 2 damage per level and 1 per equip level. With 3 -// pieces of equipment, that is a total of 5 per level. Base damage is -// 40 and enemies start with a decrease of 25. So with a 5 level difference, -// players and enemies are equally matched. -int calc_damage(MMO* env, int pid, int target_id) { - Entity* attacker = get_entity(env, pid); - Entity* defender = get_entity(env, target_id); - - int attack = 40 + LEVEL_MUL*attacker->comb_lvl + attacker->equipment_attack; - int defense = LEVEL_MUL*defender->comb_lvl + defender->equipment_defense; - - // These buffs compensate for enemies not having equipment - if (attacker->type == ENTITY_ENEMY) { - attack += 3*EQUIP_MUL*attacker->comb_lvl - 25; - } - if (defender->type == ENTITY_ENEMY) { - defense += 3*EQUIP_MUL*defender->comb_lvl; - } - - int damage = fmax(attack - defense, 0); - - // Not very / normal / super effective - return damage * EFFECT_MATRIX[attacker->element][defender->element]; -} - -int find_target(MMO* env, int pid, int entity_type) { - Entity* entity = get_entity(env, pid); - int r = entity->r; - int c = entity->c; - int weapon_id = entity->equipment[SLOT_HELD]; - int anim; - int* flat_deltas; - int num_deltas = 0; - if (weapon_id == 0 || ITEMS[weapon_id].type == I_TOOL) { - flat_deltas = (int*)ATTACK_BASIC; - anim = ANIM_ATTACK; - num_deltas = 1; - } else if (ITEMS[weapon_id].type == I_BOW) { - flat_deltas = (int*)ATTACK_BOW; - anim = ANIM_BOW; - num_deltas = 12; - } else if (ITEMS[weapon_id].type == I_SWORD) { - flat_deltas = (int*)ATTACK_SWORD; - anim = ANIM_SWORD; - num_deltas = 3; - } else { - assert(false); - exit(1); - } - - entity->anim = anim; - int (*deltas)[num_deltas][2] = (int(*)[num_deltas][2])flat_deltas; - for (int direction = 0; direction < 4; direction++) { - for (int idx = 0; idx < num_deltas; idx++) { - int dr = deltas[direction][idx][0]; - int dc = deltas[direction][idx][1]; - int rr = r + dr; - int cc = c + dc; - - int adr = map_offset(env, rr, cc); - int target_id = env->pids[adr]; - if (target_id == -1) { - continue; - } - - Entity* target = get_entity(env, target_id); - if (target->type != entity_type) { - continue; - } - - entity->dir = direction; - return target_id; - } - } - return -1; -} - -void drop_loot(MMO* env, int pid) { - Entity* entity = get_entity(env, pid); - int loot_tier = level_tier(entity->comb_lvl); - if (loot_tier > env->tiers) { - loot_tier = env->tiers; - } - - int drop = item_index(I_TOOL, loot_tier); - int r = entity->r; - int c = entity->c; - - // Drop loot on a free tile - for (int dr = -1; dr <= 1; dr++) { - for (int dc = -1; dc <= 1; dc++) { - int adr = map_offset(env, r+dr, c+dc); - if (env->items[adr] != 0) { - continue; - } - env->items[adr] = drop; - Respawnable elem = {.id = drop, .r = r+dr, .c = c+dc}; - add_to_buffer(env->drop_respawn_buffer, elem, env->tick); - return; - } - } -} - -void attack(MMO* env, int pid, int target_id) { - Entity* attacker = get_entity(env, pid); - Entity* defender = get_entity(env, target_id); - - // Extra check avoids multiple xp/loot drops - // if two players attack the same target at the same time - if (defender->hp == 0) { - return; - } - - attacker->in_combat = IN_COMBAT_TICKS; - defender->in_combat = IN_COMBAT_TICKS; - int dmg = calc_damage(env, pid, target_id); - - // Simple case: target survives - if (dmg < defender->hp) { - defender->hp -= dmg; - return; - } - - // Defender dies - defender->hp = 0; - if (defender->type == ENTITY_PLAYER) { - Reward* reward = &env->reward_struct[target_id]; - Reward* ret = &env->returns[target_id]; - reward->death = env->reward_death; - ret->death += env->reward_death; - env->reward_struct[target_id].death = -1; - add_player_log(env, target_id); - } else { - // Add to respawn buffer - Respawnable respawnable = {.id = target_id, - .r = defender->spawn_r, .c = defender->spawn_c}; - add_to_buffer(env->enemy_respawn_buffer, respawnable, env->tick); - } - - if (attacker->type == ENTITY_PLAYER) { - Reward* reward = &env->reward_struct[pid]; - Reward* ret = &env->returns[pid]; - int attacker_lvl = attacker->comb_lvl; - int defender_lvl = defender->comb_lvl; - - // Level up for defeating worthy foe - if (defender_lvl >= attacker_lvl && attacker_lvl < env->levels) { - attacker->comb_lvl += 1; - reward->comb_lvl = env->reward_combat_level; - ret->comb_lvl += env->reward_combat_level; - } - if (defender->type == ENTITY_ENEMY) { - drop_loot(env, target_id); - attacker->gold += 1 + defender_lvl / 10; - // Overflow - if (attacker->gold > 99) { - attacker->gold = 99; - } - } - } -} - -void use_item(MMO* env, int pid, int inventory_idx) { - Entity* player = &env->players[pid]; - Reward* reward = &env->reward_struct[pid]; - Reward* ret = &env->returns[pid]; - int item_id = player->inventory[inventory_idx]; - - if (item_id == 0) { - return; - } - - Item* item = &ITEMS[item_id]; - int item_type = item->type; - int tier = item->tier; - - // Consumable - if (item_type == I_HERB) { - int hp_restore = 50 + 10*tier; - if (player->hp > player->hp_max - hp_restore) { - player->hp = player->hp_max; - } else { - player->hp += hp_restore; - } - player->inventory[inventory_idx] = 0; - return; - } - - // Cannot equip in combat - if (player->in_combat > 0) { - return; - } - - int element = -1; - int attack = 0; - int defense = 0; - int equip_slot = 0; - - if (item_type == I_HELM) { - equip_slot = SLOT_HELM; - defense = EQUIP_MUL*tier_level(tier); - } else if (item_type == I_CHEST) { - equip_slot = SLOT_CHEST; - defense = EQUIP_MUL*tier_level(tier); - } else if (item_type == I_LEGS) { - equip_slot = SLOT_LEGS; - defense = EQUIP_MUL*tier_level(tier); - } else if (item_type == I_SWORD) { - equip_slot = SLOT_HELD; - attack = 3*EQUIP_MUL*tier_level(tier); - } else if (item_type == I_BOW) { - equip_slot = SLOT_HELD; - attack = 3*EQUIP_MUL*tier_level(tier - 0.5); - } else if (item_type == I_TOOL) { - equip_slot = SLOT_HELD; - } else if (item_type == I_EARTH) { - equip_slot = SLOT_GEM; - element = ELEM_EARTH; - } else if (item_type == I_FIRE) { - equip_slot = SLOT_GEM; - element = ELEM_FIRE; - } else if (item_type == I_AIR) { - equip_slot = SLOT_GEM; - element = ELEM_AIR; - } else if (item_type == I_WATER) { - equip_slot = SLOT_GEM; - element = ELEM_WATER; - } else { - exit(1); - } - - float item_reward = env->reward_item_level * (float)tier / env->tiers; - - // Unequip item if already equipped - if (player->is_equipped[inventory_idx]) { - player->is_equipped[inventory_idx] = 0; - player->equipment[equip_slot] = 0; - player->equipment_attack -= attack; - player->equipment_defense -= defense; - if (item_type == I_TOOL) { - reward->item_tool_lvl = -item_reward; - } else { - if (attack > 0) { - reward->item_atk_lvl = -item_reward; - ret->item_atk_lvl -= item_reward; - } - if (defense > 0) { - reward->item_def_lvl = -item_reward; - ret->item_def_lvl -= item_reward; - } - } - if (equip_slot == SLOT_GEM) { - player->element = ELEM_NEUTRAL; - } - return; - } - - // Another item is already equipped. We don't support switching - // gear without unequipping because it adds complexity to the item repr - if (player->equipment[equip_slot] != 0) { - return; - } - - // Equip the current item - player->is_equipped[inventory_idx] = 1; - player->equipment[equip_slot] = item_id; - player->equipment_attack += attack; - player->equipment_defense += defense; - if (item_type == I_TOOL) { - reward->item_tool_lvl = item_reward; - } else { - if (attack > 0) { - reward->item_atk_lvl = item_reward; - ret->item_atk_lvl += item_reward; - } - if (defense > 0) { - reward->item_def_lvl = item_reward; - ret->item_def_lvl += item_reward; - } - } - - // Update element for gems - if (element != -1) { - player->element = element; - } -} - -void enemy_ai(MMO* env, int pid) { - Entity* enemy = get_entity(env, pid); - int r = enemy->r; - int c = enemy->c; - - for (int rr = r-NPC_AGGRO_RANGE; rr <= r+NPC_AGGRO_RANGE; rr++) { - for (int cc = c-NPC_AGGRO_RANGE; cc <= c+NPC_AGGRO_RANGE; cc++) { - int adr = map_offset(env, rr, cc); - int target_id = env->pids[adr]; - if (target_id == -1 || target_id >= env->num_players) { - continue; - } - - int dr = rr - r; - int dc = cc - c; - int abs_dr = abs(dr); - int abs_dc = abs(dc); - - int direction; - if (enemy->ranged) { - if (abs_dr == 0 && abs_dc <= NPC_AGGRO_RANGE) { - direction = (dc > 0) ? ATN_RIGHT : ATN_LEFT; - enemy->anim = ANIM_BOW; - attack(env, pid, target_id); - } else if (abs_dc == 0 && abs_dr <= NPC_AGGRO_RANGE) { - direction = (dr > 0) ? ATN_DOWN : ATN_UP; - enemy->anim = ANIM_BOW; - attack(env, pid, target_id); - } else { - if (abs_dr > abs_dc) { - direction = (dc > 0) ? ATN_RIGHT : ATN_LEFT; - } else { - direction = (dr > 0) ? ATN_DOWN : ATN_UP; - } - // Move along shortest axis - move(env, pid, direction, false); - } - } else { - if (abs_dr + abs_dc == 1) { - if (dr > 0) { - direction = ATN_DOWN; - } else if (dr < 0) { - direction = ATN_UP; - } else if (dc > 0) { - direction = ATN_RIGHT; - } else { - direction = ATN_LEFT; - } - enemy->anim = ANIM_SWORD; - attack(env, pid, target_id); - } else { - // Move along longest axis - if (abs_dr > abs_dc) { - direction = (dr > 0) ? ATN_DOWN : ATN_UP; - } else { - direction = (dc > 0) ? ATN_RIGHT : ATN_LEFT; - } - move(env, pid, direction, false); - } - } - enemy->dir = direction; - return; - } - } - wander(env, pid); -} - -void c_reset(MMO* env) { - env->tick = 0; - - env->market_sells = 0; - env->market_buys = 0; - - clear_respawn_buffer(env->resource_respawn_buffer); - clear_respawn_buffer(env->enemy_respawn_buffer); - - // TODO: Check width/height args! - generate_terrain(env->terrain, env->rendered, env->width, env->height, - env->x_window, env->y_window); - - for (int i = 0; i < env->width*env->height; i++) { - env->pids[i] = -1; - env->items[i] = 0; - //env->counts[i] = 0; - } - - // Pid crops? - int ore_count = 0; - int herb_count = 0; - int wood_count = 0; - int hilt_count = 0; - int earth_gem_count = 0; - int fire_gem_count = 0; - int air_gem_count = 0; - int water_gem_count = 0; - int player_count = 0; - int enemy_count = 0; - - // Randomly generate spawn candidates - int *spawn_cands = calloc(env->width*env->height, sizeof(int)); - range((int*)spawn_cands, env->width*env->height); - shuffle((int*)spawn_cands, env->width*env->height); - - for (int cand_idx = 0; cand_idx < env->width*env->height; cand_idx++) { - int cand = spawn_cands[cand_idx]; - int r = cand / env->width; - int c = cand % env->width; - int tile = env->terrain[cand]; - - if (!is_grass(tile)) { - continue; - } - - // Materials only spawn south - //if (r < env->height/2) { - // continue; - //} - - int spawned = false; - int i_type; - for (int d = 0; d < 4; d++) { - int adr = map_offset(env, r+DELTAS[d][0], c+DELTAS[d][1]); - int tile = env->terrain[adr]; - if (is_stone(tile)) { - if (ore_count < env->num_resources) { - i_type = I_ORE; - ore_count += 1; - spawned = true; - break; - } - if (hilt_count < env->num_weapons) { - i_type = I_HILT; - hilt_count += 1; - spawned = true; - break; - } - } else if (is_water(tile)) { - if (herb_count < env->num_resources) { - i_type = I_HERB; - herb_count += 1; - spawned = true; - break; - } - if (wood_count < env->num_weapons) { - i_type = I_WOOD; - wood_count += 1; - spawned = true; - break; - } - } - } - - int adr = map_offset(env, r, c); - //int tier = 1 + env->tiers*level/env->levels; - int tier = 0; - while (tier < 1 || tier > env->tiers) { - tier = sample_exponential(1); - } - - if (spawned) { - env->items[adr] = item_index(i_type, tier); - continue; - } - - // Spawn gems - i_type = 0; - if (tile == TILE_SPRING_GRASS && earth_gem_count < env->num_gems) { - earth_gem_count += 1; - i_type = I_EARTH; - } else if (tile == TILE_SUMMER_GRASS && fire_gem_count < env->num_gems) { - fire_gem_count += 1; - i_type = I_FIRE; - } else if (tile == TILE_AUTUMN_GRASS && air_gem_count < env->num_gems) { - air_gem_count += 1; - i_type = I_AIR; - } else if (tile == TILE_WINTER_GRASS && water_gem_count < env->num_gems) { - water_gem_count += 1; - i_type = I_WATER; - } - - if (i_type > 0) { - env->items[adr] = item_index(i_type, tier); - } - - if ( - player_count == env->num_players && - enemy_count == env->num_enemies && - ore_count == env->num_resources && - herb_count == env->num_resources && - wood_count == env->num_weapons && - hilt_count == env->num_weapons && - earth_gem_count == env->num_gems && - fire_gem_count == env->num_gems && - air_gem_count == env->num_gems && - water_gem_count == env->num_gems - ) { - break; - } - } - - assert(ore_count == env->num_resources); - assert(herb_count == env->num_resources); - assert(wood_count == env->num_weapons); - assert(hilt_count == env->num_weapons); - assert(earth_gem_count == env->num_gems); - assert(fire_gem_count == env->num_gems); - assert(air_gem_count == env->num_gems); - assert(water_gem_count == env->num_gems); - free(spawn_cands); - - //int distance = abs(r - env->height/2); - for (int player_count = 0; player_count < env->num_players; player_count++) { - int pid = player_count; - Entity* player = &env->players[pid]; - player->type = ENTITY_PLAYER; - player->element = ELEM_NEUTRAL; - player->comb_lvl = 1; - player->prof_lvl = 1; - player->hp_max = 99; - spawn(env, player); - int adr = map_offset(env, player->r, player->c); - env->pids[adr] = pid; - // Debug starter gear - //give_starter_gear(env, pid, env->tiers); - } - - // Spawn enemies off of middle Y - //int level = fmax(1, env->levels * (distance-12) / (0.9*env->height/2 - 24)); - //level = fmin(level, env->levels); - for (int enemy_count = 0; enemy_count < env->num_enemies; enemy_count++) { - int level = 0; - while (level < 1 || level > env->levels) { - level = sample_exponential(8); - } - if (rand() % 8 == 0) { - level = 1; - } - //if (distance > 8 && r < env->height/2 && enemy_count < env->num_enemies) { - Entity* enemy = &env->enemies[enemy_count]; - enemy->type = ENTITY_ENEMY; - enemy->hp_max = 99; - enemy->wander_range = 3; - - spawn(env, enemy); - int adr = map_offset(env, enemy->r, enemy->c); - char tile = env->terrain[adr]; - - int element = ELEM_NEUTRAL; - int ranged = true; - if (level < 15) { - ranged = false; - } else if (tile == TILE_SPRING_GRASS) { - element = ELEM_EARTH; - } else if (tile == TILE_SUMMER_GRASS) { - element = ELEM_FIRE; - } else if (tile == TILE_AUTUMN_GRASS) { - element = ELEM_AIR; - } else if (tile == TILE_WINTER_GRASS) { - element = ELEM_WATER; - } - enemy->element = element; - enemy->ranged = ranged; - - env->pids[adr] = env->num_players + enemy_count; - enemy->comb_lvl = level; - } - - compute_all_obs(env); -} - -void c_step(MMO* env) { - env->tick += 1; - int tick = env->tick; - - // Respawn resources - RespawnBuffer* buffer = env->resource_respawn_buffer; - while (has_elements(buffer, tick)) { - Respawnable item = pop_from_buffer(buffer, tick); - int item_id = item.id; - assert(item_id > 0); - int adr = map_offset(env, item.r, item.c); - env->items[adr] = item_id; - } - - // Respawn enemies - buffer = env->enemy_respawn_buffer; - while (has_elements(buffer, tick)) { - int pid = pop_from_buffer(buffer, tick).id; - assert(pid >= 0); - Entity* entity = get_entity(env, pid); - int lvl = entity->comb_lvl; - spawn(env, entity); - int adr = map_offset(env, entity->r, entity->c); - env->pids[adr] = pid; - entity->comb_lvl = lvl; - } - - // Despawn dropped items - buffer = env->drop_respawn_buffer; - while (has_elements(buffer, tick)) { - Respawnable item = pop_from_buffer(buffer, tick); - int id = item.id; - int r = item.r; - int c = item.c; - int adr = map_offset(env, r, c); - if (env->items[adr] == id) { - env->items[adr] = 0; - } - } - - for (int pid = 0; pid < env->num_players + env->num_enemies; pid++) { - Entity* entity = get_entity(env, pid); - entity->time_alive += 1; - int entity_type = entity->type; - int r = entity->r; - int c = entity->c; - int adr = map_offset(env, r, c); - - // Respawn dead entity - if (entity->hp == 0) { - if (entity->anim != ANIM_DEATH) { - entity->anim = ANIM_DEATH; - } else if (env->pids[adr] == pid) { - env->pids[adr] = -1; - } else if (entity_type == ENTITY_PLAYER) { - spawn(env, entity); - adr = map_offset(env, entity->r, entity->c); - env->pids[adr] = pid; - //give_starter_gear(env, pid, env->tiers); - } - continue; - } - - // Teleportitis: Randomly teleport players and enemies - // to a safe tile. This prevents players from clumping - // and messing up training dynamics - double prob = (double)rand() / RAND_MAX; - if (prob < env->teleportitis_prob) { - r = entity->r; - c = entity->c; - adr = map_offset(env, r, c); - env->pids[adr] = -1; - - int idx = safe_tile(env, 5); - r = idx / env->width; - c = idx % env->width; - - adr = map_offset(env, r, c); - env->pids[adr] = pid; - - entity->r = r; - entity->c = c; - } - - if (entity_type == ENTITY_PLAYER) { - int min_comb_prof = entity->prof_lvl; - if (min_comb_prof > entity->comb_lvl) { - min_comb_prof = entity->comb_lvl; - } - entity->min_comb_prof[entity->min_comb_prof_idx] = min_comb_prof; - entity->min_comb_prof_idx += 1; - if (entity->min_comb_prof_idx == 500) { - entity->min_comb_prof_idx = 0; - if (min_comb_prof <= entity->min_comb_prof[0]) { - add_player_log(env, pid); - - // Has not improved in 500 ticks - r = entity->r; - c = entity->c; - adr = map_offset(env, r, c); - env->pids[adr] = -1; - int lvl = entity->comb_lvl; - spawn(env, entity); - r = entity->r; - c = entity->c; - adr = map_offset(env, r, c); - env->pids[adr] = pid; - if (entity->type == ENTITY_PLAYER) { - //give_starter_gear(env, pid, env->tiers); - } else { - entity->comb_lvl = lvl; - } - continue; - } - } - } - - entity->anim = ANIM_IDLE; - - // Restore 1 HP each tick - if (entity->hp < entity->hp_max) { - entity->hp += 1; - } - - // Decrement combat counter - if (entity->in_combat > 0) { - entity->in_combat -= 1; - } - - // Enemy AI - if (entity_type == ENTITY_ENEMY) { - enemy_ai(env, pid); - continue; - } - - Reward* reward = &env->reward_struct[pid]; - *reward = (Reward){0}; - - // Update entity heading - int action = env->actions[pid]; - if (is_move(action)) { - entity->dir = action - ATN_DOWN; - } else if (is_run(action)) { - entity->dir = action - ATN_DOWN_SHIFT; - } - - // Market mode - int ui_mode = entity->ui_mode; - if (is_buy(ui_mode)) { - if (action != ATN_NOOP) { - entity->ui_mode = MODE_PLAY; - } - if (!is_num(action)) { - continue; - } - if (entity->in_combat > 0) { - continue; - } - int action_idx = action - ATN_ONE; - if (ui_mode == MODE_BUY_TIER) { - if (action_idx >= env->tiers) { - continue; - } - entity->market_tier = action_idx + 1; - entity->ui_mode = MODE_BUY_ITEM; - continue; - } - if (action_idx >= 11) { - continue; - } - int market_tier = entity->market_tier; - int item_id = I_N*(market_tier - 1) + action_idx + 1; - ItemMarket* market = &env->market[item_id]; - - int stock = market->stock; - if (stock == 0) { - continue; - } - - MarketOffer* offer = &market->offers[stock-1]; - - int price = offer->price; - Entity* buyer = get_entity(env, pid); - if (buyer->gold < price) { - continue; - } - - int inventory_idx = get_free_inventory_idx(env, pid); - if (inventory_idx == -1) { - continue; - } - - buyer->gold -= price; - buyer->inventory[inventory_idx] = market->item_id; - - Entity* seller = &env->players[offer->seller]; - seller->gold += price; - if (seller->gold > 99) { - seller->gold = 99; - } - - market->stock -= 1; - env->market_buys += 1; - reward->market_buy = env->reward_market; - Reward* ret = &env->returns[pid]; - ret->market_buy += env->reward_market; - // env->rewards[buyer_id].gold += price; - // if (env->rewards[buyer_id].gold > 99) { - // env->rewards[buyer_id].gold = 99; - // } - - entity->inventory[inventory_idx] = item_id; - entity->purchases += 1; - } else if (is_sell(ui_mode)) { - if (action != ATN_NOOP) { - entity->ui_mode = MODE_PLAY; - } - if (!is_num(action)) { - continue; - } - if (entity->in_combat > 0) { - continue; - } - int action_idx = action - ATN_ONE; - if (ui_mode == MODE_SELL_SELECT) { - int item_type = entity->inventory[action_idx]; - if (item_type == 0) { - continue; - } - entity->sell_idx = action_idx; - entity->ui_mode = MODE_SELL_PRICE; - continue; - } - int price = action_idx + 1; //sell_price(action_idx); - int inventory_idx = entity->sell_idx; - int item_type = entity->inventory[inventory_idx]; - if (item_type == 0) { - continue; - } - if (entity->is_equipped[inventory_idx]) { - use_item(env, pid, inventory_idx); - } - - ItemMarket* market = &env->market[item_type]; - int stock = market->stock; - - // TODO: Will have to update once prices become dynamic - if (stock == MAX_MARKET_OFFERS) { - continue; - } - - MarketOffer* offer = &market->offers[stock]; - offer->id = market->next_offer_id; - offer->seller = pid; - offer->price = price; - market->next_offer_id += 1; - market->stock += 1; - - entity->inventory[inventory_idx] = 0; - entity->sales += 1; - env->market_sells += 1; - reward->market_sell = env->reward_market; - Reward* ret = &env->returns[pid]; - ret->market_sell += env->reward_market; - } else if (action == ATN_ATTACK) { - int target_id = find_target(env, pid, ENTITY_ENEMY); - if (target_id != -1) { - attack(env, pid, target_id); - } - } else if (is_move(action)) { - move(env, pid, action, false); - } else if (is_run(action)) { - move(env, pid, action - ATN_DOWN_SHIFT, true); - } else if (is_num(action)) { - use_item(env, pid, action - ATN_ONE); - } else if (action == ATN_BUY) { - entity->ui_mode = MODE_BUY_TIER; - } else if (action == ATN_SELL) { - entity->ui_mode = MODE_SELL_SELECT; - } - } - compute_all_obs(env); - for (int pid = 0; pid < env->num_players; pid++) { - Reward* reward = &env->reward_struct[pid]; - env->rewards[pid] = ( - reward->death + reward->comb_lvl - + reward->prof_lvl + reward->item_atk_lvl + reward->item_def_lvl - + reward->market_buy + reward->market_sell - ); - } -} - -#define FRAME_RATE 60 -#define TICK_FRAMES 36 -#define DELAY_FRAMES 24 -#define SPRITE_SIZE 128 -#define TILE_SIZE 64 -#define X_WINDOW 7 -#define Y_WINDOW 5 - -#define SCREEN_WIDTH TILE_SIZE * (2*X_WINDOW + 1) -#define SCREEN_HEIGHT TILE_SIZE * (2*Y_WINDOW + 1) - -#define NUM_PLAYER_TEXTURES 10 - -// Health bars -#define HEALTH_BAR_WIDTH 48 -#define HEALTH_BAR_HEIGHT 6 - -#define WATER_ANIMS 3 -#define WATER_ANIM_FRAMES 4 -#define WATER_TICKS_PER_FRAME TICK_FRAMES / WATER_ANIM_FRAMES - -#define COMMAND_CHARS 16 - -#define ANIM_IDLE 0 -#define ANIM_MOVE 1 -#define ANIM_ATTACK 2 -#define ANIM_SWORD 3 -#define ANIM_BOW 4 -#define ANIM_DEATH 5 -#define ANIM_RUN 6 - -#define MAX_ANIM_FRAMES 16 -#define SPRITE_SIZE 128 - -#define OVERLAY_NONE 0 -#define OVERLAY_COUNTS 1 -#define OVERLAY_VALUE 2 - -#define ITEM_TYPES 17 - -int ITEM_TEXTURES[ITEM_TYPES*MAX_TIERS]; - -struct Client { - Texture2D tiles; - Texture2D players[5][NUM_PLAYER_TEXTURES]; - Shader shader; - int shader_camera_x_loc; - float shader_camera_x; - int shader_camera_y_loc; - float shader_camera_y; - int shader_time_loc; - float shader_time; - int shader_terrain_loc; - int shader_map_width_loc; - int shader_map_height_loc; - unsigned char *shader_terrain_data; - Texture2D shader_terrain; - int shader_texture_tiles_loc; - Texture2D shader_texture_tiles; - int shader_resolution_loc; - float shader_resolution[3]; - - //Texture2D players; - Texture2D items; - Texture2D inventory; - Texture2D inventory_equip; - Texture2D inventory_selected; - Font font; - int* terrain; - int command_mode; - char command[COMMAND_CHARS]; - int command_len; - Camera2D camera; - RenderTexture2D map_buffer; - RenderTexture2D ui_buffer; - int render_mode; - Texture2D overlay_texture; - int active_overlay; - int my_player; - int start_time; - int frame; -}; - -#define TILE_SPRING_GRASS 0 -#define TILE_SUMMER_GRASS 1 -#define TILE_AUTUMN_GRASS 2 -#define TILE_WINTER_GRASS 3 -#define TILE_SPRING_DIRT 4 -#define TILE_SUMMER_DIRT 5 -#define TILE_AUTUMN_DIRT 6 -#define TILE_WINTER_DIRT 7 -#define TILE_SPRING_STONE 8 -#define TILE_SUMMER_STONE 9 -#define TILE_AUTUMN_STONE 10 -#define TILE_WINTER_STONE 11 -#define TILE_SPRING_WATER 12 -#define TILE_SUMMER_WATER 13 -#define TILE_AUTUMN_WATER 14 -#define TILE_WINTER_WATER 15 - -#define RENDER_MODE_FIXED 0 -#define RENDER_MODE_CENTERED 1 - -char* KEYS[12] = { - "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=" -}; - -int TILE_UV[16][2] = { - {0, 8*TILE_SIZE}, - {0, 13*TILE_SIZE}, - {4*TILE_SIZE, 8*TILE_SIZE}, - {2*TILE_SIZE, 8*TILE_SIZE}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {8*TILE_SIZE, 3*TILE_SIZE}, - {8*TILE_SIZE, 3*TILE_SIZE}, - {8*TILE_SIZE, 3*TILE_SIZE}, - {8*TILE_SIZE, 3*TILE_SIZE}, - {0, 4*TILE_SIZE}, - {0, 4*TILE_SIZE}, - {0, 4*TILE_SIZE}, - {0, 4*TILE_SIZE}, -}; - -typedef struct Animation Animation; -struct Animation { - int num_frames; - int tiles_traveled; - int offset; // Number of tiles from the top of the sheet - int frames[10]; // Order of frames in sheet, left to right -}; - -Animation ANIMATIONS[7] = { - (Animation){ // ANIM_IDLE - .num_frames = 1, - .tiles_traveled = 0, - .offset = 0, - .frames = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - }, - (Animation){ // ANIM_MOVE - .num_frames = 6, - .tiles_traveled = 1, - .offset = 4, - .frames = {0, 1, 2, 3, 4, 5, 0, 0, 0, 0} - }, - (Animation){ // ANIM_ATTACK - .num_frames = 2, - .tiles_traveled = 0, - .offset = 0, - .frames = {1, 2, 0, 0, 0, 0, 0, 0, 0, 0} - }, - (Animation){ // ANIM_SWORD - .num_frames = 8, - .tiles_traveled = 0, - .offset = 8, - .frames = {0, 1, 6, 3, 4, 7, 0, 0, 0, 0} - }, - (Animation){ // ANIM_BOW - .num_frames = 8, - .tiles_traveled = 0, - .offset = 0, - .frames = {8, 9, 10, 11, 12, 13, 14, 15, 0, 0} - }, - (Animation){ // ANIM_DEATH - .num_frames = 3, - .tiles_traveled = 0, - .offset = 0, - .frames = {5, 6, 7, 0, 0, 0, 0, 0, 0, 0} - }, - (Animation){ // ANIM_RUN - .num_frames = 6, - .tiles_traveled = 2, - .offset = 4, - .frames = {0, 1, 6, 3, 4, 7, 0, 0, 0, 0} - }, -}; - -#define TEX_FULL -2 -#define TEX_EMPTY -1 -#define TEX_TL_CORNER 0 -#define TEX_T_FLAT 1 -#define TEX_TR_CORNER 2 -#define TEX_L_FLAT 3 -#define TEX_CENTER 4 -#define TEX_R_FLAT 5 -#define TEX_BL_CORNER 6 -#define TEX_B_FLAT 7 -#define TEX_BR_CORNER 8 -#define TEX_TL_DIAG 9 -#define TEX_TR_DIAG 10 -#define TEX_BL_DIAG 11 -#define TEX_BR_DIAG 12 -#define TEX_TRR_DIAG 13 -#define TEX_BRR_DIAG 14 - -#define OFF 20 -#define GRASS_OFFSET 512+32 -#define WATER_OFFSET OFF * 2 -#define STONE_OFFSET OFF * 1 -#define DIRT_OFFSET 0 - -void render_conversion(char* flat_tiles, int* flat_converted, int R, int C) { - char* tex_codes = tile_atlas; - char (*tiles)[C] = (char(*)[C])flat_tiles; - int (*converted)[C] = (int(*)[C])flat_converted; - - for (int r = 1; r < R-1; r++) { - for (int c = 1; c < C-1; c++) { - int tile = tiles[r][c]; - assert(flat_tiles[r*C + c] == tile); - int byte_code = 0; - if (is_grass(tile)) { - byte_code = 255; - } else { - if (tiles[r-1][c-1] != tile) { - byte_code += 128; - } - if (tiles[r-1][c] != tile) { - byte_code += 64; - } - if (tiles[r-1][c+1] != tile) { - byte_code += 32; - } - if (tiles[r][c-1] != tile) { - byte_code += 16; - } - if (tiles[r][c+1] != tile) { - byte_code += 8; - } - if (tiles[r+1][c-1] != tile) { - byte_code += 4; - } - if (tiles[r+1][c] != tile) { - byte_code += 2; - } - if (tiles[r+1][c+1] != tile) { - byte_code += 1; - } - } - - // Code maps local tile regions to a snapping tile index - int code = tex_codes[byte_code]; - int idx = code; - if (code == TEX_FULL) { - if (is_dirt(tile)) { - idx = DIRT_OFFSET + rand() % 5; - } else if (is_stone(tile)) { - idx = STONE_OFFSET + rand() % 5; - } else if (is_water(tile)) { - idx = WATER_OFFSET + rand() % 5; - } - } else if (is_dirt(tile)) { - idx += DIRT_OFFSET + 5; - } else if (is_stone(tile)) { - idx += STONE_OFFSET + 5; - } else if (is_water(tile)) { - idx += WATER_OFFSET + 5; - } - - if (!is_grass(tile)) { - if (tile == TILE_SUMMER_DIRT || tile == TILE_SUMMER_STONE - || tile == TILE_SUMMER_WATER) { - idx += 3*OFF; - } else if (tile == TILE_AUTUMN_DIRT || tile == TILE_AUTUMN_STONE - || tile == TILE_AUTUMN_WATER) { - idx += 6*OFF; - } else if (tile == TILE_WINTER_DIRT || tile == TILE_WINTER_STONE - || tile == TILE_WINTER_WATER) { - idx += 9*OFF; - } - } - if (is_grass(tile) || code == TEX_EMPTY) { - int num_spring = 0; - int num_summer = 0; - int num_autumn = 0; - int num_winter = 0; - for (int rr = r-1; rr <= r+1; rr++) { - for (int cc = c-1; cc <= c+1; cc++) { - int tile = tiles[rr][cc]; - if (tile == TILE_SPRING_GRASS) { - num_spring += 1; - } else if (tile == TILE_SUMMER_GRASS) { - num_summer += 1; - } else if (tile == TILE_AUTUMN_GRASS) { - num_autumn += 1; - } else if (tile == TILE_WINTER_GRASS) { - num_winter += 1; - } - } - } - - if (num_spring == 0 && num_summer == 0 - && num_autumn == 0 && num_winter == 0) { - idx = 240; - } else { - int lookup = (1000*num_spring + 100*num_summer - + 10*num_autumn + num_winter); - int offset = (rand() % 4) * 714; // num_lerps; - idx = lerps[lookup] + offset + 240 + 5*4*3*4; - } - } - if (code == TEX_FULL && is_water(tile)) { - int variant = (rand() % 5); - int anim = rand() % 3; - idx = 240 + 3*4*4*variant + 4*4*anim; - if (tile == TILE_SPRING_WATER) { - idx += 0; - } else if (tile == TILE_SUMMER_WATER) { - idx += 4; - } else if (tile == TILE_AUTUMN_WATER) { - idx += 8; - } else if (tile == TILE_WINTER_WATER) { - idx += 12; - } - } - converted[r][c] = idx; - } - } -} - -Client* make_client(MMO* env) { - InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "NMMO3"); - SetTargetFPS(FRAME_RATE); - - Client* client = calloc(1, sizeof(Client)); - client->start_time = time(NULL); - client->frame = 0; - client->command_len = 0; - - client->terrain = calloc(env->height*env->width, sizeof(int)); - render_conversion(env->terrain, client->terrain, env->height, env->width); - - client->shader = LoadShader("", TextFormat("resources/nmmo3/map_shader_%i.fs", GLSL_VERSION)); - - // TODO: These should be int locs? - client->shader_map_width_loc = GetShaderLocation(client->shader, "map_width"); - client->shader_map_height_loc = GetShaderLocation(client->shader, "map_height"); - client->shader_camera_x_loc = GetShaderLocation(client->shader, "camera_x"); - client->shader_camera_y_loc = GetShaderLocation(client->shader, "camera_y"); - client->shader_time_loc = GetShaderLocation(client->shader, "time"); - client->shader_resolution_loc = GetShaderLocation(client->shader, "resolution"); - client->shader_texture_tiles_loc = GetShaderLocation(client->shader, "texture_tiles"); - client->shader_terrain_loc = GetShaderLocation(client->shader, "terrain"); - Image img = GenImageColor(env->width, env->height, WHITE); - ImageFormat(&img, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - client->shader_terrain = LoadTextureFromImage(img); - UnloadImage(img); - client->shader_terrain_data = malloc(env->width*env->height*4); - //SetShaderValue(client->shader, client->shader_terrain_loc, &client->terrain, SHADER_UNIFORM_INT); - - for (int i = 0; i < env->width*env->height; i++) { - int tile = client->terrain[i]; - //if (tile >= 240 && tile < 240+4*4*4*4) { - // tile += 3.9*delta; - //} - - client->shader_terrain_data[4*i] = tile/64; - client->shader_terrain_data[4*i+1] = tile%64; - //client->shader_terrain_data[2*i] = 0; - //client->shader_terrain_data[2*i+1] = 0; - client->shader_terrain_data[4*i+2] = 0; - client->shader_terrain_data[4*i+3] = 255; - } - - client->render_mode = RENDER_MODE_CENTERED; - client->tiles = LoadTexture("resources/nmmo3/merged_sheet.png"); - - //client->players = LoadTexture("../resource/neutral_0.png"); - client->items = LoadTexture("resources/nmmo3/items_condensed.png"); - client->inventory = LoadTexture("resources/nmmo3/inventory_64.png"); - client->inventory_equip = LoadTexture("resources/nmmo3/inventory_64_selected.png"); - client->inventory_selected = LoadTexture("resources/nmmo3/inventory_64_press.png"); - client->font = LoadFont("resources/nmmo3/ManaSeedBody.ttf"); - for (int i = 0; i < NUM_PLAYER_TEXTURES; i++) { - client->players[0][i] = LoadTexture(TextFormat("resources/nmmo3/neutral_%d.png", i)); - client->players[1][i] = LoadTexture(TextFormat("resources/nmmo3/fire_%d.png", i)); - client->players[2][i] = LoadTexture(TextFormat("resources/nmmo3/water_%d.png", i)); - client->players[3][i] = LoadTexture(TextFormat("resources/nmmo3/earth_%d.png", i)); - client->players[4][i] = LoadTexture(TextFormat("resources/nmmo3/air_%d.png", i)); - } - - // TODO: Why do I need to cast here? - client->camera = (Camera2D){ - .target = {.x = env->width/2*TILE_SIZE, .y = env->height/2*TILE_SIZE}, - .offset = {.x = 0.0, .y = 0.0}, - .rotation = 0.0, - .zoom = 1.0, - }; - - int buffer_width = SCREEN_WIDTH + 4*TILE_SIZE; - int buffer_height = SCREEN_HEIGHT + 4*TILE_SIZE; - client->map_buffer = LoadRenderTexture(buffer_width, buffer_height); - client->ui_buffer = LoadRenderTexture(SCREEN_WIDTH, SCREEN_HEIGHT); - - return client; -} - -void close_client(Client* client) { - UnloadRenderTexture(client->map_buffer); - UnloadRenderTexture(client->ui_buffer); - for (int i = 0; i < NUM_PLAYER_TEXTURES; i++) { - for (int element = 0; element < 5; element++) { - UnloadTexture(client->players[element][i]); - } - } - UnloadFont(client->font); - UnloadTexture(client->tiles); - UnloadTexture(client->items); - UnloadTexture(client->inventory); - UnloadTexture(client->inventory_equip); - UnloadTexture(client->inventory_selected); - UnloadTexture(client->shader_terrain); - free(client->shader_terrain_data); - CloseWindow(); -} - -void draw_health_bar(int bar_x, int bar_y, int health, int max_health) { - DrawRectangle(bar_x, bar_y, HEALTH_BAR_WIDTH, - HEALTH_BAR_HEIGHT, RED); - DrawRectangle(bar_x, bar_y, - HEALTH_BAR_WIDTH * health / max_health, - HEALTH_BAR_HEIGHT, GREEN); - DrawRectangleLines(bar_x, bar_y, HEALTH_BAR_WIDTH, - HEALTH_BAR_HEIGHT, BLACK); -} - -void draw_inventory_item(Client* client, int idx, int item_type) { - if (item_type == 0) { - return; - } - Vector2 pos = { - .x = TILE_SIZE*idx + TILE_SIZE/4, - .y = SCREEN_HEIGHT - 5*TILE_SIZE/4, - }; - Rectangle source_rect = { - .x = TILE_SIZE*(ITEMS[item_type].tier - 1), - .y = TILE_SIZE*(ITEMS[item_type].type - 1), - .width = TILE_SIZE, - .height = TILE_SIZE, - }; - DrawTextureRec(client->items, source_rect, pos, WHITE); -} - -void draw_inventory_slot(Client* client, int idx, Texture2D* tex) { - Vector2 pos = { - .x = TILE_SIZE*idx + TILE_SIZE/4, - .y = SCREEN_HEIGHT - 5*TILE_SIZE/4, - }; - Rectangle source_rect = { - .x = 0, - .y = 0, - .width = TILE_SIZE, - .height = TILE_SIZE, - }; - DrawTextureRec(*tex, source_rect, pos, WHITE); -} - -void draw_inventory_label(Client* client, int idx, const char* label) { - Vector2 pos = { - .x = TILE_SIZE*idx + TILE_SIZE/2, - .y = SCREEN_HEIGHT - 5*TILE_SIZE/4 - 20, - }; - DrawTextEx(client->font, label, pos, 20, 4, YELLOW); -} - -void draw_all_slots(Client* client, Entity* player, int action) { - for (int i = 0; i < 12; i++) { - Texture2D* tex; - int mode = player->ui_mode; - if (i == action - ATN_ONE) { - tex = &client->inventory_selected; - } else if ((mode==MODE_PLAY || mode==MODE_SELL_SELECT) && - player->is_equipped[i] == 1) { - tex = &client->inventory_equip; - } else { - tex = &client->inventory; - } - //TODO: Draw inventory slot - draw_inventory_slot(client, i, tex); - } -} - -void draw_ui(Client* client, MMO* env, Entity* player, int action) { - draw_all_slots(client, player, action); - - int mode = player->ui_mode; - if (mode == MODE_PLAY || mode == MODE_SELL_SELECT) { - for (int idx = 0; idx < INVENTORY_SIZE; idx++) { - int item_type = player->inventory[idx]; - draw_inventory_item(client, idx, item_type); - } - } else if (mode == MODE_SELL_PRICE) { - for (int tier = 0; tier < 5; tier++) { - int item_type = item_index(I_SILVER, tier+1); - draw_inventory_item(client, tier, item_type); - } - for (int tier = 0; tier < 5; tier++) { - int item_type = item_index(I_GOLD, tier+1); - draw_inventory_item(client, tier+5, item_type); - } - for (int idx = 0; idx < 10; idx++) { - int price = idx + 1; - draw_inventory_label(client, idx, TextFormat("$%d", price)); - } - } else if (mode == MODE_BUY_TIER) { - for (int tier = 0; tier < MAX_TIERS; tier++) { - int item_type = item_index(I_SWORD, tier+1); - draw_inventory_item(client, tier, item_type); - draw_inventory_label(client, tier, TextFormat("T%d", tier+1)); - } - } else if (mode == MODE_BUY_ITEM) { - int tier = player->market_tier; - for (int idx = 0; idx < 11; idx++) { - int item_id = I_N*(tier-1) + idx + 1; - draw_inventory_item(client, idx, item_id); - // TODO: add prices to obs - int price = peek_price(&env->market[item_id]); - //price = extra_player_ob[idx + P.INVENTORY]; - const char* label = (price == 0) ? "Out!" : TextFormat("$%d", price); - draw_inventory_label(client, idx, label); - } - } - - // Draw number keys - for (int i = 0; i < 12; i++) { - Vector2 pos = { - .x = TILE_SIZE*i + TILE_SIZE/2 - 4, - .y = SCREEN_HEIGHT - TILE_SIZE/2 - 12, - }; - DrawTextEx(client->font, KEYS[i], pos, 20, 0, YELLOW); - } - - if (mode != MODE_PLAY) { - char* label; - if (mode == MODE_BUY_TIER || mode == MODE_BUY_ITEM) { - label = (char*) TextFormat("Buy Mode (b=cancel)\n\nYour gold: $%d", player->gold); - } else { - label = (char*) TextFormat("Sell Mode (v=cancel)"); - } - - Vector2 pos = { - .x = TILE_SIZE/2, - .y = SCREEN_HEIGHT - 2.5*TILE_SIZE, - }; - DrawTextEx(client->font, label, pos, 20, 4, YELLOW); - } - - if (player->in_combat > 0) { - Vector2 pos = { - .x = SCREEN_WIDTH - 500, - .y = TILE_SIZE/2, - }; - DrawTextEx(client->font, TextFormat("In combat. Cannot equip items."), - pos, 20, 4, RED); - } -} - -int simple_hash(int n) { - return ((n * 2654435761) & 0xFFFFFFFF) % INT_MAX; -} - -void draw_entity(Client* client, MMO* env, int pid, float delta) { - Entity* entity = get_entity(env, pid); - Animation* animation = &ANIMATIONS[entity->anim]; - - // Player texture - int element = entity->element; - int hashed = simple_hash(pid + client->start_time % 100); - Texture2D* tex = &client->players[element][hashed % NUM_PLAYER_TEXTURES]; - - int frame = delta * animation->num_frames; - Rectangle source_rect = { - .x = SPRITE_SIZE*animation->frames[frame], - .y = SPRITE_SIZE*(animation->offset + entity->dir), - .width = SPRITE_SIZE, - .height = SPRITE_SIZE, - }; - - float dx = 0; - float dy = 0; - if (entity->dir == 0) { - dy = -animation->tiles_traveled; - } else if (entity->dir == 1) { - dy = animation->tiles_traveled; - } else if (entity->dir == 2) { - dx = -animation->tiles_traveled; - } else if (entity->dir == 3) { - dx = animation->tiles_traveled; - } - dx = (1.0 - delta) * dx; - dy = (1.0 - delta) * dy; - - int x_pos = (dx + entity->c - 0.5f)*TILE_SIZE; - int y_pos = (dy + entity->r - 0.5f)*TILE_SIZE; - Vector2 pos = {.x = x_pos, .y = y_pos}; - - DrawTextureRec(*tex, source_rect, pos, WHITE); - - // Health bar - int bar_x = x_pos + TILE_SIZE - HEALTH_BAR_WIDTH/2; - int bar_y = y_pos; - draw_health_bar(bar_x, bar_y, entity->hp, entity->hp_max); - - // Overhead text - int comb_lvl = entity->comb_lvl; - int prof_lvl = entity->prof_lvl; - char* txt; - Color color; - if (entity->type == ENTITY_PLAYER) { - txt = (char*) TextFormat("%d: Lv %d/%d", pid, comb_lvl, prof_lvl); - color = GREEN; - } else { - txt = (char*) TextFormat("%d: Lv %d", pid, comb_lvl); - color = RED; - } - - Vector2 text_pos = {.x = bar_x, .y = bar_y - 20}; - DrawTextEx(client->font, txt, text_pos, 14, 1, color); -} - -void draw_min(Client* client, MMO* env, int x, int y, - int width, int height, int C, int R, float scale, float delta) { - client->shader_resolution[0] = GetRenderWidth(); - client->shader_resolution[1] = GetRenderHeight(); - client->shader_resolution[2] = client->camera.zoom; - client->shader_camera_x = client->camera.target.x; - client->shader_camera_y = client->camera.target.y; - client->shader_time = delta; - - BeginShaderMode(client->shader); - float map_width = env->width; - float map_height = env->height; - SetShaderValue(client->shader, client->shader_map_width_loc, &map_width, SHADER_UNIFORM_FLOAT); - SetShaderValue(client->shader, client->shader_map_height_loc, &map_height, SHADER_UNIFORM_FLOAT); - SetShaderValue(client->shader, client->shader_camera_x_loc, &client->shader_camera_x, SHADER_UNIFORM_FLOAT); - SetShaderValue(client->shader, client->shader_camera_y_loc, &client->shader_camera_y, SHADER_UNIFORM_FLOAT); - SetShaderValue(client->shader, client->shader_time_loc, &client->shader_time, SHADER_UNIFORM_FLOAT); - SetShaderValue(client->shader, client->shader_resolution_loc, client->shader_resolution, SHADER_UNIFORM_VEC3); - - SetShaderValueTexture(client->shader, client->shader_texture_tiles_loc, client->tiles); - - UpdateTexture(client->shader_terrain, client->shader_terrain_data); - SetShaderValueTexture(client->shader, client->shader_terrain_loc, client->shader_terrain); - - DrawRectangle( - client->camera.target.x - GetRenderWidth()/2/client->camera.zoom, - client->camera.target.y - GetRenderHeight()/2/client->camera.zoom, - GetRenderWidth()/client->camera.zoom, - GetRenderHeight()/client->camera.zoom, - WHITE - ); - - EndShaderMode(); - - for (int r = y; r < y+height; r++) { - for (int c = x; c < x+width; c++) { - int adr = r*C + c; - int tile = client->terrain[adr]; - if (tile >= 240 && tile < 240+4*4*4*4) { - tile += 3.9*delta; - } - //int u = TILE_SIZE*(tile % 64); - //int v = TILE_SIZE*(tile / 64); - Vector2 pos = { - .x = c*TILE_SIZE, - .y = r*TILE_SIZE, - }; - if (IsKeyDown(KEY_H) && env->pids[adr] != -1) { - DrawRectangle(pos.x, pos.y, TILE_SIZE, TILE_SIZE, (Color){0, 255, 255, 128}); - } - /* - Rectangle source_rect = (Rectangle){ - .x = u, - .y = v, - .width = TILE_SIZE, - .height = TILE_SIZE, - }; - - DrawTextureRec(client->tiles, source_rect, pos, (Color){255, 255, 255, 128}); - */ - - // Draw item - if (env->items[adr] != 0) { - int item_id = env->items[adr]; - int item_tier = ITEMS[item_id].tier; - int item_type = ITEMS[item_id].type; - Rectangle source_rect = { - .x = (item_tier - 1)*TILE_SIZE, - .y = (item_type - 1)*TILE_SIZE, - .width = TILE_SIZE, - .height = TILE_SIZE, - }; - DrawTextureRec(client->items, source_rect, pos, WHITE); - } - } - } - -} - -void render_centered(Client* client, MMO* env, int pid, int action, float delta) { - Entity* player = get_entity(env, pid); - int r = player->r; - int c = player->c; - - //Animation* animation = ANIM_SPRITE[player->anim]; - //float travel_x, travel_y; - //travel_x, travel_y = animation.get_travel(player.dir) - float travel_x = 0; - float travel_y = 0; - Animation* animation = &ANIMATIONS[player->anim]; - if (player->dir == 0) { - travel_y = -animation->tiles_traveled; - } else if (player->dir == 1) { - travel_y = animation->tiles_traveled; - } else if (player->dir == 2) { - travel_x = animation->tiles_traveled; - } else if (player->dir == 3) { - travel_x = -animation->tiles_traveled; - } - travel_x *= TILE_SIZE; - travel_y *= TILE_SIZE; - - client->camera.offset.x = SCREEN_WIDTH/2; - client->camera.offset.y = SCREEN_HEIGHT/2; - client->camera.target.x = (c + 0.5)*TILE_SIZE + (delta - 1)*travel_x; - client->camera.target.y = (r + 0.5)*TILE_SIZE + (1 - delta)*travel_y; - client->camera.zoom = 1.0; - - int start_c = c - X_WINDOW - 2; - if (start_c < 0) { - start_c = 0; - } - - int start_r = r - Y_WINDOW - 2; - if (start_r < 0) { - start_r = 0; - } - - int end_r = r + Y_WINDOW + 3; - if (end_r > env->height) { - end_r = env->height; - } - - int end_c = c + X_WINDOW + 3; - if (end_c > env->width) { - end_c = env->width; - } - - //BeginMode2D(client.camera); - BeginMode2D(client->camera); - draw_min(client, env, start_c, - start_r, end_c-start_c, end_r-start_r, - env->width, env->height, 1, delta); - - for (int pid = 0; pid < env->num_players+env->num_enemies; pid++) { - draw_entity(client, env, pid, delta); - } - - EndMode2D(); - draw_ui(client, env, player, action); -} - -bool up_key() { - return IsKeyDown(KEY_UP) || IsKeyDown(KEY_W); -} - -bool down_key() { - return IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S); -} - -bool left_key() { - return IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A); -} - -bool right_key() { - return IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D); -} - -bool shift_key() { - return IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); -} - -int process_centered_input() { - if (IsKeyDown(KEY_ESCAPE)) { - CloseWindow(); - } - - if (shift_key()) { - if (down_key()) { - return ATN_DOWN_SHIFT; - } else if (up_key()) { - return ATN_UP_SHIFT; - } else if (left_key()) { - return ATN_LEFT_SHIFT; - } else if (right_key()) { - return ATN_RIGHT_SHIFT; - } - } else if (up_key()) { - return ATN_UP; - } else if (down_key()) { - return ATN_DOWN; - } else if (left_key()) { - return ATN_LEFT; - } else if (right_key()) { - return ATN_RIGHT; - } else if (IsKeyDown(KEY_SPACE)) { - return ATN_ATTACK; - } else if (IsKeyDown(KEY_ONE)) { - return ATN_ONE; - } else if (IsKeyDown(KEY_TWO)) { - return ATN_TWO; - } else if (IsKeyDown(KEY_THREE)) { - return ATN_THREE; - } else if (IsKeyDown(KEY_FOUR)) { - return ATN_FOUR; - } else if (IsKeyDown(KEY_FIVE)) { - return ATN_FIVE; - } else if (IsKeyDown(KEY_SIX)) { - return ATN_SIX; - } else if (IsKeyDown(KEY_SEVEN)) { - return ATN_SEVEN; - } else if (IsKeyDown(KEY_EIGHT)) { - return ATN_EIGHT; - } else if (IsKeyDown(KEY_NINE)) { - return ATN_NINE; - } else if (IsKeyDown(KEY_ZERO)) { - return ATN_ZERO; - } else if (IsKeyDown(KEY_MINUS)) { - return ATN_MINUS; - } else if (IsKeyDown(KEY_EQUAL)) { - return ATN_EQUALS; - } else if (IsKeyDown(KEY_V)) { - return ATN_SELL; - } else if (IsKeyDown(KEY_B)) { - return ATN_BUY; - } - return ATN_NOOP; -} - -void process_fixed_input(Client* client) { - float move_speed = 20 / client->camera.zoom; - float zoom_delta = 0.05; - float zoom = client->camera.zoom; - if (shift_key()) { - move_speed *= 2; - zoom_delta *= 2; - } - if (down_key()) { - client->camera.target.y += move_speed; - } - if (up_key()) { - client->camera.target.y -= move_speed; - } - if (left_key()) { - client->camera.target.x -= move_speed; - } - if (right_key()) { - client->camera.target.x += move_speed; - } - if ((IsKeyDown(KEY_EQUAL) || IsKeyDown(KEY_E)) && zoom < 8.0) { - client->camera.zoom *= (1 + zoom_delta); - } - if ((IsKeyDown(KEY_MINUS) || IsKeyDown(KEY_Q)) && zoom > 1.0/32.0) { - client->camera.zoom *= (1 - zoom_delta); - } -} - -void render_fixed(Client* client, MMO* env, float delta) { - // Draw tilemap - float y = client->camera.target.y; - float x = client->camera.target.x; - float zoom = client->camera.zoom; - - BeginMode2D(client->camera); - - int X = GetRenderWidth(); - int Y = GetRenderHeight(); - client->camera.offset.x = X/2; - client->camera.offset.y = Y/2; - - int start_r = (y - Y/2/zoom) / TILE_SIZE; - if (start_r < 0) { - start_r = 0; - } - - int start_c = (x - X/2/zoom) / TILE_SIZE; - if (start_c < 0) { - start_c = 0; - } - - int end_r = (y + Y/2/zoom) / TILE_SIZE + 1; - if (end_r > env->height) { - end_r = env->height; - } - - int end_c = (x + X/2/zoom) / TILE_SIZE + 1; - if (end_c > env->width) { - end_c = env->width; - } - - /* - if client.active_overlay is None: - overlay = None - elif client.active_overlay == 'counts': - overlay = client.overlays.counts - overlay = smooth_cyan(overlay) - overlay = overlay[start_r:end_r, start_c:end_c] - elif client.active_overlay == 'value': - overlay = client.overlays.value_function - overlay = clip_rgb(overlay) - overlay = overlay[start_r:end_r, start_c:end_c] - */ - - draw_min(client, env, start_c, start_r, - end_c-start_c, end_r-start_r, env->width, env->height, 1, delta); - - for (int pid = 0; pid < env->num_players+env->num_enemies; pid++) { - draw_entity(client, env, pid, delta); - } - - EndMode2D(); -} - -// Did not finish porting console from Cython -void process_command_input(Client* client, MMO* env) { - int key = GetCharPressed(); - while (key > 0) { - if (key >= 32 && key <= 125 && client->command_len < COMMAND_CHARS) { - client->command[client->command_len] = key; - client->command_len += 1; - } - key = GetCharPressed(); - } - if (IsKeyPressed(KEY_BACKSPACE)) { - client->command_len = client->command_len - 1; - } - if (IsKeyPressed(KEY_ENTER)) { - char* command = client->command; - client->command_len = 0; - - if (client->command_len == 5 && strncmp(command, "help", 5) == 0) { - //client->command = COMMAND_HELP; - } else { - client->command_mode = false; - } - - if (client->command_len == 11 && strncmp(command, "overlay env", 11) == 0) { - client->active_overlay = OVERLAY_NONE; - } else if (client->command_len == 14 && strncmp(command, "overlay counts", 14) == 0) { - client->active_overlay = OVERLAY_COUNTS; - //arr = smooth_cyan(client->overlays.counts); - //Image.fromarray(arr).save('overlays/counts.png'); - //client->overlay_texture = rl.LoadTexture('overlays/counts.png'.encode()); - } else if (client->command_len == 13 && strncmp(command, "overlay value", 13) == 0) { - client->active_overlay = OVERLAY_VALUE; - //arr = clip_rgb(client->overlays.value_function); - //Image.fromarray(arr).save('overlays/values.png'); - //client->overlay_texture = rl.LoadTexture('overlays/values.png'.encode()); - } else if (client->command_len == 4 && strncmp(command, "play", 4) == 0) { - client->my_player = 0; - client->render_mode = RENDER_MODE_CENTERED; - } else if (client->command_len >= 9 && strncmp(command, "follow ", 7) == 0) { - /* - char* pid = command + 7; - pid = pid; - int pid = atoi(pid); - if (pid < 0 || pid > env->num_players) { - client->command = "Invalid player id"; - } - client->my_player = pid; - client->render_mode = RENDER_MODE_CENTERED; - */ - } - } - - Color term_color = {255, 255, 255, 200}; - DrawRectangle(0, 0, SCREEN_WIDTH, 32, term_color); - client->command[client->command_len] = '\0'; - const char* text = TextFormat("> %s", client->command); - DrawText(text, 10, 10, 20, BLACK); -} - -int c_render(MMO* env) { - if (env->client == NULL) { - // Must reset before making client - env->client = make_client(env); - } - Client* client = env->client; - float delta = (float)client->frame / 36.0f; - - BeginDrawing(); - ClearBackground(BLANK); - int action = 0; - - if (IsKeyDown(KEY_ESCAPE)) { - CloseWindow(); - exit(0); - } - if (IsKeyPressed(KEY_TAB)) { - ToggleBorderlessWindowed(); - if (client->render_mode == RENDER_MODE_CENTERED) { - client->render_mode = RENDER_MODE_FIXED; - } else { - client->render_mode = RENDER_MODE_CENTERED; - } - } - if (IsKeyPressed(KEY_GRAVE)) { // tilde - client->command_mode = !client->command_mode; - GetCharPressed(); // clear tilde key - } - if (client->render_mode == RENDER_MODE_FIXED) { - if (!client->command_mode) { - process_fixed_input(client); - } - render_fixed(client, env, delta); - } else { - if (!client->command_mode) { - action = process_centered_input(); - } - render_centered(client, env, client->my_player, action, delta); - } - if (client->command_mode) { - process_command_input(client, env); - } - - if (IsKeyDown(KEY_H)) { - DrawTextEx(client->font, TextFormat("FPS: %d", GetFPS()), - (Vector2){16, 16}, 24, 4, YELLOW); - } - - EndDrawing(); - client->frame += 1; - if (client->frame >= 36) { - client->frame = 0; - } - return action; -} - - diff --git a/pufferlib/ocean/nmmo3/nmmo3.py b/pufferlib/ocean/nmmo3/nmmo3.py deleted file mode 100644 index 32623501b..000000000 --- a/pufferlib/ocean/nmmo3/nmmo3.py +++ /dev/null @@ -1,232 +0,0 @@ -from pdb import set_trace as T -import numpy as np -from types import SimpleNamespace -import gymnasium -import pettingzoo -import time - -from pufferlib.ocean.nmmo3 import binding -#import binding - -import pufferlib - -class NMMO3(pufferlib.PufferEnv): - def __init__(self, width=8*[512], height=8*[512], num_envs=4, - num_players=1024, num_enemies=2048, num_resources=2048, - num_weapons=1024, num_gems=512, tiers=5, levels=40, - teleportitis_prob=0.001, enemy_respawn_ticks=2, - item_respawn_ticks=100, x_window=7, y_window=5, - reward_combat_level=1.0, reward_prof_level=1.0, - reward_item_level=0.5, reward_market=0.01, - reward_death=-1.0, log_interval=128, buf=None, seed=0): - - self.log_interval = log_interval - - if len(width) > num_envs: - width = width[:num_envs] - if len(height) > num_envs: - height = height[:num_envs] - - if not isinstance(width, list): - width = num_envs * [width] - if not isinstance(height, list): - height = num_envs * [height] - if not isinstance(num_players, list): - num_players = num_envs * [num_players] - if not isinstance(num_enemies, list): - num_enemies = num_envs * [num_enemies] - if not isinstance(num_resources, list): - num_resources = num_envs * [num_resources] - if not isinstance(num_weapons, list): - num_weapons = num_envs * [num_weapons] - if not isinstance(num_gems, list): - num_gems = num_envs * [num_gems] - if not isinstance(tiers, list): - tiers = num_envs * [tiers] - if not isinstance(levels, list): - levels = num_envs * [levels] - if not isinstance(teleportitis_prob, list): - teleportitis_prob = num_envs * [teleportitis_prob] - if not isinstance(enemy_respawn_ticks, list): - enemy_respawn_ticks = num_envs * [enemy_respawn_ticks] - if not isinstance(item_respawn_ticks, list): - item_respawn_ticks = num_envs * [item_respawn_ticks] - - assert isinstance(width, list) - assert isinstance(height, list) - assert isinstance(num_players, list) - assert isinstance(num_enemies, list) - assert isinstance(num_resources, list) - assert isinstance(num_weapons, list) - assert isinstance(num_gems, list) - assert isinstance(tiers, list) - assert isinstance(levels, list) - assert isinstance(teleportitis_prob, list) - assert isinstance(enemy_respawn_ticks, list) - assert isinstance(item_respawn_ticks, list) - assert isinstance(x_window, int) - assert isinstance(y_window, int) - - assert len(width) == num_envs - assert len(height) == num_envs - assert len(num_players) == num_envs - assert len(num_enemies) == num_envs - assert len(num_resources) == num_envs - assert len(num_weapons) == num_envs - assert len(num_gems) == num_envs - assert len(tiers) == num_envs - assert len(levels) == num_envs - assert len(teleportitis_prob) == num_envs - assert len(enemy_respawn_ticks) == num_envs - assert len(item_respawn_ticks) == num_envs - - total_players = 0 - total_enemies = 0 - for idx in range(num_envs): - assert isinstance(width[idx], int) - assert isinstance(height[idx], int) - - if num_players[idx] is None: - num_players[idx] = width[idx] * height[idx] // 2048 - if num_enemies[idx] is None: - num_enemies[idx] = width[idx] * height[idx] // 512 - if num_resources[idx] is None: - num_resources[idx] = width[idx] * height[idx] // 1024 - if num_weapons[idx] is None: - num_weapons[idx] = width[idx] * height[idx] // 2048 - if num_gems[idx] is None: - num_gems[idx] = width[idx] * height[idx] // 4096 - if tiers[idx] is None: - if height[idx] <= 128: - tiers[idx] = 1 - elif height[idx] <= 256: - tiers[idx] = 2 - elif height[idx] <= 512: - tiers[idx] = 3 - elif height[idx] <= 1024: - tiers[idx] = 4 - else: - tiers[idx] = 5 - if levels[idx] is None: - if height[idx] <= 128: - levels[idx] = 7 - elif height[idx] <= 256: - levels[idx] = 15 - elif height[idx] <= 512: - levels[idx] = 31 - elif height[idx] <= 1024: - levels[idx] = 63 - else: - levels[idx] = 99 - - total_players += num_players[idx] - total_enemies += num_enemies[idx] - - self.players_flat = np.zeros((total_players, 51+501+3), dtype=np.intc) - self.enemies_flat = np.zeros((total_enemies, 51+501+3), dtype=np.intc) - self.rewards_flat = np.zeros((total_players, 10), dtype=np.float32) - #map_obs = np.zeros((total_players, 11*15 + 47 + 10), dtype=np.intc) - #counts = np.zeros((num_envs, height, width), dtype=np.uint8) - #terrain = np.zeros((num_envs, height, width), dtype=np.uint8) - #rendered = np.zeros((num_envs, height, width, 3), dtype=np.uint8) - actions = np.zeros((total_players), dtype=np.intc) - self.actions = actions - - self.num_agents = total_players - self.num_players = total_players - self.num_enemies = total_enemies - - self.comb_goal_mask = np.array([1, 0, 1, 0, 1, 1, 0, 1, 1, 1]) - self.prof_goal_mask = np.array([0, 0, 0, 1, 0, 0, 1, 1, 1, 1]) - self.tick = 0 - - self.single_observation_space = gymnasium.spaces.Box(low=0, - high=255, shape=(11*15*10+47+10,), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.Discrete(26) - self.render_mode = 'human' - - super().__init__(buf) - player_count = 0 - enemy_count = 0 - c_envs = [] - for i in range(num_envs): - players = num_players[i] - enemies = num_enemies[i] - env_id = binding.env_init( - self.observations[player_count:player_count+players], - self.actions[player_count:player_count+players], - self.rewards[player_count:player_count+players], - self.terminals[player_count:player_count+players], - self.truncations[player_count:player_count+players], - i + seed*num_envs, - width=width[i], - height=height[i], - num_players=num_players[i], - num_enemies=num_enemies[i], - num_resources=num_resources[i], - num_weapons=num_weapons[i], - num_gems=num_gems[i], - tiers=tiers[i], - levels=levels[i], - teleportitis_prob=teleportitis_prob[i], - enemy_respawn_ticks=enemy_respawn_ticks[i], - item_respawn_ticks=item_respawn_ticks[i], - x_window=x_window, - y_window=y_window, - reward_combat_level=reward_combat_level, - reward_prof_level=reward_prof_level, - reward_item_level=reward_item_level, - reward_market=reward_market, - reward_death=reward_death, - ) - c_envs.append(env_id) - player_count += players - enemy_count += enemies - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - self.rewards.fill(0) - self.is_reset = True - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - if not hasattr(self, 'is_reset'): - raise Exception('Must call reset before step') - self.rewards.fill(0) - self.actions[:] = actions[:] - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - self.tick += 1 - return self.observations, self.rewards, self.terminals, self.truncations, info - - def render(self): - for i in range(36): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(cls, timeout=10, atn_cache=1024): - env = cls(num_envs=1) - env.reset() - tick = 0 - - actions = np.random.randint(0, 2, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'{env.__class__.__name__}: SPS: {env.num_agents * tick / (time.time() - start)}') - -if __name__ == '__main__': - test_performance(NMMO3) diff --git a/pufferlib/ocean/nmmo3/simplex.h b/pufferlib/ocean/nmmo3/simplex.h deleted file mode 100644 index 07b3e7db3..000000000 --- a/pufferlib/ocean/nmmo3/simplex.h +++ /dev/null @@ -1,148 +0,0 @@ -// Original work (noise library) Copyright (c) 2008 Casey Duncan -// Modified work (vec_noise library) Copyright (c) 2017 Zev Benjamin -// Single-file C port (this file) Copyright (c) 2024 Joseph Suarez -// Distributed under the MIT license. This is a simple copy-paste job. -// I did this because the original code mixed Python bindings into the -// C source, so there wasn't a clean way to use it as a C standalone. - -#include -const float GRAD3[][3] = { - {1,1,0},{-1,1,0},{1,-1,0},{-1,-1,0}, - {1,0,1},{-1,0,1},{1,0,-1},{-1,0,-1}, - {0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}, - {1,0,-1},{-1,0,-1},{0,-1,1},{0,1,1}}; - -const float GRAD4[][4] = { - {0,1,1,1}, {0,1,1,-1}, {0,1,-1,1}, {0,1,-1,-1}, - {0,-1,1,1}, {0,-1,1,-1}, {0,-1,-1,1}, {0,-1,-1,-1}, - {1,0,1,1}, {1,0,1,-1}, {1,0,-1,1}, {1,0,-1,-1}, - {-1,0,1,1}, {-1,0,1,-1}, {-1,0,-1,1}, {-1,0,-1,-1}, - {1,1,0,1}, {1,1,0,-1}, {1,-1,0,1}, {1,-1,0,-1}, - {-1,1,0,1}, {-1,1,0,-1}, {-1,-1,0,1}, {-1,-1,0,-1}, - {1,1,1,0}, {1,1,-1,0}, {1,-1,1,0}, {1,-1,-1,0}, - {-1,1,1,0}, {-1,1,-1,0}, {-1,-1,1,0}, {-1,-1,-1,0}}; - -// At the possible cost of unaligned access, we use char instead of -// int here to try to ensure that this table fits in L1 cache -const unsigned char PERM[] = { - 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, - 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, - 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, - 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, - 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, - 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, - 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, - 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, - 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, - 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, - 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, - 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, - 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, - 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, - 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, - 141, 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, - 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, - 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, - 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, - 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, - 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, - 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, - 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, - 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, - 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, - 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, - 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, - 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, - 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, - 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, - 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, - 180}; - -const unsigned char SIMPLEX[][4] = { - {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {1,2,3,0},{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{1,3,2,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},{1,0,2,3},{1,0,3,2},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,1,3}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, - {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1}, - {3,2,1,0}}; - -#define fastfloor(n) (int)(n) - (((n) < 0.0f) & ((n) != (int)(n))) - -// Fast sine/cosine functions from -// http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/page__st__80 -// Note the input to these functions is not radians -// instead x = [0, 2] for r = [0, 2*PI] - -inline float fast_sin(float x) -{ - // Convert the input value to a range of -1 to 1 - // x = x * (1.0f / PI); - - // Wrap around - volatile float z = (x + 25165824.0f); - x = x - (z - 25165824.0f); - - #if LOW_SINE_PRECISION - return 4.0f * (x - x * fabsf(x)); - #else - { - float y = x - x * fabsf(x); - const float Q = 3.1f; - const float P = 3.6f; - return y * (Q + P * fabsf(y)); - } - #endif -} - -inline float fast_cos(float x) -{ - return fast_sin(x + 0.5f); -} - -// 2D simplex skew factors -#define F2 0.3660254037844386f // 0.5 * (sqrt(3.0) - 1.0) -#define G2 0.21132486540518713f // (3.0 - sqrt(3.0)) / 6.0 - -float -noise2(float x, float y) -{ - int i1, j1, II, JJ, c; - float s = (x + y) * F2; - float i = floorf(x + s); - float j = floorf(y + s); - float t = (i + j) * G2; - - float xx[3], yy[3], f[3]; - float noise[3] = {0.0f, 0.0f, 0.0f}; - int g[3]; - - xx[0] = x - (i - t); - yy[0] = y - (j - t); - - i1 = xx[0] > yy[0]; - j1 = xx[0] <= yy[0]; - - xx[2] = xx[0] + G2 * 2.0f - 1.0f; - yy[2] = yy[0] + G2 * 2.0f - 1.0f; - xx[1] = xx[0] - i1 + G2; - yy[1] = yy[0] - j1 + G2; - - II = (int) i & 255; - JJ = (int) j & 255; - g[0] = PERM[II + PERM[JJ]] % 12; - g[1] = PERM[II + i1 + PERM[JJ + j1]] % 12; - g[2] = PERM[II + 1 + PERM[JJ + 1]] % 12; - - for (c = 0; c <= 2; c++) - f[c] = 0.5f - xx[c]*xx[c] - yy[c]*yy[c]; - - for (c = 0; c <= 2; c++) - if (f[c] > 0) - noise[c] = f[c]*f[c]*f[c]*f[c] * (GRAD3[g[c]][0]*xx[c] + GRAD3[g[c]][1]*yy[c]); - - return (noise[0] + noise[1] + noise[2]) * 70.0f; -} diff --git a/pufferlib/ocean/nmmo3/tile_atlas.h b/pufferlib/ocean/nmmo3/tile_atlas.h deleted file mode 100644 index 3e86b74bd..000000000 --- a/pufferlib/ocean/nmmo3/tile_atlas.h +++ /dev/null @@ -1,3 +0,0 @@ -int lerps[10000] = {0, 0, 4, 14, 34, 69, 125, 209, 329, 494, 1, 5, 15, 35, 70, 126, 210, 330, 495, 0, 6, 16, 36, 71, 127, 211, 331, 496, 0, 0, 17, 37, 72, 128, 212, 332, 497, 0, 0, 0, 38, 73, 129, 213, 333, 498, 0, 0, 0, 0, 74, 130, 214, 334, 499, 0, 0, 0, 0, 0, 131, 215, 335, 500, 0, 0, 0, 0, 0, 0, 216, 336, 501, 0, 0, 0, 0, 0, 0, 0, 337, 502, 0, 0, 0, 0, 0, 0, 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 7, 18, 39, 75, 132, 217, 338, 504, 0, 8, 19, 40, 76, 133, 218, 339, 505, 0, 0, 20, 41, 77, 134, 219, 340, 506, 0, 0, 0, 42, 78, 135, 220, 341, 507, 0, 0, 0, 0, 79, 136, 221, 342, 508, 0, 0, 0, 0, 0, 137, 222, 343, 509, 0, 0, 0, 0, 0, 0, 223, 344, 510, 0, 0, 0, 0, 0, 0, 0, 345, 511, 0, 0, 0, 0, 0, 0, 0, 0, 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 21, 43, 80, 138, 224, 346, 513, 0, 0, 22, 44, 81, 139, 225, 347, 514, 0, 0, 0, 45, 82, 140, 226, 348, 515, 0, 0, 0, 0, 83, 141, 227, 349, 516, 0, 0, 0, 0, 0, 142, 228, 350, 517, 0, 0, 0, 0, 0, 0, 229, 351, 518, 0, 0, 0, 0, 0, 0, 0, 352, 519, 0, 0, 0, 0, 0, 0, 0, 0, 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 46, 84, 143, 230, 353, 521, 0, 0, 0, 47, 85, 144, 231, 354, 522, 0, 0, 0, 0, 86, 145, 232, 355, 523, 0, 0, 0, 0, 0, 146, 233, 356, 524, 0, 0, 0, 0, 0, 0, 234, 357, 525, 0, 0, 0, 0, 0, 0, 0, 358, 526, 0, 0, 0, 0, 0, 0, 0, 0, 527, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 87, 147, 235, 359, 528, 0, 0, 0, 0, 88, 148, 236, 360, 529, 0, 0, 0, 0, 0, 149, 237, 361, 530, 0, 0, 0, 0, 0, 0, 238, 362, 531, 0, 0, 0, 0, 0, 0, 0, 363, 532, 0, 0, 0, 0, 0, 0, 0, 0, 533, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 150, 239, 364, 534, 0, 0, 0, 0, 0, 151, 240, 365, 535, 0, 0, 0, 0, 0, 0, 241, 366, 536, 0, 0, 0, 0, 0, 0, 0, 367, 537, 0, 0, 0, 0, 0, 0, 0, 0, 538, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 242, 368, 539, 0, 0, 0, 0, 0, 0, 243, 369, 540, 0, 0, 0, 0, 0, 0, 0, 370, 541, 0, 0, 0, 0, 0, 0, 0, 0, 542, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 371, 543, 0, 0, 0, 0, 0, 0, 0, 372, 544, 0, 0, 0, 0, 0, 0, 0, 0, 545, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 373, 546, 0, 0, 0, 0, 0, 0, 0, 0, 547, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 10, 24, 49, 90, 153, 245, 374, 549, 0, 11, 25, 50, 91, 154, 246, 375, 550, 0, 0, 26, 51, 92, 155, 247, 376, 551, 0, 0, 0, 52, 93, 156, 248, 377, 552, 0, 0, 0, 0, 94, 157, 249, 378, 553, 0, 0, 0, 0, 0, 158, 250, 379, 554, 0, 0, 0, 0, 0, 0, 251, 380, 555, 0, 0, 0, 0, 0, 0, 0, 381, 556, 0, 0, 0, 0, 0, 0, 0, 0, 557, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 27, 53, 95, 159, 252, 382, 558, 0, 0, 28, 54, 96, 160, 253, 383, 559, 0, 0, 0, 55, 97, 161, 254, 384, 560, 0, 0, 0, 0, 98, 162, 255, 385, 561, 0, 0, 0, 0, 0, 163, 256, 386, 562, 0, 0, 0, 0, 0, 0, 257, 387, 563, 0, 0, 0, 0, 0, 0, 0, 388, 564, 0, 0, 0, 0, 0, 0, 0, 0, 565, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 56, 99, 164, 258, 389, 566, 0, 0, 0, 57, 100, 165, 259, 390, 567, 0, 0, 0, 0, 101, 166, 260, 391, 568, 0, 0, 0, 0, 0, 167, 261, 392, 569, 0, 0, 0, 0, 0, 0, 262, 393, 570, 0, 0, 0, 0, 0, 0, 0, 394, 571, 0, 0, 0, 0, 0, 0, 0, 0, 572, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 102, 168, 263, 395, 573, 0, 0, 0, 0, 103, 169, 264, 396, 574, 0, 0, 0, 0, 0, 170, 265, 397, 575, 0, 0, 0, 0, 0, 0, 266, 398, 576, 0, 0, 0, 0, 0, 0, 0, 399, 577, 0, 0, 0, 0, 0, 0, 0, 0, 578, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 171, 267, 400, 579, 0, 0, 0, 0, 0, 172, 268, 401, 580, 0, 0, 0, 0, 0, 0, 269, 402, 581, 0, 0, 0, 0, 0, 0, 0, 403, 582, 0, 0, 0, 0, 0, 0, 0, 0, 583, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 270, 404, 584, 0, 0, 0, 0, 0, 0, 271, 405, 585, 0, 0, 0, 0, 0, 0, 0, 406, 586, 0, 0, 0, 0, 0, 0, 0, 0, 587, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 407, 588, 0, 0, 0, 0, 0, 0, 0, 408, 589, 0, 0, 0, 0, 0, 0, 0, 0, 590, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 409, 591, 0, 0, 0, 0, 0, 0, 0, 0, 592, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 593, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 30, 59, 105, 174, 273, 410, 594, 0, 0, 31, 60, 106, 175, 274, 411, 595, 0, 0, 0, 61, 107, 176, 275, 412, 596, 0, 0, 0, 0, 108, 177, 276, 413, 597, 0, 0, 0, 0, 0, 178, 277, 414, 598, 0, 0, 0, 0, 0, 0, 278, 415, 599, 0, 0, 0, 0, 0, 0, 0, 416, 600, 0, 0, 0, 0, 0, 0, 0, 0, 601, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 62, 109, 179, 279, 417, 602, 0, 0, 0, 63, 110, 180, 280, 418, 603, 0, 0, 0, 0, 111, 181, 281, 419, 604, 0, 0, 0, 0, 0, 182, 282, 420, 605, 0, 0, 0, 0, 0, 0, 283, 421, 606, 0, 0, 0, 0, 0, 0, 0, 422, 607, 0, 0, 0, 0, 0, 0, 0, 0, 608, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 112, 183, 284, 423, 609, 0, 0, 0, 0, 113, 184, 285, 424, 610, 0, 0, 0, 0, 0, 185, 286, 425, 611, 0, 0, 0, 0, 0, 0, 287, 426, 612, 0, 0, 0, 0, 0, 0, 0, 427, 613, 0, 0, 0, 0, 0, 0, 0, 0, 614, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 186, 288, 428, 615, 0, 0, 0, 0, 0, 187, 289, 429, 616, 0, 0, 0, 0, 0, 0, 290, 430, 617, 0, 0, 0, 0, 0, 0, 0, 431, 618, 0, 0, 0, 0, 0, 0, 0, 0, 619, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 291, 432, 620, 0, 0, 0, 0, 0, 0, 292, 433, 621, 0, 0, 0, 0, 0, 0, 0, 434, 622, 0, 0, 0, 0, 0, 0, 0, 0, 623, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 293, 435, 624, 0, 0, 0, 0, 0, 0, 0, 436, 625, 0, 0, 0, 0, 0, 0, 0, 0, 626, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 437, 627, 0, 0, 0, 0, 0, 0, 0, 0, 628, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 65, 115, 189, 294, 438, 630, 0, 0, 0, 66, 116, 190, 295, 439, 631, 0, 0, 0, 0, 117, 191, 296, 440, 632, 0, 0, 0, 0, 0, 192, 297, 441, 633, 0, 0, 0, 0, 0, 0, 298, 442, 634, 0, 0, 0, 0, 0, 0, 0, 443, 635, 0, 0, 0, 0, 0, 0, 0, 0, 636, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 118, 193, 299, 444, 637, 0, 0, 0, 0, 119, 194, 300, 445, 638, 0, 0, 0, 0, 0, 195, 301, 446, 639, 0, 0, 0, 0, 0, 0, 302, 447, 640, 0, 0, 0, 0, 0, 0, 0, 448, 641, 0, 0, 0, 0, 0, 0, 0, 0, 642, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 196, 303, 449, 643, 0, 0, 0, 0, 0, 197, 304, 450, 644, 0, 0, 0, 0, 0, 0, 305, 451, 645, 0, 0, 0, 0, 0, 0, 0, 452, 646, 0, 0, 0, 0, 0, 0, 0, 0, 647, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 306, 453, 648, 0, 0, 0, 0, 0, 0, 307, 454, 649, 0, 0, 0, 0, 0, 0, 0, 455, 650, 0, 0, 0, 0, 0, 0, 0, 0, 651, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 308, 456, 652, 0, 0, 0, 0, 0, 0, 0, 457, 653, 0, 0, 0, 0, 0, 0, 0, 0, 654, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 458, 655, 0, 0, 0, 0, 0, 0, 0, 0, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 657, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 121, 199, 309, 459, 658, 0, 0, 0, 0, 122, 200, 310, 460, 659, 0, 0, 0, 0, 0, 201, 311, 461, 660, 0, 0, 0, 0, 0, 0, 312, 462, 661, 0, 0, 0, 0, 0, 0, 0, 463, 662, 0, 0, 0, 0, 0, 0, 0, 0, 663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 202, 313, 464, 664, 0, 0, 0, 0, 0, 203, 314, 465, 665, 0, 0, 0, 0, 0, 0, 315, 466, 666, 0, 0, 0, 0, 0, 0, 0, 467, 667, 0, 0, 0, 0, 0, 0, 0, 0, 668, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 316, 468, 669, 0, 0, 0, 0, 0, 0, 317, 469, 670, 0, 0, 0, 0, 0, 0, 0, 470, 671, 0, 0, 0, 0, 0, 0, 0, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 318, 471, 673, 0, 0, 0, 0, 0, 0, 0, 472, 674, 0, 0, 0, 0, 0, 0, 0, 0, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 473, 676, 0, 0, 0, 0, 0, 0, 0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 205, 319, 474, 679, 0, 0, 0, 0, 0, 206, 320, 475, 680, 0, 0, 0, 0, 0, 0, 321, 476, 681, 0, 0, 0, 0, 0, 0, 0, 477, 682, 0, 0, 0, 0, 0, 0, 0, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 207, 322, 478, 684, 0, 0, 0, 0, 0, 0, 323, 479, 685, 0, 0, 0, 0, 0, 0, 0, 480, 686, 0, 0, 0, 0, 0, 0, 0, 0, 687, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, 481, 688, 0, 0, 0, 0, 0, 0, 0, 482, 689, 0, 0, 0, 0, 0, 0, 0, 0, 690, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 483, 691, 0, 0, 0, 0, 0, 0, 0, 0, 692, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 325, 484, 694, 0, 0, 0, 0, 0, 0, 326, 485, 695, 0, 0, 0, 0, 0, 0, 0, 486, 696, 0, 0, 0, 0, 0, 0, 0, 0, 697, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 327, 487, 698, 0, 0, 0, 0, 0, 0, 0, 488, 699, 0, 0, 0, 0, 0, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 489, 701, 0, 0, 0, 0, 0, 0, 0, 0, 702, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 703, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, 490, 704, 0, 0, 0, 0, 0, 0, 0, 491, 705, 0, 0, 0, 0, 0, 0, 0, 0, 706, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 492, 707, 0, 0, 0, 0, 0, 0, 0, 0, 708, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 709, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 493, 710, 0, 0, 0, 0, 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 712, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 713, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -char tile_atlas[256] = {-2, 9, 7, 7, 10, 7, 7, 7, 5, 5, 8, 8, 8, 8, 8, 8, 3, 6, 6, 6, 3, 6, 6, 6, -1, -1, -1, -1, -1, -1, -1, -1, 11, 5, 8, 8, 14, 8, 8, 8, 5, 5, 8, 8, 8, 8, 8, 8, 0, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, -1, -1, 0, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, -1, -1, 0, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 13, 6, 6, 3, 6, 6, 6, 2, 2, -1, -1, -1, -1, -1, -1, 3, 6, 6, 6, 3, 6, 6, 6, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, -1, -1, 0, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, -1, -1, 0, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, -1, -1, 0, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; \ No newline at end of file diff --git a/pufferlib/ocean/pacman/binding.c b/pufferlib/ocean/pacman/binding.c deleted file mode 100644 index f9f8c1c81..000000000 --- a/pufferlib/ocean/pacman/binding.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "pacman.h" - -#define Env PacmanEnv -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->randomize_starting_position = unpack(kwargs, "randomize_starting_position"); - env->min_start_timeout = unpack(kwargs, "min_start_timeout"); - env->max_start_timeout = unpack(kwargs, "max_start_timeout"); - env->frightened_time = unpack(kwargs, "frightened_time"); - env->max_mode_changes = unpack(kwargs, "max_mode_changes"); - env->scatter_mode_length = unpack(kwargs, "scatter_mode_length"); - env->chase_mode_length = unpack(kwargs, "chase_mode_length"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/pacman/helpers.h b/pufferlib/ocean/pacman/helpers.h deleted file mode 100644 index d258ca1ed..000000000 --- a/pufferlib/ocean/pacman/helpers.h +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include - -typedef struct Position { - int x; - int y; -} Position; - - -#define DOWN (0) -#define UP (1) -#define RIGHT (2) -#define LEFT (3) - -static inline int reverse_direction(int direction) { - return direction ^ 1; -} - -static inline bool pos_equal(Position a, Position b) { - return a.x == b.x && a.y == b.y; -} - -static inline int pos_distance_squared(Position pos, Position target) { - int dx = pos.x - target.x; - int dy = pos.y - target.y; - return dx * dx + dy * dy; -} - -static inline Position pos_move(Position pos, int direction, int distance) { - switch (direction) { - case UP: - pos.y -= distance; - break; - case DOWN: - pos.y += distance; - break; - case LEFT: - pos.x -= distance; - break; - case RIGHT: - pos.x += distance; - break; - } - - return pos; -} - -static inline int rand_range(int min, int max) { - if (min == max) { - return min; - } - - return min + (rand() % (max - min)); -} diff --git a/pufferlib/ocean/pacman/pacman.c b/pufferlib/ocean/pacman/pacman.c deleted file mode 100644 index a1a9d599a..000000000 --- a/pufferlib/ocean/pacman/pacman.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include "pacman.h" -#include "puffernet.h" - -void demo() { - // printf("OBSERVATIONS_COUNT: %d\n", OBSERVATIONS_COUNT); - Weights* weights = load_weights("resources/pacman/pacman_weights.bin", 170117); - int logit_sizes[1] = {4}; - LinearLSTM* net = make_linearlstm(weights, 1, OBSERVATIONS_COUNT, logit_sizes, 1); - - PacmanEnv env = { - .randomize_starting_position = false, - .min_start_timeout = 0, // randomized ghost delay range - .max_start_timeout = 49, - .frightened_time = 35, // ghost frighten time - .max_mode_changes = 6, - .scatter_mode_length = 700, - .chase_mode_length = 70, - }; - allocate(&env); - c_reset(&env); - - Client* client = make_client(&env); - bool human_control = false; - - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) env.actions[0] = DOWN; - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) env.actions[0] = UP; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) env.actions[0] = LEFT; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) env.actions[0] = RIGHT; - human_control = true; - } else { - human_control = false; - } - - if (!human_control) { - forward_linearlstm(net, env.observations, env.actions); - } - - c_step(&env); - if (env.terminals[0]) { - c_reset(&env); - } - - for (int i = 0; i < FRAMES; i++) { - c_render(&env); - } - } - free_linearlstm(net); - free(weights); - free_allocated(&env); - close_client(client); -} - -void performance_test() { - long test_time = 10; - PacmanEnv env = {}; - allocate(&env); - c_reset(&env); - - long start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - env.actions[0] = rand() % 4; - c_step(&env); - i++; - } - long end = time(NULL); - printf("SPS: %ld\n", i / (end - start)); - free_allocated(&env); -} - -int main() { - //performance_test(); - demo(); - return 0; -} diff --git a/pufferlib/ocean/pacman/pacman.h b/pufferlib/ocean/pacman/pacman.h deleted file mode 100644 index b00c7002c..000000000 --- a/pufferlib/ocean/pacman/pacman.h +++ /dev/null @@ -1,903 +0,0 @@ -#include "helpers.h" -#include "raylib.h" -#include -#include -#include -#include -#include -#include - -#define PX_PADDING_TOP 40 // 40px padding on top of the window - -#define LOG_BUFFER_SIZE 1024 - -#define MAX_STEPS 10000 // max steps before truncation - -#define GHOST_OBSERVATIONS_COUNT 9 -#define PLAYER_OBSERVATIONS_COUNT 11 -#define NUM_GHOSTS 4 -#define FRAMES 7 // Fixed number of frames per step for interpolation - -#define NUM_DOTS 240 -#define NUM_POWERUPS 4 -#define OBSERVATIONS_COUNT \ - (PLAYER_OBSERVATIONS_COUNT + GHOST_OBSERVATIONS_COUNT * NUM_GHOSTS + NUM_DOTS + NUM_POWERUPS) - -#define PINKY_TARGET_LEAD 4 -#define INKY_TARGET_LEAD 2 -#define CLYDE_TARGET_RADIUS 8 - -typedef struct Log Log; -struct Log { - float episode_return; - float episode_length; - float score; - float n; -}; - -typedef enum Tile { - WALL_TILE = '#', - DOT_TILE = '.', - POWER_TILE = 'x', - PLAYER_TILE = 'p', - EMPTY_TILE = ' ', - - INKY_TILE = '1', - BLINKY_TILE = '2', - PINKY_TILE = '3', - CLYDE_TILE = '4', -} Tile; - -#define MAP_HEIGHT 31 -#define MAP_WIDTH 28 - -static const char original_map[MAP_HEIGHT][MAP_WIDTH] = { - "############################", - "#............##............#", - "#.####.#####.##.#####.####.#", - "#x####.#####.##.#####.####x#", - "#.####.#####.##.#####.####.#", - "#..........................#", - "#.####.##.########.##.####.#", - "#.####.##.########.##.####.#", - "#......##....##....##......#", - "######.##### ## #####.######", - "######.##### ## #####.######", - "######.## 1234 ##.######", - "######.## ######## ##.######", - "######.## ######## ##.######", - " . ######## . ", - "######.## ######## ##.######", - "######.## ######## ##.######", - "######.## ##.######", - "######.## ######## ##.######", - "######.## ######## ##.######", - "#............##............#", - "#.####.#####.##.#####.####.#", - "#.####.#####.##.#####.####.#", - "#x..##.......p .......##..x#", - "###.##.##.########.##.##.###", - "###.##.##.########.##.##.###", - "#......##....##....##......#", - "#.##########.##.##########.#", - "#.##########.##.##########.#", - "#..........................#", - "############################", -}; - -static const Position GHOST_CORNERS[NUM_GHOSTS] = { - {3, -3}, // PINKY - {MAP_WIDTH - 4, -3}, // BLINKY - {MAP_WIDTH - 1, MAP_HEIGHT}, // INKY - {0, MAP_HEIGHT} // CLYDE -}; - -static const char GHOST_TILES[NUM_GHOSTS] = {PINKY_TILE, BLINKY_TILE, INKY_TILE, CLYDE_TILE}; - -typedef struct Ghost { - Position spawn_pos; - Position pos; - Position last_pos; // use for interpolation - Position target; - int direction; - int start_timeout; // randomized delay before moving - bool frightened; // whether the ghost is frightened - bool return_to_spawn; // whether to return to spawn position - bool half_move; -} Ghost; - -typedef struct Client Client; -typedef struct PacmanEnv { - Client *client; - bool randomize_starting_position; // randomize player starting position - int min_start_timeout; // randomized ghost delay range - int max_start_timeout; - int frightened_time; // ghost frighten time - int max_mode_changes; - int scatter_mode_length; - int chase_mode_length; - - float *observations; - int *actions; - float *rewards; - char *terminals; - Log log; - - int step_count; - int score; - - Tile *game_map; - float *pickup_obs; - float **pickup_obs_map; - - int remaining_pickups; - - Position *possible_spawn_pos; - - Position player_spawn_pos; // for when it's not randomized - Position player_pos; - Position last_player_pos; - int player_direction; - - bool reverse_directions; - bool scatter_mode; - int mode_time_left; - int mode_changes; - - int frightened_time_left; - - bool player_caught; - - Ghost ghosts[NUM_GHOSTS]; -} PacmanEnv; - -void add_log(PacmanEnv *env) { - env->log.score += env->score; - env->log.episode_return += env->score; - env->log.episode_length = env->step_count; - env->log.n++; -} - -static inline Position pos_move_wrapped(Position pos, int direction, int distance) { - pos = pos_move(pos, direction, distance); - if (pos.x < 0) - pos.x = MAP_WIDTH - 1; - else if (pos.x >= MAP_WIDTH) - pos.x = 0; - return pos; -} - -static inline Tile *tile_at(PacmanEnv *env, Position pos) { - return &env->game_map[pos.y * MAP_WIDTH + pos.x]; -} - -static inline bool can_move_in_direction(PacmanEnv *env, Position pos, int direction) { - return *tile_at(env, pos_move_wrapped(pos, direction, 1)) != WALL_TILE; -} - -void init(PacmanEnv *env) { - int dot_count = 0; - Position pos; - Tile source_tile; - Tile *target_tile; - - env->game_map = (Tile *)calloc(MAP_WIDTH * MAP_HEIGHT, sizeof(Tile)); - env->possible_spawn_pos = (Position *)calloc(NUM_DOTS, sizeof(Position)); - - env->pickup_obs = (float *)calloc(NUM_DOTS + NUM_POWERUPS, sizeof(float)); - env->pickup_obs_map = (float **)calloc(MAP_WIDTH * MAP_HEIGHT, sizeof(float *)); - - int obs_map_n = 0; - - // one time map setup - for (int y = 0; y < MAP_HEIGHT; y++) { - for (int x = 0; x < MAP_WIDTH; x++) { - source_tile = original_map[y][x]; - - pos = (Position){x, y}; - target_tile = tile_at(env, pos); - *target_tile = source_tile; - - float **p = &env->pickup_obs_map[y * MAP_WIDTH + x]; - *p = NULL; - - switch (source_tile) { - case DOT_TILE: - env->possible_spawn_pos[dot_count] = pos; - dot_count++; - case POWER_TILE: - *p = &env->pickup_obs[obs_map_n++]; - break; - case PLAYER_TILE: - env->player_spawn_pos = pos; - break; - default: - break; - } - - for (int i = 0; i < NUM_GHOSTS; i++) { - if (source_tile == GHOST_TILES[i]) { - env->ghosts[i].spawn_pos = pos; - } - } - } - } -} - -void allocate(PacmanEnv *env) { - init(env); - env->observations = (float *)calloc(OBSERVATIONS_COUNT, sizeof(float)); - env->actions = (int *)calloc(1, sizeof(int)); - env->rewards = (float *)calloc(1, sizeof(float)); - env->terminals = (char *)calloc(1, sizeof(char)); -} - -void c_close(PacmanEnv *env) { - free(env->game_map); - free(env->possible_spawn_pos); - - free(env->pickup_obs); - free(env->pickup_obs_map); -} - -void free_allocated(PacmanEnv *env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -#define INV_MAP_WIDTH (1.0f / MAP_WIDTH) -#define INV_MAP_HEIGHT (1.0f / MAP_HEIGHT) - -void compute_observations(PacmanEnv *env) { - float *obs = env->observations; - // player observations - obs[0] = env->player_pos.x * INV_MAP_WIDTH; - obs[1] = env->player_pos.y * INV_MAP_HEIGHT; - obs[2] = env->player_direction == UP; - obs[3] = env->player_direction == DOWN; - obs[4] = env->player_direction == LEFT; - obs[5] = env->player_direction == RIGHT; - obs[6] = can_move_in_direction(env, env->player_pos, UP); - obs[7] = can_move_in_direction(env, env->player_pos, DOWN); - obs[8] = can_move_in_direction(env, env->player_pos, LEFT); - obs[9] = can_move_in_direction(env, env->player_pos, RIGHT); - obs[10] = env->frightened_time_left / (float)env->frightened_time; - - // ghost obs - for (int i = 0; i < NUM_GHOSTS; i++) { - Ghost *ghost = &env->ghosts[i]; - int p = PLAYER_OBSERVATIONS_COUNT + (i * GHOST_OBSERVATIONS_COUNT); - - obs[p] = ghost->pos.x * INV_MAP_WIDTH; - obs[p + 1] = ghost->pos.y * INV_MAP_HEIGHT; - obs[p + 2] = ghost->direction == UP; - obs[p + 3] = ghost->direction == DOWN; - obs[p + 4] = ghost->direction == LEFT; - obs[p + 5] = ghost->direction == RIGHT; - obs[p + 6] = !ghost->frightened && !ghost->return_to_spawn; - obs[p + 7] = ghost->frightened; - obs[p + 8] = ghost->return_to_spawn; - } - - memcpy(obs + PLAYER_OBSERVATIONS_COUNT + (NUM_GHOSTS * GHOST_OBSERVATIONS_COUNT), - env->pickup_obs, sizeof(float) * (NUM_DOTS + NUM_POWERUPS)); -} - -void update_interpolation(PacmanEnv *env) { - env->last_player_pos = env->player_pos; - - for (int i = 0; i < NUM_GHOSTS; i++) { - Ghost *ghost = &env->ghosts[i]; - if (!ghost->frightened || !ghost->half_move) { - ghost->last_pos = ghost->pos; - } - } -} - -static inline void reset_round(PacmanEnv *env) { - env->scatter_mode = false; - env->mode_time_left = 0; - env->mode_changes = 0; - env->frightened_time_left = 0; - - env->step_count = 0; - env->remaining_pickups = NUM_DOTS + NUM_POWERUPS; - - for (int i = 0; i < NUM_DOTS + NUM_POWERUPS; i++) { - env->pickup_obs[i] = 1.0f; - } - - for (int i = 0; i < NUM_GHOSTS; i++) { - Ghost *ghost = &env->ghosts[i]; - ghost->pos = ghost->spawn_pos; - ghost->direction = UP; - ghost->start_timeout = rand_range(env->min_start_timeout, env->max_start_timeout); - ghost->frightened = false; - ghost->return_to_spawn = false; - ghost->half_move = false; - } - - for (int y = 0; y < MAP_HEIGHT; y++) { - for (int x = 0; x < MAP_WIDTH; x++) { - env->game_map[y * MAP_WIDTH + x] = (Tile)original_map[y][x]; - } - } - - if (env->randomize_starting_position) { - int player_randomizer = rand() % NUM_DOTS; - env->player_pos = env->possible_spawn_pos[player_randomizer]; - } else { - env->player_pos = env->player_spawn_pos; - } - env->player_direction = RIGHT; -} - -void c_reset(PacmanEnv *env) { - env->score = 0; - reset_round(env); - compute_observations(env); - update_interpolation(env); -} - -static inline void set_frightened(PacmanEnv *env) { - env->frightened_time_left = env->frightened_time; - env->reverse_directions = true; - - for (int i = 0; i < NUM_GHOSTS; i++) { - env->ghosts[i].frightened = !env->ghosts[i].return_to_spawn; - } -} - -static inline void unset_pickup_obs(PacmanEnv *env, Position pos) { - float *obs = env->pickup_obs_map[pos.y * MAP_WIDTH + pos.x]; - if (obs != NULL) { - *obs = 0.0f; - } -} - -static inline void player_move(PacmanEnv *env, int action) { - Position new_pos = pos_move_wrapped(env->player_pos, action, 1); - Tile *new_tile = tile_at(env, new_pos); - - // if the player action is into a wall, move in the current direction - if (*new_tile == WALL_TILE) { - new_pos = pos_move_wrapped(env->player_pos, env->player_direction, 1); - new_tile = tile_at(env, new_pos); - } else { - env->player_direction = action; - } - - if (*new_tile != WALL_TILE) { - env->player_pos = new_pos; - - if (*new_tile == POWER_TILE) { - set_frightened(env); - } - if (*new_tile == DOT_TILE || *new_tile == POWER_TILE) { - env->score += 1.0f; - env->rewards[0] += 1.0f; - - env->remaining_pickups--; - *new_tile = EMPTY_TILE; - - unset_pickup_obs(env, new_pos); - } - } -} - -static inline int ghost_movement_options(PacmanEnv *env, Ghost *ghost, int *directions) { - int n = 0; - int rev = reverse_direction(ghost->direction); - - for (int i = 0; i < 4; i++) { - if (i != rev && can_move_in_direction(env, ghost->pos, i)) { - directions[n++] = i; - } - } - return n; -} - -static inline int min_index(int *array, int length) { - int min_index = 0; - for (int i = 1; i < length; i++) { - if (array[i] < array[min_index]) { - min_index = i; - } - } - return min_index; -} - -static inline int ghost_direction(PacmanEnv *env, Ghost *ghost) { - int directions[4]; - int distances[4]; - if (env->reverse_directions && !ghost->return_to_spawn) { - return reverse_direction(ghost->direction); - } - - int option_count = ghost_movement_options(env, ghost, directions); - - if (option_count == 1) { - return directions[0]; - } - - if (ghost->frightened) { - int random_index = rand() % option_count; - return directions[random_index]; - } - - for (int i = 0; i < option_count; i++) { - distances[i] = pos_distance_squared(pos_move(ghost->pos, directions[i], 1), ghost->target); - } - - return directions[min_index(distances, option_count)]; -} - -#define PINKY 0 -#define BLINKY 1 -#define INKY 2 -#define CLYDE 3 - -static inline void set_chase_targets(PacmanEnv *env) { - env->ghosts[PINKY].target = pos_move(env->player_pos, env->player_direction, PINKY_TARGET_LEAD); - env->ghosts[BLINKY].target = env->player_pos; - - Position inky_intermediate = pos_move(env->player_pos, env->player_direction, INKY_TARGET_LEAD); - env->ghosts[INKY].target.x = 2 * inky_intermediate.x - env->ghosts[1].pos.x; - env->ghosts[INKY].target.y = 2 * inky_intermediate.y - env->ghosts[1].pos.y; - - int clyde_distance = pos_distance_squared(env->player_pos, env->ghosts[3].pos); - if (clyde_distance > CLYDE_TARGET_RADIUS * CLYDE_TARGET_RADIUS) { - env->ghosts[CLYDE].target = env->player_pos; - } else { - env->ghosts[CLYDE].target = GHOST_CORNERS[3]; - } -} - -static inline bool check_collision(Position a, Position old_a, Position b, Position old_b) { - return (a.x >= b.x - 1 && a.x <= b.x + 1 && a.y == b.y) || - (a.y >= b.y - 1 && a.y <= b.y + 1 && a.x == b.x); -} - -static inline void ghost_move(PacmanEnv *env, Ghost *ghost, Position old_player_pos) { - Position old_ghost_position = ghost->pos; - --ghost->start_timeout; - - if (ghost->frightened && ghost->half_move) { - ghost->half_move = false; - } else { - ghost->half_move = true; - - if (ghost->return_to_spawn) { - ghost->target = ghost->spawn_pos; - } - - ghost->direction = ghost_direction(env, ghost); - - if (ghost->start_timeout < 0) { - Position new_pos = pos_move_wrapped(ghost->pos, ghost->direction, 1); - if (*tile_at(env, new_pos) != WALL_TILE) { - ghost->pos = new_pos; - } - } - } - - if (ghost->return_to_spawn) { - if (pos_equal(ghost->pos, ghost->spawn_pos)) { - ghost->return_to_spawn = false; - } - } else if (check_collision(ghost->pos, old_ghost_position, env->player_pos, old_player_pos)) { - if (ghost->frightened) { - ghost->frightened = false; - ghost->half_move = false; - ghost->return_to_spawn = true; - - env->rewards[0] += 1.0f; - } else { - env->player_caught = true; - } - } -} - -static inline void check_mode_change(PacmanEnv *env) { - if (env->mode_changes > env->max_mode_changes) { - return; - } - - if (--env->mode_time_left <= 0) { - env->scatter_mode = !env->scatter_mode; - env->reverse_directions = true; - env->mode_changes++; - - if (env->scatter_mode) { - env->mode_time_left = env->scatter_mode_length; - } else { - env->mode_time_left = env->chase_mode_length; - } - } -} - -void c_step(PacmanEnv *env) { - update_interpolation(env); - - Position old_player_pos = env->player_pos; - int action = env->actions[0]; - - env->step_count += 1; - env->terminals[0] = 0; - env->rewards[0] = 0.0f; - - env->reverse_directions = false; - env->player_caught = false; - - if (env->frightened_time_left > 0) { - env->frightened_time_left--; - } else { - for (int i = 0; i < NUM_GHOSTS; i++) { - env->ghosts[i].frightened = false; - env->ghosts[i].half_move = false; - } - } - - check_mode_change(env); - if (env->scatter_mode) { - for (int i = 0; i < NUM_GHOSTS; i++) { - env->ghosts[i].target = GHOST_CORNERS[i]; - } - } else { - set_chase_targets(env); - } - - player_move(env, action); - - for (int i = 0; i < NUM_GHOSTS; i++) { - ghost_move(env, &env->ghosts[i], old_player_pos); - } - - compute_observations(env); - - if (env->player_caught || env->step_count >= MAX_STEPS || env->remaining_pickups <= 0) { - add_log(env); - - env->terminals[0] = 1; - c_reset(env); - } -} - -typedef struct DirectionSprites { - Texture2D up; - Texture2D down; - Texture2D left; - Texture2D right; -} DirectionSprites; - -typedef struct Client Client; -struct Client { - int tile_size; - int frame; - - Texture2D tileset; - Texture2D pacman; - Texture2D frightened; - DirectionSprites ghost_sprites[4]; - DirectionSprites eyes; -}; - -Vector2 lerp_position(Position a, Position b, float progress) { - if (abs(a.x - b.x) > 1) { - b.x = a.x; - } - - float a_x = (float)a.x; - float a_y = (float)a.y; - float b_x = (float)b.x; - float b_y = (float)b.y; - - return (Vector2){a_x + (b_x - a_x) * progress, a_y + (b_y - a_y) * progress}; -} - -void draw_tiled(Client *client, Texture2D texture, Vector2 position, float rotation, bool flip_x, - float source_width, float source_height) { - Rectangle source = (Rectangle){0, 0, flip_x ? -source_width : source_width, source_height}; - - DrawTexturePro(texture, source, - (Rectangle){(position.x + 0.50f) * client->tile_size, - (position.y + 0.50f) * client->tile_size + PX_PADDING_TOP, - client->tile_size * 1.5f, client->tile_size * 1.5f}, - (Vector2){client->tile_size * 0.75f, client->tile_size * 0.75f}, rotation, - WHITE); -} - -void draw_entity(Client *client, Texture2D texture, Position previous_pos, Position pos, - float progress, float rotation, bool flip_x, float source_width, - float source_height) { - Vector2 position; - - if (pos.x == 0 && previous_pos.x == MAP_WIDTH - 1) { - position = lerp_position((Position){-1, previous_pos.y}, pos, progress); - draw_tiled(client, texture, position, rotation, flip_x, source_width, source_height); - - position.x += (float)MAP_WIDTH; - draw_tiled(client, texture, position, rotation, flip_x, source_width, source_height); - } else if (previous_pos.x == 0 && pos.x == MAP_WIDTH - 1) { - position = lerp_position((Position){MAP_WIDTH, previous_pos.y}, pos, progress); - draw_tiled(client, texture, position, rotation, flip_x, source_width, source_height); - - position.x -= (float)MAP_WIDTH; - draw_tiled(client, texture, position, rotation, flip_x, source_width, source_height); - } else { - position = lerp_position(previous_pos, pos, progress); - draw_tiled(client, texture, position, rotation, flip_x, source_width, source_height); - } -} - - -Client *make_client(PacmanEnv *env) { - Client *client = (Client *)calloc(1, sizeof(Client)); - env->client = client; - client->tile_size = 20; - - update_interpolation(env); - - srand(time(NULL)); - - InitWindow(client->tile_size * MAP_WIDTH, client->tile_size * MAP_HEIGHT + PX_PADDING_TOP, - "PufferLib Pacman"); - SetTargetFPS(60); - - client->tileset = LoadTexture("resources/pacman/tileset.png"); - client->pacman = LoadTexture("resources/shared/puffers_128.png"); - client->frightened = LoadTexture("resources/pacman/scared.png"); - - client->ghost_sprites[0].up = LoadTexture("resources/pacman/pinky_up.png"); - client->ghost_sprites[0].down = LoadTexture("resources/pacman/pinky_down.png"); - client->ghost_sprites[0].left = LoadTexture("resources/pacman/pinky_left.png"); - client->ghost_sprites[0].right = LoadTexture("resources/pacman/pinky_right.png"); - - client->ghost_sprites[1].up = LoadTexture("resources/pacman/blinky_up.png"); - client->ghost_sprites[1].down = LoadTexture("resources/pacman/blinky_down.png"); - client->ghost_sprites[1].left = LoadTexture("resources/pacman/blinky_left.png"); - client->ghost_sprites[1].right = LoadTexture("resources/pacman/blinky_right.png"); - - client->ghost_sprites[2].up = LoadTexture("resources/pacman/inky_up.png"); - client->ghost_sprites[2].down = LoadTexture("resources/pacman/inky_down.png"); - client->ghost_sprites[2].left = LoadTexture("resources/pacman/inky_left.png"); - client->ghost_sprites[2].right = LoadTexture("resources/pacman/inky_right.png"); - - client->ghost_sprites[3].up = LoadTexture("resources/pacman/clyde_up.png"); - client->ghost_sprites[3].down = LoadTexture("resources/pacman/clyde_down.png"); - client->ghost_sprites[3].left = LoadTexture("resources/pacman/clyde_left.png"); - client->ghost_sprites[3].right = LoadTexture("resources/pacman/clyde_right.png"); - - client->eyes.up = LoadTexture("resources/pacman/eyes_up.png"); - client->eyes.down = LoadTexture("resources/pacman/eyes_down.png"); - client->eyes.left = LoadTexture("resources/pacman/eyes_left.png"); - client->eyes.right = LoadTexture("resources/pacman/eyes_right.png"); - - return client; -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -void render_ghost(Client *client, Ghost *ghost, DirectionSprites *sprites, float progress) { - Texture2D texture; - - if (ghost->frightened) { - texture = client->frightened; - - progress *= 0.5f; - if (!ghost->half_move) { - progress += 0.5f; - } - } else { - if (ghost->return_to_spawn) { - sprites = &client->eyes; - } - - switch (ghost->direction) { - case UP: - texture = sprites->up; - break; - case DOWN: - texture = sprites->down; - break; - case LEFT: - texture = sprites->left; - break; - case RIGHT: - texture = sprites->right; - break; - } - } - - draw_entity(client, texture, ghost->last_pos, ghost->pos, progress, 0.0f, false, 16, 16); -} - -void render_player(Client *client, PacmanEnv *env, float progress) { - float rotation = 0.0f; - - if (env->player_direction == UP) { - rotation = 270.0f; - } else if (env->player_direction == DOWN) { - rotation = 90.0f; - } - - draw_entity(client, client->pacman, env->last_player_pos, env->player_pos, progress, - rotation, env->player_direction == LEFT, 128, 128); -} - -bool is_wall(PacmanEnv *env, int x, int y) { - Position pos = {x, y}; - if (pos.x < 0 || pos.x >= MAP_WIDTH || pos.y < 0 || pos.y >= MAP_HEIGHT) { - return true; - } - - return *tile_at(env, pos) == WALL_TILE; -} - -#define TILE_N (1 << 0) -#define TILE_NE (1 << 1) -#define TILE_E (1 << 2) -#define TILE_SE (1 << 3) -#define TILE_S (1 << 4) -#define TILE_SW (1 << 5) -#define TILE_W (1 << 6) -#define TILE_NW (1 << 7) - -int get_tile_index(PacmanEnv *env, Position pos) { - int tile_bits = 0; - - if (is_wall(env, pos.x, pos.y - 1)) - tile_bits |= TILE_N; - if (is_wall(env, pos.x + 1, pos.y - 1)) - tile_bits |= TILE_NE; - if (is_wall(env, pos.x + 1, pos.y)) - tile_bits |= TILE_E; - if (is_wall(env, pos.x + 1, pos.y + 1)) - tile_bits |= TILE_SE; - if (is_wall(env, pos.x, pos.y + 1)) - tile_bits |= TILE_S; - if (is_wall(env, pos.x - 1, pos.y + 1)) - tile_bits |= TILE_SW; - if (is_wall(env, pos.x - 1, pos.y)) - tile_bits |= TILE_W; - if (is_wall(env, pos.x - 1, pos.y - 1)) - tile_bits |= TILE_NW; - - switch (tile_bits) { - case TILE_NW | TILE_N | TILE_NE | TILE_W | TILE_E | TILE_SE: - case TILE_NW | TILE_N | TILE_NE | TILE_W | TILE_E | TILE_SW: - case TILE_NW | TILE_N | TILE_NE | TILE_W | TILE_E: - return 13; - - case TILE_NE | TILE_E | TILE_SE | TILE_N | TILE_S | TILE_NW: - case TILE_NE | TILE_E | TILE_SE | TILE_N | TILE_S | TILE_SW: - case TILE_NE | TILE_E | TILE_SE | TILE_N | TILE_S: - return 6; - - case TILE_SE | TILE_S | TILE_SW | TILE_E | TILE_W | TILE_NW: - case TILE_SE | TILE_S | TILE_SW | TILE_E | TILE_W | TILE_NE: - case TILE_SE | TILE_S | TILE_SW | TILE_E | TILE_W: - return 1; - - case TILE_SW | TILE_W | TILE_NW | TILE_S | TILE_N | TILE_NE: - case TILE_SW | TILE_W | TILE_NW | TILE_S | TILE_N | TILE_SE: - case TILE_SW | TILE_W | TILE_NW | TILE_S | TILE_N: - return 8; - case TILE_S | TILE_E | TILE_SE: - return 0; - case TILE_S | TILE_W | TILE_SW: - return 2; - case TILE_N | TILE_E | TILE_NE: - return 12; - case TILE_N | TILE_W | TILE_NW: - return 14; - - case 255 & ~TILE_NW: - return 25; - case 255 & ~TILE_NE: - return 26; - case 255 & ~TILE_SW: - return 31; - case 255 & ~TILE_SE: - return 32; - } - - return 7; -} - -void render_tile(Client *client, PacmanEnv *env, Position pos) { - int tile_index = get_tile_index(env, pos); - int tile_x = tile_index % 6; - int tile_y = tile_index / 6; - Rectangle source = (Rectangle){tile_x * 9, tile_y * 9, 8, 8}; - - DrawTexturePro(client->tileset, source, - (Rectangle){pos.x * client->tile_size, - pos.y * client->tile_size + PX_PADDING_TOP, client->tile_size, - client->tile_size}, - (Vector2){0, 0}, 0.0f, WHITE); -} - -void render_map(Client *client, PacmanEnv *env) { - for (int y = 0; y < MAP_HEIGHT; y++) { - for (int x = 0; x < MAP_WIDTH; x++) { - char tile = env->game_map[y * MAP_WIDTH + x]; - if (tile == WALL_TILE) { - render_tile(client, env, (Position){x, y}); - } else if (tile == DOT_TILE) { - float width = client->tile_size / 4.0f; - float height = client->tile_size / 4.0f; - DrawRectangle(x * client->tile_size + client->tile_size / 2.0f - width / 2.0f, - y * client->tile_size + client->tile_size / 2.0f - height / 2.0f + - PX_PADDING_TOP, - width, height, PUFF_WHITE); - } else if (tile == POWER_TILE) { - DrawCircle(x * client->tile_size + client->tile_size / 2.0f, - y * client->tile_size + client->tile_size / 2.0f + PX_PADDING_TOP, - client->tile_size / 3.0f, PUFF_RED); - } - } - } -} - -void handle_input(PacmanEnv *env) { - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - if (IsKeyPressed(KEY_TAB)) { - ToggleFullscreen(); - } -} - -void c_render(PacmanEnv *env) { - if (env->client == NULL) { - env->client = make_client(env); - } - Client *client = env->client; - - float progress = client->frame / (float)FRAMES; - client->frame = (client->frame + 1) % (FRAMES); - - handle_input(env); - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - render_map(client, env); - - render_player(client, env, progress); - for (int i = 0; i < 4; i++) { - render_ghost(client, &env->ghosts[i], &client->ghost_sprites[i], progress); - } - - DrawText(TextFormat("Score: %i", env->score), 10, 10, 20, WHITE); - DrawText(TextFormat("Mode: %s", env->scatter_mode ? "Scatter" : "Chase"), 150, 10, 20, WHITE); - - EndDrawing(); -} - -void unload_direction_sprites(DirectionSprites *sprites) { - UnloadTexture(sprites->up); - UnloadTexture(sprites->down); - UnloadTexture(sprites->left); - UnloadTexture(sprites->right); -} - -void close_client(Client *client) { - CloseWindow(); - - UnloadTexture(client->tileset); - UnloadTexture(client->pacman); - UnloadTexture(client->frightened); - for (int i = 0; i < 4; i++) { - unload_direction_sprites(&client->ghost_sprites[i]); - } - unload_direction_sprites(&client->eyes); - free(client); -} diff --git a/pufferlib/ocean/pacman/pacman.py b/pufferlib/ocean/pacman/pacman.py deleted file mode 100644 index 1521b6ffb..000000000 --- a/pufferlib/ocean/pacman/pacman.py +++ /dev/null @@ -1,79 +0,0 @@ -import pufferlib - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.pacman import binding - - -class Pacman(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, - randomize_starting_position = 0, - min_start_timeout = 0, - max_start_timeout = 49, - frightened_time = 35, - max_mode_changes = 6, - scatter_mode_length = 70, - chase_mode_length = 140, - log_interval=128, - buf=None, seed=0): - - ghost_observations_count = 9 - player_observations_count = 11 - num_ghosts = 4 - - num_dots = 244 - observations_count = (player_observations_count + ghost_observations_count * num_ghosts + num_dots) - - self.single_observation_space = gymnasium.spaces.Box( - low=0, - high=1, - shape=(observations_count,), - dtype=np.float32 - ) - self.single_action_space = gymnasium.spaces.Discrete(4) - - self.render_mode = render_mode - self.num_agents = num_envs - self.log_interval = log_interval - self.human_action = None - self.tick = 0 - - super().__init__(buf) - - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, - randomize_starting_position = randomize_starting_position, - min_start_timeout = min_start_timeout, - max_start_timeout = max_start_timeout, - frightened_time = frightened_time, - max_mode_changes = max_mode_changes, - scatter_mode_length = scatter_mode_length, - chase_mode_length = chase_mode_length, - ) - - def reset(self, seed=None): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - for _ in range(7): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) diff --git a/pufferlib/ocean/pong/binding.c b/pufferlib/ocean/pong/binding.c deleted file mode 100644 index 4a84ae1d7..000000000 --- a/pufferlib/ocean/pong/binding.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "pong.h" - -#define Env Pong -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->paddle_width = unpack(kwargs, "paddle_width"); - env->paddle_height = unpack(kwargs, "paddle_height"); - env->ball_width = unpack(kwargs, "ball_width"); - env->ball_height = unpack(kwargs, "ball_height"); - env->paddle_speed = unpack(kwargs, "paddle_speed"); - env->ball_initial_speed_x = unpack(kwargs, "ball_initial_speed_x"); - env->ball_initial_speed_y = unpack(kwargs, "ball_initial_speed_y"); - env->ball_max_speed_y = unpack(kwargs, "ball_max_speed_y"); - env->ball_speed_y_increment = unpack(kwargs, "ball_speed_y_increment"); - env->max_score = unpack(kwargs, "max_score"); - env->frameskip = unpack(kwargs, "frameskip"); - env->continuous = unpack(kwargs, "continuous"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/pong/cy_pong.pyx b/pufferlib/ocean/pong/cy_pong.pyx deleted file mode 100644 index 88ba2b66f..000000000 --- a/pufferlib/ocean/pong/cy_pong.pyx +++ /dev/null @@ -1,114 +0,0 @@ -cimport numpy as cnp -from libc.stdlib cimport calloc, free -import os - -cdef extern from "pong.h": - ctypedef char* LOG_KEYS[]; - ctypedef struct Client: - pass - - ctypedef struct Pong: - Client* client; - float log[4]; - float* observations; - float* actions; - float* rewards; - unsigned char* terminals; - float paddle_yl; - float paddle_yr; - float ball_x; - float ball_y; - float ball_vx; - float ball_vy; - unsigned int score_l; - unsigned int score_r; - float width; - float height; - float paddle_width; - float paddle_height; - float ball_width; - float ball_height; - float paddle_speed; - float ball_initial_speed_x; - float ball_initial_speed_y; - float ball_max_speed_y; - float ball_speed_y_increment; - unsigned int max_score; - float min_paddle_y; - float max_paddle_y; - float paddle_dir; - int tick; - int n_bounces; - int win; - int frameskip; - int continuous; - - void init(Pong* env) - void c_reset(Pong* env) - void c_step(Pong* env) - - void c_render(Pong* env) - -cdef class CyPong: - cdef: - Pong* envs - int num_envs - float width - float height - float paddle_width - float paddle_height - float ball_width - float ball_height - - def __init__(self, float[:, :] observations, float[:] actions, - float[:] rewards, unsigned char[:] terminals, int num_envs, - float width, float height, float paddle_width, float paddle_height, - float ball_width, float ball_height, float paddle_speed, - float ball_initial_speed_x, float ball_initial_speed_y, - float ball_max_speed_y, float ball_speed_y_increment, - unsigned int max_score, int frameskip, int continuous): - - self.num_envs = num_envs - self.envs = calloc(num_envs, sizeof(Pong)) - - cdef int i - for i in range(num_envs): - self.envs[i] = Pong( - observations = &observations[i, 0], - actions = &actions[i], - rewards = &rewards[i], - terminals = &terminals[i], - width=width, - height=height, - paddle_width=paddle_width, - paddle_height=paddle_height, - ball_width=ball_width, - ball_height=ball_height, - paddle_speed=paddle_speed, - ball_initial_speed_x=ball_initial_speed_x, - ball_initial_speed_y=ball_initial_speed_y, - ball_max_speed_y=ball_max_speed_y, - ball_speed_y_increment=ball_speed_y_increment, - max_score=max_score, - frameskip=frameskip, - continuous=continuous, - ) - init(&self.envs[i]) - - def reset(self): - cdef int i - for i in range(self.num_envs): - c_reset(&self.envs[i]) - - def step(self): - cdef int i - - for i in range(self.num_envs): - c_step(&self.envs[i]) - - def render(self): - cdef Pong* env = &self.envs[0] - c_render(env) - - def close(self): - free(self.envs) diff --git a/pufferlib/ocean/pong/pong.c b/pufferlib/ocean/pong/pong.c deleted file mode 100644 index 3a91aa877..000000000 --- a/pufferlib/ocean/pong/pong.c +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include "pong.h" -#include "puffernet.h" - -void demo() { - Weights* weights = load_weights("resources/pong/pong_weights.bin", 133764); - - int logit_sizes[1] = {3}; - LinearLSTM* net = make_linearlstm(weights, 1, 8, logit_sizes, 1); - - Pong env = { - .width = 500, - .height = 640, - .paddle_width = 20, - .paddle_height = 70, - .ball_width = 32, - .ball_height = 32, - .paddle_speed = 8, - .ball_initial_speed_x = 10, - .ball_initial_speed_y = 1, - .ball_speed_y_increment = 3, - .ball_max_speed_y = 13, - .max_score = 21, - .frameskip = 1, - .continuous = 0, - }; - allocate(&env); - c_reset(&env); - c_render(&env); - SetTargetFPS(60); - int frame = 0; - while (!WindowShouldClose()) { - // User can take control of the paddle - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if(env.continuous) { - float move = GetMouseWheelMove(); - float clamped_wheel = fmaxf(-1.0f, fminf(1.0f, move)); - env.actions[0] = clamped_wheel; - printf("Mouse wheel move: %f\n", env.actions[0]); - } else { - env.actions[0] = 0.0; - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) env.actions[0] = 1.0; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) env.actions[0] = 2.0; - } - } else if (frame % 8 == 0) { - // Apply frameskip outside the env for smoother rendering - int* actions = (int*)env.actions; - forward_linearlstm(net, env.observations, actions); - env.actions[0] = actions[0]; - } - - frame = (frame + 1) % 8; - c_step(&env); - c_render(&env); - } - free_linearlstm(net); - free(weights); - free_allocated(&env); - close_client(env.client); -} - -void test_performance(int timeout) { - Pong env = { - .width = 500, - .height = 640, - .paddle_width = 20, - .paddle_height = 70, - .ball_width = 32, - .ball_height = 32, - .paddle_speed = 8, - .ball_initial_speed_x = 10, - .ball_initial_speed_y = 1, - .ball_speed_y_increment = 3, - .ball_max_speed_y = 13, - .max_score = 21, - .frameskip = 1, - .continuous = 0, - }; - allocate(&env); - c_reset(&env); - - int start = time(NULL); - int num_steps = 0; - while (time(NULL) - start < timeout) { - env.actions[0] = rand() % 3; - c_step(&env); - num_steps++; - } - - int end = time(NULL); - float sps = num_steps / (end - start); - printf("Test Environment SPS: %f\n", sps); - free_allocated(&env); -} - -int main() { - demo(); - //test_performance(10); -} diff --git a/pufferlib/ocean/pong/pong.h b/pufferlib/ocean/pong/pong.h deleted file mode 100644 index f4e718c2c..000000000 --- a/pufferlib/ocean/pong/pong.h +++ /dev/null @@ -1,331 +0,0 @@ -#include -#include -#include -#include "raylib.h" - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -typedef struct Client Client; -typedef struct Pong Pong; -struct Pong { - Client* client; - Log log; - float* observations; - float* actions; - float* rewards; - unsigned char* terminals; - float paddle_yl; - float paddle_yr; - float ball_x; - float ball_y; - float ball_vx; - float ball_vy; - unsigned int score_l; - unsigned int score_r; - float width; - float height; - float paddle_width; - float paddle_height; - float ball_width; - float ball_height; - float paddle_speed; - float ball_initial_speed_x; - float ball_initial_speed_y; - float ball_max_speed_y; - float ball_speed_y_increment; - unsigned int max_score; - float min_paddle_y; - float max_paddle_y; - float paddle_dir; - int tick; - int n_bounces; - int win; - int frameskip; - int continuous; -}; - -void init(Pong* env) { - // logging - env->tick = 0; - env->n_bounces = 0; - env->win = 0; - - // precompute - env->min_paddle_y = -env->paddle_height / 2; - env->max_paddle_y = env->height - env->paddle_height/2; - - env->paddle_dir = 0; -} - -void allocate(Pong* env) { - init(env); - env->observations = (float*)calloc(8, sizeof(float)); - env->actions = (float*)calloc(1, sizeof(float)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void free_allocated(Pong* env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); -} - -void c_close(Pong* env) { -} - -void add_log(Pong* env) { - float score = (float)env->score_r - (float)env->score_l; - env->log.episode_length += env->tick; - env->log.episode_return += score; - env->log.score += score; - env->log.perf += (float)(env->score_r) / ((float)env->score_l + (float)env->score_r); - env->log.n += 1; -} - -void compute_observations(Pong* env) { - env->observations[0] = (env->paddle_yl - env->min_paddle_y) / (env->max_paddle_y - env->min_paddle_y); - env->observations[1] = (env->paddle_yr - env->min_paddle_y) / (env->max_paddle_y - env->min_paddle_y); - env->observations[2] = env->ball_x / env->width; - env->observations[3] = env->ball_y / env->height; - env->observations[4] = (env->ball_vx + env->ball_initial_speed_x) / (2 * env->ball_initial_speed_x); - env->observations[5] = (env->ball_vy + env->ball_max_speed_y) / (2 * env->ball_max_speed_y); - env->observations[6] = env->score_l / env->max_score; - env->observations[7] = env->score_r / env->max_score; -} - -void reset_round(Pong* env) { - env->paddle_yl = env->height / 2 - env->paddle_height / 2; - env->paddle_yr = env->height / 2 - env->paddle_height / 2; - env->ball_x = env->width / 5; - env->ball_y = env->height / 2 - env->ball_height / 2; - env->ball_vx = env->ball_initial_speed_x; - env->ball_vy = (rand() % 2 - 1) * env->ball_initial_speed_y; - env->tick = 0; - env->n_bounces = 0; -} - -void c_reset(Pong* env) { - reset_round(env); - env->score_l = 0; - env->score_r = 0; - compute_observations(env); -} - -void c_step(Pong* env) { - env->tick += 1; - env->rewards[0] = 0; - env->terminals[0] = 0; - // move ego paddle - if (env->continuous) { - env->paddle_dir = env->actions[0]; - } else { - float act = env->actions[0]; - env->paddle_dir = 0; - if (act == 0.0) { // still - env->paddle_dir = 0; - } else if (act == 1.0) { // up - env->paddle_dir = 1; - } else if (act == 2.0) { // down - env->paddle_dir = -1; - } - } - - for (int i = 0; i < env->frameskip; i++) { - env->paddle_yr += env->paddle_speed * env->paddle_dir; - - // move opponent paddle - float opp_paddle_delta = env->ball_y - (env->paddle_yl + env->paddle_height / 2); - opp_paddle_delta = fminf(fmaxf(opp_paddle_delta, -env->paddle_speed), env->paddle_speed); - env->paddle_yl += opp_paddle_delta; - - // clip paddles - env->paddle_yr = fminf(fmaxf( - env->paddle_yr, env->min_paddle_y), env->max_paddle_y); - env->paddle_yl = fminf(fmaxf( - env->paddle_yl, env->min_paddle_y), env->max_paddle_y); - - // move ball - env->ball_x += env->ball_vx; - env->ball_y += env->ball_vy; - - // handle collision with top & bottom walls - if (env->ball_y < 0 || env->ball_y + env->ball_height > env->height) { - env->ball_vy = -env->ball_vy; - } - - // handle collision on left - if (env->ball_x < 0) { - if (env->ball_y + env->ball_height > env->paddle_yl && \ - env->ball_y < env->paddle_yl + env->paddle_height) { - // collision with paddle - env->ball_vx = -env->ball_vx; - env->n_bounces += 1; - } else { - // collision with wall: WIN - env->win = 1; - env->score_r += 1; - env->rewards[0] = 1; // agent wins - if (env->score_r == env->max_score) { - env->terminals[0] = 1; - add_log(env); - c_reset(env); - return; - } else { - reset_round(env); - return; - } - } - } - - // handle collision on right (TODO duplicated code) - if (env->ball_x + env->ball_width > env->width) { - if (env->ball_y + env->ball_height > env->paddle_yr && \ - env->ball_y < env->paddle_yr + env->paddle_height) { - // collision with paddle - env->ball_vx = -env->ball_vx; - env->n_bounces += 1; - env->rewards[0] = 0.1; // agent bounced the ball - // ball speed change - env->ball_vy += env->ball_speed_y_increment * env->paddle_dir; - env->ball_vy = fminf(fmaxf(env->ball_vy, -env->ball_max_speed_y), env->ball_max_speed_y); - if (fabsf(env->ball_vy) < 0.01) { // we dont want a horizontal ball - env->ball_vy = env->ball_speed_y_increment; - } - } else { - // collision with wall: LOSE - env->win = 0; - env->score_l += 1; - env->rewards[0] = -1.0; - if (env->score_l == env->max_score) { - env->terminals[0] = 1; - add_log(env); - c_reset(env); - return; - } else { - reset_round(env); - return; - } - } - - // clip ball - env->ball_x = fminf(fmaxf(env->ball_x, 0), env->width - env->ball_width); - env->ball_y = fminf(fmaxf(env->ball_y, 0), env->height - env->ball_height); - } - compute_observations(env); - } -} - -typedef struct Client Client; -struct Client { - float width; - float height; - float paddle_width; - float paddle_height; - float ball_width; - float ball_height; - float x_pad; - Color paddle_left_color; - Color paddle_right_color; - Color ball_color; - Texture2D ball; -}; - -Client* make_client(Pong* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = env->width; - client->height = env->height; - client->paddle_width = env->paddle_width; - client->paddle_height = env->paddle_height; - client->ball_width = env->ball_width; - client->ball_height = env->ball_height; - client->x_pad = 3*client->paddle_width; - client->paddle_left_color = (Color){255, 0, 0, 255}; - client->paddle_right_color = (Color){0, 255, 255, 255}; - client->ball_color = (Color){255, 255, 255, 255}; - - InitWindow(env->width + 2*client->x_pad, env->height, "PufferLib Pong"); - SetTargetFPS(60 / env->frameskip); - - client->ball = LoadTexture("resources/shared/puffers_128.png"); - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -void c_render(Pong* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - Client* client = env->client; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - // Draw left paddle - DrawRectangle( - client->x_pad - client->paddle_width, - client->height - env->paddle_yl - client->paddle_height, - client->paddle_width, - client->paddle_height, - client->paddle_left_color - ); - - // Draw right paddle - DrawRectangle( - client->width + client->x_pad, - client->height - env->paddle_yr - client->paddle_height, - client->paddle_width, - client->paddle_height, - client->paddle_right_color - ); - - // Draw ball - DrawTexturePro( - client->ball, - (Rectangle){ - (env->ball_vx > 0) ? 0 : 128, - 0, 128, 128, - }, - (Rectangle){ - client->x_pad + env->ball_x, - client->height - env->ball_y - client->ball_height, - client->ball_width, - client->ball_height - }, - (Vector2){0, 0}, - 0, - WHITE - ); - - //DrawFPS(10, 10); - - // Draw scores - DrawText( - TextFormat("%i", env->score_l), - client->width / 2 + client->x_pad - 50 - MeasureText(TextFormat("%i", env->score_l), 30) / 2, - 10, 30, (Color){0, 187, 187, 255} - ); - DrawText( - TextFormat("%i", env->score_r), - client->width / 2 + client->x_pad + 50 - MeasureText(TextFormat("%i", env->score_r), 30) / 2, - 10, 30, (Color){0, 187, 187, 255} - ); - - EndDrawing(); -} diff --git a/pufferlib/ocean/pong/pong.py b/pufferlib/ocean/pong/pong.py deleted file mode 100644 index 1a37a693b..000000000 --- a/pufferlib/ocean/pong/pong.py +++ /dev/null @@ -1,159 +0,0 @@ -'''High-perf Pong - -Inspired from https://gist.github.com/Yttrmin/18ecc3d2d68b407b4be1 -& https://jair.org/index.php/jair/article/view/10819/25823 -& https://www.youtube.com/watch?v=PSQt5KGv7Vk -''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.pong import binding -#import binding - -class Pong(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, - width=500, height=640, paddle_width=20, paddle_height=70, - ball_width=32, ball_height=32, paddle_speed=8, - ball_initial_speed_x=10, ball_initial_speed_y=1, - ball_speed_y_increment=3, ball_max_speed_y=13, - max_score=21, frameskip=1, continuous=False, log_interval=128, - buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=1, shape=(8,), dtype=np.float32, - ) - if continuous: - self.single_action_space = gymnasium.spaces.Box( - low=-1, high=1, dtype=np.float32, - ) - else: - self.single_action_space = gymnasium.spaces.Discrete(3) - - self.render_mode = render_mode - self.num_agents = num_envs - self.continuous = continuous - self.log_interval = log_interval - self.human_action = None - self.tick = 0 - - super().__init__(buf) - if continuous: - self.actions = self.actions.flatten() - else: - self.actions = self.actions.astype(np.float32) - - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, width=width, height=height, - paddle_width=paddle_width, paddle_height=paddle_height, - ball_width=ball_width, ball_height=ball_height, - paddle_speed=paddle_speed, ball_initial_speed_x=ball_initial_speed_x, - ball_initial_speed_y=ball_initial_speed_y, - ball_max_speed_y=ball_max_speed_y, ball_speed_y_increment=ball_speed_y_increment, - max_score=max_score, frameskip=frameskip, continuous=continuous - ) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - if self.continuous: - self.actions[:] = np.clip(actions.flatten(), -1.0, 1.0) - else: - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -#from cy_pong import CyPong -class CythonPong(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, - width=500, height=640, paddle_width=20, paddle_height=70, - ball_width=32, ball_height=32, paddle_speed=8, - ball_initial_speed_x=10, ball_initial_speed_y=1, - ball_speed_y_increment=3, ball_max_speed_y=13, - max_score=21, frameskip=1, continuous=False, report_interval=128, buf=None): - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=1, shape=(8,), dtype=np.float32, - ) - if continuous: - self.single_action_space = gymnasium.spaces.Box( - low=-1, high=1, dtype=np.float32, - ) - else: - self.single_action_space = gymnasium.spaces.Discrete(3) - - self.render_mode = render_mode - self.num_agents = num_envs - self.continuous = continuous - self.report_interval = report_interval - self.human_action = None - self.tick = 0 - - super().__init__(buf) - if continuous: - self.actions = self.actions.flatten() - else: - self.actions = self.actions.astype(np.float32) - self.c_envs = CyPong(self.observations, self.actions, self.rewards, - self.terminals, num_envs, width, height, - paddle_width, paddle_height, ball_width, ball_height, - paddle_speed, ball_initial_speed_x, ball_initial_speed_y, - ball_max_speed_y, ball_speed_y_increment, max_score, frameskip, continuous) - - def reset(self, seed=None): - self.tick = 0 - self.c_envs.reset() - return self.observations, [] - - def step(self, actions): - if self.continuous: - self.actions[:] = np.clip(actions.flatten(), -1.0, 1.0) - else: - self.actions[:] = actions - - self.c_envs.step() - info = [] - self.tick += 1 - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - self.c_envs.render() - - def close(self): - self.c_envs.close() - -def test_performance(cls, timeout=10, atn_cache=1024): - env = cls(num_envs=1000) - env.reset() - tick = 0 - - actions = np.random.randint(0, 2, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'{env.__class__.__name__}: SPS: {env.num_agents * tick / (time.time() - start)}') - -if __name__ == '__main__': - test_performance(Pong) diff --git a/pufferlib/ocean/pysquared/pysquared.py b/pufferlib/ocean/pysquared/pysquared.py deleted file mode 100644 index eee180bc7..000000000 --- a/pufferlib/ocean/pysquared/pysquared.py +++ /dev/null @@ -1,131 +0,0 @@ -'''Pure python version of Squared, a simple single-agent sample environment. - Use this as a template for your own envs. -''' - -# We only use Gymnasium for their spaces API for compatibility with other libraries. -import gymnasium -import numpy as np - -import pufferlib - -NOOP = 0 -DOWN = 1 -UP = 2 -LEFT = 3 -RIGHT = 4 - -EMPTY = 0 -AGENT = 1 -TARGET = 2 - -# Inherit from PufferEnv -class PySquared(pufferlib.PufferEnv): - # Required keyword arguments: render_mode, buf, seed - def __init__(self, render_mode='ansi', size=11, buf=None, seed=0): - # Required attributes below - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(size*size,), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.Discrete(5) - self.render_mode = render_mode - self.num_agents = 1 - - # Call super after initializing attributes - super().__init__(buf) - - # Add anything else you want - self.size = size - - # All methods below are required with the signatures shown - def reset(self, seed=0): - self.observations[0, :] = EMPTY - self.observations[0, self.size*self.size//2] = AGENT - self.r = self.size//2 - self.c = self.size//2 - self.tick = 0 - while True: - target_r, target_c = np.random.randint(0, self.size, 2) - if target_r != self.r or target_c != self.c: - self.observations[0, target_r*self.size + target_c] = TARGET - break - - # Observations are read from self. Don't create extra copies - return self.observations, [] - - def step(self, actions): - atn = actions[0] - - # Note that terminals, rewards, etc. are updated in-place - self.terminals[0] = False - self.rewards[0] = 0 - - self.observations[0, self.r*self.size + self.c] = EMPTY - - if atn == DOWN: - self.r += 1 - elif atn == RIGHT: - self.c += 1 - elif atn == UP: - self.r -= 1 - elif atn == LEFT: - self.c -= 1 - - # Info is a list of dictionaries - info = [] - pos = self.r*self.size + self.c - if (self.tick > 3*self.size - or self.r < 0 - or self.c < 0 - or self.r >= self.size - or self.c >= self.size): - self.terminals[0] = True - self.rewards[0] = -1.0 - info = [{'reward': -1.0}] - self.reset() - elif self.observations[0, pos] == TARGET: - self.terminals[0] = True - self.rewards[0] = 1.0 - info = [{'reward': 1.0}] - self.reset() - else: - self.observations[0, pos] = AGENT - self.tick += 1 - - # Return the in-place versions. Don't copy! - return self.observations, self.rewards, self.terminals, self.truncations, info - - def render(self): - # Quick ascii rendering. If you want a Python-based renderer, - # we highly recommend Raylib over PyGame etc. If you use the - # C-style Python API, it will be very easy to port to C native later. - chars = [] - grid = self.observations.reshape(self.size, self.size) - for row in grid: - for val in row: - if val == AGENT: - color = 94 - elif val == TARGET: - color = 91 - else: - color = 90 - chars.append(f'\033[{color}m██\033[0m') - chars.append('\n') - return ''.join(chars) - - def close(self): - pass - -if __name__ == '__main__': - env = PySquared() - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, 5, (CACHE, 1)) - - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[steps % CACHE]) - steps += 1 - - print('PySquared SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/render.py b/pufferlib/ocean/render.py index 1a6b02404..584b2d4be 100644 --- a/pufferlib/ocean/render.py +++ b/pufferlib/ocean/render.py @@ -1,5 +1,4 @@ import numpy as np -import os from cffi import FFI from raylib import rl, colors @@ -10,16 +9,19 @@ ANSI_COLORS = [30, 34, 36, 90, 31, 97, 91, 37] -COLORS = np.array([ - [6, 24, 24 ], # Background - [0, 0, 255], # Food - [0, 128, 255], # Corpse - [128, 128, 128], # Wall - [255, 0, 0], # Snake - [255, 255, 255], # Snake - [255, 85, 85], # Snake - [170, 170, 170], # Snake -], dtype=np.uint8) +COLORS = np.array( + [ + [6, 24, 24], # Background + [0, 0, 255], # Food + [0, 128, 255], # Corpse + [128, 128, 128], # Wall + [255, 0, 0], # Snake + [255, 255, 255], # Snake + [255, 85, 85], # Snake + [170, 170, 170], # Snake + ], + dtype=np.uint8, +) def any_key_down(keys): @@ -28,12 +30,14 @@ def any_key_down(keys): return True return False + def any_key_pressed(keys): for key in keys: if rl.IsKeyPressed(key): return True return False + def cdata_to_numpy(): image = rl.LoadImageFromScreen() data_pointer = image.data @@ -42,15 +46,17 @@ def cdata_to_numpy(): channels = 4 data_size = width * height * channels cdata = FFI().buffer(data_pointer, data_size) - return np.frombuffer(cdata, dtype=np.uint8 - ).reshape((height, width, channels)) + return np.frombuffer(cdata, dtype=np.uint8).reshape((height, width, channels)) + def make_texture(width, height): rendered = np.zeros((height, width, 4), dtype=np.uint8) - raylib_image = pyray.Image(FFI().from_buffer(rendered.data), - width, height, 1, pyray.PIXELFORMAT_UNCOMPRESSED_R8G8B8) + raylib_image = pyray.Image( + FFI().from_buffer(rendered.data), width, height, 1, pyray.PIXELFORMAT_UNCOMPRESSED_R8G8B8 + ) return rl.LoadTextureFromImage(raylib_image) + class AnsiRender: def __init__(self, colors=None): self.colors = colors @@ -58,17 +64,17 @@ def __init__(self, colors=None): self.colors = ANSI_COLORS def render(self, grid): - frame = '' + frame = "" for v in range(grid.shape[0]): lines = [] - for line in grid[v-1:-v, v-1:-v]: - lines.append(''.join([ - f'\033[{ANSI_COLORS[val]}m██\033[0m' for val in line])) + for line in grid[v - 1 : -v, v - 1 : -v]: + lines.append("".join([f"\033[{ANSI_COLORS[val]}m██\033[0m" for val in line])) - frame = '\n'.join(lines) + frame = "\n".join(lines) return frame - + + class RGBArrayRender: def __init__(self, colors=None, upscale=1): self.colors = colors @@ -86,9 +92,11 @@ def render(self, grid): return frame + class GridRender: - def __init__(self, width, height, screen_width=1080, screen_height=720, - colors=None, fps=60, name='PufferLib Raylib Renderer'): + def __init__( + self, width, height, screen_width=1080, screen_height=720, colors=None, fps=60, name="PufferLib Raylib Renderer" + ): self.width = width self.height = height self.fps = fps @@ -103,9 +111,9 @@ def __init__(self, width, height, screen_width=1080, screen_height=720, self.height = height camera = pyray.Camera2D() - camera.target= (width/2, height/2) - camera.rotation = 0.0 - camera.zoom = min(screen_width/width, screen_height/height) + camera.target = (width / 2, height / 2) + camera.rotation = 0.0 + camera.zoom = min(screen_width / width, screen_height / height) self.camera = camera self.speed = min(screen_width, screen_height) / 100 @@ -127,8 +135,8 @@ def render(self, grid, *args, end_drawing=True): screen_height = rl.GetScreenHeight() camera = self.camera - camera.offset.x = screen_width/2 - camera.offset.y = screen_height/2 + camera.offset.x = screen_width / 2 + camera.offset.y = screen_height / 2 fps = rl.GetFPS() or self.fps fps_mul = self.fps / fps @@ -136,9 +144,9 @@ def render(self, grid, *args, end_drawing=True): zoom_speed = 0.01 * fps_mul if any_key_down([rl.KEY_SPACE]): - camera.zoom = min(screen_width/self.width, screen_height/self.height) - camera.target.x = self.width/2 - camera.target.y = self.height/2 + camera.zoom = min(screen_width / self.width, screen_height / self.height) + camera.target.x = self.width / 2 + camera.target.y = self.height / 2 if any_key_down([rl.KEY_LEFT_SHIFT]): speed *= 3 @@ -170,38 +178,37 @@ def render(self, grid, *args, end_drawing=True): rl.EndMode2D() if self.show_help: # Stats - rl.DrawText(f'FPS: {fps}'.encode(), 10, 10, 20, PUFF_TEXT) - rl.DrawText(f'Zoom: {camera.zoom:.2f}'.encode(), 10, 30, 20, PUFF_TEXT) - rl.DrawText(f'X: {camera.offset.x:.2f}'.encode(), 10, 50, 20, PUFF_TEXT) - rl.DrawText(f'Y: {camera.offset.y:.2f}'.encode(), 10, 70, 20, PUFF_TEXT) - rl.DrawText(f'Speed: {speed:.2f}'.encode(), 10, 90, 20, PUFF_TEXT) + rl.DrawText(f"FPS: {fps}".encode(), 10, 10, 20, PUFF_TEXT) + rl.DrawText(f"Zoom: {camera.zoom:.2f}".encode(), 10, 30, 20, PUFF_TEXT) + rl.DrawText(f"X: {camera.offset.x:.2f}".encode(), 10, 50, 20, PUFF_TEXT) + rl.DrawText(f"Y: {camera.offset.y:.2f}".encode(), 10, 70, 20, PUFF_TEXT) + rl.DrawText(f"Speed: {speed:.2f}".encode(), 10, 90, 20, PUFF_TEXT) # Controls - rl.DrawText('Move: WASD/HJKL'.encode(), 10, 120, 20, PUFF_TEXT) - rl.DrawText('Zoom: QE/-+'.encode(), 10, 140, 20, PUFF_TEXT) - rl.DrawText('Turbo: Shift'.encode(), 10, 160, 20, PUFF_TEXT) - rl.DrawText('Help: Tab/~'.encode(), 10, 180, 20, PUFF_TEXT) - rl.DrawText('Reset: Space'.encode(), 10, 200, 20, PUFF_TEXT) + rl.DrawText("Move: WASD/HJKL".encode(), 10, 120, 20, PUFF_TEXT) + rl.DrawText("Zoom: QE/-+".encode(), 10, 140, 20, PUFF_TEXT) + rl.DrawText("Turbo: Shift".encode(), 10, 160, 20, PUFF_TEXT) + rl.DrawText("Help: Tab/~".encode(), 10, 180, 20, PUFF_TEXT) + rl.DrawText("Reset: Space".encode(), 10, 200, 20, PUFF_TEXT) if end_drawing: rl.EndDrawing() return cdata_to_numpy() + class GameRender: - def __init__(self, width, height, screen_width=1080, screen_height=720, - colors=None, name='PufferLib Raylib Game'): - self.client = GridRender(width, height, - screen_width, screen_height, colors, name) + def __init__(self, width, height, screen_width=1080, screen_height=720, colors=None, name="PufferLib Raylib Game"): + self.client = GridRender(width, height, screen_width, screen_height, colors, name) def render(self, grid, x, y): self.client.camera.target.x = x self.client.camera.target.y = y return self.client.render(grid) + class TestGameRender: - def __init__(self, width, height, colors=None, - tile_size=16, name='PufferLib Raylib Game'): + def __init__(self, width, height, colors=None, tile_size=16, name="PufferLib Raylib Game"): assert width % tile_size == 0 assert height % tile_size == 0 assert (width // tile_size) % 2 == 1 @@ -239,9 +246,8 @@ def render(self, grid, agent_positions): dy = self.y_tiles // 2 -if __name__ == '__main__': +if __name__ == "__main__": renderer = GridRender(256, 256) grid = np.random.randint(0, 3, (256, 256), dtype=np.uint8) while True: frame = renderer.render(grid) - diff --git a/pufferlib/ocean/robocode/build_local.sh b/pufferlib/ocean/robocode/build_local.sh deleted file mode 100644 index 4dd2865c0..000000000 --- a/pufferlib/ocean/robocode/build_local.sh +++ /dev/null @@ -1,3 +0,0 @@ -clang -Wall -Wuninitialized -Wmisleading-indentation -fsanitize=address,undefined,bounds,pointer-overflow,leak -ferror-limit=3 -g -o robocode robocode.c -I./raylib-5.0_linux_amd64/include/ -L./raylib-5.0_linux_amd64/lib/ -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 -DPLATFORM_DESKTOP - - diff --git a/pufferlib/ocean/robocode/robocode b/pufferlib/ocean/robocode/robocode deleted file mode 100755 index 59131fc78..000000000 Binary files a/pufferlib/ocean/robocode/robocode and /dev/null differ diff --git a/pufferlib/ocean/robocode/robocode.c b/pufferlib/ocean/robocode/robocode.c deleted file mode 100644 index c7b3105f2..000000000 --- a/pufferlib/ocean/robocode/robocode.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "robocode.h" - -int main() { - Env env = {0}; - env.num_agents = 2; - env.width = 768; - env.height = 576; - allocate_env(&env); - reset(&env); - - Client* client = make_client(&env); - - while (!WindowShouldClose()) { - for (int i = 0; i < NUM_ACTIONS; i++) { - env.actions[i] = 0; - } - - env.actions[0] = 16.0f; - float x = env.robots[0].x; - float y = env.robots[0].y; - float op_x = env.robots[1].x; - float op_y = env.robots[1].y; - float gun_heading = env.robots[0].gun_heading; - float angle_to_op = 180*atan2(op_y - y, op_x - x)/M_PI; - float gun_delta = angle_to_op - gun_heading; - if (gun_delta < -180) gun_delta += 360; - env.actions[2] = (gun_delta > 0) ? 1.0f : -1.0f; - if (gun_delta < 5 && gun_delta > -5) env.actions[4] = 1.0; - - env.actions[5] = 16.0f; - x = env.robots[1].x; - y = env.robots[1].y; - op_x = env.robots[0].x; - op_y = env.robots[0].y; - gun_heading = env.robots[1].gun_heading; - angle_to_op = 180*atan2(op_y - y, op_x - x)/M_PI; - gun_delta = angle_to_op - gun_heading; - if (gun_delta < -180) gun_delta += 360; - env.actions[7] = (gun_delta > 0) ? 1.0f : -1.0f; - if (gun_delta < 5 && gun_delta > -5) env.actions[9] = 1.0; - - - //if (IsKeyPressed(KEY_ESCAPE)) break; - if (IsKeyDown(KEY_W)) env.actions[0] = 16.0f; - if (IsKeyDown(KEY_S)) env.actions[0] = -16.0f; - if (IsKeyDown(KEY_A)) env.actions[1] = -2.0f; - if (IsKeyDown(KEY_D)) env.actions[1] = 2.0f; - if (IsKeyDown(KEY_Q)) env.actions[2] = -1.0f; - if (IsKeyDown(KEY_E)) env.actions[2] = 1.0f; - if (IsKeyDown(KEY_SPACE)) env.actions[4] = 1.0f; - - step(&env); - render(client, &env); - } - CloseWindow(); - return 0; -} diff --git a/pufferlib/ocean/robocode/robocode.h b/pufferlib/ocean/robocode/robocode.h deleted file mode 100644 index d57636d8a..000000000 --- a/pufferlib/ocean/robocode/robocode.h +++ /dev/null @@ -1,369 +0,0 @@ -#include -#include -#include -#include -#include -#include "raylib.h" - -#define NUM_ACTIONS 5 -#define NUM_BULLETS 16 - -float cos_deg(float deg) { - return cos(deg * 3.14159265358979323846 / 180.0); -} - -float sin_deg(float deg) { - return sin(deg * 3.14159265358979323846 / 180.0); -} - -typedef struct Bullet Bullet; -struct Bullet { - float x; - float y; - float heading; - float firepower; - bool live; -}; - -typedef struct Robot Robot; -struct Robot { - float x; - float y; - float v; - float heading; - float gun_heading; - float radar_heading_prev; - float radar_heading; - float gun_heat; - int energy; - int bullet_idx; -}; - -typedef struct Env Env; -struct Env { - int num_agents; - int width; - int height; - Robot* robots; - Bullet* bullets; - float* actions; -}; - -void allocate_env(Env* env) { - env->robots = (Robot*)calloc(env->num_agents, sizeof(Robot)); - env->bullets = (Bullet*)calloc(NUM_BULLETS*env->num_agents, sizeof(Bullet)); - env->actions = (float*)calloc(NUM_ACTIONS*env->num_agents, sizeof(float)); -} - -void free_env(Env* env) { - free(env->robots); - free(env->actions); -} - -void move(Env* env, Robot* robot, float distance) { - float dx = cos_deg(robot->heading); - float dy = sin_deg(robot->heading); - //float accel = 1.0;//2.0*distance / (robot->v * robot->v); - float accel = distance; - - if (accel > 1.0) { - accel = 1.0; - } else if (accel < -2.0) { - accel = -2.0; - } - - robot->v += accel; - if (robot->v > 8.0) { - robot->v = 8.0; - } else if (robot->v < -8.0) { - robot->v = -8.0; - } - - float new_x = robot->x + dx * robot->v; - float new_y = robot->y + dy * robot->v; - - // Collision check - for (int j = 0; j < env->num_agents; j++) { - Robot* target = &env->robots[j]; - if (target == robot) { - continue; - } - float dx = target->x - new_x; - float dy = target->y - new_y; - float dist = sqrt(dx*dx + dy*dy); - if (dist > 32.0f) { - continue; - } - - target->energy -= 0.6; - robot->energy -= 0.6; - return; - } - - robot->x = new_x; - robot->y = new_y; - -} - -float turn(Env* env, Robot* robot, float degrees) { - float abs_v = fabs(robot->v); - float d_angle = 10 - 0.75*abs_v; - if (degrees > d_angle) { - degrees = d_angle; - } else if (degrees < -d_angle) { - degrees = -d_angle; - } - - robot->heading += degrees; - if (robot->heading > 360) { - robot->heading -= 360; - } else if (robot->heading < 0) { - robot->heading += 360; - } - return degrees; -} - -void fire(Env* env, Robot* robot, float firepower) { - if (robot->gun_heat > 0) { - return; - } - if (robot->energy < firepower) { - return; - } - robot->energy -= firepower; - - Bullet* bullet = &env->bullets[robot->bullet_idx]; - robot->bullet_idx = (robot->bullet_idx + 1) % NUM_BULLETS; - robot->gun_heat += 1.0f + firepower/5.0f; - - bullet->x = robot->x + 64*cos_deg(robot->gun_heading); - bullet->y = robot->y + 64*sin_deg(robot->gun_heading); - bullet->heading = robot->gun_heading; - bullet->firepower = firepower; - bullet->live = true; -} - -void reset(Env* env) { - int idx = 0; - float x, y; - while (idx < env->num_agents) { - Robot* robot = &env->robots[idx]; - x = 16 + rand() % (env->width-32); - y = 16 + rand() % (env->height-32); - bool collided = false; - for (int j = 0; j < idx; j++) { - Robot* other = &env->robots[j]; - float dx = x - other->x; - float dy = y - other->y; - float dist = sqrt(dx*dx + dy*dy); - if (dist < 32.0f) { - collided = true; - break; - } - } - if (!collided) { - robot->x = x; - robot->y = y; - robot->v = 0; - robot->heading = 0; - robot->energy = 100; - robot->gun_heat = 3; - idx += 1; - } - } -} - -void step(Env* env) { - // Update bullets - for (int agent = 0; agent < env->num_agents; agent++) { - Robot* robot = &env->robots[agent]; - if (robot->energy <= 0) { - reset(env); - return; - } - - for (int blt = 0; blt < NUM_BULLETS; blt++) { - Bullet* bullet = &env->bullets[agent*NUM_BULLETS + blt]; - if (!bullet->live) { - continue; - } - - float v = 20.0f - 3.0f*bullet->firepower; - bullet->x += v*cos_deg(bullet->heading); - bullet->y += v*sin_deg(bullet->heading); - - // Bounds check - if (bullet->x < 0 || bullet->x > env->width - || bullet->y < 0 || bullet->y > env->height) { - bullet->live = false; - continue; - } - - // Collision check - for (int j = 0; j < env->num_agents; j++) { - Robot* target = &env->robots[j]; - float dx = target->x - bullet->x; - float dy = target->y - bullet->y; - float dist = sqrt(dx*dx + dy*dy); - if (dist > 32.0f) { - continue; - } - - float damage = 4*bullet->firepower; - if (bullet->firepower > 1.0f) { - damage += 2*(bullet->firepower - 1.0f); - } - - target->energy -= damage; - robot->energy += 3*bullet->firepower; - bullet->live = false; - } - } - } - - for (int i = 0; i < env->num_agents; i++) { - Robot* robot = &env->robots[i]; - int atn_offset = i*NUM_ACTIONS; - - // Cool down gun - if (robot->gun_heat > 0) { - robot->gun_heat -= 0.1f; - } - - // Move - int move_atn = env->actions[atn_offset]; - move(env, robot, move_atn); - - // Turn - int turn_atn = env->actions[atn_offset + 1]; - float turn_degrees = turn(env, robot, turn_atn); - - // Gun - float gun_degrees = env->actions[atn_offset + 2] + turn_degrees; - robot->gun_heading += gun_degrees; - if (robot->gun_heading > 360) { - robot->gun_heading -= 360; - } else if (robot->gun_heading < 0) { - robot->gun_heading += 360; - } - - // Radar - float radar_degrees = env->actions[atn_offset + 3] + gun_degrees; - robot->radar_heading_prev = robot->radar_heading; - robot->radar_heading += radar_degrees; - if (robot->radar_heading > 360) { - robot->radar_heading -= 360; - } else if (robot->radar_heading < 0) { - robot->radar_heading += 360; - } - - // Fire - float firepower = env->actions[atn_offset + 4]; - if (firepower > 0) { - fire(env, robot, firepower); - } - - // Clip position - if (robot->x < 16) { - robot->x = 16; - } else if (robot->x > env->width - 16) { - robot->x = env->width - 16; - } - if (robot->y < 16) { - robot->y = 16; - } else if (robot->y > env->height - 16) { - robot->y = env->height - 16; - } - } -} - -typedef struct Client Client; -struct Client { - Texture2D atlas; -}; - -Client* make_client(Env* env) { - InitWindow(768, 576, "PufferLib Ray Robocode"); - SetTargetFPS(60); - Client* client = (Client*)calloc(1, sizeof(Client)); - client->atlas = LoadTexture("resources/robocode/robocode.png"); - return client; -} - -void close_client(Client* client) { - UnloadTexture(client->atlas); - CloseWindow(); -} - -void render(Client* client, Env* env) { - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - for (int x = 0; x < env->width; x+=64) { - for (int y = 0; y < env->height; y+=64) { - int src_x = 64 * ((x*33409+ y*30971) % 5); - Rectangle src_rect = (Rectangle){src_x, 0, 64, 64}; - Vector2 dest_pos = (Vector2){x, y}; - DrawTextureRec(client->atlas, src_rect, dest_pos, WHITE); - } - } - - for (int i = 0; i < env->num_agents; i++) { - int atn_offset = i*NUM_ACTIONS; - int turn_atn = env->actions[atn_offset + 1]; - int gun_atn = env->actions[atn_offset + 2] + turn_atn; - int radar_atn = env->actions[atn_offset + 3] + gun_atn; - - Robot robot = env->robots[i]; - Vector2 robot_pos = (Vector2){robot.x, robot.y}; - - // Radar - float radar_left = (radar_atn > 0) ? robot.radar_heading: robot.radar_heading_prev; - float radar_right = (radar_atn > 0) ? robot.radar_heading_prev : robot.radar_heading; - Vector2 radar_left_pos = (Vector2){ - robot.x + 1200*cos_deg(radar_left), - robot.y + 1200*sin_deg(radar_left) - }; - Vector2 radar_right_pos = (Vector2){ - robot.x + 1200*cos_deg(radar_right), - robot.y + 1200*sin_deg(radar_right) - }; - DrawTriangle(robot_pos, radar_left_pos, radar_right_pos, (Color){0, 255, 0, 128}); - - // Gun - Vector2 gun_pos = (Vector2){ - robot.x + 64*cos_deg(robot.gun_heading), - robot.y + 64*sin_deg(robot.gun_heading) - }; - //DrawLineEx(robot_pos, gun_pos, 4, WHITE); - - // Robot - //DrawCircle(robot.x, robot.y, 32, RED); - //DrawCircle(robot.x, robot.y, 16, WHITE); - float theta = robot.heading; - float dx = cos_deg(theta); - float dy = sin_deg(theta); - int src_y = 64 + 64*(i%2); - Rectangle body_rect = (Rectangle){0, src_y, 64, 64}; - Rectangle radar_rect = (Rectangle){64, src_y, 64, 64}; - Rectangle gun_rect = (Rectangle){128, src_y, 64, 64}; - Rectangle dest_rect = (Rectangle){robot.x, robot.y, 64, 64}; - Vector2 origin = (Vector2){32, 32}; - DrawTexturePro(client->atlas, body_rect, dest_rect, origin, robot.heading+90, WHITE); - DrawTexturePro(client->atlas, radar_rect, dest_rect, origin, robot.radar_heading+90, WHITE); - DrawTexturePro(client->atlas, gun_rect, dest_rect, origin, robot.gun_heading+90, WHITE); - - DrawText(TextFormat("%i", robot.energy), robot.x-16, robot.y-48, 12, WHITE); - } - - for (int i = 0; i < env->num_agents*NUM_BULLETS; i++) { - Bullet bullet = env->bullets[i]; - if (!bullet.live) { - continue; - } - Vector2 bullet_pos = (Vector2){bullet.x, bullet.y}; - DrawCircleV(bullet_pos, 4, WHITE); - } - - EndDrawing(); -} diff --git a/pufferlib/ocean/rocket_lander/cy_rocket_lander.pyx b/pufferlib/ocean/rocket_lander/cy_rocket_lander.pyx deleted file mode 100644 index 31a7fcb4f..000000000 --- a/pufferlib/ocean/rocket_lander/cy_rocket_lander.pyx +++ /dev/null @@ -1,129 +0,0 @@ -cimport numpy as cnp -from libc.stdlib cimport calloc, free -import os - -cdef extern from "rocket_lander.h": - int LOG_BUFFER_SIZE - - ctypedef struct b2WorldId: - unsigned short index1 - unsigned short revision - ctypedef struct b2BodyId: - int index1 - unsigned short revision - unsigned char world0 - ctypedef struct b2Vec2: - float x - float y - - ctypedef struct Log: - float episode_return; - float episode_length; - float score; - - ctypedef struct LogBuffer - LogBuffer* allocate_logbuffer(int) - void free_logbuffer(LogBuffer*) - Log aggregate_and_clear(LogBuffer*) - - ctypedef struct Entity: - b2BodyId bodyId; - b2Vec2 extent; - - ctypedef struct Lander: - float* observations; - float* actions; - float* reward; - unsigned char* terminal; - unsigned char* truncation; - LogBuffer* log_buffer; - Log log; - int tick; - b2WorldId world_id; - b2BodyId barge_id; - b2BodyId lander_id; - Entity barge; - Entity lander; - - ctypedef struct Client - - void init_lander(Lander* env) - void reset(Lander* env) - void step(Lander* env) - void free_lander(Lander* env) - - Client* make_client(Lander* env) - void render(Client* client, Lander* env) - void close_client(Client* client) - -cdef class CyRocketLander: - cdef: - Lander* envs - Client* client - LogBuffer* logs - int num_envs - - def __init__(self, cnp.ndarray observations, cnp.ndarray actions, - cnp.ndarray rewards, cnp.ndarray terminals, - cnp.ndarray truncations, int num_envs): - - self.num_envs = num_envs - self.client = NULL - self.envs = calloc(num_envs, sizeof(Lander)) - self.logs = allocate_logbuffer(LOG_BUFFER_SIZE) - - cdef: - cnp.ndarray observations_i - cnp.ndarray actions_i - cnp.ndarray rewards_i - cnp.ndarray terminals_i - cnp.ndarray truncations_i - - cdef int i - for i in range(num_envs): - observations_i = observations[i:i+1] - actions_i = actions[i:i+1] - rewards_i = rewards[i:i+1] - terminals_i = terminals[i:i+1] - truncations_i = truncations[i:i+1] - self.envs[i] = Lander( - observations = observations_i.data, - actions = actions_i.data, - reward = rewards_i.data, - terminal = terminals_i.data, - truncation = truncations_i.data, - log_buffer=self.logs, - ) - init_lander(&self.envs[i]) - - def reset(self): - cdef int i - for i in range(self.num_envs): - reset(&self.envs[i]) - - def step(self): - cdef int i - for i in range(self.num_envs): - step(&self.envs[i]) - - def render(self): - cdef Lander* env = &self.envs[0] - if self.client == NULL: - self.client = make_client(env) - - render(self.client, env) - - def close(self): - if self.client != NULL: - close_client(self.client) - self.client = NULL - - cdef int i - for i in range(self.num_envs): - free_lander(&self.envs[i]) - free(self.envs) - free(self.logs) - - def log(self): - cdef Log log = aggregate_and_clear(self.logs) - return log diff --git a/pufferlib/ocean/rocket_lander/rocket_lander.c b/pufferlib/ocean/rocket_lander/rocket_lander.c deleted file mode 100644 index d21616647..000000000 --- a/pufferlib/ocean/rocket_lander/rocket_lander.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "rocket_lander.h" - -int main(void) { - demo(); - return 0; -} - - - - /* - Entity legs[2] = {0}; - for (int i = 0; i < 2; i++) { - float leg_i = (i == 0) ? -1 : 1; - b2Vec2 leg_extent = (b2Vec2){LEG_W / SCALE, LEG_H / SCALE}; - - b2BodyDef leg = b2DefaultBodyDef(); - leg.type = b2_dynamicBody; - leg.position = (b2Vec2){-leg_i * LEG_AWAY, INITIAL_Y - LANDER_HEIGHT/2 - leg_extent.y/2}; - //leg.position = (b2Vec2){0, 0}; - leg.rotation = b2MakeRot(leg_i * 1.05); - b2BodyId leg_id = b2CreateBody(world_id, &leg); - - b2Polygon leg_box = b2MakeBox(leg_extent.x, leg_extent.y); - b2ShapeDef leg_shape = b2DefaultShapeDef(); - b2CreatePolygonShape(leg_id, &leg_shape, &leg_box); - - float joint_x = leg_i*LANDER_WIDTH/2; - float joint_y = INITIAL_Y - LANDER_HEIGHT/2 - leg_extent.y/2; - b2Vec2 joint_p = (b2Vec2){joint_x, joint_y}; - - b2RevoluteJointDef joint = b2DefaultRevoluteJointDef(); - joint.bodyIdA = lander_id; - joint.bodyIdB = leg_id; - joint.localAnchorA = b2Body_GetLocalPoint(lander_id, joint_p); - joint.localAnchorB = b2Body_GetLocalPoint(leg_id, joint_p); - joint.localAnchorB = (b2Vec2){i * 0.5, LEG_DOWN}; - joint.enableMotor = true; - joint.enableLimit = true; - joint.maxMotorTorque = LEG_SPRING_TORQUE; - joint.motorSpeed = 0.3*i; - - if (i == 0) { - joint.lowerAngle = 40 * DEGTORAD; - joint.upperAngle = 45 * DEGTORAD; - } else { - joint.lowerAngle = -45 * DEGTORAD; - joint.upperAngle = -40 * DEGTORAD; - } - - b2JointId joint_id = b2CreateRevoluteJoint(world_id, &joint); - - legs[i] = (Entity){ - .extent = leg_extent, - .bodyId = leg_id, - }; - } - */ - - diff --git a/pufferlib/ocean/rocket_lander/rocket_lander.h b/pufferlib/ocean/rocket_lander/rocket_lander.h deleted file mode 100644 index 26cedc1cf..000000000 --- a/pufferlib/ocean/rocket_lander/rocket_lander.h +++ /dev/null @@ -1,479 +0,0 @@ -#include -#include -#include -#include - -#include "raylib.h" -#include "box2d/box2d.h" - -// This shows how to use Box2D v3 with raylib. -// It also show how to use Box2D with pixel units. -// -#define LOG_BUFFER_SIZE 1024 - -typedef struct Log Log; -struct Log { - float episode_return; - float episode_length; - float score; -}; - -typedef struct LogBuffer LogBuffer; -struct LogBuffer { - Log* logs; - int length; - int idx; -}; - -LogBuffer* allocate_logbuffer(int size) { - LogBuffer* logs = (LogBuffer*)calloc(1, sizeof(LogBuffer)); - logs->logs = (Log*)calloc(size, sizeof(Log)); - logs->length = size; - logs->idx = 0; - return logs; -} - -void free_logbuffer(LogBuffer* buffer) { - free(buffer->logs); - free(buffer); -} - -void add_log(LogBuffer* logs, Log* log) { - if (logs->idx == logs->length) { - return; - } - logs->logs[logs->idx] = *log; - logs->idx += 1; - //printf("Log: %f, %f, %f\n", log->episode_return, log->episode_length, log->score); -} - -Log aggregate_and_clear(LogBuffer* logs) { - Log log = {0}; - if (logs->idx == 0) { - return log; - } - for (int i = 0; i < logs->idx; i++) { - log.episode_return += logs->logs[i].episode_return; - log.episode_length += logs->logs[i].episode_length; - log.score += logs->logs[i].score; - } - log.episode_return /= logs->idx; - log.episode_length /= logs->idx; - log.score /= logs->idx; - logs->idx = 0; - return log; -} - -typedef struct Entity -{ - b2BodyId bodyId; - b2Vec2 extent; -} Entity; - -#define GROUND_COUNT 14 -#define BOX_COUNT 10 - -const float SCALE = 30; -const float VIEWPORT_W = 1000; -const float VIEWPORT_H = 800; -const float GRAVITY = 9.8f; -const float W = VIEWPORT_W / SCALE; -const float H = VIEWPORT_H / SCALE; - -const float BARGE_FRICTION = 2; -const float BARGE_HEIGHT = 10; -const float BARGE_WIDTH = 100; - -const float LANDER_WIDTH = 20; -const float LANDER_HEIGHT = 227; - -const float LEG_AWAY = 20; -const float LEG_DOWN = 0.3; -const float LEG_W = 30; -const float LEG_H = 10*LANDER_HEIGHT / 8; -const float LEG_SPRING_TORQUE = LANDER_HEIGHT / 2; - -const float INITIAL_Y = 500; - - -const float PX_PER_METER = 1.0f; -const float DEGTORAD = PI / 180.0f; - -const float THRUST_SCALE = 1000000; -const float SIDE_THRUST_SCALE = 1000000; - -void DrawEntity(const Entity* entity, Color color) -{ - // The boxes were created centered on the bodies, but raylib draws textures starting at the top left corner. - // b2Body_GetWorldPoint gets the top left corner of the box accounting for rotation. - b2Vec2 p = b2Body_GetWorldPoint(entity->bodyId, (b2Vec2){0, 0}); - float width = 2*entity->extent.x; - float height = 2*entity->extent.y; - - b2Rot rotation = b2Body_GetRotation(entity->bodyId); - float radians = b2Rot_GetAngle(rotation); - float degrees = radians / DEGTORAD; - - //b2Rot rotation = b2Body_GetRotation(entity->bodyId); - //float radians = b2Rot_GetAngle(rotation); - //printf("\t: x: %f, y: %f, w: %f, h: %f, deg: %f\n", p.x, p.y, width, height, degrees); - - Rectangle rec = (Rectangle){ - PX_PER_METER*p.x, - -PX_PER_METER*p.y, - PX_PER_METER*width, - PX_PER_METER*height, - }; - DrawRectanglePro(rec, (Vector2){rec.width/2, rec.height/2}, -degrees, color); - //DrawTextureEx(entity->texture, ps, RAD2DEG * radians, 1.0f, WHITE); - - // I used these circles to ensure the coordinates are correct - //DrawCircleV(ps, 5.0f, BLACK); - //p = b2Body_GetWorldPoint(entity->bodyId, (b2Vec2){0.0f, 0.0f}); - //ps = (Vector2){ p.x, p.y }; - //DrawCircleV(ps, 5.0f, BLUE); - //p = b2Body_GetWorldPoint(entity->bodyId, (b2Vec2){ entity->extent.x, entity->extent.y }); - //ps = (Vector2){ p.x, p.y }; - //DrawCircleV(ps, 5.0f, RED); -} - - -typedef struct Lander Lander; -struct Lander { - float* observations; - float* actions; - float* reward; - unsigned char* terminal; - unsigned char* truncation; - LogBuffer* log_buffer; - Log log; - int tick; - b2WorldId world_id; - b2BodyId barge_id; - b2BodyId lander_id; - Entity barge; - Entity lander; -}; - -void init_lander(Lander* env) { - b2SetLengthUnitsPerMeter(PX_PER_METER); - - // 128 pixels per meter is a appropriate for this scene. The boxes are 128 pixels wide. - b2WorldDef worldDef = b2DefaultWorldDef(); - - // Realistic gravity is achieved by multiplying gravity by the length unit. - worldDef.gravity.y = -9.8f * PX_PER_METER; - b2WorldId world_id = b2CreateWorld(&worldDef); - env->world_id = world_id; - - b2BodyDef barge_body = b2DefaultBodyDef(); - barge_body.position = (b2Vec2){0, 0}; - barge_body.type = b2_staticBody; - b2BodyId barge_id = b2CreateBody(world_id, &barge_body); - env->barge_id = barge_id; - - b2Vec2 barge_extent = (b2Vec2){BARGE_WIDTH/2, BARGE_HEIGHT/2}; - b2Polygon barge_box = b2MakeBox(barge_extent.x, barge_extent.y); - b2ShapeDef barge_shape = b2DefaultShapeDef(); - b2CreatePolygonShape(barge_id, &barge_shape, &barge_box); - Entity barge = { - .extent = barge_extent, - .bodyId = barge_id, - }; - env->barge = barge; - - b2BodyDef lander_body = b2DefaultBodyDef(); - lander_body.position = (b2Vec2){0, INITIAL_Y}; - lander_body.type = b2_dynamicBody; - b2BodyId lander_id = b2CreateBody(world_id, &lander_body); - env->lander_id = lander_id; - - b2Vec2 lander_extent = (b2Vec2){LANDER_WIDTH / 2, LANDER_HEIGHT / 2}; - b2Polygon lander_box = b2MakeBox(lander_extent.x, lander_extent.y); - b2ShapeDef lander_shape = b2DefaultShapeDef(); - b2CreatePolygonShape(lander_id, &lander_shape, &lander_box); - Entity lander = { - .extent = lander_extent, - .bodyId = lander_id, - }; - env->lander = lander; -} - -void allocate_lander(Lander* env) { - env->observations = (float*)calloc(6, sizeof(float)); - env->actions = (float*)calloc(3, sizeof(float)); - env->reward = (float*)calloc(1, sizeof(float)); - env->terminal = (unsigned char*)calloc(1, sizeof(unsigned char)); - env->truncation = (unsigned char*)calloc(1, sizeof(unsigned char)); - env->log_buffer = allocate_logbuffer(LOG_BUFFER_SIZE); - init_lander(env); -} - -void compute_observations_and_reward(Lander* env, float prev_x, float prev_y) { - b2Transform transform = b2Body_GetTransform(env->lander_id); - b2Vec2 pos = transform.p; - float rot = b2Rot_GetAngle(transform.q); - b2Vec2 vel = b2Body_GetLinearVelocity(env->lander_id); - float ang_vel = b2Body_GetAngularVelocity(env->lander_id); - - float reward_x = (fabsf(prev_x) - fabsf(pos.x))/ 1000; - float reward_y = (fabsf(prev_y) - fabsf(pos.y))/ 1000; - //float reward_rot = -fabsf(rot)/ PI / 10; - //printf("Reward: %f, %f, %f\n", reward_x, reward_y, reward_rot); - float reward = reward_x + reward_y;// + reward_rot; - - reward = 0; - if (env->actions[0] == 0) { - reward = 1; - } - - env->reward[0] = reward; - env->log.episode_return += reward; - - env->observations[0] = pos.x / 500; - env->observations[1] = pos.y / 1200; - env->observations[2] = vel.x / 200; - env->observations[3] = vel.y / 200; - env->observations[4] = rot / PI / 10; - env->observations[5] = ang_vel / PI; -} - -void reset(Lander* env) { - env->log = (Log){0}; - env->tick = 0; - - b2Body_SetTransform( - env->lander_id, - (b2Vec2){0, INITIAL_Y}, - b2MakeRot(0) - ); - b2Body_SetLinearVelocity(env->lander_id, (b2Vec2){0, 0}); - b2Body_SetAngularVelocity(env->lander_id, 0.0f); - - b2Transform transform = b2Body_GetTransform(env->lander_id); - b2Vec2 pos = transform.p; - env->reward[0] = 0; - compute_observations_and_reward(env, pos.x, pos.y); - env->reward[0] = 0; -} - -void step(Lander* env) { - env->reward[0] = 0; - b2Transform transform = b2Body_GetTransform(env->lander_id); - b2Vec2 pos = transform.p; - printf("Pos x: %f, y: %f\n", pos.x, pos.y); - - b2Vec2 p_thrust = b2Body_GetWorldPoint(env->lander_id, - (b2Vec2){0, -LANDER_HEIGHT/2}); - b2Vec2 p_left = b2Body_GetWorldPoint(env->lander_id, - (b2Vec2){-LANDER_WIDTH/2, LANDER_HEIGHT/2}); - b2Vec2 p_right= b2Body_GetWorldPoint(env->lander_id, - (b2Vec2){LANDER_WIDTH/2, LANDER_HEIGHT/2}); - - b2Vec2 force = (b2Vec2){0, 0}; - b2Rot rotation = b2Body_GetRotation(env->lander_id); - float radians = b2Rot_GetAngle(rotation); - - - // Main thruster - float atn_thrust = THRUST_SCALE * env->actions[0]; - float rad_thrust = radians + 0.02*(float)rand()/RAND_MAX; - force = (b2Vec2){ - atn_thrust*sin(rad_thrust), - atn_thrust*cos(rad_thrust) - }; - b2Body_ApplyForce(env->lander_id, force, p_thrust, true); - - // Top left thruster - float atn_left = SIDE_THRUST_SCALE * env->actions[1]; - float rad_left = -radians + PI/2 + 0.02*(float)rand()/RAND_MAX; - force = (b2Vec2){ - atn_left*sin(rad_left), - atn_left*cos(rad_left) - }; - b2Body_ApplyForce(env->lander_id, force, p_left, true); - - // Top right thruster - float atn_right = SIDE_THRUST_SCALE * env->actions[2]; - float rad_right = -radians - PI/2 + 0.02*(float)rand()/RAND_MAX; - force = (b2Vec2){ - atn_right*sin(rad_right), - atn_right*cos(rad_right) - }; - b2Body_ApplyForce(env->lander_id, force, p_right, true); - - b2World_Step(env->world_id, 60.0f, 4); - - - transform = b2Body_GetTransform(env->lander_id); - bool do_reset = false; - if (transform.p.x < -500 || transform.p.x > 500 || transform.p.y > 1200) { - do_reset = true; - //env->reward[0] -= 1.0; - //env->log.episode_return -= 1.0; - } - if (transform.p.y < 120) { - do_reset = true; - printf("Transform y: %f\n", transform.p.y); - } - if (env->tick > 1000) { - do_reset = true; - printf("Tick: %i\n", env->tick); - } - if (do_reset) { - printf("Reset\n"); - env->log.episode_length = env->tick; - add_log(env->log_buffer, &env->log); - reset(env); - } - env->tick += 1; - - compute_observations_and_reward(env, pos.x, pos.y); - //printf("Reward: %f\n", env->reward[0]); - //env->reward[0] = -(atn_thrust + atn_left + atn_right) / 10000000; -} - -void free_lander(Lander* env) { - free_logbuffer(env->log_buffer); - b2DestroyWorld(env->world_id); -} - -typedef struct Client Client; -struct Client { - Camera2D camera; -}; - -Client* make_client(Lander* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - - int width = 1920, height = 1080; - InitWindow(width, height, "box2d-raylib"); - SetTargetFPS(60); - - client->camera = (Camera2D){ - .target = (Vector2){0, 0}, - .offset = (Vector2){width/2, 9*height/10}, - .rotation = 0.0f, - .zoom = 1.0f, - }; - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -void render(Client* client, Lander* env) { - if (IsKeyPressed(KEY_ESCAPE)) { - exit(0); - } - BeginDrawing(); - ClearBackground(DARKGRAY); - BeginMode2D(client->camera); - - env->actions[0] = 0; - env->actions[1] = 0; - env->actions[2] = 0; - if (IsKeyDown(KEY_W)) { - env->actions[0] = 1; - } - if (IsKeyDown(KEY_Q)) { - env->actions[1] = 1; - } - if (IsKeyDown(KEY_E)) { - env->actions[2] = 1; - } - - - /* - b2Rot rotation = b2Body_GetRotation(lander_id); - float radians = b2Rot_GetAngle(rotation); - float mag = 1000000; - - if (IsKeyDown(KEY_W)) { - float rad_thrust = radians + 0.02*(float)rand()/RAND_MAX; - b2Vec2 force = (b2Vec2){mag*sin(rad_thrust), mag*cos(rad_thrust)}; - b2Body_ApplyForce(lander_id, force, p_thrust, true); - DrawCircle(p_thrust.x, -p_thrust.y, 20, RED); - } - if (IsKeyDown(KEY_Q)) { - float rad_left = -radians + PI/2 + 0.02*(float)rand()/RAND_MAX; - if (rad_left > PI) { - rad_left -= 2*PI; - } - b2Vec2 force = (b2Vec2){mag*sin(rad_left), mag*cos(rad_left)}; - b2Body_ApplyForce(lander_id, force, p_left, true); - DrawCircle(p_left.x, -p_left.y, 20, RED); - } - if (IsKeyDown(KEY_E)) { - float rad_right = -radians - PI/2 + 0.02*(float)rand()/RAND_MAX; - b2Vec2 force = (b2Vec2){mag*sin(rad_right), mag*cos(rad_right)}; - b2Body_ApplyForce(lander_id, force, p_right, true); - DrawCircle(p_right.x, -p_right.y, 20, RED); - } - - - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - Vector2 mousePos = GetScreenToWorld2D(GetMousePosition(), camera); - float x = mousePos.x; - float y = -mousePos.y; - b2Vec2 origin = (b2Vec2){x, y}; - b2Vec2 p = b2Body_GetWorldPoint(lander_id, - (b2Vec2){0, -LANDER_HEIGHT/2}); - float mag = 1000; - b2Vec2 force = (b2Vec2){ - mag * (p.x - origin.x), - mag * (p.y - origin.y), - }; - b2Body_ApplyForce(lander_id, force, p, true); - DrawLine(mousePos.x, mousePos.y, p.x, -p.y, RED); - } - - b2Transform transform = b2Body_GetTransform(lander_id); - printf("y: %f\n", transform.p.y); - if (transform.p.y < 120) { - reset(&env); - } - */ - - //DrawRectangle(0, 0, 100, 100, RED); - DrawEntity(&env->barge, RED); - DrawEntity(&env->lander, BLUE); - //DrawEntity(&legs[0], GREEN); - //DrawEntity(&legs[1], GREEN); - EndMode2D(); - EndDrawing(); -} - -void demo() { - Lander env = {0}; - allocate_lander(&env); - Client* client = make_client(&env); - - while (!WindowShouldClose()) { - step(&env); - render(client, &env); - } -} - -void test_render() { - InitWindow(1920, 1080, "box2d-raylib"); - SetTargetFPS(60); - - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(DARKGRAY); - - Rectangle rec = (Rectangle){500, 500, 200, 200}; - Vector2 origin = (Vector2){rec.width/2, rec.height/2}; - DrawRectanglePro(rec, origin, 45, RED); - - DrawCircle(500, 500, 30, BLUE); - - EndDrawing(); - } - -} - - diff --git a/pufferlib/ocean/rocket_lander/rocket_lander.py b/pufferlib/ocean/rocket_lander/rocket_lander.py deleted file mode 100644 index aee2f67a8..000000000 --- a/pufferlib/ocean/rocket_lander/rocket_lander.py +++ /dev/null @@ -1,73 +0,0 @@ -'''High-perf Pong - -Inspired from https://gist.github.com/Yttrmin/18ecc3d2d68b407b4be1 -& https://jair.org/index.php/jair/article/view/10819/25823 -& https://www.youtube.com/watch?v=PSQt5KGv7Vk -''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.rocket_lander.cy_rocket_lander import CyRocketLander - -class RocketLander(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, report_interval=32, buf=None): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(6,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(4) - self.render_mode = render_mode - self.num_agents = num_envs - self.report_interval = report_interval - - super().__init__(buf) - self.float_actions = np.zeros((num_envs, 3), dtype=np.float32) - self.c_envs = CyRocketLander(self.observations, self.float_actions, self.rewards, - self.terminals, self.truncations, num_envs) - - def reset(self, seed=None): - self.tick = 0 - self.c_envs.reset() - return self.observations, [] - - def step(self, actions): - self.float_actions[:, :] = 0 - self.float_actions[:, 0] = actions == 1 - self.float_actions[:, 1] = actions == 2 - self.float_actions[:, 2] = actions == 3 - self.c_envs.step() - - info = [] - if self.tick % self.report_interval == 0: - log = self.c_envs.log() - if log['episode_length'] > 0: - info.append(log) - - self.tick += 1 - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - self.c_envs.render() - - def close(self): - self.c_envs.close() - -def test_performance(timeout=10, atn_cache=1024): - env = RocketLander(num_envs=1000) - env.reset() - tick = 0 - - actions = np.random.randint(0, 2, (atn_cache, env.num_envs)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: %f', env.num_envs * tick / (time.time() - start)) - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/rware/binding.c b/pufferlib/ocean/rware/binding.c deleted file mode 100644 index 3cf1d5d14..000000000 --- a/pufferlib/ocean/rware/binding.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "rware.h" - -#define Env CRware -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->map_choice = unpack(kwargs, "map_choice"); - env->num_agents = unpack(kwargs, "num_agents"); - env->num_requested_shelves = unpack(kwargs, "num_requested_shelves"); - env->grid_square_size = unpack(kwargs, "grid_square_size"); - env->human_agent_idx = unpack(kwargs, "human_agent_idx"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/rware/rware.c b/pufferlib/ocean/rware/rware.c deleted file mode 100644 index 4afa28508..000000000 --- a/pufferlib/ocean/rware/rware.c +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#include "rware.h" -#include "puffernet.h" -#define MAP_TINY_WIDTH 640 -#define MAP_TINY_HEIGHT 704 -#define MAP_SMALL_WIDTH 1280 -#define MAP_SMALL_HEIGHT 640 -#define MAP_MEDIUM_WIDTH 1280 -#define MAP_MEDIUM_HEIGHT 640 - -void demo(int map_choice) { - int width; - int height; - if (map_choice == 1) { - width = MAP_TINY_WIDTH; - height = MAP_TINY_HEIGHT; - } else if (map_choice == 2) { - width = MAP_SMALL_WIDTH; - height = MAP_SMALL_HEIGHT; - } else { - width = MAP_MEDIUM_WIDTH; - height = MAP_MEDIUM_HEIGHT; - } - CRware env = { - .width = width, - .height = height, - .map_choice = map_choice, - .num_agents = 8, - .num_requested_shelves = 8, - .grid_square_size = 64, - .human_agent_idx = 0, - .reward_type = 2 - }; - Weights* weights = load_weights("resources/rware/rware_weights.bin", 136454); - int logit_sizes[1] = {5}; - LinearLSTM* net = make_linearlstm(weights, env.num_agents, 27, logit_sizes, 1); - - allocate(&env); - c_reset(&env); - c_render(&env); - - int tick = 1; - while (!WindowShouldClose()) { - if (tick % 12 == 0) { - tick = 0; - - int human_action = env.actions[env.human_agent_idx]; - for (int i = 0; i < env.num_agents * 27; i++) { - net->obs[i] = (float)env.observations[i]; - } - forward_linearlstm(net, net->obs, env.actions); - - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env.actions[env.human_agent_idx] = human_action; - } - - c_step(&env); - - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env.actions[env.human_agent_idx] = NOOP; - } - } - tick++; - - if (IsKeyDown(KEY_LEFT_SHIFT)) { - // Handle keyboard input only for selected agent - if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) { - env.actions[env.human_agent_idx] = FORWARD; - } - if (IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) { - env.actions[env.human_agent_idx] = LEFT; - } - if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) { - env.actions[env.human_agent_idx] = RIGHT; - } - if (IsKeyPressed(KEY_SPACE) || IsKeyPressed(KEY_ENTER)) { - env.actions[env.human_agent_idx] = TOGGLE_LOAD; - } - // Add agent switching with TAB key - if (IsKeyPressed(KEY_TAB)) { - env.human_agent_idx = (env.human_agent_idx + 1) % env.num_agents; - } - } - - c_render(&env); - } - //close_client(client); - free_allocated(&env); -} - -void performance_test() { - long test_time = 10; - CRware env = { - .width = 1280, - .height = 704, - .map_choice = 2, - .num_agents = 4, - .num_requested_shelves = 4, - .reward_type = 2 - }; - allocate(&env); - c_reset(&env); - - long start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - env.actions[0] = rand() % 5; - c_step(&env); - i++; - } - long end = time(NULL); - printf("SPS: %ld\n", i / (end - start)); - free_allocated(&env); -} - -int main() { - demo(2); - //performance_test(); - return 0; -} diff --git a/pufferlib/ocean/rware/rware.h b/pufferlib/ocean/rware/rware.h deleted file mode 100644 index 20d5728c2..000000000 --- a/pufferlib/ocean/rware/rware.h +++ /dev/null @@ -1,851 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" - -#define NOOP 0 -#define FORWARD 1 -#define LEFT 2 -#define RIGHT 3 -#define TOGGLE_LOAD 4 -#define TICK_RATE 1.0f/60.0f -#define NUM_DIRECTIONS 4 - -// warehouse states -#define EMPTY 0 -#define SHELF 1 -#define REQUESTED_SHELF 2 -#define GOAL 3 - -// agent states -#define UNLOADED 0 -#define HOLDING_REQUESTED_SHELF 1 -#define HOLDING_EMPTY_SHELF 2 - -// observation types -#define SELF_OBS 3 -#define VISION_OBS 24 - -// Facing directions -#define FACING_RIGHT 0 -#define FACING_DOWN 1 -#define FACING_LEFT 2 -#define FACING_UP 3 - -// Reward Type -#define INDIVIDUAL 1 -#define GLOBAL 2 -static const int DIRECTIONS[NUM_DIRECTIONS] = {0, 1, 2, 3}; -static const int DIRECTION_VECTORS[NUM_DIRECTIONS][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; -static const int SURROUNDING_VECTORS[8][2] = {{0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1}}; -static const int tiny_map[110] = { - 0,0,0,0,0,0,0,0,0,0, - 0,1,1,0,0,0,0,1,1,0, - 0,1,1,0,0,0,0,1,1,0, - 0,1,1,0,0,0,0,1,1,0, - 0,1,1,0,0,0,0,1,1,0, - 0,1,1,0,0,0,0,1,1,0, - 0,1,1,0,0,0,0,1,1,0, - 0,1,1,0,0,0,0,1,1,0, - 0,1,1,0,0,0,0,1,1,0, - 0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,3,3,0,0,0,0 -}; -static const int tiny_shelf_locations[32] = { - 11, 12, 17, 18, // Row 1 - 21, 22, 27, 28, // Row 2 - 31, 32, 37, 38, // Row 3 - 41, 42, 47, 48, // Row 4 - 51, 52, 57, 58, // Row 5 - 61, 62, 67, 68, // Row 6 - 71, 72, 77, 78, // Row 7 - 81, 82, 87, 88 // Row 8 -}; - -static const int small_map[200] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 3,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0, - 3,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -}; - -static const int small_shelf_locations[80] = { - 22,23,24,25,26,27,28,29,31,32,33,34,35,36,37,38, // Row 1 - 42,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58, // Row 2 - 91,92,93,94,95,96,97,98, // Row 4 (right side only) - 111,112,113,114,115,116,117,118, // Row 5 (right side only) - 142,143,144,145,146,147,148,149,151,152,153,154,155,156,157,158, // Row 7 (both sides) - 162,163,164,165,166,167,168,169,171,172,173,174,175,176,177,178 // Row 8 (both sides) -}; - -static const int medium_map[320] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 3,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0, - 3,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -}; - -static const int medium_shelf_locations[144]={ - 22,23,24,25,26,27,28,29,31,32,33,34,35,36,37,38, // Row 1 - 42,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58, // Row 2 - 82,83,84,85,86,87,88,89,91,92,93,94,95,96,97,98, // Row 4 - 102,103,104,105,106,107,108,109,111,112,113,114,115,116,117,118, // Row 5 - 151,152,153,154,155,156,157,158, // Row 7 (right side only) - 171,172,173,174,175,176,177,178, // Row 8 (right side only) - 202,203,204,205,206,207,208,209,211,212,213,214,215,216,217,218, // Row 10 - 222,223,224,225,226,227,228,229,231,232,233,234,235,236,237,238, // Row 11 - 262,263,264,265,266,267,268,269,271,272,273,274,275,276,277,278, // Row 13 - 282,283,284,285,286,287,288,289,291,292,293,294,295,296,297,298 // Row 14 -}; - -static const int map_sizes[3] = {110, 200, 320}; -static const int map_rows[3] = {11, 10, 16}; -static const int map_cols[3] = {10, 20, 20}; -static const int* maps[3] = {tiny_map, small_map, medium_map}; - -static inline int max(int a, int b) { - return (a > b) ? a : b; -} - -typedef struct Client Client; -typedef struct CRware CRware; -typedef struct Log Log; - -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -typedef struct MovementGraph MovementGraph; -struct MovementGraph { - int* target_positions; - int* cycle_ids; - int* weights; - int num_cycles; -}; - -struct CRware { - Client* client; - float* observations; - int* actions; - float* rewards; - unsigned char* terminals; - Log* agent_logs; - Log log; - float* scores; - int reward_type; - int width; - int height; - int map_choice; - int* warehouse_states; - int num_agents; - int num_requested_shelves; - int* agent_locations; - int* old_agent_locations; - int* agent_directions; - int* agent_states; - int human_agent_idx; - int grid_square_size; - int* original_shelve_locations; - MovementGraph* movement_graph; -}; - -void add_log(CRware* env, Log* agent_log) { - env->log.episode_return += agent_log->episode_return; - env->log.episode_length += agent_log->episode_length; - env->log.score += agent_log->score; - env->log.perf += fmaxf(0.0, agent_log->score - 0.01*agent_log->episode_length); - env->log.n += 1; -} - -int find_agent_at_position(CRware* env, int position) { - for (int i = 0; i < env->num_agents; i++) { - if (env->agent_locations[i] == position) { - return i; - } - } - return -1; -} - -void place_agent(CRware* env, int agent_idx) { - // map size fixed at top - int map_size = map_sizes[env->map_choice - 1]; - - int found_valid_position = 0; - while (!found_valid_position) { - int random_pos = rand() % map_size; - - // Skip if position is not empty - if (env->warehouse_states[random_pos] != EMPTY) { - continue; - } - - // Skip if another agent is already here - int agent_at_position = find_agent_at_position(env, random_pos); - if (agent_at_position != -1) { - continue; - } - - // Position is valid, place the agent - env->old_agent_locations[agent_idx] = random_pos; - env->agent_locations[agent_idx] = random_pos; - env->agent_directions[agent_idx] = rand() % 4; - env->agent_states[agent_idx] = 0; - found_valid_position = 1; - } -} - -int request_new_shelf(CRware* env) { - int total_shelves; - const int* shelf_locations; - if (env->map_choice == 1) { - total_shelves = 32; - shelf_locations = tiny_shelf_locations; - } else if (env->map_choice == 2) { - total_shelves = 80; - shelf_locations = small_shelf_locations; - } else { - total_shelves = 144; - shelf_locations = medium_shelf_locations; - } - int random_index = rand() % total_shelves; - int shelf_location = shelf_locations[random_index]; - if (env->warehouse_states[shelf_location] == SHELF ) { - env->warehouse_states[shelf_location] = REQUESTED_SHELF; - return 1; - } - return 0; -} - -void generate_map(CRware* env,const int* map) { - // seed new random - srand(time(NULL)); - int map_size = map_sizes[env->map_choice - 1]; - memcpy(env->warehouse_states, map, map_size * sizeof(int)); - - int requested_shelves_count = 0; - while (requested_shelves_count < env->num_requested_shelves) { - requested_shelves_count += request_new_shelf(env); - } - // set agents random locations - for (int i = 0; i < env->num_agents; i++) { - place_agent(env, i); - } -} - -MovementGraph* init_movement_graph(CRware* env) { - MovementGraph* graph = (MovementGraph*)calloc(1, sizeof(MovementGraph)); - graph->target_positions = (int*)calloc(env->num_agents, sizeof(int)); - graph->cycle_ids = (int*)calloc(env->num_agents, sizeof(int)); - graph->weights = (int*)calloc(env->num_agents, sizeof(int)); - graph->num_cycles = 0; - - // Initialize arrays - for (int i = 0; i < env->num_agents; i++) { - graph->target_positions[i] = -1; // No target - graph->cycle_ids[i] = -1; // Not in cycle - } - return graph; -} - -void init(CRware* env) { - int map_size = map_sizes[env->map_choice - 1]; - env->warehouse_states = (int*)calloc(map_size, sizeof(int)); - env->agent_locations = (int*)calloc(env->num_agents, sizeof(int)); - env->old_agent_locations = (int*)calloc(env->num_agents, sizeof(int)); - env->agent_directions = (int*)calloc(env->num_agents, sizeof(int)); - env->agent_states = (int*)calloc(env->num_agents, sizeof(int)); - env->scores = (float*)calloc(env->num_agents, sizeof(float)); - env->movement_graph = init_movement_graph(env); - env->agent_logs = (Log*)calloc(env->num_agents, sizeof(Log)); - if (env->map_choice == 1) { - generate_map(env, tiny_map); - } - else if (env->map_choice == 2) { - generate_map(env, small_map); - } - else { - generate_map(env, medium_map); - } -} - -void allocate(CRware* env) { - init(env); - env->observations = (float*)calloc(env->num_agents*(SELF_OBS+VISION_OBS), sizeof(float)); - env->actions = (int*)calloc(env->num_agents, sizeof(int)); - env->rewards = (float*)calloc(env->num_agents, sizeof(float)); - env->terminals = (unsigned char*)calloc(env->num_agents, sizeof(unsigned char)); -} - -void c_close(CRware* env) { - free(env->warehouse_states); - free(env->agent_locations); - free(env->agent_directions); - free(env->agent_states); - free(env->movement_graph->target_positions); - free(env->movement_graph->cycle_ids); - free(env->movement_graph->weights); - free(env->movement_graph); - free(env->agent_logs); - free(env->scores); -} - -void free_allocated(CRware* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void compute_observations(CRware* env) { - int surround_indices[8]; - int cols = map_cols[env->map_choice - 1]; - int rows = map_rows[env->map_choice - 1]; - float (*observations)[SELF_OBS+VISION_OBS] = (float(*)[SELF_OBS+VISION_OBS])env->observations; - for (int i = 0; i < env->num_agents; i++) { - // Agent location, direction, state - float* obs = &observations[i][0]; - int agent_location = env->agent_locations[i]; - int current_x = agent_location % cols; - int current_y = agent_location / cols; - // Self observations - obs[0] = env->agent_locations[i] / (float)(rows*cols); - obs[1] = (env->agent_directions[i] + 1) / 4.0; - obs[2] = env->agent_states[i]; - // Vision observations - for (int j = 0; j < 8; j++) { - int new_x = current_x + SURROUNDING_VECTORS[j][0]; - int new_y = current_y + SURROUNDING_VECTORS[j][1]; - surround_indices[j] = new_x + new_y * cols; - // other robots location and rotation if on that spot - for (int k = 0; k < env->num_agents; k++) { - if(i==k){ - continue; - } - if(env->agent_locations[k] == surround_indices[j]){ - obs[3 + j*3] = 1; - obs[4 + j*3] = (env->agent_directions[k] + 1) / 4.0; - break; - } else { - obs[3 + j*3] = 0; - obs[4 + j*3] = 0; - break; - } - } - // boundary check - if (new_x < 0 || new_x >= cols || new_y < 0 || new_y >= rows) { - obs[5 + j*3] = 0; - } else { - obs[5 + j*3] = (env->warehouse_states[surround_indices[j]] + 1) / 4.0; - } - } - } -} - -void c_reset(CRware* env) { - - env->terminals[0] = 0; - // set agents in center - env->human_agent_idx = 0; - if (env->map_choice == 1) { - generate_map(env, tiny_map); - } else if (env->map_choice == 2) { - generate_map(env, small_map); - } else { - generate_map(env, medium_map); - } - for(int x = 0;xnum_agents; x++){ - env->scores[x] = 0.0; - } - compute_observations(env); - -} - -int get_direction(CRware* env, int action, int agent_idx) { - // For reference: - // 0 = right (initial), 1 = down, 2 = left, 3 = up - int current_direction = env->agent_directions[agent_idx]; - if (action == FORWARD) { - return current_direction; - } - else if (action == LEFT) { - // Rotate counter-clockwise - return (current_direction + 3) % NUM_DIRECTIONS; - } - else if (action == RIGHT) { - // Rotate clockwise - return (current_direction + 1) % NUM_DIRECTIONS; - } - return current_direction; -} - -int get_new_position(CRware* env, int agent_idx) { - int cols = map_cols[env->map_choice - 1]; - int rows = map_rows[env->map_choice - 1]; - int current_position_x = env->agent_locations[agent_idx] % cols; - int current_position_y = env->agent_locations[agent_idx] / cols; - int current_direction = env->agent_directions[agent_idx]; - int new_position_x = current_position_x + DIRECTION_VECTORS[current_direction][0]; - int new_position_y = current_position_y + DIRECTION_VECTORS[current_direction][1]; - int new_position = new_position_x + new_position_y * cols; - // check boundary - if (new_position_x < 0 || new_position_x >= cols || new_position_y < 0 || new_position_y >= rows) { - return -1; - } - // check if holding shelf and next position is a shelf - int agent_state = env->agent_states[agent_idx]; - int warehouse_state = env->warehouse_states[new_position]; - if ((agent_state == HOLDING_EMPTY_SHELF || agent_state == HOLDING_REQUESTED_SHELF) && - (warehouse_state == SHELF || warehouse_state == REQUESTED_SHELF)) { - return -1; - } - // check if agent is trying to move into a goal with empty shelf - if (agent_state == HOLDING_EMPTY_SHELF && warehouse_state == GOAL) { - return -1; - } - return new_position; -} - -int detect_cycle_for_agent(CRware* env, int start_agent) { - MovementGraph* graph = env->movement_graph; - - // If already processed or no target, skip - if (graph->cycle_ids[start_agent] != -1 || - graph->target_positions[start_agent] == -1) { - return -1; - } - - // Initialize tortoise and hare - int tortoise = find_agent_at_position(env, graph->target_positions[start_agent]); - if (tortoise == -1) return -1; - int hare = tortoise; - - // Move hare ahead by one step - hare = find_agent_at_position(env, graph->target_positions[hare]); - if (hare == -1) return -1; - - // Loop to detect cycle - while (tortoise != hare) { - tortoise = find_agent_at_position(env, graph->target_positions[tortoise]); - if (tortoise == -1) return -1; - - hare = find_agent_at_position(env, graph->target_positions[hare]); - if (hare == -1) return -1; - hare = find_agent_at_position(env, graph->target_positions[hare]); - if (hare == -1) return -1; - } - - // Find the start of the cycle - tortoise = start_agent; - while (tortoise != hare) { - tortoise = find_agent_at_position(env, graph->target_positions[tortoise]); - hare = find_agent_at_position(env, graph->target_positions[hare]); - } - - int cycle_start = tortoise; - - // Mark all agents in the cycle - int cycle_id = graph->num_cycles++; - int current = cycle_start; - do { - graph->cycle_ids[current] = cycle_id; - current = find_agent_at_position(env, graph->target_positions[current]); - } while (current != cycle_start); - - return cycle_id; -} - -void detect_cycles(CRware* env) { - for (int i = 0; i < env->num_agents; i++) { - if(env->movement_graph->cycle_ids[i] == -1) { - detect_cycle_for_agent(env, i); - } - } -} - -void calculate_weights(CRware* env) { - MovementGraph* graph = env->movement_graph; - - // First pass: identify leaf nodes (agents not targeted by others) - for (int i = 0; i < env->num_agents; i++) { - if (graph->cycle_ids[i] != -1) continue; // Skip agents in cycles - - bool is_leaf = true; - for (int j = 0; j < env->num_agents; j++) { - if (graph->target_positions[j] != env->agent_locations[i]) continue; - is_leaf = false; - break; - } - - if (is_leaf) { - graph->weights[i] = 1; // Leaf node - } - } - - bool changed = true; - while(changed) { - changed = false; - for (int i = 0; i < env->num_agents; i++) { - if (graph->cycle_ids[i] != -1) continue; - - // Find agents targeting this agent's position - int max_child_weight = 0; - for (int j = 0; j < env->num_agents; j++) { - if (graph->target_positions[j] != env->agent_locations[i]) continue; - max_child_weight = max(max_child_weight, graph->weights[j]); - } - - if (max_child_weight == 0 || graph->weights[i] == max_child_weight + 1) { - continue; - } - graph->weights[i] = max_child_weight + 1; - changed = true; - } - } -} - -void update_movement_graph(CRware* env, int agent_idx) { - MovementGraph* graph = env->movement_graph; - int new_position = get_new_position(env, agent_idx); - if (new_position == -1) { - return; - } - graph->target_positions[agent_idx] = new_position; - - // reset cycle and weights - for (int i = 0; i < env->num_agents; i++) { - graph->cycle_ids[i] = -1; - graph->weights[i] = 0; - } - graph->num_cycles = 0; - - // detect cycles with Floyd algorithm - detect_cycles(env); - - // calculate weights for tree - calculate_weights(env); -} - -void move_agent(CRware* env, int agent_idx) { - - int new_position = get_new_position(env, agent_idx); - // check boundary - if (new_position == -1) { - return; - } - // if reach goal - int agent_state = env->agent_states[agent_idx]; - int agent_location = env->agent_locations[agent_idx]; - int new_position_state = env->warehouse_states[new_position]; - int current_position_state = env->warehouse_states[agent_location]; - if (new_position_state == GOAL && agent_state== HOLDING_REQUESTED_SHELF) { - if (current_position_state != GOAL) { - env->warehouse_states[agent_location] = 0; - } - env->agent_locations[agent_idx] = new_position; - return; - } - // if agent is holding requested shelf - if (agent_state == HOLDING_REQUESTED_SHELF) { - if (current_position_state != GOAL) { - env->warehouse_states[agent_location] = 0; - } - env->warehouse_states[new_position] = REQUESTED_SHELF; - } - // if agent is holding empty shelf - if (agent_state == HOLDING_EMPTY_SHELF) { - if (current_position_state != GOAL){ - env->warehouse_states[agent_location] = 0; - } - env->warehouse_states[new_position] = SHELF; - } - env->agent_locations[agent_idx] = new_position; - env->movement_graph->target_positions[agent_idx] = -1; -} - -void pickup_shelf(CRware* env, int agent_idx) { - // pickup shelf - const int* map = maps[env->map_choice - 1]; - int agent_location = env->agent_locations[agent_idx]; - int agent_state = env->agent_states[agent_idx]; - int current_position_state = env->warehouse_states[agent_location]; - int original_map_state = map[agent_location]; - if ((current_position_state == REQUESTED_SHELF) && (agent_state==UNLOADED)) { - env->rewards[agent_idx] = 0.5; - env->agent_logs[agent_idx].episode_return += 0.5; - env->agent_states[agent_idx]=HOLDING_REQUESTED_SHELF; - } - // return empty shelf - else if (agent_state == HOLDING_EMPTY_SHELF && current_position_state == original_map_state - && original_map_state != GOAL) { - env->agent_states[agent_idx]=UNLOADED; - env->warehouse_states[agent_location] = original_map_state; - env->rewards[agent_idx] = 1.0; - - env->agent_logs[agent_idx].score = 1.0; - env->agent_logs[agent_idx].episode_return += 1.0; - - env->scores[agent_idx] = 0; - add_log(env, &env->agent_logs[agent_idx]); - env->agent_logs[agent_idx] = (Log){0}; - } - // drop shelf at goal - else if (agent_state == HOLDING_REQUESTED_SHELF && current_position_state == GOAL) { - env->agent_states[agent_idx]=HOLDING_EMPTY_SHELF; - env->rewards[agent_idx] = 0.5; - env->agent_logs[agent_idx].episode_return += 0.5; - env->agent_logs[agent_idx].score = 1.0; - int shelf_count = 0; - while (shelf_count < 1) { - shelf_count += request_new_shelf(env); - } - } -} - -void process_cycle_movements(CRware* env, MovementGraph* graph) { - for (int cycle = 0; cycle < graph->num_cycles; cycle++) { - int cycle_size = 0; - for (int i = 0; i < env->num_agents; i++) { - if (graph->cycle_ids[i] == cycle) { - cycle_size++; - } - } - if (cycle_size == 2) continue; - - bool can_move_cycle = true; - // Verify all agents in cycle can move - for (int i = 0; i < env->num_agents; i++) { - if (graph->cycle_ids[i] != cycle) continue; - int new_pos = get_new_position(env, i); - if (new_pos == -1) { - can_move_cycle = false; - break; - } - } - - // Move all agents in cycle if possible - if (!can_move_cycle) continue; - for (int i = 0; i < env->num_agents; i++) { - if (graph->cycle_ids[i] != cycle) continue; - if (env->actions[i] != FORWARD) continue; - move_agent(env, i); - } - } -} - -void process_tree_movements(CRware* env, MovementGraph* graph) { - int max_weight = 0; - for (int i = 0; i < env->num_agents; i++) { - if (graph->cycle_ids[i] == -1 && graph->weights[i] > max_weight) { - max_weight = graph->weights[i]; - } - } - // Process from highest weight to lowest - for (int weight = max_weight; weight > 0; weight--) { - for (int i = 0; i < env->num_agents; i++) { - if (graph->cycle_ids[i] != -1 || graph->weights[i] != weight) continue; - if (env->actions[i] != FORWARD) continue; - - int new_pos = get_new_position(env, i); - if (new_pos == -1) continue; - - int target_agent = find_agent_at_position(env, new_pos); - if (target_agent != -1) continue; - move_agent(env, i); - } - } -} - -void c_step(CRware* env) { - memset(env->rewards, 0, env->num_agents * sizeof(float)); - MovementGraph* graph = env->movement_graph; - for (int i = 0; i < env->num_agents; i++) { - env->old_agent_locations[i] = env->agent_locations[i]; - env->agent_logs[i].episode_length += 1; - int action = env->actions[i]; - - // Handle direction changes and non-movement actions - if (action != NOOP && action != TOGGLE_LOAD) { - env->agent_directions[i] = get_direction(env, action, i); - } - if (action == TOGGLE_LOAD) { - pickup_shelf(env, i); - } - if (action == FORWARD) { - update_movement_graph(env, i); - } - } - int is_movement=0; - for(int i=0; inum_agents; i++) { - if (env->actions[i] == FORWARD) is_movement++; - } - if (is_movement>=1) { - // Process movements in cycles first - process_cycle_movements(env, graph); - // process tree movements - process_tree_movements(env, graph); - } - - compute_observations(env); -} - -const Color STONE_GRAY = (Color){80, 80, 80, 255}; -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_GREY = (Color){128, 128, 128, 255}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; -const Color PUFF_BACKGROUND2 = (Color){18, 72, 72, 255}; - -struct Client { - float width; - float height; - int tick; - Texture2D puffers; -}; - -Client* make_client(CRware* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = env->width; - client->height = env->height; - client->tick = 0; - InitWindow(env->width, env->height, "PufferLib Ray RWare"); - SetTargetFPS(60); - client->puffers = LoadTexture("resources/shared/puffers_128.png"); - return client; -} - -void c_render(CRware* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - Client* client = env->client; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - int map_size = map_sizes[env->map_choice - 1]; - int cols = map_cols[env->map_choice - 1]; - for (int i = 0; i < map_size; i++) { - int state = env->warehouse_states[i]; - Color color; - if (state == SHELF) { - color = PUFF_CYAN; - } else if (state == REQUESTED_SHELF) { - color = RED; - } else if (state == GOAL) { - color = STONE_GRAY; - } else { - color = PUFF_BACKGROUND; - } - - int x_pos = i % cols * env->grid_square_size; - int y_pos = i / cols * env->grid_square_size; - DrawRectangle( - x_pos, // x position - y_pos, // y position - env->grid_square_size, - env->grid_square_size, - color - ); - - DrawRectangleLines( - x_pos, - y_pos, - env->grid_square_size, - env->grid_square_size, - PUFF_GREY - ); - - DrawRectangleLines( - x_pos + 1, - y_pos + 1, - env->grid_square_size - 2, - env->grid_square_size - 2, - state != EMPTY ? WHITE : BLANK - ); - } - // draw agent - for (int j = 0; j < env->num_agents; j++) { - int cols = map_cols[env->map_choice - 1]; - - int old_agent_location = env->old_agent_locations[j]; - int old_x_pos = (old_agent_location % cols) * env->grid_square_size; - int old_y_pos = (old_agent_location / cols) * env->grid_square_size; - - int new_agent_location = env->agent_locations[j]; - int new_x_pos = (new_agent_location % cols) * env->grid_square_size; - int new_y_pos = (new_agent_location / cols) * env->grid_square_size; - - float interp_old = (1.0f - 1.0f/12.0f*(float)client->tick); - float interp_new = 1.0f/12.0f*(float)client->tick; - - int x_pos = interp_old*(float)old_x_pos + interp_new*(float)new_x_pos; - int y_pos = interp_old*(float)old_y_pos + interp_new*(float)new_y_pos; - - int starting_sprite_x = 0; - int rotation = 90*env->agent_directions[j]; - if (rotation == 180) { - starting_sprite_x = 128; - rotation = 0; - } - DrawTexturePro( - client->puffers, - (Rectangle){starting_sprite_x, 0, 128, 128}, - (Rectangle){ - x_pos + env->grid_square_size/2, - y_pos + env->grid_square_size/2, - env->grid_square_size, - env->grid_square_size - }, - (Vector2){env->grid_square_size/2, env->grid_square_size/2}, - rotation, - //env->agent_states[j] != UNLOADED ? RED : WHITE - WHITE - ); - // put a number on top of the agent - DrawText( - TextFormat("%d", j), - x_pos + env->grid_square_size/2, - y_pos + env->grid_square_size/2, - 20, - WHITE - ); - } - - client->tick = (client->tick + 1) % 12; - - EndDrawing(); -} -void close_client(Client* client) { - CloseWindow(); - free(client); -} diff --git a/pufferlib/ocean/rware/rware.py b/pufferlib/ocean/rware/rware.py deleted file mode 100644 index 25fb18116..000000000 --- a/pufferlib/ocean/rware/rware.py +++ /dev/null @@ -1,102 +0,0 @@ -'''High-perf Pong - -Inspired from https://gist.github.com/Yttrmin/18ecc3d2d68b407b4be1 -& https://jair.org/index.php/jair/article/view/10819/25823 -& https://www.youtube.com/watch?v=PSQt5KGv7Vk -''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.rware import binding - -PLAYER_OBS_N = 27 - -class Rware(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, report_interval=1, - width=1280, height=640, - num_agents=4, - map_choice=1, - num_requested_shelves=4, - grid_square_size=64, - human_agent_idx=0, - reward_type=1, - buf = None, seed=0): - - # env - self.num_agents = num_envs*num_agents - self.render_mode = render_mode - self.report_interval = report_interval - - self.num_obs = 27 - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(self.num_obs,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(5) - - super().__init__(buf=buf) - c_envs = [] - for i in range(num_envs): - env_id = binding.env_init( - self.observations[i*num_agents:(i+1)*num_agents], - self.actions[i*num_agents:(i+1)*num_agents], - self.rewards[i*num_agents:(i+1)*num_agents], - self.terminals[i*num_agents:(i+1)*num_agents], - self.truncations[i*num_agents:(i+1)*num_agents], - i + seed * num_envs, - width=width, - height=height, - map_choice=map_choice, - num_agents=num_agents, - num_requested_shelves=num_requested_shelves, - grid_square_size=grid_square_size, - human_agent_idx=human_agent_idx - ) - c_envs.append(env_id) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - self.tick += 1 - - info = [] - if self.tick % self.report_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - num_envs=1000; - env = MyRware(num_envs=num_envs) - env.reset() - tick = 0 - - actions = np.random.randint(0, env.single_action_space.n, (atn_cache, 5*num_envs)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - sps = num_envs * tick / (time.time() - start) - print(f'SPS: {sps:,}') -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/sanity.py b/pufferlib/ocean/sanity.py index a90d1f094..bcd93361c 100644 --- a/pufferlib/ocean/sanity.py +++ b/pufferlib/ocean/sanity.py @@ -6,7 +6,7 @@ class Bandit(gymnasium.Env): - '''Pufferlib Bandit environment + """Pufferlib Bandit environment Simulates a classic multiarmed bandit problem. @@ -17,18 +17,17 @@ class Bandit(gymnasium.Env): reward_scale: The scale of the reward reward_noise: The standard deviation of the reward signal hard_fixed_seed: All instances of the environment should share the same seed. - ''' - def __init__(self, num_actions=4, reward_scale=1, - reward_noise=0, hard_fixed_seed=42): + """ + + def __init__(self, num_actions=4, reward_scale=1, reward_noise=0, hard_fixed_seed=42): self.num_actions = num_actions self.reward_scale = reward_scale self.reward_noise = reward_noise self.hard_fixed_seed = hard_fixed_seed - self.observation=np.ones(1, dtype=np.float32) - self.observation_space=gymnasium.spaces.Box( - low=-1, high=1, shape=(1,)) - self.action_space=gymnasium.spaces.Discrete(num_actions) - self.render_mode = 'ansi' + self.observation = np.ones(1, dtype=np.float32) + self.observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(1,)) + self.action_space = gymnasium.spaces.Discrete(num_actions) + self.render_mode = "ansi" def reset(self, seed=None): # Bandit problem requires a single fixed seed @@ -59,10 +58,11 @@ def step(self, action): # Couples reward noise to scale reward = (reward + reward_noise) * self.reward_scale - return self.observation, reward, True, False, {'score': correct} + return self.observation, reward, True, False, {"score": correct} + class Memory(gymnasium.Env): - '''Pufferlib Memory environment + """Pufferlib Memory environment Repeat the observed sequence after a delay. It is randomly generated upon every reset. This is a test of memory length and capacity. It starts requiring credit assignment if you make the sequence too long. @@ -74,15 +74,15 @@ class Memory(gymnasium.Env): Args: mem_length: The length of the sequence mem_delay: The number of 0s between the sequence and the agent's response - ''' + """ + def __init__(self, mem_length=1, mem_delay=0): self.mem_length = mem_length self.mem_delay = mem_delay self.horizon = 2 * mem_length + mem_delay - self.observation_space=gymnasium.spaces.Box( - low=-1, high=1, shape=(1,)) - self.action_space=gymnasium.spaces.Discrete(2) - self.render_mode = 'ansi' + self.observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(1,)) + self.action_space = gymnasium.spaces.Discrete(2) + self.render_mode = "ansi" def reset(self, seed=None): if seed is not None: @@ -90,7 +90,7 @@ def reset(self, seed=None): np.random.seed(seed) self.solution = np.random.randint(0, 2, size=self.horizon).astype(np.float32) - self.solution[-(self.mem_length + self.mem_delay):] = -1 + self.solution[-(self.mem_length + self.mem_delay) :] = -1 self.submission = np.zeros(self.horizon) - 1 self.tick = 1 @@ -117,8 +117,7 @@ def step(self, action): info = {} if terminal: - info['score'] = np.all( - self.solution[:self.mem_length] == self.submission[-self.mem_length:]) + info["score"] = np.all(self.solution[: self.mem_length] == self.submission[-self.mem_length :]) return ob, reward, terminal, False, info @@ -130,30 +129,31 @@ def _render(val): c = 91 else: c = 90 - return f'\033[{c}m██\033[0m' + return f"\033[{c}m██\033[0m" chars = [] for val in self.solution: c = _render(val) chars.append(c) - chars.append(' Solution\n') + chars.append(" Solution\n") for val in self.submission: c = _render(val) chars.append(c) - chars.append(' Prediction\n') + chars.append(" Prediction\n") - return ''.join(chars) + return "".join(chars) class Multiagent(pettingzoo.ParallelEnv): - '''Pufferlib Multiagent environment + """Pufferlib Multiagent environment Agent 1 must pick action 0 and Agent 2 must pick action 1 Observation space: Box(0, 1, (1,)). 0 for Agent 1 and 1 for Agent 2 Action space: Discrete(2). Which action to take. - ''' + """ + def __init__(self): self.observation = { 1: np.zeros(1, dtype=np.float32), @@ -167,20 +167,19 @@ def __init__(self): 1: False, 2: False, } - self.possible_agents=[1, 2] - self.agents=[1, 2] - self.render_mode = 'ansi' + self.possible_agents = [1, 2] + self.agents = [1, 2] + self.render_mode = "ansi" def observation_space(self, agent): - return gymnasium.spaces.Box( - low=0, high=1, shape=(1,)) + return gymnasium.spaces.Box(low=0, high=1, shape=(1,)) def action_space(self, agent): return gymnasium.spaces.Discrete(2) def reset(self, seed=None): # Reallocating is faster than zeroing - self.view=np.zeros((2, 5), dtype=np.float32) + self.view = np.zeros((2, 5), dtype=np.float32) return self.observation, {} def step(self, action): @@ -202,8 +201,8 @@ def step(self, action): reward[2] = 0 info = { - 1: {'score': reward[1]}, - 2: {'score': reward[2]}, + 1: {"score": reward[1]}, + 2: {"score": reward[2]}, } return self.observation, reward, self.terminal, self.truncated, info @@ -215,22 +214,23 @@ def _render(val): c = 90 else: c = 90 - return f'\033[{c}m██\033[0m' + return f"\033[{c}m██\033[0m" chars = [] for row in self.view: for val in row: c = _render(val) chars.append(c) - chars.append('\n') - return ''.join(chars) + chars.append("\n") + return "".join(chars) + class Password(gymnasium.Env): - '''Pufferlib Password environment + """Pufferlib Password environment Guess the password, which is a static binary string. Your policy has to not determinize before it happens to get the reward, and it also has to - latch onto the reward within a few instances of getting it. + latch onto the reward within a few instances of getting it. Observation space: Box(0, 1, (password_length,)). A binary vector containing your guesses so far, so that the environment will be solvable without memory. Action space: Discrete(2). Your guess for the next digit. @@ -238,15 +238,14 @@ class Password(gymnasium.Env): Args: password_length: The number of binary digits in the password. hard_fixed_seed: A fixed seed for the environment. It should be the same for all instances. This environment does not make sense when randomly generated. - ''' - + """ + def __init__(self, password_length=5, hard_fixed_seed=42): self.password_length = password_length self.hard_fixed_seed = hard_fixed_seed - self.observation_space=gymnasium.spaces.Box( - low=-1, high=1, shape=(password_length,)) - self.action_space=gymnasium.spaces.Discrete(2) - self.render_mode = 'ansi' + self.observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(password_length,)) + self.action_space = gymnasium.spaces.Discrete(2) + self.render_mode = "ansi" def reset(self, seed=None): # Bandit problem requires a single fixed seed @@ -257,8 +256,7 @@ def reset(self, seed=None): np.random.seed(seed) self.observation = np.zeros(self.password_length, dtype=np.float32) - 1 - self.solution = np.random.randint( - 0, 2, size=self.password_length).astype(np.float32) + self.solution = np.random.randint(0, 2, size=self.password_length).astype(np.float32) self.tick = 0 return self.observation, {} @@ -276,7 +274,7 @@ def step(self, action): if terminal: reward = float(np.all(self.observation == self.solution)) - info['score'] = reward + info["score"] = reward return self.observation, reward, terminal, False, info @@ -288,32 +286,30 @@ def _render(val): c = 91 else: c = 90 - return f'\033[{c}m██\033[0m' + return f"\033[{c}m██\033[0m" chars = [] for val in self.solution: c = _render(val) chars.append(c) - chars.append(' Solution\n') + chars.append(" Solution\n") for val in self.observation: c = _render(val) chars.append(c) - chars.append(' Prediction\n') + chars.append(" Prediction\n") + + return "".join(chars) - return ''.join(chars) class Performance(gymnasium.Env): def __init__(self, delay_mean=0, delay_std=0, bandwidth=1): np.random.seed(time.time_ns() % 2**32) - self.observation_space = gymnasium.spaces.Box( - low=-2**20, high=2**20, - shape=(bandwidth,), dtype=np.float32 - ) + self.observation_space = gymnasium.spaces.Box(low=-(2**20), high=2**20, shape=(bandwidth,), dtype=np.float32) self.action_space = gymnasium.spaces.Discrete(2) self.observation = self.observation_space.sample() - self.render_mode = 'ansi' + self.render_mode = "ansi" def reset(self, seed=None): return self.observation, {} @@ -321,40 +317,39 @@ def reset(self, seed=None): def step(self, action): start = time.process_time() idx = 0 - target_time = self.delay_mean + self.delay_std*np.random.randn() + target_time = self.delay_mean + self.delay_std * np.random.randn() while time.process_time() - start < target_time: idx += 1 return self.observation, 0, False, False, {} + class PerformanceEmpiric(gymnasium.Env): def __init__(self, count_n=0, count_std=0, bandwidth=1): np.random.seed(time.time_ns() % 2**32) - self.observation_space = gymnasium.spaces.Box( - low=-2**20, high=2**20, - shape=(bandwidth,), dtype=np.float32 - ) + self.observation_space = gymnasium.spaces.Box(low=-(2**20), high=2**20, shape=(bandwidth,), dtype=np.float32) self.action_space = gymnasium.spaces.Discrete(2) self.observation = self.observation_space.sample() self.count_n = count_n self.count_std = count_std self.bandwidth = bandwidth - self.render_mode = 'ansi' + self.render_mode = "ansi" def reset(self, seed=None): return self.observation, {} def step(self, action): idx = 0 - target = self.count_n + self.count_std * np.random.randn() + target = self.count_n + self.count_std * np.random.randn() while idx < target: idx += 1 return self.observation, 0, False, False, {} + class Spaces(gymnasium.Env): - '''Pufferlib Spaces environment + """Pufferlib Spaces environment A simple environment with hierarchical observation and action spaces @@ -364,47 +359,51 @@ class Spaces(gymnasium.Env): 0.5 reward is given for each correct action Does not provide rendering - ''' + """ + def __init__(self): - self.observation_space = gymnasium.spaces.Dict({ - 'image': gymnasium.spaces.Box( - low=0, high=1, shape=(5, 5), dtype=np.float32), - 'flat': gymnasium.spaces.Box( - low=-1, high=1, shape=(5,), dtype=np.int8), - }) - self.action_space = gymnasium.spaces.Dict({ - 'image': gymnasium.spaces.Discrete(2), - 'flat': gymnasium.spaces.Discrete(2), - }) - self.render_mode = 'ansi' + self.observation_space = gymnasium.spaces.Dict( + { + "image": gymnasium.spaces.Box(low=0, high=1, shape=(5, 5), dtype=np.float32), + "flat": gymnasium.spaces.Box(low=-1, high=1, shape=(5,), dtype=np.int8), + } + ) + self.action_space = gymnasium.spaces.Dict( + { + "image": gymnasium.spaces.Discrete(2), + "flat": gymnasium.spaces.Discrete(2), + } + ) + self.render_mode = "ansi" def reset(self, seed=None): self.observation = { - 'image': np.random.rand(5, 5).astype(np.float32), - 'flat': np.random.randint(-1, 2, (5,), dtype=np.int8), + "image": np.random.rand(5, 5).astype(np.float32), + "flat": np.random.randint(-1, 2, (5,), dtype=np.int8), } - self.image_sign = np.sum(self.observation['image']) > 0 - self.flat_sign = np.sum(self.observation['flat']) > 0 + self.image_sign = np.sum(self.observation["image"]) > 0 + self.flat_sign = np.sum(self.observation["flat"]) > 0 return self.observation, {} def step(self, action): assert isinstance(action, dict) - assert 'image' in action and action['image'] in (0, 1) - assert 'flat' in action and action['flat'] in (0, 1) + assert "image" in action and action["image"] in (0, 1) + assert "flat" in action and action["flat"] in (0, 1) reward = 0 - if self.image_sign == action['image']: + if self.image_sign == action["image"]: reward += 0.5 - if self.flat_sign == action['flat']: + if self.flat_sign == action["flat"]: reward += 0.5 info = dict(score=reward) return self.observation, reward, True, False, info + class Squared(gymnasium.Env): - '''Pufferlib Squared environment + """Pufferlib Squared environment Agent starts at the center of a square grid. Targets are placed on the perimeter of the grid. @@ -418,15 +417,16 @@ class Squared(gymnasium.Env): Args: distance_to_target: The distance from the center to the closest target. num_targets: The number of targets to randomly generate. - - ''' + + """ MOVES = [(0, -1), (0, 1), (-1, 0), (1, 0), (1, -1), (-1, -1), (1, 1), (-1, 1)] - def __init__(self, + def __init__( + self, distance_to_target=1, num_targets=-1, - ): + ): grid_size = 2 * distance_to_target + 1 if num_targets == -1: num_targets = 4 * distance_to_target @@ -436,14 +436,17 @@ def __init__(self, self.num_targets = num_targets self.grid_size = grid_size self.max_ticks = num_targets * distance_to_target - self.observation_space = gymnasium.spaces.Box( - low=-1, high=1, shape=(grid_size, grid_size)) + self.observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(grid_size, grid_size)) self.action_space = gymnasium.spaces.Discrete(8) - self.render_mode = 'ansi' + self.render_mode = "ansi" def _all_possible_targets(self, grid_size): - return [(x, y) for x in range(grid_size) for y in range(grid_size) - if x == 0 or y == 0 or x == grid_size - 1 or y == grid_size - 1] + return [ + (x, y) + for x in range(grid_size) + for y in range(grid_size) + if x == 0 or y == 0 or x == grid_size - 1 or y == grid_size - 1 + ] def reset(self, seed=None): if seed is not None: @@ -470,7 +473,7 @@ def step(self, action): x += dx y += dy - min_dist = min([max(abs(x-tx), abs(y-ty)) for tx, ty in self.targets]) + min_dist = min([max(abs(x - tx), abs(y - ty)) for tx, ty in self.targets]) # This reward function will return 0.46 average reward for an unsuccessful # episode with distance_to_target=4 and num_targets=1 (0.5 for solve) # It looks reasonable but is not very discriminative @@ -479,36 +482,35 @@ def step(self, action): # This reward function will return 1 when the agent moves in the right direction # (plus an adjustment for the 0 reset reward) to average 1 for success # It is not much better than the previous one. - #reward = state.distance_to_target - min_dist - state.tick + 1/state.max_ticks + # reward = state.distance_to_target - min_dist - state.tick + 1/state.max_ticks # This function will return 0, 0.2, 0.4, ... 1 for successful episodes (n=5) # And will drop rewards to 0 or less as soon as an error is made # Somewhat smoother but actually worse than the previous ones # reward = (state.distance_to_target - min_dist - state.tick) / (state.max_ticks - state.tick) - # This one nicely tracks the task completed metric but does not optimize well - #if state.distance_to_target - min_dist - state.tick == 1: + # if state.distance_to_target - min_dist - state.tick == 1: # reward = 1 - #else: + # else: # reward = -state.tick if (x, y) in self.targets: self.targets.remove((x, y)) - #state.grid[x, y] = 0 + # state.grid[x, y] = 0 - dist_from_origin = max(abs(x-self.distance_to_target), abs(y-self.distance_to_target)) + dist_from_origin = max(abs(x - self.distance_to_target), abs(y - self.distance_to_target)) if dist_from_origin >= self.distance_to_target: self.agent_pos = self.distance_to_target, self.distance_to_target else: self.agent_pos = x, y - + self.grid[self.agent_pos] = -1 self.tick += 1 done = self.tick >= self.max_ticks score = (self.num_targets - len(self.targets)) / self.num_targets - info = {'score': score} if done else {} + info = {"score": score} if done else {} return self.grid, reward, done, False, info @@ -522,12 +524,13 @@ def render(self): color = 91 else: color = 90 - chars.append(f'\033[{color}m██\033[0m') - chars.append('\n') - return ''.join(chars) + chars.append(f"\033[{color}m██\033[0m") + chars.append("\n") + return "".join(chars) + class Stochastic(gymnasium.Env): - '''Pufferlib Stochastic environment + """Pufferlib Stochastic environment The optimal policy is to play action 0 < p % of the time and action 1 < (1 - p) % This is a test of whether your algorithm can learn a nontrivial stochastic policy. @@ -539,14 +542,14 @@ class Stochastic(gymnasium.Env): Args: p: The optimal probability for action 0 horizon: How often the environment should reset - ''' + """ + def __init__(self, p=0.75, horizon=1000): self.p = p self.horizon = horizon - self.observation_space = gymnasium.spaces.Box( - low=0, high=1, shape=(1,)) + self.observation_space = gymnasium.spaces.Box(low=0, high=1, shape=(1,)) self.action_space = gymnasium.spaces.Discrete(2) - self.render_mode = 'ansi' + self.render_mode = "ansi" def reset(self, seed=None): if seed is not None: @@ -569,15 +572,15 @@ def step(self, action): terminal = self.tick == self.horizon atn0_frac = self.count / self.tick - proximity_to_p = 1 - (self.p - atn0_frac)**2 + proximity_to_p = 1 - (self.p - atn0_frac) ** 2 - reward = proximity_to_p if ( - (action == 0 and atn0_frac < self.p) or - (action == 1 and atn0_frac >= self.p)) else 0 + reward = ( + proximity_to_p if ((action == 0 and atn0_frac < self.p) or (action == 1 and atn0_frac >= self.p)) else 0 + ) info = {} if terminal: - info['score'] = proximity_to_p + info["score"] = proximity_to_p return np.zeros(1, dtype=np.float32), reward, terminal, False, info @@ -589,37 +592,37 @@ def _render(val): c = 91 else: c = 90 - return f'\033[{c}m██\033[0m' + return f"\033[{c}m██\033[0m" + chars = [] if self.tick == 0: solution = 0 else: solution = 0 if self.count / self.tick < self.p else 1 chars.append(_render(solution)) - chars.append(' Solution\n') + chars.append(" Solution\n") chars.append(_render(self.action)) - chars.append(' Prediction\n') + chars.append(" Prediction\n") + + return "".join(chars) - return ''.join(chars) class Continuous(gymnasium.Env): def __init__(self, discretize=False): - self.observation_space=gymnasium.spaces.Box( - low=-1, high=1, shape=(6,)) + self.observation_space = gymnasium.spaces.Box(low=-1, high=1, shape=(6,)) self.discretize = discretize if discretize: - self.action_space=gymnasium.spaces.Discrete(4) + self.action_space = gymnasium.spaces.Discrete(4) else: - self.action_space=gymnasium.spaces.Box( - low=-1, high=1, shape=(2,)) + self.action_space = gymnasium.spaces.Box(low=-1, high=1, shape=(2,)) - self.render_mode = 'human' + self.render_mode = "human" self.client = None def reset(self, seed=None, options=None): # pos_x, pos_y, vel_x, vel_y, target_x, target_y - self.state = 2*np.random.rand(6)-1 + self.state = 2 * np.random.rand(6) - 1 self.state[2:4] = 0 self.tick = 0 @@ -637,7 +640,7 @@ def step(self, action): elif action == 3: accel_y = 0.1 else: - accel_x, accel_y = 0.1*action + accel_x, accel_y = 0.1 * action self.state[2] += accel_x self.state[3] += accel_y @@ -647,9 +650,9 @@ def step(self, action): pos_x, pos_y, vel_x, vel_y, target_x, target_y = self.state if pos_x < -1 or pos_x > 1 or pos_y < -1 or pos_y > 1: - return self.state, -1, True, False, {'score': 0} + return self.state, -1, True, False, {"score": 0} - dist = np.sqrt((pos_x - target_x)**2 + (pos_y - target_y)**2) + dist = np.sqrt((pos_x - target_x) ** 2 + (pos_y - target_y) ** 2) reward = 0.02 * (1 - dist) self.tick += 1 @@ -661,10 +664,10 @@ def step(self, action): info = {} if done: reward = 5.0 - info = {'score': 1} + info = {"score": 1} elif truncated: reward = 0.0 - info = {'score': 0} + info = {"score": 0} return self.state, reward, done, truncated, info @@ -676,6 +679,7 @@ def render(self): frame, atn = self.client.render(pos_x, pos_y, target_x, target_y) return frame + class RaylibClient: def __init__(self, width=1080, height=720, size=20): self.width = width @@ -683,20 +687,20 @@ def __init__(self, width=1080, height=720, size=20): self.size = size from raylib import rl - rl.InitWindow(width, height, - "PufferLib Simple Continuous".encode()) + + rl.InitWindow(width, height, "PufferLib Simple Continuous".encode()) rl.SetTargetFPS(10) self.rl = rl from cffi import FFI + self.ffi = FFI() def _cdata_to_numpy(self): image = self.rl.LoadImageFromScreen() width, height, channels = image.width, image.height, 4 - cdata = self.ffi.buffer(image.data, width*height*channels) - return np.frombuffer(cdata, dtype=np.uint8 - ).reshape((height, width, channels))[:, :, :3] + cdata = self.ffi.buffer(image.data, width * height * channels) + return np.frombuffer(cdata, dtype=np.uint8).reshape((height, width, channels))[:, :, :3] def render(self, pos_x, pos_y, target_x, target_y): rl = self.rl @@ -713,10 +717,10 @@ def render(self, pos_x, pos_y, target_x, target_y): rl.BeginDrawing() rl.ClearBackground([6, 24, 24, 255]) - pos_x = int((0.5+pos_x/2) * self.width) - pos_y = int((0.5+pos_y/2) * self.height) - target_x = int((0.5+target_x/2) * self.width) - target_y = int((0.5+target_y/2) * self.height) + pos_x = int((0.5 + pos_x / 2) * self.width) + pos_y = int((0.5 + pos_y / 2) * self.height) + target_x = int((0.5 + target_x / 2) * self.width) + target_y = int((0.5 + target_y / 2) * self.height) rl.DrawCircle(pos_x, pos_y, self.size, [255, 0, 0, 255]) rl.DrawCircle(target_x, target_y, self.size, [0, 0, 255, 255]) diff --git a/pufferlib/ocean/snake/README.md b/pufferlib/ocean/snake/README.md deleted file mode 100644 index 4ea7c0d15..000000000 --- a/pufferlib/ocean/snake/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# PufferLib Multi-Snake - -This is a simple multi-agent snake environment runnable with any number of snakes, board size, food, etc. I originally implemented this to demonstrate how simple it is to implement ultra high performance environments that run at millions of steps per second. The exact same approaches you see here are used in all of my more complex simulators. - -# Cython version - -The Cython version is the original. It runs over 10M steps/second/core on a high-end CPU. This is the version that we currently have bound to training. You can use it with the PufferLib demo script (--env snake) or import it from pufferlib/environments/ocean. There are a number of default board sizes and settings. If you would like to contribute games to PufferLib, you can use this project as a template. There is a bit of bloat in the .py file because we have to trick PufferLib's vectorization into thinking this is a vecenv. In the future, there will be a more standard advanced API. - -Key concepts: -- Memory views: Cython provides a way to access numpy arrays as C arrays or structs. This gives you C-speed numpy indexing and prevents you from having to copy data around. When running with multiprocessing, the observation buffers are stored in shared memory, so you are literally simulating into the experience buffer. -- No memory management: All data is allocated by Numpy and passed to C. This is fast and also prevents any chance of leaks -- No python callbacks: Compile and optimize with annotations enabled (see setup.py) to ensure that the Cython code never calls back to Python. You should be able to get >>1M agent steps/second for almost any sim - -# C version - -The C version is a direct port of the Cython version, plus a few minor tweaks. It includes a pure C raylib client and a pure C MLP forward pass for running local inference. I made this so that we could run a cool demo in the browser 100% client side. I may port additional simulators in the future, and you are welcome to contribute C code to PufferLib, but this is not required. You can make things plenty fast in Cython. To build this locally, all you need is the raylib source. If you want to build for web, follow RayLib's emscripten setup. - diff --git a/pufferlib/ocean/snake/__init__.py b/pufferlib/ocean/snake/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pufferlib/ocean/snake/binding.c b/pufferlib/ocean/snake/binding.c deleted file mode 100644 index 360c021f6..000000000 --- a/pufferlib/ocean/snake/binding.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "snake.h" - -#define Env CSnake -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->num_snakes = unpack(kwargs, "num_snakes"); - env->vision = unpack(kwargs, "vision"); - env->leave_corpse_on_death = unpack(kwargs, "leave_corpse_on_death"); - env->food = unpack(kwargs, "num_food"); - env->reward_food = unpack(kwargs, "reward_food"); - env->reward_corpse = unpack(kwargs, "reward_corpse"); - env->reward_death = unpack(kwargs, "reward_death"); - env->max_snake_length = unpack(kwargs, "max_snake_length"); - env->cell_size = unpack(kwargs, "cell_size"); - init_csnake(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/snake/snake.c b/pufferlib/ocean/snake/snake.c deleted file mode 100644 index 1e2112c84..000000000 --- a/pufferlib/ocean/snake/snake.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include "snake.h" -#include "puffernet.h" - -int demo() { - CSnake env = { - .num_snakes = 256, - .width = 640, - .height = 360, - .max_snake_length = 200, - .food = 4096, - .vision = 5, - .leave_corpse_on_death = true, - .reward_food = 1.0f, - .reward_corpse = 0.5f, - .reward_death = -1.0f, - }; - allocate_csnake(&env); - c_reset(&env); - - Weights* weights = load_weights("resources/snake/snake_weights.bin", 256773); - int logit_sizes[] = {4}; - LinearLSTM* net = make_linearlstm(weights, env.num_snakes, 968, logit_sizes, 1); - env.client = make_client(2, env.width, env.height); - - while (!WindowShouldClose()) { - // User can take control of the first snake - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) env.actions[0] = 0; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) env.actions[0] = 1; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) env.actions[0] = 2; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) env.actions[0] = 3; - } else { - memset(net->obs, 0, env.num_snakes*968*sizeof(float)); - for (int i = 0; i < env.num_snakes*121; i++) { - int obs = env.observations[i]; - net->obs[i*8 + obs] = 1.0f; - } - forward_linearlstm(net, net->obs, env.actions); - } - c_step(&env); - c_render(&env); - } - free_linearlstm(net); - free(weights); - close_client(env.client); - free_csnake(&env); - return 0; -} - -void test_performance(float test_time) { - CSnake env = { - .num_snakes = 1024, - .width = 1280, - .height = 720, - .max_snake_length = 200, - .food = 16384, - .vision = 5, - .leave_corpse_on_death = true, - .reward_food = 1.0f, - .reward_corpse = 0.5f, - .reward_death = -1.0f, - }; - allocate_csnake(&env); - c_reset(&env); - - int start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - for (int j = 0; j < env.num_snakes; j++) { - env.actions[j] = rand()%4; - } - c_step(&env); - i++; - } - int end = time(NULL); - free_csnake(&env); - printf("SPS: %f\n", (float)env.num_snakes*i / (end - start)); -} - -int main() { - demo(); - // test_performance(30); - return 0; -} diff --git a/pufferlib/ocean/snake/snake.h b/pufferlib/ocean/snake/snake.h deleted file mode 100644 index 30a9a4d40..000000000 --- a/pufferlib/ocean/snake/snake.h +++ /dev/null @@ -1,348 +0,0 @@ -#include -#include -#include -#include -#include "raylib.h" - -#define EMPTY 0 -#define FOOD 1 -#define CORPSE 2 -#define WALL 3 - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -typedef struct Client Client; -typedef struct CSnake CSnake; -struct CSnake { - char* observations; - int* actions; - float* rewards; - unsigned char* terminals; - Log log; - Log* snake_logs; - char* grid; - int* snake; - int* snake_lengths; - int* snake_ptr; - int* snake_lifetimes; - int* snake_colors; - int num_snakes; - int width; - int height; - int max_snake_length; - int food; - int vision; - int window; - int obs_size; - unsigned char leave_corpse_on_death; - float reward_food; - float reward_corpse; - float reward_death; - int tick; - int cell_size; - Client* client; -}; - -/** - * Add a snake's log to the main log when the snake's episode ends (dies or hits a wall). - * This should only be called during termination/truncation conditions for a specific snake. - * Accumulates the snake's stats into the main log and resets the snake's individual log. - */ -void add_log(CSnake* env, int snake_id) { - env->log.perf += env->snake_logs[snake_id].perf; - env->log.score += env->snake_logs[snake_id].score; - env->log.episode_return += env->snake_logs[snake_id].episode_return; - env->log.episode_length += env->snake_logs[snake_id].episode_length; - env->log.n += 1; -} - -void init_csnake(CSnake* env) { - env->grid = (char*)calloc(env->width*env->height, sizeof(char)); - env->snake = (int*)calloc(env->num_snakes*2*env->max_snake_length, sizeof(int)); - env->snake_lengths = (int*)calloc(env->num_snakes, sizeof(int)); - env->snake_ptr = (int*)calloc(env->num_snakes, sizeof(int)); - env->snake_lifetimes = (int*)calloc(env->num_snakes, sizeof(int)); - env->snake_colors = (int*)calloc(env->num_snakes, sizeof(int)); - env->snake_logs = (Log*)calloc(env->num_snakes, sizeof(Log)); - env->tick = 0; - env->client = NULL; - env->snake_colors[0] = 7; - for (int i = 1; inum_snakes; i++) - env->snake_colors[i] = i%4 + 4; // Randomize snake colors -} - -void c_close(CSnake* env) { - free(env->grid); - free(env->snake); - free(env->snake_lengths); - free(env->snake_ptr); - free(env->snake_lifetimes); - free(env->snake_colors); - free(env->snake_logs); -} -void allocate_csnake(CSnake* env) { - int obs_size = (2*env->vision + 1) * (2*env->vision + 1); - env->observations = (char*)calloc(env->num_snakes*obs_size, sizeof(char)); - env->actions = (int*)calloc(env->num_snakes, sizeof(int)); - env->rewards = (float*)calloc(env->num_snakes, sizeof(float)); - init_csnake(env); -} - -void free_csnake(CSnake* env) { - c_close(env); - free(env->observations); - free(env->actions); - free(env->rewards); -} - -void compute_observations(CSnake* env) { - for (int i = 0; i < env->num_snakes; i++) { - int head_ptr = i*2*env->max_snake_length + 2*env->snake_ptr[i]; - int r_offset = env->snake[head_ptr] - env->vision; - int c_offset = env->snake[head_ptr+1] - env->vision; - for (int r = 0; r < 2 * env->vision + 1; r++) { - for (int c = 0; c < 2 * env->vision + 1; c++) { - env->observations[i*env->obs_size + r*env->window + c] = env->grid[ - (r_offset + r)*env->width + c_offset + c]; - } - } - } -} - -void delete_snake(CSnake* env, int snake_id) { - while (env->snake_lengths[snake_id] > 0) { - int head_ptr = env->snake_ptr[snake_id]; - int head_offset = 2*env->max_snake_length*snake_id + 2*head_ptr; - int head_r = env->snake[head_offset]; - int head_c = env->snake[head_offset + 1]; - if (env->leave_corpse_on_death && env->snake_lengths[snake_id] % 2 == 0) - env->grid[head_r*env->width + head_c] = CORPSE; - else - env->grid[head_r*env->width + head_c] = EMPTY; - - env->snake[head_offset] = -1; - env->snake[head_offset + 1] = -1; - env->snake_lengths[snake_id]--; - if (head_ptr == 0) - env->snake_ptr[snake_id] = env->max_snake_length - 1; - else - env->snake_ptr[snake_id]--; - } -} - -void spawn_snake(CSnake* env, int snake_id) { - int head_r, head_c, tile, grid_idx; - delete_snake(env, snake_id); - do { - head_r = rand() % (env->height - 1); - head_c = rand() % (env->width - 1); - grid_idx = head_r*env->width + head_c; - tile = env->grid[grid_idx]; - } while (tile != EMPTY && tile != CORPSE); - int snake_offset = 2*env->max_snake_length*snake_id; - env->snake[snake_offset] = head_r; - env->snake[snake_offset + 1] = head_c; - env->snake_lengths[snake_id] = 1; - env->snake_ptr[snake_id] = 0; - env->snake_lifetimes[snake_id] = 0; - env->grid[grid_idx] = env->snake_colors[snake_id]; - env->snake_logs[snake_id] = (Log){0}; -} - -void spawn_food(CSnake* env) { - int idx, tile; - do { - int r = rand() % (env->height - 1); - int c = rand() % (env->width - 1); - idx = r*env->width + c; - tile = env->grid[idx]; - } while (tile != EMPTY && tile != CORPSE); - env->grid[idx] = FOOD; -} - -void c_reset(CSnake* env) { - env->window = 2*env->vision+1; - env->obs_size = env->window*env->window; - env->tick = 0; - env->log = (Log){0}; - - for (int i = 0; i < env->num_snakes; i++) - env->snake_logs[i] = (Log){0}; - - for (int r = 0; r < env->vision; r++) { - for (int c = 0; c < env->width; c++) - env->grid[r*env->width + c] = WALL; - } - for (int r = env->height - env->vision; r < env->height; r++) { - for (int c = 0; c < env->width; c++) - env->grid[r*env->width + c] = WALL; - } - for (int r = 0; r < env->height; r++) { - for (int c = 0; c < env->vision; c++) - env->grid[r*env->width + c] = WALL; - for (int c = env->width - env->vision; c < env->width; c++) - env->grid[r*env->width + c] = WALL; - } - for (int i = 0; i < env->num_snakes; i++) - spawn_snake(env, i); - for (int i = 0; i < env->food; i++) - spawn_food(env); - - compute_observations(env); -} - -void step_snake(CSnake* env, int i) { - env->snake_logs[i].episode_length += 1; - int atn = env->actions[i]; - int dr = 0; - int dc = 0; - switch (atn) { - case 0: dr = -1; break; // up - case 1: dr = 1; break; // down - case 2: dc = -1; break; // left - case 3: dc = 1; break; // right - } - - int head_ptr = env->snake_ptr[i]; - int snake_offset = 2*env->max_snake_length*i; - int head_offset = snake_offset + 2*head_ptr; - int next_r = dr + env->snake[head_offset]; - int next_c = dc + env->snake[head_offset + 1]; - - // Disallow moving into own neck - int prev_head_offset = head_offset - 2; - if (prev_head_offset < 0) - prev_head_offset += 2*env->max_snake_length; - int prev_r = env->snake[prev_head_offset]; - int prev_c = env->snake[prev_head_offset + 1]; - if (prev_r == next_r && prev_c == next_c) { - next_r = env->snake[head_offset] - dr; - next_c = env->snake[head_offset + 1] - dc; - } - - int tile = env->grid[next_r*env->width + next_c]; - if (tile >= WALL) { - env->rewards[i] = env->reward_death; - env->snake_logs[i].episode_return += env->reward_death; - env->snake_logs[i].score = env->snake_lengths[i]; - env->snake_logs[i].perf = env->snake_logs[i].score / env->snake_logs[i].episode_length; - add_log(env, i); - spawn_snake(env, i); - return; - } - - head_ptr++; // Circular buffer - if (head_ptr >= env->max_snake_length) - head_ptr = 0; - head_offset = snake_offset + 2*head_ptr; - env->snake[head_offset] = next_r; - env->snake[head_offset + 1] = next_c; - env->snake_ptr[i] = head_ptr; - env->snake_lifetimes[i]++; - - bool grow; - if (tile == FOOD) { - env->rewards[i] = env->reward_food; - env->snake_logs[i].episode_return += env->reward_food; - spawn_food(env); - grow = true; - } else if (tile == CORPSE) { - env->rewards[i] = env->reward_corpse; - env->snake_logs[i].episode_return += env->reward_corpse; - grow = true; - } else { - env->rewards[i] = 0.0; - grow = false; - } - - int snake_length = env->snake_lengths[i]; - if (grow && snake_length < env->max_snake_length - 1) { - env->snake_lengths[i]++; - } else { - int tail_ptr = head_ptr - snake_length; - if (tail_ptr < 0) // Circular buffer - tail_ptr = env->max_snake_length + tail_ptr; - int tail_r = env->snake[snake_offset + 2*tail_ptr]; - int tail_c = env->snake[snake_offset + 2*tail_ptr + 1]; - int tail_offset = 2*env->max_snake_length*i + 2*tail_ptr; - env->snake[tail_offset] = -1; - env->snake[tail_offset + 1] = -1; - env->grid[tail_r*env->width + tail_c] = EMPTY; - } - env->grid[next_r*env->width + next_c] = env->snake_colors[i]; -} - -void c_step(CSnake* env){ - env->tick++; - for (int i = 0; i < env->num_snakes; i++) - step_snake(env, i); - - compute_observations(env); -} - -// Raylib client -Color COLORS[] = { - (Color){6, 24, 24, 255}, - (Color){0, 0, 255, 255}, - (Color){0, 128, 255, 255}, - (Color){128, 128, 128, 255}, - (Color){255, 0, 0, 255}, - (Color){255, 255, 255, 255}, - (Color){255, 85, 85, 255}, - (Color){170, 170, 170, 255}, - (Color){0, 255, 255, 255}, - (Color){255, 255, 0, 255}, -}; - -typedef struct Client Client; -struct Client { - int cell_size; - int width; - int height; -}; - -Client* make_client(int cell_size, int width, int height) { - Client* client= (Client*)malloc(sizeof(Client)); - client->cell_size = cell_size; - client->width = width; - client->height = height; - InitWindow(width*cell_size, height*cell_size, "PufferLib Snake"); - SetTargetFPS(10); - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -void c_render(CSnake* env) { - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - if (env->client == NULL) { - env->client = make_client(env->cell_size, env->width, env->height); - } - - Client* client = env->client; - - BeginDrawing(); - ClearBackground(COLORS[0]); - int sz = client->cell_size; - for (int y = 0; y < env->height; y++) { - for (int x = 0; x < env->width; x++){ - int tile = env->grid[y*env->width + x]; - if (tile != EMPTY) - DrawRectangle(x*sz, y*sz, sz, sz, COLORS[tile]); - } - } - EndDrawing(); -} diff --git a/pufferlib/ocean/snake/snake.py b/pufferlib/ocean/snake/snake.py deleted file mode 100644 index 1883b4c22..000000000 --- a/pufferlib/ocean/snake/snake.py +++ /dev/null @@ -1,124 +0,0 @@ -'''High-perf many-agent snake. Inspired by snake env from https://github.com/dnbt777''' - -import numpy as np -import gymnasium - -import pufferlib -from pufferlib import APIUsageError -from pufferlib.ocean.snake import binding - -class Snake(pufferlib.PufferEnv): - def __init__(self, num_envs=16, width=640, height=360, - num_snakes=256, num_food=4096, - vision=5, leave_corpse_on_death=True, - reward_food=0.1, reward_corpse=0.1, reward_death=-1.0, - report_interval=128, max_snake_length=1024, - render_mode='human', buf=None, seed=0): - - if num_envs is not None: - num_snakes = num_envs * [num_snakes] - width = num_envs * [width] - height = num_envs * [height] - num_food = num_envs * [num_food] - leave_corpse_on_death = num_envs * [leave_corpse_on_death] - - if not (len(num_snakes) == len(width) == len(height) == len(num_food)): - raise APIUsageError('num_snakes, width, height, num_food must be lists of equal length') - - for w, h in zip(width, height): - if w < 2*vision+2 or h < 2*vision+2: - raise APIUsageError('width and height must be at least 2*vision+2') - - max_area = max([w*h for h, w in zip(height, width)]) - self.max_snake_length = min(max_snake_length, max_area) - self.report_interval = report_interval - - self.single_observation_space = gymnasium.spaces.Box( - low=0, high=2, shape=(2*vision+1, 2*vision+1), dtype=np.int8) - self.single_action_space = gymnasium.spaces.Discrete(4) - self.num_agents = sum(num_snakes) - self.render_mode = render_mode - self.tick = 0 - - self.cell_size = int(np.ceil(1280 / max(max(width), max(height)))) - - super().__init__(buf) - c_envs = [] - offset = 0 - for i in range(num_envs): - ns = num_snakes[i] - obs_slice = self.observations[offset:offset+ns] - act_slice = self.actions[offset:offset+ns] - rew_slice = self.rewards[offset:offset+ns] - term_slice = self.terminals[offset:offset+ns] - trunc_slice = self.truncations[offset:offset+ns] - # Seed each env uniquely: i + seed * num_envs - env_seed = i + seed * num_envs - env_id = binding.env_init( - obs_slice, - act_slice, - rew_slice, - term_slice, - trunc_slice, - env_seed, - width=width[i], - height=height[i], - num_snakes=ns, - num_food=num_food[i], - vision=vision, - leave_corpse_on_death=leave_corpse_on_death[i], - reward_food=reward_food, - reward_corpse=reward_corpse, - reward_death=reward_death, - max_snake_length=self.max_snake_length, - cell_size=self.cell_size - ) - c_envs.append(env_id) - offset += ns - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=None): - self.tick = 0 - if seed is None: - binding.vec_reset(self.c_envs, 0) - else: - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.report_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, self.cell_size) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = Snake() - env.reset() - tick = 0 - - total_snakes = env.num_agents - actions = np.random.randint(0, 4, (atn_cache, total_snakes)) - - import time - start = time.time() - while time.time() - start < timeout: - atns = actions[tick % atn_cache] - env.step(atns) - tick += 1 - - print(f'SPS: %f', total_snakes * tick / (time.time() - start)) - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/squared/binding.c b/pufferlib/ocean/squared/binding.c deleted file mode 100644 index be9dfade2..000000000 --- a/pufferlib/ocean/squared/binding.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "squared.h" - -#define Env Squared -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->size = unpack(kwargs, "size"); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/squared/squared.c b/pufferlib/ocean/squared/squared.c deleted file mode 100644 index a8b66185d..000000000 --- a/pufferlib/ocean/squared/squared.c +++ /dev/null @@ -1,39 +0,0 @@ -/* Pure C demo file for Squared. Build it with: - * bash scripts/build_ocean.sh target local (debug) - * bash scripts/build_ocean.sh target fast - * We suggest building and debugging your env in pure C first. You - * get faster builds and better error messages. To keep this example - * simple, it does not include C neural nets. See Target for that. - */ - -#include "squared.h" - -int main() { - Squared env = {.size = 11}; - env.observations = (unsigned char*)calloc(env.size*env.size, sizeof(unsigned char)); - env.actions = (int*)calloc(1, sizeof(int)); - env.rewards = (float*)calloc(1, sizeof(float)); - env.terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); - - c_reset(&env); - c_render(&env); - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env.actions[0] = 0; - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) env.actions[0] = UP; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) env.actions[0] = DOWN; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) env.actions[0] = LEFT; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) env.actions[0] = RIGHT; - } else { - env.actions[0] = rand() % 5; - } - c_step(&env); - c_render(&env); - } - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} - diff --git a/pufferlib/ocean/squared/squared.h b/pufferlib/ocean/squared/squared.h deleted file mode 100644 index 512d95d25..000000000 --- a/pufferlib/ocean/squared/squared.h +++ /dev/null @@ -1,148 +0,0 @@ -/* Squared: a sample single-agent grid env. - * Use this as a tutorial and template for your first env. - * See the Target env for a slightly more complex example. - * Star PufferLib on GitHub to support. It really, really helps! - */ - -#include -#include -#include "raylib.h" - -const unsigned char NOOP = 0; -const unsigned char DOWN = 1; -const unsigned char UP = 2; -const unsigned char LEFT = 3; -const unsigned char RIGHT = 4; - -const unsigned char EMPTY = 0; -const unsigned char AGENT = 1; -const unsigned char TARGET = 2; - -// Required struct. Only use floats! -typedef struct { - float perf; // Recommended 0-1 normalized single real number perf metric - float score; // Recommended unnormalized single real number perf metric - float episode_return; // Recommended metric: sum of agent rewards over episode - float episode_length; // Recommended metric: number of steps of agent episode - // Any extra fields you add here may be exported to Python in binding.c - float n; // Required as the last field -} Log; - -// Required that you have some struct for your env -// Recommended that you name it the same as the env file -typedef struct { - Log log; // Required field. Env binding code uses this to aggregate logs - unsigned char* observations; // Required. You can use any obs type, but make sure it matches in Python! - int* actions; // Required. int* for discrete/multidiscrete, float* for box - float* rewards; // Required - unsigned char* terminals; // Required. We don't yet have truncations as standard yet - int size; - int tick; - int r; - int c; -} Squared; - -void add_log(Squared* env) { - env->log.perf += (env->rewards[0] > 0) ? 1 : 0; - env->log.score += env->rewards[0]; - env->log.episode_length += env->tick; - env->log.episode_return += env->rewards[0]; - env->log.n++; -} - -// Required function -void c_reset(Squared* env) { - int tiles = env->size*env->size; - memset(env->observations, 0, tiles*sizeof(unsigned char)); - env->observations[tiles/2] = AGENT; - env->r = env->size/2; - env->c = env->size/2; - env->tick = 0; - int target_idx; - do { - target_idx = rand() % tiles; - } while (target_idx == tiles/2); - env->observations[target_idx] = TARGET; -} - -// Required function -void c_step(Squared* env) { - env->tick += 1; - - int action = env->actions[0]; - env->terminals[0] = 0; - env->rewards[0] = 0; - - env->observations[env->r*env->size + env->c] = EMPTY; - - if (action == DOWN) { - env->r += 1; - } else if (action == RIGHT) { - env->c += 1; - } else if (action == UP) { - env->r -= 1; - } else if (action == LEFT) { - env->c -= 1; - } - - if (env->tick > 3*env->size - || env->r < 0 - || env->c < 0 - || env->r >= env->size - || env->c >= env->size) { - env->terminals[0] = 1; - env->rewards[0] = -1.0; - add_log(env); - c_reset(env); - return; - } - - int pos = env->r*env->size + env->c; - if (env->observations[pos] == TARGET) { - env->terminals[0] = 1; - env->rewards[0] = 1.0; - add_log(env); - c_reset(env); - return; - } - - env->observations[pos] = AGENT; -} - -// Required function. Should handle creating the client on first call -void c_render(Squared* env) { - if (!IsWindowReady()) { - InitWindow(64*env->size, 64*env->size, "PufferLib Squared"); - SetTargetFPS(5); - } - - // Standard across our envs so exiting is always the same - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - int px = 64; - for (int i = 0; i < env->size; i++) { - for (int j = 0; j < env->size; j++) { - int tex = env->observations[i*env->size + j]; - if (tex == EMPTY) { - continue; - } - Color color = (tex == AGENT) ? (Color){0, 187, 187, 255} : (Color){187, 0, 0, 255}; - DrawRectangle(j*px, i*px, px, px, color); - } - } - - EndDrawing(); -} - -// Required function. Should clean up anything you allocated -// Do not free env->observations, actions, rewards, terminals -void c_close(Squared* env) { - if (IsWindowReady()) { - CloseWindow(); - } -} diff --git a/pufferlib/ocean/squared/squared.py b/pufferlib/ocean/squared/squared.py deleted file mode 100644 index 533778810..000000000 --- a/pufferlib/ocean/squared/squared.py +++ /dev/null @@ -1,64 +0,0 @@ -'''A simple sample environment. Use this as a template for your own envs.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.squared import binding - -class Squared(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, log_interval=128, size=11, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(size*size,), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.Discrete(5) - self.render_mode = render_mode - self.num_agents = num_envs - self.log_interval = log_interval - - super().__init__(buf) - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, size=size) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - - self.actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 4096 - - env = Squared(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, 5, (CACHE, N)) - - i = 0 - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[i % CACHE]) - steps += N - i += 1 - - print('Squared SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/tactical/__init__.py b/pufferlib/ocean/tactical/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pufferlib/ocean/tactical/binding.c b/pufferlib/ocean/tactical/binding.c deleted file mode 100644 index ec6d07914..000000000 --- a/pufferlib/ocean/tactical/binding.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "tactical.h" -#define Env Tactical -#include "../env_binding.h" - -// no init args needed -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - return 0; -} - -// no logging implemented atm -static int my_log(PyObject* dict, Log* log) { - return 0; -} diff --git a/pufferlib/ocean/tactical/maps.h b/pufferlib/ocean/tactical/maps.h deleted file mode 100644 index 9da656fe1..000000000 --- a/pufferlib/ocean/tactical/maps.h +++ /dev/null @@ -1,111 +0,0 @@ -// - = EMPTY (not walkable, not necessarily rendered) -// . = GROUND (walkable) -// | = HOLE (not walkable, blocks view) -// # = WALL (not walkable) - -unsigned int map1_height = 12; -unsigned int map1_width = 12; -char map1[] = - "............" - ".#|..#.#|..#" - "............" - "..||....||.." - "|##..#|##..#" - "............" - "............" - ".#|..#.#|..#" - "............" - "..||....||.." - "|##..#|##..#" - "............"; - -unsigned int map2_height = 28; -unsigned int map2_width = 29; -char map2[] = - "-------------#.--------------" - "-----------.#..--------------" - "----------......-------------" - "---------.|.#|...------------" - "--------........#.-----------" - "-------.............---------" - "------...............--------" - "------..............#.-------" - "-----..................------" - "---.....................-----" - "--.|.....................----" - "-..|......................---" - ".................|#|.......--" - "-#..............||||.......--" - "--..............#|#|........." - "---...............#.........-" - "----.......................--" - "-----.....................---" - "------...................----" - "-------.................-----" - "--------...............------" - "---------.............-------" - "----------...........--------" - "-----------.........---------" - "------------.......----------" - "-------------.....-----------" - "--------------|.#------------" - "---------------.-------------"; - -unsigned int map3_height = 28; -unsigned int map3_width = 29; -char map3[] = - "-------------#.--------------" - "-----------.#..--------------" - "----------.......------------" - "---------.|.#|..#------------" - "--------........#.-----------" - "-------.............---------" - "------...........#...--------" - "------....||........#.-------" - "-----..................------" - "---.............##......-----" - "--.|......#..............----" - "-..|.....##...............---" - "..................#|#......--" - "-###............|..........--" - "--..............#|#.........-" - "---...............#.........-" - "----......#|...............--" - "-----.....................---" - "------...........#|#.....----" - "-------............#....-----" - "--------...............------" - "---------...##|#.......------" - "----------...........--------" - "-----------.........---------" - "------------.......----------" - "-------------.....-----------" - "--------------..#------------" - "---------------.-------------"; - -char* get_map(int map_id) { - switch (map_id) { - case 1: return map1; break; - case 2: return map2; break; - case 3: return map3; break; - default: printf("Invalid map id <%i>\n", map_id); exit(1); - } -} - -unsigned int get_map_height(int map_id) { - switch (map_id) { - case 1: return map1_height; break; - case 2: return map2_height; break; - case 3: return map3_height; break; - default: printf("Invalid map id <%i>\n", map_id); exit(1); - } -} - -unsigned int get_map_width(int map_id) { - switch (map_id) { - case 1: return map1_width; break; - case 2: return map2_width; break; - case 3: return map3_width; break; - default: printf("Invalid map id <%i>\n", map_id); exit(1); - } -} diff --git a/pufferlib/ocean/tactical/tactical.c b/pufferlib/ocean/tactical/tactical.c deleted file mode 100644 index c1f8fa5aa..000000000 --- a/pufferlib/ocean/tactical/tactical.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "tactical.h" - - -int main() { - Tactical* env = init_tactical(); - // allocate(&env); - - env->client = init_client(env); - - c_reset(env); - while (!WindowShouldClose()) { - if (IsKeyPressed(KEY_Q) || IsKeyPressed(KEY_BACKSPACE)) break; - c_step(env); - c_render(env); - } - - close_client(env->client); - free_tactical(env); - - // free_linearlstm(net); - // free(weights); - // free_allocated(&env); - // close_client(client); -} - diff --git a/pufferlib/ocean/tactical/tactical.h b/pufferlib/ocean/tactical/tactical.h deleted file mode 100644 index f810d1f3b..000000000 --- a/pufferlib/ocean/tactical/tactical.h +++ /dev/null @@ -1,1320 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" -#include "maps.h" - - -#define CELL_EMPTY 0 -#define CELL_GROUND 1 -#define CELL_HOLE 2 -#define CELL_WALL 3 - -#define MAX_TEXT_ANIMATIONS 100 // max number of text animations - -const Color COLOR_BACKGROUND = {6, 24, 24, 255}; // window background -const Color COLOR_CELL_GRASS = {150, 200, 150, 255}; // top of WALL cells -const Color COLOR_CELL_DIRT = {80, 50, 50, 255}; // side of WALL cells -const Color COLOR_CELL_GROUND = {150, 150, 170, 255}; // GROUND cells -// const Color COLOR_CELL_GRASS = {163, 197, 69, 255}; // top of WALL cells -// const Color COLOR_CELL_DIRT = {40, 20, 5, 255}; // side of WALL cells -// const Color COLOR_CELL_GROUND = {112, 123, 111, 255}; // GROUND cells -const Color COLOR_CELL_BORDER = RAYWHITE; // border of GROUND cells -const Color COLOR_CELL_MOVE = DARKGREEN; -const Color COLOR_CELL_MOVE_TEXT = RAYWHITE; -const Color COLOR_ACTIVE_PLAYER = RAYWHITE; // border around active player circle -const Color COLOR_PLAYER1 = RED; // player 1 color (character and circle) -const Color COLOR_PLAYER2 = GREEN; // player 2 color (character and circle) -const Color COLOR_TEXT_DEFAULT = RAYWHITE; // main text color -const Color COLOR_HEALTH = RED; -const Color COLOR_ACTION_POINTS = SKYBLUE; -const Color COLOR_MOVEMENT_POINTS = LIME; -const Color COLOR_SPELL = GOLD; -const Color COLOR_SPELL_COOLDOWN = BROWN; -const Color COLOR_CELL_SPELL = BEIGE; -const Color COLOR_CELL_ACTIVE_SPELL = {255, 161, 0, 255}; -const Color COLOR_CELL_INACTIVE_SPELL = {150, 150, 255, 255}; -const Color COLOR_ENTITY_NAME = PURPLE; -const Color COLOR_ENTITY_NAME_HOVER = YELLOW; - - -// TODO many leaks... - -// forward declarations -typedef struct Tactical Tactical; -typedef struct Entity Entity; -typedef struct Spell Spell; -typedef struct Client Client; - -// arghh annoying -void add_animation_text(Tactical* env, Client* client, const char* text, int cell, Color color, int font_size, float duration); -void compute_movement(Tactical* env, Entity* entity); - -typedef struct Log { - float score; - float n; -} Log; - -typedef struct Tactical { - Log log; - Client* client; - int num_agents; - unsigned char* observations; - int* actions; - float* rewards; - unsigned char* terminals; - unsigned char* truncations; - - unsigned int n_entities; - Entity* entities; - // pointers to entities (these won't be allocated), assume 1v1 for now - Entity* player1; - Entity* player2; - Entity* current_player; - int current_player_idx; - - unsigned int map_width; - unsigned int map_height; - unsigned int map_size; // width * height - unsigned int* map; - Entity** cell_to_entity; - - unsigned int* movement_path; - int* movement_distance; -} Tactical; - -// Entity (player, summoned creature...) -struct Entity { - const char* name; - int cell; - Color color; - int health_points_total; - int action_points_total; - int movement_points_total; - int health_points_current; - int action_points_current; - int movement_points_current; - - // spells - Spell* spells; - int spell_count; -}; - -struct Spell { - const char* name; - int ap_cost; - int cooldown; - int remaining_cooldown; - int range; // TODO add different range types (default, in a line, in diagonal...) - bool line_of_sight; // whether spell can be casted across walls - bool modifiable_range; // whether spell range can be increased or decreased from other spells - bool cast_in_line; // whether spell range can be increased or decreased from other spells - // TODO add a "zone of effect" shape that lists the deltas that the spell touches around the cell - void (*effect)(Tactical*, Entity*, int, Spell*); // pointer to a function that takes in the env, the caster and the target cell - bool (*render_animation)(Tactical*, Client*, int, int, float, Spell*); // pointer to a function that takes in the env, the client, the caster cell, the target cell and the progress (in seconds), that renders the spell animation and returns true when the animation is finished - int damage; // damage dealt by the spell - int animation_state; - int aoe_range; // 0: single-target; 1: 5 cells in total (1+4); 2: 13 cells total (1+4+8) -}; - -struct Client { - int width; - int height; - - float cw; // cell width - float ch; // cell height - float offset_x; // offset for the whole map - float offset_y; // offset for the whole map - float dy; // vertical offset for wall cells - - // current cell (if any) under the mouse cursor - int mx; - int my; - int mrow; - int mcol; - int mcell; - int mcell_type; - - bool* movement_cells; - Spell* active_spell; - bool* spell_cells; - bool* active_spell_cells; - - // for drawing - float *xa, *xb, *xc, *xd, *xe, *ya, *yb, *yc, *yd, *ye; - - // animations (move) - Entity* move_anim_entity; - int* move_anim_path; - int move_anim_path_idx; - int move_anim_path_length; - float move_anim_progress; - float move_anim_dx; // delta in position with respect to the center of the cell - float move_anim_dy; - float move_anim_cells_per_second; - - // animations (text) - char* text_anim_texts[MAX_TEXT_ANIMATIONS]; - float text_anim_x0[MAX_TEXT_ANIMATIONS]; - float text_anim_x1[MAX_TEXT_ANIMATIONS]; - float text_anim_y0[MAX_TEXT_ANIMATIONS]; - float text_anim_y1[MAX_TEXT_ANIMATIONS]; - float text_anim_progress[MAX_TEXT_ANIMATIONS]; - float text_anim_duration[MAX_TEXT_ANIMATIONS]; // in seconds - Color text_anim_color[MAX_TEXT_ANIMATIONS]; - int text_anim_font_size[MAX_TEXT_ANIMATIONS]; - int text_anim_count; - - // animations (spells) -- only one at a time - Spell* spell_anim; - float spell_anim_progress; - int spell_anim_caster_cell; - int spell_anim_target_cell; - - clock_t last_render_time; - double dt; // in seconds - float max_fps; - -}; - -void free_tactical(Tactical* env) { - free(env->rewards); - free(env->observations); - free(env->actions); - free(env->map); - free(env->movement_path); - free(env->movement_distance); - free(env->entities); - - free(env); // do this last -} - -int get_cell(Tactical* env, int row, int col) { - if (row < 0 || row >= env->map_height) return -1; - if (col < 0 || col >= env->map_width) return -1; - return row * env->map_width + col; -} -int get_row(Tactical* env, int cell) { - return cell / env->map_width; -} -int get_col(Tactical* env, int cell) { - return cell % env->map_width; -} -int get_cell_with_delta(Tactical* env, int cell, int delta_row, int delta_col) { - return get_cell(env, get_row(env, cell) + delta_row, get_col(env, cell) + delta_col); -} - - -//////////// -// SPELLS // -//////////// - -void update_cooldowns(Entity* entity) { - for (int i = 0; i < entity->spell_count; ++i) { - if (entity->spells[i].remaining_cooldown > 0) { - entity->spells[i].remaining_cooldown--; - } - } -} - -void cast_spell(Tactical* env, Entity* caster, Spell* spell, int target_cell) { - // check if the spell can be cast - if (caster->action_points_current < spell->ap_cost) { - printf("Not enough action points to cast %s.\n", spell->name); - return; - } - if (spell->remaining_cooldown > 0) { - printf("Spell %s is on cooldown for %d more turns.\n", spell->name, spell->remaining_cooldown); - return; - } - - // cast the spell - spell->effect(env, caster, target_cell, spell); - spell->animation_state = 0; - caster->action_points_current -= spell->ap_cost; - spell->remaining_cooldown = spell->cooldown; -} - -void spell_explosive_arrow(Tactical* env, Entity* caster, int target_cell, Spell* spell) { - for (int delta_row = -spell->aoe_range; delta_row <= spell->aoe_range; ++delta_row) { - for (int delta_col = -(spell->aoe_range - abs(delta_row)); delta_col <= spell->aoe_range - abs(delta_row); ++delta_col) { - int cell = get_cell_with_delta(env, target_cell, delta_row, delta_col); - if (env->map[cell] != CELL_GROUND) continue; - Entity* target = env->cell_to_entity[cell]; - if (target) { - target->health_points_current -= spell->damage; - } - } - } -} -bool spell_explosive_arrow_anim(Tactical* env, Client* client, int caster_cell, int target_cell, float t, Spell* spell) { - float xe0 = client->xe[caster_cell]; - float xe1 = client->xe[target_cell]; - float ye0 = client->ye[caster_cell]; - float ye1 = client->ye[target_cell]; - - float phase1_duration = 0.5; - float phase2_duration = 0.2; - - Vector2 vec = GetSplinePointBezierQuad( - (Vector2){xe0, ye0 - 2 * client->ch}, - (Vector2){(xe0 + xe1) / 2, (ye0 + ye1) / 2 - 200}, - (Vector2){xe1, ye1}, - fmin(t / phase1_duration, 1.0)); - - if (t <= phase1_duration) { - DrawCircle(vec.x, vec.y, 10, (Color){255, 0, 0, 255}); - } else if (t <= phase1_duration + phase2_duration) { - if (spell->animation_state == 0) { - spell->animation_state = 1; - // get all players hit - for (int delta_row = -spell->aoe_range; delta_row <= spell->aoe_range; ++delta_row) { - for (int delta_col = -(spell->aoe_range - abs(delta_row)); delta_col <= spell->aoe_range - abs(delta_row); ++delta_col) { - int cell = get_cell_with_delta(env, target_cell, delta_row, delta_col); - if (env->map[cell] != CELL_GROUND) continue; - Entity* target = env->cell_to_entity[cell]; - if (target) { - add_animation_text(env, client, TextFormat("-%i HP", spell->damage), - cell, COLOR_HEALTH, 20, 1.2); - } - } - } - } - DrawCircle(vec.x, vec.y, 10 + (t - phase1_duration) * 400, - (Color){255, 0, 0, 255 * (1 - (t - phase1_duration) / phase2_duration)}); - } else { - return true; - } - return false; -} -Spell create_spell_explosive_arrow() { - Spell spell; - spell.name = "Explosive Arrow"; - spell.ap_cost = 4; - spell.cooldown = 0; - spell.remaining_cooldown = 0; - spell.line_of_sight = true; - spell.cast_in_line = false; - spell.range = 11; - spell.damage = 200; - spell.aoe_range = 2; - spell.effect = spell_explosive_arrow; - spell.render_animation = spell_explosive_arrow_anim; - return spell; -} - -void spell_flying_arrow(Tactical* env, Entity* caster, int target_cell, Spell* spell) { - Entity* target = env->cell_to_entity[target_cell]; - if (target) { - target->health_points_current -= spell->damage; - } -} -bool spell_flying_arrow_anim(Tactical* env, Client* client, int caster_cell, int target_cell, float t, Spell* spell) { - float xe0 = client->xe[caster_cell]; - float xe1 = client->xe[target_cell]; - float ye0 = client->ye[caster_cell]; - float ye1 = client->ye[target_cell]; - - float phase1_duration = 0.8; - - Vector2 vec = GetSplinePointBezierQuad( - (Vector2){xe0, ye0 - 2 * client->ch}, - (Vector2){(xe0 + xe1) / 2, (ye0 + ye1) / 2 - 700}, - (Vector2){xe1, ye1}, - fmin(t / phase1_duration, 1.0)); - - if (t <= phase1_duration) { - DrawCircle(vec.x, vec.y, 10, (Color){0, 255, 0, 255}); - } else { - Entity* target = env->cell_to_entity[target_cell]; - if (target) { - add_animation_text(env, client, TextFormat("-%i HP", spell->damage), - target_cell, COLOR_HEALTH, 20, 1.2); - } - return true; - } - return false; -} -Spell create_spell_flying_arrow() { - Spell spell; - spell.name = "Flying Arrow"; - spell.ap_cost = 3; - spell.cooldown = 0; - spell.remaining_cooldown = 0; - spell.line_of_sight = false; - spell.cast_in_line = false; - spell.range = 14; - spell.damage = 100; - spell.aoe_range = 0; - spell.effect = spell_flying_arrow; - spell.render_animation = spell_flying_arrow_anim; - return spell; -} - -void spell_rooting_arrow(Tactical* env, Entity* caster, int target_cell, Spell* spell) { - Entity* target = env->cell_to_entity[target_cell]; - if (target) { - target->movement_points_current -= 3; - if (target == env->current_player) - compute_movement(env, target); - } -} -bool spell_rooting_arrow_anim(Tactical* env, Client* client, int caster_cell, int target_cell, float t, Spell* spell) { - float xe0 = client->xe[caster_cell]; - float xe1 = client->xe[target_cell]; - float ye0 = client->ye[caster_cell]; - float ye1 = client->ye[target_cell]; - - float phase1_duration = 0.3; - float phase2_duration = 0.3; - - if (t <= phase1_duration) { - float progress = t / phase1_duration; - DrawLineEx( - (Vector2){xe0, ye0}, - (Vector2){(1-progress)*xe0 + progress*xe1, (1-progress)*ye0 + progress*ye1}, - 4, (Color){200, 100, 0, 255}); - } else if (t <= phase1_duration + phase2_duration) { - if (spell->animation_state == 0) { - spell->animation_state = 1; - // get all players hit - Entity* target = env->cell_to_entity[target_cell]; - if (target) { - add_animation_text(env, client, "-3 MP", target_cell, COLOR_MOVEMENT_POINTS, 20, 1.2); - } - } - DrawLineEx((Vector2){xe0, ye0}, (Vector2){xe1, ye1}, 4, (Color){200, 100, 0, 255}); - float progress = (t - phase1_duration) / phase2_duration; - for (int i = -3; i <= 3; ++i) { - float angle = -M_PI/2 + i * M_PI/12; - float new_x = xe1 + cos(angle) * 200; - float new_y = ye1 + -sin(angle) * 200; - - DrawLineEx( - (Vector2){xe1, ye1}, - (Vector2){(1-progress)*xe1 + progress*new_x, (1-progress)*ye1 + progress*new_y}, - 4, (Color){200, 100, 0, 255}); - } - } else { - return true; - } - return false; -} -Spell create_spell_rooting_arrow() { - Spell spell; - spell.name = "Rooting Arrow"; - spell.ap_cost = 2; - spell.cooldown = 2; - spell.remaining_cooldown = 0; - spell.line_of_sight = true; - spell.cast_in_line = false; - spell.range = 8; - spell.damage = 0; - spell.aoe_range = 0; - spell.effect = spell_rooting_arrow; - spell.render_animation = spell_rooting_arrow_anim; - return spell; -} - -void spell_wind_arrow(Tactical* env, Entity* caster, int target_cell, Spell* spell) { - Entity* target = env->cell_to_entity[target_cell]; - if (target) { - target->health_points_current -= spell->damage; - } -} -bool spell_wind_arrow_anim(Tactical* env, Client* client, int caster_cell, int target_cell, float t, Spell* spell) { - float xe0 = client->xe[caster_cell]; - float xe1 = client->xe[target_cell]; - float ye0 = client->ye[caster_cell]; - float ye1 = client->ye[target_cell]; - - float phase1_duration = 0.4; - - if (t <= phase1_duration) { - float progress = t / phase1_duration; - DrawLineEx((Vector2){xe0, ye0-client->ch}, (Vector2){xe1, ye1-client->ch}, 4, - (Color){50, 200, 50, 125 + 125 * sin(progress * 10)}); - } else { - Entity* target = env->cell_to_entity[target_cell]; - if (target) { - add_animation_text(env, client, TextFormat("-%i HP", spell->damage), - target_cell, COLOR_HEALTH, 20, 1.2); - } - return true; - } - return false; -} -Spell create_spell_wind_arrow() { - Spell spell; - spell.name = "Wind Arrow"; - spell.ap_cost = 4; - spell.cooldown = 0; - spell.remaining_cooldown = 0; - spell.line_of_sight = true; - spell.cast_in_line = true; - spell.range = 999; - spell.damage = 400; - spell.aoe_range = 0; - spell.effect = spell_wind_arrow; - spell.render_animation = spell_wind_arrow_anim; - return spell; -} - -void spell_swift_rabbit(Tactical* env, Entity* caster, int target_cell, Spell* spell) { - Entity* target = env->cell_to_entity[target_cell]; - if (target) { - target->movement_points_current += 5; - compute_movement(env, target); - } -} -bool spell_swift_rabbit_anim(Tactical* env, Client* client, int caster_cell, int target_cell, float t, Spell* spell) { - Entity* target = env->cell_to_entity[target_cell]; - if (target && spell->animation_state == 0) { - add_animation_text(env, client, "+5 MP", - target_cell, COLOR_MOVEMENT_POINTS, 20, 1.2); - } - spell->animation_state = 1; - - float phase1_duration = 0.3; - - if (t <= phase1_duration) { - DrawCircle(client->xe[caster_cell], client->ye[caster_cell], t * 3000, (Color){0, 255, 0, 255 - t/0.3*255}); - } else { - return true; - } - return false; -} -Spell create_spell_swift_rabbit() { - Spell spell; - spell.name = "Swift Rabbit"; - spell.ap_cost = 2; - spell.cooldown = 4; - spell.remaining_cooldown = 0; - spell.line_of_sight = true; - spell.cast_in_line = false; - spell.range = 0; - spell.damage = 0; - spell.aoe_range = 0; - spell.effect = spell_swift_rabbit; - spell.render_animation = spell_swift_rabbit_anim; - return spell; -} - - -void assign_spells(Entity* entity) { - // TODO assign different spells based on class - entity->spell_count = 5; - entity->spells = malloc(entity->spell_count * sizeof(Spell)); - entity->spells[0] = create_spell_explosive_arrow(); - entity->spells[1] = create_spell_flying_arrow(); - entity->spells[2] = create_spell_rooting_arrow(); - entity->spells[3] = create_spell_wind_arrow(); - entity->spells[4] = create_spell_swift_rabbit(); -} - -void compute_observations(Tactical* env) { - -} - -void compute_movement(Tactical* env, Entity* entity) { - // Do a BFS from the entity's current position to find all reachable cells - // within a distance of the entity's available movement points. - // Store the result in env->movement_path, where each reachable cell - // points to the previous cell in the path, and in env->movement_distance, - // where each reachable cell stores the distance to the player (or -1 if unreachable). - - // reset arrays - for (int i = 0; i < env->map_size; ++i) { - env->movement_path[i] = 0; - env->movement_distance[i] = -1; - } - - // compute walkable cells mask - bool* walkable_cells = calloc(env->map_size, sizeof(bool)); - for (int i = 0; i < env->map_size; ++i) { - // set ground cells to be walkable (TODO this should be pre-computed) - if (env->map[i] == CELL_GROUND) { - walkable_cells[i] = true; - } - // set all cells with entities to be non-walkable (TODO this should be updated whenever an entity moves or is added/removed) - for (int j = 0; j < env->n_entities; ++j) { - const unsigned int cell = env->entities[j].cell; - walkable_cells[cell] = false; - } - } - - // TODO these can be calloc'ed once and reused (memset them to 0 each time this function is called) - // EDIT: no, don't use memset for arrays of int, dangerous - int* queue = calloc(env->map_size, sizeof(int)); - int* visited = calloc(env->map_size, sizeof(int)); - int* distances = calloc(env->map_size, sizeof(int)); - int front = 0; - int rear = 0; - - // TODO can be static - const int next_row_delta[4] = {1, -1, 0, 0}; - const int next_col_delta[4] = {0, 0, 1, -1}; - - int start_pos = entity->cell; - queue[rear++] = start_pos; - visited[start_pos] = 1; - distances[start_pos] = 0; - - while (front < rear) { - int current = queue[front++]; - int row = current / env->map_width; - int col = current % env->map_width; - int current_distance = distances[current]; - - if (current_distance >= entity->movement_points_current) - continue; - - // explore neighbors - for (int i = 0; i < 4; ++i) { - int next_row = row + next_row_delta[i]; - int next_col = col + next_col_delta[i]; - - // boundary check - if (next_row < 0 || next_col < 0 || next_row >= env->map_height || next_col >= env->map_width) - continue; - - int next = next_row * env->map_width + next_col; - - // skip if already visited or not a ground cell - if (visited[next] || !walkable_cells[next]) - continue; - - // mark as visited and record distance - visited[next] = 1; - distances[next] = current_distance + 1; - env->movement_path[next] = current; // store previous cell in the path - env->movement_distance[next] = distances[next]; // store previous cell in the path - - // enqueue neighbor - queue[rear++] = next; - } - } - - // cleanup - free(queue); - free(visited); - free(distances); - free(walkable_cells); -} - -void move_entity(Tactical* env, Entity* entity, const int cell) { - env->cell_to_entity[entity->cell] = NULL; - entity->cell = cell; - env->cell_to_entity[entity->cell] = entity; - entity->movement_points_current -= env->movement_distance[cell]; - compute_movement(env, entity); -} - -bool try_move_entity(Tactical* env, Entity* entity, const int cell) { - // TODO i don't like this. Checks should be in game logic, not client. - if (env->movement_path[cell]) { - move_entity(env, entity, cell); - return true; - } - return false; -} - -Tactical* init_tactical() { - Tactical* env = calloc(1, sizeof(Tactical)); - - env->num_agents = 1; - - env->rewards = calloc(env->num_agents, sizeof(float)); - env->observations = calloc(env->num_agents*121*121*4, sizeof(unsigned char)); - env->actions = calloc(env->num_agents*1, sizeof(int)); - - // init map - int map_id = 3; // ok - char* map_str = get_map(map_id); - env->map_height = get_map_height(map_id); - env->map_width = get_map_width(map_id); - env->map_size = env->map_height * env->map_width; - env->map = calloc(env->map_height * env->map_width, sizeof(unsigned int)); - for (int i = 0; i < env->map_height; i++) { - for (int j = 0; j < env->map_width; j++) { - int idx = i * env->map_width + j; - switch (map_str[idx]) { - case '-': env->map[idx] = CELL_EMPTY; break; - case '.': env->map[idx] = CELL_GROUND; break; - case '|': env->map[idx] = CELL_HOLE; break; - case '#': env->map[idx] = CELL_WALL; break; - default: printf("Invalid map character <%c> at row <%i> and column <%i>\n", map1[idx], i, j); exit(1); - } - } - } - - env->cell_to_entity = (Entity**)calloc(env->map_size, sizeof(Entity*)); - - // init players - env->entities = calloc(2, sizeof(Entity)); - env->n_entities = 2; - env->player1 = &env->entities[0]; - env->player2 = &env->entities[1]; - - env->player1->name = "Player 1"; - env->player1->cell = get_cell(env, 17, 13); - env->player1->color = COLOR_PLAYER1; - env->player1->health_points_total = 2500; - env->player1->action_points_total = 12; - env->player1->movement_points_total = 6; - env->player1->health_points_current = 2500; - env->player1->action_points_current = 12; - env->player1->movement_points_current = 6; - - env->player2->name = "Player 2"; - env->player2->cell = get_cell(env, 12, 13); - env->player2->color = COLOR_PLAYER2; - env->player2->health_points_total = 2500; - env->player2->action_points_total = 12; - env->player2->movement_points_total = 6; - env->player2->health_points_current = 2500; - env->player2->action_points_current = 12; - env->player2->movement_points_current = 6; - - env->cell_to_entity[env->player1->cell] = env->player1; - env->cell_to_entity[env->player2->cell] = env->player2; - - assign_spells(env->player1); - assign_spells(env->player2); - - // // define a class - // Class warrior = { - // "Warrior", - // (Spell[]){push}, // List of spells - // 1 // Spell count - // }; - - env->current_player_idx = 0; - env->current_player = &env->entities[env->current_player_idx]; - - env->movement_path = calloc(env->map_size, sizeof(unsigned int)); - env->movement_distance = calloc(env->map_size, sizeof(int)); - compute_movement(env, env->current_player); - - return env; -} - -void next_player(Tactical* env) { - // reset current player AP and MP - env->current_player->movement_points_current = env->current_player->movement_points_total; - env->current_player->action_points_current = env->current_player->action_points_total; - // decrease current player cooldowns - update_cooldowns(env->current_player); - // switch to next player - env->current_player_idx = (env->current_player_idx + 1) % env->n_entities; - env->current_player = &env->entities[env->current_player_idx]; - compute_movement(env, env->current_player); -} - -void c_reset(Tactical* env) { - compute_observations(env); -} - -int c_step(Tactical* env) { - if (false) { - c_reset(env); - int winner = 2; - return winner; - } - - compute_observations(env); - return 0; -} - - -//////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////// RENDERING /////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - - -Client* init_client(Tactical* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = 1200; - client->height = 900; - - client->movement_cells = malloc(env->map_size * sizeof(bool)); - client->spell_cells = malloc(env->map_size * sizeof(bool)); - client->active_spell_cells = malloc(env->map_size * sizeof(bool)); - client->active_spell = NULL; - - // TODO fill the screen automatically (these are hardcoded for map 2) - client->cw = 80; - client->ch = client->cw / 2; - client->offset_x = 560; - client->offset_y = -200; - client->dy = client->ch * 0.4; - - client->mcell = -1; - client->mcell_type = -1; - - client->move_anim_path = calloc(env->map_size, sizeof(int)); - client->move_anim_cells_per_second = 6; - - client->text_anim_count = 0; - - client->spell_anim = NULL; - - client->xa = calloc(env->map_size, sizeof(float)); - client->xb = calloc(env->map_size, sizeof(float)); - client->xc = calloc(env->map_size, sizeof(float)); - client->xd = calloc(env->map_size, sizeof(float)); - client->xe = calloc(env->map_size, sizeof(float)); - client->ya = calloc(env->map_size, sizeof(float)); - client->yb = calloc(env->map_size, sizeof(float)); - client->yc = calloc(env->map_size, sizeof(float)); - client->yd = calloc(env->map_size, sizeof(float)); - client->ye = calloc(env->map_size, sizeof(float)); - for (int row = 0; row < env->map_height; ++row) { - for (int col = 0; col < env->map_width; ++col) { - int cell = get_cell(env, row, col); - client->xa[cell] = client->offset_x + 0.5 * client->cw * (col - row); - client->xb[cell] = client->xa[cell] - client->cw / 2; - client->xc[cell] = client->xa[cell] + client->cw / 2; - client->xd[cell] = client->xa[cell]; - client->xe[cell] = client->xa[cell]; - client->ya[cell] = client->offset_y + 0.5 * client->ch * (col + row + 2); - client->yb[cell] = client->ya[cell] + client->ch / 2; - client->yc[cell] = client->ya[cell] + client->ch / 2; - client->yd[cell] = client->ya[cell] + client->ch; - client->ye[cell] = client->yb[cell]; - } - } - - client->last_render_time = clock(); - client->dt = 0.0f; - client->max_fps = 120; - - InitWindow(client->width, client->height, "Tactical RL"); - SetTargetFPS(60); - - return client; -} - -int get_cell_at_cursor(Client* client, Tactical* env) { - // to get the formula: we know that cell (row, col) starts at coordinates - // x = offset_x + 0.5 * cw * (col - row); - // y = offset_y + 0.5 * ch * (col + row + 2); - // solve this 2x2 linear system to write (row, col)) as a function of (x, y) and we get the formulas below - const int mx = GetMouseX(); - const int my = GetMouseY(); - const int mrow = floor((my - client->offset_y) / client->ch - (mx - client->offset_x) / client->cw - 1); - const int mcol = floor((my - client->offset_y) / client->ch + (mx - client->offset_x) / client->cw - 1); - client->mx = mx; - client->my = my; - client->mrow = mrow; - client->mcol = mcol; - const int mcell = (mrow < 0 || mcol < 0 || mrow >= env->map_height || mcol >= env->map_width) ? -1 : get_cell(env, mrow, mcol); - return mcell; -} - -void draw_debug_info(Client* client) { - //DrawText(TextFormat("%i FPS", (int)(1 / client->dt)), 10, 10, 20, COLOR_TEXT_DEFAULT); - DrawText(TextFormat("Mouse: %i, %i", client->mx, client->my), 150, 10, 15, COLOR_TEXT_DEFAULT); - DrawText(TextFormat("Cell: %i (row %i, col %i)", client->mcell, client->mrow, client->mcol), 150, 30, 15, COLOR_TEXT_DEFAULT); - DrawText(TextFormat("Cell type: %s", - client->mcell_type == CELL_EMPTY ? "EMPTY" : - client->mcell_type == CELL_GROUND ? "GROUND" : - client->mcell_type == CELL_HOLE ? "HOLE" : - client->mcell_type == CELL_WALL ? "WALL" : - client->mcell_type == -1 ? "NONE" : "UNKNOWN"), 150, 45, 15, COLOR_TEXT_DEFAULT); -} - -void draw_player(Client* client, Entity* player, float x, float y, Color color, bool is_current_player, Color cell_color) { - // draw the little guy - if (is_current_player) DrawEllipse(x, y, 0.33 * client->cw, 0.33 * client->ch, COLOR_ACTIVE_PLAYER); - DrawEllipse(x, y, 0.3 * client->cw, 0.3 * client->ch, color); - DrawEllipse(x, y, 0.23 * client->cw, 0.23 * client->ch, cell_color); - DrawLineEx((Vector2){x - 0.1 * client->cw, y}, (Vector2){x, y - 0.7 * client->ch}, 2, color); - DrawLineEx((Vector2){x + 0.1 * client->cw, y}, (Vector2){x, y - 0.7 * client->ch}, 2, color); - DrawLineEx((Vector2){x, y - 0.7 * client->ch}, (Vector2){x, y - 1.1 * client->ch}, 2, color); - DrawLineEx((Vector2){x, y - 1.0 * client->ch}, (Vector2){x - 0.2 * client->cw, y - 0.8 * client->ch}, 2, color); - DrawLineEx((Vector2){x, y - 1.0 * client->ch}, (Vector2){x + 0.2 * client->cw, y - 0.8 * client->ch}, 2, color); - DrawCircle(x, y - 1.3 * client->ch, 0.2 * client->ch, color); - - // draw hp, ap and mp above the little guy - DrawText(TextFormat("%i", player->health_points_current), - x - MeasureText(TextFormat("%i", player->health_points_current), 15) / 2, y - 2.0 * client->ch, 15, COLOR_HEALTH); - DrawText(TextFormat("%i", player->action_points_current), - x - MeasureText(TextFormat("%i", player->action_points_current), 15) - 4, y - 2.4 * client->ch, 15, COLOR_ACTION_POINTS); - DrawText(TextFormat("%i", player->movement_points_current), - x + 4, y - 2.4 * client->ch, 15, COLOR_MOVEMENT_POINTS); -} - -void draw_cells_and_entities(Client* client, Tactical* env) { - // draw isometric cells - // (ground) - // a - // b e c (b<->c = cw) - // d - // (a<->d = ch) - - // first draw ground cells - for (int cell = 0; cell < env->map_size; ++cell) { - int cell_type = env->map[cell]; - if (cell_type == CELL_GROUND) { - // draw isometric cell (a, b, c, d) - Color cell_color = COLOR_CELL_GROUND; - if (client->movement_cells[cell]) { - cell_color = COLOR_CELL_MOVE; - } else if (client->active_spell) { - if (client->active_spell_cells[cell]) { - cell_color = COLOR_CELL_ACTIVE_SPELL; - } else if (client->spell_cells[cell]) { - cell_color = COLOR_CELL_SPELL; - } else { - cell_color = COLOR_CELL_INACTIVE_SPELL; - } - } - // DrawTriangleStrip((Vector2[]){{xa, ya}, {xb, yb}, {xc, yc}, {xd, yd}}, 4, cell_color); - DrawTriangleStrip((Vector2[]){ - {client->xa[cell], client->ya[cell]}, - {client->xb[cell], client->yb[cell]}, - {client->xc[cell], client->yc[cell]}, - {client->xd[cell], client->yd[cell]}}, 4, cell_color); - if (client->movement_cells[cell]) { - const unsigned int dist = env->movement_distance[cell]; - const char* text = TextFormat("%i", dist); - DrawText(text, - client->xe[cell] - MeasureText(text, 12) / 2, - client->ye[cell] - 6, 12, COLOR_CELL_MOVE_TEXT); - } - // draw white border around cell - DrawLineStrip((Vector2[]){ - {client->xa[cell], client->ya[cell]}, - {client->xb[cell], client->yb[cell]}, - {client->xd[cell], client->yd[cell]}, - {client->xc[cell], client->yc[cell]}, - {client->xa[cell], client->ya[cell]}}, 5, COLOR_CELL_BORDER); - } - } - - // then draw walls and entities alternatively, from top-left to bottom-right, for correct z-order - bool draw_horizontally = true; // draw row by row, from top-left to bottom-right (if false: column by column) - if (client->move_anim_entity) { - int col = get_col(env, client->move_anim_path[client->move_anim_path_idx]); - int col_next = get_col(env, client->move_anim_path[client->move_anim_path_idx + 1]); - if (col == col_next) { - draw_horizontally = false; // this is all for correct depth (z-order) rendering - } - } - for (int i = 0; i < env->map_size; ++i) { - int row, col; - if (draw_horizontally) { - row = i / env->map_width; - col = i % env->map_width; - } else { - row = i % env->map_height; - col = i / env->map_height; - } - int cell = get_cell(env, row, col); - int cell_type = env->map[cell]; - if (cell_type == CELL_WALL) { - // draw isometric cell (a, b, c, d) shifted up by dy ("grass") - DrawTriangleStrip((Vector2[]){ - {client->xa[cell], client->ya[cell] - client->dy}, - {client->xb[cell], client->yb[cell] - client->dy}, - {client->xc[cell], client->yc[cell] - client->dy}, - {client->xd[cell], client->yd[cell] - client->dy}}, 4, COLOR_CELL_GRASS); - // draw connections between (a, b, c, d) and the shifted up cell ("dirt") - DrawTriangleStrip((Vector2[]){ - {client->xc[cell], client->yc[cell]}, - {client->xc[cell], client->yc[cell] - client->dy}, - {client->xd[cell], client->yd[cell]}, - {client->xd[cell], client->yd[cell] - client->dy}, - {client->xb[cell], client->yb[cell]}, - {client->xb[cell], client->yb[cell] - client->dy}}, 6, COLOR_CELL_DIRT); - } - - // draw entity at cell (if any) - Color cell_color = COLOR_CELL_GROUND; - if (client->movement_cells[cell]) { - cell_color = COLOR_CELL_MOVE; - } else if (client->active_spell && client->spell_cells[cell]) { - cell_color = cell == client->mcell ? COLOR_CELL_ACTIVE_SPELL : COLOR_CELL_SPELL; - } - Entity* entity = env->cell_to_entity[cell]; - if (entity && entity != client->move_anim_entity) { - draw_player(client, entity, - client->xe[cell], client->ye[cell], - entity->color, entity == env->current_player, cell_color); - } - // if entity is under move animation, handle it differently - if (client->move_anim_entity && client->move_anim_path[client->move_anim_path_idx] == cell) { - draw_player(client, client->move_anim_entity, - client->xe[cell] + client->move_anim_dx, client->ye[cell] + client->move_anim_dy, - client->move_anim_entity->color, client->move_anim_entity == env->current_player, cell_color); - } - } -} - -void draw_player_dashboard(Client* client, Entity* dashboard_entity, bool is_current_player) { - // Health, action points, movement points - DrawText(dashboard_entity->name, 40, client->height - 150, 25, - is_current_player ? COLOR_ENTITY_NAME : COLOR_ENTITY_NAME_HOVER); - DrawText(TextFormat("HP: %i / %i", dashboard_entity->health_points_current, dashboard_entity->health_points_total), - 40, client->height - 120, 25, COLOR_HEALTH); - DrawText(TextFormat("AP: %i / %i", dashboard_entity->action_points_current, dashboard_entity->action_points_total), - 40, client->height - 90, 25, COLOR_ACTION_POINTS); - DrawText(TextFormat("MP: %i / %i", dashboard_entity->movement_points_current, dashboard_entity->movement_points_total), - 40, client->height - 60, 25, COLOR_MOVEMENT_POINTS); - - // Spells - DrawText("Spells", 300, client->height - 150, 20, COLOR_TEXT_DEFAULT); - for (int i = 0; i < dashboard_entity->spell_count; ++i) { - Spell* spell = &dashboard_entity->spells[i]; - bool cooldown = spell->remaining_cooldown > 0; - bool spell_active = !cooldown && dashboard_entity->action_points_current >= spell->ap_cost; - DrawText(TextFormat("[%i]", i+1), 300, client->height - 125 + i * 20, 20, spell_active ? COLOR_SPELL : COLOR_SPELL_COOLDOWN); - DrawText(TextFormat("(%i AP)", spell->ap_cost), 300 + 30, client->height - 125 + i * 20, 20, cooldown ? COLOR_SPELL_COOLDOWN : COLOR_ACTION_POINTS); - if (spell->remaining_cooldown > 0) { - DrawText(TextFormat("%s (cooldown: %i)", spell->name, spell->remaining_cooldown), - 300 + 100, client->height - 125 + i * 20, 20, COLOR_SPELL_COOLDOWN); - } else { - DrawText(spell->name, - 300 + 100, client->height - 125 + i * 20, 20, spell_active ? COLOR_SPELL : COLOR_SPELL_COOLDOWN); - } - } -} - -void update_animation_move(Tactical* env, Client* client) { - client->move_anim_progress += client->dt * client->move_anim_cells_per_second; - if (client->move_anim_progress >= 1) { - client->move_anim_progress = fmod(client->move_anim_progress, 1); - client->move_anim_path_idx += 1; - } - if (client->move_anim_path_idx == client->move_anim_path_length) { - // reached last cell: stop animation - client->move_anim_entity = NULL; - } else { - int current_cell = client->move_anim_path[client->move_anim_path_idx]; - int next_cell = client->move_anim_path[client->move_anim_path_idx + 1]; - int current_row = get_row(env, current_cell); - int next_row = get_row(env, next_cell); - int current_col = get_col(env, current_cell); - int next_col = get_col(env, next_cell); - int move_dx, move_dy; - if (next_row == current_row + 1) { - move_dx = -1; - move_dy = 1; - } else if (next_row == current_row - 1) { - move_dx = 1; - move_dy = -1; - } else if (next_col == current_col + 1) { - move_dx = 1; - move_dy = 1; - } else if (next_col == current_col - 1) { - move_dx = -1; - move_dy = -1; - } else { - // should be an impossible case - move_dx = 0; - move_dy = 0; - } - client->move_anim_dx = client->move_anim_progress * move_dx * client->cw * 0.5; - client->move_anim_dy = client->move_anim_progress * move_dy * client->ch * 0.5; - } -} - -void add_animation_text(Tactical* env, Client* client, const char* text, int cell, Color color, int font_size, float duration) { - int idx = client->text_anim_count; - if (idx < MAX_TEXT_ANIMATIONS) { - client->text_anim_texts[idx] = malloc(strlen(text) + 1); - strcpy(client->text_anim_texts[idx], text); - - client->text_anim_x0[idx] = client->xe[cell] - MeasureText(text, font_size) / 2; - client->text_anim_y0[idx] = client->ye[cell] - 2.5 * client->ch; - client->text_anim_x1[idx] = client->text_anim_x0[idx]; - client->text_anim_y1[idx] = client->text_anim_y0[idx] - 3 * client->ch; - - client->text_anim_progress[idx] = 0; - client->text_anim_duration[idx] = duration; - client->text_anim_color[idx] = color; - client->text_anim_font_size[idx] = font_size; - client->text_anim_count++; - } else { - printf("client->text_anim_texts array is full, cannot add more strings"); - } -} - -void remove_animation_text(Client* client, int index) { - if (index < 0 || index >= client->text_anim_count) { - printf("Invalid index in remove_animation_text\n"); - return; - } - - free(client->text_anim_texts[index]); - - // shift all strings after the removed index to the left - for (int i = index; i < client->text_anim_count - 1; i++) { - client->text_anim_texts[i] = client->text_anim_texts[i + 1]; - client->text_anim_x0[i] = client->text_anim_x0[i + 1]; - client->text_anim_x1[i] = client->text_anim_x1[i + 1]; - client->text_anim_y0[i] = client->text_anim_y0[i + 1]; - client->text_anim_y1[i] = client->text_anim_y1[i + 1]; - client->text_anim_progress[i] = client->text_anim_progress[i + 1]; - client->text_anim_duration[i] = client->text_anim_duration[i + 1]; - client->text_anim_color[i] = client->text_anim_color[i + 1]; - client->text_anim_font_size[i] = client->text_anim_font_size[i + 1]; - } - client->text_anim_count--; -} - -void update_animations_text(Client* client) { - for (int i = client->text_anim_count - 1; i >= 0; --i) { - // backward loop because of the way strings are shifted in remove_animation_text - client->text_anim_progress[i] += client->dt / client->text_anim_duration[i]; - if (client->text_anim_progress[i] >= 1) { - remove_animation_text(client, i); - } - } -} - -void draw_animations_text(Client* client) { - for (int i = 0; i < client->text_anim_count; ++i) { - float t = client->text_anim_progress[i]; - float x = (1 - t) * client->text_anim_x0[i] + t * client->text_anim_x1[i]; - float y = (1 - t) * client->text_anim_y0[i] + t * client->text_anim_y1[i]; - Color color = client->text_anim_color[i]; - color.a = (int)((1 - t) * 255); - DrawText(client->text_anim_texts[i], x, y, client->text_anim_font_size[i], color); - } -} - -void update_animation_spell(Client* client) { - client->spell_anim_progress += client->dt; -} - -void draw_animation_spell(Tactical* env, Client* client) { - if (client->spell_anim) { - bool finished = client->spell_anim->render_animation( - env, - client, - client->spell_anim_caster_cell, - client->spell_anim_target_cell, - client->spell_anim_progress, - client->spell_anim - ); - if (finished) { - client->spell_anim = NULL; - } - } -} - -int c_render(Tactical* env) { - if (IsKeyDown(KEY_Q) || IsKeyDown(KEY_BACKSPACE)) { - return 1; // close window - } - - if (env->client == NULL) { - env->client = init_client(env); - } - - Client* client = env->client; - // cap FPS and compute dt - /* - clock_t current_time; - do { - current_time = clock(); - client->dt = (double)(current_time - client->last_render_time) / CLOCKS_PER_SEC; - } while (client->dt < 1 / client->max_fps); - client->last_render_time = current_time; - */ - client->dt = 1.0 / 60.0; - - BeginDrawing(); - ClearBackground(COLOR_BACKGROUND); - - int cursor = MOUSE_CURSOR_DEFAULT; - - // get current cell at cursor position (if any), and draw debug info - client->mcell = get_cell_at_cursor(client, env); - client->mcell_type = client->mcell == -1 ? -1 : env->map[client->mcell]; - draw_debug_info(client); - - const int mcell = client->mcell; - // movement path display, if applicable ; and spells - memset(client->movement_cells, 0, env->map_size * sizeof(bool)); - if (client->spell_anim) { - update_animation_spell(client); - } else if (client->active_spell) { - if (mcell != -1 && client->spell_cells[mcell]) { - cursor = MOUSE_CURSOR_POINTING_HAND; - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - cast_spell(env, env->current_player, client->active_spell, mcell); - add_animation_text(env, client, TextFormat("-%i AP", client->active_spell->ap_cost), - env->current_player->cell, COLOR_ACTION_POINTS, 20, 1.2); - client->spell_anim = client->active_spell; - client->spell_anim_caster_cell = env->current_player->cell; - client->spell_anim_target_cell = mcell; - client->spell_anim_progress = 0.0f; - client->active_spell = NULL; - } - } else { - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - client->active_spell = NULL; - } - } - } else { - if (mcell != -1 && env->movement_path[mcell] && !client->move_anim_entity) { - int cell = mcell; - int path_length = env->movement_distance[mcell]; - for (int i = path_length; i >= 0; --i) { - if (i != 0) { - client->movement_cells[cell] = true; - } - client->move_anim_path[i] = cell; // precompute in case it's used, cause after moving the env->movement_path is no longer valid - cell = env->movement_path[cell]; - } - cursor = MOUSE_CURSOR_POINTING_HAND; - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - add_animation_text(env, client, TextFormat("-%i MP", path_length), - env->current_player->cell, COLOR_MOVEMENT_POINTS, 20, 1.2); - - if (try_move_entity(env, env->current_player, mcell)) { - // start move animation - client->move_anim_entity = env->current_player; - client->move_anim_path_idx = 0; - client->move_anim_path_length = path_length; - client->move_anim_progress = 0; - client->move_anim_dx = 0; - client->move_anim_dy = 0; - } - } - } - } - - if (client->move_anim_entity) { - update_animation_move(env, client); - } - update_animations_text(client); - - // KEYS - - if (IsKeyPressed(KEY_SPACE)) { - client->active_spell = NULL; - next_player(env); - } - - int tentative_spell_id = -1; - if (IsKeyPressed(KEY_ONE)) tentative_spell_id = 0; - else if (IsKeyPressed(KEY_TWO)) tentative_spell_id = 1; - else if (IsKeyPressed(KEY_THREE)) tentative_spell_id = 2; - else if (IsKeyPressed(KEY_FOUR)) tentative_spell_id = 3; - else if (IsKeyPressed(KEY_FIVE)) tentative_spell_id = 4; - - if (tentative_spell_id >= 0 && tentative_spell_id < env->current_player->spell_count) { - Spell* spell = &env->current_player->spells[tentative_spell_id]; - if (spell->remaining_cooldown == 0 && env->current_player->action_points_current >= spell->ap_cost) { - client->active_spell = spell; - - // COMPUTE LINES OF SIGHT (this should be in Env, not client) - // set all ground cells to 1 by default (within castable range of the spell) - memset(client->spell_cells, 0, env->map_size * sizeof(bool)); - for (int delta_row = -spell->range; delta_row <= spell->range; ++delta_row) { - for (int delta_col = -(spell->range - abs(delta_row)); delta_col <= spell->range - abs(delta_row); ++delta_col) { - int cell = get_cell_with_delta(env, env->current_player->cell, delta_row, delta_col); - if (client->active_spell->cast_in_line && delta_row != 0 && delta_col != 0) continue; - if (cell != -1 && env->map[cell] == CELL_GROUND) { - client->spell_cells[cell] = true; - } - } - } - - if (spell->line_of_sight) { - // Bresenham line algorithm - for (int i = 0; i < env->map_size; ++i) { - if (!client->spell_cells[i]) continue; - int x0 = get_col(env, env->current_player->cell); - int y0 = get_row(env, env->current_player->cell); - int x1 = get_col(env, i); - int y1 = get_row(env, i); - int dx = x1 - x0; - int dy = y1 - y0; - int nx = abs(dx); - int ny = abs(dy); - int sign_x = dx > 0 ? 1 : -1; - int sign_y = dy > 0 ? 1 : -1; - int px = x0; - int py = y0; - int ix = 0; - int iy = 0; - while (ix < nx || iy < ny) { - int new_cell = get_cell(env, py, px); - if (new_cell != -1 && new_cell != env->current_player->cell && (env->map[new_cell] == CELL_WALL || env->cell_to_entity[new_cell] != NULL)) { - client->spell_cells[i] = false; - break; - } - int decision = (1 + 2 * ix) * ny - (1 + 2 * iy) * nx; - if (decision == 0) { - // next step is diagonal - px += sign_x; - py += sign_y; - ix += 1; - iy += 1; - } else if (decision < 0) { - // next step is horizontal - px += sign_x; - ix += 1; - } else { - // next step is vertical - py += sign_y; - iy += 1; - } - } - } - } - } - } - memset(client->active_spell_cells, 0, env->map_size * sizeof(bool)); - if (client->active_spell && client->spell_cells[mcell]) { - Spell* spell = client->active_spell; - for (int delta_row = -spell->aoe_range; delta_row <= spell->aoe_range; ++delta_row) { - for (int delta_col = -(spell->aoe_range - abs(delta_row)); delta_col <= spell->aoe_range - abs(delta_row); ++delta_col) { - int cell = get_cell_with_delta(env, mcell, delta_row, delta_col); - if (env->map[cell] == CELL_GROUND) { - client->active_spell_cells[cell] = true; - } - } - } - } - - if (IsKeyPressed(KEY_ESCAPE)) { - client->active_spell = NULL; - } - - draw_cells_and_entities(client, env); - draw_animation_spell(env, client); - draw_animations_text(client); - - // Write info about keys - DrawText("Press Q or Backspace to exit", 600, 10, 15, COLOR_TEXT_DEFAULT); - DrawText("Press the corresponding key [1-5] to cast a spell", 600, 25, 15, COLOR_TEXT_DEFAULT); - DrawText("Press Space to skip turn", 600, 40, 15, COLOR_TEXT_DEFAULT); - - // Draw player dashboard (health, action points, movement points, spells) - Entity* dashboard_entity = env->current_player; - if (client->mcell != -1 && env->cell_to_entity[client->mcell] && env->cell_to_entity[client->mcell] != env->current_player) { - dashboard_entity = env->cell_to_entity[mcell]; - cursor = MOUSE_CURSOR_POINTING_HAND; - } - draw_player_dashboard(client, dashboard_entity, dashboard_entity == env->current_player); - - SetMouseCursor(cursor); - - EndDrawing(); - return 0; -} - -void close_client(Client* client) { - CloseWindow(); - free(client->movement_cells); - free(client); -} diff --git a/pufferlib/ocean/tactical/tactical.py b/pufferlib/ocean/tactical/tactical.py deleted file mode 100644 index 0ded63cca..000000000 --- a/pufferlib/ocean/tactical/tactical.py +++ /dev/null @@ -1,508 +0,0 @@ -import numpy as np -import gymnasium -import os -#from raylib import rl -#import heapq - -import pufferlib -from pufferlib.ocean.tactical import binding -# from pufferlib.environments.ocean import render - -EMPTY = 0 -GROUND = 1 -HOLE = 2 -WALL = 3 - -MAP_DICT = { - '_': EMPTY, - '.': GROUND, - '|': HOLE, - '#': WALL, -} - - -class Tactical: - def __init__(self, num_envs=200, render_mode='human', seed=0): - self.num_envs = num_envs - self.render_mode = render_mode - - # env spec (TODO) - self.observation_space = gymnasium.spaces.Box( - low=0, high=2, shape=(10,), dtype=np.uint8) - self.action_space = gymnasium.spaces.Discrete(4) - self.single_observation_space = self.observation_space - self.single_action_space = self.action_space - self.num_agents = self.num_envs - self.render_mode = render_mode - self.emulated = None - self.done = False - self.buf = pufferlib.namespace( - observations = np.zeros( - (num_envs, 10), dtype=np.uint8), - rewards = np.zeros(num_envs, dtype=np.float32), - terminals = np.zeros(num_envs, dtype=bool), - truncations = np.zeros(num_envs, dtype=bool), - masks = np.ones(num_envs, dtype=bool), - ) - self.actions = np.zeros(num_envs, dtype=np.uint32) - - self.c_envs = binding.vec_init( - self.buf.observations, - self.actions, - self.buf.rewards, - self.buf.terminals, - self.buf.truncations, - num_envs, - seed, - num_obs=self.buf.observations.shape[1] - ) - - # render - # if render_mode == 'human': - # self.client = RaylibClient() - - # map_path = 'pufferlib/environments/ocean/tactical/map.txt' - # map_path = 'pufferlib/environments/ocean/tactical/map_test.txt' - # print(map_path) - # self.load_map(map_path) - - def load_map(self, filename): - with open(filename, 'r') as f: - self.map_str = [line.strip() for line in f.read().strip().split('\n') if line[0] != ';'] - self.map_width = len(self.map_str[0]) - self.map_height = len(self.map_str) - self.map = np.zeros((self.map_height, self.map_width), dtype=np.uint8) - for i, row in enumerate(self.map_str): - for j, cell in enumerate(row): - self.map[i, j] = MAP_DICT[cell] - - def reset(self, seed=None): - self.c_envs = [] - for i in range(self.num_envs): - self.c_envs.append(binding.vec_init( - self.buf.observations[i], - self.actions[i:i+1], - self.buf.rewards[i:i+1])) - binding.vec_reset(self.c_envs[i]) - - return self.buf.observations, {} - - def step(self, actions): - self.actions[:] = actions - for c_env in self.c_envs: - binding.vec_step(c_env) - - info = {} - - return (self.buf.observations, self.buf.rewards, - self.buf.terminals, self.buf.truncations, info) - - def render(self): - return binding.vec_render(self.c_envs, 0) - # if self.render_mode == 'human': - # return self.client.render(self.map) - - def close(self): - for c_env in self.c_envs: - binding.vec_close(c_env) - -''' -def a_star_search(map, start, goal): - frontier = [] - heapq.heappush(frontier, (0, start)) - - came_from = {} - cost_so_far = {} - came_from[start] = None - cost_so_far[start] = 0 - - while len(frontier) > 0: - current = heapq.heappop(frontier)[1] - - if current == goal: - break - - for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]: - next = (current[0] + dx, current[1] + dy) - if next[0] < 0 or next[1] < 0 or next[0] >= map.shape[0] or next[1] >= map.shape[1] or map[next] != GROUND: - continue - new_cost = cost_so_far[current] + 1 - if next not in cost_so_far or new_cost < cost_so_far[next]: - cost_so_far[next] = new_cost - priority = new_cost + abs(next[0] - goal[0]) + abs(next[1] - goal[1]) - heapq.heappush(frontier, (priority, next)) - came_from[next] = current - - # return came_from, cost_so_far - # reconstruct path - path = [] - if goal not in came_from: # no path was found - return [] - assert current == goal - while current != start: - path.append(current) - current = came_from[current] - # path.append(start) - path.reverse() - return path - - -class RaylibClient: - def __init__(self): - self.screenw = 1200 - self.screenh = 900 - rl.InitWindow(self.screenw, self.screenh, "Puffer Tactical".encode()) - rl.SetTargetFPS(60) - - self.row = 12 - self.col = 12 - - self.anim = False - self.anim_type = None - self.anim_path = None - self.anim_path_progress = None - - self.spell_mode = False - - self.cra_bottom = rl.LoadTexture('pufferlib/environments/ocean/tactical/sacri_bottom.png'.encode()) - self.cra_top = rl.LoadTexture('pufferlib/environments/ocean/tactical/sacri_top.png'.encode()) - self.cra_left = rl.LoadTexture('pufferlib/environments/ocean/tactical/sacri_left.png'.encode()) - self.cra_right = rl.LoadTexture('pufferlib/environments/ocean/tactical/sacri_right.png'.encode()) - self.cra_tex = self.cra_bottom - - def render(self, map): - # TODO : rather than compute isometric coordinates - # could be easier to do all cartesian and use a coordinate conversion (linear algebra, some cos/sin) - # to go back and forth between the two coordinate systems? - # see https://en.wikipedia.org/wiki/Isometric_projection - if rl.IsKeyDown(rl.KEY_ESCAPE): - exit(0) - - if rl.IsKeyDown(rl.KEY_E) and not self.anim: - self.spell_mode = True - if rl.IsKeyDown(rl.KEY_R) and not self.anim: - self.spell_mode = False - - nrows, ncols = map.shape - - # figure out dimensions so the map scales to fit on the screen - - # map width = 14, map height = 16 - # find map width (longest bottomleft-topright diagonal) - - mapw = -1 - for i in range(nrows): - horizontal_line = [map[i-k,k] for k in range(min(i + 1, ncols))] - if set(horizontal_line) == {EMPTY}: continue - i0, i1 = 0, len(horizontal_line) - 1 - while horizontal_line[i0] == EMPTY: i0 += 1 - while horizontal_line[i1] == EMPTY: i1 -= 1 - mapw = max(mapw, i1 - i0 + 1) - maph = -1 - for i in range(ncols): - vertical_line = [map[k,i+k] for k in range(min(ncols - i, nrows))] - if set(vertical_line) == {EMPTY}: continue - i0, i1 = 0, len(vertical_line) - 1 - while vertical_line[i0] == EMPTY: i0 += 1 - while vertical_line[i1] == EMPTY: i1 -= 1 - maph = max(maph, i1 - i0 + 1) - - - padding_top = 100 - padding_bottom = 100 - cw_max = (self.screenw) / mapw - ch_max = (self.screenh - padding_top - padding_bottom) / maph - # we want ch = cw / 2 -> pick the best ratio - if ch_max > cw_max / 2: - cw = cw_max - ch = cw / 2 - else: - ch = ch_max - cw = ch * 2 - - # figure out correct offset to center the game - xmin = 1e9 - ymin = 1e9 - for i, row in enumerate(map): - for j, cell in enumerate(row): - # todo not the most efficient + avoid code repetition - if cell != EMPTY: - xa = 0.5 * (j + 1) * cw - 0.5 * (i + 1) * cw - ya = 0.5 * (j + 1) * ch + 0.5 * (i + 1) * ch - xmin = min(xmin, xa - cw / 2) - ymin = min(ymin, ya) - - # import sys; sys.exit(0) - - offset_x = -xmin + (self.screenw-cw*mapw)/2 # center - offset_y = -ymin + padding_top - # cw = 80 - # ch = cw / 2 - - rl.BeginDrawing() - rl.ClearBackground(render.PUFF_BACKGROUND) - - # get mouse pos - mx, my = rl.GetMouseX(), rl.GetMouseY() - rl.DrawText(f"Mouse: {mx}, {my}".encode(), 15, 10, 20, render.PUFF_TEXT) - # get corresponding cell (if any) - # to get the formula: we know that cell (row, col) = (i, j) starts at coordinates - # x = offset_x + 0.5 * (j + 1) * cw - 0.5 * (i + 1) * cw - # y = offset_y + 0.5 * (j + 1) * ch + 0.5 * (i + 1) * ch - # Solve this to write i and j as a function of x and y and we get the formulas below - ci = int((offset_x - mx) / cw + (my - offset_y) / ch - 1) - cj = int((mx - offset_x) / cw + (my - offset_y) / ch - 1) - cell = None if ci < 0 or cj < 0 or ci >= nrows or cj >= ncols else (ci, cj) - rl.DrawText(f"Cell: {cell}".encode(), 15, 35, 20, render.PUFF_TEXT) - - - # movement - movement = np.zeros_like(map) - - if not self.anim and not self.spell_mode: - if cell is not None: - # draw movement path - path = a_star_search(map, start=(self.row, self.col), goal=(ci, cj)) - if path: - path_rows, path_cols = zip(*path) - movement[path_rows, path_cols] = 1 - - if rl.IsMouseButtonPressed(rl.MOUSE_BUTTON_LEFT): - if cell is not None and map[cell] == GROUND: - # self.row = ci - # self.col = cj - self.anim = True - self.anim_type = 'move' - self.anim_path = [(self.row, self.col)] + path - self.anim_path_progress = 0 - - # line of sight - los = np.ones_like(map) - - for i in range(nrows): - for j in range(ncols): - cell = map[i, j] - if cell != GROUND: - los[i, j] = 0 - elif (i, j) == (self.row, self.col): - los[i, j] = 0 - else: - # use bresenham-based supercover line algorithm - # http://eugen.dedu.free.fr/projects/bresenham/ - # note: bresenham alone doesnt find all cells covered by the lines - # implementation from https://www.redblobgames.com/grids/line-drawing/#supercover (covers all quadrants) <- here it is explained very well, the algo is pretty simple - # now we could precompute this on the map for every pair of points - # the question is: if we add one obstacle, how does it change lines of sight? mb its fast enough to just simulate in real time? - # ONE OTHER APPROACH: for every pair of points, assume one point is the observer and the other is a wall (so, ignoring the geometry of the map). then, what lines of sight do we have? then we just need to do a logical and for all lines of sight. not sure its even faster though, it doesnt seem to be. - # an optimization: instead of doing lines of sight for all pair of points, we could check between observer and all border cells of the map? then, we set all cells to line of sight true and as soon as we hit an obstacle, we'll set all subsequent cells to line of sight false. this should hit all the cells? - # bressenham: check all points between character and (i, j), if any is an obstacle then cancel the line of sight - x0 = self.col - y0 = self.row - x1 = j - y1 = i - ### - dx = x1 - x0 - dy = y1 - y0 - nx = abs(dx) - ny = abs(dy) - sign_x = 1 if dx > 0 else -1 - sign_y = 1 if dy > 0 else -1 - px = x0 - py = y0 - ix = 0 - iy = 0 - while ix < nx or iy < ny: - if map[py, px] == WALL: - los[i, j] = 0 - break - decision = (1 + 2 * ix) * ny - (1 + 2 * iy) * nx - if decision == 0: - # next step is diagonal - px += sign_x - py += sign_y - ix += 1 - iy += 1 - elif decision < 0: - # next step is horizontal - px += sign_x - ix += 1 - else: - # next step is vertical - py += sign_y - iy += 1 - - - - # bool IsMouseButtonPressed(int button); - - - # naive (O(n^3)) for each pair of cell A, B - # we draw the line from the center of cell A to the center of cell B - # then we use bressenham's algo https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm - # to find all the cells that the line goes through - # if any of these is an obstacle, then there is no line of sight between A and B. Otherwise there is. - - # maybe better: for each obstacle, directly find all the cells this obstacle hides and mask them - - - - - # draw cells from top-left to bottom-right - # isometric cell link to bottom link to top - # (ground) 4 - # a a 5 a 6 - # b e c (b<->c = cw) b 0 c b 7 c - # d 1 d 2 d - # (a<->d = ch) 3 - # cell dimensions (as per drawing above) - for i, row in enumerate(map): - for j, cell in enumerate(row): - # compute isometrics coordinates (points a,b,c,d) -- TODO of course all this should be precomputed - xa = offset_x + 0.5 * (j + 1) * cw - 0.5 * (i + 1) * cw - xb, xc, xd = xa - cw / 2, xa + cw / 2, xa - ya = offset_y + 0.5 * (j + 1) * ch + 0.5 * (i + 1) * ch - yb, yc, yd = ya + ch / 2, ya + ch / 2, ya + ch - xe, ye = xa, yb - # draw cell - if cell == WALL: - dy = ch * 0.4 - x4, x5, x6, x7 = xa, xb, xc, xd - y4, y5, y6, y7 = ya - dy, yb - dy, yc - dy, yd - dy - rl.DrawTriangleStrip([(x4, y4), (x5, y5), (x6, y6), (x7, y7)], 4, [163, 197, 69, 255]) # top square - rl.DrawTriangleStrip([(xc, yc), (x6, y6), (xd, yd), (x7, y7), (xb, yb), (x5, y5)], 6, [40, 20, 5, 255]) # connection with ground - elif cell == HOLE: - pass # leave empty, as a hole should be - elif cell == GROUND: - if movement[(i, j)]: - col = [0, 180, 0, 255] - elif self.spell_mode: - #elif abs(i - self.row) + abs(j - self.col) <= 10 and abs(i - self.row) + abs(j - self.col) > 0: - if los[(i, j)]: - if (i, j) == (ci, cj): - col = [255, 165, 0, 255] - else: - col = [68, 109, 153, 255] - else: - col = [112, 123, 111, 255] - else: - col = [189, 205, 125, 255] if (i + j) % 2 == 0 else [180, 195, 118, 255] - rl.DrawTriangleStrip([(xa, ya), (xb, yb), (xc, yc), (xd, yd)], 4, col) - - # draw white border around cell - rl.DrawLineStrip([(xa, ya), (xb, yb), (xd, yd), (xc, yc), (xa, ya)], 5, (255, 255, 255, 255)) - # Draw dirt below the cell - if cell == GROUND or cell == WALL: - # here we only draw what will be seen ; maybe it's faster to draw everything and not do any checks - dy = ch * 0.7 - x0, x1, x2, x3 = xa, xb, xc, xd - y0, y1, y2, y3 = ya + dy, yb + dy, yc + dy, yd + dy - if i == len(map) - 1 or map[i+1,j] in [HOLE, EMPTY]: - rl.DrawTriangleStrip([(xb, yb), (x1, y1), (xd, yd), (x3, y3)], 4, [68, 48, 10, 255]) # left side (b-1-3-d boundary) - if j == len(row) - 1 or map[i,j+1] in [HOLE, EMPTY]: - rl.DrawTriangleStrip([(xd, yd), (x3, y3), (xc, yc), (x2, y2)], 4, [95, 77, 21, 255]) # right side (d-3-2-c boundary) - - - - # draw character - - xe = offset_x + 0.5 * (self.col + 1) * cw - 0.5 * (self.row + 1) * cw - ye = offset_y + 0.5 * (self.col + 1) * ch + 0.5 * (self.row + 1) * ch + ch / 2 - - xe_m = offset_x + 0.5 * (cj + 1) * cw - 0.5 * (ci + 1) * cw - ye_m = offset_y + 0.5 * (cj + 1) * ch + 0.5 * (ci + 1) * ch + ch / 2 - - # 465*1129 - cra_tex_w = 465 - cra_tex_h = 1129 - cra_tex_desired_h = 1.6 * ch - scale = cra_tex_desired_h / cra_tex_h - cra_tex_desired_w = cra_tex_w * scale - cra_x = xe - cra_tex_desired_w / 2 - cra_y = ye - cra_tex_desired_h + 0.1 * ch - - if self.anim and self.anim_type == "move": - # cur is updated when we arrive at the center of a new cell - cur = self.anim_path[int(self.anim_path_progress)] - self.row, self.col = cur - transition_progress = self.anim_path_progress - int(self.anim_path_progress) - if cur == self.anim_path[-1]: - self.anim = False - else: - next = self.anim_path[int(self.anim_path_progress)+1] - # use correct facing of the texture - if next[0] == cur[0] + 1: - self.cra_tex = self.cra_bottom - self.movx, self.movy = -1, 1 - elif next[0] == cur[0] - 1: - self.cra_tex = self.cra_top - self.movx, self.movy = 1, -1 - elif next[1] == cur[1] + 1: - self.cra_tex = self.cra_right - self.movx, self.movy = 1, 1 - elif next[1] == cur[1] - 1: - self.cra_tex = self.cra_left - self.movx, self.movy = -1, -1 - # add a delta to the x,y texture position for continuous movement - delta_x = (transition_progress) * cw * 0.5 * self.movx - delta_y = (transition_progress) * ch * 0.5 * self.movy - self.anim_path_progress += 0.1 - cur = self.anim_path[int(self.anim_path_progress)] - self.row, self.col = cur - else: - delta_x = delta_y = 0 - - coef = 0.35 - thickness = 2 - if self.anim and self.anim_type == 'move': - col = [189, 205, 125, 255] if (self.anim_path[0][0] + self.anim_path[0][1]) % 2 == 0 else [180, 195, 118, 255] - else: - col = [189, 205, 125, 255] if (self.row + self.col) % 2 == 0 else [180, 195, 118, 255] - rl.DrawEllipse(int(xe + delta_x), int(ye + delta_y), cw * coef, ch * coef, [255, 0, 0, 255]) - rl.DrawEllipse(int(xe + delta_x), int(ye + delta_y), cw * coef - thickness, ch * coef - thickness, col) - - rl.DrawTextureEx(self.cra_tex, (cra_x + delta_x, cra_y + delta_y), 0, scale, [255, 255, 255, 255]) - - # void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points - # rl.DrawSplineLinear([(xe, ye), (mx, my)], 10, 5, [255, 0, 0, 255]) - # rl.DrawSplineBezierQuadratic([(xe, ye-cra_tex_desired_h/2), ((xe+mx)/2,(ye+my)/2-200), (mx, my)], 3, 5, [255, 0, 0, 255]) - - if rl.IsMouseButtonPressed(rl.MOUSE_BUTTON_LEFT) and self.spell_mode and los[ci,cj]: - self.anim = True - self.anim_type = "spell" - self.spell_mode = False - - self.anim_path = [(xe, ye-cra_tex_desired_h/2), ((xe+mx)/2,(ye+my)/2-200), (xe_m, ye_m)] - self.anim_path_progress = 0.01 - - if self.anim and self.anim_type == "spell": - self.anim_path_progress += 0.025 - pt = rl.GetSplinePointBezierQuad(*self.anim_path, min(self.anim_path_progress, 1.0)) - - if self.anim_path_progress <= 1.0: - rl.DrawCircle(int(pt.x), int(pt.y), 10, [255, 0, 0, 255]) - else: - rl.DrawCircle(int(pt.x), int(pt.y), 10 + (self.anim_path_progress - 1.0) * 100, [255, 0, 0, int(255 - (self.anim_path_progress - 1.0) * 1200)]) - - if self.anim_path_progress >= 1.2: - self.anim = False - - rl.EndDrawing() - return render.cdata_to_numpy() -''' - - -if __name__ == '__main__': - PROFILE = False - env = Tactical(num_envs=1, render_mode='human') - env.reset() - import time - t0 = time.time() - steps = 0 - while not PROFILE or time.time() - t0 < 10: - env.step([0] * env.num_envs) - if not PROFILE: - if env.render() == 1: # exit code - break - steps += 1 - print('SPS:', 1 * steps / (time.time() - t0)) - diff --git a/pufferlib/ocean/target/binding.c b/pufferlib/ocean/target/binding.c deleted file mode 100644 index f8e7bde78..000000000 --- a/pufferlib/ocean/target/binding.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "target.h" - -#define Env Target -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->num_agents = unpack(kwargs, "num_agents"); - env->num_goals = unpack(kwargs, "num_goals"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/target/target.c b/pufferlib/ocean/target/target.c deleted file mode 100644 index 61a91a9fc..000000000 --- a/pufferlib/ocean/target/target.c +++ /dev/null @@ -1,64 +0,0 @@ -/* Pure C demo file for Target. Build it with: - * bash scripts/build_ocean.sh target local (debug) - * bash scripts/build_ocean.sh target fast - * We suggest building and debugging your env in pure C first. You - * get faster builds and better error messages - */ -#include "target.h" - -/* Puffernet is our lightweight cpu inference library that - * lets you load basic PyTorch model architectures so that - * you can run them in pure C or on the web via WASM - */ -#include "puffernet.h" - -int main() { - int num_agents = 8; - int num_goals = 4; - int num_obs = 2*(num_agents + num_goals) + 4; - - // Weights are exported by running puffer export - Weights* weights = load_weights("resources/target/target_weights.bin", 137743); - - int logit_sizes[2] = {9, 5}; - LinearLSTM* net = make_linearlstm(weights, num_agents, num_obs, logit_sizes, 2); - - Target env = { - .width = 1080, - .height = 720, - .num_agents = num_agents, - .num_goals = num_goals - }; - init(&env); - - // Allocate these manually since they aren't being passed from Python - env.observations = calloc(env.num_agents*num_obs, sizeof(float)); - env.actions = calloc(2*env.num_agents, sizeof(int)); - env.rewards = calloc(env.num_agents, sizeof(float)); - env.terminals = calloc(env.num_agents, sizeof(unsigned char)); - - // Always call reset and render first - c_reset(&env); - c_render(&env); - - // while(True) will break web builds - while (!WindowShouldClose()) { - for (int i=0; i -#include -#include -#include "raylib.h" - -// Required struct. Only use floats! -typedef struct { - float perf; // Recommended 0-1 normalized single real number perf metric - float score; // Recommended unnormalized single real number perf metric - float episode_return; // Recommended metric: sum of agent rewards over episode - float episode_length; // Recommended metric: number of steps of agent episode - // Any extra fields you add here may be exported to Python in binding.c - float n; // Required as the last field -} Log; - -typedef struct { - Texture2D puffer; - Texture2D star; -} Client; - -typedef struct { - float x; - float y; - float heading; - float speed; - int ticks_since_reward; -} Agent; - -typedef struct { - float x; - float y; -} Goal; - -// Required that you have some struct for your env -// Recommended that you name it the same as the env file -typedef struct { - Log log; // Required field. Env binding code uses this to aggregate logs - Client* client; - Agent* agents; - Goal* goals; - float* observations; // Required. You can use any obs type, but make sure it matches in Python! - int* actions; // Required. int* for discrete/multidiscrete, float* for box - float* rewards; // Required - unsigned char* terminals; // Required. We don't yet have truncations as standard yet - int width; - int height; - int num_agents; - int num_goals; -} Target; - -/* Recommended to have an init function of some kind if you allocate - * extra memory. This should be freed by c_close. Don't forget to call - * this in binding.c! - */ -void init(Target* env) { - env->agents = calloc(env->num_agents, sizeof(Agent)); - env->goals = calloc(env->num_goals, sizeof(Goal)); -} - -void update_goals(Target* env) { - for (int a=0; anum_agents; a++) { - Agent* agent = &env->agents[a]; - for (int g=0; gnum_goals; g++) { - Goal* goal = &env->goals[g]; - float dx = (goal->x - agent->x); - float dy = (goal->y - agent->y); - float dist = sqrt(dx*dx + dy*dy); - if (dist > 32) { - continue; - } - goal->x = rand() % env->width; - goal->y = rand() % env->height; - env->rewards[a] = 1.0f; - env->log.perf += 1.0f; - env->log.score += 1.0f; - env->log.episode_length += agent->ticks_since_reward; - agent->ticks_since_reward = 0; - env->log.episode_return += 1.0f; - env->log.n++; - } - } -} - -/* Recommended to have an observation function of some kind because - * you need to compute agent observations in both reset and in step. - * If using float obs, try to normalize to roughly -1 to 1 by dividing - * by an appropriate constant. - */ -void compute_observations(Target* env) { - int obs_idx = 0; - for (int a=0; anum_agents; a++) { - Agent* agent = &env->agents[a]; - for (int g=0; gnum_goals; g++) { - Goal* goal = &env->goals[g]; - env->observations[obs_idx++] = (goal->x - agent->x)/env->width; - env->observations[obs_idx++] = (goal->y - agent->y)/env->height; - } - for (int a=0; anum_agents; a++) { - Agent* other = &env->agents[a]; - env->observations[obs_idx++] = (other->x - agent->x)/env->width; - env->observations[obs_idx++] = (other->y - agent->y)/env->height; - } - env->observations[obs_idx++] = agent->heading/(2*PI); - env->observations[obs_idx++] = env->rewards[a]; - env->observations[obs_idx++] = agent->x/env->width; - env->observations[obs_idx++] = agent->y/env->height; - } -} - -// Required function -void c_reset(Target* env) { - for (int i=0; inum_agents; i++) { - env->agents[i].x = rand() % env->width; - env->agents[i].y = rand() % env->height; - env->agents[i].ticks_since_reward = 0; - } - for (int i=0; inum_goals; i++) { - env->goals[i].x = rand() % env->width; - env->goals[i].y = rand() % env->height; - } - compute_observations(env); -} - -float clip(float val, float min, float max) { - if (val < min) { - return min; - } else if (val > max) { - return max; - } - return val; -} - -// Required function -void c_step(Target* env) { - for (int i=0; inum_agents; i++) { - env->rewards[i] = 0; - Agent* agent = &env->agents[i]; - agent->ticks_since_reward += 1; - - agent->heading += ((float)env->actions[2*i] - 4.0f)/12.0f; - agent->heading = clip(agent->heading, 0, 2*PI); - - agent->speed += 1.0f*((float)env->actions[2*i + 1] - 2.0f); - agent->speed = clip(agent->speed, -20.0f, 20.0f); - - agent->x += agent->speed*cosf(agent->heading); - agent->x = clip(agent->x, 0, env->width); - - agent->y += agent->speed*sinf(agent->heading); - agent->y = clip(agent->y, 0, env->height); - - if (agent->ticks_since_reward % 512 == 0) { - env->agents[i].x = rand() % env->width; - env->agents[i].y = rand() % env->height; - } - } - update_goals(env); - compute_observations(env); -} - -// Required function. Should handle creating the client on first call -void c_render(Target* env) { - if (env->client == NULL) { - InitWindow(env->width, env->height, "PufferLib Target"); - SetTargetFPS(60); - env->client = (Client*)calloc(1, sizeof(Client)); - - // Don't do this before calling InitWindow - env->client->puffer = LoadTexture("resources/shared/puffers_128.png"); - env->client->star = LoadTexture("resources/target/star.png"); - } - - // Standard across our envs so exiting is always the same - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - for (int i=0; inum_goals; i++) { - Goal* goal = &env->goals[i]; - DrawTexture( - env->client->star, - goal->x - 32, - goal->y - 32, - WHITE - ); - } - - for (int i=0; inum_agents; i++) { - Agent* agent = &env->agents[i]; - float heading = agent->heading; - DrawTexturePro( - env->client->puffer, - (Rectangle){ - (heading < PI/2 || heading > 3*PI/2) ? 0 : 128, - 0, 128, 128, - }, - (Rectangle){ - agent->x - 64, - agent->y - 64, - 128, - 128 - }, - (Vector2){0, 0}, - 0, - WHITE - ); - } - - EndDrawing(); -} - -// Required function. Should clean up anything you allocated -// Do not free env->observations, actions, rewards, terminals -void c_close(Target* env) { - free(env->agents); - free(env->goals); - if (env->client != NULL) { - Client* client = env->client; - UnloadTexture(client->puffer); - UnloadTexture(client->star); - CloseWindow(); - free(client); - } -} diff --git a/pufferlib/ocean/target/target.py b/pufferlib/ocean/target/target.py deleted file mode 100644 index f370a15b1..000000000 --- a/pufferlib/ocean/target/target.py +++ /dev/null @@ -1,78 +0,0 @@ -'''A simple sample environment. Use this as a template for your own envs.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.target import binding - -class Target(pufferlib.PufferEnv): - def __init__(self, num_envs=1, width=1080, height=720, num_agents=8, - num_goals=4, render_mode=None, log_interval=128, size=11, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(2*(num_agents+num_goals) + 4,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.MultiDiscrete([9, 5]) - - self.render_mode = render_mode - self.num_agents = num_envs*num_agents - self.log_interval = log_interval - - super().__init__(buf) - c_envs = [] - for i in range(num_envs): - c_env = binding.env_init( - self.observations[i*num_agents:(i+1)*num_agents], - self.actions[i*num_agents:(i+1)*num_agents], - self.rewards[i*num_agents:(i+1)*num_agents], - self.terminals[i*num_agents:(i+1)*num_agents], - self.truncations[i*num_agents:(i+1)*num_agents], - seed, width=width, height=height, - num_agents=num_agents, num_goals=num_goals) - c_envs.append(c_env) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - self.actions[:] = actions - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 512 - - env = Target(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(env.single_action_space.nvec, size=(CACHE, 2)) - - i = 0 - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[i % CACHE]) - steps += env.num_agents - i += 1 - - print('Target SPS:', int(steps / (time.time() - start))) diff --git a/pufferlib/ocean/tcg/build_local.sh b/pufferlib/ocean/tcg/build_local.sh deleted file mode 100644 index f4f6ffa25..000000000 --- a/pufferlib/ocean/tcg/build_local.sh +++ /dev/null @@ -1,3 +0,0 @@ -clang -Wall -Wuninitialized -Wmisleading-indentation -fsanitize=address,undefined,bounds,pointer-overflow,leak -ferror-limit=3 -g -o tcg tcg.c -I./raylib-5.0_linux_amd64/include/ -L./raylib-5.0_linux_amd64/lib/ -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 -DPLATFORM_DESKTOP - - diff --git a/pufferlib/ocean/tcg/build_web.sh b/pufferlib/ocean/tcg/build_web.sh deleted file mode 100644 index 5022b353c..000000000 --- a/pufferlib/ocean/tcg/build_web.sh +++ /dev/null @@ -1 +0,0 @@ -emcc -o build/game.html tcg.c -Os -Wall ./raylib/src/libraylib.a -I./raylib/src -L. -L./raylib/src/libraylib.a -sASSERTIONS=2 -gsource-map -s USE_GLFW=3 -sUSE_WEBGL2=1 -s ASYNCIFY -sFILESYSTEM -s FORCE_FILESYSTEM=1 --shell-file ./raylib/src/minshell.html -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES3 diff --git a/pufferlib/ocean/tcg/tcg.c b/pufferlib/ocean/tcg/tcg.c deleted file mode 100644 index dd3fcec54..000000000 --- a/pufferlib/ocean/tcg/tcg.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "tcg.h" - -int main() { - TCG env = {0}; // MUST ZERO - allocate_tcg(&env); - reset(&env); - - init_client(&env); - - int atn = -1; - while (!WindowShouldClose()) { - if (atn != -1) { - step(&env, atn); - atn = -1; - } - - if (IsKeyPressed(KEY_ONE)) atn = 0; - if (IsKeyPressed(KEY_TWO)) atn = 1; - if (IsKeyPressed(KEY_THREE)) atn = 2; - if (IsKeyPressed(KEY_FOUR)) atn = 3; - if (IsKeyPressed(KEY_FIVE)) atn = 4; - if (IsKeyPressed(KEY_SIX)) atn = 5; - if (IsKeyPressed(KEY_SEVEN)) atn = 6; - if (IsKeyPressed(KEY_EIGHT)) atn = 7; - if (IsKeyPressed(KEY_NINE)) atn = 8; - if (IsKeyPressed(KEY_ZERO)) atn = 9; - if (IsKeyPressed(KEY_ENTER)) atn = 10; - - if (env.turn == 1) { - atn = rand() % 11; - } - - render(&env); - } - free_tcg(&env); - return 0; -} diff --git a/pufferlib/ocean/tcg/tcg.h b/pufferlib/ocean/tcg/tcg.h deleted file mode 100644 index b82d50b9a..000000000 --- a/pufferlib/ocean/tcg/tcg.h +++ /dev/null @@ -1,606 +0,0 @@ -#include -#include -#include -#include -#include "raylib.h" - -#define HAND_SIZE 10 -#define BOARD_SIZE 10 -#define DECK_SIZE 60 -#define STACK_SIZE 100 - -#define ACTION_ENTER 10 -#define ACTION_NOOP 11 - -#define TO_USER true; -#define TO_STACK false; - -typedef struct TCG TCG; -typedef bool (*call)(TCG*, unsigned char); -bool phase_untap(TCG* env, unsigned char atn); -bool phase_draw(TCG* env, unsigned char atn); -bool phase_play(TCG* env, unsigned char atn); -bool phase_attack(TCG* env, unsigned char atn); -bool phase_block(TCG* env, unsigned char atn); -void reset(TCG* env); - -typedef struct Stack Stack; -struct Stack { - call data[STACK_SIZE]; - int idx; -}; - -void push(Stack* stack, call fn) { - assert(stack->idx < STACK_SIZE); - stack->data[stack->idx] = fn; - stack->idx += 1; -} - -call pop(Stack* stack) { - assert(stack->idx > 0); - stack->idx -= 1; - return stack->data[stack->idx]; -} - -call peek(Stack* stack) { - assert(stack->idx > 0); - return stack->data[stack->idx - 1]; -} - -typedef struct Card Card; -struct Card { - int cost; - int attack; - int health; - bool is_land; - bool remove; - bool tapped; - bool attacking; - int defending; -}; - -typedef struct CardArray CardArray; -struct CardArray { - Card* cards; - int length; - int max; -}; - -CardArray* allocate_card_array(int max) { - CardArray* hand = (CardArray*)calloc(1, sizeof(CardArray)); - hand->cards = (Card*)calloc(max, sizeof(Card)); - hand->max = max; - return hand; -} - -void free_card_array(CardArray* ary) { - free(ary->cards); - free(ary); -} - -void condense_card_array(CardArray* ary) { - int idx = 0; - for (int i = 0; i < ary->length; i++) { - if (!ary->cards[i].remove) { - ary->cards[idx] = ary->cards[i]; - idx += 1; - } - } - ary->length = idx; -} - -struct TCG { - CardArray* my_hand; - CardArray* my_board; - CardArray* my_deck; - int my_health; - int my_mana; - bool my_land_played; - - CardArray* op_hand; - CardArray* op_board; - CardArray* op_deck; - int op_health; - int op_mana; - bool op_land_played; - - Stack* stack; - //bool attackers[BOARD_SIZE]; - //bool defenders[BOARD_SIZE][BOARD_SIZE]; - int block_idx; - int turn; -}; - -void allocate_tcg(TCG* env) { - env->stack = calloc(1, sizeof(Stack)); - env->my_hand = allocate_card_array(HAND_SIZE); - env->op_hand = allocate_card_array(HAND_SIZE); - env->my_board = allocate_card_array(BOARD_SIZE); - env->op_board = allocate_card_array(BOARD_SIZE); - env->my_deck = allocate_card_array(DECK_SIZE); - env->op_deck = allocate_card_array(DECK_SIZE); -} - -void free_tcg(TCG* env) { - free_card_array(env->my_hand); - free_card_array(env->op_hand); - free_card_array(env->my_board); - free_card_array(env->op_board); - free_card_array(env->my_deck); - free_card_array(env->op_deck); -} - -void randomize_deck(CardArray* deck) { - for (int i = 0; i < deck->length; i++) { - deck->cards[i].defending = -1; - if (rand() % 3 == 0) { - deck->cards[i].is_land = true; - } else { - int cost = rand() % 6; - deck->cards[i].cost = cost; - deck->cards[i].attack = cost + 1; - deck->cards[i].health = cost + 1; - } - } -} - -void draw_card(TCG* env, CardArray* deck, CardArray* hand) { - if (deck->length == 0) { - reset(env); - return; - } - if (hand->length == hand->max) { - return; - } - Card card = deck->cards[deck->length - 1]; - hand->cards[hand->length] = card; - deck->length -= 1; - hand->length += 1; -} - -bool can_attack(CardArray* board) { - for (int i = 0; i < board->length; i++) { - if (!board->cards[i].is_land) { - return true; - } - } - return false; -} - -int tappable_mana(TCG* env) { - CardArray* board = (env->turn == 0) ? env->my_board : env->op_board; - int tappable = 0; - for (int i = 0; i < board->length; i++) { - Card card = board->cards[i]; - if (card.is_land && !card.tapped) { - tappable += 1; - } - } - return tappable; -} - -bool can_play(TCG* env) { - CardArray* hand = (env->turn == 0) ? env->my_hand : env->op_hand; - int* mana = (env->turn == 0) ? &env->my_mana : &env->op_mana; - bool* land_played = (env->turn == 0) ? &env->my_land_played : &env->op_land_played; - - int min_cost = 99; - for (int i = 0; i < hand->length; i++) { - if (hand->cards[i].is_land && !*land_played) { - return true; - } else if (hand->cards[i].cost < min_cost) { - min_cost = hand->cards[i].cost; - } - } - - int tappable = tappable_mana(env); - return *mana + tappable >= min_cost; -} - -bool phase_untap(TCG* env, unsigned char atn) { - printf("PHASE_UNTAP\n"); - bool* land_played = (env->turn == 0) ? &env->my_land_played : &env->op_land_played; - *land_played = false; - - env->turn = 1 - env->turn; - CardArray* board = (env->turn == 0) ? env->my_board : env->op_board; - - int* mana = (env->turn == 0) ? &env->my_mana : &env->op_mana; - *mana = 0; - - for (int i = 0; i < board->length; i++) { - Card card = board->cards[i]; - if (card.is_land && card.tapped) { - board->cards[i].tapped = false; - } - } - - push(env->stack, phase_draw); - return TO_STACK; -} - -bool phase_draw(TCG* env, unsigned char atn) { - printf("PHASE_DRAW\n"); - CardArray* deck = (env->turn == 0) ? env->my_deck : env->op_deck; - CardArray* hand = (env->turn == 0) ? env->my_hand : env->op_hand; - draw_card(env, deck, hand); - push(env->stack, phase_play); - return TO_STACK; -} - -bool phase_play(TCG* env, unsigned char atn) { - printf("PHASE_PLAY\n"); - CardArray* hand = (env->turn == 0) ? env->my_hand : env->op_hand; - CardArray* board = (env->turn == 0) ? env->my_board : env->op_board; - int* mana = (env->turn == 0) ? &env->my_mana : &env->op_mana; - bool* land_played = (env->turn == 0) ? &env->my_land_played : &env->op_land_played; - - if (board->length == BOARD_SIZE) { - printf("\t Board full\n"); - push(env->stack, phase_attack); - return TO_STACK; - } - - if (!can_play(env)) { - printf("\t No valid moves\n"); - push(env->stack, phase_attack); - return TO_STACK; - } - - if (atn == ACTION_NOOP) { - push(env->stack, phase_play); - return TO_USER; - } else if (atn == ACTION_ENTER) { - push(env->stack, phase_attack); - return TO_STACK; - } else if (atn >= hand->length) { - printf("\t Invalid action: %i\n. Hand length: %i\n", atn, hand->length); - push(env->stack, phase_play); - return TO_USER; - } - - Card card = hand->cards[atn]; - if (card.is_land) { - if (*land_played) { - printf("\t Already played land this turn\n"); - push(env->stack, phase_play); - return TO_USER; - } - board->cards[board->length] = card; - board->length += 1; - *land_played = true; - hand->cards[atn].remove = true; - condense_card_array(hand); - printf("\t Land played\n"); - push(env->stack, phase_play); - return TO_USER; - } - - if (card.cost > *mana + tappable_mana(env)) { - printf("\t Not enough mana\n"); - push(env->stack, phase_play); - return TO_USER; - } - - // Auto tap lands? - for (int i = 0; i < board->length; i++) { - if (card.cost <= *mana) { - break; - } - Card card = board->cards[i]; - if (card.is_land && !card.tapped) { - *mana += 1; - board->cards[i].tapped = true; - } - } - - assert(*mana >= card.cost); - *mana -= card.cost; - board->cards[board->length] = card; - board->length += 1; - hand->cards[atn].remove = true; - condense_card_array(hand); - printf("\t Card played\n"); - push(env->stack, phase_play); - return TO_USER; -} - -bool phase_attack(TCG* env, unsigned char atn) { - printf("PHASE_ATTACK\n"); - CardArray* board = (env->turn == 0) ? env->my_board : env->op_board; - - if (!can_attack(board)) { - printf("\t No valid attacks. Phase end\n"); - push(env->stack, phase_untap); - return TO_STACK; - } - - if (atn == ACTION_NOOP) { - push(env->stack, phase_attack); - return TO_USER; - } else if (atn == ACTION_ENTER) { - printf("\t Attacks confirmed. Phase end\n"); - env->turn = 1 - env->turn; - push(env->stack, phase_block); - return TO_STACK; - } else if (atn >= board->length) { - printf("\t Invalid action %i\n", atn); - push(env->stack, phase_attack); - return TO_USER; - } else if (board->cards[atn].is_land) { - printf("\t Cannot attack with land\n"); - push(env->stack, phase_attack); - return TO_USER; - } else { - printf("\t Setting attacker %i\n", atn); - board->cards[atn].attacking = !board->cards[atn].attacking; - push(env->stack, phase_attack); - return TO_USER; - } -} - -bool phase_block(TCG* env, unsigned char atn) { - printf("PHASE_BLOCK\n"); - CardArray* defender_board = (env->turn == 0) ? env->my_board : env->op_board; - CardArray* board = (env->turn == 0) ? env->op_board : env->my_board; - int* health = (env->turn == 0) ? &env->op_health : &env->my_health; - - while (env->block_idx < board->length && !board->cards[env->block_idx].attacking) { - printf("\t Skipping block for %i (not attacking)\n", env->block_idx); - env->block_idx++; - } - - bool can_block = false; - for (int i = 0; i < defender_board->length; i++) { - Card* card = &defender_board->cards[i]; - if (card->is_land) { - continue; - } - if (card->defending == -1 || card->defending == env->block_idx) { - can_block = true; - printf("\t Can block with %i\n", i); - break; - } - } - if (!can_block) { - env->block_idx = board->length; - } - - if (env->block_idx == board->length) { - printf("\t Attacker board length: %i\n", board->length); - for (int atk = 0; atk < board->length; atk++) { - printf("\t Resolving %i\n", atk); - Card* attacker = &board->cards[atk]; - if (!attacker->attacking) { - printf("\t Not attacking\n"); - continue; - } - int attacker_attack = attacker->attack; - int attacker_health = attacker->health; - for (int def = 0; def < defender_board->length; def++) { - Card* defender = &defender_board->cards[def]; - if (defender->defending != atk) { - continue; - } - if (attacker_attack >= defender->health) { - attacker_attack -= defender->health; - attacker_health -= defender->attack; - defender->health = 0; - defender->remove = true; - } else { - attacker_health -= defender->attack; - attacker_attack = 0; - } - if (attacker_health <= 0) { - attacker->remove = true; - break; - } - } - printf("\t Reducing health by %i\n", attacker_attack); - *health -= attacker_attack; - } - - if (*health <= 0) { - printf("\t Game over\n"); - reset(env); - } - - condense_card_array(env->my_board); - condense_card_array(env->op_board); - - CardArray* defender_deck = (env->turn == 0) ? env->my_deck : env->op_deck; - CardArray* defender_hand = (env->turn == 0) ? env->my_hand : env->op_hand; - draw_card(env, defender_deck, defender_hand); - - for (int i = 0; i < board->length; i++) { - board->cards[i].attacking = false; - } - for (int i = 0; i < defender_board->length; i++) { - defender_board->cards[i].defending = -1; - } - printf("\t Set block idx to 0\n"); - env->block_idx = 0; - env->turn = 1 - env->turn; - push(env->stack, phase_untap); - return TO_STACK; - } - - if (atn == ACTION_NOOP) { - push(env->stack, phase_block); - return TO_USER; - } else if (atn == ACTION_ENTER) { - printf("\t Manual block confirm %i\n", env->block_idx); - env->block_idx++; - push(env->stack, phase_block); - return TO_STACK; - } else if (atn >= defender_board->length) { - printf("\t Invalid block action %i\n", atn); - push(env->stack, phase_block); - return TO_USER; - } else if (defender_board->cards[atn].is_land) { - printf("\t Cannot block with land\n"); - push(env->stack, phase_block); - return TO_USER; - } - - for (int i = 0; i < env->block_idx; i++) { - if (defender_board->cards[atn].defending == i) { - printf("\t Already blocked\n"); - push(env->stack, phase_block); - return TO_USER; - } - } - printf("\t Blocking index %i with %i\n", env->block_idx, atn); - Card* card = &defender_board->cards[atn]; - if (card->defending == env->block_idx) { - card->defending = -1; - } else { - card->defending = env->block_idx; - } - push(env->stack, phase_block); - return TO_USER; -} - -void step(TCG* env, unsigned char atn) { - printf("Turn: %i, Action: %i\n", env->turn, atn); - while (true) { - call fn = pop(env->stack); - bool return_to_user = fn(env, atn); - if (return_to_user) { - return; - } - atn = ACTION_NOOP; - } -} - -void reset(TCG* env) { - env->my_deck->length = DECK_SIZE; - env->op_deck->length = DECK_SIZE; - env->my_hand->length = 0; - env->op_hand->length = 0; - env->my_board->length = 0; - env->op_board->length = 0; - env->my_health = 20; - env->op_health = 20; - randomize_deck(env->my_deck); - randomize_deck(env->op_deck); - env->turn = rand() % 2; - for (int i = 0; i < 5; i++) { - draw_card(env, env->my_deck, env->my_hand); - draw_card(env, env->op_deck, env->op_hand); - } - push(env->stack, phase_draw); - step(env, ACTION_NOOP); -} - -void init_client(TCG* env) { - InitWindow(1080, 720, "Puffer the Schooling TCG"); - SetTargetFPS(60); -} - -void close_client(TCG* env) { - CloseWindow(); -} - -int card_x(int col, int n) { - int cards_width = 72*n; - int offset = 72*col; - return GetScreenWidth()/2 - cards_width/2 + offset; -} - -int card_y(int row) { - return 64 + (128 + 20)*row; -} - -void render_card(Card* card, int x, int y, Color color) { - DrawRectangle(x, y, 64, 128, color); - if (card->is_land) { - DrawText("Land", x + 16, y + 40, 16, WHITE); - } else { - DrawText(TextFormat("%i", card->cost), x + 32, y+16, 20, WHITE); - DrawText(TextFormat("%i", card->attack), x + 32, y + 40, 20, WHITE); - DrawText(TextFormat("%i", card->health), x + 32, y + 64, 20, WHITE); - } -} - -void render_label(int x, int y, int idx) { - DrawText(TextFormat("%i", (idx+1)%10), x+32, y+96, 20, YELLOW); -} - -void render(TCG* env) { - BeginDrawing(); - ClearBackground((Color){6, 24, 24, 255}); - - for (int i = 0; i < env->my_hand->length; i++) { - Card card = env->my_hand->cards[i]; - int x = card_x(i, env->my_hand->length); - int y = card_y(3); - render_card(&card, x, y, RED); - if (env->turn == 0) { - render_label(x, y, i); - } - } - - for (int i = 0; i < env->my_board->length; i++) { - Card card = env->my_board->cards[i]; - int x = card_x(i, env->my_board->length); - int y = card_y(2); - if (card.attacking) { - y -= 16; - } - Color color = (card.tapped) ? (Color){128, 0, 0, 255}: RED; - render_card(&card, x, y, color); - if (env->turn == 0) { - render_label(x, y, i); - } - } - - for (int i = 0; i < env->op_board->length; i++) { - Card card = env->op_board->cards[i]; - int x = card_x(i, env->op_board->length); - int y = card_y(1); - if (card.attacking) { - y += 16; - } - Color color = (card.tapped) ? (Color){0, 0, 128, 255}: BLUE; - render_card(&card, x, y, color); - } - - for (int i = 0; i < env->my_board->length; i++) { - Card card = env->my_board->cards[i]; - if (card.defending == -1) { - continue; - } - DrawLineEx( - (Vector2){32+card_x(i, env->my_board->length), 64+card_y(2)}, - (Vector2){32+card_x(card.defending, env->op_board->length), 64+card_y(1)}, - 3.0f, WHITE - ); - } - - for (int i = 0; i < env->op_hand->length; i++) { - Card card = env->op_hand->cards[i]; - int x = card_x(i, env->op_hand->length); - int y = card_y(0); - render_card(&card, x, y, BLUE); - } - - int x = GetScreenWidth() - 128; - int y = 32; - - call fn = peek(env->stack); - if (fn == phase_draw) { - DrawText("Draw", x, y, 20, WHITE); - } else if (fn == phase_play) { - DrawText("Play", x, y, 20, WHITE); - } else if (fn == phase_attack) { - DrawText("Attack", x, y, 20, WHITE); - } else if (fn == phase_block) { - DrawText("Block", x, y, 20, WHITE); - } - - DrawText(TextFormat("Health: %i", env->my_health), 32, 32, 20, WHITE); - DrawText(TextFormat("Health: %i", env->op_health), 32, GetScreenHeight() - 64, 20, WHITE); - - EndDrawing(); -} diff --git a/pufferlib/ocean/template/binding.c b/pufferlib/ocean/template/binding.c deleted file mode 100644 index 3d1bc5bf2..000000000 --- a/pufferlib/ocean/template/binding.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "template.h" - -#define Env Template -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->size = unpack(kwargs, "size"); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "score", log->score); - return 0; -} diff --git a/pufferlib/ocean/template/template.c b/pufferlib/ocean/template/template.c deleted file mode 100644 index a7226c83b..000000000 --- a/pufferlib/ocean/template/template.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "template.h" - -int main() { - Template env = {.size = 5}; - env.observations = (unsigned char*)calloc(1, sizeof(unsigned char)); - env.actions = (int*)calloc(1, sizeof(int)); - env.rewards = (float*)calloc(1, sizeof(float)); - env.terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); - - c_reset(&env); - c_render(&env); - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) { - env.actions[0] = 0; - } else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) { - env.actions[0] = 1; - } else { - env.actions[0] = -1; - } - } else { - env.actions[0] = rand() % 2; - } - c_step(&env); - c_render(&env); - } - free(env.observations); - free(env.actions); - free(env.rewards); - free(env.terminals); - c_close(&env); -} - diff --git a/pufferlib/ocean/template/template.h b/pufferlib/ocean/template/template.h deleted file mode 100644 index 7ce724eff..000000000 --- a/pufferlib/ocean/template/template.h +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include "raylib.h" - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -// Only use floats! -typedef struct { - float score; - float n; // Required as the last field -} Log; - -typedef struct { - Log log; // Required field - unsigned char* observations; // Required field. Ensure type matches in .py and .c - int* actions; // Required field. Ensure type matches in .py and .c - float* rewards; // Required field - unsigned char* terminals; // Required field - int size; - int x; - int goal; -} Template; - -void c_reset(Template* env) { - env->x = 0; - env->goal = (rand()%2 == 0) ? env->size : -env->size; -} - -void c_step(Template* env) { - env->rewards[0] = 0; - env->terminals[0] = 0; - if (env->actions[0] == 0) { - env->x -= 1; - } else if (env->actions[0] == 1) { - env->x += 1; - } - if (env->x == env->goal) { - c_reset(env); - env->rewards[0] = 1; - env->terminals[0] = 1; - env->log.score += 1; - env->log.n += 1; - } else if (env->x == -env->goal) { - c_reset(env); - env->rewards[0] = -1; - env->terminals[0] = 1; - env->log.score -= 1; - env->log.n += 1; - } - env->observations[0] = (env->goal > 0) ? 1 : -1; -} - -void c_render(Template* env) { - if (!IsWindowReady()) { - InitWindow(1080, 720, "PufferLib Template"); - SetTargetFPS(5); - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - DrawText("Go to the red square!", 20, 20, 20, PUFF_WHITE); - DrawRectangle(540 - 32 + 64*env->goal, 360 - 32, 64, 64, PUFF_RED); - DrawRectangle(540 - 32 + 64*env->x, 360 - 32, 64, 64, PUFF_CYAN); - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - EndDrawing(); -} - -void c_close(Template* env) { - if (IsWindowReady()) { - CloseWindow(); - } -} diff --git a/pufferlib/ocean/template/template.py b/pufferlib/ocean/template/template.py deleted file mode 100644 index b73abd765..000000000 --- a/pufferlib/ocean/template/template.py +++ /dev/null @@ -1,54 +0,0 @@ -'''A minimal template for your own envs.''' - -import gymnasium -import numpy as np - -import pufferlib -from pufferlib.ocean.template import binding - -class Template(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, log_interval=128, size=5, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(1,), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.Discrete(2) - self.render_mode = render_mode - self.num_agents = num_envs - - super().__init__(buf) - self.c_envs = binding.vec_init(self.observations, self.actions, self.rewards, - self.terminals, self.truncations, num_envs, seed, size=size) - self.size = size - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - info = [binding.vec_log(self.c_envs)] - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - N = 4096 - env = Template(num_envs=N) - env.reset() - steps = 0 - - CACHE = 1024 - actions = np.random.randint(0, 5, (CACHE, N)) - - import time - start = time.time() - while time.time() - start < 10: - env.step(actions[steps % CACHE]) - steps += 1 - - print('Squared SPS:', int(env.num_agents*steps / (time.time() - start))) diff --git a/pufferlib/ocean/terraform/binding.c b/pufferlib/ocean/terraform/binding.c deleted file mode 100644 index 1c88f2aab..000000000 --- a/pufferlib/ocean/terraform/binding.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "terraform.h" - -#define Env Terraform -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->size = unpack(kwargs, "size"); - env->num_agents = unpack(kwargs, "num_agents"); - env->reward_scale = unpack(kwargs, "reward_scale"); - env->reset_frequency = unpack(kwargs, "reset_frequency"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "quadrant_progress", log->quadrant_progress); - return 0; -} diff --git a/pufferlib/ocean/terraform/simplex.h b/pufferlib/ocean/terraform/simplex.h deleted file mode 100644 index 07b3e7db3..000000000 --- a/pufferlib/ocean/terraform/simplex.h +++ /dev/null @@ -1,148 +0,0 @@ -// Original work (noise library) Copyright (c) 2008 Casey Duncan -// Modified work (vec_noise library) Copyright (c) 2017 Zev Benjamin -// Single-file C port (this file) Copyright (c) 2024 Joseph Suarez -// Distributed under the MIT license. This is a simple copy-paste job. -// I did this because the original code mixed Python bindings into the -// C source, so there wasn't a clean way to use it as a C standalone. - -#include -const float GRAD3[][3] = { - {1,1,0},{-1,1,0},{1,-1,0},{-1,-1,0}, - {1,0,1},{-1,0,1},{1,0,-1},{-1,0,-1}, - {0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}, - {1,0,-1},{-1,0,-1},{0,-1,1},{0,1,1}}; - -const float GRAD4[][4] = { - {0,1,1,1}, {0,1,1,-1}, {0,1,-1,1}, {0,1,-1,-1}, - {0,-1,1,1}, {0,-1,1,-1}, {0,-1,-1,1}, {0,-1,-1,-1}, - {1,0,1,1}, {1,0,1,-1}, {1,0,-1,1}, {1,0,-1,-1}, - {-1,0,1,1}, {-1,0,1,-1}, {-1,0,-1,1}, {-1,0,-1,-1}, - {1,1,0,1}, {1,1,0,-1}, {1,-1,0,1}, {1,-1,0,-1}, - {-1,1,0,1}, {-1,1,0,-1}, {-1,-1,0,1}, {-1,-1,0,-1}, - {1,1,1,0}, {1,1,-1,0}, {1,-1,1,0}, {1,-1,-1,0}, - {-1,1,1,0}, {-1,1,-1,0}, {-1,-1,1,0}, {-1,-1,-1,0}}; - -// At the possible cost of unaligned access, we use char instead of -// int here to try to ensure that this table fits in L1 cache -const unsigned char PERM[] = { - 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, - 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, - 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, - 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, - 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, - 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, - 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, - 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, - 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, - 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, - 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, - 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, - 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, - 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, - 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, - 141, 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, - 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, - 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, - 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, - 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, - 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, - 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, - 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, - 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, - 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, - 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, - 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, - 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, - 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, - 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, - 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, - 180}; - -const unsigned char SIMPLEX[][4] = { - {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {1,2,3,0},{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{1,3,2,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},{1,0,2,3},{1,0,3,2},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},{0,0,0,0},{0,0,0,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,1,3}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, - {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1}, - {3,2,1,0}}; - -#define fastfloor(n) (int)(n) - (((n) < 0.0f) & ((n) != (int)(n))) - -// Fast sine/cosine functions from -// http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/page__st__80 -// Note the input to these functions is not radians -// instead x = [0, 2] for r = [0, 2*PI] - -inline float fast_sin(float x) -{ - // Convert the input value to a range of -1 to 1 - // x = x * (1.0f / PI); - - // Wrap around - volatile float z = (x + 25165824.0f); - x = x - (z - 25165824.0f); - - #if LOW_SINE_PRECISION - return 4.0f * (x - x * fabsf(x)); - #else - { - float y = x - x * fabsf(x); - const float Q = 3.1f; - const float P = 3.6f; - return y * (Q + P * fabsf(y)); - } - #endif -} - -inline float fast_cos(float x) -{ - return fast_sin(x + 0.5f); -} - -// 2D simplex skew factors -#define F2 0.3660254037844386f // 0.5 * (sqrt(3.0) - 1.0) -#define G2 0.21132486540518713f // (3.0 - sqrt(3.0)) / 6.0 - -float -noise2(float x, float y) -{ - int i1, j1, II, JJ, c; - float s = (x + y) * F2; - float i = floorf(x + s); - float j = floorf(y + s); - float t = (i + j) * G2; - - float xx[3], yy[3], f[3]; - float noise[3] = {0.0f, 0.0f, 0.0f}; - int g[3]; - - xx[0] = x - (i - t); - yy[0] = y - (j - t); - - i1 = xx[0] > yy[0]; - j1 = xx[0] <= yy[0]; - - xx[2] = xx[0] + G2 * 2.0f - 1.0f; - yy[2] = yy[0] + G2 * 2.0f - 1.0f; - xx[1] = xx[0] - i1 + G2; - yy[1] = yy[0] - j1 + G2; - - II = (int) i & 255; - JJ = (int) j & 255; - g[0] = PERM[II + PERM[JJ]] % 12; - g[1] = PERM[II + i1 + PERM[JJ + j1]] % 12; - g[2] = PERM[II + 1 + PERM[JJ + 1]] % 12; - - for (c = 0; c <= 2; c++) - f[c] = 0.5f - xx[c]*xx[c] - yy[c]*yy[c]; - - for (c = 0; c <= 2; c++) - if (f[c] > 0) - noise[c] = f[c]*f[c]*f[c]*f[c] * (GRAD3[g[c]][0]*xx[c] + GRAD3[g[c]][1]*yy[c]); - - return (noise[0] + noise[1] + noise[2]) * 70.0f; -} diff --git a/pufferlib/ocean/terraform/terraform.c b/pufferlib/ocean/terraform/terraform.c deleted file mode 100644 index 02bb4fcb6..000000000 --- a/pufferlib/ocean/terraform/terraform.c +++ /dev/null @@ -1,247 +0,0 @@ -#include "terraform.h" -#include "puffernet.h" - -void allocate(Terraform* env) { - env->observations = (float*)calloc(env->num_agents*442, sizeof(float)); - env->actions = (int*)calloc(3*env->num_agents, sizeof(int)); - env->rewards = (float*)calloc(env->num_agents, sizeof(float)); - env->terminals = (unsigned char*)calloc(env->num_agents, sizeof(unsigned char)); - init(env); -} - -void free_allocated(Terraform* env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - free_initialized(env); -} - -typedef struct TerraformNet TerraformNet; -struct TerraformNet { - int num_agents; - float* local_obs2d; - float* global_obs2d; - float* obs_1d; - Conv2D* local_conv1; - ReLU* relu1; - Conv2D* local_conv2; - ReLU* relu2; - Conv2D* global_conv1; - ReLU* relu3; - Conv2D* global_conv2; - ReLU* relu4; - Linear* flat; - CatDim1* cat1; - CatDim1* cat2; - Linear* proj; - ReLU* relu5; - LSTM* lstm; - Linear* actor; - Linear* value_fn; - Multidiscrete* multidiscrete; -}; -TerraformNet* init_terranet(Weights* weights, int num_agents, int vision_size, int quadrant_size) { - TerraformNet* net = calloc(1, sizeof(TerraformNet)); - int hidden_size = 512; - int cnn_channels = 32; - int local_conv1_output_size = 3; - int local_conv2_output_size = 1; - int global_conv1_output_size = 4; - int global_conv2_output_size = 2; - int local_cnn_flat_size = cnn_channels * (local_conv2_output_size * local_conv2_output_size); - int global_cnn_flat_size = cnn_channels * (global_conv2_output_size * global_conv2_output_size); - - net->num_agents = num_agents; - net->local_obs2d = calloc(num_agents * vision_size * vision_size * 2, sizeof(float)); // 2 channels - height map & deltas - net->global_obs2d = calloc(num_agents * quadrant_size * quadrant_size * 2, sizeof(float)); // 2 channels - global volume map and agent location - net->obs_1d = calloc(num_agents * 5, sizeof(float)); // 2 additional features - - net->local_conv1 = make_conv2d(weights, num_agents, vision_size, vision_size, 2, cnn_channels, 5, 3); - net->relu1 = make_relu(num_agents, cnn_channels * local_conv1_output_size * local_conv1_output_size); - net->local_conv2 = make_conv2d(weights, num_agents, local_conv1_output_size, local_conv1_output_size, cnn_channels, cnn_channels, 3, 1); - net->relu2 = make_relu(num_agents, cnn_channels * local_conv2_output_size * local_conv2_output_size); - net->global_conv1 = make_conv2d(weights, num_agents, quadrant_size, quadrant_size, 2, cnn_channels, 3, 1); - net->relu3 = make_relu(num_agents, cnn_channels * global_conv1_output_size * global_conv1_output_size); - net->global_conv2 = make_conv2d(weights, num_agents, global_conv1_output_size, global_conv1_output_size, cnn_channels, cnn_channels, 3, 1); - net->relu4 = make_relu(num_agents, cnn_channels * global_conv2_output_size * global_conv2_output_size); - net->flat = make_linear(weights, num_agents, 5, hidden_size); - net->cat1 = make_cat_dim1(num_agents, local_cnn_flat_size, global_cnn_flat_size); - net->cat2 = make_cat_dim1(num_agents, local_cnn_flat_size + global_cnn_flat_size, hidden_size); - net->proj = make_linear(weights, num_agents, local_cnn_flat_size + global_cnn_flat_size + hidden_size, hidden_size); - net->relu5 = make_relu(num_agents, hidden_size); - net->actor = make_linear(weights, num_agents, hidden_size, 13); // +1 for pass move - net->value_fn = make_linear(weights, num_agents, hidden_size, 1); - net->lstm = make_lstm(weights, num_agents, hidden_size, 512); - int logit_sizes[3] = {5, 5, 3}; - net->multidiscrete = make_multidiscrete(num_agents, logit_sizes, 3); - return net; -} - -void free_terranet(TerraformNet* net) { - free(net->local_obs2d); - free(net->global_obs2d); - free(net->obs_1d); - free(net->local_conv1); - free(net->relu1); - free(net->local_conv2); - free(net->relu2); - free(net->global_conv1); - free(net->relu3); - free(net->global_conv2); - free(net->relu4); - free(net->flat); - free(net->cat1); - free(net->cat2); - free(net->proj); - free(net->relu5); - free(net->lstm); - free(net->actor); - free(net->value_fn); - free(net); -} - -void forward(TerraformNet* net, float* observations, int* actions, int vision_size, int quadrant_size) { - int local_vision_size = vision_size * vision_size; - int global_quadrant_size = quadrant_size * quadrant_size; - // Clear previous observations - memset(net->local_obs2d, 0, net->num_agents * vision_size * vision_size * 2 * sizeof(float)); - memset(net->global_obs2d, 0, net->num_agents * quadrant_size * quadrant_size * 2 * sizeof(float)); - memset(net->obs_1d, 0, net->num_agents * 5 * sizeof(float)); - - // Reshape observations into 2D boards and additional features - float (*local_obs2d)[2][vision_size][vision_size] = (float (*)[2][vision_size][vision_size])net->local_obs2d; - float (*global_obs2d)[2][quadrant_size][quadrant_size] = (float (*)[2][quadrant_size][quadrant_size])net->global_obs2d; - float (*obs_1d)[5] = (float (*)[5])net->obs_1d; - - for (int b = 0; b < net->num_agents; b++) { - int b_offset = b * (local_vision_size * 2 + global_quadrant_size * 2 + 5); // offset for each batch - - // Process local vision board - int obs_2d_idx = 0; - for(int z = 0; z < 2; z++) { - for (int i = 0; i < vision_size; i++) { - for (int j = 0; j < vision_size; j++) { - local_obs2d[b][z][i][j] = observations[b_offset + obs_2d_idx]; - obs_2d_idx++; - } - } - } - - // Process additional features - obs_1d[b][0] = observations[b_offset + obs_2d_idx]; - obs_1d[b][1] = observations[b_offset + obs_2d_idx + 1]; - obs_1d[b][2] = observations[b_offset + obs_2d_idx + 2]; - obs_1d[b][3] = observations[b_offset + obs_2d_idx + 3]; - obs_1d[b][4] = observations[b_offset + obs_2d_idx + 4]; - obs_2d_idx += 5; - - // Process global quadrant board - for(int z = 0; z < 2; z++) { - for (int i = 0; i < quadrant_size; i++) { - for (int j = 0; j < quadrant_size; j++) { - global_obs2d[b][z][i][j] = observations[b_offset + obs_2d_idx]; - obs_2d_idx++; - } - } - } - } - - // Forward pass through the network - // local convs - conv2d(net->local_conv1, net->local_obs2d); - relu(net->relu1, net->local_conv1->output); - conv2d(net->local_conv2, net->relu1->output); - relu(net->relu2, net->local_conv2->output); - // global convs - conv2d(net->global_conv1, net->global_obs2d); - relu(net->relu3, net->global_conv1->output); - conv2d(net->global_conv2, net->relu3->output); - relu(net->relu4, net->global_conv2->output); - - linear(net->flat, net->obs_1d); - - cat_dim1(net->cat1, net->relu2->output, net->relu4->output); - cat_dim1(net->cat2, net->cat1->output, net->flat->output); - linear(net->proj, net->cat2->output); - relu(net->relu5, net->proj->output); - - lstm(net->lstm, net->relu5->output); - linear(net->actor, net->lstm->state_h); - linear(net->value_fn, net->lstm->state_h); - - // Get action by taking argmax of actor output - softmax_multidiscrete(net->multidiscrete, net->actor->output, actions); - -} - -void demo() { - Weights* weights = load_weights("resources/terraform/puffer_terraform_weights.bin", 2476814); - TerraformNet* net = init_terranet(weights, 1, 11, 6); - srand(time(NULL)); - Terraform env = {.size = 64, .num_agents = 1, .reset_frequency = 8192, .reward_scale = 0.04f}; - allocate(&env); - - c_reset(&env); - c_render(&env); - while (!WindowShouldClose()) { - forward(net, env.observations, env.actions, 11, 6); - int policy_actions[3] = {env.actions[0], env.actions[1], env.actions[2]}; - - if(IsKeyDown(KEY_LEFT_SHIFT)) { - // When shift is held, stop the dozer - env.actions[0] = 2; // Stop vertical movement - env.actions[1] = 2; // Stop horizontal movement - env.actions[2] = 0; // no scoop or drop - // Override with keyboard controls if keys are pressed - if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) env.actions[0] = 4; - if (IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) env.actions[0] = 0; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) env.actions[1] = 0; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) env.actions[1] = 4; - if (IsKeyPressed(KEY_SPACE)) env.actions[2] = 1; - if (IsKeyPressed(KEY_ENTER)) env.actions[2] = 2; - } - c_step(&env); - c_render(&env); - } - free_allocated(&env); - close_client(env.client); - free_terranet(net); - free(weights); -} - -void test_performance(int timeout) { - srand(time(NULL)); - Terraform env = { - .size = 64, - .num_agents = 8, - .reset_frequency = 512, - .reward_scale = 0.01f, - }; - allocate(&env); - c_reset(&env); - - int start = time(NULL); - int num_steps = 0; - while (time(NULL) - start < timeout) { - for (int i = 0; i < env.num_agents; i++) { - env.actions[3*i] = rand() % 5; - env.actions[3*i + 1] = rand() % 5; - env.actions[3*i + 2] = rand() % 3; - } - - c_step(&env); - num_steps++; - } - - int end = time(NULL); - float sps = num_steps * env.num_agents / (end - start); - printf("Test Environment SPS: %f\n", sps); - free_allocated(&env); -} - -int main() { - // test_performance(10); - demo(); -} - diff --git a/pufferlib/ocean/terraform/terraform.h b/pufferlib/ocean/terraform/terraform.h deleted file mode 100644 index 21a214721..000000000 --- a/pufferlib/ocean/terraform/terraform.h +++ /dev/null @@ -1,1134 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "raylib.h" -#include "simplex.h" -#include "raymath.h" -#include "rlgl.h" - -#if defined(PLATFORM_DESKTOP) - #define GLSL_VERSION 330 -#else - #define GLSL_VERSION 100 -#endif - -const unsigned char NOOP = 0; -const unsigned char DOWN = 1; -const unsigned char UP = 2; -const unsigned char LEFT = 3; -const unsigned char RIGHT = 4; - -const unsigned char EMPTY = 0; -const unsigned char AGENT = 1; -const unsigned char TARGET = 2; - -#define MAX_DIRT_HEIGHT 32.0f -#define BUCKET_MAX_HEIGHT 1.0f -#define DOZER_MAX_V 2.0f -#define DOZER_CAPACITY 100.0f -#define BUCKET_OFFSET 2.0f -#define BUCKET_WIDTH 2.5f -#define BUCKET_LENGTH 0.8f -#define BUCKET_HEIGHT 1.0f -#define SCOOP_SIZE 1 -#define VISION 5 -#define OBSERVATION_SIZE (2*VISION + 1) -#define TOTAL_OBS (OBSERVATION_SIZE*OBSERVATION_SIZE + 4) -#define DOZER_STEP_HEIGHT 5.0f -struct timespec ts; - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; - float quadrant_progress; -}; - -typedef struct Dozer { - float x; - float y; - float z; - float v; - float heading; - float bucket_height; - float bucket_tilt; - float load; - int target_quadrant; - int* load_indices; - float quadrant_progress; - float highest_quadrant_progress; - float target_quadrant_delta; -} Dozer; - -typedef struct Client Client; -typedef struct Terraform { - Log log; - Log* agent_logs; - Client* client; - Dozer* dozers; - float* observations; - int* actions; - float* rewards; - float* returns; - unsigned char* terminals; - int size; - int tick; - float* orig_map; - float* map; - float* target_map; - int num_agents; - int reset_frequency; - float reward_scale; - float initial_total_delta; - float current_total_delta; - float delta_progress; - int* stuck_count; - int* grid_indices; - int num_quadrants; - float* quadrant_deltas; - float* current_quadrant_deltas; - float quadrants_solved; - int* complete_quadrants; - int* in_progress_quadrants; - float* volume_deltas; - float* quadrant_volume_deltas; - float* quadrant_centroids; -} Terraform; - -float randf(float min, float max) { - return min + (max - min)*(float)rand()/(float)RAND_MAX; -} - -void perlin_noise(float* map, int width, int height, - float base_frequency, int octaves, int offset_x, int offset_y, float glob_scale) { - float frequencies[octaves]; - for (int i = 0; i < octaves; i++) { - frequencies[i] = base_frequency*pow(2, i); - } - - float min_value = FLT_MAX; - float max_value = FLT_MIN; - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - for (int oct = 0; oct < octaves; oct++) { - float freq = frequencies[oct]; - map[adr] += (1.0/pow(2, oct))*noise2(freq*c + offset_x, freq*r + offset_y); - } - float val = map[adr]; - if (val < min_value) { - min_value = val; - } - if (val > max_value) { - max_value = val; - } - } - } - - float scale = 1.0/(max_value - min_value); - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - int adr = r*width + c; - map[adr] = glob_scale * scale * (map[adr] - min_value); - if (map[adr] < 34.0f) { - map[adr] = 0.0f; - } else { - map[adr] -= 34.0f; - } - } - } -} - -int map_idx(Terraform* env, float x, float y) { - return env->size*(int)y + (int)x; -} - -void calculate_total_delta(Terraform* env) { - env->initial_total_delta = 0.0f; - env->current_total_delta = 0.0f; - // Calculate total volume in original and target maps - float original_volume = 0.0f; - float target_volume = 0.0f; - for (int i = 0; i < env->size * env->size; i++) { - original_volume += env->orig_map[i]; - target_volume += env->target_map[i]; - } - - float scale_factor = target_volume / original_volume; - for (int i = 0; i < env->size * env->size; i++) { - if(env->orig_map[i] * scale_factor > MAX_DIRT_HEIGHT) { - env->orig_map[i] = MAX_DIRT_HEIGHT; - } else { - env->orig_map[i] *= scale_factor; - } - } - - for (int i = 0; i < env->size * env->size; i++) { - float delta = fabsf(env->orig_map[i] - env->target_map[i]); - env->initial_total_delta += delta; - env->quadrant_deltas[env->grid_indices[i]] += delta; - env->quadrant_volume_deltas[env->grid_indices[i]] += (env->orig_map[i] - env->target_map[i]); - env->volume_deltas[env->grid_indices[i]] += (env->orig_map[i] - env->target_map[i]); - } - memcpy(env->current_quadrant_deltas, env->quadrant_deltas, env->num_quadrants*sizeof(float)); - env->current_total_delta = env->initial_total_delta; - env->delta_progress = 0.0f; -} - -void assign_grid_indices(Terraform* env) { - int num_quads_x = (env->size + 10) / 11; - int num_quads_y = (env->size + 10) / 11; - for (int i = 0; i < env->size*env->size; i++) { - int y = i / env->size; - int x = i % env->size; - int quad_x = x / 11; - int quad_y = y / 11; - int grid_index = quad_y * num_quads_x + quad_x; - env->grid_indices[i] = grid_index; - } - env->num_quadrants = num_quads_x * num_quads_y; -} - -void assign_quadrant_centroids(Terraform* env) { - int num_quads_x = (env->size + 10) / 11; - int num_quads_y = (env->size + 10) / 11; - for (int i = 0; i < env->num_quadrants; i++) { - float centroid_x = (i % num_quads_x) * 11 + 5; - float centroid_y = (i / num_quads_x) * 11 + 5; - env->quadrant_centroids[i*2] = centroid_x; - env->quadrant_centroids[i*2 + 1] = centroid_y; - } -} - -void init(Terraform* env) { - env->orig_map = calloc(env->size*env->size, sizeof(float)); - env->map = calloc(env->size*env->size, sizeof(float)); - env->target_map = calloc(env->size*env->size, sizeof(float)); - env->grid_indices = calloc(env->size*env->size, sizeof(int)); - assign_grid_indices(env); - env->quadrant_centroids = calloc(env->num_quadrants*2, sizeof(float)); - assign_quadrant_centroids(env); - env->quadrant_deltas = calloc(env->num_quadrants, sizeof(float)); - env->complete_quadrants = calloc(env->num_quadrants, sizeof(int)); - env->in_progress_quadrants = calloc(env->num_quadrants*(env->num_agents+1), sizeof(int)); - env->current_quadrant_deltas = calloc(env->num_quadrants, sizeof(float)); - env->quadrant_volume_deltas = calloc(env->num_quadrants, sizeof(float)); - env->volume_deltas = calloc(env->num_quadrants, sizeof(float)); - env->agent_logs = calloc(env->num_agents, sizeof(Log)); - // for (int i = 0; i < env->size*env->size; i++) { - // env->target_map[i] = 1; // Initialize all to empty - // } - - // Calculate grid dimensions for quadrants - const int QUADRANT_SIZE = 11; - const int MIN_SPACING = 3; - const int TOTAL_SPACE = QUADRANT_SIZE + MIN_SPACING; - - // Calculate how many quadrants we can fit in each dimension - int num_quadrants_x = (env->size - MIN_SPACING) / TOTAL_SPACE; - int num_quadrants_y = (env->size - MIN_SPACING) / TOTAL_SPACE; - - // Place quadrants in a grid pattern - for (int grid_y = 0; grid_y < num_quadrants_y; grid_y++) { - for (int grid_x = 0; grid_x < num_quadrants_x; grid_x++) { - // Calculate starting position for this quadrant - int start_x = MIN_SPACING + grid_x * TOTAL_SPACE; - int start_y = MIN_SPACING + grid_y * TOTAL_SPACE; - - // Place the 11x11 quadrant - for (int y = 0; y < QUADRANT_SIZE; y++) { - for (int x = 0; x < QUADRANT_SIZE; x++) { - int pos_x = start_x + x; - int pos_y = start_y + y; - if (pos_x < env->size && pos_y < env->size) { - env->target_map[pos_y * env->size + pos_x] = 1; // Mark as target - } - } - } - } - } - env->dozers = calloc(env->num_agents, sizeof(Dozer)); - for (int i = 0; i < env->num_agents; i++) { - env->dozers[i].load_indices = calloc((2*SCOOP_SIZE + 1)*(2*SCOOP_SIZE + 1), sizeof(int)); - for (int j = 0; j < (2*SCOOP_SIZE + 1)*(2*SCOOP_SIZE + 1); j++) { - env->dozers[i].load_indices[j] = -1; - } - env->dozers[i].quadrant_progress = 0.0f; - env->dozers[i].highest_quadrant_progress = 0.0f; - } - clock_gettime(CLOCK_REALTIME, &ts); - unsigned int base_seed = (unsigned int)(ts.tv_nsec ^ ts.tv_sec ^ getpid()); - unsigned int seed1 = base_seed; - unsigned int seed2 = base_seed + 99991; - srand(seed1); - int offset_x1 = rand() % 10000; - int offset_y1 = rand() % 10000; - srand(seed2); - int offset_x2 = rand() % 10000; - int offset_y2 = rand() % 10000; - perlin_noise(env->orig_map, env->size, env->size, 1.0/(env->size / 4.0), 8, offset_x1, offset_y1, MAX_DIRT_HEIGHT+20); - // perlin_noise(env->target_map, env->size, env->size, 1.0/(env->size / 4.0), 8, offset_x2, offset_y2, MAX_DIRT_HEIGHT+55); - env->returns = calloc(env->num_agents, sizeof(float)); - calculate_total_delta(env); - env->stuck_count = calloc(env->num_agents, sizeof(int)); - env->tick = rand() % 512; - env->quadrants_solved = 0.0f; -} - -void free_initialized(Terraform* env) { - free(env->orig_map); - free(env->map); - for (int i = 0; i < env->num_agents; i++) { - free(env->dozers[i].load_indices); - } - free(env->dozers); - free(env->returns); - free(env->target_map); - free(env->stuck_count); - free(env->quadrant_deltas); - free(env->complete_quadrants); - free(env->grid_indices); - free(env->current_quadrant_deltas); - free(env->in_progress_quadrants); - free(env->agent_logs); - free(env->quadrant_volume_deltas); - free(env->volume_deltas); - free(env->quadrant_centroids); -} - -void add_log(Terraform* env, Log* agent_log) { - env->log.perf += agent_log->perf; - env->log.score += agent_log->score; - env->log.episode_length += agent_log->episode_length; - env->log.episode_return += agent_log->episode_return; - env->log.n++; - env->log.quadrant_progress += agent_log->quadrant_progress; -} - -void compute_all_observations(Terraform* env) { - int dialate = 1; - int max_obs = 319; - float (*observations)[max_obs] = (float(*)[max_obs])env->observations; - int channel_diff_offset = (2*VISION+1)*(2*VISION+1); - for (int i = 0; i < env->num_agents; i++) { - int obs_idx = 0; - float* obs = &observations[i][obs_idx]; - int x_offset = env->dozers[i].x - dialate*VISION; - int y_offset = env->dozers[i].y - dialate*VISION; - for (int y = 0; y < 2 * dialate * VISION + 1; y += dialate) { // ROW loop (Y-axis) - for (int x = 0; x < 2 * dialate * VISION + 1; x += dialate) { // COLUMN loop (X-axis) - int map_x = x_offset + x; - int map_y = y_offset + y; - - if (map_x < 0 || map_x >= env->size || map_y < 0 || map_y >= env->size) { - obs[obs_idx] = 0; - obs[obs_idx + channel_diff_offset] = 0; - obs_idx++; - continue; - } - int map_idx = map_y * env->size + map_x; - obs[obs_idx] = ((float)env->map[map_idx]) / MAX_DIRT_HEIGHT; - float diff = ((float)(env->target_map[map_idx] - env->map[map_idx])) / (MAX_DIRT_HEIGHT * 2.0f); - obs[obs_idx + channel_diff_offset] = diff; - obs_idx++; - } - } - obs_idx += channel_diff_offset; - - Dozer* dozer = &env->dozers[i]; - obs[obs_idx++] = dozer->x / env->size; - obs[obs_idx++] = dozer->y / env->size; - obs[obs_idx++] = (dozer->v) / (DOZER_MAX_V); - // This is -5? - obs[obs_idx++] = (dozer->heading) / (2*PI); - obs[obs_idx++] = dozer->load / DOZER_CAPACITY; - // float goal_x = env->quadrant_centroids[dozer->target_quadrant*2]; - // float goal_y = env->quadrant_centroids[dozer->target_quadrant*2+1]; - // float rel_x = goal_x - dozer->x; - // float rel_y = goal_y - dozer->y; - // float max_dist = sqrtf(2) * env->size; - // obs[obs_idx++] = rel_x / max_dist; - // obs[obs_idx++] = rel_y / max_dist; - - // Current and target quadrant - 249 - // obs[obs_idx++] = (float)dozer->target_quadrant / env->num_quadrants; - // obs[obs_idx++] = (float)env->grid_indices[map_idx(env, dozer->x, dozer->y)] / env->num_quadrants; - // relative directions to target quadrant center - 251 - for (int q = 0; q < env->num_quadrants; q++) { - obs[obs_idx++] = env->quadrant_volume_deltas[q] / 121.0f; - } - float location_conv[env->num_quadrants]; - memset(location_conv, 0, env->num_quadrants*sizeof(float)); - location_conv[env->grid_indices[map_idx(env, dozer->x, dozer->y)]] = 1.0f; - memcpy(obs + obs_idx, location_conv, env->num_quadrants*sizeof(float)); - obs_idx += env->num_quadrants; - } -} - -void c_reset(Terraform* env) { - memcpy(env->map, env->orig_map, env->size*env->size*sizeof(float)); - memset(env->observations, 0, env->num_agents*319*sizeof(float)); - memset(env->returns, 0, env->num_agents*sizeof(float)); - env->tick = 0; - env->current_total_delta = env->initial_total_delta; - env->delta_progress = 0.0f; - env->quadrants_solved = 0.0f; - memset(env->stuck_count, 0, env->num_agents*sizeof(int)); - memcpy(env->quadrant_volume_deltas, env->volume_deltas, env->num_quadrants*sizeof(float)); - memset(env->complete_quadrants, 0, env->num_quadrants*sizeof(int)); - - int num_quadrants_to_precomplete = rand() % 5 + 25; // e.g. 30 to 34 - - // Create array of available quadrants - int available[env->num_quadrants]; - int num_available = 0; - for (int i = 0; i < env->num_quadrants; i++) { - if (!env->complete_quadrants[i]) { - available[num_available++] = i; - } - } - - // Complete exactly num_quadrants_to_precomplete quadrants - // for (int i = 0; i < num_quadrants_to_precomplete && num_available > 0; i++) { - // // Pick random quadrant from remaining available ones - // int idx = rand() % num_available; - // int quad = available[idx]; - - // // Complete the quadrant - // for (int j = 0; j < env->size*env->size; j++) { - // if(env->grid_indices[j] == quad) { - // env->map[j] = env->target_map[j]; - // } - // } - - // env->complete_quadrants[quad] = 1; - // env->current_quadrant_deltas[quad] = 0.0f; - // env->quadrant_volume_deltas[quad] = 0.0f; - // env->quadrants_solved++; - - // // Remove used quadrant by swapping with last available one - // available[idx] = available[--num_available]; - // } - - // // adjust remaining volume - // float remaining_target_sum = 0.0f; - // float remaining_map_sum = 0.0f; - // for(int i = 0; i < env->size*env->size; i++) { - // int quad = env->grid_indices[i]; - // if (!env->complete_quadrants[quad]) { - // remaining_target_sum += env->target_map[i]; - // remaining_map_sum += env->map[i]; - // } - // } - // // 2. Compute scale factor - // float scale = (remaining_map_sum > 0.0f) ? (remaining_target_sum / remaining_map_sum) : 1.0f; - // for (int i = 0; i < env->size * env->size; i++) { - // int quad = env->grid_indices[i]; - // if (!env->complete_quadrants[quad]) { - // env->map[i] *= scale; - // if (env->map[i] > MAX_DIRT_HEIGHT) env->map[i] = MAX_DIRT_HEIGHT; - // } - // } - int available_quadrants[env->num_quadrants - (int)env->quadrants_solved]; - int available_quadrants_count = 0; - for (int i = 0; i < env->num_quadrants; i++) { - if (!env->complete_quadrants[i]) { - available_quadrants[available_quadrants_count++] = i; - } - } - for (int i = 0; i < env->num_agents; i++) { - Dozer temp = {0}; - env->agent_logs[i] = (Log){0}; - temp.load_indices = env->dozers[i].load_indices; - env->dozers[i] = temp; - do { - env->dozers[i].x = rand() % env->size; - env->dozers[i].y = rand() % env->size; - } while (env->map[map_idx(env, env->dozers[i].x, env->dozers[i].y)] != 0.0f); - for (int j = 0; j < (2*SCOOP_SIZE + 1)*(2*SCOOP_SIZE + 1); j++) { - env->dozers[i].load_indices[j] = -1; - } - } - compute_all_observations(env); -} - -void illegal_action(Terraform* env, int agent_idx) { - env->rewards[agent_idx] += -0.05f; - env->returns[agent_idx] += -0.05f; - env->agent_logs[agent_idx].episode_return += -0.05f; -} - -float scoop_dirt(Terraform* env, float x, float y, int bucket_atn, int agent_idx, Dozer* dozer){ - int scoop_idx = map_idx(env, x, y); - float map_height = env->map[scoop_idx]; - float target_height = env->target_map[scoop_idx]; - float delta_pre = fabsf(map_height - target_height); - float load_pre = dozer->load; - - if (bucket_atn == 0) { - return 0.0f; - } else if (bucket_atn == 1) { // Load - // Can't load while backing up - if (dozer->v < 0) { - illegal_action(env, agent_idx); - return 0.0f; - } - - if (dozer->load >= DOZER_CAPACITY) { - illegal_action(env, agent_idx); - return 0.0f; - } - // if it aint broken dont fix it penalty - // if (env->complete_quadrants[env->grid_indices[scoop_idx]]) { - // env->rewards[agent_idx] += (-1.0f / (SCOOP_SIZE*2 + 1)); - // env->returns[agent_idx] += (-1.0f / (SCOOP_SIZE*2 + 1)); - // env->agent_logs[agent_idx].episode_return += (-1.0f / (SCOOP_SIZE*2 + 1)); - // env->complete_quadrants[env->grid_indices[scoop_idx]] = 0; - // env->quadrants_solved -= 1.0f; - // } - - // Load up to 1 unit of dirt - float load_amount = 1.0f; - if (map_height <= 1.0f) { - load_amount = map_height; - } - - // Don't overload the bucket - if (dozer->load + load_amount > DOZER_CAPACITY) { - load_amount = DOZER_CAPACITY - dozer->load; - } - - dozer->load += load_amount; - env->map[scoop_idx] -= load_amount; - map_height -= load_amount; - env->quadrant_volume_deltas[env->grid_indices[scoop_idx]] -= load_amount; - } else if (bucket_atn == 2) { // Unload - // Can't unload while moving forward - if (dozer->v > 0) { - illegal_action(env, agent_idx); - return 0.0f; - } - - if (dozer->load == 0) { - illegal_action(env, agent_idx); - return 0.0f; - } - // if it aint broken dont fix it penalty - // if (env->complete_quadrants[env->grid_indices[scoop_idx]]) { - // env->rewards[agent_idx] += (-1.0f / (SCOOP_SIZE*2 + 1)); - // env->returns[agent_idx] += (-1.0f / (SCOOP_SIZE*2 + 1)); - // env->agent_logs[agent_idx].episode_return += (-1.0f / (SCOOP_SIZE*2 + 1)); - // env->complete_quadrants[env->grid_indices[scoop_idx]] = 0; - // env->quadrants_solved -= 1.0f; - // } - - float unload_amount = 1.0f; - if (dozer->load < unload_amount) { - unload_amount = dozer->load; - } - - if (map_height + unload_amount > MAX_DIRT_HEIGHT) { - unload_amount = MAX_DIRT_HEIGHT - map_height; - } - - dozer->load -= unload_amount; - env->map[scoop_idx] += unload_amount; - map_height += unload_amount; - env->quadrant_volume_deltas[env->grid_indices[scoop_idx]] += unload_amount; - } - - // Reward for terraforming towards target map - float delta_post = fabsf(map_height - target_height); - float load_post = dozer->load; - env->current_total_delta += (delta_post - delta_pre); - float normalize_value = (2*SCOOP_SIZE + 1)*(2*SCOOP_SIZE + 1) + 1; - float reward = (delta_pre + env->reward_scale*load_pre) - (delta_post + env->reward_scale*load_post); - reward /= normalize_value; - return reward; -} - -void c_step(Terraform* env) { - env->tick += 1; - if ((env->reset_frequency && env->tick % env->reset_frequency == 0) || env->current_total_delta < 0.01f) { - if(env->current_total_delta < 0.01f) { - for (int i = 0; i < env->num_agents; i++) { - env->rewards[i] = 1.0f; - env->returns[i] = 1.0f; - env->agent_logs[i].episode_return += 1.0f; - } - } - for (int i = 0; i < env->num_agents; i++) { - env->agent_logs[i].episode_length = env->tick; - env->agent_logs[i].score = env->delta_progress; - env->agent_logs[i].perf = env->delta_progress; - add_log(env, &env->agent_logs[i]); - } - c_reset(env); - return; - } - - memset(env->terminals, 0, env->num_agents*sizeof(unsigned char)); - memset(env->rewards, 0, env->num_agents*sizeof(float)); - int (*actions)[3] = (int(*)[3])env->actions; - for (int i = 0; i < env->num_agents; i++) { - env->agent_logs[i].episode_length = env->tick; - Dozer* dozer = &env->dozers[i]; - int* atn = actions[i]; - float accel = ((float)atn[0] - 2.0f) / 2.0f; // Discrete(5) -> [-1, 1] - float steer = ((float)atn[1] - 2.0f) / 10.0f; // Discrete(5) -> [-0.2, 0.2] - int bucket_atn = atn[2]; - - float cx = dozer->x + BUCKET_OFFSET*cosf(dozer->heading); - float cy = dozer->y + BUCKET_OFFSET*sinf(dozer->heading); - float total_change = 0.0f; - int load_idx = 0; - for (int x = cx - SCOOP_SIZE; x < cx + SCOOP_SIZE; x++) { - for (int y = cy - SCOOP_SIZE; y < cy + SCOOP_SIZE; y++) { - if (x < 0 || x >= env->size || y < 0 || y >= env->size) { - env->dozers[i].load_indices[load_idx] = -1; - load_idx++; - continue; - } - env->dozers[i].load_indices[load_idx] = map_idx(env, x, y); - load_idx++; - total_change += scoop_dirt(env, x, y, bucket_atn, i, dozer); - - } - } - env->rewards[i] += total_change; - env->returns[i] += total_change; - env->agent_logs[i].episode_return += total_change; - - dozer->heading += steer; - if (dozer->heading > 2*PI) { - dozer->heading -= 2*PI; - } - if (dozer->heading < 0) { - dozer->heading += 2*PI; - } - - dozer->v += accel; - if (dozer->v > DOZER_MAX_V) { - dozer->v = DOZER_MAX_V; - } - if (dozer->v < -DOZER_MAX_V) { - dozer->v = -DOZER_MAX_V; - } - int idx = map_idx(env, dozer->x, dozer->y); - float dozer_height = env->map[idx]; - - // Raytrace collision - for (int d=0; dv; d++) { - float x = dozer->x + d*cosf(dozer->heading); - float y = dozer->y + d*sinf(dozer->heading); - if (x < 0 || x >= env->size-1 || y < 0 || y >= env->size-1) { - continue; - } - - int dst_idx = map_idx(env, x, y); - float dst_height = env->map[dst_idx]; - if (fabsf(dozer_height - dst_height) > DOZER_STEP_HEIGHT) { - dozer->v = 0; - } - } - - // Box collision around final destination - float dst_x = dozer->x + dozer->v*cosf(dozer->heading); - float dst_y = dozer->y + dozer->v*sinf(dozer->heading); - for (int x=(int)(dst_x-1.0f); x<=(int)(dst_x+1.0f); x++) { - for (int y=(int)(dst_y-1.0f); y<=(int)(dst_y+1.0f); y++) { - if (x < 0 || x >= env->size-1 || y < 0 || y >= env->size-1) { - continue; - } - int dst_idx = map_idx(env, x, y); - float dst_height = env->map[dst_idx]; - if (fabsf(dozer_height - dst_height) > DOZER_STEP_HEIGHT) { - dozer->v = 0; - env->stuck_count[i]++; - } - } - } - - dozer->x += dozer->v*cosf(dozer->heading); - dozer->y += dozer->v*sinf(dozer->heading); - if (dozer->x < 0) { - dozer->x = 0; - } - if (dozer->x >= env->size) { - dozer->x = env->size - 1; - } - if (dozer->y < 0) { - dozer->y = 0; - } - if (dozer->y >= env->size) { - dozer->y = env->size - 1; - } - - // Teleportitis - if (env->tick % 512 == 0) { - do { - env->dozers[i].x = rand() % env->size; - env->dozers[i].y = rand() % env->size; - env->stuck_count[i] = 0; - } while (env->map[map_idx(env, env->dozers[i].x, env->dozers[i].y)] != 0.0f); - } - - } - int marked_to_skip[env->num_agents]; - memset(marked_to_skip, 0, env->num_agents*sizeof(int)); - for(int i = 0; i < env->num_agents; i++) { - if(marked_to_skip[i]) { - continue; - } - // compute delta progress - if (env->initial_total_delta > 0) { - env->delta_progress = 1.0f - (env->current_total_delta / env->initial_total_delta); - env->delta_progress = fmaxf(0.0f, fminf(1.0f, env->delta_progress)); - } - } - - //printf("observations\n"); - compute_all_observations(env); - //int action = env->actions[0]; -} - -void c_close(Terraform* env) { - free_initialized(env); -} - -Mesh* create_heightmap_mesh(float* heightMap, Vector3 size) { - int mapX = size.x; - int mapZ = size.z; - - // NOTE: One vertex per pixel - Mesh* mesh = (Mesh*)calloc(1, sizeof(Mesh)); - mesh->triangleCount = (mapX - 1)*(mapZ - 1)*2; // One quad every four pixels - - mesh->vertexCount = mesh->triangleCount*3; - - mesh->vertices = (float *)RL_MALLOC(mesh->vertexCount*3*sizeof(float)); - mesh->normals = (float *)RL_MALLOC(mesh->vertexCount*3*sizeof(float)); - mesh->texcoords = (float *)RL_MALLOC(mesh->vertexCount*2*sizeof(float)); - mesh->colors = NULL; - UploadMesh(mesh, false); - return mesh; -} - -void update_heightmap_mesh(Mesh* mesh, float* heightMap, Vector3 size) { - int mapX = size.x; - int mapZ = size.z; - - int vCounter = 0; // Used to count vertices float by float - int tcCounter = 0; // Used to count texcoords float by float - int nCounter = 0; // Used to count normals float by float - - //Vector3 scaleFactor = { size.x/(mapX - 1), 1.0f, size.z/(mapZ - 1) }; - Vector3 scaleFactor = { 1.0f, 1.0f, 1.0f}; - - Vector3 vA = { 0 }; - Vector3 vB = { 0 }; - Vector3 vC = { 0 }; - Vector3 vN = { 0 }; - - for (int z = 0; z < mapZ-1; z++) - { - for (int x = 0; x < mapX-1; x++) - { - // Fill vertices array with data - //---------------------------------------------------------- - - // one triangle - 3 vertex - mesh->vertices[vCounter] = (float)x*scaleFactor.x; - mesh->vertices[vCounter + 1] = heightMap[x + z*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 2] = (float)z*scaleFactor.z; - - mesh->vertices[vCounter + 3] = (float)x*scaleFactor.x; - mesh->vertices[vCounter + 4] = heightMap[x + (z + 1)*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z; - - mesh->vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x; - mesh->vertices[vCounter + 7] = heightMap[(x + 1) + z*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 8] = (float)z*scaleFactor.z; - - // Another triangle - 3 vertex - mesh->vertices[vCounter + 9] = mesh->vertices[vCounter + 6]; - mesh->vertices[vCounter + 10] = mesh->vertices[vCounter + 7]; - mesh->vertices[vCounter + 11] = mesh->vertices[vCounter + 8]; - - mesh->vertices[vCounter + 12] = mesh->vertices[vCounter + 3]; - mesh->vertices[vCounter + 13] = mesh->vertices[vCounter + 4]; - mesh->vertices[vCounter + 14] = mesh->vertices[vCounter + 5]; - - mesh->vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x; - mesh->vertices[vCounter + 16] = heightMap[(x + 1) + (z + 1)*mapX]*scaleFactor.y; - mesh->vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z; - vCounter += 18; // 6 vertex, 18 floats - - // Fill texcoords array with data - //-------------------------------------------------------------- - mesh->texcoords[tcCounter] = (float)x/(mapX - 1); - mesh->texcoords[tcCounter + 1] = (float)z/(mapZ - 1); - - mesh->texcoords[tcCounter + 2] = (float)x/(mapX - 1); - mesh->texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1); - - mesh->texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1); - mesh->texcoords[tcCounter + 5] = (float)z/(mapZ - 1); - - mesh->texcoords[tcCounter + 6] = mesh->texcoords[tcCounter + 4]; - mesh->texcoords[tcCounter + 7] = mesh->texcoords[tcCounter + 5]; - - mesh->texcoords[tcCounter + 8] = mesh->texcoords[tcCounter + 2]; - mesh->texcoords[tcCounter + 9] = mesh->texcoords[tcCounter + 3]; - - mesh->texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1); - mesh->texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1); - tcCounter += 12; // 6 texcoords, 12 floats - - // Fill normals array with data - //-------------------------------------------------------------- - for (int i = 0; i < 18; i += 9) - { - vA.x = mesh->vertices[nCounter + i]; - vA.y = mesh->vertices[nCounter + i + 1]; - vA.z = mesh->vertices[nCounter + i + 2]; - - vB.x = mesh->vertices[nCounter + i + 3]; - vB.y = mesh->vertices[nCounter + i + 4]; - vB.z = mesh->vertices[nCounter + i + 5]; - - vC.x = mesh->vertices[nCounter + i + 6]; - vC.y = mesh->vertices[nCounter + i + 7]; - vC.z = mesh->vertices[nCounter + i + 8]; - - vN = Vector3Normalize(Vector3CrossProduct(Vector3Subtract(vB, vA), Vector3Subtract(vC, vA))); - - mesh->normals[nCounter + i] = vN.x; - mesh->normals[nCounter + i + 1] = vN.y; - mesh->normals[nCounter + i + 2] = vN.z; - - mesh->normals[nCounter + i + 3] = vN.x; - mesh->normals[nCounter + i + 4] = vN.y; - mesh->normals[nCounter + i + 5] = vN.z; - - mesh->normals[nCounter + i + 6] = vN.x; - mesh->normals[nCounter + i + 7] = vN.y; - mesh->normals[nCounter + i + 8] = vN.z; - } - - nCounter += 18; // 6 vertex, 18 floats - } - } - - // Upload vertex data to GPU (static mesh) - UpdateMeshBuffer(*mesh, 0, mesh->vertices, mesh->vertexCount * 3 * sizeof(float), 0); // Update vertices - UpdateMeshBuffer(*mesh, 2, mesh->normals, mesh->vertexCount * 3 * sizeof(float), 0); // Update normals -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; -const Color PUFF_BACKGROUND2 = (Color){18, 72, 72, 255}; - -typedef struct Client Client; -struct Client { - Texture2D ball; - Camera3D camera; - Mesh* mesh; - Model model; - Mesh* target_mesh; - Model target_model; - Texture2D texture; - Model dozer; - Shader shader; - Shader target_shader; - Texture2D shader_terrain; - int shader_terrain_loc; - unsigned char *shader_terrain_data; -}; - -Client* make_client(Terraform* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - InitWindow(1080, 720, "PufferLib Terraform"); - SetConfigFlags(FLAG_MSAA_4X_HINT); - SetTargetFPS(60); - Camera3D camera = { 0 }; - // - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) - camera.fovy = 45.0f; // Camera field-of-view Y - camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - camera.position = (Vector3){ 3*env->size/4, env->size, 3*env->size/4}; - camera.target = (Vector3){ env->size/2, 0, env->size/2-1}; - client->camera = camera; - - client->shader = LoadShader( - TextFormat("resources/terraform/shader_%i.vs", GLSL_VERSION), - TextFormat("resources/terraform/shader_%i.fs", GLSL_VERSION) - ); - client->target_shader = LoadShader( - TextFormat("resources/terraform/shader_%i.vs", GLSL_VERSION), - TextFormat("resources/terraform/target_shader_%i.fs", GLSL_VERSION) - ); - - Image img = GenImageColor(env->size, env->size, WHITE); - ImageFormat(&img, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - client->shader_terrain = LoadTextureFromImage(img); - UnloadImage(img); - - client->shader_terrain_loc = GetShaderLocation(client->target_shader, "terrain"); - SetShaderValueTexture(client->target_shader, client->shader_terrain_loc, client->shader_terrain); - - client->shader_terrain_data = calloc(4*env->size*env->size, sizeof(unsigned char)); - - int shader_width_loc = GetShaderLocation(client->target_shader, "width"); - SetShaderValue(client->target_shader, shader_width_loc, &env->size, SHADER_UNIFORM_INT); - - int shader_height_loc = GetShaderLocation(client->target_shader, "height"); - SetShaderValue(client->target_shader, shader_height_loc, &env->size, SHADER_UNIFORM_INT); - - //Image checked = GenImageChecked(env->size, env->size, 2, 2, PUFF_RED, PUFF_CYAN); - img = LoadImage("resources/terraform/perlin.jpg"); - client->texture = LoadTextureFromImage(img); - client->dozer = LoadModel("resources/terraform/dozer.glb"); - UnloadImage(img); - client->mesh = NULL; - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client->mesh); - free(client->shader_terrain_data); - free(client->target_mesh); - free(client); - -} - -void handle_camera_controls(Client* client) { - static Vector2 prev_mouse_pos = {0}; - static bool is_dragging = false; - float camera_move_speed = 0.5f; - - // Handle mouse drag for camera movement - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - prev_mouse_pos = GetMousePosition(); - is_dragging = true; - } - - if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - is_dragging = false; - } - - if (is_dragging) { - Vector2 current_mouse_pos = GetMousePosition(); - Vector2 delta = { - -(current_mouse_pos.x - prev_mouse_pos.x) * camera_move_speed, - (current_mouse_pos.y - prev_mouse_pos.y) * camera_move_speed - }; - - // Apply 45-degree rotation to the movement - // For a -45 degree rotation (clockwise) - float cos45 = -0.7071f; // cos(-45°) - float sin45 = 0.7071f; // sin(-45°) - Vector2 rotated_delta = { - delta.x * cos45 - delta.y * sin45, - delta.x * sin45 + delta.y * cos45 - }; - - // Update camera position (only X and Y) - client->camera.position.z += rotated_delta.x; - client->camera.position.x += rotated_delta.y; - - // Update camera target (only X and Y) - client->camera.target.z += rotated_delta.x; - client->camera.target.x += rotated_delta.y; - - prev_mouse_pos = current_mouse_pos; - } - - // Handle mouse wheel for zoom - float wheel = GetMouseWheelMove(); - if (wheel != 0) { - float zoom_factor = 1.0f - (wheel * 0.1f); - // Calculate the current direction vector from target to position - Vector3 direction = { - client->camera.position.x - client->camera.target.x, - client->camera.position.y - client->camera.target.y, - client->camera.position.z - client->camera.target.z - }; - - // Scale the direction vector by the zoom factor - direction.x *= zoom_factor; - direction.y *= zoom_factor; - direction.z *= zoom_factor; - - // Update the camera position based on the scaled direction - client->camera.position.x = client->camera.target.x + direction.x; - client->camera.position.y = client->camera.target.y + direction.y; - client->camera.position.z = client->camera.target.z + direction.z; - } -} - -void c_render(Terraform* env) { - if (env->client == NULL) { - env->client = make_client(env); - env->client->mesh = create_heightmap_mesh(env->map, (Vector3){env->size, 1, env->size}); - update_heightmap_mesh(env->client->mesh, env->map, (Vector3){env->size, 1, env->size}); - env->client->model = LoadModelFromMesh(*env->client->mesh); - - env->client->target_mesh = create_heightmap_mesh(env->target_map, (Vector3){env->size, 1, env->size}); - update_heightmap_mesh(env->client->target_mesh, env->target_map, (Vector3){env->size, 1, env->size}); - env->client->target_model = LoadModelFromMesh(*env->client->target_mesh); - } - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - Client* client = env->client; - - handle_camera_controls(client); - //Camera3D* camera = &client->camera; - //camera->position = (Vector3){ x+30, z+100.0f, y+30 }; - //camera->target = (Vector3){ x, 0, y-1}; - rlSetBlendFactorsSeparate(RL_SRC_ALPHA, RL_ONE_MINUS_SRC_ALPHA, RL_ONE, RL_ONE, RL_FUNC_ADD, RL_MAX); - - if (env->tick % 10 == 0) { - update_heightmap_mesh(client->mesh, env->map, (Vector3){env->size, 1, env->size}); - update_heightmap_mesh(client->target_mesh, env->target_map, (Vector3){env->size, 1, env->size}); - for (int i = 0; i < env->size*env->size; i++) { - client->shader_terrain_data[4*i] = env->map[i]; - client->shader_terrain_data[4*i+3] = 255; - } - UpdateTexture(client->shader_terrain, client->shader_terrain_data); - SetShaderValueTexture(client->target_shader, env->client->shader_terrain_loc, env->client->shader_terrain); - - } - //client->model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = client->texture; - client->model.materials[0].shader = client->shader; - - //client->target_model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = client->texture; - client->target_model.materials[0].shader = client->target_shader; - - //update_heightmap_mesh(client->mesh, env->map, (Vector3){env->size, 1, env->size}); - //client->model = LoadModelFromMesh(*client->mesh); - - BeginDrawing(); - ClearBackground((Color){143, 86, 29, 255}); - BeginMode3D(client->camera); - /* - for(int i = 0; i < env->size*env->size; i++) { - float height = env->map[i]; - int x = i%env->size; - int z = i/env->size; - DrawCube((Vector3){x, height, z}, 1.0f, 1.0f, 1.0f, DARKGREEN); - DrawCubeWires((Vector3){x, height, z}, 1.0f, 1.0f, 1.0f, MAROON); - } - */ - - BeginShaderMode(client->shader); - DrawModel(client->model, (Vector3){0, 0, 0}, 1.0f, (Color){156, 50, 20, 255}); - EndShaderMode(); - rlDisableDepthTest(); // Add this line - - BeginBlendMode(RL_BLEND_CUSTOM_SEPARATE); // Add this line - BeginShaderMode(client->target_shader); - DrawModel(client->target_model, (Vector3){0, 0, 0}, 1.0f, (Color){156, 50, 20, 255}); - EndShaderMode(); - EndBlendMode(); - rlEnableDepthTest(); // Add this line - // for(int i = 0; i < env->size; i += 11){ - // // draw grid lines every 11 units - // DrawLine3D((Vector3){i, 0, 0}, (Vector3){i, 0, env->size-1}, RED); - // DrawLine3D((Vector3){0, 0, i}, (Vector3){env->size-1, 0, i}, RED); - // } - for (int i = 0; i < env->num_agents; i++) { - Dozer* dozer = &env->dozers[i]; - int x = (int)dozer->x; - int z = (int)dozer->y; - int size = (int)env->size; - - // Get height from map using correct indexing - float y = env->map[z * size + x] + 0.5f; - float yy = y; - rlPushMatrix(); - rlTranslatef(dozer->x, y, dozer->y); - rlRotatef(-90.f - dozer->heading*RAD2DEG, 0, 1, 0); - // if(i ==0 ){ - // DrawCube((Vector3){0,50,0}, 10.0f, 10.0f, 10.0f, RED); - // } - DrawModel(client->dozer, (Vector3){0, 0, 0}, 0.25f, WHITE); - rlPopMatrix(); - // DrawCube((Vector3){dozer->x, y, dozer->y}, 1.0f, 1.0f, 1.0f, PUFF_WHITE); - if(IsKeyDown(KEY_LEFT_CONTROL) && i == 0) { - int dialate = 1; - int x_offset = env->dozers[i].x - dialate*VISION; - int y_offset = env->dozers[i].y - dialate*VISION; - for (int x = 0; x < 2*dialate*VISION + 1; x+=dialate) { - for (int y = 0; y < 2*dialate*VISION + 1; y+=dialate) { - if(x_offset + x < 0 || x_offset + x >= env->size || y_offset + y < 0 || y_offset + y >= env->size) { - continue; - } - float obs_x = x_offset + x; - float obs_y = y_offset + y; - Color clr = PUFF_WHITE; - int idx = y*(2*VISION+1) + x; - int obs_idx = 319*i + 121 + idx; - if(env->observations[obs_idx] == 1.0f) { - clr = GREEN; - } else if(env->observations[obs_idx] == 0.66f) { - clr = PUFF_RED; - } else if(env->observations[obs_idx] == 0.33f) { - clr = YELLOW; - } - for(int j = 0; j < (2*SCOOP_SIZE + 1)*(2*SCOOP_SIZE + 1); j++) { - if(env->dozers[i].load_indices[j] == map_idx(env, x_offset + x, y_offset + y)){ - clr = BLUE; - break; - } - } - DrawCube((Vector3){x_offset + x, yy, y_offset + y}, 0.5f, 0.5f, 0.5f, clr); - } - } - int step = 1; - for (int k = 0; k < env->size; k += step) { - for (int l = 0; l < env->size; l += step) { - int idx = k * env->size + l; - Color color = RED; - if (env->grid_indices[idx] == env->dozers[i].target_quadrant) { - color = GREEN; - DrawSphere((Vector3){l, 0, k}, 0.1f, color); - - } - } - } - for(int j = 0; j < env->num_quadrants; j++){ - Color color = PURPLE; - if(env->quadrant_volume_deltas[j] > 0.0f) { - color = RED; - } else if(env->quadrant_volume_deltas[j] < 0.0f) { - color = GREEN; - } - DrawLine3D((Vector3){env->quadrant_centroids[j*2], 0, env->quadrant_centroids[j*2+1]}, (Vector3){env->dozers[i].x, 0, env->dozers[i].y}, color); - } - } - - - } - EndMode3D(); - //DrawText(TextFormat("Dozer x: %f", x), 10, 150, 20, PUFF_WHITE); - DrawText(TextFormat("score: %f", env->delta_progress), 10, 170, 20, PUFF_WHITE); - DrawText(TextFormat("load: %f", env->dozers[0].load), 10, 190, 20, PUFF_WHITE); - DrawText(TextFormat("Timestep: %d", env->tick), 10, 210, 20, PUFF_WHITE); - DrawText(TextFormat("Current Quadrant: %d", env->grid_indices[map_idx(env, env->dozers[0].x, env->dozers[0].y)]), 10, 230, 20, PUFF_WHITE); - DrawFPS(10, 10); - EndDrawing(); -} diff --git a/pufferlib/ocean/terraform/terraform.py b/pufferlib/ocean/terraform/terraform.py deleted file mode 100644 index 9bb886d17..000000000 --- a/pufferlib/ocean/terraform/terraform.py +++ /dev/null @@ -1,85 +0,0 @@ -'''A simple sample environment. Use this as a template for your own envs.''' - -import gymnasium -import numpy as np -import random -import pufferlib -from pufferlib.ocean.terraform import binding -import time -OBS_SIZE = 11 - -class Terraform(pufferlib.PufferEnv): - def __init__(self, num_envs=1, num_agents=8, map_size=64, - render_mode=None, log_interval=32, buf=None, seed=0, reset_frequency=8192, - reward_scale=0.01): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(2*OBS_SIZE*OBS_SIZE + 5 + 36*2,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.MultiDiscrete([5, 5, 3], dtype=np.int32) - self.render_mode = render_mode - self.num_agents = num_envs*num_agents - self.log_interval = log_interval - self.reset_frequency = reset_frequency - self.reward_scale = reward_scale - super().__init__(buf) - c_envs = [] - for i in range(num_envs): - c_env = binding.env_init( - self.observations[i*num_agents:(i+1)*num_agents], - self.actions[i*num_agents:(i+1)*num_agents], - self.rewards[i*num_agents:(i+1)*num_agents], - self.terminals[i*num_agents:(i+1)*num_agents], - self.truncations[i*num_agents:(i+1)*num_agents], - seed, - size=map_size, - num_agents=num_agents, - reset_frequency=reset_frequency, - reward_scale=reward_scale, - ) - c_envs.append(c_env) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=None): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.tick += 1 - self.actions[:] = actions - binding.vec_step(self.c_envs) - - episode_returns = self.rewards[self.terminals] - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - TIME = 10 - env = Terraform(num_envs=512, num_agents=1, render_mode='human', map_size=64, seed=0) - actions = np.random.randint(0, 5, (512, 3)) # Changed from the stack approach - - - import time - steps = 0 - start = time.time() - while time.time() - start < TIME: - env.step(actions) - steps += 2048 - - print('SPS:', env.num_agents * steps / (time.time() - start)) - - - - - diff --git a/pufferlib/ocean/tetris/binding.c b/pufferlib/ocean/tetris/binding.c deleted file mode 100644 index 5e4007516..000000000 --- a/pufferlib/ocean/tetris/binding.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "tetris.h" - -#define Env Tetris -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->n_rows = unpack(kwargs, "n_rows"); - env->n_cols = unpack(kwargs, "n_cols"); - env->deck_size = unpack(kwargs, "deck_size"); - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "ep_length", log->ep_length); - assign_to_dict(dict, "ep_return", log->ep_return); - assign_to_dict(dict, "avg_combo", log->avg_combo); - assign_to_dict(dict, "lines_deleted", log->lines_deleted); - - assign_to_dict(dict, "atn_frac_soft_drop", log->atn_frac_soft_drop); - assign_to_dict(dict, "atn_frac_hard_drop", log->atn_frac_hard_drop); - assign_to_dict(dict, "atn_frac_rotate", log->atn_frac_rotate); - return 0; -} \ No newline at end of file diff --git a/pufferlib/ocean/tetris/tetris.c b/pufferlib/ocean/tetris/tetris.c deleted file mode 100644 index 14995f9ff..000000000 --- a/pufferlib/ocean/tetris/tetris.c +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include "tetris.h" -#include "puffernet.h" -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -void demo() { - Tetris env = { - .n_rows = 20, - .n_cols = 10, - .deck_size=3, - }; - allocate(&env); - env.client = make_client(&env); - c_reset(&env); - - Weights* weights = load_weights("resources/tetris/tetris_weights.bin", 163208); - int logit_sizes[1] = {7}; - LinearLSTM* net = make_linearlstm(weights, 1, 234, logit_sizes, 1); - - while (!WindowShouldClose()) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)){ - env.actions[0] = 1; - } - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)){ - env.actions[0] = 2; - } - if (IsKeyPressed(KEY_UP) || IsKeyDown(KEY_W)) { - env.actions[0] = 3; - } - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) { - env.actions[0] = 4; - } - if (IsKeyPressed(KEY_SPACE)) { - env.actions[0] = 5; - } - if (IsKeyPressed(KEY_C)) { - env.actions[0] = 6; - } - } else { - forward_linearlstm(net, env.observations, env.actions); - } - - c_step(&env); - env.actions[0] = 0; - c_render(&env); - } - free_linearlstm(net); - free_allocated(&env); - close_client(env.client); -} - -int main() { - demo(); -} diff --git a/pufferlib/ocean/tetris/tetris.h b/pufferlib/ocean/tetris/tetris.h deleted file mode 100644 index baadd69af..000000000 --- a/pufferlib/ocean/tetris/tetris.h +++ /dev/null @@ -1,612 +0,0 @@ -#include "raylib.h" -#include "tetrominoes.h" -#include -#include -#include -#include -#include -#include -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -#define HALF_LINEWIDTH 1 -#define SQUARE_SIZE 32 - -#define ACTION_NO_OP 0 -#define ACTION_LEFT 1 -#define ACTION_RIGHT 2 -#define ACTION_ROTATE 3 -#define ACTION_SOFT_DROP 4 -#define ACTION_HARD_DROP 5 -#define ACTION_HOLD 6 - -#define TICKS_FALL 4 // how many ticks before the tetromino naturally falls down of one square -#define MAX_TICKS 10000 -#define PERSONAL_BEST 12565 -#define SCORE_SOFT_DROP 1 -#define REWARD_SOFT_DROP 0.01f -#define SCORE_HARD_DROP 2 -#define REWARD_HARD_DROP 0.02f -#define REWARD_INVALID_ACTION 0.0f - -const int SCORE_COMBO[5] = {0, 100, 300, 500, 1000}; -const float REWARD_COMBO[5] = {0, 0.1, 0.3, 0.5, 1.0}; - -typedef struct Log { - float perf; - float score; - float ep_length; - float ep_return; - float lines_deleted; - float avg_combo; - float atn_frac_soft_drop; - float atn_frac_hard_drop; - float atn_frac_rotate; - float n; -} Log; - -typedef struct Client { - int total_cols; - int total_rows; - int ui_rows; - int deck_rows; - int preview_target_rotation; - int preview_target_col; -} Client; - -typedef struct Tetris { - Client *client; - Log log; - float *observations; - int *actions; - float *rewards; - unsigned char *terminals; - - int n_rows; - int n_cols; - int deck_size; - int *grid; - int tick; - int tick_fall; - int score; - int can_swap; - - int *tetromino_deck; - int hold_tetromino; - int cur_position_in_deck; - int cur_tetromino; - int cur_tetromino_row; - int cur_tetromino_col; - int cur_tetromino_rot; - - float ep_return; - int lines_deleted; - int count_combos; - int atn_count_hard_drop; - int atn_count_soft_drop; - int atn_count_rotate; -} Tetris; - -void init(Tetris *env) { - env->grid = (int *)calloc(env->n_rows * env->n_cols, sizeof(int)); - env->tetromino_deck = calloc(env->deck_size, sizeof(int)); -} - -void allocate(Tetris *env) { - init(env); - env->observations = (float *)calloc( - env->n_cols * env->n_rows + 6 + NUM_TETROMINOES * env->deck_size + NUM_TETROMINOES, sizeof(float)); - env->actions = (int *)calloc(1, sizeof(int)); - env->rewards = (float *)calloc(1, sizeof(float)); - env->terminals = (unsigned char *)calloc(1, sizeof(unsigned char)); -} - -void c_close(Tetris *env) { - free(env->grid); - free(env->tetromino_deck); -} - -void free_allocated(Tetris *env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void add_log(Tetris *env) { - env->log.score += env->score; - env->log.perf += env->score / ((float)PERSONAL_BEST); - env->log.ep_length += env->tick; - env->log.ep_return += env->ep_return; - env->log.lines_deleted += env->lines_deleted; - env->log.avg_combo += env->count_combos > 0 ? ((float)env->lines_deleted) / ((float)env->count_combos) : 1.0f; - env->log.atn_frac_hard_drop += env->atn_count_hard_drop / ((float)env->tick); - env->log.atn_frac_soft_drop += env->atn_count_soft_drop / ((float)env->tick); - env->log.atn_frac_rotate += env->atn_count_rotate / ((float)env->tick); - env->log.n += 1; -} - -void compute_observations(Tetris *env) { - memset(env->observations, 0.0, - (env->n_cols * env->n_rows + 6 + NUM_TETROMINOES * env->deck_size + NUM_TETROMINOES) * sizeof(float)); - - // content of the grid: 1st channel is the grid, 2nd channel is the - for (int i = 0; i < env->n_cols * env->n_rows; i++) { - env->observations[i] = env->grid[i] > 0; - } - - for (int r = 0; r < SIZE; r++) { - for (int c = 0; c < SIZE; c++) { - if (TETROMINOES[env->cur_tetromino][env->cur_tetromino_rot][r][c] == 1) { - env->observations[(env->cur_tetromino_row + r) * env->n_cols + c + env->cur_tetromino_col] = 2; - } - } - } - int offset = env->n_cols * env->n_rows; - env->observations[offset] = env->tick / ((float)MAX_TICKS); - env->observations[offset + 1] = env->tick_fall / ((float)TICKS_FALL); - env->observations[offset + 2] = env->cur_tetromino_row / ((float)env->n_rows); - env->observations[offset + 3] = env->cur_tetromino_col / ((float)env->n_cols); - env->observations[offset + 4] = env->cur_tetromino_rot; - env->observations[offset + 5] = env->can_swap; - - // deck, one hot endoded - int tetromino_id; - for (int j = 0; j < env->deck_size; j++) { - tetromino_id = env->tetromino_deck[(env->cur_position_in_deck + j) % env->deck_size]; - env->observations[offset + 4 + j * NUM_TETROMINOES + tetromino_id] = 1; - } - - // hold, one hot endoded - if (env->hold_tetromino > -1) { - env->observations[offset + 4 + env->deck_size * NUM_TETROMINOES + env->hold_tetromino] = 1; - } -} - -void restore_grid(Tetris *env) { memset(env->grid, 0, env->n_rows * env->n_cols * sizeof(int)); } - -void initialize_deck(Tetris *env) { - for (int i = 0; i < env->deck_size; i++) { - env->tetromino_deck[i] = rand() % NUM_TETROMINOES; - } - env->cur_position_in_deck = 0; - env->cur_tetromino = env->tetromino_deck[env->cur_position_in_deck]; -} - -void spawn_new_tetromino(Tetris *env) { - env->tetromino_deck[env->cur_position_in_deck] = rand() % NUM_TETROMINOES; - env->cur_position_in_deck = (env->cur_position_in_deck + 1) % env->deck_size; - env->cur_tetromino = env->tetromino_deck[env->cur_position_in_deck]; - env->cur_tetromino_rot = 0; - env->cur_tetromino_col = env->n_cols / 2; - env->cur_tetromino_row = 0; - env->tick_fall = 0; -} - -bool can_spawn_new_tetromino(Tetris *env) { - int next_tetromino = env->tetromino_deck[(env->cur_position_in_deck + 1) % env->deck_size]; - for (int c = 0; c < TETROMINOES_FILLS_COL[next_tetromino][0]; c++) { - for (int r = 0; r < TETROMINOES_FILLS_ROW[next_tetromino][0]; r++) { - if ((env->grid[r * env->n_cols + c + env->n_cols / 2] > 0) && (TETROMINOES[next_tetromino][0][r][c] == 1)) { - return false; - } - } - } - return true; -} - -bool can_soft_drop(Tetris *env) { - if (env->cur_tetromino_row == (env->n_rows - TETROMINOES_FILLS_ROW[env->cur_tetromino][env->cur_tetromino_rot])) { - return false; - } - for (int c = 0; c < TETROMINOES_FILLS_COL[env->cur_tetromino][env->cur_tetromino_rot]; c++) { - for (int r = 0; r < TETROMINOES_FILLS_ROW[env->cur_tetromino][env->cur_tetromino_rot]; r++) { - if ((env->grid[(r + env->cur_tetromino_row + 1) * env->n_cols + c + env->cur_tetromino_col] > 0) && - (TETROMINOES[env->cur_tetromino][env->cur_tetromino_rot][r][c] == 1)) { - return false; - } - } - } - return true; -} - -bool can_go_left(Tetris *env) { - if (env->cur_tetromino_col == 0) { - return false; - } - for (int c = 0; c < TETROMINOES_FILLS_COL[env->cur_tetromino][env->cur_tetromino_rot]; c++) { - for (int r = 0; r < TETROMINOES_FILLS_ROW[env->cur_tetromino][env->cur_tetromino_rot]; r++) { - if ((env->grid[(r + env->cur_tetromino_row) * env->n_cols + c + env->cur_tetromino_col - 1] > 0) && - (TETROMINOES[env->cur_tetromino][env->cur_tetromino_rot][r][c] == 1)) { - return false; - } - } - } - return true; -} - -bool can_go_right(Tetris *env) { - if (env->cur_tetromino_col == (env->n_cols - TETROMINOES_FILLS_COL[env->cur_tetromino][env->cur_tetromino_rot])) { - return false; - } - for (int c = 0; c < TETROMINOES_FILLS_COL[env->cur_tetromino][env->cur_tetromino_rot]; c++) { - for (int r = 0; r < TETROMINOES_FILLS_ROW[env->cur_tetromino][env->cur_tetromino_rot]; r++) { - if ((env->grid[(r + env->cur_tetromino_row) * env->n_cols + c + env->cur_tetromino_col + 1] > 0) && - (TETROMINOES[env->cur_tetromino][env->cur_tetromino_rot][r][c] == 1)) { - return false; - } - } - } - return true; -} - -bool can_hold(Tetris *env) { - if (env->can_swap == 0) { - return false; - } - if (env->hold_tetromino == -1) { - return true; - } - for (int c = 0; c < TETROMINOES_FILLS_COL[env->hold_tetromino][env->cur_tetromino_rot]; c++) { - for (int r = 0; r < TETROMINOES_FILLS_ROW[env->hold_tetromino][env->cur_tetromino_rot]; r++) { - if ((env->grid[(r + env->cur_tetromino_row) * env->n_cols + c + env->cur_tetromino_col + 1] > 0) && - (TETROMINOES[env->hold_tetromino][env->cur_tetromino_rot][r][c] == 1)) { - return false; - } - } - } - return true; -} - -bool can_rotate(Tetris *env) { - int next_rot = (env->cur_tetromino_rot + 1) % NUM_ROTATIONS; - if (env->cur_tetromino_col > (env->n_cols - TETROMINOES_FILLS_COL[env->cur_tetromino][next_rot])) { - return false; - } - if (env->cur_tetromino_row > (env->n_rows - TETROMINOES_FILLS_ROW[env->cur_tetromino][next_rot])) { - return false; - } - for (int c = 0; c < TETROMINOES_FILLS_COL[env->cur_tetromino][next_rot]; c++) { - for (int r = 0; r < TETROMINOES_FILLS_ROW[env->cur_tetromino][next_rot]; r++) { - if ((env->grid[(r + env->cur_tetromino_row) * env->n_cols + c + env->cur_tetromino_col] > 0) && - (TETROMINOES[env->cur_tetromino][next_rot][r][c] == 1)) { - return false; - } - } - } - return true; -} - -bool is_full_row(Tetris *env, int row) { - for (int c = 0; c < env->n_cols; c++) { - if (env->grid[row * env->n_cols + c] == 0) { - return false; - } - } - return true; -} - -void clear_row(Tetris *env, int row) { - for (int r = row; r > 0; r--) { - for (int c = 0; c < env->n_cols; c++) { - env->grid[r * env->n_cols + c] = env->grid[(r - 1) * env->n_cols + c]; - } - } - for (int c = 0; c < env->n_cols; c++) { - env->grid[c] = 0; - } -} - -void c_reset(Tetris *env) { - env->score = 0; - env->hold_tetromino = -1; - env->tick = 0; - env->tick_fall = 0; - env->can_swap = 1; - - env->ep_return = 0.0; - env->count_combos = 0; - env->lines_deleted = 0; - env->atn_count_hard_drop = 0; - env->atn_count_soft_drop = 0; - env->atn_count_rotate = 0; - - restore_grid(env); - initialize_deck(env); - spawn_new_tetromino(env); - compute_observations(env); -} - -void place_tetromino(Tetris *env) { - int row_to_check = env->cur_tetromino_row + TETROMINOES_FILLS_ROW[env->cur_tetromino][env->cur_tetromino_rot] - 1; - int lines_deleted = 0; - env->can_swap = 1; - - for (int c = 0; c < TETROMINOES_FILLS_COL[env->cur_tetromino][env->cur_tetromino_rot]; - c++) { // Fill the main grid with the tetromino - for (int r = 0; r < TETROMINOES_FILLS_ROW[env->cur_tetromino][env->cur_tetromino_rot]; r++) { - if (TETROMINOES[env->cur_tetromino][env->cur_tetromino_rot][r][c] == 1) { - env->grid[(r + env->cur_tetromino_row) * env->n_cols + c + env->cur_tetromino_col] = - env->cur_tetromino + 1; - } - } - } - for (int r = 0; r < TETROMINOES_FILLS_ROW[env->cur_tetromino][env->cur_tetromino_rot]; - r++) { // Proceed to delete the complete rows - if (is_full_row(env, row_to_check)) { - clear_row(env, row_to_check); - lines_deleted += 1; - } else { - row_to_check -= 1; - } - } - if (lines_deleted > 0) { - env->count_combos += 1; - env->lines_deleted += lines_deleted; - env->score += SCORE_COMBO[lines_deleted]; - env->rewards[0] += REWARD_COMBO[lines_deleted]; - env->ep_return += REWARD_COMBO[lines_deleted]; - } - if ((!can_spawn_new_tetromino(env)) || (env->tick >= MAX_TICKS)) { - env->terminals[0] = 1; - add_log(env); - c_reset(env); - } else { - spawn_new_tetromino(env); - } -} - -void c_step(Tetris *env) { - env->terminals[0] = 0; - env->rewards[0] = 0.0; - env->tick += 1; - env->tick_fall += 1; - int action = env->actions[0]; - - if (action == ACTION_LEFT) { - if (can_go_left(env)) { - env->cur_tetromino_col -= 1; - } else { - env->rewards[0] += REWARD_INVALID_ACTION; - env->ep_return += REWARD_INVALID_ACTION; - // action = ACTION_HARD_DROP; - } - } - if (action == ACTION_RIGHT) { - if (can_go_right(env)) { - env->cur_tetromino_col += 1; - } else { - env->rewards[0] += REWARD_INVALID_ACTION; - env->ep_return += REWARD_INVALID_ACTION; - // action = ACTION_HARD_DROP; - } - } - if (action == ACTION_ROTATE) { - env->atn_count_rotate += 1; - if (can_rotate(env)) { - env->cur_tetromino_rot = (env->cur_tetromino_rot + 1) % NUM_ROTATIONS; - } else { - env->rewards[0] += REWARD_INVALID_ACTION; - env->ep_return += REWARD_INVALID_ACTION; - // action = ACTION_HARD_DROP; - } - } - if (action == ACTION_SOFT_DROP) { - env->atn_count_soft_drop += 1; - if (can_soft_drop(env)) { - env->cur_tetromino_row += 1; - env->score += SCORE_SOFT_DROP; - env->rewards[0] += REWARD_SOFT_DROP; - env->ep_return += REWARD_SOFT_DROP; - } else { - env->rewards[0] += REWARD_INVALID_ACTION; - env->ep_return += REWARD_INVALID_ACTION; - // action = ACTION_HARD_DROP; - } - } - if (action == ACTION_HOLD) { - if (can_hold(env)) { - int t1 = env->cur_tetromino; - int t2 = env->hold_tetromino; - if (t2 == -1) { - spawn_new_tetromino(env); - env->hold_tetromino = t1; - env->can_swap = 0; - } else { - env->cur_tetromino = t2; - env->tetromino_deck[env->cur_position_in_deck] = t2; - env->hold_tetromino = t1; - env->can_swap = 0; - env->cur_tetromino_rot = 0; - env->cur_tetromino_col = env->n_cols / 2; - env->cur_tetromino_row = 0; - env->tick_fall = 0; - } - } else { - env->rewards[0] += REWARD_INVALID_ACTION; - env->ep_return += REWARD_INVALID_ACTION; - // action = ACTION_HARD_DROP; - } - } - if (action == ACTION_HARD_DROP) { - env->atn_count_hard_drop += 1; - while (can_soft_drop(env)) { - env->cur_tetromino_row += 1; - env->score += SCORE_HARD_DROP; - env->rewards[0] += REWARD_HARD_DROP; - env->ep_return += REWARD_HARD_DROP; - } - place_tetromino(env); - } - if (env->tick_fall == TICKS_FALL) { - env->tick_fall = 0; - if (!can_soft_drop(env)) { - place_tetromino(env); - } else { - env->cur_tetromino_row += 1; - } - } - compute_observations(env); -} - -Client *make_client(Tetris *env) { - Client *client = (Client *)calloc(1, sizeof(Client)); - client->ui_rows = 1; - client->deck_rows = SIZE; - client->total_rows = 1 + client->ui_rows + 1 + client->deck_rows + 1 + env->n_rows + 1; - client->total_cols = max(1 + env->n_cols + 1, 1 + 3 * (env->deck_size - 1)); - client->preview_target_col = env->n_cols / 2; - client->preview_target_rotation = 0; - InitWindow(SQUARE_SIZE * client->total_cols, SQUARE_SIZE * client->total_rows, "PufferLib Tetris"); - SetTargetFPS(10); - return client; -} - -void close_client(Client *client) { - CloseWindow(); - free(client); -} - -Color BORDER_COLOR = (Color){100, 100, 100, 255}; -Color DASH_COLOR = (Color){80, 80, 80, 255}; -Color DASH_COLOR_BRIGHT = (Color){150, 150, 150, 255}; -Color DASH_COLOR_DARK = (Color){50, 50, 50, 255}; - -void c_render(Tetris *env) { - if (env->client == NULL) { - env->client = make_client(env); - } - Client *client = env->client; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - if (IsKeyPressed(KEY_TAB)) { - ToggleFullscreen(); - } - - BeginDrawing(); - ClearBackground(BLACK); - int x, y; - Color color; - - // outer grid - for (int r = 0; r < client->total_rows; r++) { - for (int c = 0; c < client->total_cols; c++) { - x = c * SQUARE_SIZE; - y = r * SQUARE_SIZE; - if ((c == 0) || (c == client->total_cols - 1) || - ((r >= 1 + client->ui_rows + 1) && (r < 1 + client->ui_rows + 1 + client->deck_rows)) || - ((r >= 1 + client->ui_rows + 1 + client->deck_rows + 1) && (c >= env->n_rows)) || (r == 0) || - (r == 1 + client->ui_rows) || (r == 1 + client->ui_rows + 1 + client->deck_rows) || - (r == client->total_rows - 1)) { - DrawRectangle(x + HALF_LINEWIDTH, y + HALF_LINEWIDTH, SQUARE_SIZE - 2 * HALF_LINEWIDTH, - SQUARE_SIZE - 2 * HALF_LINEWIDTH, BORDER_COLOR); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, DASH_COLOR_DARK); - DrawRectangle(x - HALF_LINEWIDTH, y + SQUARE_SIZE - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, - DASH_COLOR_DARK); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, DASH_COLOR_DARK); - DrawRectangle(x + SQUARE_SIZE - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, - DASH_COLOR_DARK); - } - } - } - // main grid - for (int r = 0; r < env->n_rows; r++) { - for (int c = 0; c < env->n_cols; c++) { - x = (c + 1) * SQUARE_SIZE; - y = (1 + client->ui_rows + 1 + client->deck_rows + 1 + r) * SQUARE_SIZE; - color = - (env->grid[r * env->n_cols + c] == 0) ? BLACK : TETROMINOES_COLORS[env->grid[r * env->n_cols + c] - 1]; - DrawRectangle(x + HALF_LINEWIDTH, y + HALF_LINEWIDTH, SQUARE_SIZE - 2 * HALF_LINEWIDTH, - SQUARE_SIZE - 2 * HALF_LINEWIDTH, color); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, DASH_COLOR); - DrawRectangle(x - HALF_LINEWIDTH, y + SQUARE_SIZE - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, - DASH_COLOR); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, DASH_COLOR); - DrawRectangle(x + SQUARE_SIZE - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, - DASH_COLOR); - } - } - - // current tetromino - for (int r = 0; r < SIZE; r++) { - for (int c = 0; c < SIZE; c++) { - x = (c + env->cur_tetromino_col + 1) * SQUARE_SIZE; - y = (1 + client->ui_rows + 1 + client->deck_rows + 1 + r + env->cur_tetromino_row) * SQUARE_SIZE; - - if (TETROMINOES[env->cur_tetromino][env->cur_tetromino_rot][r][c] == 1) { - color = TETROMINOES_COLORS[env->cur_tetromino]; - DrawRectangle(x + HALF_LINEWIDTH, y + HALF_LINEWIDTH, SQUARE_SIZE - 2 * HALF_LINEWIDTH, - SQUARE_SIZE - 2 * HALF_LINEWIDTH, color); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, DASH_COLOR); - DrawRectangle(x - HALF_LINEWIDTH, y + SQUARE_SIZE - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, - DASH_COLOR); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, DASH_COLOR); - DrawRectangle(x + SQUARE_SIZE - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, - DASH_COLOR); - } - } - } - - // Deck grid - int tetromino_id; - for (int i = 0; i < env->deck_size - 1; i++) { - tetromino_id = env->tetromino_deck[(env->cur_position_in_deck + 1 + i) % env->deck_size]; - for (int r = 0; r < SIZE; r++) { - for (int c = 0; c < 2; c++) { - x = (c + 1 + 3 * i) * SQUARE_SIZE; - y = (1 + client->ui_rows + 1 + r) * SQUARE_SIZE; - int r_offset = (SIZE - TETROMINOES_FILLS_ROW[tetromino_id][0]); - if (r < r_offset) { - color = BLACK; - } else { - color = - (TETROMINOES[tetromino_id][0][r - r_offset][c] == 0) ? BLACK : TETROMINOES_COLORS[tetromino_id]; - } - DrawRectangle(x + HALF_LINEWIDTH, y + HALF_LINEWIDTH, SQUARE_SIZE - 2 * HALF_LINEWIDTH, - SQUARE_SIZE - 2 * HALF_LINEWIDTH, color); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, - DASH_COLOR_BRIGHT); - DrawRectangle(x - HALF_LINEWIDTH, y + SQUARE_SIZE - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, - DASH_COLOR_BRIGHT); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, - DASH_COLOR_BRIGHT); - DrawRectangle(x + SQUARE_SIZE - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, - DASH_COLOR_BRIGHT); - } - } - } - - // hold tetromino - for (int r = 0; r < SIZE; r++) { - for (int c = 0; c < 2; c++) { - x = (client->total_cols - 3 + c) * SQUARE_SIZE; - y = (1 + client->ui_rows + 1 + r) * SQUARE_SIZE; - if (env->hold_tetromino > -1) { - int r_offset = (SIZE - TETROMINOES_FILLS_ROW[env->hold_tetromino][0]); - if (r < r_offset) { - color = BLACK; - } else { - color = (env->hold_tetromino > -1) && (TETROMINOES[env->hold_tetromino][0][r - r_offset][c] == 0) - ? BLACK - : TETROMINOES_COLORS[env->hold_tetromino]; - } - } else { - color = BLACK; - } - DrawRectangle(x + HALF_LINEWIDTH, y + HALF_LINEWIDTH, SQUARE_SIZE - 2 * HALF_LINEWIDTH, - SQUARE_SIZE - 2 * HALF_LINEWIDTH, color); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, DASH_COLOR_BRIGHT); - DrawRectangle(x - HALF_LINEWIDTH, y + SQUARE_SIZE - HALF_LINEWIDTH, SQUARE_SIZE, 2 * HALF_LINEWIDTH, - DASH_COLOR_BRIGHT); - DrawRectangle(x - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, DASH_COLOR_BRIGHT); - DrawRectangle(x + SQUARE_SIZE - HALF_LINEWIDTH, y - HALF_LINEWIDTH, 2 * HALF_LINEWIDTH, SQUARE_SIZE, - DASH_COLOR_BRIGHT); - } - } - // Draw UI - DrawText(TextFormat("Score: %i", env->score), SQUARE_SIZE + 4, SQUARE_SIZE + 4, 30, (Color){255, 160, 160, 255}); - EndDrawing(); -} diff --git a/pufferlib/ocean/tetris/tetris.py b/pufferlib/ocean/tetris/tetris.py deleted file mode 100644 index 6834b1932..000000000 --- a/pufferlib/ocean/tetris/tetris.py +++ /dev/null @@ -1,87 +0,0 @@ -import gymnasium -import numpy as np -import pufferlib -from pufferlib.ocean.tetris import binding - -class Tetris(pufferlib.PufferEnv): - def __init__( - self, - num_envs=1, - n_cols=10, - n_rows=20, - deck_size=3, - render_mode=None, - log_interval=32, - buf=None, - seed=0 - ): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(n_cols*n_rows + 6 + 7 * (deck_size + 1),), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(7) - self.render_mode = render_mode - self.log_interval = log_interval - self.num_agents = num_envs - - super().__init__(buf) - self.deck_size = deck_size - self.n_cols = n_cols - self.n_rows = n_rows - self.c_envs = binding.vec_init( - self.observations, - self.actions, - self.rewards, - self.terminals, - self.truncations, - num_envs, - seed, - n_cols=n_cols, - n_rows=n_rows, - deck_size=deck_size, - ) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -if __name__ == '__main__': - TIME = 10 - num_envs = 4096 - env = Tetris(num_envs=num_envs) - actions = [ - [env.single_action_space.sample() for _ in range(num_envs) ]for _ in range(1000) - ] - obs, _ = env.reset(seed = np.random.randint(0,1000)) - - import time - start = time.time() - tick = 0 - - while time.time() - start < TIME: - action = actions[tick%1000] - env.render() - print(np.array(obs[0][0:200]).reshape(20,10), obs[0][200:206], obs[0][206:(206+7*4)]) - obs, _, _, _, _ = env.step(action) - tick += 1 - print('SPS:', (tick*num_envs) / (time.time() - start)) - env.close() - diff --git a/pufferlib/ocean/tetris/tetrominoes.h b/pufferlib/ocean/tetris/tetrominoes.h deleted file mode 100644 index 2a08c5c12..000000000 --- a/pufferlib/ocean/tetris/tetrominoes.h +++ /dev/null @@ -1,250 +0,0 @@ -#include "raylib.h" - -#define NUM_TETROMINOES 7 -#define NUM_ROTATIONS 4 -#define SIZE 4 - -const Color TETROMINOES_COLORS[NUM_TETROMINOES] = { - (Color){255, 255, 0, 255}, // Yellow - (Color){0, 255, 255, 255}, // Cyan - (Color){0, 255, 0, 255}, // Green - (Color){255, 0, 0, 255}, // Red - (Color){128, 0, 128, 255}, // Purple - (Color){255, 165, 0, 255}, // Orange - (Color){0, 0, 255, 255}, // Blue -}; - -const int TETROMINOES[NUM_TETROMINOES][NUM_ROTATIONS][SIZE][SIZE] = { - { - { - {1, 1, 0, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 0, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 0, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 0, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - }, - { - { - {1, 0, 0, 0}, - {1, 0, 0, 0}, - {1, 0, 0, 0}, - {1, 0, 0, 0}, - }, - { - {1, 1, 1, 1}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 0, 0, 0}, - {1, 0, 0, 0}, - {1, 0, 0, 0}, - {1, 0, 0, 0}, - }, - { - {1, 1, 1, 1}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - }, - { - { - {1, 0, 0, 0}, - {1, 1, 0, 0}, - {0, 1, 0, 0}, - {0, 0, 0, 0}, - }, - { - {0, 1, 1, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 0, 0, 0}, - {1, 1, 0, 0}, - {0, 1, 0, 0}, - {0, 0, 0, 0}, - }, - { - {0, 1, 1, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - }, - { - { - {0, 1, 0, 0}, - {1, 1, 0, 0}, - {1, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 0, 0}, - {0, 1, 1, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {0, 1, 0, 0}, - {1, 1, 0, 0}, - {1, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 0, 0}, - {0, 1, 1, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - }, - { - { - {0, 1, 0, 0}, - {1, 1, 0, 0}, - {0, 1, 0, 0}, - {0, 0, 0, 0}, - }, - { - {0, 1, 0, 0}, - {1, 1, 1, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 0, 0, 0}, - {1, 1, 0, 0}, - {1, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 1, 0}, - {0, 1, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - }, - { - { - {1, 0, 0, 0}, - {1, 0, 0, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 1, 0}, - {1, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 0, 0}, - {0, 1, 0, 0}, - {0, 1, 0, 0}, - {0, 0, 0, 0}, - }, - { - {0, 0, 1, 0}, - {1, 1, 1, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - }, - { - { - {0, 1, 0, 0}, - {0, 1, 0, 0}, - {1, 1, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 0, 0, 0}, - {1, 1, 1, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 0, 0}, - {1, 0, 0, 0}, - {1, 0, 0, 0}, - {0, 0, 0, 0}, - }, - { - {1, 1, 1, 0}, - {0, 0, 1, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - } -}; - - -const int TETROMINOES_FILLS_COL[NUM_TETROMINOES][NUM_ROTATIONS] = { - { - 2,2,2,2 - }, - { - 1,4,1,4 - }, - { - 2,3,2,3 - }, - { - 2,3,2,3 - }, - { - 2,3,2,3 - }, - { - 2,3,2,3 - }, - { - 2,3,2,3 - } -}; - - -const int TETROMINOES_FILLS_ROW[NUM_TETROMINOES][NUM_ROTATIONS] = { - { - 2,2,2,2 - }, - { - 4,1,4,1 - }, - { - 3,2,3,2 - }, - { - 3,2,3,2 - }, - { - 3,2,3,2 - }, - { - 3,2,3,2 - }, - { - 3,2,3,2 - } -}; diff --git a/pufferlib/ocean/torch.py b/pufferlib/ocean/torch.py index baeda1131..449d396b4 100644 --- a/pufferlib/ocean/torch.py +++ b/pufferlib/ocean/torch.py @@ -1,834 +1,60 @@ -from types import SimpleNamespace -from typing import Any, Tuple - -from gymnasium import spaces - from torch import nn import torch -from torch.distributions.normal import Normal -from torch import nn import torch.nn.functional as F import pufferlib import pufferlib.models -from pufferlib.models import Default as Policy -from pufferlib.models import Convolutional as Conv -Recurrent = pufferlib.models.LSTMWrapper -from pufferlib.pytorch import layer_init, _nativize_dtype, nativize_tensor -import numpy as np - - -class Boids(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128, **kwargs): - super().__init__() - self.hidden_size = hidden_size - self.is_continuous = False - self.network = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(4, hidden_size)), - nn.GELU(), - pufferlib.pytorch.layer_init(nn.Linear(hidden_size, hidden_size)), - ) - self.action_vec = tuple(env.single_action_space.nvec) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, sum(self.action_vec)), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - batch, n, = observations.shape - return self.network(observations.reshape(batch, n//4, 4)).max(dim=1)[0] - - def decode_actions(self, flat_hidden, state=None): - value = self.value_fn(flat_hidden) - action = self.actor(flat_hidden).split(self.action_vec, dim=1) - return action, value - -class NMMO3LSTM(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size=512, hidden_size=512): - super().__init__(env, policy, input_size, hidden_size) - -class NMMO3(nn.Module): - def __init__(self, env, hidden_size=512, output_size=512, **kwargs): - super().__init__() - self.hidden_size = hidden_size - #self.dtype = pufferlib.pytorch.nativize_dtype(env.emulated) - self.num_actions = env.single_action_space.n - self.factors = np.array([4, 4, 17, 5, 3, 5, 5, 5, 7, 4]) - offsets = torch.tensor([0] + list(np.cumsum(self.factors)[:-1])).view(1, -1, 1, 1) - self.register_buffer('offsets', offsets) - self.cum_facs = np.cumsum(self.factors) - - self.multihot_dim = self.factors.sum() - self.is_continuous = False - - self.map_2d = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Conv2d(self.multihot_dim, 128, 5, stride=3)), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Conv2d(128, 128, 3, stride=1)), - nn.Flatten(), - ) - - self.player_discrete_encoder = nn.Sequential( - nn.Embedding(128, 32), - nn.Flatten(), - ) - self.proj = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(1817, hidden_size)), - nn.ReLU(), - ) - - self.layer_norm = nn.LayerNorm(hidden_size) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(output_size, self.num_actions), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init(nn.Linear(output_size, 1), std=1) - - def forward(self, x, state=None): - hidden = self.encode_observations(x) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - batch = observations.shape[0] - ob_map = observations[:, :11*15*10].view(batch, 11, 15, 10) - ob_player = observations[:, 11*15*10:-10] - ob_reward = observations[:, -10:] - - batch = ob_map.shape[0] - map_buf = torch.zeros(batch, 59, 11, 15, dtype=torch.float32, device=observations.device) - codes = ob_map.permute(0, 3, 1, 2) + self.offsets - map_buf.scatter_(1, codes, 1) - ob_map = self.map_2d(map_buf) - - player_discrete = self.player_discrete_encoder(ob_player.int()) - - obs = torch.cat([ob_map, player_discrete, ob_player.to(ob_map.dtype), ob_reward], dim=1) - obs = self.proj(obs) - return obs - - def decode_actions(self, flat_hidden): - flat_hidden = self.layer_norm(flat_hidden) - action = self.actor(flat_hidden) - value = self.value_fn(flat_hidden) - return action, value - -class Terraform(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128, **kwargs): - super().__init__() - self.hidden_size = hidden_size - self.is_continuous = False - - self.local_net_2d = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(2, cnn_channels, 5, stride=3)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.ReLU(), - nn.Flatten(), - ) - - self.global_net_2d = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(2, cnn_channels, 3, stride=1)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.ReLU(), - nn.Flatten(), - ) - - self.net_1d = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Linear(5, hidden_size)), - nn.Flatten(), - ) - self.proj = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(hidden_size + cnn_channels*5, hidden_size)), - nn.ReLU(), - ) - self.atn_dim = env.single_action_space.nvec.tolist() - self.actor = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, sum(self.atn_dim)), std=0.01) - self.value = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - hidden = self.encode_observations(observations, state) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - # breakpoint() - obs_2d = observations[:, :242].reshape(-1, 2, 11, 11).float() - obs_1d = observations[:, 242:247].reshape(-1, 5).float() - location_2d = observations[:, 247:].reshape(-1,2, 6, 6).float() - hidden_local_2d = self.local_net_2d(obs_2d) - hidden_global_2d = self.global_net_2d(location_2d) - hidden_1d = self.net_1d(obs_1d) - hidden = torch.cat([hidden_local_2d, hidden_global_2d, hidden_1d], dim=1) - return self.proj(hidden) - - def decode_actions(self, hidden): - action = self.actor(hidden) - action = torch.split(action, self.atn_dim, dim=1) - #action = [head(hidden) for head in self.actor] - value = self.value(hidden) - return action, value - - -class G2048(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128): - super().__init__() - self.hidden_size = hidden_size - self.is_continuous = False - - self.cnn = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(1, cnn_channels, 2, stride=1)), - nn.GELU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 2, stride=1)), - nn.Flatten(), - nn.GELU(), - pufferlib.pytorch.layer_init( - nn.Linear(128, hidden_size), std=0.01), - ) - - self.decoder = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward_eval(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward(self, x, state=None): - return self.forward_eval(x, state) - - def encode_observations(self, observations, state=None): - #observations = F.one_hot(observations.long(), 16).view(-1, 16, 4, 4).float() - observations = observations.float().view(-1, 1, 4, 4) - return self.cnn(observations) - - def decode_actions(self, hidden): - action = self.decoder(hidden) - value = self.value(hidden) - return action, value - -class Snake(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128): - super().__init__() - self.hidden_size = hidden_size - self.is_continuous = False - - encode_dim = cnn_channels - - ''' - self.network= nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(8, cnn_channels, 5, stride=3)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.ReLU(), - nn.Flatten(), - ) - self.proj = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(encode_dim, hidden_size)), - nn.ReLU(), - ) - - ''' - self.encoder= torch.nn.Sequential( - nn.Linear(8*np.prod(env.single_observation_space.shape), hidden_size), - nn.GELU(), - ) - self.decoder = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - #observations = F.one_hot(observations.long(), 8).permute(0, 3, 1, 2).float() - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - observations = F.one_hot(observations.long(), 8).view(-1, 11*11*8).float() - return self.encoder(observations) - - def decode_actions(self, hidden): - action = self.decoder(hidden) - value = self.value(hidden) - return action, value - -''' -class Snake(pufferlib.models.Default): - def __init__(self, env, hidden_size=128): - super().__init__() - - def encode_observations(self, observations, state=None): - observations = F.one_hot(observations.long(), 8).view(-1, 11*11*8).float() - super().encode_observations(observations, state) -''' - -class Grid(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128, **kwargs): - super().__init__() - self.hidden_size = hidden_size - self.network = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(32, cnn_channels, 5, stride=3)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.Flatten(), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Linear(cnn_channels, hidden_size)), - nn.ReLU(), - ) - - self.is_continuous = isinstance(env.single_action_space, pufferlib.spaces.Box) - if self.is_continuous: - self.decoder_mean = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.shape[0]), std=0.01) - self.decoder_logstd = nn.Parameter(torch.zeros( - 1, env.single_action_space.shape[0])) - else: - num_actions = env.single_action_space.n - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, num_actions), std=0.01) - - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - hidden = observations.view(-1, 11, 11).long() - hidden = F.one_hot(hidden, 32).permute(0, 3, 1, 2).float() - hidden = self.network(hidden) - return hidden - - def decode_actions(self, flat_hidden, state=None): - value = self.value_fn(flat_hidden) - if self.is_continuous: - mean = self.decoder_mean(flat_hidden) - logstd = self.decoder_logstd.expand_as(mean) - std = torch.exp(logstd) - probs = torch.distributions.Normal(mean, std) - batch = flat_hidden.shape[0] - return probs, value - else: - action = self.actor(flat_hidden) - return action, value - -class Go(nn.Module): - def __init__(self, env, cnn_channels=64, hidden_size=128, **kwargs): - super().__init__() - self.hidden_size = hidden_size - self.is_continuous = False - # 3 categories 2 boards. - # categories = player, opponent, empty - # boards = current, previous - self.cnn = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(2, cnn_channels, 3, stride=1)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride = 1)), - nn.Flatten(), - ) - - obs_size = env.single_observation_space.shape[0] - self.grid_size = int(np.sqrt((obs_size-2)/2)) - output_size = self.grid_size - 4 - cnn_flat_size = cnn_channels * output_size * output_size - - self.flat = pufferlib.pytorch.layer_init(nn.Linear(2,32)) - - self.proj = pufferlib.pytorch.layer_init(nn.Linear(cnn_flat_size + 32, hidden_size)) - - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - grid_size = int(np.sqrt((observations.shape[1] - 2) / 2)) - full_board = grid_size * grid_size - black_board = observations[:, :full_board].view(-1,1, grid_size,grid_size).float() - white_board = observations[:, full_board:-2].view(-1,1, grid_size, grid_size).float() - board_features = torch.cat([black_board, white_board],dim=1) - flat_feature1 = observations[:, -2].unsqueeze(1).float() - flat_feature2 = observations[:, -1].unsqueeze(1).float() - # Pass board through cnn - cnn_features = self.cnn(board_features) - # Pass extra feature - flat_features = torch.cat([flat_feature1, flat_feature2],dim=1) - flat_features = self.flat(flat_features) - # pass all features - features = torch.cat([cnn_features, flat_features], dim=1) - features = F.relu(self.proj(features)) - - return features - - def decode_actions(self, flat_hidden, state=None): - value = self.value_fn(flat_hidden) - action = self.actor(flat_hidden) - return action, value - -class MOBA(nn.Module): - def __init__(self, env, cnn_channels=128, hidden_size=128, **kwargs): - super().__init__() - self.hidden_size = hidden_size - self.cnn = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(16 + 3, cnn_channels, 5, stride=3)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.Flatten(), - ) - self.flat = pufferlib.pytorch.layer_init(nn.Linear(26, 128)) - self.proj = pufferlib.pytorch.layer_init(nn.Linear(128+cnn_channels, hidden_size)) - - self.is_continuous = isinstance(env.single_action_space, pufferlib.spaces.Box) - if self.is_continuous: - self.decoder_mean = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.shape[0]), std=0.01) - self.decoder_logstd = nn.Parameter(torch.zeros( - 1, env.single_action_space.shape[0])) - else: - self.atn_dim = env.single_action_space.nvec.tolist() - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, sum(self.atn_dim)), std=0.01) - - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - cnn_features = observations[:, :-26].view(-1, 11, 11, 4).long() - map_features = F.one_hot(cnn_features[:, :, :, 0], 16).permute(0, 3, 1, 2).float() - extra_map_features = (cnn_features[:, :, :, -3:].float() / 255).permute(0, 3, 1, 2) - cnn_features = torch.cat([map_features, extra_map_features], dim=1) - #print('observations 2d: ', map_features[0].cpu().numpy().tolist()) - cnn_features = self.cnn(cnn_features) - #print('cnn features: ', cnn_features[0].detach().cpu().numpy().tolist()) - - flat_features = observations[:, -26:].float() / 255.0 - #print('observations 1d: ', flat_features[0, 0]) - flat_features = self.flat(flat_features) - #print('flat features: ', flat_features[0].detach().cpu().numpy().tolist()) - - features = torch.cat([cnn_features, flat_features], dim=1) - features = F.relu(self.proj(F.relu(features))) - #print('features: ', features[0].detach().cpu().numpy().tolist()) - return features - - def decode_actions(self, flat_hidden): - #print('lstm: ', flat_hidden[0].detach().cpu().numpy().tolist()) - value = self.value_fn(flat_hidden) - if self.is_continuous: - mean = self.decoder_mean(flat_hidden) - logstd = self.decoder_logstd.expand_as(mean) - std = torch.exp(logstd) - probs = torch.distributions.Normal(mean, std) - batch = flat_hidden.shape[0] - return probs, value - else: - action = self.actor(flat_hidden) - action = torch.split(action, self.atn_dim, dim=1) - - #argmax_samples = [torch.argmax(a, dim=1).detach().cpu().numpy().tolist() for a in action] - #print('argmax samples: ', argmax_samples) - - return action, value - -class TrashPickup(nn.Module): - def __init__(self, env, cnn_channels=32, hidden_size=128, **kwargs): - super().__init__() - self.hidden_size = hidden_size - self.is_continuous = False - self.network= nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv2d(5, cnn_channels, 5, stride=3)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv2d(cnn_channels, cnn_channels, 3, stride=1)), - nn.ReLU(), - nn.Flatten(), - pufferlib.pytorch.layer_init(nn.Linear(cnn_channels, hidden_size)), - ) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - observations = observations.view(-1, 5, 11, 11).float() - return self.network(observations) - - def decode_actions(self, flat_hidden): - action = self.actor(flat_hidden) - value = self.value_fn(flat_hidden) - return action, value - -class TowerClimbLSTM(pufferlib.models.LSTMWrapper): - def __init__(self, env, policy, input_size = 256, hidden_size = 256): - super().__init__(env, policy, input_size, hidden_size) - -class TowerClimb(nn.Module): - def __init__(self, env, cnn_channels=16, hidden_size = 256, **kwargs): - self.hidden_size = hidden_size - self.is_continuous = False - super().__init__() - self.network = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Conv3d(1, cnn_channels, 3, stride = 1)), - nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Conv3d(cnn_channels, cnn_channels, 3, stride=1)), - nn.Flatten() - ) - cnn_flat_size = cnn_channels * 1 * 1 * 5 - - # Process player obs - self.flat = pufferlib.pytorch.layer_init(nn.Linear(3,16)) +from pufferlib.models import Default as Policy # noqa: F401 +from pufferlib.models import Convolutional as Conv # noqa: F401 - # combine - self.proj = pufferlib.pytorch.layer_init( - nn.Linear(cnn_flat_size + 16, hidden_size)) - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std = 0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1 ), std=1) - def forward(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - board_state = observations[:,:225] - player_info = observations[:, -3:] - board_features = board_state.view(-1, 1, 5,5,9).float() - cnn_features = self.network(board_features) - flat_features = self.flat(player_info.float()) - - features = torch.cat([cnn_features,flat_features],dim = 1) - features = self.proj(features) - return features - - def decode_actions(self, flat_hidden): - action = self.actor(flat_hidden) - value = self.value_fn(flat_hidden) - - return action, value - - -class ImpulseWarsLSTM(Recurrent): - def __init__(self, env: pufferlib.PufferEnv, policy: nn.Module, input_size: int = 512, hidden_size: int = 512): - super().__init__(env, policy, input_size, hidden_size) - - -class ImpulseWarsPolicy(nn.Module): - def __init__( - self, - env: pufferlib.PufferEnv, - cnn_channels: int = 64, - weapon_type_embedding_dims: int = 2, - input_size: int = 512, - hidden_size: int = 512, - batch_size: int = 131_072, - num_drones: int = 2, - continuous: bool = False, - is_training: bool = True, - device: str = "cuda", - **kwargs, - ): - super().__init__() - self.hidden_size = hidden_size - - self.is_continuous = continuous - - self.numDrones = num_drones - self.isTraining = is_training - from pufferlib.ocean.impulse_wars import binding - self.obsInfo = SimpleNamespace(**binding.get_consts(self.numDrones)) - - self.discreteFactors = np.array( - [self.obsInfo.wallTypes] * self.obsInfo.numNearWallObs - + [self.obsInfo.wallTypes + 1] * self.obsInfo.numFloatingWallObs - + [self.numDrones + 1] * self.obsInfo.numProjectileObs, - ) - discreteOffsets = torch.tensor([0] + list(np.cumsum(self.discreteFactors)[:-1]), device=device).view( - 1, -1 - ) - self.register_buffer("discreteOffsets", discreteOffsets, persistent=False) - self.discreteMultihotDim = self.discreteFactors.sum() - - multihotBuffer = torch.zeros(batch_size, self.discreteMultihotDim, device=device) - self.register_buffer("multihotOutput", multihotBuffer, persistent=False) - - # most of the observation is a 2D array of bytes, but the end - # contains around 200 floats; this allows us to treat the end - # of the observation as a float array - _, *self.dtype = _nativize_dtype( - np.dtype((np.uint8, (self.obsInfo.continuousObsBytes,))), - np.dtype((np.float32, (self.obsInfo.continuousObsSize,))), - ) - self.dtype = tuple(self.dtype) - - self.weaponTypeEmbedding = nn.Embedding(self.obsInfo.weaponTypes, weapon_type_embedding_dims) - - # each byte in the map observation contains 4 values: - # - 2 bits for wall type - # - 1 bit for is floating wall - # - 1 bit for is weapon pickup - # - 3 bits for drone index - self.register_buffer( - "unpackMask", - torch.tensor([0x60, 0x10, 0x08, 0x07], dtype=torch.uint8), - persistent=False, - ) - self.register_buffer("unpackShift", torch.tensor([5, 4, 3, 0], dtype=torch.uint8), persistent=False) - - self.mapObsInputChannels = (self.obsInfo.wallTypes + 1) + 1 + 1 + self.numDrones - self.mapCNN = nn.Sequential( - layer_init( - nn.Conv2d( - self.mapObsInputChannels, - cnn_channels, - kernel_size=5, - stride=3, - ) - ), - nn.ReLU(), - layer_init(nn.Conv2d(cnn_channels, cnn_channels, kernel_size=3, stride=1)), - nn.ReLU(), - nn.Flatten(), - ) - cnnOutputSize = self._computeCNNShape() - - featuresSize = ( - cnnOutputSize - + (self.obsInfo.numNearWallObs * (self.obsInfo.wallTypes + self.obsInfo.nearWallPosObsSize)) - + ( - self.obsInfo.numFloatingWallObs - * (self.obsInfo.wallTypes + 1 + self.obsInfo.floatingWallInfoObsSize) - ) - + ( - self.obsInfo.numWeaponPickupObs - * (weapon_type_embedding_dims + self.obsInfo.weaponPickupPosObsSize) - ) - + ( - self.obsInfo.numProjectileObs - * (weapon_type_embedding_dims + self.obsInfo.projectileInfoObsSize + self.numDrones + 1) - ) - + ((self.numDrones - 1) * (weapon_type_embedding_dims + self.obsInfo.enemyDroneObsSize)) - + (self.obsInfo.droneObsSize + weapon_type_embedding_dims) - + self.obsInfo.miscObsSize - ) - - self.encoder = nn.Sequential( - layer_init(nn.Linear(featuresSize, input_size)), - nn.ReLU(), - ) - - if self.is_continuous: - self.actorMean = layer_init(nn.Linear(hidden_size, env.single_action_space.shape[0]), std=0.01) - self.actorLogStd = nn.Parameter(torch.zeros(1, env.single_action_space.shape[0])) - else: - self.actionDim = env.single_action_space.nvec.tolist() - self.actor = layer_init(nn.Linear(hidden_size, sum(self.actionDim)), std=0.01) - - self.critic = layer_init(nn.Linear(hidden_size, 1), std=1.0) - - def forward(self, obs: torch.Tensor, state = None) -> Tuple[torch.Tensor, torch.Tensor]: - hidden = self.encode_observations(obs) - actions, value = self.decode_actions(hidden) - return actions, value - - def unpack(self, batchSize: int, obs: torch.Tensor) -> torch.Tensor: - # prepare map obs to be unpacked - mapObs = obs[:, : self.obsInfo.mapObsSize].reshape((batchSize, -1, 1)) - # unpack wall types, weapon pickup types, and drone indexes - mapObs = (mapObs & self.unpackMask) >> self.unpackShift - # reshape so channels are first, required for torch conv2d - return mapObs.permute(0, 2, 1).reshape( - (batchSize, 4, self.obsInfo.mapObsRows, self.obsInfo.mapObsColumns) - ) - - def encode_observations(self, obs: torch.Tensor, state: Any = None) -> torch.Tensor: - batchSize = obs.shape[0] - - mapObs = self.unpack(batchSize, obs) - - # one hot encode wall types - wallTypeObs = mapObs[:, 0, :, :].long() - wallTypes = F.one_hot(wallTypeObs, self.obsInfo.wallTypes + 1).permute(0, 3, 1, 2).float() - - # unsqueeze floating wall booleans (is wall a floating wall) - floatingWallObs = mapObs[:, 1, :, :].unsqueeze(1) - - # unsqueeze map pickup booleans (does map tile contain a weapon pickup) - mapPickupObs = mapObs[:, 2, :, :].unsqueeze(1) - - # one hot drone indexes - droneIndexObs = mapObs[:, 3, :, :].long() - droneIndexes = F.one_hot(droneIndexObs, self.numDrones).permute(0, 3, 1, 2).float() - - # combine all map observations and feed through CNN - mapObs = torch.cat((wallTypes, floatingWallObs, mapPickupObs, droneIndexes), dim=1) - map = self.mapCNN(mapObs) - - # process discrete observations - multihotInput = ( - obs[:, self.obsInfo.nearWallTypesObsOffset : self.obsInfo.projectileTypesObsOffset] - + self.discreteOffsets - ) - multihotOutput = self.multihotOutput[:batchSize].zero_() - multihotOutput.scatter_(1, multihotInput.long(), 1) - - weaponTypeObs = obs[:, self.obsInfo.projectileTypesObsOffset : self.obsInfo.discreteObsSize].int() - weaponTypes = self.weaponTypeEmbedding(weaponTypeObs).float() - weaponTypes = torch.flatten(weaponTypes, start_dim=1, end_dim=-1) - - # process continuous observations - continuousObs = nativize_tensor(obs[:, self.obsInfo.continuousObsOffset :], self.dtype) - # combine all observations and feed through final linear encoder - features = torch.cat((map, multihotOutput, weaponTypes, continuousObs), dim=-1) - - return self.encoder(features) - - def decode_actions(self, hidden: torch.Tensor): - if self.is_continuous: - actionMean = self.actorMean(hidden) - if self.isTraining: - actionLogStd = self.actorLogStd.expand_as(actionMean) - actionStd = torch.exp(actionLogStd) - action = Normal(actionMean, actionStd) - else: - action = actionMean - else: - action = self.actor(hidden) - action = torch.split(action, self.actionDim, dim=1) - - value = self.critic(hidden) - - return action, value - - def _computeCNNShape(self) -> int: - mapSpace = spaces.Box( - low=0, - high=1, - shape=(self.mapObsInputChannels, self.obsInfo.mapObsRows, self.obsInfo.mapObsColumns), - dtype=np.float32, - ) +Recurrent = pufferlib.models.LSTMWrapper - with torch.no_grad(): - t = torch.as_tensor(mapSpace.sample()[None]) - return self.mapCNN(t).shape[1] class Drive(nn.Module): def __init__(self, env, input_size=128, hidden_size=128, **kwargs): super().__init__() self.hidden_size = hidden_size + + # Determine ego dimension from environment's dynamics model + self.ego_dim = 10 if env.dynamics_model == "jerk" else 7 + self.ego_encoder = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Linear(7, input_size)), + pufferlib.pytorch.layer_init(nn.Linear(self.ego_dim, input_size)), nn.LayerNorm(input_size), # nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Linear(input_size, input_size)) + pufferlib.pytorch.layer_init(nn.Linear(input_size, input_size)), ) - max_road_objects = 13 + max_road_objects = 10 self.road_encoder = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Linear(max_road_objects, input_size)), + pufferlib.pytorch.layer_init(nn.Linear(max_road_objects, input_size)), nn.LayerNorm(input_size), # nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Linear(input_size, input_size)) + pufferlib.pytorch.layer_init(nn.Linear(input_size, input_size)), ) max_partner_objects = 7 self.partner_encoder = nn.Sequential( - pufferlib.pytorch.layer_init( - nn.Linear(max_partner_objects, input_size)), + pufferlib.pytorch.layer_init(nn.Linear(max_partner_objects, input_size)), nn.LayerNorm(input_size), # nn.ReLU(), - pufferlib.pytorch.layer_init( - nn.Linear(input_size, input_size)) + pufferlib.pytorch.layer_init(nn.Linear(input_size, input_size)), ) - self.shared_embedding = nn.Sequential( nn.GELU(), - pufferlib.pytorch.layer_init(nn.Linear(3*input_size, hidden_size)), + pufferlib.pytorch.layer_init(nn.Linear(3 * input_size, hidden_size)), ) self.is_continuous = isinstance(env.single_action_space, pufferlib.spaces.Box) - self.atn_dim = env.single_action_space.nvec.tolist() - self.actor = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, sum(self.atn_dim)), std = 0.01) - self.value_fn = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1 ), std=1) - + if self.is_continuous: + self.atn_dim = (env.single_action_space.shape[0],) * 2 + else: + self.atn_dim = env.single_action_space.nvec.tolist() + + self.actor = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, sum(self.atn_dim)), std=0.01) + self.value_fn = pufferlib.pytorch.layer_init(nn.Linear(hidden_size, 1), std=1) + def forward(self, observations, state=None): hidden = self.encode_observations(observations) actions, value = self.decode_actions(hidden) @@ -836,185 +62,42 @@ def forward(self, observations, state=None): def forward_train(self, x, state=None): return self.forward(x, state) - + def encode_observations(self, observations, state=None): - ego_dim = 7 + ego_dim = self.ego_dim partner_dim = 63 * 7 - road_dim = 200*7 + road_dim = 200 * 7 ego_obs = observations[:, :ego_dim] - partner_obs = observations[:, ego_dim:ego_dim+partner_dim] - road_obs = observations[:, ego_dim+partner_dim:ego_dim+partner_dim+road_dim] - + partner_obs = observations[:, ego_dim : ego_dim + partner_dim] + road_obs = observations[:, ego_dim + partner_dim : ego_dim + partner_dim + road_dim] + partner_objects = partner_obs.view(-1, 63, 7) road_objects = road_obs.view(-1, 200, 7) road_continuous = road_objects[:, :, :6] # First 6 features road_categorical = road_objects[:, :, 6] - road_onehot = F.one_hot(road_categorical.long(), num_classes=7) # Shape: [batch, 200, 7] + road_onehot = F.one_hot((road_categorical + 1).long(), num_classes=4) # Shape: [batch, 200, 4] road_objects = torch.cat([road_continuous, road_onehot], dim=2) ego_features = self.ego_encoder(ego_obs) partner_features, _ = self.partner_encoder(partner_objects).max(dim=1) road_features, _ = self.road_encoder(road_objects).max(dim=1) - + concat_features = torch.cat([ego_features, road_features, partner_features], dim=1) - + # Pass through shared embedding embedding = F.relu(self.shared_embedding(concat_features)) # embedding = self.shared_embedding(concat_features) return embedding - - def decode_actions(self, flat_hidden): - action = self.actor(flat_hidden) - action = torch.split(action, self.atn_dim, dim=1) - value = self.value_fn(flat_hidden) - return action, value - -class Tetris(nn.Module): - def __init__( - self, - env, - cnn_channels=32, - input_size=128, - hidden_size=128, - **kwargs - ): - super().__init__() - self.hidden_size = hidden_size - self.cnn_channels = cnn_channels - self.n_cols = env.n_cols - self.n_rows = env.n_rows - self.scalar_input_size = (6 + 7 * (env.deck_size + 1)) - self.flat_conv_size = cnn_channels * 3 * 10 - self.is_continuous = isinstance(env.single_action_space, pufferlib.spaces.Box) - - self.conv_grid = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Conv2d(2, cnn_channels, kernel_size=(5, 3), stride=(2,1), padding=(2,1))), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Conv2d(cnn_channels, cnn_channels, kernel_size=(5, 3), stride=(2,1), padding=(2,1))), - nn.ReLU(), - pufferlib.pytorch.layer_init(nn.Conv2d(cnn_channels, cnn_channels, kernel_size=(5, 5), stride=(2,1), padding=(2,2))), - nn.ReLU(), - nn.Flatten(), - pufferlib.pytorch.layer_init(nn.Linear(self.flat_conv_size, input_size)), - ) - - self.fc_scalar = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(self.scalar_input_size, input_size)), - nn.ReLU(), - ) - - self.proj = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(2 * input_size, hidden_size)), - nn.ReLU(), - ) - - self.actor = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(hidden_size, 7), std=0.01), - nn.Flatten() - ) - - self.value_fn = nn.Sequential( - pufferlib.pytorch.layer_init(nn.Linear(hidden_size, 1)), - nn.ReLU(), - ) - - def forward(self, observations, state=None): - hidden = self.encode_observations(observations) - actions, value = self.decode_actions(hidden) - return actions, value - - def forward_train(self, x, state=None): - return self.forward(x, state) - - def encode_observations(self, observations, state=None): - B = observations.shape[0] - grid_info = observations[:, 0:(self.n_cols * self.n_rows)].view(B, self.n_rows, self.n_cols) # (B, n_rows, n_cols) - grid_info = torch.stack([(grid_info == 1).float(), (grid_info == 2).float()], dim=1) # (B, 2, n_rows, n_cols) - scalar_info = observations[:, (self.n_cols * self.n_rows):(self.n_cols * self.n_rows + self.scalar_input_size)].float() - - grid_feat = self.conv_grid(grid_info) # (B, input_size) - scalar_feat = self.fc_scalar(scalar_info) # (B, input_size) - combined = torch.cat([grid_feat, scalar_feat], dim=-1) # (B, 2 * input_size) - features = self.proj(combined) # (B, hidden_size) - return features - - def decode_actions(self, hidden): - action = self.actor(hidden) # (B, 4 * n_cols) - value = self.value_fn(hidden) # (B, 1) - return action, value - -class Drone(nn.Module): - ''' Drone policy. Flattens obs and applies a linear layer. - ''' - def __init__(self, env, hidden_size=128): - super().__init__() - self.hidden_size = hidden_size - self.is_multidiscrete = isinstance(env.single_action_space, - pufferlib.spaces.MultiDiscrete) - self.is_continuous = isinstance(env.single_action_space, - pufferlib.spaces.Box) - try: - self.is_dict_obs = isinstance(env.env.observation_space, pufferlib.spaces.Dict) - except: - self.is_dict_obs = isinstance(env.observation_space, pufferlib.spaces.Dict) - - if self.is_dict_obs: - self.dtype = pufferlib.pytorch.nativize_dtype(env.emulated) - input_size = int(sum(np.prod(v.shape) for v in env.env.observation_space.values())) - self.encoder = nn.Linear(input_size, self.hidden_size) - else: - self.encoder = torch.nn.Sequential( - nn.Linear(np.prod(env.single_observation_space.shape), hidden_size), - nn.GELU(), - ) - - if self.is_multidiscrete: - self.action_nvec = tuple(env.single_action_space.nvec) - self.decoder = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, sum(self.action_nvec)), std=0.01) - elif not self.is_continuous: - self.decoder = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.n), std=0.01) + def decode_actions(self, flat_hidden): + if self.is_continuous: + parameters = self.actor(flat_hidden) + loc, scale = torch.split(parameters, self.atn_dim, dim=1) + std = torch.nn.functional.softplus(scale) + 1e-4 + action = torch.distributions.Normal(loc, std) else: - self.decoder_mean = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, env.single_action_space.shape[0]), std=0.01) - self.decoder_logstd = nn.Parameter(torch.zeros( - 1, env.single_action_space.shape[0])) - - self.value = pufferlib.pytorch.layer_init( - nn.Linear(hidden_size, 1), std=1) - - def forward_eval(self, observations, state=None): - hidden = self.encode_observations(observations, state=state) - logits, values = self.decode_actions(hidden) - return logits, values - - def forward(self, observations, state=None): - return self.forward_eval(observations, state) - - def encode_observations(self, observations, state=None): - '''Encodes a batch of observations into hidden states. Assumes - no time dimension (handled by LSTM wrappers).''' - batch_size = observations.shape[0] - if self.is_dict_obs: - observations = pufferlib.pytorch.nativize_tensor(observations, self.dtype) - observations = torch.cat([v.view(batch_size, -1) for v in observations.values()], dim=1) - else: - observations = observations.view(batch_size, -1) - return self.encoder(observations.float()) + action = self.actor(flat_hidden) + action = torch.split(action, self.atn_dim, dim=1) - def decode_actions(self, hidden): - '''Decodes a batch of hidden states into (multi)discrete actions. - Assumes no time dimension (handled by LSTM wrappers).''' - if self.is_multidiscrete: - logits = self.decoder(hidden).split(self.action_nvec, dim=1) - elif self.is_continuous: - mean = self.decoder_mean(hidden) - logstd = self.decoder_logstd.expand_as(mean) - std = torch.exp(logstd) - logits = torch.distributions.Normal(mean, std) - else: - logits = self.decoder(hidden) + value = self.value_fn(flat_hidden) - values = self.value(hidden) - return logits, values + return action, value diff --git a/pufferlib/ocean/tower_climb/binding.c b/pufferlib/ocean/tower_climb/binding.c deleted file mode 100644 index c80d8153f..000000000 --- a/pufferlib/ocean/tower_climb/binding.c +++ /dev/null @@ -1,101 +0,0 @@ -#include "tower_climb.h" - -#define Env CTowerClimb -#define MY_SHARED -#include "../env_binding.h" - -static PyObject* my_shared(PyObject* self, PyObject* args, PyObject* kwargs) { - int num_maps = unpack(kwargs, "num_maps"); - Level* levels = calloc(num_maps, sizeof(Level)); - PuzzleState* puzzle_states = calloc(num_maps, sizeof(PuzzleState)); - - for (int i = 0; i < num_maps; i++) { - int goal_height = rand() % 4 + 5; - int min_moves = 10; - int max_moves = 15; - init_level(&levels[i]); - init_puzzle_state(&puzzle_states[i]); - cy_init_random_level(&levels[i], goal_height, max_moves, min_moves, i); - levelToPuzzleState(&levels[i], &puzzle_states[i]); - } - - PyObject* levels_handle = PyLong_FromVoidPtr(levels); - PyObject* puzzles_handle = PyLong_FromVoidPtr(puzzle_states); - PyObject* state = PyDict_New(); - PyDict_SetItemString(state, "levels", levels_handle); - PyDict_SetItemString(state, "puzzles", puzzles_handle); - return PyLong_FromVoidPtr(state); -} - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->num_maps = unpack(kwargs, "num_maps"); - env->reward_climb_row = unpack(kwargs, "reward_climb_row"); - env->reward_fall_row = unpack(kwargs, "reward_fall_row"); - env->reward_illegal_move = unpack(kwargs, "reward_illegal_move"); - env->reward_move_block = unpack(kwargs, "reward_move_block"); - init(env); - - PyObject* handle_obj = PyDict_GetItemString(kwargs, "state"); - if (handle_obj == NULL) { - PyErr_SetString(PyExc_KeyError, "Key 'state' not found in kwargs"); - return 1; - } - - // Check if handle_obj is a PyLong - if (!PyLong_Check(handle_obj)) { - PyErr_SetString(PyExc_TypeError, "state handle must be an integer"); - return 1; - } - - // Convert PyLong to PyObject* (state dictionary) - PyObject* state_dict = (PyObject*)PyLong_AsVoidPtr(handle_obj); - if (state_dict == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid state dictionary pointer"); - return 1; - } - - // Verify it’s a dictionary - if (!PyDict_Check(state_dict)) { - PyErr_SetString(PyExc_TypeError, "State pointer does not point to a dictionary"); - return 1; - } - - // Basic validation: check reference count - if (state_dict->ob_refcnt <= 0) { - PyErr_SetString(PyExc_RuntimeError, "State dictionary has invalid reference count"); - return 1; - } - - PyObject* levels_obj = PyDict_GetItemString(state_dict, "levels"); - if (levels_obj == NULL) { - PyErr_SetString(PyExc_KeyError, "Key 'levels' not found in state"); - return 1; - } - if (!PyLong_Check(levels_obj)) { - PyErr_SetString(PyExc_TypeError, "levels must be an integer"); - return 1; - } - env->all_levels = (Level*)PyLong_AsVoidPtr(levels_obj); - - PyObject* puzzles_obj = PyDict_GetItemString(state_dict, "puzzles"); - if (!PyObject_TypeCheck(puzzles_obj, &PyLong_Type)) { - PyErr_SetString(PyExc_TypeError, "puzzles handle must be an integer"); - return 1; - } - PuzzleState* puzzles = (PuzzleState*)PyLong_AsVoidPtr(puzzles_obj); - if (!puzzles) { - PyErr_SetString(PyExc_ValueError, "Invalid puzzles handle"); - return 1; - } - env->all_puzzles = puzzles; - - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/tower_climb/tower_climb.c b/pufferlib/ocean/tower_climb/tower_climb.c deleted file mode 100644 index d5489958e..000000000 --- a/pufferlib/ocean/tower_climb/tower_climb.c +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include "tower_climb.h" -#include "puffernet.h" - -typedef struct TowerClimbNet TowerClimbNet; -struct TowerClimbNet { - int num_agents; - float* obs_3d; - float* obs_1d; - Conv3D* conv1; - ReLU* relu1; - Conv3D* conv2; - Linear* flat; - CatDim1* cat; - Linear* proj; - LSTM* lstm; - Linear* actor; - Linear* value_fn; - Multidiscrete* multidiscrete; -}; - -TowerClimbNet* init_tower_climb_net(Weights* weights, int num_agents) { - TowerClimbNet* net = calloc(1, sizeof(TowerClimbNet)); - int hidden_size = 256; - int cnn_channels = 16; - // Calculate correct output sizes for Conv3D layers - // First conv: (5,5,9) -> (4,4,8) with kernel=2, stride=1 - // Second conv: (4,4,8) -> (3,3,7) with kernel=2, stride=1 - int cnn_flat_size = cnn_channels * 1 * 1 * 5; // Match PyTorch size - - net->num_agents = num_agents; - net->obs_3d = calloc(5 * 5 * 9, sizeof(float)); - net->obs_1d = calloc(3, sizeof(float)); - net->conv1 = make_conv3d(weights, num_agents, 9, 5, 5, 1, cnn_channels, 3, 1); - net->relu1 = make_relu(num_agents, cnn_channels * 3 * 3 * 7); - net->conv2 = make_conv3d(weights, num_agents, 7, 3, 3, cnn_channels, cnn_channels, 3, 1); - net->flat = make_linear(weights, num_agents, 3, 16); - net->cat = make_cat_dim1(num_agents, cnn_flat_size, 16); - net->proj = make_linear(weights, num_agents, cnn_flat_size + 16, hidden_size); - net->actor = make_linear(weights, num_agents, hidden_size, 6); - net->value_fn = make_linear(weights, num_agents, hidden_size, 1); - net->lstm = make_lstm(weights, num_agents, hidden_size, hidden_size); - int logit_sizes[1] = {6}; - net->multidiscrete = make_multidiscrete(num_agents, logit_sizes, 1); - return net; -} - -void forward(TowerClimbNet* net, unsigned char* observations, int* actions) { - int vision_size = 5 * 5 * 9; - int player_size = 3; - // clear previous observations - memset(net->obs_3d, 0, vision_size * sizeof(float)); - memset(net->obs_1d, 0, player_size * sizeof(float)); - // reshape board to 3d tensor - float (*obs_3d)[1][5][5][9] = (float (*)[1][5][5][9])net->obs_3d; - float (*obs_1d)[3] = (float (*)[3])net->obs_1d; - // process vision board - int obs_3d_idx = 0; - for (int b = 0; b < 1; b++) { - for (int d = 0; d < 5; d++) { - for (int h = 0; h < 5; h++) { - for (int w = 0; w < 9; w++) { - obs_3d[b][0][d][h][w] = observations[obs_3d_idx]; - obs_3d_idx++; - } - } - } - } - // process player board - for (int i = 0; i < player_size; i++) { - obs_1d[0][i] = observations[vision_size + i]; - } - - conv3d(net->conv1, net->obs_3d); - relu(net->relu1, net->conv1->output); - conv3d(net->conv2, net->relu1->output); - linear(net->flat, net->obs_1d); - cat_dim1(net->cat, net->conv2->output, net->flat->output); - linear(net->proj, net->cat->output); - lstm(net->lstm, net->proj->output); - linear(net->actor, net->lstm->state_h); - linear(net->value_fn, net->lstm->state_h); - softmax_multidiscrete(net->multidiscrete, net->actor->output, actions); -} - -void free_tower_climb_net(TowerClimbNet* net) { - free(net->obs_3d); - free(net->obs_1d); - free(net->conv1); - free(net->relu1); - free(net->conv2); - free(net->flat); - free(net->cat); - free(net->proj); - free(net->actor); - free(net->value_fn); - free(net->lstm); - free(net->multidiscrete); - free(net); -} - -void demo() { - Weights* weights = load_weights("resources/tower_climb/tower_climb_weights.bin", 560407); - TowerClimbNet* net = init_tower_climb_net(weights, 1); - - int num_maps = 1; // Generate 1 map only to start faster - Level* levels = calloc(num_maps, sizeof(Level)); - PuzzleState* puzzle_states = calloc(num_maps, sizeof(PuzzleState)); - - srand(time(NULL)); - - for (int i = 0; i < num_maps; i++) { - int goal_height = rand() % 4 + 5; - int min_moves = 10; - int max_moves = 15; - init_level(&levels[i]); - init_puzzle_state(&puzzle_states[i]); - cy_init_random_level(&levels[i], goal_height, max_moves, min_moves, i); - levelToPuzzleState(&levels[i], &puzzle_states[i]); - } - - CTowerClimb* env = allocate(); - env->num_maps = num_maps; - env->all_levels = levels; - env->all_puzzles = puzzle_states; - - int random_level = 5 + (rand() % 4); - init_random_level(env, random_level, 15, 10, rand()); - c_reset(env); - c_render(env); - Client* client = env->client; - client->enable_animations = 1; - int tick = 0; - while (!WindowShouldClose()) { - if (tick % 6 == 0 && !client->isMoving) { - tick = 0; - int human_action = env->actions[0]; - forward(net, env->observations, env->actions); - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env->actions[0] = human_action; - } - c_step(env); - if (IsKeyDown(KEY_LEFT_SHIFT)) { - env->actions[0] = NOOP; - } - } - tick++; - if (IsKeyDown(KEY_LEFT_SHIFT)) { - // Camera controls - if (IsKeyPressed(KEY_UP)) { // || IsKeyPressed(KEY_W)) { - env->actions[0] = UP; - } - if (IsKeyPressed(KEY_LEFT)) { //|| IsKeyPressed(KEY_A)) { - env->actions[0] = LEFT; - } - if (IsKeyPressed(KEY_RIGHT)) { //|| IsKeyPressed(KEY_D)) { - env->actions[0] = RIGHT; - } - if (IsKeyPressed(KEY_DOWN)) { //|| IsKeyPressed(KEY_S)){ - env->actions[0] = DOWN; - } - if (IsKeyPressed(KEY_SPACE)){ - env->actions[0] = GRAB; - } - if (IsKeyPressed(KEY_RIGHT_SHIFT)){ - env->actions[0] = DROP; - } - } - c_render(env); - - // Handle delayed level reset after puffer animation finishes - if (env->pending_reset) { - bool shouldReset = false; - - if (env->celebrationStarted) { - // Wait for full celebration sequence: 0.8s climbing + 0.4s beam + 0.7s banner = 1.9s total - float celebrationDuration = GetTime() - env->celebrationStartTime; - shouldReset = (celebrationDuration >= 1.9f); - } else { - // No celebration; reset when banner finishes - shouldReset = (!client->showBanner || client->bannerType != 1); - } - - if (shouldReset) { - env->pending_reset = false; - c_reset(env); - } - } - } - close_client(client); - free_allocated(env); - free_tower_climb_net(net); - free(weights); - free(levels[0].map); - free(levels); - free(puzzle_states[0].blocks); - free(puzzle_states); -} - -void performance_test() { - long test_time = 10; - CTowerClimb* env = allocate(); - int seed = 0; - init_random_level(env, 8, 25, 15, seed); - long start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - env->actions[0] = rand() % 5; - c_step(env); - i++; - } - long end = time(NULL); - printf("SPS: %ld\n", i / (end - start)); - free_allocated(env); -} - -int main() { - demo(); - // performance_test(); - return 0; -} - - diff --git a/pufferlib/ocean/tower_climb/tower_climb.h b/pufferlib/ocean/tower_climb/tower_climb.h deleted file mode 100644 index e86572493..000000000 --- a/pufferlib/ocean/tower_climb/tower_climb.h +++ /dev/null @@ -1,2151 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" -#include "raymath.h" -#include "rlgl.h" -#include -#include - -#if defined(PLATFORM_DESKTOP) - #define GLSL_VERSION 330 -#else // PLATFORM_ANDROID, PLATFORM_WEB - #define GLSL_VERSION 100 - -#endif -#define RLIGHTS_IMPLEMENTATION - -#include "rlights.h" - -#define NOOP -1 -#define UP 3 -#define LEFT 2 -#define RIGHT 0 -#define DOWN 1 -#define GRAB 4 -#define DROP 5 -// robot state -#define DEFAULT 0 -#define HANGING 1 -// observation space -#define PLAYER_OBS 3 -#define OBS_VISION 225 -// PLG VS ENV -#define PLG_MODE 0 -#define RL_MODE 1 -//logs -#define LOG_BUFFER_SIZE 1024 -// level size -#define row_max 10 -#define col_max 10 -#define depth_max 10 -// block bytes -#define BLOCK_BYTES 125 -// FNV-1a hash function -#define FNV_OFFSET 0xcbf29ce484222325ULL -#define FNV_PRIME 0x100000001b3ULL -// moves -#define MOVE_ILLEGAL 0 -#define MOVE_SUCCESS 1 -#define MOVE_DEATH 2 -// bitmask operations -#define SET_BIT(mask, i) ( (mask)[(i)/8] |= (1 << ((i)%8)) ) -#define CLEAR_BIT(mask, i) ( (mask)[(i)/8] &= ~(1 << ((i)%8)) ) -#define TEST_BIT(mask, i) ( ((mask)[(i)/8] & (1 << ((i)%8))) != 0 ) - -// BFS -#define MAX_BFS_SIZE 10000000 -#define MAX_NEIGHBORS 6 // based on action space - -// hash table -#define TABLE_SIZE 10000003 - -// direction vectors -#define NUM_DIRECTIONS 4 -static const int BFS_DIRECTION_VECTORS_X[NUM_DIRECTIONS] = {1, 0, -1, 0}; -static const int BFS_DIRECTION_VECTORS_Z[NUM_DIRECTIONS] = {0, 1, 0, -1}; -// shimmy wrap constants -static const int wrap_x[4][2] = { - {1,1}, - {1,-1}, - {-1,-1}, - {-1, 1} -}; -static const int wrap_z[4][2] = { - {-1, 1}, - {1, 1}, - {1, -1}, - {-1, -1} -}; -static const int wrap_orientation[4][2] = { - {DOWN,UP}, - {LEFT, RIGHT}, - {UP, DOWN}, - {RIGHT, LEFT} -}; - -typedef struct Level Level; -struct Level { - int* map; - int rows; - int cols; - int size; - int total_length; - int goal_location; - int spawn_location; -}; - -void init_level(Level* lvl){ - lvl->map = calloc(1000,sizeof(unsigned int)); - lvl->rows = 10; - lvl->cols = 10; - lvl->size = 100; - lvl->total_length = 1000; - lvl->goal_location = 999; - lvl->spawn_location = 0; -} - -void reset_level(Level* lvl){ - lvl->goal_location = 999; - lvl->spawn_location = 0; - memset(lvl->map, 0, 1000 * sizeof(unsigned int)); -} - -void free_level(Level* lvl){ - free(lvl->map); - free(lvl); -} - -typedef struct PuzzleState PuzzleState; -struct PuzzleState { - unsigned char* blocks; - int robot_position; - int robot_orientation; - int robot_state; - int block_grabbed; -}; - -void init_puzzle_state(PuzzleState* ps){ - ps->blocks = calloc(BLOCK_BYTES, sizeof(unsigned char)); -} - -void free_puzzle_state(PuzzleState* ps){ - free(ps->blocks); - free(ps); -} - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -typedef struct Client Client; -typedef struct CTowerClimb CTowerClimb; - -void trigger_banner(Client* client, int type); -struct CTowerClimb { - Client* client; - unsigned char* observations; - int* actions; - float* rewards; - unsigned char* terminals; - unsigned char* truncations; - Log log; - Log buffer; - float score; - int num_maps; - Level* all_levels; - PuzzleState* all_puzzles; - Level* level; - PuzzleState* state; // Contains blocks bitmask, position, orientation, etc. - int rows_cleared; - float reward_climb_row; - float reward_fall_row; - float reward_illegal_move; - float reward_move_block; - bool pending_reset; - bool goal_reached; - // Celebration timing (for visual effects) - float celebrationStartTime; - bool celebrationStarted; - bool bannerTriggered; - // Glow effect for visited positions - int visitedPositions[100]; // Track last 100 positions - float visitedTimes[100]; // Time when each position was visited - int visitedCount; - int visitedIndex; -}; - -void add_log(CTowerClimb* env) { - env->log.perf += env->buffer.perf; - env->log.score += env->buffer.score; - env->log.episode_return += env->buffer.episode_return; - env->log.episode_length += env->buffer.episode_length; - env->log.n += 1.0; - env->buffer = (Log){0}; -} - -void levelToPuzzleState(Level* level, PuzzleState* state) { - memset(state->blocks, 0, BLOCK_BYTES); - for (int i = 0; i < level->total_length; i++) { - if (level->map[i] == 1) { - SET_BIT(state->blocks, i); - } - } - state->robot_position = level->spawn_location; - state->robot_orientation = UP; - state->robot_state = 0; - state->block_grabbed = -1; -} - -void init(CTowerClimb* env) { - env->level = calloc(1, sizeof(Level)); - env->state = calloc(1, sizeof(PuzzleState)); - init_level(env->level); - init_puzzle_state(env->state); - env->rows_cleared = 0; - - // Initialize with minimal map storage to avoid fallback in c_reset - // env->num_maps = 0; - // env->all_levels = NULL; - // env->all_puzzles = NULL; - // env->pending_reset = false; - // env->goal_reached = false; - // env->bannerTriggered = false; -} - -void setPuzzle(CTowerClimb* env, PuzzleState* src, Level* lvl){ - memcpy(env->state->blocks, src->blocks, BLOCK_BYTES * sizeof(unsigned char)); - env->state->robot_position = src->robot_position; - env->state->robot_orientation = src->robot_orientation; - env->state->robot_state = src->robot_state; - env->state->block_grabbed = src->block_grabbed; - memcpy(env->level->map, lvl->map, lvl->total_length * sizeof(int)); - env->level->rows = lvl->rows; - env->level->cols = lvl->cols; - env->level->size = lvl->size; - env->level->total_length = lvl->total_length; - env->level->goal_location = lvl->goal_location; - env->level->spawn_location = lvl->spawn_location; -} - -CTowerClimb* allocate() { - CTowerClimb* env = (CTowerClimb*)calloc(1, sizeof(CTowerClimb)); - init(env); - env->observations = (unsigned char*)calloc(OBS_VISION+PLAYER_OBS, sizeof(unsigned char)); - env->actions = (int*)calloc(1, sizeof(int)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); - return env; -} - -void c_close(CTowerClimb* env) { - free_level(env->level); - free_puzzle_state(env->state); - free(env); -} - -void free_allocated(CTowerClimb* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void calculate_window_bounds(int* bounds, int center_pos, int window_size, int max_size) { - int half_size = window_size / 2; - // Try to center on position - bounds[0] = center_pos - half_size; // start - bounds[1] = bounds[0] + window_size; // end - // Adjust if window is larger than max size - if (window_size > max_size) { - bounds[0] = 0; - bounds[1] = max_size; - } - // Adjust if too close to start - else if (bounds[0] < 0) { - bounds[0] = 0; - bounds[1] = window_size; - } - // Adjust if too close to end - else if (bounds[1] > max_size) { - bounds[1] = max_size; - bounds[0] = bounds[1] - window_size; - if (bounds[0] < 0) bounds[0] = 0; - } -} - -void compute_observations(CTowerClimb* env) { - int sz = env->level->size; - int cols = env->level->cols; - int rows = env->level->rows; - int max_floors = env->level->total_length / sz; - // Get player position - int current_floor = env->state->robot_position / sz; - int grid_pos = env->state->robot_position % sz; - int player_x = grid_pos % cols; - int player_z = grid_pos / cols; - // Calculate window bounds using the new function - int y_bounds[2], x_bounds[2], z_bounds[2]; - calculate_window_bounds(y_bounds, current_floor + 1, 5, max_floors); - calculate_window_bounds(x_bounds, player_x, 9, cols); - calculate_window_bounds(z_bounds, player_z, 5, rows); - // Fill in observations - for (int y = 0; y < 5; y++) { - int world_y = y + y_bounds[0]; - for (int z = 0; z < 5; z++) { - int world_z = z + z_bounds[0]; - for (int x = 0; x < 9; x++) { - int world_x = x + x_bounds[0]; - int obs_idx = x + z * 9 + y * (9 * 5); - // Check if position is out of bounds - int board_idx = world_y * sz + world_z * cols + world_x; - // Position is in bounds, set observation - if (board_idx == env->state->robot_position) { - env->observations[obs_idx] = 3; - continue; - } - else if (board_idx == env->level->goal_location){ - env->observations[obs_idx] = 2; - continue; - } - // Use bitmask directly instead of board_state array - env->observations[obs_idx] = TEST_BIT(env->state->blocks, board_idx); - } - } - } - // Add player state information at the end - int state_start = 9 * 5 * 5; - env->observations[state_start] = env->state->robot_orientation; - env->observations[state_start + 1] = env->state->robot_state; - env->observations[state_start + 2] = (env->state->block_grabbed != -1); -} - -void c_reset(CTowerClimb* env) { - env->terminals[0] = 0; - env->rows_cleared = 0; - env->goal_reached = false; - env->celebrationStarted = false; - env->bannerTriggered = false; - // Initialize glow tracking - env->visitedCount = 0; - env->visitedIndex = 0; - memset(env->visitedPositions, -1, sizeof(env->visitedPositions)); - memset(env->visitedTimes, 0, sizeof(env->visitedTimes)); - memset(env->state->blocks, 0, BLOCK_BYTES * sizeof(unsigned char)); - - // Always use pre-generated maps (ensure at least 1 exists during initialization) - // printf("num maps: %d\n", env->num_maps); - if (env->num_maps > 0) { - int idx = rand() % env->num_maps; - setPuzzle(env, &env->all_puzzles[idx], &env->all_levels[idx]); - } else { - // Emergency fallback: use a simple default level - env->level->goal_location = 999; - env->level->spawn_location = 0; - memset(env->level->map, 0, env->level->total_length * sizeof(int)); - env->level->map[0] = 1; // Ground block - levelToPuzzleState(env->level, env->state); - } - - compute_observations(env); -} - -void illegal_move(CTowerClimb* env){ - env->rewards[0] = env->reward_illegal_move; - env->buffer.episode_return += env->reward_illegal_move; -} - -void death(CTowerClimb* env){ - env->rewards[0] = -1; - env->buffer.episode_return -= 1; - env->buffer.perf = 0; - add_log(env); -} - -int isGoal( PuzzleState* s, Level* lvl) { - if (s->robot_position - lvl->size != lvl->goal_location) return 0; - return 1; -} - -int move(PuzzleState* outState, int action, int mode, CTowerClimb* env, const Level* lvl){ - int new_position = outState->robot_position + BFS_DIRECTION_VECTORS_X[action] + BFS_DIRECTION_VECTORS_Z[action]*lvl->cols; - outState->robot_position = new_position; - return 1; -} - -int climb(PuzzleState* outState, int action, int mode, CTowerClimb* env, const Level* lvl){ - int cell_direct_above = outState->robot_position + lvl->size; - int cell_next_above = cell_direct_above + BFS_DIRECTION_VECTORS_X[action] + BFS_DIRECTION_VECTORS_Z[action]*lvl->cols; - int goal = lvl->goal_location; - - int in_bounds = cell_direct_above < lvl->total_length && cell_next_above < lvl->total_length; - int cells_blocking_climb = TEST_BIT(outState->blocks, cell_direct_above) || TEST_BIT(outState->blocks, cell_next_above); - int goal_blocking_climb = cell_direct_above == goal || cell_next_above == goal; - int can_climb = in_bounds && !cells_blocking_climb && !goal_blocking_climb; - - if (!can_climb) return 0; - int floor_cleared = (cell_direct_above / lvl->size) - 2; - if(mode == RL_MODE && floor_cleared > env->rows_cleared){ - env->rows_cleared = floor_cleared; - env->rewards[0] = env->reward_climb_row; - env->buffer.episode_return += env->reward_climb_row; - env->buffer.score = floor_cleared; - } - outState->robot_position = cell_next_above; - outState->robot_state = 0; - return 1; -} - -int drop(PuzzleState* outState, int action, int mode, CTowerClimb* env, const Level* lvl){ - int next_cell = outState->robot_position + BFS_DIRECTION_VECTORS_X[action] + BFS_DIRECTION_VECTORS_Z[action]*lvl->cols; - int next_below_cell = next_cell - lvl->size; - int next_double_below_cell = next_cell - 2*lvl->size; - if (next_below_cell < 0) return 0; - int step_down = next_double_below_cell >= 0 && TEST_BIT(outState->blocks, next_double_below_cell); - if(mode == RL_MODE){ - env->rewards[0] = env->reward_fall_row; - env->buffer.episode_return += env->reward_fall_row; - } - if (step_down){ - outState->robot_position = next_below_cell; - return 1; - } else { - outState->robot_position = next_below_cell; - outState->robot_orientation = (outState->robot_orientation + 2) % 4; - outState->robot_state = 1; - return 1; - } -} - -int drop_from_hang(PuzzleState* outState, int action, int mode, const Level* lvl){ - int below_cell = outState->robot_position - lvl->size; - while(below_cell > lvl->size && !TEST_BIT(outState->blocks, below_cell)){ - below_cell -= lvl->size; - } - if (below_cell >= lvl->size) { - outState->robot_position = below_cell+lvl->size; - outState->robot_state = 0; - return 1; - } - if (mode == PLG_MODE) return MOVE_ILLEGAL; - return MOVE_DEATH; -} -static inline int bfs_is_valid_position(int pos, const Level* level) { - return (pos >= 0 && pos < level->total_length); -} - -// Helper function to check block stability -static int bfs_is_block_stable(const PuzzleState* state, int position, const Level* level) { - const int fs = level->size; - const int positions[] = { - position - fs, // Bottom - position - fs - 1, // Left - position - fs + 1, // Right - position - fs - level->cols, // Front - position - fs + level->cols // Back - }; - - for (int i = 0; i < 5; i++) { - if (bfs_is_valid_position(positions[i], level) && TEST_BIT(state->blocks, positions[i])) { - return 1; - } - } - return 0; -} - -int will_fall(PuzzleState* outState, int position, const Level* lvl){ - int valid = bfs_is_valid_position(position, lvl); - int block_or_goal = TEST_BIT(outState->blocks, position) || position == lvl->goal_location; - int stable = bfs_is_block_stable(outState, position, lvl); - return valid && block_or_goal && !stable; -} - -int handle_block_falling(PuzzleState* outState, int* affected_blocks, int* blocks_to_move, int affected_blocks_count, const Level* lvl) { - // Create queue for blocks that need to be checked for falling - int bfs_queue[lvl->total_length]; // Max possible blocks to check - int front = 0; - int rear = 0; - int fs = lvl->size; - int cols = lvl->cols; - - // Add initially affected blocks to queue - for (int i = 0; i < affected_blocks_count; i++) { - if (affected_blocks[i] == -1){ - break; - } - bfs_queue[rear] = affected_blocks[i]; - rear++; - } - // First check all blocks above and adjacent to moved blocks - for (int i = 0; i < lvl->cols; i++) { - int block_pos = blocks_to_move[i]; - if (block_pos == -1){ - continue; - } - // Add block directly above - int cell_above = block_pos + fs; // Assuming 100 is floor height - // If valid block above and unstable, add to queue - if (will_fall(outState, cell_above, lvl)) { - bfs_queue[rear++] = cell_above; - } - // Check edge-supported blocks - int edge_blocks[4] = { - cell_above - 1, // left - cell_above + 1, // right - cell_above - cols, // front (assuming 10 is width) - cell_above + cols // back - }; - // Add valid edge blocks to queue - for (int j = 0; j < 4; j++) { - if (will_fall(outState, edge_blocks[j], lvl)) { - bfs_queue[rear++] = edge_blocks[j]; - } - } - } - // Process queue until empty - while (front < rear) { - int current = bfs_queue[front++]; - int falling_position = current; - int found_support = 0; - // Check if block is goal (2) - if (current == lvl->goal_location) { - // Goal block is falling - level failed - return 0; - } - // Remove block from current position - CLEAR_BIT(outState->blocks, current); - // Keep moving down until support found or bottom reached - while (!found_support && falling_position >= fs) { // 100 represents one floor down - // Place block temporarily to check stability - SET_BIT(outState->blocks, falling_position); - // Check if block is stable - if (bfs_is_block_stable(outState, falling_position, lvl)) { - found_support = 1; - } else { - // Remove block if not stable - CLEAR_BIT(outState->blocks, falling_position); - falling_position -= fs; // Move down one level - } - } - // Check blocks that might be affected by this fall - int original_above = current + fs; // Block directly above original position - if (will_fall(outState, original_above, lvl)) { - bfs_queue[rear++] = original_above; - } - // Check edge blocks that might be affected - int edge_blocks[4] = { - original_above - 1, // Left - original_above + 1, // Right - original_above - cols, // Front (assuming 10 is the width) - original_above + cols // Back - }; - for (int i = 0; i < 4; i++) { - if (will_fall(outState, edge_blocks[i], lvl)) { - bfs_queue[rear++] = edge_blocks[i]; - } - } - } - return 1; -} - -int push(PuzzleState* outState, int action, const Level* lvl, int mode, CTowerClimb* env, int block_offset){ - int first_block_index = outState->robot_position + BFS_DIRECTION_VECTORS_X[outState->robot_orientation] + BFS_DIRECTION_VECTORS_Z[outState->robot_orientation]*lvl->cols; - int* blocks_to_move = calloc(lvl->cols, sizeof(int)); - for(int i = 0; i < lvl->cols; i++) { - blocks_to_move[i] = (i == 0) ? first_block_index : -1; - } - for (int i = 0; i < lvl->cols; i++){ - int b_address = first_block_index + i*block_offset; - if(i!=0 && blocks_to_move[i-1] == -1){ - break; - } - if(TEST_BIT(outState->blocks, b_address)){ - blocks_to_move[i] = b_address; - } - } - int affected_blocks[lvl->cols]; - int count = 0; - for (int i = 0; i < lvl->cols; i++){ - // invert conditional - int b_index = blocks_to_move[i]; - if (b_index == -1){ - continue; - } - if(i==0){ - CLEAR_BIT(outState->blocks, b_index); - } - int grid_pos = b_index % lvl->size; - int x = grid_pos % lvl->cols; - int z = grid_pos / lvl->cols; - // Check if movement would cross floor boundaries - if ((x == 0 && block_offset == -1) || // Don't move left off floor - (x == lvl->cols-1 && block_offset == 1) || // Don't move right off floor - (z == 0 && block_offset == -lvl->cols) || // Don't move forward off floor - (z == lvl->rows-1 && block_offset == lvl->cols)) { // Don't move back off floor - continue; - } - - // If we get here, movement is safe - SET_BIT(outState->blocks, b_index + block_offset); - affected_blocks[count] = b_index + block_offset; - count++; - } - outState->block_grabbed = -1; - int result = handle_block_falling(outState, affected_blocks, blocks_to_move,count, lvl); - free(blocks_to_move); - return result; -} - -int pull(PuzzleState* outState, int action, const Level* lvl, int mode, CTowerClimb* env, int block_offset){ - int pull_block = outState->robot_position + BFS_DIRECTION_VECTORS_X[outState->robot_orientation] + BFS_DIRECTION_VECTORS_Z[outState->robot_orientation]*lvl->cols; - int block_in_front = TEST_BIT(outState->blocks, pull_block); - int block_behind = TEST_BIT(outState->blocks, outState->robot_position + block_offset); - int cell_below_next_position = outState->robot_position + block_offset - lvl->size; - int backwards_walkable = bfs_is_valid_position(cell_below_next_position, lvl) && TEST_BIT(outState->blocks, cell_below_next_position); - if (block_behind){ - return 0; - } - if (block_in_front){ - CLEAR_BIT(outState->blocks, pull_block); - SET_BIT(outState->blocks, outState->robot_position); - if (backwards_walkable){ - outState->block_grabbed = outState->robot_position; - outState->robot_position = outState->robot_position + block_offset; - } - else { - outState->robot_position = cell_below_next_position; - outState->robot_state = 1; - outState->block_grabbed = -1; - } - } - int blocks_to_move[10]; - for(int i = 0; i<10; i++){ - blocks_to_move[i] = -1; - } - blocks_to_move[0] = pull_block; - int affected_blocks[1] = {-1}; - return handle_block_falling(outState, affected_blocks, blocks_to_move, 1, lvl); -} - -int shimmy_normal(PuzzleState* outState, int action, const Level* lvl, int local_direction, int mode, CTowerClimb* env){ - int next_cell = outState->robot_position + BFS_DIRECTION_VECTORS_X[local_direction] + BFS_DIRECTION_VECTORS_Z[local_direction]*lvl->cols; - int above_next_cell = next_cell + lvl->size; - if (bfs_is_valid_position(above_next_cell, lvl) && !TEST_BIT(outState->blocks, above_next_cell)){ - outState->robot_position = next_cell; - return 1; - } - return 0; -} - -int wrap_around(PuzzleState* outState, int action, const Level* lvl, int mode, CTowerClimb* env){ - int action_idx = (action == LEFT) ? 0 : 1; - int grid_pos = outState->robot_position % lvl->size; - int x = grid_pos % lvl->cols; - int z = grid_pos / lvl->cols; - int current_floor = outState->robot_position / lvl->size; - int new_x = x + wrap_x[outState->robot_orientation][action_idx]; - int new_z = z + wrap_z[outState->robot_orientation][action_idx]; - int new_pos = new_x + new_z*lvl->cols + current_floor*lvl->size; - if (TEST_BIT(outState->blocks, new_pos + lvl->size)){ - return 0; - } - outState->robot_position = new_pos; - outState->robot_orientation = wrap_orientation[outState->robot_orientation][action_idx]; - return 1; -} - -int climb_from_hang(PuzzleState* outState, int action, const Level* lvl, int next_cell, int mode, CTowerClimb* env){ - int climb_index = next_cell + lvl->size; - int direct_above_index = outState->robot_position + lvl->size; - int can_climb = bfs_is_valid_position(climb_index, lvl) && bfs_is_valid_position(direct_above_index, lvl) - && !TEST_BIT(outState->blocks, climb_index) && !TEST_BIT(outState->blocks, direct_above_index); - if (can_climb){ - outState->robot_position = climb_index; - outState->robot_state = 0; - return 1; - } - return 0; -} - -int applyAction(PuzzleState* outState, int action, Level* lvl, int mode, CTowerClimb* env) { - // necessary variables - int next_dx = BFS_DIRECTION_VECTORS_X[outState->robot_orientation]; - int next_dz = BFS_DIRECTION_VECTORS_Z[outState->robot_orientation]; - int grid_pos = outState->robot_position % lvl->size; - int x = grid_pos % lvl->cols; - int z = grid_pos / lvl->cols; - int next_cell = outState->robot_position + next_dx + next_dz*lvl->cols; - int next_below_cell = next_cell - lvl->size; - int walkable = bfs_is_valid_position(next_below_cell, lvl) && TEST_BIT(outState->blocks, next_below_cell); - int block_in_front = bfs_is_valid_position(next_cell, lvl) && (TEST_BIT(outState->blocks, next_cell) || next_cell == lvl->goal_location); - int movement_action = (action >= 0 && action < 4); - int move_orient_check = (action == outState->robot_orientation); - int standing_and_holding_nothing = outState->robot_state == 0 && outState->block_grabbed == -1; - int hanging = outState->robot_state == 1; - // Handle movement actions with common orientation check - if (standing_and_holding_nothing && movement_action) { - // If orientation doesn't match action, just rotate - if (!move_orient_check) { - outState->robot_orientation = action; - return 1; - } - // Now handle the actual movement cases - if (walkable && !block_in_front) { - return move(outState, action, mode, env, lvl); - } - if (block_in_front) { - return climb(outState, action, mode, env, lvl); - } - if (!block_in_front && !walkable) { - return drop(outState, action, mode, env, lvl); - } - } - - if(hanging && movement_action){ - if (action == UP){ - return climb_from_hang(outState, action, lvl, next_cell, mode, env); - } - if (action == DOWN){ - return 0; - } - int local_direction = outState->robot_orientation; - if (action == LEFT){ - local_direction = (outState->robot_orientation + 3) % 4; - } - if (action == RIGHT){ - local_direction = (outState->robot_orientation + 1) % 4; - } - int shimmy_cell = next_cell + BFS_DIRECTION_VECTORS_X[local_direction] + BFS_DIRECTION_VECTORS_Z[local_direction]*lvl->cols; - int shimmy_path_cell = outState->robot_position + BFS_DIRECTION_VECTORS_X[local_direction] + BFS_DIRECTION_VECTORS_Z[local_direction]*lvl->cols; - - int basic_shimmy = bfs_is_valid_position(shimmy_cell, lvl) && bfs_is_valid_position(shimmy_path_cell, lvl) && TEST_BIT(outState->blocks, shimmy_cell) && !TEST_BIT(outState->blocks, shimmy_path_cell); - int rotation_shimmy = bfs_is_valid_position(shimmy_path_cell, lvl) && TEST_BIT(outState->blocks, shimmy_path_cell); - int in_bounds = x + next_dx >= 0 && x + next_dx < lvl->cols && z + next_dz >= 0 && z + next_dz < lvl->rows; - int wrap_shimmy = in_bounds && !TEST_BIT(outState->blocks, shimmy_path_cell) && !TEST_BIT(outState->blocks, shimmy_cell); - - if(basic_shimmy){ - return shimmy_normal(outState, action, lvl, local_direction, mode, env); - } - else if(rotation_shimmy){ - //rotate shimmy - static const int LEFT_TURNS[] = {3, 0, 1, 2}; // RIGHT->UP, DOWN->RIGHT, LEFT->DOWN, UP->LEFT - static const int RIGHT_TURNS[] = {1, 2, 3, 0}; // RIGHT->DOWN, DOWN->LEFT, LEFT->UP, UP->RIGHT - - outState->robot_orientation = (action == LEFT) ? - LEFT_TURNS[outState->robot_orientation] : - RIGHT_TURNS[outState->robot_orientation]; - outState->robot_state = 1; - return 1; - } - else if(wrap_shimmy){ - return wrap_around(outState, action, lvl, mode, env); - } - } - // drop from hang action - if (action == DROP && !standing_and_holding_nothing) { - return drop_from_hang(outState, action, mode, lvl); - } - // grab action - if (action == GRAB && standing_and_holding_nothing - && block_in_front){ - if (outState->block_grabbed == -1){ - outState->block_grabbed = next_cell; - return 1; - } - } - if (action == GRAB && outState->block_grabbed != -1){ - outState->block_grabbed = -1; - return 1; - } - - // push or pull block - if (movement_action && block_in_front && outState->block_grabbed != -1){ - int result = 0; - int block_offset = BFS_DIRECTION_VECTORS_X[action] + BFS_DIRECTION_VECTORS_Z[action] * lvl->cols; - if (outState->robot_orientation == action){ - result = push(outState, action, lvl, mode, env, block_offset); - } else if(outState->robot_orientation == (action+2)%4){ - result = pull(outState, action, lvl, mode, env, block_offset); - } else { - outState->robot_orientation = action; - outState->block_grabbed = -1; - result = 1; - } - // block fell on top of player - if (TEST_BIT(outState->blocks, outState->robot_position)){ - if (mode == PLG_MODE){ - return MOVE_ILLEGAL; - } - if (mode == RL_MODE){ - return MOVE_DEATH; - } - } - if (mode == RL_MODE && result == 1){ - env->rewards[0] = env->reward_move_block; - env->buffer.episode_return += env->reward_move_block; - } - return result; - } - return 0; -} - -void c_step(CTowerClimb* env) { - env->buffer.episode_length += 1.0; - env->rewards[0] = 0.0; - if(env->buffer.episode_length > 60){ - env->rewards[0] = 0; - env->buffer.perf = 0; - add_log(env); - if (env->client && !env->bannerTriggered) { - trigger_banner(env->client, 2); // Timeout = failure - env->bannerTriggered = true; - } - c_reset(env); - } - - // Prevent movement if goal is reached (during celebration) - if (env->goal_reached) { - compute_observations(env); - return; - } - - // Create next state - int move_result = applyAction(env->state, env->actions[0], env->level, RL_MODE, env); - if (move_result == MOVE_ILLEGAL) { - illegal_move(env); - return; - } - if (move_result == MOVE_DEATH){ - death(env); - if (env->client && !env->bannerTriggered) { - trigger_banner(env->client, 2); // Death = failure - env->bannerTriggered = true; - } - c_reset(env); - } - - // Check for goal state - if (isGoal(env->state, env->level)) { - env->goal_reached = true; - env->rewards[0] = 1.0; - env->buffer.episode_return +=1.0; - env->buffer.perf = 1.0; - add_log(env); - if (env->client) { - // Start celebration immediately when goal is reached - env->celebrationStarted = true; - env->celebrationStartTime = GetTime(); - env->pending_reset = true; // Mark for delayed reset - // Banner will be triggered after beam effect completes in render function - } else { - c_reset(env); // If no client, reset immediately - } - } - - // Track the cube the player is standing on or climbing on for glow effect - int standingOnPosition = env->state->robot_position - env->level->size; - // Only track if the position is valid and has cube - if (standingOnPosition >= 0 && TEST_BIT(env->state->blocks, standingOnPosition)) { - env->visitedPositions[env->visitedIndex] = standingOnPosition; - env->visitedTimes[env->visitedIndex] = GetTime(); - env->visitedIndex = (env->visitedIndex + 1) % 100; - if (env->visitedCount < 100) env->visitedCount++; - } - - // Update observations - compute_observations(env); -} - -typedef struct BFSNode { - PuzzleState state; - int depth; // how many moves from start - int parent; // index in BFS array of who generated me - int action; // which action led here (if you want to reconstruct the path) -} BFSNode; - -static BFSNode* queueBuffer = NULL; -static int front = 0, back = 0; - -// hash table for visited states -typedef struct VisitedNode { - PuzzleState state; - uint64_t hashVal; - struct VisitedNode* next; -} VisitedNode; - -static VisitedNode* visitedTable[TABLE_SIZE]; -// Helper to incorporate a 32-bit integer into the hash one byte at a time. -static inline uint64_t fnv1a_hash_int(uint64_t h, int value) { - // Break the int into 4 bytes (assuming 32-bit int). - // This ensures consistent hashing regardless of CPU endianness. - unsigned char bytes[4]; - bytes[0] = (unsigned char)((value >> 0) & 0xFF); - bytes[1] = (unsigned char)((value >> 8) & 0xFF); - bytes[2] = (unsigned char)((value >> 16) & 0xFF); - bytes[3] = (unsigned char)((value >> 24) & 0xFF); - for (int i = 0; i < 4; i++) { - h ^= bytes[i]; - h *= FNV_PRIME; - } - return h; -} - -uint64_t hashPuzzleState(const PuzzleState *s) { - uint64_t h = FNV_OFFSET; - // 1) Hash the 125-byte bitmask - for (int i = 0; i < BLOCK_BYTES; i++) { - h ^= s->blocks[i]; - h *= FNV_PRIME; - } - // 2) Hash the int fields (position, orientation, state, block_grabbed) - h = fnv1a_hash_int(h, s->robot_position); - h = fnv1a_hash_int(h, s->robot_orientation); - h = fnv1a_hash_int(h, s->robot_state); - h = fnv1a_hash_int(h, s->block_grabbed); - return h; -} -// Compares two puzzle states fully -int equalPuzzleState(const PuzzleState* a, const PuzzleState* b) { - // compare bitmask - if (memcmp(a->blocks, b->blocks, BLOCK_BYTES) != 0) return 0; - // compare other fields - if (a->robot_position != b->robot_position) return 0; - if (a->robot_orientation != b->robot_orientation) return 0; - if (a->robot_state != b->robot_state) return 0; - if (a->block_grabbed != b->block_grabbed) return 0; - return 1; -} - -void resetVisited(void) { - memset(visitedTable, 0, sizeof(visitedTable)); -} - -// Helper function to find a node in the hash table -static VisitedNode* findNode(const PuzzleState* s, uint64_t hv, size_t idx) { - VisitedNode* node = visitedTable[idx]; - while (node) { - if (node->hashVal == hv && equalPuzzleState(&node->state, s)) { - return node; - } - node = node->next; - } - return NULL; -} - -int isVisited(const PuzzleState* s) { - uint64_t hv = hashPuzzleState(s); - size_t idx = (size_t)(hv % TABLE_SIZE); - return findNode(s, hv, idx) != NULL; -} - -void markVisited(const PuzzleState* s) { - uint64_t hv = hashPuzzleState(s); - size_t idx = (size_t)(hv % TABLE_SIZE); - // Return if already present - if (findNode(s, hv, idx)) { - return; - } - // Insert new node - VisitedNode* node = (VisitedNode*)malloc(sizeof(VisitedNode)); - node->state.blocks = (unsigned char*)malloc(BLOCK_BYTES * sizeof(unsigned char)); - // Copy the blocks data - memcpy(node->state.blocks, s->blocks, BLOCK_BYTES * sizeof(unsigned char)); - // Copy other fields - node->state.robot_position = s->robot_position; - node->state.robot_orientation = s->robot_orientation; - node->state.robot_state = s->robot_state; - node->state.block_grabbed = s->block_grabbed; - node->hashVal = hv; - node->next = visitedTable[idx]; - visitedTable[idx] = node; -} - -static PuzzleState copyPuzzleState(const PuzzleState* src) -{ - PuzzleState dst = { - .blocks = NULL, - .robot_position = src->robot_position, - .robot_orientation = src->robot_orientation, - .robot_state = src->robot_state, - .block_grabbed = src->block_grabbed - }; - if(src->blocks) { - dst.blocks = (unsigned char*)calloc(BLOCK_BYTES, sizeof(unsigned char)); - if (dst.blocks) { - memcpy(dst.blocks, src->blocks, BLOCK_BYTES); - } - } - return dst; -} - -// This function fills out up to MAX_NEIGHBORS BFSNodes in 'outNeighbors' -// from a given BFSNode 'current'. It returns how many neighbors it produced. -int getNeighbors(const BFSNode* current, BFSNode* outNeighbors, Level* lvl) { - int count = 0; - // We'll read the current BFSNode's puzzle state - const PuzzleState* curState = ¤t->state; - // Try each action - for (int i = 0; i < 6; i++) { - int action = i; - // 1) Make a copy of the current puzzle state - PuzzleState newState = copyPuzzleState(curState); - // 2) Attempt to apply the action to newState - int success = applyAction(&newState, action, lvl, PLG_MODE, NULL); - if (!success) { - // Move was invalid, skip - free(newState.blocks); - continue; - } - // 3) If valid, build a BFSNode - BFSNode neighbor; - neighbor.state = newState; - neighbor.depth = current->depth + 1; - neighbor.parent = -1; // BFS sets or overwrites this later - neighbor.action = action; // record which action led here - // 4) Add to 'outNeighbors' array - outNeighbors[count++] = neighbor; - // If you only allow up to 6 total, we can break if we reach that - if (count >= MAX_NEIGHBORS) break; - } - return count; // how many valid neighbors we produced -} - -void freeQueueBuffer(BFSNode* queueBuffer, int back){ - if (!queueBuffer) return; - for (int i = 0; i < back; i++) { - free(queueBuffer[i].state.blocks); - } - free(queueBuffer); -} - -// Example BFS -int bfs(PuzzleState* start, int maxDepth, Level* lvl, int min_moves) { - // Clear or init your BFS queue - queueBuffer = (BFSNode*)malloc(MAX_BFS_SIZE * sizeof(BFSNode)); - if (!queueBuffer) { - printf("Failed to allocate memory for BFS queue\n"); - return 0; - } - front = 0; - back = 0; - // Enqueue start node - BFSNode startNode; - startNode.state = copyPuzzleState(start); // copy puzzle state - startNode.depth = 0; - startNode.parent = -1; - startNode.action = -1; - queueBuffer[back++] = startNode; - // BFS loop - while (front < back) { - if (back >= MAX_BFS_SIZE) { - printf("BFS queue overflow! Increase MAX_BFS_SIZE or optimize search.\n"); - freeQueueBuffer(queueBuffer, back); - queueBuffer = NULL; - return 0; - } - BFSNode current = queueBuffer[front]; - int currentIndex = front; - front++; - // If current.state is the goal, reconstruct path - if (isGoal(¤t.state, lvl)) { - if(current.depth < min_moves){ - freeQueueBuffer(queueBuffer, back); - queueBuffer = NULL; - return 0; - } - // Store nodes in order - BFSNode* path = (BFSNode*)malloc((current.depth + 1) * sizeof(BFSNode)); - BFSNode node = current; - int idx = current.depth; - // Walk backwards to get path - while (idx >= 0) { - path[idx] = node; - if (node.parent != -1) { - node = queueBuffer[node.parent]; - } - idx--; - } - free(path); - freeQueueBuffer(queueBuffer, back); - queueBuffer = NULL; - return 1; - } - if (current.depth >= maxDepth) continue; - // generate neighbors - BFSNode neighbors[MAX_NEIGHBORS]; - int nCount = getNeighbors(¤t, neighbors, lvl); - for (int i = 0; i < nCount; i++) { - PuzzleState* nxt = &neighbors[i].state; - if (!isVisited(nxt)) { - markVisited(nxt); - neighbors[i].depth = current.depth + 1; - neighbors[i].parent = currentIndex; - queueBuffer[back++] = neighbors[i]; - } else { - free(nxt->blocks); - } - } - } - freeQueueBuffer(queueBuffer, back); - queueBuffer = NULL; - // If we exit while, no solution found within maxDepth - //printf("No solution within %d moves.\n", maxDepth); - return 0; -} - -void cleanupVisited(void) { - for (int i = 0; i < TABLE_SIZE; i++) { - VisitedNode* current = visitedTable[i]; - while (current != NULL) { - VisitedNode* next = current->next; - free(current->state.blocks); - free(current); - current = next; - } - visitedTable[i] = NULL; - } -} -int verify_level(Level* level, int max_moves, int min_moves){ - // converting level to puzzle state - PuzzleState* state = calloc(1, sizeof(PuzzleState)); - init_puzzle_state(state); - levelToPuzzleState(level, state); - // reset visited hash table - resetVisited(); - markVisited(state); - // Run BFS - int solvable = bfs(state, max_moves, level, min_moves); - cleanupVisited(); - free_puzzle_state(state); - return solvable; -} - -void gen_level(Level* lvl, int goal_level) { - // Initialize an illegal level in case we need to return early - int legal_width_size = 8; - int legal_depth_size = 8; - int area = depth_max * col_max; - int spawn_created = 0; - int spawn_index = -1; - int goal_created =0; - int goal_index = -1; - for(int y= 0; y < row_max; y++){ - for(int z = depth_max - 1; z >= 0; z--){ - for(int x = 0; x< col_max; x++){ - int block_index = x + col_max * z + area * y; - int within_legal_bounds = x>=1 && x < legal_width_size && z >= 1 && z < legal_depth_size && y>=1 && y < goal_level; - int allowed_block_placement = within_legal_bounds && (z <= (legal_depth_size - y)); - if (allowed_block_placement){ - int chance = (rand() % 2 ==0) ? 1 : 0; - lvl->map[block_index] = chance; - // create spawn point above an existing block - if (spawn_created == 0 && y == 2 && lvl->map[block_index - area] == 1){ - spawn_created = 1; - spawn_index = block_index; - lvl->map[spawn_index] = 0; - } - } - if (!goal_created && y == goal_level && - (lvl->map[block_index + col_max - area] == 1 || - lvl->map[block_index - 1 - area] == 1 || - lvl->map[block_index + 1 - area] == 1)) { - // 33% chance to place goal here, unless we're at the last valid position - if (rand() % 3 == 0 || (x == col_max-1 && z == 0)) { - goal_created = 1; - goal_index = block_index; - lvl->map[goal_index] = 2; - } - } - } - } - } - if (!spawn_created || spawn_index < 0) { - return; - } - if (!goal_created || goal_index < 0) { - return; - } - lvl->rows = row_max; - lvl->cols = col_max; - lvl->size = row_max * col_max; - lvl->total_length = row_max * col_max * depth_max; - lvl->goal_location = goal_index; - lvl->spawn_location = spawn_index; -} - -void init_random_level(CTowerClimb* env, int goal_level, int max_moves, int min_moves, int seed) { - time_t t; - srand((unsigned) time(&t) + seed); // Increment seed for each level - reset_level(env->level); - gen_level(env->level, goal_level); - // guarantee a map is created - while(env->level->spawn_location == 0 || env->level->goal_location == 999 || verify_level(env->level,max_moves, min_moves) == 0){ - reset_level(env->level); - gen_level(env->level,goal_level); - } - levelToPuzzleState(env->level, env->state); -} - -void cy_init_random_level(Level* level, int goal_level, int max_moves, int min_moves, int seed) { - time_t t; - srand((unsigned) time(&t) + seed); // Increment seed for each level - gen_level(level, goal_level); - // guarantee a map is created - while(level->spawn_location == 0 || level->goal_location == 999 || verify_level(level,max_moves, min_moves) == 0){ - gen_level(level, goal_level); - } -} - -const Color STONE_GRAY = (Color){80, 80, 80, 255}; -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_GREY = (Color){128, 128, 128, 255}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; -const Color PUFF_BACKGROUND2 = (Color){18, 72, 72, 255}; - -typedef enum { - ANIM_IDLE, - ANIM_RUNNING, - ANIM_CLIMBING, - ANIM_HANGING, - ANIM_START_GRABBING, - ANIM_GRABBING, - ANIM_SHIMMY_RIGHT, - ANIM_SHIMMY_LEFT, -} AnimationState; - -struct Client { - float width; - float height; - Texture2D background; - Camera3D camera; - Model robot; - Model puffer; - Light lights[MAX_LIGHTS]; - Shader shader; - ModelAnimation* animations; - int animFrameCounter; - AnimationState animState; - int previousRobotPosition; - Vector3 visualPosition; - Vector3 targetPosition; - int isMoving; - float moveProgress; - Model cube; - float scale; - int enable_animations; - // Camera rotation controls - Vector2 lastMousePos; - bool isDragging; - float cameraDistance; - float cameraAngleX; - float cameraAngleY; - bool followPlayer; - // Lighting smoothing - float lightingSmoothing; - float previousLightIntensity; - // UI state - float bannerStartTime; - int bannerType; // 0=none, 1=success, 2=failure - bool showBanner; -}; - -void trigger_banner(Client* client, int type) { - client->bannerType = type; - client->bannerStartTime = GetTime(); - client->showBanner = true; -} - -Client* make_client(CTowerClimb* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - - // Calculate screen dimensions based on level size - float levelWidth = (float)env->level->cols; - float levelDepth = (float)env->level->rows; - int totalFloors = env->level->total_length / env->level->size; - float levelHeight = (float)totalFloors; - - // Calculate appropriate window size to fit level better - // Use aspect ratio based on level dimensions, with some padding - float levelAspectRatio = levelWidth / levelHeight; - int targetHeight = 900; - int targetWidth = (int)(targetHeight * levelAspectRatio * 1.4f * 0.8f); // 20% smaller window width - - // Clamp width to reasonable bounds - client->width = fmaxf(640, fminf(targetWidth, 1120)); // Reduced bounds by 20% - client->height = targetHeight; - - SetConfigFlags(FLAG_MSAA_4X_HINT); // Enable MSAA - InitWindow(client->width, client->height, "PufferLib Ray Tower Climb"); - SetTargetFPS(60); - - // Calculate camera distance to fit entire level height - float fovRad = 45.0f * DEG2RAD; - float minDistance = (levelHeight * 0.6f) / tanf(fovRad * 0.5f); // Extra margin with 0.6f factor - - // Position camera to ensure proper spacing around puffer cube (goal) - int goalFloor = env->level->goal_location / env->level->size; - float goalHeight = (float)goalFloor; - - // Calculate visible height at the current distance - float visibleHeight = 2.0f * minDistance * tanf(fovRad * 0.5f); - - float bottomConstraint = -1.0f + visibleHeight * 0.5f; // 1-tile space below bot - float topConstraint = goalHeight + 2.0f - visibleHeight * 0.5f; // 2-tile space above puffer - - // Use the higher constraint to ensure both conditions are met - float cameraHeight = fmaxf(bottomConstraint, topConstraint); - - // Clamp to reasonable bounds and apply the -1 tile adjustment requested - cameraHeight = fmaxf(cameraHeight - 1.0f, levelHeight * 0.2f); - - Vector3 levelCenter = {(levelWidth - 1) * 0.5f, cameraHeight, (levelDepth - 1) * 0.5f}; - - // camera - auto-positioned to fit entire level - client->camera = (Camera3D){ 0 }; - client->camera.position = (Vector3){ levelCenter.x, levelCenter.y, levelCenter.z + minDistance }; - client->camera.target = levelCenter; - client->camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - client->camera.fovy = 45.0f; - client->camera.projection = CAMERA_PERSPECTIVE; - // load background - client->background = LoadTexture("resources/tower_climb/space2.jpg"); - // load robot & cube models - client->robot = LoadModel("resources/tower_climb/small_astro.glb"); - client->cube = LoadModel("resources/tower_climb/spacerock.glb"); - client->puffer = LoadModel("resources/shared/puffer.glb"); - printf("Loaded puffer.glb with %d meshes and %d materials\n", client->puffer.meshCount, client->puffer.materialCount); - if (client->puffer.meshCount == 0) { - printf("WARNING: puffer.glb failed to load, trying puffer.usdz...\n"); - client->puffer = LoadModel("resources/tower_climb/puffer.usdz"); - printf("Loaded puffer.usdz with %d meshes and %d materials\n", client->puffer.meshCount, client->puffer.materialCount); - if (client->puffer.meshCount == 0) { - printf("ERROR: Both puffer files failed to load!\n"); - } - } - BoundingBox bounds = GetModelBoundingBox(client->cube); - float cubeSize = bounds.max.x - bounds.min.x; - float scale = 1.0f / cubeSize; - client->scale = scale; - int animCount = 0; - client->animations = LoadModelAnimations("resources/tower_climb/small_astro.glb", &animCount); - printf("Loaded %d animations\n", animCount); - client->animState = ANIM_IDLE; - client->animFrameCounter = 0; - UpdateModelAnimation(client->robot, client->animations[4], 0); - // Load and configure shader - char vsPath[256]; - char fsPath[256]; - sprintf(vsPath, "resources/tower_climb/shaders/gls%i/lighting.vs", GLSL_VERSION); - sprintf(fsPath, "resources/tower_climb/shaders/gls%i/lighting.fs", GLSL_VERSION); - client->shader = LoadShader(vsPath, fsPath); - // Get shader locations - client->shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(client->shader, "viewPos"); - // Set up ambient light (increased for softer overall lighting) - int ambientLoc = GetShaderLocation(client->shader, "ambient"); - float ambient[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - SetShaderValue(client->shader, ambientLoc, ambient, SHADER_UNIFORM_VEC4); - // apply lighting shader - client->robot.materials[0].shader = client->shader; - client->cube.materials[0].shader = client->shader; - client->puffer.materials[0].shader = client->shader; - // Create softer directional lighting for better depth perception - client->lights[0] = CreateLight(LIGHT_DIRECTIONAL, - (Vector3){ 0.0f, 10.0f, 0.0f }, // High above for top lighting - (Vector3){ 0.5f, -1.0f, 0.3f }, // Direction: down and slightly forward - (Color){ 180, 180, 190, 255 }, // Softer warm white for tops - client->shader); - - client->lights[1] = CreateLight(LIGHT_DIRECTIONAL, - (Vector3){ 0.0f, 5.0f, 0.0f }, // Side lighting - (Vector3){ -0.8f, -0.2f, 0.0f }, // Direction: from right side - (Color){ 100, 100, 120, 255 }, // Softer cool side light - client->shader); - - client->lights[2] = CreateLight(LIGHT_DIRECTIONAL, - (Vector3){ 0.0f, 3.0f, 0.0f }, // Front lighting - (Vector3){ 0.0f, -0.3f, -0.9f }, // Direction: toward camera - (Color){ 70, 70, 85, 255 }, // Softer front fill - client->shader); - - client->lights[3] = CreateLight(LIGHT_POINT, - (Vector3){ 5.0f, 15.0f, 5.0f }, // High ambient light - Vector3Zero(), - (Color){ 50, 50, 55, 255 }, // Slightly softer ambient fill - client->shader); - - // Make sure all models' materials use the lighting shader - for (int i = 0; i < client->robot.materialCount; i++) { - client->robot.materials[i].shader = client->shader; - } - for (int i = 0; i < client->cube.materialCount; i++) { - client->cube.materials[i].shader = client->shader; - } - for (int i = 0; i < client->puffer.materialCount; i++) { - client->puffer.materials[i].shader = client->shader; - } - client->animState = ANIM_IDLE; - client->previousRobotPosition = env->state->robot_position; - // Initialize visual position to match starting robot position - int floor = env->state->robot_position / env->level->size; - int grid_pos = env->state->robot_position % env->level->size; - int x = grid_pos % env->level->cols; - int z = grid_pos / env->level->cols; - client->visualPosition = (Vector3){ - x * 1.0f, - floor * 1.0f, - z * 1.0f - }; - client->targetPosition = client->visualPosition; // Initialize target to match - - // Initialize camera rotation controls - client->lastMousePos = (Vector2){0, 0}; - client->isDragging = false; - client->cameraDistance = minDistance; // Use calculated distance - client->cameraAngleX = 0.0f; // Horizontal rotation - client->cameraAngleY = 20.0f; // Vertical rotation (looking down) - client->followPlayer = false; // Player following disabled by default - client->bannerStartTime = 0.0f; - client->bannerType = 0; - client->showBanner = false; - - // Initialize lighting smoothing - client->lightingSmoothing = 0.1f; // Smoothing factor (0.1 = slow, 0.9 = fast) - client->previousLightIntensity = 1.0f; - - return client; -} - -void orient_hang_offset(Client* client, CTowerClimb* env, int reverse){ - client->visualPosition.y -= 0.2f * reverse; - if (env->state->robot_orientation == 0) { // Facing +x - client->visualPosition.x += 0.4f * reverse; - } else if (env->state->robot_orientation == 1) { // Facing +z - client->visualPosition.z += 0.4f * reverse; - } else if (env->state->robot_orientation == 2) { // Facing -x - client->visualPosition.x -= 0.4f * reverse; - } else if (env->state->robot_orientation == 3) { // Facing -z - client->visualPosition.z -= 0.4f * reverse; - } -} - -// Animation configuration -typedef struct { - int animationIndex; - int frameRate; - int maxFrames; - int startFrame; // Added startFrame configuration - AnimationState nextState; -} AnimConfig; - -static const AnimConfig ANIM_CONFIGS[] = { - [ANIM_IDLE] = {4, 1, -1, 0, ANIM_IDLE}, // Loops from start - [ANIM_CLIMBING] = {1, 6, -1, 0, ANIM_IDLE}, // Start from beginning - [ANIM_HANGING] = {2, 0, 1, 0, ANIM_HANGING}, // Static frame - [ANIM_START_GRABBING] = {3, 6, -2, 0, ANIM_GRABBING}, // Normal grab start - [ANIM_GRABBING] = {3, 4, -2, -2, ANIM_GRABBING}, // Start at second-to-last frame - [ANIM_RUNNING] = {5, 4, -1, 0, ANIM_IDLE}, // Start from beginning - [ANIM_SHIMMY_RIGHT] = {7, 2, 87, 0, ANIM_HANGING}, // Start from beginning - [ANIM_SHIMMY_LEFT] = {6, 2, 87, 0, ANIM_HANGING} // Start from beginning -}; - -static void update_animation(Client* client, AnimationState newState) { - if (!client->enable_animations) return; - const AnimConfig* config = &ANIM_CONFIGS[newState]; - client->animState = newState; - // Handle negative startFrame (counting from end) - int startFrame = config->startFrame; - if (startFrame < 0) { - startFrame = client->animations[config->animationIndex].frameCount + startFrame; - } - client->animFrameCounter = startFrame; - UpdateModelAnimation(client->robot, client->animations[config->animationIndex], startFrame); - if (newState == ANIM_IDLE || newState == ANIM_GRABBING || newState == ANIM_HANGING || newState == ANIM_START_GRABBING) { - client->visualPosition = client->targetPosition; - } -} - -static void update_position(Client* client, CTowerClimb* env) { - int floor = env->state->robot_position / env->level->size; - int grid_pos = env->state->robot_position % env->level->size; - int x = grid_pos % env->level->cols; - int z = grid_pos / env->level->cols; - client->targetPosition = (Vector3){x * 1.0f, floor * 1.0f, z * 1.0f}; -} - -static void process_animation_frame(Client* client, CTowerClimb* env) { - if (!client->enable_animations) return; - const AnimConfig* config = &ANIM_CONFIGS[client->animState]; - if (!client->isMoving && client->animState != ANIM_IDLE) return; - - client->animFrameCounter += config->frameRate; - UpdateModelAnimation(client->robot, client->animations[config->animationIndex], - client->animFrameCounter); - // Handle shimmy movement lerping - if (client->isMoving && (client->animState == ANIM_SHIMMY_LEFT || - client->animState == ANIM_SHIMMY_RIGHT)) { - float progress = 0.065f; - // Horizontal movement for UP/DOWN, vertical movement for LEFT/RIGHT - bool facingNS = env->state->robot_orientation == UP || env->state->robot_orientation == DOWN; - if (facingNS) { - client->visualPosition.x = Lerp(client->visualPosition.x, client->targetPosition.x, progress); - } else { - client->visualPosition.z = Lerp(client->visualPosition.z, client->targetPosition.z, progress); - } - } - // Check for animation completion - int maxFrames = config->maxFrames; - if (maxFrames < 0) { - maxFrames = client->animations[config->animationIndex].frameCount + maxFrames; - } - // If we've reached the end of the animation, update the animation state - if (maxFrames > 0 && client->animFrameCounter >= maxFrames) { - client->isMoving = false; - update_animation(client, config->nextState); - client->visualPosition = client->targetPosition; - if (config->nextState == ANIM_HANGING) { - orient_hang_offset(client, env, 1); - } - } -} - -static void handle_hanging_movement(Client* client, CTowerClimb* env) { - bool is_wrap_shimmy = fabs(client->targetPosition.x - client->visualPosition.x) > 0.5f && - fabs(client->targetPosition.z - client->visualPosition.z) > 0.5f; - // First ensure we have the correct hanging offset if we just transitioned to hanging - if ((int)client->visualPosition.x == client->visualPosition.x && (int)client->visualPosition.z == client->visualPosition.z) { - orient_hang_offset(client, env, 1); - } - if (is_wrap_shimmy) { - client->isMoving = false; - update_animation(client, ANIM_HANGING); - client->visualPosition = client->targetPosition; - orient_hang_offset(client, env, 1); - return; - } - // Determine movement direction based on orientation - bool moving_right = false; - switch (env->state->robot_orientation) { - case UP: moving_right = client->targetPosition.x > client->visualPosition.x; break; - case DOWN: moving_right = client->targetPosition.x < client->visualPosition.x; break; - case RIGHT: moving_right = client->targetPosition.z < client->visualPosition.z; break; - case LEFT: moving_right = client->targetPosition.z > client->visualPosition.z; break; - } - if (client->targetPosition.y < client->visualPosition.y) { - update_animation(client, ANIM_HANGING); - orient_hang_offset(client, env, 1); - client->isMoving = false; - } else { - update_animation(client, moving_right ? ANIM_SHIMMY_RIGHT : ANIM_SHIMMY_LEFT); - } -} - -static void update_camera(Client* client, CTowerClimb* env) { - Vector3 targetCenter; - - if (client->followPlayer) { - int floor = env->state->robot_position / env->level->size; - int goalFloor = env->level->goal_location / env->level->size; - - // Calculate target position (center of play area, following player height) - Vector3 desiredTarget = { - (env->level->cols - 1) * 0.5f, - floor * 1.0f, // Follow player floor - (env->level->rows - 1) * 0.5f - }; - - // Stop following when goal is near top of screen - // Calculate how much higher the camera target can go before goal disappears - float fovRad = client->camera.fovy * DEG2RAD; - float visibleHeight = 2.0f * client->cameraDistance * tanf(fovRad * 0.5f); - float maxTargetY = goalFloor - (visibleHeight * 0.2f); // Stop when goal is 20% from top - - // Limit the desired target to not exceed the max height - if (desiredTarget.y > maxTargetY) { - desiredTarget.y = maxTargetY; - } - - // Smooth following with interpolation - float followSpeed = 0.02f; // Very smooth following - targetCenter.x = client->camera.target.x + (desiredTarget.x - client->camera.target.x) * followSpeed; - targetCenter.y = client->camera.target.y + (desiredTarget.y - client->camera.target.y) * followSpeed; - targetCenter.z = client->camera.target.z + (desiredTarget.z - client->camera.target.z) * followSpeed; - } else { - // Keep current target when not following - targetCenter = client->camera.target; - } - - // Convert spherical coordinates to cartesian for free rotation - float radX = client->cameraAngleX * DEG2RAD; - float radY = client->cameraAngleY * DEG2RAD; - - Vector3 cameraOffset = { - cosf(radY) * sinf(radX) * client->cameraDistance, - sinf(radY) * client->cameraDistance, - cosf(radY) * cosf(radX) * client->cameraDistance - }; - - // Update camera position and target - client->camera.position = Vector3Add(targetCenter, cameraOffset); - client->camera.target = targetCenter; -} - -static void draw_background(Client* client) { - float scaleWidth = (float)client->width / client->background.width; - float scaleHeight = (float)client->height / client->background.height; - float scale = fmax(scaleWidth, scaleHeight) * 1.0f; - - Rectangle dest = { - .x = (client->width - client->background.width * scale) * 0.5f, - .y = (client->height - client->background.height * scale) * 0.5f, - .width = client->background.width * scale, - .height = client->background.height * scale - }; - Rectangle source = {0, 0, client->background.width, client->background.height}; - DrawTexturePro(client->background, source, dest, (Vector2){0, 0}, 0.0f, WHITE); -} - -static void draw_level(Client* client, CTowerClimb* env) { - int cols = env->level->cols; - int sz = env->level->size; - float currentTime = GetTime(); - - for(int i = 0; i < env->level->total_length; i++) { - int floor = i / sz; - int grid_pos = i % sz; - int x = grid_pos % cols; - int z = grid_pos / cols; - Vector3 pos = {x * 1.0f, floor * 1.0f, z * 1.0f}; - - // Check if this position should glow (recently visited) - float glowAlpha = 0.0f; - for (int j = 0; j < env->visitedCount; j++) { - if (env->visitedPositions[j] == i) { - float timeSinceVisit = currentTime - env->visitedTimes[j]; - if (timeSinceVisit < 3.0f) { // Glow for 3 seconds - glowAlpha = fmaxf(glowAlpha, (1.0f - timeSinceVisit / 3.0f) * 0.9f); // Much more prominent glow - } - } - } - - if(TEST_BIT(env->state->blocks, i)) { - // Create position-based variation for cube distinctiveness - int posHash = (x * 73 + z * 37 + floor * 13) % 256; - float variation = (float)posHash / 255.0f; - - // Base cube color with subtle position-based tinting - Color cubeColor = (Color){ - (unsigned char)(240 + variation * 15), // Slight brightness variation - (unsigned char)(240 + sinf(variation * 6.28f) * 10), // Subtle color shift - (unsigned char)(240 + cosf(variation * 6.28f) * 10), // Subtle color shift - 255 - }; - - // Always draw the cube model with its original texture first - DrawModel(client->cube, pos, client->scale, cubeColor); - - // Add red glow overlay if recently visited - if (glowAlpha > 0.0f) { - float glowIntensity = glowAlpha * 1.2f; // Moderate intensity boost - - // Add red glow as overlay effects (preserving underlying cube texture) - EndShaderMode(); - - // Inner glow - more opaque, tighter to cube - DrawCube(pos, 1.0f, 1.0f, 1.0f, (Color){255, 0, 0, (unsigned char)(glowIntensity * 80)}); - - // Middle glow - medium opacity, slightly larger - DrawCube(pos, 1.03f, 1.03f, 1.03f, (Color){255, 40, 40, (unsigned char)(glowIntensity * 60)}); - - // Outer glow - subtle, largest - DrawCube(pos, 1.06f, 1.06f, 1.06f, (Color){255, 80, 80, (unsigned char)(glowIntensity * 40)}); - - BeginShaderMode(client->shader); - } - - // Varied wireframe colors based on position and state - Color wireColor; - if (i == env->state->block_grabbed) { - wireColor = (Color){255, 0, 0, 255}; // Bright red for grabbed block - } else { - // Position-based wireframe variation for distinctiveness - float hue = fmodf(variation * 180.0f + floor * 30.0f, 360.0f); - unsigned char r, g, b; - - // Simple HSV to RGB conversion for varied wireframe colors with clamping - if (hue < 60) { - r = 180; - float gVal = 120 + hue; - g = (unsigned char)(gVal > 255 ? 255 : gVal); - b = 120; - } else if (hue < 120) { - float rVal = 240 - hue; - r = (unsigned char)(rVal < 0 ? 0 : rVal); - g = 180; - b = 120; - } else if (hue < 180) { - r = 120; - g = 180; - float bVal = 120 + (hue - 120); - b = (unsigned char)(bVal > 255 ? 255 : bVal); - } else if (hue < 240) { - r = 120; - float gVal = 240 - (hue - 120); - g = (unsigned char)(gVal < 0 ? 0 : gVal); - b = 180; - } else if (hue < 300) { - float rVal = 120 + (hue - 240); - r = (unsigned char)(rVal > 255 ? 255 : rVal); - g = 120; - b = 180; - } else { - r = 180; - g = 120; - float bVal = 240 - (hue - 240); - b = (unsigned char)(bVal < 0 ? 0 : bVal); - } - - wireColor = (Color){r, g, b, 255}; - } - - // Draw main wireframe - DrawCubeWires(pos, 1.0f, 1.0f, 1.0f, wireColor); - - // Add secondary wireframe for depth/texture variation - if (floor % 2 == 0) { - // Even floors get thicker edge lines - Color edgeColor = (Color){ - (unsigned char)(wireColor.r * 0.7f), - (unsigned char)(wireColor.g * 0.7f), - (unsigned char)(wireColor.b * 0.7f), - 180 - }; - DrawCubeWires(pos, 1.02f, 1.02f, 1.02f, edgeColor); - } else { - // Odd floors get inner detail lines - float detailR = wireColor.r * 1.3f; - float detailG = wireColor.g * 1.3f; - float detailB = wireColor.b * 1.3f; - Color detailColor = (Color){ - (unsigned char)(detailR > 255 ? 255 : detailR), - (unsigned char)(detailG > 255 ? 255 : detailG), - (unsigned char)(detailB > 255 ? 255 : detailB), - 160 - }; - DrawCubeWires(pos, 0.98f, 0.98f, 0.98f, detailColor); - } - } - if (i == env->level->goal_location) { - EndShaderMode(); - - // Puffer cube outline - DrawCubeWires(pos, 1.0f, 1.0f, 1.0f, PUFF_CYAN); - - BeginShaderMode(client->shader); - - if (client->puffer.meshCount > 0) { - // Calculate animations - float time = GetTime(); - float spinAngle = fmodf(time * 90.0f, 360.0f); // Spinning - float bobOffset = sinf(time * 2.0f) * 0.09f; // Gentle bobbing - - // Celebratory backflip when player climbs on cube - float celebratoryFlip = 0.0f; - float pufferYOffset = -0.3f; // Default position in cube - if (env->celebrationStarted && env->goal_reached) { - float celebrationDuration = time - env->celebrationStartTime; - // Start flip at 0.6s (after climbing completes), complete at 1.0s - if (celebrationDuration >= 0.8f && celebrationDuration < 1.2f) { - // Move puffer up in cube during celebration - pufferYOffset = -0.1f; // Higher position in cube - // Backflip over 0.4 seconds (from 0.6s to 1.0s) - float flipProgress = (celebrationDuration - 0.6f) / 0.55f; // 0 to 1 over 0.4s - celebratoryFlip = flipProgress * 360.0f; // One complete backflip - } - } - - Vector3 pufferPos = {pos.x, pos.y + pufferYOffset + bobOffset, pos.z}; // Inside cube with bob - - // Draw the animated puffer - rlPushMatrix(); - rlTranslatef(pufferPos.x, pufferPos.y, pufferPos.z); - rlRotatef(-90.0f, 0.0f, 0.0f, 1.0f); // Z-axis rotation (upright) - rlRotatef(spinAngle, 1.0f, 0.0f, 0.0f); // Normal spinning - rlRotatef(celebratoryFlip, 0.0f, -1.0f, 0.0f); // Celebratory backflip around Z-axis (in place) - rlScalef(120.0f, 120.0f, 90.0f); // Scale - less compressed front-to-back - - DrawModel(client->puffer, (Vector3){0, 0, 0}, 1.0f, WHITE); - - rlPopMatrix(); - } - } - } -} - -static void draw_robot(Client* client, CTowerClimb* env) { - Vector3 pos = client->visualPosition; - pos.y -= 0.5f; - - if (env->goal_reached && env->celebrationStarted) { - // Beam of light effect when goal is reached and climbing animation completes - float time = GetTime(); - float celebrationDuration = time - env->celebrationStartTime; - - // Start beam effect at height of puffer's flip (0.8s) and complete by 1.2s - if (celebrationDuration > 0.8f && celebrationDuration < 1.2f) { - // Beam the player upwards with light effect - float beamProgress = (celebrationDuration - 0.8f) / 0.4f; // 0 to 1 over 0.4s - float beamHeight = beamProgress * 6.0f; // Beam upwards - pos.y += beamHeight; - - // Draw beam of light effect - EndShaderMode(); // Temporarily exit shader mode for bright effects - - // Main beam cylinder - DrawCylinder(pos, 0.15f, 0.15f, beamHeight + 3.0f, 12, (Color){255, 255, 255, 120}); - DrawCylinder(pos, 0.08f, 0.08f, beamHeight + 4.0f, 8, (Color){255, 255, 255, 180}); - - // Light ray effects - for (int i = 0; i < 20; i++) { - float angle = (float)i / 20.0f * 2.0f * PI + time * 3.0f; - float radius = 0.1f + sinf(time * 4.0f + i) * 0.05f; - float height = (float)i / 20.0f * (beamHeight + 2.0f); - - Vector3 rayStart = {pos.x + cosf(angle) * radius, pos.y + height, pos.z + sinf(angle) * radius}; - Vector3 rayEnd = {pos.x, pos.y + height + 0.2f, pos.z}; - - // Draw light rays as thick lines using cylinders - Vector3 direction = Vector3Subtract(rayEnd, rayStart); - float lineLength = Vector3Length(direction); - Vector3 center = Vector3Add(rayStart, Vector3Scale(direction, 0.5f)); - - // Use c - DrawCylinder(center, 0.01f, 0.01f, lineLength, 4, (Color){255, 255, 200, 150}); - } - - // Ascending sparkles - for (int i = 0; i < 15; i++) { - float sparkleTime = fmodf(time * 2.0f + (float)i * 0.3f, 2.0f); - float sparkleHeight = sparkleTime * (beamHeight + 1.0f); - float sparkleRadius = sinf(sparkleTime * PI) * 0.2f; - float angle = (float)i * 0.4f + time; - - Vector3 sparklePos = { - pos.x + cosf(angle) * sparkleRadius, - pos.y + sparkleHeight, - pos.z + sinf(angle) * sparkleRadius - }; - - // Small bright points instead of large spheres - DrawSphere(sparklePos, 0.02f, (Color){255, 255, 150, 255}); - } - - BeginShaderMode(client->shader); // Re-enter shader mode - } - } - - // Draw robot (with transparency if being beamed up, invisible after beam completes) - Color robotColor = WHITE; - bool shouldDrawRobot = true; - - if (env->goal_reached && env->celebrationStarted) { - float time = GetTime(); - float celebrationDuration = time - env->celebrationStartTime; - - if (celebrationDuration > 0.8f && celebrationDuration < 1.2f) { - // Make robot increasingly transparent as it gets beamed up - float beamProgress = (celebrationDuration - 0.8f) / 0.4f; // 0 to 1 over 0.4s - int alpha = (int)(255 * (1.0f - beamProgress)); - robotColor = (Color){255, 255, 255, alpha}; - } else if (celebrationDuration >= 1.2f) { - // After beam completes, don't draw robot at all - shouldDrawRobot = false; - } - } - - if (shouldDrawRobot) { - rlPushMatrix(); - rlTranslatef(pos.x, pos.y, pos.z); - rlRotatef(90.0f, 1, 0, 0); - rlRotatef(-90.0f + env->state->robot_orientation * 90.0f, 0, 0, 1); - DrawModel(client->robot, (Vector3){0, 0, 0}, 0.5f, robotColor); - rlPopMatrix(); - } -} - - - -static void draw_ui(Client* client, CTowerClimb* env) { - // Draw timer (time remaining) - float timeRemaining = 60.0f - env->buffer.episode_length; - if (timeRemaining < 0) timeRemaining = 0; - - // Timer background - int timerX = client->width - 120; - int timerY = 20; - int rectHeight = 40; - int fontSize = 20; - DrawRectangle(timerX - 10, timerY - 5, 100, rectHeight, (Color){0, 0, 0, 150}); - DrawRectangleLines(timerX - 10, timerY - 5, 100, rectHeight, WHITE); - - // Timer text with color based on urgency - Color timerColor = WHITE; - if (timeRemaining <= 10) timerColor = RED; - else if (timeRemaining <= 20) timerColor = YELLOW; - - // Center the text vertically in the rectangle - int textY = timerY - 5 + (rectHeight - fontSize) / 2; - DrawText(TextFormat("Time: %.0f", timeRemaining), timerX, textY, fontSize, timerColor); - - // Draw banner if active - if (client->showBanner) { - float currentTime = GetTime(); - float bannerDuration = currentTime - client->bannerStartTime; - - if (bannerDuration < 0.7f) { // Show for 0.7 seconds - // Banner background - int bannerHeight = 80; - int bannerY = (client->height - bannerHeight) / 2; - DrawRectangle(0, bannerY, client->width, bannerHeight, (Color){0, 0, 0, 200}); - DrawRectangleLines(0, bannerY, client->width, bannerHeight, WHITE); - - // Banner text - const char* text = ""; - Color textColor = WHITE; - if (client->bannerType == 1) { - text = "LEVEL COMPLETED!"; - textColor = GREEN; - } else if (client->bannerType == 2) { - text = "LEVEL FAILED!"; - textColor = RED; - } - - int fontSize = 40; - int textWidth = MeasureText(text, fontSize); - int textX = (client->width - textWidth) / 2; - int textY = bannerY + (bannerHeight - fontSize) / 2; - - // Add pulsing effect - float pulse = sinf(currentTime * 8.0f) * 0.3f + 0.7f; - Color pulsedColor = { - (unsigned char)(textColor.r * pulse), - (unsigned char)(textColor.g * pulse), - (unsigned char)(textColor.b * pulse), - textColor.a - }; - - DrawText(text, textX, textY, fontSize, pulsedColor); - } else { - // Hide banner after duration - client->showBanner = false; - } - } -} - -static void render_scene(Client* client, CTowerClimb* env) { - BeginDrawing(); - ClearBackground(BLACK); - EndShaderMode(); - draw_background(client); - BeginShaderMode(client->shader); - BeginMode3D(client->camera); - // Update shader camera position - float cameraPos[3] = { - client->camera.position.x, - client->camera.position.y, - client->camera.position.z - }; - SetShaderValue(client->shader, client->shader.locs[SHADER_LOC_VECTOR_VIEW], - cameraPos, SHADER_UNIFORM_VEC3); - - // Calculate dynamic lighting intensity based on player position and smooth it - int playerFloor = env->state->robot_position / env->level->size; - float targetIntensity = 0.8f + (playerFloor * 0.05f); // Slightly brighter at higher floors - targetIntensity = fminf(targetIntensity, 1.2f); // Cap the intensity - - // Smooth the lighting transition - client->previousLightIntensity = client->previousLightIntensity + - (targetIntensity - client->previousLightIntensity) * client->lightingSmoothing; - - // Apply the smoothed lighting intensity to the main directional light - Color adjustedMainLight = { - (unsigned char)(180 * client->previousLightIntensity), - (unsigned char)(180 * client->previousLightIntensity), - (unsigned char)(190 * client->previousLightIntensity), - 255 - }; - - // Update the main light with the smoothed intensity - client->lights[0].color = adjustedMainLight; - BeginBlendMode(BLEND_ALPHA); - draw_level(client, env); - EndBlendMode(); - draw_robot(client, env); - EndMode3D(); - EndShaderMode(); - draw_ui(client, env); - EndDrawing(); -} - -void c_render(CTowerClimb* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - Client* client = env->client; - - // Check if we should trigger success banner when beam effect starts - if (env->goal_reached && env->celebrationStarted && !env->bannerTriggered) { - float currentTime = GetTime(); - float celebrationDuration = currentTime - env->celebrationStartTime; - - // Trigger banner when beam effect starts (0.8s celebration time) - if (celebrationDuration >= 0.8f) { - trigger_banner(client, 1); // Success! - env->bannerTriggered = true; // Mark as triggered to prevent multiple calls - } - } - - if (IsKeyDown(KEY_ESCAPE)) exit(0); - - // Toggle player following with spacebar - if (IsKeyPressed(KEY_SPACE)) { - client->followPlayer = !client->followPlayer; - } - - // Camera controls - float cameraSpeed = 0.5f; - float zoomSpeed = 2.0f; - - // Pan controls (WASD only - arrow keys reserved for player) - if (IsKeyDown(KEY_A)) { - client->camera.position.x -= cameraSpeed; - client->camera.target.x -= cameraSpeed; - } - if (IsKeyDown(KEY_D)) { - client->camera.position.x += cameraSpeed; - client->camera.target.x += cameraSpeed; - } - if (IsKeyDown(KEY_W)) { - client->camera.position.y += cameraSpeed; - client->camera.target.y += cameraSpeed; - } - if (IsKeyDown(KEY_S)) { - client->camera.position.y -= cameraSpeed; - client->camera.target.y -= cameraSpeed; - } - - // Zoom controls (Q/E or +/-) - if (IsKeyDown(KEY_Q) || IsKeyDown(KEY_KP_SUBTRACT)) { - client->camera.position.z += zoomSpeed; // Zoom out - } - if (IsKeyDown(KEY_E) || IsKeyDown(KEY_KP_ADD)) { - client->camera.position.z -= zoomSpeed; // Zoom in - } - - // FOV zoom controls (Z/X) - if (IsKeyDown(KEY_Z)) { - client->camera.fovy = fmaxf(client->camera.fovy - 1.0f, 10.0f); // Zoom in (min 10°) - } - if (IsKeyDown(KEY_X)) { - client->camera.fovy = fminf(client->camera.fovy + 1.0f, 90.0f); // Zoom out (max 90°) - } - - // Mouse scroll wheel for distance zoom - float wheelMove = GetMouseWheelMove(); - if (wheelMove != 0) { - client->cameraDistance = fmaxf(client->cameraDistance - wheelMove * 2.0f, 3.0f); - } - - // Click and drag camera rotation - Vector2 mousePos = GetMousePosition(); - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - client->isDragging = true; - client->lastMousePos = mousePos; - } - - if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - client->isDragging = false; - } - - if (client->isDragging) { - Vector2 mouseDelta = { - mousePos.x - client->lastMousePos.x, - mousePos.y - client->lastMousePos.y - }; - - // Convert mouse movement to rotation - float sensitivity = 0.5f; - client->cameraAngleX += mouseDelta.x * sensitivity; - client->cameraAngleY = fmaxf(fminf(client->cameraAngleY - mouseDelta.y * sensitivity, 89.0f), -89.0f); - - client->lastMousePos = mousePos; - } - // Handle state transitions - drop animation - if (env->state->robot_state == DEFAULT && client->animState == ANIM_HANGING && client->enable_animations) { - update_animation(client, ANIM_IDLE); - client->isMoving = false; - client->visualPosition = client->targetPosition; - } - // grab animation - if (env->state->block_grabbed != -1 && - client->animState != ANIM_GRABBING && - client->animState != ANIM_START_GRABBING && client->enable_animations) { - update_animation(client, ANIM_START_GRABBING); - client->isMoving = true; - } else if (env->state->block_grabbed == -1 && client->animState == ANIM_GRABBING && client->enable_animations) { - update_animation(client, ANIM_IDLE); - } - // Handle position changes - if (env->state->robot_position != client->previousRobotPosition && client->enable_animations) { - if (client->isMoving) client->visualPosition = client->targetPosition; - client->isMoving = true; - update_position(client, env); - float verticalDiff = client->targetPosition.y - client->visualPosition.y; - if (verticalDiff > 0.5) { - orient_hang_offset(client, env, client->animState == ANIM_HANGING ? 0 : 1); - update_animation(client, ANIM_CLIMBING); - } else if (env->state->robot_state == HANGING) { - handle_hanging_movement(client, env); - } else { - update_animation(client, verticalDiff < 0 ? ANIM_IDLE : ANIM_RUNNING); - if (verticalDiff < 0) { - client->isMoving = false; - client->visualPosition = client->targetPosition; - } - } - client->previousRobotPosition = env->state->robot_position; - } - if(!client->enable_animations) { - update_position(client, env); - client->visualPosition = client->targetPosition; - } - process_animation_frame(client, env); - update_camera(client, env); - render_scene(client, env); -} - -void close_client(Client* client) { - // First unload all animations - UnloadModelAnimations(client->animations, 8); // We know we have 8 animations - // Then unload models (which will also unload their materials and meshes) - UnloadModel(client->robot); - UnloadModel(client->puffer); - UnloadModel(client->cube); - // Unload shader - UnloadShader(client->shader); - // Unload texture - UnloadTexture(client->background); - CloseWindow(); - free(client); -} diff --git a/pufferlib/ocean/tower_climb/tower_climb.py b/pufferlib/ocean/tower_climb/tower_climb.py deleted file mode 100644 index 80b19dde8..000000000 --- a/pufferlib/ocean/tower_climb/tower_climb.py +++ /dev/null @@ -1,73 +0,0 @@ -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.tower_climb import binding - - -class TowerClimb(pufferlib.PufferEnv): - def __init__(self, num_envs=4096, render_mode=None, report_interval=1, - num_maps=50, reward_climb_row = .25, reward_fall_row = 0, reward_illegal_move = -0.01, - reward_move_block = 0.2, buf = None, seed=0): - - # env - self.num_agents = num_envs - self.render_mode = render_mode - self.report_interval = report_interval - - self.num_obs = 228 - self.single_observation_space = gymnasium.spaces.Box(low=0, high=255, - shape=(self.num_obs,), dtype=np.uint8) - self.single_action_space = gymnasium.spaces.Discrete(6) - - super().__init__(buf=buf) - c_envs = [] - self.c_state = binding.shared(num_maps=num_maps) - self.c_envs = binding.vec_init(self.observations, self.actions, - self.rewards, self.terminals, self.truncations, num_envs, seed, - num_maps=num_maps, reward_climb_row=reward_climb_row, - reward_fall_row=reward_fall_row, reward_illegal_move=reward_illegal_move, - reward_move_block=reward_move_block, state=self.c_state) - - def reset(self, seed=None): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - self.tick += 1 - info = [] - if self.tick % self.report_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - #binding.vec_close(self.c_envs) - pass - -def test_performance(timeout=10, atn_cache=1024): - num_envs=1000; - env = TowerClimb(num_envs=num_envs) - env.reset() - tick = 0 - - actions = np.random.randint(0, env.single_action_space.n, (atn_cache, num_envs)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - sps = num_envs * tick / (time.time() - start) - print(f'SPS: {sps:,}') diff --git a/pufferlib/ocean/trash_pickup/README.md b/pufferlib/ocean/trash_pickup/README.md deleted file mode 100644 index 670c14299..000000000 --- a/pufferlib/ocean/trash_pickup/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# TrashPickup Environment - -A lightweight multi-agent reinforcement learning (RL) environment designed for coordination and cooperation research. Agents pick up trash and deposit it in bins for rewards. - -## Key Features -- **Multi-Agent Coordination:** Encourages teamwork, efficient planning, and resource allocation. -- **Configurable Setup:** Adjustable grid size, number of agents, trash, bins, and episode length. -- **Discrete Action Space:** Actions include `UP`, `DOWN`, `LEFT`, `RIGHT`. -- **Fast and Lightweight:** Optimized for rapid training and testing. - -## Example Research Goals -- Investigate emergent behaviors like task allocation and coordination. -- Study efficient resource collection and bin-pushing strategies. - -## Ideal For -- RL researchers exploring multi-agent cooperation. -- Students learning about multi-agent systems. -- Developers testing scalable RL algorithms. diff --git a/pufferlib/ocean/trash_pickup/binding.c b/pufferlib/ocean/trash_pickup/binding.c deleted file mode 100644 index 4e23da1b3..000000000 --- a/pufferlib/ocean/trash_pickup/binding.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "trash_pickup.h" - -#define Env CTrashPickupEnv -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->num_agents = unpack(kwargs, "num_agents"); - env->grid_size = unpack(kwargs, "grid_size"); - env->num_trash = unpack(kwargs, "num_trash"); - env->num_bins = unpack(kwargs, "num_bins"); - env->max_steps = unpack(kwargs, "max_steps"); - env->agent_sight_range = unpack(kwargs, "agent_sight_range"); - initialize_env(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "trash_collected", log->trash_collected); - return 0; -} diff --git a/pufferlib/ocean/trash_pickup/cy_trash_pickup.pyx b/pufferlib/ocean/trash_pickup/cy_trash_pickup.pyx deleted file mode 100644 index 114a67aa2..000000000 --- a/pufferlib/ocean/trash_pickup/cy_trash_pickup.pyx +++ /dev/null @@ -1,110 +0,0 @@ -cimport numpy as cnp -from libc.stdlib cimport calloc, free # Use calloc for zero-initialized allocation -from libc.stdint cimport uint64_t - -cdef extern from "trash_pickup.h": - int LOG_BUFFER_SIZE - - ctypedef struct Log: - float perf; - float score; - float episode_return; - float episode_length; - float trash_collected; - - ctypedef struct LogBuffer - LogBuffer* allocate_logbuffer(int) - void free_logbuffer(LogBuffer*) - Log aggregate_and_clear(LogBuffer*) - - ctypedef struct CTrashPickupEnv: - char* observations - int* actions - float* rewards - unsigned char* dones - LogBuffer* log_buffer - - int grid_size - int num_agents - int num_trash - int num_bins - int max_steps - int agent_sight_range - - - ctypedef struct Client - - void initialize_env(CTrashPickupEnv* env) - void free_allocated(CTrashPickupEnv* env) - - Client* make_client(CTrashPickupEnv* env) - void close_client(Client* client) - void c_render(Client* client, CTrashPickupEnv* env) - void c_reset(CTrashPickupEnv* env) - void c_step(CTrashPickupEnv* env) - -cdef class CyTrashPickup: - cdef: - CTrashPickupEnv* envs - Client* client - LogBuffer* logs - int num_envs - - def __init__(self, char[:, :] observations, int[:] actions, - float[:] rewards, unsigned char[:] terminals, int num_envs, - int num_agents=3, int grid_size=10, int num_trash=15, - int num_bins=2, int max_steps=300, int agent_sight_range=5): - self.num_envs = num_envs - self.envs = calloc(num_envs, sizeof(CTrashPickupEnv)) - if self.envs == NULL: - raise MemoryError("Failed to allocate memory for CTrashPickupEnv") - self.client = NULL - - self.logs = allocate_logbuffer(LOG_BUFFER_SIZE) - - cdef int inc = num_agents - - cdef int i - for i in range(num_envs): - self.envs[i] = CTrashPickupEnv( - observations=&observations[inc*i, 0], - actions=&actions[inc*i], - rewards=&rewards[inc*i], - dones=&terminals[inc*i], - log_buffer=self.logs, - grid_size=grid_size, - num_agents=num_agents, - num_trash=num_trash, - num_bins=num_bins, - max_steps=max_steps, - agent_sight_range=agent_sight_range - ) - initialize_env(&self.envs[i]) - - def reset(self): - cdef int i - for i in range(self.num_envs): - c_reset(&self.envs[i]) - - def step(self): - cdef int i - for i in range(self.num_envs): - c_step(&self.envs[i]) - - def render(self): - cdef CTrashPickupEnv* env = &self.envs[0] - if self.client == NULL: - self.client = make_client(env) - - c_render(self.client, env) - - def close(self): - if self.client != NULL: - close_client(self.client) - self.client = NULL - - free(self.envs) - - def log(self): - cdef Log log = aggregate_and_clear(self.logs) - return log diff --git a/pufferlib/ocean/trash_pickup/trash_pickup.c b/pufferlib/ocean/trash_pickup/trash_pickup.c deleted file mode 100644 index 1a313ecd9..000000000 --- a/pufferlib/ocean/trash_pickup/trash_pickup.c +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include "trash_pickup.h" -#include "puffernet.h" - -// Demo function for visualizing the TrashPickupEnv -void demo(int grid_size, int num_agents, int num_trash, int num_bins, int max_steps) { - CTrashPickupEnv env = { - .grid_size = grid_size, - .num_agents = num_agents, - .num_trash = num_trash, - .num_bins = num_bins, - .max_steps = max_steps, - .agent_sight_range = 5, - .do_human_control = true - }; - - bool use_pretrained_model = true; - - Weights* weights; - ConvLSTM* net; - - if (use_pretrained_model){ - weights = load_weights("resources/trash_pickup/trash_pickup_weights.bin", 150245); - int vision = 2*env.agent_sight_range + 1; - net = make_convlstm(weights, env.num_agents, vision, 5, 32, 128, 4); - } - - allocate(&env); - c_reset(&env); - c_render(&env); - - int tick = 0; - while (!WindowShouldClose()) { - if (tick % 2 == 0) { - // Random actions for all agents - for (int i = 0; i < env.num_agents; i++) { - if (use_pretrained_model) - { - for (int e = 0; e < env.total_num_obs; e++) { - net->obs[e] = env.observations[e]; - } - forward_convlstm(net, net->obs, env.actions); - } - else{ - env.actions[i] = rand() % 4; // 0 = UP, 1 = DOWN, 2 = LEFT, 3 = RIGHT - } - // printf("action: %d \n", env.actions[i]); - } - - // Override human control actions - if (IsKeyDown(KEY_LEFT_SHIFT)) { - // Handle keyboard input only for selected agent - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) { - env.actions[0] = ACTION_UP; - } - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) { - env.actions[0] = ACTION_LEFT; - } - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) { - env.actions[0] = ACTION_RIGHT; - } - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) { env.actions[0] = ACTION_DOWN; } - } - - // Step the environment and render the grid - c_step(&env); - - } - tick++; - - c_render(&env); - } - - free_convlstm(net); - free(weights); - free_allocated(&env); - //close_client(client); -} - -// Performance test function for benchmarking -void performance_test() { - long test_time = 10; // Test duration in seconds - - CTrashPickupEnv env = { - .grid_size = 10, - .num_agents = 4, - .num_trash = 20, - .num_bins = 1, - .max_steps = 150, - .agent_sight_range = 5 - }; - allocate(&env); - c_reset(&env); - - long start = time(NULL); - int i = 0; - int inc = env.num_agents; - while (time(NULL) - start < test_time) { - for (int e = 0; e < env.num_agents; e++) { - env.actions[e] = rand() % 4; - } - c_step(&env); - i += inc; - } - long end = time(NULL); - printf("SPS: %ld\n", i / (end - start)); - free_allocated(&env); -} - - -// Main entry point -int main() { - demo(20, 8, 40, 2, 300); // Visual demo - //performance_test(); // Uncomment for benchmarking - return 0; -} diff --git a/pufferlib/ocean/trash_pickup/trash_pickup.h b/pufferlib/ocean/trash_pickup/trash_pickup.h deleted file mode 100644 index ebafd4445..000000000 --- a/pufferlib/ocean/trash_pickup/trash_pickup.h +++ /dev/null @@ -1,572 +0,0 @@ -#include -#include -#include -#include -#include -#include "raylib.h" - -#define EMPTY 0 -#define TRASH 1 -#define TRASH_BIN 2 -#define AGENT 3 - -#define ACTION_UP 0 -#define ACTION_DOWN 1 -#define ACTION_LEFT 2 -#define ACTION_RIGHT 3 - -#define LOG_BUFFER_SIZE 1024 - -typedef struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float trash_collected; - float n; -} Log; - -typedef struct { - int type; // Entity type: EMPTY, TRASH, TRASH_BIN, AGENT - int pos_x; - int pos_y; - bool presence; // Whether or not Entity is present (not applicable to all types) - bool carrying; // Whether agent is carrying trash (only applicable to Agent types) -} Entity; - -typedef struct { - Entity* entity; - int index; // Index in the positions array (-1 if not applicable) -} GridCell; - -typedef struct Client { - int window_width; - int window_height; - int header_offset; - int cell_size; - Texture2D agent_texture; -} Client; - -typedef struct { - // Interface for PufferLib - Client* client; - char* observations; - int* actions; - float* rewards; - unsigned char* terminals; - Log log; - - int grid_size; - int num_agents; - int num_trash; - int num_bins; - int max_steps; - int current_step; - - int total_num_obs; - - int agent_sight_range; - - float positive_reward; - float negative_reward; - float total_episode_reward; - - GridCell* grid; // 1D array for grid - Entity* entities; // Indicies (0 - num_agents) for agents, (num_agents - num_bins) for bins, (num_bins - num_trash) for trash. - - bool do_human_control; -} CTrashPickupEnv; - -void add_log(CTrashPickupEnv* env, Log* log) { - env->log.perf += log->perf; - env->log.score += log->score; - env->log.episode_return += log->episode_return; - env->log.episode_length += log->episode_length; - env->log.trash_collected += log->trash_collected; - env->log.n += 1; -} - -int get_grid_index(CTrashPickupEnv* env, int x, int y) { - return (y * env->grid_size) + x; -} - -// returns the start index of each type of entity for iteration purposes -int get_entity_type_start_index(CTrashPickupEnv* env, int type) -{ - if (type == AGENT) - return 0; - else if (type == TRASH_BIN) - return env->num_agents; - else if (type == TRASH) - return env->num_agents + env->num_bins; - else - return -1; -} - -// Entity Attribute Based Obs-Space -/* -void compute_observations(CTrashPickupEnv* env) { - float* obs = env->observations; - float norm_factor = 1.0f / env->grid_size; - - int obs_index = 0; - - for (int agent_idx = 0; agent_idx < env->num_agents; agent_idx++){ - float current_norm_pos_x = (float) (env->entities[agent_idx].pos_x) * norm_factor; - float current_norm_pos_y = (float) (env->entities[agent_idx].pos_y) * norm_factor; - - // Add the observing agent's own position and carrying status - obs[obs_index++] = current_norm_pos_x; - obs[obs_index++] = current_norm_pos_y; - obs[obs_index++] = env->entities[agent_idx].carrying ? 1.0f : 0.0f; - - // Add other observations from other entities (other agents, bins, trash) - for (int i = 0; i < env->num_agents + env->num_bins + env->num_trash; i++) { - // skip if current this agent - if (agent_idx == i) - continue; - - obs[obs_index++] = ((float) (env->entities[i].pos_x) * norm_factor) - current_norm_pos_x; - obs[obs_index++] = ((float) (env->entities[i].pos_y) * norm_factor) - current_norm_pos_y; - - if (env->entities[i].type == AGENT) { - obs[obs_index++] = env->entities[i].carrying ? 1.0f : 0.0f; - } - else if (env->entities[i].type == TRASH_BIN) { - obs[obs_index++] = env->entities[i].presence ? 1.0f : 0.0f; - } - } - } -} -*/ - -// Local crop version -void compute_observations(CTrashPickupEnv* env) { - int sight_range = env->agent_sight_range; - char* obs = env->observations; - - int obs_dim = 2*env->agent_sight_range + 1; - int channel_offset = obs_dim*obs_dim; - memset(obs, 0, env->total_num_obs*sizeof(char)); - - for (int agent_idx = 0; agent_idx < env->num_agents; agent_idx++) { - // Add obs for whether the agent is carrying or not - //obs[obs_index++] = env->entities[agent_idx].carrying; - - // Get the agent's position - int agent_x = env->entities[agent_idx].pos_x; - int agent_y = env->entities[agent_idx].pos_y; - - // Iterate over the sight range - for (int dy = -sight_range; dy <= sight_range; dy++) { - for (int dx = -sight_range; dx <= sight_range; dx++) { - int cell_x = agent_x + dx; - int cell_y = agent_y + dy; - int obs_x = dx + env->agent_sight_range; - int obs_y = dy + env->agent_sight_range; - - // Check if the cell is within bounds - if (cell_x < 0 || cell_x >= env->grid_size || cell_y < 0 || cell_y >= env->grid_size) { - continue; - } - - Entity* thisEntity = env->grid[get_grid_index(env, cell_x, cell_y)].entity; - if (!thisEntity) { - continue; - } - - int offset = agent_idx*5*channel_offset + obs_y*obs_dim + obs_x; - int obs_idx = offset + thisEntity->type*channel_offset; - obs[obs_idx] = 1; - obs_idx = offset + 4*channel_offset; - obs[obs_idx] = (float)thisEntity->carrying; - } - } - } -} - -// Helper functions -void place_random_entities(CTrashPickupEnv* env, int count, int item_type, int gridIndexStart) { - int placed = 0; - while (placed < count) - { - int x = rand() % env->grid_size; - int y = rand() % env->grid_size; - - GridCell* gridCell = &env->grid[get_grid_index(env, x, y)]; - - if (gridCell->entity != NULL) - continue; - - // Allocate and initialize a new Entity - Entity* newEntity = &env->entities[gridIndexStart]; - newEntity->type = item_type; - newEntity->pos_x = x; - newEntity->pos_y = y; - newEntity->presence = true; - newEntity->carrying = false; - - gridCell->index = gridIndexStart; - gridCell->entity = newEntity; - - gridIndexStart++; - placed++; - } -} - -void add_reward(CTrashPickupEnv* env, int agent_idx, float reward){ - env->rewards[agent_idx] += reward; - env->total_episode_reward += reward; -} - -void move_agent(CTrashPickupEnv* env, int agent_idx, int action) { - Entity* thisAgent = &env->entities[agent_idx]; - - int move_dir_x = 0; - int move_dir_y = 0; - if (action == ACTION_UP) move_dir_y = -1; - else if (action == ACTION_DOWN) move_dir_y = 1; - else if (action == ACTION_LEFT) move_dir_x = -1; - else if (action == ACTION_RIGHT) move_dir_x = 1; - else printf("Undefined action: %d", action); - - int new_x = thisAgent->pos_x + move_dir_x; - int new_y = thisAgent->pos_y + move_dir_y; - - if (new_x < 0 || new_x >= env->grid_size || new_y < 0 || new_y >= env->grid_size) - return; - - GridCell* currentGridCell = &env->grid[get_grid_index(env, thisAgent->pos_x, thisAgent->pos_y)]; - GridCell* newGridCell = &env->grid[get_grid_index(env, new_x, new_y)]; - int cell_state_type = newGridCell->entity ? newGridCell->entity->type : EMPTY; - - if (cell_state_type == EMPTY) - { - thisAgent->pos_x = new_x; - thisAgent->pos_y = new_y; - - newGridCell->entity = currentGridCell->entity; - newGridCell->index = agent_idx; - - currentGridCell->index = -1; - currentGridCell->entity = NULL; - } - else if (cell_state_type == TRASH && thisAgent->carrying == false) - { - Entity* thisTrash = &env->entities[newGridCell->index]; - thisTrash->presence = false; // Mark as not present - thisTrash->pos_x = -1; - thisTrash->pos_y = -1; - - thisAgent->pos_x = new_x; - thisAgent->pos_y = new_y; - thisAgent->carrying = true; - - newGridCell->entity = currentGridCell->entity; - newGridCell->index = currentGridCell->index; - - currentGridCell->entity = NULL; - currentGridCell->index = -1; - - add_reward(env, agent_idx, env->positive_reward); - } - else if (cell_state_type == TRASH_BIN) - { - if (thisAgent->carrying) - { - // Deposit trash into bin - thisAgent->carrying = false; - add_reward(env, agent_idx, env->positive_reward); - } - else - { - int new_bin_x = new_x + move_dir_x; - int new_bin_y = new_y + move_dir_y; - - if (new_bin_x < 0 || new_bin_x >= env->grid_size || new_bin_y < 0 || new_bin_y >= env->grid_size) - return; - - GridCell* newGridCellForBin = &env->grid[get_grid_index(env, new_bin_x, new_bin_y)]; - if (newGridCellForBin->entity == NULL) { - // Move the bin - Entity* thisBin = newGridCell->entity; - thisBin->pos_x = new_bin_x; - thisBin->pos_y = new_bin_y; - - // Move the agent - thisAgent->pos_x = new_x; - thisAgent->pos_y = new_y; - - newGridCellForBin->entity = newGridCell->entity; - newGridCellForBin->index = newGridCell->index; - - newGridCell->entity = currentGridCell->entity; - newGridCell->index = currentGridCell->index; - - currentGridCell->entity = NULL; - currentGridCell->index = -1; - } - // else don't move the agent - } - } -} - -bool is_episode_over(CTrashPickupEnv* env) { - for (int i = 0; i < env->num_agents; i++) - { - if (env->entities[i].carrying) - return false; - } - - int start_index = get_entity_type_start_index(env, TRASH); - for (int i = start_index; i < start_index + env->num_trash; i++) - { - if (env->entities[i].presence) - return false; - } - - return true; -} - -void c_reset(CTrashPickupEnv* env) { - env->current_step = 0; - env->total_episode_reward = 0; - - for (int i = 0; i < env->grid_size * env->grid_size; i++) - { - env->grid[i].entity = NULL; - env->grid[i].index = -1; - } - - // Place trash, bins, and agents randomly across the grid. - place_random_entities(env, env->num_agents, AGENT, 0); - place_random_entities(env, env->num_bins, TRASH_BIN, get_entity_type_start_index(env, TRASH_BIN)); - place_random_entities(env, env->num_trash, TRASH, get_entity_type_start_index(env, TRASH)); - - compute_observations(env); -} - -// Environment functions -void initialize_env(CTrashPickupEnv* env) { - env->current_step = 0; - - env->positive_reward = 0.5f; // / env->num_trash; - env->negative_reward = -0.0f; // / (env->max_steps * env->num_agents); - - env->grid = (GridCell*)calloc(env->grid_size * env->grid_size, sizeof(GridCell)); - env->entities = (Entity*)calloc(env->num_agents + env->num_bins + env->num_trash, sizeof(Entity)); - env->total_num_obs = env->num_agents * ((((env->agent_sight_range * 2 + 1) * (env->agent_sight_range * 2 + 1)) * 5)); -} - -void allocate(CTrashPickupEnv* env) { - initialize_env(env); - env->observations = (char*)calloc(env->total_num_obs, sizeof(char)); - env->actions = (int*)calloc(env->num_agents, sizeof(int)); - env->rewards = (float*)calloc(env->num_agents, sizeof(float)); - env->terminals = (unsigned char*)calloc(env->num_agents, sizeof(unsigned char)); -} - -void c_step(CTrashPickupEnv* env) { - // Reset reward for each agent - memset(env->rewards, 0, sizeof(float) * env->num_agents); - memset(env->terminals, 0, sizeof(unsigned char) * env->num_agents); - - for (int i = 0; i < env->num_agents; i++) { - move_agent(env, i, env->actions[i]); - add_reward(env, i, env->negative_reward); // small negative reward to encourage efficiency - } - - env->current_step++; - if (env->current_step >= env->max_steps || is_episode_over(env)) - { - memset(env->terminals, 1, sizeof(unsigned char) * env->num_agents); - - Log log = {0}; - - log.episode_length = env->current_step; - log.episode_return = env->total_episode_reward; - - int total_trash_not_collected = 0; - for (int i = env->num_agents + 1; i < env->num_agents + env->num_trash; i++) - { - total_trash_not_collected += env->entities[i].presence; - } - - log.trash_collected = (float) (env->num_trash - total_trash_not_collected); - log.score = log.trash_collected; - log.perf = log.score / env->num_trash; - add_log(env, &log); - c_reset(env); - } - - compute_observations(env); -} - -void c_close(CTrashPickupEnv* env) { - free(env->grid); - free(env->entities); -} - -void free_allocated(CTrashPickupEnv* env) { - free(env->observations); - free(env->actions); - free(env->rewards); - free(env->terminals); - c_close(env); -} - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; -const Color PUFF_LINES = (Color){50, 50, 50, 255}; - -// Initialize a rendering client -Client* make_client(CTrashPickupEnv* env) { - const int CELL_SIZE = 40; - Client* client = (Client*)malloc(sizeof(Client)); - client->cell_size = CELL_SIZE; - client->header_offset = 60; - client->window_width = env->grid_size * CELL_SIZE; - client->window_height = client->window_width + client->header_offset; - - InitWindow(client->window_width, client->window_height, "Trash Pickup Environment"); - SetTargetFPS(60); - - client->agent_texture = LoadTexture("resources/shared/puffers_128.png"); - - return client; -} - -// Render the TrashPickup environment -void c_render(CTrashPickupEnv* env) { - if (env->client == NULL) { - env->client = make_client(env); - } - Client* client = env->client; - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - // Draw header with current step and total episode reward - int start_index = get_entity_type_start_index(env, TRASH); - int total_trash_not_collected = 0; - for (int i = start_index; i < start_index + env->num_trash; i++){ - total_trash_not_collected += env->entities[i].presence; - } - - DrawText( - TextFormat( - "Step: %d\nTotal Episode Reward: %.2f\nTrash Collected: %d/%d", - env->current_step, - env->total_episode_reward, - env->num_trash - total_trash_not_collected, - env->num_trash - ), - 5, 2, 10, PUFF_WHITE - ); - - // Draw the grid and its elements - for (int x = 0; x < env->grid_size; x++) { - for (int y = 0; y < env->grid_size; y++) { - GridCell gridCell = env->grid[get_grid_index(env, x, y)]; - - int cell_type; - if (gridCell.entity) - { - cell_type = gridCell.entity->type; - } - else - { - cell_type = EMPTY; - } - - int screen_x = x * client->cell_size; - int screen_y = y * client->cell_size + client->header_offset; - - Rectangle cell_rect = { - .x = screen_x, - .y = screen_y, - .width = client->cell_size, - .height = client->cell_size - }; - - // Draw grid cell border - DrawRectangleLines((int)cell_rect.x, (int)cell_rect.y, (int)cell_rect.width, (int)cell_rect.height, PUFF_LINES); - - // Draw grid cell content - if (cell_type == EMPTY) - continue; - - if (cell_type == TRASH) { - DrawRectangle( - screen_x + client->cell_size / 4, - screen_y + client->cell_size / 4, - client->cell_size / 2, - client->cell_size / 2, - PUFF_CYAN - ); - } else if (cell_type == TRASH_BIN) { - DrawRectangle( - screen_x + client->cell_size / 8, - screen_y + client->cell_size / 8, - 3 * client->cell_size / 4, - 3 * client->cell_size / 4, - PUFF_RED - ); - } else if (cell_type == AGENT) { - Color color; - if (env->do_human_control && gridCell.index == 0) - { - // Make human controlled agent red - color = (Color){255, 128, 128, 255}; - } - else - { - // Non-human controlled agent - color = WHITE; - } - - DrawTexturePro( - client->agent_texture, - (Rectangle) {0, 0, 128, 128}, - (Rectangle) { - screen_x + client->cell_size / 2, - screen_y + client->cell_size / 2, - client->cell_size, - client->cell_size - }, - (Vector2){client->cell_size / 2, client->cell_size / 2}, - 0, - color - ); - - Entity* thisAgent = &env->entities[gridCell.index]; - - if (thisAgent->carrying) - { - DrawRectangle( - screen_x + client->cell_size / 2, - screen_y + client->cell_size / 2, - client->cell_size / 4, - client->cell_size / 4, - PUFF_CYAN - ); - } - } - } - } - - EndDrawing(); -} - -// Cleanup and free the rendering client -void close_client(Client* client) { - UnloadTexture(client->agent_texture); - CloseWindow(); - free(client); -} diff --git a/pufferlib/ocean/trash_pickup/trash_pickup.py b/pufferlib/ocean/trash_pickup/trash_pickup.py deleted file mode 100644 index 57409d0ee..000000000 --- a/pufferlib/ocean/trash_pickup/trash_pickup.py +++ /dev/null @@ -1,125 +0,0 @@ -import numpy as np -from gymnasium import spaces - -import pufferlib -from pufferlib.ocean.trash_pickup import binding - -class TrashPickupEnv(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, report_interval=1, buf=None, - grid_size=10, num_agents=3, num_trash=15, num_bins=2, max_steps=300, agent_sight_range=5, seed=0): - # Env Setup - self.render_mode = render_mode - self.report_interval = report_interval - - # Validate num_agents - if not isinstance(num_agents, int) or num_agents <= 0: - raise ValueError("num_agents must be an integer greater than 0.") - self.num_agents = num_envs * num_agents - self.num_agents_per_env = num_agents - - # Handle num_trash input - if not isinstance(num_trash, int) or num_trash <= 0: - raise ValueError("num_trash must be an int > 0") - self.num_trash = num_trash - - # Handle num_bins input - if not isinstance(num_bins, int) or num_bins <= 0: - raise ValueError("num_bins must be an int > 0") - self.num_bins = num_bins - - if not isinstance(max_steps, int) or max_steps < 10: - raise ValueError("max_steps must be an int >= 10") - self.max_steps = max_steps - - if not isinstance(agent_sight_range, int) or agent_sight_range < 2: - raise ValueError("agent sight range must be an int >= 2") - self.agent_sight_range = agent_sight_range - - # Calculate minimum required grid size - min_grid_size = int((num_agents + self.num_trash + self.num_bins) ** 0.5) + 1 - if not isinstance(grid_size, int) or grid_size < min_grid_size: - raise ValueError( - f"grid_size must be an integer >= {min_grid_size}. " - f"Received grid_size={grid_size}, with num_agents={num_agents}, num_trash={self.num_trash}, and num_bins={self.num_bins}." - ) - self.grid_size = grid_size - - # Entity Attribute Based Obs-Space - # num_obs_trash = num_trash * 3 # [presence, x pos, y pos] for each trash - # num_obs_bin = num_bins * 2 # [x pos, y pos] for each bin - # num_obs_agent = num_agents * 3 # [carrying trash, x pos, y pos] for each agent - # self.num_obs = num_obs_trash + num_obs_bin + num_obs_agent; - - # 2D Local crop obs space - self.num_obs = ((((agent_sight_range * 2 + 1) * (agent_sight_range * 2 + 1)) * 5)); # one-hot encoding for all cell types in local crop around agent (minus the cell the agent is currently in) - - self.single_observation_space = spaces.Box(low=0, high=1, - shape=(self.num_obs,), dtype=np.int8) - self.single_action_space = spaces.Discrete(4) - - super().__init__(buf=buf) - c_envs = [] - for i in range(num_envs): - env_id = binding.env_init( - self.observations[i*num_agents:(i+1)*num_agents], - self.actions[i*num_agents:(i+1)*num_agents], - self.rewards[i*num_agents:(i+1)*num_agents], - self.terminals[i*num_agents:(i+1)*num_agents], - self.truncations[i*num_agents:(i+1)*num_agents], - i + seed * num_envs, - num_agents=num_agents, - grid_size=grid_size, - num_trash=num_trash, - num_bins=num_bins, - max_steps=max_steps, - agent_sight_range=agent_sight_range, - ) - c_envs.append(env_id) - - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=None): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - self.tick += 1 - - info = [] - if self.tick % self.report_interval == 0: - log = binding.vec_log(self.c_envs) - if log: - info.append(log) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = TrashPickupEnv(num_envs=1024, grid_size=10, num_agents=4, - num_trash=20, num_bins=1, max_steps=150, agent_sight_range=5) - - env.reset() - tick = 0 - - actions = np.random.randint(0, 4, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: %f', env.num_agents * tick / (time.time() - start)) - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/tripletriad/binding.c b/pufferlib/ocean/tripletriad/binding.c deleted file mode 100644 index 4c725d313..000000000 --- a/pufferlib/ocean/tripletriad/binding.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "tripletriad.h" - -#define Env CTripleTriad -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->card_width = unpack(kwargs, "card_width"); - env->card_height = unpack(kwargs, "card_height"); - init_ctripletriad(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - assign_to_dict(dict, "n", log->n); - return 0; -} diff --git a/pufferlib/ocean/tripletriad/tripletriad.c b/pufferlib/ocean/tripletriad/tripletriad.c deleted file mode 100644 index 3fa786254..000000000 --- a/pufferlib/ocean/tripletriad/tripletriad.c +++ /dev/null @@ -1,110 +0,0 @@ -#include "tripletriad.h" -#include "puffernet.h" -#include - -#define NOOP -1 - -void interactive() { - Weights* weights = load_weights("resources/tripletriad/tripletriad_weights.bin", 148751); - int logit_sizes[1] = {14}; - LinearLSTM* net = make_linearlstm(weights, 1, 114, logit_sizes, 1); - - CTripleTriad env = { - .width = 990, - .height = 690, - .card_width = 576 / 3, - .card_height = 672 / 3, - .game_over = 0, - .num_cards = 10, - }; - allocate_ctripletriad(&env); - c_reset(&env); - env.client = make_client(env.width, env.height); - - int tick = 0; - int action; - while (!WindowShouldClose()) { - action = NOOP; - - // User can take control of the player - if (IsKeyDown(KEY_LEFT_SHIFT)) { - // Handle Card Selection ( 1-5 for selecting a card) - if (IsKeyPressed(KEY_ONE)) action = SELECT_CARD_1; - if (IsKeyPressed(KEY_TWO)) action = SELECT_CARD_2; - if (IsKeyPressed(KEY_THREE)) action = SELECT_CARD_3; - if (IsKeyPressed(KEY_FOUR)) action = SELECT_CARD_4; - if (IsKeyPressed(KEY_FIVE)) action = SELECT_CARD_5; - - // Handle Card Placement ( 1-9 for placing a card) - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - Vector2 mousePos = GetMousePosition(); - - // Calculate the offset for the board - int boardOffsetX = 196 + 10; // 196 from the DrawRectangle call in render(), plus 10 for padding - int boardOffsetY = 30; // From the DrawRectangle call in render() - - // Adjust mouse position relative to the board - int relativeX = mousePos.x - boardOffsetX; - int relativeY = mousePos.y - boardOffsetY; - - // Calculate cell indices - int cellX = relativeX / env.card_width; - int cellY = relativeY / env.card_height; - - // Calculate the cell index (1-9) based on the click position - int cellIndex = cellY * 3 + cellX+1; - - // Ensure the click is within the game board - if (cellX >= 0 && cellX < 3 && cellY >= 0 && cellY < 3) { - action = cellIndex + 4; - } - } - } else if (tick % 45 == 0) { - forward_linearlstm(net, env.observations, env.actions); - action = env.actions[0]; - } - - tick = (tick + 1) % 45; - - if (action != NOOP) { - env.actions[0] = action; - c_step(&env); - } - - c_render(&env); - } - free_linearlstm(net); - free(weights); - close_client(env.client); - free_allocated_ctripletriad(&env); -} - -void performance_test() { - long test_time = 10; - CTripleTriad env = { - .width = 990, - .height = 690, - .card_width = 576 / 3, - .card_height = 672 / 3, - .game_over = 0, - .num_cards = 10, - }; - allocate_ctripletriad(&env); - c_reset(&env); - - long start = time(NULL); - int i = 0; - while (time(NULL) - start < test_time) { - c_step(&env); - i++; - } - long end = time(NULL); - printf("SPS: %ld\n", i / (end - start)); - free_allocated_ctripletriad(&env); -} - -int main() { - // performance_test(); - interactive(); - return 0; -} diff --git a/pufferlib/ocean/tripletriad/tripletriad.h b/pufferlib/ocean/tripletriad/tripletriad.h deleted file mode 100644 index 1e39cf2e6..000000000 --- a/pufferlib/ocean/tripletriad/tripletriad.h +++ /dev/null @@ -1,653 +0,0 @@ -#include -#include -#include "raylib.h" -#include - -#define SELECT_CARD_1 0 -#define SELECT_CARD_2 1 -#define SELECT_CARD_3 2 -#define SELECT_CARD_4 3 -#define SELECT_CARD_5 4 -#define PLACE_CARD_1 5 -#define PLACE_CARD_2 6 -#define PLACE_CARD_3 7 -#define PLACE_CARD_4 8 -#define PLACE_CARD_5 9 -#define PLACE_CARD_6 10 -#define PLACE_CARD_7 11 -#define PLACE_CARD_8 12 -#define PLACE_CARD_9 13 -#define TICK_RATE 1.0f/60.0f -#define MAX_EPISODE_LENGTH 30 - -const Color PUFF_RED = (Color){187, 0, 0, 255}; -const Color PUFF_CYAN = (Color){0, 187, 187, 255}; -const Color PUFF_WHITE = (Color){241, 241, 241, 241}; -const Color PUFF_BACKGROUND = (Color){6, 24, 24, 255}; - -// how to start game compile - LD_LIBRARY_PATH=raylib-5.0_linux_amd64/lib ./tripletriadgame - -typedef struct Log Log; -struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -}; - -typedef struct Client Client; -typedef struct CTripleTriad CTripleTriad; -struct CTripleTriad { - float* observations; - int* actions; - float* rewards; - unsigned char* terminals; - Log log; - int card_width; - int card_height; - float* board_x; - float* board_y; - int** board_states; - int width; - int height; - int game_over; - int num_cards; - int*** cards_in_hand; - int* card_selected; - int** card_locations; - int* action_masks; - int*** board_card_values; - int* score; - int tick; - float perf; - float episode_return; - float episode_length; - Client* client; -}; - -void add_log(CTripleTriad* env) { - env->log.perf += env->perf; - env->log.score += env->score[0]; - env->log.episode_return += env->episode_return; - env->log.episode_length += env->episode_length; - env->log.n += 1; -} - -void generate_board_positions(CTripleTriad* env) { - for (int row = 0; row < 3; row++) { - for (int col = 0; col < 3; col++) { - int idx = row * 3 + col; - env->board_x[idx] = col* env->card_width; - env->board_y[idx] = row*env->card_height; - } - } -} - -void generate_cards_in_hand(CTripleTriad* env) { - for(int i=0; i< 2; i++) { - for(int j=0; j< 5; j++) { - for(int k=0; k< 4; k++) { - env->cards_in_hand[i][j][k] = (rand() % 7) + 1; - } - } - } -} - -void generate_card_locations(CTripleTriad* env) { - for(int i=0; i< 2; i++) { - for(int j=0; j< 5; j++) { - env->card_locations[i][j] = 0; - } - } -} - -void generate_card_selections(CTripleTriad* env) { - for(int i=0; i< 2; i++) { - env->card_selected[i] = -1; - } -} - -void generate_board_states(CTripleTriad* env) { - for(int i=0; i< 3; i++) { - for(int j=0; j< 3; j++) { - env->board_states[i][j] = 0; - } - } -} - -void generate_board_card_values(CTripleTriad* env) { - for(int i=0; i< 3; i++) { - for(int j=0; j< 3; j++) { - for(int k=0; k< 4; k++) { - env->board_card_values[i][j][k] = 0; - } - } - } -} - -void generate_scores(CTripleTriad* env) { - for(int i=0; i< 2; i++) { - env->score[i] = 5; - } -} - -void init_ctripletriad(CTripleTriad* env) { - // Allocate memory for board_x, board_y, and board_states - env->board_x = (float*)calloc(9, sizeof(float)); - env->board_y = (float*)calloc(9, sizeof(float)); - env->cards_in_hand = (int***)calloc(2, sizeof(int**)); - env->card_selected = (int*)calloc(2, sizeof(int)); - env->card_locations = (int**)calloc(2, sizeof(int*)); - env->action_masks = (int*)calloc(15, sizeof(int)); - env->board_states = (int**)calloc(3, sizeof(int*)); - env->board_card_values = (int***)calloc(3, sizeof(int**)); - env->score = (int*)calloc(2, sizeof(int)); - for(int i=0; i< 2; i++) { - env->cards_in_hand[i] = (int**)calloc(5, sizeof(int*)); - for(int j=0; j< 5; j++) { - env->cards_in_hand[i][j] = (int*)calloc(4, sizeof(int)); - } - } - for(int i=0; i< 3; i++) { - env->board_states[i] = (int*)calloc(3, sizeof(int)); - } - for(int i=0; i< 2; i++) { - env->card_locations[i] = (int*)calloc(5, sizeof(int)); - } - for(int i=0; i< 3; i++) { - env->board_card_values[i] = (int**)calloc(3, sizeof(int*)); - for(int j=0; j< 3; j++) { - env->board_card_values[i][j] = (int*)calloc(4, sizeof(int)); - } - } - generate_board_positions(env); - generate_cards_in_hand(env); - generate_card_locations(env); - generate_card_selections(env); - generate_board_states(env); - generate_board_card_values(env); - generate_scores(env); -} - -void allocate_ctripletriad(CTripleTriad* env) { - env->actions = (int*)calloc(1, sizeof(int)); - env->observations = (float*)calloc(env->width*env->height, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); - env->rewards = (float*)calloc(1, sizeof(float)); - init_ctripletriad(env); -} - -void c_close(CTripleTriad* env) { - free(env->board_x); - free(env->board_y); - for(int i=0; i< 2; i++) { - for(int j=0; j< 5; j++) { - free(env->cards_in_hand[i][j]); - } - free(env->cards_in_hand[i]); - free(env->card_locations[i]); - } - free(env->cards_in_hand); - free(env->card_locations); - free(env->card_selected); - free(env->action_masks); - for(int i=0; i< 3; i++) { - free(env->board_states[i]); - } - free(env->board_states); - for(int i=0; i< 3; i++) { - for(int j=0; j< 3; j++) { - free(env->board_card_values[i][j]); - } - free(env->board_card_values[i]); - } - free(env->board_card_values); - free(env->score); -} - -void free_allocated_ctripletriad(CTripleTriad* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void compute_observations(CTripleTriad* env) { - int idx=0; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - env->observations[idx] = env->board_states[i][j]; - idx++; - } - } - for (int i = 0; i < 15; i++) { - env->observations[idx] = env->action_masks[i]; - idx++; - } - - for (int i = 0; i < 2; i++) { - env->observations[idx] = env->card_selected[i]; - idx++; - } - for (int i = 0; i < 2; i++) { - env->observations[idx] = env->score[i]; - idx++; - } - for (int i=0;i<3;i++) { - for (int j=0;j<3;j++) { - for (int k=0;k<4;k++) { - env->observations[idx] = env->board_card_values[i][j][k]; - idx++; - } - } - } - for (int i=0;i<2;i++){ - for (int j=0;j<5;j++) { - for (int k=0;k<4;k++) { - env->observations[idx] = env->cards_in_hand[i][j][k]; - idx++; - } - } - } - for (int i=0;i<2;i++) { - for (int j=0;j<5;j++) { - env->observations[idx] = env->card_locations[i][j]; - idx++; - } - } -} - -void c_reset(CTripleTriad* env) { - env->game_over = 0; - for(int i=0; i< 2; i++) { - for(int j=0; j< 5; j++) { - for(int k=0; k< 4; k++) { - env->cards_in_hand[i][j][k] = (rand() % 7) + 1; - } - } - } - for(int i=0; i< 2; i++) { - for(int j=0; j< 5; j++) { - env->card_locations[i][j] = 0; - } - } - for(int i=0; i< 2; i++) { - env->card_selected[i] = -1; - } - for(int i=0; i< 3; i++) { - for(int j=0; j< 3; j++) { - env->board_states[i][j] = 0; - } - } - for (int i = 0; i < 15; i++) { - env->action_masks[i] = 0; - } - for (int i=0; i< 3; i++) { - for (int j=0; j< 3; j++) { - for (int k=0; k< 4; k++) { - env->board_card_values[i][j][k] = 0; - } - } - } - for(int i=0; i< 2; i++) { - env->score[i] = 5; - } - env->terminals[0] = 0; - compute_observations(env); - env->tick = 0; - env->episode_length = 0; - env->episode_return = 0; -} - -void select_card(CTripleTriad* env, int card_selected, int player) { - int player_idx = (player == 1) ? 0 : 1; - env->card_selected[player_idx] = card_selected-1; -} - -void place_card(CTripleTriad* env, int card_placement, int player) { - // Determine the player index (0 for player 1, 1 for player 2) - int player_idx = (player == 1) ? 0 : 1; - // Update the card's location on the board - env->card_locations[player_idx][env->card_selected[player_idx]] = card_placement; - // Update the board state to reflect the player who placed the card - env->board_states[(card_placement-1)/3][(card_placement-1)%3] = player; - // Copy the card values from the player's hand to the board - for (int i = 0; i < 4; i++) { - env->board_card_values[(card_placement-1)/3][(card_placement-1)%3][i] = env->cards_in_hand[player_idx][env->card_selected[player_idx]][i]; - } -} - -void update_action_masks(CTripleTriad* env) { - // First, reset all action masks to 0 (available) - for (int i = 0; i < 15; i++) { - env->action_masks[i] = 0; - } - - // Update masks for card placement - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 5; j++) { - if (env->card_locations[i][j] != 0) { - int action_idx = env->card_locations[i][j] + 4; - if (action_idx >= 5 && action_idx < 14) { - env->action_masks[action_idx] = 1; // Mark as unavailable - } - } - } - } -} - -void check_win_condition(CTripleTriad* env, int player) { - int count = 0; - for (int i=0; i< 3; i++) { - for (int j=0; j< 3; j++) { - if (env->board_states[i][j] !=0) { - count++; - } - } - } - if (count == 9) { - // add a draw condition and winner value is 0 - if (env->score[0] == env->score[1]) { - env->terminals[0] = 1; - env->rewards[0] = 0.0; - env->game_over = 1; - } else { - int winner = env->score[0] > env->score[1] ? 1 : -1; - env->terminals[0] = 1; - env->rewards[0] = winner; // 1 for player win, -1 for opponent win - env->episode_return += winner; - env->game_over = 1; - } - } - return; -} - -int get_bot_card_placement(CTripleTriad* env) { - int valid_placements[9]; // Maximum 9 possible placements - int num_valid_placements = 0; - - // Find valid placements - for (int i = 5; i < 14; i++) { - if (env->action_masks[i] == 0) { - valid_placements[num_valid_placements++] = i - 4; - if (num_valid_placements == 9) break; // Safety check - } - } - - // Randomly select a valid placement - if (num_valid_placements > 0) { - return valid_placements[rand() % num_valid_placements]; - } - - // If no valid placements, return 0 (this should not happen in a normal game) - return 0; -} - -int get_bot_card_selection(CTripleTriad* env) { - int valid_selections[5]; // Maximum 5 possible selections - int num_valid_selections = 0; - - // Find valid selections - for (int i = 0; i < 5; i++) { - if (env->card_locations[1][i] == 0) { // Check if the card has not been placed - valid_selections[num_valid_selections++] = i + 1; - } - } - - // Randomly select a valid card - if (num_valid_selections > 0) { - return valid_selections[rand() % num_valid_selections]; - } - - // If no valid selections, return 0 (this should not happen in a normal game) - return 0; -} - -bool check_legal_placement(CTripleTriad* env, int card_placement, int player) { - int row = (card_placement - 1) / 3; - int col = (card_placement - 1) % 3; - if (env->board_states[row][col] != 0) { - return 0; - } else { - return 1; - } -} - -void check_card_conversions(CTripleTriad* env, int card_placement, int player) { - // Given that card locations last 4 values of the most inner array are organized, NSEW. - // Check the cards in those directions and what their values are - // If the card in the direction is greater than the card in the current location, then convert the game state of the lower value card to the player with the higher value card. - int card_idx = card_placement - 1; - int row = card_idx / 3; - int col = card_idx % 3; - int enemy_player = (player == 1) ? -1 : 1; - int player_idx = (player == 1) ? 0 : 1; - int enemy_player_idx = (player == 1) ? 1 : 0; - int values[4] = { - env->board_card_values[row][col][0], // North - env->board_card_values[row][col][1], // South - env->board_card_values[row][col][2], // East - env->board_card_values[row][col][3] // West - }; - - int adjacent_indices[4][2] = { - {row - 1, col}, // North - {row + 1, col}, // South - {row, col + 1}, // East - {row, col - 1} // West - }; - - int adjacent_value_indices[4] = {1, 0, 3, 2}; // South, North, West, East - - for (int i = 0; i < 4; i++) { - int adj_row = adjacent_indices[i][0]; - int adj_col = adjacent_indices[i][1]; - - // Check if the adjacent cell is within the board - if (adj_row >= 0 && adj_row < 3 && adj_col >= 0 && adj_col < 3) { - int adjacent_value = env->board_card_values[adj_row][adj_col][adjacent_value_indices[i]]; - if (adjacent_value < values[i] && adjacent_value != 0 && env->board_states[adj_row][adj_col] == enemy_player) { - env->board_states[adj_row][adj_col] = player; - env->score[player_idx]++; - env->score[enemy_player_idx]--; - } - } - } -} - -void c_step(CTripleTriad* env) { - env->episode_length += 1; - env->rewards[0] = 0.0; - int action = env->actions[0]; - - if (env->episode_length >= MAX_EPISODE_LENGTH) { - env->game_over = 1; - env->episode_return -= 1.0; - env->rewards[0] -= 1.0; - } - - // reset the game if game over - if (env->game_over == 1) { - env->perf = (env->score[0] > env->score[1]) ? 1.0 : 0.0; - add_log(env); - c_reset(env); - return; - } - // select a card if the card is in the range of 1-5 and the card is not placed - if (action >= SELECT_CARD_1 && action <= SELECT_CARD_5 ) { - // Prevent model from just swapping between selected cards to avoid playing - env->episode_return -= 0.1; - env->rewards[0] -= 0.1; - - int card_selected = action + 1; - - if(env->card_locations[0][card_selected-1] == 0) { - select_card(env, card_selected, 1); - } - } - // place a card if the card is in the range of 1-9 and the card is selected - else if (action >= PLACE_CARD_1 && action <= PLACE_CARD_9 ) { - int card_placement = action - 4; - bool card_placed = false; - if(env->card_selected[0] >= 0) { - if(check_legal_placement(env, card_placement, 1)) { - place_card(env,card_placement, 1); - check_card_conversions(env, card_placement, 1); - check_win_condition(env, 1); - update_action_masks(env); - env->card_selected[0] = -1; - card_placed = true; - } else { - env->episode_return -= 0.1; - env->rewards[0] -= 0.1; - } - } else { - env->episode_return -= 0.1; - env->rewards[0] -= 0.1; - } - - // opponent turn - if (env->terminals[0] == 0 && card_placed == true ) { - int bot_card_selected = get_bot_card_selection(env); - if(bot_card_selected > 0) { - select_card(env,bot_card_selected, -1); - int bot_card_placement = get_bot_card_placement(env); - place_card(env,bot_card_placement, -1); - check_card_conversions(env, bot_card_placement, -1); - check_win_condition(env, -1); - update_action_masks(env); - env->card_selected[1] = -1; - } - - } - } - if (env->terminals[0] == 1) { - env->game_over=1; - } - compute_observations(env); -} - -typedef struct Client Client; -struct Client { - float width; - float height; -}; - -Client* make_client(int width, int height) { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = width; - client->height = height; - - InitWindow(width, height, "PufferLib Ray TripleTriad"); - SetTargetFPS(60); - - return client; -} - -void c_render(CTripleTriad* env) { - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - - if (env->client == NULL) { - env->client = make_client(env->width, env->height); - } - - BeginDrawing(); - ClearBackground(PUFF_BACKGROUND); - - // create 3x3 board for triple triad - for (int row = 0; row < 3; row++) { - for (int col = 0; col < 3; col++) { - int board_idx = row * 3 + col; - Color piece_color=PURPLE; - if (env->board_states[row][col] == 0.0) { - piece_color = PUFF_BACKGROUND; - } else if (env->board_states[row][col] == 1.0) { - piece_color = PUFF_CYAN; - } else if (env->board_states[row][col] == -1.0) { - piece_color = PUFF_RED; - } - int x = env->board_x[board_idx]; - int y = env->board_y[board_idx]; - DrawRectangle(x+196+10 , y+10 , env->card_width, env->card_height, piece_color); - DrawRectangleLines(x+196+10 , y+10 , env->card_width, env->card_height, PUFF_WHITE); - } - } - for(int i=0; i< 2; i++) { - for(int j=0; j< 5; j++) { - // starting locations for cards in hand - int card_x = (i == 0) ? 10 : (env->width - env->card_width - 10); - int card_y = 10 + env->card_height/2*j; - - // locations if card is placed - if (env->card_locations[i][j] != 0) { - card_x = env->board_x[env->card_locations[i][j]-1] + 196 + 10; - card_y = env->board_y[env->card_locations[i][j]-1] + 10; - } - // Draw card background - // adjusts card color based on board state - Color card_color = (i != 0) ? PUFF_RED : PUFF_CYAN; - // check if index is in bounds first - if (env->card_locations[i][j] != 0) { - if (env->board_states[(env->card_locations[i][j]-1)/3][(env->card_locations[i][j]-1)%3] == -1) { - card_color = PUFF_RED; - } else if (env->board_states[(env->card_locations[i][j]-1)/3][(env->card_locations[i][j]-1)%3] == 1) { - card_color = PUFF_CYAN; - } else { - card_color = (i != 0) ? PUFF_CYAN : PUFF_RED; - } - } - DrawRectangle(card_x, card_y, env->card_width, env->card_height, card_color); - // change background if card is selected, highlight it - Rectangle rect = (Rectangle){card_x, card_y, env->card_width, env->card_height}; - if (env->card_selected[i] == j) { - DrawRectangleLinesEx(rect, 3, PUFF_RED); - } else { - DrawRectangleLinesEx(rect, 2, PUFF_WHITE); - } - - for(int k=0; k< 4; k++) { - int x_offset, y_offset; - switch(k) { - case 0: // North (top) - x_offset = card_x + 25; - y_offset = card_y + 5; - break; - case 1: // South (bottom) - x_offset = card_x + 25; - y_offset = card_y + 45; - break; - case 2: // East (right) - x_offset = card_x + 45; - y_offset = card_y + 25; - break; - case 3: // West (left) - x_offset = card_x + 5; - y_offset = card_y + 25; - break; - } - - Color text_color = PUFF_WHITE; - DrawText(TextFormat("%d", env->cards_in_hand[i][j][k]), x_offset, y_offset, 20, text_color); - } - // add a little text on the top right that says Card 1, Card 2, Card 3, Card 4, Card 5 - DrawText(TextFormat("Card %d", j+1), card_x + env->card_width -50, card_y + 5, 10, PUFF_WHITE); - } - if (i == 0) { - DrawText(TextFormat("%d", env->score[i]), env->card_width *0.4, env->height - 100, 100, PUFF_WHITE); - } else { - DrawText(TextFormat("%d", env->score[i]), env->width - env->card_width *.6, env->height - 100, 100, PUFF_WHITE); - } - } - EndDrawing(); - - //PlaySound(client->sound); -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} diff --git a/pufferlib/ocean/tripletriad/tripletriad.py b/pufferlib/ocean/tripletriad/tripletriad.py deleted file mode 100644 index fce49c4ca..000000000 --- a/pufferlib/ocean/tripletriad/tripletriad.py +++ /dev/null @@ -1,65 +0,0 @@ -import numpy as np -import gymnasium - -import pufferlib -from pufferlib.ocean.tripletriad import binding - -class TripleTriad(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, report_interval=1, - width=990, height=690, card_width=192, card_height=224, buf=None, seed=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(114,), dtype=np.float32) - self.single_action_space = gymnasium.spaces.Discrete(14) - self.report_interval = report_interval - self.render_mode = render_mode - self.num_agents = num_envs - - super().__init__(buf=buf) - self.c_envs = binding.vec_init(self.observations, self.actions, - self.rewards, self.terminals, self.truncations, num_envs, seed, width=width, height=height, - card_width=card_width, card_height=card_height) - - def reset(self, seed=None): - self.tick = 0 - if seed is None: - binding.vec_reset(self.c_envs, 0) - else: - binding.vec_reset(self.c_envs, seed) - return self.observations, [] - - def step(self, actions): - self.actions[:] = actions - binding.vec_step(self.c_envs) - self.tick += 1 - - info = [] - if self.tick % self.report_interval == 0: - info.append(binding.vec_log(self.c_envs)) - - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - env = TripleTriad(num_envs=1000) - env.reset() - tick = 0 - - actions = np.random.randint(0, 2, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - atn = actions[tick % atn_cache] - env.step(atn) - tick += 1 - - print(f'SPS: {env.num_agents * tick / (time.time() - start):,}') - -if __name__ == '__main__': - test_performance() diff --git a/pufferlib/ocean/whisker_racer/binding.c b/pufferlib/ocean/whisker_racer/binding.c deleted file mode 100644 index 88582daaf..000000000 --- a/pufferlib/ocean/whisker_racer/binding.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "whisker_racer.h" - -#define Env WhiskerRacer -#include "../env_binding.h" - -static int my_init(Env* env, PyObject* args, PyObject* kwargs) { - env->frameskip = unpack(kwargs, "frameskip"); - env->width = unpack(kwargs, "width"); - env->height = unpack(kwargs, "height"); - env->llw_ang = unpack(kwargs, "llw_ang"); - env->flw_ang = unpack(kwargs, "flw_ang"); - env->frw_ang = unpack(kwargs, "frw_ang"); - env->rrw_ang = unpack(kwargs, "rrw_ang"); - env->max_whisker_length = unpack(kwargs, "max_whisker_length"); - env->turn_pi_frac = unpack(kwargs, "turn_pi_frac"); - env->maxv = unpack(kwargs, "maxv"); - env->render = unpack(kwargs, "render"); - env->continuous = unpack(kwargs, "continuous"); - env->reward_yellow = unpack(kwargs, "reward_yellow"); - env->reward_green = unpack(kwargs, "reward_green"); - env->gamma = unpack(kwargs, "gamma"); - env->track_width = unpack(kwargs, "track_width"); - env->num_radial_sectors = unpack(kwargs, "num_radial_sectors"); - env->num_points = unpack(kwargs, "num_points"); - env->bezier_resolution = unpack(kwargs, "bezier_resolution"); - env->turn_pi_frac = unpack(kwargs, "turn_pi_frac"); - env->w_ang = unpack(kwargs, "w_ang"); - env->corner_thresh = unpack(kwargs, "corner_thresh"); - env->ftmp1 = unpack(kwargs, "ftmp1"); - env->ftmp2 = unpack(kwargs, "ftmp2"); - env->ftmp3 = unpack(kwargs, "ftmp3"); - env->ftmp4 = unpack(kwargs, "ftmp4"); - env->mode7 = unpack(kwargs, "mode7"); - env->render_many = unpack(kwargs, "render_many"); - env->rng = unpack(kwargs, "rng"); - env->method = unpack(kwargs, "method"); - env->i = unpack(kwargs, "i"); - - init(env); - return 0; -} - -static int my_log(PyObject* dict, Log* log) { - assign_to_dict(dict, "perf", log->perf); - assign_to_dict(dict, "score", log->score); - assign_to_dict(dict, "episode_return", log->episode_return); - assign_to_dict(dict, "episode_length", log->episode_length); - return 0; -} diff --git a/pufferlib/ocean/whisker_racer/whisker_racer.c b/pufferlib/ocean/whisker_racer/whisker_racer.c deleted file mode 100644 index 9be82283b..000000000 --- a/pufferlib/ocean/whisker_racer/whisker_racer.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include "whisker_racer.h" -#include "puffernet.h" - -void demo() { - printf("demo\n"); - Weights* weights = load_weights("resources/whisker_racer/puffer_whisker_racer_weights.bin", 133124); - int logit_sizes[1] = {3}; - LinearLSTM* net = make_linearlstm(weights, 1, 3, logit_sizes, 1); - - WhiskerRacer env = { - .frameskip = 1, - .width = 1080, - .height = 720, - .max_whisker_length = 100, - .turn_pi_frac = 40, - .maxv = 5, - .render = 0, - .continuous = 0, - .reward_yellow = 0.25, - .reward_green = -0.001, - .track_width = 75, - .num_radial_sectors = 180, - .num_points = 16, - .bezier_resolution = 4, - .w_ang = 0.777, - .corner_thresh = 0.5, - .mode7 = 1, // If mode7 = 1 then 640X480 recommended - .render_many = 0, - .rng = 3, // rng = 3 for puffer track - .i = 1, // i = 1 for puffer track - .method = 1, // method = 1 for puffer track - }; - - allocate(&env); - - env.client = make_client(&env); - - c_reset(&env); - int frame = 0; - SetTargetFPS(60); - while (!WindowShouldClose()) { - // User can take control of the paddle - if (IsKeyDown(KEY_LEFT_SHIFT)) { - if(env.continuous) { - float move = GetMouseWheelMove(); - float clamped_wheel = fmaxf(-1.0f, fminf(1.0f, move)); - env.actions[0] = clamped_wheel; - } else { - env.actions[0] = 1.0; // Straight - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) env.actions[0] = 0.0; // Left - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) env.actions[0] = 2.0; // Right - } - } else if (frame % 4 == 0) { - // Apply frameskip outside the env for smoother rendering - int* actions = (int*)env.actions; - forward_linearlstm(net, env.observations, actions); - env.actions[0] = actions[0]; - } - - frame = (frame + 1) % 4; - c_step(&env); - c_render(&env); - } - free_linearlstm(net); - free(weights); - free_allocated(&env); - close_client(env.client); -} - -int main() { - demo(); -} diff --git a/pufferlib/ocean/whisker_racer/whisker_racer.h b/pufferlib/ocean/whisker_racer/whisker_racer.h deleted file mode 100644 index 933f0a660..000000000 --- a/pufferlib/ocean/whisker_racer/whisker_racer.h +++ /dev/null @@ -1,971 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "raylib.h" -#include - -#define LEFT 0 -#define NOOP 1 -#define RIGHT 2 - -#define PI2 PI * 2 - -#define MAX_CONTROL_POINTS 32 -#define NUM_RADIAL_SECTORS 16 -#define MAX_BEZIER_RESOLUTION 16 - -typedef struct { - Vector2 position; -} ControlPoint; - -typedef struct { - ControlPoint controls[MAX_CONTROL_POINTS]; - int num_points; - Vector2 centerline[MAX_CONTROL_POINTS * MAX_BEZIER_RESOLUTION]; - Vector2 inner_edge[MAX_CONTROL_POINTS * MAX_BEZIER_RESOLUTION]; - Vector2 outer_edge[MAX_CONTROL_POINTS * MAX_BEZIER_RESOLUTION]; - int total_points; - Vector2 curbs[MAX_CONTROL_POINTS][4]; - int curb_count; -} Track; - -typedef struct Log { - float perf; - float score; - float episode_return; - float episode_length; - float n; -} Log; - -typedef struct Client { - float width; // 640 - float height; // 480 - //float llw_ang; // left left whisker angle - float flw_ang; // front left whisker angle - float frw_ang; // front right whisker angle - //float rrw_ang; // right right whisker angle - float max_whisker_length; - float turn_pi_frac; // (pi / turn_pi_frac is the turn angle) - float maxv; // 5 - int render; - int debug; -} Client; - -typedef struct WhiskerRacer { - Client* client; - Log log; - float* observations; - float* actions; - float* rewards; - unsigned char* terminals; - int i; - - int debug; - unsigned int rng; - int render_many; - - float corner_thresh; - float ftmp1; - float ftmp2; - float ftmp3; - float ftmp4; - int method; - - float reward_yellow; - float reward_green; - float gamma; - - // Game - int width; - int height; - float score; - int tick; - int max_score; - int half_max_score; - int frameskip; - int render; - int continuous; - int current_sector; - int sectors_completed[NUM_RADIAL_SECTORS]; - int total_sectors_crossed; - int track_width; - int num_radial_sectors; - int num_points; - int bezier_resolution; - Track track; - - // Car - float px; - float py; - float ang; - float vx; - float vy; - float v; - int near_point_idx; - float maxv; - float turn_pi_frac; - - // Whiskers - int num_whiskers; - Vector2 whisker_dirs[2]; - float w_ang; - float llw_ang; // left left whisker angle - float flw_ang; // front left whisker angle - float frw_ang; // front right whisker angle - float rrw_ang; // right right whisker angle - float llw_length; - float flw_length; - float ffw_length; - float frw_length; - float rrw_length; - float max_whisker_length; - - // Math - float inv_width; - float inv_height; - float inv_maxv; - float inv_pi2; - float inv_bezier_res; - - Texture2D puffer; - - int texture_initialized; - int mode7; - -} WhiskerRacer; - -void c_close(WhiskerRacer* env) { - //unload_track(); -} - -void free_allocated(WhiskerRacer* env) { - free(env->actions); - free(env->observations); - free(env->terminals); - free(env->rewards); - c_close(env); -} - -void add_log(WhiskerRacer* env) { - env->log.episode_length += env->tick; - if (env->log.episode_length > 0.01f) { - } - env->log.episode_return += env->score; - env->log.score += env->score; - env->log.perf += env->score / (float)env->max_score; - env->log.n += 1; -} - -void compute_observations(WhiskerRacer* env) { - env->observations[0] = env->flw_length; - env->observations[1] = env->frw_length; - env->observations[2] = env->score / 100.0f; -} - -Client* make_client(WhiskerRacer* env) { - Client* client = (Client*)calloc(1, sizeof(Client)); - client->width = env->width; - client->height = env->height; - //client->llw_ang = env->llw_ang; - client->flw_ang = env->flw_ang; - client->frw_ang = env->frw_ang; - //client->rrw_ang = env->rrw_ang; - client->max_whisker_length = env->max_whisker_length; - client->turn_pi_frac = env->turn_pi_frac; - client->maxv = env->maxv; - - InitWindow(env->width, env->height, "PufferLib Whisker Racer"); - if (env->render_many) SetTargetFPS(10 / env->frameskip); - else SetTargetFPS(60 / env->frameskip); - env->puffer = LoadTexture("resources/shared/puffers_128.png"); - - return client; -} - -void close_client(Client* client) { - CloseWindow(); - free(client); -} - -void get_random_start(WhiskerRacer* env) { - int start_idx = rand() % env->track.total_points; - env->near_point_idx = start_idx; - - env->px = env->track.centerline[start_idx].x; - env->py = env->track.centerline[start_idx].y; - - int next_idx = (start_idx + 1) % env->track.total_points; - float dx = env->track.centerline[next_idx].x - env->px; - float dy = env->track.centerline[next_idx].y - env->py; - env->ang = atan2f(dy, dx); - - //env->whisker_dirs[0] = (Vector2){cosf(env->ang + env->llw_ang), sinf(env->ang + env->llw_ang)}; - //env->whisker_dirs[1] = (Vector2){cosf(env->ang + env->flw_ang), sinf(env->ang + env->flw_ang)}; - //env->whisker_dirs[2] = (Vector2){cosf(env->ang), sinf(env->ang)}; - //env->whisker_dirs[3] = (Vector2){cosf(env->ang + env->frw_ang), sinf(env->ang + env->frw_ang)}; - //env->whisker_dirs[4] = (Vector2){cosf(env->ang + env->rrw_ang), sinf(env->ang + env->rrw_ang)}; - env->whisker_dirs[0] = (Vector2){cosf(env->ang + env->flw_ang), sinf(env->ang + env->flw_ang)}; - env->whisker_dirs[1] = (Vector2){cosf(env->ang + env->frw_ang), sinf(env->ang + env->frw_ang)}; - - env->v = env->maxv; - //env->llw_length = 0.25f; - env->flw_length = 0.50f; - //env->ffw_length = 1.00f; - env->frw_length = 0.50f; - //env->rrw_length = 0.25f; -} - -void reset_radial_progress(WhiskerRacer* env) { - float center_x = env->width * 0.5f; - float center_y = env->height * 0.5f; - - float angle = atan2f(env->py - center_y, env->px - center_x); - if (angle < 0) angle += PI2; - - env->current_sector = (int)(angle / (PI2 / 16.0f)) % 16; - - for (int i = 0; i < 16; i++) { - env->sectors_completed[i] = 0; - } - env->total_sectors_crossed = 0; -} - -void reset_round(WhiskerRacer* env) { - get_random_start(env); - reset_radial_progress(env); - env->vx = 0.0f; - env->vy = 0.0f; - env->v = env->maxv; -} - -void c_reset(WhiskerRacer* env) { - env->score = 0; - reset_round(env); - env->tick = 0; - compute_observations(env); -} - -// Line segment intersection helper function -// Returns 1 if intersection found, 0 otherwise -// If intersection found, stores the parameter t in *t_out (0 <= t <= 1 along the whisker ray) -static inline int line_segment_intersect(Vector2 ray_start, Vector2 ray_dir, float ray_length, - Vector2 seg_start, Vector2 seg_end, float* t_out) { - Vector2 seg_dir = {seg_end.x - seg_start.x, seg_end.y - seg_start.y}; - Vector2 diff = {seg_start.x - ray_start.x, seg_start.y - ray_start.y}; - - float cross_rd_sd = ray_dir.x * seg_dir.y - ray_dir.y * seg_dir.x; - - // Lines are parallel - if (fabsf(cross_rd_sd) < 1e-3f) { - return 0; - } - - float cross_diff_sd = diff.x * seg_dir.y - diff.y * seg_dir.x; - float cross_diff_rd = diff.x * ray_dir.y - diff.y * ray_dir.x; - - float t = cross_diff_sd / cross_rd_sd; - float u = cross_diff_rd / cross_rd_sd; - - // Check if intersection is within both line segments - if (t >= 0.0f && t <= ray_length && u >= 0.0f && u <= 1.0f) { - *t_out = t; - return 1; - } - - return 0; -} - -void update_nearest_point(WhiskerRacer* env) { - float min_dist_sq = 100000; - int closest_seg = env->near_point_idx; - Vector2 car_pos = {env->px, env->py}; - - int search_range = 3; - for (int offset = 0; offset <= search_range; offset++) { - int i = (env->near_point_idx + offset + env->track.total_points) % env->track.total_points; - - Vector2 center = env->track.centerline[i]; - float dx = car_pos.x - center.x; - float dy = car_pos.y - center.y; - float dist_sq = dx * dx + dy * dy; - - if (dist_sq < min_dist_sq) { - min_dist_sq = dist_sq; - closest_seg = i; - } - } - - env->near_point_idx = closest_seg; -} - -void calc_whisker_lengths(WhiskerRacer* env) { - float max_len = env->max_whisker_length; - float inv_max_len = 1.0f / max_len; - - update_nearest_point(env); - - float* lengths[2] = { - //&env->llw_length, - &env->flw_length, - //&env->ffw_length, - &env->frw_length - //&env->rrw_length - }; - - Vector2 car_pos = {env->px, env->py}; - - for (int w = 0; w < 2; ++w) { - Vector2 whisker_dir = env->whisker_dirs[w]; - float min_hit_distance = max_len; - - int window_size = 10; - for (int offset = -window_size/2; offset <= window_size/2; offset++) { - int i = (env->near_point_idx + offset + env->track.total_points) % env->track.total_points; - int next_i = (i + 1) % env->track.total_points; - - float t; - - if (line_segment_intersect(car_pos, whisker_dir, max_len, - env->track.inner_edge[i], env->track.inner_edge[next_i], &t)) { - if (t < min_hit_distance) { - min_hit_distance = t; - } - if (t < 0.05) break; - } - - if (line_segment_intersect(car_pos, whisker_dir, max_len, - env->track.outer_edge[i], env->track.outer_edge[next_i], &t)) { - if (t < min_hit_distance) { - min_hit_distance = t; - } - if (t < 0.05) break; - } - } - - *lengths[w] = fminf(1.0f, fmaxf(0.0f, min_hit_distance * inv_max_len)); - - if (*lengths[w] < 0.05f) { // Car has crashed - for (int j = 0; j < 2; j++) *lengths[j] = 0.0f; - env->terminals[0] = 1; - add_log(env); - c_reset(env); - } - } -} - -void update_radial_progress(WhiskerRacer* env) { - float center_x = env->width * 0.5f; - float center_y = env->height * 0.5f; - - float angle = atan2f(env->py - center_y, env->px - center_x); - - if (angle < 0) angle += PI2; - - int sector = (int)(angle / (PI2 / 16.0f)); - sector = sector % env->num_radial_sectors; - - if (sector != env->current_sector) { - int expected_next = (env->current_sector + 1) % 16; - if (sector == expected_next) { - if (!env->sectors_completed[sector]) { - env->sectors_completed[sector] = 1; - env->total_sectors_crossed++; - env->rewards[0] += env->reward_yellow; - env->score += env->reward_yellow; - } else { // full lap - env->rewards[0] += env->reward_yellow; - env->score += env->reward_yellow; - } - } - env->current_sector = sector; - } -} - -Vector2 EvaluateCubicBezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) { - float u = 1.0f - t; - float tt = t * t; - float uu = u * u; - float uuu = uu * u; - float ttt = tt * t; - - Vector2 result; - result.x = uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x; - result.y = uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y; - return result; -} - -Vector2 GetBezierDerivative(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) { - float u = 1.0f - t; - float tt = t * t; - float uu = u * u; - - Vector2 result; - result.x = -3 * uu * p0.x + 3 * uu * p1.x - 6 * u * t * p1.x + 6 * u * t * p2.x - 3 * tt * p2.x + 3 * tt * p3.x; - result.y = -3 * uu * p0.y + 3 * uu * p1.y - 6 * u * t * p1.y + 6 * u * t * p2.y - 3 * tt * p2.y + 3 * tt * p3.y; - return result; -} - -Vector2 NormalizeVector(Vector2 v) { - float length = sqrtf(v.x * v.x + v.y * v.y); - if (length < 0.00001f) return (Vector2){0, 0}; - return (Vector2){v.x / length, v.y / length}; -} - -Vector2 GetPerpendicular(Vector2 v) { - return (Vector2){-v.y, v.x}; -} - -void GenerateRandomControlPoints(WhiskerRacer* env) { - float center_x = env->width * 0.5f; - float center_y = env->height * 0.5f; - - int n = env->num_points; - - if (env->method == -1) { - env->method = rand() % 3; - } - - if (env->method == 0) { - // Randomly choose distinct, non-adjacent indices for tight and medium corners - int opt1 = rand() % n; - int opt2; - do { - opt2 = rand() % n; - } while (opt2 == opt1 || abs(opt2 - opt1) == 1 || abs(opt2 - opt1) == n - 1); - - int opt3, opt4; - do { - opt3 = rand() % n; - } while (opt3 == opt1 || opt3 == opt2); - - do { - opt4 = rand() % n; - } while (opt4 == opt1 || opt4 == opt2 || opt4 == opt3 || abs(opt4 - opt3) == 1 || abs(opt4 - opt3) == n - 1); - - // Generate control points - for (int i = 0; i < n; i++) { - float angle = (PI2 * i) / n; - - float dist_from_center; - if (i == opt1) { - dist_from_center = env->height * 0.2 + (rand() % 30); - } else if (i == opt2 || i == opt3) { - dist_from_center = env->height * 0.3 + (rand() % 40); - } else { - dist_from_center = env->height * 0.5 + (rand() % 30); - } - - env->track.controls[i].position.x = center_x + dist_from_center * cosf(angle); - env->track.controls[i].position.y = center_y + dist_from_center * 0.8f * sinf(angle); - } - } // end method 0 - else if (env->method == 1) { - - int corner_types[n]; - int assigned[n]; - - // type 0 is close to center, 1 is medium, 2 is far from center - for (int i = 0; i < n; i++) { - corner_types[i] = 2; - assigned[i] = 0; - } - - int num_med = (n + 2) / 5; - for (int placed = 0; placed < num_med; placed++) { - int attempts = 0; - int pos; - do { - pos = rand() % n; - bool valid = (assigned[pos] == 0); - if (valid) break; - attempts++; - } while (attempts < 50); - - if (attempts < 50) { - corner_types[pos] = 1; - assigned[pos] = 1; - } - } - - int num_close = (n + 2) / 8; - for (int placed = 0; placed < num_close; placed++) { - int attempts = 0; - int pos; - do { - pos = rand() % n; - int prev_prev = (pos - 2 + n) % n; - int prev = (pos - 1 + n) % n; - int next = (pos + 1) % n; - - bool valid = (corner_types[pos] == 2) && - (corner_types[prev_prev] > 0) && - (corner_types[prev] > 0) && - (corner_types[next] > 0) && - //(corner_types[next_next] > 0) && - (assigned[pos] == 0); - - if (valid) break; - attempts++; - } while (attempts < 50); - - if (attempts < 50) { - corner_types[pos] = 0; - assigned[pos] = 1; - } - } - - for (int i = 0; i < n; i++ ) { - int prev = (i - 1 + n) % n; - int next = (i + 1 + n) % n; - if (corner_types[prev] == 0 && corner_types[i] == 2 && corner_types[next] == 0) { - corner_types[i] = 1; - } - } - - // Generate control points - for (int i = 0; i < n; i++) { - float angle = (PI2 * i) / n; - - float dist_from_center; - if (corner_types[i] == 0) { - dist_from_center = env->height * 0.35 + (rand() % 30); - } else if (corner_types[i] == 1) { - dist_from_center = env->height * 0.45 + (rand() % 40); - } else { - dist_from_center = env->height * 0.6 + (rand() % 30); - } - - env->track.controls[i].position.x = center_x + dist_from_center * 1.2f * cosf(angle); - env->track.controls[i].position.y = center_y + dist_from_center * 0.7f * sinf(angle); - } - - } // end method 1 - else { - float base_radius = env->height * 0.5f; - float variation_strength = 0.5; - float track_stretch_x = 1.0; - float track_stretch_y = 0.6; - - float freq1 = 2.0f + (rand() % 5); - float amp1 = (1.0f / freq1) * (0.9f + 0.2f * (rand() % 100) / 100.0f); - float phase1 = PI2 * (rand() % 100) / 100.0f; - - float freq2 = 1.0f + (rand() % 2); - float amp2 = 0.2f + 0.2f * (rand() % 100) / 100.0f; - float phase2 = PI2 * (rand() % 100) / 100.0f; - - float freq3 = 10.0f + 0.5f * (rand() % 3); - float amp3 = 0.3f + 0.1f * (rand() % 100) / 100.0f; - float phase3 = PI2 * (rand() % 100) / 100.0f; - - for (int i = 0; i < n; i++) { - float angle = (PI2 * i) / n; - - float radius_variation = amp1 * cosf(freq1 * angle + phase1) + - amp2 * cosf(freq2 * angle + phase2) + - amp3 * cosf(freq3 * angle + phase3); - - float radius = base_radius + (base_radius * variation_strength * radius_variation); - - env->track.controls[i].position.x = center_x + radius * track_stretch_x * cosf(angle); - env->track.controls[i].position.y = center_y + radius * track_stretch_y * sinf(angle); - } - } // end method 2 - - float tw2 = env->track_width * 0.5f; - - for (int i = 0; i < n; i++) { - if (env->track.controls[i].position.x < tw2) env->track.controls[i].position.x = tw2; - if (env->track.controls[i].position.x > env->width - tw2) env->track.controls[i].position.x = env->width - tw2; - if (env->track.controls[i].position.y < tw2) env->track.controls[i].position.y = tw2; - if (env->track.controls[i].position.y > env->height - tw2) env->track.controls[i].position.y = env->height - tw2; - - Vector2 prev = env->track.controls[(i - 1 + n) % n].position; - Vector2 curr = env->track.controls[i].position; - Vector2 next = env->track.controls[(i + 1) % n].position; - - - float vx1 = prev.x - curr.x; - float vy1 = prev.y - curr.y; - float vx2 = next.x - curr.x; - float vy2 = next.y - curr.y; - - float dot = vx1 * vx2 + vy1 * vy2; - float mag1 = sqrtf(vx1 * vx1 + vy1 * vy1); - float mag2 = sqrtf(vx2 * vx2 + vy2 * vy2); - - if (mag1 < 1e-3f || mag2 < 1e-3f) continue; - - float angle_cos = dot / (mag1 * mag2); - - if (angle_cos > env->corner_thresh) { - float dx = curr.x - center_x; - float dy = curr.y - center_y; - float dist = sqrtf(dx * dx + dy * dy); - - float adjust_scale = 0.0f; - if (dist > 200) { - adjust_scale = 0.3f * angle_cos; - } - - env->track.controls[i].position.x = env->track.controls[i].position.x - dx * adjust_scale; - env->track.controls[i].position.y = env->track.controls[i].position.y - dy * adjust_scale; - } - } -} - -void GenerateTrackCenterline(WhiskerRacer* env) { - int point_index = 0; - - for (int i = 0; i < env->num_points; i++) { - Vector2 p0 = env->track.controls[i].position; - Vector2 p3 = env->track.controls[(i + 1) % env->num_points].position; - - Vector2 prev = env->track.controls[(i - 1 + env->num_points) % env->num_points].position; - Vector2 next = env->track.controls[(i + 2) % env->num_points].position; - - Vector2 dir1 = NormalizeVector((Vector2){p3.x - prev.x, p3.y - prev.y}); - Vector2 dir2 = NormalizeVector((Vector2){next.x - p0.x, next.y - p0.y}); - - float dist = sqrtf((p3.x - p0.x) * (p3.x - p0.x) + (p3.y - p0.y) * (p3.y - p0.y)); - - float control_length; - if (i == 1 || i == 3) { - control_length = dist * 0.2f; - } else if (i == 0 || i == 4) { - control_length = dist * 0.3f; - } else { - control_length = dist * 0.4f; - } - - Vector2 p1 = (Vector2){p0.x + dir1.x * control_length, p0.y + dir1.y * control_length}; - Vector2 p2 = (Vector2){p3.x - dir2.x * control_length, p3.y - dir2.y * control_length}; - - for (int j = 0; j < env->bezier_resolution && point_index < MAX_CONTROL_POINTS * env->bezier_resolution - 1; j++) { - float t = (float)j * env->inv_bezier_res; - env->track.centerline[point_index] = EvaluateCubicBezier(p0, p1, p2, p3, t); - point_index++; - } - } - env->track.total_points = point_index; -} - -void GenerateTrackEdges(WhiskerRacer* env) { - for (int i = 0; i < env->track.total_points; i++) { - Vector2 current = env->track.centerline[i]; - Vector2 next = env->track.centerline[(i + 1) % env->track.total_points]; - - Vector2 tangent = NormalizeVector((Vector2){next.x - current.x, next.y - current.y}); - Vector2 normal = GetPerpendicular(tangent); - - // Create inner and outer edges - float half_width = env->track_width * 0.5f; - env->track.inner_edge[i] = (Vector2){current.x - normal.x * half_width, current.y - normal.y * half_width}; - env->track.outer_edge[i] = (Vector2){current.x + normal.x * half_width, current.y + normal.y * half_width}; - } -} - -void GenerateCurbs(WhiskerRacer* env) { - env->track.curb_count = 0; - - for (int i = 0; i < env->num_points; i++) { - Vector2 prev = env->track.controls[(i - 1 + env->num_points) % env->num_points].position; - Vector2 curr = env->track.controls[i].position; - Vector2 next = env->track.controls[(i + 1) % env->num_points].position; - - float vx1 = prev.x - curr.x; - float vy1 = prev.y - curr.y; - float vx2 = next.x - curr.x; - float vy2 = next.y - curr.y; - - Vector2 to_prev = {vx1, vy1}; - Vector2 to_next = {vx2, vy2}; - - float cross = to_prev.x * to_next.y - to_prev.y * to_next.x; - float dot = vx1 * vx2 + vy1 * vy2; - - float mag1 = sqrtf(vx1 * vx1 + vy1 * vy1); - float mag2 = sqrtf(vx2 * vx2 + vy2 * vy2); - - if (mag1 < 1e-3f || mag2 < 1e-3f) continue; - - float angle_cos = dot / (mag1 * mag2); - - if (angle_cos > -0.8f) { - int apex_idx = i * env->bezier_resolution; - - Vector2* edge_points = (cross > 0) ? env->track.inner_edge : env->track.outer_edge; - - for (int j = 0; j < 4; j++) { - int idx = (apex_idx - 1 + j + env->track.total_points) % env->track.total_points; // -2? todo - env->track.curbs[env->track.curb_count][j] = edge_points[idx]; - } - env->track.curb_count++; - } - } -} - -void GenerateRandomTrack(WhiskerRacer* env) { - GenerateRandomControlPoints(env); - GenerateTrackCenterline(env); - GenerateTrackEdges(env); - GenerateCurbs(env); -} - -void TopDownTexture(WhiskerRacer* env, RenderTexture2D* mode7RenderTexture, Vector2* center_points) { - if (env->texture_initialized == 0) { - *mode7RenderTexture = LoadRenderTexture(env->width, env->height); - - BeginTextureMode(*mode7RenderTexture); - ClearBackground(DARKGREEN); - SetConfigFlags(FLAG_MSAA_4X_HINT); - ClearBackground(DARKGREEN); - DrawSplineBasis(center_points, env->track.total_points + 3, env->track_width, BLACK); - //DrawSplineBasis(center_points, env->track.total_points + 3, 2, WHITE); - - for (int i = 0; i < env->track.curb_count; i++) { - Vector2 curb_points[4]; - for (int j = 0; j < 4; j++) { - curb_points[j] = env->track.curbs[i][j]; - curb_points[j].y = env->height - curb_points[j].y; // Flip Y coordinate - } - DrawSplineBasis(curb_points, 4, 5.0f, RED); // 5 pixel wide red curbs - } - - EndTextureMode(); - env->texture_initialized = 1; - } -} - -void Mode7(WhiskerRacer* env, RenderTexture2D mode7RenderTexture) { - BeginDrawing(); - ClearBackground(SKYBLUE); - - Texture2D scene = mode7RenderTexture.texture; - float camX = env->px; - float camY = env->py; - float camAngle = env->ang; - - Image sceneImage = LoadImageFromTexture(scene); - Color* pixels = LoadImageColors(sceneImage); - - int height = env->height; - int width = env->width; - float inv_width = env->inv_width; - - int horizon = height / 2; - - float cos_ang = cosf(camAngle); - float sin_ang = sinf(camAngle); - - float cos_ang2 = -2.0f * cos_ang; - float sin_ang2 = 2.0f * sin_ang; - - float height9 = 9.0f * height; - - for (int screenY = horizon; screenY < height; screenY += 3) { - float row = (float)(screenY - horizon); - - if (row < 0.0001) row = 0.0001; - float z = height9 / row; - - float dx = -sin_ang * z; - float dy = cos_ang * z; - - float sx = camX + dy + dx; - float sy = camY - dx + dy; - - dx = (sin_ang2 * z) * inv_width * 3.0f; - dy = (cos_ang2 * z) * inv_width * 3.0f; - - for (int screenX = 0; screenX < width; screenX += 3) { - int srcX = (int)sx; - int srcY = (int)sy; - - if (srcX >= 0 && srcX < width && - srcY >= 0 && srcY < height) { - Color color = pixels[srcY * width + srcX]; - DrawRectangle(screenX, screenY, 3, 3, color); - } - - sx += dx; - sy += dy; - } - } - UnloadImageColors(pixels); - UnloadImage(sceneImage); - - EndDrawing(); -} - -void Draw(WhiskerRacer* env, Vector2* center_points) { - BeginDrawing(); - SetConfigFlags(FLAG_MSAA_4X_HINT); - ClearBackground(DARKGREEN); - DrawSplineBasis(center_points, env->track.total_points + 3, env->track_width, BLACK); - //DrawSplineBasis(center_points, env->track.total_points + 3, 2, WHITE); - for (int i = 0; i < env->track.curb_count; i++) { - Vector2 curb_points[4]; - for (int j = 0; j < 4; j++) { - curb_points[j] = env->track.curbs[i][j]; - curb_points[j].y = env->height - curb_points[j].y; // Flip Y coordinate - } - DrawSplineBasis(curb_points, 4, 5.0f, RED); // 5 pixel wide red curbs - } - - float puffer_width = 48.0f; - float puffer_height = 48.0f; - float puffer_x = env->px; - float puffer_y = env->height - env->py; - Vector2 origin = {puffer_width / 2.0f, puffer_height / 2.0f}; - - DrawTexturePro( - env->puffer, - (Rectangle){0, 0, 128, 128}, - (Rectangle){puffer_x, puffer_y, puffer_width, puffer_height}, - origin, - (-env->ang * 180.0f / PI) - 10, - (Color){255, 255, 255, 255} - ); - - EndDrawing(); -} - -void c_render(WhiskerRacer* env) { - - static RenderTexture2D mode7RenderTexture; - - env->render = 1; - if (env->client == NULL) { - env->client = make_client(env); - } - - if (IsKeyDown(KEY_ESCAPE)) { - exit(0); - } - if (IsKeyPressed(KEY_TAB)) { - ToggleFullscreen(); - } - - if (IsKeyDown(KEY_M)) { - if (env->mode7 == 1) { - env->mode7 = 0; - } - else { - env->mode7 = 1; - } - } - - if (env->render_many) - { - env->method = rand() % 3; - GenerateRandomTrack(env); - } - - Vector2* center_points = malloc(sizeof(Vector2) * (env->track.total_points + 3)); - //center_points[0] = (Vector2){SCREEN_WIDTH*0.5f, SCREEN_HEIGHT*0.5f}; - for (int i = 0; i < env->track.total_points; i++) { - center_points[i] = env->track.centerline[i]; - center_points[i].y = env->height - center_points[i].y; - } - - // Without enough overlap it draws a C rather than an O - center_points[env->track.total_points] = center_points[0]; - center_points[env->track.total_points + 1] = center_points[1]; - center_points[env->track.total_points + 2] = center_points[2]; - - if (env->mode7 == 1) { - TopDownTexture(env, &mode7RenderTexture, center_points); - Mode7(env, mode7RenderTexture); - } - else { - Draw(env, center_points); - } - - free(center_points); -} - -void init(WhiskerRacer* env) { - env->tick = 0; - - env->debug = 0; - - env->inv_width = 1.0f / env->width; - env->inv_height = 1.0f / env->height; - env->inv_maxv = 1.0f / env->maxv; - env->inv_pi2 = 1.0f / PI2; - env->inv_bezier_res = 1.0f / env->bezier_resolution; - - env->flw_ang = -env->w_ang; - env->frw_ang = env->w_ang; - - env->texture_initialized = 0; - - srand(env->rng + env->i); - - GenerateRandomTrack(env); -} - -void allocate(WhiskerRacer* env) { - init(env); - env->observations = (float*)calloc(3, sizeof(float)); - env->actions = (float*)calloc(1, sizeof(float)); - env->rewards = (float*)calloc(1, sizeof(float)); - env->terminals = (unsigned char*)calloc(1, sizeof(unsigned char)); -} - -void step_frame(WhiskerRacer* env, float action) { - float act = 0.0; - - if (action == LEFT) { - act = -1.0; - env->ang += PI / env->turn_pi_frac; - } else if (action == RIGHT) { - act = 1.0; - env->ang -= PI / env->turn_pi_frac; - } - if (env->ang > PI2) { - env->ang -= PI2; - } - else if (env->ang < 0) { - env->ang += PI2; - } - if (env->continuous){ - act = action; - } - //env->whisker_dirs[0] = (Vector2){cosf(env->ang + env->llw_ang), sinf(env->ang + env->llw_ang)}; // left-left - //env->whisker_dirs[1] = (Vector2){cosf(env->ang + env->flw_ang), sinf(env->ang + env->flw_ang)}; // front-left - //env->whisker_dirs[2] = (Vector2){cosf(env->ang), sinf(env->ang)}; // front-forward - //env->whisker_dirs[3] = (Vector2){cosf(env->ang + env->frw_ang), sinf(env->ang + env->frw_ang)}; // front-right - //env->whisker_dirs[4] = (Vector2){cosf(env->ang + env->rrw_ang), sinf(env->ang + env->rrw_ang)}; // right- - env->whisker_dirs[0] = (Vector2){cosf(env->ang + env->flw_ang), sinf(env->ang + env->flw_ang)}; - env->whisker_dirs[1] = (Vector2){cosf(env->ang + env->frw_ang), sinf(env->ang + env->frw_ang)}; - - env->vx = env->v * cosf(env->ang); - env->vy = env->v * sinf(env->ang); - env->px = env->px + env->vx; - env->py = env->py + env->vy; - if (env->px < 0) env->px = 0; - else if (env->px > env->width) env->px = env->width; - if (env->py < 0) env->py = 0; - else if (env->py > env->height) env->py = env->height; - - calc_whisker_lengths(env); - - update_radial_progress(env); -} - -void c_step(WhiskerRacer* env) { - env->terminals[0] = 0; - env->rewards[0] = 0.0; - - float action = env->actions[0]; - for (int i = 0; i < env->frameskip; i++) { - env->tick += 1; - step_frame(env, action); - } - compute_observations(env); -} diff --git a/pufferlib/ocean/whisker_racer/whisker_racer.py b/pufferlib/ocean/whisker_racer/whisker_racer.py deleted file mode 100644 index 5430ff3fe..000000000 --- a/pufferlib/ocean/whisker_racer/whisker_racer.py +++ /dev/null @@ -1,111 +0,0 @@ -import numpy as np -import gymnasium -import time - -import pufferlib -from pufferlib.ocean.whisker_racer import binding - -class WhiskerRacer(pufferlib.PufferEnv): - def __init__(self, num_envs=1, render_mode=None, - frameskip=4, width=1080, height=720, - llw_ang=-3.14/4, flw_ang=-3.14/6, - frw_ang=3.14/6, rrw_ang=3.14/4, - max_whisker_length=100, - turn_pi_frac=20, - maxv=5, render=0, - continuous=False, log_interval=128, - reward_yellow=0.25, reward_green=0.0, gamma=0.9, track_width=50, - num_radial_sectors=16, num_points=4, bezier_resolution=16, w_ang=0.523, - corner_thresh=0.5, ftmp1=0.1, ftmp2=0.1, ftmp3=0.1, ftmp4=0.1, - mode7=0, render_many=0, seed=42, - buf=None, rng=42, i=1, method=0): - self.single_observation_space = gymnasium.spaces.Box(low=0, high=1, - shape=(3,), dtype=np.float32) - self.render_mode = render_mode - self.num_agents = num_envs - self.continuous = continuous - self.log_interval = log_interval - self.tick = 0 - - if continuous: - self.single_action_space = gymnasium.spaces.Box(low=-1, high=1, shape=(1,), dtype=np.float32) - else: - self.single_action_space = gymnasium.spaces.Discrete(3) - - super().__init__(buf) - - if continuous: - self.actions = self.actions.flatten() - else: - self.actions = self.actions.astype(np.float32) - - c_envs = [] - for i in range(num_envs): - env_id = binding.env_init( - self.observations[i:i+1], - self.actions[i:i+1], - self.rewards[i:i+1], - self.terminals[i:i+1], - self.truncations[i:i+1], - seed, num_envs=num_envs, seed=seed, frameskip=frameskip, width=width, height=height, - llw_ang=llw_ang, flw_ang=flw_ang, frw_ang=frw_ang, rrw_ang=rrw_ang, max_whisker_length=max_whisker_length, - turn_pi_frac=turn_pi_frac, maxv=maxv, render=render, continuous=continuous, - reward_yellow=reward_yellow, reward_green=reward_green, gamma=gamma, track_width=track_width, - num_radial_sectors=num_radial_sectors, num_points=num_points, bezier_resolution=bezier_resolution, w_ang=w_ang, - corner_thresh=corner_thresh, ftmp1=ftmp1,ftmp2=ftmp2,ftmp3=ftmp3,ftmp4=ftmp4, - mode7=mode7, render_many=render_many, rng=rng+i, i=i, method=method - ) - c_envs.append(env_id) - self.c_envs = binding.vectorize(*c_envs) - - def reset(self, seed=0): - binding.vec_reset(self.c_envs, seed) - self.tick = 0 - return self.observations, [] - - def step(self, actions): - #start = time.time() - if self.continuous: - self.actions[:] = np.clip(actions.flatten(), -1.0, 1.0) - else: - self.actions[:] = actions - - self.tick += 1 - binding.vec_step(self.c_envs) - - info = [] - if self.tick % self.log_interval == 0: - info.append(binding.vec_log(self.c_envs)) - #end = time.time() - #print(f"python step took {end - start:.3e} seconds") - return (self.observations, self.rewards, - self.terminals, self.truncations, info) - - def render(self): - binding.vec_render(self.c_envs, 0) - - def close(self): - binding.vec_close(self.c_envs) - -def test_performance(timeout=10, atn_cache=1024): - print("test_performance in whisker_racer.py") - env = WhiskerRacer(num_envs=1) - env.reset() - tick = 0 - - actions = np.random.randint(0, 2, (atn_cache, env.num_agents)) - - import time - start = time.time() - while time.time() - start < timeout: - print("atn = actions[tick % atn_cache] in whisker_racer.py") - atn = actions[tick % atn_cache] - print("env.step in whisker_racer.py") - env.step(atn) - tick += 1 - - print(f'SPS: %f', env.num_agents * tick / (time.time() - start)) - -if __name__ == '__main__': - print("whisker_racer.py") - test_performance() diff --git a/pufferlib/pufferl.py b/pufferlib/pufferl.py index 92686a701..fb2a7fbcd 100644 --- a/pufferlib/pufferl.py +++ b/pufferlib/pufferl.py @@ -4,7 +4,8 @@ import contextlib import warnings -warnings.filterwarnings('error', category=RuntimeWarning) + +warnings.filterwarnings("error", category=RuntimeWarning) import os import sys @@ -13,6 +14,7 @@ import time import random import shutil +import subprocess import argparse import importlib import configparser @@ -31,36 +33,43 @@ import pufferlib.sweep import pufferlib.vector import pufferlib.pytorch +import pufferlib.utils + try: from pufferlib import _C except ImportError: - raise ImportError('Failed to import C/CUDA advantage kernel. If you have non-default PyTorch, try installing with --no-build-isolation') + raise ImportError( + "Failed to import C/CUDA advantage kernel. If you have non-default PyTorch, try installing with --no-build-isolation" + ) import rich import rich.traceback from rich.table import Table from rich.console import Console from rich_argparse import RichHelpFormatter + rich.traceback.install(show_locals=False) -import signal # Aggressively exit on ctrl+c +import signal # Aggressively exit on ctrl+c + signal.signal(signal.SIGINT, lambda sig, frame: os._exit(0)) # Assume advantage kernel has been built if CUDA compiler is available ADVANTAGE_CUDA = shutil.which("nvcc") is not None + class PuffeRL: def __init__(self, config, vecenv, policy, logger=None): # Backend perf optimization - torch.set_float32_matmul_precision('high') - torch.backends.cudnn.deterministic = config['torch_deterministic'] + torch.set_float32_matmul_precision("high") + torch.backends.cudnn.deterministic = config["torch_deterministic"] torch.backends.cudnn.benchmark = True # Reproducibility - seed = config['seed'] - #random.seed(seed) - #np.random.seed(seed) - #torch.manual_seed(seed) + seed = config["seed"] + # random.seed(seed) + # np.random.seed(seed) + # torch.manual_seed(seed) # Vecenv info vecenv.async_reset(seed) @@ -70,29 +79,36 @@ def __init__(self, config, vecenv, policy, logger=None): self.total_agents = total_agents # Experience - if config['batch_size'] == 'auto' and config['bptt_horizon'] == 'auto': - raise pufferlib.APIUsageError('Must specify batch_size or bptt_horizon') - elif config['batch_size'] == 'auto': - config['batch_size'] = total_agents * config['bptt_horizon'] - elif config['bptt_horizon'] == 'auto': - config['bptt_horizon'] = config['batch_size'] // total_agents - - batch_size = config['batch_size'] - horizon = config['bptt_horizon'] + if config["batch_size"] == "auto" and config["bptt_horizon"] == "auto": + raise pufferlib.APIUsageError("Must specify batch_size or bptt_horizon") + elif config["batch_size"] == "auto": + config["batch_size"] = total_agents * config["bptt_horizon"] + elif config["bptt_horizon"] == "auto": + config["bptt_horizon"] = config["batch_size"] // total_agents + + batch_size = config["batch_size"] + horizon = config["bptt_horizon"] segments = batch_size // horizon self.segments = segments if total_agents > segments: - raise pufferlib.APIUsageError( - f'Total agents {total_agents} <= segments {segments}' - ) + raise pufferlib.APIUsageError(f"Total agents {total_agents} <= segments {segments}") - device = config['device'] - self.observations = torch.zeros(segments, horizon, *obs_space.shape, + device = config["device"] + self.observations = torch.zeros( + segments, + horizon, + *obs_space.shape, dtype=pufferlib.pytorch.numpy_to_torch_dtype_dict[obs_space.dtype], - pin_memory=device == 'cuda' and config['cpu_offload'], - device='cpu' if config['cpu_offload'] else device) - self.actions = torch.zeros(segments, horizon, *atn_space.shape, device=device, - dtype=pufferlib.pytorch.numpy_to_torch_dtype_dict[atn_space.dtype]) + pin_memory=device == "cuda" and config["cpu_offload"], + device="cpu" if config["cpu_offload"] else device, + ) + self.actions = torch.zeros( + segments, + horizon, + *atn_space.shape, + device=device, + dtype=pufferlib.pytorch.numpy_to_torch_dtype_dict[atn_space.dtype], + ) self.values = torch.zeros(segments, horizon, device=device) self.logprobs = torch.zeros(segments, horizon, device=device) self.rewards = torch.zeros(segments, horizon, device=device) @@ -103,64 +119,72 @@ def __init__(self, config, vecenv, policy, logger=None): self.ep_lengths = torch.zeros(total_agents, device=device, dtype=torch.int32) self.ep_indices = torch.arange(total_agents, device=device, dtype=torch.int32) self.free_idx = total_agents + self.render = config["render"] + self.render_interval = config["render_interval"] + + if self.render: + ensure_drive_binary() # LSTM - if config['use_rnn']: + if config["use_rnn"]: n = vecenv.agents_per_batch h = policy.hidden_size - self.lstm_h = {i*n: torch.zeros(n, h, device=device) for i in range(total_agents//n)} - self.lstm_c = {i*n: torch.zeros(n, h, device=device) for i in range(total_agents//n)} + self.lstm_h = {i * n: torch.zeros(n, h, device=device) for i in range(total_agents // n)} + self.lstm_c = {i * n: torch.zeros(n, h, device=device) for i in range(total_agents // n)} # Minibatching & gradient accumulation - minibatch_size = config['minibatch_size'] - max_minibatch_size = config['max_minibatch_size'] + minibatch_size = config["minibatch_size"] + max_minibatch_size = config["max_minibatch_size"] self.minibatch_size = min(minibatch_size, max_minibatch_size) if minibatch_size > max_minibatch_size and minibatch_size % max_minibatch_size != 0: raise pufferlib.APIUsageError( - f'minibatch_size {minibatch_size} > max_minibatch_size {max_minibatch_size} must divide evenly') + f"minibatch_size {minibatch_size} > max_minibatch_size {max_minibatch_size} must divide evenly" + ) if batch_size < minibatch_size: - raise pufferlib.APIUsageError( - f'batch_size {batch_size} must be >= minibatch_size {minibatch_size}' - ) + raise pufferlib.APIUsageError(f"batch_size {batch_size} must be >= minibatch_size {minibatch_size}") self.accumulate_minibatches = max(1, minibatch_size // max_minibatch_size) - self.total_minibatches = int(config['update_epochs'] * batch_size / self.minibatch_size) - self.minibatch_segments = self.minibatch_size // horizon + self.total_minibatches = int(config["update_epochs"] * batch_size / self.minibatch_size) + self.minibatch_segments = self.minibatch_size // horizon if self.minibatch_segments * horizon != self.minibatch_size: raise pufferlib.APIUsageError( - f'minibatch_size {self.minibatch_size} must be divisible by bptt_horizon {horizon}' + f"minibatch_size {self.minibatch_size} must be divisible by bptt_horizon {horizon}" ) # Torch compile self.uncompiled_policy = policy self.policy = policy - if config['compile']: - self.policy = torch.compile(policy, mode=config['compile_mode']) - self.policy.forward_eval = torch.compile(policy, mode=config['compile_mode']) - pufferlib.pytorch.sample_logits = torch.compile(pufferlib.pytorch.sample_logits, mode=config['compile_mode']) + if config["compile"]: + self.policy = torch.compile(policy, mode=config["compile_mode"]) + self.policy.forward_eval = torch.compile(policy, mode=config["compile_mode"]) + pufferlib.pytorch.sample_logits = torch.compile( + pufferlib.pytorch.sample_logits, mode=config["compile_mode"] + ) # Optimizer - if config['optimizer'] == 'adam': + if config["optimizer"] == "adam": optimizer = torch.optim.Adam( self.policy.parameters(), - lr=config['learning_rate'], - betas=(config['adam_beta1'], config['adam_beta2']), - eps=config['adam_eps'], + lr=config["learning_rate"], + betas=(config["adam_beta1"], config["adam_beta2"]), + eps=config["adam_eps"], ) - elif config['optimizer'] == 'muon': + elif config["optimizer"] == "muon": from heavyball import ForeachMuon - warnings.filterwarnings(action='ignore', category=UserWarning, module=r'heavyball.*') + + warnings.filterwarnings(action="ignore", category=UserWarning, module=r"heavyball.*") import heavyball.utils - heavyball.utils.compile_mode = config['compile_mode'] if config['compile'] else None + + heavyball.utils.compile_mode = config["compile_mode"] if config["compile"] else None optimizer = ForeachMuon( self.policy.parameters(), - lr=config['learning_rate'], - betas=(config['adam_beta1'], config['adam_beta2']), - eps=config['adam_eps'], + lr=config["learning_rate"], + betas=(config["adam_beta1"], config["adam_beta2"]), + eps=config["adam_eps"], ) else: - raise ValueError(f'Unknown optimizer: {config["optimizer"]}') + raise ValueError(f"Unknown optimizer: {config['optimizer']}") self.optimizer = optimizer @@ -170,27 +194,23 @@ def __init__(self, config, vecenv, policy, logger=None): self.logger = NoLogger(config) # Learning rate scheduler - epochs = config['total_timesteps'] // config['batch_size'] + epochs = config["total_timesteps"] // config["batch_size"] self.scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs) self.total_epochs = epochs # Automatic mixed precision - precision = config['precision'] + precision = config["precision"] self.amp_context = contextlib.nullcontext() - if config.get('amp', True) and config['device'] == 'cuda': - self.amp_context = torch.amp.autocast(device_type='cuda', dtype=getattr(torch, precision)) - if precision not in ('float32', 'bfloat16'): - raise pufferlib.APIUsageError(f'Invalid precision: {precision}: use float32 or bfloat16') + if config.get("amp", True) and config["device"] == "cuda": + self.amp_context = torch.amp.autocast(device_type="cuda", dtype=getattr(torch, precision)) + if precision not in ("float32", "bfloat16"): + raise pufferlib.APIUsageError(f"Invalid precision: {precision}: use float32 or bfloat16") # Initializations self.config = config self.vecenv = vecenv self.epoch = 0 self.global_step = 0 - self.env_sps = 0 # Environment-only SPS tracking - self.eval_sps = 0 # Evaluate, examples generation tracking - self.env_time_elapsed = 0 - self.eval_forward_time_elapsed = 0 self.last_log_step = 0 self.last_log_time = time.time() self.start_time = time.time() @@ -218,37 +238,35 @@ def sps(self): def evaluate(self): profile = self.profile epoch = self.epoch - profile('eval', epoch) - profile('eval_misc', epoch, nest=True) + profile("eval", epoch) + profile("eval_misc", epoch, nest=True) config = self.config - device = config['device'] + device = config["device"] - if config['use_rnn']: + if config["use_rnn"]: for k in self.lstm_h: self.lstm_h[k] = torch.zeros(self.lstm_h[k].shape, device=device) self.lstm_c[k] = torch.zeros(self.lstm_c[k].shape, device=device) - # Track environment-only SPS - eval_start_time = time.time() self.full_rows = 0 while self.full_rows < self.segments: - profile('env', epoch) + profile("env", epoch) o, r, d, t, info, env_id, mask = self.vecenv.recv() - - profile('eval_misc', epoch) + + profile("eval_misc", epoch) env_id = slice(env_id[0], env_id[-1] + 1) - done_mask = d + t # TODO: Handle truncations separately + + done_mask = d + t # TODO: Handle truncations separately self.global_step += int(mask.sum()) - profile('eval_copy', epoch) + profile("eval_copy", epoch) o = torch.as_tensor(o) - o_device = o.to(device)#, non_blocking=True) - r = torch.as_tensor(r).to(device)#, non_blocking=True) - d = torch.as_tensor(d).to(device)#, non_blocking=True) + o_device = o.to(device) # , non_blocking=True) + r = torch.as_tensor(r).to(device) # , non_blocking=True) + d = torch.as_tensor(d).to(device) # , non_blocking=True) - profile('eval_forward', epoch) - forward_start = time.time() + profile("eval_forward", epoch) with torch.no_grad(), self.amp_context: state = dict( reward=r, @@ -257,26 +275,25 @@ def evaluate(self): mask=mask, ) - if config['use_rnn']: - state['lstm_h'] = self.lstm_h[env_id.start] - state['lstm_c'] = self.lstm_c[env_id.start] + if config["use_rnn"]: + state["lstm_h"] = self.lstm_h[env_id.start] + state["lstm_c"] = self.lstm_c[env_id.start] logits, value = self.policy.forward_eval(o_device, state) action, logprob, _ = pufferlib.pytorch.sample_logits(logits) r = torch.clamp(r, -1, 1) - self.eval_forward_time_elapsed += time.time() - forward_start - profile('eval_copy', epoch) + profile("eval_copy", epoch) with torch.no_grad(): - if config['use_rnn']: - self.lstm_h[env_id.start] = state['lstm_h'] - self.lstm_c[env_id.start] = state['lstm_c'] + if config["use_rnn"]: + self.lstm_h[env_id.start] = state["lstm_h"] + self.lstm_c[env_id.start] = state["lstm_c"] # Fast path for fully vectorized envs l = self.ep_lengths[env_id.start].item() - batch_rows = slice(self.ep_indices[env_id.start].item(), 1+self.ep_indices[env_id.stop - 1].item()) + batch_rows = slice(self.ep_indices[env_id.start].item(), 1 + self.ep_indices[env_id.stop - 1].item()) - if config['cpu_offload']: + if config["cpu_offload"]: self.observations[batch_rows, l] = o else: self.observations[batch_rows, l] = o_device @@ -289,9 +306,9 @@ def evaluate(self): # Note: We are not yet handling masks in this version self.ep_lengths[env_id] += 1 - if l+1 >= config['bptt_horizon']: + if l + 1 >= config["bptt_horizon"]: num_full = env_id.stop - env_id.start - self.ep_indices[env_id] = self.free_idx + torch.arange(num_full, device=config['device']).int() + self.ep_indices[env_id] = self.free_idx + torch.arange(num_full, device=config["device"]).int() self.ep_lengths[env_id] = 0 self.free_idx += num_full self.full_rows += num_full @@ -300,7 +317,7 @@ def evaluate(self): if isinstance(logits, torch.distributions.Normal): action = np.clip(action, self.vecenv.action_space.low, self.vecenv.action_space.high) - profile('eval_misc', epoch) + profile("eval_misc", epoch) for i in info: for k, v in pufferlib.unroll_nested_dict(i): if isinstance(v, np.ndarray): @@ -310,25 +327,13 @@ def evaluate(self): else: self.stats[k].append(v) - profile('env', epoch) + profile("env", epoch) self.vecenv.send(action) - profile('eval_misc', epoch) + profile("eval_misc", epoch) self.free_idx = self.total_agents self.ep_indices = torch.arange(self.total_agents, device=device, dtype=torch.int32) self.ep_lengths.zero_() - - # Calculate environment-only SPS - eval_time_elapsed = time.time() - eval_start_time - self.env_time_elapsed += eval_time_elapsed - env_steps_collected = self.global_step - - if self.env_time_elapsed > 0: - self.eval_sps = env_steps_collected / self.env_time_elapsed - self.env_sps = env_steps_collected / (self.env_time_elapsed - self.eval_forward_time_elapsed) - else: - self.eval_sps = 0 - self.env_sps = 0 profile.end() return self.stats @@ -336,34 +341,42 @@ def evaluate(self): def train(self): profile = self.profile epoch = self.epoch - profile('train', epoch) + profile("train", epoch) losses = defaultdict(float) config = self.config - device = config['device'] + device = config["device"] - b0 = config['prio_beta0'] - a = config['prio_alpha'] - clip_coef = config['clip_coef'] - vf_clip = config['vf_clip_coef'] - anneal_beta = b0 + (1 - b0)*a*self.epoch/self.total_epochs + b0 = config["prio_beta0"] + a = config["prio_alpha"] + clip_coef = config["clip_coef"] + vf_clip = config["vf_clip_coef"] + anneal_beta = b0 + (1 - b0) * a * self.epoch / self.total_epochs self.ratio[:] = 1 for mb in range(self.total_minibatches): - profile('train_misc', epoch, nest=True) + profile("train_misc", epoch, nest=True) self.amp_context.__enter__() shape = self.values.shape advantages = torch.zeros(shape, device=device) - advantages = compute_puff_advantage(self.values, self.rewards, - self.terminals, self.ratio, advantages, config['gamma'], - config['gae_lambda'], config['vtrace_rho_clip'], config['vtrace_c_clip']) + advantages = compute_puff_advantage( + self.values, + self.rewards, + self.terminals, + self.ratio, + advantages, + config["gamma"], + config["gae_lambda"], + config["vtrace_rho_clip"], + config["vtrace_c_clip"], + ) - profile('train_copy', epoch) + profile("train_copy", epoch) adv = advantages.abs().sum(axis=1) prio_weights = torch.nan_to_num(adv**a, 0, 0, 0) - prio_probs = (prio_weights + 1e-6)/(prio_weights.sum() + 1e-6) + prio_probs = (prio_weights + 1e-6) / (prio_weights.sum() + 1e-6) idx = torch.multinomial(prio_probs, self.minibatch_segments) - mb_prio = (self.segments*prio_probs[idx, None])**-anneal_beta + mb_prio = (self.segments * prio_probs[idx, None]) ** -anneal_beta mb_obs = self.observations[idx] mb_actions = self.actions[idx] mb_logprobs = self.logprobs[idx] @@ -375,8 +388,8 @@ def train(self): mb_returns = advantages[idx] + mb_values mb_advantages = advantages[idx] - profile('train_forward', epoch) - if not config['use_rnn']: + profile("train_forward", epoch) + if not config["use_rnn"]: mb_obs = mb_obs.reshape(-1, *self.vecenv.single_observation_space.shape) state = dict( @@ -388,7 +401,7 @@ def train(self): logits, newvalue = self.policy(mb_obs, state) actions, newlogprob, entropy = pufferlib.pytorch.sample_logits(logits, action=mb_actions) - profile('train_misc', epoch) + profile("train_misc", epoch) newlogprob = newlogprob.reshape(mb_logprobs.shape) logratio = newlogprob - mb_logprobs ratio = logratio.exp() @@ -397,12 +410,20 @@ def train(self): with torch.no_grad(): old_approx_kl = (-logratio).mean() approx_kl = ((ratio - 1) - logratio).mean() - clipfrac = ((ratio - 1.0).abs() > config['clip_coef']).float().mean() + clipfrac = ((ratio - 1.0).abs() > config["clip_coef"]).float().mean() adv = advantages[idx] - adv = compute_puff_advantage(mb_values, mb_rewards, mb_terminals, - ratio, adv, config['gamma'], config['gae_lambda'], - config['vtrace_rho_clip'], config['vtrace_c_clip']) + adv = compute_puff_advantage( + mb_values, + mb_rewards, + mb_terminals, + ratio, + adv, + config["gamma"], + config["gae_lambda"], + config["vtrace_rho_clip"], + config["vtrace_c_clip"], + ) adv = mb_advantages adv = mb_prio * (adv - adv.mean()) / (adv.std() + 1e-8) @@ -415,49 +436,49 @@ def train(self): v_clipped = mb_values + torch.clamp(newvalue - mb_values, -vf_clip, vf_clip) v_loss_unclipped = (newvalue - mb_returns) ** 2 v_loss_clipped = (v_clipped - mb_returns) ** 2 - v_loss = 0.5*torch.max(v_loss_unclipped, v_loss_clipped).mean() + v_loss = 0.5 * torch.max(v_loss_unclipped, v_loss_clipped).mean() entropy_loss = entropy.mean() - loss = pg_loss + config['vf_coef']*v_loss - config['ent_coef']*entropy_loss - self.amp_context.__enter__() # TODO: AMP needs some debugging + loss = pg_loss + config["vf_coef"] * v_loss - config["ent_coef"] * entropy_loss + self.amp_context.__enter__() # TODO: AMP needs some debugging # This breaks vloss clipping? self.values[idx] = newvalue.detach().float() # Logging - profile('train_misc', epoch) - losses['policy_loss'] += pg_loss.item() / self.total_minibatches - losses['value_loss'] += v_loss.item() / self.total_minibatches - losses['entropy'] += entropy_loss.item() / self.total_minibatches - losses['old_approx_kl'] += old_approx_kl.item() / self.total_minibatches - losses['approx_kl'] += approx_kl.item() / self.total_minibatches - losses['clipfrac'] += clipfrac.item() / self.total_minibatches - losses['importance'] += ratio.mean().item() / self.total_minibatches + profile("train_misc", epoch) + losses["policy_loss"] += pg_loss.item() / self.total_minibatches + losses["value_loss"] += v_loss.item() / self.total_minibatches + losses["entropy"] += entropy_loss.item() / self.total_minibatches + losses["old_approx_kl"] += old_approx_kl.item() / self.total_minibatches + losses["approx_kl"] += approx_kl.item() / self.total_minibatches + losses["clipfrac"] += clipfrac.item() / self.total_minibatches + losses["importance"] += ratio.mean().item() / self.total_minibatches # Learn on accumulated minibatches - profile('learn', epoch) + profile("learn", epoch) loss.backward() if (mb + 1) % self.accumulate_minibatches == 0: - torch.nn.utils.clip_grad_norm_(self.policy.parameters(), config['max_grad_norm']) + torch.nn.utils.clip_grad_norm_(self.policy.parameters(), config["max_grad_norm"]) self.optimizer.step() self.optimizer.zero_grad() # Reprioritize experience - profile('train_misc', epoch) - if config['anneal_lr']: + profile("train_misc", epoch) + if config["anneal_lr"]: self.scheduler.step() y_pred = self.values.flatten() y_true = advantages.flatten() + self.values.flatten() var_y = y_true.var() explained_var = torch.nan if var_y == 0 else 1 - (y_true - y_pred).var() / var_y - losses['explained_variance'] = explained_var.item() + losses["explained_variance"] = explained_var.item() profile.end() logs = None self.epoch += 1 - done_training = dist_sum(self.global_step, device) >= config['total_timesteps'] + done_training = self.global_step >= config["total_timesteps"] if done_training or self.global_step == 0 or time.time() > self.last_log_time + 0.25: logs = self.mean_and_log() self.losses = losses @@ -467,11 +488,45 @@ def train(self): self.last_log_step = self.global_step profile.clear() - if self.epoch % config['checkpoint_interval'] == 0 or done_training: + if self.epoch % config["checkpoint_interval"] == 0 or done_training: self.save_checkpoint() - self.msg = f'Checkpoint saved at update {self.epoch}' - - return logs + self.msg = f"Checkpoint saved at update {self.epoch}" + + if self.render and self.epoch % self.render_interval == 0: + model_dir = os.path.join(self.config["data_dir"], f"{self.config['env']}_{self.logger.run_id}") + model_files = glob.glob(os.path.join(model_dir, "model_*.pt")) + + if model_files: + # Take the latest checkpoint + latest_cpt = max(model_files, key=os.path.getctime) + bin_path = f"{model_dir}.bin" + + # Export to .bin for rendering with raylib + try: + export_args = {"env_name": self.config["env"], "load_model_path": latest_cpt, **self.config} + + export( + args=export_args, + env_name=self.config["env"], + vecenv=self.vecenv, + policy=self.uncompiled_policy, + path=bin_path, + silent=True, + ) + pufferlib.utils.render_videos(self.config, self.vecenv, self.logger, self.global_step, bin_path) + + except Exception as e: + print(f"Failed to export model weights: {e}") + + if self.config["eval"]["wosac_realism_eval"] and ( + self.epoch % self.config["eval"]["eval_interval"] == 0 or done_training + ): + pufferlib.utils.run_wosac_eval_in_subprocess(self.config, self.logger, self.global_step) + + if self.config["eval"]["human_replay_eval"] and ( + self.epoch % self.config["eval"]["eval_interval"] == 0 or done_training + ): + pufferlib.utils.run_human_replay_eval_in_subprocess(self.config, self.logger, self.global_step) def mean_and_log(self): config = self.config @@ -484,28 +539,28 @@ def mean_and_log(self): self.stats[k] = v - device = config['device'] + device = config["device"] agent_steps = int(dist_sum(self.global_step, device)) logs = { - 'SPS': dist_sum(self.sps, device), - 'agent_steps': agent_steps, - 'uptime': time.time() - self.start_time, - 'epoch': int(dist_sum(self.epoch, device)), - 'learning_rate': self.optimizer.param_groups[0]["lr"], - **{f'environment/{k}': v for k, v in self.stats.items()}, - **{f'losses/{k}': v for k, v in self.losses.items()}, - **{f'performance/{k}': v['elapsed'] for k, v in self.profile}, - #**{f'environment/{k}': dist_mean(v, device) for k, v in self.stats.items()}, - #**{f'losses/{k}': dist_mean(v, device) for k, v in self.losses.items()}, - #**{f'performance/{k}': dist_sum(v['elapsed'], device) for k, v in self.profile}, + "SPS": dist_sum(self.sps, device), + "agent_steps": agent_steps, + "uptime": time.time() - self.start_time, + "epoch": int(dist_sum(self.epoch, device)), + "learning_rate": self.optimizer.param_groups[0]["lr"], + **{f"environment/{k}": v for k, v in self.stats.items()}, + **{f"losses/{k}": v for k, v in self.losses.items()}, + **{f"performance/{k}": v["elapsed"] for k, v in self.profile}, + # **{f'environment/{k}': dist_mean(v, device) for k, v in self.stats.items()}, + # **{f'losses/{k}': dist_mean(v, device) for k, v in self.losses.items()}, + # **{f'performance/{k}': dist_sum(v['elapsed'], device) for k, v in self.profile}, } if torch.distributed.is_initialized(): - if torch.distributed.get_rank() != 0: - self.logger.log(logs, agent_steps) - return logs - else: - return None + if torch.distributed.get_rank() != 0: + self.logger.log(logs, agent_steps) + return logs + else: + return None self.logger.log(logs, agent_steps) return logs @@ -515,21 +570,21 @@ def close(self): self.utilization.stop() model_path = self.save_checkpoint() run_id = self.logger.run_id - path = os.path.join(self.config['data_dir'], f'{self.config["env"]}_{run_id}.pt') + path = os.path.join(self.config["data_dir"], f"{self.config['env']}_{run_id}.pt") shutil.copy(model_path, path) return path def save_checkpoint(self): if torch.distributed.is_initialized(): - if torch.distributed.get_rank() != 0: - return - + if torch.distributed.get_rank() != 0: + return + run_id = self.logger.run_id - path = os.path.join(self.config['data_dir'], f'{self.config["env"]}_{run_id}') + path = os.path.join(self.config["data_dir"], f"{self.config['env']}_{run_id}") if not os.path.exists(path): os.makedirs(path) - model_name = f'model_{self.config["env"]}_{self.epoch:06d}.pt' + model_name = f"model_{self.config['env']}_{self.epoch:06d}.pt" model_path = os.path.join(path, model_name) if os.path.exists(model_path): return model_path @@ -537,33 +592,29 @@ def save_checkpoint(self): torch.save(self.uncompiled_policy.state_dict(), model_path) state = { - 'optimizer_state_dict': self.optimizer.state_dict(), - 'global_step': self.global_step, - 'agent_step': self.global_step, - 'update': self.epoch, - 'model_name': model_name, - 'run_id': run_id, + "optimizer_state_dict": self.optimizer.state_dict(), + "global_step": self.global_step, + "agent_step": self.global_step, + "update": self.epoch, + "model_name": model_name, + "run_id": run_id, } - state_path = os.path.join(path, 'trainer_state.pt') - torch.save(state, state_path + '.tmp') - os.rename(state_path + '.tmp', state_path) + state_path = os.path.join(path, "trainer_state.pt") + torch.save(state, state_path + ".tmp") + os.rename(state_path + ".tmp", state_path) return model_path - def print_dashboard(self, clear=False, idx=[0], - c1='[cyan]', c2='[white]', b1='[bright_cyan]', b2='[bright_white]'): + def print_dashboard(self, clear=False, idx=[0], c1="[cyan]", c2="[white]", b1="[bright_cyan]", b2="[bright_white]"): config = self.config - sps = dist_sum(self.sps, config['device']) - eval_sps = dist_sum(self.eval_sps, config['device']) - env_sps = dist_sum(self.env_sps, config['device']) - agent_steps = dist_sum(self.global_step, config['device']) + sps = dist_sum(self.sps, config["device"]) + agent_steps = dist_sum(self.global_step, config["device"]) if torch.distributed.is_initialized(): - if torch.distributed.get_rank() != 0: - return - + if torch.distributed.get_rank() != 0: + return + profile = self.profile console = Console() - dashboard = Table(box=rich.box.ROUNDED, expand=True, - show_header=False, border_style='bright_cyan') + dashboard = Table(box=rich.box.ROUNDED, expand=True, show_header=False, border_style="bright_cyan") table = Table(box=None, expand=True, show_header=False) dashboard.add_row(table) @@ -574,52 +625,53 @@ def print_dashboard(self, clear=False, idx=[0], table.add_column(justify="right", width=13) table.add_row( - f'{b1}PufferLib {b2}3.0 {idx[0]*" "}:blowfish:', - f'{c1}CPU: {b2}{np.mean(self.utilization.cpu_util):.1f}{c2}%', - f'{c1}GPU: {b2}{np.mean(self.utilization.gpu_util):.1f}{c2}%', - f'{c1}DRAM: {b2}{np.mean(self.utilization.cpu_mem):.1f}{c2}%', - f'{c1}VRAM: {b2}{np.mean(self.utilization.gpu_mem):.1f}{c2}%', + f"{b1}PufferLib {b2}3.0 {idx[0] * ' '}:blowfish:", + f"{c1}CPU: {b2}{np.mean(self.utilization.cpu_util):.1f}{c2}%", + f"{c1}GPU: {b2}{np.mean(self.utilization.gpu_util):.1f}{c2}%", + f"{c1}DRAM: {b2}{np.mean(self.utilization.cpu_mem):.1f}{c2}%", + f"{c1}VRAM: {b2}{np.mean(self.utilization.gpu_mem):.1f}{c2}%", ) idx[0] = (idx[0] - 1) % 10 - + s = Table(box=None, expand=True) - remaining = 'A hair past a freckle' + remaining = "A hair past a freckle" if sps != 0: - remaining = duration((config['total_timesteps'] - agent_steps)/sps, b2, c2) - - s.add_column(f"{c1}Summary", justify='left', vertical='top', width=10) - s.add_column(f"{c1}Value", justify='right', vertical='top', width=14) - s.add_row(f'{c2}Env', f'{b2}{config["env"]}') - s.add_row(f'{c2}Params', abbreviate(self.model_size, b2, c2)) - s.add_row(f'{c2}Steps', abbreviate(agent_steps, b2, c2)) - s.add_row(f'{c2}SPS', abbreviate(sps, b2, c2)) - s.add_row(f'{c2}Evaluate SPS', abbreviate(eval_sps, b2, c2)) - s.add_row(f'{c2}Env SPS', abbreviate(env_sps, b2, c2)) - s.add_row(f'{c2}Epoch', f'{b2}{self.epoch}') - s.add_row(f'{c2}Uptime', duration(self.uptime, b2, c2)) - s.add_row(f'{c2}Remaining', remaining) - - delta = profile.eval['buffer'] + profile.train['buffer'] + remaining = duration((config["total_timesteps"] - agent_steps) / sps, b2, c2) + + s.add_column(f"{c1}Summary", justify="left", vertical="top", width=10) + s.add_column(f"{c1}Value", justify="right", vertical="top", width=14) + s.add_row(f"{c2}Env", f"{b2}{config['env']}") + s.add_row(f"{c2}Params", abbreviate(self.model_size, b2, c2)) + s.add_row(f"{c2}Steps", abbreviate(agent_steps, b2, c2)) + s.add_row(f"{c2}SPS", abbreviate(sps, b2, c2)) + s.add_row(f"{c2}Epoch", f"{b2}{self.epoch}") + s.add_row(f"{c2}Uptime", duration(self.uptime, b2, c2)) + s.add_row(f"{c2}Remaining", remaining) + + delta = profile.eval["buffer"] + profile.train["buffer"] p = Table(box=None, expand=True, show_header=False) p.add_column(f"{c1}Performance", justify="left", width=10) p.add_column(f"{c1}Time", justify="right", width=8) p.add_column(f"{c1}%", justify="right", width=4) - p.add_row(*fmt_perf('Evaluate', b1, delta, profile.eval, b2, c2)) - p.add_row(*fmt_perf(' Forward', c2, delta, profile.eval_forward, b2, c2)) - p.add_row(*fmt_perf(' Env', c2, delta, profile.env, b2, c2)) - p.add_row(*fmt_perf(' Copy', c2, delta, profile.eval_copy, b2, c2)) - p.add_row(*fmt_perf(' Misc', c2, delta, profile.eval_misc, b2, c2)) - p.add_row(*fmt_perf('Train', b1, delta, profile.train, b2, c2)) - p.add_row(*fmt_perf(' Forward', c2, delta, profile.train_forward, b2, c2)) - p.add_row(*fmt_perf(' Learn', c2, delta, profile.learn, b2, c2)) - p.add_row(*fmt_perf(' Copy', c2, delta, profile.train_copy, b2, c2)) - p.add_row(*fmt_perf(' Misc', c2, delta, profile.train_misc, b2, c2)) - - l = Table(box=None, expand=True, ) - l.add_column(f'{c1}Losses', justify="left", width=16) - l.add_column(f'{c1}Value', justify="right", width=8) + p.add_row(*fmt_perf("Evaluate", b1, delta, profile.eval, b2, c2)) + p.add_row(*fmt_perf(" Forward", c2, delta, profile.eval_forward, b2, c2)) + p.add_row(*fmt_perf(" Env", c2, delta, profile.env, b2, c2)) + p.add_row(*fmt_perf(" Copy", c2, delta, profile.eval_copy, b2, c2)) + p.add_row(*fmt_perf(" Misc", c2, delta, profile.eval_misc, b2, c2)) + p.add_row(*fmt_perf("Train", b1, delta, profile.train, b2, c2)) + p.add_row(*fmt_perf(" Forward", c2, delta, profile.train_forward, b2, c2)) + p.add_row(*fmt_perf(" Learn", c2, delta, profile.learn, b2, c2)) + p.add_row(*fmt_perf(" Copy", c2, delta, profile.train_copy, b2, c2)) + p.add_row(*fmt_perf(" Misc", c2, delta, profile.train_misc, b2, c2)) + + l = Table( + box=None, + expand=True, + ) + l.add_column(f"{c1}Losses", justify="left", width=16) + l.add_column(f"{c1}Value", justify="right", width=8) for metric, value in self.losses.items(): - l.add_row(f'{c2}{metric}', f'{b2}{value:.3f}') + l.add_row(f"{c2}{metric}", f"{b2}{value:.3f}") monitor = Table(box=None, expand=True, pad_edge=False) monitor.add_row(s, p, l) @@ -640,13 +692,13 @@ def print_dashboard(self, clear=False, idx=[0], self.last_stats = self.stats for metric, value in (self.stats or self.last_stats).items(): - try: # Discard non-numeric values + try: # Discard non-numeric values int(value) except: continue u = left if i % 2 == 0 else right - u.add_row(f'{c2}{metric}', f'{b2}{value:.3f}') + u.add_row(f"{c2}{metric}", f"{b2}{value:.3f}") i += 1 if i == 30: break @@ -657,13 +709,15 @@ def print_dashboard(self, clear=False, idx=[0], with console.capture() as capture: console.print(dashboard) - print('\033[0;0H' + capture.get()) + print("\033[0;0H" + capture.get()) + -def compute_puff_advantage(values, rewards, terminals, - ratio, advantages, gamma, gae_lambda, vtrace_rho_clip, vtrace_c_clip): - '''CUDA kernel for puffer advantage with automatic CPU fallback. You need +def compute_puff_advantage( + values, rewards, terminals, ratio, advantages, gamma, gae_lambda, vtrace_rho_clip, vtrace_c_clip +): + """CUDA kernel for puffer advantage with automatic CPU fallback. You need nvcc (in cuda-dev-tools or in a cuda-dev docker base) for PufferLib to - compile the fast version.''' + compile the fast version.""" device = values.device if not ADVANTAGE_CUDA: @@ -673,8 +727,9 @@ def compute_puff_advantage(values, rewards, terminals, ratio = ratio.cpu() advantages = advantages.cpu() - torch.ops.pufferlib.compute_puff_advantage(values, rewards, terminals, - ratio, advantages, gamma, gae_lambda, vtrace_rho_clip, vtrace_c_clip) + torch.ops.pufferlib.compute_puff_advantage( + values, rewards, terminals, ratio, advantages, gamma, gae_lambda, vtrace_rho_clip, vtrace_c_clip + ) if not ADVANTAGE_CUDA: return advantages.to(device) @@ -686,13 +741,14 @@ def abbreviate(num, b2, c2): if num < 1e3: return str(num) elif num < 1e6: - return f'{num/1e3:.1f}K' + return f"{num / 1e3:.1f}K" elif num < 1e9: - return f'{num/1e6:.1f}M' + return f"{num / 1e6:.1f}M" elif num < 1e12: - return f'{num/1e9:.1f}B' + return f"{num / 1e9:.1f}B" else: - return f'{num/1e12:.2f}T' + return f"{num / 1e12:.2f}T" + def duration(seconds, b2, c2): if seconds < 0: @@ -703,9 +759,11 @@ def duration(seconds, b2, c2): s = seconds % 60 return f"{b2}{h}{c2}h {b2}{m}{c2}m {b2}{s}{c2}s" if h else f"{b2}{m}{c2}m {b2}{s}{c2}s" if m else f"{b2}{s}{c2}s" + def fmt_perf(name, color, delta_ref, prof, b2, c2): - percent = 0 if delta_ref == 0 else int(100*prof['buffer']/delta_ref - 1e-5) - return f'{color}{name}', duration(prof['elapsed'], b2, c2), f'{b2}{percent:2d}{c2}%' + percent = 0 if delta_ref == 0 else int(100 * prof["buffer"] / delta_ref - 1e-5) + return f"{color}{name}", duration(prof["elapsed"], b2, c2), f"{b2}{percent:2d}{c2}%" + def dist_sum(value, device): if not torch.distributed.is_initialized(): @@ -715,12 +773,14 @@ def dist_sum(value, device): torch.distributed.all_reduce(tensor, op=torch.distributed.ReduceOp.SUM) return tensor.item() + def dist_mean(value, device): if not torch.distributed.is_initialized(): return value return dist_sum(value, device) / torch.distributed.get_world_size() + class Profile: def __init__(self, frequency=5): self.profiles = defaultdict(lambda: defaultdict(float)) @@ -737,7 +797,7 @@ def __call__(self, name, epoch, nest=False): if epoch % self.frequency != 0: return - #if torch.cuda.is_available(): + # if torch.cuda.is_available(): # torch.cuda.synchronize() tick = time.time() @@ -745,16 +805,16 @@ def __call__(self, name, epoch, nest=False): self.pop(tick) self.stack.append(name) - self.profiles[name]['start'] = tick + self.profiles[name]["start"] = tick def pop(self, end): profile = self.profiles[self.stack.pop()] - delta = end - profile['start'] - profile['elapsed'] += delta - profile['delta'] += delta + delta = end - profile["start"] + profile["elapsed"] += delta + profile["delta"] += delta def end(self): - #if torch.cuda.is_available(): + # if torch.cuda.is_available(): # torch.cuda.synchronize() end = time.time() @@ -763,9 +823,10 @@ def end(self): def clear(self): for prof in self.profiles.values(): - if prof['delta'] > 0: - prof['buffer'] = prof['delta'] - prof['delta'] = 0 + if prof["delta"] > 0: + prof["buffer"] = prof["delta"] + prof["delta"] = 0 + class Utilization(Thread): def __init__(self, delay=1, maxlen=20): @@ -780,18 +841,18 @@ def __init__(self, delay=1, maxlen=20): def run(self): while not self.stopped: - self.cpu_util.append(100*psutil.cpu_percent()/psutil.cpu_count()) + self.cpu_util.append(100 * psutil.cpu_percent() / psutil.cpu_count()) mem = psutil.virtual_memory() - self.cpu_mem.append(100*mem.active/mem.total) + self.cpu_mem.append(100 * mem.active / mem.total) if torch.cuda.is_available(): # Monitoring in distributed crashes nvml if torch.distributed.is_initialized(): - time.sleep(self.delay) - continue + time.sleep(self.delay) + continue self.gpu_util.append(torch.cuda.utilization()) free, total = torch.cuda.mem_get_info() - self.gpu_mem.append(100*(total-free)/total) + self.gpu_mem.append(100 * (total - free) / total) else: self.gpu_util.append(0) self.gpu_mem.append(0) @@ -801,6 +862,7 @@ def run(self): def stop(self): self.stopped = True + def downsample(arr, m): if len(arr) < m: return arr @@ -813,14 +875,15 @@ def downsample(arr, m): arr = arr[:-1] arr = np.array(arr) n = len(arr) - n = (n//m)*m + n = (n // m) * m arr = arr[-n:] downsampled = arr.reshape(m, -1).mean(axis=1) return np.concatenate([downsampled, [last]]) + class NoLogger: def __init__(self, args): - self.run_id = str(int(100*time.time())) + self.run_id = str(int(100 * time.time())) def log(self, logs, step): pass @@ -828,11 +891,13 @@ def log(self, logs, step): def close(self, model_path): pass + class NeptuneLogger: - def __init__(self, args, load_id=None, mode='async'): + def __init__(self, args, load_id=None, mode="async"): import neptune as nept - neptune_name = args['neptune_name'] - neptune_project = args['neptune_project'] + + neptune_name = args["neptune_name"] + neptune_project = args["neptune_project"] neptune = nept.init_run( project=f"{neptune_name}/{neptune_project}", capture_hardware_metrics=False, @@ -841,7 +906,7 @@ def __init__(self, args, load_id=None, mode='async'): capture_traceback=False, with_id=load_id, mode=mode, - tags = [args['tag']] if args['tag'] is not None else [], + tags=[args["tag"]] if args["tag"] is not None else [], ) self.run_id = neptune._sys_id self.neptune = neptune @@ -853,25 +918,27 @@ def log(self, logs, step): self.neptune[k].append(v, step=step) def close(self, model_path): - self.neptune['model'].track_files(model_path) + self.neptune["model"].track_files(model_path) self.neptune.stop() def download(self): - self.neptune["model"].download(destination='artifacts') - return f'artifacts/{self.run_id}.pt' - + self.neptune["model"].download(destination="artifacts") + return f"artifacts/{self.run_id}.pt" + + class WandbLogger: - def __init__(self, args, load_id=None, resume='allow'): + def __init__(self, args, load_id=None, resume="allow"): import wandb + wandb.init( id=load_id or wandb.util.generate_id(), - project=args['wandb_project'], - group=args['wandb_group'], + project=args["wandb_project"], + group=args["wandb_group"], allow_val_change=True, save_code=False, resume=resume, config=args, - tags = [args['tag']] if args['tag'] is not None else [], + tags=[args["tag"]] if args["tag"] is not None else [], ) self.wandb = wandb self.run_id = wandb.run.id @@ -880,26 +947,27 @@ def log(self, logs, step): self.wandb.log(logs, step=step) def close(self, model_path): - artifact = self.wandb.Artifact(self.run_id, type='model') + artifact = self.wandb.Artifact(self.run_id, type="model") artifact.add_file(model_path) self.wandb.run.log_artifact(artifact) self.wandb.finish() def download(self): - artifact = self.wandb.use_artifact(f'{self.run_id}:latest') + artifact = self.wandb.use_artifact(f"{self.run_id}:latest") data_dir = artifact.download() model_file = max(os.listdir(data_dir)) - return f'{data_dir}/{model_file}' - + return f"{data_dir}/{model_file}" + + def train(env_name, args=None, vecenv=None, policy=None, logger=None): args = args or load_config(env_name) # Assume TorchRun DDP is used if LOCAL_RANK is set - if 'LOCAL_RANK' in os.environ: - world_size = int(os.environ.get('WORLD_SIZE', 1)) + if "LOCAL_RANK" in os.environ: + world_size = int(os.environ.get("WORLD_SIZE", 1)) print("World size", world_size) - master_addr = os.environ.get('MASTER_ADDR', 'localhost') - master_port = os.environ.get('MASTER_PORT', '29500') + master_addr = os.environ.get("MASTER_ADDR", "localhost") + master_port = os.environ.get("MASTER_PORT", "29500") local_rank = int(os.environ["LOCAL_RANK"]) print(f"rank: {local_rank}, MASTER_ADDR={master_addr}, MASTER_PORT={master_port}") torch.cuda.set_device(local_rank) @@ -908,39 +976,37 @@ def train(env_name, args=None, vecenv=None, policy=None, logger=None): vecenv = vecenv or load_env(env_name, args) policy = policy or load_policy(args, vecenv, env_name) - if 'LOCAL_RANK' in os.environ: - args['train']['device'] = torch.cuda.current_device() - torch.distributed.init_process_group(backend='nccl', world_size=world_size) + if "LOCAL_RANK" in os.environ: + args["train"]["device"] = torch.cuda.current_device() + torch.distributed.init_process_group(backend="nccl", world_size=world_size) policy = policy.to(local_rank) - model = torch.nn.parallel.DistributedDataParallel( - policy, device_ids=[local_rank], output_device=local_rank - ) - if hasattr(policy, 'lstm'): - #model.lstm = policy.lstm + model = torch.nn.parallel.DistributedDataParallel(policy, device_ids=[local_rank], output_device=local_rank) + if hasattr(policy, "lstm"): + # model.lstm = policy.lstm model.hidden_size = policy.hidden_size model.forward_eval = policy.forward_eval policy = model.to(local_rank) - if args['neptune']: + if args["neptune"]: logger = NeptuneLogger(args) - elif args['wandb']: + elif args["wandb"]: logger = WandbLogger(args) - train_config = dict(**args['train'], env=env_name) + train_config = dict(**args["train"], env=env_name, eval=args.get("eval", {})) pufferl = PuffeRL(train_config, vecenv, policy, logger) all_logs = [] - while pufferl.global_step < train_config['total_timesteps']: - if train_config['device'] == 'cuda': + while pufferl.global_step < train_config["total_timesteps"]: + if train_config["device"] == "cuda": torch.compiler.cudagraph_mark_step_begin() pufferl.evaluate() - if train_config['device'] == 'cuda': + if train_config["device"] == "cuda": torch.compiler.cudagraph_mark_step_begin() logs = pufferl.train() if logs is not None: - if pufferl.global_step > 0.20*train_config['total_timesteps']: + if pufferl.global_step > 0.20 * train_config["total_timesteps"]: all_logs.append(logs) # Final eval. You can reset the env here, but depending on @@ -961,115 +1027,198 @@ def train(env_name, args=None, vecenv=None, policy=None, logger=None): pufferl.logger.close(model_path) return all_logs + def eval(env_name, args=None, vecenv=None, policy=None): + """Evaluate a policy.""" + args = args or load_config(env_name) - backend = args['vec']['backend'] - if backend != 'PufferEnv': - backend = 'Serial' - args['vec'] = dict(backend=backend, num_envs=1) - vecenv = vecenv or load_env(env_name, args) + wosac_enabled = args["eval"]["wosac_realism_eval"] + human_replay_enabled = args["eval"]["human_replay_eval"] - policy = policy or load_policy(args, vecenv, env_name) - ob, info = vecenv.reset() - driver = vecenv.driver_env - num_agents = vecenv.observation_space.shape[0] - device = args['train']['device'] - - state = {} - if args['train']['use_rnn']: - state = dict( - lstm_h=torch.zeros(num_agents, policy.hidden_size, device=device), - lstm_c=torch.zeros(num_agents, policy.hidden_size, device=device), + if wosac_enabled: + print(f"Running WOSAC realism evaluation. \n") + from pufferlib.ocean.benchmark.evaluator import WOSACEvaluator + + backend = args["eval"]["backend"] + assert backend == "PufferEnv" or not wosac_enabled, "WOSAC evaluation only supports PufferEnv backend." + args["vec"] = dict(backend=backend, num_envs=1) + args["env"]["num_agents"] = args["eval"]["wosac_num_agents"] + args["env"]["init_mode"] = args["eval"]["wosac_init_mode"] + args["env"]["control_mode"] = args["eval"]["wosac_control_mode"] + args["env"]["init_steps"] = args["eval"]["wosac_init_steps"] + args["env"]["goal_behavior"] = args["eval"]["wosac_goal_behavior"] + args["env"]["goal_radius"] = args["eval"]["wosac_goal_radius"] + + vecenv = vecenv or load_env(env_name, args) + policy = policy or load_policy(args, vecenv, env_name) + + evaluator = WOSACEvaluator(args) + + # Collect ground truth trajectories from the dataset + gt_trajectories = evaluator.collect_ground_truth_trajectories(vecenv) + + # Roll out trained policy in the simulator + simulated_trajectories = evaluator.collect_simulated_trajectories(args, vecenv, policy) + + if args["eval"]["wosac_sanity_check"]: + evaluator._quick_sanity_check(gt_trajectories, simulated_trajectories) + + # Analyze and compute metrics + results = evaluator.compute_metrics( + gt_trajectories, simulated_trajectories, args["eval"]["wosac_aggregate_results"] ) - frames = [] - while True: - render = driver.render() - if len(frames) < args['save_frames']: - frames.append(render) - - # Screenshot Ocean envs with F12, gifs with control + F12 - if driver.render_mode == 'ansi': - print('\033[0;0H' + render + '\n') - time.sleep(1/args['fps']) - elif driver.render_mode == 'rgb_array': - pass - #import cv2 - #render = cv2.cvtColor(render, cv2.COLOR_RGB2BGR) - #cv2.imshow('frame', render) - #cv2.waitKey(1) - #time.sleep(1/args['fps']) - - with torch.no_grad(): - ob = torch.as_tensor(ob).to(device) - logits, value = policy.forward_eval(ob, state) - action, logprob, _ = pufferlib.pytorch.sample_logits(logits) - action = action.cpu().numpy().reshape(vecenv.action_space.shape) - - if isinstance(logits, torch.distributions.Normal): - action = np.clip(action, vecenv.action_space.low, vecenv.action_space.high) - - ob = vecenv.step(action)[0] - - if len(frames) > 0 and len(frames) == args['save_frames']: - import imageio - imageio.mimsave(args['gif_path'], frames, fps=args['fps'], loop=0) - frames.append('Done') + if args["eval"]["wosac_aggregate_results"]: + import json + + print("WOSAC_METRICS_START") + print(json.dumps(results)) + print("WOSAC_METRICS_END") + + return results + + elif human_replay_enabled: + print("Running human replay evaluation.\n") + from pufferlib.ocean.benchmark.evaluator import HumanReplayEvaluator + + backend = args["eval"].get("backend", "PufferEnv") + args["vec"] = dict(backend=backend, num_envs=1) + args["env"]["num_agents"] = args["eval"]["human_replay_num_agents"] + args["env"]["control_mode"] = args["eval"]["human_replay_control_mode"] + args["env"]["scenario_length"] = 91 # Standard scenario length + + vecenv = vecenv or load_env(env_name, args) + policy = policy or load_policy(args, vecenv, env_name) + + evaluator = HumanReplayEvaluator(args) + + # Run rollouts with human replays + results = evaluator.rollout(args, vecenv, policy) + + import json + + print("HUMAN_REPLAY_METRICS_START") + print(json.dumps(results)) + print("HUMAN_REPLAY_METRICS_END") + + return results + else: # Standard evaluation: Render + backend = args["vec"]["backend"] + if backend != "PufferEnv": + backend = "Serial" + + args["vec"] = dict(backend=backend, num_envs=1) + vecenv = vecenv or load_env(env_name, args) + policy = policy or load_policy(args, vecenv, env_name) + + ob, info = vecenv.reset() + driver = vecenv.driver_env + num_agents = vecenv.observation_space.shape[0] + device = args["train"]["device"] + + # Rebuild visualize binary if saving frames (for C-based rendering) + if args["save_frames"] > 0: + ensure_drive_binary() + + state = {} + if args["train"]["use_rnn"]: + state = dict( + lstm_h=torch.zeros(num_agents, policy.hidden_size, device=device), + lstm_c=torch.zeros(num_agents, policy.hidden_size, device=device), + ) + + frames = [] + while True: + render = driver.render() + if len(frames) < args["save_frames"]: + frames.append(render) + + # Screenshot Ocean envs with F12, gifs with control + F12 + if driver.render_mode == "ansi": + print("\033[0;0H" + render + "\n") + time.sleep(1 / args["fps"]) + elif driver.render_mode == "rgb_array": + pass + # import cv2 + # render = cv2.cvtColor(render, cv2.COLOR_RGB2BGR) + # cv2.imshow('frame', render) + # cv2.waitKey(1) + # time.sleep(1/args['fps']) + + with torch.no_grad(): + ob = torch.as_tensor(ob).to(device) + logits, value = policy.forward_eval(ob, state) + action, logprob, _ = pufferlib.pytorch.sample_logits(logits) + action = action.cpu().numpy().reshape(vecenv.action_space.shape) + + if isinstance(logits, torch.distributions.Normal): + action = np.clip(action, vecenv.action_space.low, vecenv.action_space.high) + + ob = vecenv.step(action)[0] + + if len(frames) > 0 and len(frames) == args["save_frames"]: + import imageio + + imageio.mimsave(args["gif_path"], frames, fps=args["fps"], loop=0) + frames.append("Done") + def sweep(args=None, env_name=None): args = args or load_config(env_name) - if not args['wandb'] and not args['neptune']: - raise pufferlib.APIUsageError('Sweeps require either wandb or neptune') + if not args["wandb"] and not args["neptune"]: + raise pufferlib.APIUsageError("Sweeps require either wandb or neptune") - method = args['sweep'].pop('method') + method = args["sweep"].pop("method") try: sweep_cls = getattr(pufferlib.sweep, method) except: - raise pufferlib.APIUsageError(f'Invalid sweep method {method}. See pufferlib.sweep') + raise pufferlib.APIUsageError(f"Invalid sweep method {method}. See pufferlib.sweep") - sweep = sweep_cls(args['sweep']) - points_per_run = args['sweep']['downsample'] - target_key = f'environment/{args["sweep"]["metric"]}' - for i in range(args['max_runs']): + sweep = sweep_cls(args["sweep"]) + points_per_run = args["sweep"]["downsample"] + target_key = f"environment/{args['sweep']['metric']}" + for i in range(args["max_runs"]): seed = time.time_ns() & 0xFFFFFFFF random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) sweep.suggest(args) - total_timesteps = args['train']['total_timesteps'] + total_timesteps = args["train"]["total_timesteps"] all_logs = train(env_name, args=args) all_logs = [e for e in all_logs if target_key in e] scores = downsample([log[target_key] for log in all_logs], points_per_run) - costs = downsample([log['uptime'] for log in all_logs], points_per_run) - timesteps = downsample([log['agent_steps'] for log in all_logs], points_per_run) + costs = downsample([log["uptime"] for log in all_logs], points_per_run) + timesteps = downsample([log["agent_steps"] for log in all_logs], points_per_run) for score, cost, timestep in zip(scores, costs, timesteps): - args['train']['total_timesteps'] = timestep + args["train"]["total_timesteps"] = timestep sweep.observe(args, score, cost) # Prevent logging final eval steps as training steps - args['train']['total_timesteps'] = total_timesteps + args["train"]["total_timesteps"] = total_timesteps + def profile(args=None, env_name=None, vecenv=None, policy=None): args = load_config() vecenv = vecenv or load_env(env_name, args) policy = policy or load_policy(args, vecenv) - train_config = dict(**args['train'], env=args['env_name'], tag=args['tag']) - pufferl = PuffeRL(train_config, vecenv, policy, neptune=args['neptune'], wandb=args['wandb']) + train_config = dict(**args["train"], env=args["env_name"], tag=args["tag"]) + pufferl = PuffeRL(train_config, vecenv, policy, neptune=args["neptune"], wandb=args["wandb"]) - import torchvision.models as models from torch.profiler import profile, record_function, ProfilerActivity + with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA], record_shapes=True) as prof: with record_function("model_inference"): for _ in range(10): stats = pufferl.evaluate() pufferl.train() - print(prof.key_averages().table(sort_by='cuda_time_total', row_limit=10)) + print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10)) prof.export_chrome_trace("trace.json") -def export(args=None, env_name=None, vecenv=None, policy=None): + +def export(args=None, env_name=None, vecenv=None, policy=None, path=None, silent=False): args = args or load_config(env_name) vecenv = vecenv or load_env(env_name, args) policy = policy or load_policy(args, vecenv) @@ -1077,110 +1226,153 @@ def export(args=None, env_name=None, vecenv=None, policy=None): weights = [] for name, param in policy.named_parameters(): weights.append(param.data.cpu().numpy().flatten()) - print(name, param.shape, param.data.cpu().numpy().ravel()[0]) - - path = f'{args["env_name"]}_weights.bin' + if not silent: + print(name, param.shape, param.data.cpu().numpy().ravel()[0]) + weights = np.concatenate(weights) + if path is None: + path = f"pufferlib/resources/drive/{args['env_name']}_weights.bin" + weights.tofile(path) - print(f'Saved {len(weights)} weights to {path}') + + if not silent: + print(f"Saved {len(weights)} weights to {path}") + + +def ensure_drive_binary(): + """Delete existing visualize binary and rebuild it. This ensures the + binary is always up-to-date with the latest code changes. + """ + if os.path.exists("./visualize"): + print("Removing existing visualize binary...") + os.remove("./visualize") + + print("Building visualize binary...") + try: + result = subprocess.run( + ["bash", "scripts/build_ocean.sh", "visualize", "local"], capture_output=True, text=True, timeout=300 + ) + + if result.returncode == 0: + print("Successfully built visualize binary") + else: + print(f"Build failed: {result.stderr}") + raise RuntimeError("Failed to build visualize binary for rendering") + except subprocess.TimeoutExpired: + raise RuntimeError("Build timed out") + except Exception as e: + raise RuntimeError(f"Build error: {e}") + def autotune(args=None, env_name=None, vecenv=None, policy=None): - package = args['package'] - module_name = 'pufferlib.ocean' if package == 'ocean' else f'pufferlib.environments.{package}' + package = args["package"] + module_name = "pufferlib.ocean" if package == "ocean" else f"pufferlib.environments.{package}" env_module = importlib.import_module(module_name) - env_name = args['env_name'] + env_name = args["env_name"] make_env = env_module.env_creator(env_name) - pufferlib.vector.autotune(make_env, batch_size=args['train']['env_batch_size']) - + pufferlib.vector.autotune(make_env, batch_size=args["train"]["env_batch_size"]) + + def load_env(env_name, args): - package = args['package'] - module_name = 'pufferlib.ocean' if package == 'ocean' else f'pufferlib.environments.{package}' + package = args["package"] + module_name = "pufferlib.ocean" if package == "ocean" else f"pufferlib.environments.{package}" env_module = importlib.import_module(module_name) make_env = env_module.env_creator(env_name) - return pufferlib.vector.make(make_env, env_kwargs=args['env'], **args['vec']) + return pufferlib.vector.make(make_env, env_kwargs=args["env"], **args["vec"]) + -def load_policy(args, vecenv, env_name=''): - package = args['package'] - module_name = 'pufferlib.ocean' if package == 'ocean' else f'pufferlib.environments.{package}' +def load_policy(args, vecenv, env_name=""): + package = args["package"] + module_name = "pufferlib.ocean" if package == "ocean" else f"pufferlib.environments.{package}" env_module = importlib.import_module(module_name) - device = args['train']['device'] - policy_cls = getattr(env_module.torch, args['policy_name']) - policy = policy_cls(vecenv.driver_env, **args['policy']) + device = args["train"]["device"] + policy_cls = getattr(env_module.torch, args["policy_name"]) + policy = policy_cls(vecenv.driver_env, **args["policy"]) - rnn_name = args['rnn_name'] + rnn_name = args["rnn_name"] if rnn_name is not None: - rnn_cls = getattr(env_module.torch, args['rnn_name']) - policy = rnn_cls(vecenv.driver_env, policy, **args['rnn']) + rnn_cls = getattr(env_module.torch, args["rnn_name"]) + policy = rnn_cls(vecenv.driver_env, policy, **args["rnn"]) policy = policy.to(device) - load_id = args['load_id'] + load_id = args["load_id"] if load_id is not None: - if args['neptune']: - path = NeptuneLogger(args, load_id, mode='read-only').download() - elif args['wandb']: + if args["neptune"]: + path = NeptuneLogger(args, load_id, mode="read-only").download() + elif args["wandb"]: path = WandbLogger(args, load_id).download() else: - raise pufferlib.APIUsageError('No run id provided for eval') + raise pufferlib.APIUsageError("No run id provided for eval") state_dict = torch.load(path, map_location=device) - state_dict = {k.replace('module.', ''): v for k, v in state_dict.items()} + state_dict = {k.replace("module.", ""): v for k, v in state_dict.items()} policy.load_state_dict(state_dict) - load_path = args['load_model_path'] - if load_path == 'latest': + load_path = args["load_model_path"] + if load_path == "latest": load_path = max(glob.glob(f"experiments/{env_name}*.pt"), key=os.path.getctime) if load_path is not None: state_dict = torch.load(load_path, map_location=device) - state_dict = {k.replace('module.', ''): v for k, v in state_dict.items()} + state_dict = {k.replace("module.", ""): v for k, v in state_dict.items()} policy.load_state_dict(state_dict) - #state_path = os.path.join(*load_path.split('/')[:-1], 'state.pt') - #optim_state = torch.load(state_path)['optimizer_state_dict'] - #pufferl.optimizer.load_state_dict(optim_state) + # state_path = os.path.join(*load_path.split('/')[:-1], 'state.pt') + # optim_state = torch.load(state_path)['optimizer_state_dict'] + # pufferl.optimizer.load_state_dict(optim_state) return policy -def load_config(env_name): + +def load_config(env_name, config_dir=None): parser = argparse.ArgumentParser( - description=f':blowfish: PufferLib [bright_cyan]{pufferlib.__version__}[/]' - ' demo options. Shows valid args for your env and policy', - formatter_class=RichHelpFormatter, add_help=False) - parser.add_argument('--load-model-path', type=str, default=None, - help='Path to a pretrained checkpoint') - parser.add_argument('--load-id', type=str, - default=None, help='Kickstart/eval from from a finished Wandb/Neptune run') - parser.add_argument('--render-mode', type=str, default='auto', - choices=['auto', 'human', 'ansi', 'rgb_array', 'raylib', 'None']) - parser.add_argument('--save-frames', type=int, default=0) - parser.add_argument('--gif-path', type=str, default='eval.gif') - parser.add_argument('--fps', type=float, default=15) - parser.add_argument('--max-runs', type=int, default=200, help='Max number of sweep runs') - parser.add_argument('--wandb', action='store_true', help='Use wandb for logging') - parser.add_argument('--wandb-project', type=str, default='pufferlib') - parser.add_argument('--wandb-group', type=str, default='debug') - parser.add_argument('--neptune', action='store_true', help='Use neptune for logging') - parser.add_argument('--neptune-name', type=str, default='pufferai') - parser.add_argument('--neptune-project', type=str, default='ablations') - parser.add_argument('--local-rank', type=int, default=0, help='Used by torchrun for DDP') - parser.add_argument('--tag', type=str, default=None, help='Tag for experiment') + description=f":blowfish: PufferLib [bright_cyan]{pufferlib.__version__}[/]" + " demo options. Shows valid args for your env and policy", + formatter_class=RichHelpFormatter, + add_help=False, + ) + parser.add_argument("--load-model-path", type=str, default=None, help="Path to a pretrained checkpoint") + parser.add_argument( + "--load-id", type=str, default=None, help="Kickstart/eval from from a finished Wandb/Neptune run" + ) + parser.add_argument( + "--render-mode", type=str, default="auto", choices=["auto", "human", "ansi", "rgb_array", "raylib", "None"] + ) + parser.add_argument("--save-frames", type=int, default=0) + parser.add_argument("--gif-path", type=str, default="eval.gif") + parser.add_argument("--fps", type=float, default=15) + parser.add_argument("--max-runs", type=int, default=200, help="Max number of sweep runs") + parser.add_argument("--wandb", action="store_true", help="Use wandb for logging") + parser.add_argument("--wandb-project", type=str, default="pufferlib") + parser.add_argument("--wandb-group", type=str, default="debug") + parser.add_argument("--neptune", action="store_true", help="Use neptune for logging") + parser.add_argument("--neptune-name", type=str, default="pufferai") + parser.add_argument("--neptune-project", type=str, default="ablations") + parser.add_argument("--local-rank", type=int, default=0, help="Used by torchrun for DDP") + parser.add_argument("--tag", type=str, default=None, help="Tag for experiment") args = parser.parse_known_args()[0] + if config_dir is None: + puffer_dir = os.path.dirname(os.path.realpath(__file__)) + else: + print("Using custom config dir:", config_dir) + puffer_dir = config_dir + # Load defaults and config - puffer_dir = os.path.dirname(os.path.realpath(__file__)) - puffer_config_dir = os.path.join(puffer_dir, 'config/**/*.ini') - puffer_default_config = os.path.join(puffer_dir, 'config/default.ini') - if env_name == 'default': + puffer_config_dir = os.path.join(puffer_dir, "config/**/*.ini") + puffer_default_config = os.path.join(puffer_dir, "config/default.ini") + if env_name == "default": p = configparser.ConfigParser() p.read(puffer_default_config) else: for path in glob.glob(puffer_config_dir, recursive=True): p = configparser.ConfigParser() p.read([puffer_default_config, path]) - if env_name in p['base']['env_name'].split(): break + if env_name in p["base"]["env_name"].split(): + break else: - raise pufferlib.APIUsageError('No config for env_name {}'.format(env_name)) + raise pufferlib.APIUsageError("No config for env_name {}".format(env_name)) # Dynamic help menu from config def puffer_type(value): @@ -1191,51 +1383,52 @@ def puffer_type(value): for section in p.sections(): for key in p[section]: - fmt = f'--{key}' if section == 'base' else f'--{section}.{key}' - parser.add_argument( - fmt.replace('_', '-'), - default=puffer_type(p[section][key]), - type=puffer_type - ) + fmt = f"--{key}" if section == "base" else f"--{section}.{key}" + parser.add_argument(fmt.replace("_", "-"), default=puffer_type(p[section][key]), type=puffer_type) - parser.add_argument('-h', '--help', default=argparse.SUPPRESS, - action='help', help='Show this help message and exit') + parser.add_argument( + "-h", "--help", default=argparse.SUPPRESS, action="help", help="Show this help message and exit" + ) # Unpack to nested dict parsed = vars(parser.parse_args()) args = defaultdict(dict) for key, value in parsed.items(): next = args - for subkey in key.split('.'): + for subkey in key.split("."): prev = next next = next.setdefault(subkey, {}) prev[subkey] = value - args['train']['use_rnn'] = args['rnn_name'] is not None + args["train"]["use_rnn"] = args["rnn_name"] is not None return args + def main(): - err = 'Usage: puffer [train, eval, sweep, autotune, profile, export] [env_name] [optional args]. --help for more info' + err = ( + "Usage: puffer [train, eval, sweep, autotune, profile, export] [env_name] [optional args]. --help for more info" + ) if len(sys.argv) < 3: raise pufferlib.APIUsageError(err) mode = sys.argv.pop(1) env_name = sys.argv.pop(1) - if mode == 'train': + if mode == "train": train(env_name=env_name) - elif mode == 'eval': + elif mode == "eval": eval(env_name=env_name) - elif mode == 'sweep': + elif mode == "sweep": sweep(env_name=env_name) - elif mode == 'autotune': + elif mode == "autotune": autotune(env_name=env_name) - elif mode == 'profile': + elif mode == "profile": profile(env_name=env_name) - elif mode == 'export': + elif mode == "export": export(env_name=env_name) else: raise pufferlib.APIUsageError(err) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/pufferlib/pufferlib.py b/pufferlib/pufferlib.py index 5bef2b0a5..e718241a0 100644 --- a/pufferlib/pufferlib.py +++ b/pufferlib/pufferlib.py @@ -1,10 +1,7 @@ import os -import sys import warnings -from contextlib import redirect_stdout, redirect_stderr, contextmanager -from types import SimpleNamespace -from collections.abc import Mapping +from contextlib import redirect_stdout, redirect_stderr from io import StringIO from functools import wraps @@ -13,10 +10,10 @@ import pufferlib.spaces -ENV_ERROR = ''' +ENV_ERROR = """ Environment missing required attribute {}. The most common cause is calling super() before you have assigned the attribute. -''' +""" def set_buffers(env, buf=None): @@ -35,34 +32,37 @@ def set_buffers(env, buf=None): else: env.actions = np.zeros(atn_space.shape, dtype=np.int32) else: - env.observations = buf['observations'] - env.rewards = buf['rewards'] - env.terminals = buf['terminals'] - env.truncations = buf['truncations'] - env.masks = buf['masks'] - env.actions = buf['actions'] + env.observations = buf["observations"] + env.rewards = buf["rewards"] + env.terminals = buf["terminals"] + env.truncations = buf["truncations"] + env.masks = buf["masks"] + env.actions = buf["actions"] + class PufferEnv: def __init__(self, buf=None): - if not hasattr(self, 'single_observation_space'): - raise APIUsageError(ENV_ERROR.format('single_observation_space')) - if not hasattr(self, 'single_action_space'): - raise APIUsageError(ENV_ERROR.format('single_action_space')) - if not hasattr(self, 'num_agents'): - raise APIUsageError(ENV_ERROR.format('num_agents')) + if not hasattr(self, "single_observation_space"): + raise APIUsageError(ENV_ERROR.format("single_observation_space")) + if not hasattr(self, "single_action_space"): + raise APIUsageError(ENV_ERROR.format("single_action_space")) + if not hasattr(self, "num_agents"): + raise APIUsageError(ENV_ERROR.format("num_agents")) if self.num_agents < 1: - raise APIUsageError('num_agents must be >= 1') + raise APIUsageError("num_agents must be >= 1") - if hasattr(self, 'observation_space'): - raise APIUsageError('PufferEnvs must define single_observation_space, not observation_space') - if hasattr(self, 'action_space'): - raise APIUsageError('PufferEnvs must define single_action_space, not action_space') + if hasattr(self, "observation_space"): + raise APIUsageError("PufferEnvs must define single_observation_space, not observation_space") + if hasattr(self, "action_space"): + raise APIUsageError("PufferEnvs must define single_action_space, not action_space") if not isinstance(self.single_observation_space, pufferlib.spaces.Box): - raise APIUsageError('Native observation_space must be a Box') - if (not isinstance(self.single_action_space, pufferlib.spaces.Discrete) - and not isinstance(self.single_action_space, pufferlib.spaces.MultiDiscrete) - and not isinstance(self.single_action_space, pufferlib.spaces.Box)): - raise APIUsageError('Native action_space must be a Discrete, MultiDiscrete, or Box') + raise APIUsageError("Native observation_space must be a Box") + if ( + not isinstance(self.single_action_space, pufferlib.spaces.Discrete) + and not isinstance(self.single_action_space, pufferlib.spaces.MultiDiscrete) + and not isinstance(self.single_action_space, pufferlib.spaces.Box) + ): + raise APIUsageError("Native action_space must be a Discrete, MultiDiscrete, or Box") set_buffers(self, buf) @@ -76,17 +76,17 @@ def agent_per_batch(self): @property def emulated(self): - '''Native envs do not use emulation''' + """Native envs do not use emulation""" return False @property def done(self): - '''Native envs handle resets internally''' + """Native envs handle resets internally""" return False @property def driver_env(self): - '''For compatibility with Multiprocessing''' + """For compatibility with Multiprocessing""" return self def reset(self, seed=None): @@ -100,20 +100,29 @@ def close(self): def async_reset(self, seed=None): _, self.infos = self.reset(seed) - assert isinstance(self.infos, list), 'PufferEnvs must return info as a list of dicts' + assert isinstance(self.infos, list), "PufferEnvs must return info as a list of dicts" def send(self, actions): _, _, _, _, self.infos = self.step(actions) - assert isinstance(self.infos, list), 'PufferEnvs must return info as a list of dicts' + assert isinstance(self.infos, list), "PufferEnvs must return info as a list of dicts" def recv(self): - return (self.observations, self.rewards, self.terminals, - self.truncations, self.infos, self.agent_ids, self.masks) + return ( + self.observations, + self.rewards, + self.terminals, + self.truncations, + self.infos, + self.agent_ids, + self.masks, + ) + ### Postprocessing class ResizeObservation(gymnasium.Wrapper): - '''Fixed downscaling wrapper. Do NOT use gym.wrappers.ResizeObservation - It uses a laughably slow OpenCV resize. -50% on Atari just from that.''' + """Fixed downscaling wrapper. Do NOT use gym.wrappers.ResizeObservation + It uses a laughably slow OpenCV resize. -50% on Atari just from that.""" + def __init__(self, env, downscale=2): super().__init__(env) self.downscale = downscale @@ -121,19 +130,20 @@ def __init__(self, env, downscale=2): assert y_size % downscale == 0 and x_size % downscale == 0 y_size = env.observation_space.shape[0] // downscale x_size = env.observation_space.shape[1] // downscale - self.observation_space = gymnasium.spaces.Box( - low=0, high=255, shape=(y_size, x_size), dtype=np.uint8) + self.observation_space = gymnasium.spaces.Box(low=0, high=255, shape=(y_size, x_size), dtype=np.uint8) def reset(self, seed=None, options=None): obs, info = self.env.reset(seed=seed, options=options) - return obs[::self.downscale, ::self.downscale], info + return obs[:: self.downscale, :: self.downscale], info def step(self, action): obs, reward, terminal, truncated, info = self.env.step(action) - return obs[::self.downscale, ::self.downscale], reward, terminal, truncated, info + return obs[:: self.downscale, :: self.downscale], reward, terminal, truncated, info + class ClipAction(gymnasium.Wrapper): - '''Wrapper for Gymnasium environments that clips actions''' + """Wrapper for Gymnasium environments that clips actions""" + def __init__(self, env): self.env = env assert isinstance(env.action_space, gymnasium.spaces.Box) @@ -153,8 +163,9 @@ def step(self, action): class EpisodeStats(gymnasium.Wrapper): - '''Wrapper for Gymnasium environments that stores - episodic returns and lengths in infos''' + """Wrapper for Gymnasium environments that stores + episodic returns and lengths in infos""" + def __init__(self, env): self.env = env self.observation_space = env.observation_space @@ -164,7 +175,7 @@ def __init__(self, env): def reset(self, seed=None, options=None): self.info = dict(episode_return=[], episode_length=0) # TODO: options - return self.env.reset(seed=seed)#, options=options) + return self.env.reset(seed=seed) # , options=options) def step(self, action): observation, reward, terminated, truncated, info = super().step(action) @@ -175,8 +186,8 @@ def step(self, action): self.info[k].append(v) - self.info['episode_return'].append(reward) - self.info['episode_length'] += 1 + self.info["episode_return"].append(reward) + self.info["episode_length"] += 1 info = {} if terminated or truncated: @@ -192,7 +203,7 @@ def step(self, action): continue try: - x = int(v) # probably a value + x = int(v) # probably a value info[k] = v continue except TypeError: @@ -200,15 +211,17 @@ def step(self, action): return observation, reward, terminated, truncated, info + class PettingZooWrapper: - '''PettingZoo does not provide a ParallelEnv wrapper. This code is adapted from - their AEC wrapper, to prevent unneeded conversions to/from AEC''' + """PettingZoo does not provide a ParallelEnv wrapper. This code is adapted from + their AEC wrapper, to prevent unneeded conversions to/from AEC""" + def __init__(self, env): self.env = env def __getattr__(self, name): - '''Returns an attribute with ``name``, unless ``name`` starts with an underscore.''' - if name.startswith('_') and name != '_cumulative_rewards': + """Returns an attribute with ``name``, unless ``name`` starts with an underscore.""" + if name.startswith("_") and name != "_cumulative_rewards": raise AttributeError(f'accessing private attribute "{name}" is prohibited') return getattr(self.env, name) @@ -244,11 +257,13 @@ def action_space(self, agent): return self.env.action_space(agent) def __str__(self) -> str: - '''Returns a name which looks like: "max_observation".''' - return f'{type(self).__name__}<{str(self.env)}>' + """Returns a name which looks like: "max_observation".""" + return f"{type(self).__name__}<{str(self.env)}>" + class MeanOverAgents(PettingZooWrapper): - '''Averages over agent infos''' + """Averages over agent infos""" + def _mean(self, infos): list_infos = {} for agent, info in infos.items(): @@ -277,15 +292,14 @@ def step(self, actions): infos = self._mean(infos) return observations, rewards, terminations, truncations, infos + class MultiagentEpisodeStats(PettingZooWrapper): - '''Wrapper for PettingZoo environments that stores - episodic returns and lengths in infos''' + """Wrapper for PettingZoo environments that stores + episodic returns and lengths in infos""" + def reset(self, seed=None, options=None): observations, infos = super().reset(seed=seed, options=options) - self.infos = { - agent: dict(episode_return=[], episode_length=0) - for agent in self.possible_agents - } + self.infos = {agent: dict(episode_return=[], episode_length=0) for agent in self.possible_agents} return observations, infos def step(self, actions): @@ -301,8 +315,8 @@ def step(self, actions): agent_info[k].append(v) # Saved to self. TODO: Clean up - agent_info['episode_return'].append(rewards[agent]) - agent_info['episode_length'] += 1 + agent_info["episode_return"].append(rewards[agent]) + agent_info["episode_length"] += 1 agent_info = {} all_infos[agent] = agent_info @@ -319,18 +333,21 @@ def step(self, actions): continue try: - x = int(v) # probably a value + x = int(v) # probably a value agent_info[k] = v continue except TypeError: pass return observations, rewards, terminations, truncations, all_infos + + ### Exceptions class EnvironmentSetupError(RuntimeError): def __init__(self, e, package): super().__init__(self.message) + class APIUsageError(RuntimeError): """Exception raised when the API is used incorrectly.""" @@ -338,16 +355,15 @@ def __init__(self, message="API usage error."): self.message = message super().__init__(self.message) + class InvalidAgentError(ValueError): """Exception raised when an invalid agent key is used.""" def __init__(self, agent_id, agents): - message = ( - f'Invalid agent/team ({agent_id}) specified. ' - f'Valid values:\n{agents}' - ) + message = f"Invalid agent/team ({agent_id}) specified. Valid values:\n{agents}" super().__init__(message) + class GymToGymnasium: def __init__(self, env): self.env = env @@ -370,6 +386,7 @@ def step(self, action): def close(self): self.env.close() + ### Wrappers class PettingZooTruncatedWrapper: def __init__(self, env): @@ -405,6 +422,7 @@ def step(self, actions): def close(self): self.env.close() + ### Misc def unroll_nested_dict(d): if not isinstance(d, dict): @@ -417,15 +435,18 @@ def unroll_nested_dict(d): else: yield k, v + def silence_warnings(original_func, category=DeprecationWarning): @wraps(original_func) def wrapper(*args, **kwargs): with warnings.catch_warnings(): warnings.simplefilter("ignore", category=category) return original_func(*args, **kwargs) + return wrapper -class Suppress(): + +class Suppress: def __init__(self): self.f = StringIO() self.null_1 = os.open(os.devnull, os.O_WRONLY | os.O_TRUNC | os.O_CREAT) diff --git a/pufferlib/pytorch.py b/pufferlib/pytorch.py index caf92632b..92d4af77a 100644 --- a/pufferlib/pytorch.py +++ b/pufferlib/pytorch.py @@ -1,16 +1,10 @@ import sys -from pdb import set_trace as T from typing import Dict, List, Tuple, Union import numpy as np import torch -from torch import nn -from torch.distributions import Categorical from torch.distributions.utils import logits_to_probs -import pufferlib -import pufferlib.models - numpy_to_torch_dtype_dict = { np.dtype("float64"): torch.float64, @@ -44,27 +38,28 @@ NativeDTypeValue = Tuple[torch.dtype, List[int], int, int] NativeDType = Union[NativeDTypeValue, Dict[str, Union[NativeDTypeValue, "NativeDType"]]] + # TODO: handle discrete obs # Spend some time trying to break this fn with differnt obs def nativize_dtype(emulated) -> NativeDType: # sample dtype - the dtype of what we obtain from the environment (usually bytes) - sample_dtype: np.dtype = emulated['observation_dtype'] + sample_dtype: np.dtype = emulated["observation_dtype"] # structured dtype - the gym.Space converted numpy dtype # the observation represents (could be dict, tuple, box, etc.) - structured_dtype: np.dtype = emulated['emulated_observation_dtype'] + structured_dtype: np.dtype = emulated["emulated_observation_dtype"] subviews, dtype, shape, offset, delta = _nativize_dtype(sample_dtype, structured_dtype) if subviews is None: return (dtype, shape, offset, delta) else: return subviews + def round_to(x, base): - return int(base * np.ceil(x/base)) + return int(base * np.ceil(x / base)) -def _nativize_dtype(sample_dtype: np.dtype, - structured_dtype: np.dtype, - offset: int = 0) -> NativeDType: + +def _nativize_dtype(sample_dtype: np.dtype, structured_dtype: np.dtype, offset: int = 0) -> NativeDType: if structured_dtype.fields is None: if structured_dtype.subdtype is not None: dtype, shape = structured_dtype.subdtype @@ -85,8 +80,7 @@ def _nativize_dtype(sample_dtype: np.dtype, start_offset = offset all_delta = 0 for name, (dtype, _) in structured_dtype.fields.items(): - views, dtype, shape, offset, delta = _nativize_dtype( - sample_dtype, dtype, offset) + views, dtype, shape, offset, delta = _nativize_dtype(sample_dtype, dtype, offset) if views is not None: subviews[name] = views @@ -146,13 +140,15 @@ def nativize_observation(observation, emulated): # float is natively supported, but only if that is the actual correct type return nativize_tensor( observation, - emulated['observation_dtype'], - emulated['emulated_observation_dtype'], + emulated["observation_dtype"], + emulated["emulated_observation_dtype"], ) + def flattened_tensor_size(native_dtype): return _flattened_tensor_size(native_dtype) + def _flattened_tensor_size(native_dtype): if isinstance(native_dtype, tuple): return np.prod(native_dtype[1]) # shape @@ -162,12 +158,14 @@ def _flattened_tensor_size(native_dtype): res += _flattened_tensor_size(dtype) return res + def layer_init(layer, std=np.sqrt(2), bias_const=0.0): """CleanRL's default layer initialization""" torch.nn.init.orthogonal_(layer.weight, std) torch.nn.init.constant_(layer.bias, bias_const) return layer + # taken from torch.distributions.Categorical def log_prob(logits, value): value = value.long().unsqueeze(-1) @@ -175,6 +173,7 @@ def log_prob(logits, value): value = value[..., :1] return log_pmf.gather(-1, value).squeeze(-1) + # taken from torch.distributions.Categorical def entropy(logits): min_real = torch.finfo(logits.dtype).min @@ -182,10 +181,12 @@ def entropy(logits): p_log_p = logits * logits_to_probs(logits) return -p_log_p.sum(-1) + def entropy_probs(logits, probs): p_log_p = logits * probs return -p_log_p.sum(-1) + def sample_logits(logits, action=None): is_discrete = isinstance(logits, torch.Tensor) if isinstance(logits, torch.distributions.Normal): @@ -199,12 +200,10 @@ def sample_logits(logits, action=None): elif is_discrete: logits = logits.unsqueeze(0) # TODO: Double check this - else: #multi-discrete + else: # multi-discrete logits = torch.nn.utils.rnn.pad_sequence( - [l.transpose(0,1) for l in logits], - batch_first=False, - padding_value=-torch.inf - ).permute(1,2,0) + [l.transpose(0, 1) for l in logits], batch_first=False, padding_value=-torch.inf + ).permute(1, 2, 0) # This can fail on nans etc normalized_logits = logits - logits.logsumexp(dim=-1, keepdim=True) diff --git a/pufferlib/resources/battle/artillery.glb b/pufferlib/resources/battle/artillery.glb deleted file mode 100644 index a3bdc67da..000000000 Binary files a/pufferlib/resources/battle/artillery.glb and /dev/null differ diff --git a/pufferlib/resources/battle/base.glb b/pufferlib/resources/battle/base.glb deleted file mode 100644 index a67a8e2d9..000000000 Binary files a/pufferlib/resources/battle/base.glb and /dev/null differ diff --git a/pufferlib/resources/battle/bomber.glb b/pufferlib/resources/battle/bomber.glb deleted file mode 100644 index 073b49cea..000000000 Binary files a/pufferlib/resources/battle/bomber.glb and /dev/null differ diff --git a/pufferlib/resources/battle/car.glb b/pufferlib/resources/battle/car.glb deleted file mode 100644 index 4c8bb7a5d..000000000 Binary files a/pufferlib/resources/battle/car.glb and /dev/null differ diff --git a/pufferlib/resources/battle/drone.glb b/pufferlib/resources/battle/drone.glb deleted file mode 100644 index 024eb4873..000000000 Binary files a/pufferlib/resources/battle/drone.glb and /dev/null differ diff --git a/pufferlib/resources/battle/fighter.glb b/pufferlib/resources/battle/fighter.glb deleted file mode 100644 index 56cfb9241..000000000 Binary files a/pufferlib/resources/battle/fighter.glb and /dev/null differ diff --git a/pufferlib/resources/battle/mothership.glb b/pufferlib/resources/battle/mothership.glb deleted file mode 100644 index 68e04c6c5..000000000 Binary files a/pufferlib/resources/battle/mothership.glb and /dev/null differ diff --git a/pufferlib/resources/battle/shader_330.fs b/pufferlib/resources/battle/shader_330.fs deleted file mode 100644 index 9b85cd396..000000000 --- a/pufferlib/resources/battle/shader_330.fs +++ /dev/null @@ -1,128 +0,0 @@ -#version 330 - -// Input vertex attributes (from vertex shader) -in vec2 fragTexCoord; // Fragment input: vertex attribute: texture coordinates -in vec4 fragColor; // Fragment input: vertex attribute: color -in vec3 fragPosition; // Fragment input: vertex attribute: position - -// Input uniform values -uniform sampler2D texture0; // Fragment input: texture - -// Output fragment color -out vec4 finalColor; // Fragment output: pixel color - - -//Ashima's simplex noise -vec3 mod289(vec3 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec2 mod289(vec2 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec3 permute(vec3 x) { - return mod289(((x*34.0)+10.0)*x); -} - -float snoise(vec2 v) - { - const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 - 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) - -0.577350269189626, // -1.0 + 2.0 * C.x - 0.024390243902439); // 1.0 / 41.0 -// First corner - vec2 i = floor(v + dot(v, C.yy) ); - vec2 x0 = v - i + dot(i, C.xx); - -// Other corners - vec2 i1; - //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 - //i1.y = 1.0 - i1.x; - i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); - // x0 = x0 - 0.0 + 0.0 * C.xx ; - // x1 = x0 - i1 + 1.0 * C.xx ; - // x2 = x0 - 1.0 + 2.0 * C.xx ; - vec4 x12 = x0.xyxy + C.xxzz; - x12.xy -= i1; - -// Permutations - i = mod289(i); // Avoid truncation effects in permutation - vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) - + i.x + vec3(0.0, i1.x, 1.0 )); - - vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); - m = m*m ; - m = m*m ; - -// Gradients: 41 points uniformly over a line, mapped onto a diamond. -// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) - - vec3 x = 2.0 * fract(p * C.www) - 1.0; - vec3 h = abs(x) - 0.5; - vec3 ox = floor(x + 0.5); - vec3 a0 = x - ox; - -// Normalise gradients implicitly by scaling m -// Approximation of: m *= inversesqrt( a0*a0 + h*h ); - m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); - -// Compute final noise value at P - vec3 g; - g.x = a0.x * x0.x + h.x * x0.y; - g.yz = a0.yz * x12.xz + h.yz * x12.yw; - return 130.0 * dot(m, g); -} - - -float rand(vec2 co){ - return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - -void main() -{ - // Color based on height (e.g., gradient from blue to red) - float height = fragPosition.y/256.0; - float delta = 0.0; - for (int i = -4; i < 4; i++) { - float scale = pow(2.0, float(i)); - delta += 0.02*snoise((1.0+fragPosition.y)*fragPosition.xz/scale); - } - //float scale = pow(2.0, 4.0); - //float delta = round(2.0*snoise(fragPosition.xz/scale))/10.0; - float val = 0.5 + height; - - - float black = 0.25 - 0.25*fract(20.0*height); - //height = round(height*5.0)/5.0; - vec4 start_color = vec4(128.0/255.0, 48.0/255.0, 0.0, 1.0); - vec4 mid_color = vec4(150.0/255.0, 75.0/255.0, 0.0/255.0, 1.0); - vec4 end_color = vec4(48.0/255.0, 128.0/255.0, 0.0, 1.0); - - - //if (height < 0.5) { - // finalColor.rgba = mix(start_color, mid_color, 2.0*height); - //} else { - // finalColor.rgba = mix(mid_color, end_color, 2.0*(height-0.5)); - //} - - finalColor.rgba = mix(mid_color, end_color, height); - - finalColor.rgb -= black; - - if (height < 0.001) { - finalColor.rgb = start_color.rgb + delta; - } - - //if (height < 5.0) { - // float val = 0.5 + height/10.0; - // finalColor.rgba = vec4(val+delta, (val+delta)/2.0, 0.0, 1.0); - //} else if (height < 15.0) { - // float val = 0.5 + (height - 5.0)/5.0; - // finalColor.rgba = vec4(val+delta, (val+delta)/3.0, 0.0, 1.0); - //} else { - // float val = 0.5 + (height - 15.0)/(32.0 - 15.0)/2.0; - // finalColor.rgba = vec4(val+delta, (val+delta)/4.0, 0.0, 1.0); - //} -} - diff --git a/pufferlib/resources/battle/shader_330.vs b/pufferlib/resources/battle/shader_330.vs deleted file mode 100644 index 18ae59858..000000000 --- a/pufferlib/resources/battle/shader_330.vs +++ /dev/null @@ -1,28 +0,0 @@ -#version 330 - -// Input vertex attributes -in vec3 vertexPosition; -in vec2 vertexTexCoord; -in vec3 vertexNormal; -in vec4 vertexColor; - -// Input uniform values -uniform mat4 mvp; - -// Output vertex attributes (to fragment shader) -out vec2 fragTexCoord; -out vec4 fragColor; -out vec3 fragPosition; - -// NOTE: Add your custom variables here - -void main() -{ - // Send vertex attributes to fragment shader - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragPosition = vertexPosition; - - // Calculate final vertex position - gl_Position = mvp*vec4(vertexPosition, 1.0); -} diff --git a/pufferlib/resources/battle/tank.glb b/pufferlib/resources/battle/tank.glb deleted file mode 100644 index fbd804257..000000000 Binary files a/pufferlib/resources/battle/tank.glb and /dev/null differ diff --git a/pufferlib/resources/blastar/blastar_weights.bin b/pufferlib/resources/blastar/blastar_weights.bin deleted file mode 100644 index 8a82b04f4..000000000 Binary files a/pufferlib/resources/blastar/blastar_weights.bin and /dev/null differ diff --git a/pufferlib/resources/blastar/enemy_bullet.png b/pufferlib/resources/blastar/enemy_bullet.png deleted file mode 100644 index 9d905479d..000000000 Binary files a/pufferlib/resources/blastar/enemy_bullet.png and /dev/null differ diff --git a/pufferlib/resources/blastar/enemy_ship.png b/pufferlib/resources/blastar/enemy_ship.png deleted file mode 100644 index a27c1bc61..000000000 Binary files a/pufferlib/resources/blastar/enemy_ship.png and /dev/null differ diff --git a/pufferlib/resources/blastar/player_bullet.png b/pufferlib/resources/blastar/player_bullet.png deleted file mode 100644 index 73c6ebc1e..000000000 Binary files a/pufferlib/resources/blastar/player_bullet.png and /dev/null differ diff --git a/pufferlib/resources/blastar/player_death_explosion.png b/pufferlib/resources/blastar/player_death_explosion.png deleted file mode 100644 index 604c091e3..000000000 Binary files a/pufferlib/resources/blastar/player_death_explosion.png and /dev/null differ diff --git a/pufferlib/resources/blastar/player_ship.png b/pufferlib/resources/blastar/player_ship.png deleted file mode 100644 index dcb2bb3de..000000000 Binary files a/pufferlib/resources/blastar/player_ship.png and /dev/null differ diff --git a/pufferlib/resources/breakout/breakout_weights.bin b/pufferlib/resources/breakout/breakout_weights.bin deleted file mode 100644 index 82c61a9b4..000000000 Binary files a/pufferlib/resources/breakout/breakout_weights.bin and /dev/null differ diff --git a/pufferlib/resources/cartpole/cartpole_weights.bin b/pufferlib/resources/cartpole/cartpole_weights.bin deleted file mode 100644 index 35c0a7579..000000000 Binary files a/pufferlib/resources/cartpole/cartpole_weights.bin and /dev/null differ diff --git a/pufferlib/resources/connect4/connect4_weights.bin b/pufferlib/resources/connect4/connect4_weights.bin deleted file mode 100644 index 11dbaf78f..000000000 Binary files a/pufferlib/resources/connect4/connect4_weights.bin and /dev/null differ diff --git a/pufferlib/resources/convert/convert_weights.bin b/pufferlib/resources/convert/convert_weights.bin deleted file mode 100644 index 312cbb448..000000000 Binary files a/pufferlib/resources/convert/convert_weights.bin and /dev/null differ diff --git a/pufferlib/resources/cpr/cpr_weights.bin b/pufferlib/resources/cpr/cpr_weights.bin deleted file mode 100644 index d0c4213a1..000000000 Binary files a/pufferlib/resources/cpr/cpr_weights.bin and /dev/null differ diff --git a/pufferlib/resources/cpr/inflated_puff.png b/pufferlib/resources/cpr/inflated_puff.png deleted file mode 100644 index c33e7b25c..000000000 Binary files a/pufferlib/resources/cpr/inflated_puff.png and /dev/null differ diff --git a/pufferlib/resources/cpr/puffers_128.png b/pufferlib/resources/cpr/puffers_128.png deleted file mode 100755 index cfedf9d82..000000000 Binary files a/pufferlib/resources/cpr/puffers_128.png and /dev/null differ diff --git a/pufferlib/resources/drive/agent_attempts_goal.png b/pufferlib/resources/drive/agent_attempts_goal.png new file mode 100644 index 000000000..faa48ecc5 Binary files /dev/null and b/pufferlib/resources/drive/agent_attempts_goal.png differ diff --git a/pufferlib/resources/drive/agent_attempts_goal_highligh.png b/pufferlib/resources/drive/agent_attempts_goal_highligh.png new file mode 100644 index 000000000..f7778cb68 Binary files /dev/null and b/pufferlib/resources/drive/agent_attempts_goal_highligh.png differ diff --git a/pufferlib/resources/drive/binaries/map_000.bin b/pufferlib/resources/drive/binaries/map_000.bin new file mode 100644 index 000000000..ae229bdee Binary files /dev/null and b/pufferlib/resources/drive/binaries/map_000.bin differ diff --git a/pufferlib/resources/drive/examples_a_b.png b/pufferlib/resources/drive/examples_a_b.png new file mode 100644 index 000000000..5a323806c Binary files /dev/null and b/pufferlib/resources/drive/examples_a_b.png differ diff --git a/pufferlib/resources/drive/output.gif b/pufferlib/resources/drive/output.gif new file mode 100644 index 000000000..e7a728494 Binary files /dev/null and b/pufferlib/resources/drive/output.gif differ diff --git a/pufferlib/resources/drive/pre_and_post_respawn.png b/pufferlib/resources/drive/pre_and_post_respawn.png new file mode 100644 index 000000000..c3b657804 Binary files /dev/null and b/pufferlib/resources/drive/pre_and_post_respawn.png differ diff --git a/pufferlib/resources/drive/puffer_drive_weights.bin b/pufferlib/resources/drive/puffer_drive_weights.bin index 29737db60..4a5b30e74 100644 Binary files a/pufferlib/resources/drive/puffer_drive_weights.bin and b/pufferlib/resources/drive/puffer_drive_weights.bin differ diff --git a/pufferlib/resources/drive/realistic_collision_event_post_respawn.png b/pufferlib/resources/drive/realistic_collision_event_post_respawn.png new file mode 100644 index 000000000..c73aed681 Binary files /dev/null and b/pufferlib/resources/drive/realistic_collision_event_post_respawn.png differ diff --git a/pufferlib/resources/drone/drone_weights.bin b/pufferlib/resources/drone/drone_weights.bin deleted file mode 100644 index 4961312af..000000000 Binary files a/pufferlib/resources/drone/drone_weights.bin and /dev/null differ diff --git a/pufferlib/resources/enduro/enduro_spritesheet.png b/pufferlib/resources/enduro/enduro_spritesheet.png deleted file mode 100644 index f7308ce44..000000000 Binary files a/pufferlib/resources/enduro/enduro_spritesheet.png and /dev/null differ diff --git a/pufferlib/resources/enduro/enduro_weights.bin b/pufferlib/resources/enduro/enduro_weights.bin deleted file mode 100644 index cff7b9904..000000000 Binary files a/pufferlib/resources/enduro/enduro_weights.bin and /dev/null differ diff --git a/pufferlib/resources/freeway/freeway_weights.bin b/pufferlib/resources/freeway/freeway_weights.bin deleted file mode 100644 index 97b4c5309..000000000 Binary files a/pufferlib/resources/freeway/freeway_weights.bin and /dev/null differ diff --git a/pufferlib/resources/freeway/tex_car_body.png b/pufferlib/resources/freeway/tex_car_body.png deleted file mode 100644 index 3395674a6..000000000 Binary files a/pufferlib/resources/freeway/tex_car_body.png and /dev/null differ diff --git a/pufferlib/resources/freeway/tex_car_wheels.png b/pufferlib/resources/freeway/tex_car_wheels.png deleted file mode 100644 index 6449a55a9..000000000 Binary files a/pufferlib/resources/freeway/tex_car_wheels.png and /dev/null differ diff --git a/pufferlib/resources/freeway/tex_chicken0.png b/pufferlib/resources/freeway/tex_chicken0.png deleted file mode 100644 index 1093605eb..000000000 Binary files a/pufferlib/resources/freeway/tex_chicken0.png and /dev/null differ diff --git a/pufferlib/resources/freeway/tex_truck_body.png b/pufferlib/resources/freeway/tex_truck_body.png deleted file mode 100644 index e65b76d78..000000000 Binary files a/pufferlib/resources/freeway/tex_truck_body.png and /dev/null differ diff --git a/pufferlib/resources/freeway/tex_truck_wheels.png b/pufferlib/resources/freeway/tex_truck_wheels.png deleted file mode 100644 index c12c26aa1..000000000 Binary files a/pufferlib/resources/freeway/tex_truck_wheels.png and /dev/null differ diff --git a/pufferlib/resources/g2048/g2048_weights.bin b/pufferlib/resources/g2048/g2048_weights.bin deleted file mode 100644 index 4dc7989f0..000000000 Binary files a/pufferlib/resources/g2048/g2048_weights.bin and /dev/null differ diff --git a/pufferlib/resources/go/go_weights.bin b/pufferlib/resources/go/go_weights.bin deleted file mode 100644 index 8677889da..000000000 Binary files a/pufferlib/resources/go/go_weights.bin and /dev/null differ diff --git a/pufferlib/resources/impulse_wars/create_texture.py b/pufferlib/resources/impulse_wars/create_texture.py deleted file mode 100644 index fd7c0abde..000000000 --- a/pufferlib/resources/impulse_wars/create_texture.py +++ /dev/null @@ -1,37 +0,0 @@ -import numpy as np -from PIL import Image - -PUFF_BG = (0, 0, 0) -PUFF_CYAN = (0, 121, 241) -PUFF_RED = (187, 0, 0) -PUFF_YELLOW = (160, 160, 0) -PUFF_GREEN = (0, 187, 0) - -img = np.zeros((256, 256, 3), dtype=np.uint8) -img[:] = PUFF_BG - -b = 6 - -img[:128, :b] = PUFF_CYAN -img[:128, 128-b:128] = PUFF_CYAN -img[:b, :128] = PUFF_CYAN -img[128-b:128, :128] = PUFF_CYAN - -img[:128, 128:128+b] = PUFF_RED -img[:128, 256-b:256] = PUFF_RED -img[:b, 128:256] = PUFF_RED -img[128-b:128, 128:256] = PUFF_RED - -img[128:256, :b] = PUFF_YELLOW -img[128:256, 128-b:128] = PUFF_YELLOW -img[128:128+b, :128] = PUFF_YELLOW -img[256-b:256, :128] = PUFF_YELLOW - -img[128:256, 128:256] = (0, 40, 0) -img[128:256, 128:128+b] = PUFF_GREEN -img[128:256, 256-b:256] = PUFF_GREEN -img[128:128+b, 128:256] = PUFF_GREEN -img[256-b:256, 128:256] = PUFF_GREEN - -Image.fromarray(img).save('wall_texture_map.png') - diff --git a/pufferlib/resources/impulse_wars/shaders/gls100/bloom.fs b/pufferlib/resources/impulse_wars/shaders/gls100/bloom.fs deleted file mode 100644 index 0b93594b9..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls100/bloom.fs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2025 Le Juez Victor - * - * This software is provided "as-is", without any express or implied warranty. In no event - * will the authors be held liable for any damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, including commercial - * applications, and to alter it and redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not claim that you - * wrote the original software. If you use this software in a product, an acknowledgment - * in the product documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be misrepresented - * as being the original software. - * - * 3. This notice may not be removed or altered from any source distribution. - */ - -#version 100 - -precision mediump float; - -#define BLOOM_DISABLED 0 -#define BLOOM_ADDITIVE 1 -#define BLOOM_SOFT_LIGHT 2 - -varying vec2 fragTexCoord; - -uniform sampler2D uTexColor; -uniform sampler2D uTexBloomBlur; - -uniform lowp int uBloomMode; -uniform float uBloomIntensity; - -void main() -{ - // Sampling scene color texture - vec3 result = texture2D(uTexColor, fragTexCoord).rgb; - - // Apply bloom - vec3 bloom = texture2D(uTexBloomBlur, fragTexCoord).rgb; - bloom *= uBloomIntensity; - - if (uBloomMode == BLOOM_SOFT_LIGHT) { - bloom = clamp(bloom.rgb, vec3(0.0), vec3(1.0)); - result = max((result + bloom) - (result * bloom), vec3(0.0)); - } else if (uBloomMode == BLOOM_ADDITIVE) { - result += bloom; - } - - // Final color output - gl_FragColor = vec4(result, 1.0); -} \ No newline at end of file diff --git a/pufferlib/resources/impulse_wars/shaders/gls100/blur.fs b/pufferlib/resources/impulse_wars/shaders/gls100/blur.fs deleted file mode 100644 index b6caf9e84..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls100/blur.fs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2025 Le Juez Victor - * - * This software is provided "as-is", without any express or implied warranty. In no event - * will the authors be held liable for any damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, including commercial - * applications, and to alter it and redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not claim that you - * wrote the original software. If you use this software in a product, an acknowledgment - * in the product documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be misrepresented - * as being the original software. - * - * 3. This notice may not be removed or altered from any source distribution. - */ - -// NOTE: The coefficients for the two-pass Gaussian blur were generated using: -// https://lisyarus.github.io/blog/posts/blur-coefficients-generator.html - -#version 100 - -precision mediump float; - -varying vec2 fragTexCoord; - -uniform sampler2D uTexture; -uniform vec2 uTexelDir; - -// hard‐coded offsets & weights -const float O0 = -4.455269417428358; -const float O1 = -2.4751038298192056; -const float O2 = -0.4950160492928827; -const float O3 = 1.485055021558738; -const float O4 = 3.465172537482815; -const float O5 = 5.0; - -const float W0 = 0.14587920530480702; -const float W1 = 0.19230308352110734; -const float W2 = 0.21647621943673803; -const float W3 = 0.20809835496561988; -const float W4 = 0.17082879595769634; -const float W5 = 0.06641434081403137; - -void main() { - vec3 result = vec3(0.0); - result += texture2D(uTexture, fragTexCoord + uTexelDir * O0).rgb * W0; - result += texture2D(uTexture, fragTexCoord + uTexelDir * O1).rgb * W1; - result += texture2D(uTexture, fragTexCoord + uTexelDir * O2).rgb * W2; - result += texture2D(uTexture, fragTexCoord + uTexelDir * O3).rgb * W3; - result += texture2D(uTexture, fragTexCoord + uTexelDir * O4).rgb * W4; - result += texture2D(uTexture, fragTexCoord + uTexelDir * O5).rgb * W5; - - gl_FragColor = vec4(result, 1.0); -} diff --git a/pufferlib/resources/impulse_wars/shaders/gls100/grid.fs b/pufferlib/resources/impulse_wars/shaders/gls100/grid.fs deleted file mode 100644 index 8229aa7da..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls100/grid.fs +++ /dev/null @@ -1,40 +0,0 @@ -#version 100 - -precision highp float; - -// Input vertex attributes (from vertex shader) -varying vec3 fragPosition; -varying vec4 fragColor; - -// Input uniform values -uniform vec2 pos[4]; -uniform vec4 color[4]; - -const float falloff = 6.0; -const float epsilon = 0.1; - -void main() -{ - vec3 lightAccum = vec3(0.0); - - // Texel color fetching from texture sampler - for (int i = 0; i < 4; i++) { - vec2 playerPos = pos[i]; - vec4 playerColor = color[i]; - float dist = distance(playerPos, fragPosition.xz); - if (dist == 0.0) { - continue; - } - - float intensity = falloff / (dist * dist); - lightAccum.r += intensity * (playerColor.r / 255.0); - lightAccum.g += intensity * (playerColor.g / 255.0); - lightAccum.b += intensity * (playerColor.b / 255.0); - } - - if (length(lightAccum) < epsilon) { - discard; - } - - gl_FragColor = vec4(lightAccum, 1.0); -} diff --git a/pufferlib/resources/impulse_wars/shaders/gls100/shader.vs b/pufferlib/resources/impulse_wars/shaders/gls100/shader.vs deleted file mode 100644 index f2b3c6c57..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls100/shader.vs +++ /dev/null @@ -1,32 +0,0 @@ -#version 100 - -// Input vertex attributes -attribute vec3 vertexPosition; -attribute vec2 vertexTexCoord; -attribute vec3 vertexNormal; -attribute vec4 vertexColor; - -// Input uniform values -uniform mat4 mvp; -uniform mat4 matModel; -uniform mat4 matNormal; - -// Output vertex attributes (to fragment shader) -varying vec3 fragPosition; -varying vec2 fragTexCoord; -varying vec4 fragColor; -varying vec3 fragNormal; - -// NOTE: Add here your custom variables - -void main() -{ - // Send vertex attributes to fragment shader - fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0))); - - // Calculate final vertex position - gl_Position = mvp*vec4(vertexPosition, 1.0); -} diff --git a/pufferlib/resources/impulse_wars/shaders/gls330/bloom.fs b/pufferlib/resources/impulse_wars/shaders/gls330/bloom.fs deleted file mode 100644 index 246acb6af..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls330/bloom.fs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2025 Le Juez Victor - * - * This software is provided "as-is", without any express or implied warranty. In no event - * will the authors be held liable for any damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, including commercial - * applications, and to alter it and redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not claim that you - * wrote the original software. If you use this software in a product, an acknowledgment - * in the product documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be misrepresented - * as being the original software. - * - * 3. This notice may not be removed or altered from any source distribution. - */ - -#version 330 core - -#define BLOOM_DISABLED 0 -#define BLOOM_ADDITIVE 1 -#define BLOOM_SOFT_LIGHT 2 - -noperspective in vec2 fragTexCoord; - -uniform sampler2D uTexColor; -uniform sampler2D uTexBloomBlur; - -uniform lowp int uBloomMode; -uniform float uBloomIntensity; - -out vec4 fragColor; - -void main() -{ - // Sampling scene color texture - vec3 result = texture(uTexColor, fragTexCoord).rgb; - - // Apply bloom - vec3 bloom = texture(uTexBloomBlur, fragTexCoord).rgb; - bloom *= uBloomIntensity; - - if (uBloomMode == BLOOM_SOFT_LIGHT) { - bloom = clamp(bloom.rgb, vec3(0.0), vec3(1.0)); - result = max((result + bloom) - (result * bloom), vec3(0.0)); - } else if (uBloomMode == BLOOM_ADDITIVE) { - result += bloom; - } - - // Final color output - fragColor = vec4(result, 1.0); -} \ No newline at end of file diff --git a/pufferlib/resources/impulse_wars/shaders/gls330/blur.fs b/pufferlib/resources/impulse_wars/shaders/gls330/blur.fs deleted file mode 100644 index f853a495e..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls330/blur.fs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2025 Le Juez Victor - * - * This software is provided "as-is", without any express or implied warranty. In no event - * will the authors be held liable for any damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, including commercial - * applications, and to alter it and redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not claim that you - * wrote the original software. If you use this software in a product, an acknowledgment - * in the product documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be misrepresented - * as being the original software. - * - * 3. This notice may not be removed or altered from any source distribution. - */ - -// NOTE: The coefficients for the two-pass Gaussian blur were generated using: -// https://lisyarus.github.io/blog/posts/blur-coefficients-generator.html - -#version 330 core - -noperspective in vec2 fragTexCoord; - -uniform sampler2D uTexture; -uniform vec2 uTexelDir; - -out vec4 fragColor; - -const int SAMPLE_COUNT = 6; - -const float OFFSETS[6] = float[6]( - -4.455269417428358, - -2.4751038298192056, - -0.4950160492928827, - 1.485055021558738, - 3.465172537482815, - 5 -); - -const float WEIGHTS[6] = float[6]( - 0.14587920530480702, - 0.19230308352110734, - 0.21647621943673803, - 0.20809835496561988, - 0.17082879595769634, - 0.06641434081403137 -); - -void main() -{ - vec3 result = vec3(0.0); - - for (int i = 0; i < SAMPLE_COUNT; ++i) - { - result += texture(uTexture, fragTexCoord + uTexelDir * OFFSETS[i]).rgb * WEIGHTS[i]; - } - - fragColor = vec4(result, 1.0); -} diff --git a/pufferlib/resources/impulse_wars/shaders/gls330/grid.fs b/pufferlib/resources/impulse_wars/shaders/gls330/grid.fs deleted file mode 100644 index 71f525d9a..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls330/grid.fs +++ /dev/null @@ -1,40 +0,0 @@ -#version 330 - -// Input vertex attributes (from vertex shader) -in vec3 fragPosition; -in vec4 fragColor; - -// Input uniform values -uniform vec2 pos[4]; -uniform vec4 color[4]; - -// Output fragment color -out vec4 finalColor; - -const float falloff = 6.0; -const float epsilon = 0.1; - -void main() -{ - vec3 lightAccum = vec3(0.0); - - // Texel color fetching from texture sampler - for (int i = 0; i < 4; i++) { - vec2 playerPos = pos[i]; - vec4 playerColor = color[i]; - float dist = distance(playerPos, fragPosition.xz); - if (dist == 0.0) { - continue; - } - - float intensity = falloff / (dist * dist); - lightAccum.r += intensity * (playerColor.r / 255.0); - lightAccum.g += intensity * (playerColor.g / 255.0); - lightAccum.b += intensity * (playerColor.b / 255.0); - } - - if (length(lightAccum) < epsilon) { - discard; - } - finalColor = vec4(lightAccum, 1.0); -} diff --git a/pufferlib/resources/impulse_wars/shaders/gls330/shader.vs b/pufferlib/resources/impulse_wars/shaders/gls330/shader.vs deleted file mode 100644 index f8ec45f19..000000000 --- a/pufferlib/resources/impulse_wars/shaders/gls330/shader.vs +++ /dev/null @@ -1,32 +0,0 @@ -#version 330 - -// Input vertex attributes -in vec3 vertexPosition; -in vec2 vertexTexCoord; -in vec3 vertexNormal; -in vec4 vertexColor; - -// Input uniform values -uniform mat4 mvp; -uniform mat4 matModel; -uniform mat4 matNormal; - -// Output vertex attributes (to fragment shader) -out vec3 fragPosition; -out vec2 fragTexCoord; -out vec4 fragColor; -out vec3 fragNormal; - -// NOTE: Add here your custom variables - -void main() -{ - // Send vertex attributes to fragment shader - fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0))); - - // Calculate final vertex position - gl_Position = mvp*vec4(vertexPosition, 1.0); -} diff --git a/pufferlib/resources/impulse_wars/wall_texture_map.png b/pufferlib/resources/impulse_wars/wall_texture_map.png deleted file mode 100644 index ba711f5d2..000000000 Binary files a/pufferlib/resources/impulse_wars/wall_texture_map.png and /dev/null differ diff --git a/pufferlib/resources/moba/bloom_shader_100.fs b/pufferlib/resources/moba/bloom_shader_100.fs deleted file mode 100644 index 1e1dfbfc7..000000000 --- a/pufferlib/resources/moba/bloom_shader_100.fs +++ /dev/null @@ -1,39 +0,0 @@ -#version 330 - -precision mediump float; - -// Input vertex attributes (from vertex shader) -varying vec2 fragTexCoord; -varying vec4 fragColor; - -// Input uniform values -uniform sampler2D texture0; -uniform vec4 colDiffuse; - -// NOTE: Add here your custom variables - -const vec2 size = vec2(800, 450); // Framebuffer size -const float samples = 5.0; // Pixels per axis; higher = bigger glow, worse performance -const float quality = 2.5; // Defines size factor: Lower = smaller glow, better quality - -void main() -{ - vec4 sum = vec4(0); - vec2 sizeFactor = vec2(1)/size*quality; - - // Texel color fetching from texture sampler - vec4 source = texture2D(texture0, fragTexCoord); - - const int range = 2; // should be = (samples - 1)/2; - - for (int x = -range; x <= range; x++) - { - for (int y = -range; y <= range; y++) - { - sum += texture2D(texture0, fragTexCoord + vec2(x, y)*sizeFactor); - } - } - - // Calculate final fragment color - gl_FragColor = ((sum/(samples*samples)) + source)*colDiffuse; -} diff --git a/pufferlib/resources/moba/bloom_shader_330.fs b/pufferlib/resources/moba/bloom_shader_330.fs deleted file mode 100644 index 028b84866..000000000 --- a/pufferlib/resources/moba/bloom_shader_330.fs +++ /dev/null @@ -1,39 +0,0 @@ -#version 330 - -//precision mediump float; - -// Input vertex attributes (from vertex shader) -varying vec2 fragTexCoord; -varying vec4 fragColor; - -// Input uniform values -uniform sampler2D texture0; -uniform vec4 colDiffuse; - -// NOTE: Add here your custom variables - -const vec2 size = vec2(800, 450); // Framebuffer size -const float samples = 5.0; // Pixels per axis; higher = bigger glow, worse performance -const float quality = 2.5; // Defines size factor: Lower = smaller glow, better quality - -void main() -{ - vec4 sum = vec4(0); - vec2 sizeFactor = vec2(1)/size*quality; - - // Texel color fetching from texture sampler - vec4 source = texture2D(texture0, fragTexCoord); - - const int range = 2; // should be = (samples - 1)/2; - - for (int x = -range; x <= range; x++) - { - for (int y = -range; y <= range; y++) - { - sum += texture2D(texture0, fragTexCoord + vec2(x, y)*sizeFactor); - } - } - - // Calculate final fragment color - gl_FragColor = ((sum/(samples*samples)) + source)*colDiffuse; -} diff --git a/pufferlib/resources/moba/dota_map.png b/pufferlib/resources/moba/dota_map.png deleted file mode 100644 index 199c8a634..000000000 Binary files a/pufferlib/resources/moba/dota_map.png and /dev/null differ diff --git a/pufferlib/resources/moba/game_map.npy b/pufferlib/resources/moba/game_map.npy deleted file mode 100644 index 3fb1b41a3..000000000 Binary files a/pufferlib/resources/moba/game_map.npy and /dev/null differ diff --git a/pufferlib/resources/moba/map_shader_100.fs b/pufferlib/resources/moba/map_shader_100.fs deleted file mode 100644 index 38cb967d7..000000000 --- a/pufferlib/resources/moba/map_shader_100.fs +++ /dev/null @@ -1,86 +0,0 @@ -#version 100 - -// Vertex shader will need to pass these as varyings -precision mediump float; - -varying vec2 fragTexCoord; -varying vec4 fragColor; - -// Input uniform values -uniform sampler2D texture0; -uniform sampler2D texture1; -uniform vec4 colDiffuse; - -//uniform vec3 resolution; -uniform vec4 mouse; -uniform float time; -uniform float camera_x; -uniform float camera_y; - -const float SCALE_FACTOR = 16.0; -const float GLOW = 32.0; -const float DIST = GLOW / SCALE_FACTOR; - -float rand(vec2 co){ - return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - -float round(float x) { - return floor(x + 0.5); -} - -void main() -{ - vec3 resolution = vec3(2560.0, 1440.0, 1.0); - float CONVERT_X = resolution.x / 32.0 / 128.0; - float CONVERT_Y = resolution.y / 32.0 / 128.0; - - vec2 scaledTexCoord = vec2(CONVERT_X * fragTexCoord.x + camera_x, CONVERT_Y * fragTexCoord.y + camera_y); - float dist = DIST / resolution.x; // Pass texture size as uniform from JS - - float borderColor = 0.8 + 0.2 * sin(time); - - vec4 texelColor = texture2D(texture1, scaledTexCoord) * colDiffuse * fragColor; - vec4 texelRight = texture2D(texture1, vec2(scaledTexCoord.x + dist, scaledTexCoord.y)) * colDiffuse * fragColor; - vec4 texelLeft = texture2D(texture1, vec2(scaledTexCoord.x - dist, scaledTexCoord.y)) * colDiffuse * fragColor; - vec4 texelDown = texture2D(texture1, vec2(scaledTexCoord.x, scaledTexCoord.y + dist)) * colDiffuse * fragColor; - vec4 texelUp = texture2D(texture1, vec2(scaledTexCoord.x, scaledTexCoord.y - dist)) * colDiffuse * fragColor; - - vec4 texelRightDown = texture2D(texture1, vec2(scaledTexCoord.x + dist, scaledTexCoord.y + dist)) * colDiffuse * fragColor; - vec4 texelLeftDown = texture2D(texture1, vec2(scaledTexCoord.x - dist, scaledTexCoord.y + dist)) * colDiffuse * fragColor; - vec4 texelRightUp = texture2D(texture1, vec2(scaledTexCoord.x + dist, scaledTexCoord.y - dist)) * colDiffuse * fragColor; - vec4 texelLeftUp = texture2D(texture1, vec2(scaledTexCoord.x - dist, scaledTexCoord.y - dist)) * colDiffuse * fragColor; - - vec2 tilePos = fract(scaledTexCoord * resolution.xy); - - bool isBorder = (texelColor.rgb == vec3(0.0, 0.0, 0.0)) && ( - (texelColor != texelRight) || - (texelColor != texelDown) || - (texelColor != texelLeft) || - (texelColor != texelUp) || - (texelColor != texelRightDown) || - (texelColor != texelLeftDown) || - (texelColor != texelRightUp) || - (texelColor != texelLeftUp)); - - float lerp = 10.0 * (scaledTexCoord.x - scaledTexCoord.y); - float lerp_red = clamp(lerp, 0.0, 1.0); - float lerp_cyan = clamp(1.0 - lerp, 0.0, 1.0); - - float inp_x = round(4096.0 * scaledTexCoord.x) / 8.0; - float inp_y = round(4096.0 * scaledTexCoord.y) / 8.0; - - vec2 inp = vec2(inp_x, inp_y); - - if (isBorder) { - gl_FragColor = vec4(lerp_red * borderColor, lerp_cyan * borderColor, (lerp_cyan + 0.5) * borderColor, 1.0); - } else if (texelColor.rgb == vec3(1.0, 1.0, 1.0)) { - gl_FragColor = vec4(18.0 / 255.0 * lerp_red + 6.0 / 255.0, 18.0 / 255.0 * lerp_cyan + 6.0 / 255.0, 18.0 / 255.0 * lerp_cyan + 6.0 / 255.0, 1.0); - float noise = sin(100.0 * inp.x - 100.0 * inp.y + cos(100.0 * inp.y)); - gl_FragColor.rgb += 0.005 + 0.005 * vec3(lerp_red * noise, lerp_cyan * noise, lerp_cyan * noise); - } else if (texelColor.rgb == vec3(0.0, 0.0, 0.0)) { - gl_FragColor = vec4(0.5 * lerp_red, 0.5 * lerp_cyan, 0.5 * lerp_cyan, 1.0); - } else { - gl_FragColor = texelColor; - } -} diff --git a/pufferlib/resources/moba/map_shader_330.fs b/pufferlib/resources/moba/map_shader_330.fs deleted file mode 100644 index 2ee7bd4b3..000000000 --- a/pufferlib/resources/moba/map_shader_330.fs +++ /dev/null @@ -1,150 +0,0 @@ -#version 330 - -// Input vertex attributes (from vertex shader) -in vec2 fragTexCoord; // Fragment input: vertex attribute: texture coordinates -in vec4 fragColor; // Fragment input: vertex attribute: color - -// Input uniform values -uniform sampler2D texture0; // Fragment input: texture -uniform sampler2D texture1; // Fragment input: texture -uniform vec4 colDiffuse; // Fragment input: tint color normalized [0.0f..1.0f] - -uniform vec3 resolution; // Fragment input: .xy texture resolution in pixels, .z scale factor -uniform vec4 mouse; // Fragment input: .xy mouse position on texture in pixels, .z mouse LMB down, .w mouse RMB down -uniform float time; // Fragment input: elapsed time in seconds since program started -uniform float camera_x; -uniform float camera_y; - -// Output fragment color -out vec4 outputColor; // Fragment output: pixel color - -// Fixed scale factor -const float SCALE_FACTOR = 16.0; - -// Glow parameter (number of border pixels to alter) -const float GLOW = 4.0; - -const float DIST = GLOW / SCALE_FACTOR; - -float rand(vec2 co){ - return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - -void main() -{ - //float CONVERT_X = 255.0 / textureSize(texture0, 0).x / SCALE_FACTOR; - //float CONVERT_Y = 255.0 / textureSize(texture0, 0).y / SCALE_FACTOR; - - //float CONVERT_X = 41.0 / 255.0; - //float CONVERT_Y = 23.0 / 255.0; - - //float convert_x = 41.0 / 128.0; - //float convert_y = 23.0 / 128.0; - - float CONVERT_X = textureSize(texture0, 0).x / 32 / 128.0; - float CONVERT_Y = textureSize(texture0, 0).y / 32 / 128.0; - - // Calculate the scaled texture coordinates - //vec2 scaledTexCoord = vec2(CONVERT_X*fragTexCoord.x, CONVERT_Y*fragTexCoord.y); - vec2 scaledTexCoord = vec2(CONVERT_X*fragTexCoord.x + camera_x, CONVERT_Y*fragTexCoord.y + camera_y); - float dist = DIST / textureSize(texture1, 0).x; - - // Calculate the scaled texture coordinates - //vec2 scaledTexCoord = vec2(fragTexCoord.x/SCALE_FACTOR + camera_x, fragTexCoord.y/SCALE_FACTOR + camera_y); - //float dist = DIST / textureSize(texture1, 0).x; - - float borderColor = 0.8 + 0.2*sin(time); - - // Texel color fetching from texture sampler with scaled coordinates - vec4 texelColor = texture(texture1, scaledTexCoord) * colDiffuse * fragColor; - vec4 texelRight = texture(texture1, vec2(scaledTexCoord.x + dist, scaledTexCoord.y)) * colDiffuse * fragColor; - vec4 texelLeft = texture(texture1, vec2(scaledTexCoord.x - dist, scaledTexCoord.y)) * colDiffuse * fragColor; - vec4 texelDown = texture(texture1, vec2(scaledTexCoord.x, scaledTexCoord.y + dist)) * colDiffuse * fragColor; - vec4 texelUp = texture(texture1, vec2(scaledTexCoord.x, scaledTexCoord.y - dist)) * colDiffuse * fragColor; - - vec4 texelRightDown = texture(texture1, vec2(scaledTexCoord.x + dist, scaledTexCoord.y + dist)) * colDiffuse * fragColor; - vec4 texelLeftDown = texture(texture1, vec2(scaledTexCoord.x - dist, scaledTexCoord.y + dist)) * colDiffuse * fragColor; - vec4 texelRightUp = texture(texture1, vec2(scaledTexCoord.x + dist, scaledTexCoord.y - dist)) * colDiffuse * fragColor; - vec4 texelLeftUp = texture(texture1, vec2(scaledTexCoord.x - dist, scaledTexCoord.y - dist)) * colDiffuse * fragColor; - - // Calculate the position within the scaled tile - vec2 tilePos = fract(scaledTexCoord * resolution.xy); - - // Check if the pixel is on the border of the tile - bool isBorder = - (texelColor.rgb == vec3(0.0, 0.0, 0.0)) && ( - (texelColor != texelRight) || - (texelColor != texelDown) || - (texelColor != texelLeft) || - (texelColor != texelUp) || - (texelColor != texelRightDown) || - (texelColor != texelLeftDown) || - (texelColor != texelRightUp) || - (texelColor != texelLeftUp)); - - float lerp = 10*(scaledTexCoord.x - scaledTexCoord.y); - float lerp_red = clamp(lerp, 0, 1); - float lerp_cyan = clamp(1.0-lerp, 0, 1); - - // Add some noise - //float inp_x = round(512 * scaledTexCoord.x); - //float inp_y = round(512 * scaledTexCoord.y); - - float inp_x = round(4096 * scaledTexCoord.x) / 8.0; - float inp_y = round(4096 * scaledTexCoord.y) / 8.0; - - vec2 inp = vec2(inp_x, inp_y); - //float noise = sin(inp.x * 0.01 + time * 0.1) * sin(inp.y * 0.01 + time * 0.1); - //float noise = sin(0.1*(inp.x - inp.y)) + sin(0.1*(inp.x + inp.y)); - //float noise = rand(inp); - - if (isBorder) { - // Change border pixels to (0, 128, 128, 255) - outputColor = vec4(lerp_red*borderColor, lerp_cyan*borderColor, (lerp_cyan+0.5)*borderColor, 1.0); - } else if (texelColor.rgb == vec3(1.0, 1.0, 1.0)) { - // Change white pixels to (6, 24, 24, 255) - outputColor = vec4(18.0/255.0*lerp_red + 6.0/255.0, 18.0/255.0*lerp_cyan + 6.0/255.0, 18.0/255.0*lerp_cyan + 6.0/255.0, 1.0); - float noise = sin(100*inp.x - 100*inp.y + cos(100*inp.y)); - outputColor.rgb += 0.005 + 0.005*vec3(lerp_red*noise, lerp_cyan*noise, lerp_cyan*noise); - } else if (texelColor.rgb == vec3(0.0, 0.0, 0.0)) { - // Change black pixels to cyan (0, 255, 255, 255) - outputColor = vec4(0.5*lerp_red, 0.5*lerp_cyan, 0.5*lerp_cyan, 1.0); - } else { - // Keep other colors unchanged - outputColor = texelColor; - } - - - /* - if (scaledTexCoord.x < scaledTexCoord.y) { - if (isBorder) { - // Change border pixels to (0, 128, 128, 255) - outputColor = vec4(0.0, borderColor, borderColor, 1.0); - } else if (texelColor.rgb == vec3(1.0, 1.0, 1.0)) { - // Change white pixels to (6, 24, 24, 255) - outputColor = vec4(6.0/255.0, 24.0/255.0, 24.0/255.0, 1.0); - } else if (texelColor.rgb == vec3(0.0, 0.0, 0.0)) { - // Change black pixels to cyan (0, 255, 255, 255) - outputColor = vec4(0.0, 0.5, 0.5, 1.0); - } else { - // Keep other colors unchanged - outputColor = texelColor; - } - } else { - if (isBorder) { - // Change border pixels to (128, 0, 0, 255) - outputColor = vec4(borderColor, 0.0, 0.0, 1.0); - } else if (texelColor.rgb == vec3(1.0, 1.0, 1.0)) { - // Change white pixels to (24, 6, 6, 255) - outputColor = vec4(24.0/255.0, 6.0/255.0, 6.0/255.0, 1.0); - } else if (texelColor.rgb == vec3(0.0, 0.0, 0.0)) { - // Change black pixels to red (255, 0, 0, 255) - outputColor = vec4(0.5, 0.0, 0.0, 1.0); - } else { - // Keep other colors unchanged - outputColor = texelColor; - } - } - */ -} - diff --git a/pufferlib/resources/moba/moba_assets.png b/pufferlib/resources/moba/moba_assets.png deleted file mode 100755 index 10d28efcb..000000000 Binary files a/pufferlib/resources/moba/moba_assets.png and /dev/null differ diff --git a/pufferlib/resources/moba/moba_weights.bin b/pufferlib/resources/moba/moba_weights.bin deleted file mode 100644 index cbd29a7af..000000000 Binary files a/pufferlib/resources/moba/moba_weights.bin and /dev/null differ diff --git a/pufferlib/resources/nmmo3/ASSETS_LICENSE.md b/pufferlib/resources/nmmo3/ASSETS_LICENSE.md deleted file mode 100644 index 44515579f..000000000 --- a/pufferlib/resources/nmmo3/ASSETS_LICENSE.md +++ /dev/null @@ -1 +0,0 @@ -Characters and assets subject to the license of the original artists. In particular, we use Mana Seed assets by Seliel the Shaper under a valid license purchased from itch.io. You may not repurpose these assets for other projects without purchasing your own license. To mitigate abuse, we release only collated spritesheets as exported by our postprocessor. diff --git a/pufferlib/resources/nmmo3/air_0.png b/pufferlib/resources/nmmo3/air_0.png deleted file mode 100644 index 624223ece..000000000 Binary files a/pufferlib/resources/nmmo3/air_0.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_1.png b/pufferlib/resources/nmmo3/air_1.png deleted file mode 100644 index 87f522f42..000000000 Binary files a/pufferlib/resources/nmmo3/air_1.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_2.png b/pufferlib/resources/nmmo3/air_2.png deleted file mode 100644 index 0a1a4d559..000000000 Binary files a/pufferlib/resources/nmmo3/air_2.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_3.png b/pufferlib/resources/nmmo3/air_3.png deleted file mode 100644 index 4cf5f8184..000000000 Binary files a/pufferlib/resources/nmmo3/air_3.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_4.png b/pufferlib/resources/nmmo3/air_4.png deleted file mode 100644 index 047a4c468..000000000 Binary files a/pufferlib/resources/nmmo3/air_4.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_5.png b/pufferlib/resources/nmmo3/air_5.png deleted file mode 100644 index 97acec4a4..000000000 Binary files a/pufferlib/resources/nmmo3/air_5.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_6.png b/pufferlib/resources/nmmo3/air_6.png deleted file mode 100644 index 9b6fa25eb..000000000 Binary files a/pufferlib/resources/nmmo3/air_6.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_7.png b/pufferlib/resources/nmmo3/air_7.png deleted file mode 100644 index 90cae931b..000000000 Binary files a/pufferlib/resources/nmmo3/air_7.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_8.png b/pufferlib/resources/nmmo3/air_8.png deleted file mode 100644 index c9395f554..000000000 Binary files a/pufferlib/resources/nmmo3/air_8.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/air_9.png b/pufferlib/resources/nmmo3/air_9.png deleted file mode 100644 index b4fa1d31d..000000000 Binary files a/pufferlib/resources/nmmo3/air_9.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_0.png b/pufferlib/resources/nmmo3/earth_0.png deleted file mode 100644 index e8a405ed1..000000000 Binary files a/pufferlib/resources/nmmo3/earth_0.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_1.png b/pufferlib/resources/nmmo3/earth_1.png deleted file mode 100644 index d991bf00e..000000000 Binary files a/pufferlib/resources/nmmo3/earth_1.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_2.png b/pufferlib/resources/nmmo3/earth_2.png deleted file mode 100644 index d396be45b..000000000 Binary files a/pufferlib/resources/nmmo3/earth_2.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_3.png b/pufferlib/resources/nmmo3/earth_3.png deleted file mode 100644 index 4df1546a4..000000000 Binary files a/pufferlib/resources/nmmo3/earth_3.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_4.png b/pufferlib/resources/nmmo3/earth_4.png deleted file mode 100644 index c715a5d7c..000000000 Binary files a/pufferlib/resources/nmmo3/earth_4.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_5.png b/pufferlib/resources/nmmo3/earth_5.png deleted file mode 100644 index 75e1b70b9..000000000 Binary files a/pufferlib/resources/nmmo3/earth_5.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_6.png b/pufferlib/resources/nmmo3/earth_6.png deleted file mode 100644 index ed2fa3d70..000000000 Binary files a/pufferlib/resources/nmmo3/earth_6.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_7.png b/pufferlib/resources/nmmo3/earth_7.png deleted file mode 100644 index 0105c53a2..000000000 Binary files a/pufferlib/resources/nmmo3/earth_7.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_8.png b/pufferlib/resources/nmmo3/earth_8.png deleted file mode 100644 index 215e95989..000000000 Binary files a/pufferlib/resources/nmmo3/earth_8.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/earth_9.png b/pufferlib/resources/nmmo3/earth_9.png deleted file mode 100644 index c00f505f4..000000000 Binary files a/pufferlib/resources/nmmo3/earth_9.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_0.png b/pufferlib/resources/nmmo3/fire_0.png deleted file mode 100644 index f141d886f..000000000 Binary files a/pufferlib/resources/nmmo3/fire_0.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_1.png b/pufferlib/resources/nmmo3/fire_1.png deleted file mode 100644 index cdd3b216a..000000000 Binary files a/pufferlib/resources/nmmo3/fire_1.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_2.png b/pufferlib/resources/nmmo3/fire_2.png deleted file mode 100644 index b7ffe84da..000000000 Binary files a/pufferlib/resources/nmmo3/fire_2.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_3.png b/pufferlib/resources/nmmo3/fire_3.png deleted file mode 100644 index 6e5f48d36..000000000 Binary files a/pufferlib/resources/nmmo3/fire_3.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_4.png b/pufferlib/resources/nmmo3/fire_4.png deleted file mode 100644 index 2a0017a1d..000000000 Binary files a/pufferlib/resources/nmmo3/fire_4.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_5.png b/pufferlib/resources/nmmo3/fire_5.png deleted file mode 100644 index b81021075..000000000 Binary files a/pufferlib/resources/nmmo3/fire_5.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_6.png b/pufferlib/resources/nmmo3/fire_6.png deleted file mode 100644 index 64d3cf9c8..000000000 Binary files a/pufferlib/resources/nmmo3/fire_6.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_7.png b/pufferlib/resources/nmmo3/fire_7.png deleted file mode 100644 index c23ba2db8..000000000 Binary files a/pufferlib/resources/nmmo3/fire_7.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_8.png b/pufferlib/resources/nmmo3/fire_8.png deleted file mode 100644 index 2c302a117..000000000 Binary files a/pufferlib/resources/nmmo3/fire_8.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/fire_9.png b/pufferlib/resources/nmmo3/fire_9.png deleted file mode 100644 index 76b23b9b6..000000000 Binary files a/pufferlib/resources/nmmo3/fire_9.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/inventory_64.png b/pufferlib/resources/nmmo3/inventory_64.png deleted file mode 100644 index b1eb19de1..000000000 Binary files a/pufferlib/resources/nmmo3/inventory_64.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/inventory_64_press.png b/pufferlib/resources/nmmo3/inventory_64_press.png deleted file mode 100644 index 3a7304bd0..000000000 Binary files a/pufferlib/resources/nmmo3/inventory_64_press.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/inventory_64_selected.png b/pufferlib/resources/nmmo3/inventory_64_selected.png deleted file mode 100644 index 7801e0285..000000000 Binary files a/pufferlib/resources/nmmo3/inventory_64_selected.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/items_condensed.png b/pufferlib/resources/nmmo3/items_condensed.png deleted file mode 100644 index 874f13896..000000000 Binary files a/pufferlib/resources/nmmo3/items_condensed.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/map_shader_100.fs b/pufferlib/resources/nmmo3/map_shader_100.fs deleted file mode 100644 index bf7dee1c5..000000000 --- a/pufferlib/resources/nmmo3/map_shader_100.fs +++ /dev/null @@ -1,57 +0,0 @@ -precision mediump float; - -// Input uniforms (unchanged from original) -uniform sampler2D terrain; -uniform sampler2D texture_tiles; -uniform vec4 colDiffuse; -uniform vec3 resolution; -uniform vec4 mouse; -uniform float time; -uniform float camera_x; -uniform float camera_y; -uniform float map_width; -uniform float map_height; - -// Constants -const float TILE_SIZE = 64.0; -const float TILES_PER_ROW = 64.0; - -void main() -{ - float ts = TILE_SIZE * resolution.z; - // Get the screen pixel coordinates - vec2 pixelPos = gl_FragCoord.xy; - - float x_offset = camera_x/64.0 + pixelPos.x/ts - resolution.x/ts/2.0; - float y_offset = camera_y/64.0 - pixelPos.y/ts + resolution.y/ts/2.0; - float x_floor = floor(x_offset); - float y_floor = floor(y_offset); - float x_frac = x_offset - x_floor; - float y_frac = y_offset - y_floor; - - // Environment size calculation - vec2 uv = vec2( - x_floor/map_width, - y_floor/map_height - ); - - vec2 tile_rg = texture2D(terrain, uv).rg; - float tile_high_byte = floor(tile_rg.r * 255.0); - float tile_low_byte = floor(tile_rg.g * 255.0); - float tile = tile_high_byte * 64.0 + tile_low_byte; - - // Handle animated tiles - if (tile >= 240.0 && tile < (240.0 + 4.0*4.0*4.0*4.0)) { - tile += floor(3.9 * time); - } - - tile_high_byte = floor(tile/64.0); - tile_low_byte = floor(mod(tile, 64.0)); - - vec2 tile_uv = vec2( - tile_low_byte/64.0 + x_frac/64.0, - tile_high_byte/64.0 + y_frac/64.0 - ); - - gl_FragColor = texture2D(texture_tiles, tile_uv); -} diff --git a/pufferlib/resources/nmmo3/map_shader_330.fs b/pufferlib/resources/nmmo3/map_shader_330.fs deleted file mode 100644 index 8b8ac6ee7..000000000 --- a/pufferlib/resources/nmmo3/map_shader_330.fs +++ /dev/null @@ -1,68 +0,0 @@ -#version 330 - -// Input vertex attributes (from vertex shader) -in vec2 fragTexCoord; -in vec4 fragColor; - -// Input uniform values -uniform sampler2D terrain; -uniform sampler2D texture_tiles; // Tile sprite sheet texture -uniform vec4 colDiffuse; -uniform vec3 resolution; -uniform vec4 mouse; -uniform float time; -uniform float camera_x; -uniform float camera_y; -uniform float map_width; -uniform float map_height; - -// Output fragment color -out vec4 outputColor; - -float TILE_SIZE = 64.0; - -// Number of tiles per row in the sprite sheet -const int TILES_PER_ROW = 64; // Adjust this based on your sprite sheet layout - -void main() -{ - float ts = TILE_SIZE * resolution.z; - - // Get the screen pixel coordinates - vec2 pixelPos = gl_FragCoord.xy; - - float x_offset = camera_x/64.0 + pixelPos.x/ts - resolution.x/ts/2.0; - float y_offset = camera_y/64.0 - pixelPos.y/ts + resolution.y/ts/2.0; - - float x_floor = floor(x_offset); - float y_floor = floor(y_offset); - - float x_frac = x_offset - x_floor; - float y_frac = y_offset - y_floor; - - // TODO: This is the env size - vec2 uv = vec2( - x_floor/map_width, - y_floor/map_height - ); - vec2 tile_rg = texture(terrain, uv).rg; - - int tile_high_byte = int(tile_rg.r*255.0); - int tile_low_byte = int(tile_rg.g*255.0); - - int tile = tile_high_byte*64 + tile_low_byte; - if (tile >= 240 && tile < 240+4*4*4*4) { - tile += int(3.9*time); - } - - tile_high_byte = int(tile/64.0); - tile_low_byte = int(tile%64); - - vec2 tile_uv = vec2( - tile_low_byte/64.0 + x_frac/64.0, - tile_high_byte/64.0 + y_frac/64.0 - ); - - outputColor = texture(texture_tiles, tile_uv); -} - diff --git a/pufferlib/resources/nmmo3/merged_sheet.png b/pufferlib/resources/nmmo3/merged_sheet.png deleted file mode 100644 index 580ae5b66..000000000 Binary files a/pufferlib/resources/nmmo3/merged_sheet.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_0.png b/pufferlib/resources/nmmo3/neutral_0.png deleted file mode 100644 index d76713d48..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_0.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_1.png b/pufferlib/resources/nmmo3/neutral_1.png deleted file mode 100644 index 5dcc1e8ad..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_1.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_2.png b/pufferlib/resources/nmmo3/neutral_2.png deleted file mode 100644 index c3dfc5a65..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_2.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_3.png b/pufferlib/resources/nmmo3/neutral_3.png deleted file mode 100644 index 64719d36a..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_3.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_4.png b/pufferlib/resources/nmmo3/neutral_4.png deleted file mode 100644 index 484181411..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_4.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_5.png b/pufferlib/resources/nmmo3/neutral_5.png deleted file mode 100644 index 444d00b03..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_5.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_6.png b/pufferlib/resources/nmmo3/neutral_6.png deleted file mode 100644 index 245c24c17..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_6.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_7.png b/pufferlib/resources/nmmo3/neutral_7.png deleted file mode 100644 index ce005436c..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_7.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_8.png b/pufferlib/resources/nmmo3/neutral_8.png deleted file mode 100644 index 520803743..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_8.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/neutral_9.png b/pufferlib/resources/nmmo3/neutral_9.png deleted file mode 100644 index ee1fbd0b3..000000000 Binary files a/pufferlib/resources/nmmo3/neutral_9.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/nmmo3_help.png b/pufferlib/resources/nmmo3/nmmo3_help.png deleted file mode 100644 index a4c7a9607..000000000 Binary files a/pufferlib/resources/nmmo3/nmmo3_help.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/nmmo3_weights.bin b/pufferlib/resources/nmmo3/nmmo3_weights.bin deleted file mode 100644 index 515b14962..000000000 Binary files a/pufferlib/resources/nmmo3/nmmo3_weights.bin and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_0.png b/pufferlib/resources/nmmo3/water_0.png deleted file mode 100644 index e743a38a3..000000000 Binary files a/pufferlib/resources/nmmo3/water_0.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_1.png b/pufferlib/resources/nmmo3/water_1.png deleted file mode 100644 index fb41fefae..000000000 Binary files a/pufferlib/resources/nmmo3/water_1.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_2.png b/pufferlib/resources/nmmo3/water_2.png deleted file mode 100644 index 1f228a41e..000000000 Binary files a/pufferlib/resources/nmmo3/water_2.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_3.png b/pufferlib/resources/nmmo3/water_3.png deleted file mode 100644 index 10ec9bd04..000000000 Binary files a/pufferlib/resources/nmmo3/water_3.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_4.png b/pufferlib/resources/nmmo3/water_4.png deleted file mode 100644 index 14a015a6a..000000000 Binary files a/pufferlib/resources/nmmo3/water_4.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_5.png b/pufferlib/resources/nmmo3/water_5.png deleted file mode 100644 index d132d09b7..000000000 Binary files a/pufferlib/resources/nmmo3/water_5.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_6.png b/pufferlib/resources/nmmo3/water_6.png deleted file mode 100644 index 9979284bc..000000000 Binary files a/pufferlib/resources/nmmo3/water_6.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_7.png b/pufferlib/resources/nmmo3/water_7.png deleted file mode 100644 index 1eeec44cf..000000000 Binary files a/pufferlib/resources/nmmo3/water_7.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_8.png b/pufferlib/resources/nmmo3/water_8.png deleted file mode 100644 index 6c57ecee7..000000000 Binary files a/pufferlib/resources/nmmo3/water_8.png and /dev/null differ diff --git a/pufferlib/resources/nmmo3/water_9.png b/pufferlib/resources/nmmo3/water_9.png deleted file mode 100644 index 323f3850d..000000000 Binary files a/pufferlib/resources/nmmo3/water_9.png and /dev/null differ diff --git a/pufferlib/resources/pacman/blinky_down.png b/pufferlib/resources/pacman/blinky_down.png deleted file mode 100644 index d6409ee7a..000000000 Binary files a/pufferlib/resources/pacman/blinky_down.png and /dev/null differ diff --git a/pufferlib/resources/pacman/blinky_left.png b/pufferlib/resources/pacman/blinky_left.png deleted file mode 100644 index bd2428fb8..000000000 Binary files a/pufferlib/resources/pacman/blinky_left.png and /dev/null differ diff --git a/pufferlib/resources/pacman/blinky_right.png b/pufferlib/resources/pacman/blinky_right.png deleted file mode 100644 index 5595b72a9..000000000 Binary files a/pufferlib/resources/pacman/blinky_right.png and /dev/null differ diff --git a/pufferlib/resources/pacman/blinky_up.png b/pufferlib/resources/pacman/blinky_up.png deleted file mode 100644 index dac4ff658..000000000 Binary files a/pufferlib/resources/pacman/blinky_up.png and /dev/null differ diff --git a/pufferlib/resources/pacman/clyde_down.png b/pufferlib/resources/pacman/clyde_down.png deleted file mode 100644 index cd76c448a..000000000 Binary files a/pufferlib/resources/pacman/clyde_down.png and /dev/null differ diff --git a/pufferlib/resources/pacman/clyde_left.png b/pufferlib/resources/pacman/clyde_left.png deleted file mode 100644 index 3e01829c3..000000000 Binary files a/pufferlib/resources/pacman/clyde_left.png and /dev/null differ diff --git a/pufferlib/resources/pacman/clyde_right.png b/pufferlib/resources/pacman/clyde_right.png deleted file mode 100644 index 5a38d2695..000000000 Binary files a/pufferlib/resources/pacman/clyde_right.png and /dev/null differ diff --git a/pufferlib/resources/pacman/clyde_up.png b/pufferlib/resources/pacman/clyde_up.png deleted file mode 100644 index d7ac243fc..000000000 Binary files a/pufferlib/resources/pacman/clyde_up.png and /dev/null differ diff --git a/pufferlib/resources/pacman/eyes_down.png b/pufferlib/resources/pacman/eyes_down.png deleted file mode 100644 index debcf0449..000000000 Binary files a/pufferlib/resources/pacman/eyes_down.png and /dev/null differ diff --git a/pufferlib/resources/pacman/eyes_left.png b/pufferlib/resources/pacman/eyes_left.png deleted file mode 100644 index 055e1782c..000000000 Binary files a/pufferlib/resources/pacman/eyes_left.png and /dev/null differ diff --git a/pufferlib/resources/pacman/eyes_right.png b/pufferlib/resources/pacman/eyes_right.png deleted file mode 100644 index 2d33fe8b1..000000000 Binary files a/pufferlib/resources/pacman/eyes_right.png and /dev/null differ diff --git a/pufferlib/resources/pacman/eyes_up.png b/pufferlib/resources/pacman/eyes_up.png deleted file mode 100644 index dff8dc873..000000000 Binary files a/pufferlib/resources/pacman/eyes_up.png and /dev/null differ diff --git a/pufferlib/resources/pacman/inky_down.png b/pufferlib/resources/pacman/inky_down.png deleted file mode 100644 index 232621e95..000000000 Binary files a/pufferlib/resources/pacman/inky_down.png and /dev/null differ diff --git a/pufferlib/resources/pacman/inky_left.png b/pufferlib/resources/pacman/inky_left.png deleted file mode 100644 index e34ced288..000000000 Binary files a/pufferlib/resources/pacman/inky_left.png and /dev/null differ diff --git a/pufferlib/resources/pacman/inky_right.png b/pufferlib/resources/pacman/inky_right.png deleted file mode 100644 index 5ba36239d..000000000 Binary files a/pufferlib/resources/pacman/inky_right.png and /dev/null differ diff --git a/pufferlib/resources/pacman/inky_up.png b/pufferlib/resources/pacman/inky_up.png deleted file mode 100644 index 4921b4eb8..000000000 Binary files a/pufferlib/resources/pacman/inky_up.png and /dev/null differ diff --git a/pufferlib/resources/pacman/pacman_weights.bin b/pufferlib/resources/pacman/pacman_weights.bin deleted file mode 100644 index 55e1fa11c..000000000 Binary files a/pufferlib/resources/pacman/pacman_weights.bin and /dev/null differ diff --git a/pufferlib/resources/pacman/pinky_down.png b/pufferlib/resources/pacman/pinky_down.png deleted file mode 100644 index 90ebd9bbb..000000000 Binary files a/pufferlib/resources/pacman/pinky_down.png and /dev/null differ diff --git a/pufferlib/resources/pacman/pinky_left.png b/pufferlib/resources/pacman/pinky_left.png deleted file mode 100644 index db976d9cb..000000000 Binary files a/pufferlib/resources/pacman/pinky_left.png and /dev/null differ diff --git a/pufferlib/resources/pacman/pinky_right.png b/pufferlib/resources/pacman/pinky_right.png deleted file mode 100644 index fa8240f44..000000000 Binary files a/pufferlib/resources/pacman/pinky_right.png and /dev/null differ diff --git a/pufferlib/resources/pacman/pinky_up.png b/pufferlib/resources/pacman/pinky_up.png deleted file mode 100644 index aa49b2e8d..000000000 Binary files a/pufferlib/resources/pacman/pinky_up.png and /dev/null differ diff --git a/pufferlib/resources/pacman/scared.png b/pufferlib/resources/pacman/scared.png deleted file mode 100644 index 707ea9a39..000000000 Binary files a/pufferlib/resources/pacman/scared.png and /dev/null differ diff --git a/pufferlib/resources/pacman/tileset.png b/pufferlib/resources/pacman/tileset.png deleted file mode 100644 index 8a13e2f69..000000000 Binary files a/pufferlib/resources/pacman/tileset.png and /dev/null differ diff --git a/pufferlib/resources/pong/pong_weights.bin b/pufferlib/resources/pong/pong_weights.bin deleted file mode 100644 index 7d9f0d6f1..000000000 Binary files a/pufferlib/resources/pong/pong_weights.bin and /dev/null differ diff --git a/pufferlib/resources/robocode/robocode.png b/pufferlib/resources/robocode/robocode.png deleted file mode 100755 index ede30de2e..000000000 Binary files a/pufferlib/resources/robocode/robocode.png and /dev/null differ diff --git a/pufferlib/resources/rware/rware_weights.bin b/pufferlib/resources/rware/rware_weights.bin deleted file mode 100644 index 9b5689b89..000000000 Binary files a/pufferlib/resources/rware/rware_weights.bin and /dev/null differ diff --git a/pufferlib/resources/shared/puffer.glb b/pufferlib/resources/shared/puffer.glb deleted file mode 100644 index 159398cf9..000000000 Binary files a/pufferlib/resources/shared/puffer.glb and /dev/null differ diff --git a/pufferlib/resources/shared/puffers.png b/pufferlib/resources/shared/puffers.png deleted file mode 100644 index 873c7d081..000000000 Binary files a/pufferlib/resources/shared/puffers.png and /dev/null differ diff --git a/pufferlib/resources/shared/puffers_128.png b/pufferlib/resources/shared/puffers_128.png deleted file mode 100755 index cfedf9d82..000000000 Binary files a/pufferlib/resources/shared/puffers_128.png and /dev/null differ diff --git a/pufferlib/resources/snake/snake_weights.bin b/pufferlib/resources/snake/snake_weights.bin deleted file mode 100644 index b854b2773..000000000 Binary files a/pufferlib/resources/snake/snake_weights.bin and /dev/null differ diff --git a/pufferlib/resources/target/star.png b/pufferlib/resources/target/star.png deleted file mode 100644 index 2738c1864..000000000 Binary files a/pufferlib/resources/target/star.png and /dev/null differ diff --git a/pufferlib/resources/target/target_weights.bin b/pufferlib/resources/target/target_weights.bin deleted file mode 100644 index fea80dbb0..000000000 Binary files a/pufferlib/resources/target/target_weights.bin and /dev/null differ diff --git a/pufferlib/resources/terraform/dozer.glb b/pufferlib/resources/terraform/dozer.glb deleted file mode 100644 index f3bbbbd36..000000000 Binary files a/pufferlib/resources/terraform/dozer.glb and /dev/null differ diff --git a/pufferlib/resources/terraform/perlin.jpg b/pufferlib/resources/terraform/perlin.jpg deleted file mode 100644 index 8f196925a..000000000 Binary files a/pufferlib/resources/terraform/perlin.jpg and /dev/null differ diff --git a/pufferlib/resources/terraform/puffer_terraform_weights.bin b/pufferlib/resources/terraform/puffer_terraform_weights.bin deleted file mode 100644 index 2809577aa..000000000 Binary files a/pufferlib/resources/terraform/puffer_terraform_weights.bin and /dev/null differ diff --git a/pufferlib/resources/terraform/shader_100.fs b/pufferlib/resources/terraform/shader_100.fs deleted file mode 100644 index 9e672d3a6..000000000 --- a/pufferlib/resources/terraform/shader_100.fs +++ /dev/null @@ -1,93 +0,0 @@ -#version 100 - -// Precision qualifiers (required in GLSL 100) -precision highp float; - -// Input from vertex shader (use 'varying' instead of 'in') -varying vec2 fragTexCoord; // Texture coordinates -varying vec4 fragColor; // Vertex color -varying vec3 fragPosition; // Vertex position - -// Uniforms (texture0 is not used in this shader but kept for completeness) -uniform sampler2D texture0; - -// Ashima's simplex noise (unchanged, but ensure precision) -vec3 mod289(vec3 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec2 mod289(vec2 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec3 permute(vec3 x) { - return mod289(((x * 34.0) + 10.0) * x); -} - -float snoise(vec2 v) { - const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 - 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) - -0.577350269189626, // -1.0 + 2.0 * C.x - 0.024390243902439); // 1.0 / 41.0 - // First corner - vec2 i = floor(v + dot(v, C.yy)); - vec2 x0 = v - i + dot(i, C.xx); - - // Other corners - vec2 i1; - i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); - vec4 x12 = x0.xyxy + C.xxzz; - x12.xy -= i1; - - // Permutations - i = mod289(i); // Avoid truncation effects in permutation - vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) - + i.x + vec3(0.0, i1.x, 1.0)); - - vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); - m = m * m; - m = m * m; - - // Gradients - vec3 x = 2.0 * fract(p * C.www) - 1.0; - vec3 h = abs(x) - 0.5; - vec3 ox = floor(x + 0.5); - vec3 a0 = x - ox; - - // Normalise gradients - m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); - - // Compute final noise value - vec3 g; - g.x = a0.x * x0.x + h.x * x0.y; - g.yz = a0.yz * x12.xz + h.yz * x12.yw; - return 130.0 * dot(m, g); -} - -float rand(vec2 co) { - return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); -} - -void main() { - float height = fragPosition.y / 32.0; - float delta = 0.0; - for (float i = -4.0; i < 4.0; i += 1.0) { // Use float for loop counter - float scale = pow(2.0, i); - delta += 0.02 * snoise((1.0 + fragPosition.y) * fragPosition.xz / scale); - } - float val = 0.5 + height; - - float black = 0.25 - 0.25 * fract(20.0 * height); - vec4 start_color = vec4(128.0 / 255.0, 48.0 / 255.0, 0.0, 1.0); - vec4 mid_color = vec4(255.0 / 255.0, 184.0 / 255.0, 0.0 / 255.0, 1.0); - vec4 end_color = vec4(220.0 / 255.0, 48.0 / 255.0, 0.0, 1.0); - - vec4 color = mix(mid_color, end_color, height); - color.rgb -= black; - - if (height < 0.001) { - color.rgb = start_color.rgb + delta; - } - - gl_FragColor = color; // Output to gl_FragColor instead of finalColor -} diff --git a/pufferlib/resources/terraform/shader_100.vs b/pufferlib/resources/terraform/shader_100.vs deleted file mode 100644 index 8bb4f69b0..000000000 --- a/pufferlib/resources/terraform/shader_100.vs +++ /dev/null @@ -1,19 +0,0 @@ -#version 100 - -precision mediump float; - -attribute vec3 vertexPosition; -attribute vec2 vertexTexCoord; -attribute vec3 vertexNormal; -attribute vec4 vertexColor; -uniform mat4 mvp; -varying vec2 fragTexCoord; -varying vec4 fragColor; -varying vec3 fragPosition; -void main() -{ - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragPosition = vertexPosition; - gl_Position = mvp * vec4(vertexPosition, 1.0); -} diff --git a/pufferlib/resources/terraform/shader_330.fs b/pufferlib/resources/terraform/shader_330.fs deleted file mode 100644 index 92e14b80f..000000000 --- a/pufferlib/resources/terraform/shader_330.fs +++ /dev/null @@ -1,128 +0,0 @@ -#version 330 - -// Input vertex attributes (from vertex shader) -in vec2 fragTexCoord; // Fragment input: vertex attribute: texture coordinates -in vec4 fragColor; // Fragment input: vertex attribute: color -in vec3 fragPosition; // Fragment input: vertex attribute: position - -// Input uniform values -uniform sampler2D texture0; // Fragment input: texture - -// Output fragment color -out vec4 finalColor; // Fragment output: pixel color - - -//Ashima's simplex noise -vec3 mod289(vec3 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec2 mod289(vec2 x) { - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec3 permute(vec3 x) { - return mod289(((x*34.0)+10.0)*x); -} - -float snoise(vec2 v) - { - const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 - 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) - -0.577350269189626, // -1.0 + 2.0 * C.x - 0.024390243902439); // 1.0 / 41.0 -// First corner - vec2 i = floor(v + dot(v, C.yy) ); - vec2 x0 = v - i + dot(i, C.xx); - -// Other corners - vec2 i1; - //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 - //i1.y = 1.0 - i1.x; - i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); - // x0 = x0 - 0.0 + 0.0 * C.xx ; - // x1 = x0 - i1 + 1.0 * C.xx ; - // x2 = x0 - 1.0 + 2.0 * C.xx ; - vec4 x12 = x0.xyxy + C.xxzz; - x12.xy -= i1; - -// Permutations - i = mod289(i); // Avoid truncation effects in permutation - vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) - + i.x + vec3(0.0, i1.x, 1.0 )); - - vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); - m = m*m ; - m = m*m ; - -// Gradients: 41 points uniformly over a line, mapped onto a diamond. -// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) - - vec3 x = 2.0 * fract(p * C.www) - 1.0; - vec3 h = abs(x) - 0.5; - vec3 ox = floor(x + 0.5); - vec3 a0 = x - ox; - -// Normalise gradients implicitly by scaling m -// Approximation of: m *= inversesqrt( a0*a0 + h*h ); - m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); - -// Compute final noise value at P - vec3 g; - g.x = a0.x * x0.x + h.x * x0.y; - g.yz = a0.yz * x12.xz + h.yz * x12.yw; - return 130.0 * dot(m, g); -} - - -float rand(vec2 co){ - return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - -void main() -{ - // Color based on height (e.g., gradient from blue to red) - float height = fragPosition.y/32.0; - float delta = 0.0; - for (int i = -4; i < 4; i++) { - float scale = pow(2.0, float(i)); - delta += 0.02*snoise((1.0+fragPosition.y)*fragPosition.xz/scale); - } - //float scale = pow(2.0, 4.0); - //float delta = round(2.0*snoise(fragPosition.xz/scale))/10.0; - float val = 0.5 + height; - - - float black = 0.25 - 0.25*fract(20.0*height); - //height = round(height*5.0)/5.0; - vec4 start_color = vec4(128.0/255.0, 48.0/255.0, 0.0, 1.0); - vec4 mid_color = vec4(255.0/255.0, 184.0/255.0, 0.0/255.0, 1.0); - vec4 end_color = vec4(220.0/255.0, 48.0/255.0, 0.0, 1.0); - - - //if (height < 0.5) { - // finalColor.rgba = mix(start_color, mid_color, 2.0*height); - //} else { - // finalColor.rgba = mix(mid_color, end_color, 2.0*(height-0.5)); - //} - - finalColor.rgba = mix(mid_color, end_color, height); - - finalColor.rgb -= black; - - if (height < 0.001) { - finalColor.rgb = start_color.rgb + delta; - } - - //if (height < 5.0) { - // float val = 0.5 + height/10.0; - // finalColor.rgba = vec4(val+delta, (val+delta)/2.0, 0.0, 1.0); - //} else if (height < 15.0) { - // float val = 0.5 + (height - 5.0)/5.0; - // finalColor.rgba = vec4(val+delta, (val+delta)/3.0, 0.0, 1.0); - //} else { - // float val = 0.5 + (height - 15.0)/(32.0 - 15.0)/2.0; - // finalColor.rgba = vec4(val+delta, (val+delta)/4.0, 0.0, 1.0); - //} -} - diff --git a/pufferlib/resources/terraform/shader_330.vs b/pufferlib/resources/terraform/shader_330.vs deleted file mode 100644 index 18ae59858..000000000 --- a/pufferlib/resources/terraform/shader_330.vs +++ /dev/null @@ -1,28 +0,0 @@ -#version 330 - -// Input vertex attributes -in vec3 vertexPosition; -in vec2 vertexTexCoord; -in vec3 vertexNormal; -in vec4 vertexColor; - -// Input uniform values -uniform mat4 mvp; - -// Output vertex attributes (to fragment shader) -out vec2 fragTexCoord; -out vec4 fragColor; -out vec3 fragPosition; - -// NOTE: Add your custom variables here - -void main() -{ - // Send vertex attributes to fragment shader - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragPosition = vertexPosition; - - // Calculate final vertex position - gl_Position = mvp*vec4(vertexPosition, 1.0); -} diff --git a/pufferlib/resources/terraform/target_shader_100.fs b/pufferlib/resources/terraform/target_shader_100.fs deleted file mode 100644 index c091a1ee2..000000000 --- a/pufferlib/resources/terraform/target_shader_100.fs +++ /dev/null @@ -1,37 +0,0 @@ -#version 100 -precision mediump float; -varying vec2 fragTexCoord; -varying vec4 fragColor; -varying vec3 fragPosition; -uniform int width; -uniform int height; -uniform sampler2D texture0; -uniform sampler2D terrain; -void main() -{ - float x = fragPosition.x/float(width); - float y = fragPosition.z/float(height); - - float terrain_height = texture2D(terrain, vec2(x, y)).r * 32.0; - float target_height = fragPosition.y; - float abs_delta = abs(target_height/32.0 - terrain_height/32.0); - - vec4 start_color = vec4(0.0, 0.0, 0.0, 1.0); - vec4 end_color = vec4(0.0, 1.0, 1.0, 1.0); - vec4 delta_color = mix(start_color, end_color, abs_delta); - - if (abs_delta > 0.0) { - abs_delta += 0.25; - } - - float grid = 16.0; - float glow = max(exp(-grid * abs(sin(3.14159 * fragPosition.x / grid))), exp(-grid * abs(sin(3.14159 * fragPosition.z / grid)))); - - float black = 0.25 - 0.25*fract(20.0*target_height/32.0); - float r = delta_color.r - black; - float g = max(glow, delta_color.g - black); - float b = max(glow, delta_color.b - black); - float a = max(glow, abs_delta); - - gl_FragColor = vec4(delta_color.r-black, delta_color.g-black, delta_color.b-black, abs_delta); -} diff --git a/pufferlib/resources/terraform/target_shader_330.fs b/pufferlib/resources/terraform/target_shader_330.fs deleted file mode 100644 index 3dc50f9df..000000000 --- a/pufferlib/resources/terraform/target_shader_330.fs +++ /dev/null @@ -1,49 +0,0 @@ -#version 330 - -// Input vertex attributes (from vertex shader) -in vec2 fragTexCoord; // Fragment input: vertex attribute: texture coordinates -in vec4 fragColor; // Fragment input: vertex attribute: color -in vec3 fragPosition; // Fragment input: vertex attribute: position -uniform int width; -uniform int height; - -// Input uniform values -uniform sampler2D texture0; // Fragment input: texture -uniform sampler2D terrain; // Fragment input: texture - -// Output fragment color -out vec4 finalColor; // Fragment output: pixel color - -void main() -{ - // Color based on height (e.g., gradient from blue to red) - - float x = fragPosition.x/float(width); - float y = fragPosition.z/float(height); - - float terrain_height = texture(terrain, vec2(x, y)).r * 32.0; - float target_height = fragPosition.y; - float abs_delta = abs(target_height/32.0 - terrain_height/32.0); - - vec4 start_color = vec4(0.0, 0.0, 0.0, 1.0); - vec4 end_color = vec4(0.0, 1.0, 1.0, 1.0); - vec4 delta_color = mix(start_color, end_color, abs_delta); - - if (abs_delta > 0.0) { - abs_delta += 0.25; - } - - float grid = 16.0; - float glow = max(exp(-grid * abs(sin(3.14159 * fragPosition.x / grid))), exp(-grid * abs(sin(3.14159 * fragPosition.z / grid)))); - - float black = 0.25 - 0.25*fract(20.0*target_height/32.0); - float r = delta_color.r - black; - float g = max(glow, delta_color.g - black); - float b = max(glow, delta_color.b - black); - float a = max(glow, abs_delta); - // finalColor.rgba = vec4(r, g, b, a); - - - finalColor.rgba = vec4(delta_color.r-black, delta_color.g-black, delta_color.b-black, abs_delta); -} - diff --git a/pufferlib/resources/terraform/target_shader_330.vs b/pufferlib/resources/terraform/target_shader_330.vs deleted file mode 100644 index 2859f64b5..000000000 --- a/pufferlib/resources/terraform/target_shader_330.vs +++ /dev/null @@ -1,28 +0,0 @@ -#version 330 - -// Input vertex attributes -in vec3 vertexPosition; -in vec2 vertexTexCoord; -in vec3 vertexNormal; -in vec4 vertexColor; - -// Input uniform values -uniform mat4 mvp; - -// Output vertex attributes (to fragment shader) -out vec2 fragTexCoord; -out vec4 fragColor; -out vec3 fragPosition; - -// NOTE: Add your custom variables here - -void main() -{ - // Send vertex attributes to fragment shader - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragPosition = vertexPosition; - - // Calculate final vertex position - gl_Position = mvp*vec4(vertexPosition + vec3(0.0, 0.0, 0.0), 1.0); -} diff --git a/pufferlib/resources/tetris/tetris_weights.bin b/pufferlib/resources/tetris/tetris_weights.bin deleted file mode 100644 index 766bb369e..000000000 Binary files a/pufferlib/resources/tetris/tetris_weights.bin and /dev/null differ diff --git a/pufferlib/resources/tower_climb/shaders/gls100/lighting.fs b/pufferlib/resources/tower_climb/shaders/gls100/lighting.fs deleted file mode 100644 index 15061eea1..000000000 --- a/pufferlib/resources/tower_climb/shaders/gls100/lighting.fs +++ /dev/null @@ -1,77 +0,0 @@ -#version 100 - -precision mediump float; - -// Input vertex attributes (from vertex shader) -varying vec3 fragPosition; -varying vec2 fragTexCoord; -varying vec4 fragColor; -varying vec3 fragNormal; - -// Input uniform values -uniform sampler2D texture0; -uniform vec4 colDiffuse; - -// NOTE: Add here your custom variables - -#define MAX_LIGHTS 4 -#define LIGHT_DIRECTIONAL 0 -#define LIGHT_POINT 1 - -struct Light { - int enabled; - int type; - vec3 position; - vec3 target; - vec4 color; -}; - -// Input lighting values -uniform Light lights[MAX_LIGHTS]; -uniform vec4 ambient; -uniform vec3 viewPos; - -void main() -{ - // Texel color fetching from texture sampler - vec4 texelColor = texture2D(texture0, fragTexCoord); - vec3 lightDot = vec3(0.0); - vec3 normal = normalize(fragNormal); - vec3 viewD = normalize(viewPos - fragPosition); - vec3 specular = vec3(0.0); - - vec4 tint = colDiffuse * fragColor; - - // NOTE: Implement here your fragment shader code - - for (int i = 0; i < MAX_LIGHTS; i++) - { - if (lights[i].enabled == 1) - { - vec3 light = vec3(0.0); - - if (lights[i].type == LIGHT_DIRECTIONAL) - { - light = -normalize(lights[i].target - lights[i].position); - } - - if (lights[i].type == LIGHT_POINT) - { - light = normalize(lights[i].position - fragPosition); - } - - float NdotL = max(dot(normal, light), 0.0); - lightDot += lights[i].color.rgb*NdotL; - - float specCo = 0.0; - if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), 16.0); // 16 refers to shine - specular += specCo; - } - } - - vec4 finalColor = (texelColor*((tint + vec4(specular, 1.0))*vec4(lightDot, 1.0))); - finalColor += texelColor*(ambient/10.0); - - // Gamma correction - gl_FragColor = pow(finalColor, vec4(1.0/2.2)); -} \ No newline at end of file diff --git a/pufferlib/resources/tower_climb/shaders/gls100/lighting.vs b/pufferlib/resources/tower_climb/shaders/gls100/lighting.vs deleted file mode 100644 index f973d2cd0..000000000 --- a/pufferlib/resources/tower_climb/shaders/gls100/lighting.vs +++ /dev/null @@ -1,59 +0,0 @@ -#version 100 - -// Input vertex attributes -attribute vec3 vertexPosition; -attribute vec2 vertexTexCoord; -attribute vec3 vertexNormal; -attribute vec4 vertexColor; - -// Input uniform values -uniform mat4 mvp; -uniform mat4 matModel; - -// Output vertex attributes (to fragment shader) -varying vec3 fragPosition; -varying vec2 fragTexCoord; -varying vec4 fragColor; -varying vec3 fragNormal; - -// NOTE: Add here your custom variables - -// https://github.com/glslify/glsl-inverse -mat3 inverse(mat3 m) -{ - float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2]; - float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2]; - float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2]; - - float b01 = a22*a11 - a12*a21; - float b11 = -a22*a10 + a12*a20; - float b21 = a21*a10 - a11*a20; - - float det = a00*b01 + a01*b11 + a02*b21; - - return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11), - b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10), - b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det; -} - -// https://github.com/glslify/glsl-transpose -mat3 transpose(mat3 m) -{ - return mat3(m[0][0], m[1][0], m[2][0], - m[0][1], m[1][1], m[2][1], - m[0][2], m[1][2], m[2][2]); -} - -void main() -{ - // Send vertex attributes to fragment shader - fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - - mat3 normalMatrix = transpose(inverse(mat3(matModel))); - fragNormal = normalize(normalMatrix*vertexNormal); - - // Calculate final vertex position - gl_Position = mvp*vec4(vertexPosition, 1.0); -} \ No newline at end of file diff --git a/pufferlib/resources/tower_climb/shaders/gls330/lighting.fs b/pufferlib/resources/tower_climb/shaders/gls330/lighting.fs deleted file mode 100644 index 7b4d92cf4..000000000 --- a/pufferlib/resources/tower_climb/shaders/gls330/lighting.fs +++ /dev/null @@ -1,78 +0,0 @@ -#version 330 - -// Input vertex attributes (from vertex shader) -in vec3 fragPosition; -in vec2 fragTexCoord; -in vec4 fragColor; -in vec3 fragNormal; - -// Input uniform values -uniform sampler2D texture0; -uniform vec4 colDiffuse; - -// Output fragment color -out vec4 finalColor; - -// NOTE: Add here your custom variables - -#define MAX_LIGHTS 4 -#define LIGHT_DIRECTIONAL 0 -#define LIGHT_POINT 1 - -struct Light { - int enabled; - int type; - vec3 position; - vec3 target; - vec4 color; -}; - -// Input lighting values -uniform Light lights[MAX_LIGHTS]; -uniform vec4 ambient; -uniform vec3 viewPos; - -void main() -{ - // Texel color fetching from texture sampler - vec4 texelColor = texture(texture0, fragTexCoord); - vec3 lightDot = vec3(0.0); - vec3 normal = normalize(fragNormal); - vec3 viewD = normalize(viewPos - fragPosition); - vec3 specular = vec3(0.0); - - vec4 tint = colDiffuse * fragColor; - - // NOTE: Implement here your fragment shader code - - for (int i = 0; i < MAX_LIGHTS; i++) - { - if (lights[i].enabled == 1) - { - vec3 light = vec3(0.0); - - if (lights[i].type == LIGHT_DIRECTIONAL) - { - light = -normalize(lights[i].target - lights[i].position); - } - - if (lights[i].type == LIGHT_POINT) - { - light = normalize(lights[i].position - fragPosition); - } - - float NdotL = max(dot(normal, light), 0.0); - lightDot += lights[i].color.rgb*NdotL; - - float specCo = 0.0; - if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), 16.0); // 16 refers to shine - specular += specCo; - } - } - - finalColor = (texelColor*((tint + vec4(specular, 1.0))*vec4(lightDot, 1.0))); - finalColor += texelColor*(ambient/10.0)*tint; - - // Gamma correction - finalColor = pow(finalColor, vec4(1.0/2.2)); -} \ No newline at end of file diff --git a/pufferlib/resources/tower_climb/shaders/gls330/lighting.vs b/pufferlib/resources/tower_climb/shaders/gls330/lighting.vs deleted file mode 100644 index 6b1763542..000000000 --- a/pufferlib/resources/tower_climb/shaders/gls330/lighting.vs +++ /dev/null @@ -1,32 +0,0 @@ -#version 330 - -// Input vertex attributes -in vec3 vertexPosition; -in vec2 vertexTexCoord; -in vec3 vertexNormal; -in vec4 vertexColor; - -// Input uniform values -uniform mat4 mvp; -uniform mat4 matModel; -uniform mat4 matNormal; - -// Output vertex attributes (to fragment shader) -out vec3 fragPosition; -out vec2 fragTexCoord; -out vec4 fragColor; -out vec3 fragNormal; - -// NOTE: Add here your custom variables - -void main() -{ - // Send vertex attributes to fragment shader - fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0))); - - // Calculate final vertex position - gl_Position = mvp*vec4(vertexPosition, 1.0); -} \ No newline at end of file diff --git a/pufferlib/resources/tower_climb/small_astro.glb b/pufferlib/resources/tower_climb/small_astro.glb deleted file mode 100644 index 2f250bbcf..000000000 Binary files a/pufferlib/resources/tower_climb/small_astro.glb and /dev/null differ diff --git a/pufferlib/resources/tower_climb/space2.jpg b/pufferlib/resources/tower_climb/space2.jpg deleted file mode 100644 index 28ba60d10..000000000 Binary files a/pufferlib/resources/tower_climb/space2.jpg and /dev/null differ diff --git a/pufferlib/resources/tower_climb/spacerock.glb b/pufferlib/resources/tower_climb/spacerock.glb deleted file mode 100644 index c0aaa8c72..000000000 Binary files a/pufferlib/resources/tower_climb/spacerock.glb and /dev/null differ diff --git a/pufferlib/resources/tower_climb/tower_climb_weights.bin b/pufferlib/resources/tower_climb/tower_climb_weights.bin deleted file mode 100644 index e5136c78a..000000000 Binary files a/pufferlib/resources/tower_climb/tower_climb_weights.bin and /dev/null differ diff --git a/pufferlib/resources/trash_pickup/trash_pickup_weights.bin b/pufferlib/resources/trash_pickup/trash_pickup_weights.bin deleted file mode 100644 index b72a0a99b..000000000 Binary files a/pufferlib/resources/trash_pickup/trash_pickup_weights.bin and /dev/null differ diff --git a/pufferlib/resources/tripletriad/tripletriad_weights.bin b/pufferlib/resources/tripletriad/tripletriad_weights.bin deleted file mode 100644 index af04c2380..000000000 Binary files a/pufferlib/resources/tripletriad/tripletriad_weights.bin and /dev/null differ diff --git a/pufferlib/resources/whisker_racer/puffer_whisker_racer_weights.bin b/pufferlib/resources/whisker_racer/puffer_whisker_racer_weights.bin deleted file mode 100644 index 344e7417f..000000000 Binary files a/pufferlib/resources/whisker_racer/puffer_whisker_racer_weights.bin and /dev/null differ diff --git a/pufferlib/spaces.py b/pufferlib/spaces.py index bf9582df4..800f93ab8 100644 --- a/pufferlib/spaces.py +++ b/pufferlib/spaces.py @@ -9,17 +9,17 @@ MultiDiscrete = (gym.spaces.MultiDiscrete, gymnasium.spaces.MultiDiscrete) Tuple = (gym.spaces.Tuple, gymnasium.spaces.Tuple) + def joint_space(space, n): if isinstance(space, Discrete): return gymnasium.spaces.MultiDiscrete([space.n] * n) elif isinstance(space, MultiDiscrete): - return gymnasium.spaces.Box(low=0, - high=np.repeat(space.nvec[None] - 1, n, axis=0), - shape=(n, len(space)), dtype=space.dtype) + return gymnasium.spaces.Box( + low=0, high=np.repeat(space.nvec[None] - 1, n, axis=0), shape=(n, len(space)), dtype=space.dtype + ) elif isinstance(space, Box): low = np.repeat(space.low[None], n, axis=0) high = np.repeat(space.high[None], n, axis=0) - return gymnasium.spaces.Box(low=low, high=high, - shape=(n, *space.shape), dtype=space.dtype) + return gymnasium.spaces.Box(low=low, high=high, shape=(n, *space.shape), dtype=space.dtype) else: - raise ValueError(f'Unsupported space: {space}') + raise ValueError(f"Unsupported space: {space}") diff --git a/pufferlib/sweep.py b/pufferlib/sweep.py index 3af13015b..fa405f73e 100644 --- a/pufferlib/sweep.py +++ b/pufferlib/sweep.py @@ -1,7 +1,6 @@ import numpy as np import random import math -import warnings from copy import deepcopy @@ -10,8 +9,6 @@ import torch import pyro from pyro.contrib import gp as gp -from pyro.contrib.gp.kernels import Kernel -from pyro.contrib.gp.models import GPRegression class Space: @@ -19,163 +16,173 @@ def __init__(self, min, max, scale, mean, is_integer=False): self.min = min self.max = max self.scale = scale - self.mean = mean # TODO: awkward to have just this normalized + self.mean = mean # TODO: awkward to have just this normalized self.norm_min = self.normalize(min) self.norm_max = self.normalize(max) self.norm_mean = self.normalize(mean) self.is_integer = is_integer + class Linear(Space): def __init__(self, min, max, scale, mean, is_integer=False): - if scale == 'auto': + if scale == "auto": scale = 0.5 super().__init__(min, max, scale, mean, is_integer) def normalize(self, value): - #assert isinstance(value, (int, float)) - zero_one = (value - self.min)/(self.max - self.min) - return 2*zero_one - 1 + # assert isinstance(value, (int, float)) + zero_one = (value - self.min) / (self.max - self.min) + return 2 * zero_one - 1 def unnormalize(self, value): - zero_one = (value + 1)/2 + zero_one = (value + 1) / 2 value = zero_one * (self.max - self.min) + self.min if self.is_integer: value = round(value) return value + class Pow2(Space): def __init__(self, min, max, scale, mean, is_integer=False): - if scale == 'auto': + if scale == "auto": scale = 0.5 - #scale = 2 / (np.log2(max) - np.log2(min)) + # scale = 2 / (np.log2(max) - np.log2(min)) super().__init__(min, max, scale, mean, is_integer) def normalize(self, value): - #assert isinstance(value, (int, float)) - #assert value != 0.0 - zero_one = (math.log(value, 2) - math.log(self.min, 2))/(math.log(self.max, 2) - math.log(self.min, 2)) - return 2*zero_one - 1 + # assert isinstance(value, (int, float)) + # assert value != 0.0 + zero_one = (math.log(value, 2) - math.log(self.min, 2)) / (math.log(self.max, 2) - math.log(self.min, 2)) + return 2 * zero_one - 1 def unnormalize(self, value): - zero_one = (value + 1)/2 - log_spaced = zero_one*(math.log(self.max, 2) - math.log(self.min, 2)) + math.log(self.min, 2) + zero_one = (value + 1) / 2 + log_spaced = zero_one * (math.log(self.max, 2) - math.log(self.min, 2)) + math.log(self.min, 2) rounded = round(log_spaced) - return 2 ** rounded + return 2**rounded + class Log(Space): base: int = 10 def __init__(self, min, max, scale, mean, is_integer=False): - if scale == 'time': + if scale == "time": # TODO: Set scaling param intuitively based on number of jumps from min to max scale = 1 / (np.log2(max) - np.log2(min)) - elif scale == 'auto': + elif scale == "auto": scale = 0.5 super().__init__(min, max, scale, mean, is_integer) def normalize(self, value): - #assert isinstance(value, (int, float)) - #assert value != 0.0 - zero_one = (math.log(value, self.base) - math.log(self.min, self.base))/(math.log(self.max, self.base) - math.log(self.min, self.base)) - return 2*zero_one - 1 + # assert isinstance(value, (int, float)) + # assert value != 0.0 + zero_one = (math.log(value, self.base) - math.log(self.min, self.base)) / ( + math.log(self.max, self.base) - math.log(self.min, self.base) + ) + return 2 * zero_one - 1 def unnormalize(self, value): - zero_one = (value + 1)/2 - log_spaced = zero_one*(math.log(self.max, self.base) - math.log(self.min, self.base)) + math.log(self.min, self.base) - value = self.base ** log_spaced + zero_one = (value + 1) / 2 + log_spaced = zero_one * (math.log(self.max, self.base) - math.log(self.min, self.base)) + math.log( + self.min, self.base + ) + value = self.base**log_spaced if self.is_integer: value = round(value) return value + class Logit(Space): base: int = 10 def __init__(self, min, max, scale, mean, is_integer=False): - if scale == 'auto': + if scale == "auto": scale = 0.5 super().__init__(min, max, scale, mean, is_integer) def normalize(self, value): - #assert isinstance(value, (int, float)) - #assert value != 0.0 - #assert value != 1.0 - zero_one = (math.log(1-value, self.base) - math.log(1-self.min, self.base))/(math.log(1-self.max, self.base) - math.log(1-self.min, self.base)) - return 2*zero_one - 1 + # assert isinstance(value, (int, float)) + # assert value != 0.0 + # assert value != 1.0 + zero_one = (math.log(1 - value, self.base) - math.log(1 - self.min, self.base)) / ( + math.log(1 - self.max, self.base) - math.log(1 - self.min, self.base) + ) + return 2 * zero_one - 1 def unnormalize(self, value): - zero_one = (value + 1)/2 - log_spaced = zero_one*(math.log(1-self.max, self.base) - math.log(1-self.min, self.base)) + math.log(1-self.min, self.base) + zero_one = (value + 1) / 2 + log_spaced = zero_one * (math.log(1 - self.max, self.base) - math.log(1 - self.min, self.base)) + math.log( + 1 - self.min, self.base + ) return 1 - self.base**log_spaced + def _params_from_puffer_sweep(sweep_config): param_spaces = {} for name, param in sweep_config.items(): - if name in ('method', 'metric', 'goal', 'downsample'): + if name in ("method", "metric", "goal", "downsample"): continue assert isinstance(param, dict) if any(isinstance(param[k], dict) for k in param): param_spaces[name] = _params_from_puffer_sweep(param) continue - - assert 'distribution' in param - distribution = param['distribution'] - search_center = param['mean'] + + assert "distribution" in param + distribution = param["distribution"] + search_center = param["mean"] kwargs = dict( - min=param['min'], - max=param['max'], - scale=param['scale'], + min=param["min"], + max=param["max"], + scale=param["scale"], mean=search_center, ) - if distribution == 'uniform': + if distribution == "uniform": space = Linear(**kwargs) - elif distribution == 'int_uniform': + elif distribution == "int_uniform": space = Linear(**kwargs, is_integer=True) - elif distribution == 'uniform_pow2': + elif distribution == "uniform_pow2": space = Pow2(**kwargs, is_integer=True) - elif distribution == 'log_normal': + elif distribution == "log_normal": space = Log(**kwargs) - elif distribution == 'logit_normal': + elif distribution == "logit_normal": space = Logit(**kwargs) else: - raise ValueError(f'Invalid distribution: {distribution}') + raise ValueError(f"Invalid distribution: {distribution}") param_spaces[name] = space return param_spaces + class Hyperparameters: def __init__(self, config, verbose=True): self.spaces = _params_from_puffer_sweep(config) self.flat_spaces = dict(pufferlib.unroll_nested_dict(self.spaces)) self.num = len(self.flat_spaces) - self.metric = config['metric'] - goal = config['goal'] - assert goal in ('maximize', 'minimize') - self.optimize_direction = 1 if goal == 'maximize' else -1 + self.metric = config["metric"] + goal = config["goal"] + assert goal in ("maximize", "minimize") + self.optimize_direction = 1 if goal == "maximize" else -1 - self.search_centers = np.array([ - e.norm_mean for e in self.flat_spaces.values()]) - self.min_bounds = np.array([ - e.norm_min for e in self.flat_spaces.values()]) - self.max_bounds = np.array([ - e.norm_max for e in self.flat_spaces.values()]) - self.search_scales = np.array([ - e.scale for e in self.flat_spaces.values()]) + self.search_centers = np.array([e.norm_mean for e in self.flat_spaces.values()]) + self.min_bounds = np.array([e.norm_min for e in self.flat_spaces.values()]) + self.max_bounds = np.array([e.norm_max for e in self.flat_spaces.values()]) + self.search_scales = np.array([e.scale for e in self.flat_spaces.values()]) if verbose: - print('Min random sample:') + print("Min random sample:") for name, space in self.flat_spaces.items(): - print(f'\t{name}: {space.unnormalize(max(space.norm_mean - space.scale, space.norm_min))}') + print(f"\t{name}: {space.unnormalize(max(space.norm_mean - space.scale, space.norm_min))}") - print('Max random sample:') + print("Max random sample:") for name, space in self.flat_spaces.items(): - print(f'\t{name}: {space.unnormalize(min(space.norm_mean + space.scale, space.norm_max))}') + print(f"\t{name}: {space.unnormalize(min(space.norm_mean + space.scale, space.norm_max))}") def sample(self, n, mu=None, scale=1): if mu is None: @@ -187,14 +194,14 @@ def sample(self, n, mu=None, scale=1): n_input, n_dim = mu.shape scale = scale * self.search_scales mu_idxs = np.random.randint(0, n_input, n) - samples = scale*(2*np.random.rand(n, n_dim) - 1) + mu[mu_idxs] + samples = scale * (2 * np.random.rand(n, n_dim) - 1) + mu[mu_idxs] return np.clip(samples, self.min_bounds, self.max_bounds) def from_dict(self, params): flat_params = dict(pufferlib.unroll_nested_dict(params)) values = [] for key, space in self.flat_spaces.items(): - assert key in flat_params, f'Missing hyperparameter {key}' + assert key in flat_params, f"Missing hyperparameter {key}" val = flat_params[key] normed = space.normalize(val) values.append(normed) @@ -218,8 +225,8 @@ def _fill(self, params, spaces, flat_sample, idx=0): def pareto_points(observations, eps=1e-6): - scores = np.array([e['output'] for e in observations]) - costs = np.array([e['cost'] for e in observations]) + scores = np.array([e["output"] for e in observations]) + costs = np.array([e["cost"] for e in observations]) pareto = [] idxs = [] for idx, obs in enumerate(observations): @@ -233,13 +240,14 @@ def pareto_points(observations, eps=1e-6): return pareto, idxs -class Random: - def __init__(self, - sweep_config, - global_search_scale = 1, - random_suggestions = 1024, - ): +class Random: + def __init__( + self, + sweep_config, + global_search_scale=1, + random_suggestions=1024, + ): self.hyperparameters = Hyperparameters(sweep_config) self.global_search_scale = global_search_scale self.random_suggestions = random_suggestions @@ -252,22 +260,25 @@ def suggest(self, fill=None): def observe(self, hypers, score, cost, is_failure=False): params = self.hyperparameters.from_dict(hypers) - self.success_observations.append(dict( - input=hypers, - output=score, - cost=cost, - is_failure=is_failure, - )) + self.success_observations.append( + dict( + input=hypers, + output=score, + cost=cost, + is_failure=is_failure, + ) + ) -class ParetoGenetic: - def __init__(self, - sweep_config, - global_search_scale = 1, - suggestions_per_pareto = 1, - bias_cost = True, - log_bias = False, - ): +class ParetoGenetic: + def __init__( + self, + sweep_config, + global_search_scale=1, + suggestions_per_pareto=1, + bias_cost=True, + log_bias=False, + ): self.hyperparameters = Hyperparameters(sweep_config) self.global_search_scale = global_search_scale self.suggestions_per_pareto = suggestions_per_pareto @@ -281,7 +292,7 @@ def suggest(self, fill=None): return self.hyperparameters.to_dict(suggestion, fill), {} candidates, _ = pareto_points(self.success_observations) - pareto_costs = np.array([e['cost'] for e in candidates]) + pareto_costs = np.array([e["cost"] for e in candidates]) if self.bias_cost: if self.log_bias: @@ -289,25 +300,26 @@ def suggest(self, fill=None): else: cost_dists = np.abs(pareto_costs[:, None] - pareto_costs[None, :]) - cost_dists += (np.max(pareto_costs) + 1)*np.eye(len(pareto_costs)) # mask self-distance + cost_dists += (np.max(pareto_costs) + 1) * np.eye(len(pareto_costs)) # mask self-distance idx = np.argmax(np.min(cost_dists, axis=1)) - search_centers = candidates[idx]['input'] + search_centers = candidates[idx]["input"] else: - search_centers = np.stack([e['input'] for e in candidates]) + search_centers = np.stack([e["input"] for e in candidates]) - suggestions = self.hyperparameters.sample( - len(candidates)*self.suggestions_per_pareto, mu=search_centers) + suggestions = self.hyperparameters.sample(len(candidates) * self.suggestions_per_pareto, mu=search_centers) suggestion = suggestions[np.random.randint(0, len(suggestions))] return self.hyperparameters.to_dict(suggestion, fill), {} def observe(self, hypers, score, cost, is_failure=False): params = self.hyperparameters.from_dict(hypers) - self.success_observations.append(dict( - input=params, - output=score, - cost=cost, - is_failure=is_failure, - )) + self.success_observations.append( + dict( + input=params, + output=score, + cost=cost, + is_failure=is_failure, + ) + ) def create_gp(x_dim, scale_length=1.0): @@ -325,19 +337,21 @@ def create_gp(x_dim, scale_length=1.0): optimizer = torch.optim.Adam(model.parameters(), lr=0.0001) return model, optimizer + # TODO: Eval defaults class Protein: - def __init__(self, - sweep_config, - max_suggestion_cost = 3600, - resample_frequency = 0, - num_random_samples = 50, - global_search_scale = 1, - random_suggestions = 1024, - suggestions_per_pareto = 256, - seed_with_search_center = True, - expansion_rate = 0.25, - ): + def __init__( + self, + sweep_config, + max_suggestion_cost=3600, + resample_frequency=0, + num_random_samples=50, + global_search_scale=1, + random_suggestions=1024, + suggestions_per_pareto=256, + seed_with_search_center=True, + expansion_rate=0.25, + ): self.hyperparameters = Hyperparameters(sweep_config) self.num_random_samples = num_random_samples self.global_search_scale = global_search_scale @@ -368,16 +382,16 @@ def suggest(self, fill): return self.hyperparameters.to_dict(self.suggestion, fill), info elif self.resample_frequency and self.suggestion_idx % self.resample_frequency == 0: candidates, _ = pareto_points(self.success_observations) - suggestions = np.stack([e['input'] for e in candidates]) + suggestions = np.stack([e["input"] for e in candidates]) best_idx = np.random.randint(0, len(candidates)) best = suggestions[best_idx] return self.hyperparameters.to_dict(best, fill), info - params = np.array([e['input'] for e in self.success_observations]) + params = np.array([e["input"] for e in self.success_observations]) params = torch.from_numpy(params) # Scores variable y - y = np.array([e['output'] for e in self.success_observations]) + y = np.array([e["output"] for e in self.success_observations]) # Transformed scores min_score = np.min(y) @@ -390,7 +404,7 @@ def suggest(self, fill): self.gp_score.eval() # Log costs - c = np.array([e['cost'] for e in self.success_observations]) + c = np.array([e["cost"] for e in self.success_observations]) log_c = np.log(c) @@ -406,12 +420,11 @@ def suggest(self, fill): self.gp_cost.eval() candidates, pareto_idxs = pareto_points(self.success_observations) - pareto_costs = np.array([e['cost'] for e in candidates]) + pareto_costs = np.array([e["cost"] for e in candidates]) ### Sample suggestions - search_centers = np.stack([e['input'] for e in candidates]) - suggestions = self.hyperparameters.sample( - len(candidates)*self.suggestions_per_pareto, mu=search_centers) + search_centers = np.stack([e["input"] for e in candidates]) + suggestions = self.hyperparameters.sample(len(candidates) * self.suggestions_per_pareto, mu=search_centers) ### Predict scores and costs suggestions = torch.from_numpy(suggestions) @@ -423,9 +436,9 @@ def suggest(self, fill): gp_log_c_norm = gp_log_c_norm.numpy() # Unlinearize - gp_y = gp_y_norm*(max_score - min_score) + min_score + gp_y = gp_y_norm * (max_score - min_score) + min_score - gp_log_c = gp_log_c_norm*(log_c_max - log_c_min) + log_c_min + gp_log_c = gp_log_c_norm * (log_c_max - log_c_min) + log_c_min gp_c = np.exp(gp_log_c) gp_c_min = np.min(gp_c) @@ -441,24 +454,24 @@ def suggest(self, fill): max_c_mask = gp_c < self.max_suggestion_cost - target = (1 + self.expansion_rate)*np.random.rand() + target = (1 + self.expansion_rate) * np.random.rand() weight = 1 - abs(target - gp_log_c_norm) - suggestion_scores = self.hyperparameters.optimize_direction * max_c_mask * ( - gp_y_norm*weight) + suggestion_scores = self.hyperparameters.optimize_direction * max_c_mask * (gp_y_norm * weight) best_idx = np.argmax(suggestion_scores) info = dict( - cost = gp_c[best_idx].item(), - score = gp_y[best_idx].item(), - rating = suggestion_scores[best_idx].item(), + cost=gp_c[best_idx].item(), + score=gp_y[best_idx].item(), + rating=suggestion_scores[best_idx].item(), ) - print('Predicted -- ', - f'Score: {info["score"]:.3f}', - f'Cost: {info["cost"]:.3f}', - f'Rating: {info["rating"]:.3f}', + print( + "Predicted -- ", + f"Score: {info['score']:.3f}", + f"Cost: {info['cost']:.3f}", + f"Rating: {info['rating']:.3f}", ) - ''' + """ if info['rating'] < 10: from bokeh.models import ColumnDataSource, LinearColorMapper from bokeh.plotting import figure, show @@ -507,16 +520,16 @@ def suggest(self, fill): y=gp_y_pareto[idxs], )) - p = figure(title='Hyperparam Test', - x_axis_label='Cost', + p = figure(title='Hyperparam Test', + x_axis_label='Cost', y_axis_label='Score') # Original data p.scatter( - x='x', - y='y', - color={'field': 'order', 'transform': mapper}, - size=10, + x='x', + y='y', + color={'field': 'order', 'transform': mapper}, + size=10, source=source ) @@ -526,7 +539,7 @@ def suggest(self, fill): #p.line(x='x', y='y', color='green', source=gp_pareto_source) show(p) - ''' + """ best = suggestions[best_idx].numpy() return self.hyperparameters.to_dict(best, fill), info @@ -544,7 +557,7 @@ def observe(self, hypers, score, cost, is_failure=False): self.success_observations.append(new_observation) return - success_params = np.stack([e['input'] for e in self.success_observations]) + success_params = np.stack([e["input"] for e in self.success_observations]) dist = np.linalg.norm(params - success_params, axis=1) same = np.where(dist < 1e-6)[0] if len(same) > 0: @@ -552,6 +565,7 @@ def observe(self, hypers, score, cost, is_failure=False): else: self.success_observations.append(new_observation) + def _carbs_params_from_puffer_sweep(sweep_config): from carbs import ( Param, @@ -562,48 +576,45 @@ def _carbs_params_from_puffer_sweep(sweep_config): param_spaces = {} for name, param in sweep_config.items(): - if name in ('method', 'name', 'metric', 'max_score'): + if name in ("method", "name", "metric", "max_score"): continue assert isinstance(param, dict) if any(isinstance(param[k], dict) for k in param): param_spaces[name] = _carbs_params_from_puffer_sweep(param) continue - - assert 'distribution' in param - distribution = param['distribution'] - search_center = param['mean'] + + assert "distribution" in param + distribution = param["distribution"] + search_center = param["mean"] kwargs = dict( - min=param['min'], - max=param['max'], + min=param["min"], + max=param["max"], ) - if distribution == 'uniform': + if distribution == "uniform": space = LinearSpace(**kwargs) - elif distribution in ('int_uniform', 'uniform_pow2'): + elif distribution in ("int_uniform", "uniform_pow2"): space = LinearSpace(**kwargs, is_integer=True) - elif distribution == 'log_normal': + elif distribution == "log_normal": space = LogSpace(**kwargs) - elif distribution == 'logit_normal': + elif distribution == "logit_normal": space = LogitSpace(**kwargs) else: - raise ValueError(f'Invalid distribution: {distribution}') + raise ValueError(f"Invalid distribution: {distribution}") - param_spaces[name] = Param( - name=name, - space=space, - search_center=search_center - ) + param_spaces[name] = Param(name=name, space=space, search_center=search_center) return param_spaces -class Carbs: - def __init__(self, - sweep_config: dict, - max_suggestion_cost: float = 3600, - resample_frequency: int = 5, - num_random_samples: int = 10, - ): +class Carbs: + def __init__( + self, + sweep_config: dict, + max_suggestion_cost: float = 3600, + resample_frequency: int = 5, + num_random_samples: int = 10, + ): param_spaces = _carbs_params_from_puffer_sweep(sweep_config) flat_spaces = [e[1] for e in pufferlib.unroll_nested_dict(param_spaces)] for e in flat_spaces: @@ -626,13 +637,14 @@ def __init__(self, def suggest(self, args): self.suggestion = self.carbs.suggest().suggestion - for k in ('train', 'env'): - for name, param in args['sweep'][k].items(): + for k in ("train", "env"): + for name, param in args["sweep"][k].items(): if name in self.suggestion: args[k][name] = self.suggestion[name] def observe(self, hypers, score, cost, is_failure=False): from carbs import ObservationInParam + self.carbs.observe( ObservationInParam( input=self.suggestion, diff --git a/pufferlib/utils.py b/pufferlib/utils.py new file mode 100644 index 000000000..c1ffeae16 --- /dev/null +++ b/pufferlib/utils.py @@ -0,0 +1,284 @@ +import os +import sys +import glob +import shutil +import subprocess +import json + + +def run_human_replay_eval_in_subprocess(config, logger, global_step): + """ + Run human replay evaluation in a subprocess and log metrics to wandb. + + """ + try: + run_id = logger.run_id + model_dir = os.path.join(config["data_dir"], f"{config['env']}_{run_id}") + model_files = glob.glob(os.path.join(model_dir, "model_*.pt")) + + if not model_files: + print("No model files found for human replay evaluation") + return + + latest_cpt = max(model_files, key=os.path.getctime) + + # Prepare evaluation command + eval_config = config["eval"] + cmd = [ + sys.executable, + "-m", + "pufferlib.pufferl", + "eval", + config["env"], + "--load-model-path", + latest_cpt, + "--eval.wosac-realism-eval", + "False", + "--eval.human-replay-eval", + "True", + "--eval.human-replay-num-agents", + str(eval_config["human_replay_num_agents"]), + "--eval.human-replay-control-mode", + str(eval_config["human_replay_control_mode"]), + ] + + # Run human replay evaluation in subprocess + result = subprocess.run(cmd, capture_output=True, text=True, timeout=600, cwd=os.getcwd()) + + if result.returncode == 0: + # Extract JSON from stdout between markers + stdout = result.stdout + if "HUMAN_REPLAY_METRICS_START" in stdout and "HUMAN_REPLAY_METRICS_END" in stdout: + start = stdout.find("HUMAN_REPLAY_METRICS_START") + len("HUMAN_REPLAY_METRICS_START") + end = stdout.find("HUMAN_REPLAY_METRICS_END") + json_str = stdout[start:end].strip() + human_replay_metrics = json.loads(json_str) + + # Log to wandb if available + if hasattr(logger, "wandb") and logger.wandb: + logger.wandb.log( + { + "eval/human_replay_collision_rate": human_replay_metrics["collision_rate"], + "eval/human_replay_offroad_rate": human_replay_metrics["offroad_rate"], + "eval/human_replay_completion_rate": human_replay_metrics["completion_rate"], + }, + step=global_step, + ) + else: + print(f"Human replay evaluation failed with exit code {result.returncode}: {result.stderr}") + + except subprocess.TimeoutExpired: + print("Human replay evaluation timed out") + except Exception as e: + print(f"Failed to run human replay evaluation: {e}") + + +def run_wosac_eval_in_subprocess(config, logger, global_step): + """ + Run WOSAC evaluation in a subprocess and log metrics to wandb. + + Args: + config: Configuration dictionary containing data_dir, env, and wosac settings + logger: Logger object with run_id and optional wandb attribute + epoch: Current training epoch + global_step: Current global training step + + Returns: + None. Prints error messages if evaluation fails. + """ + try: + run_id = logger.run_id + model_dir = os.path.join(config["data_dir"], f"{config['env']}_{run_id}") + model_files = glob.glob(os.path.join(model_dir, "model_*.pt")) + + if not model_files: + print("No model files found for WOSAC evaluation") + return + + latest_cpt = max(model_files, key=os.path.getctime) + + # Prepare evaluation command + eval_config = config.get("eval", {}) + cmd = [ + sys.executable, + "-m", + "pufferlib.pufferl", + "eval", + config["env"], + "--load-model-path", + latest_cpt, + "--eval.wosac-realism-eval", + "True", + "--eval.wosac-num-agents", + str(eval_config.get("wosac_num_agents", 256)), + "--eval.wosac-init-mode", + str(eval_config.get("wosac_init_mode", "create_all_valid")), + "--eval.wosac-control-mode", + str(eval_config.get("wosac_control_mode", "control_tracks_to_predict")), + "--eval.wosac-init-steps", + str(eval_config.get("wosac_init_steps", 10)), + "--eval.wosac-goal-behavior", + str(eval_config.get("wosac_goal_behavior", 2)), + "--eval.wosac-goal-radius", + str(eval_config.get("wosac_goal_radius", 2.0)), + "--eval.wosac-sanity-check", + str(eval_config.get("wosac_sanity_check", False)), + "--eval.wosac-aggregate-results", + str(eval_config.get("wosac_aggregate_results", True)), + ] + + # Run WOSAC evaluation in subprocess + result = subprocess.run(cmd, capture_output=True, text=True, timeout=600, cwd=os.getcwd()) + + if result.returncode == 0: + # Extract JSON from stdout between markers + stdout = result.stdout + if "WOSAC_METRICS_START" in stdout and "WOSAC_METRICS_END" in stdout: + start = stdout.find("WOSAC_METRICS_START") + len("WOSAC_METRICS_START") + end = stdout.find("WOSAC_METRICS_END") + json_str = stdout[start:end].strip() + wosac_metrics = json.loads(json_str) + + # Log to wandb if available + if hasattr(logger, "wandb") and logger.wandb: + logger.wandb.log( + { + "eval/wosac_realism_meta_score": wosac_metrics["realism_meta_score"], + "eval/wosac_ade": wosac_metrics["ade"], + "eval/wosac_min_ade": wosac_metrics["min_ade"], + "eval/wosac_total_num_agents": wosac_metrics["total_num_agents"], + }, + step=global_step, + ) + else: + print(f"WOSAC evaluation failed with exit code {result.returncode}: {result.stderr}") + + except subprocess.TimeoutExpired: + print("WOSAC evaluation timed out") + except Exception as e: + print(f"Failed to run WOSAC evaluation: {e}") + + +def render_videos(config, vecenv, logger, global_step, bin_path): + """ + Generate and log training videos using C-based rendering. + + Args: + config: Configuration dictionary containing data_dir, env, and render settings + vecenv: Vectorized environment with driver_env attribute + logger: Logger object with run_id and optional wandb attribute + epoch: Current training epoch + global_step: Current global training step + bin_path: Path to the exported .bin model weights file + + Returns: + None. Prints error messages if rendering fails. + """ + if not os.path.exists(bin_path): + print(f"Binary weights file does not exist: {bin_path}") + return + + run_id = logger.run_id + model_dir = os.path.join(config["data_dir"], f"{config['env']}_{run_id}") + + # Now call the C rendering function + try: + # Create output directory for videos + video_output_dir = os.path.join(model_dir, "videos") + os.makedirs(video_output_dir, exist_ok=True) + + # Copy the binary weights to the expected location + expected_weights_path = "resources/drive/puffer_drive_weights.bin" + os.makedirs(os.path.dirname(expected_weights_path), exist_ok=True) + shutil.copy2(bin_path, expected_weights_path) + + # TODO: Fix memory leaks so that this is not needed + # Suppress AddressSanitizer exit code (temp) + env = os.environ.copy() + env["ASAN_OPTIONS"] = "exitcode=0" + + cmd = ["xvfb-run", "-a", "-s", "-screen 0 1280x720x24", "./visualize"] + + # Add render configurations + if config["show_grid"]: + cmd.append("--show-grid") + if config["obs_only"]: + cmd.append("--obs-only") + if config["show_lasers"]: + cmd.append("--lasers") + if config["show_human_logs"]: + cmd.append("--log-trajectories") + if vecenv.driver_env.goal_radius is not None: + cmd.extend(["--goal-radius", str(vecenv.driver_env.goal_radius)]) + if vecenv.driver_env.init_steps > 0: + cmd.extend(["--init-steps", str(vecenv.driver_env.init_steps)]) + if config["render_map"] is not None: + map_path = config["render_map"] + if os.path.exists(map_path): + cmd.extend(["--map-name", map_path]) + if vecenv.driver_env.init_mode is not None: + cmd.extend(["--init-mode", str(vecenv.driver_env.init_mode)]) + if vecenv.driver_env.control_mode is not None: + cmd.extend(["--control-mode", str(vecenv.driver_env.control_mode)]) + + # Specify output paths for videos + cmd.extend(["--output-topdown", "resources/drive/output_topdown.mp4"]) + cmd.extend(["--output-agent", "resources/drive/output_agent.mp4"]) + + # Add environment configuration + env_cfg = getattr(vecenv, "driver_env", None) + if env_cfg is not None: + n_policy = getattr(env_cfg, "max_controlled_agents", -1) + try: + n_policy = int(n_policy) + except (TypeError, ValueError): + n_policy = -1 + if n_policy > 0: + cmd += ["--num-policy-controlled-agents", str(n_policy)] + if getattr(env_cfg, "num_maps", False): + cmd.extend(["--num-maps", str(env_cfg.num_maps)]) + if getattr(env_cfg, "scenario_length", None): + cmd.extend(["--scenario-length", str(env_cfg.scenario_length)]) + + # Call C code that runs eval_gif() in subprocess + result = subprocess.run(cmd, cwd=os.getcwd(), capture_output=True, text=True, timeout=120, env=env) + + vids_exist = os.path.exists("resources/drive/output_topdown.mp4") and os.path.exists( + "resources/drive/output_agent.mp4" + ) + + if result.returncode == 0 or (result.returncode == 1 and vids_exist): + # Move both generated videos to the model directory + videos = [ + ("resources/drive/output_topdown.mp4", f"epoch_{epoch:06d}_topdown.mp4"), + ("resources/drive/output_agent.mp4", f"epoch_{epoch:06d}_agent.mp4"), + ] + + for source_vid, target_filename in videos: + if os.path.exists(source_vid): + target_gif = os.path.join(video_output_dir, target_filename) + shutil.move(source_vid, target_gif) + + # Log to wandb if available + if hasattr(logger, "wandb") and logger.wandb: + import wandb + + view_type = "world_state" if "topdown" in target_filename else "agent_view" + logger.wandb.log( + {f"render/{view_type}": wandb.Video(target_gif, format="mp4")}, + step=global_step, + ) + else: + print(f"Video generation completed but {source_vid} not found") + else: + print(f"C rendering failed with exit code {result.returncode}: {result.stdout}") + + except subprocess.TimeoutExpired: + print("C rendering timed out") + except Exception as e: + print(f"Failed to generate GIF: {e}") + + finally: + # Clean up bin weights file + if os.path.exists(expected_weights_path): + os.remove(expected_weights_path) diff --git a/pufferlib/vector.py b/pufferlib/vector.py index d06c84f6b..e691f0e69 100644 --- a/pufferlib/vector.py +++ b/pufferlib/vector.py @@ -1,6 +1,5 @@ # TODO: Check actions passed to envs are right shape? On first call at least -from pdb import set_trace as T import numpy as np import time @@ -10,7 +9,6 @@ from pufferlib.emulation import GymnasiumPufferEnv, PettingZooPufferEnv from pufferlib import PufferEnv, set_buffers import pufferlib.spaces -import gymnasium RESET = 0 STEP = 1 @@ -20,35 +18,40 @@ MAIN = 5 INFO = 6 + def recv_precheck(vecenv): if vecenv.flag != RECV: - raise pufferlib.APIUsageError('Call reset before stepping') + raise pufferlib.APIUsageError("Call reset before stepping") vecenv.flag = SEND + def send_precheck(vecenv, actions): if vecenv.flag != SEND: - raise pufferlib.APIUsageError('Call (async) reset + recv before sending') + raise pufferlib.APIUsageError("Call (async) reset + recv before sending") actions = np.asarray(actions) if not vecenv.initialized: vecenv.initialized = True if not vecenv.action_space.contains(actions): - raise pufferlib.APIUsageError('Actions do not match action space') + raise pufferlib.APIUsageError("Actions do not match action space") vecenv.flag = RECV return actions + def reset(vecenv, seed=42): vecenv.async_reset(seed) obs, rewards, terminals, truncations, infos, env_ids, masks = vecenv.recv() return obs, infos + def step(vecenv, actions): actions = np.asarray(actions) vecenv.send(actions) obs, rewards, terminals, truncations, infos, env_ids, masks = vecenv.recv() - return obs, rewards, terminals, truncations, infos # include env_ids or no? + return obs, rewards, terminals, truncations, infos # include env_ids or no? + class Serial: reset = reset @@ -57,7 +60,7 @@ class Serial: @property def num_envs(self): return self.agents_per_batch - + def __init__(self, env_creators, env_args, env_kwargs, num_envs, buf=None, seed=0, **kwargs): self.driver_env = env_creators[0](*env_args[0], **env_kwargs[0]) self.agents_per_batch = self.driver_env.num_agents * num_envs @@ -68,7 +71,6 @@ def __init__(self, env_creators, env_args, env_kwargs, num_envs, buf=None, seed= self.action_space = pufferlib.spaces.joint_space(self.single_action_space, self.agents_per_batch) self.observation_space = pufferlib.spaces.joint_space(self.single_observation_space, self.agents_per_batch) - set_buffers(self, buf) self.envs = [] @@ -81,7 +83,7 @@ def __init__(self, env_creators, env_args, env_kwargs, num_envs, buf=None, seed= terminals=self.terminals[ptr:end], truncations=self.truncations[ptr:end], masks=self.masks[ptr:end], - actions=self.actions[ptr:end] + actions=self.actions[ptr:end], ) ptr = end seed_i = seed + i if seed is not None else None @@ -122,8 +124,8 @@ def async_reset(self, seed=None): if seed is None: ob, i = env.reset() else: - ob, i = env.reset(seed=seed+i) - + ob, i = env.reset(seed=seed + i) + if isinstance(i, list): infos.extend(i) else: @@ -163,38 +165,59 @@ def notify(self): def recv(self): recv_precheck(self) - return (self.observations, self.rewards, self.terminals, self.truncations, - self.infos, self.agent_ids, self.masks) + return ( + self.observations, + self.rewards, + self.terminals, + self.truncations, + self.infos, + self.agent_ids, + self.masks, + ) def close(self): for env in self.envs: env.close() -def _worker_process(env_creators, env_args, env_kwargs, obs_shape, obs_dtype, atn_shape, atn_dtype, - num_envs, num_agents, num_workers, worker_idx, send_pipe, recv_pipe, shm, is_native, seed): +def _worker_process( + env_creators, + env_args, + env_kwargs, + obs_shape, + obs_dtype, + atn_shape, + atn_dtype, + num_envs, + num_agents, + num_workers, + worker_idx, + send_pipe, + recv_pipe, + shm, + is_native, + seed, +): # Environments read and write directly to shared memory - shape = (num_workers, num_envs*num_agents) - atn_arr = np.ndarray((*shape, *atn_shape), - dtype=atn_dtype, buffer=shm['actions'])[worker_idx] + shape = (num_workers, num_envs * num_agents) + atn_arr = np.ndarray((*shape, *atn_shape), dtype=atn_dtype, buffer=shm["actions"])[worker_idx] buf = dict( - observations=np.ndarray((*shape, *obs_shape), - dtype=obs_dtype, buffer=shm['observations'])[worker_idx], - rewards=np.ndarray(shape, dtype=np.float32, buffer=shm['rewards'])[worker_idx], - terminals=np.ndarray(shape, dtype=bool, buffer=shm['terminals'])[worker_idx], - truncations=np.ndarray(shape, dtype=bool, buffer=shm['truncateds'])[worker_idx], - masks=np.ndarray(shape, dtype=bool, buffer=shm['masks'])[worker_idx], + observations=np.ndarray((*shape, *obs_shape), dtype=obs_dtype, buffer=shm["observations"])[worker_idx], + rewards=np.ndarray(shape, dtype=np.float32, buffer=shm["rewards"])[worker_idx], + terminals=np.ndarray(shape, dtype=bool, buffer=shm["terminals"])[worker_idx], + truncations=np.ndarray(shape, dtype=bool, buffer=shm["truncateds"])[worker_idx], + masks=np.ndarray(shape, dtype=bool, buffer=shm["masks"])[worker_idx], actions=atn_arr, ) - buf['masks'][:] = True + buf["masks"][:] = True if is_native and num_envs == 1: envs = env_creators[0](*env_args[0], **env_kwargs[0], buf=buf, seed=seed) else: - envs = Serial(env_creators, env_args, env_kwargs, num_envs, buf=buf, seed=seed*num_envs) + envs = Serial(env_creators, env_args, env_kwargs, num_envs, buf=buf, seed=seed * num_envs) - semaphores=np.ndarray(num_workers, dtype=np.uint8, buffer=shm['semaphores']) - notify=np.ndarray(num_workers, dtype=bool, buffer=shm['notify']) + semaphores = np.ndarray(num_workers, dtype=np.uint8, buffer=shm["semaphores"]) + notify = np.ndarray(num_workers, dtype=bool, buffer=shm["notify"]) start = time.time() while True: if notify[worker_idx]: @@ -224,40 +247,57 @@ def _worker_process(env_creators, env_args, env_kwargs, obs_shape, obs_dtype, at else: semaphores[worker_idx] = MAIN + class Multiprocessing: - '''Runs environments in parallel using multiprocessing + """Runs environments in parallel using multiprocessing Use this vectorization module for most applications - ''' + """ + reset = reset step = step @property def num_envs(self): return self.agents_per_batch - - def __init__(self, env_creators, env_args, env_kwargs, - num_envs, num_workers=None, batch_size=None, - zero_copy=True, sync_traj=True, overwork=False, seed=0, **kwargs): + + def __init__( + self, + env_creators, + env_args, + env_kwargs, + num_envs, + num_workers=None, + batch_size=None, + zero_copy=True, + sync_traj=True, + overwork=False, + seed=0, + **kwargs, + ): if batch_size is None: batch_size = num_envs if num_workers is None: num_workers = num_envs import psutil + cpu_cores = psutil.cpu_count(logical=False) if num_workers > cpu_cores and not overwork: - raise pufferlib.APIUsageError(' '.join([ - f'num_workers ({num_workers}) > hardware cores ({cpu_cores}) is disallowed by default.', - 'PufferLib multiprocessing is heavily optimized for 1 process per hardware core.', - 'If you really want to do this, set overwork=True (--vec-overwork in our demo.py).', - ])) + raise pufferlib.APIUsageError( + " ".join( + [ + f"num_workers ({num_workers}) > hardware cores ({cpu_cores}) is disallowed by default.", + "PufferLib multiprocessing is heavily optimized for 1 process per hardware core.", + "If you really want to do this, set overwork=True (--vec-overwork in our demo.py).", + ] + ) + ) num_batches = num_envs / batch_size if zero_copy and num_batches != int(num_batches): # This is so you can have n equal buffers - raise pufferlib.APIUsageError( - 'zero_copy: num_envs must be divisible by batch_size') + raise pufferlib.APIUsageError("zero_copy: num_envs must be divisible by batch_size") self.num_environments = num_envs envs_per_worker = num_envs // num_workers @@ -295,37 +335,37 @@ def __init__(self, env_creators, env_args, env_kwargs, self.observation_space = pufferlib.spaces.joint_space(self.single_observation_space, self.agents_per_batch) self.agent_ids = np.arange(num_agents).reshape(num_workers, agents_per_worker) - from multiprocessing import RawArray, set_start_method + from multiprocessing import RawArray + # Mac breaks without setting fork... but setting it breaks sweeps on 2nd run - #set_start_method('fork') + # set_start_method('fork') self.shm = dict( observations=RawArray(obs_ctype, num_agents * int(np.prod(obs_shape))), actions=RawArray(atn_ctype, num_agents * int(np.prod(atn_shape))), - rewards=RawArray('f', num_agents), - terminals=RawArray('b', num_agents), - truncateds=RawArray('b', num_agents), - masks=RawArray('b', num_agents), - semaphores=RawArray('c', num_workers), - notify=RawArray('b', num_workers), + rewards=RawArray("f", num_agents), + terminals=RawArray("b", num_agents), + truncateds=RawArray("b", num_agents), + masks=RawArray("b", num_agents), + semaphores=RawArray("c", num_workers), + notify=RawArray("b", num_workers), ) shape = (num_workers, agents_per_worker) self.obs_batch_shape = (self.agents_per_batch, *obs_shape) self.atn_batch_shape = (self.workers_per_batch, agents_per_worker, *atn_shape) - self.actions = np.ndarray((*shape, *atn_shape), - dtype=atn_dtype, buffer=self.shm['actions']) + self.actions = np.ndarray((*shape, *atn_shape), dtype=atn_dtype, buffer=self.shm["actions"]) self.buf = dict( - observations=np.ndarray((*shape, *obs_shape), - dtype=obs_dtype, buffer=self.shm['observations']), - rewards=np.ndarray(shape, dtype=np.float32, buffer=self.shm['rewards']), - terminals=np.ndarray(shape, dtype=bool, buffer=self.shm['terminals']), - truncations=np.ndarray(shape, dtype=bool, buffer=self.shm['truncateds']), - masks=np.ndarray(shape, dtype=bool, buffer=self.shm['masks']), - semaphores=np.ndarray(num_workers, dtype=np.uint8, buffer=self.shm['semaphores']), - notify=np.ndarray(num_workers, dtype=bool, buffer=self.shm['notify']), + observations=np.ndarray((*shape, *obs_shape), dtype=obs_dtype, buffer=self.shm["observations"]), + rewards=np.ndarray(shape, dtype=np.float32, buffer=self.shm["rewards"]), + terminals=np.ndarray(shape, dtype=bool, buffer=self.shm["terminals"]), + truncations=np.ndarray(shape, dtype=bool, buffer=self.shm["truncateds"]), + masks=np.ndarray(shape, dtype=bool, buffer=self.shm["masks"]), + semaphores=np.ndarray(num_workers, dtype=np.uint8, buffer=self.shm["semaphores"]), + notify=np.ndarray(num_workers, dtype=bool, buffer=self.shm["notify"]), ) - self.buf['semaphores'][:] = MAIN + self.buf["semaphores"][:] = MAIN from multiprocessing import Pipe, Process + self.send_pipes, w_recv_pipes = zip(*[Pipe() for _ in range(num_workers)]) w_send_pipes, self.recv_pipes = zip(*[Pipe() for _ in range(num_workers)]) self.recv_pipe_dict = {p: i for i, p in enumerate(self.recv_pipes)} @@ -337,11 +377,24 @@ def __init__(self, env_creators, env_args, env_kwargs, seed_i = seed + i if seed is not None else None p = Process( target=_worker_process, - args=(env_creators[start:end], env_args[start:end], - env_kwargs[start:end], obs_shape, obs_dtype, - atn_shape, atn_dtype, envs_per_worker, driver_env.num_agents, - num_workers, i, w_send_pipes[i], w_recv_pipes[i], - self.shm, is_native, seed_i) + args=( + env_creators[start:end], + env_args[start:end], + env_kwargs[start:end], + obs_shape, + obs_dtype, + atn_shape, + atn_dtype, + envs_per_worker, + driver_env.num_agents, + num_workers, + i, + w_send_pipes[i], + w_recv_pipes[i], + self.shm, + is_native, + seed_i, + ), ) p.start() self.processes.append(p) @@ -369,13 +422,13 @@ def recv(self): # Bandaid patch for new experience buffer desync if self.sync_traj: worker = self.waiting_workers[0] - sem = self.buf['semaphores'][worker] + sem = self.buf["semaphores"][worker] if sem >= MAIN: self.waiting_workers.pop(0) self.ready_workers.append(worker) else: worker = self.waiting_workers.pop(0) - sem = self.buf['semaphores'][worker] + sem = self.buf["semaphores"][worker] if sem >= MAIN: self.ready_workers.append(worker) else: @@ -410,8 +463,7 @@ def recv(self): # microseconds of extra index processing time completed = np.zeros(self.num_workers, dtype=bool) completed[self.ready_workers] = True - buffers = completed.reshape( - -1, self.workers_per_batch).all(axis=1) + buffers = completed.reshape(-1, self.workers_per_batch).all(axis=1) start = buffers.argmax() if not buffers[start]: continue @@ -421,26 +473,25 @@ def recv(self): w_slice = slice(start, end) s_range = range(start, end) self.waiting_workers.extend(s_range) - self.ready_workers = [e for e in self.ready_workers - if e not in s_range] + self.ready_workers = [e for e in self.ready_workers if e not in s_range] break elif len(self.ready_workers) >= self.workers_per_batch: # Full async path for batch size > 1. Alawys copies # data because of non-contiguous worker indices # Can be faster for envs with small observations - w_slice = self.ready_workers[:self.workers_per_batch] + w_slice = self.ready_workers[: self.workers_per_batch] s_range = w_slice self.waiting_workers.extend(s_range) - self.ready_workers = self.ready_workers[self.workers_per_batch:] + self.ready_workers = self.ready_workers[self.workers_per_batch :] break self.w_slice = w_slice buf = self.buf - o = buf['observations'][w_slice].reshape(self.obs_batch_shape) - r = buf['rewards'][w_slice].ravel() - d = buf['terminals'][w_slice].ravel() - t = buf['truncations'][w_slice].ravel() + o = buf["observations"][w_slice].reshape(self.obs_batch_shape) + r = buf["rewards"][w_slice].ravel() + d = buf["terminals"][w_slice].ravel() + t = buf["truncations"][w_slice].ravel() infos = [] for i in s_range: @@ -449,7 +500,7 @@ def recv(self): self.infos[i] = [] agent_ids = self.agent_ids[w_slice].ravel() - m = buf['masks'][w_slice].ravel() + m = buf["masks"][w_slice].ravel() self.batch_mask = m return o, r, d, t, infos, agent_ids, m @@ -457,16 +508,16 @@ def recv(self): def send(self, actions): actions = send_precheck(self, actions).reshape(self.atn_batch_shape) # TODO: What shape? - + idxs = self.w_slice self.actions[idxs] = actions - self.buf['semaphores'][idxs] = STEP + self.buf["semaphores"][idxs] = STEP def async_reset(self, seed=0): # Flush any waiting workers while self.waiting_workers: worker = self.waiting_workers.pop(0) - sem = self.buf['semaphores'][worker] + sem = self.buf["semaphores"][worker] if sem >= MAIN: self.ready_workers.append(worker) if sem == INFO: @@ -479,18 +530,18 @@ def async_reset(self, seed=0): self.flag = RECV self.ready_workers = [] - self.ready_next_workers = [] # Used to evenly sample workers + self.ready_next_workers = [] # Used to evenly sample workers self.waiting_workers = list(range(self.num_workers)) self.infos = [[] for _ in range(self.num_workers)] - self.buf['semaphores'][:] = RESET + self.buf["semaphores"][:] = RESET for i in range(self.num_workers): - start = i*self.envs_per_worker - end = (i+1)*self.envs_per_worker - self.send_pipes[i].send(seed+i) + start = i * self.envs_per_worker + end = (i + 1) * self.envs_per_worker + self.send_pipes[i].send(seed + i) def notify(self): - self.buf['notify'][:] = True + self.buf["notify"][:] = True def log_worker_ram_usage(self): """Log RAM usage of all worker processes""" @@ -535,16 +586,17 @@ def close(self): for p in self.processes: p.terminate() -class Ray(): - '''Runs environments in parallel on multiple processes using Ray + +class Ray: + """Runs environments in parallel on multiple processes using Ray Use this module for distributed simulation on a cluster. - ''' + """ + reset = reset step = step - def __init__(self, env_creators, env_args, env_kwargs, num_envs, - num_workers=None, batch_size=None, **kwargs): + def __init__(self, env_creators, env_args, env_kwargs, num_envs, num_workers=None, batch_size=None, **kwargs): if batch_size is None: batch_size = num_envs if num_workers is None: @@ -574,12 +626,14 @@ def __init__(self, env_creators, env_args, env_kwargs, num_envs, self.single_action_space = driver_env.single_action_space self.action_space = pufferlib.spaces.joint_space(self.single_action_space, self.agents_per_batch) self.observation_space = pufferlib.spaces.joint_space(self.single_observation_space, self.agents_per_batch) - + self.agent_ids = np.arange(num_agents).reshape(num_workers, agents_per_worker) import ray + if not ray.is_initialized(): import logging + ray.init( include_dashboard=False, # WSL Compatibility logging_level=logging.ERROR, @@ -591,10 +645,7 @@ def __init__(self, env_creators, env_args, env_kwargs, num_envs, end = start + envs_per_worker self.envs.append( ray.remote(Serial).remote( - env_creators[start:end], - env_args[start:end], - env_kwargs[start:end], - envs_per_worker + env_creators[start:end], env_args[start:end], env_kwargs[start:end], envs_per_worker ) ) @@ -613,8 +664,7 @@ def recv(self): recvs = self.ray.get(self.async_handles[:workers_per_batch]) env_id = [_ for _ in range(workers_per_batch)] else: - ready, busy = self.ray.wait( - self.async_handles, num_returns=workers_per_batch) + ready, busy = self.ray.wait(self.async_handles, num_returns=workers_per_batch) env_id = [self.async_handles.index(e) for e in ready] recvs = self.ray.get(ready) @@ -665,52 +715,54 @@ def close(self): def make(env_creator_or_creators, env_args=None, env_kwargs=None, backend=PufferEnv, num_envs=1, seed=0, **kwargs): if num_envs < 1: - raise pufferlib.APIUsageError('num_envs must be at least 1') + raise pufferlib.APIUsageError("num_envs must be at least 1") if num_envs != int(num_envs): - raise pufferlib.APIUsageError('num_envs must be an integer') + raise pufferlib.APIUsageError("num_envs must be an integer") if isinstance(backend, str): try: backend = getattr(pufferlib.vector, backend) except: - raise pufferlib.APIUsageError(f'Invalid backend: {backend}') + raise pufferlib.APIUsageError(f"Invalid backend: {backend}") if backend == PufferEnv: env_args = env_args or [] env_kwargs = env_kwargs or {} vecenv = env_creator_or_creators(*env_args, **env_kwargs) if not isinstance(vecenv, PufferEnv): - raise pufferlib.APIUsageError('Native vectorization requires a native PufferEnv. Use Serial or Multiprocessing instead.') + raise pufferlib.APIUsageError( + "Native vectorization requires a native PufferEnv. Use Serial or Multiprocessing instead." + ) if num_envs != 1: - raise pufferlib.APIUsageError('Native vectorization is for PufferEnvs that handle all per-process vectorization internally. If you want to run multiple separate Python instances on a single process, use Serial or Multiprocessing instead') + raise pufferlib.APIUsageError( + "Native vectorization is for PufferEnvs that handle all per-process vectorization internally. If you want to run multiple separate Python instances on a single process, use Serial or Multiprocessing instead" + ) return vecenv - if 'num_workers' in kwargs: - if kwargs['num_workers'] == 'auto': - kwargs['num_workers'] = num_envs + if "num_workers" in kwargs: + if kwargs["num_workers"] == "auto": + kwargs["num_workers"] = num_envs # TODO: None? - envs_per_worker = num_envs / kwargs['num_workers'] + envs_per_worker = num_envs / kwargs["num_workers"] if envs_per_worker != int(envs_per_worker): - raise pufferlib.APIUsageError('num_envs must be divisible by num_workers') + raise pufferlib.APIUsageError("num_envs must be divisible by num_workers") - if 'batch_size' in kwargs: - if kwargs['batch_size'] == 'auto': + if "batch_size" in kwargs: + if kwargs["batch_size"] == "auto": if num_envs == 1: - kwargs['batch_size'] = 1 + kwargs["batch_size"] = 1 else: - kwargs['batch_size'] = num_envs // 2 + kwargs["batch_size"] = num_envs // 2 - batch_size = kwargs['batch_size'] + batch_size = kwargs["batch_size"] if batch_size is None: batch_size = num_envs if batch_size % envs_per_worker != 0: - raise pufferlib.APIUsageError( - 'batch_size must be divisible by (num_envs / num_workers)') - - + raise pufferlib.APIUsageError("batch_size must be divisible by (num_envs / num_workers)") + if env_args is None: env_args = [] @@ -725,23 +777,23 @@ def make(env_creator_or_creators, env_args=None, env_kwargs=None, backend=Puffer env_creators = env_creator_or_creators if len(env_creators) != num_envs: - raise pufferlib.APIUsageError('env_creators must be a list of length num_envs') + raise pufferlib.APIUsageError("env_creators must be a list of length num_envs") if len(env_args) != num_envs: - raise pufferlib.APIUsageError('env_args must be a list of length num_envs') + raise pufferlib.APIUsageError("env_args must be a list of length num_envs") if len(env_kwargs) != num_envs: - raise pufferlib.APIUsageError('env_kwargs must be a list of length num_envs') + raise pufferlib.APIUsageError("env_kwargs must be a list of length num_envs") for i in range(num_envs): if not callable(env_creators[i]): - raise pufferlib.APIUsageError('env_creators must be a list of callables') + raise pufferlib.APIUsageError("env_creators must be a list of callables") if not isinstance(env_args[i], (list, tuple)): - raise pufferlib.APIUsageError('env_args must be a list of lists or tuples') + raise pufferlib.APIUsageError("env_args must be a list of lists or tuples") if not isinstance(env_kwargs[i], dict): - raise pufferlib.APIUsageError('env_kwargs must be a list of dictionaries') + raise pufferlib.APIUsageError("env_kwargs must be a list of dictionaries") # Keeps batch size consistent when debugging with Serial backend - if backend is Serial and 'batch_size' in kwargs: - num_envs = kwargs['batch_size'] + if backend is Serial and "batch_size" in kwargs: + num_envs = kwargs["batch_size"] # TODO: Check num workers is not greater than num envs. This results in # different Serial vs Multiprocessing behavior @@ -752,14 +804,15 @@ def make(env_creator_or_creators, env_args=None, env_kwargs=None, backend=Puffer raise pufferlib.APIUsageError(f'Invalid argument: {k}') # TODO: First step action space check - + return backend(env_creators, env_args, env_kwargs, num_envs, **kwargs) + def make_seeds(seed, num_envs): if isinstance(seed, int): return [seed + i for i in range(num_envs)] - err = f'seed {seed} must be an integer or a list of integers' + err = f"seed {seed} must be an integer or a list of integers" if isinstance(seed, (list, tuple)): if len(seed) != num_envs: raise pufferlib.APIUsageError(err) @@ -768,48 +821,53 @@ def make_seeds(seed, num_envs): raise pufferlib.APIUsageError(err) + def check_envs(envs, driver): valid = (PufferEnv, GymnasiumPufferEnv, PettingZooPufferEnv) if not isinstance(driver, valid): - raise pufferlib.APIUsageError(f'env_creator must be {valid}') + raise pufferlib.APIUsageError(f"env_creator must be {valid}") driver_obs = driver.single_observation_space driver_atn = driver.single_action_space for env in envs: if not isinstance(env, valid): - raise pufferlib.APIUsageError(f'env_creators must be {valid}') + raise pufferlib.APIUsageError(f"env_creators must be {valid}") obs_space = env.single_observation_space if obs_space != driver_obs: - raise pufferlib.APIUsageError(f'\n{obs_space}\n{driver_obs} obs space mismatch') + raise pufferlib.APIUsageError(f"\n{obs_space}\n{driver_obs} obs space mismatch") atn_space = env.single_action_space if atn_space != driver_atn: - raise pufferlib.APIUsageError(f'\n{atn_space}\n{driver_atn} atn space mismatch') - -def autotune(env_creator, batch_size, max_envs=194, model_forward_s=0.0, - max_env_ram_gb=32, max_batch_vram_gb=0.05, time_per_test=5): - '''Determine the optimal vectorization parameters for your system''' + raise pufferlib.APIUsageError(f"\n{atn_space}\n{driver_atn} atn space mismatch") + + +def autotune( + env_creator, + batch_size, + max_envs=194, + model_forward_s=0.0, + max_env_ram_gb=32, + max_batch_vram_gb=0.05, + time_per_test=5, +): + """Determine the optimal vectorization parameters for your system""" # TODO: fix multiagent if batch_size is None: - raise ValueError('batch_size must not be None') + raise ValueError("batch_size must not be None") if max_envs < batch_size: - raise ValueError('max_envs < min_batch_size') + raise ValueError("max_envs < min_batch_size") num_cores = psutil.cpu_count(logical=False) idle_ram = psutil.Process().memory_info().rss load_ram = idle_ram # Initial profile to estimate single-core performance - print('Profiling single-core performance for ~', time_per_test, 'seconds') + print("Profiling single-core performance for ~", time_per_test, "seconds") env = env_creator() env.reset() obs_space = env.single_observation_space - actions = [ - np.array([env.single_action_space.sample() - for _ in range(env.num_agents)]) - for _ in range(1000) - ] + actions = [np.array([env.single_action_space.sample() for _ in range(env.num_agents)]) for _ in range(1000)] num_agents = env.num_agents steps = 0 @@ -823,7 +881,7 @@ def autotune(env_creator, batch_size, max_envs=194, model_forward_s=0.0, env.reset() reset_times.append(time.time() - s) else: - env.step(actions[steps%1000]) + env.step(actions[steps % 1000]) step_times.append(time.time() - s) steps += 1 @@ -835,35 +893,30 @@ def autotune(env_creator, batch_size, max_envs=194, model_forward_s=0.0, reset_mean = np.mean(reset_times) ram_usage = max(1, (idle_ram - load_ram)) / 1e9 - obs_size_gb = ( - np.prod(obs_space.shape) - * np.dtype(obs_space.dtype).itemsize - * num_agents - / 1e9 - ) + obs_size_gb = np.prod(obs_space.shape) * np.dtype(obs_space.dtype).itemsize * num_agents / 1e9 # Max bandwidth bandwidth = obs_size_gb * sps throughput = bandwidth * num_cores - print('Profile complete') - print(f' SPS: {sps:.3f}') - print(f' STD: {step_variance:.3f}%') - print(f' Reset: {reset_percent:.3f}%') - print(f' RAM: {1000*ram_usage:.3f} MB/env') - print(f' Bandwidth: {bandwidth:.3f} GB/s') - print(f' Throughput: {throughput:.3f} GB/s ({num_cores} cores)') + print("Profile complete") + print(f" SPS: {sps:.3f}") + print(f" STD: {step_variance:.3f}%") + print(f" Reset: {reset_percent:.3f}%") + print(f" RAM: {1000 * ram_usage:.3f} MB/env") + print(f" Bandwidth: {bandwidth:.3f} GB/s") + print(f" Throughput: {throughput:.3f} GB/s ({num_cores} cores)") print() # Cap envs based on max allowed RAM max_allowed_by_ram = max_env_ram_gb // ram_usage if max_allowed_by_ram < max_envs: max_envs = int(max_allowed_by_ram) - print('Reducing max envs to', max_envs, 'based on RAM') + print("Reducing max envs to", max_envs, "based on RAM") # Cap envs based on estimated max speedup - #linear_speedup = (num_cores * steps / sum_time) // 500 - #if linear_speedup < max_envs and linear_speedup > num_cores: + # linear_speedup = (num_cores * steps / sum_time) // 500 + # if linear_speedup < max_envs and linear_speedup > num_cores: # max_envs = int(linear_speedup) # print('Reducing max envs to', max_envs, 'based on single-core speed') @@ -871,25 +924,27 @@ def autotune(env_creator, batch_size, max_envs=194, model_forward_s=0.0, hardware_envs = max_envs - (max_envs % num_cores) if hardware_envs > batch_size and hardware_envs != max_envs: max_envs = int(hardware_envs) - print('Reducing max envs to', max_envs, 'based on core division') + print("Reducing max envs to", max_envs, "based on core division") max_allowed_by_vram = max_batch_vram_gb // obs_size_gb if max_allowed_by_vram < batch_size: - raise ValueError('max_allowed_by_vram < batch_size') + raise ValueError("max_allowed_by_vram < batch_size") print() configs = [] # Strategy 1: one batch per core strategy_cores = min(num_cores, max_envs // batch_size) - configs.append(dict( - num_envs=batch_size*strategy_cores, - num_workers=strategy_cores, - batch_size=batch_size, - backend=Multiprocessing, - )) - - strategy_min_envs_per_worker = int(np.ceil((batch_size+1) / num_cores)) + configs.append( + dict( + num_envs=batch_size * strategy_cores, + num_workers=strategy_cores, + batch_size=batch_size, + backend=Multiprocessing, + ) + ) + + strategy_min_envs_per_worker = int(np.ceil((batch_size + 1) / num_cores)) strategy_num_envs = [] for envs_per_worker in range(strategy_min_envs_per_worker, batch_size): num_envs = envs_per_worker * num_cores @@ -899,44 +954,52 @@ def autotune(env_creator, batch_size, max_envs=194, model_forward_s=0.0, continue # Strategy 2: Full async. Only reasonable for low bandwidth - #if throughput < 1.5: - configs.append(dict( - num_envs=num_envs, - num_workers=num_cores, - batch_size=batch_size, - zero_copy=False, - backend=Multiprocessing, - )) + # if throughput < 1.5: + configs.append( + dict( + num_envs=num_envs, + num_workers=num_cores, + batch_size=batch_size, + zero_copy=False, + backend=Multiprocessing, + ) + ) # Strategy 3: Contiguous blocks. Only reasonable for high bandwidth num_batchs = num_envs / batch_size if num_batchs != int(num_batchs): continue if throughput > 0.5: - configs.append(dict( - num_envs=num_envs, - num_workers=num_cores, - batch_size=batch_size, - backend=Multiprocessing, - )) - + configs.append( + dict( + num_envs=num_envs, + num_workers=num_cores, + batch_size=batch_size, + backend=Multiprocessing, + ) + ) + # Strategy 4: Full sync - perhaps nichely useful for strategy_cores in range(num_cores, 1, -1): if batch_size % strategy_cores != 0: continue - configs.append(dict( - num_envs=batch_size, - num_workers=strategy_cores, - batch_size=batch_size, - backend=Multiprocessing, - )) + configs.append( + dict( + num_envs=batch_size, + num_workers=strategy_cores, + batch_size=batch_size, + backend=Multiprocessing, + ) + ) # Strategy 5: Serial - configs.append(dict( - num_envs=batch_size, - backend=Serial, - )) + configs.append( + dict( + num_envs=batch_size, + backend=Serial, + ) + ) for config in configs: with pufferlib.Suppress(): @@ -948,7 +1011,7 @@ def autotune(env_creator, batch_size, max_envs=194, model_forward_s=0.0, start = time.time() while time.time() - start < time_per_test: s = time.time() - envs.send(actions[steps%1000]) + envs.send(actions[steps % 1000]) step_time += time.time() - s if model_forward_s > 0: @@ -963,11 +1026,11 @@ def autotune(env_creator, batch_size, max_envs=194, model_forward_s=0.0, end = time.time() envs.close() sps = steps * envs.agents_per_batch / step_time - print(f'SPS: {sps:.3f}') + print(f"SPS: {sps:.3f}") for k, v in config.items(): - if k == 'backend': + if k == "backend": v = v.__name__ - print(f' {k}: {v}') + print(f" {k}: {v}") print() diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 000000000..f109b17cb --- /dev/null +++ b/ruff.toml @@ -0,0 +1,9 @@ +line-length = 120 +target-version = "py310" + +[lint.isort] +lines-after-imports = 2 +split-on-trailing-comma = false + +[lint.mccabe] +max-complexity = 12 diff --git a/scripts/build_ocean.sh b/scripts/build_ocean.sh index 88909d44f..290a72af1 100755 --- a/scripts/build_ocean.sh +++ b/scripts/build_ocean.sh @@ -5,7 +5,12 @@ ENV=$1 MODE=${2:-local} PLATFORM="$(uname -s)" -SRC_DIR="pufferlib/ocean/$ENV" + +if [ "$ENV" = "visualize" ]; then + SRC_DIR="pufferlib/ocean/drive" +else + SRC_DIR="pufferlib/ocean/$ENV" +fi WEB_OUTPUT_DIR="build_web/$ENV" RAYLIB_NAME='raylib-5.5_macos' BOX2D_NAME='box2d-macos-arm64' @@ -56,7 +61,7 @@ if [ "$MODE" = "web" ]; then -DPLATFORM_WEB \ -DGRAPHICS_API_OPENGL_ES3 \ --preload-file pufferlib/resources/$1@resources/$1 \ - --preload-file pufferlib/resources/shared@resources/shared + --preload-file pufferlib/resources/shared@resources/shared echo "Web build completed: $WEB_OUTPUT_DIR/game.html" echo "Preloaded files:" echo " pufferlib/resources/$1@resources$1" @@ -64,17 +69,30 @@ if [ "$MODE" = "web" ]; then exit 0 fi +# Detect available compiler and set compiler-specific flags +if command -v clang >/dev/null 2>&1; then + COMPILER="clang" + ERROR_LIMIT_FLAG="-ferror-limit=3" + echo "Using clang compiler" +else + COMPILER="gcc" + ERROR_LIMIT_FLAG="-fmax-errors=3" + echo "Using gcc compiler (clang not found)" +fi + FLAGS=( -Wall -I./$RAYLIB_NAME/include -I./$BOX2D_NAME/include -I./$BOX2D_NAME/src -I./pufferlib/extensions + -I./inih-r62 "$SRC_DIR/$ENV.c" -o "$ENV" + ./inih-r62/ini.c $LINK_ARCHIVES -lm -lpthread - -ferror-limit=3 + $ERROR_LIMIT_FLAG -DPLATFORM_DESKTOP ) @@ -97,11 +115,11 @@ if [ "$MODE" = "local" ]; then -fsanitize=address,undefined,bounds,pointer-overflow,leak -fno-omit-frame-pointer ) - fi - clang -g -O0 ${FLAGS[@]} + fi + $COMPILER -g -O0 ${FLAGS[@]} elif [ "$MODE" = "fast" ]; then echo "Building optimized $ENV for local testing..." - clang -pg -O2 -DNDEBUG ${FLAGS[@]} + $COMPILER -pg -O2 -DNDEBUG ${FLAGS[@]} echo "Built to: $ENV" else echo "Invalid mode specified: local|fast|web" diff --git a/scripts/build_simple.sh b/scripts/build_simple.sh index 8d0711370..c61f58269 100644 --- a/scripts/build_simple.sh +++ b/scripts/build_simple.sh @@ -34,7 +34,7 @@ if [ "$MODE" = "debug" ]; then FLAGS+=( -fsanitize=address,undefined,bounds,pointer-overflow,leak -g ) - fi + fi clang -g -O0 ${FLAGS[@]} echo "Built to: $FILENAME (debug mode)" elif [ "$MODE" = "release" ]; then @@ -44,4 +44,4 @@ elif [ "$MODE" = "release" ]; then else echo "Invalid mode specified: debug|release" exit 1 -fi \ No newline at end of file +fi diff --git a/scripts/minshell.html b/scripts/minshell.html index 4068ca36c..d052141b2 100644 --- a/scripts/minshell.html +++ b/scripts/minshell.html @@ -34,9 +34,9 @@