From 5e296847223f3e94b9a35efb1590dae21f566dfc Mon Sep 17 00:00:00 2001 From: simplefl Date: Sat, 16 May 2015 15:54:14 +0200 Subject: [PATCH] Init ladder prototype. Challenge stuff is still mostly broken. --- app/controllers/challenges_controller.rb | 8 +- app/controllers/contesters_controller.rb | 16 ++- app/models/challenge.rb | 118 +++++++++++------------ app/models/contest.rb | 32 +++--- app/models/contester.rb | 2 + app/views/challenges/new.html.erb | 20 +--- app/views/contesters/_ladder.html.erb | 60 ++++++++++++ app/views/contesters/edit.html.erb | 15 ++- app/views/contests/current.html.erb | 7 +- app/views/contests/historical.html.erb | 5 +- app/views/contests/show.html.erb | 6 +- 11 files changed, 185 insertions(+), 104 deletions(-) create mode 100644 app/views/contesters/_ladder.html.erb diff --git a/app/controllers/challenges_controller.rb b/app/controllers/challenges_controller.rb index b652cee..240b1b7 100644 --- a/app/controllers/challenges_controller.rb +++ b/app/controllers/challenges_controller.rb @@ -18,10 +18,13 @@ class ChallengesController < ApplicationController end def new + #No new challenges for now. + raise AccessError @challenge = Challenge.new @challenge.user = cuser @challenge.contester2 = Contester.active.find params[:id] - @challenge.get_contester1 + contest = @challenge.contester2.contest + @challenge.contester1 = @challenge.user.active_contesters.of_contest(contest).first raise AccessError unless @challenge.can_create? cuser end @@ -62,7 +65,8 @@ class ChallengesController < ApplicationController def destroy raise AccessError unless @challenge.can_destroy? cuser @challenge.destroy - return_to + #return_to FIX ME from challenge side + render text: t(:challenges_cleared) end private diff --git a/app/controllers/contesters_controller.rb b/app/controllers/contesters_controller.rb index 9818859..5c8f376 100644 --- a/app/controllers/contesters_controller.rb +++ b/app/controllers/contesters_controller.rb @@ -22,6 +22,9 @@ class ContestersController < ApplicationController @contester = Contester.new params[:contester] @contester.user = cuser raise AccessError unless @contester.can_create? cuser + if @contester.contest.contest_type == Contest::TYPE_LADDER + @contester.score = @contester.contest.contesters.active.count + 1 + end if @contester.save flash[:notice] = t(:contests_join) @@ -34,9 +37,20 @@ class ContestersController < ApplicationController def update raise AccessError unless @contester.can_update? cuser + + if @contester.contest.contest_type == Contest::TYPE_LADDER + old_rank = @contester.score + new_rank = params[:contester][:score].to_i + raise Error, t(:rank_invalid) unless new_rank > 0 and + new_rank <= @contester.contest.contesters.active.count + if old_rank != new_rank + @contester.contest.update_ranks(@contester, old_rank, new_rank) + end + end + if @contester.update_attributes params[:contester] flash[:notice] = t(:contests_contester_update) - redirect_to @contester + redirect_to @contester.contest else render :edit end diff --git a/app/models/challenge.rb b/app/models/challenge.rb index c55b289..93be423 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -36,7 +36,7 @@ class Challenge < ActiveRecord::Base attr_protected :id, :updated_at, :created_at, :default_time, :user_id, :status - validates_presence_of :contester1, :contester2, :server, :map1 + validates_presence_of :contester1, :contester2 validates_presence_of :map2, :on => :update #validates_datetime :match_time, :default_time #validates_length_of [:details, :response], :maximum => 255, :allow_blank => true, :allow_nil => true @@ -111,102 +111,102 @@ class Challenge < ActiveRecord::Base def validate_teams if contester1.team == contester2.team - errors.add_to_base I18n.t(:challenges_yourself) + errors.add :base, I18n.t(:challenges_yourself) end if contester1.contest != contester2.contest - errors.add_to_base I18n.t(:challenges_opponent_contest) + errors.add :base, I18n.t(:challenges_opponent_contest) end if !contester2.active or !contester2.team.active - errors.add_to_base I18n.t(:challenges_opponent_inactive) + errors.add :base, I18n.t(:challenges_opponent_inactive) end if !contester1.active or !contester1.team.active - errors.add_to_base I18n.t(:challenges_inactive) + errors.add :base, I18n.t(:challenges_inactive) end end def validate_contest if contester1.contest.end.past? or contester1.contest.status == Contest::STATUS_CLOSED - errors.add_to_base I18n.t(:contests_closed) + errors.add :base, I18n.t(:contests_closed) end if contester1.contest.contest_type != Contest::TYPE_LADDER and !match - errors.add_to_base I18n.t(:contests_notladder) + errors.add :base, I18n.t(:contests_notladder) end end def validate_mandatory - return unless mandatory + # return unless mandatory - if contester2.score < contester1.score - errors.add_to_base I18n.t(:challenges_mandatory) - end - if Challenge.pending.count(:conditions => \ - ["contester1_id = ? AND contester2_id = ? AND mandatory = true AND default_time < UTC_TIMESTAMP()", - contester1.id, contester2.id]) > 0 - errors.add_to_base I18n.t(:challenges_mandatory_handled) - end - if Match.of_contester(contester2).on_week(match_time).count > 0 - errors.add_to_base I18n.t(:challenges_opponent_week) - end - if Challenge.of_contester(contester2).mandatory.on_week(match_time).count > 0 - errors.add_to_base I18n.t(:challenges_opponent_mandatory_week) - end - if Challenge.of_contester(contester2).mandatory.on_week(default_time).count > 0 - errors.add_to_base I18n.t(:challenges_opponent_mandatory_week_defaulttime) - end - if Match.of_contester(contester2).around(default_time).count > 0 - errors.add_to_base I18n.t(:challenges_opponent_defaulttime) - end + # if contester2.score < contester1.score + # errors.add :base, I18n.t(:challenges_mandatory) + # end + # if Challenge.pending.count(:conditions => \ + # ["contester1_id = ? AND contester2_id = ? AND mandatory = true AND default_time < UTC_TIMESTAMP()", + # contester1.id, contester2.id]) > 0 + # errors.add :base, I18n.t(:challenges_mandatory_handled) + # end + # if Match.of_contester(contester2).on_week(match_time).count > 0 + # errors.add :base, I18n.t(:challenges_opponent_week) + # end + # if Challenge.of_contester(contester2).mandatory.on_week(match_time).count > 0 + # errors.add :base, I18n.t(:challenges_opponent_mandatory_week) + # end + # if Challenge.of_contester(contester2).mandatory.on_week(default_time).count > 0 + # errors.add :base, I18n.t(:challenges_opponent_mandatory_week_defaulttime) + # end + # if Match.of_contester(contester2).around(default_time).count > 0 + # errors.add :base, I18n.t(:challenges_opponent_defaulttime) + # end end def validate_match_time - if (match_time-get_margin).past? - if get_margin > 86400 - errors.add_to_base I18n.t(:matches_time1) + get_margin / 60 / 60 / 24 + I18n.t(:matches_time2) - else - errors.add_to_base I18n.t(:matches_time1) + get_margin / 60 + I18n.t(:matches_time3) - end - end - if Challenge.of_contester(contester2).around(match_time).pending.count > 0 - errors.add_to_base I18n.t(:challenges_opponent_specifictime) - end - if Match.of_contester(contester2).around(match_time).count > 0 - errors.add_to_base I18n.t(:challenges_opponent_match_specifictime) - end - if match_time > contester1.contest.end - errors.add_to_base I18n.t(:contests_end) - end +# if (match_time-get_margin).past? +# if get_margin > 86400 +# errors.add :base, I18n.t(:matches_time1) + get_margin / 60 / 60 / 24 + I18n.t(:matches_time2) +# else +# errors.add :base, I18n.t(:matches_time1) + get_margin / 60 + I18n.t(:matches_time3) +# end +# end +# if Challenge.of_contester(contester2).around(match_time).pending.count > 0 +# errors.add :base, I18n.t(:challenges_opponent_specifictime) +# end +# if Match.of_contester(contester2).around(match_time).count > 0 +# errors.add :base, I18n.t(:challenges_opponent_match_specifictime) +# end +# if match_time > contester1.contest.end +# errors.add :base, I18n.t(:contests_end) +# end end def validate_server - unless server and server.official - errors.add_to_base I18n.t(:servers_notavailable) - end - unless server.is_free match_time - errors.add_to_base I18n.t(:servers_notfree_specifictime) - end - if !server.is_free default_time - errors.add_to_base I18n.t(:servers_notfree_defaulttime) - end + # unless server and server.official + # errors.add :base, I18n.t(:servers_notavailable) + # end + # unless server.is_free match_time + # errors.add :base, I18n.t(:servers_notfree_specifictime) + # end + # if !server.is_free default_time + # errors.add :base, I18n.t(:servers_notfree_defaulttime) + # end end def validate_map1 unless contester1.contest.maps.exists?(map1) - errors.add_to_base I18n.t(:contests_map_notavailable) + errors.add :base, I18n.t(:contests_map_notavailable) end end def validate_map2 unless contester2.contest.maps.exists?(map2) - errors.add_to_base I18n.t(:contests_map_notavailable) + errors.add :base, I18n.t(:contests_map_notavailable) end end def validate_status if mandatory and ![STATUS_ACCEPTED, STATUS_DEFAULT, STATUS_FORFEIT].include? status - errors.add_to_base I18n.t(:challenges_mandatory_invalidresult) + errors.add :base, I18n.t(:challenges_mandatory_invalidresult) end unless statuses.include? status - errors.add_to_base I18n.t(:challenges_mandatory_invalidresult) + errors.add :base, I18n.t(:challenges_mandatory_invalidresult) end end @@ -251,10 +251,10 @@ class Challenge < ActiveRecord::Base end def can_update? cuser - cuser and (contester2.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING and autodefault.future? + cuser and (contester2.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING# and autodefault.future? end def can_destroy? cuser - cuser and (contester1.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING and autodefault.future? + cuser and (contester1.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING# and autodefault.future? end end diff --git a/app/models/contest.rb b/app/models/contest.rb index bb2ebf2..33da2c2 100644 --- a/app/models/contest.rb +++ b/app/models/contest.rb @@ -107,23 +107,27 @@ class Contest < ActiveRecord::Base end end - def elo_score score1, score2, diff, level = self.modulus_base, weight = self.weight, moduluses = [self.modulus_even, self.modulus_3to1, self.modulus_4to0] - points = (score2-score1).abs - modulus = moduluses[0].to_f if points <= 1 - modulus = moduluses[1].to_f if points == 2 - modulus = moduluses[2].to_f if points >= 3 - if score1 == score2 - result = 0.5 - elsif score1 > score2 - result = 1.0 - elsif score2 > score1 - result = 0.0 + def update_ranks contester, old_rank, new_rank + if old_rank < new_rank + Contester.update_all(["score = score -1, trend = ?", Contester::TREND_UP], + ["contest_id = ? and score > ? and score <= ?", + self.id, old_rank, new_rank]) + contester.trend = Contester::TREND_DOWN + elsif old_rank > new_rank + Contester.update_all(["score = score + 1, trend = ?", Contester::TREND_DOWN], + ["contest_id = ? and score < ? and score >= ?", + self.id, old_rank, new_rank]) + contester.trend = Contester::TREND_UP end - prob = 1.0/(10**(diff.to_f/weight.to_f)+1.0) - total = (level.to_f*modulus*(result-prob)).round - return total + contester.score = new_rank end + def ladder_ranks_unique? + c = Contester.where({:contest_id => self.id}) + c.uniq.pluck(:score).count == c.count + end + + def can_create? cuser cuser and cuser.admin? end diff --git a/app/models/contester.rb b/app/models/contester.rb index 99bf2f2..eafd233 100644 --- a/app/models/contester.rb +++ b/app/models/contester.rb @@ -27,6 +27,8 @@ class Contester < ActiveRecord::Base attr_accessor :user scope :active, :include => :team, :conditions => {"contesters.active" => true} + # ranked is used for ladder. lower score the higher the rank + scope :ranked, :select => "contesters.*", :order => "score ASC, win DESC, loss ASC" scope :ordered, :select => "contesters.*, (score + extra) AS total_score", :order => "total_score DESC, score DESC, win DESC, loss ASC" scope :chronological, :order => "created_at DESC" scope :of_contest, lambda { |contest| {:conditions => {"contesters.contest_id" => contest.id}} } diff --git a/app/views/challenges/new.html.erb b/app/views/challenges/new.html.erb index 9045e92..7267baa 100644 --- a/app/views/challenges/new.html.erb +++ b/app/views/challenges/new.html.erb @@ -9,30 +9,12 @@ <%= f.hidden_field :contester2_id %>
- <% if @challenge.errors.empty? or @challenge.server %> + <% if @challenge.errors.empty? %>

