Documentation
¶
Overview ¶
Package problab 提供 Problab 引擎的「組裝入口(assembler)」與「運行入口(runtime entry)」。
你可以把 Problab 視為一個「可被後端/模擬器使用的 runtime」,它負責把下列三個必需的地基組裝在一起,並提供建立 Machine 的入口:
- Catalog:遊戲目錄(Single Source of Truth / SSOT),定義有哪些遊戲、各自對應的設定檔名稱(ConfigName)。
- LogicRegistry:邏輯註冊表,提供「如何依據設定(LogicKey)建出遊戲邏輯」的 builders。
- CoreFactory:亂數核心工廠(PRNG factory),保證可重現(reproducible)與可審計(auditable)。
設計重點:
- 遊戲設定來源不綁定檔案路徑,一律以 fs.FS 注入;大型 Optimal Artifact 可另外用 WithOptimalDir 指定普通檔案目錄以啟用 mmap。
- Problab 會持有一份 Catalog(你要跑哪一批遊戲/設定檔)與一份 LogicRegistry(你支援哪些遊戲邏輯)。
- Machine 是對外提供 Spin 的最小單位;遊戲邏輯開發者(數學家)主要操作的是 sdk 內的型別與資料結構。
典型使用情境:
- 後端服務(HTTP / gRPC):由 Problab 建立 Machine,Machine 對外提供 Spin。
- 模擬器(sim):由 Problab 建立多台 Machine 進行大量模擬。
注意:此套引擎目前以 Slot 領域為中心(Spin -> Result),不是泛用遊戲框架。
Index ¶
- Constants
- Variables
- func Configs(cfgs ...fs.FS) []fs.FS
- func Logics(regs ...*slot.LogicRegistry) []*slot.LogicRegistry
- type BetConfig
- type BetRunner
- type BetRunnerOption
- type ChooseMode
- type DevSimReport
- type DevSimulator
- func (d *DevSimulator) RestoreSim(be64 string, betmode int, round int) (DevSimReport, error)
- func (d *DevSimulator) RestoreSpins(be64 string, betmode int, round int) (DevSpinReport, error)
- func (d *DevSimulator) SetCfg(c BetConfig) error
- func (d *DevSimulator) Sim(betmode int, round int) (DevSimReport, error)
- func (d *DevSimulator) Spins(betmode int, round int) (DevSpinReport, error)
- type DevSpinReport
- type Gacha
- type Machine
- type MachineConfig
- type MachinePool
- func (mp *MachinePool) Available() int
- func (p *MachinePool) Close()
- func (p *MachinePool) Closed() bool
- func (mp *MachinePool) ClosedReason() string
- func (mp *MachinePool) Fatals() int
- func (mp *MachinePool) Inflight() int
- func (mp *MachinePool) Metrics() MachinePoolMetrics
- func (mp *MachinePool) Panics() int
- func (mp *MachinePool) PoolSize() int
- func (mp *MachinePool) ReBuild() int
- func (p *MachinePool) Spin(ctx context.Context, req *dto.SpinRequest) (dto dto.SpinResult, err error)
- type MachinePoolMetrics
- type OptimalRuntime
- type Problab
- func (p *Problab) All() []catalog.Entry
- func (p *Problab) BuildRuntime(poolSize int) (*SlotRuntime, error)
- func (p *Problab) Close() error
- func (p *Problab) Closed() bool
- func (p *Problab) DeriveSeed(parent []byte, stream core.StreamID) ([]byte, error)
- func (p *Problab) EntryById(id spec.GID) (catalog.Entry, bool)
- func (p *Problab) EntryByName(name string) (catalog.Entry, bool)
- func (p *Problab) Freeze()
- func (p *Problab) IDs() []spec.GID
- func (p *Problab) NewCore(seed int64) (*core.Core, error)
- func (p *Problab) NewCoreWithSeedBytes(seed []byte) (*core.Core, error)
- func (p *Problab) NewCoreWithSeedString(seed string) (*core.Core, error)
- func (p *Problab) NewDevSimulator(gid spec.GID, seed int64) (*DevSimulator, error)
- func (p *Problab) NewMachine(id spec.GID, cfg MachineConfig) (*Machine, error)
- func (p *Problab) NewSimulator(id spec.GID, cfg MachineConfig) (*Simulator, error)
- func (p *Problab) Register(ents ...catalog.Entry) error
- func (p *Problab) RegisterAll() error
- func (p *Problab) SnapshotFormat() string
- func (p *Problab) Summary() ([]catalog.Summary, error)
- type ProblabOption
- type RuntimeHealth
- type Seed
- type SeedMaker
- func NewSeedMaker(seed int64) *SeedMakerdeprecated
- type SettingSource
- type Simulator
- func (s *Simulator) SetCfg(c BetConfig) error
- func (s *Simulator) Sim(betMode int, round int, showpb bool) (*stats.StatReport, time.Duration, error)
- func (s *Simulator) SimMP(betMode int, rounds int, mp int, showpb bool) (*stats.StatReport, time.Duration, error)
- func (s *Simulator) SimPlayers(mp int, players int, initBets int, betMode int, rounds int, showpb bool) (*stats.StatReport, *stats.EstimatorPlayers, time.Duration, error)
- type SlotRuntime
- func (rt *SlotRuntime) Close()
- func (rt *SlotRuntime) Closed() bool
- func (rt *SlotRuntime) ClosedReason() string
- func (rt *SlotRuntime) Health() RuntimeHealth
- func (rt *SlotRuntime) PoolMetrics(gid spec.GID) (MachinePoolMetrics, bool)
- func (rt *SlotRuntime) Spin(ctx context.Context, req *dto.SpinRequest) (dto.SpinResult, error)
- type SpinInput
Constants ¶
const DefaultMaxSegments = 10
DefaultMaxSegments 是 BetRunner 與消費端預設的段數上限。它是防止遊戲永不結束的保守 執行限制,不保證所有正常的遊戲都能在這個段數內完成。
Variables ¶
var ( // ErrBetInProgress 表示一局尚未結束(或已不可繼續、尚未 Reset)時又呼叫 Bet。 ErrBetInProgress = errors.New("bet runner: a bet is in progress") // ErrNoPendingChoice 表示目前沒有停在選擇點、等待 Next 的局。 ErrNoPendingChoice = errors.New("bet runner: no pending choice") // ErrChoiceOutOfRange 表示 choice 不在 [0, len(ChooseNext)) 內。 ErrChoiceOutOfRange = errors.New("bet runner: choice out of range") // ErrSegmentLimit 表示下一段會超過段數上限。上限是執行限制,不代表遊戲故障。 ErrSegmentLimit = errors.New("bet runner: segment limit reached") // ErrBetBroken 表示本局執行後沒有有效結果,或自動選擇的續段失敗,本局不可繼續,必須 Reset。 ErrBetBroken = errors.New("bet runner: the bet cannot continue; call Reset") // ErrInvalidChooseMode 表示選擇方式不是已定義的值。 ErrInvalidChooseMode = errors.New("bet runner: invalid choose mode") )
BetRunner 的錯誤。執行前的錯誤不改變進行中的局,修正後可以重送。
var ( // ErrSegmentInput 表示輸入違反結構不變量:Cycle < 0;Cycle 為 0 卻帶選項或 Choice; // Cycle > 0 卻沒有選項,或 Choice 不在 [0, len(ChooseNext)) 內。 ErrSegmentInput = errors.New("invalid segment input") // ErrStartSnapshotRejected 表示指定的起始快照被 PRNG 拒絕,且已還原到執行前。 ErrStartSnapshotRejected = errors.New("start snapshot rejected") // ErrContinuationUnsupported 表示機台開啟 optimal、未指定快照而請求續段,但入口 // ChooseNext[Choice] 沒有配置在這個 bet mode 的 after 中(含沒有 after 的設定)。 ErrContinuationUnsupported = errors.New("continuation is not supported by the optimal setting") // ErrGameRejected 表示遊戲回傳 nil,拒絕了這次請求(例如不合法的入口或 checkpoint)。 ErrGameRejected = errors.New("game rejected the request") )
單段執行的拒絕原因。執行前的拒絕(ErrSegmentInput、ErrStartSnapshotRejected、 ErrContinuationUnsupported)不改變 RNG 與 Machine 狀態;ErrGameRejected 發生在遊戲執行 之後,RNG 可能已消耗(指定快照或抽庫時由恢復守衛還原)。
var ErrChooseModeRequired = errors.New("a choice game requires choose mode first or last")
ErrChooseModeRequired 表示以 ChoosePlayer(未指定選擇方式)執行時遇到選擇點:不會做 選擇的消費端(Simulator、DevSimulator)必須指定 ChooseFirst 或 ChooseLast。
Functions ¶
func Configs ¶
Configs 用來把一或多個設定檔來源(fs.FS)打包成 New() 需要的參數。
為什麼是 fs.FS:
- 你可以用 go:embed 把 configs 直接編進 binary(部署最穩定,不依賴工作目錄)。
- 也可以用 os.DirFS 在本機開發時讀取目錄。
- 甚至可以用自製的 MultiFS 來合併多個來源。
Problab 不解析「路徑」:它只依賴 fs.FS + ConfigName(檔名)來取得設定內容。
func Logics ¶
func Logics(regs ...*slot.LogicRegistry) []*slot.LogicRegistry
Logics 用來把一或多個邏輯註冊表(LogicRegistry)打包成 New() 需要的參數。
一個 LogicRegistry 代表「一個邏輯模組」提供的 builders 集合。 例如:
- basegame 模組提供 basegame 的 builder
- freegame 模組提供 freegame 的 builder
New() 會把多個 registries 合併成單一 registry;若出現重複 LogicKey,會以 error 直接失敗(避免行為不確定)。
Types ¶
type BetConfig ¶ added in v0.17.0
type BetConfig struct {
Choose ChooseMode // ChooseFirst、ChooseLast 自動選擇;ChoosePlayer 不自動選擇
MaxSegments int // 一局的段數上限;0 使用 DefaultMaxSegments,不得為負
}
BetConfig 是 Simulator 與 DevSimulator 的執行設定。零值即預設:ChoosePlayer(不自動 選擇,遇到選擇點時回報 ErrChooseModeRequired)與 DefaultMaxSegments。
type BetRunner ¶ added in v0.17.0
type BetRunner struct {
// contains filtered or unexported fields
}
BetRunner 在一台本機 Machine 上執行一筆下注(從第 0 段到局結束),並以 Assembler 組出 完整局。它是正式逐段操作在本機的有狀態封裝:同一個實例保存局內狀態,呼叫端只送 choice;正式服務仍使用 Machine.Spin,由後端每次送入恢復所需的資料。
回傳的結果以 IsGameEnd 判斷單位:未結束是本段,結束是完整局(單段局的本段即完整局)。 ChooseFirst、ChooseLast 的 Bet 一律回傳完整局;ChoosePlayer 的 Bet 與 Next 在局結束前 回傳本段,最後一次回傳完整局。計分時,局結束以完整局的 TotalWin 覆蓋,不累加先前 回傳的段:
Bet(in, ChoosePlayer) → 未結束,TotalWin = 10(第 0 段) Next(0) → 未結束,TotalWin = 20(第 1 段) Next(1) → 已結束,TotalWin = 60(完整局:10 + 20 + 第 2 段的 30)
完整局不保留段邊界:最後一段的起始快照與輸入 checkpoint 無法從完整局還原。
使用契約:
- 一個 BetRunner 同一時間只由一個執行者使用;局進行中不得直接操作它持有的 Machine。
- 回傳的段結果與完整局結果都是借用的唯讀結果,有效到 Machine 的下一次執行,或下一次 Bet、Next、Reset;需要保留時自行複製或轉成 DTO。
- 不接受取消:呼叫端在每次呼叫前後自行檢查,需要時 Reset。ChooseFirst、ChooseLast 執行到局結束才返回;段數上限只限制執行次數,不保證完成時間。
- 只用於 deterministic 的本機 Machine(例如 NewMachine);不執行 production reseed。
- checkpoint 依遊戲的穩定契約持有參照(交出後不得再修改),不做深拷貝。
- 遊戲或 RNG 的內部 panic 照常向外傳遞,沿用棄用 Machine 的契約;Reset 不代表可以恢復。
func NewBetRunner ¶ added in v0.17.0
func NewBetRunner(m *Machine, opts ...BetRunnerOption) (*BetRunner, error)
NewBetRunner 建立綁定 m 的 BetRunner。段數上限預設為 DefaultMaxSegments,可以 WithMaxSegments 覆寫;m 為 nil 或上限不大於 0 時回傳 error。
func (*BetRunner) Bet ¶ added in v0.17.0
func (r *BetRunner) Bet(in SpinInput, mode ChooseMode) (*buf.SpinResult, error)
Bet 開始一筆下注並執行到停止點:ChooseFirst、ChooseLast 跑到局結束;ChoosePlayer 跑到 第一個選擇點,或第 0 段就結束。in 只使用 BetMode、BetMult、CaptureSnapshots 與 StartSnap(只作用於第 0 段);Cycle、Choice、ChooseNext、Checkpoint 必須為零值。
選擇方式、操作狀態、輸入與投注的檢查失敗時保留原狀態(例如上一局的完整局仍然有效)。 通過檢查後上一局即失效;之後若指定的快照被拒絕,狀態回到沒有進行中的局。
type BetRunnerOption ¶ added in v0.17.0
type BetRunnerOption func(*betRunnerOptions)
BetRunnerOption 設定 NewBetRunner。
func WithMaxSegments ¶ added in v0.17.0
func WithMaxSegments(n int) BetRunnerOption
WithMaxSegments 設定一局的段數上限(含第 0 段),必須大於 0。
type ChooseMode ¶ added in v0.17.0
type ChooseMode uint8
ChooseMode 是一筆下注的選擇方式。零值 ChoosePlayer 停在選擇點,交還呼叫端。
ChooseFirst、ChooseLast 是兩種固定政策:每個選擇點都選 ChooseNext 的第一個或最後一個 位置(不是固定的入口值)。結果只代表該政策,不代表所有選法;其他政策以 ChoosePlayer 在外層逐次決定。
const ( ChoosePlayer ChooseMode = iota // 停在選擇點,由呼叫端以 Next 選擇 ChooseFirst // 每個選擇點選 ChooseNext 的第一個位置,跑到局結束 ChooseLast // 每個選擇點選 ChooseNext 的最後一個位置,跑到局結束 )
func ParseChooseMode ¶ added in v0.17.0
func ParseChooseMode(s string) (ChooseMode, error)
ParseChooseMode 解析外部參數(server、CLI)的選擇方式:空字串為 ChoosePlayer(不指定), "first"、"last" 為對應的模式。其他值(包括 "player" 與大小寫不同的寫法)回傳包住 ErrInvalidChooseMode 的 error。
func (ChooseMode) String ¶ added in v0.17.0
func (c ChooseMode) String() string
type DevSimReport ¶
type DevSimReport struct {
Before string `json:"before"`
After string `json:"after"`
Stat *stats.StatReport `json:"statistic"`
}
type DevSimulator ¶
type DevSimulator struct {
// contains filtered or unexported fields
}
DevSimulator
只提供給Dev模式使用的模擬器,單線(不併發),重點在可審計、可重現
func (*DevSimulator) RestoreSim ¶
func (d *DevSimulator) RestoreSim(be64 string, betmode int, round int) (DevSimReport, error)
func (*DevSimulator) RestoreSpins ¶
func (d *DevSimulator) RestoreSpins(be64 string, betmode int, round int) (DevSpinReport, error)
func (*DevSimulator) SetCfg ¶ added in v0.17.0
func (d *DevSimulator) SetCfg(c BetConfig) error
SetCfg 以 c 整份取代 Spins、RestoreSpins 與 Sim、RestoreSim 的執行設定;規則同 Simulator.SetCfg。
func (*DevSimulator) Sim ¶
func (d *DevSimulator) Sim(betmode int, round int) (DevSimReport, error)
func (*DevSimulator) Spins ¶
func (d *DevSimulator) Spins(betmode int, round int) (DevSpinReport, error)
Spins 從機台目前的位置連續執行 round 局,回傳每局結果與統計。每局經 BetRunner 依執行 設定跑完整局並擷取快照,統計由 Recorder 計算;Before/After 是機台在整批前後的位置, 可交給 RestoreSpins 重跑同一批。
type DevSpinReport ¶
type DevSpinReport struct {
Before string `json:"start_b64u"`
After string `json:"after_b64u"`
Round int `json:"round"`
Rtp float64 `json:"rtp"`
TotalBet int `json:"total_bet"`
TotalWin int `json:"total_win"`
BaseWin int `json:"base_win"`
FreeWin int `json:"free_win"`
Results []dto.SpinResult `json:"results"`
Choose string `json:"choose,omitempty"` // 選擇類遊戲自動選擇的方式;未指定時不輸出
}
type Gacha ¶ added in v0.5.0
type Gacha struct {
Picker *sampler.AliasTableF64 `json:"picker"` // 抽樣表
SeedLen int `json:"seed_len"` // 抽到對應第幾個種子,就要 * SeedLen 取[n*SeedLen:(n+1)*SeedLen]
}
Machine 封裝一台「可對外提供 Spin」的遊戲機台。
你可以把 Machine 視為 Game 的「外殼(shell)」:
- 對外:提供 Spin 入口(HTTP/模擬器通常只操作 Machine)。
- 對內:持有 RNG(Core)與真正執行遊戲邏輯的核心(sdk/slot.Game)。
並發語意:
- Machine 預設不是 lock-free 結構;它持有可重用的請求、結果與快照緩衝區(熱路徑),因此同一台 Machine 不應被多 goroutine 同時執行。
- Spin 會上鎖;SpinUnsafe 不上鎖,呼叫端必須保證獨占同一台 Machine。
- 若要併發模擬,由更高層建立多台 Machine 分散到不同 worker 並管理其生命週期。
Buffer 語意(非常重要,影響 DX 與正確性):
- Machine 持有交給遊戲的請求,每次執行前重設;遊戲不得保留請求的參照。
- 結果與快照緩衝區會被重用(避免 GC),每次執行會覆寫內容。
- 你若需要在執行後保留結果,請在下一次呼叫前轉成 DTO(或自行 copy 你需要的欄位)。
錯誤語意:可預期的輸入錯誤以 error 回傳;內部執行故障以 panic 呈現,由最外層執行邊界 (MachinePool,或離線工具自己的工作迴圈)處理。RNG 恢復失敗後 Machine 不可再用;遊戲 panic 後,即使 RNG 已還原,Machine 也不保證可以繼續使用。
Gacha is the legacy JSON compatibility shape for optimizer.Gacha. Deprecated: runtime Machines use the immutable internal Artifact instead.
type Machine ¶
type Machine struct {
BetUnits []int // 押注單位(由遊戲設定衍生;通常給外部列舉 UI/測試)
// contains filtered or unexported fields
}
func (*Machine) OmitExtend ¶ added in v0.17.0
OmitExtend 回傳此 Machine 的 Extend 省略政策(true 表示結果不含 Extend)。
func (*Machine) RestoreCore ¶
RestoreCore 恢復Core狀態暫存 當前僅提供恢復Core狀態
之後要實作斷手重連時候提供checkpoint加入必要恢復資訊時實作 Restore() <- 保留語意
func (*Machine) SnapshotCore ¶
SnapshotCore 取得Core狀態暫存 當前僅提供取得Core狀態
之後要實作斷手重連時候提供checkpoint加入必要恢復資訊時實作 SnapShot() <- 保留語意
func (*Machine) Spin ¶
func (m *Machine) Spin(r *dto.SpinRequest) (dto.SpinResult, error)
Spin 為主要公開入口,會驗證投注請求,執行一段遊戲並回傳該段的結果。
可預期的輸入錯誤回傳 errs.Warn:請求驗證、DTO 解析、被 PRNG 拒絕的外部起始快照、 違反結構不變量的續段請求(ErrSegmentInput)、續段的入口沒有配置在 optimal 設定中 (ErrContinuationUnsupported),以及遊戲拒絕請求(ErrGameRejected)。後三者可以 errors.Is 判斷。production reseed 失敗回傳 errs.Fatal。其他內部執行故障以 panic 呈現, 由 MachinePool 汰換機台;RNG 恢復失敗後 Machine 不可再用。
func (*Machine) SpinUnsafe ¶ added in v0.17.0
func (m *Machine) SpinUnsafe(in SpinInput) *buf.SpinResult
SpinUnsafe 是本機執行入口:一次執行一段,回傳借用的結果,到同一台 Machine 的下一次 呼叫為止;需要保留的呼叫端自行複製(轉 DTO 即是複製)。
Unsafe 指的是呼叫端責任,不是記憶體安全:
- 跳過對外入口的完整請求驗證。BetMode 在範圍內、BetMult >= 1、投注額乘法不溢位由 呼叫端保證;不合法時行為未定義(例如 bet mode 越界時 panic)。
- 仍維護 Machine 執行所需的前置條件:違反結構不變量、指定的起始快照被拒絕、或目前的 optimal 設定不支援續段時回傳 nil,不執行遊戲、不消耗 RNG。
- 不上鎖。呼叫期間由呼叫端獨占 Machine,不得與 Spin、RestoreCore、SnapshotCore 等任何操作並行;同步完成後交給另一個 goroutine 是允許的。
- 不執行 production reseed,只供呼叫端已掌握 RNG 生命週期的本機執行使用; 正式服務使用 Spin。
起點:StartSnap 非空時從它執行本段(跳過抽庫),回傳後機台恢復到執行前,只作用於 這一次呼叫;否則 optimal 開啟時抽庫並在執行後還原選取狀態,未啟用時從機台目前的 RNG 執行。
CaptureSnapshots 為 true 時,把起始快照(指定快照、抽出的 seed,或執行前的機台狀態) 與結束快照寫進結果的 State,快照借用 Machine 的緩衝區;為 false 時不擷取,並清除結果 State 中前一次留下的 RNG 快照(不碰遊戲輸出的 Checkpoint)。
遊戲回傳 nil 時原樣回傳,不觸碰 State;RNG 照常還原。nil 一律表示這次呼叫沒有有效結果。
內部執行故障以 panic 呈現。發生任何內部執行 panic 後,該 Machine 必須棄用: SpinUnsafe 不檢查 Machine 是否曾經 RNG 恢復失敗,接住 panic 後繼續使用屬於違反契約。
func (*Machine) UsesOptimal ¶ added in v0.15.0
UsesOptimal reports whether optimal routing is on for this Machine, from the loaded runtime source rather than the config flag. Some segments may still run on the machine RNG when their slot has no gacha.
type MachineConfig ¶ added in v0.17.0
type MachineConfig struct {
Seed Seed
Setting SettingSource
// Unoptimized 為 true 時忽略設定中的 optimal_setting,直接執行遊戲邏輯,
// 不從已發布的 Optimal Artifact 抽 seed。optimizer 收集必須使用此選項。
Unoptimized bool
}
MachineConfig 是 NewMachine 與 NewSimulator 的建構設定。每個概念只有一個欄位, 零值即預設:未提供 seed、使用 catalog 設定、依設定使用 optimal。
type MachinePool ¶
type MachinePool struct {
// contains filtered or unexported fields
}
MachinePool 專門管理「某一款遊戲」的所有機台實例。 它透過兩個通道管理機台生命週期:
- pool:健康且可用的機台,供 Spin() 借出 / 歸還。
- broken:在運作過程中發生錯誤或 panic 的壞機台,送往此通道以便後續檢查、維修或丟棄。
若某台機台於遊戲執行期間發生 panic 或 fatal error,該機台會被送至 broken,並立即補上一台新機以維持容量。 整體機制確保整個系統在高併發下仍保持穩定與可用性。
func (*MachinePool) Available ¶
func (mp *MachinePool) Available() int
Available 回傳當下 pool 可用機台數(len(pool))。在高併發下為近似值。
func (*MachinePool) Close ¶
func (p *MachinePool) Close()
Close 進入關閉狀態:
- 通知之後所有 Spin() 應該直接回error
- defer 歸還/補機時會觀察 done,避免對已關閉狀態進行 send
func (*MachinePool) ClosedReason ¶
func (mp *MachinePool) ClosedReason() string
func (*MachinePool) Fatals ¶
func (mp *MachinePool) Fatals() int
func (*MachinePool) Inflight ¶
func (mp *MachinePool) Inflight() int
func (*MachinePool) Metrics ¶
func (mp *MachinePool) Metrics() MachinePoolMetrics
Metrics 回傳一期的觀測快照;上層可用於 log、/metrics、或餵給 Prometheus/OTEL exporter。
func (*MachinePool) Panics ¶
func (mp *MachinePool) Panics() int
func (*MachinePool) PoolSize ¶
func (mp *MachinePool) PoolSize() int
func (*MachinePool) ReBuild ¶
func (mp *MachinePool) ReBuild() int
func (*MachinePool) Spin ¶
func (p *MachinePool) Spin(ctx context.Context, req *dto.SpinRequest) (dto dto.SpinResult, err error)
type MachinePoolMetrics ¶
type MachinePoolMetrics struct {
GameName string `json:"game_name"`
GameID spec.GID `json:"game_id"`
PoolSize int `json:"pool_size"` // 目標容量(初始化指定)
Available int `json:"available"` // 當下可借出的機台數(len(pool))
Inflight int `json:"inflight"` // 使用中(借出未歸還)
BrokenBacklog int `json:"broken_backlog"` // broken channel 當下 backlog(len(broken))
Rebuild int `json:"rebuild"` // 補機次數
Panics int `json:"panics"` // panic 次數
Fatals int `json:"fatals"` // fatal 次數
Closed bool `json:"closed"` // 是否已關閉
CloseReason string `json:"close_reason"` // 關閉原因
CloseInflight int `json:"close_inflight"` // Close() 當下 inflight(-1 表示尚未關閉)
CloseAvail int `json:"close_avail"` // Close() 當下 available(-1 表示尚未關閉)
Closebroken int `json:"close_broken"` // Close() 當下 broken backlog(-1 表示尚未關閉)
}
MachinePoolMetrics 是一期提供的「拉取式(pull)」觀測快照。
設計原則:
- 不綁任何 metrics/telemetry SDK(Prometheus / OpenTelemetry 等),由上層自己決定如何輸出。
- 欄位值以讀取當下為主;其中 Available/brokenBacklog 來自 len(chan),在高併發下是「近似值」但足夠用於營運觀測。
- 關閉瞬間的快照(CloseInflight/CloseAvail/Closebroken)只會在 Close 時寫入一次,用於事後排查。
type OptimalRuntime ¶ added in v0.5.0
type OptimalRuntime struct {
Gachas []*Gacha // 對應每個 betmode,len(Gachas) == len(BetUnits)
Bank [][]byte // 對應每個 betmode,每個 []byte 是完整的 SeedBank
}
OptimalRuntime is retained for source compatibility with the legacy public shape. Deprecated: Problab now owns one immutable shared Artifact per bundle.
type Problab ¶
type Problab struct {
// contains filtered or unexported fields
}
Problab 是「組裝器(assembler)」與「運行入口(runtime entry)」:
它把三個必需的地基組合起來:
- Catalog:遊戲目錄(Single Source of Truth / SSOT),定義有哪些遊戲、各自對應的設定檔名稱。
- LogicRegistry:邏輯註冊表,提供「如何依據設定(LogicKey)建出遊戲邏輯」的 builders。
- CoreFactory:亂數核心工廠(PRNG factory),保證可重現(reproducible)與可審計(auditable)。
遊戲設定來源不綁定任何檔案路徑,一律由 fs.FS 提供;Optimal Artifact 則可選擇 fs.FS memory backend 或普通目錄 mmap backend。
使用流程通常分成兩階段:
- 註冊/組裝階段(registration/build):建立 catalog、合併 registries、檢查重複與缺漏。
- 執行階段(runtime):依據遊戲 ID 產生 Machine,並在 Machine 上執行 Spin。
重要設計原則:
- Catalog 的 ID 唯一性只保證在「同一個 Problab instance」內(不同 Problab 之間不做全域保證)。
- 你要跑哪一批遊戲、哪一套設定檔、哪一批邏輯,必須由 New() 的參數明確決定。
- runtime 一旦開始(例如已建立 Machine 並對外服務),不建議再變更 Catalog/Registry(避免非預期行為)。
實務例子(概念示意,細節依你的實作為準):
// 1) 準備 configs(通常是 go:embed 或 DirFS)
// 2) 準備一或多個邏輯模組的 LogicRegistry
// 3) 組裝 Problab,取得可建立 Machine 的入口
// lab, _ := problab.New(cf, problab.Configs(cfgFS), problab.Logics(reg1, reg2))
// m, _ := lab.NewMachine(1001, problab.MachineConfig{Seed: problab.SeedInt64(42)})
// // m.Spin(...) -> 取得結果(通常再轉成 DTO 回傳)
func New ¶
func New(cf core.PRNGFactory, cfgs []fs.FS, logics []*slot.LogicRegistry, opts ...ProblabOption) (*Problab, error)
New 建立一個 Problab instance。
這是「組裝階段(registration/build)」的入口:
- 會建立 Catalog(通常同時做檔名存在性/重複性檢查,避免 runtime 才爆)。
- 會合併多個 LogicRegistry 成為單一 registry(重複 LogicKey 直接視為錯誤)。
- 會保存 CoreFactory,確保由這個 Problab 建出來的 Machine 在 RNG 行為上具有一致性。
參數要求(是合約的一部分):
- cf 不能為 nil:沒有 RNG 工廠就無法建立可重現/可審計的核心。
- cfgs 至少一個:沒有設定檔來源,Catalog 無法解析 GameSetting。
- logics 至少一個:沒有邏輯 builders,就算解析出設定也無法建出可執行的遊戲邏輯。
回傳的 Problab 會持有:cat(目錄)、reg(合併後 registry)、cf(RNG 工廠)、optimalFS(可選的優化文件系統)。
func NewAuto ¶
func NewAuto(cf core.PRNGFactory, cfgs []fs.FS, logics []*slot.LogicRegistry, opts ...ProblabOption) (*Problab, error)
NewAuto 建立一個直接進入執行階段的 Problab instance。
回傳的 Problab 會持有:cat(目錄)、reg(合併後 registry)、cf(RNG 工廠)、optimalFS(可選的優化文件系統)。
func (*Problab) BuildRuntime ¶
func (p *Problab) BuildRuntime(poolSize int) (*SlotRuntime, error)
func (*Problab) Close ¶ added in v0.6.0
Close releases resources owned by this Problab instance. Runtime and pool users must be stopped first; Close is safe to call more than once.
func (*Problab) DeriveSeed ¶ added in v0.8.0
DeriveSeed derives a deterministic child seed using this Problab's Factory.
func (*Problab) NewCoreWithSeedBytes ¶ added in v0.8.0
func (*Problab) NewCoreWithSeedString ¶ added in v0.8.0
func (*Problab) NewMachine ¶
NewMachine 建立一台 deterministic、保留 Extend 的 Machine。
需要 Extend 的分析(tag、完整歷程、playerexp、optimizer)都使用 Machine;只需要 統計數字時請改用 NewSimulator。正式服務的 production Machine 只由 BuildRuntime 建立,公開 API 不提供單台 production Machine。
func (*Problab) NewSimulator ¶
NewSimulator 建立一個 deterministic、省略 Extend 的 Simulator。Simulator 只產生 統計數字;需要 Extend 時請改用 NewMachine。
func (*Problab) RegisterAll ¶
RegisterAll
會掃描 catalog 持有的設定檔來源(fs.FS),把所有可辨識的設定檔(.yaml/.yml/.json)嘗試解析成 *spec.GameSetting,並用設定檔內宣告的 GameID/GameName 產生對應的 catalog.Entry 來批次註冊。
行為特性(重要):
- Fail-fast:任何一個檔案讀取/解析/基本檢查失敗,都會立刻回傳 error(不會忽略、也不會繼續掃完)。
- 原子性:只有當「全部檔案」都成功解析並通過基本檢查時,才會呼叫 Register(...) 一次性寫入。 因此不會出現只註冊了一半、導致 catalog 處於半完成狀態的情況。
- 穩定性:會依檔名排序後再處理,確保行為 determinism(方便重現與除錯)。
注意:
- RegisterAll 只負責「把設定檔宣告的遊戲資訊放進 Catalog」。
遊戲邏輯(LogicBuilder / LogicRegistry)是否支援該 LogicKey,屬於後續 Problab 組裝/建機台時的責任。
func (*Problab) SnapshotFormat ¶ added in v0.6.0
SnapshotFormat returns the optional identifier used by persisted PRNG snapshots in Optimal Artifacts.
type ProblabOption ¶ added in v0.5.0
type ProblabOption func(*Problab)
ProblabOption 是 Problab 的選項函數類型。
func WithOptimalDir ¶ added in v0.6.0
func WithOptimalDir(root string) ProblabOption
WithOptimalDir configures a regular-file optimal directory. Gacha seed banks are opened through the read-only mmap backend on supported Unix platforms; a mapped file must not be rewritten in place. It is mutually exclusive with WithOptimalFS.
func WithOptimalFS ¶ added in v0.5.0
func WithOptimalFS(optimalFS fs.FS) ProblabOption
WithOptimalFS 設置可攜式優化文件系統。gacha 的 seed bank 只讀入記憶體一次。 正式環境若要 mmap 請使用 WithOptimalDir;已映射的檔案不得原地改寫。
func WithSeedEntropy ¶ added in v0.8.0
func WithSeedEntropy(entropy io.Reader) ProblabOption
WithSeedEntropy configures the entropy source used only when Problab asks the Factory to generate a new root seed. It is not the per-cycle reseed provider; that capability belongs to the concrete PRNG Factory.
type RuntimeHealth ¶ added in v0.3.0
type Seed ¶ added in v0.17.0
type Seed struct {
// contains filtered or unexported fields
}
Seed 是建構 Machine/Simulator 時的根 seed material。
只能由 SeedBytes、SeedInt64、SeedString 產生;零值 Seed{} 表示「未提供」, 此時由 PRNG Factory 的 GenerateSeed 產生 seed。明確提供的空 material(例如 SeedBytes(nil) 或 SeedString(""))是合法的 deterministic seed,與「未提供」不同。
seed 是交給 Factory New(seed) 推導 PRNG 狀態的根材料,不是 PRNG 快照; 需要從精確狀態開始時,請改用 Machine.RestoreCore。
func SeedString ¶ added in v0.17.0
SeedString 以字串未經修改的 UTF-8 位元組作為 seed material。
type SeedMaker ¶ added in v0.5.0
type SeedMaker struct {
// contains filtered or unexported fields
}
SeedMaker retains the legacy PCG64 child-seed sequence for source compatibility. New code should use PRNGFactory.DeriveSeed with an explicit StreamID instead.
func NewSeedMaker
deprecated
added in
v0.5.0
type SettingSource ¶ added in v0.17.0
type SettingSource struct {
// contains filtered or unexported fields
}
SettingSource 描述遊戲設定的來源。零值表示使用 catalog 中該遊戲的設定; 自訂設定只能以 SettingFromJSON 或 SettingFromYAML 提供原始 bytes, 一律由 Problab 解析並完成設定驗證,不接受手工建立的 *spec.GameSetting。
func SettingFromJSON ¶ added in v0.17.0
func SettingFromJSON(raw []byte) SettingSource
SettingFromJSON 以 JSON 原始設定作為來源。呼叫時即複製 raw。
func SettingFromYAML ¶ added in v0.17.0
func SettingFromYAML(raw []byte) SettingSource
SettingFromYAML 以 YAML 原始設定作為來源。呼叫時即複製 raw。
type Simulator ¶
type Simulator struct {
GameName string // 遊戲名稱
GameId spec.GID // 遊戲名稱enum
// contains filtered or unexported fields
}
Simulator 用於模擬遊戲行為,可建立多台機台並平行紀錄統計。
func (*Simulator) SetCfg ¶ added in v0.17.0
SetCfg 以 c 整份取代目前的執行設定:從預設值開始套用,不合併上次的設定,所以 SetCfg(BetConfig{}) 回到預設。驗證失敗時回傳 errs.Warn,原設定不變。只改設定,不動 機台、RNG 與統計;執行期間不得呼叫。
func (*Simulator) Sim ¶
func (s *Simulator) Sim(betMode int, round int, showpb bool) (*stats.StatReport, time.Duration, error)
Sim 單線模擬器:以一台機台連續跑指定 round 並回傳統計結果與用時
type SlotRuntime ¶
type SlotRuntime struct {
// contains filtered or unexported fields
}
func (*SlotRuntime) Close ¶ added in v0.2.0
func (rt *SlotRuntime) Close()
Close transitions the runtime into a closed state. It is safe to call multiple times.
func (*SlotRuntime) Closed ¶ added in v0.2.0
func (rt *SlotRuntime) Closed() bool
Closed reports whether the runtime has been closed.
func (*SlotRuntime) ClosedReason ¶
func (rt *SlotRuntime) ClosedReason() string
func (*SlotRuntime) Health ¶ added in v0.3.0
func (rt *SlotRuntime) Health() RuntimeHealth
func (*SlotRuntime) PoolMetrics ¶ added in v0.3.0
func (rt *SlotRuntime) PoolMetrics(gid spec.GID) (MachinePoolMetrics, bool)
func (*SlotRuntime) Spin ¶
func (rt *SlotRuntime) Spin(ctx context.Context, req *dto.SpinRequest) (dto.SpinResult, error)
type SpinInput ¶ added in v0.17.0
type SpinInput struct {
BetMode int // 必須在 Machine 的 bet mode 範圍內
BetMult int // 必須 >= 1;零值不合法。下注單位 × BetMult 不得溢位
CaptureSnapshots bool // 是否把起始/結束 RNG 快照寫進結果的 State
Cycle int // 第幾段,0 為新局
Choice int // ChooseNext 的位置索引;Cycle 為 0 時必須為 0
ChooseNext []int // 上一段結果的 ChooseNext;Cycle 為 0 時必須為空。Machine 會複製
Checkpoint any // 上一段結果的 checkpoint;遊戲註冊的型別
StartSnap []byte // 本段的指定起始快照,空表示不指定。Machine 會複製
}
SpinInput 是 SpinUnsafe 的輸入。呼叫端應使用帶欄位名的 literal,之後新增欄位時 不必修改;新增欄位的零值等於新增前的行為。
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
dev
command
|
|
|
exp
command
|
|
|
opt
command
|
|
|
run
command
|
|
|
svr
command
|
|
|
demo_strategies
Package demo_strategies assembles explicitly selected factories for demo games.
|
Package demo_strategies assembles explicitly selected factories for demo games. |
|
internal
|
|
|
optimalrt
Package optimalrt owns the immutable runtime representation of optimizer artifacts.
|
Package optimalrt owns the immutable runtime representation of optimizer artifacts. |
|
v2
Package v2 contains the second-generation Problab math-intent optimizer.
|
Package v2 contains the second-generation Problab math-intent optimizer. |
|
v2/examples/rgs
Package rgs demonstrates application-owned conversion and Tuner injection.
|
Package rgs demonstrates application-owned conversion and Tuner injection. |
|
Package playerexp analyzes finite player experiences on a sequential, deterministic game.
|
Package playerexp analyzes finite player experiences on a sequential, deterministic game. |
|
cli
Package cli composes playerexp with terminal progress and CSV publication.
|
Package cli composes playerexp with terminal progress and CSV publication. |
|
sdk
|
|
|
layer
Package layer 提供與盤面位置對應的值型別狀態容器。
|
Package layer 提供與盤面位置對應的值型別狀態容器。 |
|
sampler
Package sampler 提供一系列高效能的加權抽樣演算法與工具。
|
Package sampler 提供一系列高效能的加權抽樣演算法與工具。 |
|
strategy
Package strategy defines the contract between player strategies and the executor that runs them (playerexp).
|
Package strategy defines the contract between player strategies and the executor that runs them (playerexp). |
|
strategy/builtin
Package builtin provides optional strategy factories.
|
Package builtin provides optional strategy factories. |
|
tag
Package tag compiles named result predicates into immutable bit assignments.
|
Package tag compiles named result predicates into immutable bit assignments. |
|
api/dev
Package dev 提供 ProbLab 的「內部 Dev Panel」HTTP endpoints。
|
Package dev 提供 ProbLab 的「內部 Dev Panel」HTTP endpoints。 |
|
app
Package app 提供應用程式生命週期管理(App),負責統一啟動與關閉多個 Component。
|
Package app 提供應用程式生命週期管理(App),負責統一啟動與關閉多個 Component。 |