3636import org .chocosolver .solver .Settings ;
3737import org .chocosolver .solver .Solution ;
3838import org .chocosolver .solver .Solver ;
39+ import org .chocosolver .solver .variables .IntVar ;
3940
4041import lombok .AllArgsConstructor ;
4142import lombok .Getter ;
@@ -59,10 +60,15 @@ public class PuzzleSolver {
5960 private Settings chocoSettings ;
6061
6162 /**
62- * @param zebraModel
63- * @return a list of choco-solver solutions; useful to count solutions
63+ * Solve the puzzle returning a lazy stream to all solutions in a raw form,
64+ * close to the underlying Choco solver model
65+ *
66+ * @return a list of raw solutions - map from {@link Attribute} to person ID;
67+ * useful when just counting solutions
6468 */
65- Stream <Map <Attribute , Integer >> solveChoco (ZebraModel zebraModel ) {
69+ Stream <Map <Attribute , Integer >> solveChoco () {
70+ ZebraModel zebraModel = toModel ();
71+ log .debug ("Solving puzzle {}" , puzzle );
6672 for (Fact fact : puzzle .getFacts ()) {
6773 fact .postTo (zebraModel );
6874 }
@@ -78,27 +84,38 @@ public boolean tryAdvance(Consumer<? super Map<Attribute, Integer>> action) {
7884 return false ;
7985 }
8086 solution .record ();
81- action .accept (zebraModel .getVariableMap ().entrySet ().stream ()
87+ // We extract the values of only the relevant variables. Returning a copy of the
88+ // complete solution is not efficient because the solution contains much more
89+ // internal variables and data.
90+ // For question puzzles, this can be further improved by extracting only the
91+ // attributes for towards and about, but the optimization is currently not worth
92+ // the added complexity.
93+ Set <Entry <Attribute , IntVar >> variables = zebraModel .getVariableMap ().entrySet ();
94+ action .accept (variables .stream ()
8295 .collect (Collectors .toMap (e -> e .getKey (), e -> solution .getIntVal (e .getValue ()))));
8396 return true ;
8497 }
8598 };
8699 return StreamSupport .stream (spliterator , false );
87100 }
88101
102+ /**
103+ * Solves the puzzles eagerly.
104+ *
105+ * @return a list of all distinct solution.
106+ */
89107 public List <PuzzleSolution > solve () {
90- log .debug ("Solving puzzle {}" , puzzle );
91108 List <PuzzleSolution > zebraSolutions = solveToStream ().distinct ().collect (Collectors .toList ());
92109 log .trace ("Found {} distinct solutions" , zebraSolutions .size ());
93110 return zebraSolutions ;
94111 }
95112
113+ /**
114+ * @return a lazy stream of all solutions with possible duplicates
115+ */
96116 public Stream <PuzzleSolution > solveToStream () {
97- ZebraModel zebraModel = toModel ();
98- Stream <Map <Attribute , Integer >> solutions = solveChoco (zebraModel );
99- // 16% of runtime is here
100- Stream <PuzzleSolution > stream = solutions .map (choco -> fromChocoSolution (choco ));
101- return stream ;
117+ // 16% of the typical CPU cost of this method is in the map.
118+ return solveChoco ().map (this ::fromChocoSolution );
102119 }
103120
104121 private PuzzleSolution fromChocoSolution (Map <Attribute , Integer > choco ) {
@@ -114,7 +131,7 @@ private PuzzleSolution fromChocoSolution(Map<Attribute, Integer> choco) {
114131 return builder .build ();
115132 }
116133
117- ZebraModel toModel () {
134+ private ZebraModel toModel () {
118135 ZebraModel model = new ZebraModel (chocoSettings );
119136 for (Entry <AttributeType , Set <Attribute >> entry : puzzle .getAttributeSets ().entrySet ()) {
120137 entry .getKey ().addToModel (model , entry .getValue (), puzzle .numPeople ());
0 commit comments