rrschedule 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = rrschedule
2
2
 
3
- rrschedule makes it easier to generate Round-Robin schedules for sport leagues. To generate a schedule, it needs a team list, a season
3
+ rrschedule makes it easier to generate round-robin schedules for sport leagues. To generate a schedule, it needs a team list, a season
4
4
  start date, the day(s) of the week where the games are played and some other options.
5
5
 
6
6
  It takes into consideration physical constraints such as the number of playing surfaces availables and game times.
@@ -8,13 +8,13 @@ Each round of the round-robin is splitted into multiple gamedays that respect th
8
8
 
9
9
  Say for example that you want to generate a round-robin schedule for your 15-teams volleyball league.
10
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. rrschedule will put the remaining games of this round on the next gameday
11
+ impossible to complete one round in a single day. So rrschedule will put the remaining games of this round on the next gameday
12
12
  and will start a new round right after.
13
13
 
14
14
 
15
15
  == Installation
16
16
  gem install rrschedule
17
- require 'rrschedule.rb'
17
+ require 'rrschedule'
18
18
 
19
19
  == Prepare the schedule
20
20
  Time.zone = "America/New_York"
@@ -42,10 +42,10 @@ and will start a new round right after.
42
42
  Time.zone.parse("2010/12/29")
43
43
  ],
44
44
 
45
- #1 for Round Robin, 2 for Double Round Robin and so on
45
+ #1 for Round Robin, 2 for Double Round Robin and so on. Default is 1
46
46
  :cycles => 1,
47
47
 
48
- #Shuffle team order before each cycle
48
+ #Shuffle team order before each cycle. Default is true
49
49
  :shuffle_initial_order => true,
50
50
 
51
51
  #Times of the day where the games are played
@@ -95,9 +95,13 @@ and will start a new round right after.
95
95
  puts "\n"
96
96
  end
97
97
 
98
- == Other
98
+ == Issues / Other
99
+
100
+ Playing surfaces and game times need to be distributed evenly among all competitors. At the moment
101
+ the same teams will play on the same surfaces and at the same game times most of the time.
102
+ Note that it only happens when a full round can be completed on a single gameday. Otherwise there will
103
+ be a natural rotation and teams will play on different surfaces and at different times between gamedays.
99
104
 
100
105
  Hope this gem will be useful to some people!
101
- As you can see, there are no tests yet but they should come quite soon.
102
106
 
103
107
  You can read my blog here: www.rubyfleebie.com
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ begin
11
11
  gem.homepage = "http://github.com/flamontagne/rrschedule"
12
12
  gem.authors = ["flamontagne"]
13
13
  gem.add_dependency 'activesupport'
14
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
15
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
16
  end
16
17
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
data/lib/rrschedule.rb CHANGED
@@ -8,14 +8,26 @@ module RRSchedule
8
8
  attr_reader :teams, :rounds
9
9
 
10
10
  def initialize(params={})
