From 5bbcda3d4ee7c01b8cd33b12734c6cd0b6410600 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sun, 26 Oct 2025 15:47:41 -0500 Subject: [PATCH] Update card test generation and card info templating * now accepts lower case name matching * partial matching also works with confirmation with multiple matches --- Utils/cardInfo.tmpl | 2 +- Utils/gen-card-test.pl | 158 +++++++++++++++++++++++++++++++++++------ 2 files changed, 136 insertions(+), 24 deletions(-) diff --git a/Utils/cardInfo.tmpl b/Utils/cardInfo.tmpl index 5b04c6a01b1..c0f7b5013a5 100644 --- a/Utils/cardInfo.tmpl +++ b/Utils/cardInfo.tmpl @@ -2,6 +2,6 @@ [=$cardName=] [=$manaCost=] [=$typeLine=] - [=$abilities=][= $powerToughness ? "\n $powerToughness" : "" =] + [=$abilities=][=$loyalty ? "\n Loyalty:$loyalty" : "" =][=$powerToughness ? "\n $powerToughness" : "" =] */ private static final String [=$classNameLower=] = "[=$cardName=]"; \ No newline at end of file diff --git a/Utils/gen-card-test.pl b/Utils/gen-card-test.pl index 1817ed3a63f..152f5ec8aa3 100755 --- a/Utils/gen-card-test.pl +++ b/Utils/gen-card-test.pl @@ -11,7 +11,6 @@ You can add as many additional cards as you like. You can also call the script without arguments and it will prompt you for card names =cut - use Text::Template; use strict; use File::Path qw(make_path); @@ -40,12 +39,87 @@ sub fixCost { $string; } +# Resolve a user-provided card name to the canonical card key in %cards. +# Tries: +# 1) exact key +# 2) case-insensitive exact match +# 3) case-insensitive substring match (if single match => use it, if multiple => warn and return undef) +sub resolveCardName { + my ($input) = @_; + return undef unless defined $input; + # trim whitespace + $input =~ s/^\s+|\s+$//g; + return $input if exists $cards{$input}; + + my $lc_input = lc $input; + + # case-insensitive exact + foreach my $k (keys %cards) { + return $k if lc($k) eq $lc_input; + } + + # substring (partial) matches + my @matches = grep { index(lc($_), $lc_input) != -1 } keys %cards; + if (@matches == 1) { + return $matches[0]; + } elsif (@matches > 1) { + @matches = sort @matches; + # If not interactive, don't block; print candidates and return undef + unless (-t STDIN) { + warn "Multiple matches found for '$input' (non-interactive):\n"; + foreach my $m (@matches) { warn " $m\n"; } + warn "Please be more specific.\n"; + return undef; + } + + print "Multiple matches found for '$input':\n"; + my $i = 0; + foreach my $m (@matches) { + $i++; + print " $i) $m\n"; + } + + while (1) { + print "Select a number (1-$i) or 0 to cancel: "; + my $choice = ; + unless (defined $choice) { print "\nNo selection (EOF). Skipping.\n"; return undef; } + chomp $choice; + $choice =~ s/^\s+|\s+$//g; + + # numeric choice + if ($choice =~ /^\d+$/) { + my $num = int($choice); + if ($num == 0) { + return undef; + } elsif ($num >= 1 && $num <= $i) { + return $matches[$num - 1]; + } + } else { + # try exact name match among candidates (case-insensitive) + foreach my $m (@matches) { + return $m if lc($m) eq lc($choice); + } + } + + print "Invalid selection, please try again.\n"; + } + } + + return undef; +} + + sub generateCardInfo { my ($cardName, $infoTemplate) = @_; + # attempt to resolve loosely if direct lookup fails if (!exists $cards{$cardName}) { - warn "Card name doesn't exist: $cardName (skipping)\n"; - return ""; + my $resolved = resolveCardName($cardName); + if (!defined $resolved) { + warn "Card name doesn't exist: $cardName (skipping)\n"; + return ""; + } + $cardName = $resolved; } my %vars; @@ -61,17 +135,30 @@ sub generateCardInfo { $vars{'manaCost'} = $card[4]; $vars{'typeLine'} = $card[5]; - my $cardAbilities = $card[8]; + # Check if this is a planeswalker + my $isPlaneswalker = $card[5] =~ /Planeswalker/i; + + my $cardAbilities; + if ($isPlaneswalker) { + # For planeswalkers: field 6 is loyalty, field 7 is abilities + $vars{'loyalty'} = $card[6] if $card[6]; # loyalty + $cardAbilities = $card[7]; + } else { + # For non-planeswalkers: field 6/7 is power/toughness, field 8 is abilities + if ($card[6]) { + $vars{'powerToughness'} = "$card[6]/$card[7]"; + } + $cardAbilities = $card[8]; + } + my @abilities = split(/\$/, $cardAbilities); my $abilitiesFormatted = join("\n ", @abilities); $vars{'abilities'} = $abilitiesFormatted; - if ($card[6]) { - $vars{'powerToughness'} = "$card[6]/$card[7]" - } return $infoTemplate->fill_in(HASH => \%vars); } + my $author; if (-e $authorFile) { open(DATA, $authorFile) || die "can't open $authorFile : $!"; @@ -110,7 +197,7 @@ while (my $line = ) { } close(DATA); -# Get card names from arguments +# Get card names from arguments or prompt my @cardNames = @ARGV; if (@cardNames == 0) { print 'Enter a card name: '; @@ -127,17 +214,27 @@ if (@cardNames == 0) { } } +# Trim whitespace for all inputs +foreach my $i (0..$#cardNames) { + $cardNames[$i] =~ s/^\s+|\s+$//g if defined $cardNames[$i]; +} + # Main card is the first one -my $mainCardName = $cardNames[0]; -my @additionalCards = @cardNames[1..$#cardNames]; +my $mainCardNameInput = $cardNames[0]; + +# Resolve main card with loose matching +my $resolvedMain = resolveCardName($mainCardNameInput); +if (!defined $resolvedMain) { + die "Card name doesn't exist or is ambiguous: $mainCardNameInput\n"; +} +my $mainCardName = $resolvedMain; + +my @additionalCardsInput = (); +if (@cardNames > 1) { + @additionalCardsInput = @cardNames[1..$#cardNames]; +} if (!exists $cards{$mainCardName}) { - my $possible; - foreach $possible (sort keys(%cards)) { - if ($possible =~ m/$mainCardName/img && $mainCardName =~ m/..../) { - print("Did you mean $possible?\n"); - } - } die "Card name doesn't exist: $mainCardName\n"; } @@ -153,7 +250,17 @@ $vars{'classNameLower'} = lcfirst(toCamelCase($mainCardName)); $vars{'cardNameFirstLetter'} = lc substr($mainCardName, 0, 1); foreach my $setName (keys %{$cards{$originalName}}) { - $setCode = lc($sets{$setName}); + if (exists $sets{$setName}) { + $setCode = lc($sets{$setName}); + last; # Use the first valid set found + } +} + +# Fallback if no valid set code was found +unless (defined $setCode) { + warn "Warning: No valid set code found for card '$mainCardName'. Using 'unk' as fallback.\n"; + warn "Available sets for this card: " . join(", ", keys %{$cards{$originalName}}) . "\n"; + $setCode = 'unk'; } # Check if card is already implemented @@ -177,10 +284,15 @@ $vars{'setCode'} = $setCode; # Generate main card info my $allCardInfo = generateCardInfo($mainCardName, $infoTemplate); -# Generate additional card info templates -foreach my $additionalCard (@additionalCards) { - my $additionalInfo = generateCardInfo($additionalCard, $infoTemplate); - if (defined $additionalInfo) { +# Generate additional card info templates (resolve each loosely) +foreach my $additionalCardInput (@additionalCardsInput) { + my $resolved = resolveCardName($additionalCardInput); + if (!defined $resolved) { + warn "Skipping additional card (not found or ambiguous): $additionalCardInput\n"; + next; + } + my $additionalInfo = generateCardInfo($resolved, $infoTemplate); + if (defined $additionalInfo && $additionalInfo ne '') { $allCardInfo .= "\n\n" . $additionalInfo; } } @@ -193,6 +305,6 @@ print CARD $result; close CARD; print "$fileName\n"; -if (@additionalCards > 0) { - print "Additional cards included: " . join(", ", @additionalCards) . "\n"; +if (@additionalCardsInput > 0) { + print "Additional cards included: " . join(", ", map { resolveCardName($_) // $_ } @additionalCardsInput) . "\n"; } \ No newline at end of file