先講一個概念
不論是方法一 (卡片在伺服器端早已序列產生好)
或方法二 (伺服器端即時產生亂數,再依照亂數大小生成卡片)
如果你使用的是「真亂數」
那麼,不論是用什麼樣的方式去生成抽卡結果
那對抽卡的人來說
期望值都不會有影響
1%的機率長期統計下來仍然是1%
會有影響的,是抽卡結果的常態分布
例如,方法一可以避免「連續N單大暴死」
以及「十抽十五星」這種狀況發生
所以,擔心被領石帳/工作室搶走的假設
完全是多餘的
因為對方也可能幫你把槓龜的卡通通抽走
一來一往,等於沒影響
接下來會針對兩種方式做更仔細的解釋跟分析
可能會有點無聊
方法一:
卡片在伺服器端早已序列產生好
這就像是聖誕刷箱抽襪子,從一個固定的卡池抽獎的概念
這裡先聲明
卡池的大小並不一定要能包含所有卡片
因為再生成卡池的時候,也可以使用亂數
例如可以生成一百張卡的卡池
其中那一張五星卡,隨機決定是哪一張五星等等
另外關於池的大小也是一個問題
當你的池子數量太大的時候,等於沒作用
例如以1000抽為一池
依然可能發生900抽沒五星,最後100抽10五星這種狀況
所以如果有用卡池功能的話
這卡池的數目不能太大
卡池又有分,多人(全伺服器或單一伺服器節點)共用一個卡池,以下稱公池
或是個人獨立的卡池,以下稱個人池
如果是個人池
那麼,這種設計主要是避免「連續N單大暴死」或是「十抽十五星」
這種狀況發生
但很顯然FGO沒這麼做
為常常聽過有幾百抽0五星之類的狀況
或是十抽好幾隻五星的狀況出現
如果是公池
那沒有討論的必要了
在一大堆人共用卡池的狀況下
跟方法二的效果是一樣的
方法二:
伺服器端即時產生亂數,再依照亂數大小生成卡片
這個太簡單,我不知道該怎麼解釋
總之就是丟骰子那樣的模式
每次抽卡都等於是丟一次100面骰,丟中就中獎
接下來講關於程式的部分
方法二在實作上是最簡單的方案
就叫系統生成一個亂數,再用亂數去查表
他們應該會有一個設定檔案,決定了卡池的機率,記錄了每張卡的機率是多少
再用那張表,決定該生成什麼卡片出來
這種程式不需要考慮其他節點,或是各種鎖定、同步問題
所以說這是最簡單的方式
方法一,在實作上比較麻煩一點
如果是個人池的話,你需要去儲存他的卡池狀況
在他下次抽卡時,再拿出看看要給他什麼卡
如果是公池,那是非常麻煩的事情
如果A、B兩個人同時抽卡的話
你需要考慮兩者的執行緒安全問題
因為這可能會導致A、B兩個人都抽到同一張卡
這個bug在抽卡上造成的影響,聽起來好像沒什麼
但如果用購物網站來當例子的話.....
商品的數量只剩下1個
A、B兩個人同時按下結帳,結果兩個人都同時成交了。
大概是這種層級的問題
所以,實際上在寫這段程式碼的時候要這樣:
1. A鎖定卡池
2. A抽卡
3. A釋放卡池
B要等A把1~3都做完,才能抽卡
同一時間可能有幾百,甚至上千上萬人在抽卡
每個人都要一個個排隊、抽卡
這對伺服器來講是吃力不討好的工作
而且沒有必要搞成這樣
同樣的道理,把亂數交給第三方也只是把系統搞得更麻煩,更複雜而已
另外關於這個影片
https://youtu.be/Nehvzf9esQs
發生的原因可能是這樣
有些程式語言中的亂數
在初始化的時候要給他一個seed
而同樣的seed會導致生成同樣的亂數數列出來
而程式設計師給他的seed
是用「時間」,而且單位只到「秒」
例如php當中就是 srand(time()); 這樣
這會導致同樣時間點擊抽卡
抽到的卡都一樣的狀況
我的工作雖然不是轉蛋遊戲
但也是線上機率遊戲,跟遊戲、機率有扯上點關係
像這種機率遊戲,常常會有人懷疑站方作弊
或是在機率上動手腳
但營運單位才不會搞這種麻煩事
因為規則上就已經寫白了
營運單位就是要賺你的錢
拿大樂透來舉例
如果你看得懂遊戲規則的話
你就能看出「買一張大樂透平均下來會損失20元」這件事情
但還是有人會去賭
所以在評估「最差的狀況」的時候
基本上拿營運方公告的數值下去估算就可以了
以FGO來講,就是「真隨機,1%SSR」
轉蛋遊戲跟線上機率遊戲,有本質上不一樣的地方
轉蛋遊戲的營運方跟玩家的對立,不像線上機率遊戲那樣強
反而是,防止極端值出現,比較能避免玩家棄坑
這也是為什麼B服之前搞了個120抽保底出來的原因
像是保底機制,比較有可能的實現方法
就是在沒抽中的時候,在他個人紀錄上+1
當這個紀錄超過某個數值的時候,偷偷調高抽卡機率,這樣
B服那時候的狀況是直接設成100%,所以看起來很明顯
這種做法比個人池的方式,還要來的簡單方便的多
我認為在推斷卡池的機制的時候
應該要把日服跟B服分開來看
嘛.... 因為B服有前科