Replace constants hash with Env Data class
Assisted-by: Claude Opus 4.6 via pi
change kmryzvunpxqyttmxlnuwtrsloyoupyvp
commit 37c24426f44f0e247d8dae922971c6ac35734da0
author Alpha Chen <alpha@kejadlen.dev>
date
parent nqmmuqoo
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