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)。
設計重點:
- Problab 本身不綁定任何「檔案路徑」概念:設定檔來源一律以 fs.FS 的形式注入。
- Problab 會持有一份 Catalog(你要跑哪一批遊戲/設定檔)與一份 LogicRegistry(你支援哪些遊戲邏輯)。
- Machine 是對外提供 Spin 的最小單位;遊戲邏輯開發者(數學家)主要操作的是 sdk 內的型別與資料結構。
典型使用情境:
- 後端服務(HTTP / gRPC):由 Problab 建立 Machine,Machine 對外提供 Spin。
- 模擬器(sim):由 Problab 建立多台 Machine 進行大量模擬。
注意:此套引擎目前以 Slot 領域為中心(Spin -> Result),不是泛用遊戲框架。
Index ¶
- func Configs(cfgs ...fs.FS) []fs.FS
- func Logics(regs ...*slot.LogicRegistry) []*slot.LogicRegistry
- 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) Sim(betmode int, round int) (DevSimReport, error)
- func (d *DevSimulator) Spins(betmode int, round int) (DevSpinReport, error)
- type DevSpinReport
- type Machine
- 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 Problab
- func (p *Problab) All() []catalog.Entry
- func (p *Problab) BuildRuntime(poolSize int) (*SlotRuntime, 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) NewDevSimulator(gid spec.GID, seed int64) (*DevSimulator, error)
- func (p *Problab) NewMachine(id spec.GID, isSim bool) (*Machine, error)
- func (p *Problab) NewMachineByJSON(raw []byte, seed int64) (*Machine, error)
- func (p *Problab) NewMachineByYAML(raw []byte, seed int64) (*Machine, error)
- func (p *Problab) NewMachineWithSeed(id spec.GID, seed int64, isSim bool) (*Machine, error)
- func (p *Problab) NewSimulator(id spec.GID) (*Simulator, error)
- func (p *Problab) NewSimulatorByJSON(raw []byte, seed int64) (*Simulator, error)
- func (p *Problab) NewSimulatorByYAML(raw []byte, seed int64) (*Simulator, error)
- func (p *Problab) NewSimulatorWithSeed(id spec.GID, seed int64) (*Simulator, error)
- func (p *Problab) Register(ents ...catalog.Entry) error
- func (p *Problab) RegisterAll() error
- func (p *Problab) Summary() ([]catalog.Summary, error)
- type RuntimeHealth
- type Simulator
- 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)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
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 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) Sim ¶
func (d *DevSimulator) Sim(betmode int, round int) (DevSimReport, error)
func (*DevSimulator) Spins ¶
func (d *DevSimulator) Spins(betmode int, round int) (DevSpinReport, error)
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"`
}
type Machine ¶
type Machine struct {
BetUnits []int // 押注單位(由遊戲設定衍生;通常給外部列舉 UI/測試)
SpinRequest *buf.SpinRequest // 可重用的請求 buffer(每次 Spin 會覆寫/填充)
SpinResult *buf.SpinResult // 可重用的結果 buffer(熱路徑;每次 Spin 會覆寫)
// contains filtered or unexported fields
}
Machine 封裝一台「可對外提供 Spin」的遊戲機台。
你可以把 Machine 視為 Game 的「外殼(shell)」:
- 對外:提供 Spin 入口(HTTP/模擬器通常只操作 Machine)。
- 對內:持有 RNG(Core)與真正執行遊戲邏輯的核心(sdk/slot.Game)。
並發語意:
- Machine 預設不是 lock-free 結構;它內含可重用的 request/result buffer(熱路徑),因此同一台 Machine 不應被多 goroutine 同時 Spin。
- 若要併發模擬,由更高層建立多台 Machine 分散到不同 worker 並管理其生命週期。
Buffer 語意(非常重要,影響 DX 與正確性):
- SpinRequest / SpinResult 會被重用(避免 GC),每次 Spin 會覆寫內容。
- 你若需要在 Spin 後保留結果,請在離開臨界區前轉成 DTO(或自行 copy 你需要的欄位)。
initseed 用於記錄出生時的 seed(追溯/重現的基礎資訊);完整審計仍以 Core 的 Snapshot/Restore 為準。
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 為主要公開入口,會驗證投注請求,執行遊戲並回傳Spin結果。
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 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)。
Problab 本身不綁定任何「檔案路徑」概念:設定檔來源一律由 fs.FS 提供。
使用流程通常分成兩階段:
- 註冊/組裝階段(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, false) // // m.Spin(...) -> 取得結果(通常再轉成 DTO 回傳)
func New ¶
func New(cf core.PRNGFactory, cfgs []fs.FS, logics []*slot.LogicRegistry) (*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 工廠)。
func NewAuto ¶
func NewAuto(cf core.PRNGFactory, cfgs []fs.FS, logics []*slot.LogicRegistry) (*Problab, error)
NewAuto 建立一個直接進入執行階段的 Problab instance。
回傳的 Problab 會持有:cat(目錄)、reg(合併後 registry)、cf(RNG 工廠)。
func (*Problab) BuildRuntime ¶
func (p *Problab) BuildRuntime(poolSize int) (*SlotRuntime, error)
func (*Problab) NewMachine ¶
NewMachine 依據 Catalog 內的遊戲 ID 建立一台 Machine。
行為:
- 由 Catalog 取得對應的 GameSetting(通常來自 fs.FS 內的 YAML/JSON)。
- 以 CoreFactory 產生 RNG 核心(seed 由 crypto/rand 產生)。
- 透過 LogicRegistry 依據 GameSetting 內的 LogicKey 建出可執行的遊戲邏輯。
isSim 用於區分「模擬/分析」與「對外服務」的執行模式(例如:某些dto深拷貝行為可能只在 prod 開啟以增加 sim 的性能)。
注意:seed 會被記錄在 Machine 內(initseed),用於追溯/重現;真正的可審計能力以 Core 的 Snapshot/Restore 合約為準。
func (*Problab) NewMachineByJSON ¶
func (*Problab) NewMachineByYAML ¶
func (*Problab) NewMachineWithSeed ¶
NewMachineWithSeed 與 NewMachine 相同,但由呼叫端指定初始 seed。
使用情境:
- 可重現的測試:同一份設定 + 同一個 seed,應產生一致的隨機序列(取決於 Core 實作)。
注意:seed 只是「出生入口」。若要在任意時間點完整重現,請使用 Core 的 Snapshot/Restore(以 []byte 交換狀態)。
func (*Problab) NewSimulatorByJSON ¶
func (*Problab) NewSimulatorByYAML ¶
func (*Problab) NewSimulatorWithSeed ¶
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 組裝/建機台時的責任。
type RuntimeHealth ¶ added in v0.3.0
type Simulator ¶
type Simulator struct {
GameName string // 遊戲名稱
GameId spec.GID // 遊戲名稱enum
// contains filtered or unexported fields
}
Simulator 用於模擬遊戲行為,可建立多台機台並平行紀錄統計。
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)
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
dev
command
|
|
|
run
command
|
|
|
svr
command
|
|
|
sdk
|
|
|
sampler
Package sampler 提供一系列高效能的加權抽樣演算法與工具。
|
Package sampler 提供一系列高效能的加權抽樣演算法與工具。 |
|
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。 |