
// HistoricHomes.java
// Krzysztof Kuchcinski and Radoslaw Szymanek, 2008
// Slightly modified by Andrew Davison, ad@coe.psu.ac.th, Dec. 2022

/* Logic puzzle about houses.

   Each year the Glendale Women's Club sponsors a Historic Homes 
   Tour in which five old houses are opened to the public. This 
   year the five homes are on different streets (Azalea Drive,
   Crepe Myrtle Court, Jasmine Boulevard, Magnolia Street, and 
   Oleander Road), and each was built in a different year 
   (1860, 1870, 1890, 1900, and 1920).

   Can you give the vOrder in which the tour visited the five homes
   (identifying them by streets) and match each with its year?

   Conditions:
   1. The home on Jasmine is 20 years older than the one on Azalea.

   2. The third home on the tour was built in 1860.

   3. The tour visited the home on Magnolia sometime before the one
      built in 1890.

   4. The tour visited the home on Oleander (which wasn't the last of
      the five to be built) sometime before it visited the one on 
      Jasmine, which in turn was seen sometime before the one 
      built in 1900.

   These conditions are labelled as S1..S4 in the code.

   Determine: Visit order -- Street -- Year

  More info on logic puzzles: 
  https://en.wikipedia.org/wiki/Logic_puzzle

  The coding approach is the same as that used in Zebra.java,
  but a bit more complex since it uses Element constraints.

  Usage:
     > compile HistoricHomes.java
     > run HistoricHomes

     1860 : [3rd, Oleander_Road]
     1870 : [Jasmine_Boulevard, 4th]
     1890 : [Azalea_Drive, 2nd]
     1900 : [Crepe_Myrtle_Court, 5th]
     1920 : [1st, Magnolia_Street]
*/


import org.jacop.constraints.*;
import org.jacop.core.*;
import org.jacop.search.*;
  // uses Jacop: https://jacop.cs.lth.se/

import java.util.*;



public class HistoricHomes
{
  private static final int NUM_HOMES = 5;


  public static void main(String args[])
  {
    Store store = new Store();
    List<IntVar> vars = new ArrayList<>();

    // create constraint variables as two arrays
    IntVar vOrder[] = new IntVar[NUM_HOMES];  // visit order
    IntVar streets[] = new IntVar[NUM_HOMES];


    // define labels for the arrays, and their index positions.
    /* This naming approach makes it much easier to understand
       the constraints representing the conditions */

    String streetNms[] = { "Azalea_Drive",
                            "Crepe_Myrtle_Court",
                            "Jasmine_Boulevard",
                            "Magnolia_Street",
                            "Oleander_Road"  };
    int iAzalea = 0,  iCrepe = 1, 
        iJasmine = 2, iMagnolia = 3, iOleander = 4;

    String visitOrder[] = {"1st", "2nd", "3rd", "4th", "5th"};
    int i1st = 0, i2nd = 1, i3rd = 2, i4th = 3, i5th = 4;


    // use the labels as variable names in the arrays
    for (int i = 0; i < NUM_HOMES; i++) {
      vOrder[i] = new IntVar(store, visitOrder[i]);
      vOrder[i].addDom(1860, 1860); // the domain is years
                         // there are 5 possible year values
      vOrder[i].addDom(1870, 1870);
      vOrder[i].addDom(1890, 1890);
      vOrder[i].addDom(1900, 1900);
      vOrder[i].addDom(1920, 1920);

      streets[i] = new IntVar(store, streetNms[i]);
      streets[i].addDom(1860, 1860);  // 5 possible year values
      streets[i].addDom(1870, 1870);
      streets[i].addDom(1890, 1890);
      streets[i].addDom(1900, 1900);
      streets[i].addDom(1920, 1920);

      vars.add(vOrder[i]);
      vars.add(streets[i]);
    }

    store.impose(new Alldifferent(streets));
    store.impose(new Alldifferent(vOrder));

    // S1. The home on Jasmine is 20 years older than the one on Azalea
    store.impose(new XplusCeqZ(streets[iAzalea], -20,
                               streets[iJasmine]));


    // S2. The third home on the tour was built in 1860
    store.impose(new XeqC(vOrder[i3rd], 1860));


    // S3. The tour visited the home on Magnolia sometime before 
    // the one built in 1890.

    // Pos of Magnolia Street in vOrder[] determines its order
    IntVar index1 = new IntVar(store, "index1", 1, NUM_HOMES);
    store.impose(Element.choose(index1, vOrder, streets[iMagnolia]));
       // constrain  Magnolia = vOrder[index1]

    // Pos of 1890 in vOrder[] determines its order
    IntVar index2 = new IntVar(store, "index2", 1, NUM_HOMES);
    IntVar value1890 = new IntVar(store, "1890", 1890, 1890);
    store.impose(Element.choose(index2, vOrder, value1890));
       // constrain  1890 = vOrder[index2]

    // link these indicies
    store.impose(new XltY(index1, index2));

    // implied constraints
    store.impose(new XneqC(streets[iMagnolia], 1890));
    store.impose(new XneqC(vOrder[i1st], 1890));
    store.impose(new XneqY(vOrder[i5th], streets[iMagnolia]));


    /* S4. The tour visited the home on Oleander (which wasn't 
       the last of the five to be built) sometime before it 
       visited the one on Jasmine, which in turn was
       seen sometime before the one built in 1900. */
    store.impose(new XneqC(streets[iOleander], 1920));

    // Index 3 specifies the order for Oleander
    IntVar index3 = new IntVar(store, "index3", 1, NUM_HOMES);
    store.impose(Element.choose(index3, vOrder, streets[iOleander]));
       // constrain  Oleander = vOrder[index3]

    // Index 4 specifies the order for Jasmine
    IntVar index4 = new IntVar(store, "index4", 1, NUM_HOMES);
    store.impose(Element.choose(index4, vOrder, streets[iJasmine]));
       // constrain  Jasmine = vOrder[index4]

    // index 5 specifies the order for the building built in 1900
    IntVar index5 = new IntVar(store, "index5", 1, NUM_HOMES);
    IntVar value1900 = new IntVar(store, "1900", 1900, 1900);
    store.impose(Element.choose(index5, vOrder, value1900));
       // constrain  1900 = vOrder[index5]

    // link these indicies
    store.impose(new XltY(index3, index4));
    store.impose(new XltY(index4, index5));

    // implied constraints
    store.impose(new XneqY(streets[iOleander], vOrder[i5th]));
    store.impose(new XneqY(streets[iOleander], vOrder[i4th]));
    store.impose(new XneqC(streets[iOleander], 1900));

    store.impose(new XneqC(vOrder[i1st], 1900));
    store.impose(new XneqC(vOrder[i2nd], 1900));

    store.impose(new XneqY(vOrder[i1st], streets[iJasmine]));
    store.impose(new XneqY(vOrder[i5th], streets[iJasmine]));

    // vars.add(index1);
    // vars.add(index2);
    // vars.add(index3);
    // vars.add(index4);


    SelectChoicePoint<IntVar> select =
      new SimpleSelect<>(vars.toArray(new IntVar[1]),
                         null, new IndomainMin<>());

    Search<IntVar> search = new DepthFirstSearch<>();
    search.setPrintInfo(false);
    search.labeling(store, select);

    // JacopUtils.printSolns(search);
    System.out.println("Year : associated labels");
    JacopUtils.printGrouped(search);
      /* the variables are printed in groups based on their 
         values, which will be one of the years.
      */

  } /* main */


}  // end of HistoricHomes class