<%= namelink @challenge.contester1.team %> vs <%= namelink @challenge.contester2.team %>

- -

- <%= f.label :match_time %>
- <%= f.datetime_select :match_time %> -

-

- <%= f.check_box :mandatory %> - <%= f.label :mandatory %> - ( Default time will be following Sunday <%= @challenge.contester1.contest.default_time.strftime("%H:%M") %> ) -

-

- <%= f.label :server_id %>
- <%= f.select :server_id, Server.hlds.active.collect{|c| [c.name, c.id]} %> -

-

- <%= f.label :map1_id, "Your Map" %>
- <%= f.select :map1_id, @challenge.contester1.contest.maps.basic.collect{|m| [m.name, m.id]} %> -

<%= f.label :details, "Information for the opponent" %>
<%= f.text_area :details, :cols => 40, :rows => 7 %> diff --git a/app/views/contesters/_ladder.html.erb b/app/views/contesters/_ladder.html.erb new file mode 100644 index 0000000..4980141 --- /dev/null +++ b/app/views/contesters/_ladder.html.erb @@ -0,0 +1,60 @@ + + + + + + + + + + + + <% if actions and cuser %> + + <% end %> + + + + + <% contesters = contest.contesters.active.ranked + contesters.each_with_index do |contester, rank| %> + + + <% if contester.trend == Contester::TREND_UP %> + + <% elsif contester.trend == Contester::TREND_DOWN %> + + <% elsif contester.trend == Contester::TREND_FLAT %> + + <% else %> + + <% end %> + + + + + + + <% if actions and cuser%> + + <% end %> + + <% end %> + +
RankTeamWinLossDraw
<%= h rank + 1%>.<%= icon 'chevron-up' %><%= icon 'chevron-down' %><%= icon 'minus' %><%= flag contester.team.country %><%= link_to (h contester.team.name), contester %><%= icon 'trophy' if contester == contester.contest.winner %><%= h contester.win %><%= h contester.loss %><%= h contester.draw %> + <% if false %> + <% current = cuser.active_contesters.of_contest(contest).first %> + <% if current %> + <% challenge = Challenge.new + challenge.contester1 = current + challenge.contester2 = contester %> + <% if challenge.can_create? cuser %> + <%= link_to 'C', controller: 'challenges', id: contester, action: 'new' %> + <% end %> + <% end %> + <% end %> + <% if cuser and cuser.admin? %> + <%= link_to icon('pencil'), edit_contester_path(contester) %> + <%= link_to icon('times'), contester, confirm: 'Are you sure?', method: :delete %> + <% end %> +
diff --git a/app/views/contesters/edit.html.erb b/app/views/contesters/edit.html.erb index 5e90b6c..f6e01ea 100644 --- a/app/views/contesters/edit.html.erb +++ b/app/views/contesters/edit.html.erb @@ -9,10 +9,17 @@ <%= f.label :extra, "Extra Points" %> <%= f.text_field :extra %>

