001package ball.game.scrabble;
002/*-
003 * ##########################################################################
004 * Game Applications and Utilities
005 * $Id: Bag.java 5285 2020-02-05 04:23:21Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-game/trunk/src/main/java/ball/game/scrabble/Bag.java $
007 * %%
008 * Copyright (C) 2010 - 2020 Allen D. Ball
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * ##########################################################################
022 */
023import java.util.LinkedList;
024import java.util.Locale;
025import java.util.ResourceBundle;
026import java.util.SortedMap;
027import java.util.TreeMap;
028
029import static java.util.Collections.unmodifiableSortedMap;
030import static java.util.Objects.requireNonNull;
031
032/**
033 * Scrabble {@link Bag}.
034 *
035 * {@bean.info}
036 *
037 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
038 * @version $Revision: 5285 $
039 */
040public class Bag extends LinkedList<Tile> implements Cloneable {
041    private static final long serialVersionUID = -1363859449887620815L;
042
043    /** @serial */ private final Locale locale;
044    /** @serial */ private final SortedMap<Character,Integer> frequencies;
045    /** @serial */ private final SortedMap<Character,Integer> points;
046
047    /**
048     * No-argument constructor.
049     */
050    public Bag() { this(Locale.ENGLISH); }
051
052    /**
053     * @param   language        The {@link Locale} language.
054     */
055    public Bag(String language) { this(new Locale(language)); }
056
057    private Bag(Locale locale) {
058        super();
059
060        this.locale = requireNonNull(locale, "locale");
061
062        frequencies = unmodifiableSortedMap(new Frequencies());
063        points = unmodifiableSortedMap(new Points());
064
065        for (Character key : frequencies.keySet()) {
066            for (int i = 0, n = frequencies.get(key); i < n; i += 1) {
067                add(new Tile(key, points.get(key)));
068            }
069        }
070    }
071
072    /**
073     * Method to get the {@link Locale} for this {@link Bag}.
074     *
075     * @return  The {@link Locale} for this {@link Bag}.
076     */
077    public Locale getLocale() { return locale; }
078
079    /**
080     * Method to get the frequency {@link SortedMap} for this {@link Bag}.
081     *
082     * @return  The frequency {@link SortedMap} for this {@link Bag}.
083     */
084    public SortedMap<Character,Integer> frequencies() { return frequencies; }
085
086    /**
087     * Method to get the points {@link SortedMap} for this {@link Bag}.
088     *
089     * @return  The points {@link SortedMap} for this {@link Bag}.
090     */
091    public SortedMap<Character,Integer> points() { return points; }
092
093    /**
094     * Method to draw the next {@link Tile} in the {@link Bag}.
095     *
096     * @return  The next {@link Tile}.
097     *
098     * @see #pollFirst()
099     */
100    public Tile draw() { return pollFirst(); }
101
102    /**
103     * Method to draw a specific {@link Tile} from the {@link Bag}.
104     *
105     * @param   letter          The name of the requested {@link Tile}.
106     *
107     * @return  The requested {@link Tile}; {@code null} if no matching
108     *          {@link Tile} is available.
109     *
110     * @see #remove(int)
111     */
112    public Tile draw(char letter) {
113        int index = -1;
114
115        for (int i = 0, n = size(); i < n; i += 1) {
116            if (get(i).getLetter() == letter) {
117                index = i;
118                break;
119            }
120        }
121
122        return (0 <= index && index < size()) ? remove(index) : null;
123    }
124
125    @Override
126    public Tile[] toArray() { return toArray(new Tile[] { }); }
127
128    @Override
129    public Bag clone() { return (Bag) super.clone(); }
130
131    private abstract class MapImpl extends TreeMap<Character,Integer> {
132        private static final long serialVersionUID = -9083183924608405152L;
133
134        protected MapImpl() { super(); }
135
136        protected void load(String name) {
137            ResourceBundle bundle =
138                ResourceBundle.getBundle(name, getLocale());
139
140            for (String key : bundle.keySet()) {
141                put(key.charAt(0), Integer.valueOf(bundle.getString(key)));
142            }
143        }
144    }
145
146    private class Frequencies extends MapImpl implements Cloneable {
147        private static final long serialVersionUID = -5133821366028022851L;
148
149        public Frequencies() {
150            super();
151
152            load(getClass().getName());
153        }
154
155        @Override
156        public Frequencies clone() { return (Frequencies) super.clone(); }
157    }
158
159    private class Points extends MapImpl implements Cloneable {
160        private static final long serialVersionUID = 6515810713447725344L;
161
162        public Points() {
163            super();
164
165            load(getClass().getName());
166        }
167
168        @Override
169        public Points clone() { return (Points) super.clone(); }
170    }
171}