Skip to content

Commit e26e114

Browse files
rewrite to asynchronous processing.
1 parent cdd222e commit e26e114

File tree

8 files changed

+204
-227
lines changed

8 files changed

+204
-227
lines changed

README.md

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
## 特徴
44

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

88

99
## 景品定義
1010

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

1414
```ts
1515
const wins = ["ハワイ旅行", "", "", ""] as const;
@@ -26,7 +26,8 @@ const loses = ["ドリンクバー無料", "次回100円無料"] as const;
2626
* `Configure<WINS, LOSES>``当選商品``落選商品``確率`などを保持します。
2727
* `WINS``LOSES` を定義します。(内部では`ストリングリテラル型`とその`配列`で保持します)
2828
* 各確率を定義します。(百分率ではないので、割合で定義して構いません)
29-
* `gain` は内部で当選確率を算出するためのオフセット値になります。`0` 以上を指定してください。(`100``10000`くらいが妥当です。)
29+
* `gain` は内部で当選確率を算出するためのオフセット値になります。`0` 以上を指定してください。
30+
* ヒストグラムの取得では、`ratioの合計数値` × `gain` 分のデータを取得します。したがって、`gain`を上げすぎると抽選処理のパフォーマンスに影響が出てきます。
3031

3132

3233
```ts
@@ -54,14 +55,12 @@ const config = new gacha.Configure(
5455
* `Repository<WINS, LOSES>` に、`過去の抽選結果``現在の在庫数` の取得要求に対して応答するクラスです。
5556
* `Repository<WINS, LOSES>``interface` なので、実装が必要です。
5657
* 実際にはデータベースやストレージなどと連携することになります。
57-
* 実装する関数は全て`同期関数`です。データベース等は概ね非同期関数になると思いますので、
58-
データベース等のストレージから情報を取得するインターフェースです。`Repository<WINS, LOSES>``interface` なので、クラスを作成する必要があります。定義する関数は全て同期関数なので、抽選実行前に予め非同期処理を完了させて、即価として初期化するのもアリです。
5958

60-
#### `getPastResultHistogram(n: number): { [_ in WINS | LOSES]: number }`
59+
#### `getPastResultHistogram(n: number): Promise<{ [_ in WINS | LOSES]: number; }>`
6160

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

64-
#### `getStocks(): { [_ in WINS]: number }`
63+
#### `getStocks(): Promise<{ [_ in WINS]: number; }>`
6564

6665
* 現在の在庫数を返却します。
6766
* これは`WINS`のみです。
@@ -75,23 +74,24 @@ import * as gacha from "@codianz/gacha";
7574
class SampleRepo implements gacha.Repository<wins_t, loses_t> {
7675
private m_data: Array<wins_t | loses_t> = [];
7776

78-
getPastResultHistogram(n: number): { [_ in wins_t | loses_t]: number } {
77+
getPastResultHistogram(n: number): Promise<{ [_ in wins_t | loses_t]: number; }> {
7978
const data = this.m_data.slice(-n);
80-
return {
81-
gold: data.filter((v) => v === "gold").length,
82-
silver: data.filter((v) => v === "silver").length,
83-
bronze: data.filter((v) => v === "bronze").length,
84-
none1: data.filter((v) => v === "none1").length,
85-
none2: data.filter((v) => v === "none2").length,
86-
};
79+
return Promise.resolve({
80+
gold: data.filter((v) => v === "gold").length,
81+
silver: data.filter((v) => v === "silver").length,
82+
bronze: data.filter((v) => v === "bronze").length,
83+
none1: data.filter((v) => v === "none1").length,
84+
none2: data.filter((v) => v === "none2").length,
85+
});
8786
}
8887

89-
getStocks(): { [_ in wins_t]: number } {
90-
return {
88+
getStocks(): Promise<{ [_ in wins_t]: number; }> {
89+
const stocks = this.m_stocks;
90+
return Promise.resolve(stocks ? { ...stocks } : {
9191
gold: 1,
9292
silver: 1,
93-
bronze: 1
94-
};
93+
bronze: 1,
94+
});
9595
}
9696
};
9797
```
@@ -100,11 +100,14 @@ class SampleRepo implements gacha.Repository<wins_t, loses_t> {
100100

101101
* `Engine<WINS, LOSES>`を生成して、`execute()`を実行します。
102102
* この `result` に応じて在庫数や、抽選記録を残すのはこのライブラリでは行いません。
103-
* 抽選実施が同時に発生し在庫確保ができなかった場合は、再度、`execute()`を実行します。この際、`getStocks()`の返却値を変更しましょう。
103+
* 抽選実施が同時に発生し在庫確保ができなかった場合は、再度、`execute()`を実行します。
104+
* 非同期関数なので、リトライ処理は気をつけてね。
104105

105106
```ts
106107
import * as gacha from "@codianz/gacha";
107108

108109
const engine = new gacha.Engine(config, repo);
109-
const result = engine.execute();
110+
engine.execute().then(result => {
111+
/** result に結果が入ります */
112+
});
110113
```

package-lock.json

Lines changed: 0 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codianz/gacha",
3-
"version": "0.0.1",
3+
"version": "0.0.2",
44
"description": "lottery algorithm.",
55
"main": "./dist/src/index.js",
66
"scripts": {
@@ -18,6 +18,7 @@
1818
"author": "Terukazu Inoue",
1919
"license": "MIT",
2020
"homepage": "https://github.com/CODIANZ/npm.gacha",
21+
"typings": "./dist/src/index.d.ts",
2122
"repository": {
2223
"type": "git",
2324
"url": "https://github.com/CODIANZ/npm.gacha.git"

src/configure.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ export class Configure<WINS extends string, LOSES extends string>
33
{
44
wins: readonly WINS[];
55
loses: readonly LOSES[];
6-
ratio: { [_ in WINS | LOSES]: number };
6+
ratio: { [_ in WINS | LOSES]: number; };
77
gain: number;
88

9-
constructor(wins: readonly WINS[], loses: readonly LOSES[], ratio: { [_ in WINS | LOSES]: number }, gain: number) {
9+
constructor(wins: readonly WINS[], loses: readonly LOSES[], ratio: { [_ in WINS | LOSES]: number; }, gain: number) {
1010
this.wins = wins;
1111
this.loses = loses;
1212
this.ratio = ratio;

src/engine.ts

Lines changed: 79 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class Engine<WINS extends string, LOSES extends string> {
2020
this.m_wins_and_loses = [...config.wins, ...config.loses];
2121
}
2222

23-
private getPercentage(src: { [_ in WINS | LOSES]: number }): { [_ in WINS | LOSES]: number } {
23+
private getPercentage(src: { [_ in WINS | LOSES]: number; }): { [_ in WINS | LOSES]: number; } {
2424
const total = this.getTotal(src);
2525
const result = {...src};
2626
this.m_wins_and_loses.forEach((key) => {
@@ -29,12 +29,12 @@ export class Engine<WINS extends string, LOSES extends string> {
2929
return result;
3030
};
3131

32-
private getTotal(src: { [_ in WINS | LOSES]: number }): number {
32+
private getTotal(src: { [_ in WINS | LOSES]: number; }): number {
3333
return this.m_config.wins.reduce((acc, key) => acc + src[key], 0) +
3434
this.m_config.loses.reduce((acc, key) => acc + src[key], 0);
3535
}
3636

37-
private getPartialPercentage(src: { [_ in WINS | LOSES]?: number }): { [_ in WINS | LOSES]?: number } {
37+
private getPartialPercentage(src: { [_ in WINS | LOSES]?: number; }): { [_ in WINS | LOSES]?: number; } {
3838
const total = this.getPartialTotal(src);
3939
const result = {...src};
4040
this.m_wins_and_loses.forEach((key) => {
@@ -46,90 +46,98 @@ export class Engine<WINS extends string, LOSES extends string> {
4646
return result;
4747
};
4848

49-
private getPartialTotal(src: { [_ in WINS | LOSES]?: number }): number {
49+
private getPartialTotal(src: { [_ in WINS | LOSES]?: number; }): number {
5050
return this.m_wins_and_loses.reduce((acc, key) => {
5151
const value = src[key];
5252
return value !== undefined ? acc + value : acc;
5353
}, 0)
5454
}
5555

56-
execute(): WINS | LOSES {
57-
const ideal_percentage = this.getPercentage(this.m_config.ratio);
58-
// console.log("ideal_percentage", ideal_percentage);
59-
60-
const past_data = this.m_data_repository.getPastResultHistogram(this.getTotal(this.m_config.ratio) * 100);
61-
// console.log("past_data", past_data);
62-
63-
const offset_past_data = (() => {
64-
const data = {...past_data};
65-
const gain = this.m_config.gain;
66-
const ratio = this.m_config.ratio;
67-
this.m_wins_and_loses.forEach((key) => {
68-
data[key] += ratio[key] * gain;
56+
public execute(): Promise<WINS | LOSES> {
57+
return this.m_data_repository.getPastResultHistogram(this.getTotal(this.m_config.ratio) * 100)
58+
.then((past_data) => {
59+
return this.m_data_repository.getStocks()
60+
.then((stocks) => {
61+
return {past_data, stocks};
6962
});
70-
return data;
71-
})();
72-
// console.log("offset_past_data", offset_past_data);
73-
74-
const past_percentage = this.getPercentage(offset_past_data);
75-
// console.log("past_percentage", past_percentage);
76-
77-
const availables = (() => {
78-
const result: { [_ in WINS | LOSES]?: number } = {};
79-
const keys: (WINS | LOSES)[] = [];
80-
const stocks = this.m_data_repository.getStocks();
81-
this.m_wins_and_loses.forEach((key) => {
82-
if(past_percentage[key] < ideal_percentage[key]){
83-
if(this.isWins(key)){
84-
if(stocks[key] > 0){
63+
})
64+
.then(({past_data, stocks}) => {
65+
// console.log("past_data", past_data);
66+
// console.log("stocks", stocks);
67+
68+
const ideal_percentage = this.getPercentage(this.m_config.ratio);
69+
// console.log("ideal_percentage", ideal_percentage);
70+
71+
const offset_past_data = (() => {
72+
const data = {...past_data};
73+
const gain = this.m_config.gain;
74+
const ratio = this.m_config.ratio;
75+
this.m_wins_and_loses.forEach((key) => {
76+
data[key] += ratio[key] * gain;
77+
});
78+
return data;
79+
})();
80+
// console.log("offset_past_data", offset_past_data);
81+
82+
const past_percentage = this.getPercentage(offset_past_data);
83+
// console.log("past_percentage", past_percentage);
84+
85+
const availables = (() => {
86+
const result: { [_ in WINS | LOSES]?: number; } = {};
87+
const keys: (WINS | LOSES)[] = [];
88+
this.m_wins_and_loses.forEach((key) => {
89+
if(past_percentage[key] < ideal_percentage[key]){
90+
if(this.isWins(key)){
91+
if(stocks[key] > 0){
92+
keys.push(key);
93+
}
94+
}
95+
else{
8596
keys.push(key);
8697
}
8798
}
88-
else{
89-
keys.push(key);
90-
}
99+
});
100+
101+
if(keys.length === 0){
102+
this.m_wins_and_loses.forEach((key) => {
103+
result[key] = this.m_config.ratio[key];
104+
});
91105
}
92-
});
93-
94-
if(keys.length === 0){
106+
else{
107+
keys.forEach((key) => {
108+
result[key] = this.m_config.ratio[key];
109+
});
110+
}
111+
return result;
112+
})();
113+
// console.log("availables", availables);
114+
115+
const available_percentage = this.getPartialPercentage(availables);
116+
// console.log("available_percentage", available_percentage);
117+
118+
const gacha_result = ((): WINS | LOSES | undefined => {
119+
let random = Math.random();
120+
let result: WINS | LOSES | undefined = undefined;
95121
this.m_wins_and_loses.forEach((key) => {
96-
result[key] = this.m_config.ratio[key];
122+
if(result !== undefined) return;
123+
const value = available_percentage[key];
124+
if(value !== undefined){
125+
random -= value;
126+
if(random <= 0){
127+
result = key;
128+
}
129+
}
97130
});
131+
return result;
132+
})();
133+
134+
if(gacha_result === undefined){
135+
throw new Error("logic error");
98136
}
99137
else{
100-
keys.forEach((key) => {
101-
result[key] = this.m_config.ratio[key];
102-
});
138+
return gacha_result;
103139
}
104-
return result;
105-
})();
106-
// console.log("availables", availables);
107-
108-
const available_percentage = this.getPartialPercentage(availables);
109-
// console.log("available_percentage", available_percentage);
110-
111-
const gacha_result = ((): WINS | LOSES | undefined => {
112-
let random = Math.random();
113-
let result: WINS | LOSES | undefined = undefined;
114-
this.m_wins_and_loses.forEach((key) => {
115-
if(result !== undefined) return;
116-
const value = available_percentage[key];
117-
if(value !== undefined){
118-
random -= value;
119-
if(random <= 0){
120-
result = key;
121-
}
122-
}
123-
});
124-
return result;
125-
})();
126-
127-
if(gacha_result === undefined){
128-
throw new Error("logic error");
129-
}
130-
else{
131-
return gacha_result;
132-
}
140+
});
133141
}
134142
}
135143

src/repository.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
export interface Repository<WINS extends string, LOSES extends string> {
2-
// 過去 N 件分のデータを取得する
3-
getPastResultHistogram(n: number): { [_ in WINS | LOSES]: number };
4-
// 在庫数を取得する
5-
getStocks(): { [_ in WINS]: number };
2+
getPastResultHistogram(n: number): Promise<{ [_ in WINS | LOSES]: number; }>;
3+
getStocks(): Promise<{ [_ in WINS]: number; }>;
64
}

0 commit comments

Comments
 (0)