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