Init ladder prototype. Challenge stuff is still mostly broken.

This commit is contained in:
simplefl 2015-05-16 15:54:14 +02:00
parent 7abbf51145
commit 5e29684722
11 changed files with 185 additions and 104 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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}} }

View file

@ -9,30 +9,12 @@
<%= f.hidden_field :contester2_id %>
<div class="contentWide">
<% if @challenge.errors.empty? or @challenge.server %>
<% if @challenge.errors.empty? %>
<h3 class="center">
<%= namelink @challenge.contester1.team %>
vs
<%= namelink @challenge.contester2.team %>
</h3>
<p>
<%= f.label :match_time %><br />
<%= f.datetime_select :match_time %>
</p>
<p>
<%= f.check_box :mandatory %>
<%= f.label :mandatory %>
( Default time will be following Sunday <%= @challenge.contester1.contest.default_time.strftime("%H:%M") %> )
</p>
<p>
<%= f.label :server_id %><br />
<%= f.select :server_id, Server.hlds.active.collect{|c| [c.name, c.id]} %>
</p>
<p>
<%= f.label :map1_id, "Your Map" %><br />
<%= f.select :map1_id, @challenge.contester1.contest.maps.basic.collect{|m| [m.name, m.id]} %>
</p>
<p>
<%= f.label :details, "Information for the opponent" %><br />
<%= f.text_area :details, :cols => 40, :rows => 7 %>

View file

@ -0,0 +1,60 @@
<table class="contest striped">
<thead>
<tr>
<th class="rank">Rank</th>
<th class="movement"></th>
<th class="flag"></th>
<th class="team">Team</th>
<th class="awards"></th>
<th class="win">Win</th>
<th class="loss">Loss</th>
<th class="draw">Draw</th>
<% if actions and cuser %>
<th class="actions"></th>
<% end %>
</tr>
</thead>
<tbody>
<% contesters = contest.contesters.active.ranked
contesters.each_with_index do |contester, rank| %>
<tr>
<td><%= h rank + 1%>.</td>
<% if contester.trend == Contester::TREND_UP %>
<td><%= icon 'chevron-up' %></td>
<% elsif contester.trend == Contester::TREND_DOWN %>
<td><%= icon 'chevron-down' %></td>
<% elsif contester.trend == Contester::TREND_FLAT %>
<td><%= icon 'minus' %></td>
<% else %>
<td></td>
<% end %>
<td><%= flag contester.team.country %></td>
<td><%= link_to (h contester.team.name), contester %></td>
<td><%= icon 'trophy' if contester == contester.contest.winner %></td>
<td><%= h contester.win %></td>
<td><%= h contester.loss %></td>
<td><%= h contester.draw %></td>
<% if actions and cuser%>
<td class="actions">
<% 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 %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>

View file

@ -9,10 +9,17 @@
<%= f.label :extra, "Extra Points" %>
<%= f.text_field :extra %>
</div>
<div class="fields horizontal">
<%= f.label :score %>
<%= f.text_field :score %>
</div>
<% if @contester.contest.contest_type == Contest::TYPE_LADDER %>
<div class="fields horizontal">
<%= f.label :rank %>
<%= f.text_field :score %>
</div>
<% else %>
<div class="fields horizontal">
<%= f.label :score %>
<%= f.text_field :score %>
</div>
<% end %>
<div class="fields horizontal">
<%= f.label :win %>
<%= f.text_field :win %>

View file

@ -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 %>

View file

@ -11,7 +11,10 @@
<div class="wide centered">
<% 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 %>
</div>

View file

@ -20,9 +20,6 @@
<dd>Sunday: <%= Time.use_zone(timezone_offset) { @contest.default_time.strftime("%H:%M %Z") } %></dd>
</dl>
<% 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 @@
<div class="standings">
<% 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 %>