lib/rrschedule.rb in rrschedule-0.1.3 vs lib/rrschedule.rb in rrschedule-0.1.4

- old
+ new

@@ -6,18 +6,30 @@ class Schedule attr_accessor :playing_surfaces, :game_times, :cycles, :wdays, :start_date, :exclude_dates, :shuffle_initial_order attr_reader :teams, :rounds def initialize(params={}) - self.teams = params[:teams] || [1,2,3,4,5] - self.playing_surfaces = params[:playing_surfaces] || ["--"] + self.teams = params[:teams] || [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] + self.playing_surfaces = Array(params[:playing_surfaces]).empty? ? ["Surface A", "Surface B"] : Array(params[:playing_surfaces]) self.cycles = params[:cycles] || 1 - self.game_times = params[:game_times] || ["Game time not specified"] + + self.game_times = Array(params[:game_times]).empty? ? ["7:00 PM", "9:00 PM"] : Array(params[:game_times]) + self.game_times.collect! do |gt| + begin + DateTime.parse(gt) + rescue + raise "game times must be valid time representations in the string form (e.g. 3:00 PM, 11:00 AM, 18:20, etc)" + end + end + self.shuffle_initial_order = params[:shuffle_initial_order] || true self.exclude_dates = params[:exclude_dates] || [] self.start_date = params[:start_date] || Time.now.beginning_of_day self.wdays = Array(params[:wdays]).empty? ? [1] : Array(params[:wdays]) + + raise "each value in wdays must be between 0 and 6" if self.wdays.reject{|w| (0..6).member?(w)}.size > 0 + self end def generate(params={}) @teams = @teams.sort_by{rand} if self.shuffle_initial_order @@ -61,12 +73,13 @@ @teams = @teams.sort_by{rand} initial_order = @teams.clone end end end until @teams == initial_order && current_cycle==self.cycles - @teams.delete(:dummy) - slice(all_games) + #@teams.delete(:dummy) + slice(all_games) + self end def face_to_face(team_a,team_b) res=[] @@ -81,11 +94,11 @@ res << "#{@schedule.keys.size.to_s} gamedays\n" @schedule.sort.each do |gd,games| res << gd.strftime("%Y-%m-%d") + "\n" res << "==========\n" games.each do |g| - res << "#{g.team_a.to_s} VS #{g.team_b.to_s} on playing surface #{g.playing_surface} at #{g.game_time}\n" + res << "#{g.team_a.to_s} VS #{g.team_b.to_s} on playing surface #{g.playing_surface} at #{g.game_time.strftime("%I:%M %p")}\n" end res << "\n" end res end @@ -100,28 +113,44 @@ gms << games.select{|g| g.team_a == team || g.team_b == team} end gms.flatten end + #TODO: returns true if the generated schedule is a valid round-robin (for testing purpose) + def round_robin? + + #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.size != (@teams.size*self.cycles)-self.cycles + + #check if each team plays the same number of games against each other + self.teams.each do |t1| + self.teams.reject{|t| t == t1}.each do |t2| + return false unless self.face_to_face(t1,t2).size == self.cycles || [t1,t2].include?(:dummy) + end + end + return true + end + private def teams=(arr) @teams = arr.clone - @teams << :dummy if arr.size.odd? + raise ":dummy is a reserved team name. Please use something else" if @teams.member?(:dummy) + raise "at least 2 teams are required" if @teams.size == 1 + @teams << :dummy if @teams.size.odd? end #Let's slice our games according to our physical constraints def slice(games) res={} slices = games.each_slice(games_per_day) - wdays_stack = self.wdays.clone - + wdays_stack = self.wdays.clone cur_date = self.start_date slices.each_with_index do |slice,i| gt_stack = self.game_times.clone ps_stack = self.playing_surfaces.clone - wdays_stack=self.wdays.clone if wdays_stack.empty? + wdays_stack = self.wdays.clone if wdays_stack.empty? cur_wday = wdays_stack.shift cur_date = next_game_date(cur_date,cur_wday) cur_gt = gt_stack.shift @@ -136,9 +165,11 @@ :game_date => cur_date) cur_gt = gt_stack.shift if ps_stack.empty? gt_stack = self.game_times.clone if gt_stack.empty? ps_stack = self.playing_surfaces.clone if ps_stack.empty? end + + res[cur_date] = res[cur_date].sort_by {|g| [g.game_time,g.playing_surface]} cur_date += 1.day end @schedule = res end