Генератор колоды Beta
This commit is contained in:
257
script/build_roles_json.rb
Normal file
257
script/build_roles_json.rb
Normal file
@@ -0,0 +1,257 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "json"
|
||||
require_relative "../config/app"
|
||||
|
||||
Hanami.app.prepare
|
||||
|
||||
module ClashDeckGenerator2
|
||||
module Scripts
|
||||
class BuildRolesJson
|
||||
OUTPUT_PATH = File.expand_path("../config/cards/roles.json", __dir__)
|
||||
|
||||
BASE_ROLES_BY_TYPE = {
|
||||
"spell" => %w[spell],
|
||||
"building" => %w[building defense],
|
||||
"troop" => %w[support]
|
||||
}.freeze
|
||||
|
||||
ROLE_OVERRIDES = {
|
||||
# cycle
|
||||
"Skeletons" => %w[cycle defense],
|
||||
"Ice Spirit" => %w[cycle support],
|
||||
"Electro Spirit" => %w[cycle support],
|
||||
"Fire Spirit" => %w[cycle support splash],
|
||||
"Heal Spirit" => %w[cycle support],
|
||||
"Goblins" => %w[cycle defense],
|
||||
"Spear Goblins" => %w[cycle support],
|
||||
"Bats" => %w[cycle anti_air],
|
||||
"Wall Breakers" => %w[cycle win_condition],
|
||||
"Bomber" => %w[cycle splash support],
|
||||
|
||||
# win conditions
|
||||
"Hog Rider" => %w[win_condition],
|
||||
"Royal Giant" => %w[win_condition tank],
|
||||
"Goblin Barrel" => %w[win_condition],
|
||||
"Balloon" => %w[win_condition],
|
||||
"Giant" => %w[win_condition tank],
|
||||
"Golem" => %w[win_condition tank],
|
||||
"Electro Giant" => %w[win_condition tank],
|
||||
"Goblin Drill" => %w[win_condition],
|
||||
"X-Bow" => %w[win_condition building defense],
|
||||
"Mortar" => %w[win_condition building defense],
|
||||
"Ram Rider" => %w[win_condition support],
|
||||
"Graveyard" => %w[win_condition spell],
|
||||
"Royal Hogs" => %w[win_condition],
|
||||
"Battle Ram" => %w[win_condition],
|
||||
"Elixir Golem" => %w[win_condition tank],
|
||||
"Lava Hound" => %w[win_condition tank anti_air],
|
||||
"Goblin Barrel" => %w[win_condition],
|
||||
"Goblin Drill" => %w[win_condition building],
|
||||
"Miner" => %w[win_condition support],
|
||||
"Goblin Giant" => %w[win_condition tank],
|
||||
"Three Musketeers" => %w[win_condition support anti_air],
|
||||
"Skeleton Barrel" => %w[win_condition],
|
||||
"Rocket" => %w[spell finisher],
|
||||
"Fireball" => %w[spell finisher splash],
|
||||
"Poison" => %w[spell finisher splash],
|
||||
"Lightning" => %w[spell finisher],
|
||||
"Void" => %w[spell finisher],
|
||||
|
||||
# anti air
|
||||
"Archers" => %w[anti_air support cycle],
|
||||
"Musketeer" => %w[anti_air support],
|
||||
"Wizard" => %w[anti_air support splash],
|
||||
"Baby Dragon" => %w[anti_air support splash],
|
||||
"Electro Dragon" => %w[anti_air support splash],
|
||||
"Inferno Dragon" => %w[anti_air defense],
|
||||
"Mega Minion" => %w[anti_air support],
|
||||
"Flying Machine" => %w[anti_air support],
|
||||
"Hunter" => %w[anti_air defense],
|
||||
"Firecracker" => %w[anti_air support splash],
|
||||
"Minions" => %w[anti_air support],
|
||||
"Minion Horde" => %w[anti_air support],
|
||||
"Skeleton Dragons" => %w[anti_air support splash],
|
||||
"Phoenix" => %w[anti_air support],
|
||||
"Executioner" => %w[anti_air support splash],
|
||||
"Tesla" => %w[anti_air building defense],
|
||||
"Inferno Tower" => %w[building defense],
|
||||
"Magic Archer" => %w[support anti_air],
|
||||
"Zappies" => %w[support anti_air defense],
|
||||
"Dart Goblin" => %w[support anti_air],
|
||||
"Electro Wizard" => %w[support anti_air defense],
|
||||
"Mother Witch" => %w[support anti_air],
|
||||
"Little Prince" => %w[support anti_air],
|
||||
|
||||
# tanks / mini tanks
|
||||
"Knight" => %w[tank defense],
|
||||
"Valkyrie" => %w[tank defense splash],
|
||||
"Ice Golem" => %w[tank cycle defense],
|
||||
"P.E.K.K.A" => %w[tank defense],
|
||||
"Mini P.E.K.K.A" => %w[defense tank],
|
||||
"Mega Knight" => %w[tank defense splash],
|
||||
"Royal Ghost" => %w[tank support splash],
|
||||
"Dark Prince" => %w[tank support splash],
|
||||
"Prince" => %w[tank support],
|
||||
"Skeleton King" => %w[tank support],
|
||||
"Golden Knight" => %w[tank support],
|
||||
"Monk" => %w[tank defense],
|
||||
"Mighty Miner" => %w[defense tank],
|
||||
"Giant Skeleton" => %w[tank splash],
|
||||
"Lumberjack" => %w[support tank],
|
||||
"Bandit" => %w[support],
|
||||
"Battle Healer" => %w[support tank],
|
||||
"Goblin Giantess" => %w[tank support],
|
||||
|
||||
# buildings / defense
|
||||
"Cannon" => %w[building defense],
|
||||
"Tesla" => %w[building defense anti_air],
|
||||
"Bomb Tower" => %w[building defense splash],
|
||||
"Tombstone" => %w[building defense],
|
||||
"Goblin Cage" => %w[building defense],
|
||||
"Goblin Hut" => %w[building defense],
|
||||
"Barbarian Hut" => %w[building defense],
|
||||
"Furnace" => %w[building defense splash],
|
||||
"Elixir Collector" => %w[building support],
|
||||
"Goblin Drill" => %w[building win_condition],
|
||||
"X-Bow" => %w[building win_condition defense],
|
||||
"Mortar" => %w[building win_condition defense],
|
||||
|
||||
# splash / control
|
||||
"Bowler" => %w[splash support defense],
|
||||
"Witch" => %w[support splash],
|
||||
"Bomber" => %w[support splash cycle],
|
||||
"Arrows" => %w[spell splash],
|
||||
"Zap" => %w[spell cycle],
|
||||
"Giant Snowball" => %w[spell cycle],
|
||||
"Royal Delivery" => %w[spell defense splash],
|
||||
"Tornado" => %w[spell control defense],
|
||||
"Rage" => %w[spell support],
|
||||
"Freeze" => %w[spell control],
|
||||
"Clone" => %w[spell support],
|
||||
"Mirror" => %w[spell support],
|
||||
"Earthquake" => %w[spell building_hate],
|
||||
"The Log" => %w[spell cycle defense],
|
||||
"Barbarian Barrel" => %w[spell cycle defense],
|
||||
|
||||
# support / utility
|
||||
"Skeleton Army" => %w[defense],
|
||||
"Guards" => %w[defense],
|
||||
"Royal Recruits" => %w[defense],
|
||||
"Rascals" => %w[defense support anti_air],
|
||||
"Cannon Cart" => %w[support defense],
|
||||
"Sparky" => %w[support defense],
|
||||
"Night Witch" => %w[support],
|
||||
"Princess" => %w[support splash],
|
||||
"Ice Wizard" => %w[support defense splash],
|
||||
"Fisherman" => %w[defense control],
|
||||
"Goblin Demolisher" => %w[support splash],
|
||||
"Suspicious Bush" => %w[support],
|
||||
"Battle Ram" => %w[win_condition],
|
||||
"Royal Hogs" => %w[win_condition],
|
||||
"Royal Recruits" => %w[defense],
|
||||
"Electro Giant" => %w[win_condition tank],
|
||||
"Electro Dragon" => %w[anti_air support splash],
|
||||
"Goblin Machine" => %w[support],
|
||||
"Spirit Empress" => %w[support],
|
||||
"Boss Bandit" => %w[support],
|
||||
"Goblins" => %w[cycle defense]
|
||||
}.freeze
|
||||
|
||||
def call
|
||||
repo = ClashDeckGenerator2::Repos::CardsRepo.new
|
||||
cards = unwrap(repo.all)
|
||||
|
||||
cards_by_name = {}
|
||||
cards.each do |card|
|
||||
cards_by_name[card.name] ||= card
|
||||
end
|
||||
|
||||
result = {
|
||||
"_meta" => {
|
||||
"version" => 1,
|
||||
"description" => "Card roles mapping for deck generation",
|
||||
"generated_from_db" => true,
|
||||
"cards_count" => cards_by_name.size,
|
||||
"available_roles" => available_roles
|
||||
},
|
||||
"cards" => {}
|
||||
}
|
||||
|
||||
cards_by_name.keys.sort.each do |card_name|
|
||||
card = cards_by_name[card_name]
|
||||
roles = roles_for(card)
|
||||
result["cards"][card_name] = roles
|
||||
end
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(OUTPUT_PATH))
|
||||
File.write(OUTPUT_PATH, JSON.pretty_generate(result))
|
||||
|
||||
puts "roles.json generated: #{OUTPUT_PATH}"
|
||||
puts "Unique cards exported: #{cards_by_name.size}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unwrap(result)
|
||||
if result.is_a?(Array) && result.first == :cards
|
||||
result.last
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def roles_for(card)
|
||||
roles = []
|
||||
roles.concat(BASE_ROLES_BY_TYPE.fetch(card.type, []))
|
||||
|
||||
base_name = base_card_name(card.name)
|
||||
override_roles = ROLE_OVERRIDES[card.name] || ROLE_OVERRIDES[base_name] || []
|
||||
roles.concat(override_roles)
|
||||
|
||||
roles = roles.map(&:to_s).map(&:strip).reject(&:empty?).uniq.sort
|
||||
|
||||
roles = fallback_roles(card) if roles.empty?
|
||||
roles
|
||||
end
|
||||
|
||||
def base_card_name(name)
|
||||
name
|
||||
.sub(/ Evolution\z/, "")
|
||||
.sub(/ Hero\z/, "")
|
||||
end
|
||||
|
||||
def fallback_roles(card)
|
||||
case card.type
|
||||
when "spell"
|
||||
["spell"]
|
||||
when "building"
|
||||
%w[building defense]
|
||||
when "troop"
|
||||
["support"]
|
||||
else
|
||||
["support"]
|
||||
end
|
||||
end
|
||||
|
||||
def available_roles
|
||||
%w[
|
||||
anti_air
|
||||
building
|
||||
building_hate
|
||||
control
|
||||
cycle
|
||||
defense
|
||||
finisher
|
||||
spell
|
||||
splash
|
||||
support
|
||||
tank
|
||||
win_condition
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ClashDeckGenerator2::Scripts::BuildRolesJson.new.call
|
||||
@@ -4,40 +4,14 @@ require_relative "../config/app"
|
||||
|
||||
Hanami.app.prepare
|
||||
|
||||
repo = ClashDeckGenerator2::Repos::CardsRepo.new
|
||||
roles_repo = ClashDeckGenerator2::Services::CardRolesRepository.new
|
||||
generator = ClashDeckGenerator2::Services::DeckGenerator.new(
|
||||
cards_repo: repo,
|
||||
roles_repo: roles_repo
|
||||
)
|
||||
|
||||
puts roles_repo.roles_for("Knight").inspect
|
||||
puts roles_repo.roles_for("Hog Rider").inspect
|
||||
puts roles_repo.has_role?("Hog Rider", "win_condition")
|
||||
puts roles_repo.has_role?("Tesla", "win_condition")
|
||||
puts roles_repo.meta.inspect
|
||||
|
||||
cards = repo.all
|
||||
cards = cards.last if cards.is_a?(Array) && cards.first == :cards
|
||||
|
||||
rarity_counts = cards.group_by(&:rarity).transform_values(&:size)
|
||||
|
||||
puts "RARITY COUNTS:"
|
||||
rarity_counts.each do |rarity, count|
|
||||
puts "#{rarity}: #{count}"
|
||||
end
|
||||
|
||||
puts "========================================"
|
||||
puts "POOL DIAGNOSTICS"
|
||||
puts "========================================"
|
||||
puts "Total cards: #{cards.size}"
|
||||
puts "Spells: #{cards.count { |c| c.type == 'spell' }}"
|
||||
puts "Troops: #{cards.count { |c| c.type == 'troop' }}"
|
||||
puts "Buildings: #{cards.count { |c| c.type == 'building' }}"
|
||||
puts "Champions: #{cards.count { |c| c.rarity == 'champion' }}"
|
||||
puts "Heroes: #{cards.count { |c| c.rarity == 'hero' }}"
|
||||
puts "Evolutions: #{cards.count { |c| c.rarity == 'evolution' }}"
|
||||
puts "Avg elixir all cards: #{(cards.sum(&:elixir_cost).to_f / cards.size).round(2)}"
|
||||
puts "========================================"
|
||||
|
||||
generator = ClashDeckGenerator2::Services::DeckGenerator.new(cards_repo: repo)
|
||||
deck = generator.call
|
||||
|
||||
deck = deck.last if deck.is_a?(Array) && deck.first == :cards
|
||||
|
||||
puts "========================================"
|
||||
@@ -45,7 +19,11 @@ puts "DECK GENERATED SUCCESSFULLY"
|
||||
puts "========================================"
|
||||
|
||||
deck.each_with_index do |card, index|
|
||||
puts "#{index + 1}. #{card.name} | #{card.type} | #{card.rarity} | #{card.elixir_cost}"
|
||||
roles = roles_repo.roles_for(card.name)
|
||||
meta_flag = card.is_meta == 1 ? "META" : "NON-META"
|
||||
|
||||
puts "#{index + 1}. #{card.name} | #{card.type} | #{card.rarity} | #{card.elixir_cost} | #{meta_flag}"
|
||||
puts " roles: #{roles.join(', ')}"
|
||||
end
|
||||
|
||||
puts "----------------------------------------"
|
||||
@@ -56,10 +34,18 @@ puts "Unique cards count: #{names.uniq.size}"
|
||||
|
||||
avg_elixir = deck.sum(&:elixir_cost).to_f / deck.size
|
||||
puts "Average elixir: #{avg_elixir.round(2)}"
|
||||
puts "Spells count: #{deck.count { |c| c.type == 'spell' }}"
|
||||
puts "Spells by type: #{deck.count { |c| c.type == 'spell' }}"
|
||||
puts "Troops count: #{deck.count { |c| c.type == 'troop' }}"
|
||||
puts "Buildings count: #{deck.count { |c| c.type == 'building' }}"
|
||||
puts "Champions count: #{deck.count { |c| c.rarity == 'champion' }}"
|
||||
puts "Heroes count: #{deck.count { |c| c.rarity == 'hero' }}"
|
||||
puts "Evolutions count: #{deck.count { |c| c.rarity == 'evolution' }}"
|
||||
puts "Meta cards count: #{deck.count { |c| c.is_meta == 1 }}"
|
||||
|
||||
all_roles = deck.flat_map { |card| roles_repo.roles_for(card.name) }.uniq.sort
|
||||
puts "Deck roles coverage: #{all_roles.join(', ')}"
|
||||
|
||||
puts "Win conditions count: #{deck.count { |c| roles_repo.has_role?(c.name, 'win_condition') }}"
|
||||
puts "Anti-air count: #{deck.count { |c| roles_repo.has_role?(c.name, 'anti_air') }}"
|
||||
puts "Spell-role count: #{deck.count { |c| roles_repo.has_role?(c.name, 'spell') }}"
|
||||
puts "========================================"
|
||||
Reference in New Issue
Block a user