Skip to content

Instantly share code, notes, and snippets.

@meew0
Created August 24, 2016 18:54
Show Gist options
  • Save meew0/781b9547dc125f7878459ee503109417 to your computer and use it in GitHub Desktop.
Save meew0/781b9547dc125f7878459ee503109417 to your computer and use it in GitHub Desktop.
Elgyem .battle command
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