Created
August 24, 2016 18:54
-
-
Save meew0/781b9547dc125f7878459ee503109417 to your computer and use it in GitHub Desktop.
Elgyem .battle command
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Elgyem::Battle | |
extend ElgyemCommand | |
# OU, UU, RU, NU, PU, Ubers, BSS - or singles for all | |
# BSD, VGC, DOU, DUU, DUbers - or doubles for all | |
# BST, TOU - or triples for all | |
# BSS, BSD, BST - or battlespot for all | |
# OU, UU, RU, NU, PU, Ubers - or smogon for all | |
# Random | |
BATTLESPOT = [:bss, :bsd, :bst].freeze | |
TIER_ALIASES = { | |
singles: [:ou, :uu, :ru, :nu, :pu, :ubers, :bss], | |
doubles: [:bsd, :vgc, :dou, :duu, :dubers], | |
triples: [:bst, :tou], | |
battlespot: BATTLESPOT, | |
bs: BATTLESPOT, | |
battlespotsingles: :bss, | |
battlespotdoubles: :bsd, | |
battlespottriples: :bst, | |
doublesou: :dou, | |
doublesuu: :duu, | |
doublesubers: :dubers, | |
triplesou: :tou, | |
smogon: [:ou, :uu, :ru, :nu, :pu, :ubers], | |
random: :random # maps to nothing but still needs to appear in tier list | |
} | |
ALL_TIERS = (TIER_ALIASES.keys + TIER_ALIASES.values).flatten.uniq | |
TIER_ALIASES[:all] = TIER_ALIASES.values.flatten.uniq # make sure `:all` is handled correctly, i. e. resolves to all tiers (exclude the aliases like `:smogon` though) | |
ALL_TIERS << :all # it still needs to appear in the tier list | |
GAME_NAMES = { | |
ps: 'Pokémon Showdown', | |
cart: 'Cartridge', | |
any: '[Any game]' | |
}.freeze | |
@requests = {} | |
def self.requests | |
@requests | |
end | |
def self.process_aliases(request) | |
# We don't need to check the game | |
request = request[1..-1] | |
# Resolve aliases, then flatten (so we have say, `singles` as individual tiers, not as a sub-array) and uniq | |
# (in case something redundant was specified) | |
request.map { |e| TIER_ALIASES[e] || e }.flatten.uniq | |
end | |
def self.tier_union(r1, r2) | |
process_aliases(r1) & process_aliases(r2) | |
end | |
def self.game_union(g1, g2) | |
return :any if g1 == :any && g2 == :any | |
return g1 if g2 == :any | |
return g2 if g1 == :any | |
return g1 if g1 == g2 | |
nil | |
end | |
def self.requests_match?(r1, r2) | |
# If the games don't match we don't need to bother | |
return false unless r1.first == r2.first || r1.first == :any || r2.first == :any | |
# Otherwise, we're looking for the union of the tiers after aliases. | |
union = tier_union(r1, r2) | |
return false if union.empty? | |
union | |
end | |
command(:battle, min_args: 1) do |event, game, *tiers| | |
# If the requests array doesn't exist, create it. | |
requests_array = (@requests[event.server.id] ||= []) | |
# Make game and tiers lowercase symbols for performance (and code style) | |
game = game.downcase.to_sym | |
tiers.map! { |e| e.downcase.to_sym } | |
# If there are no tiers specified, count as all tiers | |
tiers = [:all] if tiers.empty? | |
# Check for invalid game in request | |
next 'Invalid game in request, should be one of `ps`, `cart`, `any`' unless [:ps, :cart, :any].include? game | |
# Check for invalid tiers in request | |
invalid_tiers = tiers.reject { |tier| ALL_TIERS.include? tier } | |
next "Invalid tiers in request: #{invalid_tiers.map { |tier| "`#{tier}`"}.join(', ')}. Valid tiers: #{ALL_TIERS.map { |tier| "`#{tier}`"}.join(', ')}" unless invalid_tiers.empty? | |
# Create the request object (actually an array) | |
own_request = [game] + tiers | |
# Full request structure: | |
# [ts, uid, cid, game, *tiers] | |
# We need the match (i.e. union of tiers) in a separate variable, so declare that. | |
request_match = nil | |
full_found_request = requests_array.find do |e| | |
next if e[1] == event.author.id # Ignore requests from self | |
a = requests_match?(own_request, e[3..-1]) # Check whether there is a match | |
request_match = a if a # If there is a match, set the match variable and return it | |
end | |
if full_found_request | |
_, uid, _, *found_request = full_found_request # We only need the uid, game and tiers - ignore the rest | |
game_key = game_union(found_request[0], own_request[0]) | |
game_name = GAME_NAMES[game_key] # The name of the shared game (e.g. "Pokémon Showdown") | |
tiers_list = request_match.map { |e| "`#{e}`" }.join(', ') # Names of all shared tiers | |
requests_array.delete(full_found_request) # Delete the request from the array so it's not triggered again | |
"<@#{uid}> and #{event.author.mention}, a match has been found on #{game_name} in tiers: #{tiers_list}" | |
else | |
full_own_request = [Time.now, event.user.id, event.channel.id] + own_request | |
requests_array << full_own_request | |
"#{event.author.mention}, no immediate match was found - you have been queued" | |
end | |
end | |
end | |
# The heartbeat event - listed separately (can be in a separate file). | |
# If this is never added anywhere requests will never expire. | |
# Time in seconds after which requests will expire. | |
EXPIRY_SECONDS = 3600 | |
bot.heartbeat do |event| | |
Elgyem::Battle.requests.each do |_, reqs| | |
reqs.clone.each do |req| | |
if Time.now - req[0] > EXPIRY_SECONDS | |
# Request is expired | |
begin | |
event.bot.send_message(req[2], "<@#{req[1]}>, your battle request (`#{req[3..-1].join(' ')}`) has expired!") | |
reqs.delete(req) | |
rescue => e | |
puts "Error while processing expired requests: #{e}" | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment