Skip to content

CODIANZ/npm.gacha

Folders and files

NameName
Last commit message
Last commit date

Latest commit

0704d71 · Jul 3, 2023

History

6 Commits
Jun 30, 2023
Jul 3, 2023
Jul 3, 2023
Jun 30, 2023
Jun 30, 2023
Jun 30, 2023
Jul 3, 2023
Jun 30, 2023
Jul 3, 2023
Jun 30, 2023

Repository files navigation

抽選ライブラリ

特徴

  • 抽選処理はサービスの利益率に絡む重要箇所なので、効率よりも型の安全性コードのメンテナンス性を重視しています。
  • デジスロやデジバチのようなギャンブル機とは異なり、抽選結果が分散されているため、確率を重視して抽選を行います。(射幸心を煽る仕掛けは含まれていません)

景品定義

景品には WINSLOSES に分類され、それぞれ、当選落選 に対応します。 具体的には下記のように定義してください。(トランスパイルでエラーになるからといって、血迷っても anyas なんかは使わないでください。)

const wins = ["ハワイ旅行", "松", "竹", "梅"] as const;
const loses = ["ドリンクバー無料", "次回100円無料"] as const;

WINSLOSES の違い

  • WINS は在庫管理を行います。確率により当選可能商品だとしても、該当商品の在庫が存在しない場合には除外されます。
  • LOSES は在庫管理を行いません。(Repository<WINS, LOSES>を参照)

抽選の設定

  • Configure<WINS, LOSES>当選商品落選商品確率などを保持します。
  • WINSLOSES を定義します。(内部ではストリングリテラル型とその配列で保持します)
  • 各確率を定義します。(百分率ではないので、割合で定義して構いません)
  • gain は内部で当選確率を算出するためのオフセット値になります。0 以上を指定してください。
  • ヒストグラムの取得では、ratioの合計数値 × gain 分のデータを取得します。したがって、gainを上げすぎると抽選処理のパフォーマンスに影響が出てきます。
import * as gacha from "@codianz/gacha";

const wins = ["gold", "silver", "bronze"] as const;
const loses = ["none1", "none2"] as const;

const config = new gacha.Configure(
  wins,
  loses,
  {
    gold: 1,
    silver: 20,
    bronze: 30,
    none1: 20,
    none2: 50
  },
  1000
);

データアクセス

  • Repository<WINS, LOSES> に、過去の抽選結果現在の在庫数 の取得要求に対して応答するクラスです。
  • Repository<WINS, LOSES>interface なので、実装が必要です。
  • 実際にはデータベースやストレージなどと連携することになります。

getPastResultHistogram(n: number): Promise<{ [_ in WINS | LOSES]: number; }>

直近 n 個のデータからヒストグラムを返却します。

getStocks(): Promise<{ [_ in WINS]: number; }>

  • 現在の在庫数を返却します。
  • これはWINSのみです。
  • LOSES は在庫管理対象外です。
  • 在庫管理を行わず、確率だけで管理する場合は、全て1以上の値を固定値として返却することで実現可能です。
import * as gacha from "@codianz/gacha";

class SampleRepo implements gacha.Repository<wins_t, loses_t> {
  private m_data: Array<wins_t | loses_t> = [];

  getPastResultHistogram(n: number): Promise<{ [_ in wins_t | loses_t]: number; }> {
    const data = this.m_data.slice(-n);
    return Promise.resolve({
        gold: data.filter((v) => v === "gold").length,
        silver: data.filter((v) => v === "silver").length,
        bronze: data.filter((v) => v === "bronze").length,
        none1: data.filter((v) => v === "none1").length,
        none2: data.filter((v) => v === "none2").length,
    });
  }

  getStocks(): Promise<{ [_ in wins_t]: number; }> {
    const stocks = this.m_stocks;
    return Promise.resolve(stocks ? { ...stocks } : {
      gold: 1,
      silver: 1,
      bronze: 1,
    });
  }
};

抽選の実行

  • Engine<WINS, LOSES>を生成して、execute()を実行します。
  • この result に応じて在庫数や、抽選記録を残すのはこのライブラリでは行いません。
  • 抽選実施が同時に発生し在庫確保ができなかった場合は、再度、execute()を実行します。
  • 非同期関数なので、リトライ処理は気をつけてね。
import * as gacha from "@codianz/gacha";

const engine = new gacha.Engine(config, repo);
engine.execute().then(result => {
  /** result に結果が入ります */
});

実行結果サンプル

在庫数未指定・試行回数連続1,000,000 x 10回

