# frozen_string_literal: true module EuchreCamp module Services class EloCalculator # Standard Elo K-factor K_FACTOR = 32 # Calculate Elo ratings for a 2v2 Euchre match # # @param player_ratings [Hash] player_id => current_rating # @param team_1_players [Array] player IDs for team 1 # @param team_2_players [Array] player IDs for team 2 # @param team_1_score [Integer] score for team 1 # @param team_2_score [Integer] score for team 2 # @return [Hash] player_id => { rating_before:, rating_after:, rating_change: } def self.calculate(player_ratings:, team_1_players:, team_2_players:, team_1_score:, team_2_score:) # Validate inputs raise ArgumentError, "Team 1 must have exactly 2 players" unless team_1_players.length == 2 raise ArgumentError, "Team 2 must have exactly 2 players" unless team_2_players.length == 2 # Determine winner (first to 10 points) team_1_wins = team_1_score >= 10 team_2_wins = team_2_score >= 10 unless team_1_wins || team_2_wins raise ArgumentError, "One team must reach 10 points to win" end if team_1_wins && team_2_wins raise ArgumentError, "Only one team can win (both reached 10 points)" end winner = team_1_wins ? :team_1 : :team_2 # Calculate average ratings for each team team_1_avg = team_1_players.map { |id| player_ratings[id] }.sum / 2.0 team_2_avg = team_2_players.map { |id| player_ratings[id] }.sum / 2.0 # Calculate expected scores using Elo formula # E = 1 / (1 + 10^((R_opp - R_team) / 400)) expected_team_1 = 1.0 / (1.0 + 10.0**((team_2_avg - team_1_avg) / 400.0)) expected_team_2 = 1.0 / (1.0 + 10.0**((team_1_avg - team_2_avg) / 400.0)) # Actual scores (1 for win, 0 for loss) actual_team_1 = team_1_wins ? 1.0 : 0.0 actual_team_2 = team_2_wins ? 1.0 : 0.0 # Calculate rating changes for each player results = {} # Team 1 players team_1_players.each do |player_id| old_rating = player_ratings[player_id] rating_change = K_FACTOR * (actual_team_1 - expected_team_1) new_rating = (old_rating + rating_change).round results[player_id] = { rating_before: old_rating, rating_after: new_rating, rating_change: rating_change.round } end # Team 2 players team_2_players.each do |player_id| old_rating = player_ratings[player_id] rating_change = K_FACTOR * (actual_team_2 - expected_team_2) new_rating = (old_rating + rating_change).round results[player_id] = { rating_before: old_rating, rating_after: new_rating, rating_change: rating_change.round } end results end end end end