/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package uk.co.iscoding.freecell.game;
import com.google.common.base.Objects;
import uk.co.iscoding.freecell.cards.PlayingCard.Suit;
import uk.co.iscoding.freecell.cards.PlayingCard.Rank;
import uk.co.iscoding.freecell.cards.PlayingCard;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
*
* @author Stuart David James McHattie
* @version 1.0 2011-06-30
* @since 2011-06-30
*/
public class CardCollection {
protected enum RankOrder {
ANY(0),
ASCENDING(1),
DESCENDING(-1);
public final int factor;
private RankOrder(int factor) {
this.factor = factor;
}
}
protected enum Cycle {
CAN,
CANNOT,
}
protected enum AllowedSuit {
ANY,
SAME_SUIT,
SAME_COLOUR,
ALTERNATING_SUIT,
ALTERNATING_COLOUR,
}
protected enum AllowedColour {
IDENTICAL,
ALTERNATING,
}
protected enum StartingRank {
ANY,
ACE(Rank.ACE),
KING(Rank.KING);
public final Rank rank;
private StartingRank() {
this.rank = null;
}
private StartingRank(Rank rank) {
this.rank = rank;
}
}
protected enum StartingSuit {
ANY,
SPADE(Suit.SPADES),
HEART(Suit.HEARTS),
CLUB(Suit.CLUBS),
DIAMOND(Suit.DIAMONDS);
public final Suit suit;
private StartingSuit() {
this.suit = null;
}
private StartingSuit(Suit suit) {
this.suit = suit;
}
}
protected enum SortMode {
ASCENDING,
DESCENDING,
}
public static void moveCards(CardCollection from, CardCollection to, int numCards) {
to.addCollection(from.getTopCards(numCards));
from.removeTopCards(numCards);
}
private final List<PlayingCard> cards;
private final int maxCards;
private String name = "";
RankOrder rankMode = RankOrder.ANY;
Cycle cycleMode = Cycle.CANNOT;
AllowedSuit suitMode = AllowedSuit.ANY;
StartingRank startingRank = StartingRank.ANY;
StartingSuit startingSuit = StartingSuit.ANY;
CardCollection(int maxCards) {
cards = new ArrayList<PlayingCard>(maxCards);
this.maxCards = maxCards;
}
public void setName(String name) {
this.name = name;
}
boolean addCard(PlayingCard card, boolean ignoreRules) {
if (cards.size() == maxCards) {
return false;
}
if (ignoreRules || canAddCard(card)) {
cards.add(card);
return true;
} else {
return false;
}
}
public boolean addCard(PlayingCard card) {
return addCard(card, false);
}
public boolean addCollection(CardCollection newCards) {
if (cards.size() + newCards.getNumberOfCards() > maxCards) {
return false;
} else if (canAddCard(newCards.getBottomCard())) {
while (newCards.getNumberOfCards() > 0) {
addCard(newCards.getBottomCard(), true);
newCards.removeCardAt(0);
}
} else {
return false;
}
return true;
}
boolean canAddCard(PlayingCard card) {
if (cards.size() == maxCards) {
// Collection is full
return false;
}
if (cards.size() == 0) {
if (startingRank != StartingRank.ANY && card.getRank() != startingRank.rank) return false;
if (startingSuit != StartingSuit.ANY && card.getSuit() != startingSuit.suit) return false;
} else {
PlayingCard topCard = getTopCard();
int rankDifference = card.rankDifference(topCard);
if (cycleMode == Cycle.CAN && Math.abs(rankDifference) == 12) rankDifference += 13 * rankMode.factor;
if (rankMode != RankOrder.ANY && rankDifference != rankMode.factor) return false;
if (suitMode == AllowedSuit.ALTERNATING_COLOUR && card.isSameColourAs(topCard)) return false;
if (suitMode == AllowedSuit.SAME_COLOUR && !card.isSameColourAs(topCard)) return false;
if (suitMode == AllowedSuit.ALTERNATING_SUIT && card.isSameSuitAs(topCard)) return false;
if (suitMode == AllowedSuit.SAME_SUIT && !card.isSameSuitAs(topCard)) return false;
}
return true;
}
public void removeTopCard() {
removeTopCards(1);
}
void removeTopCards(int nCards) {
if (nCards > cards.size()) nCards = cards.size();
for (int i = 0; i < nCards; i++) {
cards.remove(cards.size() - 1);
}
}
void removeCardAt(int index) {
if (index >= 0 && index < cards.size()) {
cards.remove(index);
}
}
public void removeCard(PlayingCard card) {
cards.remove(card);
}
public int getIndexOf(PlayingCard card) {
return cards.indexOf(card);
}
PlayingCard getBottomCard() {
if (cards.size() == 0) {
throw new IllegalStateException("The CardCollection is empty");
}
return cards.get(0);
}
PlayingCard getTopCard() {
if (cards.size() == 0) {
throw new IllegalStateException("The CardCollection is empty");
}
return cards.get(cards.size() - 1);
}
public CardCollection getTopCards(int nCards) {
if (cards.size() == 0) {
throw new IllegalStateException("The CardCollection is empty");
}
if (nCards > cards.size()) return this;
CardCollection topCards = new CardCollection(nCards);
for (int i = cards.size() - nCards; i < cards.size(); i++) {
topCards.addCard(cards.get(i), true);
}
return topCards;
}
public CardCollection getLongestSequenceNoLongerThan(int maxCards) {
for (int numCards = Math.min(maxCards, getNumberOfCards()); numCards > 0 ; numCards--) {
CardCollection candidates = getTopCards(numCards);
if (candidates.isDescendingRank() && candidates.isAlternatingColour()) {
return candidates;
}
}
return null; // This only happens if there are no cards in the collection
}
public PlayingCard getCard(int index) {
if (index < 0 || index >= cards.size()) {
throw new IndexOutOfBoundsException(
String.format("Requested index %d was not within CardCollection of size %d", index, cards.size()));
}
return cards.get(index);
}
public void sortCards(SortMode mode) {
switch (mode) {
case ASCENDING:
Collections.sort(cards, new Comparator<PlayingCard>() {
@Override
public int compare(PlayingCard o1, PlayingCard o2) {
return o1.getCardNumber() - o2.getCardNumber();
}
});
break;
case DESCENDING:
Collections.sort(cards, new Comparator<PlayingCard>() {
@Override
public int compare(PlayingCard o1, PlayingCard o2) {
return o2.getCardNumber() - o1.getCardNumber();
}
});
break;
}
}
public boolean isAlternatingColour() {
for (int i = 1; i < cards.size(); i++) {
if (cards.get(i).isSameColourAs(cards.get(i - 1))) return false;
}
return true;
}
public boolean isDescendingRank() {
for (int i = 1; i < cards.size(); i++) {
if (cards.get(i).rankDifference(cards.get(i-1)) != -1) return false;
}
return true;
}
public int getNumberOfCards() {
return cards.size();
}
protected void populateDeepClone(CardCollection clone) {
for (PlayingCard card: cards) {
clone.addCard(card.deepClone(), true);
}
clone.name = name;
clone.rankMode = rankMode;
clone.cycleMode = cycleMode;
clone.suitMode = suitMode;
clone.startingRank = startingRank;
clone.startingSuit = startingSuit;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof CardCollection)) return false;
CardCollection otherCollection = (CardCollection)other;
if (maxCards != otherCollection.maxCards) return false;
if (rankMode != otherCollection.rankMode) return false;
if (cycleMode != otherCollection.cycleMode) return false;
if (suitMode != otherCollection.suitMode) return false;
if (startingRank != otherCollection.startingRank) return false;
if (startingSuit != otherCollection.startingSuit) return false;
if (cards.size() != otherCollection.cards.size()) return false;
for (int i = 0; i < cards.size(); i++) {
if (!cards.get(i).equals(otherCollection.cards.get(i))) return false;
}
return true;
}
@Override
public int hashCode() {
return Objects.hashCode(cards, maxCards);
}
@Override
public String toString() {
if (name.length() > 0) {
return name;
} else {
return super.toString();
}
}
}