/******************************************************************************* * Solitaire * * Copyright (C) 2016 by Martin P. Robillard * * See: https://github.com/prmr/Solitaire * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package ca.mcgill.cs.stg.solitaire.cards; /** * An immutable description of a playing card. This abstraction * is designed to be independent of game logic, so it does * not provide any service that relies on the knowledge * of the rules of any particular game. * * This class implements the Flyweight design pattern: * there can only ever be one instance of a card that * represents a specific real-world playing card (such as ace * of spaces). In the absence of serialization and reflection, * this ensures that the behavior of the == operator is identical * to that of the equals method when two card arguments are * provided. */ public final class Card { // Indexed by suit, then rank private static final Card[][] CARDS = new Card[Suit.values().length][]; // Create the flyweight objects static { for( Suit suit : Suit.values() ) { CARDS[suit.ordinal()] = new Card[Rank.values().length]; for( Rank rank : Rank.values() ) { CARDS[suit.ordinal()][rank.ordinal()] = new Card(rank, suit); } } } /** * Represents the rank of the card. */ public enum Rank { ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING } /** * Represents the suit of the card. */ public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES } private final Rank aRank; private final Suit aSuit; private Card(Rank pRank, Suit pSuit ) { aRank = pRank; aSuit = pSuit; } /** * @param pRank The rank of the card (from ace to kind) * @param pSuit The suit of the card (clubs, diamond, spades, hearts) * @return The card object representing the card with pRank and pSuit */ public static Card get(Rank pRank, Suit pSuit) { assert pRank != null && pSuit != null; return CARDS[pSuit.ordinal()][pRank.ordinal()]; } /** * @param pId The id string for the card. This is needs to have * been produced by Card.getIDString to be considered a * valid input to this method. * @return The card object with id string pId */ public static Card get( String pId ) { assert pId != null; int id = Integer.parseInt(pId); return get(Rank.values()[id % Rank.values().length], Suit.values()[id / Rank.values().length]); } /** * Obtain the rank of the card. * @return An object representing the rank of the card. */ public Rank getRank() { return aRank; } /** * @param pCard The card to compare against * @return True if and only if pCard's suit is of the same color as * this card. */ public boolean sameColorAs(Card pCard) { assert pCard != null; if( getSuit() == Suit.DIAMONDS || getSuit() == Suit.HEARTS ) { return pCard.getSuit() == Suit.DIAMONDS || pCard.getSuit() == Suit.HEARTS; } else { return pCard.getSuit() == Suit.CLUBS || pCard.getSuit() == Suit.SPADES; } } /** * @return A string uniquely representing this card. The string * format is not specified except that it is fully compatible * with the format expected by Card.get(String). */ public String getIDString() { return Integer.toString(getSuit().ordinal() * Rank.values().length + getRank().ordinal()); } /** * Obtain the suit of the card. * @return An object representing the suit of the card */ public Suit getSuit() { return aSuit; } /** * @see java.lang.Object#toString() */ @Override public String toString() { return aRank + " of " + aSuit; } }