olympic 0.0.1 → 0.0.2

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.
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