Skip to content

Releases: zintix-labs/problab

v0.15.0

Choose a tag to compare

@nextso nextso released this 04 Oct 15:27

v0.15.0 Strategy-driven player experience analysis

New: finite player experience analysis

Use make exp to evaluate player experiences under configurable strategies, separately from the long-run simulator.

Configure player counts, initial balances, win-multiplier buckets, ordered events, and confidence levels through YAML.

Inject game-scoped strategy factories and tag predicates.

Give strategies the executor’s settled balance, completed spin count, total wager, and total win.

Analyze event encounter frequencies, win distributions, player RTP, and bankrupt/satisfied/dissatisfied/other exits.

Run players sequentially on a continuous deterministic RNG stream, using the configured native or optimal runtime.

Display player completion progress and preserve completed-player statistics when execution fails or is canceled.

Each player is limited to 20,000 completed spins. This release supports complete-result games without checkpoints or continuation steps.

Reports

Each run publishes five detailed CSV files and one complete, versioned report.json.

The JSON report includes all analysis groups, settings, event tags, aggregate statistics, and execution status. All six files are published together; failed publication does not leave a partially published report.

Reports contain seed material and should not be distributed to parties that must not receive it.

Strategies and integration

Added the optional playerexp/strategies.Monotone factory: fixed wagering, a configurable satisfaction target, and a spin limit.

Separated demo strategy assembly into demo/demo_strategies.

Added Machine.SpinOffline for sequential deterministic offline execution with validated wagers and borrowed result buffers.

Made Lab construction an explicit injection point in cmd/opt.

Breaking changes

Replace imports of demo/demo_logic/game_tags with demo/demo_tags, and update the package qualifier to demo_tags.

Replace game_tags.GameTagCatalog with tag.GameTagCatalog from sdk/tag.

No compatibility forwarding package is provided. YAML tag names and predicate semantics are unchanged.

v0.14.0

Choose a tag to compare

@nextso nextso released this 01 Oct 15:36

v0.14.0 — Shared Optimizer CLI Entry Point
Introduces optimizer/v2/cli.Run, allowing applications to reuse the official execution flow, progress display, result summaries, and distribution CSV output without maintaining their own CLI implementation.

  • Accept configuration bytes, a Problab instance, collection tags, and a result converter through Options.
  • Validate required tag injection before executing any plans, with actionable diagnostics.
  • Provide consistent cancellation and exit-code handling without closing the caller-owned Problab instance.
  • Improve terminal detection and synchronize concurrent progress events.
  • Simplify cmd/opt and remove obsolete POC build constraints and comments.
    Existing YAML configuration, core optimization logic, and artifact formats remain unchanged. Applications with custom CLI implementations can migrate to the new entry point at their convenience.

v0.13.0

Choose a tag to compare

@nextso nextso released this 30 Sep 07:18

v0.13.0 — Minimum-CV Selection, Trim/Damp Screens, and Synchronized Layers

Optimizer

  • Replace per-bucket lexicographic selection in Step 5 with a single LP minimizing the unconditional second moment within the feasible region retained by Steps 1–4.
  • With fixed expectation and positive RTP, this objective is equivalent to minimizing CV. Existing hard constraints and earlier intent-stage restrictions remain in effect.
  • Add fixed-expectation validation, selection statistics, and numerical and reproducibility tests.

This changes the selection policy, not just performance. Re-optimizing an existing configuration may produce different bucket probabilities, CV, and output hashes. Unique solutions and cross-platform byte-for-byte reproducibility are not guaranteed.

Screen Generation

  • Add per-column top trimming through trim, using -1 for unavailable cells.
  • Add GenScreenWithDamp and its explicit reel-set counterpart to generate both normal and display screens with upper and lower damp symbols.
  • Use independent, reusable buffers. Given identical settings and initial RNG state, normal and WithDamp generation produce identical normal screens and final RNG states.

Layers and Screen Operations

  • Add sdk/layer with basic value types, named basic types, reset operations, and independent snapshots.
  • Allow Clear, Gravity, FillScreen, and FillScreenByHole to accept heterogeneous layers for synchronized clearing, movement, and refill resets.
  • Preserve negative cells in place. Gravity can move symbols across gaps; refill skips negative cells without consuming reel positions.
  • Add synchronization, negative-cell scoring, allocation, and performance tests, and reduce overhead when no layers are supplied.

