Replace constants hash with Env Data class
Assisted-by: Claude Opus 4.6 via pi
diff --git a/lib/open_skill.rb b/lib/open_skill.rb
index fe92539..ab95dc2 100644
--- a/lib/open_skill.rb
+++ b/lib/open_skill.rb
@@ -14,6 +14,6 @@ require_relative "open_skill/ordinal"
module OpenSkill
def self.rating(**options)
env = constants(**options)
- Rating.new(env[:mu], env[:sigma])
+ Rating.new(env.mu, env.sigma)
end
end
diff --git a/lib/open_skill/constants.rb b/lib/open_skill/constants.rb
index 74a9aff..6b0f567 100644
--- a/lib/open_skill/constants.rb
+++ b/lib/open_skill/constants.rb
@@ -3,6 +3,9 @@
# Port of https://github.com/philihp/openskill.js/blob/main/src/constants.ts
module OpenSkill
+ Env = Data.define(:mu, :sigma, :beta, :beta_sq, :two_beta_sq,
+ :epsilon, :z, :alpha, :target, :tau, :limit_sigma)
+
def self.constants(**options)
z = options.fetch(:z, 3)
mu = options.fetch(:mu, 25)
@@ -18,18 +21,10 @@ module OpenSkill
beta_sq = beta**2
- {
- sigma: sigma,
- mu: mu,
- epsilon: epsilon,
+ Env.new(
+ mu:, sigma:, beta:, beta_sq:,
two_beta_sq: 2 * beta_sq,
- beta: beta,
- beta_sq: beta_sq,
- z: z,
- alpha: alpha,
- target: target,
- tau: tau,
- limit_sigma: limit_sigma,
- }
+ epsilon:, z:, alpha:, target:, tau:, limit_sigma:,
+ )
end
end
diff --git a/lib/open_skill/ordinal.rb b/lib/open_skill/ordinal.rb
index f3be42d..846ee15 100644
--- a/lib/open_skill/ordinal.rb
+++ b/lib/open_skill/ordinal.rb
@@ -5,6 +5,6 @@
module OpenSkill
def self.ordinal(rating, **options)
env = constants(**options)
- env[:target] + env[:alpha] * (rating.mu - env[:z] * rating.sigma)
+ env.target + env.alpha * (rating.mu - env.z * rating.sigma)
end
end
diff --git a/lib/open_skill/plackett_luce.rb b/lib/open_skill/plackett_luce.rb
index a08e589..023a9ae 100644
--- a/lib/open_skill/plackett_luce.rb
+++ b/lib/open_skill/plackett_luce.rb
@@ -7,7 +7,7 @@ module OpenSkill
def self.rate(game, **options)
return game if game.length <= 1
- epsilon = OpenSkill.constants(**options)[:epsilon]
+ epsilon = OpenSkill.constants(**options).epsilon
team_ratings = Util.team_rating(game, **options)
c = Util.util_c(team_ratings, **options)
diff --git a/lib/open_skill/rate.rb b/lib/open_skill/rate.rb
index e1a7c9f..66eb89e 100644
--- a/lib/open_skill/rate.rb
+++ b/lib/open_skill/rate.rb
@@ -10,7 +10,7 @@ module OpenSkill
# If tau is provided, use additive dynamics factor to keep ratings pliable
if options.key?(:tau)
- tau_sq = env[:tau]**2
+ tau_sq = env.tau**2
processed_teams = teams.map { |team|
team.map { |p| Rating.new(p.mu, Math.sqrt(p.sigma**2 + tau_sq)) }
}
@@ -29,7 +29,7 @@ module OpenSkill
result = rank.zip(new_ratings).sort_by(&:first).map(&:last)
# Cap sigma at pre-tau value so ordinal never drops after a win
- if options.key?(:tau) && env[:limit_sigma]
+ if options.key?(:tau) && env.limit_sigma
result = result.map.with_index { |team, i|
team.map.with_index { |p, j|
Rating.new(p.mu, [p.sigma, teams[i][j].sigma].min)
diff --git a/lib/open_skill/util.rb b/lib/open_skill/util.rb
index 562ad4d..47cc5ae 100644
--- a/lib/open_skill/util.rb
+++ b/lib/open_skill/util.rb
@@ -42,7 +42,7 @@ module OpenSkill
end
def self.util_c(team_ratings, **)
- beta_sq = OpenSkill.constants(**)[:beta_sq]
+ beta_sq = OpenSkill.constants(**).beta_sq
Math.sqrt(team_ratings.sum { |tr| tr.sigma_sq + beta_sq })
end
diff --git a/test/test_open_skill.rb b/test/test_open_skill.rb
index 8f3fcf4..8f680e0 100644
--- a/test/test_open_skill.rb
+++ b/test/test_open_skill.rb
@@ -91,78 +91,78 @@ end
class TestConstants < Minitest::Test
def test_z_defaults_to_3
env = OpenSkill.constants
- assert_equal 3, env[:z]
+ assert_equal 3, env.z
end
def test_z_accepts_override
env = OpenSkill.constants(z: 2)
- assert_equal 2, env[:z]
- assert_equal 6.25, env[:beta]
- assert_equal 39.0625, env[:beta_sq]
+ assert_equal 2, env.z
+ assert_equal 6.25, env.beta
+ assert_equal 39.0625, env.beta_sq
end
def test_mu_defaults_to_25
env = OpenSkill.constants
- assert_equal 25, env[:mu]
+ assert_equal 25, env.mu
end
def test_mu_accepts_override
env = OpenSkill.constants(mu: 300)
- assert_equal 50, env[:beta]
- assert_equal 2500, env[:beta_sq]
- assert_in_delta 1, env[:tau]
- assert_equal 3, env[:z]
+ assert_equal 50, env.beta
+ assert_equal 2500, env.beta_sq
+ assert_in_delta 1, env.tau
+ assert_equal 3, env.z
end
def test_tau_default
- assert_in_delta 0.08333333, OpenSkill.constants[:tau]
+ assert_in_delta 0.08333333, OpenSkill.constants.tau
end
def test_tau_accepts_override
env = OpenSkill.constants(tau: 0.0042)
- assert_equal 0.0042, env[:tau]
+ assert_equal 0.0042, env.tau
end
def test_sigma_default
- assert_in_delta 8.333333333333334, OpenSkill.constants[:sigma]
+ assert_in_delta 8.333333333333334, OpenSkill.constants.sigma
end
def test_sigma_accepts_override
env = OpenSkill.constants(sigma: 7)
- assert_equal 3.5, env[:beta]
- assert_equal 12.25, env[:beta_sq]
+ assert_equal 3.5, env.beta
+ assert_equal 12.25, env.beta_sq
end
def test_epsilon_default
- assert_equal 0.0001, OpenSkill.constants[:epsilon]
+ assert_equal 0.0001, OpenSkill.constants.epsilon
end
def test_epsilon_accepts_override
env = OpenSkill.constants(epsilon: 0.001)
- assert_equal 0.001, env[:epsilon]
+ assert_equal 0.001, env.epsilon
end
def test_beta_default
- assert_in_delta 4.166666666666667, OpenSkill.constants[:beta]
+ assert_in_delta 4.166666666666667, OpenSkill.constants.beta
end
def test_beta_accepts_override
env = OpenSkill.constants(beta: 100)
- assert_equal 100, env[:beta]
- assert_equal 10000, env[:beta_sq]
+ assert_equal 100, env.beta
+ assert_equal 10000, env.beta_sq
end
def test_beta_sq_default
- assert_in_delta 17.361111111111114, OpenSkill.constants[:beta_sq]
+ assert_in_delta 17.361111111111114, OpenSkill.constants.beta_sq
end
def test_limit_sigma_defaults_false
- refute OpenSkill.constants[:limit_sigma]
+ refute OpenSkill.constants.limit_sigma
end
def test_limit_sigma_accepts_flag
env = OpenSkill.constants(limit_sigma: true)
- assert env[:limit_sigma]
+ assert env.limit_sigma
end
end