
// Zebra.java
// Krzysztof Kuchcinski and Radoslaw Szymanek, 2008
// Slightly modified by Andrew Davison, ad@coe.psu.ac.th, Dec. 2022

/* The Zebra logic puzzle.
   https://en.wikipedia.org/wiki/Zebra_Puzzle

  Conditions:

    0. There are five houses.

    1. The Englishman lives in the red house.
    2. The Spaniard owns a dog.
    3. The Japanese is a painter.
    4. The Ukrainian drinks tea.
    5. The Norwegian lives in the first house on the left.
    6. The owner of the green house drinks coffee.
    7. The green house is on the right of the white one.
    8. The sculptor breeds snails.
    9. The diplomat lives in the yellow house.
    10. Milk is drunk in the middle house.
    11. The Norwegian's house is next to the blue one.
    12. The violinist drinks fruit juice.
    13. The fox is in a house next to that of the doctor.
    14. The horse is in a house next to that of the diplomat.

  These conditions are labelled as S1..S14 in the code.

  Qusetions: Who owns the Zebra, and who drinks water?
  Answers: The Japanese, and the Norwegian.

  This version replaces cigarettes by jobs:
    Parliaments --> painter
    Old Gold --> sculptor
    Lucky Strike --> violinist
    Kools --> diplomat
    Chesterfields --> doctor

  More info on logic puzzles: 
  https://en.wikipedia.org/wiki/Logic_puzzle

  The coding approach used here is also employed in BabySitting.java
  and HistoricHomes.java.

  Usage:
     > compile Zebra.java
     > run Zebra

     1 : [fox, diplomat, yellow, norwegian, water]
     2 : [tea, ukrainian, horse, blue, doctor]
     3 : [red, english, snails, sculptor, milk]
     4 : [dog, spaniard, white, juice, violinist]
     5 : [painter, green, coffee, japanese, zebra]
*/

import org.jacop.constraints.*;
import org.jacop.core.*;
import org.jacop.search.*;
  // uses Jacop: https://jacop.cs.lth.se/

import java.util.*;


public class Zebra
{
  private static final int NUM_HOUSES = 5;


  public static void main(String args[])
  {
    Store store = new Store();
    List<IntVar> vars = new ArrayList<>();

    model(store, vars);

    SelectChoicePoint<IntVar> select =
      new SimpleSelect<>(vars.toArray(new IntVar[1]),
                         new MostConstrainedStatic<>(),
                         new IndomainMin<>());
       // first consider the variables with the most constraints

    Search<IntVar> search = new DepthFirstSearch<IntVar>();
    search.setPrintInfo(false);
    search.labeling(store, select);

    // JacopUtils.printSolns(search);
    System.out.println("House : associated labels");
    JacopUtils.printGrouped(search);
      /* the variables are printed in groups based on their 
         values, which will be in the range 1..NUM_HOUSES.
      */
  } /* main */



  private static void model(Store store, List<IntVar> vars)
  {
    // create constraint variables as five arrays
    IntVar color[] = new IntVar[NUM_HOUSES];
    IntVar nat[] = new IntVar[NUM_HOUSES];
    IntVar drink[] = new IntVar[NUM_HOUSES];
    IntVar pet[] = new IntVar[NUM_HOUSES];
    IntVar prof[] = new IntVar[NUM_HOUSES];

    // define labels for the arrays, and their index positions.
    /* This naming approach makes it much easier to understand
       the constraints representing the conditions */

    String[] colorNms = {"red", "green", "white", "yellow", "blue"};
    int red = 0, green = 1, white = 2, yellow = 3, blue = 4;

    String[] natNms =
    {"english", "spaniard", "japanese", "ukrainian", "norwegian"};
    int english = 0, spaniard = 1, japanese = 2, 
        ukrainian = 3, norwegian = 4;

    String[] drinkNms = {"tea", "coffee", "milk", "juice", "water"};
    int tea = 0, coffee = 1, milk = 2, juice = 3 , water = 4;

    String[] petNms = {"dog", "snails", "fox", "horse", "zebra"};
    int dog = 0, snails = 1, fox = 2, horse = 3, zebra = 4 ;

    String[] profNms =
    {"painter", "sculptor", "diplomat", "violinist", "doctor"};
    int painter = 0, sculptor = 1, diplomat = 2, 
       violinist = 3, doctor = 4;

    // use the labels as variable names in the five arrays
    for (int i = 0; i < NUM_HOUSES; i++) {
      color[i] = new IntVar(store, colorNms[i], 1, NUM_HOUSES);
      pet[i] = new IntVar(store, petNms[i], 1, NUM_HOUSES);
      drink[i] = new IntVar(store, drinkNms[i], 1, NUM_HOUSES);
      nat[i] = new IntVar(store, natNms[i], 1, NUM_HOUSES);
      prof[i] = new IntVar(store, profNms[i], 1, NUM_HOUSES);
      vars.add(color[i]);
      vars.add(nat[i]);
      vars.add(pet[i]);
      vars.add(drink[i]);
      vars.add(prof[i]);
    }

    store.impose(new Alldifferent(color));
    store.impose(new Alldifferent(pet));
    store.impose(new Alldifferent(drink));
    store.impose(new Alldifferent(nat));
    store.impose(new Alldifferent(prof));

    // S1 to S10
    store.impose(new XeqY(nat[english], color[red]));
         // The Englishman lives in the red house.

    store.impose(new XeqY(nat[spaniard], pet[dog]));
         // The Spaniard owns a dog.

    store.impose(new XeqY(nat[japanese], prof[painter]));
         // The Japanese is a painter.

    store.impose(new XeqY(nat[ukrainian], drink[tea]));
        // The Ukrainian drinks tea.

    store.impose(new XeqC(nat[norwegian], 1));
        // The Norwegian lives in the first house on the left.

    store.impose(new XeqY(color[green], drink[coffee]));
        // The owner of the green house drinks coffee.

    store.impose(new XplusCeqZ(color[white], 1, color[green]));
        // The green house is on the right of the white one.

    store.impose(new XeqY(prof[sculptor], pet[snails]));
        // The sculptor breeds snails.

    store.impose(new XeqY(prof[diplomat], color[yellow]));
       // The diplomat lives in the yellow house.

    store.impose(new XeqC(drink[milk], 3));
       // Milk is drunk in the middle house.

    // S11. The Norwegian's house is next to the blue one.
    store.impose(new XneqY(nat[norwegian], color[blue]));
    store.impose(new Eq(new XplusCeqZ(nat[norwegian], 1, color[blue]),
                        new Not(new XplusCeqZ(color[blue], 1,
                                              nat[norwegian]))));

    // S12. The violinist drinks fruit juice.
    store.impose(new XeqY(prof[violinist], drink[juice]));

    // S13. The fox is in a house next to that of the doctor.
    store.impose(new XneqY(pet[fox], prof[doctor]));
    store.impose(new Or(new XplusCeqZ(pet[fox], 1, prof[doctor]),
                        new XplusCeqZ(prof[doctor], 1, pet[fox])));

    // S14. The horse is in a house next to that of the diplomat.
    store.impose(new XneqY(pet[horse], prof[diplomat]));
    IntVar dist14 = new IntVar(store, "dist14", -1, 1);
    store.impose(new XplusYeqZ(dist14, pet[horse], prof[diplomat]));
    //vars.add(dist14);
  } /* model */

}   // end of Zebra class
