ftp.nice.ch/Attic/openStep/games/Solitaire.3.1.s.tgz#/Solitaire.3.1/Frameworks/Solitaire/CardSet.subproj/CardPile.m

This is CardPile.m in view mode; [Download] [Up]

/* indent:4  tabsize:8  font:fixed-width */

#import "CardPile.h"
#import <Solitaire/CardSet.h>

#if defined (WIN32)
#define random() rand()
#define srandom(x) srand(x)
#else
#import <libc.h>
#endif

@implementation CardPile

/*"
	Instances of this class provide low-level functionality for card games.  
	CardPiles contain an ordered list of Card objects which can be added to, 
	removed from, or shuffled by clients of the class.
"*/


+ (void) initialize
/*"
	Sets a random seed based on the time before initializing the class.
"*/
{
	if (self == [CardPile class]) {
	    srandom(time(0));
	}
}


- init
/*"
	Calls our designated initializer #initForCardSize: with CS_SMALL as
	the argument.
"*/
{
    [self initForCardSize:CS_SMALL];
    return self;
}


- initForCardSize:(CardSize)aSize
/*"
	Our designated initializer. aSize can be one of CS_SMALL or CS_LARGE,
	for small or large cards. The new pile will contain no cards.
"*/
{
    [super init];
    cardSize = aSize;
    cards = [[NSMutableArray allocWithZone:[self zone]] init];
    return self;
}


- (void) setCardSize:(CardSize)aSize
/*"
	Sets our card size. It should be one of CS_SMALL or CS_LARGE. If
	the current cards are of a different size than aSize they'll all
	be resized and recached.
"*/
{
    if (aSize != cardSize) {
        // have to change all the cards to the right class
	
        Card* newCard;
        Card* oldCard;
        int i;
        int cardCount = [cards count];
	
        for (i = 0; i < cardCount; i++) {
            oldCard = [cards objectAtIndex:i];
            if (aSize == CS_SMALL) {
                newCard = [[SmallCard allocWithZone:[self zone]] initSuit:[oldCard suit] value:[oldCard value] faceUp:[oldCard isFaceUp]];	
            }
            else {
                newCard = [[Card allocWithZone:[self zone]] initSuit:[oldCard suit] value:[oldCard value] faceUp:[oldCard isFaceUp]];
            }

            // They will be inserted into the card list which
            // will retain them.
            [cards removeObjectAtIndex:i];
            [cards insertObject:newCard atIndex:i];
            [newCard release];
        }
        cardSize = aSize;
    } 
}


- (CardSize) cardSize
/*"
	Returns the current size of our cards.
"*/
{
    return cardSize;
}


- (void) dealloc
/*"
	Releases our resources (our cards).
"*/
{
    [self empty];
    [cards release];
    [super dealloc]; 
}


- (void) addCard:(Card*)aCard
/*"
	Adds aCard to the top of our deck.
"*/
{
    return [self insertCard:aCard at:CS_TOP];
}


- (void) insertCard:(Card*)aCard at:(int)aPosition
/*"
	Adds a card to the pile at the specified position. The constants CS_TOP
	and CS_BOTTOM can be used to specify the top and bottom of the pile.
"*/
{
    if (aCard != nil) {
        if (aPosition == CS_TOP)
        {
            [cards addObject:aCard];
        }
        else
        {
            [cards insertObject:aCard atIndex:aPosition];
        }
    }
}


- (void) addPile:(CardPile*)aCardPile
/*"
	Adds aCardPile to the top of our cards we already have.
"*/
{
    return [self insertPile:aCardPile at:CS_TOP];
}


- (void) addCopyOfPile:(CardPile*)aCardPile
/*"
   Acts as a cover for #insertCopyOfCardPile:at:, but always adds cards to
   the top of a pile.
"*/
{
    return [self insertCopyOfPile:aCardPile at:CS_TOP];
}


- (void) insertPile:(CardPile*)aCardPile at:(int)aPosition
/*"
   Adds another pile of cards to this pile at a specific position.  The
   constants CS_TOP and CS_BOTTOM can be used to specify the top and bottom
   of the pile, respectively.
"*/
{
    int        cardIndex;

    if (aPosition == CS_TOP)
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cards addObject:[aCardPile cardAtIndex:cardIndex]];
        }
    }
    else
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cards insertObject:[aCardPile cardAtIndex:cardIndex] atIndex:aPosition++];
        }
    } 
}


- (void) insertCopyOfPile:(CardPile*)aCardPile at:(int)aPosition
/*"
   Adds another pile of cards to this pile at a specific position.  The
   constants CS_TOP and CS_BOTTOM can be used to specify the top and bottom
   of the pile, respectively.  The cards added are copies of (and
   independent from) the source cards.
"*/
{
    int        cardIndex;

    if (aPosition == CS_TOP)
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cards addObject:[[[aCardPile cardAtIndex:cardIndex] copy] autorelease]];
        }
    }
    else
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cards insertObject:[[[aCardPile cardAtIndex:cardIndex] copy] autorelease] atIndex:aPosition++];
        }
    } 
}


