Добавил генерацию колоды и улучшил бд
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ClashDeckGenerator2
|
||||
module Relations
|
||||
class Cards < DB::Relation
|
||||
schema :cards, infer: true
|
||||
|
||||
def meta
|
||||
def meta_cards_scope
|
||||
where(is_meta: 1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ClashDeckGenerator2
|
||||
module Repos
|
||||
class CardsRepo < DB::Repo[:cards]
|
||||
commands :create
|
||||
|
||||
def meta_cards
|
||||
cards.meta.to_a
|
||||
cards.meta_cards_scope.to_a
|
||||
end
|
||||
|
||||
def all_cards
|
||||
cards.to_a
|
||||
end
|
||||
def all
|
||||
cards.to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
36
app/services/deck_generator.rb
Normal file
36
app/services/deck_generator.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ClashDeckGenerator2
|
||||
module Services
|
||||
class DeckGenerator
|
||||
DECK_SIZE = 8
|
||||
|
||||
def initialize(cards_repo:)
|
||||
@cards_repo = cards_repo
|
||||
end
|
||||
|
||||
def call(only_meta: true)
|
||||
pool = only_meta ? @cards_repo.meta_cards : @cards_repo.all_cards
|
||||
deck = sample_unique(pool, DECK_SIZE)
|
||||
|
||||
{
|
||||
cards: deck,
|
||||
avg_elixir: average_elixir(deck)
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sample_unique(pool, n)
|
||||
raise "Not enough cards in pool (need #{n}, have #{pool.size})" if pool.size < n
|
||||
|
||||
pool.sample(n)
|
||||
end
|
||||
|
||||
def average_elixir(deck)
|
||||
sum = deck.sum { |c| c[:elixir_cost].to_i }
|
||||
sum.fdiv(deck.size).round(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,15 +1,71 @@
|
||||
# This seeds file should create the database records required to run the app.
|
||||
#
|
||||
# The code should be idempotent so that it can be executed at any time.
|
||||
#
|
||||
# To load the seeds, run `hanami db seed`. Seeds are also loaded as part of `hanami db prepare`.
|
||||
# frozen_string_literal: true
|
||||
|
||||
# For example, if you have appropriate repos available:
|
||||
#
|
||||
# category_repo = Hanami.app["repos.category_repo"]
|
||||
# category_repo.create(title: "General")
|
||||
#
|
||||
# Alternatively, you can use relations directly:
|
||||
#
|
||||
# categories = Hanami.app["relations.categories"]
|
||||
# categories.insert(title: "General")
|
||||
repo = ClashDeckGenerator2::Repos::CardsRepo.new
|
||||
|
||||
CARD_TYPES = %w[troop spell building].freeze
|
||||
RARITIES = %w[common rare epic legendary champion].freeze
|
||||
|
||||
COMMON_CARDS = [
|
||||
# ========================
|
||||
# TROOPS
|
||||
# ========================
|
||||
{ name: "Skeletons", elixir_cost: 1, rarity: "common", type: "troop", is_meta: 1 },
|
||||
{ name: "Electro Spirit", elixir_cost: 1, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Fire Spirit", elixir_cost: 1, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Ice Spirit", elixir_cost: 1, rarity: "common", type: "troop", is_meta: 0 },
|
||||
|
||||
{ name: "Goblins", elixir_cost: 2, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Spear Goblins", elixir_cost: 2, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Bomber", elixir_cost: 2, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Bats", elixir_cost: 2, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Berserker", elixir_cost: 2, rarity: "common", type: "troop", is_meta: 0 },
|
||||
|
||||
{ name: "Archers", elixir_cost: 3, rarity: "common", type: "troop", is_meta: 1 },
|
||||
{ name: "Knight", elixir_cost: 3, rarity: "common", type: "troop", is_meta: 1 },
|
||||
{ name: "Minions", elixir_cost: 3, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Goblin Gang", elixir_cost: 3, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Skeleton Barrel", elixir_cost: 3, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Firecracker", elixir_cost: 3, rarity: "common", type: "troop", is_meta: 1 },
|
||||
{ name: "Skeleton Dragons", elixir_cost: 4, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Barbarians", elixir_cost: 5, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Minion Horde", elixir_cost: 5, rarity: "common", type: "troop", is_meta: 1 },
|
||||
{ name: "Rascals", elixir_cost: 5, rarity: "common", type: "troop", is_meta: 1 },
|
||||
{ name: "Royal Giant", elixir_cost: 6, rarity: "common", type: "troop", is_meta: 1 },
|
||||
{ name: "Elite Barbarians", elixir_cost: 6, rarity: "common", type: "troop", is_meta: 0 },
|
||||
{ name: "Royal Recruits", elixir_cost: 7, rarity: "common", type: "troop", is_meta: 0 },
|
||||
|
||||
# ========================
|
||||
# SPELLS
|
||||
# ========================
|
||||
{ name: "Zap", elixir_cost: 2, rarity: "common", type: "spell", is_meta: 1 },
|
||||
{ name: "Giant Snowball", elixir_cost: 2, rarity: "common", type: "spell", is_meta: 0 },
|
||||
{ name: "Arrows", elixir_cost: 3, rarity: "common", type: "spell", is_meta: 0 },
|
||||
{ name: "Royal Delivery", elixir_cost: 3, rarity: "common", type: "spell", is_meta: 0 },
|
||||
|
||||
# ========================
|
||||
# BUILDINGS
|
||||
# ========================
|
||||
{ name: "Cannon", elixir_cost: 3, rarity: "common", type: "building", is_meta: 0 },
|
||||
{ name: "Mortar", elixir_cost: 4, rarity: "common", type: "building", is_meta: 0 },
|
||||
{ name: "Tesla", elixir_cost: 4, rarity: "common", type: "building", is_meta: 0 }
|
||||
].freeze
|
||||
|
||||
CARDS = COMMON_CARDS
|
||||
|
||||
CARDS.each do |c|
|
||||
raise "Missing name" if c[:name].nil? || c[:name].strip.empty?
|
||||
raise "Missing elixir_cost for #{c[:name]}" if c[:elixir_cost].nil?
|
||||
raise "Missing rarity for #{c[:name]}" if c[:rarity].nil?
|
||||
raise "Missing type for #{c[:name]}" if c[:type].nil?
|
||||
|
||||
raise "Unknown rarity: #{c[:rarity]} for #{c[:name]}" unless RARITIES.include?(c[:rarity])
|
||||
raise "Unknown type: #{c[:type]} for #{c[:name]}" unless CARD_TYPES.include?(c[:type])
|
||||
end
|
||||
|
||||
names = CARDS.map { |c| c[:name] }
|
||||
duplicates = names.group_by { |name| name }.select { |_k, v| v.size > 1 }.keys
|
||||
raise "Duplicate cards found: #{duplicates.join(', ')}" if duplicates.any?
|
||||
|
||||
CARDS.each do |card|
|
||||
repo.create(card)
|
||||
end
|
||||
31
script/test_deck.rb
Normal file
31
script/test_deck.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../config/app"
|
||||
|
||||
Hanami.app.prepare
|
||||
|
||||
repo = ClashDeckGenerator2::Repos::CardsRepo.new
|
||||
cards = repo.all.select { |card| card.rarity == "common" }
|
||||
|
||||
raise "No common cards found in DB." if cards.empty?
|
||||
raise "Not enough common cards to build a deck. Need at least 8, got #{cards.size}." if cards.size < 8
|
||||
|
||||
deck = cards.sample(8)
|
||||
|
||||
puts "========================================"
|
||||
puts "DECK GENERATED SUCCESSFULLY"
|
||||
puts "========================================"
|
||||
|
||||
deck.each_with_index do |card, index|
|
||||
puts "#{index + 1}. #{card.name} | #{card.type} | #{card.rarity} | #{card.elixir_cost}"
|
||||
end
|
||||
|
||||
puts "----------------------------------------"
|
||||
puts "Cards count: #{deck.size}"
|
||||
|
||||
names = deck.map(&:name)
|
||||
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 "========================================"
|
||||
Reference in New Issue
Block a user