Seed management for tournament brackets
SeedList is designed for Rails-powered tournament engines that need to persist a 1-indexed ordered list of players (ranked low-to-high by skill or past performance) and then match them up appropriately in the first round of a bracket.
Players are then matched up according to a strategy specified on a per-instance basis.
See also: https://github.com/agoragames/bracket_tree
class Tournament < ActiveRecord::Base
has_many :players
seed :players
end
class Player < ActiveRecord::Base
belongs_to :tournament
end
rake seed_list:install:migrations db:migrate
t = Tournament.create
=> #<Tournament id: 2>
t.seed_list
=> #<SeedList::Model id: 2, tournament_id: 2, tournament_type: "Tournament", list: #<SeedList::List:0x00000003a74c40 @list=[]>>
p = t.players.create
=> #<Player id: 5, tournament_id: 2>
t.seed_list.list
=> #<SeedList::List:0x00000002e0b878 @list=[5]>
# Seed numbers start at 1
p.seed
=> 1
When a player is created, the id
is pushed to the list in the highest (worst-place) seed
position. You can then move the seed to another position in the list.
3.times { t.players.create }
p.seed = 4
t.seed_list.list
=> #<SeedList::List:0x00000003ca7468 @list=[6, 7, 8, 5]>
Once your players have been created and moved to the appropriate seed positions, you can use the included seeding strategies to match them up appropriately in the first round of the bracket.
You can easily implement your own strategies as well. The initializer must accept an array of objects that respond to #seed with an integer, and the #seed method must return an array of those objects sorted appropriately as pairs.
t.players.map { |p| p.seed }
=> [1, 2, 3 , 4]
# A Knockout tournament matches players at random. The seed position is irrelevant.
SeedList::Strategy::Knockout.new(t.players).seed.map { |p| p.seed }
=> [2, 3, 4, 1]
# A Playoff tournament matches the best players against the worst.
SeedList::Strategy::Playoff.new(t.players).seed.map { |p| p.seed }
=> [1, 4, 2, 3]
# An Amateur tournament matches by skill similarity straight down
SeedList::Strategy::Amateur.new(t.players).seed.map { |p| p.seed }
=> [1, 2, 3, 4]
SeedList includes a few Thor tasks to make seeds easy to manage from the command line.
Thor must load the Rails environment to import tasks from SeedList and other gems.
lib/tasks/environment.thor
require File.expand_path('config/environment')
$ thor list
seed_list
---------
thor seed_list:edit TOURNAMENT_ID # Reposition seeds using EDITOR (interactive)
thor seed_list:export TOURNAMENT_ID # Export line-separated seeds to STDOUT
thor seed_list:import TOURNAMENT_ID # Import line-separated seeds from STDIN
Your Player model is assumed to have a #login attribute to use these tasks.
Export seeds to a file
$ thor seed_list:export TOURNAMENT_ID > ./seeds
$ cat seeds
albafunk
deborahbayerii
ramonalockman
danlind
Import seeds from a file
$ cat ./seeds | thor seed_list:import TOURNAMENT_ID
Edit seeds in VIM
$ EDITOR=vim thor seed_list:edit TOURNAMENT_ID
The serialized column "_seed_list" on your "tournaments" table has moved to a column called "list" on the seed_lists table created by a migration included in >= 1.0.0. You will need to write your own migration to create an associated SeedList::Model object for each of your existing tournaments and copy the existing list into it. You can then safely remove the deprecated "_seed_list" column.
See the MIT-LICENSE file.
Contributions are awesome. Feature branch pull requests are the preferred method.
Written by Logan Koester