Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions app/models/competition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,25 @@ def players_per_heat(vessel_count)
return possibles[mods.find_index(max_mod)]
end

# An alternative to players_per_heat that spreads the deficit amongst the other heats (same as the local tournaments)
def players_per_heat_v2(vessel_count, limits)
possibles = (vessel_count > limits[1] && vessel_count < 2 * limits[0] - 1) ? Array(limits[1]/2..limits[1]) : Array(limits[0]..limits[1])
vessel_count = vessels.includes(:player).where(players: { is_human: true }).count unless vessel_count
mods = possibles.map { |e| vessel_count % e }
zero_index = mods.reverse.find_index(0)
if !zero_index.nil?
# zero means we can have even heats
return possibles.reverse[zero_index], 0
end
players_per_full_heat, heats_with_one_less = players_per_heat_better(vessel_count + 1, limits)
return players_per_full_heat, heats_with_one_less + 1
end

# Get the range of players per heat
def get_per_heat_limits()
return [6, max(8, max_players_per_heat)] rescue [6, 10]
end

def has_remaining_heats?(stage)
heats.where(stage: stage, ended_at: nil).any?
end
Expand Down
32 changes: 23 additions & 9 deletions lib/armory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def generate_heats(competition, stage, strategy, force=false)
strategy.apply!(competition, stage)
end

class GroupedStragegy
class GroupedStrategy
def generate_with_groups(competition, stage, groups)
groups.each.with_index do |g,k|
heat = competition.heats.where(stage: stage, order: k).first_or_create
Expand All @@ -16,31 +16,45 @@ def generate_with_groups(competition, stage, groups)
end
end
end

# Pass shuffled vessels for randomised, or ordered for ranked
def generate_groups(competition, limits, player_vessels)
vessel_count = player_vessels.length
players_per_full_heat, heats_with_one_less = competition.players_per_heat_v2(vessel_count, limits)
full_heats = (vessel_count / players_per_full_heat.to_f - heats_with_one_less).ceil
groups = full_heats > 0 ?
Array(0..full_heats - 1).map {|s| player_vessels.slice(s * players_per_full_heat, players_per_full_heat)}.concat(
Array(0..heats_with_one_less - 1).map {|s| player_vessels.slice(full_heats * players_per_full_heat + s * (players_per_full_heat - 1), players_per_full_heat - 1)}) :
player_vessels
return groups
end
end

class RandomDistributionStrategy < GroupedStragegy
class RandomDistributionStrategy < GroupedStrategy
def apply!(competition, stage)
npc_vessels = competition.vessels.includes(:player).where(players: { is_human: false }) rescue []
player_vessels = competition.vessels.includes(:player).where(players: { is_human: true })
players_per_heat = competition.players_per_heat(player_vessels.count)
puts "RandomDistribution for #{player_vessels.count} players in groups of #{players_per_heat}"
groups = player_vessels.shuffle.in_groups_of(players_per_heat, false).map { |g| g + npc_vessels }
limits = competition.get_per_heat_limits()
puts "RandomDistribution for #{player_vessels.count} players in groups of #{limits[0]}—#{limits[1]} with #{npc_vessels.length} npcs"
groups = generate_groups(competition, limits, player_vessels.shuffle)
groups = groups.map { |g| g + npc_vessels }
generate_with_groups(competition, stage, groups)
end
end

class TournamentRankingStrategy < GroupedStragegy
class TournamentRankingStrategy < GroupedStrategy
def apply!(competition, stage)
ranked_vessels = competition.rankings.includes(:vessel).sort_by { |e| e.score }.map(&:vessel)
npc_vessels = competition.vessels.includes(:player).where(players: { is_human: false })
player_vessels = ranked_vessels.filter { |v| v.player.is_human }
players_per_heat = competition.players_per_heat(player_vessels.count)
groups = player_vessels.in_groups_of(players_per_heat, false).map { |g| g + npc_vessels }
limits = competition.get_per_heat_limits()
groups = generate_groups(competition, limits, ranked_vessels)
groups = groups.map { |g| g + npc_vessels }
generate_with_groups(competition, stage, groups)
end
end

class SinglePlayerStrategy < GroupedStragegy
class SinglePlayerStrategy < GroupedStrategy
def apply!(competition, stage)
npc_vessels = competition.vessels.includes(:player).where(players: { is_human: false })
player_vessels = competition.vessels.includes(:player).where(players: { is_human: true })
Expand Down