mirror of
https://github.com/magefree/mage.git
synced 2025-12-29 23:12:10 -08:00
* Added a deck hash tag that's shown at the start of the game log to be able to identify a deck.
This commit is contained in:
parent
3bdf2a7957
commit
0fb7cf8317
7 changed files with 95 additions and 15 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
package mage.utils;
|
package mage.utils;
|
||||||
|
|
||||||
import mage.constants.CardType;
|
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.MagePermanent;
|
import mage.cards.MagePermanent;
|
||||||
|
import mage.constants.CardType;
|
||||||
import mage.view.CardView;
|
import mage.view.CardView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ public class DeckBuilder {
|
||||||
deckSize = deckCardSize;
|
deckSize = deckCardSize;
|
||||||
deck = new Deck();
|
deck = new Deck();
|
||||||
|
|
||||||
final Collection<MageScoredCard> remainingCards = new ArrayList<MageScoredCard>();
|
final Collection<MageScoredCard> remainingCards = new ArrayList<>();
|
||||||
Set<String> names = new HashSet<String>();
|
Set<String> names = new HashSet<>();
|
||||||
for (final Card card : spellCardPool) {
|
for (final Card card : spellCardPool) {
|
||||||
if (names.contains(card.getName())) {
|
if (names.contains(card.getName())) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -155,7 +155,7 @@ public class DeckBuilder {
|
||||||
private static void addLandsToDeck(List<ColoredManaSymbol> allowedColors, List<String> setsToUse, List<Card> landCardPool, RateCallback callback) {
|
private static void addLandsToDeck(List<ColoredManaSymbol> allowedColors, List<String> setsToUse, List<Card> landCardPool, RateCallback callback) {
|
||||||
|
|
||||||
// Calculate statistics per color.
|
// Calculate statistics per color.
|
||||||
final Map<String, Integer> colorCount = new HashMap<String, Integer>();
|
final Map<String, Integer> colorCount = new HashMap<>();
|
||||||
for (final Card card : deck.getCards()) {
|
for (final Card card : deck.getCards()) {
|
||||||
|
|
||||||
for (String symbol : card.getManaCost().getSymbols()) {
|
for (String symbol : card.getManaCost().getSymbols()) {
|
||||||
|
|
@ -170,7 +170,7 @@ public class DeckBuilder {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
Integer typeCount = colorCount.get(symbol);
|
Integer typeCount = colorCount.get(symbol);
|
||||||
if (typeCount == null) {
|
if (typeCount == null) {
|
||||||
typeCount = new Integer(0);
|
typeCount = 0;
|
||||||
}
|
}
|
||||||
typeCount += 1;
|
typeCount += 1;
|
||||||
colorCount.put(symbol, typeCount);
|
colorCount.put(symbol, typeCount);
|
||||||
|
|
@ -180,7 +180,7 @@ public class DeckBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add suitable non basic lands to deck in order of pack.
|
// Add suitable non basic lands to deck in order of pack.
|
||||||
final Map<String, Integer> colorSource = new HashMap<String, Integer>();
|
final Map<String, Integer> colorSource = new HashMap<>();
|
||||||
for (final ColoredManaSymbol color : ColoredManaSymbol.values()) {
|
for (final ColoredManaSymbol color : ColoredManaSymbol.values()) {
|
||||||
colorSource.put(color.toString(), 0);
|
colorSource.put(color.toString(), 0);
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +236,7 @@ public class DeckBuilder {
|
||||||
private static class MageScoredCard {
|
private static class MageScoredCard {
|
||||||
|
|
||||||
private Card card;
|
private Card card;
|
||||||
private int score;
|
private final int score;
|
||||||
|
|
||||||
private static final int SINGLE_PENALTY[] = {0, 1, 1, 3, 6, 9};
|
private static final int SINGLE_PENALTY[] = {0, 1, 1, 3, 6, 9};
|
||||||
//private static final int DOUBLE_PENALTY[] = { 0, 0, 1, 2, 4, 6 };
|
//private static final int DOUBLE_PENALTY[] = { 0, 0, 1, 2, 4, 6 };
|
||||||
|
|
@ -266,10 +266,10 @@ public class DeckBuilder {
|
||||||
|
|
||||||
private int getManaCostScore(Card card, List<ColoredManaSymbol> allowedColors) {
|
private int getManaCostScore(Card card, List<ColoredManaSymbol> allowedColors) {
|
||||||
int converted = card.getManaCost().convertedManaCost();
|
int converted = card.getManaCost().convertedManaCost();
|
||||||
final Map<String, Integer> singleCount = new HashMap<String, Integer>();
|
final Map<String, Integer> singleCount = new HashMap<>();
|
||||||
int maxSingleCount = 0;
|
int maxSingleCount = 0;
|
||||||
int multicolor = 0;
|
int multicolor = 0;
|
||||||
Set<String> colors = new HashSet<String>();
|
Set<String> colors = new HashSet<>();
|
||||||
for (String symbol : card.getManaCost().getSymbols()) {
|
for (String symbol : card.getManaCost().getSymbols()) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
symbol = symbol.replace("{", "").replace("}", "");
|
symbol = symbol.replace("{", "").replace("}", "");
|
||||||
|
|
@ -289,7 +289,7 @@ public class DeckBuilder {
|
||||||
}
|
}
|
||||||
Integer typeCount = singleCount.get(symbol);
|
Integer typeCount = singleCount.get(symbol);
|
||||||
if (typeCount == null) {
|
if (typeCount == null) {
|
||||||
typeCount = new Integer(0);
|
typeCount = 0;
|
||||||
}
|
}
|
||||||
typeCount += 1;
|
typeCount += 1;
|
||||||
singleCount.put(symbol, typeCount);
|
singleCount.put(symbol, typeCount);
|
||||||
|
|
|
||||||
|
|
@ -29,18 +29,25 @@
|
||||||
package mage.cards.decks;
|
package mage.cards.decks;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.repository.CardInfo;
|
import mage.cards.repository.CardInfo;
|
||||||
import mage.cards.repository.CardRepository;
|
import mage.cards.repository.CardRepository;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
|
import mage.util.DeckUtil;
|
||||||
|
|
||||||
public class Deck implements Serializable {
|
public class Deck implements Serializable {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private final Set<Card> cards = new LinkedHashSet<>();
|
private final Set<Card> cards = new LinkedHashSet<>();
|
||||||
private final Set<Card> sideboard = new LinkedHashSet<>();
|
private final Set<Card> sideboard = new LinkedHashSet<>();
|
||||||
|
private long deckHashCode = 0;
|
||||||
|
|
||||||
public static Deck load(DeckCardLists deckCardLists) throws GameException {
|
public static Deck load(DeckCardLists deckCardLists) throws GameException {
|
||||||
return Deck.load(deckCardLists, false);
|
return Deck.load(deckCardLists, false);
|
||||||
|
|
@ -53,23 +60,39 @@ public class Deck implements Serializable {
|
||||||
public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards) throws GameException {
|
public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards) throws GameException {
|
||||||
Deck deck = new Deck();
|
Deck deck = new Deck();
|
||||||
deck.setName(deckCardLists.getName());
|
deck.setName(deckCardLists.getName());
|
||||||
|
List<String> deckCardNames = new ArrayList<>();
|
||||||
for (DeckCardInfo deckCardInfo: deckCardLists.getCards()) {
|
for (DeckCardInfo deckCardInfo: deckCardLists.getCards()) {
|
||||||
Card card = createCard(deckCardInfo, mockCards);
|
Card card = createCard(deckCardInfo, mockCards);
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
deck.cards.add(card);
|
deck.cards.add(card);
|
||||||
|
deckCardNames.add(card.getName());
|
||||||
} else if (!ignoreErrors) {
|
} else if (!ignoreErrors) {
|
||||||
throw createCardNotFoundGameException(deckCardInfo, deckCardLists.getName());
|
throw createCardNotFoundGameException(deckCardInfo, deckCardLists.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
List<String> sbCardNames = new ArrayList<>();
|
||||||
for (DeckCardInfo deckCardInfo: deckCardLists.getSideboard()) {
|
for (DeckCardInfo deckCardInfo: deckCardLists.getSideboard()) {
|
||||||
Card card = createCard(deckCardInfo, mockCards);
|
Card card = createCard(deckCardInfo, mockCards);
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
deck.sideboard.add(card);
|
deck.sideboard.add(card);
|
||||||
|
sbCardNames.add(card.getName());
|
||||||
} else if (!ignoreErrors) {
|
} else if (!ignoreErrors) {
|
||||||
throw createCardNotFoundGameException(deckCardInfo, deckCardLists.getName());
|
throw createCardNotFoundGameException(deckCardInfo, deckCardLists.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Collections.sort(deckCardNames);
|
||||||
|
Collections.sort(sbCardNames);
|
||||||
|
String deckString = deckCardNames.toString() + sbCardNames.toString();
|
||||||
|
deck.setDeckHashCode(DeckUtil.fixedHash(deckString));
|
||||||
|
// try{
|
||||||
|
// MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
||||||
|
// messageDigest.update(deckString.getBytes());
|
||||||
|
// String encryptedString = new String(messageDigest.digest());
|
||||||
|
// deck.setDeckHashCode(encryptedString.hashCode());
|
||||||
|
// }
|
||||||
|
// catch (NoSuchAlgorithmException e) {
|
||||||
|
// // nothing
|
||||||
|
// }
|
||||||
return deck;
|
return deck;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,4 +160,13 @@ public class Deck implements Serializable {
|
||||||
public Set<Card> getSideboard() {
|
public Set<Card> getSideboard() {
|
||||||
return sideboard;
|
return sideboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getDeckHashCode() {
|
||||||
|
return deckHashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeckHashCode(long deckHashCode) {
|
||||||
|
this.deckHashCode = deckHashCode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -400,6 +400,7 @@ public abstract class MatchImpl implements Match {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
// make sure the deck name (needed for Tiny Leaders) won't get lost by sideboarding
|
// make sure the deck name (needed for Tiny Leaders) won't get lost by sideboarding
|
||||||
deck.setName(player.getDeck().getName());
|
deck.setName(player.getDeck().getName());
|
||||||
|
deck.setDeckHashCode(player.getDeck().getDeckHashCode());
|
||||||
player.submitDeck(deck);
|
player.submitDeck(deck);
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
|
@ -425,6 +426,7 @@ public abstract class MatchImpl implements Match {
|
||||||
sb.append(" QUITTED");
|
sb.append(" QUITTED");
|
||||||
}
|
}
|
||||||
sb.append("<br/>");
|
sb.append("<br/>");
|
||||||
|
sb.append("DeckHash: ").append(mp.getDeck().getDeckHashCode()).append("<br/>");
|
||||||
}
|
}
|
||||||
if (getDraws() > 0) {
|
if (getDraws() > 0) {
|
||||||
sb.append(" Draws: ").append(getDraws()).append("<br/>");
|
sb.append(" Draws: ").append(getDraws()).append("<br/>");
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ public class MatchPlayer {
|
||||||
if (this.deck != null) {
|
if (this.deck != null) {
|
||||||
// preserver deck name, important for Tiny Leaders format
|
// preserver deck name, important for Tiny Leaders format
|
||||||
deck.setName(this.getDeck().getName());
|
deck.setName(this.getDeck().getName());
|
||||||
|
deck.setDeckHashCode(this.getDeck().getDeckHashCode());
|
||||||
}
|
}
|
||||||
this.deck = deck;
|
this.deck = deck;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2783,13 +2783,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
card = game.getCard(card.getId());
|
card = game.getCard(card.getId());
|
||||||
}
|
}
|
||||||
if (!game.isSimulation()) {
|
if (!game.isSimulation()) {
|
||||||
StringBuilder sb = new StringBuilder(this.getName()).append(" puts ").append(withName ? card.getLogName() : "a face down card ");
|
StringBuilder sb = new StringBuilder(this.getName()).append(" puts ").append(withName ? card.getLogName() : "a face down card");
|
||||||
switch(fromZone) {
|
switch(fromZone) {
|
||||||
case EXILED:
|
case EXILED:
|
||||||
sb.append("from exile zone ");
|
sb.append(" from exile zone ");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sb.append(fromZone != null ? new StringBuilder("from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") : "");
|
sb.append(fromZone != null ? new StringBuilder(" from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") : "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sb.append(card.getOwnerId().equals(this.getId()) ? "into his or her hand" : "into its owner's hand");
|
sb.append(card.getOwnerId().equals(this.getId()) ? "into his or her hand" : "into its owner's hand");
|
||||||
|
|
@ -2927,7 +2927,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
card = game.getCard(card.getId());
|
card = game.getCard(card.getId());
|
||||||
}
|
}
|
||||||
game.informPlayers(new StringBuilder(this.getName())
|
game.informPlayers(new StringBuilder(this.getName())
|
||||||
.append(" moves ").append(withName ? card.getLogName() : "a card face down ")
|
.append(" moves ").append(withName ? card.getLogName() : "a card face down").append(" ")
|
||||||
.append(fromZone != null ? new StringBuilder("from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") : "")
|
.append(fromZone != null ? new StringBuilder("from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") : "")
|
||||||
.append("to the exile zone").toString());
|
.append("to the exile zone").toString());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
45
Mage/src/mage/util/DeckUtil.java
Normal file
45
Mage/src/mage/util/DeckUtil.java
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
* permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
* conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* The views and conclusions contained in the software and documentation are those of the
|
||||||
|
* authors and should not be interpreted as representing official policies, either expressed
|
||||||
|
* or implied, of BetaSteward_at_googlemail.com.
|
||||||
|
*/
|
||||||
|
package mage.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author LevelX2
|
||||||
|
*/
|
||||||
|
public class DeckUtil {
|
||||||
|
|
||||||
|
public static long fixedHash(String string) {
|
||||||
|
long h = 1125899906842597L; // prime
|
||||||
|
int len = string.length();
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
h = 31 * h + string.charAt(i);
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue