Public 3d6vsTN combat encounter analyzer with editable fighters, rule toggles, and Monte Carlo simulation. https://pages.gi7b.org/gi7b/3d6vsTN-combat-analyzer/
  • JavaScript 91.8%
  • HTML 5.9%
  • CSS 1.4%
  • Python 0.9%
Find a file
2026-08-27 12:00:02 +08:00
logs docs: record Simple-mode lock sync item from gitb-3d6ttrpg (2/2 D&D-style, per-mode aggregation, margin=hits) 2026-07-22 22:37:42 +08:00
scripts chore(scripts): add 07-12 experiment harnesses 2026-07-22 08:16:37 +08:00
server fix(server): move /api/health before catch-all route 2026-07-11 19:17:29 +08:00
src Automated backup commit 2026-08-27 12:00 PST 2026-08-27 12:00:02 +08:00
tests Automated backup commit 2026-08-27 12:00 PST 2026-08-27 12:00:02 +08:00
.gitignore feat(server): add Express backend, auth, AI playtester, snapshots 2026-07-11 19:14:53 +08:00
AGENTS.md docs: add AGENTS.md design-methodology charter 2026-07-22 08:17:10 +08:00
deploy.py fix(deploy): use temp directory outside repo root for build artifacts 2026-07-10 00:05:00 +08:00
DIALOGUE (conflicted copy 2026-08-07 141720).md Automated backup commit 2026-08-27 12:00 PST 2026-08-27 12:00:02 +08:00
DIALOGUE.md docs: record Simple-mode lock sync item from gitb-3d6ttrpg (2/2 D&D-style, per-mode aggregation, margin=hits) 2026-07-22 22:37:42 +08:00
docker-compose.yml feat(server): add Express backend, auth, AI playtester, snapshots 2026-07-11 19:14:53 +08:00
index (conflicted copy 2026-08-07 141720).html Automated backup commit 2026-08-27 12:00 PST 2026-08-27 12:00:02 +08:00
index.html feat(simulator,ai): ready/aim/ammo economy, flanking, balanced + skirmish AE 2026-07-22 08:16:18 +08:00
README (conflicted copy 2026-08-07 141720).md Automated backup commit 2026-08-27 12:00 PST 2026-08-27 12:00:02 +08:00
README.md docs: sync README + RULES-NOTES with 07-12/07-13 state, consolidate backlog 2026-07-22 08:24:08 +08:00
RULES-NOTES (conflicted copy 2026-08-07 141720).md Automated backup commit 2026-08-27 12:00 PST 2026-08-27 12:00:02 +08:00
RULES-NOTES.md docs: record Simple-mode lock sync item from gitb-3d6ttrpg (2/2 D&D-style, per-mode aggregation, margin=hits) 2026-07-22 22:37:42 +08:00
smoke-test.js feat(terrain): add Random and Random (2d6) options to terrain dropdown 2026-07-11 21:39:08 +08:00
TOTM-GUIDE.md feat(grammar-of-position): apply movement vocabulary 2026-07-11 18:35:14 +08:00

3d6 vs TN Combat Analyzer

A static HTML/JS Monte Carlo combat simulator for the gi7b 3d6-vs-target-number tabletop RPG system.

Features

  • 3d6 vs TN attack, defense, damage, and toughness resolution
  • Condition box tracking with spillover and injury penalties
  • Equipment tables for weapons, armor, and shields
  • Sample fighters and adversary templates
  • Rule toggle modules
  • Action Economy modes: Standard (balanced 2 attack / 2 defense), Simple AE (1 attack + 1 defense), Complex AE (2 attacks + 2 defenses), and Skirmish AE (2 move / 1 attack / 1 defense — engine-level, not yet in the Mode dropdown)
  • Ranged mechanics: ready/reload, skill-capped aim, ammo economy with AI retreat, friendly fire into melee, turtle/crouch vs ranged, point-blank penalty, and bow draw weight (Effective Draw = STR + Archery skill, skill capped at +6)
  • Luck/Unluck toggle — per-encounter uses, 4d6 keep best/worst 3
  • Weapon grades (poor/mundane/good/fine) and an expanded bow list (selfbows, composite bows, recurve)
  • Skirmishing: reach superiority, staff defense bonus, constricted-weapon penalties, broken-ground movement-cost asymmetry
  • Flanking / gang-up numeric modifiers and narrowing-ground 1d6 bottleneck ratio
  • 2×2 simulator layout (widened): Characters and Scenario up top, Results and Metrics below
  • Chart.js visualizations and JSON/CSV export

