Implemented initial meld test using new AI

git-svn-id: svn://sunsvr01.isp.uni-luebeck.de/swproj13/trunk@441 72836036-5685-4462-b002-a69064685172
This commit is contained in:
Jannis Harder 2011-06-14 21:05:15 +02:00
parent 751d5a3aa9
commit 3ffad85972
6 changed files with 164 additions and 26 deletions

View file

@ -32,11 +32,15 @@ public class TurnLogic {
private GameSettings settings; private GameSettings settings;
private int stoneCount; private int stoneCount;
private int maxSetSize; private int maxSetSize;
private int maxHandPoints;
private Var<Boolean> trueVar; private Var<Boolean> trueVar;
private Var<Integer> totalPoints;
private List<Var<Boolean>> onTable = new ArrayList<Var<Boolean>>(); 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>> stoneValue = new ArrayList<Var<Integer>>();
private List<Var<Integer>> leftStoneValue = 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<Integer>> rightStoneValue = new ArrayList<Var<Integer>>();
@ -76,6 +80,7 @@ public class TurnLogic {
trueVar = solver.makeVar(true); trueVar = solver.makeVar(true);
maxHandPoints = 0;
int i = 0; int i = 0;
for (Stone stone : tableStones) { for (Stone stone : tableStones) {
addStone(i, true, stone); addStone(i, true, stone);
@ -89,6 +94,19 @@ public class TurnLogic {
for (i = 0; i < stoneCount; i++) { for (i = 0; i < stoneCount; i++) {
addConstraints(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) { private void addStone(int i, boolean table, Stone stone) {
@ -100,9 +118,22 @@ public class TurnLogic {
isJoker.add(stone.isJoker()); isJoker.add(stone.isJoker());
if (stone.isJoker()) { if (stone.isJoker()) {
stoneValue.add(solver.makeRangeVar(1, settings.getHighestValue())); 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())); stoneColor.add(solver.makeVar(settings.getStoneColors()));
} else { } else {
stoneValue.add(solver.makeVar(stone.getValue())); 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())); stoneColor.add(solver.makeVar(stone.getColor()));
} }
@ -282,6 +313,13 @@ public class TurnLogic {
add(unless(onTable.get(i), add(unless(onTable.get(i),
constant(stoneColor.get(i), StoneColor.RED))); 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() { public boolean solve() {
@ -342,10 +380,12 @@ public class TurnLogic {
System.out.print("[" System.out.print("["
+ stoneColor.get(i).getValue().toString().substring(0, 1) + stoneColor.get(i).getValue().toString().substring(0, 1)
+ stoneValue.get(i).getValue() + "]"); + stoneValue.get(i).getValue() + "]");
// + "," + leftNeighbor.get(i).getValue() + /*
// hasLeftNeighbor.get(i).getValue() + lowCount.get(i).getValue() + "," * + "," + leftNeighbor.get(i).getValue() +
// + rightNeighbor.get(i).getValue() + * hasLeftNeighbor.get(i).getValue() + lowCount.get(i).getValue() + ","
// hasRightNeighbor.get(i).getValue() + highCount.get(i).getValue() + * + rightNeighbor.get(i).getValue() +
// "]"); * hasRightNeighbor.get(i).getValue() + highCount.get(i).getValue() +
* "]");
*/
} }
} }

View file

@ -7,6 +7,7 @@ import jrummikub.ai.fdsolver.constraint.FilterConstraint;
import jrummikub.ai.fdsolver.constraint.IfConstraint; import jrummikub.ai.fdsolver.constraint.IfConstraint;
import jrummikub.ai.fdsolver.constraint.IndexConstraint; import jrummikub.ai.fdsolver.constraint.IndexConstraint;
import jrummikub.ai.fdsolver.constraint.LessThan; import jrummikub.ai.fdsolver.constraint.LessThan;
import jrummikub.ai.fdsolver.constraint.ListSumConstraint;
import jrummikub.ai.fdsolver.constraint.OffsetConstraint; import jrummikub.ai.fdsolver.constraint.OffsetConstraint;
import jrummikub.ai.fdsolver.constraint.SameConstraint; import jrummikub.ai.fdsolver.constraint.SameConstraint;
import jrummikub.ai.fdsolver.constraint.SumConstraint; import jrummikub.ai.fdsolver.constraint.SumConstraint;
@ -46,6 +47,10 @@ public class Constraints {
return new SumConstraint(x, y, z); 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) { public static <T extends Comparable<T>> Constraint lessThan(Var<T> x, Var<T> y) {
return new LessThan<T>(false, x, y); return new LessThan<T>(false, x, y);
} }

View file

@ -42,6 +42,9 @@ public class Var<T> implements Comparable<Var<T>> {
range.remove(value); range.remove(value);
solver.logInvalidation(this, value); solver.logInvalidation(this, value);
makeDirty(); makeDirty();
if (range.size() == 0) {
solver.contradiction = true;
}
} }
HashSet<Constraint> getConstraints() { HashSet<Constraint> getConstraints() {

View file

@ -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;
}
}

View file

@ -1,12 +1,15 @@
package jrummikub.model; 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.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.TreeMap; import java.util.TreeMap;
import jrummikub.ai.TurnLogic;
import jrummikub.control.AIUtil; import jrummikub.control.AIUtil;
import jrummikub.util.Pair; import jrummikub.util.Pair;
@ -44,8 +47,8 @@ public class Hand extends StoneTray<Stone> implements IHand {
} }
@Override @Override
protected Pair<Position, Direction> fixInvalidDrop(Stone stone, Position pos, protected Pair<Position, Direction> fixInvalidDrop(Stone stone,
Direction dir) { Position pos, Direction dir) {
double x = pos.getX(); double x = pos.getX();
double y = pos.getY(); 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); return new Pair<Position, Direction>(new Position(0, y), RIGHT);
} else { } else {
if (getFreeRowSpace((int) y) == 0) { 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 { } 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 @Override
public boolean isInitialMeldPossible(GameSettings settings) { public boolean isInitialMeldPossible(GameSettings settings) {
AIUtil aiUtil = new AIUtil(settings); List<Stone> handStones = new ArrayList<Stone>();
List<Stone> stones = new ArrayList<Stone>(); for (Pair<Stone, Position> entry : this) {
handStones.add(entry.getFirst());
for (Iterator<Pair<Stone, Position>> iter = this.iterator(); iter.hasNext();) {
stones.add(iter.next().getFirst());
} }
Pair<TreeMap<Pair<Integer, StoneColor>, Integer>, Integer> stoneCounts = AIUtil TurnLogic turnLogic = new TurnLogic(settings,
.countStones(stones); Collections.<Stone> emptyList(), handStones);
return turnLogic.solve();
Pair<List<StoneSet>, Integer> result = aiUtil.findSetsWithTotalPoints(
settings.getInitialMeldThreshold(), stoneCounts.getFirst(),
stoneCounts.getSecond());
return (result.getSecond() >= settings.getInitialMeldThreshold());
} }
@Override @Override
public int getIdenticalStoneCount() { public int getIdenticalStoneCount() {
List<Stone> stones = new ArrayList<Stone>(); 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()); stones.add(iter.next().getFirst());
} }

View file

@ -221,7 +221,7 @@ public class HandTest {
} }
/** */ /** */
@Test @Test(timeout = 1000)
public void testValidHuge() { public void testValidHuge() {
List<Stone> stones = new ArrayList<Stone>(); List<Stone> stones = new ArrayList<Stone>();
for (int i = 1; i <= 10; i++) { for (int i = 1; i <= 10; i++) {
@ -232,7 +232,7 @@ public class HandTest {
} }
/** */ /** */
@Test @Test(timeout = 1000)
public void testInvalidHuge() { public void testInvalidHuge() {
testInitialMeld(false, Arrays.asList(new Stone(1, RED), new Stone(2, RED), 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, new Stone(4, RED), new Stone(6, RED), new Stone(8, RED), new Stone(10,