rrschedule 0.1.3 → 0.1.4

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/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