A highly parallelised, Monte Carlo Progression Simulator for custom BasketballGM Prog scripts
Progbox is a native C++ engine that runs hundreds or thousands of independent progression passes over a BasketballGM roster. It outputs aggregated statistics and tuner-focused diagnostic charts, serving as a powerful sandbox for balancing progression algorithms.
This engine ports the following NoEyeTest (NET) progression scripts into native C++:
- NET v3.2 (python ver branch: dev_v3.2)
- NET v4 (python ver branch: updatealgo)
- NET v4.1 (python ver branch: dev_v4.1)
With this rewrite, all scripts live in a single codebase. no more switching branches to test different algorithms.
The previous Python implementation suffered from two limitations: maintaining branching config dictionaries, and the GIL blocking true multithreading. Progbox solves both using a Registry Pattern.
Progression scripts are now self-contained C++ objects. Adding a new script requires zero changes to the core engine. The result is a single, compact binary you can plug into any frontend/backend pipeline (tested on Linux and WSL, Ubuntu 20+ as I require std::filesystem).
graph TD
A[CLI Execution] --> B[Parse Args & Create Timestamped Output Dir]
B --> C[ProgressionRegistry::create]
C --> D[Load & Filter Players from BBGM Export]
D --> E{Players Found?}
E -- No --> F[Exit 1: Check export/year]
E -- Yes --> G[Write metadata.json]
G --> H[SimEngine::run - Multi-threaded Monte Carlo]
H --> I{Results Valid?}
I -- No --> J[Exit 1: Simulation Error]
I -- Yes --> K[Analytics Pipeline]
K --> L[Export outputs.csv]
K --> M[Export summary.csv]
K --> N[Export godprogs.json & superlucky.json]
L & M & N --> O[Python Post-Processor]
O --> P[analysis.xlsx & 8 Diagnostic Charts]
.
├── src/
│ ├── main.cpp # Entry point, CLI parsing, orchestration
├── include/scripts/
│ ├── v321_progression.hpp # NET v3.2.1 implementation
│ └── v41_progression.hpp # NET v4.1 implementation
├── include
│ ├── sim_engine.hpp # Thread-pool Monte Carlo harness
│ ├── analytics.hpp # Raw CSV/JSON export logic
│ ├── progression_registry.hpp # Auto-discovery of progression scripts, (TO EDIT WHEN NEW SCRIPTS ARE ADDED)
│ ├── i_progression.hpp # Interface all scripts must implement
| ├── ovr_math.hpp # posted BBGM OVR calculation logic
| ├── json.hpp # https://github.com/nlohmann/json
| ├── progress.hpp # factory progress bars!!
│ └── core_types.hpp # shared data structures
├── tools/
│ └── analysis.py # Python post-processor (Excel/Charts)
├── data/
│ ├── export.json # BBGM league export
│ └── teaminfo.json # Team ID to Name mapping
├── outputs/
│ └── {YYYYMMDDHHMMSS}/ # Timestamped run directory
├── buildprogbox.sh # Quick build script
└── CMakeLists.txt
- Compiler: C++17 or higher (GCC/Clang recommended).
- Dependencies: nlohmann/json (header-only, already included in the include folder).
- Build:
# Quick build (Linux/WSL) chmod +x buildprogbox.sh && ./buildprogbox.sh # Or via CMake directly mkdir build && cd build cmake .. && make -j$(nproc)
Required only for the Excel workbook and diagnostic charts.
pip install numpy pandas scipy matplotlib openpyxlPlace your BBGM league export at data/export.json and team metadata at data/teaminfo.json.
Configuration is handled entirely via CLI arguments. No source code editing required.
./progbox data/export.json data/teaminfo.json ./outputs \
-v v41 \
-r 1000 \
-y 2021 \
-w 12 \
-s 69| Argument | Description | Default |
|---|---|---|
export.json |
Path to BBGM player export | (Required) |
teaminfo.json |
Path to team info lookup | (Required) |
output_dir |
Base directory for results | (Required) |
-v, --version |
Progression script ID | v321 |
-r, --runs |
Number of Monte Carlo passes | 1000 |
-y, --year |
Season year (for age calculation) | 2021 |
-w, --workers |
Thread pool size (0 = auto) |
hardware_concurrency |
-s, --seed |
Master RNG seed (0 = random) |
0 |
Note: The master RNG derives each run's seed, so the same seed always produces the exact same set of simulations. Important for reproducibility of the monte carlo simulations. Otherwise, the simulations would not hold water mathematically and practically for any form of cross-script or tuning comparison.
The C++ engine applies a strict filter pipeline: players must have tid >= -1, non-empty stats, PER > 0, and age >= 25.
All outputs are written to outputs/{RUN_TS}/:
Reproducibility tracking. Records the exact CLI args, progression version, CalVer timestamp, and seed used.
Long-format table with one row per player × run.
| Column Group | Description |
|---|---|
Run, RunSeed |
Simulation index and its specific RNG seed |
Name, Team, Age, PlayerID |
Player metadata |
Baseline, Ovr |
OVR before and after progression |
Delta, PctChange, AboveBaseline |
Outcome metrics |
PER, DWS, EWA |
Input stats driving the algorithm |
dIQ … 3Pt |
Final simulated attribute values (15 attrs) |
Per-player aggregated distributions computed natively in C++ (mean, std dev, min/max, Q10/Q25/Q75/Q90 quantiles, and % of runs above baseline).
Logs every rare god-progression event (name, run_seed, age, ovr, bonus, chance) and a summary map of player_name → total_god_prog_count.
After C++ exports the data, it automatically invokes tools/analysis.py. This script generates a styled analysis.xlsx workbook and an 8-chart diagnostic dashboard in charts/. The charts are specific to v4.1 mostly, but are easily modifiable.
All thresholds and splits are derived dynamically from the dataset:
- Age Tier Profiles: KDE density curves per age group checking if tiers produce distinct shapes.
- Composite Calibration: Observed mean delta vs composite score, overlaid on the formula's theoretical lo/hi band.
- Physical vs Cognitive: Mean attribute delta for physical vs skill groups per age tier (validates age-gate restrictions).
- Player Reliability: Scatter plot (Mean Delta vs Std Delta) identifying Boom-or-Bust vs Locked-In players.
- Attribute Heatmap: Player × Attribute grid showing net movement, highlighting physical decline gates.
- Composite Tier Separation: Violin plots checking if within-age-group composite quartiles strictly order outcomes.
- Outcome Range: Horizontal span bars (P5/P25/P50/P75/P95) showing realistic floors and ceilings per player.
- Convergence: Running mean delta ± MCSE for high-variance players to validate if
runsis high enough.
You never need to touch the engine code.
1. Create the implementation in src/scripts/vXXX_progression.hpp:
#include "i_progression.hpp"
namespace progbox {
class VXXXProgression final : public IProgressionStrategy {
public:
[[nodiscard]] std::string version() const noexcept override { return "vXXX"; }
ProgressionResult progress(const PlayerState& state, std::mt19937_64& rng) const override {
// Your algorithm here
return result;
}
};
}2. Register it in progression_registry.hpp:
#include "scripts/vXXX_progression.hpp"
// Inside the registry vector:
{
"vXXX",
"VX.X - Short description of your script",
[]() -> std::unique_ptr<IProgressionStrategy> { return std::make_unique<VXXXProgression>(); }
},Rebuild. The CLI (-h), registry, and engine will automatically discover and support ./progbox ... -v vXXX.