summaryrefslogtreecommitdiffstats
path: root/src/jrummikub/ai/TurnLogic.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jrummikub/ai/TurnLogic.java')
-rw-r--r--src/jrummikub/ai/TurnLogic.java345
1 files changed, 345 insertions, 0 deletions
diff --git a/src/jrummikub/ai/TurnLogic.java b/src/jrummikub/ai/TurnLogic.java
new file mode 100644
index 0000000..dcb95fc
--- /dev/null
+++ b/src/jrummikub/ai/TurnLogic.java
@@ -0,0 +1,345 @@
+package jrummikub.ai;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import jrummikub.ai.fdsolver.Constraint;
+import jrummikub.ai.fdsolver.Solver;
+import jrummikub.ai.fdsolver.Var;
+import jrummikub.ai.fdsolver.constraint.IfConstraint;
+import jrummikub.ai.fdsolver.constraint.IndexConstraint;
+import jrummikub.ai.fdsolver.constraint.LessThan;
+import jrummikub.model.GameSettings;
+import jrummikub.model.Stone;
+import jrummikub.model.StoneColor;
+import static jrummikub.ai.fdsolver.Constraints.*;
+
+/**
+ * Provides Humanlike Advanced Logic (HAL) that models the options given to a
+ * player in a turn to the Solver MCP.
+ */
+public class TurnLogic {
+ private Solver solver = new Solver();
+ private GameSettings settings;
+ private int stoneCount;
+ private int maxSetSize;
+
+ private Var<Boolean> trueVar;
+
+ private List<Var<Boolean>> onTable = new ArrayList<Var<Boolean>>();
+
+ private List<Var<Integer>> stoneValue = new ArrayList<Var<Integer>>();
+ private List<Var<Integer>> leftStoneValue = new ArrayList<Var<Integer>>();
+ private List<Var<Integer>> rightStoneValue = new ArrayList<Var<Integer>>();
+
+ private List<Var<StoneColor>> stoneColor = new ArrayList<Var<StoneColor>>();
+ private List<Var<StoneColor>> leftStoneColor = new ArrayList<Var<StoneColor>>();
+ private List<Var<StoneColor>> rightStoneColor = new ArrayList<Var<StoneColor>>();
+
+ private List<Var<Integer>> lowCount = new ArrayList<Var<Integer>>();
+ private List<Var<Integer>> leftLowCount = new ArrayList<Var<Integer>>();
+ private List<Var<Integer>> rightLowCount = new ArrayList<Var<Integer>>();
+
+ private List<Var<Integer>> highCount = new ArrayList<Var<Integer>>();
+ private List<Var<Integer>> leftHighCount = new ArrayList<Var<Integer>>();
+ private List<Var<Integer>> rightHighCount = new ArrayList<Var<Integer>>();
+
+ private List<Var<Integer>> setSize = new ArrayList<Var<Integer>>();
+
+ private List<Var<Boolean>> isRun = new ArrayList<Var<Boolean>>();
+
+ private List<Var<Integer>> leftNeighbor = new ArrayList<Var<Integer>>();
+ private List<Var<Integer>> rightNeighbor = new ArrayList<Var<Integer>>();
+
+ private List<Var<Integer>> stoneID = new ArrayList<Var<Integer>>();
+
+ private List<Var<Boolean>> hasLeftNeighbor = new ArrayList<Var<Boolean>>();
+ private List<Var<Boolean>> hasRightNeighbor = new ArrayList<Var<Boolean>>();
+
+ private List<Boolean> isJoker = new ArrayList<Boolean>();
+
+ public TurnLogic(GameSettings settings, Collection<Stone> tableStones,
+ Collection<Stone> handStones) {
+ this.settings = settings;
+ stoneCount = tableStones.size() + handStones.size();
+ maxSetSize = Math.max(settings.getHighestValue(), settings
+ .getStoneColors().size());
+
+ trueVar = solver.makeVar(true);
+
+ int i = 0;
+ for (Stone stone : tableStones) {
+ addStone(i, true, stone);
+ i++;
+ }
+ for (Stone stone : handStones) {
+ addStone(i, false, stone);
+ i++;
+ }
+
+ for (i = 0; i < stoneCount; i++) {
+ addConstraints(i);
+ }
+ }
+
+ private void addStone(int i, boolean table, Stone stone) {
+ if (table) {
+ onTable.add(trueVar);
+ } else {
+ onTable.add(solver.makeBoolVar());
+ }
+ isJoker.add(stone.isJoker());
+ if (stone.isJoker()) {
+ stoneValue.add(solver.makeRangeVar(1, settings.getHighestValue()));
+ stoneColor.add(solver.makeVar(settings.getStoneColors()));
+ } else {
+ stoneValue.add(solver.makeVar(stone.getValue()));
+ stoneColor.add(solver.makeVar(stone.getColor()));
+ }
+
+ leftStoneValue.add(solver.makeRangeVar(1, settings.getHighestValue()));
+ rightStoneValue.add(solver.makeRangeVar(1, settings.getHighestValue()));
+ leftStoneColor.add(solver.makeVar(settings.getStoneColors()));
+ rightStoneColor.add(solver.makeVar(settings.getStoneColors()));
+
+ lowCount.add(solver.makeRangeVar(0, maxSetSize - 1));
+ leftLowCount.add(solver.makeRangeVar(0, maxSetSize - 1));
+ rightLowCount.add(solver.makeRangeVar(0, maxSetSize - 1));
+
+ highCount.add(solver.makeRangeVar(0, maxSetSize - 1));
+ leftHighCount.add(solver.makeRangeVar(0, maxSetSize - 1));
+ rightHighCount.add(solver.makeRangeVar(0, maxSetSize - 1));
+
+ setSize.add(solver.makeRangeVar(2, maxSetSize - 1));
+
+ isRun.add(solver.makeBoolVar());
+
+ leftNeighbor.add(solver.makeRangeVar(0, stoneCount - 1));
+ rightNeighbor.add(solver.makeRangeVar(0, stoneCount - 1));
+
+ stoneID.add(solver.makeVar(i));
+
+ hasLeftNeighbor.add(solver.makeBoolVar());
+ hasRightNeighbor.add(solver.makeBoolVar());
+ }
+
+ private void add(Constraint c) {
+ solver.addConstraint(c);
+ }
+
+ private void addConstraints(int i) {
+ // Linking of neighbors
+ add(when(hasLeftNeighbor.get(i),
+ index(stoneID.get(i), leftNeighbor.get(i), rightNeighbor)));
+ add(unless(hasLeftNeighbor.get(i), constant(leftNeighbor.get(i), 0)));
+ add(when(hasLeftNeighbor.get(i),
+ index(trueVar, leftNeighbor.get(i), hasRightNeighbor)));
+ add(when(hasRightNeighbor.get(i),
+ index(stoneID.get(i), rightNeighbor.get(i), leftNeighbor)));
+ add(unless(hasRightNeighbor.get(i), constant(rightNeighbor.get(i), 0)));
+ add(when(hasRightNeighbor.get(i),
+ index(trueVar, rightNeighbor.get(i), hasLeftNeighbor)));
+ // hand stones have no neighbors
+ add(unless(onTable.get(i), constant(hasLeftNeighbor.get(i), false)));
+ add(unless(onTable.get(i), constant(hasRightNeighbor.get(i), false)));
+
+ // low/high counts and size
+ add(unless(hasLeftNeighbor.get(i), constant(lowCount.get(i), 0)));
+ add(unless(hasRightNeighbor.get(i), constant(highCount.get(i), 0)));
+
+ add(when(hasLeftNeighbor.get(i),
+ index(leftLowCount.get(i), leftNeighbor.get(i), lowCount)));
+ add(unless(hasLeftNeighbor.get(i), constant(leftLowCount.get(i), 0)));
+
+ add(when(hasRightNeighbor.get(i),
+ index(rightLowCount.get(i), rightNeighbor.get(i), lowCount)));
+ add(unless(hasRightNeighbor.get(i), constant(rightLowCount.get(i), 0)));
+
+ add(when(hasLeftNeighbor.get(i),
+ index(leftHighCount.get(i), leftNeighbor.get(i), highCount)));
+ add(unless(hasLeftNeighbor.get(i), constant(leftHighCount.get(i), 0)));
+
+ add(when(hasRightNeighbor.get(i),
+ index(rightHighCount.get(i), rightNeighbor.get(i), highCount)));
+ add(unless(hasRightNeighbor.get(i), constant(rightHighCount.get(i), 0)));
+
+ add(when(hasLeftNeighbor.get(i),
+ index(setSize.get(i), leftNeighbor.get(i), setSize)));
+
+ add(when(hasRightNeighbor.get(i),
+ index(setSize.get(i), rightNeighbor.get(i), setSize)));
+
+ add(when(hasLeftNeighbor.get(i),
+ offset(1, leftLowCount.get(i), lowCount.get(i))));
+ add(when(hasRightNeighbor.get(i),
+ offset(1, lowCount.get(i), rightLowCount.get(i))));
+
+ add(when(hasRightNeighbor.get(i),
+ offset(1, rightHighCount.get(i), highCount.get(i))));
+ add(when(hasLeftNeighbor.get(i),
+ offset(1, highCount.get(i), leftHighCount.get(i))));
+
+ add(when(onTable.get(i),
+ sum(lowCount.get(i), highCount.get(i), setSize.get(i))));
+
+ add(unless(onTable.get(i), constant(setSize.get(i), 2)));
+
+ // set same rules
+ add(when(hasLeftNeighbor.get(i),
+ index(isRun.get(i), leftNeighbor.get(i), isRun)));
+ add(when(hasRightNeighbor.get(i),
+ index(isRun.get(i), rightNeighbor.get(i), isRun)));
+
+ add(unless(onTable.get(i), constant(isRun.get(i), false)));
+
+ // rule neighbors
+ add(when(hasLeftNeighbor.get(i),
+ index(leftStoneColor.get(i), leftNeighbor.get(i), stoneColor)));
+ add(unless(hasLeftNeighbor.get(i),
+ constant(leftStoneColor.get(i), StoneColor.RED)));
+ add(when(hasRightNeighbor.get(i),
+ index(rightStoneColor.get(i), rightNeighbor.get(i), stoneColor)));
+ add(unless(hasRightNeighbor.get(i),
+ constant(rightStoneColor.get(i), StoneColor.RED)));
+
+ add(when(hasLeftNeighbor.get(i),
+ index(leftStoneValue.get(i), leftNeighbor.get(i), stoneValue)));
+ add(unless(hasLeftNeighbor.get(i), constant(leftStoneValue.get(i), 1)));
+ add(when(hasRightNeighbor.get(i),
+ index(rightStoneValue.get(i), rightNeighbor.get(i), stoneValue)));
+ add(unless(hasRightNeighbor.get(i), constant(rightStoneValue.get(i), 1)));
+ // general rules
+
+ add(when(
+ hasLeftNeighbor.get(i),
+ when(isRun.get(i),
+ lessThanEq(leftStoneValue.get(i), stoneValue.get(i)))));
+ add(when(
+ hasRightNeighbor.get(i),
+ when(isRun.get(i),
+ lessThanEq(stoneValue.get(i), rightStoneValue.get(i)))));
+
+ add(when(
+ hasLeftNeighbor.get(i),
+ when(isRun.get(i),
+ lessThanEq(leftStoneColor.get(i), stoneColor.get(i)))));
+ add(when(
+ hasRightNeighbor.get(i),
+ when(isRun.get(i),
+ lessThanEq(stoneColor.get(i), rightStoneColor.get(i)))));
+
+ // run rules
+
+ add(when(
+ hasLeftNeighbor.get(i),
+ when(isRun.get(i),
+ offset(1, leftStoneValue.get(i), stoneValue.get(i)))));
+ add(when(
+ hasRightNeighbor.get(i),
+ when(isRun.get(i),
+ offset(1, stoneValue.get(i), rightStoneValue.get(i)))));
+
+ add(when(
+ hasLeftNeighbor.get(i),
+ when(isRun.get(i),
+ same(leftStoneColor.get(i), stoneColor.get(i)))));
+ add(when(
+ hasRightNeighbor.get(i),
+ when(isRun.get(i),
+ same(stoneColor.get(i), rightStoneColor.get(i)))));
+
+ // group rules
+ add(when(
+ hasLeftNeighbor.get(i),
+ unless(isRun.get(i),
+ same(leftStoneValue.get(i), stoneValue.get(i)))));
+ add(when(
+ hasRightNeighbor.get(i),
+ unless(isRun.get(i),
+ same(stoneValue.get(i), rightStoneValue.get(i)))));
+
+ add(when(
+ hasLeftNeighbor.get(i),
+ unless(isRun.get(i),
+ lessThan(leftStoneColor.get(i), stoneColor.get(i)))));
+ add(when(
+ hasRightNeighbor.get(i),
+ unless(isRun.get(i),
+ lessThan(stoneColor.get(i), rightStoneColor.get(i)))));
+
+ // joker defaulting
+ if (isJoker.get(i)) {
+ add(unless(onTable.get(i), constant(stoneValue.get(i), 1)));
+ add(unless(onTable.get(i),
+ constant(stoneColor.get(i), StoneColor.RED)));
+ }
+ }
+
+ public boolean solve() {
+ Set<Integer> tableIDs = new HashSet<Integer>();
+ Set<Integer> leftIDs = new HashSet<Integer>();
+ boolean res = solver.solve();
+ if (!res)
+ return false;
+ System.out.println("== Hand ==");
+ for (int i = 0; i < stoneCount; i++) {
+ if (onTable.get(i).getValue()) {
+ tableIDs.add(i);
+ if (!hasLeftNeighbor.get(i).getValue()) {
+ leftIDs.add(i);
+ }
+ } else {
+ outputStone(i);
+ }
+ }
+ System.out.println("");
+ System.out.println("== Table ==");
+ boolean newLine = false;
+ boolean first = true;
+ int nextID = -1; // leftIDs.iterator().next();
+ while (!tableIDs.isEmpty()) {
+ if (tableIDs.contains(nextID)) {
+ tableIDs.remove(nextID);
+ leftIDs.remove(nextID);
+ if (newLine) {
+ if (!first) {
+ System.out.println("");
+ }
+ first = false;
+ newLine = false;
+ }
+ outputStone(nextID);
+ if (!hasRightNeighbor.get(nextID).getValue()) {
+ newLine = true;
+ nextID = -1;
+ } else {
+ nextID = rightNeighbor.get(nextID).getValue();
+ }
+ } else {
+ if (leftIDs.isEmpty()) {
+ nextID = tableIDs.iterator().next();
+ } else {
+ nextID = leftIDs.iterator().next();
+ }
+ newLine = true;
+ }
+ }
+ System.out.println("");
+ System.out.println("");
+ return res;
+ }
+
+ private void outputStone(int i) {
+ System.out.print("["
+ + stoneColor.get(i).getValue().toString().substring(0, 1)
+ + stoneValue.get(i).getValue() + "]");
+ // + "," + leftNeighbor.get(i).getValue() +
+ // hasLeftNeighbor.get(i).getValue() + lowCount.get(i).getValue() + ","
+ // + rightNeighbor.get(i).getValue() +
+ // hasRightNeighbor.get(i).getValue() + highCount.get(i).getValue() +
+ // "]");
+ }
+}