001 /*
002 Copyright (c) 2005 Garrett Smith
003 The MIT License
004 
005 Permission is hereby granted, free of charge, to any person obtaining a copy 
006 of this software and associated documentation files (the "Software"), to deal
007 in the Software without restriction, including without limitation the rights
008 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
009 copies of the Software, and to permit persons to whom the Software is 
010 furnished to do so, subject to the following conditions:
011 
012 The above copyright notice and this permission notice shall be included in all 
013 copies or substantial portions of the Software.
014 
015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
018 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
021 THE SOFTWARE.
022 */
023 
024 // $Id: CardContainer.java.html,v 1.1 2005/06/14 06:50:55 gsmith Exp $
025 
026 package garrettsmith.playingcards;
027 
028 import java.util.*;
029 
030 /**
031  * Represents a collection of cards, typically comprised of one or more decks.
032  
033  @author Garrett Smith, gsmith at northwestern dot edu
034  @version Blackjack v1.0
035  @since Blackjack v1.0
036  */
037 public class CardContainer {
038 
039     private static final   Random      _RANDOM = new Random();
040 
041   private final           CardList     _cards;
042   private final            CardList       _discardedCards;
043   private         boolean        _autoReset;
044   private         Deck           _deck;
045   private               Double         _resetPercent       = null;
046   private final         int            _originalSize;
047   private               Integer        _resetNumber        = null;
048 
049   /**
050    * Creates a shuffled container with its cards set to 
051    * those of one <code>deck</code>.
052    @param deck
053    */
054     public
055     CardContainerfinal Deck deck ) {
056         thisdeck, );
057     }
058 
059     /**
060      * Creates a shuffled container with cards from <code>numDecks</code> 
061      * number of <code>decks</code>.
062      */
063     public
064     CardContainerfinal Deck deck, final int numDecks ) {
065         thisdeck, numDecks, true );
066     }
067 
068 
069     /**
070      * Creates a container with cards from <code>numDecks</code> 
071      * number of <code>decks</code>; will be shuffled according to the value of
072      <code>shuffle</code>.
073      */
074     public
075     CardContainerfinal Deck deck, final int numDecks, final boolean shuffle ) {
076         _deck = deck;
077         _originalSize = _deck.size() * numDecks;
078         _cards = new CardList_originalSize );        
079         _discardedCards = new CardList_originalSize );
080         for int i = 0; i < numDecks; i++ ) {
081             _cards.addAll_deck.getAllCards() );
082         }
083         if shuffle shuffle();
084     }
085 
086     /**
087      * Creates an unshuffled container with its cards equal to the contents of
088      <code>cards<code>.
089      */
090     public
091   CardContainerfinal CardList cards ) {
092       _originalSize = cards.size();
093       _cards = new CardListcards );
094       _discardedCards = new CardList_originalSize );
095     }
096 
097     /**
098      * Returns whether or not auto reset is on.
099      *
100      @return <code>true</code> if auto reset is on, <code>false</code> if
101      *          not.
102      */
103     public
104     boolean isAutoResetOn() {
105         return _autoReset;
106     }
107 
108     /**
109      * Removes and returns a single card from the container.  
110      * If both auto reset is on and the reset limit is
111      * reached then the container will be reset and shuffled.
112      *
113      @return a card taken from the top of the container.
114      @throws NoMoreCardsException if there are no more cards in the container and
115      *                              auto reset is not on.
116      */
117     public
118     Card getCard() {
119         if isResetShuffleOnAndNeeded() ) {
120             resetShuffle();
121         }
122         if this.isEmpty() ) {
123             throw new NoMoreCardsException"container is out of cards" );
124         }
125         Card card = _cards.removeCard);
126         _discardedCards.addcard );
127         return card;
128     }
129 
130     /**
131      * Returns whether this container has any cards left in it.
132      *
133      @return <code>true</code> if the container has at least one card in it,
134      *         <code>false</code> if not.
135      */
136     public
137     boolean hasMoreCards() {
138         return _cards.size() );
139     }
140 
141     /**
142      * Returns whether this container is empty.
143      *
144      @return <code>true</code> if the container is empty, <code>false</code>
145      *         if not.
146      */
147     public
148     boolean isEmpty() {
149         return !hasMoreCards();
150     }
151 
152     /**
153      * Resets this container with the original number and distribution of cards;
154      * does not re-shuffle (randomly re-order) the container and does not return
155      * the cards to their original order.
156      */
157     public
158     void reset() {
159         _cards.addAll_discardedCards );
160         _discardedCards.clear();
161     }
162 
163     /**
164      * Resets this container with the original number and type distribution of
165      * cards and re-shuffles (randomly re-orders) the cards.
166      */
167     public
168     void resetShuffle() {
169         reset();
170         shuffle();
171     }
172 
173     /**
174      * Returns the original size of this container.
175      */
176     public
177     int originalSize() {
178         return _originalSize;
179     }
180 
181     /**
182      * Returns the number of undealt cards in the container
183      @return the number of undealt cards
184      */
185     public
186     int size() {
187         return _cards.size();
188     }
189 
190     /**
191      <p>
192      * Turns on auto reset, where the container is automatically reset AND
193      * reshuffled (radomly re-ordered) whenever the number of undealt cards
194      * falls below <code>percentage</code> percent the original number.
195      </p><p>
196      * If this method is invoked after {@link #setAutoReset( int )},
197      * auto reset will still be on but will use a percentage threshold rather
198      * than threshold based on the absolute number of cards remaining.
199      </p>
200      *
201      @param percentage the percent undealt cards left that must be reached
202      *                   before the deck is reset and reshuffled.  Must be
203      *                   greater than 0 and less than or equal to 1.0.
204      */
205     public
206     void setAutoResetdouble percentage ) {
207         if percentage <= 0.0 || percentage > 1.0 ) {
208             throw new IllegalArgumentException(
209                                 "percentage must be greater than 0.0 and less "
210                                 "than or equal to 1.0: " + percentage );
211         }
212         _autoReset = true;
213         _resetPercent = new Doublepercentage );
214         _resetNumber = null;
215     }
216 
217     /**
218      <p>
219      * Turns on auto reset, where the container is automatically reset AND
220      * reshuffled (radomly re-ordered) whenever the number of undealt cards
221      * falls below <code>limit</code>.
222      </p><p>
223      * If this method is invoked after {@link #setAutoReset(double)}, auto reset will still be
224      * on but will be triggered by the number of cards limit rather than a
225      * percentage limit.
226      </p>
227      *
228      @param limit the number of undealt cards that the container that must have
229      *              less than before the container is reset and reshuffled.
230      */
231     public
232     void setAutoResetfinal int limit ) {
233         if limit <= || limit > _cards.size() + _discardedCards.size() ) {
234             throw new IllegalArgumentException(
235                 "limit must be greater than zero and less than the original "
236                 "size of the deck: " + limit );
237         }
238         _autoReset = true;
239         _resetNumber = new Integerlimit );
240         _resetPercent = null;
241     }
242 
243     /**
244      * Turns off auto reset.
245      *
246      @see #setAutoReset( int )
247      @see #setAutoReset( double )
248      */
249     public
250     void setAutoResetOff() {
251         _autoReset = false;
252     }
253 
254     /**
255      <p>
256      * Shuffles (randomly orders) the contents of the container.
257      </p><p>
258      * This will not reset the container; only the undealt cards will be shuffled.
259      * Use {@link #resetShuffle()} to both reset and shuffle the container.
260      </p>
261      */
262     public
263     void shuffle() {
264         Collections.shuffle_cards, _RANDOM );
265     }
266 
267     /**
268      * Returns a string representation of this container.
269      *
270      @return a string representation of the container.
271      */
272     public
273     String toString() {
274         StringBuffer ret = new StringBuffer();
275         ret.append"CardContainer=[" );
276         int stopPoint = _cards.size() 1;
277         for int i = 0; i < stopPoint; i++ ) {
278             ret.append_cards.getCard).toString() "," );
279         }
280         ret.append_cards.getCard_cards.size() ).toString() "]" );
281         return ret.toString();
282     }  // end String toString()
283 
284     private
285     boolean isResetShuffleOnAndNeeded() {
286         return _autoReset
287                && isResetShuffleNeededByInt()
288                     || isResetShuffleNeededByDouble() );
289     }
290     
291     private
292     boolean isResetShuffleNeededByInt() {
293         return _resetNumber != null
294                && _cards.size() <= _resetNumber.intValue();
295     }
296     
297     private
298     boolean isResetShuffleNeededByDouble() {
299         return _resetPercent != null
300                && _resetPercent.doubleValue() * _originalSize )
301                      > _cards.size();
302     }
303 
304 }