GUI, deck: added deck's auto-validate on import or load, improved generation performance;

This commit is contained in:
Oleg Agafonov 2025-07-07 10:08:24 +04:00
parent ff239019d5
commit c0256da7de
2 changed files with 48 additions and 29 deletions

View file

@ -438,15 +438,15 @@ public class DeckGeneratorPool {
} }
// more spells than needed - remove // more spells than needed - remove
else if (spellsSize > (deckSize - landCount)) { if (spellsSize > nonLandSize) {
int spellsRemoved = (spellsSize) - (deckSize - landCount); int removeCount = spellsSize - nonLandSize;
for (int i = 0; i < spellsRemoved; ++i) { for (int i = 0; i < removeCount; ++i) {
deckCards.remove(RandomUtil.nextInt(deckCards.size())); deckCards.remove(RandomUtil.randomFromCollection(deckCards));
} }
} }
if (deckCards.size() != nonLandSize) { if (deckCards.size() != nonLandSize) {
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors"); logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors (wrong non land cards amount)");
} }
return deckCards; return deckCards;
@ -610,10 +610,11 @@ public class DeckGeneratorPool {
if (needCommandersCount > 0 && !genPool.cardCounts.isEmpty()) { if (needCommandersCount > 0 && !genPool.cardCounts.isEmpty()) {
throw new IllegalArgumentException("Wrong code usage: generateSpells with creatures and commanders must be called as first"); throw new IllegalArgumentException("Wrong code usage: generateSpells with creatures and commanders must be called as first");
} }
List<CardInfo> cardPool = CardRepository.instance.findCards(criteria); List<Card> cardsPool = CardRepository.instance.findCards(criteria).stream()
List<Card> commanderPool = cardPool.stream()
.map(CardInfo::createMockCard) .map(CardInfo::createMockCard)
.filter(genPool::isValidSpellCard) .filter(genPool::isValidSpellCard)
.collect(Collectors.toList());
List<Card> commandersPool = cardsPool.stream()
.filter(genPool::isValidCommander) .filter(genPool::isValidCommander)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -621,14 +622,16 @@ public class DeckGeneratorPool {
int usedCardsCount = 0; int usedCardsCount = 0;
int validCommanders = 0; int validCommanders = 0;
int reservesAdded = 0; int reservesAdded = 0;
if (cardPool.size() > 0 && cardPool.size() >= needCardsCount) { if (cardsPool.size() > 0 && cardsPool.size() >= needCardsCount) {
int tries = 0; int tries = 0;
List<Card> possibleCards = new ArrayList<>(cardsPool);
List<Card> possibleCommanders = new ArrayList<>(commandersPool);
while (true) { while (true) {
tries++; tries++;
// can't finish deck, stop and use reserved cards later // can't finish deck, stop and use reserved cards later
if (tries > DeckGenerator.MAX_TRIES) { if (tries > DeckGenerator.MAX_TRIES) {
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors"); logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors (max tries exceeded)");
break; break;
} }
@ -640,23 +643,31 @@ public class DeckGeneratorPool {
validCommanders = 0; validCommanders = 0;
deckCMCs = genPool.getCMCsForSpellCount(needCardsCount); deckCMCs = genPool.getCMCsForSpellCount(needCardsCount);
genPool.clearCards(true); genPool.clearCards(true);
possibleCards = new ArrayList<>(cardsPool);
possibleCommanders = new ArrayList<>(commandersPool);
continue; continue;
} }
break; break;
} }
if (possibleCards.isEmpty()) {
throw new IllegalStateException("Not enough cards to generate deck (possible cards is empty)");
}
// choose commander first // choose commander first
Card card = null; Card card = null;
if (validCommanders < needCommandersCount && !commanderPool.isEmpty()) { if (validCommanders < needCommandersCount && !possibleCommanders.isEmpty()) {
card = RandomUtil.randomFromCollection(commanderPool); card = RandomUtil.randomFromCollection(possibleCommanders);
} }
// choose other cards after commander // choose other cards after commander
if (card == null) { if (card == null) {
card = RandomUtil.randomFromCollection(cardPool).createMockCard(); card = RandomUtil.randomFromCollection(possibleCards);
} }
if (!genPool.isValidSpellCard(card)) { if (!genPool.isValidSpellCard(card)) {
possibleCards.remove(card);
possibleCommanders.remove(card);
continue; continue;
} }
@ -681,7 +692,7 @@ public class DeckGeneratorPool {
} }
} }
} else { } else {
throw new IllegalStateException("Not enough cards to generate deck."); throw new IllegalStateException("Not enough cards to generate deck (cards pool too small)");
} }
} }

View file

