olympic 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 137ae1004f177db1923172ff3f9083860d361871
4
- data.tar.gz: ddfa7208b78f8040eb5d8bd3faf1257b871d6aba
3
+ metadata.gz: 8fc5a712fa617f6848bd48f1884a92edb90e861e
4
+ data.tar.gz: 70b248949c0213508364d451bc79ce7156001f83
5
5
  SHA512:
6
- metadata.gz: c7c46cb6bb92473cee61f00ee75ffda261c4232f4bbb89fd6dc817cf57d170666a38cfe4541308ec6e4bd89eaa3719c372d9ac3fc93a194e08b5dc17b4c43ec7
7
- data.tar.gz: 8a1203bceb68cd9690758f5d8a22ce29d9c590ec0fcf259f06d13586a16e051358df7afa041dff90410ce7cb344c9883f96425168bc50321359a83bd72ae3af5
6
+ metadata.gz: ad34729ac639b8086370cead8746cf4ba2dc24162b5ae0c74b113dc70013c99852d4fe011311d578531468940934669e43a3333cc345a53df8e7f5db68783c47
7
+ data.tar.gz: 238d2fd84dc60ae9984f77417cf83fb56032e171a37f0e459bcbd94162467c0a8a59b0a78ea7b24b1ef4cfc971c2d2c886574d047bc5fa1b85922452da58a938
@@ -0,0 +1,17 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
3
+
4
+ # Custom for Visual Studio
5
+ *.cs diff=csharp
6
+
7
+ # Standard to msysgit
8
+ *.doc diff=astextplain
9
+ *.DOC diff=astextplain
10
+ *.docx diff=astextplain
11
+ *.DOCX diff=astextplain
12
+ *.dot diff=astextplain
13
+ *.DOT diff=astextplain
14
+ *.pdf diff=astextplain
15
+ *.PDF diff=astextplain
16
+ *.rtf diff=astextplain
17
+ *.RTF diff=astextplain
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ -f d
3
+ -r spec_helper
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in olympic.gemspec
4
4
  gemspec
5
+
6
+ gem "sqlite3"
7
+ gem "coveralls", require: false
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 medcat
1
+ Copyright (c) 2014 Jeremy Rodi
2
2
 
3
3
  MIT License
4
4
 
data/Rakefile CHANGED
@@ -1,2 +1,4 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
2
3
 
4
+ RSpec::Core::RakeTask.new('spec')
@@ -0,0 +1,22 @@
1
+ module Olympic
2
+ module Generators
3
+ class InstallGenerator < ::Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+ desc "Create a migration to add olympic tables."
7
+ source_root File.expand_path("../templates", __FILE__)
8
+
9
+ # Implement the required interface for Rails::Generators::Migration.
10
+ # This is _literally_ documented _nowhere_. Wtf.
11
+ def self.next_migration_number(dirname)
12
+ next_migration_number = current_migration_number(dirname) + 1
13
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
14
+ end
15
+
16
+ def generate_migration
17
+ migration_template "create_olympic_tables.rb", "db/migrate/create_olympic_tables.rb"
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ class CreateOlympicTables < ActiveRecord::Migration
2
+
3
+ def change
4
+ create_table :teams do |t|
5
+ # edit this.
6
+ <%- if Olympic::Settings.rating_system -%>
7
+ <%- Olympic::Settings.rating_system.required_fields.each do |key, value| -%>
8
+ t.<%= value.first %> <%= key.inspect %>, <%= value.last.inspect[1..-2] %>
9
+ <%- end -%>
10
+ <%- end -%>
11
+ raise NotImplementedError, "Update teams table in #{__FILE__}"
12
+ end
13
+
14
+ create_table :tournaments do |t|
15
+ t.string :bracket
16
+ t.integer :root_id
17
+ end
18
+
19
+ create_table :teams_tournaments, id: false do |t|
20
+ t.integer :team_id
21
+ t.integer :tournament_id
22
+ end
23
+
24
+ create_table :matches_teams, id: false do |t|
25
+ t.integer :match_id
26
+ t.integer :team_id
27
+ end
28
+
29
+ create_table :sources do |t|
30
+ t.integer :match_id
31
+ t.integer :source_id
32
+ t.string :source_type
33
+ end
34
+
35
+ create_table :matches do |t|
36
+ t.integer :tournament_id
37
+ t.integer :winner_id
38
+ t.integer :depth
39
+ t.integer :offset
40
+ t.integer :value
41
+ t.text :data
42
+ end
43
+ end
44
+
45
+ end
@@ -1,3 +1,9 @@
1
+ require "olympic/match"
2
+ require "olympic/settings"
3
+ require "olympic/rating"
4
+ require "olympic/team"
5
+ require "olympic/source"
6
+ require "olympic/tournament"
1
7
  require "olympic/version"
