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 CardContainer( final Deck deck ) {
056 this( deck, 1 );
057 }
058
059 /**
060 * Creates a shuffled container with cards from <code>numDecks</code>
061 * number of <code>decks</code>.
062 */
063 public
064 CardContainer( final Deck deck, final int numDecks ) {
065 this( deck, 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 CardContainer( final 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 CardContainer( final CardList cards ) {
092 _originalSize = cards.size();
093 _cards = new CardList( cards );
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( 0 );
126 _discardedCards.add( card );
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() > 0 );
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 setAutoReset( double 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 Double( percentage );
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 setAutoReset( final int limit ) {
233 if ( limit <= 0 || 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 Integer( limit );
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( i ).toString() + "," );
279 }
280 ret.append( _cards.getCard( _cards.size() - 1 ).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 }
|