-
- <%= f.label :score %> - <%= f.text_field :score %> -
+ <% if @contester.contest.contest_type == Contest::TYPE_LADDER %> +
+ <%= f.label :rank %> + <%= f.text_field :score %> +
+ <% else %> +
+ <%= f.label :score %> + <%= f.text_field :score %> +
+ <% end %>
<%= f.label :win %> <%= f.text_field :win %> diff --git a/app/views/contests/current.html.erb b/app/views/contests/current.html.erb index 80bb409..b58ffd6 100644 --- a/app/views/contests/current.html.erb +++ b/app/views/contests/current.html.erb @@ -5,7 +5,12 @@ <% if contest.contest_type == Contest::TYPE_BRACKET %> <%= render partial: "bracket", locals: { contest: contest } %> + <% elsif contest.contest_type == Contest::TYPE_LADDER %> + <%= render partial: "contesters/ladder", + locals: { actions: true, contest: contest } %> <% else %> - <%= render partial: "contesters/list", locals: { contesters: contest.contesters.active.ordered, actions: true } %> + <%= render partial: "contesters/list", + locals: { contesters: contest.contesters.active.ordered, + actions: true, contest: contest } %> <% end %> <% end %> diff --git a/app/views/contests/historical.html.erb b/app/views/contests/historical.html.erb index 3e85395..ca40333 100644 --- a/app/views/contests/historical.html.erb +++ b/app/views/contests/historical.html.erb @@ -11,7 +11,10 @@
<% if contest.contest_type == Contest::TYPE_BRACKET %> <%= render :partial => "bracket", :locals => {:contest => contest} %> - <% else %> + <% elsif contest.contest_type == Contest::TYPE_LADDER %> + <%= render partial: "contesters/ladder", + locals: { actions: false, contest: contest } %> + <% else %> <%= render :partial => "contesters/list", :locals => {:contesters => contest.contesters.active.ordered, :actions => false} %> <% end %>
diff --git a/app/views/contests/show.html.erb b/app/views/contests/show.html.erb index dac3088..106bd3b 100644 --- a/app/views/contests/show.html.erb +++ b/app/views/contests/show.html.erb @@ -20,9 +20,6 @@
Sunday: <%= Time.use_zone(timezone_offset) { @contest.default_time.strftime("%H:%M %Z") } %>
- <% if @contest.contest_type == Contest::TYPE_LADDER %> - <%= link_to 'Scoring', "/contests/#{@contest}/score", class: 'button' %> - <% end %> <% if cuser and cuser.admin? %> <%= link_to 'Edit Contest', edit_contest_path(@contest), class: 'button' %> <% end %> @@ -32,6 +29,9 @@
<% if @contest.contest_type == Contest::TYPE_BRACKET %> <%= render partial: 'bracket', locals: { contest: @contest } %> + <% elsif @contest.contest_type == Contest::TYPE_LADDER %> + <%= render partial: "contesters/ladder", + locals: { actions: true, contest: @contest } %> <% else %> <%= render partial: 'normal' %> <% end %>