/*
 * 2004 Utah High School Programming Contest, University of Utah
 * Take-Home Problem
 *
 * Puzzle.java
 *
 * This file contains the implementation of the Puzzle class.
 */

import java.util.Vector;

/**
 * A Puzzle collects the "input" information about a cryptogram puzzle and
 * evaluates possible solutions (i.e., decryption ciphers).  The most important
 * input data is the cryptogram text, of course, but a Puzzle may also use
 * other sources of information such as a dictionary of known English words.
 * The primary job of a Puzzle object is to evaluate ciphers that are given to
 * it: in other words, to decide how likely it is that a given cipher is the
 * right cipher.
 */
class Puzzle {
    /**
     * The encrypted message.
     */
    private final String cryptogram;
    
    /**
     * The words within the cryptogram, organized as a Vector of Strings.
     * The contents of this vector are initialized by the Puzzle constructor.
     */
    private final Vector cryptogramWords = new Vector();
    
    /**
     * The Dictionary of known words.  This is used to evaluate the quality
     * of ciphers.
     */
    private final Dictionary dict;
    
    /**
     * Construct a Puzzle object to represent a cryptogram decryption problem.
     * The information for a puzzle includes the cryptogram itself and a
     * dictionary of English words.  This constructor breaks the cryptogram
     * into individual (encrypted) words and stores those words inside the
     * Puzzle object.
     *
     * @param theCryptogram    the String containing the cryptogram text
     * @param theDict          the Dictionary of known English words
     */
    public Puzzle(String theCryptogram, Dictionary theDict) {
        this.cryptogram = theCryptogram;
        this.dict = theDict;
        
        int cryptogramLength = this.cryptogram.length();
        int noStart = -1;
        int wordStart;  // Points to the start of a word.
        int i;          // Current position in the cryptogram.
        
        wordStart = noStart;
        
        for (i = 0; i < cryptogramLength; ++i) {
            boolean atLetter = Character.isLetter(this.cryptogram.charAt(i));
            
            if (atLetter && (wordStart == noStart)) {
                // We have found the start of a new word.  Mark it.
                wordStart = i;
            }
            if (!atLetter && (wordStart != noStart)) {
                // `i' points just past the end of a word.
                // Copy the found word into our `cryptogramWords' vector.
                String word = this.cryptogram.substring(wordStart, i);
                this.cryptogramWords.add(word);
                wordStart = noStart;
            }
        }
        // Collect the word (if any) that ends at the end of the cryptogram.
        if (wordStart != noStart) {
            String word = this.cryptogram.substring(wordStart, i);
            this.cryptogramWords.add(word);
            wordStart = noStart;
        }
    }
    
    /**
     * Test the given Cipher object to see how well it decrypts this Puzzle's
     * cryptogram.  This method returns the "score" of the cipher, which is a
     * measure of how well the cipher decrypts the message.  The better the
     * cipher, the higher the score.
     *
     * @param cipher    the Cipher to be tested
     * @return          the score for the cipher
     */
    public int testCipher(Cipher cipher) {
        int cryptogramWordsLength = this.cryptogramWords.size();
        
        // TODO: Improve the scoring function for ciphers.
        //
        // The code below returns 1 if the cipher transforms every encrypted
        // word of the cryptogram (i.e., every string in
        // `this.cryptogramWords') into a word that is found in the dictionary.
        // It returns 0 if the cipher fails to decrypt at least one word.  This
        // isn't a very good way of "scoring" the ciphers.  You should
        // definitely improve this code!
        //
        for (int i = 0; i < cryptogramWordsLength; ++i) {
            StringBuffer word
                = new StringBuffer((String)this.cryptogramWords.get(i));
            cipher.decipher(word);
            if (!this.dict.lookup(word.toString())) {
                return 0;
            }
        }
        return 1;
    }
}

// End of file.

