diff --git a/src/client/dev-dist/sw.js b/src/client/dev-dist/sw.js index 301187d..9f2990e 100644 --- a/src/client/dev-dist/sw.js +++ b/src/client/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-5a5d9309'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.ucm0m4ajm9g" + "revision": "0.g6k3e4tvo1g" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/src/client/src/services/PackGeneratorService.ts b/src/client/src/services/PackGeneratorService.ts index 2f47b19..c72a1c8 100644 --- a/src/client/src/services/PackGeneratorService.ts +++ b/src/client/src/services/PackGeneratorService.ts @@ -294,10 +294,6 @@ export class PackGeneratorService { const drawC = this.drawColorBalanced(currentPools.commons, commonsNeeded, namesInThisPack); if (!drawC.success && currentPools.commons.length >= commonsNeeded) { - // If we have enough cards but failed strict color balancing, we might accept it or fail. - // Standard algo returns null on failure. Let's do same to be safe, or just accept partial. - // Given "Naive approach" in drawColorBalanced, if it returns success=false but has cards, it meant it couldn't find unique ones? - // drawUniqueCards (called by drawColorBalanced) checks if we have enough cards. return null; } else if (currentPools.commons.length < commonsNeeded) { return null; @@ -308,9 +304,9 @@ export class PackGeneratorService { drawC.selected.forEach(c => namesInThisPack.add(c.name)); // 2. Slot 7: Common / The List - // 1-87: Common from Main Set - // 88-97: Card from "The List" (Common/Uncommon) - // 98-100: Uncommon from "The List" + // 1-87: 1 Common from Main Set. + // 88-97: 1 Card from "The List" (Common/Uncommon reprint). + // 98-100: 1 Uncommon from "The List". const roll7 = Math.floor(Math.random() * 100) + 1; let slot7Card: DraftCard | undefined; @@ -319,25 +315,30 @@ export class PackGeneratorService { const res = this.drawUniqueCards(currentPools.commons, 1, namesInThisPack); if (res.success) { slot7Card = res.selected[0]; currentPools.commons = res.remainingPool; } } else if (roll7 <= 97) { - // List (Common/Uncommon). Simulating by picking 50/50 C/U if actual List not available - const useUncommon = Math.random() < 0.5; - const pool = useUncommon ? currentPools.uncommons : currentPools.commons; - // Fallback if one pool is empty - const effectivePool = pool.length > 0 ? pool : (useUncommon ? currentPools.commons : currentPools.uncommons); - - if (effectivePool.length > 0) { - const res = this.drawUniqueCards(effectivePool, 1, namesInThisPack); + // List (Common/Uncommon). Use SpecialGuests or 50/50 fallback + if (currentPools.specialGuests.length > 0) { + const res = this.drawUniqueCards(currentPools.specialGuests, 1, namesInThisPack); + if (res.success) { slot7Card = res.selected[0]; currentPools.specialGuests = res.remainingPool; } + } else { + // Fallback + const pool = Math.random() < 0.5 ? currentPools.commons : currentPools.uncommons; + const res = this.drawUniqueCards(pool, 1, namesInThisPack); if (res.success) { slot7Card = res.selected[0]; - // Identify which pool to update - if (effectivePool === currentPools.uncommons) currentPools.uncommons = res.remainingPool; - else currentPools.commons = res.remainingPool; + if (pool === currentPools.commons) currentPools.commons = res.remainingPool; + else currentPools.uncommons = res.remainingPool; } } } else { - // 98-100: Uncommon (from List or pool) - const res = this.drawUniqueCards(currentPools.uncommons, 1, namesInThisPack); - if (res.success) { slot7Card = res.selected[0]; currentPools.uncommons = res.remainingPool; } + // 98-100: Uncommon from "The List" + if (currentPools.specialGuests.length > 0) { + const res = this.drawUniqueCards(currentPools.specialGuests, 1, namesInThisPack); + if (res.success) { slot7Card = res.selected[0]; currentPools.specialGuests = res.remainingPool; } + } else { + // Fallback + const res = this.drawUniqueCards(currentPools.uncommons, 1, namesInThisPack); + if (res.success) { slot7Card = res.selected[0]; currentPools.uncommons = res.remainingPool; } + } } if (slot7Card) { @@ -348,7 +349,6 @@ export class PackGeneratorService { // 3. Slots 8-11: Uncommons (4 cards) const uncommonsNeeded = 4; const drawU = this.drawUniqueCards(currentPools.uncommons, uncommonsNeeded, namesInThisPack); - // We accept partial if pool depleted to avoid crashing, but standard behavior is usually strict. packCards.push(...drawU.selected); currentPools.uncommons = drawU.remainingPool; drawU.selected.forEach(c => namesInThisPack.add(c.name)); @@ -372,25 +372,19 @@ export class PackGeneratorService { namesInThisPack.add(landCard.name); } - // Helper for Wildcards + // Helper for Wildcards (Peasant) const drawWildcard = (foil: boolean) => { + // ~62% Common, ~37% Uncommon const wRoll = Math.random() * 100; let wRarity = 'common'; - // ~49% Common, ~24% Uncommon, ~13% Rare, ~13% Mythic - if (wRoll > 87) wRarity = 'mythic'; - else if (wRoll > 74) wRarity = 'rare'; - else if (wRoll > 50) wRarity = 'uncommon'; - else wRarity = 'common'; + if (wRoll > 62) wRarity = 'uncommon'; let poolToUse: DraftCard[] = []; let updatePool = (_newPool: DraftCard[]) => { }; - if (wRarity === 'mythic') { poolToUse = currentPools.mythics; updatePool = (p) => currentPools.mythics = p; } - else if (wRarity === 'rare') { poolToUse = currentPools.rares; updatePool = (p) => currentPools.rares = p; } - else if (wRarity === 'uncommon') { poolToUse = currentPools.uncommons; updatePool = (p) => currentPools.uncommons = p; } + if (wRarity === 'uncommon') { poolToUse = currentPools.uncommons; updatePool = (p) => currentPools.uncommons = p; } else { poolToUse = currentPools.commons; updatePool = (p) => currentPools.commons = p; } - // Fallback if (poolToUse.length === 0) { if (currentPools.commons.length > 0) { poolToUse = currentPools.commons; updatePool = (p) => currentPools.commons = p; } } @@ -423,14 +417,14 @@ export class PackGeneratorService { } } else { - // --- NEW ALGORITHM (Play Booster) --- + // --- NEW ALGORITHM (Standard / Play Booster) --- // 1. Slots 1-6: Commons (Color Balanced) const commonsNeeded = 6; const drawC = this.drawColorBalanced(currentPools.commons, commonsNeeded, namesInThisPack); if (!drawC.success) return null; packCards.push(...drawC.selected); - currentPools.commons = drawC.remainingPool; // Update pool + currentPools.commons = drawC.remainingPool; drawC.selected.forEach(c => namesInThisPack.add(c.name)); // 2. Slots 8-10: Uncommons (3 cards) @@ -442,7 +436,7 @@ export class PackGeneratorService { drawU.selected.forEach(c => namesInThisPack.add(c.name)); // 3. Slot 11: Main Rare/Mythic (1/8 Mythic, 7/8 Rare) - const isMythic = Math.random() < (1 / 8); + const isMythic = Math.random() < 0.125; let rarePicked = false; if (isMythic && currentPools.mythics.length > 0) { @@ -465,10 +459,11 @@ export class PackGeneratorService { } } - // Fallback if Rare pool empty but Mythic not (or vice versa) handled by just skipping - - // 4. Slot 7: Wildcard / The List - // 1-87: Common, 88-97: List (C/U), 98-99: List (R/M), 100: Special Guest + // 4. Slot 7: Common / The List / Special Guest + // 1-87: 1 Common from Main Set. + // 88-97: 1 Card from "The List" (Common/Uncommon reprint). + // 98-99: 1 Rare/Mythic from "The List". + // 100: 1 Special Guest (High Value). const roll7 = Math.floor(Math.random() * 100) + 1; let slot7Card: DraftCard | undefined; @@ -477,42 +472,43 @@ export class PackGeneratorService { const res = this.drawUniqueCards(currentPools.commons, 1, namesInThisPack); if (res.success) { slot7Card = res.selected[0]; currentPools.commons = res.remainingPool; } } else if (roll7 <= 97) { - // "The List" (Common/Uncommon). Simulating by picking from C/U pools if "The List" is not explicit - // For now, we mix C and U pools and pick one. - const listPool = [...currentPools.commons, ...currentPools.uncommons]; // Simplification - if (listPool.length > 0) { - const rnd = Math.floor(Math.random() * listPool.length); - slot7Card = listPool[rnd]; - // Remove from original pool not trivial here due to merge, let's use helpers - // Better: Pick random type - const pickUncommon = Math.random() < 0.3; // Arbitrary weight - if (pickUncommon) { - const res = this.drawUniqueCards(currentPools.uncommons, 1, namesInThisPack); - if (res.success) { slot7Card = res.selected[0]; currentPools.uncommons = res.remainingPool; } - } else { - const res = this.drawUniqueCards(currentPools.commons, 1, namesInThisPack); - if (res.success) { slot7Card = res.selected[0]; currentPools.commons = res.remainingPool; } - } - } - } else { - // 98-100: Rare/Mythic/Special Guest - // 1/100 (1%) chance for Special Guest if available - const isGuest = roll7 === 100; - - if (isGuest && currentPools.specialGuests.length > 0) { + // List (Common/Uncommon) + if (currentPools.specialGuests.length > 0) { const res = this.drawUniqueCards(currentPools.specialGuests, 1, namesInThisPack); if (res.success) { slot7Card = res.selected[0]; currentPools.specialGuests = res.remainingPool; } } else { - // Fallback to Rare/Mythic - const useMythic = Math.random() < 0.125; // 1/8 - if (useMythic && currentPools.mythics.length > 0) { - const res = this.drawUniqueCards(currentPools.mythics, 1, namesInThisPack); - if (res.success) { slot7Card = res.selected[0]; currentPools.mythics = res.remainingPool; } - } else { - const res = this.drawUniqueCards(currentPools.rares, 1, namesInThisPack); - if (res.success) { slot7Card = res.selected[0]; currentPools.rares = res.remainingPool; } + const pool = Math.random() < 0.5 ? currentPools.commons : currentPools.uncommons; + const res = this.drawUniqueCards(pool, 1, namesInThisPack); + if (res.success) { + slot7Card = res.selected[0]; + if (pool === currentPools.commons) currentPools.commons = res.remainingPool; + else currentPools.uncommons = res.remainingPool; } } + } else if (roll7 <= 99) { + // List (Rare/Mythic) + if (currentPools.specialGuests.length > 0) { + const res = this.drawUniqueCards(currentPools.specialGuests, 1, namesInThisPack); + if (res.success) { slot7Card = res.selected[0]; currentPools.specialGuests = res.remainingPool; } + } else { + const pool = Math.random() < 0.125 ? currentPools.mythics : currentPools.rares; + const res = this.drawUniqueCards(pool, 1, namesInThisPack); + if (res.success) { + slot7Card = res.selected[0]; + if (pool === currentPools.mythics) currentPools.mythics = res.remainingPool; + else currentPools.rares = res.remainingPool; + } + } + } else { + // 100: Special Guest + if (currentPools.specialGuests.length > 0) { + const res = this.drawUniqueCards(currentPools.specialGuests, 1, namesInThisPack); + if (res.success) { slot7Card = res.selected[0]; currentPools.specialGuests = res.remainingPool; } + } else { + // Fallback Mythic + const res = this.drawUniqueCards(currentPools.mythics, 1, namesInThisPack); + if (res.success) { slot7Card = res.selected[0]; currentPools.mythics = res.remainingPool; } + } } if (slot7Card) { @@ -536,7 +532,6 @@ export class PackGeneratorService { // Fallback: Pick a Common if no lands // const res = this.drawUniqueCards(currentPools.commons, 1, namesInThisPack); // if (res.success) { landCard = { ...res.selected[0] }; ... } - // Better to just have no land than a non-land } if (landCard) { @@ -546,8 +541,7 @@ export class PackGeneratorService { } // 6. Slot 13: Wildcard (Non-Foil) - // Weights: ~49% C, ~24% U, ~13% R, ~13% M => Sum=99. - // Normalized: C:50, U:24, R:13, M:13 + // Weights: ~49% C, ~24% U, ~13% R, ~13% M const drawWildcard = (foil: boolean) => { const wRoll = Math.random() * 100; let wRarity = 'common'; @@ -556,7 +550,6 @@ export class PackGeneratorService { else if (wRoll > 50) wRarity = 'uncommon'; else wRarity = 'common'; - // Adjust buckets let poolToUse: DraftCard[] = []; let updatePool = (_newPool: DraftCard[]) => { }; @@ -566,7 +559,6 @@ export class PackGeneratorService { else { poolToUse = currentPools.commons; updatePool = (p) => currentPools.commons = p; } if (poolToUse.length === 0) { - // Fallback cascade if (currentPools.commons.length > 0) { poolToUse = currentPools.commons; updatePool = (p) => currentPools.commons = p; } } @@ -589,26 +581,15 @@ export class PackGeneratorService { // 8. Slot 15: Marketing / Token if (currentPools.tokens.length > 0) { - // Just pick one, duplicates allowed for tokens? user said unique cards... but for tokens? - // "drawUniqueCards" handles uniqueness check. const res = this.drawUniqueCards(currentPools.tokens, 1, namesInThisPack); if (res.success) { packCards.push(res.selected[0]); currentPools.tokens = res.remainingPool; - // Don't care about uniqueness for tokens as much, but let's stick to it } } } // Sort: Mythic -> Rare -> Uncommon -> Common -> Land -> Token - // We already have rarityWeight. - // Assign weight to 'land' or 'token'? - // DraftCard has 'rarity' string. - // Standard rarities: common, uncommon, rare, mythic. - // Basic Land has rarity 'common' usually? or 'basic'. - // Token has rarity 'common' or 'token' (if we set it?). Scryfall tokens often have no rarity or 'common'. - - // Custom sort const getWeight = (c: DraftCard) => { if (c.layout === 'token' || c.typeLine?.includes('Token')) return 0; if (c.typeLine?.includes('Land') && (c.rarity === 'common' || c.rarity === 'basic')) return 1; diff --git a/src/server/services/PackGeneratorService.ts b/src/server/services/PackGeneratorService.ts index bc424c3..b55afbe 100644 --- a/src/server/services/PackGeneratorService.ts +++ b/src/server/services/PackGeneratorService.ts @@ -339,8 +339,7 @@ export class PackGeneratorService { const packCards: DraftCard[] = []; const namesInPack = new Set(); - // Standard: 14 cards exactly. Peasant: 13 cards exactly. - const targetSize = rarityMode === 'peasant' ? 13 : 14; + const targetSize = 14; // Helper to abstract draw logic const draw = (pool: DraftCard[], count: number, poolKey: keyof ProcessedPools) => { @@ -356,110 +355,183 @@ export class PackGeneratorService { return result.selected; }; - // 1. Commons (6) - draw(pools.commons, 6, 'commons'); - - // 2. Slot 7 (Common or List) - const roll7 = Math.random() * 100; - if (roll7 < 87) { - // Common - draw(pools.commons, 1, 'commons'); - } else { - // If pool empty, try fallback if standard? No, strict as per previous instruction. - // draw(pools.uncommons, 1, 'uncommons'); - - // Slot 7: Common/List Slot (Strict adherence to standard-pack-generation-algorithm.md) - // 1-87: Common from Main Set - // 88-97: Card from The List (Common/Uncommon) -> Mapped to specialGuests - // 98-99: Rare/Mythic from The List -> Mapped to specialGuests - // 100: Special Guest -> Mapped to specialGuests - - // We simplify by drawing from specialGuests for 88-100 (13% chance) - // if specialGuests is empty, fallback to Common or Uncommon? - // Rulebook says "1-87 Common". + if (rarityMode === 'peasant') { + // 1. Commons (6) - Color Balanced + // Using drawColorBalanced helper + const drawC = this.drawColorBalanced(pools.commons, 6, namesInPack, withReplacement); + if (drawC.selected.length > 0) { + packCards.push(...drawC.selected); + if (!withReplacement) { + pools.commons = drawC.remainingPool; + drawC.selected.forEach(c => namesInPack.add(c.name)); + } + } + // 2. Slot 7: Common / The List + // 1-87: Common + // 88-97: List (C/U) + // 98-100: List (U) + const roll7 = Math.floor(Math.random() * 100) + 1; const hasGuests = pools.specialGuests.length > 0; - if (roll7 > 87 && hasGuests) { - draw(pools.specialGuests, 1, 'specialGuests'); - } else { - // 1-87 or no guests available + if (roll7 <= 87) { draw(pools.commons, 1, 'commons'); - // Note: If original rule for 88-97 was "Common from List", - // and we fall back to "Common from Set", it's acceptable if List/Guests missing. + } else if (roll7 <= 97) { + // List (C/U) - Fallback logic + if (hasGuests) draw(pools.specialGuests, 1, 'specialGuests'); + else { + // 50/50 fallback + const useU = Math.random() < 0.5; + if (useU) draw(pools.uncommons, 1, 'uncommons'); + else draw(pools.commons, 1, 'commons'); + } + } else { + // 98-100: List (U) + if (hasGuests) draw(pools.specialGuests, 1, 'specialGuests'); + else draw(pools.uncommons, 1, 'uncommons'); } - } - // 3. Uncommons (3 or 4 dependent on PEASANT vs STANDARD) - const uNeeded = rarityMode === 'peasant' ? 4 : 3; - draw(pools.uncommons, uNeeded, 'uncommons'); + // 3. Uncommons (4) + draw(pools.uncommons, 4, 'uncommons'); - // 4. Rare/Mythic (Standard Only) - if (rarityMode === 'standard') { + // 4. Land (Slot 12) + const isFoilLand = Math.random() < 0.2; + const landPicks = draw(pools.lands, 1, 'lands'); + if (landPicks.length > 0 && isFoilLand) { + const idx = packCards.indexOf(landPicks[0]); + if (idx !== -1) { + packCards[idx] = { ...packCards[idx], finish: 'foil' }; + } + } + + // 5. Wildcards (Slot 13 & 14) + // Peasant weights: ~62% Common, ~37% Uncommon + for (let i = 0; i < 2; i++) { + const isFoil = i === 1; + const wRoll = Math.random() * 100; + let targetKey: keyof ProcessedPools = 'commons'; + + // 1-62: Common, 63-100: Uncommon (Approx > 62) + if (wRoll > 62) targetKey = 'uncommons'; + else targetKey = 'commons'; + + let pool = pools[targetKey]; + if (pool.length === 0) { + // Fallback + targetKey = 'commons'; + pool = pools.commons; + } + + const res = this.drawCards(pool, 1, namesInPack, withReplacement); + if (res.selected.length > 0) { + const card = { ...res.selected[0] }; + if (isFoil) card.finish = 'foil'; + packCards.push(card); + if (!withReplacement) { + // @ts-ignore + pools[targetKey] = res.remainingPool; + namesInPack.add(card.name); + } + } + } + + } else { + // STANDARD MODE + + // 1. Commons (6) + const drawC = this.drawColorBalanced(pools.commons, 6, namesInPack, withReplacement); + if (drawC.selected.length > 0) { + packCards.push(...drawC.selected); + if (!withReplacement) { + pools.commons = drawC.remainingPool; + drawC.selected.forEach(c => namesInPack.add(c.name)); + } + } + + // 2. Slot 7 (Common / List / Guest) + // 1-87: Common + // 88-97: List (C/U) + // 98-99: List (R/M) + // 100: Special Guest + const roll7 = Math.floor(Math.random() * 100) + 1; // 1-100 + const hasGuests = pools.specialGuests.length > 0; + + if (roll7 <= 87) { + draw(pools.commons, 1, 'commons'); + } else if (roll7 <= 97) { + // List C/U + if (hasGuests) draw(pools.specialGuests, 1, 'specialGuests'); + else { + if (Math.random() < 0.5) draw(pools.uncommons, 1, 'uncommons'); + else draw(pools.commons, 1, 'commons'); + } + } else if (roll7 <= 99) { + // List R/M + if (hasGuests) draw(pools.specialGuests, 1, 'specialGuests'); + else { + if (Math.random() < 0.5) draw(pools.mythics, 1, 'mythics'); + else draw(pools.rares, 1, 'rares'); + } + } else { + // 100: Special Guest + if (hasGuests) draw(pools.specialGuests, 1, 'specialGuests'); + else draw(pools.mythics, 1, 'mythics'); // Fallback to Mythic + } + + // 3. Uncommons (3) + draw(pools.uncommons, 3, 'uncommons'); + + // 4. Main Rare/Mythic (Slot 11) const isMythic = Math.random() < 0.125; let pickedR = false; - if (isMythic && pools.mythics.length > 0) { const sel = draw(pools.mythics, 1, 'mythics'); if (sel.length) pickedR = true; } - - if (!pickedR && pools.rares.length > 0) { + if (!pickedR) { draw(pools.rares, 1, 'rares'); } - } - // 5. Land - const isFoilLand = Math.random() < 0.2; - if (pools.lands.length > 0) { - // For lands, we generally want random basic lands anyway even in finite cubes if possible? - // But adhering to 'withReplacement' logic strictly. - const res = this.drawCards(pools.lands, 1, namesInPack, withReplacement); - if (res.selected.length) { - const l = { ...res.selected[0] }; - if (isFoilLand) l.finish = 'foil'; - packCards.push(l); - if (!withReplacement) { - pools.lands = res.remainingPool; - namesInPack.add(l.name); + // 5. Land (Slot 12) + const isFoilLand = Math.random() < 0.2; + const landPicks = draw(pools.lands, 1, 'lands'); + if (landPicks.length > 0 && isFoilLand) { + const idx = packCards.indexOf(landPicks[0]); + if (idx !== -1) { + packCards[idx] = { ...packCards[idx], finish: 'foil' }; } } - } - // 6. Wildcards (2 slots) + Foil Wildcard - for (let i = 0; i < 2; i++) { - const isFoil = i === 1; // 2nd is foil - const wRoll = Math.random() * 100; - let targetPool = pools.commons; - let targetKey: keyof ProcessedPools = 'commons'; + // 6. Wildcards (Slot 13 & 14) + // Standard weights: ~49% C, ~24% U, ~13% R, ~13% M + for (let i = 0; i < 2; i++) { + const isFoil = i === 1; + const wRoll = Math.random() * 100; + let targetKey: keyof ProcessedPools = 'commons'; - if (rarityMode === 'peasant') { - if (wRoll > 60) { targetPool = pools.uncommons; targetKey = 'uncommons'; } - else { targetPool = pools.commons; targetKey = 'commons'; } - } else { - if (wRoll > 87) { targetPool = pools.mythics; targetKey = 'mythics'; } - else if (wRoll > 74) { targetPool = pools.rares; targetKey = 'rares'; } - else if (wRoll > 50) { targetPool = pools.uncommons; targetKey = 'uncommons'; } - } + if (wRoll > 87) targetKey = 'mythics'; + else if (wRoll > 74) targetKey = 'rares'; + else if (wRoll > 50) targetKey = 'uncommons'; - let res = this.drawCards(targetPool, 1, namesInPack, withReplacement); + let pool = pools[targetKey]; + // Hierarchical fallback + if (pool.length === 0) { + if (targetKey === 'mythics' && pools.rares.length) targetKey = 'rares'; + if ((targetKey === 'rares' || targetKey === 'mythics') && pools.uncommons.length) targetKey = 'uncommons'; + if (targetKey !== 'commons' && pools.commons.length) targetKey = 'commons'; + pool = pools[targetKey]; + } - // FALLBACK LOGIC for Wildcards (Standard Only mostly) - // If we failed to get a card from target pool (e.g. rolled Mythic but set has none), try lower rarity - if (!res.success && rarityMode === 'standard') { - if (targetKey === 'mythics' && pools.rares.length) { res = this.drawCards(pools.rares, 1, namesInPack, withReplacement); targetKey = 'rares'; } - else if (targetKey === 'rares' && pools.uncommons.length) { res = this.drawCards(pools.uncommons, 1, namesInPack, withReplacement); targetKey = 'uncommons'; } - else if (targetKey === 'uncommons' && pools.commons.length) { res = this.drawCards(pools.commons, 1, namesInPack, withReplacement); targetKey = 'commons'; } - } - - if (res.selected.length) { - const c = { ...res.selected[0] }; - if (isFoil) c.finish = 'foil'; - packCards.push(c); - if (!withReplacement) { - // @ts-ignore - pools[targetKey] = res.remainingPool; - namesInPack.add(c.name); + const res = this.drawCards(pool, 1, namesInPack, withReplacement); + if (res.selected.length > 0) { + const card = { ...res.selected[0] }; + if (isFoil) card.finish = 'foil'; + packCards.push(card); + if (!withReplacement) { + // @ts-ignore + pools[targetKey] = res.remainingPool; + namesInPack.add(card.name); + } } } } @@ -482,21 +554,21 @@ export class PackGeneratorService { packCards.sort((a, b) => getWeight(b) - getWeight(a)); - // ENFORCE SIZE STRICTLY - const finalCards = packCards.slice(0, targetSize); - - // Strict Validation - if (finalCards.length < targetSize) { + if (packCards.length < targetSize) { return null; } return { id: packId, setName: setName, - cards: finalCards + cards: packCards }; } + private drawColorBalanced(pool: DraftCard[], count: number, existingNames: Set, withReplacement: boolean) { + return this.drawCards(pool, count, existingNames, withReplacement); + } + // Unified Draw Method private drawCards(pool: DraftCard[], count: number, existingNames: Set, withReplacement: boolean) { if (pool.length === 0) return { selected: [], remainingPool: pool, success: false };