商品 種別 当選割合 在庫数
gold WINS 1 未指定
silver WINS 20 未指定
bronze WINS 30 未指定
none1 LOSES 20 未指定
none2 LOSES 50 未指定
stock unmanaged
ideal {gold: 0.008264462809917356, silver: 0.1652892561983471, bronze: 0.24793388429752067, none1: 0.1652892561983471, none2: 0.4132231404958678}
count#1 {gold: 8281, silver: 165296, bronze: 247926, none1: 165292, none2: 413205}
ratio#1 {gold: 0.008281, silver: 0.165296, bronze: 0.247926, none1: 0.165292, none2: 0.413205}
count#2 {gold: 8279, silver: 165289, bronze: 247931, none1: 165292, none2: 413209}
ratio#2 {gold: 0.008279, silver: 0.165289, bronze: 0.247931, none1: 0.165292, none2: 0.413209}
count#3 {gold: 8279, silver: 165294, bronze: 247928, none1: 165291, none2: 413208}
ratio#3 {gold: 0.008279, silver: 0.165294, bronze: 0.247928, none1: 0.165291, none2: 0.413208}
count#4 {gold: 8278, silver: 165290, bronze: 247929, none1: 165293, none2: 413210}
ratio#4 {gold: 0.008278, silver: 0.16529, bronze: 0.247929, none1: 0.165293, none2: 0.41321}
count#5 {gold: 8283, silver: 165291, bronze: 247930, none1: 165292, none2: 413204}
ratio#5 {gold: 0.008283, silver: 0.165291, bronze: 0.24793, none1: 0.165292, none2: 0.413204}
count#6 {gold: 8281, silver: 165295, bronze: 247930, none1: 165290, none2: 413204}
ratio#6 {gold: 0.008281, silver: 0.165295, bronze: 0.24793, none1: 0.16529, none2: 0.413204}
count#7 {gold: 8278, silver: 165290, bronze: 247932, none1: 165295, none2: 413205}
ratio#7 {gold: 0.008278, silver: 0.16529, bronze: 0.247932, none1: 0.165295, none2: 0.413205}
count#8 {gold: 8279, silver: 165291, bronze: 247932, none1: 165293, none2: 413205}
ratio#8 {gold: 0.008279, silver: 0.165291, bronze: 0.247932, none1: 0.165293, none2: 0.413205}
count#9 {gold: 8282, silver: 165290, bronze: 247933, none1: 165290, none2: 413205}
ratio#9 {gold: 0.008282, silver: 0.16529, bronze: 0.247933, none1: 0.16529, none2: 0.413205}
count#10 {gold: 8278, silver: 165294, bronze: 247928, none1: 165291, none2: 413209}
ratio#10 {gold: 0.008278, silver: 0.165294, bronze: 0.247928, none1: 0.165291, none2: 0.413209}

在庫数指定・試行回数連続1,000,000 x 10回

商品 種別 当選割合 在庫数
gold WINS 1 1
silver WINS 20 20
bronze WINS 30 30
none1 LOSES 20 未指定
none2 LOSES 50 未指定
stock managed
ideal {gold: 0.008264462809917356, silver: 0.1652892561983471, bronze: 0.24793388429752067, none1: 0.1652892561983471, none2: 0.4132231404958678}
count#1 {gold: 1, silver: 20, bronze: 30, none1: 165318, none2: 412908}
ratio#1 {gold: 0.0000017292750705976547, silver: 0.0000345855014119531, bronze: 0.00005187825211792964, none1: 0.2858802961210631, none2: 0.7140315108503364}
count#2 {gold: 1, silver: 20, bronze: 30, none1: 165402, none2: 413542}
ratio#2 {gold: 0.0000017271306315253154, silver: 0.00003454261263050631, bronze: 0.00005181391894575946, none1: 0.2856708607155502, none2: 0.7142410556222419}
count#3 {gold: 1, silver: 20, bronze: 30, none1: 166016, none2: 412474}
ratio#3 {gold: 0.0000017284859672866746, silver: 0.00003456971934573349, bronze: 0.00005185457901860024, none1: 0.28695632634506457, none2: 0.7129555208706038}
count#4 {gold: 1, silver: 20, bronze: 30, none1: 165577, none2: 413455}
ratio#4 {gold: 0.0000017268681691570983, silver: 0.00003453736338314197, bronze: 0.00005180604507471295, none1: 0.2859296508445249, none2: 0.7139822788788481}
count#5 {gold: 1, silver: 20, bronze: 30, none1: 165870, none2: 413538}
ratio#5 {gold: 0.000001725747637020048, silver: 0.00003451495274040096, bronze: 0.00005177242911060144, none1: 0.2862497605525154, none2: 0.7136622263179966}
count#6 {gold: 1, silver: 20, bronze: 30, none1: 165140, none2: 413264}
ratio#6 {gold: 0.0000017287429445678574, silver: 0.00003457485889135715, bronze: 0.000051862288337035725, none1: 0.285484609865936, none2: 0.714427224243891}
count#7 {gold: 1, silver: 20, bronze: 30, none1: 165262, none2: 412960}
ratio#7 {gold: 0.000001729287032249474, silver: 0.00003458574064498948, bronze: 0.00005187861096748421, none1: 0.2857854335236126, none2: 0.7141263728377427}
count#8 {gold: 1, silver: 20, bronze: 30, none1: 165044, none2: 413559}
ratio#8 {gold: 0.0000017281484272121164, silver: 0.000034562968544242325, bronze: 0.000051844452816363494, none1: 0.28522052902079653, none2: 0.7146913354094157}
count#9 {gold: 1, silver: 20, bronze: 30, none1: 166007, none2: 412511}
ratio#9 {gold: 0.0000017284023167504654, silver: 0.00003456804633500931, bronze: 0.000051852069502513964, none1: 0.2869268833967945, none2: 0.7129849680850512}
count#10 {gold: 1, silver: 20, bronze: 30, none1: 165327, none2: 413936}
ratio#10 {gold: 0.0000017261795848192862, silver: 0.00003452359169638572, bronze: 0.00005178538754457859, none1: 0.28538409221941813, none2: 0.7145278726217561}

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published