Action Economy Modes

The simulator supports four encounter-resolution modes. The UI Mode dropdown offers Standard, Simple AE, and Complex AE — Simple AE is the UI default. Skirmish AE is implemented in the engine and exercised by tests/skirmish-test.js and the scripts/ experiments, but is not yet exposed in the dropdown. All modes run through the same action-economy engine, which resolves incoming attacks on one defender as a single aggregated exchange per round.

Mode Actions per round Default split (zones off) Split with TOTM zones Resolution
Standard 4 2 attacks + 2 defenses 1 move + 1 attack + 2 defenses Balanced symmetric default.
Simple AE 2 1 attack + 1 defense 1 move + 1 attack + 1 defense Reactions built in; lowest overhead.
Complex AE 4 2 attacks + 2 defenses 1 move + 2 attacks + 1 defense Trades a defense for an extra attack when mobile.
Skirmish AE 4 2 attacks + 2 defenses 2 moves + 1 attack + 1 defense Mobility-first; movement-cost asymmetry becomes decisive. Engine-level only.

In every mode, all attacks directed at the same defender in a round are aggregated into a single Defense Save. The exchange uses the highest attack TN among the incoming attackers, then applies a superiority modifier based on the ratio of incoming attack actions to the defender's allocated defense actions:

Ratio Modifier
1:1 0
1.5:1 / 3:2 ±1
2:1 ±2
3:1 ±3
4:1 ±4
5:1 ±5
10:1 ±10

The bonus goes to the side with more actions (defender if more defenses, attacker if more attacks), and the penalty applies to the side with fewer. Intermediate ratios are rounded to the nearest listed ratio using threshold rounding (e.g., 1.5→1, 2.0→2, 2.4→2, 4.0→4, 7.0→5, 10+→10). If the aggregated Defense Save succeeds, every incoming attack misses; if it fails, each successful attack is resolved for damage and Toughness separately.

Use the Compare Modes button to run the current encounter in both Standard and Simple AE with the same iteration count and see a side-by-side table of win rates, average rounds, and average rolls per combat. This makes the statistical impact of aggregation visible so you can decide whether the trade-off (fewer rolls, different swing) is acceptable.

Results in AE modes report average rolls per combat (attack checks, defense saves, damage checks, toughness saves) so the roll-reduction hypothesis can be observed.

Combat roadmap (Theater of the Mind)

The analyzer follows a phased rollout. Melee is complete and ranged is substantially built. Each later phase layers on the Theater of the Mind (TOTM) distance abstraction from ICRPG: distances are measured in move actions, and movement is resolved as a Maneuver check that can grant advantage or impose disadvantage based on terrain and circumstances.

  1. Melee — close-quarters baseline (done).
  2. Ranged — substantially implemented: TOTM zones and movement-to-engage, ready/reload, skill-capped aim, ammo economy, friendly fire into melee, turtle/crouch, point-blank, and bow draw weight are all in. Still missing: AI kiting / maintain-distance behavior, and perception/ambush checks in shadowed ground. Terrain shapes the fight: broken ground breaks up assault forces that rely on concentration, narrowing ground negates superior numbers and rewards quality fighters, etc.
  3. Magic — spell resolution, Toll of Magic, and magical conditions.
  4. Mounted — expand TOTM language for mount superiority. In EAS, the Persian/mounted tradition means fighting on foot is less heroic; the same Maneuver represents very different things on horseback versus on foot.
  5. Early firearms — arquebussiers and gunpowder skirmishers.
  6. Modern warfare — muskets through industrial and contemporary eras, eventually drones.

Local development

No build tools are required. Open index.html in any modern web browser.

cd /path/to/3d6vsTN-combat-analyzer
# Serve with any static server, or simply open index.html
python3 -m http.server 8000

Deployment

The analyzer runs as a Docker Compose stack on PC03 with an Express backend that serves the static app, handles authentication, and proxies AI requests to DeepSeek and PC00 Ollama.

