package edu.uky.ai.csp;

import edu.uky.ai.csp.kr.*;

/**
 * Models a Sudoku puzzle as a constraint satisfaction problem.
 * 
 * @author Stephen G. Ware
 */
public class Sudoku extends Problem {
	
	/**
	 * Constructs a specific puzzle from a string representation.
	 * 
	 * @param initial the string describing the initial state of the puzzle
	 */
	public Sudoku(String initial) {
		// Remove string decorations.
		initial = initial.replaceAll("[^\\d ]", "");
		// Variables
		Variable[] variables = new Variable[81];
		int i = 0;
		for(String row : new String[]{ "A", "B", "C", "D", "E", "F", "G", "H", "I" }) {
			for(int column = 1; column <= 9; column++) {
				Variable variable = new Variable(row + column);
				variables[i] = variable;
				Domain domain = new Domain(variable, new Integer[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9 });
				if(initial.charAt(i) != ' ')
					domain.set(Integer.parseInt(initial.substring(i, i + 1)));
				addVariable(variable, domain);
				i++;
			}
		}
		// Row constraints
		Variable[] set = new Variable[9];
		for(int row = 0; row < 81; row += 9) {
			i = 0;
			for(int v = row; v < row + 9; v++) {
				set[i] = variables[v];
				i++;
			}
			mutex(set);
		}
		// Column constraints
		for(int column = 0; column < 9; column++) {
			i = 0;
			for(int v = column; v < column + 81; v += 9) {
				set[i] = variables[v];
				i++;
			}
			mutex(set);
		}
		// Box constraints
		for(int boxy = 0; boxy < 81; boxy += 27) {
			for(int boxx = 0; boxx < 9; boxx += 3) {
				set[0] = variables[boxy + boxx];
				set[1] = variables[boxy + boxx + 1];
				set[2] = variables[boxy + boxx + 2];
				set[3] = variables[boxy + boxx + 9];
				set[4] = variables[boxy + boxx + 10];
				set[5] = variables[boxy + boxx + 11];
				set[6] = variables[boxy + boxx + 18];
				set[7] = variables[boxy + boxx + 19];
				set[8] = variables[boxy + boxx + 20];
				mutex(set);
			}
		}
	}
	
	/**
	 * Adds not equals constraints for every pair of variables in a set.
	 * 
	 * @param set a set of variables all not equal
	 */
	private final void mutex(Variable[] set) {
		for(int i = 0; i < set.length - 1; i++) {
			for(int j = i + 1; j < set.length; j++) {
				addConstraint(new NotEqualsConstraint(set[i], set[j]));
				addConstraint(new NotEqualsConstraint(set[j], set[i]));
			}
		}
	}
	
	/**
	 * Prints a string representation of the a solution (or partial solution)
	 * to a Sudoku puzzle.
	 * 
	 * @param solution the solution to be printed
	 * @return a string representation of the puzzle
	 */
	@Override
	public String toString(Solution solution) {
		if(!(solution.problem instanceof Sudoku))
			return solution.toString();
		String str = "";
		int i = 0;
		for(Variable variable : solution.problem.variables) {
			if(i % 27 == 0)
				str += "+---+---+---+\n";
			if(i % 3 == 0)
				str += "|";
			Domain domain = solution.getDomain(variable);
			str += domain.size() == 1 ? domain.getValue() : " ";
			i++;
			if(i % 9 == 0)
				str += "|\n";
		}
		return str + "+---+---+---+\n" + super.toString(solution);
	}
	
	public Domain getDomain(String variable) {
		for(Variable v : variables)
			if(v.name.equals(variable.toString()))
				return getDomain(v);
		throw new IllegalArgumentException("Variable " + variable + " not defined");
	}
}
