package javass.jass; import javass.jass.Card.Rank; import static javass.bits.Bits64.mask; import java.util.StringJoiner; /** * The packed representation of a card set * @author Charles BEAUVILLE * @author Celia HOUSSIAUX * */ public final class PackedCardSet { private PackedCardSet() {} private static final int COLOR_SIZE = 16; private static final int SPADES_INDEX = 0; private static final int HEARTS_INDEX = 16; private static final int DIAMONDS_INDEX = 32; private static final int CLUBS_INDEX = 48; /** * The packed version of an empty {@link CardSet}. */ public static final long EMPTY = 0L; /** * The packed version off a {@link CardSet} containing all {@link Card}s of jass. */ public static final long ALL_CARDS = mask(CLUBS_INDEX, Rank.COUNT) | mask(DIAMONDS_INDEX, Rank.COUNT) | mask(HEARTS_INDEX, Rank.COUNT) | mask(SPADES_INDEX, Rank.COUNT); private static final long[] trumpTab = {0b111111110L, 0b111111100L, 0b111111000L, 0b000100000L, 0b111101000L, 0b000000000L, 0b110101000L, 0b100101000L, 0b000101000L}; private static final long[] colorTab = {mask(SPADES_INDEX, Rank.COUNT), mask(HEARTS_INDEX, Rank.COUNT), mask(DIAMONDS_INDEX, Rank.COUNT), mask(CLUBS_INDEX, Rank.COUNT)}; /** * Returns true if and only if the given {@link PackedCard}set is valid, none of the unused bits are equal to 1 * @param {@link pkCardSet} the packed version of a {@link CardSet} to be checked * @return a boolean whether or not the given {@link pkCardSet} is valid */ public static boolean isValid(long pkCardSet) { return (pkCardSet | ALL_CARDS) == ALL_CARDS && pkCardSet >= 0 && pkCardSet <= ALL_CARDS; } /** * Returns the packed version of a {@link CardSet} containing all {@link Card}s of {@link Color} trump above the given {@link packedCard} * @param pkCard the {@link packedCard} version of a {@link Card} you want to find which trumps are above * @return a long which represents the packed version of the {@link CardSet} of trump cards above the given card */ public static long trumpAbove(int pkCard) { assert PackedCard.isValid(pkCard): "Invalid card set in trumpAbove function of pkCardSet"; return trumpTab[PackedCard.rank(pkCard).ordinal()] << PackedCard.color(pkCard).ordinal() * COLOR_SIZE; } /** * Returns the packed version of the {@link CardSet} containing only the given {@link packedCard} * @param pkCard the packed version of the {@link Card} from which the {@link CardSet} is created * @return a long the packed version of a {@link CardSet} containing the given {@link Card} */ public static long singleton(int pkCard) { assert PackedCard.isValid(pkCard): "Invalid card set in singleton function of pkCardSet"; return 1L << pkCard; } /** * Checks if the given {@link pkCardSet} is empty or not (ie contains no {@link Card}) * @param pkCardSet a long representing the {@link CardSet} to check * @return a boolean true only if the {@link CardSet} is empty(ie all his bits are null) */ public static boolean isEmpty(long pkCardSet) { assert isValid(pkCardSet): "Invalid card set in isEmpty function of pkCardSet"; return pkCardSet == EMPTY; } /** * Computes the size of the given {@link pkCardSet} (ie the number of {@link Card}s it contains) * @param pkCardSet a long representing the packed version of a {@link CardSet} * @return an int the size of the {@link CardSet} (ie the number of {@link Card}s it contains) */ public static int size(long pkCardSet) { assert isValid(pkCardSet): "Invalid card set in size function of pkCardSet"; return Long.bitCount(pkCardSet); } /** * Gives the packed version of the {@link Card} of the given index in the given {@link CardSet} * @param pkCardSet a long representing the {@link pkCardSet} to get the {@link Card} from * @param index an int, the index of the {@link Card} to extract from the {@link CardSet} * @return an int representing the packed version of a {@link Card} */ public static int get(long pkCardSet, int index) { assert isValid(pkCardSet): "Invalid card set in get function of pkCardSet"; assert (index < size(pkCardSet) && index >= 0): "Invalid index in get function of pkCardSet"; for (int i = 1; i <= index; i++) pkCardSet = pkCardSet ^ Long.lowestOneBit(pkCardSet); return Long.numberOfTrailingZeros(pkCardSet); } /** * Adds a {@link packedCard} to the packed version of a {@link CardSet} * @param pkCardSet a long representing the packed version of a {@link CardSet} * @param pkCard an int representing the packed version of a {@link Card} * @return a long the {@link Card} set after the given {@link Card} has been added */ public static long add(long pkCardSet, int pkCard) { assert isValid(pkCardSet): "Invalid card set in add function of pkCardSet"; assert PackedCard.isValid(pkCard): "Invalid card in add function of pkCardSet"; return pkCardSet | singleton(pkCard) ; } /** * Deletes a given {@link packedCard} from a {@link pkCardSet} * @param pkCardSet a long representing the packed version of a {@link CardSet} * @param pkCard an int representing a {@link packedCard} * @return a long the {@link CardSet} after the given {@link Card} has been removed */ public static long remove(long pkCardSet, int pkCard) { assert isValid(pkCardSet): "Invalid card set in remove function of pkCardSet"; assert PackedCard.isValid(pkCard): "Invalid card in remove function of pkCardSet"; return pkCardSet & ~singleton(pkCard); } /** * Checks if the given {@link packedCard} is in the given {@link pkCardSet} * @param pkCardSet a long representing a {@link pkCardSet} * @param pkCard an int representing a {@link packedCard} * @return a boolean true if the given card is in the given {@link CardSet} */ public static boolean contains(long pkCardSet, int pkCard) { assert isValid(pkCardSet): "Invalid card set in contains function of pkCardSet"; assert PackedCard.isValid(pkCard): "Invalid card in contains function of pkCardSet"; return (pkCardSet & singleton(pkCard)) != EMPTY; } /** * Computes the inverse of the given p{@link pkCardSet} meaning all the {@link Card}s that are not in the given {@link CardSet} * @param pkCardSet a long representing a {@link pkCardSet} * @return a long representing the packed version of the {@link CardSet} containing all the {@link Card}s that were not in the given {@link CardSet} */ public static long complement(long pkCardSet) { assert isValid(pkCardSet): "Invalid card set in complement function of pkCardSet"; return pkCardSet ^ ALL_CARDS ; } /** * Computes the union of the given {@link CardSet}s meaning the {@link CardSet} which contains all the {@link Card}s from both {@link CardSet}s * @param pkCardSet1 a long representing the packed version of a {@link CardSet} * @param pkCardSet2 a long representing the packed version of a {@link CardSet} * @return a long which represents a set containing all the {@link Card}s for both {@link CardSet}s */ public static long union(long pkCardSet1, long pkCardSet2) { assert isValid(pkCardSet1): "Invalid card set 1 in union function of pkCardSet"; assert isValid(pkCardSet2): "Invalid card set 2 in union function of pkCardSet"; return pkCardSet1 | pkCardSet2; } /** * Computes the intersection of the given {@link CardSet}s meaning the {@link CardSet} which contains the {@link Card}s that are in both {@link CardSet}s * @param pkCardSet1 a long representing the packed version of a {@link CardSet} * @param pkCardSet2 a long representing the packed version of a {@link CardSet} * @return a long which represents a {@link CardSet} containing the {@link Card}s that are in both {@link CardSet}s */ public static long intersection(long pkCardSet1, long pkCardSet2) { assert isValid(pkCardSet1): "Invalid card set 1 in intersection function of pkCardSet"; assert isValid(pkCardSet2): "Invalid card set 2 in intersection function of pkCardSet"; return pkCardSet1 & pkCardSet2; } /** * Computes the difference between the two given {@link CardSet}s meaning the {@link CardSet} which contains the {@link Card}s that are in the first but not in the second one * @param pkCardSet1 a long representing the packed version of a {@link CardSet} * @param pkCardSet2 a long representing the packed version of a {@link CardSet} * @return a long which represents a {@link CardSet} containing the {@link Card}s that are in the first but not in the second one */ public static long difference(long pkCardSet1, long pkCardSet2) { assert isValid(pkCardSet1): "Invalid card set 1 in difference function of pkCardSet"; assert isValid(pkCardSet2): "Invalid card set 2 in difference function of pkCardSet"; return pkCardSet1 & complement(pkCardSet2); } /** * Computes the packed version of a {@link CardSet} containing only the {@link Card}s of the given {@link Color}. * @param pkCardSet a long representing the packed version of a {@link CardSet} * @param color the {@link Color} of the wanted subset of the {@link CardSet}. * @return a long the packed version of a {@link CardSet} containing only the {@link Card}s of the given {@link Color}. */ public static long subsetOfColor(long pkCardSet, Card.Color color) { assert isValid(pkCardSet): "Invalid card set in subsetOfColor function of pkCardSet"; return pkCardSet & colorTab[color.ordinal()] ; } /** * Gives a textual representation of a set of cards * @param pkCardSet a long representing the packed version of a set of cards * @return a string describing the given packed set of cards */ public static String toString(long pkCardSet) { assert isValid(pkCardSet): "Invalid card set in toString function of pkCardSet"; StringJoiner sJ = new StringJoiner(",", "{", "}"); for(int i = 0; i < size(pkCardSet); ++i) sJ.add(PackedCard.toString(get(pkCardSet, i))); return sJ.toString(); } }