diff options
-rw-r--r-- | src/jrummikub/ai/TurnLogic.java | 50 | ||||
-rw-r--r-- | src/jrummikub/ai/fdsolver/Constraints.java | 5 | ||||
-rw-r--r-- | src/jrummikub/ai/fdsolver/Var.java | 3 | ||||
-rw-r--r-- | src/jrummikub/ai/fdsolver/constraint/ListSumConstraint.java | 91 | ||||
-rw-r--r-- | src/jrummikub/model/Hand.java | 37 | ||||
-rw-r--r-- | test/jrummikub/model/HandTest.java | 4 |
6 files changed, 164 insertions, 26 deletions
diff --git a/src/jrummikub/ai/TurnLogic.java b/src/jrummikub/ai/TurnLogic.java index 1ce752c..e736361 100644 --- a/src/jrummikub/ai/TurnLogic.java +++ b/src/jrummikub/ai/TurnLogic.java @@ -32,11 +32,15 @@ public class TurnLogic { private GameSettings settings; private int stoneCount; private int maxSetSize; + private int maxHandPoints; private Var<Boolean> trueVar; + private Var<Integer> totalPoints; private List<Var<Boolean>> onTable = new ArrayList<Var<Boolean>>(); + private List<Var<Integer>> pointsValue = new ArrayList<Var<Integer>>(); + 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>>(); @@ -76,6 +80,7 @@ public class TurnLogic { trueVar = solver.makeVar(true); + maxHandPoints = 0; int i = 0; for (Stone stone : tableStones) { addStone(i, true, stone); @@ -89,6 +94,19 @@ public class TurnLogic { for (i = 0; i < stoneCount; i++) { addConstraints(i); } + + totalPoints = solver.makeRangeVar(settings.getInitialMeldThreshold(), + maxHandPoints); + + List<Var<Integer>> points = new ArrayList<Var<Integer>>(); + + for (Var<Integer> var : pointsValue) { + if (var != null) { + points.add(var); + } + } + + add(sum(totalPoints, points)); } private void addStone(int i, boolean table, Stone stone) { @@ -100,9 +118,22 @@ public class TurnLogic { isJoker.add(stone.isJoker()); if (stone.isJoker()) { stoneValue.add(solver.makeRangeVar(1, settings.getHighestValue())); + if (table) { + pointsValue.add(null); + } else { + pointsValue.add(solver.makeRangeVar(0, + settings.getHighestValue())); + maxHandPoints += settings.getHighestValue(); + } stoneColor.add(solver.makeVar(settings.getStoneColors())); } else { stoneValue.add(solver.makeVar(stone.getValue())); + if (table) { + pointsValue.add(null); + } else { + pointsValue.add(solver.makeVar(0, stone.getValue())); + maxHandPoints += stone.getValue(); + } stoneColor.add(solver.makeVar(stone.getColor())); } @@ -282,6 +313,13 @@ public class TurnLogic { add(unless(onTable.get(i), constant(stoneColor.get(i), StoneColor.RED))); } + + // initial meld points + if (pointsValue.get(i) != null) { + add(when(onTable.get(i), + same(stoneValue.get(i), pointsValue.get(i)))); + add(unless(onTable.get(i), constant(pointsValue.get(i), 0))); + } } public boolean solve() { @@ -342,10 +380,12 @@ public class TurnLogic { 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() + - // "]"); + /* + * + "," + leftNeighbor.get(i).getValue() + + * hasLeftNeighbor.get(i).getValue() + lowCount.get(i).getValue() + "," + * + rightNeighbor.get(i).getValue() + + * hasRightNeighbor.get(i).getValue() + highCount.get(i).getValue() + + * "]"); + */ } } diff --git a/src/jrummikub/ai/fdsolver/Constraints.java b/src/jrummikub/ai/fdsolver/Constraints.java index 0885d2a..caa8cfa 100644 --- a/src/jrummikub/ai/fdsolver/Constraints.java +++ b/src/jrummikub/ai/fdsolver/Constraints.java @@ -7,6 +7,7 @@ import jrummikub.ai.fdsolver.constraint.FilterConstraint; import jrummikub.ai.fdsolver.constraint.IfConstraint; import jrummikub.ai.fdsolver.constraint.IndexConstraint; import jrummikub.ai.fdsolver.constraint.LessThan; +import jrummikub.ai.fdsolver.constraint.ListSumConstraint; import jrummikub.ai.fdsolver.constraint.OffsetConstraint; import jrummikub.ai.fdsolver.constraint.SameConstraint; import jrummikub.ai.fdsolver.constraint.SumConstraint; @@ -46,6 +47,10 @@ public class Constraints { return new SumConstraint(x, y, z); } + public static Constraint sum(Var<Integer> sum, List<Var<Integer>> list) { + return new ListSumConstraint(sum, list); + } + public static <T extends Comparable<T>> Constraint lessThan(Var<T> x, Var<T> y) { return new LessThan<T>(false, x, y); } diff --git a/src/jrummikub/ai/fdsolver/Var.java b/src/jrummikub/ai/fdsolver/Var.java index a454487..e885cef 100644 --- a/src/jrummikub/ai/fdsolver/Var.java +++ b/src/jrummikub/ai/fdsolver/Var.java @@ -42,6 +42,9 @@ public class Var<T> implements Comparable<Var<T>> { range.remove(value); solver.logInvalidation(this, value); makeDirty(); + if (range.size() == 0) { + solver.contradiction = true; + } } HashSet<Constraint> getConstraints() { diff --git a/src/jrummikub/ai/fdsolver/constraint/ListSumConstraint.java b/src/jrummikub/ai/fdsolver/constraint/ListSumConstraint.java new file mode 100644 index 0000000..22f480f --- /dev/null +++ b/src/jrummikub/ai/fdsolver/constraint/ListSumConstraint.java @@ -0,0 +1,91 @@ +package jrummikub.ai.fdsolver.constraint; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import jrummikub.ai.fdsolver.Constraint; +import jrummikub.ai.fdsolver.Propagator; +import jrummikub.ai.fdsolver.Satisfiability; +import jrummikub.ai.fdsolver.Var; + +public class ListSumConstraint extends Constraint { + Var<Integer> sum; + List<Var<Integer>> list; + List<Var<?>> vars = new ArrayList<Var<?>>(); + + public ListSumConstraint(Var<Integer> sum, List<Var<Integer>> list) { + this.sum = sum; + this.list = list; + vars.add(sum); + vars.addAll(list); + } + + @Override + public Collection<Var<?>> getWatchedVars() { + return vars; + } + + @Override + public Collection<Propagator> getPropagators(boolean negate) { + return Collections.emptyList(); + } + + @Override + public Satisfiability getSatisfiability() { + List<Integer> values = new ArrayList<Integer>(); + int valueSum = 0; + boolean isTaut = sum.getRange().size()==1; + for (Var<Integer> var : list) { + Iterator<Integer> it = var.getRange().iterator(); + int value = it.next(); + if (it.hasNext()) + isTaut = false; + values.add(value); + valueSum += value; + } + if (isTaut) { + if (valueSum == sum.getValue()) { + return Satisfiability.TAUT; + } else { + return Satisfiability.UNSAT; + } + } + + Set<Integer> reachableValues = new HashSet<Integer>(); + Set<Integer> newValues = new HashSet<Integer>(); + reachableValues.add(valueSum); + + if (sum.getRange().contains(valueSum)) { + return Satisfiability.SAT; + } + + for (int i = 0; i < values.size(); i++) { + Var<Integer> var = list.get(i); + int offset = values.get(i); + for (int x : var.getRange()) { + if (x == offset) + continue; + newValues.clear(); + for (int y : reachableValues) { + int newValue = x + y - offset; + if (!reachableValues.contains(newValue)) { + newValues.add(x + y - offset); + } + } + if (!Collections.disjoint(sum.getRange(), newValues)) { + return Satisfiability.SAT; + } + reachableValues.addAll(newValues); + } + } + + return Satisfiability.UNSAT; + } + +} diff --git a/src/jrummikub/model/Hand.java b/src/jrummikub/model/Hand.java index 681241a..c656afd 100644 --- a/src/jrummikub/model/Hand.java +++ b/src/jrummikub/model/Hand.java @@ -1,12 +1,15 @@ package jrummikub.model; -import static jrummikub.model.StoneTray.Direction.*; +import static jrummikub.model.StoneTray.Direction.LEFT; +import static jrummikub.model.StoneTray.Direction.RIGHT; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.TreeMap; +import jrummikub.ai.TurnLogic; import jrummikub.control.AIUtil; import jrummikub.util.Pair; @@ -44,8 +47,8 @@ public class Hand extends StoneTray<Stone> implements IHand { } @Override - protected Pair<Position, Direction> fixInvalidDrop(Stone stone, Position pos, - Direction dir) { + protected Pair<Position, Direction> fixInvalidDrop(Stone stone, + Position pos, Direction dir) { double x = pos.getX(); double y = pos.getY(); @@ -56,9 +59,11 @@ public class Hand extends StoneTray<Stone> implements IHand { return new Pair<Position, Direction>(new Position(0, y), RIGHT); } else { if (getFreeRowSpace((int) y) == 0) { - return new Pair<Position, Direction>(new Position(0, y + 1), RIGHT); + return new Pair<Position, Direction>(new Position(0, y + 1), + RIGHT); } else { - return new Pair<Position, Direction>(new Position(WIDTH - 1, y), LEFT); + return new Pair<Position, Direction>( + new Position(WIDTH - 1, y), LEFT); } } } @@ -80,29 +85,23 @@ public class Hand extends StoneTray<Stone> implements IHand { @Override public boolean isInitialMeldPossible(GameSettings settings) { - AIUtil aiUtil = new AIUtil(settings); + List<Stone> handStones = new ArrayList<Stone>(); - List<Stone> stones = new ArrayList<Stone>(); - - for (Iterator<Pair<Stone, Position>> iter = this.iterator(); iter.hasNext();) { - stones.add(iter.next().getFirst()); + for (Pair<Stone, Position> entry : this) { + handStones.add(entry.getFirst()); } - Pair<TreeMap<Pair<Integer, StoneColor>, Integer>, Integer> stoneCounts = AIUtil - .countStones(stones); - - Pair<List<StoneSet>, Integer> result = aiUtil.findSetsWithTotalPoints( - settings.getInitialMeldThreshold(), stoneCounts.getFirst(), - stoneCounts.getSecond()); - - return (result.getSecond() >= settings.getInitialMeldThreshold()); + TurnLogic turnLogic = new TurnLogic(settings, + Collections.<Stone> emptyList(), handStones); + return turnLogic.solve(); } @Override public int getIdenticalStoneCount() { List<Stone> stones = new ArrayList<Stone>(); - for (Iterator<Pair<Stone, Position>> iter = this.iterator(); iter.hasNext();) { + for (Iterator<Pair<Stone, Position>> iter = this.iterator(); iter + .hasNext();) { stones.add(iter.next().getFirst()); } diff --git a/test/jrummikub/model/HandTest.java b/test/jrummikub/model/HandTest.java index fa780a5..18db753 100644 --- a/test/jrummikub/model/HandTest.java +++ b/test/jrummikub/model/HandTest.java @@ -221,7 +221,7 @@ public class HandTest { }
/** */
- @Test
+ @Test(timeout = 1000)
public void testValidHuge() {
List<Stone> stones = new ArrayList<Stone>();
for (int i = 1; i <= 10; i++) {
@@ -232,7 +232,7 @@ public class HandTest { }
/** */
- @Test
+ @Test(timeout = 1000)
public void testInvalidHuge() {
testInitialMeld(false, Arrays.asList(new Stone(1, RED), new Stone(2, RED),
new Stone(4, RED), new Stone(6, RED), new Stone(8, RED), new Stone(10,
|