fix: expansion pack generation limit by adding a withReplacement setting and enabling it for set-based drafts.

This commit is contained in:
2025-12-17 01:45:27 +01:00
parent ca2efb5cd7
commit 97276979bf
5 changed files with 84 additions and 11 deletions

View File

@@ -214,7 +214,10 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
cards: sourceMode === 'upload' ? rawScryfallData : [],
sourceMode,
selectedSets,
settings: genSettings,
settings: {
...genSettings,
withReplacement: sourceMode === 'set'
},
numBoxes,
numPacks: sourceMode === 'set' ? (numBoxes * 36) : undefined,
filters

View File

@@ -76,6 +76,7 @@ export interface SetsMap {
export interface PackGenerationSettings {
mode: 'mixed' | 'by_set';
rarityMode: 'peasant' | 'standard'; // Peasant: 10C/3U, Standard: 10C/3U/1R
withReplacement?: boolean;
}
export class PackGeneratorService {

View File

@@ -49,6 +49,7 @@ export interface SetsMap {
export interface PackGenerationSettings {
mode: 'mixed' | 'by_set';
rarityMode: 'peasant' | 'standard'; // Peasant: 10C/3U, Standard: 10C/3U/1R
withReplacement?: boolean; // If true, pools are refilled/reshuffled for each pack (unlimited generation)
}
export class PackGeneratorService {
@@ -149,7 +150,7 @@ export class PackGeneratorService {
generatePacks(pools: ProcessedPools, sets: SetsMap, settings: PackGenerationSettings, numPacks: number): Pack[] {
console.time('generatePacks');
console.log('[PackGenerator] Starting generation:', { mode: settings.mode, rarity: settings.rarityMode, count: numPacks });
console.log('[PackGenerator] Starting generation:', { mode: settings.mode, rarity: settings.rarityMode, count: numPacks, infinite: settings.withReplacement });
// Optimize: Deep clone only what's needed?
// Actually, we destructively modify lists in the algo (shifting/drawing), so we must clone the arrays of specific pools we use.
@@ -159,7 +160,8 @@ export class PackGeneratorService {
if (settings.mode === 'mixed') {
// Mixed Mode (Chaos)
const currentPools = {
// Initial Shuffle of the master pools
let currentPools = {
commons: this.shuffle([...pools.commons]),
uncommons: this.shuffle([...pools.uncommons]),
rares: this.shuffle([...pools.rares]),
@@ -177,12 +179,42 @@ export class PackGeneratorService {
});
for (let i = 1; i <= numPacks; i++) {
const result = this.buildSinglePack(currentPools, i, 'Chaos Pack', settings.rarityMode);
if (!result) {
console.warn(`[PackGenerator] Warning: ran out of cards at pack ${i}`);
break;
// If infinite, we reset the pools for every pack (using a fresh shuffle of original pools)
let packPools = currentPools;
if (settings.withReplacement) {
packPools = {
commons: this.shuffle([...pools.commons]),
uncommons: this.shuffle([...pools.uncommons]),
rares: this.shuffle([...pools.rares]),
mythics: this.shuffle([...pools.mythics]),
lands: this.shuffle([...pools.lands]),
tokens: this.shuffle([...pools.tokens])
};
}
const result = this.buildSinglePack(packPools, i, 'Chaos Pack', settings.rarityMode);
if (result) {
newPacks.push(result);
if (!settings.withReplacement) {
// If not infinite, we must persist the depleting state
// This assumes buildSinglePack MODIFIED packPools in place (via reassigning properties).
// However, packPools is a shallow clone of currentPools if (settings.infinite) was false?
// Wait. 'let packPools = currentPools' is a reference copy.
// buildSinglePack reassigns properties of packPools.
// e.g. packPools.commons = ...
// This mutates the object 'packPools'.
// If 'packPools' IS 'currentPools', then 'currentPools' is mutated. Correct.
}
} else {
if (!settings.withReplacement) {
console.warn(`[PackGenerator] Warning: ran out of cards at pack ${i}`);
break;
} else {
// Should not happen with replacement unless pools are intrinsically empty
console.warn(`[PackGenerator] Infinite mode but failed to generate pack ${i} (empty source?)`);
}
}
newPacks.push(result);
if (i % 50 === 0) console.log(`[PackGenerator] Built ${i} packs...`);
}
@@ -208,7 +240,8 @@ export class PackGeneratorService {
const data = sets[setCode];
console.log(`[PackGenerator] Generating ${packsPerSet} packs for set ${data.name}`);
const currentPools = {
// Initial Shuffle
let currentPools = {
commons: this.shuffle([...data.commons]),
uncommons: this.shuffle([...data.uncommons]),
rares: this.shuffle([...data.rares]),
@@ -220,13 +253,26 @@ export class PackGeneratorService {
for (let i = 0; i < packsPerSet; i++) {
if (packId > numPacks) break;
const result = this.buildSinglePack(currentPools, packId, data.name, settings.rarityMode);
let packPools = currentPools;
if (settings.withReplacement) {
// Refresh pools for every pack from the source data
packPools = {
commons: this.shuffle([...data.commons]),
uncommons: this.shuffle([...data.uncommons]),
rares: this.shuffle([...data.rares]),
mythics: this.shuffle([...data.mythics]),
lands: this.shuffle([...data.lands]),
tokens: this.shuffle([...data.tokens])
};
}
const result = this.buildSinglePack(packPools, packId, data.name, settings.rarityMode);
if (result) {
newPacks.push(result);
packId++;
} else {
console.warn(`[PackGenerator] Set ${data.name} depleted at pack ${packId}`);
break;
if (!settings.withReplacement) break;
}
}
}