@ -19,7 +19,6 @@ import mage.client.dialog.AddLandDialog;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins; import mage.client.plugins.impl.Plugins;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.GUISizeHelper;
import mage.client.util.Listener; import mage.client.util.Listener;
import mage.client.util.audio.AudioManager; import mage.client.util.audio.AudioManager;
import mage.components.CardInfoPane; import mage.components.CardInfoPane;
@ -31,7 +30,6 @@ import mage.util.XmageThreadFactory;
import mage.view.CardView; import mage.view.CardView;
import mage.view.SimpleCardView; import mage.view.SimpleCardView;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.mage.card.arcane.ManaSymbols;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
@ -534,7 +532,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} }
} }
}); });
refreshDeck(true);
refreshDeck(true, false);
// auto-import dropped files from OS // auto-import dropped files from OS
if (mode == DeckEditorMode.FREE_BUILDING) { if (mode == DeckEditorMode.FREE_BUILDING) {
@ -673,20 +672,28 @@ public class DeckEditorPanel extends javax.swing.JPanel {
private void refreshDeck() { private void refreshDeck() {
if (this.isVisible()) { // TODO: test auto-close deck with active lands dialog, e.g. on timeout if (this.isVisible()) { // TODO: test auto-close deck with active lands dialog, e.g. on timeout
refreshDeck(false); refreshDeck(false, false);
} }
} }
private void refreshDeck(boolean useLayout) { private void refreshDeck(boolean useLayout, boolean useDeckValidation) {
try { try {
setCursor(new Cursor(Cursor.WAIT_CURSOR)); MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
this.txtDeckName.setText(deck.getName()); this.txtDeckName.setText(deck.getName());
deckArea.loadDeck(deck, useLayout, bigCard); deckArea.loadDeck(deck, useLayout, bigCard);
if (useDeckValidation) {
validateDeck();
}
} finally { } finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
} }
} }
private void validateDeck() {
this.deckLegalityDisplay.setVisible(true);
this.deckLegalityDisplay.validateDeck(deck);
}
private void setTimeout(int s) { private void setTimeout(int s) {
int minute = s / 60; int minute = s / 60;
int second = s - (minute * 60); int second = s - (minute * 60);
@ -762,7 +769,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (newDeck != null) { if (newDeck != null) {
deck = newDeck; deck = newDeck;
refreshDeck(); refreshDeck(false, true);
} }
// save last deck import folder // save last deck import folder
@ -818,7 +825,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
Deck deckToAppend = Deck.load(DeckImporter.importDeckFromFile(tempDeckPath, errorMessages, false), true, true); Deck deckToAppend = Deck.load(DeckImporter.importDeckFromFile(tempDeckPath, errorMessages, false), true, true);
processAndShowImportErrors(errorMessages); processAndShowImportErrors(errorMessages);
this.deck = Deck.append(deckToAppend, this.deck); this.deck = Deck.append(deckToAppend, this.deck);
refreshDeck(); refreshDeck(false, true);
} catch (GameException e1) { } catch (GameException e1) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
} finally { } finally {
@ -922,7 +929,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (newDeck != null) { if (newDeck != null) {
deck = newDeck; deck = newDeck;
refreshDeck(); refreshDeck(false, true);
return true; return true;
} }
@ -1441,7 +1448,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (newDeck != null) { if (newDeck != null) {
deck = newDeck; deck = newDeck;
refreshDeck(true); refreshDeck(true, true);
} }
// save last deck history // save last deck history
@ -1474,7 +1481,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
// in deck editor mode - clear all cards // in deck editor mode - clear all cards
deck = new Deck(); deck = new Deck();
} }
refreshDeck(); refreshDeck(false, true);
}//GEN-LAST:event_btnNewActionPerformed }//GEN-LAST:event_btnNewActionPerformed
private void btnExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExitActionPerformed private void btnExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExitActionPerformed
@ -1483,7 +1490,9 @@ public class DeckEditorPanel extends javax.swing.JPanel {
private void btnAddLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddLandActionPerformed private void btnAddLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddLandActionPerformed
AddLandDialog dialog = new AddLandDialog(); AddLandDialog dialog = new AddLandDialog();
dialog.showDialog(deck, mode, this::refreshDeck); dialog.showDialog(deck, mode, () -> {
this.refreshDeck(false, true);
});
}//GEN-LAST:event_btnAddLandActionPerformed }//GEN-LAST:event_btnAddLandActionPerformed
private void btnGenDeckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnGenDeckActionPerformed private void btnGenDeckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnGenDeckActionPerformed
@ -1501,7 +1510,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} finally { } finally {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
} }
refreshDeck(); refreshDeck(false, true);
}//GEN-LAST:event_btnGenDeckActionPerformed }//GEN-LAST:event_btnGenDeckActionPerformed
private void btnSubmitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubmitActionPerformed private void btnSubmitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubmitActionPerformed
@ -1548,8 +1557,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
}//GEN-LAST:event_btnExportActionPerformed }//GEN-LAST:event_btnExportActionPerformed
private void btnLegalityActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLegalityActionPerformed private void btnLegalityActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLegalityActionPerformed
this.deckLegalityDisplay.setVisible(true); validateDeck();
this.deckLegalityDisplay.validateDeck(deck);
}//GEN-LAST:event_btnLegalityActionPerformed }//GEN-LAST:event_btnLegalityActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables