skating-system 0.1.1
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/.gitignore +21 -0
- data/LICENSE +22 -0
- data/README +200 -0
- data/README.rdoc +17 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/skating_system.rb +6 -0
- data/lib/skating_system/performance_results.rb +114 -0
- data/lib/skating_system/ranking.rb +34 -0
- data/lib/skating_system/scorer.rb +14 -0
- data/spec/spec.opts +6 -0
- data/spec/spec/performance_results_spec.rb +107 -0
- data/spec/spec/ranking_spec.rb +61 -0
- data/spec/spec/scorer_spec.rb +31 -0
- data/spec/spec_helper.rb +11 -0
- data/stories/all.rb +5 -0
- data/stories/final_performance/final_performance +69 -0
- data/stories/final_performance/stories.rb +7 -0
- data/stories/helper.rb +7 -0
- data/stories/resources/steps/scrutineering_a_final_performance.rb +43 -0
- metadata +99 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2008 Laurie Young
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
Skating System
|
2
|
+
--------------
|
3
|
+
|
4
|
+
Provides an implementation of the skating system used for scoring dance comeptitions, as well as a few other types of competition.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
------
|
8
|
+
|
9
|
+
First create a hash of the judges marks
|
10
|
+
@couples_marks = { @entree1=>{@judge1=>1, @judge2=>1, @judge3=>3 },
|
11
|
+
@entree2=>{@judge1=>2, @judge2=>3, @judge3=>2 },
|
12
|
+
@entree3=>{@judge1=>3, @judge2=>2, @judge3=>1 }}
|
13
|
+
|
14
|
+
|
15
|
+
Create a new PerformanceResults object, and then call its score method
|
16
|
+
@performance_resuts = SkatingSystem::PerformanceResults.new
|
17
|
+
@performance_resuts.score(@couples_marks)
|
18
|
+
Then you can access the ranking for the couples via
|
19
|
+
@performance_resuts.ranking.should be(@ranking)
|
20
|
+
|
21
|
+
|
22
|
+
The skating system is as follows:
|
23
|
+
|
24
|
+
|
25
|
+
********* The Marking of Adjudicators' Cards
|
26
|
+
1.
|
27
|
+
In all rounds each judge must vote for the number of couples
|
28
|
+
demanded by the Chairman of Adjudicators.
|
29
|
+
|
30
|
+
2.
|
31
|
+
In the Final round each judge shall place all the competing
|
32
|
+
couples in order of merit in each of the dances.
|
33
|
+
|
34
|
+
3.
|
35
|
+
In the Final round the judge shall mark his first couple 1, his
|
36
|
+
second couple 2, his third 3, and so on in each of the dances.
|
37
|
+
|
38
|
+
4.
|
39
|
+
A judge must not tie couples for any place in the Final of any dance.
|
40
|
+
|
41
|
+
Note:
|
42
|
+
|
43
|
+
Number of Couples to Dance in a Final
|
44
|
+
|
45
|
+
1.
|
46
|
+
In the Final round the open system of marking shall be used.
|
47
|
+
|
48
|
+
2.
|
49
|
+
When judges are instructed to select six couples for a
|
50
|
+
Final and six couples are clearly chosen only that number
|
51
|
+
shall dance. The same procedure would be observed if the
|
52
|
+
Chairman's instructions were for any other number.
|
53
|
+
|
54
|
+
3.
|
55
|
+
If it is intended that six couples shall dance in a Final
|
56
|
+
and through a tie more couples qualify for consideration
|
57
|
+
the number to dance shall be decided by the Chairman. The
|
58
|
+
same procedure would be observed if it is intended to have
|
59
|
+
a Final of any other number.
|
60
|
+
|
61
|
+
********* The Allocation of Positions in Each Dance
|
62
|
+
5.
|
63
|
+
The winner of a particular dance is the couple who is placed
|
64
|
+
first by an absolute majority of judges; second, the couple who
|
65
|
+
is placed second or higher by an absolute majority. The
|
66
|
+
remaining positions are allocated in a similar way.
|
67
|
+
|
68
|
+
If More that One Couple have a Majority for the Same Position
|
69
|
+
|
70
|
+
6.
|
71
|
+
The couple with the largest majority shall be allocated the
|
72
|
+
position under review, and the couple with the next largest
|
73
|
+
majority, the following position.
|
74
|
+
|
75
|
+
Note:
|
76
|
+
|
77
|
+
If the position under review is the "2nd" and two couples have a
|
78
|
+
majority of "2nd and higher" places, the couple with the larger
|
79
|
+
majority shall be placed "2nd" and the other couple "3rd".
|
80
|
+
|
81
|
+
We now examine the remaining competitors' markings, and the
|
82
|
+
couple with the largest majority of "3rd and higher" places
|
83
|
+
shall be allocated the next position, which in this example, is
|
84
|
+
the "4th".
|
85
|
+
|
86
|
+
If none of the remaining couples has a majority of "3rd and
|
87
|
+
higher" places, then include the "4th" places (and, if
|
88
|
+
necessary, lower places).
|
89
|
+
|
90
|
+
********* If Two or More Couples have an Equal Majority for the Same Position
|
91
|
+
7.
|
92
|
+
a) If such majorities are equal, then the lowest total of marks
|
93
|
+
given by those judges who form the majority, shall determine the
|
94
|
+
allocation of the position under review.
|
95
|
+
|
96
|
+
Note:
|
97
|
+
|
98
|
+
If the position under review is the "2nd" and two couples have a
|
99
|
+
similar majority of "2nd and higher" places, the couple with the
|
100
|
+
lower total of marks given by those judges who form the
|
101
|
+
majority, shall be allocated the "2nd" position and the other
|
102
|
+
couple the "3rd".
|
103
|
+
|
104
|
+
b) If the totals of the marks are equal, then the next lower
|
105
|
+
place (or places, if necessary), in respect of the particular
|
106
|
+
couples concerned, must be included.
|
107
|
+
|
108
|
+
Note:
|
109
|
+
|
110
|
+
It should be noted that only the couples who have a majority for
|
111
|
+
the position under review (say, for example, the "2nd" position)
|
112
|
+
must be considered at this stage, and only their "3rd" places
|
113
|
+
(and, if necessary, lower places) should be referred to, until
|
114
|
+
the "2nd" position has been allocated.
|
115
|
+
|
116
|
+
A definite result will eventually be obtained unless the
|
117
|
+
remaining markings are exactly the same, and should the latter
|
118
|
+
be the case, there will, of course, be a tie for "2nd"
|
119
|
+
position. If two couples were concerned, they would be allocated
|
120
|
+
"2 1/2" each.
|
121
|
+
|
122
|
+
|
123
|
+
********* If No Couple receives a Majority for the Position Under Review
|
124
|
+
8.
|
125
|
+
If no couple receives a majority of "Firsts" then the winner is
|
126
|
+
the couple who are placed "2nd and higher" by a majority of
|
127
|
+
judges.
|
128
|
+
|
129
|
+
If no couple receives a majority of "1st" and "2nd" places, then
|
130
|
+
the "3rd" places (and if necessary, lower places) must be
|
131
|
+
included. (Subject to Rules 6 and 7).
|
132
|
+
|
133
|
+
The "2nd" and other places should be calculated in a similar
|
134
|
+
way.
|
135
|
+
|
136
|
+
********* Compilation of the Final Summary
|
137
|
+
9.
|
138
|
+
When all the dances have been concluded, the order ascertained
|
139
|
+
for each dance shall be carried to another sheet, showing the
|
140
|
+
position achieved by each couple in each dance. The first in
|
141
|
+
each dance shall be given one mark, the second two, and so
|
142
|
+
on. These place marks shall be added up and the couple with the
|
143
|
+
lowest aggregate shall be the winner.
|
144
|
+
|
145
|
+
|
146
|
+
********* If there is a Tie for a Place in the Final Summary
|
147
|
+
10.
|
148
|
+
a) If this results in a tie for first place, the winner shall be
|
149
|
+
the couple who has actually won the greater number of dances.
|
150
|
+
|
151
|
+
b) If there is a tie for the "2nd" place, the "2nd" prize shall
|
152
|
+
be awarded to the couple who has obtained "2nd and higher" in
|
153
|
+
the greatest number of dances. If the couples have obtained the
|
154
|
+
same number of "2nd and higher" place marks, then add the "2nd
|
155
|
+
and higher" place marks together and the couple with the lowest
|
156
|
+
total should be awarded second prize.
|
157
|
+
|
158
|
+
Note:
|
159
|
+
|
160
|
+
If more than two couples tie for second place, the second prize
|
161
|
+
shall be awarded to the couple who has obtained the most "2nd
|
162
|
+
and higher" place marks. Still only considering the remaining
|
163
|
+
"tied" couples, the "3rd" prize is awarded to the couple who has
|
164
|
+
won the most "3rd and higher" place marks.
|
165
|
+
|
166
|
+
c) If there is a tie for any remaining places they shall be
|
167
|
+
decided on similar principles.
|
168
|
+
|
169
|
+
11.
|
170
|
+
|
171
|
+
If after applying Rules 9 and 10 this still results in a tie,
|
172
|
+
then treat the judges' marks of the "tied" couples over all
|
173
|
+
dances, as for an individual dance (Rules 5 to 8). If this still
|
174
|
+
results in a tie, there shall be at the discretion of the
|
175
|
+
organizers of the competition, either a re-dance or the prizes
|
176
|
+
for the places under review shall be divided.
|
177
|
+
|
178
|
+
a) If the tie is for first place, a majority of "1st" marks to
|
179
|
+
the credit of either of the "tied" couples (4 dances-5
|
180
|
+
judges-majority 11) would win. If neither of the "tied" couples
|
181
|
+
receive a majority of "firsts" see Rule 8.
|
182
|
+
|
183
|
+
b) If the tie is for second place, a majority of "2nd and
|
184
|
+
higher" marks to the credit of either of the "tied" couples
|
185
|
+
would be necessary. If neither of the "tied" couples obtain a
|
186
|
+
majority of "2nd and higher" marks see Rule 8.
|
187
|
+
|
188
|
+
c) The "3rd" or any other "tied" places should be decided on similar principles.
|
189
|
+
|
190
|
+
d) If 3 (or more) couples tie for a place under Rule 10 (say,
|
191
|
+
2nd place) Rule 11 is applied to all couples concerned in the
|
192
|
+
tie, and the best couple is awarded the "2nd" place. Rule 10 is
|
193
|
+
now applied to the remaining "tied" couples for consideration of
|
194
|
+
the place now under review, which is the "3rd". However, if they
|
195
|
+
now tie for "3rd" under Rule 10, then Rule 11 is again applied
|
196
|
+
to these "tied" couples, commencing this time with the "3rd and
|
197
|
+
higher" judges's marks in the individual dances.
|
198
|
+
|
199
|
+
It is distributed under the MIT Licence, see licence.txt for the details
|
200
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= skating-system
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Wildfalcon. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "skating-system"
|
8
|
+
gem.summary = "Dancesport skating system implementation"
|
9
|
+
gem.description = %Q{Convert judges scores from a dance-competition into results}
|
10
|
+
gem.email = "laurie@wildfalcon.com"
|
11
|
+
gem.homepage = "http://github.com/wildfalcon/skating-system"
|
12
|
+
gem.authors = ["Wildfalcon"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
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 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "foobar #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module SkatingSystem
|
2
|
+
class PerformanceResults
|
3
|
+
|
4
|
+
def score(marks)
|
5
|
+
@marks = marks
|
6
|
+
@majority = (@marks[@marks.keys.first].size/2).ceil
|
7
|
+
|
8
|
+
tally_marks
|
9
|
+
|
10
|
+
compute_results
|
11
|
+
|
12
|
+
@marks.each do |entree, marks|
|
13
|
+
@marks[entree].merge!(@tally[entree])
|
14
|
+
@marks[entree].merge!(@results[entree])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
#Generate the ranking table for this performance
|
19
|
+
def ranking
|
20
|
+
return @ranking if @ranking
|
21
|
+
@ranking =SkatingSystem::Ranking.new
|
22
|
+
@marks.each do |entree, data|
|
23
|
+
@ranking.add(data[:result], entree)
|
24
|
+
end
|
25
|
+
@ranking
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](key)
|
29
|
+
@marks[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
private
|
35
|
+
def sum_marks(entree, position)
|
36
|
+
sum=0
|
37
|
+
@marks[entree].each do |judge, place|
|
38
|
+
sum += place if place <= position
|
39
|
+
end
|
40
|
+
sum
|
41
|
+
end
|
42
|
+
|
43
|
+
def compute_results
|
44
|
+
@results={ }
|
45
|
+
@marks.each do |entree, marks|
|
46
|
+
@results[entree] ||= { }
|
47
|
+
end
|
48
|
+
|
49
|
+
@next_result_to_compute = 1
|
50
|
+
|
51
|
+
1.upto(@marks.size) do |place|
|
52
|
+
|
53
|
+
|
54
|
+
sorted_candidate = candidates(place).sort do |entree1, entree2|
|
55
|
+
comparator = @tally[entree2][place] <=> @tally[entree1][place]
|
56
|
+
if comparator==0
|
57
|
+
comparator = sum_marks(entree1, place) <=> sum_marks(entree2, place)
|
58
|
+
end
|
59
|
+
comparator
|
60
|
+
end
|
61
|
+
|
62
|
+
sorted_candidate.each do |entree|
|
63
|
+
@results[entree][:result]=@next_result_to_compute
|
64
|
+
@next_result_to_compute += 1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#Computes an array of entrees that are candidates for this position
|
70
|
+
def candidates(place)
|
71
|
+
candidates = []
|
72
|
+
@tally.each do |entree, places|
|
73
|
+
unless @results[entree][:result]
|
74
|
+
candidates << entree if places[place] > @majority
|
75
|
+
end
|
76
|
+
end
|
77
|
+
candidates
|
78
|
+
end
|
79
|
+
|
80
|
+
#Tallys the number of judges who marked each entree in each place or heigher
|
81
|
+
# and writes the results to @tally
|
82
|
+
# eg given the results
|
83
|
+
# A B C
|
84
|
+
# 1 1 1 3
|
85
|
+
# 2 2 3 2
|
86
|
+
# 3 3 2 3
|
87
|
+
# it assigns the tally
|
88
|
+
# 1 2 3
|
89
|
+
# 1 2 2 3
|
90
|
+
# 2 0 2 3
|
91
|
+
# 3 0 1 3
|
92
|
+
def tally_marks
|
93
|
+
@tally={ }
|
94
|
+
@marks.each do |entree, marks|
|
95
|
+
@tally[entree] ||= { }
|
96
|
+
1.upto(@marks.size) do |place|
|
97
|
+
@tally[entree][place] = 0
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@marks.each do |entree, marks|
|
102
|
+
marks.each do |judge, placing|
|
103
|
+
placing.upto(@marks.size) do |place|
|
104
|
+
@tally[entree][place] += 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SkatingSystem
|
2
|
+
class Ranking
|
3
|
+
attr_accessor :rank_table
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@rank_table={ }
|
7
|
+
end
|
8
|
+
|
9
|
+
#Ads a new result to this ranking
|
10
|
+
def add(place, competitor)
|
11
|
+
raise "too many couples in higher placings" if places_higher_than(place) >= place
|
12
|
+
|
13
|
+
competitors = @rank_table[place] || []
|
14
|
+
competitors << competitor
|
15
|
+
@rank_table[place] = competitors
|
16
|
+
end
|
17
|
+
|
18
|
+
def place_for(competitor)
|
19
|
+
rank_table.each do |key, value|
|
20
|
+
return key if value.include?(competitor)
|
21
|
+
end
|
22
|
+
raise "Unkown competitor"
|
23
|
+
end
|
24
|
+
|
25
|
+
def places_higher_than(place)
|
26
|
+
higher_placings = 0
|
27
|
+
1.upto(place-1) do |p|
|
28
|
+
higher_placings += @rank_table[p] ? @rank_table[p].size : 0
|
29
|
+
end
|
30
|
+
return higher_placings
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SkatingSystem
|
2
|
+
class Scorer
|
3
|
+
def score(scorable)
|
4
|
+
score_performance(scorable) if scorable.respond_to?(:couples_marks)
|
5
|
+
end
|
6
|
+
|
7
|
+
def score_performance(performance)
|
8
|
+
results = PerformanceResults.new
|
9
|
+
results.score(performance.couples_marks)
|
10
|
+
results
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../spec_helper'
|
2
|
+
|
3
|
+
describe SkatingSystem::PerformanceResults, "tallying the marks" do
|
4
|
+
before(:all) do
|
5
|
+
@performance_resuts = SkatingSystem::PerformanceResults.new
|
6
|
+
|
7
|
+
@judge1 = mock("Judge")
|
8
|
+
@judge2 = mock("Judge")
|
9
|
+
@judge3 = mock("Judge")
|
10
|
+
|
11
|
+
@entree1 = mock("Entree")
|
12
|
+
@entree2 = mock("Entree")
|
13
|
+
@entree3 = mock("Entree")
|
14
|
+
|
15
|
+
# A B C 1 2 3
|
16
|
+
# 1 1 1 3 2 2 3 1
|
17
|
+
# 2 2 3 2 0 2 3 2
|
18
|
+
# 3 3 2 3 0 1 3 3
|
19
|
+
@couples_marks = { @entree1=>{@judge1=>1, @judge2=>1, @judge3=>3 },
|
20
|
+
@entree2=>{@judge1=>2, @judge2=>3, @judge3=>2 },
|
21
|
+
@entree3=>{@judge1=>3, @judge2=>2, @judge3=>3 }}
|
22
|
+
|
23
|
+
@performance_resuts.score(@couples_marks)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should know how many first places entree1 got" do
|
27
|
+
@performance_resuts[@entree1][1].should be(2)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should know how many first places entree2 got" do
|
31
|
+
@performance_resuts[@entree2][1].should be(0)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should know how many first places entree3 got" do
|
35
|
+
@performance_resuts[@entree3][1].should be(0)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should know how many 2nd or better places entree1 got" do
|
39
|
+
@performance_resuts[@entree1][2].should be(2)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should know how many 2nd or better places entree2 got" do
|
43
|
+
@performance_resuts[@entree2][2].should be(2)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should know how many 2nd or better places entree3 got" do
|
47
|
+
@performance_resuts[@entree3][2].should be(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should know how many 3rd or better places entree1 got" do
|
51
|
+
@performance_resuts[@entree1][3].should be(3)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should know how many 3rd or better places entree2 got" do
|
55
|
+
@performance_resuts[@entree2][3].should be(3)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should know how many 3rd or better places entree3 got" do
|
59
|
+
@performance_resuts[@entree3][3].should be(3)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe SkatingSystem::PerformanceResults, "getting the rankings" do
|
64
|
+
before(:each) do
|
65
|
+
@performance_resuts = SkatingSystem::PerformanceResults.new
|
66
|
+
|
67
|
+
@judge1 = mock("Judge")
|
68
|
+
@judge2 = mock("Judge")
|
69
|
+
@judge3 = mock("Judge")
|
70
|
+
|
71
|
+
@entree1 = mock("Entree")
|
72
|
+
@entree2 = mock("Entree")
|
73
|
+
@entree3 = mock("Entree")
|
74
|
+
|
75
|
+
# A B C 1 2 3
|
76
|
+
# 1 1 1 3 2 2 3 1
|
77
|
+
# 2 2 3 2 0 2 3 2
|
78
|
+
# 3 3 2 3 0 1 3 3
|
79
|
+
@couples_marks = { @entree1=>{@judge1=>1, @judge2=>1, @judge3=>3 },
|
80
|
+
@entree2=>{@judge1=>2, @judge2=>3, @judge3=>2 },
|
81
|
+
@entree3=>{@judge1=>3, @judge2=>2, @judge3=>3 }}
|
82
|
+
|
83
|
+
SkatingSystem::Ranking.stub!(:new).and_return(@ranking = mock(SkatingSystem::Ranking))
|
84
|
+
@ranking.stub!(:add)
|
85
|
+
|
86
|
+
@performance_resuts.score(@couples_marks)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return a Ranking when asked for one" do
|
90
|
+
@performance_resuts.ranking.should be(@ranking)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should add couple 1 to the ranking in first place" do
|
94
|
+
@ranking.should_receive(:add).with(1, @entree1)
|
95
|
+
@performance_resuts.ranking
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should add couple 2 to the ranking in second place" do
|
99
|
+
@ranking.should_receive(:add).with(2, @entree2)
|
100
|
+
@performance_resuts.ranking
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should add couple 3 to the ranking in third place" do
|
104
|
+
@ranking.should_receive(:add).with(3, @entree3)
|
105
|
+
@performance_resuts.ranking
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe SkatingSystem::Ranking do
|
4
|
+
before(:each) do
|
5
|
+
@ranking = SkatingSyatem::Ranking.new
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
describe SkatingSystem::Ranking, "adding new results" do
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@ranking = SkatingSystem::Ranking.new
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should put an entry in first place when they are entered in first place" do
|
17
|
+
entree = mock("Entree")
|
18
|
+
@ranking.add(1, entree)
|
19
|
+
@ranking.place_for(entree).should be(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should put both entries in first place when entered in first place, even if there is already a first place" do
|
23
|
+
entree1 = mock("Entree")
|
24
|
+
entree2 = mock("Entree")
|
25
|
+
@ranking.add(1, entree1)
|
26
|
+
@ranking.add(1, entree2)
|
27
|
+
@ranking.place_for(entree1).should be(1)
|
28
|
+
@ranking.place_for(entree2).should be(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should put an entry in second place when entred in second place" do
|
32
|
+
entree = mock("Entree")
|
33
|
+
@ranking.add(2, entree)
|
34
|
+
@ranking.place_for(entree).should be(2)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should put both in second place when netered in second place, even if there is already a second place" do
|
38
|
+
entree1 = mock("Entree")
|
39
|
+
entree2 = mock("Entree")
|
40
|
+
@ranking.add(2, entree1)
|
41
|
+
@ranking.add(2, entree2)
|
42
|
+
@ranking.place_for(entree1).should be(2)
|
43
|
+
@ranking.place_for(entree2).should be(2)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not allow an entry in second place if there are two first places" do
|
47
|
+
entree1 = mock("Entree")
|
48
|
+
entree2 = mock("Entree")
|
49
|
+
entree3 = mock("Entree")
|
50
|
+
@ranking.add(1, entree1)
|
51
|
+
@ranking.add(1, entree2)
|
52
|
+
lambda{ @ranking.add(2, entree3)}.should raise_error
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should reise an exception if asked about an unknown entree" do
|
56
|
+
lambda{ @ranking.place_for(mock("Entree"))}.should raise_error
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe "scoring a final performance" do
|
5
|
+
before(:each) do
|
6
|
+
@scorer = SkatingSystem::Scorer.new
|
7
|
+
@performance = mock("Performance")
|
8
|
+
@couples_marks = {:key=>:value }
|
9
|
+
SkatingSystem::PerformanceResults.stub!(:new).and_return(@pr=mock(SkatingSystem::PerformanceResults, :score=>nil))
|
10
|
+
@performance.stub!(:couples_marks).and_return(@couples_marks)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should check if the scorable thing respondes to :couples_marks" do
|
14
|
+
@performance.should_receive(:respond_to?).with(:couples_marks).and_return(true)
|
15
|
+
@scorer.score(@performance)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should delegate to score_performance" do
|
19
|
+
@scorer.should_receive(:score_performance).with(@performance)
|
20
|
+
@scorer.score(@performance)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return a the new Performance" do
|
24
|
+
@scorer.score_performance(@performance).should be(@pr)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should ask the PerformanceResults to score itself" do
|
28
|
+
@pr.should_receive(:score).with(@couples_marks)
|
29
|
+
@scorer.score(@performance)
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
lib_path = File.expand_path("#{dir}/../lib")
|
3
|
+
$LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'skating_system'
|
7
|
+
require 'ruby-debug'
|
8
|
+
|
9
|
+
Spec::Runner.configure do |config|
|
10
|
+
|
11
|
+
end
|
data/stories/all.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
Story: Scrutineering a final round dance
|
2
|
+
|
3
|
+
As a scrutineer
|
4
|
+
I want to scrutineer a final round
|
5
|
+
So that I can tell who won.
|
6
|
+
|
7
|
+
Scenario: clear cut case
|
8
|
+
Given couples 101, 102, 103, 104, 105, 106
|
9
|
+
And judges A, B, C, D, E
|
10
|
+
|
11
|
+
When couple 101 gets 1, 1, 1, 1, 1
|
12
|
+
And couple 102 gets 2, 2, 2, 2, 2
|
13
|
+
And couple 103 gets 3, 3, 3, 3, 3
|
14
|
+
And couple 104 gets 4, 4, 4, 4, 4
|
15
|
+
And couple 105 gets 5, 5, 5, 5, 5
|
16
|
+
And couple 106 gets 6, 6, 6, 6, 6
|
17
|
+
And I compute the results
|
18
|
+
|
19
|
+
Then couple 101 is in place 1
|
20
|
+
And couple 102 is in place 2
|
21
|
+
And couple 103 is in place 3
|
22
|
+
And couple 104 is in place 4
|
23
|
+
And couple 105 is in place 5
|
24
|
+
And couple 106 is in place 6
|
25
|
+
|
26
|
+
Scenario: Decidable on rule 5
|
27
|
+
Given couples 101, 102, 103
|
28
|
+
And judges A, B, C
|
29
|
+
|
30
|
+
When couple 101 gets 1, 1, 1
|
31
|
+
And couple 102 gets 2, 3, 2
|
32
|
+
And couple 103 gets 3, 2, 3
|
33
|
+
And I compute the results
|
34
|
+
|
35
|
+
Then couple 101 is in place 1
|
36
|
+
And couple 102 is in place 2
|
37
|
+
And couple 103 is in place 3
|
38
|
+
|
39
|
+
Scenario: requiring rule 6
|
40
|
+
Given couples 101, 102, 103, 104, 105
|
41
|
+
And judges A, B, C, D, E
|
42
|
+
|
43
|
+
When couple 101 gets 1, 1, 1, 5, 5
|
44
|
+
And couple 102 gets 4, 2, 4, 1, 2
|
45
|
+
And couple 103 gets 2, 4, 2, 2, 1
|
46
|
+
And couple 104 gets 3, 5, 3, 4, 4
|
47
|
+
And couple 105 gets 5, 3, 5, 3, 3
|
48
|
+
And I compute the results
|
49
|
+
Then couple 101 is in place 1
|
50
|
+
And couple 102 is in place 3
|
51
|
+
And couple 103 is in place 2
|
52
|
+
And couple 104 is in place 5
|
53
|
+
And couple 105 is in place 4
|
54
|
+
|
55
|
+
Scenario: requiring rule 7
|
56
|
+
Given couples 101, 102, 103, 104, 105
|
57
|
+
And judges A, B, C, D, E
|
58
|
+
|
59
|
+
When couple 101 gets 1, 1, 1, 5, 5
|
60
|
+
And couple 102 gets 2, 4, 3, 2, 2
|
61
|
+
And couple 103 gets 4, 2, 4, 1, 1
|
62
|
+
And couple 104 gets 3, 5, 2, 4, 4
|
63
|
+
And couple 105 gets 5, 3, 5, 3, 3
|
64
|
+
And I compute the results
|
65
|
+
Then couple 101 is in place 1
|
66
|
+
And couple 102 is in place 3
|
67
|
+
And couple 103 is in place 2
|
68
|
+
And couple 104 is in place 5
|
69
|
+
And couple 105 is in place 4
|
data/stories/helper.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("#{File.dirname(__FILE__)}/../lib")
|
2
|
+
require 'rubygems'
|
3
|
+
require 'skating_system'
|
4
|
+
require 'ruby-debug'
|
5
|
+
require 'spec'
|
6
|
+
require 'spec/story'
|
7
|
+
require File.join(File.dirname(__FILE__), *%w[resources steps scrutineering_a_final_performance])
|
@@ -0,0 +1,43 @@
|
|
1
|
+
steps_for(:scrutineering_a_final_performance) do
|
2
|
+
|
3
|
+
class Judges
|
4
|
+
def initialize(letters)
|
5
|
+
@judge_letters = letters
|
6
|
+
end
|
7
|
+
|
8
|
+
def hashed_marks(marks)
|
9
|
+
h = {}
|
10
|
+
@judge_letters.each_with_index do |judge, index|
|
11
|
+
h[judge]=marks[index].to_i
|
12
|
+
end
|
13
|
+
h
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
Given /couples ((?:(?:\d+)(?:, )?)+)/ do |numbers|
|
19
|
+
couples = numbers.split(", ")
|
20
|
+
@couples_marks = { }
|
21
|
+
couples.each do |c|
|
22
|
+
@couples_marks[c]={}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Given /judges ((?:(?:\w)(?:, )?)+)/ do |letters|
|
27
|
+
@judges = Judges.new(letters.split(", "))
|
28
|
+
end
|
29
|
+
|
30
|
+
When /couple (\d+) gets ((?:(?:\d+)(?:, )?)+)/ do |number, marks|
|
31
|
+
@couples_marks[number]=@judges.hashed_marks(marks.split(", "))
|
32
|
+
end
|
33
|
+
|
34
|
+
When "I compute the results" do
|
35
|
+
@res=SkatingSystem::PerformanceResults.new
|
36
|
+
@res.score(@couples_marks)
|
37
|
+
end
|
38
|
+
|
39
|
+
Then "couple $couple is in place $place" do |couple, place|
|
40
|
+
@res[couple][:result].should be(place.to_i)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: skating-system
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Wildfalcon
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-06 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
31
|
+
version: 1.2.9
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
description: Convert judges scores from a dance-competition into results
|
35
|
+
email: laurie@wildfalcon.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README
|
43
|
+
- README.rdoc
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- LICENSE
|
47
|
+
- README
|
48
|
+
- README.rdoc
|
49
|
+
- Rakefile
|
50
|
+
- VERSION
|
51
|
+
- lib/skating_system.rb
|
52
|
+
- lib/skating_system/performance_results.rb
|
53
|
+
- lib/skating_system/ranking.rb
|
54
|
+
- lib/skating_system/scorer.rb
|
55
|
+
- spec/spec.opts
|
56
|
+
- spec/spec/performance_results_spec.rb
|
57
|
+
- spec/spec/ranking_spec.rb
|
58
|
+
- spec/spec/scorer_spec.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
- stories/all.rb
|
61
|
+
- stories/final_performance/final_performance
|
62
|
+
- stories/final_performance/stories.rb
|
63
|
+
- stories/helper.rb
|
64
|
+
- stories/resources/steps/scrutineering_a_final_performance.rb
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/wildfalcon/skating-system
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --charset=UTF-8
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
requirements: []
|
89
|
+
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 1.3.6
|
92
|
+
signing_key:
|
93
|
+
specification_version: 3
|
94
|
+
summary: Dancesport skating system implementation
|
95
|
+
test_files:
|
96
|
+
- spec/spec/performance_results_spec.rb
|
97
|
+
- spec/spec/ranking_spec.rb
|
98
|
+
- spec/spec/scorer_spec.rb
|
99
|
+
- spec/spec_helper.rb
|