tournament 0.0.3 → 1.0.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/History.txt +14 -2
- data/README.txt +59 -41
- data/Rakefile +4 -1
- data/bin/pool +50 -21
- data/lib/tournament/bracket.rb +4 -4
- data/lib/tournament/pool.rb +51 -9
- data/tasks/doc.rake +1 -0
- metadata +4 -2
data/History.txt
CHANGED
@@ -1,8 +1,20 @@
|
|
1
|
-
== 0.0
|
1
|
+
== 1.0.0 / 2008-03-26
|
2
2
|
|
3
|
-
*
|
3
|
+
* Change the pool command line script usage to make it simpler (I hope)
|
4
|
+
* Add a new report: final_four
|
5
|
+
* FIXME: Final four report assumes payouts go to 1st and 2nd and last places.
|
6
|
+
This should be configurable.
|
7
|
+
|
8
|
+
== 0.0.3 / 2008-03-23
|
9
|
+
|
10
|
+
* Bugfix the picker wrapper script. Getting used to bones/rubyforge.
|
4
11
|
|
5
12
|
== 0.0.2 / 2008-03-23
|
6
13
|
|
7
14
|
* Changed the picker wrapper script to ruby because it seems that
|
8
15
|
that is what rubyforge tools expect.
|
16
|
+
|
17
|
+
== 0.0.1 / 2008-03-23
|
18
|
+
|
19
|
+
* Initial Release
|
20
|
+
|
data/README.txt
CHANGED
@@ -19,66 +19,82 @@ basketball tournament pool.
|
|
19
19
|
|
20
20
|
== SYNOPSIS:
|
21
21
|
|
22
|
-
The tournament command line program is installed as '
|
22
|
+
The tournament command line program is installed as 'pool'. The library
|
23
23
|
has the 2008 NCAA tournament pre-configured. If you were to use
|
24
24
|
this library for the 2009 NCAA tournament, code changes would be
|
25
25
|
necessary. FIXME (add ability to read teams from a simple configuration
|
26
|
-
file).
|
26
|
+
file). For usage, just execute
|
27
|
+
|
28
|
+
pool --help
|
29
|
+
|
30
|
+
For command specific usage, execute
|
31
|
+
|
32
|
+
pool <command> --help
|
33
|
+
|
34
|
+
where <command> is one of the available commands described below. The
|
35
|
+
pool command saves state in a file called pool.yml by default. This
|
36
|
+
can be overridden in all cases by using the --save-file option.
|
27
37
|
|
28
38
|
The pool manager would use this program as follows:
|
29
39
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
1. Choose a scoring strategy. There are various scoring strategies
|
41
|
+
that could be used. The library comes pre-configured with
|
42
|
+
two scoring strategies:
|
43
|
+
1. Basic scoring strategy: each correct pick is worth 2 X the round.
|
44
|
+
2. Upset favoring strategy: each correct pick is worth a
|
45
|
+
base amount per round plus the seed number of the winner. As
|
46
|
+
pre-configured, the base amounts per round are 3, 5, 11, 19, 30
|
47
|
+
and 40 points.
|
48
|
+
If your scoring strategy is not one of the above, you will have to
|
49
|
+
write a class in the Bracket class, in file lib/tournament/bracket.rb.
|
39
50
|
|
40
|
-
|
41
|
-
write a class in the Bracket class, in file lib/tournament/bracket.rb.
|
51
|
+
2. Create a directory to hold the pool data and change to it
|
42
52
|
|
43
|
-
|
53
|
+
3. Initialize the pool
|
44
54
|
|
45
|
-
|
55
|
+
pool setup [--scoring=upset_scoring_strategy]
|
46
56
|
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
Use the --scoring argument to change to the upset favoring
|
58
|
+
strategy. If the basic strategy is ok, the --scoring argument is
|
59
|
+
not required.
|
50
60
|
|
51
|
-
|
61
|
+
As mentioned above, unless overridden by using the --save-file
|
62
|
+
option, the pool will save itself to the file 'pool.yml'
|
52
63
|
|
53
|
-
|
64
|
+
3. Export a tournament entry YAML file
|
54
65
|
|
55
|
-
|
56
|
-
or edit YAML files by hand.
|
66
|
+
pool dump
|
57
67
|
|
58
|
-
|
68
|
+
This will save the tournament entry file as tournament.yml unless
|
69
|
+
the --entry option is used to override it.
|
59
70
|
|
60
|
-
|
71
|
+
4. Create entries. You can use the included buggy GUI (see below),
|
72
|
+
or edit YAML files by hand.
|
61
73
|
|
62
|
-
|
63
|
-
editing the YAML file by hand. Then update the pool with the new
|
64
|
-
tournament YAML file
|
74
|
+
5. Import the entry YAML files into the pool
|
65
75
|
|
66
|
-
|
76
|
+
pool entry --add=path/to/entry.yml
|
67
77
|
|
68
|
-
|
78
|
+
6. As games progress, update the tournament.yml file, again using the GUI or
|
79
|
+
editing the YAML file by hand. Then update the pool with the new
|
80
|
+
pool YAML file
|
81
|
+
|
82
|
+
pool update
|
83
|
+
|
84
|
+
7. Run reports
|
69
85
|
|
70
|
-
|
86
|
+
pool report [final_four|entry|region|leader|score]
|
71
87
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
88
|
+
8. After about 22 teams are left, run a possibility report. This report will
|
89
|
+
run through all the remaining ways the tournament can come out and
|
90
|
+
calculate the chance to win for each player. The chance to win
|
91
|
+
is defined as the percentage of possibilities that lead to that player
|
92
|
+
coming out on top in the pool. With more than about 22 teams left
|
93
|
+
(YMMV), this report could take months to run. FIXME (Investigate
|
94
|
+
possibly using EC2 or something to spread the load around, or
|
95
|
+
otherwise optimize the possibility checking algorithm)
|
80
96
|
|
81
|
-
|
97
|
+
pool report possibility
|
82
98
|
|
83
99
|
== SHOES GUI:
|
84
100
|
|
@@ -90,8 +106,10 @@ Tournament::Entry object. The picker program optionally takes one
|
|
90
106
|
argument, the path to a Tournament::Entry YAML file. It will open
|
91
107
|
with the provided entry's picks pre filled in.
|
92
108
|
|
93
|
-
The GUI may be used for keeping the NCAA tournament entry YAML file
|
94
|
-
up to date
|
109
|
+
The GUI also may be used for keeping the NCAA tournament entry YAML file
|
110
|
+
up to date:
|
111
|
+
|
112
|
+
picker tournament.yml
|
95
113
|
|
96
114
|
== REQUIREMENTS:
|
97
115
|
|
data/Rakefile
CHANGED
@@ -16,7 +16,7 @@ PROJ.authors = 'Douglas A. Seifert'
|
|
16
16
|
PROJ.email = 'doug+rubyforge@dseifert.net'
|
17
17
|
PROJ.url = 'http://www.dseifert.net/code/tournament'
|
18
18
|
PROJ.rubyforge_name = 'tournament'
|
19
|
-
PROJ.version = '0.0
|
19
|
+
PROJ.version = '1.0.0'
|
20
20
|
PROJ.group_id = 5863
|
21
21
|
|
22
22
|
PROJ.spec_opts << '--color'
|
@@ -24,4 +24,7 @@ PROJ.spec_opts << '--color'
|
|
24
24
|
PROJ.exclude = %w(tmp$ bak$ ~$ CVS \.svn ^pkg ^doc bin/fake bin/gui_v2.rb)
|
25
25
|
PROJ.exclude << '^tags$'
|
26
26
|
|
27
|
+
PROJ.rdoc_opts = ["--line-numbers", "--inline-source"]
|
28
|
+
PROJ.rdoc_template = "tasks/jamis.rb"
|
29
|
+
|
27
30
|
# EOF
|
data/bin/pool
CHANGED
@@ -31,16 +31,17 @@ Main do
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
option('save-file', 's') do
|
35
|
+
optional
|
36
36
|
argument :required
|
37
|
+
default 'pool.yml'
|
37
38
|
arity 1
|
38
39
|
description "Save file for the pool."
|
39
40
|
end
|
40
41
|
|
41
42
|
mode('setup') do
|
42
43
|
description "Sets up the pool the first time"
|
43
|
-
option('scoring', '
|
44
|
+
option('scoring', 'S') do
|
44
45
|
optional
|
45
46
|
argument :required
|
46
47
|
arity 1
|
@@ -58,56 +59,79 @@ Main do
|
|
58
59
|
end
|
59
60
|
|
60
61
|
mode('update') do
|
61
|
-
option('
|
62
|
+
option('entry', 'e') do
|
62
63
|
optional
|
63
64
|
argument :required
|
65
|
+
default 'tournament.yml'
|
64
66
|
arity 1
|
65
|
-
description "Update the tournament
|
67
|
+
description "Update the tournament entry using supplied yaml file"
|
66
68
|
end
|
67
|
-
|
69
|
+
def run
|
70
|
+
load_pool
|
71
|
+
tournament = YAML::load_file(params['entry'].value)
|
72
|
+
@pool.bracket = tournament.picks
|
73
|
+
save_pool
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
mode('entry') do
|
78
|
+
option('add', 'a') do
|
68
79
|
optional
|
69
80
|
argument :required
|
70
81
|
arity -1
|
71
82
|
description "Add an entry yaml file to the pool"
|
72
83
|
end
|
84
|
+
option('remove', 'r') do
|
85
|
+
optional
|
86
|
+
argument :required
|
87
|
+
arity -1
|
88
|
+
description "Remove an entry by name"
|
89
|
+
end
|
73
90
|
def run
|
74
91
|
load_pool
|
75
|
-
if params['
|
76
|
-
|
77
|
-
@pool.bracket = tournament.picks
|
92
|
+
if params['add'].given?
|
93
|
+
@pool.add_entry_yaml(params['add'].value)
|
78
94
|
save_pool
|
79
|
-
elsif params['
|
80
|
-
@pool.
|
95
|
+
elsif params['remove'].given?
|
96
|
+
@pool.remove_by_name(params['remove'].value)
|
81
97
|
save_pool
|
82
98
|
else
|
83
|
-
puts "Please specify
|
99
|
+
puts "Please specify add or remove"
|
84
100
|
print usage.to_s
|
85
101
|
exit_warn!
|
86
102
|
end
|
87
103
|
end
|
88
104
|
end
|
89
105
|
|
90
|
-
mode('
|
91
|
-
|
92
|
-
|
93
|
-
|
106
|
+
mode('dump') do
|
107
|
+
option('entry', 'e') do
|
108
|
+
optional
|
109
|
+
argument :required
|
110
|
+
default 'tournament.yml'
|
111
|
+
description "Dump the pool entry object to a yaml file."
|
94
112
|
end
|
95
113
|
def run
|
96
114
|
load_pool
|
97
115
|
tournament = Tournament::Entry.new('Tournament', @pool.bracket, 0)
|
98
|
-
File.open(params['
|
116
|
+
File.open(params['entry'].value, "w") do |f|
|
99
117
|
YAML::dump(tournament, f)
|
100
118
|
end
|
101
119
|
end
|
102
120
|
end
|
103
121
|
|
104
122
|
mode('report') do
|
105
|
-
|
123
|
+
available_reports = Tournament::Pool.instance_methods(false).map do |name|
|
124
|
+
if idx = name.index("_report")
|
125
|
+
name[0,idx]
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end.compact
|
130
|
+
argument('type') do
|
106
131
|
required
|
107
132
|
arity 1
|
108
|
-
|
109
|
-
|
110
|
-
description "Which report to run, score, leader, region, possibility or entry"
|
133
|
+
validate {|rt| available_reports.include?(rt)}
|
134
|
+
description "Which report to run among #{available_reports.join(', ')}"
|
111
135
|
end
|
112
136
|
def run
|
113
137
|
load_pool
|
@@ -115,4 +139,9 @@ Main do
|
|
115
139
|
end
|
116
140
|
end
|
117
141
|
|
142
|
+
def run
|
143
|
+
print usage.to_s
|
144
|
+
exit_warn!
|
145
|
+
end
|
146
|
+
|
118
147
|
end
|
data/lib/tournament/bracket.rb
CHANGED
@@ -176,7 +176,7 @@ class Tournament::Bracket
|
|
176
176
|
#num_possibilities = 0
|
177
177
|
#shifts.size.times { |n| num_possibilities |= (1 << n) }
|
178
178
|
|
179
|
-
puts "Checking #{num_possibilities} (#{number_of_outcomes}) possible outcomes."
|
179
|
+
#puts "Checking #{num_possibilities} (#{number_of_outcomes}) possible outcomes."
|
180
180
|
possibility = num_possibilities - 1
|
181
181
|
while possibility >= 0
|
182
182
|
#puts " possibility: #{Tournament::Bracket.jbin(possibility, teams.size - 1)}"
|
@@ -185,9 +185,9 @@ class Tournament::Bracket
|
|
185
185
|
real_poss |= (((possibility & (1 << i)) > 0 ? 1 : 0) << s)
|
186
186
|
end
|
187
187
|
#puts " real_poss: #{Tournament::Bracket.jbin(real_poss, teams.size - 1)}"
|
188
|
-
|
189
|
-
#puts "
|
190
|
-
yield(
|
188
|
+
real_poss = winners | real_poss
|
189
|
+
#puts " real_poss: #{Tournament::Bracket.jbin(real_poss, teams.size - 1)}"
|
190
|
+
yield(real_poss)
|
191
191
|
possibility -= 1
|
192
192
|
end
|
193
193
|
end
|
data/lib/tournament/pool.rb
CHANGED
@@ -32,6 +32,14 @@ class Tournament::Pool
|
|
32
32
|
@entries << YAML::load_file(yaml)
|
33
33
|
end
|
34
34
|
|
35
|
+
# Remove an entry by name from the pool
|
36
|
+
def remove_by_name(name)
|
37
|
+
entry = @entries.find {|e| e.name == name}
|
38
|
+
if !entry.nil?
|
39
|
+
@entries.delete(entry)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
35
43
|
# Creates a bracket for the pool by combining all the
|
36
44
|
# regions into one bracket of 64 teams. By default the
|
37
45
|
# bracket uses the basic scoring strategy.
|
@@ -289,7 +297,38 @@ class Tournament::Pool
|
|
289
297
|
end
|
290
298
|
end
|
291
299
|
|
300
|
+
def final_four_report
|
301
|
+
if @entries.size == 0
|
302
|
+
puts "There are no entries in the pool."
|
303
|
+
return
|
304
|
+
end
|
305
|
+
if self.bracket.teams_left != 4
|
306
|
+
puts "The final four report should only be run when there"
|
307
|
+
puts "are exactly four teams left in the tournament."
|
308
|
+
return
|
309
|
+
end
|
310
|
+
print "Final Four: #{self.bracket.winners[4][0,2].map{|t| "(#{t.seed}) #{t.name}"}.join(" vs. ")}"
|
311
|
+
puts " #{self.bracket.winners[4][2,2].map{|t| "(#{t.seed}) #{t.name}"}.join(" vs. ")}"
|
312
|
+
sep= "--------------+----------------+----------------------------"
|
313
|
+
puts " Championship | Champion | Winners"
|
314
|
+
puts sep
|
315
|
+
self.bracket.each_possible_bracket do |poss|
|
316
|
+
rankings = @entries.map{|p| [p, p.picks.score_against(poss)] }.sort_by {|arr| -arr[1] }
|
317
|
+
first = rankings[0][0]
|
318
|
+
second = rankings[1][0]
|
319
|
+
last = rankings[-1][0]
|
320
|
+
puts "%14s|%16s|%s" % [poss.winners[5].map{|t| t.short_name}.join("-"),
|
321
|
+
poss.champion.name, " 1: #{first.name} (#{rankings[0][1]})"]
|
322
|
+
puts "%14s|%16s|%s" % ['', '',
|
323
|
+
" 2: #{second.name} (#{rankings[1][1]})"]
|
324
|
+
puts "%14s|%16s|%s" % ['', '',
|
325
|
+
"LAST: #{last.name} (#{rankings[-1][1]})"]
|
326
|
+
puts sep
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
292
330
|
def possibility_report
|
331
|
+
$stdout.sync = true
|
293
332
|
if @entries.size == 0
|
294
333
|
puts "There are no entries in the pool."
|
295
334
|
return
|
@@ -298,6 +337,9 @@ class Tournament::Pool
|
|
298
337
|
min_ranking = @entries.map{|p| @entries.size + 1}
|
299
338
|
times_winner = @entries.map{|p| 0 }
|
300
339
|
count = 0
|
340
|
+
old_percentage = -1
|
341
|
+
old_remaining = 1_000_000_000_000
|
342
|
+
puts "Checking #{self.bracket.number_of_outcomes} possible outcomes"
|
301
343
|
start = Time.now.to_f
|
302
344
|
self.bracket.each_possible_bracket do |poss|
|
303
345
|
poss_scores = @entries.map{|p| p.picks.score_against(poss)}
|
@@ -310,15 +352,15 @@ class Tournament::Pool
|
|
310
352
|
times_winner[i] += 1 if rank == 1
|
311
353
|
end
|
312
354
|
count += 1
|
313
|
-
percentage = (count * 100 / self.bracket.number_of_outcomes)
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
355
|
+
percentage = (count * 100.0 / self.bracket.number_of_outcomes)
|
356
|
+
elapsed = Time.now.to_f - start
|
357
|
+
spp = elapsed / count
|
358
|
+
remaining = ((self.bracket.number_of_outcomes - count) * spp).to_i
|
359
|
+
if (percentage.to_i != old_percentage) || (remaining < old_remaining)
|
360
|
+
old_remaining = remaining
|
361
|
+
old_percentage = percentage.to_i
|
362
|
+
hashes = '#' * (percentage.to_i/5) + '>'
|
363
|
+
print "\rCalculating: %3d%% +#{hashes.ljust(20, '-')}+ %5d seconds remaining" % [percentage.to_i, remaining]
|
322
364
|
end
|
323
365
|
end
|
324
366
|
puts "\n"
|
data/tasks/doc.rake
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tournament
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Douglas A. Seifert
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-03-
|
12
|
+
date: 2008-03-26 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -70,6 +70,8 @@ has_rdoc: true
|
|
70
70
|
homepage: http://www.dseifert.net/code/tournament
|
71
71
|
post_install_message:
|
72
72
|
rdoc_options:
|
73
|
+
- --line-numbers
|
74
|
+
- --inline-source
|
73
75
|
- --main
|
74
76
|
- README.txt
|
75
77
|
require_paths:
|