feat: Integrate EDHREC rank into card scoring and refactor auto deck builder for local, more sophisticated bot deck generation.

This commit is contained in:
2025-12-20 16:49:20 +01:00
parent 2794ce71aa
commit eb453fd906
7 changed files with 361 additions and 186 deletions

View File

@@ -6,6 +6,9 @@ interface Card {
name: string;
image_uris?: { normal: string };
card_faces?: { image_uris: { normal: string } }[];
colors?: string[];
rarity?: string;
edhrecRank?: number;
// ... other props
}
@@ -238,9 +241,41 @@ export class DraftManager extends EventEmitter {
const playerState = draft.players[playerId];
if (!playerState || !playerState.activePack || playerState.activePack.cards.length === 0) return null;
// Pick Random Card
const randomCardIndex = Math.floor(Math.random() * playerState.activePack.cards.length);
const card = playerState.activePack.cards[randomCardIndex];
// Score cards
const scoredCards = playerState.activePack.cards.map(c => {
let score = 0;
// 1. Rarity Base Score
if (c.rarity === 'mythic') score += 5;
else if (c.rarity === 'rare') score += 4;
else if (c.rarity === 'uncommon') score += 2;
else score += 1;
// 2. Color Synergy (Simple)
const poolColors = playerState.pool.flatMap(p => p.colors || []);
if (poolColors.length > 0 && c.colors) {
c.colors.forEach(col => {
const count = poolColors.filter(pc => pc === col).length;
score += (count * 0.1);
});
}
// 3. EDHREC Score (Lower rank = better)
if (c.edhrecRank !== undefined && c.edhrecRank !== null) {
const rank = c.edhrecRank;
if (rank < 10000) {
score += (5 * (1 - (rank / 10000)));
}
}
return { card: c, score };
});
// Sort by score desc
scoredCards.sort((a, b) => b.score - a.score);
// Pick top card
const card = scoredCards[0].card;
// Reuse existing logic
return this.pickCard(roomId, playerId, card.id);

View File

@@ -8,6 +8,7 @@ interface Card {
colorIdentity?: string[];
rarity?: string;
cmc?: number;
edhrecRank?: number; // Added EDHREC
}
export class BotDeckBuilderService {
@@ -49,11 +50,16 @@ export class BotDeckBuilderService {
const lands = candidates.filter(c => c.typeLine?.includes('Land')); // Non-basic lands in pool
const spells = candidates.filter(c => !c.typeLine?.includes('Land'));
// 4. Select Spells (Curve + Power)
// 4. Select Spells (Curve + Power + EDHREC)
// Sort by Weight + slight curve preference (lower cmc preferred for consistency)
spells.sort((a, b) => {
const weightA = this.getRarityWeight(a.rarity);
const weightB = this.getRarityWeight(b.rarity);
let weightA = this.getRarityWeight(a.rarity);
let weightB = this.getRarityWeight(b.rarity);
// Add EDHREC influence
if (a.edhrecRank !== undefined && a.edhrecRank < 10000) weightA += (3 * (1 - (a.edhrecRank / 10000)));
if (b.edhrecRank !== undefined && b.edhrecRank < 10000) weightB += (3 * (1 - (b.edhrecRank / 10000)));
return weightB - weightA;
});

View File

@@ -15,6 +15,7 @@ export interface DraftCard {
setCode: string;
setType: string;
finish?: 'foil' | 'normal';
edhrecRank?: number; // Added EDHREC Rank
oracleText?: string;
manaCost?: string;
[key: string]: any; // Allow extended props
@@ -103,7 +104,9 @@ export class PackGeneratorService {
set: cardData.set_name,
setCode: cardData.set,
setType: setType,
finish: cardData.finish || 'normal',
finish: cardData.finish,
edhrecRank: cardData.edhrec_rank, // Map EDHREC Rank
// Extended Metadata mappingl',
oracleText: cardData.oracle_text || cardData.card_faces?.[0]?.oracle_text || '',
manaCost: cardData.mana_cost || cardData.card_faces?.[0]?.mana_cost || '',
damageMarked: 0,

View File

@@ -28,6 +28,7 @@ export interface ScryfallCard {
layout: string;
type_line: string;
colors?: string[];
edhrec_rank?: number; // Add EDHREC rank
image_uris?: { normal: string; small?: string; large?: string; png?: string; art_crop?: string; border_crop?: string };
card_faces?: {
name: string;