Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adds new Bracket::Base and subclasses

BracketTree::Bracket is no longer a class. Instead, BracketTree::Bracket::Base holds
the basic data, and new bracket types are subclasses of that. This allows flexibility
of template selection, as well as extending functionality in the future for tournament
types (rounds, losers/winners brackets, rematch, etc).

Because of this, BracketTree::Template::Base#generate_blank_bracket has been removed.
The corresponding BracketTree::Bracket::Base#generate_from_template method has been
added.
  • Loading branch information...
commit 99cc8ec818fdc843f0cfade4292b4f892774c2bf 1 parent b54067e
Andrew Nordman cadwallion authored
174 lib/bracket_tree/bracket.rb
View
@@ -1,176 +1,12 @@
require 'bracket_tree/node'
-
+require 'bracket_tree/bracket/base'
module BracketTree
- class Bracket
+ module Bracket
class NoSeedOrderError < Exception ; end
class SeedLimitExceededError < Exception ; end
- include Enumerable
- attr_accessor :root, :seed_order, :matches, :insertion_order
-
- def initialize options = {}
- @insertion_order = []
- @matches = []
-
- if options[:matches]
- options[:matches].each do |m|
- @matches << Match.new(m)
- end
- end
- end
-
- # Adds a Node at the given position, setting the data as the payload. Maps to
- # binary tree under the hood. The `data` can be any serializable object.
- #
- # @param Fixnum position - Seat position to add
- # @param Object data - the player object to store in the Seat position
- def add position, data
- node = Node.new position, data
- @insertion_order << position
-
- if @root.nil?
- @root = node
- else
- current = @root
- loop do
- if node.position < current.position
- if current.left.nil?
- current.left = node
- break
- else
- current = current.left
- end
- elsif node.position > current.position
- if current.right.nil?
- current.right = node
- break
- else
- current = current.right
- end
- else
- break
- end
- end
- end
- end
-
- # Replaces the data at a given node position with new payload. This is useful
- # for updating bracket data, replacing placeholders with actual data, seeding,
- # etc..
- #
- # @param [Fixnum] position - the node position to replace
- # @param payload - the new payload object to replace
- def replace position, payload
- node = at position
- if node
- node.payload = payload
- true
- else
- nil
- end
- end
-
- # Seeds bracket based on `seed_order` value of bracket. Provide an iterator
- # with players that will be inserted in the appropriate location. Will raise a
- # SeedLimitExceededError if too many players are sent, and a NoSeedOrderError if
- # the `seed_order` attribute is nil
- #
- # @param [Enumerable] players - players to be seeded
- def seed players
- if @seed_order.nil?
- raise NoSeedOrderError, 'Bracket does not have a seed order.'
- elsif players.size > @seed_order.size
- raise SeedLimitExceededError, 'cannot seed more players than seed order list.'
- else
- @seed_order.each do |position|
- replace position, players.shift
- end
- end
- end
-
- def winner
- @root.payload
- end
-
- def each(&block)
- in_order(@root, block)
- end
-
- def to_h
- @root.to_h
- end
-
- # Array of Seats mapping to the individual positions of the bracket tree. The
- # order of the nodes is important, as insertion in this order maintains the
- # binary tree
- #
- # @return Array seats
- def seats
- entries.sort_by { |node| @insertion_order.index(node.position) }
- end
-
- alias_method :to_a, :seats
-
- def at position
- find { |n| n.position == position }
- end
-
- alias_method :size, :count
-
- # Progresses the bracket by using the stored `matches` to copy data to the winning
- # and losing seats. This facilitates match progression without manually
- # manipulating bracket positions
- #
- # @param Fixnum seat - winning seat position
- # @return Boolean result - result of progression
- def match_winner seat
- match = @matches.find { |m| m.include? seat }
-
- if match
- losing_seat = match.seats.find { |s| s != seat }
-
- if match.winner_to
- replace match.winner_to, at(seat).payload
- end
-
- if match.loser_to
- replace match.loser_to, at(losing_seat).payload
- end
-
- return true
- else
- return false
- end
- end
-
- # Inverse of `match_winner`, progresses the bracket based on seat. See `match_winner`
- # for more details
- #
- # @param Fixnum seat - losing seat position
- # @return Boolean result - result of progression
- def match_loser seat
- match = @matches.find { |m| m.include? seat }
-
- if match
- winning_seat = match.seats.find { |s| s != seat }
- match_winner winning_seat
- else
- return false
- end
- end
-
- def in_order(node, block)
- if node
- unless node.left.nil?
- in_order(node.left, block)
- end
-
- block.call(node)
-
- unless node.right.nil?
- in_order(node.right, block)
- end
- end
- end
+ autoload :Base, 'bracket_tree/bracket/base'
+ autoload :SingleElimination, 'bracket_tree/bracket/single_elimination'
+ autoload :DoubleElimination, 'bracket_tree/bracket/double_elimination'
end
end
228 lib/bracket_tree/bracket/base.rb
View
@@ -0,0 +1,228 @@
+module BracketTree
+ module Bracket
+ # Basic bracketing functionality. If you wish to create a custom bracket type,
+ # inherit from this class to provide easy access to bracketing.
+ #
+ # Example:
+ # class MLGDouble < BracketTree::Bracket::Base
+ # template BracketTree::Template::DoubleElimination
+ # end
+ #
+ # bracket = MLGDouble.by_size 8
+ #
+ # This creates a bracket based on the standard double elimination template class.
+ # The template parameter can be any class that inherits from BracketTree::Template::Base,
+ # though.
+ #
+ # class MLGDoubleTemplate < BracketTree::Template::Base
+ # def self.location
+ # File.join File.dirname(__FILE__), 'templates', 'mlg_double'
+ # end
+ # end
+ #
+ # class MLGDouble < BracketTree::Bracket::Base
+ # template MLGDoubleTemplate
+ # end
+ class Base
+ class NoTemplateError < Exception ; end
+
+ class << self
+ def by_size size, options = {}
+ generate_from_template @template, size
+ end
+
+ def template class_name
+ @template = class_name
+ end
+
+ # Generates a blank bracket object from the passed Template class for the
+ # passed size
+ #
+ # @param BracketTree::Template::Base template - the Template the bracket is
+ # based on
+ # @param Fixnum size - bracket size
+ # @return BracketTree::Bracket::Base bracket - a blank bracket with hash placeholders
+ def generate_from_template template, size
+ template = template.by_size size
+ bracket = new(matches: template.matches, seed_order: template.seed_order)
+
+ template.seats.each do |position|
+ bracket.add position, {}
+ end
+
+ bracket
+ end
+ end
+
+ include Enumerable
+ attr_accessor :root, :seed_order, :matches, :insertion_order
+
+ def initialize options = {}
+ @insertion_order = []
+ @matches = []
+
+ if options[:matches]
+ options[:matches].each do |m|
+ @matches << Match.new(m)
+ end
+ end
+
+ @seed_order = options[:seed_order] if options[:seed_order]
+ end
+
+ # Adds a Node at the given position, setting the data as the payload. Maps to
+ # binary tree under the hood. The `data` can be any serializable object.
+ #
+ # @param Fixnum position - Seat position to add
+ # @param Object data - the player object to store in the Seat position
+ def add position, data
+ node = Node.new position, data
+ @insertion_order << position
+
+ if @root.nil?
+ @root = node
+ else
+ current = @root
+ loop do
+ if node.position < current.position
+ if current.left.nil?
+ current.left = node
+ break
+ else
+ current = current.left
+ end
+ elsif node.position > current.position
+ if current.right.nil?
+ current.right = node
+ break
+ else
+ current = current.right
+ end
+ else
+ break
+ end
+ end
+ end
+ end
+
+ # Replaces the data at a given node position with new payload. This is useful
+ # for updating bracket data, replacing placeholders with actual data, seeding,
+ # etc..
+ #
+ # @param [Fixnum] position - the node position to replace
+ # @param payload - the new payload object to replace
+ def replace position, payload
+ node = at position
+ if node
+ node.payload = payload
+ true
+ else
+ nil
+ end
+ end
+
+ # Seeds bracket based on `seed_order` value of bracket. Provide an iterator
+ # with players that will be inserted in the appropriate location. Will raise a
+ # SeedLimitExceededError if too many players are sent, and a NoSeedOrderError if
+ # the `seed_order` attribute is nil
+ #
+ # @param [Enumerable] players - players to be seeded
+ def seed players
+ if @seed_order.nil?
+ raise NoSeedOrderError, 'Bracket does not have a seed order.'
+ elsif players.size > @seed_order.size
+ raise SeedLimitExceededError, 'cannot seed more players than seed order list.'
+ else
+ @seed_order.each do |position|
+ replace position, players.shift
+ end
+ end
+ end
+
+ def winner
+ @root.payload
+ end
+
+ def each(&block)
+ in_order(@root, block)
+ end
+
+ def to_h
+ @root.to_h
+ end
+
+ # Array of Seats mapping to the individual positions of the bracket tree. The
+ # order of the nodes is important, as insertion in this order maintains the
+ # binary tree
+ #
+ # @return Array seats
+ def seats
+ entries.sort_by { |node| @insertion_order.index(node.position) }
+ end
+
+ alias_method :to_a, :seats
+
+ def at position
+ find { |n| n.position == position }
+ end
+
+ alias_method :size, :count
+
+ # Progresses the bracket by using the stored `matches` to copy data to the winning
+ # and losing seats. This facilitates match progression without manually
+ # manipulating bracket positions
+ #
+ # @param Fixnum seat - winning seat position
+ # @return Boolean result - result of progression
+ def match_winner seat
+ match = @matches.find { |m| m.include? seat }
+
+ if match
+ losing_seat = match.seats.find { |s| s != seat }
+
+ if match.winner_to
+ replace match.winner_to, at(seat).payload
+ end
+
+ if match.loser_to
+ replace match.loser_to, at(losing_seat).payload
+ end
+
+ return true
+ else
+ return false
+ end
+ end
+
+ # Inverse of `match_winner`, progresses the bracket based on seat. See `match_winner`
+ # for more details
+ #
+ # @param Fixnum seat - losing seat position
+ # @return Boolean result - result of progression
+ def match_loser seat
+ match = @matches.find { |m| m.include? seat }
+
+ if match
+ winning_seat = match.seats.find { |s| s != seat }
+ match_winner winning_seat
+ else
+ return false
+ end
+ end
+
+ def in_order(node, block)
+ if node
+ unless node.left.nil?
+ in_order(node.left, block)
+ end
+
+ block.call(node)
+
+ unless node.right.nil?
+ in_order(node.right, block)
+ end
+ end
+ end
+ end
+ end
+end
7 lib/bracket_tree/bracket/double_elimination.rb
View
@@ -0,0 +1,7 @@
+module BracketTree
+ module Bracket
+ class DoubleElimination < Base
+ template BracketTree::Template::DoubleElimination
+ end
+ end
+end
7 lib/bracket_tree/bracket/single_elimination.rb
View
@@ -0,0 +1,7 @@
+module BracketTree
+ module Bracket
+ class SingleElimination < Base
+ template BracketTree::Template::SingleElimination
+ end
+ end
+end
18 lib/bracket_tree/template.rb
View
@@ -53,22 +53,6 @@ def initialize
@matches = []
end
- # Generates a Bracket object with placeholder empty hashes for each Seat in the
- # Template
- #
- # @return [BracketTree::Bracket] bracket
- def generate_blank_bracket
- bracket = Bracket.new matches: @matches
-
- @seats.each do |position|
- bracket.add position, {}
- end
-
- bracket.seed_order = @starting_seats
-
- bracket
- end
-
# Returns hash representation of the Template
#
# @return [Hash] template
@@ -79,6 +63,8 @@ def to_h
matches: @matches
}
end
+
+ alias_method :seed_order, :starting_seats
end
end
end
2  lib/bracket_tree/templates/double_elimination.rb
View
@@ -1,7 +1,5 @@
module BracketTree
module Template
- # Template for double-elimination-based tournament formats. Uses the 'right' half
- # of the binary tree for the loser's bracket and the 'left' half for the winner's
class DoubleElimination < Base
class << self
def location
19 spec/unit/bracket_tree/bracket_spec.rb → spec/bracket_spec.rb
View
@@ -1,7 +1,8 @@
require 'spec_helper'
-describe BracketTree::Bracket do
- let(:bracket) { BracketTree::Bracket.new }
+describe BracketTree::Bracket::Base do
+ let(:bracket) { BracketTree::Bracket::Base.new }
+
describe '#initialize' do
it 'should create an array of Match objects if matches are passed' do
matches = [
@@ -9,7 +10,7 @@
{ seats: [5,7], winner_to: 4, loser_to: nil },
{ seats: [2,4], winner_to: 6, loser_to: nil }
]
- bracket = BracketTree::Bracket.new matches: matches
+ bracket = BracketTree::Bracket::Base.new matches: matches
bracket.matches.should be_a Array
bracket.matches.map(&:class).should == [BracketTree::Match, BracketTree::Match, BracketTree::Match]
@@ -19,6 +20,12 @@
bracket.matches.should be_a Array
bracket.matches.should == []
end
+
+ it 'should assign the seed order if seed_order is passed' do
+ expected = [1,3,5,7]
+ bracket = BracketTree::Bracket::Base.new seed_order: expected
+ bracket.seed_order.should == expected
+ end
end
describe '#add' do
@@ -88,7 +95,7 @@
end
describe '#seats' do
- let(:bracket) { BracketTree::Template::SingleElimination.by_size(4).generate_blank_bracket }
+ let(:bracket) { BracketTree::Bracket::SingleElimination.by_size 4 }
it 'should return 7 seats' do
bracket.seats.should have(7).seats
@@ -138,7 +145,7 @@
end
describe '#seed' do
- let(:bracket) { BracketTree::Template::SingleElimination.by_size(4).generate_blank_bracket }
+ let(:bracket) { BracketTree::Bracket::SingleElimination.by_size 4 }
let(:players) do
[
{ name: 'player4' },
@@ -170,7 +177,7 @@
end
describe '#match_winner' do
- let(:bracket) { BracketTree::Template::DoubleElimination.by_size(4).generate_blank_bracket }
+ let(:bracket) { BracketTree::Bracket::DoubleElimination.by_size 4 }
it 'copies the seat data to the seat specified in the match winner_to' do
bracket.at(1).payload[:seed_value] = 1
20 spec/template_spec.rb
View
@@ -55,24 +55,4 @@ def template_json
template.to_h.should == template_json
end
end
-
- describe '#generate_blank_bracket' do
- it 'should return a Bracket object with empty hashes in the seats' do
- template = TestTemplate.by_size 4
- bracket = template.generate_blank_bracket
-
- bracket.should be_a BracketTree::Bracket
- bracket.size.should == 7
- bracket.to_a.map { |n| n.payload }.should == [{}, {}, {}, {}, {}, {}, {}]
- end
-
- it 'should populate the matches from the template' do
- template = TestTemplate.by_size 4
- bracket = template.generate_blank_bracket
-
- bracket.matches.should be_a Array
- bracket.matches[0].should be_a BracketTree::Match
- bracket.matches[0].to_h.should == template.matches[0]
- end
- end
end
Please sign in to comment.
Something went wrong with that request. Please try again.