add commander color identity validation and improve deck validation logic

This commit is contained in:
Matthieu
2025-11-26 13:51:26 +01:00
parent 8671745351
commit 8d0ce534f8
4 changed files with 43 additions and 33 deletions

View File

@@ -82,7 +82,7 @@ define(['./workbox-ca84f546'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "index.html", "url": "index.html",
"revision": "0.bbuf8ja4298" "revision": "0.ufhads5pjvs"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

View File

@@ -42,6 +42,7 @@ export default function DeckEditor({ deckId, onClose }: DeckEditorProps) {
const cards = cardEntities.map(entity => ({ const cards = cardEntities.map(entity => ({
card: scryfallCards.find(c => c.id === entity.card_id) as Card, card: scryfallCards.find(c => c.id === entity.card_id) as Card,
quantity: entity.quantity, quantity: entity.quantity,
is_commander: entity.is_commander,
})); }));
setDeck({ setDeck({

View File

@@ -442,11 +442,8 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) {
const validation = validateDeck(currentDeck); const validation = validateDeck(currentDeck);
// Commander color identity validation // Commander color identity validation (for land suggestions)
const commanderColors = deckFormat === 'commander' ? getCommanderColors(commander) : []; const commanderColors = deckFormat === 'commander' ? getCommanderColors(commander) : [];
const invalidCards = deckFormat === 'commander' && commander
? selectedCards.filter(({ card }) => !isCardValidForCommander(card, commanderColors))
: [];
const deckSize = selectedCards.reduce((acc, curr) => acc + curr.quantity, 0); const deckSize = selectedCards.reduce((acc, curr) => acc + curr.quantity, 0);
const { const {
@@ -829,26 +826,6 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) {
</div> </div>
)} )}
{/* Commander Color Identity Warning */}
{deckFormat === 'commander' && commander && invalidCards.length > 0 && (
<div className="bg-yellow-500/10 border border-yellow-500 rounded-lg p-3">
<div className="flex items-start gap-2">
<AlertCircle className="text-yellow-500 flex-shrink-0 mt-0.5" size={16} />
<div className="text-sm">
<p className="text-yellow-400 font-semibold mb-1">Commander Color Identity Warning</p>
<p className="text-yellow-300 text-xs mb-2">
The following cards don't match your commander's color identity:
</p>
<ul className="list-disc list-inside text-yellow-300 text-xs">
{invalidCards.map(({ card }) => (
<li key={card.id}>{card.name}</li>
))}
</ul>
</div>
</div>
</div>
)}
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h3 className="font-bold text-xl"> <h3 className="font-bold text-xl">

View File

@@ -1,10 +1,23 @@
import { Deck } from '../types'; import { Card, Deck } from '../types';
interface DeckValidation { interface DeckValidation {
isValid: boolean; isValid: boolean;
errors: string[]; errors: string[];
} }
// Helper function to get commander color identity
function getCommanderColors(commander: Card | null): string[] {
if (!commander) return [];
return commander.colors || [];
}
// Helper function to check if a card's colors are valid for the commander
function isCardValidForCommander(card: Card, commanderColors: string[]): boolean {
if (commanderColors.length === 0) return true;
const cardColors = card.colors || [];
return cardColors.every(color => commanderColors.includes(color));
}
const FORMAT_RULES = { const FORMAT_RULES = {
standard: { standard: {
minCards: 60, minCards: 60,
@@ -42,20 +55,20 @@ const FORMAT_RULES = {
export function validateDeck(deck: Deck): DeckValidation { export function validateDeck(deck: Deck): DeckValidation {
const rules = FORMAT_RULES[deck.format as keyof typeof FORMAT_RULES]; const rules = FORMAT_RULES[deck.format as keyof typeof FORMAT_RULES];
const errors: string[] = []; const errors: string[] = [];
// Count total cards // Count total cards
const totalCards = deck.cards.reduce((acc, curr) => acc + curr.quantity, 0); const totalCards = deck.cards.reduce((acc, curr) => acc + curr.quantity, 0);
// Check minimum cards // Check minimum cards
if (totalCards < rules.minCards) { if (totalCards < rules.minCards) {
errors.push(`Deck must contain at least ${rules.minCards} cards`); errors.push(`Deck must contain at least ${rules.minCards} cards`);
} }
// Check maximum cards // Check maximum cards
if (rules.maxCards && totalCards > rules.maxCards) { if (rules.maxCards && totalCards > rules.maxCards) {
errors.push(`Deck must not contain more than ${rules.maxCards} cards`); errors.push(`Deck must not contain more than ${rules.maxCards} cards`);
} }
// Check card copies // Check card copies
const cardCounts = new Map<string, number>(); const cardCounts = new Map<string, number>();
for (const element of deck.cards) { for (const element of deck.cards) {
@@ -64,7 +77,7 @@ export function validateDeck(deck: Deck): DeckValidation {
const currentCount = cardCounts.get(card.id) || 0; const currentCount = cardCounts.get(card.id) || 0;
cardCounts.set(card.id, currentCount + quantity); cardCounts.set(card.id, currentCount + quantity);
} }
cardCounts.forEach((count, cardName) => { cardCounts.forEach((count, cardName) => {
const card = deck.cards.find(c => c.card.id === cardName)?.card; const card = deck.cards.find(c => c.card.id === cardName)?.card;
const isBasicLand = card?.name === 'Plains' || card?.name === 'Island' || card?.name === 'Swamp' || card?.name === 'Mountain' || card?.name === 'Forest'; const isBasicLand = card?.name === 'Plains' || card?.name === 'Island' || card?.name === 'Swamp' || card?.name === 'Mountain' || card?.name === 'Forest';
@@ -73,9 +86,28 @@ export function validateDeck(deck: Deck): DeckValidation {
errors.push(`${cardName} has too many copies (max ${rules.maxCopies})`); errors.push(`${cardName} has too many copies (max ${rules.maxCopies})`);
} }
}); });
// Commander-specific validations
if (deck.format === 'commander') {
const commander = deck.cards.find(card => card.is_commander)?.card;
if (!commander) {
errors.push('Commander deck must have a commander');
} else {
// Check commander color identity
const commanderColors = getCommanderColors(commander);
const invalidCards = deck.cards.filter(({ card, is_commander }) =>
!is_commander && !isCardValidForCommander(card, commanderColors)
);
if (invalidCards.length > 0) {
errors.push(`Some cards don't match commander's color identity`);
}
}
}
return { return {
isValid: errors.length === 0, isValid: errors.length === 0,
errors, errors,
}; };
} }