social-game-kit 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +8 -8
- data/lib/sgk/gacha/engine.rb +5 -11
- data/lib/sgk/gacha/multiple_draw.rb +40 -0
- data/lib/sgk/gacha/pity_system.rb +118 -0
- data/lib/sgk/gacha/result.rb +2 -7
- data/lib/sgk/version.rb +1 -1
- data/lib/sgk.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e79f7963dc2c44320694859eddb15f1db44ce3c29bff1cc1f6ba36436dbeeb0
|
|
4
|
+
data.tar.gz: d0007d4a08edf989d0a9e95e6df4222722adf83ff0b56abecae1118fb9ed2f70
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 906cf1ec805dd5f47db11677d21c34b575cf783f64738d4aecabdf2da99b0d5c2cbcef8caecb8d7a6a829d7fee054742ac48ee5c97db5b4736ae1fc7d9a687e3
|
|
7
|
+
data.tar.gz: 829e48ebdb65d71afde7a6609d65173aea83711e404033d22ef0256e5543cefdf71c835dd74dbfe652a3b952b0322e9b04bc1c1fc7a48bed358752dbbd5612bc
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -35,13 +35,13 @@ bundle install
|
|
|
35
35
|
### 基本的な使用方法
|
|
36
36
|
|
|
37
37
|
```ruby
|
|
38
|
-
require '
|
|
38
|
+
require 'ssg'
|
|
39
39
|
|
|
40
40
|
# カードマスタデータ(ActiveRecordモデル)から抽選
|
|
41
41
|
cards = GachaCard.all
|
|
42
42
|
|
|
43
43
|
# エンジンの初期化
|
|
44
|
-
engine =
|
|
44
|
+
engine = SSG::Gacha::Engine.new(cards)
|
|
45
45
|
|
|
46
46
|
# 単発抽選
|
|
47
47
|
card = engine.draw
|
|
@@ -76,7 +76,7 @@ end
|
|
|
76
76
|
# app/controllers/gacha_controller.rb
|
|
77
77
|
class GachaController < ApplicationController
|
|
78
78
|
def draw
|
|
79
|
-
engine =
|
|
79
|
+
engine = SSG::Gacha::Engine.new(GachaCard.all)
|
|
80
80
|
@result = engine.draw
|
|
81
81
|
|
|
82
82
|
# ユーザーにカードを付与する処理など
|
|
@@ -87,7 +87,7 @@ class GachaController < ApplicationController
|
|
|
87
87
|
|
|
88
88
|
def draw_multiple
|
|
89
89
|
count = params[:count].to_i.clamp(1, 10) # 最大10連
|
|
90
|
-
engine =
|
|
90
|
+
engine = SSG::Gacha::Engine.new(GachaCard.all)
|
|
91
91
|
@results = engine.draw_multiple(count)
|
|
92
92
|
|
|
93
93
|
current_user.gacha_cards.push(*@results)
|
|
@@ -98,7 +98,7 @@ class GachaController < ApplicationController
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def probabilities
|
|
101
|
-
engine =
|
|
101
|
+
engine = SSG::Gacha::Engine.new(GachaCard.all)
|
|
102
102
|
|
|
103
103
|
render json: engine.probabilities
|
|
104
104
|
end
|
|
@@ -159,14 +159,14 @@ GachaCard.create!([
|
|
|
159
159
|
|
|
160
160
|
## APIリファレンス
|
|
161
161
|
|
|
162
|
-
###
|
|
162
|
+
### SSG::Gacha::Engine
|
|
163
163
|
|
|
164
164
|
#### `initialize(card_relation)`
|
|
165
165
|
|
|
166
166
|
ActiveRecord::Relationを受け取ります。
|
|
167
167
|
|
|
168
168
|
```ruby
|
|
169
|
-
engine =
|
|
169
|
+
engine = SSG::Gacha::Engine.new(GachaCard.all)
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
#### `draw`
|
|
@@ -194,7 +194,7 @@ probs = engine.probabilities
|
|
|
194
194
|
# => {1=>70.0, 2=>25.0, 3=>4.0, 4=>1.0}
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
###
|
|
197
|
+
### SSG::Gacha::Result
|
|
198
198
|
|
|
199
199
|
ガチャ結果をラップするクラスです。
|
|
200
200
|
|
data/lib/sgk/gacha/engine.rb
CHANGED
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module SGK
|
|
4
4
|
module Gacha
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# This engine takes an ActiveRecord relation of cards and provides methods
|
|
8
|
-
# to draw cards based on their weights, supporting both single draws and
|
|
9
|
-
# multiple draws in one operation.
|
|
5
|
+
# 重み付きガチャエンジン
|
|
10
6
|
class Engine
|
|
11
7
|
def initialize(card_relation)
|
|
12
8
|
@cards = card_relation.to_a
|
|
@@ -26,16 +22,14 @@ module SGK
|
|
|
26
22
|
end
|
|
27
23
|
|
|
28
24
|
# NOTE: 浮動小数点誤差を考慮して最後のカードを返す実装だが、重み付き抽選の特性上起こる可能性はほとんどない。
|
|
29
|
-
# Returns the last card as a fallback for floating-point precision errors,
|
|
30
|
-
# though this scenario is highly unlikely given the nature of weighted random selection.
|
|
31
25
|
@cards.last
|
|
32
26
|
end
|
|
33
27
|
|
|
34
|
-
#
|
|
28
|
+
# 単一操作で複数のカードを抽選する(例:10連ガチャ)。
|
|
35
29
|
#
|
|
36
|
-
# @param count [Integer]
|
|
37
|
-
# @return [Array<Object>]
|
|
38
|
-
# @raise [ArgumentError]
|
|
30
|
+
# @param count [Integer] 抽選するカードの枚数
|
|
31
|
+
# @return [Array<Object>] 抽選されたカードの配列
|
|
32
|
+
# @raise [ArgumentError] countが正の値でない場合
|
|
39
33
|
def draw_multiple(count)
|
|
40
34
|
raise ArgumentError, "Draw count must be positive" unless count.positive?
|
|
41
35
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SGK
|
|
4
|
+
module Gacha
|
|
5
|
+
# 10連ガチャ
|
|
6
|
+
#
|
|
7
|
+
# 使用例:
|
|
8
|
+
# engine = SGK::Gacha::Engine.new(GachaCard.all)
|
|
9
|
+
# drawer = SGK::Gacha::MultipleDraw.new(engine, draw_count: 10)
|
|
10
|
+
# results = drawer.execute
|
|
11
|
+
class MultipleDraw
|
|
12
|
+
DEFAULT_DRAW_COUNT = 10
|
|
13
|
+
|
|
14
|
+
attr_reader :engine, :draw_count
|
|
15
|
+
|
|
16
|
+
# 初期化
|
|
17
|
+
#
|
|
18
|
+
# @param engine [SGK::Gacha::Engine] 使用するガチャエンジン
|
|
19
|
+
# @param draw_count [Integer] 抽選するカードの枚数(デフォルト: 10)
|
|
20
|
+
# @raise [ArgumentError] engineが無効、またはdraw_countが正の値でない場合
|
|
21
|
+
def initialize(engine, draw_count: DEFAULT_DRAW_COUNT)
|
|
22
|
+
raise ArgumentError, "Engine must be a SGK::Gacha::Engine instance" unless engine.is_a?(Engine)
|
|
23
|
+
raise ArgumentError, "Draw count must be positive" unless draw_count.positive?
|
|
24
|
+
|
|
25
|
+
@engine = engine
|
|
26
|
+
@draw_count = draw_count
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# 複数抽選を実行する
|
|
30
|
+
#
|
|
31
|
+
# @return [Array<Object>] 抽選されたカードの配列
|
|
32
|
+
def execute
|
|
33
|
+
@engine.draw_multiple(@draw_count)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# executeのエイリアス
|
|
37
|
+
alias draw execute
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SGK
|
|
4
|
+
module Gacha
|
|
5
|
+
# 100回連続でUR(Ultra Rare)カードが出なかった場合、次回の抽選でURカードを保証する
|
|
6
|
+
|
|
7
|
+
# 使用例:
|
|
8
|
+
# engine = SGK::Gacha::Engine.new(GachaCard.all)
|
|
9
|
+
# pity = SGK::Gacha::PitySystem.new(engine, guarantee_limit: 100, guaranteed_rarity: :ultra_rare)
|
|
10
|
+
#
|
|
11
|
+
# result = pity.draw(current_pity_count: user_current_pity)
|
|
12
|
+
class PitySystem
|
|
13
|
+
attr_reader :engine, :guarantee_limit, :guaranteed_rarity
|
|
14
|
+
|
|
15
|
+
# 初期化
|
|
16
|
+
#
|
|
17
|
+
# @param engine [SGK::Gacha::Engine] 抽選に使用するガチャエンジン
|
|
18
|
+
# @param guarantee_limit [Integer] 保証レアが出るまでの抽選回数(デフォルト: 100)
|
|
19
|
+
# @param guaranteed_rarity [Symbol, String] 保証するレアリティ(デフォルト: :ultra_rare)
|
|
20
|
+
# @raise [ArgumentError] guarantee_limitが正の値でない場合
|
|
21
|
+
def initialize(engine, guarantee_limit: 100, guaranteed_rarity: :ultra_rare)
|
|
22
|
+
raise ArgumentError, "Engine must be a SGK::Gacha::Engine instance" unless engine.is_a?(Engine)
|
|
23
|
+
raise ArgumentError, "Guarantee limit must be positive" unless guarantee_limit.positive?
|
|
24
|
+
|
|
25
|
+
@engine = engine
|
|
26
|
+
@guarantee_limit = guarantee_limit
|
|
27
|
+
@guaranteed_rarity = guaranteed_rarity.to_sym
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# カード抽選
|
|
31
|
+
#
|
|
32
|
+
# @param current_pity_count [Integer] 最後の保証カードからの抽選回数
|
|
33
|
+
# @return [Object] 抽選されたカード(pity_count >= guarantee_limitの場合は保証UR)
|
|
34
|
+
# @raise [ArgumentError] current_pity_countが負の値の場合
|
|
35
|
+
def draw(current_pity_count: 0)
|
|
36
|
+
raise ArgumentError, "Pity count must be non-negative" unless current_pity_count >= 0
|
|
37
|
+
|
|
38
|
+
# 保証カードを返す
|
|
39
|
+
if current_pity_count >= @guarantee_limit
|
|
40
|
+
return draw_guaranteed_card
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# それ以外の場合は通常の抽選を実行
|
|
44
|
+
@engine.draw
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# 複数のカードを抽選する
|
|
48
|
+
#
|
|
49
|
+
# @param count [Integer] 抽選するカードの枚数
|
|
50
|
+
# @param current_pity_count [Integer] 最後の保証カードからの抽選回数
|
|
51
|
+
# @return [Array<Object>] 保証カードの抽選カードの配列
|
|
52
|
+
# @raise [ArgumentError] countが正の値でない、またはpity_countが負の値の場合
|
|
53
|
+
def draw_multiple(count, current_pity_count: 0)
|
|
54
|
+
raise ArgumentError, "Draw count must be positive" unless count.positive?
|
|
55
|
+
raise ArgumentError, "Pity count must be non-negative" unless current_pity_count >= 0
|
|
56
|
+
|
|
57
|
+
results = []
|
|
58
|
+
current_count = current_pity_count
|
|
59
|
+
|
|
60
|
+
count.times do
|
|
61
|
+
card = draw(current_pity_count: current_count)
|
|
62
|
+
results << card
|
|
63
|
+
|
|
64
|
+
# 保証カードを引いた場合は抽選回数をリセット、それ以外はカウントを増やしていく
|
|
65
|
+
if is_guaranteed_card?(card)
|
|
66
|
+
current_count = 0
|
|
67
|
+
else
|
|
68
|
+
current_count += 1
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
results
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# 次の保証までの残り回数と保証回数を取得する
|
|
76
|
+
#
|
|
77
|
+
# @param current_pity_count [Integer] 最後の保証カードからの抽選回数
|
|
78
|
+
# @return [Hash] 次の保証までの残り回数と保証回数
|
|
79
|
+
# @example
|
|
80
|
+
# pity.next_guarantee_info(current_pity_count: 75)
|
|
81
|
+
# # => { pity_count: 75, remaining: 25, guaranteed_at: 100 }
|
|
82
|
+
def next_guarantee_info(current_pity_count: 0)
|
|
83
|
+
raise ArgumentError, "Pity count must be non-negative" unless current_pity_count >= 0
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
current_pity_count: current_pity_count,
|
|
87
|
+
remaining: [@guarantee_limit - current_pity_count, 0].max,
|
|
88
|
+
guaranteed_at: @guarantee_limit
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
# 保証カード(UR)を抽選する
|
|
95
|
+
#
|
|
96
|
+
# @return [Object] 保証されたカード
|
|
97
|
+
# @raise [ArgumentError] 保証カードが利用できない場合
|
|
98
|
+
def draw_guaranteed_card
|
|
99
|
+
guaranteed_cards = @engine.instance_variable_get(:@cards).select do |card|
|
|
100
|
+
card.respond_to?(:rarity) && card.rarity.to_sym == @guaranteed_rarity
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
raise ArgumentError, "No cards with rarity #{@guaranteed_rarity} found" if guaranteed_cards.empty?
|
|
104
|
+
|
|
105
|
+
# 保証カードからランダムにカードを返す
|
|
106
|
+
guaranteed_cards.sample
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# カードが保証レアリティのカードかどうかをチェックする
|
|
110
|
+
#
|
|
111
|
+
# @param card [Object] 保証レアリティのカードかどうかをチェックするカード
|
|
112
|
+
# @return [Boolean] カードが保証レアリティのカードかどうか
|
|
113
|
+
def is_guaranteed_card?(card)
|
|
114
|
+
card.respond_to?(:rarity) && card.rarity.to_sym == @guaranteed_rarity
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/sgk/gacha/result.rb
CHANGED
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module SGK
|
|
4
4
|
module Gacha
|
|
5
|
-
#
|
|
5
|
+
# ガチャ抽選の結果を表す。
|
|
6
6
|
#
|
|
7
|
-
# This class wraps a card object and provides convenient accessors
|
|
8
|
-
# for common card properties. It can be serialized to a hash for
|
|
9
|
-
# logging or API responses.
|
|
10
7
|
class Result
|
|
11
8
|
attr_reader :card
|
|
12
9
|
|
|
@@ -22,9 +19,7 @@ module SGK
|
|
|
22
19
|
@card.rarity
|
|
23
20
|
end
|
|
24
21
|
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# @return [Hash] A hash containing card_id, name, and rarity
|
|
22
|
+
# JSON変換用メソッド
|
|
28
23
|
def json_format
|
|
29
24
|
{
|
|
30
25
|
card_id: @card.id,
|
data/lib/sgk/version.rb
CHANGED
data/lib/sgk.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: social-game-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sugawara_nagisa
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-11-
|
|
10
|
+
date: 2025-11-28 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activerecord
|
|
@@ -53,6 +53,8 @@ files:
|
|
|
53
53
|
- Rakefile
|
|
54
54
|
- lib/sgk.rb
|
|
55
55
|
- lib/sgk/gacha/engine.rb
|
|
56
|
+
- lib/sgk/gacha/multiple_draw.rb
|
|
57
|
+
- lib/sgk/gacha/pity_system.rb
|
|
56
58
|
- lib/sgk/gacha/result.rb
|
|
57
59
|
- lib/sgk/version.rb
|
|
58
60
|
- sig/ssg.rbs
|