Migration Notes

  • ScreenSetting.Mask / mask is replaced by Trim / trim. Trim specifies the number of cells removed from the top of each column, not an arbitrary mask. Migrate configurations according to their intended shape rather than simply renaming the field. Omitted Trim defaults to all zeros.
  • GenReelBySymbolWeight with damp > 0 now performs additional display-symbol draws, including through the normal generation API. This changes subsequent RNG sequences. Reel-index generation does not incur these additional draws.
  • Generation behavior remains unchanged when Trim is omitted and damp = 0.
  • Existing direct ops calls remain valid. Function-value assignments using the previous fixed signatures must adopt the new variadic signatures.
  • Custom game logic that indexes arrays directly by symbol value must handle negative values before enabling Trim.
  • Layers are not concurrency-safe. Layers supplied to a synchronization operation must not be duplicated or share backing storage.

Other Changes

  • Fix a variable-shadowing issue.
  • Add source license headers.

v0.12.1

Choose a tag to compare

@nextso nextso released this 18 Sep 01:49

SDK tags and optimizer v2 isolation

  • Added sdk/tag with validated tag registration, immutable bitsets, and explicit errors for unknown tag names.
  • Optimizer v2 now uses game-scoped tags supplied through WithCollectionTags. Independent Tuners no longer share a global tag registry.
  • Collection and native/RGS replay verification retain the same run-local predicates.
  • Moved demo bg and fg definitions into demo/demo_logic/game_tags, preserving their existing behavior.
  • Optimizer v1 remains unchanged. Neither the v2 runtime nor its tests depend on v1.

Migration

  • Change collection predicate types from optimizer.IsTag to tag.IsTag from sdk/tag.
  • Explicitly register every tag referenced by your configuration, including bg and fg. Optimizer v2 no longer supplies built-in tags.
  • The YAML tag-filter syntax is unchanged.
  • Tag predicates must be read-only and safe for concurrent calls. Independent Tuner isolation does not make concurrent Run calls on a single Tuner supported.

v0.12.0

Choose a tag to compare

@nextso nextso released this 16 Sep 08:21

v0.12.0 — RGS Exports and Unified Data Exchange

New Features

  • rgs-collected exports a complete collected result library without running LP, supporting external optimization workflows.
  • rgs-optimized exports per-sample probabilities and replayed results after LP, without converting through an AliasTable.
  • Custom result conversion through WithResultConverter and dto.ResultConverter. The default IdentityConverter preserves the native DTO representation.
  • Result libraries use streaming results.jsonl.zst; Parquet uses internal ZSTD compression and is directly readable by PyArrow and pandas.
  • RGS and native formats can be combined. Collected exports run first; LP runs only when requested by subsequent formats.

Unified Parquet Schema

Columns:

record_index, class_id, class_name, probability, win_multiplier, win, bet

Collected rows have null probability; optimized rows contain unconditional point probabilities. Integer win and bet preserve exact payout values without reconstructing them from floating-point multipliers.

record_index is the zero-based JSONL row index within its own output family. Indexes must not be mixed across families.

Compatibility and Important Notes

  • Catalog DatasetID and application metadata have been removed. Consumers relying on them must be updated. Keep the run report and matching files together.
  • Partial and distinct recovery banks no longer produce catalogs or proceed to RGS export.
  • IdentityConverter includes PRNG snapshots and checkpoint data. Remove sensitive replay material through a custom converter before sharing with parties who should not receive it.
  • External probability import, CDF generation, and integer-weight conversion are not provided. External optimization and sampling remain the consumer’s responsibility.
  • The LP solving algorithm and native artifact formats remain unchanged, with byte-for-byte native output compatibility verified.

Reliability and Validation

Fixed nil CLI writer handling, failed-export directory cleanup, and replay error classification. Distribution reports now distinguish point probabilities from alias marginals.

Validated with full tests, race detection, vet, lint, Python interoperability, and streaming memory checks, including multi-Class mappings, reordered Parquet pairing, and mixed-output failure scenarios.

v0.11.0

Choose a tag to compare

@nextso nextso released this 11 Sep 08:54

Optimizer improvements

Canonical selection progress

Step 5 now displays completed/total bucket counts in interactive terminals, making long-running canonical selection easier to follow. Redirected logs retain completion/failure output without progress redraws.

The underlying optimization algorithm is unchanged.

Configurable Class weight base

Added overall.class_weight_base to configure the Class probability denominator per intent.

  • Accepts integers from 100,000 to 1,000,000,000, inclusive.
  • Defaults to 1,000,000 when omitted.
  • Requires all positive Class weights to sum exactly to the configured base.
  • Does not automatically rescale weights.

To preserve existing Class probabilities, scale every Class weight by the same factor as the base. For example, changing the base from 1M to 1B requires multiplying every weight by 1,000. When reducing the base, all scaled weights must remain positive integers.

Existing collection banks remain replayable. Resolved plans and reports now include the effective denominator, so ConfigHash values change even when the default base is retained.

Parquet collection catalogs

Optimizer v2 now exports a Parquet catalog alongside each successfully saved collection bank, including partial and distinct recovery banks.

