import org.jacop.core.*; 
import org.jacop.constraints.*; 
import org.jacop.search.*; 

// Possible constraint network for the potion riddle from Harry Potter and the
// Sorcerer's Stone, which can for example be found at
// http://www.rain.org/~mkummel/stumpers/17mar00a.html
// (in a version where the third and the sixth bottle from the left are the
// smallest and tallest, respectively).

public class Main {
 
    static Main m = new Main (); 
 
    public static void main (String[] args) { 
        Store store = new Store();  // define FD store 

        // define finite domain variables 
        IntVar[] v = new IntVar[7]; 
        int forward = 0;
        int back = 1;
        int poison = 2;
        int wine = 3;

        for (int i = 0; i < 7; i++) 
            v[i] = new IntVar(store, "Bottle"+i, 0, 3); 

        // define constraints 
        
        // One among us seven will let you move ahead,
        // Another will transport the drinker back instead,
        // Two among our number hold only nettle wine,
        // Three of us are killers, waiting hidden in line.
        // (one moves forward, one moves back, two are wine, three are poison)
        store.impose (new GCC(v, new IntVar[]{new IntVar(store, 1, 1),
                                              new IntVar(store, 1, 1),
                                              new IntVar(store, 3, 3),
                                              new IntVar(store, 2, 2)}));
        
        //  First, however slyly the poison tries to hide
        //  You will always find some on nettle wine's left side;
        //  (left besides wine is always poison)

        /* first variant
        for (int i = 1; i < 7; i++) {
            int [][] relation = new int[13][2];
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 4; k++) {
                    relation[j*4+k][0] = k;
                    relation[j*4+k][1] = j;
                }
            }
            relation[12][0] = poison;
            relation[12][1] = wine;
            store.impose (new ExtensionalSupportVA(new IntVar[]{v[i-1],v[i]}, relation));
        } 
        */

        /* second variant
        for (int i = 1; i < 7; i++)
            store.impose (new ExtensionalConflictVA(new IntVar[]{v[i-1],v[i]},
                new int[][]{{forward, wine}, {back, wine}, {wine,wine}}));
        */

        // third variant
        for (int i = 1; i < 7; i++)
            store.impose(new IfThen(new XeqC(v[i], wine), new XeqC(v[i-1], poison)));
      
        // Second, different are those who stand at either end,
        // But if you would move onward, neither is your friend;
        // (ends different but neither is forward)
        /* first variant
        store.impose(new ExtensionalSupportVA(new IntVar[]{v[0],v[6]},
                                              new int[][]{{back, poison}, {back, wine}, {poison, back},
                                                          {poison, wine}, {wine, back}, {wine, poison}}));
        */

        // second variant
        store.impose(new XneqY(v[0],v[6]));
        store.impose(new XneqC(v[0], forward));
        store.impose(new XneqC(v[6], forward));

        // Third, as you see clearly, all are different size,
        // Neither dwarf nor giant holds death in their insides;
        // (neither 2 nor 5 contains posion)
        /* first variant
        store.impose (new ExtensionalSupportVA(new IntVar[]{v[2]}, 
                                               new int[][]{{forward},{back},{wine}}));
        store.impose (new ExtensionalSupportVA(new IntVar[]{v[5]}, 
                                               new int[][]{{forward},{back},{wine}}));
        */

        // second variant
        store.impose(new XneqC(v[2], poison));
        store.impose(new XneqC(v[5], poison));

        // Fourth, the second left and the second on the right
        // Are twins once you taste them, though different at first sight.
        // 1 and 5 contain same drink
        /* first variant
        store.impose (new ExtensionalSupportVA(new IntVar[]{v[1],v[5]}, 
                                               new int[][]{{forward,forward},{back,back},
	                                                     {poison,poison},{wine,wine}}));
        */
         
        // second variant
        store.impose(new XeqY(v[1],v[5]));

 
        // search for a solution and print results 
        Search<IntVar> search = new DepthFirstSearch<IntVar>();
        search.getSolutionListener().searchAll(true); 
        search.getSolutionListener().recordSolutions(true); 
        SelectChoicePoint<IntVar> select = 
            new InputOrderSelect<IntVar>(store, v, 
                                         new IndomainMin<IntVar>()); 
        boolean result = search.labeling(store, select); 
 
        if ( result ) {
            search.getSolutionListener().printAllSolutions(); 
            System.out.println("Solution: " + v[0]+", "+v[1] +", "+ 
                                              v[2] +", "+v[3] +", " + 
                                              v[4] +", "+v[5] +", " + 
                                              v[6]);
        } else {
            System.out.println("*** No"); 
        }
    } 
} 
