tournament 0.0.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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2008-03-09
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
data/Manifest.txt ADDED
@@ -0,0 +1,29 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/benchmark_pool
6
+ bin/gui.rb
7
+ bin/picker
8
+ bin/pool
9
+ lib/tournament.rb
10
+ lib/tournament/bracket.rb
11
+ lib/tournament/entry.rb
12
+ lib/tournament/pool.rb
13
+ lib/tournament/team.rb
14
+ spec/spec_helper.rb
15
+ spec/tournament_spec.rb
16
+ static/shoes-icon.png
17
+ tasks/ann.rake
18
+ tasks/annotations.rake
19
+ tasks/bones.rake
20
+ tasks/doc.rake
21
+ tasks/gem.rake
22
+ tasks/manifest.rake
23
+ tasks/post_load.rake
24
+ tasks/rubyforge.rake
25
+ tasks/setup.rb
26
+ tasks/spec.rake
27
+ tasks/svn.rake
28
+ tasks/test.rake
29
+ test/test_tournament.rb
data/README.txt ADDED
@@ -0,0 +1,113 @@
1
+ tournament
2
+ by Douglas A. Seifert
3
+ http://www.dseifert.net/code/ncaa_pool
4
+
5
+ == DESCRIPTION:
6
+
7
+ Small library and command line program for managing a NCAA
8
+ basketball tournament pool.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * Add NCAA tournament pool entries and save them as YAML
13
+ * Run a possibilities report for determining who is likely to win
14
+ * Run other reports such as a leader board and entry report
15
+ * Buggy, bug functional, Shoes GUI included for creating the entries
16
+ and updating the tournament bracket.
17
+ * FIXME: Write a test suite.
18
+
19
+ == SYNOPSIS:
20
+
21
+ The tournament command line program is installed as 'tournament'. The library
22
+ has the 2008 NCAA tournament pre-configured. If you were to use
23
+ this library for the 2009 NCAA tournament, code changes would be
24
+ necessary. FIXME (add ability to read teams from a simple configuration
25
+ file).
26
+
27
+ The pool manager would use this program as follows:
28
+
29
+ 1. Choose a scoring strategy. There are various scoring strategies
30
+ that could be used. The library comes pre-configured with
31
+ two scoring strategies:
32
+
33
+ 1. Basic scoring strategy: each correct pick is worth 2 X the round.
34
+ 2. Upset favoring strategy: each correct pick is worth a
35
+ base amount per round plus the seed number of the winner. As
36
+ pre-configured, the base amounts per round are 3, 5, 11, 19, 30
37
+ and 40 points.
38
+
39
+ If your scoring strategy is not one of the above, you will have to
40
+ write a class in the Bracket class, in file lib/tournament/bracket.rb.
41
+
42
+ 2. Initialize the pool
43
+
44
+ tournament setup pool.yml [--scoring=upset_scoring_strategy]
45
+
46
+ Use the --scoring argument to change to the upset favoring
47
+ strategy. If the basic strategy is ok, the --scoring argument is
48
+ not required.
49
+
50
+ 3. Export a tournament bracket YAML file
51
+
52
+ tournament bracket pool.yml tournament.yml
53
+
54
+ 4. Create entries. You can use the included buggy GUI (see below),
55
+ or edit YAML files by hand.
56
+
57
+ 5. Import the entry YAML files into the pool
58
+
59
+ tournament update pool.yml --add-entry=path/to/entry.yml
60
+
61
+ 6. As games progress, update the tournament.yml file, again using the GUI or
62
+ editing the YAML file by hand. Then update the pool with the new
63
+ tournament YAML file
64
+
65
+ tournament update pool.yml --bracket=tournament.yml
66
+
67
+ 7. Run reports
68
+
69
+ tournament report pool.yml --type=[entry|region|leader|score]
70
+
71
+ 8. After about 22 teams are left, run a possibility report. This report will
72
+ run through all the remaining ways the tournament can come out and
73
+ calculate the chance to win for each player. The chance to win
74
+ is defined as the percentage of possibilities that lead to that player
75
+ coming out on top in the pool. With more than about 22 teams left
76
+ (YMMV), this report could take months to run. FIXME (Investigate
77
+ possibly using EC2 or something to spread the load around, or
78
+ otherwise optimize the possibility checking algorithm)
79
+
80
+ tournament report pool.yml --type=possibility
81
+
82
+ == REQUIREMENTS:
83
+
84
+ * main (2.8.0)
85
+
86
+ == INSTALL:
87
+
88
+ * FIXME (sudo gem install, anything else)
89
+
90
+ == LICENSE:
91
+
92
+ (The MIT License)
93
+
94
+ Copyright (c) 2008
95
+
96
+ Permission is hereby granted, free of charge, to any person obtaining
97
+ a copy of this software and associated documentation files (the
98
+ 'Software'), to deal in the Software without restriction, including
99
+ without limitation the rights to use, copy, modify, merge, publish,
100
+ distribute, sublicense, and/or sell copies of the Software, and to
101
+ permit persons to whom the Software is furnished to do so, subject to
102
+ the following conditions:
103
+
104
+ The above copyright notice and this permission notice shall be
105
+ included in all copies or substantial portions of the Software.
106
+
107
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
108
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
109
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
110
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
111
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
112
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
113
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ load 'tasks/setup.rb'
6
+
7
+ ensure_in_path 'lib'
8
+ require 'tournament'
9
+
10
+ task :default => 'spec:run'
11
+
12
+ depend_on 'main'
13
+
14
+ PROJ.name = 'tournament'
15
+ PROJ.authors = 'Douglas A. Seifert'
16
+ PROJ.email = 'doug+rubyforge@dseifert.net'
17
+ PROJ.url = 'http://www.dseifert.net/code/tournament'
18
+ PROJ.rubyforge_name = 'tournament'
19
+ PROJ.version = '0.0.1'
20
+ PROJ.group_id = 5863
21
+
22
+ PROJ.spec_opts << '--color'
23
+
24
+ PROJ.exclude = %w(tmp$ bak$ ~$ CVS \.svn ^pkg ^doc bin/fake bin/gui_v2.rb)
25
+ PROJ.exclude << '^tags$'
26
+
27
+ # EOF
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), '..', 'lib', 'tournament'))
5
+
6
+
7
+ require 'benchmark'
8
+
9
+ Tournament::Pool.test
10
+ Benchmark.bm(12) do |x|
11
+ x.report('final_8') { Tournament::Pool.test }
12
+ end
data/bin/gui.rb ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env shoes
2
+
3
+ require 'yaml'
4
+ require File.join('.', 'lib', 'tournament')
5
+
6
+ # Define some constants
7
+ WINDOW_HEIGHT = 600
8
+ WINDOW_WIDTH = 800
9
+ BLANK_LABEL = ' '
10
+
11
+ Shoes.app :width => WINDOW_WIDTH, :height => 600 do
12
+ def load_data
13
+ @entry = YAML::load_file(@save_file)
14
+ end
15
+
16
+ def save_data
17
+ @entry.name = @name.text
18
+ @entry.tie_breaker = @tie_break.text.to_i
19
+ File.open(@save_file, "w") do |f|
20
+ YAML::dump(@entry, f)
21
+ end
22
+ end
23
+
24
+ @pool = Tournament::Pool.ncaa_2008
25
+
26
+ if ARGV[1]
27
+ @save_file = ARGV[1]
28
+ load_data
29
+ else
30
+ @entry = Tournament::Entry.new
31
+ @entry.picks = Tournament::Bracket.new(@pool.bracket.teams)
32
+ #@picks = Tournament::Bracket.random_bracket(Tournament::Pool.ncaa_2008.bracket.teams)
33
+ end
34
+
35
+
36
+ @labels = Array.new(4) {Array.new(6) { Array.new }}
37
+ @teams = Array.new(4) {Array.new(6) { Array.new }}
38
+ @buttons = Array.new(4) {Array.new(6) { Array.new }}
39
+ @region_stacks = Array.new(4)
40
+ @champ = Array.new(4)
41
+
42
+ def round_stack(region_idx, round, games_per_round, gap, top, width)
43
+ stack :width => width do
44
+ if top > 0
45
+ stack :height => top, :width => 26 do
46
+ background white
47
+ end
48
+ end
49
+ 1.upto(games_per_round) do |game|
50
+ bc = game % 2 == 0 || round > 1 ? lightgreen : white
51
+ real_game = game + region_idx * games_per_round
52
+ matchup_flow(region_idx, round, game, real_game, bc, gap)
53
+ if gap > 0
54
+ stack :height => gap, :width => width do
55
+ background white
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def label_for(team, round = 1, game = 1)
63
+ label = if Tournament::Bracket::UNKNOWN_TEAM == team
64
+ "r:%d g:%d" % [round, game]
65
+ else
66
+ if round == 1 || round > 4
67
+ "%2d %s (%s)" % [team.seed, team.name, team.short_name]
68
+ else
69
+ "%4s" % team.short_name
70
+ end
71
+ end
72
+ end
73
+
74
+ def matchup_flow(region_idx, round, game, real_game, bc, gap)
75
+ @entry.picks.matchup(round,real_game).each_with_index do |team, idx|
76
+ flow do
77
+ background bc
78
+ flow_idx = (game-1)*2 + idx
79
+ @teams[region_idx][round-1][flow_idx] = team
80
+ stack :width => (width - 26) do
81
+ @labels[region_idx][round-1][flow_idx] = para label_for(team, round, game), :height => 26, :width => 175, :font => "Monospace 10px"
82
+ end
83
+ stack :width => 26 do
84
+ @buttons[region_idx][round-1][flow_idx] = button ">", :width => 26, :height => 26, :font => "Monospace 6px", :margin => 1 do
85
+ make_pick(region_idx, round, flow_idx, real_game)
86
+ end
87
+ end
88
+ end
89
+ if gap > 0 && idx == 0
90
+ stack :height => gap, :width => width do
91
+ background bc
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ def make_pick(region_idx, round, team_num, real_game)
98
+ team = @teams[region_idx][round-1][team_num]
99
+ return if team == Tournament::Bracket::UNKNOWN_TEAM
100
+
101
+ matchup = @entry.picks.matchup(round,real_game).reject{|t| t == team}[0]
102
+
103
+ if round == 4
104
+ @champ[region_idx].replace label_for(team, 6, 1)
105
+ #@labels[0][4][region_idx].replace label_for(team, 6, 1)
106
+ end
107
+
108
+ if round == 6
109
+ @overall_champ.replace label_for(team, 6, 1)
110
+ end
111
+
112
+ forward_round = round
113
+ forward_idx = team_num / 2
114
+ forward_team = @teams[region_idx][forward_round][forward_idx] rescue nil
115
+ while forward_team == matchup
116
+ ri = forward_round > 4 ? 0 : region_idx
117
+ @labels[ri][forward_round][forward_idx].replace BLANK_LABEL
118
+ forward_round += 1
119
+ forward_idx /= 2
120
+ if forward_round == 4
121
+ @champ[region_idx].replace 'Regional Champ'
122
+ end
123
+ if forward_round == 6
124
+ @overall_champ.replace 'NCAA Champion'
125
+ end
126
+ ri = forward_round > 4 ? 0 : region_idx
127
+ fi = forward_round == 5 ? region_idx : forward_idx
128
+ forward_team = @teams[ri][forward_round][fi] rescue nil
129
+ end
130
+
131
+ ri = round >= 4 ? 0 : region_idx
132
+ ti = team_num / 2
133
+ if round == 4
134
+ ti = region_idx
135
+ end
136
+ @entry.picks.set_winner(round, real_game, team)
137
+ return if round >= 6
138
+ begin
139
+ @labels[ri][round][ti].replace label_for(team, round+1, real_game)
140
+ @teams[ri][round][ti] = team
141
+ rescue Exception => e
142
+ alert "got error for ri #{ri} round #{round} team_num #{team_num}: #{e}"
143
+ end
144
+ end
145
+
146
+ def scrub_save_file
147
+ unless '.yml' == File.extname(@save_file)
148
+ @save_file += '.yml'
149
+ end
150
+ end
151
+
152
+ stack :margin => 5 do
153
+ stack do
154
+ border black, :strokewidth => 1
155
+ flow :margin_bottom => 5 do
156
+ button "Save" do
157
+ if @save_file
158
+ alert("Saving #{@save_file} ...")
159
+ else
160
+ @save_file = ask_save_file
161
+ scrub_save_file
162
+ end
163
+ save_data
164
+ end
165
+ button "Save As ..." do
166
+ @save_file = ask_save_file
167
+ scrub_save_file
168
+ save_data
169
+ end
170
+ end
171
+ stack do
172
+ para "Name"
173
+ @name = edit_line @entry.name
174
+ end
175
+ stack do
176
+ para "Tie Breaker"
177
+ @tie_break = edit_line "#{@entry.tie_breaker}"
178
+ end
179
+ end
180
+ @pool.regions.each_with_index do |region, region_idx|
181
+ @region_stacks[region_idx] = stack do
182
+ stack :margin_bottom => 5 do
183
+ title region[:name]
184
+ end
185
+ flow do
186
+ top = gap = 0
187
+ games_per_round = 16
188
+ 1.upto(4) do |round|
189
+ top = top + (round - 1) * 26 / 2
190
+ if round == 4
191
+ top = top + 13
192
+ end
193
+ gap = top * 2
194
+ width = 175
195
+ width = 75 if round > 1
196
+ games_per_round /= 2
197
+ round_stack(region_idx, round, games_per_round, gap, top, width)
198
+ end
199
+ stack :width => 200 do
200
+ stack :height => ((WINDOW_HEIGHT - 226) / 2) do
201
+ background white
202
+ end
203
+ champ = @entry.picks.winner(4, 1 + region_idx)
204
+ champ_label = if champ == Tournament::Bracket::UNKNOWN_TEAM
205
+ 'Regional Champ'
206
+ else
207
+ champ.name
208
+ end
209
+ @champ[region_idx] = para champ_label, :font => 'Monospace 10px', :height => 26
210
+ end
211
+ end
212
+ end
213
+ end # regions.each_with_index
214
+ @final_four_stack = stack do
215
+ stack :margin_bottom => 5 do
216
+ title "Final Four"
217
+ end
218
+ flow do
219
+ round_stack(0, 5, 2, 0, 0, 175)
220
+ top = 13
221
+ gap = 26
222
+ round_stack(0, 6, 1, gap, top, 175)
223
+ top = 39
224
+ gap = 78
225
+ stack :width => 175 do
226
+ stack :width => 175, :top => top do
227
+ background white
228
+ end
229
+ stack :width => 175 do
230
+ @overall_champ = para label_for(@entry.picks.champion, 7, 1), :height => 26, :width => 175, :font => "Monospace 10px"
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
data/bin/picker ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+
3
+ dir=`dirname $0`
4
+
5
+ file=''
6
+ if [ ! -z "$1" ]; then
7
+ file=`pwd`/$1
8
+ fi
9
+
10
+ cd $dir/..
11
+
12
+ ./bin/gui.rb $file
data/bin/pool ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path(
3
+ File.join(File.dirname(__FILE__), '..', 'lib', 'tournament'))
4
+
5
+ require 'rubygems'
6
+ require 'main'
7
+ require 'yaml'
8
+
9
+ Main do
10
+
11
+ # Loads the pool from the save file. If the save file
12
+ # does not exist, creates a default pool
13
+ def load_pool
14
+ @pool = YAML::load_file(save_file_name)
15
+ end
16
+
17
+ def save_file_name
18
+ params['save-file'].value
19
+ end
20
+
21
+ def init_pool
22
+ @pool = Tournament::Pool.ncaa_2008
23
+ @pool.bracket.scoring_strategy = Tournament::Bracket.strategy_for_name(params['scoring'].value)
24
+ end
25
+
26
+ def save_pool
27
+ if @pool
28
+ File.open(save_file_name, "w") do |f|
29
+ YAML::dump(@pool, f)
30
+ end
31
+ end
32
+ end
33
+
34
+ argument('save-file') do
35
+ required
36
+ argument :required
37
+ arity 1
38
+ description "Save file for the pool."
39
+ end
40
+
41
+ mode('setup') do
42
+ description "Sets up the pool the first time"
43
+ option('scoring', 's') do
44
+ optional
45
+ argument :required
46
+ arity 1
47
+ default 'basic_scoring_strategy'
48
+ validate {|s| Tournament::Bracket.available_strategies.include?(s)}
49
+ description "Sets the scoring strategy, should be one of #{Tournament::Bracket.available_strategies.join(', ')}"
50
+ end
51
+ def run
52
+ init_pool
53
+ puts "Initialized new pool"
54
+ puts "Scoring:"
55
+ puts @pool.bracket.scoring_strategy.description
56
+ save_pool
57
+ end
58
+ end
59
+
60
+ mode('update') do
61
+ option('bracket', 'b') do
62
+ optional
63
+ argument :required
64
+ arity 1
65
+ description "Update the tournament bracket using supplied yaml file"
66
+ end
67
+ option('add-entry', 'a') do
68
+ optional
69
+ argument :required
70
+ arity -1
71
+ description "Add an entry yaml file to the pool"
72
+ end
73
+ def run
74
+ load_pool
75
+ if params['bracket'].given?
76
+ tournament = YAML::load_file(params['bracket'].value)
77
+ @pool.bracket = tournament.picks
78
+ save_pool
79
+ elsif params['add-entry'].given?
80
+ @pool.add_entry_yaml(params['add-entry'].value)
81
+ save_pool
82
+ else
83
+ puts "Please specify bracket or add-entry"
84
+ print usage.to_s
85
+ exit_warn!
86
+ end
87
+ end
88
+ end
89
+
90
+ mode('bracket') do
91
+ argument('bracket-yaml') do
92
+ required
93
+ description "Dump the pool's bracket as an Tournament::Entry object to a yaml file."
94
+ end
95
+ def run
96
+ load_pool
97
+ tournament = Tournament::Entry.new('Tournament', @pool.bracket, 0)
98
+ File.open(params['bracket-yaml'].value, "w") do |f|
99
+ YAML::dump(tournament, f)
100
+ end
101
+ end
102
+ end
103
+
104
+ mode('report') do
105
+ option('type', 't') do
106
+ required
107
+ arity 1
108
+ argument :required
109
+ validate {|rt| ['score', 'region', 'leader', 'possibility', 'entry'].include?(rt)}
110
+ description "Which report to run, score, leader, region, possibility or entry"
111
+ end
112
+ def run
113
+ load_pool
114
+ @pool.send("#{params['type'].value}_report")
115
+ end
116
+ end
117
+
118
+ end