:D Mordhau Prediction Unit predicts the likely winning team on Needy's Community Mordhau servers using historical matches, player ratings, and live scoreboard data.
The first version is a small microservice system:
data-serviceowns Needys API ingestion, match storage, and live scoreboard snapshotsprediction-servicebuilds player ratings and returns win probabilitiesautobalance-servicegenerates guarded, dry-run team assignmentsdashboarddisplays the live prediction and top player ratings
The prediction baseline is intentionally explainable: it builds Elo-style player ratings from completed matches, combines both teams' ratings with live scoreboard signals, then returns a probability for each team.
For the formulas behind player rating, team probability, confidence, and the ML model, see docs/MATHEMATICS.md.
services/
data-service/ FastAPI service for ingestion and SQLite persistence
prediction-service/ FastAPI service for ratings and win probabilities
autobalance-service/ FastAPI service for dry-run team balancing
apps/
dashboard/ React + Vite dashboard
docker-compose.yml Local microservice runtime
Double-click:
Start-MordhauPredictionUnit.bat
It creates missing Python virtual environments, installs dependencies, builds the dashboard, starts both APIs, syncs Needys match/player data, serves the dashboard, and opens http://127.0.0.1:5173.
To stop everything, double-click:
Stop-MordhauPredictionUnit.bat
Logs are written to .runtime/logs.
Run each service in its own terminal.
Requires Python 3.12+ and Node.js 20+. If python is not available in your terminal, install Python and enable the PATH option during installation.
cd services/data-service
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8001cd services/prediction-service
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
set DATA_SERVICE_URL=http://127.0.0.1:8001
uvicorn app.main:app --reload --port 8002cd apps/dashboard
npm install
npm run devOpen http://127.0.0.1:5173.
If Vite dev mode has trouble on a Windows/OneDrive path, use the production build with a static server:
cd apps/dashboard
npm run build
python -m http.server 5173 --bind 127.0.0.1 -d distdocker compose up --buildThen open http://localhost:5173.
Once the services are running, ingest historical matches:
curl -X POST "http://127.0.0.1:8002/ingest/history?limit=9999"The dashboard also has an Ingest button that calls the same prediction-service endpoint.
Data service on :8001:
GET /healthPOST /ingest/history?limit=9999GET /scoreboard/currentGET /matches/history?limit=9999&order=ascGET /matches/recent?limit=20GET /players/all?limit=20000GET /stats/databaseGET /stats/sync
The data service automatically refreshes the 100 most recent matches and 200 most-active
players every 30 seconds. Configure the worker with MPU_AUTOSYNC_INTERVAL_SECONDS,
MPU_AUTOSYNC_MATCH_LIMIT, and MPU_AUTOSYNC_PLAYER_LIMIT; set the interval to 0 to disable it.
The player catalog is restricted to SKM regulars: at least 5 Skirmish appearances during the
30 days preceding the newest stored match. Match history remains intact for model training, while
inactive and aggregate-only rows are removed from the player table. Configure this with
MPU_ACTIVE_SKM_MIN_MATCHES and MPU_ACTIVE_SKM_WINDOW_DAYS.
Prediction service on :8002:
GET /healthPOST /ingest/history?limit=9999GET /predict/currentGET /predict/random?team_size=5GET /ratings?limit=20000GET /matches/recent?limit=20POST /model/trainGET /model/status
Autobalance service on :8003:
GET /healthPOST /balance/previewPOST /balance/apply(intentionally disabled during the dry-run milestone)
Dashboard quality-of-life controls:
- live Needy's sync freshness, record totals, and next-refresh countdown
- pause/resume for the 30-second dashboard refresh (the preference is remembered)
- partial refresh handling so one unavailable service does not hide healthy data
- configurable dry-run move limit and fairness target
- CSV export for the currently filtered and sorted player list
Run the dependency-free unit tests:
cd services/data-service
.venv\Scripts\python.exe -m unittest discover -s tests -vcd services/prediction-service
.venv\Scripts\python.exe -m unittest discover -s tests -vcd services/autobalance-service
.venv\Scripts\python.exe -m unittest discover -s tests -vRun the live-service smoke checks after starting the application:
cd services/data-service
.venv\Scripts\python.exe scripts\smoke_check.pycd services/prediction-service
.venv\Scripts\python.exe scripts\smoke_check.pycd services/prediction-service
.venv\Scripts\python.exe scripts\ml_smoke_check.pycd apps/dashboard
npm run buildThe baseline remains simple and explainable, but the prediction service now has a first real ML training path. It builds a time-aware dataset from completed matches:
team features before match -> eventual winner
Training is intentionally leakage-aware: each match row uses ratings and player stats known before that match, then updates player ratings after recording the label.
Train or refresh the saved model:
curl -X POST "http://127.0.0.1:8002/model/train"The trained model is a small JSON logistic regression model stored under services/prediction-service/data/, which is ignored by Git. GET /model/status returns train/test accuracy, log loss, Brier score, model-vs-baseline backtesting, and the strongest learned feature weights.
Good next upgrades:
- per-map and per-mode player ratings
- recent form weighting
- stronger models such as gradient boosting once the dataset is larger
- confidence score based on team sample size and live match progress