Introduction
Inspired by this r/dailyprogrammer problem, I've written a Scrabble tile counter. Given a Collection
of Scrabble tiles known to be in play, it can output a Map
of the remaining unused tiles and their counts, which can be converted into a Map
containing counts and a Set
of tiles with that count.
Description
Scrabble is a popular word game where players remove tiles with letters on them from a bag and use them to create words on a board. The total number of tiles as well as the frequency of each letter does not change between games.
For this challenge we will be using the tile set from the English edition, which has 100 tiles total. Here's a reference for the distribution and point value of each tile.
Each tile will be represented by the letter that appears on it, with the exception that blank tiles are represented by underscores _.
Input Description
The tiles already in play are inputted as an uppercase string. For example, if 14 tiles have been removed from the bag and are in play, you would be given an input like this:
AEERTYOXMCNB_S
Output Description
You should output the tiles that are left in the bag. The list should be in descending order of the quantity of each tile left in the bag, skipping over amounts that have no tiles.
In cases where more than one letter has the same quantity remaining, output those letters in alphabetical order, with blank tiles at the end.
10: E 9: I 8: A 7: O 5: N, R, T 4: D, L, U 3: G, S 2: F, H, P, V, W 1: B, C, J, K, M, Q, Y, Z, _ 0: X
Feedback
- I'm a pretty new to Java so general pointers around design and implementation would be much appreciated.
- My biggest design qualm was around the implementation of the
ScrabbleTile
enum.ScrabbleTile
s feel like they should be an enum, with thecharacter
and starting count that the tile is associated with.- The
isScrabbleTile
andvalueOf
methods are fairly inefficient - which makes me wonder if there's a better way to representScrabbleTile
? Note: I know I haven't implemented anything that uses either but imagine the use case where a method takes in aCollection
ofchar
s and returns the tile count. It's something that I might implement in the near future.
- The
Implementation
public static class ScrabbleTileCounter {
public static Map<ScrabbleTile, Integer> calculateTileCount(final Collection<ScrabbleTile> tiles) throws NoRemainingTilesException {
final Map<ScrabbleTile, Integer> tileCount = new HashMap<>();
tileCount.putAll(ScrabbleTile.STARTING_SCRABBLE_TILE_COUNT);
for (ScrabbleTile tile : tiles) {
if (tileCount.get(tile) < 1) {
throw new NoRemainingTilesException();
}
final int nextVal = tileCount.get(tile) - 1;
tileCount.put(tile, nextVal);
}
return tileCount;
}
public static Map<Integer, Set<ScrabbleTile>> calculateOutputTileCount(final Map<ScrabbleTile, Integer> tileCount) {
final Map<Integer, Set<ScrabbleTile>> outputTileCount = new HashMap<>();
for (Map.Entry<ScrabbleTile, Integer> entry : tileCount.entrySet()) {
final Set<ScrabbleTile> scrabbleTiles = outputTileCount.get(entry.getValue());
if (scrabbleTiles == null) {
outputTileCount.put(entry.getValue(), new HashSet<>(Arrays.asList(entry.getKey())));
} else {
scrabbleTiles.add(entry.getKey());
outputTileCount.put(entry.getValue(), scrabbleTiles);
}
}
return outputTileCount;
}
}
public enum ScrabbleTile {
A('A', 9),
B('B', 2),
C('C', 2),
D('D', 4),
E('E', 12),
F('F', 2),
G('G', 3),
H('H', 2),
I('I', 9),
J('J', 1),
K('K', 1),
L('L', 4),
M('M', 2),
N('N', 6),
O('O', 8),
P('P', 2),
Q('Q', 1),
R('R', 6),
S('S', 4),
T('T', 6),
U('U', 4),
V('V', 2),
W('W', 2),
X('X', 1),
Y('Y', 2),
Z('Z', 1),
BLANK('_', 2),
;
public static final Map<ScrabbleTile, Integer> STARTING_SCRABBLE_TILE_COUNT;
static {
final Map<ScrabbleTile, Integer> startingScrabbleTileCount = new HashMap<>();
for (ScrabbleTile tile : ScrabbleTile.values()) {
startingScrabbleTileCount.put(tile, tile.getStartingCount());
}
STARTING_SCRABBLE_TILE_COUNT = Collections.unmodifiableMap(startingScrabbleTileCount);
}
private final char charValue;
private final int startingCount;
ScrabbleTile(final char charValue, final int startingCount) {
this.charValue = charValue;
this.startingCount = startingCount;
}
public char getCharValue() {
return charValue;
}
public int getStartingCount() {
return startingCount;
}
public static boolean isScrabbleTile(final char c) {
for (final ScrabbleTile tile : ScrabbleTile.values()) {
if (tile.getCharValue() == c) {
return true;
}
}
return false;
}
public static ScrabbleTile valueOf(final char c) {
for (final ScrabbleTile tile : ScrabbleTile.values()) {
if (tile.getCharValue() == c) {
return tile;
}
}
throw new IllegalArgumentException(String.format("unable to get Scrabble Tile for character: %s", c));
}
}