
// BabySitting.java
// Krzysztof Kuchcinski and Radoslaw Szymanek, 2008
// Slightly modified by Andrew Davison, ad@coe.psu.ac.th, Dec. 2022

/* A logic puzzle about babysitting.

   Each weekday, Bonnie takes care of five of the neighbors'
   children. The children's names are Keith, Libby, Margo, 
   Nora, and Otto; last names are Fell, Grant, Hall, Ivey, 
   and Jule. Each is a different number of years old, 
   from two to six. 

   Conditions:
   1. One child is named Libby Jule.

   2. Keith is one year older than the Ivey child, who is 
      one year older than Nora.

   3. The Fell child is three years older than Margo.

   4. Otto is twice as many years old as the Hall child.

  These conditions are labelled as S1..S4 in the code.

   Can you find each child's full firsts and age?

   Solution:
     Keith Fell, five
     Libby Jule, six
     Margo Hall, two
     Nora Grant, three
     Otto Ivey, four

  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 simpler since there are less constraints.

  Usage:
     > compile BabySitting.java
     > run BabySitting

     2 : [Hall, Margo]
     3 : [Grant, Nora]
     4 : [Ivey, Otto]
     5 : [Fell, Keith]
     6 : [Libby, Jule]
*/


import org.jacop.constraints.*;
import org.jacop.core.*;
import org.jacop.search.*;
  // uses Jacop: https://jacop.cs.lth.se/

import java.util.*;


public class BabySitting
{
  private static final int NUM_KIDS = 5;


  public static void main(String args[])
  {
    Store store = new Store();
    List<IntVar> vars = new ArrayList<>();

    // create constraint variables as two arrays
    IntVar lasts[] = new IntVar[NUM_KIDS];
    IntVar firsts[] = new IntVar[NUM_KIDS];

    // define labels for the arrays, and their index positions.
    /* This naming approach makes it much easier to understand
       the constraints representing the conditions */

    String[] lastNms = {"Fell", "Grant", "Hall", "Ivey", "Jule"};
    int ifell = 0, igrant = 1, ihall = 2, iivey = 3, ijule = 4;

    String[] firstNms = {"Keith", "Libby", "Margo", "Nora", "Otto"};
    int ikeith = 0, ilibby = 1, imargo = 2, inora = 3, iotto = 4;

    // use the labels as variable names in the arrays
    for (int i = 0; i < NUM_KIDS; i++) {
      lasts[i] = new IntVar(store, lastNms[i], 2, 6);  // domain is age
      firsts[i] = new IntVar(store, firstNms[i], 2, 6);
      vars.add(lasts[i]);
      vars.add(firsts[i]);
    }

    // Each person has a different last and first name
    store.impose(new Alldifferent(lasts));
    store.impose(new Alldifferent(firsts));

    // S1. One child is named Libby Jule
    store.impose(new XeqY(firsts[ilibby], lasts[ijule]));

    // S2. Keith is one year older than the Ivey child.....
    store.impose(new XplusCeqZ(lasts[iivey], 1, firsts[ikeith]));
    // ..... who is one year older than Nora
    store.impose(new XplusCeqZ(firsts[inora], 1, lasts[iivey]));

    // S3. The Fell child is three years older than Margo
    store.impose(new XplusCeqZ(firsts[imargo], 3, lasts[ifell]));

    // S4. Otto is twice as many years old as the Hall child
    store.impose(new XmulCeqZ(lasts[ihall], 2, firsts[iotto]));


    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<>();
    search.setPrintInfo(false);
    search.labeling(store, select);

    //JacopUtils.printSolns(search);
    System.out.println("Age : associated labels");
    JacopUtils.printGrouped(search);
      /* the variables are printed in groups based on their 
         values, which will be in the age range 2..6.
      */

  } /* main */

}  // end of BabySitting class
