This commit is contained in:
Edith Boles
2024-11-12 12:08:18 -08:00
commit b5a96b621d
11 changed files with 3209095 additions and 0 deletions

143
src/tft_crunch.cr Normal file
View File

@ -0,0 +1,143 @@
require "json"
require "math"
class Champion
include JSON::Serializable
property cost : Int32
property name : String
property traits : Array(String)
end
class Trait
include JSON::Serializable
class Effect
include JSON::Serializable
@[JSON::Field(key: "minUnits")]
property min_units : Int32
@[JSON::Field(key: "maxUnits")]
property max_units : Int32
property style : Int32
end
property effects : Array(Effect)
property name : String
def active_at?(unit_count)
return effects.any? do |effect|
effect.max_units >= unit_count && effect.min_units <= unit_count
end
end
def tier_at(unit_count)
return effects.index do |effect|
effect.max_units >= unit_count && effect.min_units <= unit_count
end
end
def unique?
return effects.any? do |effect|
effect.style == 4
end
end
end
def comb(n, r)
return ((n-r+1)..n).product // (1..r).product
end
def check_active(champions, trait_data, emblems = [] of String, uniques = false)
active = [] of Tuple(Trait, Int32, Int32)
count = {} of String => Int32
champions.each do |champion|
champion.traits.tally(count)
end
count.each do |trait_name, count|
trait = trait_data[trait_name]
if trait.active_at?(count) && (uniques || !trait.unique?)
active << {trait, count, trait.tier_at(count).not_nil!}
end
end
return active
end
def score_comp(champions, trait_data)
traits = check_active(champions, trait_data)
score = 0
if champions.any?{|e| e.name == "Vander"}
score += 100
end
if traits.any?{|e| e[0].name == "Family"}
score += 100
end
traits.each do |trait, count, tier|
score += 2**tier
end
return score
end
RES_FILE = "res/en_us.json"
file = File.open(RES_FILE)
data = JSON.parse(file)
trait_data = data["sets"]["13"]["traits"].as_a.to_json
champion_data = data["sets"]["13"]["champions"].as_a.to_json
traits = Array(Trait).from_json(trait_data).index_by(&.name)
champions = Array(Champion).from_json(champion_data)
champions.reject!{ |champ| champ.traits.empty? }
cbn = champions.index_by(&.name)
unit_count = ARGV[0]?.try(&.to_i) || 7
super_count = ARGV[1]?.try(&.to_i) || 4
other_count = unit_count - super_count
cost_limit = ARGV[2]?.try(&.to_i) || 10
min_score = ARGV[3]?.try(&.to_i) || unit_count
champions.select!{ |champ| champ.cost <= cost_limit }
champions.sort_by!{ |champ| champ.name }
super_units = champions.select{ |champ| champ.traits.size == 3 }
other_units = champions.select{ |champ| champ.traits.size == 2 }
puts "#{super_count.to_s.rjust(2)}x #{super_units.size.to_s.rjust(2)} 3-trait champions"
puts "#{other_count.to_s.rjust(2)}x #{other_units.size.to_s.rjust(2)} 2-trait champions"
results = [] of Tuple(Array(Champion), Int32)
counter : UInt64 = 0
total = comb(super_units.size, super_count)
total *= comb(other_units.size, other_count)
test_array1 = [] of Champion
test_array2 = [] of Champion
super_units.each_combination(super_count, reuse: test_array1) do |units|
other_units.each_combination(other_count, reuse: test_array2) do |others|
others.concat(units)
score = score_comp(others, traits)
if score >= min_score
results << {others.dup, score}
end
if counter % 100000 == 0
percent = (counter / total * 100).format(decimal_places: 2).rjust(6) + "%"
puts "#{percent} | #{counter.to_s.rjust(10)} | #{score.to_s.rjust(3)} | #{others.map(&.name).join(", ")}"
end
counter += 1
others.clear
end
units.clear
end
output = results.sort_by{|e| e[1]}.reverse.first(50).map do |list, count|
"#{list.map(&.name).join(", ").ljust(30)} - #{count} score"
end
File.write("output.txt", output.join("\n"))
puts "\nTop Ten:"
puts "-" * 80
puts output.first(10).reverse.join("\n")