Monday, July 5, 2010

World Cup Simulation

Here's another one from Programming Praxis. The goal is to simulate the world cup rounds using their calculations for winning expectation and thier elo values (read about their calculations). Here's the simulation:


class Team
include Comparable

attr_reader :symbol, :country, :initial_elo, :new_elo
attr_accessor :world_cup_wins

# Initialization of the team.
def initialize(symbol, country, initial_elo)
@symbol = symbol
@country = country
@initial_elo = initial_elo
@new_elo = initial_elo
@world_cup_wins = 0
end

# Defined for sorting.
def <=>(team_other)
@world_cup_wins <=> team_other.world_cup_wins
end

# Print out the team and a few statistics.
def to_s
"#{@symbol} #{@country} #{@initial_elo} Wins = #{@world_cup_wins}"
end

# Calculate the winning expectation between these two teasm.
def winning_expectation(team_other)
1.0 / (1.0 + 10.0 ** ((team_other.new_elo - @new_elo) / 400.0))
end

# Simulate a game based on the winning expectation between this team and
# the other team.
def game(team_other)
w_e = winning_expectation(team_other)
if rand <= w_e
# This team won so calculate appropriately the new elos.
calc_elo(w_e, 1.0)
team_other.calc_elo(w_e, 0.0)

# Return that this team won.
return true
else
# The other team won so calculate appropriately the new elos.
calc_elo(w_e, 0.0)
team_other.calc_elo(w_e, 1.0)

# Return that the other team won.
return false
end
end

# Reset the elo to the initial_elo for the start of a
# new simulation.
def reset_elo
@new_elo = initial_elo
end

# Calculate the elo based on the winning expectation and
# whether this team won or lost.
def calc_elo(w_e, win_loss)
@new_elo = @new_elo + 60.0 * 1.0 * (win_loss - w_e)
end
end

# Teams with their initial elo scores and ordered by the pairings (e.g. in the
# first round Uruguay will play Korea, and Spain will play Portugal.
initial_rankings = [

[ 7, "URU", "Uruguay", 1890],
[25, "KOR", "Korea", 1746],
[15, "USA", "United States", 1785],
[32, "GHA", "Ghana", 1711],
[ 3, "NED", "Netherlands", 2045],
[45, "SVK", "Slovakia", 1654],
[ 1, "BRA", "Brazil", 2082],
[ 8, "CHI", "Chile", 1883],
[ 4, "ARG", "Argentina", 1966],
[10, "MEX", "Mexico", 1873],
[ 6, "GER", "Germany", 1930],
[ 5, "ENG", "England", 1945],
[19, "PAR", "Paraguay", 1771],
[26, "JPN", "Japan", 1744],
[ 2, "ESP", "Spain", 2061],
[ 9, "POR", "Portugal", 1874],
]

# Create the teams array from the inital rankings above.
teams = Array.new()
initial_rankings.each do |r|
teams << Team.new(r[1], r[2], r[3])
end

# Each simulated World Cup
1.upto(1000000) do | sim |

# Start fresh by reinitializing the current_round with
# the teams and their elo.
current_round = teams
current_round.each { |t| t.reset_elo }

# Simulate the rounds
while current_round.size > 1 do
# Create the array where we'll store the winners for the next round.
next_round = Array.new

# Loop through the round 2 at a time. The winner will get stored in the next_round array.
i = 0
while i < current_round.size
# Add the winner to the next round.
next_round << (current_round[i].game(current_round[i+1]) ? current_round[i] : current_round[i+1])
i += 2
end

# Save the current round as the next round.
current_round = next_round
end

# Add to the winner's total.
current_round[0].world_cup_wins += 1
end

# Sort by the number of wins and then print out the counts.
teams.sort!
teams.each do | t |
puts "team: #{t} "
end



It should be commented well enough for you to figure everything out (assuming you also read the explanation over at Programming Praxis), but if you have any questions or comments, don't hesitate to ask.

No comments:

Post a Comment