# On PC03
cd /home/user/3d6vstn-analyzer/repo
docker compose up -d
  • Local URL: http://192.168.254.156:27082/
  • ZeroTier URL: http://192.168.196.76:27082/
  • Public URL: http://3d6sim.gi7b.org/ — live, verified 2026-07-11 (DNS resolves via Cloudflare, curl returns 200). HTTPS returns Cloudflare 525 (origin has no cert yet) — see below.

A cron job runs every minute at /home/user/3d6vstn-analyzer/sync.sh to git pull and restart the container when the repo changes, so committed/pushed updates appear almost instantly.

Public access setup

The Nginx Proxy Manager proxy host is a real DB-backed row (proxy_host.id = 44 in the nginx-pm260202-db-1 MariaDB container), routing 3d6sim.gi7b.org → 192.168.196.76:27082. The generated nginx config lives at /data/compose/3/data/nginx/proxy_host/44.conf on PC03 — that file is regenerated from the DB every time nginx-pm260202-app-1 restarts, so don't hand-edit it. An earlier session created a manual .conf file directly (bypassing the DB) as a workaround for not having NPM admin-panel credentials; that file did not survive an app restart and was replaced by the proper DB-driven config (backup kept as 99.conf.manual-backup-20260711-204425 on PC03). See tools/IT-knowledge/skills/npm-direct-db-modification/SKILL.md for the reusable technique (BRC creds → SSH → docker exec → INSERT INTO proxy_host → restart) and its worked example for this exact host.

Remaining:

  1. Add a Cloudflare DNS A record for 3d6sim.gi7b.org — done, DNS already resolves via Cloudflare anycast IPs.
  2. HTTPS/SSL still pending — certificate_id=0, ssl_forced=0 on proxy host 44. Issuing a real Let's Encrypt cert needs the NPM app to actually run an ACME challenge (DNS-01 via Cloudflare), which isn't a DB-only operation — it needs either the NPM Admin UI (http://192.168.196.76:81, no stored login in BRC) or running certbot by hand inside the NPM container. Until then, set Cloudflare's SSL/TLS mode to "Flexible" for this domain as a stopgap, or leave HTTP-only.

Environment variables

Create a .env file in the repo root:

DEEPSEEK_API_KEY=sk-...
OLLAMA_HOST=http://192.168.196.75:11434
APP_USERNAME=justin
APP_PASSWORD=...
SESSION_SECRET=...

Forgejo Pages (static-only fallback)

Run the deployment script to inline all CSS/JS into index.html and push to the static-pages branch:

python3 deploy.py

The script reads Forgejo credentials from ~/.brc/forgejo or the workspace credential store and pushes via SSH.

AI Playtester

After running a simulation, log in to the AI panel and click Ask Playtester. The backend sends the combat log and current rule configuration to DeepSeek (or PC00 Ollama) using a prompt template. Responses are saved to analysis-logs/ for later review. You can also save project snapshots to snapshots/ to preserve rule states and recover broken functions.

File layout

index.html              Main page
docker-compose.yml      Docker Compose stack
server/                 Express backend (auth, AI proxy, snapshots)
  package.json
  Dockerfile
  index.js
  prompts/              AI prompt templates
analysis-logs/          Saved AI analyses (runtime)
snapshots/              Saved project snapshots (runtime)
src/engine.js           Dice and combat resolution
src/rules.js            Rule toggle registry
src/simulator.js        Monte Carlo runner
src/data.js             Fighters and equipment
src/fighter-editor.js   Fighter browser/editor UI
src/charts.js           Chart.js wrappers
src/app.js              Main UI wiring
src/ai-analyzer.js      AI playtester client
src/style.css           Dark theme styles
deploy.py               Static-site deployment script
README.md               This file

Verification targets

Recorded with the pre-2026-07-12 canonical Standard loop and 1,000 duels:

  • Kaveh vs Bandit: Kaveh ~100%, avg rounds ~2.4
  • Roshan vs Bandit: Roshan ~99.9%, avg rounds ~2.5
  • Kaveh vs Roshan: Kaveh ~69%, avg rounds ~4.9
  • Vahram vs Kaveh: Vahram ~91%, avg rounds ~3.8

These targets were recorded under the pre-2026-07-12 canonical per-attack loop ("one attack per fighter per round, aggregation off"). Standard mode is now the balanced 2 attack / 2 defense economy routed through the same exchange engine as the other AE modes, so current numbers may have drifted — re-run the presets or use Compare Modes to check before quoting them.