- (void)removeCard:(Card*)aCard
/*"
   Remove a specific card from the pile.  The card's retain count will
   drop by one.
"*/
{
    if (aCard != nil) {
        [cards removeObject:aCard];
    }
}


- (void) addDeck
/*"
   Adds a full deck of cards to the top of the pile, neatly ordered.
"*/
{
    int suitCounter, valueCounter;

    for (valueCounter = CS_LOWVALUE; valueCounter <= CS_HIGHVALUE; valueCounter++)
    {
        for (suitCounter = CS_LOWSUIT; suitCounter <= CS_HIGHSUIT; suitCounter++)
        {
            if (cardSize == CS_SMALL)
            {
                [self insertCard:[[[SmallCard allocWithZone:[self zone]] initSuit:suitCounter value:valueCounter] autorelease] at:CS_TOP];
            }
            else if (cardSize == CS_LARGE)
            {
                [self insertCard:[[[Card allocWithZone:[self zone]] initSuit:suitCounter value:valueCounter] autorelease] at:CS_TOP];
            }
        }
    } 
}


- (void) empty
/*"
   Removes all our cards.
"*/
{
    [cards removeAllObjects]; 
}


- (void) flip
/*"
   Turns the pile over, reversing the order of the cards and the
   orientation of each card.
"*/
{
    int 	cardIndex;
    Card* 	tmpCard;

    for (cardIndex = [cards count]- 1; cardIndex >= 0; cardIndex--)
    {
        tmpCard = [[cards objectAtIndex:cardIndex] retain];
        [tmpCard flip];
        [cards removeObjectAtIndex:cardIndex];
        [cards addObject:tmpCard];
        [tmpCard release];
    } 
}


- (void) shuffle
/*"
   Randomly re-order all card objects contained in the pile.
"*/
{
    Card*	temp;
    int		counter;
    int     swapIndex;
    int     cardCount = [cards count];

    for (counter = 0; counter < cardCount - 1; counter++)
    {
        // changed by erikk for more true randomness to the shuffle...
		swapIndex = random() % (cardCount);
		if (swapIndex == counter)
			continue;

        temp = [cards objectAtIndex:counter];
        [cards replaceObjectAtIndex:counter withObject:[cards objectAtIndex:swapIndex]];
        [cards replaceObjectAtIndex:swapIndex withObject:temp];
    }
}


- (Card*) cardAtIndex:(int)aPosition
/*"
   Allow a client to find out what card is at a given position in the pile.
   The constants CS_TOP and CS_BOTTOM can be used to specify the top and
   bottom of the pile, respectively. If aPosition is less than zero or
   larger the number of cards we have, nil is returned.
"*/
{
    if (aPosition == CS_TOP)
    {
        aPosition = [cards count]- 1;
        if (aPosition == -1 || aPosition >= [cards count])
        {
            return nil;
        }
    }
    return [cards objectAtIndex:aPosition];
}


- (Card*) topCard
/*"
   Returns our top card or nil if we don't have any cards.
"*/
{
	return [self cardAtIndex:CS_TOP];
}


- (Card*) dealTopCard
/*"
   Removes our top card from the deck and returns it. If we don't have any
   cards then nil is returned.
"*/
{
    Card* topCard = [self cardAtIndex:CS_TOP];
    [topCard retain];
    if (topCard != nil) {
        [self removeCard:topCard];
    }
    return [topCard autorelease];
}


- (Card*) bottomCard
/*"
   Returns our bottom card or nil if we don't have any cards.
"*/
{
	return [self cardAtIndex:CS_BOTTOM];
}


- (int) indexOfCard:(Card*)aCard
/*"
   Returns the index of aCard if it is in our pile. If not then NSNotFound is
   returned.
"*/
{
    return (int)[cards indexOfObject:aCard];
}


- (int) cardCount
/*"
   Returns the number of cards in our pile.
"*/
{
    return [cards count];
}


- (id) initWithCoder:(NSCoder *)aDecoder
/*"
   Unarchives a CardPile instance.
"*/
{
    cards = [[aDecoder decodeObject] retain];
    [aDecoder decodeValuesOfObjCTypes:"i", &cardSize];
    return self;
}


- (void) encodeWithCoder:(NSCoder *)aCoder
/*"
   Archives a CardPile instance.
"*/
{
    [aCoder encodeObject:cards];
    [aCoder encodeValuesOfObjCTypes:"i", &cardSize];
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.