Skip to content

Three-Cushion Solver Dataset — Open Carom Position Data

100 carom positions; 249 model-valid simulator results and 151 empty or rejected slots, with a versioned manifest and free JSON.

Author: Setviva Engineering 567 words 3 min read

An original, versioned simulator dataset with 100 carom positions, 249 model-valid results and 151 empty or rejected slots (400 evaluated result slots). Values come from a deterministic versioned physics cache, not machine learning or a real-table success study.

100
positions
249
valid solver results
151
empty or rejected result slots
400
evaluated result slots
50
three-cushion
50
free carom

Methodology

The published artifact is generated from the versioned solver cache. The engine profile, source fingerprint, counts and SHA-256 are recorded below so the download can be verified independently. A row is a simulator result only when the rule-aware engine completes the selected mode; it is not a real-table success-rate claim.

Versioned simulator data

Engine contract
3ball-carom-match-v2 / physics 2.0.0 / solver 4.2.0
Dataset SHA-256
256a8ec7e7e9a66d907bd43f5fdd6b87628dc067fd9621f2d2ebd2472c3e43ed
valid solver results
249
empty or rejected result slots
151
Review status
machine-assisted-reviewed

Download manifest

Field dictionary

position: cueIdx, mode, firstIdx, balls, shots · ball: color, p.x, p.y · shots[]: shot {angle, power, sideSpin, topSpin}, label, length, margin, pct, tol, perturbPct, cushions.

Verify the public downloads

Download verification checks file integrity and counts. It does not reproduce the physics engine, independently validate a shot or establish real-table accuracy. The complete generation environment is not publicly distributed.

Save the following as verify_3ball.py and run it with Python 3. It uses only the standard library and downloads public JSON from 3ball.app; no account or project checkout is needed.

import hashlib, json, urllib.request

BASE = 'https://3ball.app/'
ALLOWED = {'data/solutions_cache.json', 'data/solutions_cache_v3.json'}
def download(path):
    with urllib.request.urlopen(BASE + path, timeout=30) as response:
        return response.read(2_000_001)

manifest = json.loads(download('data/research-manifest.json'))
for key in ('source', 'portfolioBeta'):
    record = manifest if key == 'source' else manifest[key]
    source = record['source']
    assert source['path'] in ALLOWED, 'Unexpected dataset path'
    raw = download(source['path'])
    assert len(raw) == source['bytes'], 'File size mismatch'
    assert hashlib.sha256(raw).hexdigest() == source['sha256'], 'Hash mismatch'
    data = json.loads(raw)
    positions = list(data['positions'].values())
    assert len(positions) == record['counts']['positions'], 'Position count mismatch'
    if key == 'source':
        shots = [s for p in positions for s in p['shots']]
        assert sum(s is not None for s in shots) == record['counts']['validShots']
        assert sum(s is None for s in shots) == record['counts']['rejectedSlots']
    else:
        continuations = list(data['continuations'].values())
        assert len(continuations) == record['counts']['continuations']
        solutions = [s for p in positions + continuations for s in p['solutions']]
        assert sum(s is not None for s in solutions) == record['counts']['solutions']
    print(source['path'], 'OK', record['counts'])

Scope and limitations

This is a deterministic simulator dataset, not a real-table success study. The published cache uses the frozen V2 profile. Physics V3 is still awaiting independent real-table measurement. Margin and robustness fields are model diagnostics, not real-world probabilities.

License and privacy

The download contains computed table coordinates and solver outputs only; no photos, account identifiers, free text or personal data. Licensed CC BY 4.0.

Sample positions

PositionModeBallsSolver shotsBest margin
3cushion::white::r2.130,0.710|w0.710,0.528|y0.710,0.710three-cushion331.15
freecarom::white::r2.130,0.710|w0.710,0.528|y0.710,0.710free carom311.32
3cushion::yellow::r2.130,0.710|w0.710,0.528|y0.710,0.710three-cushion310.94
freecarom::yellow::r2.130,0.710|w0.710,0.528|y0.710,0.710free carom311.36
3cushion::white::r2.550,0.500|w0.300,0.300|y2.500,1.100three-cushion340.28
freecarom::white::r2.550,0.500|w0.300,0.300|y2.500,1.100free carom340.50
3cushion::yellow::r2.550,0.500|w0.300,0.300|y2.500,1.100three-cushion320.97
freecarom::yellow::r2.550,0.500|w0.300,0.300|y2.500,1.100free carom342.13
3cushion::white::r0.550,0.300|w1.420,0.710|y1.950,1.100three-cushion311.47
freecarom::white::r0.550,0.300|w1.420,0.710|y1.950,1.100free carom321.50
3cushion::yellow::r0.550,0.300|w1.420,0.710|y1.950,1.100three-cushion310.24
freecarom::yellow::r0.550,0.300|w1.420,0.710|y1.950,1.100free carom320.92

Download the full dataset

The complete machine-readable dataset is free to download and reuse with attribution.

Download JSON

How to use it

Use the sample coordinates and cue-ball identity to place a position in the trainer. The frozen V2 result and the current V3 replay can differ; record the engine version when comparing them.

Open the trainer

Use the sample coordinates and cue-ball identity to place a position in the trainer. The frozen V2 result and the current V3 replay can differ; record the engine version when comparing them.

Open the trainer →