class_name DeckGen ## Stapel: Elementstapel nach Manakurve (Startstapel und Gegner), Auffüllen aus der Sammlung, ## zufällige Stapel für Tests und Simulation. const CHEAP_IN_RANDOM := 12 # so viele im ersten Zug bezahlbare Karten hat ein zufälliger Stapel mindestens ## Manakurve eines Stapels mit DECK_MIN Karten: so viele Karten je Kosten 0 bis 10. const CURVE: Array[int] = [2, 5, 6, 6, 5, 3, 2, 1, 0, 0, 0] ## Zufälliger Stapel aus DECK_MIN verschiedenen Karten bis max_cost, mit genug billigen für den Anfang. static func random_deck(r: RandomNumberGenerator, max_cost: int = 99) -> Array: var cheap := Cards.all().filter(func(c): return c.cost <= Game.START_MANA) var rest := Cards.all().filter(func(c): return c.cost > Game.START_MANA and c.cost <= max_cost) Game.shuffle(cheap, r) Game.shuffle(rest, r) var out := [] for c in cheap.slice(0, CHEAP_IN_RANDOM) + rest + cheap.slice(CHEAP_IN_RANDOM): if out.size() >= Game.DECK_MIN: break out.append(c.id) return out ## Alle Karten eines Elements (oder die neutralen) in Datenreihenfolge: die Startsammlung. static func element_deck(element: String) -> Array: return Cards.all().filter(func(c): return c.elements[0] == element).map(func(c): return c.id) ## Stapel eines Elements nach der Manakurve: DECK_MIN verschiedene Karten des Elements bis max_cost, ## je Kosten so viele wie CURVE vorgibt, zufällig gewählt. Was über dem Deckel fehlt, füllen die ## erlaubten Kosten reihum von billig nach teuer auf. static func curve_deck(element: String, r: RandomNumberGenerator, max_cost: int = 10) -> Array: var by_cost := {} for c in Cards.all(): if c.elements[0] == element and c.cost <= max_cost: if not by_cost.has(c.cost): by_cost[c.cost] = [] by_cost[c.cost].append(c.id) for cost in by_cost: Game.shuffle(by_cost[cost], r) var out := [] for cost in CURVE.size(): for k in CURVE[cost]: if by_cost.has(cost) and not by_cost[cost].is_empty(): out.append(by_cost[cost].pop_back()) while out.size() < Game.DECK_MIN: var added := false for cost in range(0, max_cost + 1): if out.size() >= Game.DECK_MIN: break if by_cost.has(cost) and not by_cost[cost].is_empty(): out.append(by_cost[cost].pop_back()) added = true if not added: break return out ## Füllt deck aus pool (Karten-IDs) bis limit auf, die wertvollsten zuerst (Card.value in der Stufe ## aus levels: ID -> Stufe); bei gleichem Wert zuerst, was der Manakurve (CURVE, auf limit ## skaliert) noch fehlt, dann die billigeren, dann nach ID; nichts doppelt. static func fill(deck: Array, pool: Array, limit: int, levels: Dictionary = {}) -> Array: var out := deck.duplicate() var cards := pool.filter(func(id): return not (id in out)).map(func(id): return Cards.by_id(id).leveled(int(levels.get(id, 0)))) cards.sort_custom(func(a, b): if a.value() != b.value(): return a.value() > b.value() return a.cost < b.cost if a.cost != b.cost else a.id < b.id) var have := {} # Kosten -> Karten im Stapel for id in out: var slot := clampi(Cards.by_id(id).cost, 0, CURVE.size() - 1) have[slot] = int(have.get(slot, 0)) + 1 var i := 0 while i < cards.size() and out.size() < limit: var j := i while j < cards.size() and cards[j].value() == cards[i].value(): j += 1 var rest := [] for c in cards.slice(i, j): if out.size() >= limit: break var slot := clampi(c.cost, 0, CURVE.size() - 1) if int(have.get(slot, 0)) < CURVE[slot] * limit / Game.DECK_MIN: out.append(c.id) have[slot] = int(have.get(slot, 0)) + 1 else: rest.append(c) for c in rest: if out.size() >= limit: break out.append(c.id) i = j return out ## Stärke eines Stapels: der mittlere Kartenwert (Card.value) in den Stufen aus levels, ## 100 für lauter Grundkarten bis 500 für lauter Ultimate Rare; leer: 0. static func strength(deck: Array, levels: Dictionary) -> int: if deck.is_empty(): return 0 var sum := 0 for id in deck: sum += Cards.by_id(id).leveled(int(levels.get(id, 0))).value() return roundi(float(sum) / deck.size()) static func random_opponent(life: int = Game.START_LIFE) -> Game.Player: return Game.Player.new("Gegner", false, life)