tournament 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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