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}