11
- self.teams = params[:teams] || [1,2,3,4,5]
12
- self.playing_surfaces = params[:playing_surfaces] || ["--"]
11
+ self.teams = params[:teams] || [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
12
+ self.playing_surfaces = Array(params[:playing_surfaces]).empty? ? ["Surface A", "Surface B"] : Array(params[:playing_surfaces])
13
13
  self.cycles = params[:cycles] || 1
14
- self.game_times = params[:game_times] || ["Game time not specified"]
14
+
15
+ self.game_times = Array(params[:game_times]).empty? ? ["7:00 PM", "9:00 PM"] : Array(params[:game_times])
16
+ self.game_times.collect! do |gt|
17
+ begin
18
+ DateTime.parse(gt)
19
+ rescue
20
+ raise "game times must be valid time representations in the string form (e.g. 3:00 PM, 11:00 AM, 18:20, etc)"
21
+ end
22
+ end
23
+
15
24
  self.shuffle_initial_order = params[:shuffle_initial_order] || true
16
25
  self.exclude_dates = params[:exclude_dates] || []
17
26
  self.start_date = params[:start_date] || Time.now.beginning_of_day
18
27
  self.wdays = Array(params[:wdays]).empty? ? [1] : Array(params[:wdays])
28
+
29
+ raise "each value in wdays must be between 0 and 6" if self.wdays.reject{|w| (0..6).member?(w)}.size > 0
30
+ self
19
31
  end
20
32
 
21
33
 
@@ -63,8 +75,9 @@ module RRSchedule
63
75
  end
64
76
  end
65
77
  end until @teams == initial_order && current_cycle==self.cycles
66
- @teams.delete(:dummy)
67
- slice(all_games)
78
+ #@teams.delete(:dummy)
79
+ slice(all_games)
80
+ self
68
81
  end
69
82
 
70
83
 
@@ -83,7 +96,7 @@ module RRSchedule
83
96
  res << gd.strftime("%Y-%m-%d") + "\n"
84
97
  res << "==========\n"
85
98
  games.each do |g|
86
- res << "#{g.team_a.to_s} VS #{g.team_b.to_s} on playing surface #{g.playing_surface} at #{g.game_time}\n"
99
+ 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"
87
100
  end
88
101
  res << "\n"
89
102
  end
@@ -102,24 +115,40 @@ module RRSchedule
102
115
  gms.flatten
103
116
  end
104
117
 
118
+ #TODO: returns true if the generated schedule is a valid round-robin (for testing purpose)
119
+ def round_robin?
120
+
121
+ #each round-robin round should contains n-1 games where n is the nbr of teams (:dummy included if odd)
122
+ return false if self.rounds.size != (@teams.size*self.cycles)-self.cycles
123
+
124
+ #check if each team plays the same number of games against each other
125
+ self.teams.each do |t1|
126
+ self.teams.reject{|t| t == t1}.each do |t2|
127
+ return false unless self.face_to_face(t1,t2).size == self.cycles || [t1,t2].include?(:dummy)
128
+ end
129
+ end
130
+ return true
131
+ end
132
+
105
133
  private
106
134
 
107
135
  def teams=(arr)
108
136
  @teams = arr.clone
109
- @teams << :dummy if arr.size.odd?
137
+ raise ":dummy is a reserved team name. Please use something else" if @teams.member?(:dummy)
138
+ raise "at least 2 teams are required" if @teams.size == 1
139
+ @teams << :dummy if @teams.size.odd?
110
140
  end
111
141
 
112
142
  #Let's slice our games according to our physical constraints
113
143
  def slice(games)
114
144
  res={}
115
145
  slices = games.each_slice(games_per_day)
116
- wdays_stack = self.wdays.clone
117
-
146
+ wdays_stack = self.wdays.clone
118
147
  cur_date = self.start_date
119
148
  slices.each_with_index do |slice,i|
120
149
  gt_stack = self.game_times.clone
121
150
  ps_stack = self.playing_surfaces.clone
122
- wdays_stack=self.wdays.clone if wdays_stack.empty?
151
+ wdays_stack = self.wdays.clone if wdays_stack.empty?
123
152
 
124
153
  cur_wday = wdays_stack.shift
125
154
  cur_date = next_game_date(cur_date,cur_wday)
@@ -138,6 +167,8 @@ module RRSchedule
138
167
  gt_stack = self.game_times.clone if gt_stack.empty?
139
168
  ps_stack = self.playing_surfaces.clone if ps_stack.empty?
140
169
  end
170
+
171
+ res[cur_date] = res[cur_date].sort_by {|g| [g.game_time,g.playing_surface]}
141
172
  cur_date += 1.day
142
173
  end
143
174
  @schedule = res
@@ -1,7 +1,123 @@
1
1
  require 'helper'
2
2
 
3
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"
4
+ context "A Schedule instance" do
5
+ should "have default values for every options" do
6
+ schedule = RRSchedule::Schedule.new
7
+
8
+ assert schedule.teams.size > 2
9
+ assert_equal 1, schedule.cycles
10
+ assert schedule.game_times.respond_to?(:to_ary)
11
+ assert schedule.playing_surfaces.respond_to?(:to_ary)
12
+ assert schedule.start_date.respond_to?(:to_date)
13
+ assert schedule.shuffle_initial_order
14
+ assert schedule.wdays.select{|w| (0..6).member? w} == schedule.wdays
15
+ assert schedule.exclude_dates.empty?
16
+ end
17
+
18
+ should "have a dummy team when team number is odd" do
19
+ schedule = RRSchedule::Schedule.new(
20
+ :teams => Array(1..9)
21
+ )
22
+
23
+ assert schedule.teams.size == 10
24
+ assert schedule.teams.member?(:dummy), "There should always be a :dummy team when the nbr of teams is odd"
25
+ end
26
+
27
+ should "not have a dummy team when team number is even" do
28
+ schedule = RRSchedule::Schedule.new(
29
+ :teams => Array(1..6)
30
+ )
31
+
32
+ assert schedule.teams.size == 6
33
+ assert !schedule.teams.member?(:dummy), "There should never be a :dummy team when the nbr of teams is even"
34
+ end
35
+
36
+ should "not have a team named :dummy in the initial array" do
37
+ assert_raise RuntimeError do
38
+ schedule = RRSchedule::Schedule.new(
39
+ :teams => Array(1..4) << :dummy
40
+ )
41
+ end
42
+ end
43
+
44
+ should "not have game times that cannot convert to valid DateTime objects" do
45
+ assert_raise RuntimeError do
46
+ schedule = RRSchedule::Schedule.new(
47
+ :teams => Array(1..4),
48
+ :game_times => ["10:00 AM", "13:00", "bonjour"]
49
+ )
50
+ end
51
+ end
52
+
53
+ should "not have wdays that are not between 0 and 6" do
54
+ assert_raise RuntimeError do
55
+ schedule = RRSchedule::Schedule.new(
56
+ :wdays => [2,7]
57
+ )
58
+ end
59
+ end
60
+
61
+ should "automatically convert game times and playing surface to arrays" do
62
+ schedule = RRSchedule::Schedule.new(
63
+ :teams => Array(1..4),
64
+ :game_times => "10:00 AM",
65
+ :playing_surfaces => "the only one"
66
+ )
67
+
68
+ assert_equal [DateTime.parse("10:00 AM")], schedule.game_times
69
+ assert_equal ["the only one"], schedule.playing_surfaces
70
+ end
71
+
72
+ should "have at least one team specified" do
73
+ assert_raise RuntimeError do
74
+ schedule = RRSchedule::Schedule.new(:teams => [1])
75
+ end
76
+ end
77
+
78
+ should "have default teams if non was specified" do
79
+ schedule = RRSchedule::Schedule.new
80
+ assert schedule.teams.size > 1
81
+ end
6
82
  end
83
+
84
+ context "A generated schedule with an odd number of teams" do
85
+ setup do
86
+ @s = RRSchedule::Schedule.new(
87
+ :teams => %w(a b c d e f g h i j l),
88
+ :playing_surfaces => %w(one two),
89
+ :game_times => ["10:00 AM", "13:00 PM"]
90
+ ).generate
91
+ end
92
+
93
+ should "be a valid round-robin" do
94
+ assert @s.round_robin?
95
+ end
96
+
97
+ should "not have any :dummy teams in the schedule" do
98
+ assert @s.gamedays.collect{|gd,games| games}.flatten.select{
99
+ |g| [g.team_a,g.team_b].include?(:dummy)
100
+ }.size == 0
101
+ end
102
+ end
103
+
104
+ context "A generated schedule with an even number of teams" do
105
+ setup do
106
+ @s = RRSchedule::Schedule.new(
107
+ :teams => %w(a b c d e f g h i j l m),
108
+ :playing_surfaces => %w(one two),
109
+ :game_times => ["10:00 AM", "13:00 PM"]
110
+ ).generate
111
+ end
112
+
113
+ should "be a valid round-robin" do
114
+ assert @s.round_robin?
115
+ end
116
+
117
+ should "not have any :dummy teams in the schedule" do
118
+ assert @s.gamedays.collect{|gd,games| games}.flatten.select{
119
+ |g| [g.team_a,g.team_b].include?(:dummy)
120
+ }.size == 0
121
+ end
122
+ end
7
123
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rrschedule
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 3
10
- version: 0.1.3
9
+ - 4
10
+ version: 0.1.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - flamontagne
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-08 00:00:00 -05:00
18
+ date: 2010-11-09 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -32,6 +32,20 @@ dependencies:
32
32
  version: "0"
33
33
  type: :runtime
34
34
  version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: thoughtbot-shoulda
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
35
49
  description: This gem automate the process of creating a round-robin sport schedule.
36
50
  email: flamontagne@azanka.ca
37
51
  executables: []
@@ -86,5 +100,5 @@ signing_key:
86
100
  specification_version: 3
87
101
  summary: Round-Robin schedule generator
88
102
  test_files:
89
- - test/test_rrschedule.rb
90
103
  - test/helper.rb
104
+ - test/test_rrschedule.rb