A Go-based tool for pre-computing and serving optimal weapon builds in Escape from Tarkov. The system analyzes all possible weapon configurations with different trader level constraints to find builds that minimize recoil or maximise ergonomics (todo).
The project consists of three main components:
- Importer - Fetches weapon and modification data from the tarkov.dev GraphQL API and stores it in PostgreSQL
- Evaluator - Pre-computes optimal weapon builds for all weapons across various trader level combinations using a candidate tree algorithm
- API - REST API that serves pre-computed optimal builds based on user constraints
Additionally, an Next.js UI for visualising evaluated builds can be found here.
The system follows a batch processing architecture with three main services:
┌──────────────┐
│ tarkov.dev │
│ GraphQL │
└──────┬───────┘
│
│ fetch weapons/mods
▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Importer │────────▶│ PostgreSQL │◀────────│ Evaluator │
│ │ write │ │ read/ │ │
│ │ data │ - weapons │ write │ │
└──────────────┘ │ - mods │ builds └──────────────┘
│ - traders │
│ - conflicts │
│ - builds │
└──────┬───────┘
│
│ read builds
▼
┌──────────────┐
│ API │
│ (REST) │
└──────────────┘
- Importer: Fetches weapon and mod data from tarkov.dev GraphQL API, transforms it, and writes to the database. Optionally caches responses to disk as JSON (primarly for development purposes).
- Evaluator: Reads weapon/mod data from the database, computes optimal builds for all weapons across different trader level combinations, and writes results back to the database. Runs as a batch job.
- API: REST service that queries pre-computed builds from the database based on user-specified constraints (weapon ID, trader levels).
- PostgreSQL: Central data store holding weapon definitions, mods, trader offers, item conflicts, and computed optimal builds.
Each weapon has modification slots (e.g., "Handguard", "Muzzle") that accept compatible items. Items can have their own nested slots, forming a tree structure. The goal is to find the combination that minimizes recoil (or maximizes ergonomics - TODO).
Checking every possible combination would be intractable for complex weapons. Instead, the evaluator uses recursive search with several optimizations:
Branch-and-bound pruning — Before exploring a branch, calculate the theoretical best possible outcome for remaining slots. For recoil optimization, this means computing the lowest achievable recoil by using each slot's best recoil modifier. If even this best-case scenario can't beat the current solution, skip the entire branch.
Conflict handling — Some items are incompatible (e.g., certain stocks conflict with certain grips). When an item is selected, all incompatible items are added to an exclusion list for that branch. The evaluator also tries leaving slots empty, since avoiding a conflicting item in one slot may enable better items in other slots.
Conflict-free caching — Items without conflicts always produce the same optimal subtree. When such an item is encountered, its previously computed result (if cached) can be reused. This also enables additional pruning: if the cached subtree's stats can't improve the current best, skip evaluating that entire subtree.
Useless item pruning — Before evaluation starts, each item's potential value (its own modifier plus the best possible contribution from its nested slots) is calculated. Items whose best-case subtree cannot improve the target stat are filtered out (e.g., an item with a minimum achievable recoil of +5 when minimizing recoil).
The algorithm explores all viable branches and is guaranteed to find the globally optimal build, but pruning eliminates the vast majority of the search space.
Required:
docker&docker-composego1.22+- Task (task runner)
Optional:
nodejs(only needed for updating the tarkov.dev GraphQL schema)
# PostgreSQL connection
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your_password
POSTGRES_DB=tarkov-build-optimiser
# Application settings (optional)
ENVIRONMENT=development
POOL_SIZE_MULTIPLIER=2 # CPU cores × this value = number of evaluator workersNote: For local development, these are the recommended defaults. Docker Compose will override POSTGRES_HOST to postgres for containerized services.
To run the entire system with Docker Compose:
docker compose up -dThis will:
- Start PostgreSQL and apply migrations
- Run the importer to fetch weapon/mod data from tarkov.dev
- Run the evaluator to pre-compute optimal builds (this will take a long time!)
- Start the API server on
http://localhost:8080
Note: The importer and evaluator run as one-time jobs. Once they complete, only PostgreSQL and the API will remain running.
Set up the development environment (installs dependencies, starts Docker containers, runs migrations):
task initOr if you don't have Node.js installed:
task init:go-only- Import weapon data:
task importer:startThe importer caches data to file-caches/*.json by default. To use cached data instead of fetching from tarkov.dev:
task importer:start:use-cacheTo only cache without updating the database:
task importer:start:cache-only- Compute optimal builds:
task evaluator:startFor faster testing with limited weapons:
task evaluator:start:test-mode- Start the API:
task api:startThe API will be available at http://localhost:8080.
Start all services (database, migrations, importer, evaluator, API):
task compose:upStop all services:
task compose:downReturns a list of all weapons in the database.
Returns the pre-computed optimal build for a weapon.
Query Parameters:
build_type- Type of optimization (currently onlyrecoilis supported)jaeger_level,prapor_level,skier_level,peacekeeper_level,mechanic_level- Trader levels (1-4, defaults to 4)
Example:
curl "http://localhost:8080/api/items/weapons/5447a9cd4bdc2dbd208b4567/calculate?build_type=recoil&prapor_level=2&mechanic_level=3"All tests:
task testUnit tests only (no database required):
task test:unitIntegration tests (requires database):
task test:integrationApply migrations:
task migrate:upRollback migrations:
task migrate:downCreate a new migration:
task migrate:create -- migration_nameThe GraphQL queries are defined in internal/tarkovdev/schemas/queries.graphql. The client code is auto-generated using:
graphql-inspector- Introspects the tarkov.dev API to generateschema.graphqlgenqlient- Generates Go functions and types from the schema and queries
Update both schema and generated code:
task tarkovdevOnly fetch the latest schema:
task tarkovdev:get-schemaOnly regenerate Go code from existing schema:
task tarkovdev:regenerateView all available tasks:
task --list-all.
├── cmd/ # Entry points for binaries
│ ├── api/ # REST API server
│ ├── evaluator/ # Build optimization engine
│ ├── importer/ # Data import from tarkov.dev
│ └── migrations/ # Database migration runner
├── internal/
│ ├── candidate_tree/ # Core optimization algorithm
│ ├── evaluator/ # Build evaluation logic
│ ├── models/ # Database models
│ ├── router/ # API routes and handlers
│ ├── tarkovdev/ # GraphQL client for tarkov.dev
│ ├── db/ # Database connection utilities
│ ├── cache/ # Caching implementations
│ └── importers/ # Import logic for weapons/mods
├── migrations/ # Database migrations (goose)
├── docker/ # Dockerfiles for each service
└── file-caches/ # JSON caches for tarkov.dev data
Mozilla Public License Version 2.0 - See LICENSE for details.
- tarkov.dev API Playground - Source of weapon and modification data
- tarkov.dev - Community-maintained Escape from Tarkov database
- genqlient - A type-safe GraphQL client for Go