External tools can inspect collected outcomes without reading the seed bank or running Problab. Catalogs contain:

  • Bank record index
  • Class ID and name
  • Payout multiplier
  • Collection predicates, quotas, retained counts, and provenance metadata
  • A deterministic Dataset ID for source matching

Game IDs use decimal strings to preserve uint64 precision. Catalogs are written in bounded batches with atomic file replacement; export failures produce advisory warnings without invalidating the saved bank.

Collection data, bank format, and built-in optimization results remain unchanged. External probability import and publication are not included.

Python examples and verification instructions are available in optimizer/v2/examples/collection_parquet/.

Dependencies and verification

Added github.com/parquet-go/parquet-go v0.32.0 and its transitive dependencies.

Verification covers Go tests, race checks, PyArrow interoperability, cancellation, write failures, input immutability, Dataset ID stability, proportional weight scaling, and unchanged optimization results.

v0.10.2

Choose a tag to compare

@nextso nextso released this 08 Sep 12:05

v0.10.2
Bucket Distribution Reports

  • Added median and mean for payout multipliers within each bucket. Median uses the lower median: for an even sample count, the smaller of the two middle values.
  • Replaced seed_probability_min and seed_probability_max with seed_probability, calculated as the bucket’s unconditional probability divided by its seed count.
  • Updated CSV column order:
    class, bucket, class_global_probability, conditional_probability,
    unconditional_probability, median, mean, seed_count, seed_probability,
    collision_probability, draws_at_collision_probability
    Configurable Collision Reporting Threshold
    Added a required setting in opt_cfg.yaml:
    engine_options:
    distribution_collision_probability: 0.25
    The value must be strictly between 0 and 1. It controls the estimated number of draws at which each bucket reaches the specified collision probability.
    This setting does not affect the optimized distribution or design.risk.collision.max constraints. Collision estimates continue to use actual alias-table outcome probabilities.
    Upgrade Notes
  • Add the new configuration field. Use 0.25 to preserve the previous reporting threshold.
  • Update CSV and JSON consumers for the revised fields. Go callers must replace SeedProbabilityMin and SeedProbabilityMax with SeedProbability.
  • Projects maintaining a copied cmd/opt/progress.go should update it alongside the Problab dependency.
  • The new configuration field changes the configuration hash.
  • Existing CSV files remain unchanged. Run the optimizer again to generate the updated format.
    Validation
    Tests passed for optimizer/v2 and cmd/opt.

v0.10.1

Choose a tag to compare

@nextso nextso released this 08 Sep 05:24

Problab v0.10.1

This patch fixes Optimizer v2 collection tags that depend on game-defined
ExtendResult data.

Fixed

Optimizer v2 previously created raw collection machines with isSim=true.
Games are allowed to omit or simplify ExtendResult snapshots in simulation
mode, which meant custom collection tags could not reliably inspect extension
data.

Optimizer machines now consistently run in full-result mode across:

  • Fresh concurrent collection
  • Collection Bank replay
  • Artifact runtime replay and verification

Custom tags can now inspect the complete raw SpinResult, including
game-defined ExtendResult snapshots.

Compatibility

  • No YAML configuration changes
  • No public Go API changes
  • Existing integer and polymorphic seed behavior is unchanged
  • Deterministic PRNG streams are unchanged
  • Existing Collection Banks remain replayable
  • Core snapshot identities and collection ordering remain unchanged
  • Optimizer engine provenance is updated to intent-lp-v2.6.0

Explicitly seeded optimizer machines remain deterministic. The isSim=false
setting only selects complete result materialization.

Performance note

Games that snapshot or deep-copy extension data may use additional CPU and
memory during collection. This is an intentional correctness tradeoff required
for tags that inspect complete game results.

Validation

The release includes a regression test proving that an Extend-dependent custom
tag can collect and replay matching samples.

The full test suite, Optimizer v2 race tests, compatibility goldens, Collection
Bank replay tests, and static analysis all pass.

v0.10.0

Choose a tag to compare

@nextso nextso released this 08 Sep 03:27

Problab v0.10.0

This release improves Optimizer v2 incremental collection and introduces
polymorphic root seed material while preserving existing integer-seed behavior
and Collection Bank replay compatibility.

Highlights

Collection stream cursors

Optimizer v2 now tracks a logical collection stream cursor through Collection
Bank filenames:

  • New banks use seed_bank_<unix>_s<next>.bin.
  • Top-up runs allocate fresh worker streams after the highest recognized cursor
    from the configured replay sources.
  • Legacy banks without a cursor remain replayable and are treated as cursor 0
    with a warning.
  • Stream cursors provide best-effort separation for built-in and third-party
    PRNG factories.

