/**
 * Created on 2008.05.30
 */
package org.drools.puzzles.mastermind;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * Codemaker for a more complicated variation of the classic Mastermind
 * game. Please see: 
 * 
 * http://en.wikipedia.org/wiki/Mastermind_(board_game).
 * 
 * for the rules of the classic game.
 * 
 * This class uses 12 different colors for the code pegs.
 * It generates a pattern of 8 code pegs. Duplicates of color is allowed. 
 * The size of solution space is 12^8 = 429,981,696 patterns. 
 * 
 * This class also judges a guess. A black peg is given, if a color appears
 *  at the same positions in both code pattern and guess pattern.
 *  A white peg is given, if a color appears in both code pattern and guess
 *  pattern but appears at different positions. 
 *  
 *  For example, given the code:
 *  code: [brown, cyan, green, lime, cyan]
 *  
 *  A guess [brown, peru, red, silver, teal] will get one black peg.
 *  A guess [peru, brown, red, silver, teal] will get one white peg.
 *  A guess [teal, teal, violet, yellow, red] will get no peg at all.
 *  A guess [cyan, teal, red, green, yellow, cyan] will get two black pegs and
 *  one white peg. 
 *  
 *  When the codebreaker gets totally code.length black pegs, she wins.
 *  (In the example above, 5 black pegs is needed for winning.) 
 * 
 * @author Ellen Ning Zhao
 * @version 2008.05.30
 */
public class CodeMaker {
  
  /**
   * The color constants. Please refer to:
   * http://www.w3schools.com/HTML/html_colornames.asp
   * for their exact rendering.
   */
  public enum CodePeg {
    BROWN, CYAN, GREEN, LIME, MAROON, NAVY, PERU, RED, SILVER, TEAL, 
    VIOLET, YELLOW
  }

  /**
   * Constants of keys for a judgement.
   */
  public enum KeyPeg {
    BLACK, WHITE
  }

  public static final int CODE_SLOTS = 8;
  
  private static final CodePeg[] ALL_COLORS = CodePeg.values(); 
  
  private CodePeg[] code = new CodePeg[CODE_SLOTS];
  
  // for storing the occurrence count of each color in the code.
  private int[] colorCounts = new int[ALL_COLORS.length];

  /**
   * Default constructor. 
   */
  public CodeMaker() {
    this.generateCode();  // generates a new code
  }
  
  /**
   * Generates a random code of a 8-color pattern. 
   * Color dupicates are allowed.
   */
  public void generateCode() {
    Random rand = new Random();
    
    // clear up the member code count.
    for (int i = 0; i < this.colorCounts.length; i++) {
      this.colorCounts[i] = 0;
    }

    // assign the codepegs to code slots
    for (int i = 0; i < CODE_SLOTS; i++) {
      int randInt = rand.nextInt(CodePeg.values().length); 
      this.code[i] = ALL_COLORS[randInt];
      this.colorCounts[randInt]++; // update the color count
    }
  }

  /**
   * @return the target code (the correct answer).
   */
  public CodePeg[] getCode() {
    return this.code;
  }
 
  /**
   * Judges a guess against the member code. 
   * 
   * @param guess an array of 8 CodePeg slots.
   * @return the result map. Keys are the contants defined in the enum KeyPeg.
   *          the Integer is the count of the keypeg. 
   */
  public Map<KeyPeg, Integer> judge(CodePeg[] guess) {
    // initialize the result map
    Map<KeyPeg, Integer> result = new HashMap<KeyPeg, Integer>();
    result.put(KeyPeg.BLACK, 0);
    result.put(KeyPeg.WHITE, 0);
    
    // temp counters
    int black = 0;
    int white = 0;

    // initialize the color counter for the guess pattern
    int[] gColorCounts = new int[ALL_COLORS.length];
    for (int i = 0; i < gColorCounts.length; i++) {
      gColorCounts[i] = 0;
    }

    // count occurrence of each colour in the guess
    for (int i = 0; i < guess.length; i++) {
      gColorCounts[guess[i].ordinal()]++;
    }

    // count the total white pegs, regardless of position
    for (int i = 0; i < ALL_COLORS.length; i++) {
      white += Math.min(this.colorCounts[i], gColorCounts[i]);
    }

    // count the total black pegs
    for (int i = 0; i < CODE_SLOTS; i++) {
      if (code[i] == guess[i]) {
        black++;
      }
    }

    white = white - black;

    result.put(KeyPeg.BLACK, black);
    result.put(KeyPeg.WHITE, white);
    return result;
  }
  
  /**
   * Just for testing whether the judge() method works correctly. 
   * You can comment out this method. 
   */
  public static void main(String args[]) {
    CodeMaker maker = new CodeMaker();
    final CodePeg[] code = maker.getCode();

    // just for test.....
    CodePeg[] guess = new CodePeg[] { CodePeg.BROWN, CodePeg.YELLOW,
        CodePeg.CYAN, CodePeg.GREEN, CodePeg.BROWN, CodePeg.LIME, CodePeg.PERU,
        CodePeg.NAVY };

    for (int i = 0; i < CODE_SLOTS; i++) {
      System.out.println(code[i] + " -------- " + guess[i]);
    }
    
    System.out.println();
    
    // what's the result of my guess?
    final Map<KeyPeg, Integer> result = maker.judge(guess);
    for (KeyPeg key : result.keySet()) {
      System.out.println(key + ": " + result.get(key));
    }
    
    /******************** begin test output **************************
      BROWN -------- BROWN
      VIOLET -------- YELLOW
      MAROON -------- CYAN
      NAVY -------- GREEN
      NAVY -------- BROWN
      RED -------- LIME
      SILVER -------- PERU
      GREEN -------- NAVY
  
      WHITE: 2
      BLACK: 1
    *********************** end test output  *************************/
  }
}
