diff --git a/src/wolfgoatcabbage/CP.java b/src/wolfgoatcabbage/CP.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e43d95a1fa8f79996fabaf6b0ae03a1ece1dada
--- /dev/null
+++ b/src/wolfgoatcabbage/CP.java
@@ -0,0 +1,153 @@
+package wolfgoatcabbage;
+
+import ilog.concert.IloConstraint;
+import ilog.concert.IloException;
+import ilog.concert.IloIntVar;
+import ilog.cp.IloCP;
+import static wolfgoatcabbage.Problem.CABBAGE;
+import static wolfgoatcabbage.Problem.GOAT;
+import static wolfgoatcabbage.Problem.ITEMCOUNT;
+import static wolfgoatcabbage.Problem.TMAX;
+import static wolfgoatcabbage.Problem.WOLF;
+
+/**
+ * CP implements a constraint programming model for the logic puzzle.
+ *
+ * @author Paul A. Rubin (rubin@msu.edu)
+ */
+public final class CP implements AutoCloseable {
+
+  /* CPO objects.
+   * For the variables `far` and `farmer`, value 1 signals the entity ends the
+   * period on the far bank while value 0 signals the entity ends the period
+   * on the near bank.
+   */
+  private final IloCP cp;            // the model instance
+  private final IloIntVar[][] far;   // far[t][i] is the location of item i
+                                     // at time t
+  private final IloIntVar[][] tFar;  // transpose of far
+  private final IloIntVar[] farmer;  // location of the farmer at time t
+  private final IloIntVar[] carry;   // carry[t] is the index of the item
+                                     // carried across the river at time t
+                                     // (ITEMCOUNT if the farmer is deadheading)
+  private final IloIntVar done;      // the time period at which the problem
+                                     // is done
+
+  /**
+   * Constructor.
+   * @throws IloException if the model cannot be created
+   */
+  public CP() throws IloException {
+    // Instantiate the model.
+    cp = new IloCP();
+    // Create the variables.
+    done = cp.intVar(0, TMAX - 1);
+    far = new IloIntVar[TMAX][ITEMCOUNT + 1];
+    tFar = new IloIntVar[ITEMCOUNT + 1][TMAX];
+    carry = new IloIntVar[TMAX];
+    farmer = new IloIntVar[TMAX];
+    for (int t = 0; t < TMAX; t++) {
+      carry[t] = cp.intVar(0, ITEMCOUNT);
+      farmer[t] = cp.boolVar();
+      for (int i = 0; i < ITEMCOUNT + 1; i++) {
+        far[t][i] = cp.boolVar();
+        tFar[i][t] = far[t][i];
+      }
+    }
+    // The objective is to minimize the time at which the trip is done.
+    cp.addMinimize(done);
+    // All inventory is on the near bank at the end of period 0.
+    for (int i = 0; i < ITEMCOUNT + 1; i++) {
+      cp.addEq(far[0][i], 0);
+    }
+    // The farmer also starts on the near bank.
+    cp.addEq(farmer[0], 0);
+    // Nothing is carried in period 0.
+    cp.addEq(carry[0], ITEMCOUNT);
+    // The necessary condition for being done is that all items be on the
+    // far bank.
+    for (int i = 0; i < ITEMCOUNT; i++) {
+      cp.addEq(cp.element(tFar[i], done), 1);
+    }
+    // In each period, the status of the item carried (if any) flips while
+    // the status of other items does not change.
+    for (int t = 1; t < TMAX; t++) {
+      for (int i = 0; i < ITEMCOUNT; i++) {
+        IloConstraint c1 = cp.eq(carry[t], i);
+        IloConstraint c2 = cp.eq(cp.sum(far[t - 1][i], far[t][i]), 1);
+        IloConstraint c3 = cp.eq(far[t - 1][i], far[t][i]);
+        cp.add(cp.ifThenElse(c1, c2, c3));
+      }
+    }
+    // In odd numbered periods, trips are from near bank to far bank, and only
+    // items on the near bank can be carried. In even numbered periods, trips
+    // are from far bank to near bank, and only items on the far bank can
+    // be carried.
+    for (int t = 1; t < TMAX; t++) {
+      int z = (t % 2 == 0) ? 1 : 0;
+      cp.addEq(cp.element(far[t - 1], carry[t]), z);
+    }
+    // Until the last trip, the farmer ends up on the far bank at the end of
+    // odd numbered periods and the near bank at the end of even numbered
+    // periods. After the last trip, the farmer stays on the far bank.
+    for (int t = 1; t < TMAX; t++) {
+      IloConstraint c1 = cp.le(done, t);
+      IloConstraint c2 = cp.eq(farmer[t], 1);
+      IloConstraint c3 = cp.eq(farmer[t], t % 2);
+      cp.add(cp.ifThenElse(c1, c2, c3));
+    }
+    // If the wolf and goat are in the same place at the end of any period,
+    // the farmer must also be there. The same is true if the goat and cabbage
+    // are in the same place.
+    for (int t = 1; t < TMAX; t++) {
+      IloConstraint c1 = cp.eq(far[t][WOLF], far[t][GOAT]);
+      IloConstraint c2 = cp.eq(far[t][GOAT], far[t][CABBAGE]);
+      IloConstraint c3 = cp.eq(far[t][GOAT], farmer[t]);
+      cp.add(cp.ifThen(c1, c3));
+      cp.add(cp.ifThen(c2, c3));
+    }
+    // Suppress solver output.
+    cp.setOut(null);
+  }
+
+  /**
+   * Solves the model.
+   * @return the number of trips required
+   * @throws IloException if the solver fails
+   */
+  public int solve() throws IloException {
+    cp.solve();
+    return cp.getIntValue(done);
+  }
+
+  /**
+   * Gets the solution to the puzzle.
+   * @return a string displaying the solution
+   * @throws IloException if the solution does not exist
+   */
+  public String getSolution() throws IloException {
+    int nTrips = cp.getIntValue(done);
+    double[][] x = new double[nTrips + 1][ITEMCOUNT];
+    // Get the values of the far[][] array for actual items and trips used.
+    for (int t = 0; t <= nTrips; t++) {
+      for (int i = 0; i < ITEMCOUNT; i++) {
+        x[t][i] = cp.getValue(far[t][i]);
+      }
+    }
+    // Get the values of the carry[] array for trips actually used.
+    double[] y = new double[nTrips + 1];
+    for (int t = 0; t <= nTrips; t++) {
+      y[t] = cp.getValue(carry[t]);
+    }
+    return Problem.report(nTrips, x, y);
+  }
+
+  /**
+   * Closes the model.
+   */
+  @Override
+  public void close() {
+    cp.end();
+  }
+
+}
diff --git a/src/wolfgoatcabbage/WolfGoatCabbage.java b/src/wolfgoatcabbage/WolfGoatCabbage.java
index 7f4e010ef4e07bcbc617b356340f9965659a129d..754f08fb188c27ece90ff2cb83a58acd1d20d969 100644
--- a/src/wolfgoatcabbage/WolfGoatCabbage.java
+++ b/src/wolfgoatcabbage/WolfGoatCabbage.java
@@ -18,6 +18,7 @@ public final class WolfGoatCabbage {
    * @param args the command line arguments (ignored)
    */
   public static void main(final String[] args) {
+    System.out.println("Applying the MIP model.");
     try (MIP model = new MIP()) {
       double z = model.solve();
       System.out.println("Optimal number of trips = " + z + ".");
@@ -26,6 +27,15 @@ public final class WolfGoatCabbage {
     } catch (IloException ex) {
       System.out.println("CPLEX exception:\n" + ex.getMessage());
     }
+    System.out.println("\nApplying the CP model:");
+    try (CP model = new CP()) {
+      int z = model.solve();
+      System.out.println("Optimal number of trips = " + z + ".");
+      System.out.println("Solution:");
+      System.out.println(model.getSolution());
+    } catch (IloException ex) {
+      System.out.println("CPOptimizer exception:\n" + ex.getMessage());
+    }
   }
 
 }