2
8
 
3
9
  module Olympic
@@ -0,0 +1,20 @@
1
+ require 'olympic/bracket/base'
2
+ require 'olympic/bracket/single_elimination'
3
+ require 'olympic/bracket/double_elimination'
4
+ require 'olympic/bracket/round_robin'
5
+
6
+ module Olympic
7
+ module Bracket
8
+
9
+ BRACKETS = {
10
+ single_elimination: SingleElimination,
11
+ double_elimination: DoubleElimination,
12
+ round_robin: RoundRobin
13
+ }
14
+
15
+ def self.for(name, tournament, teams)
16
+ BRACKETS.fetch(name).new(tournament, teams)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,87 @@
1
+ require 'olympic/bracket/single_elimination'
2
+
3
+ module Olympic
4
+ module Bracket
5
+ # A basic bracket. This defines the basic API that all brackets
6
+ # should have. In order for a bracket to be compatible with
7
+ # Olympic, only these things need to be implemented.
8
+ class Base
9
+
10
+ # Initialize the bracket.
11
+ #
12
+ # @param tournament [Olympic::Tournament]
13
+ # @param teams [Array<Olympic::Team>]
14
+ def initialize(tournament, teams)
15
+ @tournament = tournament
16
+ @teams = teams
17
+ end
18
+
19
+ # @!method call
20
+ # Initializes the tournament to handle the bracket. Sets up
21
+ # all matches and sources in accordance to the bracket
22
+ # itself.
23
+ #
24
+ # @return [void]
25
+ #
26
+ # @!method matches
27
+ # Returns the number of expected matches in the bracket. If
28
+ # the number of matches is variable, it will return an array
29
+ # of numbers, which contains potential values for the number
30
+ # of matches in the bracket.
31
+ #
32
+ # @return [Numeric, Array<Numeric>]
33
+ #
34
+ # @!method rounds
35
+ # Returns the number of expected rounds in the bracket. If
36
+ # the number of rounds is variable, it will return an array
37
+ # of numbers, which contains potential values for the number
38
+ # of rounds in the bracket.
39
+ #
40
+ # @return [Numeric, Array<Numeric>]
41
+ #
42
+ # @!method rounded_teams
43
+ # In an elimination tournament, the number of teams has to be
44
+ # rounded to the nearest power of two to calculate the number
45
+ # of byes. If this is not necessary, it will return the
46
+ # number of teams.
47
+ #
48
+ # @return [Numeric]
49
+ #
50
+ # @!method byes
51
+ # The number of byes that are required in the bracket. This
52
+ # should be greater than or equal to zero.
53
+ #
54
+ # @return [Numeric]
55
+ #
56
+ # @!method first_round_matches
57
+ # The number of matches in the first round. This will always
58
+ # be a finite number, so this method will never return an
59
+ # array of numbers.
60
+ #
61
+ # @return [Numeric]
62
+ %(call matches rounds rounded_teams byes
63
+ first_round_matches).map(&:to_sym).each do |name|
64
+ define_method(name) do
65
+ _not_implemented_error(name)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # Raises an error stating that the given method wasn't
72
+ # implemented. This is the default action the API methods on
73
+ # this class takes; a subclass should redefine the methods to
74
+ # perform an action.
75
+ #
76
+ # @note This should not be used outside of the base class. If
77
+ # A `NotImplementedError` is to be raised outside of the base
78
+ # class, it should be raised with `raise`.
79
+ # @raise [NotImplementError] on call.
80
+ # @return [void]
81
+ def _not_implemented_error(name)
82
+ raise NotImplementedError, "`#{name}` is not implemented " \
83
+ "on `#{self.class}`"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,54 @@
1
+ require 'memoist'
2
+ require 'olympic/bracket/single_elimination/information'
3
+
4
+ module Olympic
5
+ module Bracket
6
+
7
+ # A single elimination tournament. A single elimination
8
+ # tournament is a tournament of a set of teams in which the
9
+ # first loss results in elimination from the tournament. Single
10
+ # elimination tournaments have the problem in which when the
11
+ # number of teams is not equal to a power of two, at least one bye
12
+ # is required.
13
+ class SingleElimination < Base
14
+ extend Memoist
15
+ include Information
16
+
17
+ # Creates a single elimination tournament, by setting up the
18
+ # needed matches (and their respective sources).
19
+ def call
20
+ matches = pair(initial_sources)
21
+ @tournament.root = matches.sort_by(&:depth).last
22
+ end
23
+
24
+ private
25
+
26
+ def pair(incoming, depth = 1)
27
+ round = incoming.each_slice(2).each_with_index.
28
+ map { |pair, i|
29
+ match = Settings.class_for(:match).
30
+ create(incoming: pair,
31
+ tournament: @tournament,
32
+ depth: depth,
33
+ offset: i) }
34
+
35
+ if round.length > 1
36
+ outgoing = round.
37
+ map { |match| Settings.class_for(:source).create(source: match) }
38
+ round + pair(outgoing, depth + 1)
39
+ else
40
+ round
41
+ end
42
+ end
43
+
44
+ # The initial sources. The "sources" in this case are the
45
+ # teams, which are mapped to an actual {Olympic::Source}.
46
+ #
47
+ # @return [Array<Olympic::Source>] The initial sources.
48
+ def initial_sources
49
+ @teams.map { |team| Settings.class_for(:source).create(source: team) }
50
+ end
51
+ memoize :initial_sources
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+ require 'memoist'
2
+
3
+ module Olympic
4
+ module Bracket
5
+ class SingleElimination
6
+
7
+ # General information about the tournament.
8
+ #
9
+ # @see Base
10
+ module Information
11
+ extend Memoist
12
+
13
+ # (see Base#matches)
14
+ def matches
15
+ @teams.size - 1
16
+ end
17
+ memoize :matches
18
+
19
+ # (see Base#rounds)
20
+ def rounds
21
+ Math.log2(@teams.size).ceil
22
+ end
23
+ memoize :rounds
24
+
25
+ # (see Base#rounded_teams)
26
+ def rounded_teams
27
+ 2 ** rounds
28
+ end
29
+ memoize :rounded_teams
30
+
31
+ # (see Base#byes)
32
+ def byes
33
+ rounded_teams - @teams.size
34
+ end
35
+ memoize :byes
36
+
37
+ # (see Base#first_round_matches)
38
+ def first_round_matches
39
+ @teams.size - 2 ** Math.log2(@teams.size).floor
40
+ end
41
+ memoize :first_round_matches
42
+ end
43
+ end
44
+ end
45
+ end
File without changes
@@ -0,0 +1,17 @@
1
+ module Olympic
2
+ module Coordinate
3
+ class Basic
4
+
5
+ # Initializes the coordinate.
6
+ #
7
+ # @param x [Numeric]
8
+ # @param y [Numeric]
9
+ # @param z [Numeric]
10
+ def initialize(x, y, z)
11
+ @x = x
12
+ @y = y
13
+ @z = z
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Olympic
2
+ class Error < StandardError; end
3
+
4
+ module Rating
5
+ class Error < Olympic::Error; end
6
+ end
7
+ end
@@ -0,0 +1,71 @@
1
+ module Olympic
2
+ module Match
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ belongs_to :tournament,
8
+ class_name: Olympic::Settings[:tournament_class],
9
+ foreign_key: Olympic::Settings[:match_tournament_key],
10
+ primary_key: Olympic::Settings[:match_primary_key]
11
+ # An outgoing source is a source that this match is a part of.
12
+ # There are many of these, unfortunately.
13
+ has_many :outgoing,
14
+ class_name: Olympic::Settings[:source_class],
15
+ foreign_key: Olympic::Settings[:source_source_key],
16
+ foreign_type: Olympic::Settings[:source_source_type],
17
+ primary_key: Olympic::Settings[:match_primary_key],
18
+ as: :sources
19
+ # An incoming source is a source that this match is owning.
20
+ # These are the sources that will participate in the match.
21
+ has_many :incoming,
22
+ class_name: Olympic::Settings[:source_class],
23
+ foreign_key: Olympic::Settings[:source_match_key],
24
+ primary_key: Olympic::Settings[:match_primary_key]
25
+ belongs_to :winner,
26
+ class_name: Olympic::Settings[:team_class],
27
+ foreign_key: Olympic::Settings[:match_winner_key],
28
+ primary_key: Olympic::Settings[:team_primary_key]
29
+ end
30
+
31
+ # If this match is ready to commence. This means that the
32
+ # participants are defined.
33
+ #
34
+ # @return [Boolean]
35
+ def ready?
36
+ !!participants
37
+ end
38
+
39
+ # If this match has a winner defined; or, in other words, is
40
+ # completed.
41
+ #
42
+ # @return [Boolean]
43
+ def completed?
44
+ winner?
45
+ end
46
+
47
+ # Returns the teams that are a part of the match. If the teams
48
+ # cannot be decided, it will return nil.
49
+ #
50
+ # @return [Array<Olympic::Team>, nil]
51
+ def participants
52
+ winners = incoming.includes(:source).map do |income|
53
+ source = income.source
54
+ case source
55
+ when Settings.class_for(:team)
56
+ source
57
+ when Settings.class_for(:match)
58
+ source.winner
59
+ else
60
+ raise Olympic::Error, "Unknown source #{source}"
61
+ end
62
+ end
63
+
64
+ if winners.any? { |winner| winner == nil }
65
+ nil
66
+ else
67
+ winners
68
+ end
69
+ end
70
+ end
71
+ end