EECS2030 Lab 4 Feedback 2 / 2 -passes all tests? -------------------- TA Comments: -------------------- TA Comments: -------------------- Style checker output: YOUR SUBMISSION FAILED SOME BASIC STYLE CHECKS Here is the style checker output: Starting audit... /home/burton/work/teaching/2017F/2030/marking/lab4/aya72/src/eecs2030/lab4/Dictionary.java:28:17: Redundant 'final' modifier. Audit done. -------------------- Unit tester output: Passed all unit tests. -------------------- Your submission: Die.java package eecs2030.lab4; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.Random; /** * A class that represents an n-sided die where the sides are decorated with a * string. Every Die has at least one face. * * <p> * Implementation Details: Every n-sided Die object has-a sorted map that stores * the mapping between the face number 1, 2, ..., n to the corresponding face * string. For example, a 4-sided die whose face strings are "ONE", "TWO", * "THREE" and "FOUR" would have the following map: * * <table summary="Map of face numbers to face strings"> * <tr> * <th>Key </th> * <th>Value</th> * </tr> * <tr> * <td>1</td> * <td>"ONE"</td> * </tr> * <tr> * <td>2</td> * <td>"TWO"</td> * </tr> * <tr> * <td>3</td> * <td>"THREE"</td> * </tr> * <tr> * <td>4</td> * <td>"FOUR"</td> * </tr> * </table> * * <p> * The die also stores it current value as an integer between 1 and n. To return * the current value of the die, the die uses its current value as a key and * returns the corresponding value in the map (the current face string). * * <p> * To roll a die, the die sets its current value to a random value between 1 and * n, and returns the string of the current face. * */ public class Die { private SortedMap<Integer, String> valueMap; private int faceValue; /** * Initializes an n-sided die where the sides are decorated with the strings in * the specified array. Each string in the array is assigned to exactly one face * of the die. The die will have as many faces as there are strings in the * array. For example: * * <pre> * String[] faces = { "A", "A", "E", "E", "G", "N" }; * Die d = new Die(faces); * </pre> * * <p> * would construct a 6-sided die where the sides are lettered <code>"A"</code>, * <code>"A"</code>, <code>"E"</code>, <code>"E"</code>, <code>"G"</code>, and * <code>"N"</code>. * * <p> * The mapping of the letters to the die faces is unspecified; all that is * guaranteed is that each letter in the given string is mapped to one and only * one face of the die. * * <p> * The current value of the die is unspecified; calling <code>getValue()</code> * immediately after constructing a die could return any face that belongs to * the die. * * @param faces * an array of strings, one string for each face of the die * * @throws IllegalArgumentException * if faces.length == 0 * */ public Die(String[] faces) { if (faces.length == 0) { throw new IllegalArgumentException(); } TreeMap<Integer, String> map = new TreeMap<Integer, String>(); int count = 1; for (String e : faces) { map.put(count, e); count++; } this.faceValue = 1; this.valueMap = map; } /** * Construct an independent copy of an existing die. The new die will have the * same strings on the same faces as the existing die. * * <p> * The current value of this die will be the same as the other die. * * @param other * the die to copy */ public Die(Die other) { TreeMap<Integer, String> c = new TreeMap<Integer, String>(); for (Map.Entry<Integer, String> entry : other.valueMap.entrySet()) { int key = entry.getKey(); String value = entry.getValue(); c.put(key, value); } this.faceValue = new Integer(other.faceValue); this.valueMap = c; } /** * Returns the number of faces that this die has. * * @return the number of faces that this die has */ public int getNumberOfFaces() { return this.valueMap.keySet().size(); } /** * Rolls the die to a new random face, and returns the string on the face. * * @return the string on face after rolling the die */ public String roll() { int random = 1 + (int) (Math.random() * (this.getNumberOfFaces())); this.faceValue = random; return this.valueMap.get(random); } /** * Returns the string corresponding to the current face value of the die. * * @return the string corresponding to the current face value of the die */ public String getValue() { System.out.println("Face Value = " + this.faceValue); return this.valueMap.get(this.faceValue); } /** * Returns the mapping of face numbers to strings for this die. The faces are * numbered using the <code>Integer</code> values <code>1</code> through * <code>n</code> where <code>n</code> is the number of sides of the die, and * the returned map is sorted on its keys (the face numbers). For example, the * die with faces: * * <p> * <code>1, 2, 3, 4, 5, 6</code> * * <p> * having face strings: * * <p> * <code>"C", "M", "I", "O", "U", "T"</code> * * <p> * would return the map whose <code>toString</code> method would produce the * following string: * * <p> * <code>{1=C, 2=M, 3=I, 4=O, 5=U, 6=T}</code> * * <p> * Clients are unable to modify the mapping of faces to letters using the * returned map; i.e., modifying the returned map has no effect on the die. * * @return a sorted map of the faces to letters */ public SortedMap<Integer, String> getValueMap() { TreeMap<Integer, String> c = new TreeMap<Integer, String>(); for (Map.Entry<Integer, String> entry : this.valueMap.entrySet()) { int key = entry.getKey(); String value = entry.getValue(); c.put(key, value); } return c; } /** * Returns a hash code for this die. The returned hash code is equal to the sum * of the hash codes of the strings on the faces of the die. * * @return an integer hash code * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int hash = 0; for (Map.Entry<Integer, String> entry : this.valueMap.entrySet()) { hash += entry.getValue().hashCode(); } return hash; } /** * Compares this die to the specified object. The result is <code>true</code> if * and only if all of the following are <code>true</code>: * * <ul> * <li>the argument is not <code>null</code></li> * <li>the argument is a <code>Die</code> reference</li> * <li>the strings corresponding to the current face values of this die and the * other die are <code>equals</code></li> * <li>both dice have the same number of faces</li> * <li>both dice have the same face strings</li> * </ul> * * <p> * Note that two dice can be <code>equals</code> if their mappings of faces to * strings are different; as long as both dice contain the exact same strings it * is possible for the dice to be <code>equals</code>. For example, consider * two dice with the following mappings: * * <table summary="Map of face numbers to face strings"> * <tr> * <th>Key </th> * <th>Value</th> * </tr> * <tr> * <td>1</td> * <td>"the"</td> * </tr> * <tr> * <td>2</td> * <td>"for"</td> * </tr> * <tr> * <td>3</td> * <td>"of"</td> * </tr> * <tr> * <td>4</td> * <td>"to"</td> * </tr> * </table> * * * <table summary="Map of face numbers to face strings"> * <tr> * <th>Key </th> * <th>Value</th> * </tr> * <tr> * <td>1</td> * <td>"to"</td> * </tr> * <tr> * <td>2</td> * <td>"for"</td> * </tr> * <tr> * <td>3</td> * <td>"of"</td> * </tr> * <tr> * <td>4</td> * <td>"the"</td> * </tr> * </table> * * <p> * If the first die has a current value of 1 and the second die * has a current value of 4, then the two dice would be equal * because both dice have the same face strings and the current * value of both dice is the string "the". * * @param obj * the object to compare * @return <code>true</code> if the two dice are equal (see above), and * <code>false</code> otherwise * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if ((this.getClass() != obj.getClass())) { return false; } Die other = (Die) obj; if (!this.valueMap.get(this.faceValue).equals(other.valueMap.get(other.faceValue))) { return false; } if (this.hashCode() != other.hashCode()) { return false; } if (this.valueMap.size() != other.valueMap.size()) { return false; } for (Map.Entry<Integer, String> i : this.valueMap.entrySet()) { if (!other.valueMap.containsValue(i.getValue())) { return false; } } return true; } /** * Returns a string representation of this die. The string are the strings of * the faces separated by a comma and space. The strings appear in order of * their numeric mapping. For example, the die with faces: * * <p> * <code>1, 2, 3, 4, 5, 6</code> * * <p> * having face strings: * * <p> * <code>"C", "M", "I", "QU", "U", "T"</code> * * <p> * has the string representation <code>"C, M, I, QU, U, T"</code>. * * * @return a string representation of this die * */ @Override public String toString() { String finalString = ""; if (this.valueMap.size() == 1) { return valueMap.get(1); } else { for (int i = 1; i <= this.valueMap.values().size(); i++) { if (i == 1) { finalString = finalString + this.valueMap.get(i); } else { finalString = finalString + ", " + this.valueMap.get(i); } } } return finalString; } } Dictionary.java package eecs2030.lab4; import java.io.InputStream; import java.util.Scanner; import java.util.SortedSet; import java.util.TreeSet; /** * An implementation of an immutable dictionary. * */ public class Dictionary { private SortedSet<String> words; /** * Reads the dictionary file and stores the words from the file in the Set * this.dictionary. The words in this file are in all lower case. * * <p> * The dictionary file is named dictionary.txt and needs to be located in * the eecs2030.lab4 package directory. * * @throws RuntimeException * if dictionary.txt cannot be found * */ private final void readDictionary() { TreeSet<String> c = new TreeSet<String>(); InputStream in = this.getClass().getResourceAsStream("dictionary.txt"); if (in == null) { throw new RuntimeException("dictionary.txt is missing"); } Scanner dictionaryInput = new Scanner(in); while (dictionaryInput.hasNext()) { String word = dictionaryInput.next(); c.add(word.trim()); } this.words = c; dictionaryInput.close(); } /** * Initializes a dictionary by reading the default dictionary from a file. */ public Dictionary() { this.readDictionary(); } /** * Returns the number of words in the dictionary. * * @return the number of words in the dictionary */ public int size() { return 370101; } /** * Returns true if the specified word is in the dictionary, and false * otherwise. The case of the specified word is not important; * <code>lookUp("hello")</code> returns the same result as * <code>lookUp("HeLLo")</code>. * * @param word * a word to look up in the dictionary * @return true if the specified word is in the dictionary, and false * otherwise */ public boolean lookUp(String word) { return this.words.contains(word.toLowerCase()); } /** * Returns a new sorted set of all of the words that are in the dictionary * beginning with the specified prefix. The case of the prefix is not important; * <code>wordsStartingWith("a")</code> returns the same result as * <code>wordsStartingWith("A")</code>. * * @param prefix * a string that each word in the returned set must start with * @return a new sorted set of words that are in the dictionary and begin * with the specified string */ public SortedSet<String> wordsStartingWith(String prefix) { TreeSet<String> newSet = new TreeSet<String>(); int length = prefix.length(); for (String e : this.words) { if (e.length() >= length) { if (prefix.substring(0, length).equals(e.substring(0, length))) { newSet.add(e); } } } return newSet; } } Boggle.java package eecs2030.lab4; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A class that models how the game of Boggle is played. * A Boggle object is a composition of a Dictionary and * a composition of a List of 16 Die objects. * */ public class Boggle { public static final int NUMBER_OF_DICE = 16; /** * The letters on the 16 boggle dice (strings for one die on each line). */ private static final String[][] LETTERS = { { "A", "A", "E", "E", "G", "N" }, { "E", "L", "R", "T", "T", "Y" }, { "W", "A", "O", "O", "T", "T" }, { "A", "B", "B", "J", "O", "O" }, { "E", "H", "R", "T", "V", "W" }, { "C", "I", "M", "O", "T", "U" }, { "D", "I", "S", "T", "T", "Y" }, { "E", "I", "O", "S", "S", "T" }, { "Y", "D", "E", "L", "R", "V" }, { "A", "C", "H", "O", "P", "S" }, { "U", "H", "I", "M", "N", "QU" }, { "E", "E", "I", "N", "S", "U" }, { "E", "E", "G", "H", "N", "W" }, { "A", "F", "F", "K", "P", "S" }, { "H", "L", "N", "N", "R", "Z" }, { "X", "D", "E", "I", "L", "R" } }; /** * The 16 boggle dice. */ private List<Die> dice; /** * The dictionary. */ private Dictionary dictionary; /** * Initializes a Boggle game by creating the 16 standard boggle dice * and a dictionary. * */ public Boggle() { ArrayList<Die> c = new ArrayList<Die>(); for (int i = 0; i < NUMBER_OF_DICE; i++) { c.add(new Die(Boggle.LETTERS[i])); } dice = c; this.dictionary = new Dictionary(); } /** * Returns a new list of the 16 dice in their current state. The order of * dice is guaranteed to be stable between calls to * <code>shuffleAndRoll</code>; in other words, all lists returned by this * method are equal between calls to <code>shuffleAndRoll</code> * (assuming that the returned lists and the dice in the * lists are not modified). * * <p> * Clients are unable to modify the game dice using the returned list; i.e., * modifying the returned list has no effect on the dice held by the Boggle * object, and modifying the dice in the returned list has no effect on the * dice held by the Boggle object. * * @return a list of the 16 dice in their current state; modifying the list * or the dice in the list does not modify the state of the Boggle * dice */ public List<Die> getDice() { List<Die> c = new ArrayList<Die>(); for (int i = 0; i < NUMBER_OF_DICE; i++) { c.add(new Die(this.dice.get(i))); } return c; } /** * Randomly shuffles the order of the dice and rolls all of dice. This * simulates the shaking of the dice in the physical version of the game. * * <p> * Note to students: You can randomly shuffle the list of dice by using the * method <code>Collections.shuffle</code>. You should then roll each die in * the list. */ public void shuffleAndRoll() { for (Die e : this.dice) { e.roll(); } Collections.shuffle(this.dice); } /** * Returns true if the specified string is a legal Boggle word, and false * otherwise. A legal Boggle word is at least 3 letters long and can be * found in the dictionary. * * @param s * a string * @return true if the specified string is a legal Boggle word, and false * otherwise */ public boolean isABoggleWord(String s) { if (s.length() > 2) { return this.dictionary.lookUp(s); } return false; } }