Visit the scrabble-score exercise on Exercism to read the full instructions and download the exercise files.
Dig Deeper
if statements
if statements
class Scrabble {
private final int score;
Scrabble(String word) {
word = word.toUpperCase();
int score = 0;
for (int i = 0; i < word.length(); i++) {
var ltr = word.substring(i, i + 1);
if ("AEIOULNRST".contains(ltr))
score += 1;
else if ("DG".contains(ltr))
score += 2;
else if ("BCMP".contains(ltr))
score += 3;
else if ("FHVWY".contains(ltr))
score += 4;
else if ("K".contains(ltr))
score += 5;
else if ("JX".contains(ltr))
score += 8;
else if ("QZ".contains(ltr))
score += 10;
}
this.score = score;
}
int getScore() {
return score;
}
}
This approach defines a private final variable to be returned by the getScore() method.
It is private because it does not need to be directly accessed from outside the class, and it is final because its value does not need to be changed once it is set.
In the constructor, a local variable is defined for being updated in the for loop.
Using the same for a variable in a nested local scope that is used in its enclosing higher scope is called [variable shadowing](https://www.geeksforgeeks.org/shadowing-in-java/).
The variable is updated by a series of if statements that checks each letter of the uppercased word.
The letter is selected as a String by the substring() method and is passed to the contains() method for the String representing the letters for a particular score.
The first if statement checks for the most common letters, and each succeeding if statement checks for letters less common than the statement before it.
When the loop is done, the class-level score variable is set to the value of the local score variable.
switch statement
switch statement
class Scrabble {
private final int score;
Scrabble(String word) {
word = word.toLowerCase();
int score = 0;
for (int i = 0; i < word.length(); i++) {
switch (word.charAt(i)) {
case 'a', 'e', 'i', 'o', 'u', 'l', 'n', 'r', 's', 't':
score += 1;
break;
case 'd', 'g':
score += 2;
break;
case 'b', 'c', 'm', 'p':
score += 3;
break;
case 'f', 'h', 'v', 'w', 'y':
score += 4;
break;
case 'k':
score += 5;
break;
case 'j', 'x':
score += 8;
break;
case 'q', 'z':
score += 10;
break;
default:
break;
}
}
this.score = score;
}
int getScore() {
return score;
}
}
This approach defines a private final variable to be returned by the getScore() method.
It is private because it does not need to be directly accessed from outside the class, and it is final because its value does not need to be changed once it is set.
In the constructor, a local variable is defined for being updated in the for loop.
Using the same name for a variable in a nested local scope that is used in its enclosing higher scope is called [variable shadowing](https://www.geeksforgeeks.org/shadowing-in-java/).
The variable is updated by a switch statement that checks each letter of the lowercased word.
If most of the input will already be lower case, it is a bit more performant to normalize the input as lowercased, since fewer characters will need to be changed.
However, it may be considered that to use upper case letters is more readable.
The letter is selected as a char by the charAt() method and is passed to the switch, with each case representing the letters for a particular score.
When the loop is done, the class-level score variable is set to the value of the local score variable.
HashMap with reduce
HashMap with reduce()
import java.util.HashMap;
import java.util.stream.IntStream;
class Scrabble {
private final int savedScore;
private final static HashMap < Integer, Integer > lookup = new HashMap(26);
private final static String[] letters = {
"AEIOULNRST",
"DG",
"BCMP",
"FHVWY",
"K",
"JX",
"QZ"
};
private final static int[] scores = {
1,
2,
3,
4,
5,
8,
10
};
static {
IntStream.range(0, letters.length)
.forEach(index -> letters[index].chars().forEach(letter -> lookup.put(letter, scores[index])));
}
Scrabble(String word) {
savedScore = word.toUpperCase().chars().reduce(0, (score, letter) -> score + lookup.get(letter));
}
int getScore() {
return savedScore;
}
}
This approach starts by importing from packages for what is needed.
A private final variable is defined to be returned by the getScore() method.
It is private because it does not need to be directly accessed from outside the class, and it is final because its value does not need to be changed once it is set.
Several private final static variables are then defined:
- a
HashMap to hold the lookups of the scores for the letters
- a
String array of the letters grouped by their common score
- an
int array of the scores for the letters
They are static because they don’t need to differ between object instantiations, so they can belong to the class itself.
In a static block, the IntStream.range() method is called to iterate an index from 0 up to but including the length of the array of letters.
In a forEach() method, each index value is passed into a lambda which calls the chars{} method on each string at the index of the letters array.
In another forEach, each letter is passed into a lambda that adds the letter and its corresponding score to the HashMap.
This works because the groups of letters and their scores are at the same index in their respective arrays.
In the constructor, chars() is called on the uppercased word and chained to the reduce() method.
The accumulator is initialized to 0, and the accumulator and each letter is passed to a lambda that adds the score looked up from the HashMap for the letter.
The score variable is set to the value returned from reduce(), which is the value of its accumulator.
Source: Exercism java/scrabble-score