diff options
Diffstat (limited to 'src/jrummikub/ai/TurnLogic.java')
-rw-r--r-- | src/jrummikub/ai/TurnLogic.java | 345 |
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() + + // "]"); + } +} |