Duplicate recovery banks

A Class-local duplicate audit now runs after collection and before Prepare.

When duplicate snapshot identities are detected:

  • The optimizer reports duplicate counts and affected Classes.
  • A deduplicated recovery bank is written as
    seed_bank_<unix>_s<next>.distinct.bin.
  • The run stops before Prepare with DuplicateReplayIdentity.
  • The generated distinct bank can be used as collected_seed for a subsequent
    top-up run.

Duplicates across different Classes are intentionally not rejected because
Prepare evaluates effective sample counts per Class bucket.

Polymorphic root seeds

The existing YAML seed key now accepts:

seed: 4127483647
seed: "utf8:autumn-build-7"
seed: "hex:9f3ac1..."
  • Integer values retain the exact legacy int64 encoding and generated streams.
  • utf8: passes the suffix as exact UTF-8 seed material.
  • hex: passes the decoded bytes as exact binary seed material.
  • Unsupported seed material is rejected before replay or fresh collection
    begins.
  • Seed compatibility depends on the configured PRNG factory. PCG64 requires
    exactly 8 bytes, while ChaCha20 supports variable-length material.
    Root seed material is not a PRNG snapshot and does not replace Collection Bank
    replay.
    Compatibility
    The following behaviors remain compatible:
  • Existing YAML configurations using integer seed values require no changes.
  • Existing integer seeds produce the same random streams.
  • Existing integer-seed ConfigHash values remain unchanged.
  • Existing Collection Banks remain replayable.
  • Stable collection ordering is unchanged.
  • Optimizer ConfigVersion remains 2.
    Breaking Go API changes
    The programmatic Optimizer v2 API has changed:
  • RunPlan.Seed: int64 → SeedSpec
  • RunOverrides.Seed: *int64 → *SeedSpec
    Go callers should use:
    v2.Int64Seed(77)
    YAML-only users are not affected by this API change.
    Upgrade notes
  • Update scripts that assume the old seed_bank_.bin filename format.
  • Accept the _s cursor suffix and optional .distinct.bin suffix.
  • If a duplicate recovery bank is produced, use that bank as the replay source
    for the next top-up run.
  • Go programs constructing Optimizer v2 plans directly must migrate integer
    seeds to v2.Int64Seed(...).
    Validation
    This release includes:
  • Pre-change integer-seed compatibility goldens.
  • Fixed Collection Bank replay compatibility tests.
  • Cursor allocation and top-up integration tests.
  • Duplicate recovery and convergence tests.
  • UTF-8 and hexadecimal seed end-to-end tests.
  • Full test-suite, race-detector, and static-analysis validation.

v0.9.0

Choose a tag to compare

@nextso nextso released this 07 Sep 09:36

v0.9.0

Replayable Optimizer Collection Banks

Problab Optimizer v2 can now persist expensive collection results as reusable
raw snapshot banks and replay them in later runs.

Highlights

  • Added the optional collection.collected_seed configuration field for
    declaring replay banks in priority order.

  • Replays every snapshot through the current game logic, bet mode, Tag rules,
    and first-matching Class classification instead of trusting historical payout
    metadata.

  • Deduplicates exact snapshot identities across replay sources, with the first
    occurrence taking precedence.

  • Separates replay work from the fresh max_spins budget. Replay fills Class
    quotas first, and deterministic fresh workers collect only the remaining
    deficits.

  • Reports per-source accepted, duplicate, unmatched, and rejected counts, plus
    per-Class replay-versus-fresh evidence through RunReport.Collection.

  • Atomically saves complete and partial collection banks before dynamic
    validation, LP construction, solving, materialization, and publication.

  • Persists banks under:

    <output.directory>/collected/game_<gid>/mode_<mode>/seed_bank_<unix_timestamp>.bin

  • Reports the saved bank's absolute path, SHA-256 digest, snapshot length, seed
    count, byte size, and partial state.

  • Handles missing, malformed, and runtime-incompatible replay sources as
    operational warnings, allowing later sources or fresh collection to continue.

  • Refreshes replay progress on a single terminal line when stderr is
    interactive, while preserving append-only output for redirects and CI logs.

Reproducibility and Compatibility

  • Existing configurations remain valid when collected_seed is omitted.
  • Configured replay paths and their declaration order participate in the
    resolved configuration hash.
  • Replay source contents are evidenced separately by the saved bank's SHA-256.
  • Fresh collection retains its existing PRNG-defined behavior and is not
    deduplicated against replay or other workers.
  • Problab does not create a latest pointer, collection manifest, directory
    index, or descriptor sidecar. A timestamped bank must be selected explicitly
    for replay.
  • Collection banks are reusable optimizer inputs and are not publishable runtime
    artifacts.