rrschedule 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 flamontagne
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,100 @@
1
+ = rrschedule
2
+
3
+ RRSchedule make it easier to generate Round-Robin sport seasons. To generate a schedule, it needs a team list, a season
4
+ start date, the day(s) of the week where the games are played and some other options.
5
+
6
+ It takes into consideration physical constraints such as the number of playing surfaces availables and game times.
7
+ Each round of the round-robin is splitted into groups that respect these constraints.
8
+
9
+ Say for example that you want to generate a round-robin schedule for your 15-teams volleyball league.
10
+ If there are only 3 volleyball fields available and that games are played each monday at 6PM and 8PM, this is technically
11
+ impossible to complete one round in a single day (only 6 games can be played). RRSchedule will put the rest of the games
12
+ for this round on the next gameday and will start a new round right after.
13
+
14
+
15
+ == Installation
16
+ gem install rrschedule (not on gemcutter yet... hold on)
17
+ require 'rrschedule.rb'
18
+
19
+ == Prepare the schedule
20
+ Time.zone = "America/New_York"
21
+ teams = ["Rockets","Jetpacks","Snakes","Cobras","Wolves","Huskies","Tigers","Lions",
22
+ "Moose","Sprinklers","Pacers","Cyclops","Munchkins","Magicians","French Fries"]
23
+
24
+ schedule=RRSchedule::Schedule.new(
25
+ #array of teams that will compete against each other in the season
26
+ :teams => teams,
27
+
28
+ #list of available playing surfaces (volleyball fields, curling sheets, tennis courts, etc)
29
+ :playing_surfaces => ["A","B","C","D"],
30
+
31
+ #day(s) of the week where games are played
32
+ :wdays => [3],
33
+
34
+ #Season will start on...
35
+ :start_date => Time.zone.parse("2010/10/13"),
36
+
37
+ #array of dates WITHOUT games
38
+ :exclude_dates => [
39
+ Time.zone.parse("2010/11/24"),
40
+ Time.zone.parse("2010/12/15"),
41
+ Time.zone.parse("2010/12/22"),
42
+ Time.zone.parse("2010/12/29")
43
+ ],
44
+
45
+ #1 for Round Robin, 2 for Double Round Robin and so on
46
+ :cycles => 1,
47
+
48
+ #Shuffle team order before each cycle
49
+ :shuffle_initial_order => true,
50
+
51
+ #Times of the day where the games are played
52
+ :game_times => ["10:00 AM", "1:00 PM"]
53
+ )
54
+
55
+ == Generate the schedule
56
+ schedule.generate
57
+
58
+ == Playing with the output
59
+
60
+ === human readable schedule
61
+ puts schedule.to_s
62
+
63
+ === Round by round... without the schedule info
64
+ #If you have an ODD number of teams you will see a "dummy" opponent in each round
65
+ schedule.rounds.each do |round|
66
+ puts "Round ##{round.round}"
67
+ round.games.each do |g|
68
+ puts g.team_a.to_s + " Vs " + g.team_b.to_s
69
+ end
70
+ puts "\n"
71
+ end
72
+
73
+ === Team schedule
74
+ test_team = "Sprinklers"
75
+ games=schedule.by_team(test_team)
76
+ puts "Schedule for team ##{test_team.to_s}"
77
+ games.each do |g|
78
+ puts "#{g.game_date.strftime("%Y-%m-%d")}: against #{g.team_a == test_team ? g.team_b.to_s : g.team_a.to_s} on playing surface ##{g.playing_surface} at #{g.game_time}"
79
+ end
80
+
81
+ === Face to Face
82
+ games=schedule.face_to_face("Lions","Moose")
83
+ puts "FACE TO FACE: Lions Vs Moose"
84
+ games.each do |g|
85
+ puts g.game_date.to_s + " on playing surface " + g.playing_surface.to_s + " at " + g.game_time.to_s
86
+ end
87
+
88
+ === Iterate the schedule
89
+ schedule.gamedays.each do |gd,games|
90
+ puts gd
91
+ puts "===================="
92
+ games.each do |g|
93
+ puts g.team_a.to_s + " Vs " + g.team_b.to_s + " on playing surface ##{g.playing_surface} at #{g.game_time}"
94
+ end
95
+ puts "\n"
96
+ end
97
+
98
+ == Copyright
99
+
100
+ Copyright (c) 2010 flamontagne. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rrschedule"
8
+ gem.summary = %Q{Round-Robin schedule generator}
9
+ gem.description = %Q{This gem automate the process of creating a round-robin sport schedule.}
10
+ gem.email = "flamontagne@azanka.ca"
11
+ gem.homepage = "http://github.com/flamontagne/rrschedule"
12
+ gem.authors = ["flamontagne"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "rrschedule #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/example.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'rrschedule.rb'
2
+ Time.zone = "America/New_York"
3
+
4
+ teams = ["Rockets","Jetpacks","Snakes","Cobras","Wolves","Huskies","Tigers","Lions","Moose","Sprinklers","Pacers","Cyclops","Munchkins","Magicians","French Fries"]
5
+ schedule=RRSchedule::Schedule.new(
6
+ :teams => teams, #array of teams that will compete against each other in the season (can be any kind of object)
7
+ :playing_surfaces => ["A","B","C","D"], #list of available playing surfaces (volleyball fields, curling sheets, tennis courts, etc)
8
+ :wdays => [3], #day(s) of the week where games are played
9
+ :start_date => Time.zone.parse("2010/10/13"), #Season will start on...
10
+ :exclude_dates => [ #array of dates WITHOUT games
11
+ Time.zone.parse("2010/11/24"),
12
+ Time.zone.parse("2010/12/15"),
13
+ Time.zone.parse("2010/12/22"),
14
+ Time.zone.parse("2010/12/29")
15
+ ],
16
+ :cycles => 1, #1 for Round Robin, 2 for Double Round Robin and so on
17
+ :shuffle_initial_order => true, #Shuffle team order before each cycle
18
+ :game_times => ["10:00 AM", "1:00 PM"] #Times of the day where the games are played
19
+ )
20
+ res=schedule.generate
21
+
22
+ #human readable schedule
23
+ puts schedule.to_s
24
+
25
+ schedule.rounds.each do |round|
26
+ puts "Round ##{round.round}"
27
+ round.games.each do |g|
28
+ puts g.team_a.to_s + " Vs " + g.team_b.to_s
29
+ end
30
+ puts "\n"
31
+ end
32
+
33
+ #display a team schedule
34
+ #test_team = "Sprinklers"
35
+ #games=schedule.by_team(test_team)
36
+ #puts "Schedule for team ##{test_team.to_s}"
37
+ #games.each do |g|
38
+ # puts "#{g.game_date.strftime("%Y-%m-%d")}: against #{g.team_a == test_team ? g.team_b.to_s : g.team_a.to_s} on playing surface ##{g.playing_surface} at #{g.game_time}"
39
+ #end
40
+
41
+ #face_to_face
42
+ #games=schedule.face_to_face("Lions","Moose")
43
+ #puts "FACE TO FACE: Lions Vs Moose"
44
+ #games.each do |g|
45
+ # puts g.game_date.to_s + " on playing surface " + g.playing_surface.to_s + " at " + g.game_time.to_s
46
+ #end
47
+
48
+ #How to iterate the schedule
49
+ #schedule.gamedays.each do |gd,games|
50
+ # puts gd
51
+ # puts "===================="
52
+ # games.each do |g|
53
+ # puts g.team_a.to_s + " Vs " + g.team_b.to_s + " on playing surface ##{g.playing_surface} at #{g.game_time}"
54
+ # end
55
+ # puts "\n"
56
+ #end
data/lib/rrschedule.rb ADDED
@@ -0,0 +1,176 @@
1
+ # rrschedule (Round Robin Schedule generator)
2
+ # Auhtor: François Lamontagne
3
+ ############################################################################################################################
4
+ require 'rubygems'
5
+ require 'active_support'
6
+
7
+ module RRSchedule
8
+ class Schedule
9
+ attr_accessor :playing_surfaces, :game_times, :cycles, :wdays, :start_date, :exclude_dates, :shuffle_initial_order
10
+ attr_reader :teams, :rounds
11
+
12
+ def initialize(params={})
13
+ store_params(params)
14
+ end
15
+
16
+
17
+ def generate(params={})
18
+ @teams = @teams.sort_by{rand} if self.shuffle_initial_order
19
+ initial_order = @teams.clone
20
+ current_cycle = 0
21
+ current_round = 0
22
+ all_games = []
23
+
24
+ #Loop start here
25
+ begin
26
+ games = []
27
+ t = @teams.clone
28
+ while !t.empty? do
29
+ team_a = t.shift
30
+ team_b = t.reverse!.shift
31
+ t.reverse!
32
+ games << {:team_a => team_a, :team_b => team_b}
33
+ all_games << {:team_a => team_a, :team_b => team_b}
34
+ end
35
+ #round completed
36
+ current_round += 1
37
+ @rounds ||= []
38
+ @rounds << Round.new(:round => current_round, :games => games.collect {|g| Game.new(:team_a => g[:team_a], :team_b => g[:team_b])})
39
+
40
+ games.reject! {|g| g[:team_a] == :dummy || g[:team_b] == :dummy}
41
+ all_games.reject! {|g| g[:team_a] == :dummy || g[:team_b] == :dummy}
42
+
43
+ @teams = @teams.insert(1,@teams.delete_at(@teams.size-1))
44
+
45
+ #If we have completed a cycle
46
+ if @teams == initial_order
47
+ current_cycle += 1
48
+ #Shuffle the teams at each cycle
49
+ if current_cycle <= self.cycles && self.shuffle_initial_order
50
+ @teams = @teams.sort_by{rand}
51
+ initial_order = @teams.clone
52
+ end
53
+ end
54
+ end until @teams == initial_order && current_cycle==self.cycles
55
+ @teams.delete(:dummy)
56
+ slice(all_games)
57
+ end
58
+
59
+
60
+ def face_to_face(team_a,team_b)
61
+ res=[]
62
+ self.gamedays.each do |gd,games|
63
+ res << games.select{|g| (g.team_a == team_a && g.team_b == team_b) || (g.team_a == team_b && g.team_b == team_a)}
64
+ end
65
+ res.flatten
66
+ end
67
+
68
+ def to_s
69
+ res = ""
70
+ res << "#{@schedule.keys.size.to_s} gamedays\n"
71
+ @schedule.sort.each do |gd,games|
72
+ gd_proc = lambda {gd.strftime("%Y-%m-%d")}
73
+ res << gd_proc.call + "\n"
74
+ res << "=" * gd_proc.call.length + "\n"
75
+ games.each do |g|
76
+ res << g.team_a.to_s + " VS " + g.team_b.to_s + " on playing surface #{g.playing_surface} at #{g.game_time}\n"
77
+ end
78
+ res << "\n"
79
+ end
80
+ res
81
+ end
82
+
83
+ def gamedays
84
+ @schedule.sort
85
+ end
86
+
87
+ def by_team(team)
88
+ gms=[]
89
+ self.gamedays.each do |gd,games|
90
+ #games = games.each {|g| g.game_date = gd}
91
+ gms << games.select{|g| g.team_a == team || g.team_b == team}
92
+ end
93
+ gms.flatten
94
+ end
95
+
96
+ private
97
+
98
+ def teams=(arr)
99
+ @teams = arr.clone
100
+ @teams << :dummy if arr.size.odd?
101
+ end
102
+
103
+ #Let's slice our games according to our physical constraints
104
+ def slice(games)
105
+ res={}
106
+ slices = games.each_slice(games_per_day)
107
+ wdays_stack = self.wdays.clone
108
+
109
+ cur_date = self.start_date
110
+ slices.each_with_index do |slice,i|
111
+ gt_stack = self.game_times.clone
112
+ ps_stack = self.playing_surfaces.clone
113
+ wdays_stack=self.wdays.clone if wdays_stack.empty?
114
+
115
+ cur_wday = wdays_stack.shift
116
+ cur_date = next_game_date(cur_date,cur_wday)
117
+ cur_gt = gt_stack.shift
118
+
119
+ res[cur_date] = []
120
+ slice.each_with_index do |g,game_index|
121
+ cur_ps = ps_stack.shift
122
+ res[cur_date] << Game.new(:team_a => g[:team_a], :team_b => g[:team_b], :playing_surface => cur_ps, :game_time => cur_gt, :game_date => cur_date)
123
+ cur_gt = gt_stack.shift if ps_stack.empty?
124
+ gt_stack = self.game_times.clone if gt_stack.empty?
125
+ ps_stack = self.playing_surfaces.clone if ps_stack.empty?
126
+ end
127
+ cur_date += 1.day
128
+ end
129
+ @schedule = res
130
+ end
131
+
132
+ def next_game_date(dt,wday)
133
+ dt += 1.days until wday == dt.wday && !self.exclude_dates.include?(dt)
134
+ dt
135
+ end
136
+
137
+ def games_per_day
138
+ self.playing_surfaces.size * self.game_times.size
139
+ end
140
+
141
+ def store_params(params)
142
+ self.teams = params[:teams] if params[:teams].respond_to?(:to_ary)
143
+ self.playing_surfaces = params[:playing_surfaces] if params[:playing_surfaces].respond_to?(:to_ary)
144
+ self.cycles = params[:cycles] if params[:cycles].respond_to?(:to_int)
145
+ self.game_times = params[:game_times] if params[:game_times].respond_to?(:to_ary)
146
+ self.shuffle_initial_order = params[:shuffle_initial_order]
147
+ self.exclude_dates = params[:exclude_dates] || []
148
+ self.start_date = params[:start_date] || Time.now.beginning_of_day
149
+ self.wdays = Array(params[:wdays]) if params[:wdays].respond_to?(:to_ary) || params[:wdays].respond_to?(:to_int)
150
+ end
151
+
152
+
153
+ end
154
+
155
+ class Game
156
+ attr_accessor :team_a, :team_b, :playing_surface, :game_time, :game_date
157
+
158
+ def initialize(params={})
159
+ self.team_a = params[:team_a]
160
+ self.team_b = params[:team_b]
161
+ self.playing_surface = params[:playing_surface]
162
+ self.game_time = params[:game_time]
163
+ self.game_date = params[:game_date]
164
+ end
165
+ end
166
+
167
+ class Round
168
+ attr_accessor :round, :games
169
+
170
+ def initialize(params={})
171
+ self.round = params[:round]
172
+ self.games = params[:games]
173
+ end
174
+ end
175
+ end
176
+
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rrschedule}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["flamontagne"]
12
+ s.date = %q{2010-11-07}
13
+ s.description = %q{This gem automate the process of creating a round-robin sport schedule.}
14
+ s.email = %q{flamontagne@azanka.ca}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/example.rb",
27
+ "lib/rrschedule.rb",
28
+ "rrschedule.gemspec",
29
+ "test/helper.rb",
30
+ "test/test_rrschedule.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/flamontagne/rrschedule}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{Round-Robin schedule generator}
37
+ s.test_files = [
38
+ "test/test_rrschedule.rb",
39
+ "test/helper.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
53
+ end
54
+ end
55
+
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'rrschedule'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestRrschedule < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rrschedule
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - flamontagne
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-07 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thoughtbot-shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: This gem automate the process of creating a round-robin sport schedule.
36
+ email: flamontagne@azanka.ca
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - lib/example.rb
52
+ - lib/rrschedule.rb
53
+ - rrschedule.gemspec
54
+ - test/helper.rb
55
+ - test/test_rrschedule.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/flamontagne/rrschedule
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.3.7
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Round-Robin schedule generator
90
+ test_files:
91
+ - test/test_rrschedule.rb
92
+ - test/helper.rb