EECS2030 Lab 4 Feedback
1.8 / 2  -passes all tests?
--------------------
TA Comments:
-it is not sufficient to check if the value maps are equal:
    public boolean equals(Object obj) {
                if (this == obj) {
                        return true;
                }
                if (obj == null) {
                        return false;
                }
                if (getClass() != obj.getClass()) {
                        return false;
                }
                Die other = (Die) obj;
                if (!this.getValue().equals(other.getValue())) {
                        return false;
                }
                List<String> strings = new ArrayList<String>(this.valueMap.values());
                List<String> otherStrings = new ArrayList<String>(other.valueMap.values());
                Collections.sort(strings);
                Collections.sort(otherStrings);
                if (!strings.equals(otherStrings)) {
                        return false;
                }
                return true;
        }

--------------------
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/aneeb/src/eecs2030/lab4/Dictionary.java:28:13: Redundant 'final' modifier.
Audit done.
--------------------
Unit tester output:

YOUR SUMBISSION FAILED SOME UNIT TESTS
Here is the test output:
java -classpath .:/home/burton/work/teaching/2017F/2030/marking/lab4/_jar/* org.junit.runner.JUnitCore eecs2030.lab4.Lab4Suite
JUnit version 4.12
.........E...................
Time: 6.193
There was 1 failure:
1) test08_equals(eecs2030.lab4.DieTest)
java.lang.AssertionError: d1 and d2 have same values but equals returned false expected:<B, A> but was:<A, B>
FAILURES!!!
Tests run: 28,  Failures: 1

--------------------
Your submission:
Die.java
package eecs2030.lab4;
import java.util.Collections;
import java.util.SortedMap;
import java.util.TreeMap;
/**
 * 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&nbsp;&nbsp;&nbsp;</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 String[] faces;
    private String value;
    /**
     * 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();
        }
        this.faces = faces;
    }
    /**
     * 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) {
        this.faces = other.faces;
        this.value = other.value;
        this.valueMap = other.valueMap;
    }
    /**
     * Returns the number of faces that this die has.
     * 
     * @return the number of faces that this die has
     */
    public int getNumberOfFaces() {
        return faces.length;
    }
    /**
     * 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 faceValue = (int) (Math.random() * faces.length) + 1;
        this.value = this.faces[faceValue - 1];
        return this.value;
    }
    /**
     * 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() {
        if (this.value == null) {
            return this.roll();
        } else {
            return this.value;
        }
    }
    /**
     * 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() {
        SortedMap<Integer, String> exp = new TreeMap<>();
        for (int i = 1; i <= this.faces.length; i++) {
            exp.put(i, this.faces[i - 1]);
        }
        this.valueMap = exp;
        return this.valueMap;
    }
    /**
     * 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 num = 0;
        for (int i = 0; i < this.faces.length; i++) {
            num += this.faces[i].hashCode();
        }
        return num;
    }
    /**
     * 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&nbsp;&nbsp;&nbsp;</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&nbsp;&nbsp;&nbsp;</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.faces.equals(other.faces)) {
            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 ans = faces[0];
        for (int i = 1; i < faces.length; i++) {
            ans = ans + ", " + faces[i];
        }
        return ans;
    }
}
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() {
        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();
            this.words.add(word.trim());
        }
        dictionaryInput.close();
    }
    /**
     * Initializes a dictionary by reading the default dictionary from a file.
     */
    public Dictionary() {
        this.words = new TreeSet<String>();
        readDictionary();
    }
    /**
     * Returns the number of words in the dictionary.
     * 
     * @return the number of words in the dictionary
     */
    public int size() {
        return this.words.size();
    }
    /**
     * 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) {
        if (this.words.contains(word.toLowerCase())) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * 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) {
        SortedSet<String> exp = new TreeSet<>();
        for (String s : words) {
            if (s.startsWith(prefix)) {
                exp.add(s);
            }
        }
        return exp;
    }
}
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 = new ArrayList<>();
    /**
     * The dictionary.
     */
    private Dictionary dictionary;
    /**
     * Initializes a Boggle game by creating the 16 standard boggle dice and a
     * dictionary.
     * 
     */
    public Boggle() {
        for (int i = 0; i < Boggle.NUMBER_OF_DICE; i++) {
            this.dice.add(new Die(LETTERS[i]));
        }
        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> die2 = new ArrayList<>();
        for (int i = 0; i < NUMBER_OF_DICE; i++) {
            Die rdie = new Die(dice.get(i));
            die2.add(rdie);
        }
        return die2;
    }
    /**
     * 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() {
        Collections.shuffle(dice);
        for (int i = 0; i < dice.size(); i++) {
            dice.get(i).roll();
        }
    }
    /**
     * 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;
    }
}