* 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:
LevelX2 2015-04-11 00:47:54 +02:00
parent 3bdf2a7957
commit 0fb7cf8317
7 changed files with 95 additions and 15 deletions

View file

@ -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;
/** /**

View file

@ -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);

View file

@ -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;
}
} }

View file

@ -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/>");

View file

@ -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;
} }

View file

@ -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());
} }

View 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;
}
}