lib/rrschedule.rb in rrschedule-0.2.6 vs lib/rrschedule.rb in rrschedule-0.2.7
- old
+ new
@@ -20,14 +20,36 @@
#This will generate the schedule based on the various parameters
def generate(params={})
raise "You need to specify at least 1 team" if @teams.nil? || @teams.empty?
raise "You need to specify at least 1 rule" if @rules.nil? || @rules.empty?
arrange_flights
+
+
+ @stats = {}
+ @teams.flatten.each do |t|
+ @stats[t] = {
+ :gt => {},
+ :ps => {}
+ }
+
+ @stats[t][:gt] = {}
+ all_gt.each do |gt|
+ @stats[t][:gt][gt] = 0
+ end
+
+ @stats[t][:ps] = {}
+ all_ps.each do |ps|
+ @stats[t][:ps][ps] = 0
+ end
+ end
+
@gamedays = []
@rounds = []
@flights.each_with_index do |teams,flight_id|
+
+
current_cycle = current_round = 0
teams = teams.sort_by{rand} if @shuffle
#loop to generate the whole round-robin(s) for the current flight
begin
@@ -39,19 +61,22 @@
team_a = t.shift
team_b = t.reverse!.shift
t.reverse!
- x = [team_a,team_b].shuffle
- matchup = {:team_a => x[0], :team_b => x[1]}
+ x = [team_a,team_b] #.shuffle
+
+ matchup = {:team_a => x[0], :team_b => x[1], :home_team_index => self.teams.index(x[0])}
games << matchup
end
+ #games = games.sort_by {|g| g[:home_team_index]}
+
#done processing round
current_round += 1
- #Team rotation
+ #Team rotation (the first team is fixed)
teams = teams.insert(1,teams.delete_at(teams.size-1))
#add the round in memory
@rounds ||= []
@rounds[flight_id] ||= []
@@ -98,11 +123,11 @@
end
res
end
#returns true if the generated schedule is a valid round-robin (for testing purpose)
- def round_robin?(flight_id=nil)
+ def round_robin?(flight_id=0)
#each round-robin round should contains n-1 games where n is the nbr of teams (:dummy included if odd)
return false if self.rounds[flight_id].size != (@flights[flight_id].size*self.cycles)-self.cycles
#check if each team plays the same number of games against each other
@flights[flight_id].each do |t1|
@@ -165,117 +190,82 @@
end
self.gamedays << Gameday.new(:date => gamedate, :games => games)
end
end
-
- def dispatch_game(game)
- @cur_rule ||= @rules.select{|r| r.wday >= self.start_date.wday}.first || @rules.first
- @cur_rule_index ||= @rules.index(@cur_rule)
-
- @gt_stack ||= @cur_rule.gt.clone
- @ps_stack ||= @cur_rule.ps.clone.shuffle
-
- @cur_gt ||= @gt_stack.shift
- @cur_ps ||= @ps_stack.shift
- @cur_date ||= next_game_date(self.start_date,@cur_rule.wday)
- @schedule ||= []
-
- #if one of the team has already plays at this gamedate, we change rule
- if @schedule.size>0
- games_this_date = @schedule.select{|v| v[:gamedate] == @cur_date}
- if games_this_date.select{|g| [game.team_a,game.team_b].include?(g[:team_a]) || [game.team_a,game.team_b].include?(g[:team_b])}.size >0
- @cur_rule_index = (@cur_rule_index < @rules.size-1) ? @cur_rule_index+1 : 0
- @cur_rule = @rules[@cur_rule_index]
- @gt_stack = @cur_rule.gt.clone
- @ps_stack = @cur_rule.ps.clone.shuffle
- @cur_gt = @gt_stack.shift
- @cur_ps = @ps_stack.shift
- @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday)
- end
+ def get_best_gt(game)
+ x = {}
+ gt_left = @gt_ps_avail.reject{|k,v| v.empty?}
+ gt_left.each_key do |gt|
+ x[gt] = [
+ @stats[game.team_a][:gt][gt] + @stats[game.team_b][:gt][gt],
+ rand(1000)
+ ]
end
+ x.sort_by{|k,v| [v[0],v[1]]}.first[0]
+ end
- @schedule << {:team_a => game.team_a, :team_b => game.team_b, :gamedate => @cur_date, :ps => @cur_ps, :gt => @cur_gt}
-
- if !@ps_stack.empty?
- @cur_ps = @ps_stack.shift
- else
- if !@gt_stack.empty?
- @cur_gt = @gt_stack.shift
- @ps_stack = @cur_rule.ps.clone.shuffle; @cur_ps = @ps_stack.shift
- else
- #PS and GT stack empty... we go to the next rule
- if @cur_rule_index < @rules.size-1
- last_rule=@cur_rule
- @cur_rule_index += 1
- @cur_rule = @rules[@cur_rule_index]
- #Go to the next date (except if the new rule is for the same weekday)
- @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday) if last_rule.wday != @cur_rule.wday
- else
- @cur_rule_index = 0
- @cur_rule = @rules[@cur_rule_index]
- @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday)
- end
- @gt_stack = @cur_rule.gt.clone; @cur_gt = @gt_stack.shift
- @ps_stack = @cur_rule.ps.clone.shuffle; @cur_ps = @ps_stack.shift
- end
+ def get_best_ps(game,gt)
+ x = {}
+ @gt_ps_avail[gt].each do |ps|
+ x[ps] = [
+ @stats[game.team_a][:ps][ps] + @stats[game.team_b][:ps][ps],
+ rand(1000)
+ ]
end
+ x.sort_by{|k,v| [v[0],v[1]]}.first[0]
+ end
+ def reset_resource_availability
+ @gt_ps_avail = {}
+ @cur_rule.gt.each do |gt|
+ @gt_ps_avail[gt] = @cur_rule.ps.clone
+ end
end
+
+ def dispatch_game(game)
+ if @cur_rule.nil?
+ @cur_rule = @rules.select{|r| r.wday >= self.start_date.wday}.first || @rules.first
+ @cur_rule_index = @rules.index(@cur_rule)
+ reset_resource_availability
+ end
- def place_game(game)
- @cur_rule ||= @rules.select{|r| r.wday >= self.start_date.wday}.first || @rules.first
+ @cur_gt = get_best_gt(game)
+ @cur_ps = get_best_ps(game,@cur_gt)
- @cur_rule_index ||= @rules.index(@cur_rule)
- @cur_gt_index ||= 0
- @cur_ps_index ||= 0
+ #increment the stats counters to lessen the chances that these teams plays on the same playing surface (or game time) too often
+ @stats[game.team_a][:gt][@cur_gt] += 1
+ @stats[game.team_a][:ps][@cur_ps] += 1
+ @stats[game.team_b][:gt][@cur_gt] += 1
+ @stats[game.team_b][:ps][@cur_ps] += 1
- @cur_gt = @cur_rule.gt[@cur_gt_index]
- @cur_ps = @cur_rule.ps[@cur_ps_index]
+ @gt_ps_avail[@cur_gt].delete(@cur_ps) #this playing surface has now been taken and is not available
+
@cur_date ||= next_game_date(self.start_date,@cur_rule.wday)
@schedule ||= []
- #if one of the team has already plays at this gamedate, we change rule
- if @schedule.size>0
- games_this_date = @schedule.select{|v| v[:gamedate] == @cur_date}
- if games_this_date.select{|g| [game.team_a,game.team_b].include?(g[:team_a]) || [game.team_a,game.team_b].include?(g[:team_b])}.size >0
- @cur_rule_index = (@cur_rule_index < @rules.size-1) ? @cur_rule_index+1 : 0
- @cur_rule = @rules[@cur_rule_index]
- @cur_ps_index=0
- @cur_gt_index=0
- @cur_ps = @cur_rule.ps.first
- @cur_gt = @cur_rule.gt.first
- @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday)
- end
- end
-
@schedule << {:team_a => game.team_a, :team_b => game.team_b, :gamedate => @cur_date, :ps => @cur_ps, :gt => @cur_gt}
- if @cur_ps_index < @cur_rule.ps.size-1
- @cur_ps_index += 1
- else
- @cur_ps_index = 0
+ x = @gt_ps_avail.reject{|k,v| v.empty?}
- if @cur_gt_index < @cur_rule.gt.size-1
- @cur_gt_index += 1
+ #no resources left, change rule
+ if x.empty?
+ if @cur_rule_index < @rules.size-1
+ last_rule=@cur_rule
+ @cur_rule_index += 1
+ @cur_rule = @rules[@cur_rule_index]
+ #Go to the next date (except if the new rule is for the same weekday)
+ @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday) if last_rule.wday != @cur_rule.wday
else
- @cur_gt_index = 0
-
- if @cur_rule_index < @rules.size-1
- @cur_rule_index += 1
- #Go to the next date (except if the new rule is for the same weekday)
- @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday) if @cur_rule.wday != @rules[@cur_rule_index].wday
- else
- @cur_rule_index = 0
- @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday)
- end
+ @cur_rule_index = 0
@cur_rule = @rules[@cur_rule_index]
+ @cur_date = next_game_date(@cur_date+=1,@cur_rule.wday)
end
+ reset_resource_availability
end
end
-
#get the next gameday
def next_game_date(dt,wday)
dt += 1 until wday == dt.wday && !self.exclude_dates.include?(dt)
dt
end
@@ -285,9 +275,25 @@
res=[]
self.gamedays.each do |gd|
res << gd.games.select {|g| (g.team_a == team_a && g.team_b == team_b) || (g.team_a == team_b && g.team_b == team_a)}
end
res.flatten
+ end
+
+ def all_gt
+ gt = []
+ @rules.each do |r|
+ gt = gt.concat(r.gt)
+ end
+ gt.flatten.uniq
+ end
+
+ def all_ps
+ ps = []
+ @rules.each do |r|
+ ps = ps.concat(r.ps)
+ end
+ ps.flatten.uniq
end
end
class Gameday
attr_accessor :date, :games