Improved AI

git-svn-id: svn://sunsvr01.isp.uni-luebeck.de/swproj13/trunk@514 72836036-5685-4462-b002-a69064685172
This commit is contained in:
Jannis Harder 2011-06-20 23:36:26 +02:00
parent 2d08646187
commit a33b0a22ac
2 changed files with 219 additions and 57 deletions

View file

@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import jrummikub.model.GameSettings; import jrummikub.model.GameSettings;
@ -64,15 +65,20 @@ public class TurnLogic {
} }
public void updateStones() throws Contradiction { public void updateStones() throws Contradiction {
checkJokers(); checkJokerCount();
for (int i : changedStones) { for (int i : changedStones) {
stones.get(i).checkState(); stones.get(i).checkState();
} }
checkScoreAndPoints(); checkScoreAndPoints();
while (!changedStones.isEmpty()) {
while (!changedStones.isEmpty()) { while (!changedStones.isEmpty()) {
updateStonesStep(); updateStonesStep();
checkScoreAndPoints(); checkScoreAndPoints();
} }
checkJokerCount();
checkScoreAndPoints();
}
} }
public void updateStonesStep() throws Contradiction { public void updateStonesStep() throws Contradiction {
@ -97,33 +103,20 @@ public class TurnLogic {
return true; return true;
} }
public void checkJokers() throws Contradiction { private void checkJokerCount() throws Contradiction {
for (int i = 0; i < jokerIDs.size(); i++) { int jokersLeft = jokerIDs.size();
StoneState left = stones.get(jokerIDs.get(i)); int jokersRight = jokerIDs.size();
for (StoneState stone : stones) {
HashSet<Integer> leftLeftGroup = new HashSet<Integer>(left.leftGroup); if (stone.needsJoker(true)) {
HashSet<Integer> leftLeftRun = new HashSet<Integer>(left.leftRun); jokersLeft--;
leftLeftGroup.remove(null); if (jokersLeft < 0) {
leftLeftRun.remove(null); throw new Contradiction();
int runID, groupID;
if (leftLeftGroup.isEmpty()) {
groupID = -1;
} else {
groupID = Collections.min(leftLeftGroup);
} }
if (leftLeftRun.isEmpty()) {
runID = -1;
} else {
runID = Collections.min(leftLeftRun);
} }
for (int j = i + 1; j < jokerIDs.size(); j++) { if (stone.needsJoker(false)) {
StoneState right = stones.get(jokerIDs.get(j)); jokersRight--;
if (jokersRight < 0) {
if (right.leftGroup.remove(groupID)) { throw new Contradiction();
changedStones.add(jokerIDs.get(j));
}
if (right.leftRun.remove(runID)) {
changedStones.add(jokerIDs.get(j));
} }
} }
} }
@ -214,8 +207,8 @@ public class TurnLogic {
public boolean isInterested(HashSet<Integer> changes) { public boolean isInterested(HashSet<Integer> changes) {
return !(Collections.disjoint(changes, leftRun) return !(Collections.disjoint(changes, leftRun)
&& Collections.disjoint(changes, rightRun) && Collections.disjoint(changes, rightRun)
&& Collections.disjoint(changes, leftGroup) && Collections.disjoint( && Collections.disjoint(changes, leftGroup) && Collections
changes, rightGroup)); .disjoint(changes, rightGroup));
} }
public <T extends Comparable<T>> boolean lessThan(T a, T b) { public <T extends Comparable<T>> boolean lessThan(T a, T b) {
@ -265,14 +258,85 @@ public class TurnLogic {
public boolean update(HashSet<Integer> changes) throws Contradiction { public boolean update(HashSet<Integer> changes) throws Contradiction {
boolean changed = false; boolean changed = false;
changed |= updateRightRuns(changes); changed |= updateRightRuns(changes);
changed |= updateLeftRuns(changes); changed |= updateLeftRuns(changes);
changed |= updateRightGroups(changes); changed |= updateRightGroups(changes);
changed |= updateLeftGroups(changes); changed |= updateLeftGroups(changes);
changed |= checkState(); changed |= checkState();
breakSymmetries(changes);
return changed; return changed;
} }
private boolean nonNullEquals(Object a, Object b) {
return a != null && b != null && a.equals(b);
}
private void breakSymmetries(HashSet<Integer> changes)
throws Contradiction {
Integer mySym = symmetryBreakerValue();
if (mySym == null) {
return;
}
for (int other : changes) {
if (other == id) {
continue;
}
StoneState otherStone = top.stones.get(other);
if (!(joker && otherStone.joker || nonNullEquals(value,
otherStone.value)
&& nonNullEquals(color, otherStone.color))) {
continue;
}
Integer otherSym = top.stones.get(other).symmetryBreakerValue();
if (otherSym == null) {
continue;
}
if ((mySym != -1 | otherSym != -1)
& ((id > other) != (mySym < otherSym))) {
throw new Contradiction();
}
}
}
private Integer symmetryBreakerValue() {
if (onTable != Boolean.TRUE) {
return -1;
}
if (leftRun.size() > 1 || leftGroup.size() > 1
|| rightRun.size() > 1 || rightGroup.size() > 1) {
return null;
}
int mode;
int neighbor;
if (leftRun.contains(null)) {
if (leftGroup.contains(null)) {
if (rightRun.contains(null)) {
if (rightGroup.contains(null)) {
return -1;
} else {
mode = 0;
neighbor = rightGroup.iterator().next();
}
} else {
mode = 1;
neighbor = rightRun.iterator().next();
}
} else {
mode = 2;
neighbor = leftGroup.iterator().next();
}
} else {
mode = 3;
neighbor = leftRun.iterator().next();
}
return neighbor + stoneCount * mode;
}
public boolean updateRightRuns(HashSet<Integer> changes) public boolean updateRightRuns(HashSet<Integer> changes)
throws Contradiction { throws Contradiction {
boolean changed = false; boolean changed = false;
@ -283,7 +347,8 @@ public class TurnLogic {
if (!other.runNeighbor(this) || !other.rightRun.contains(id)) { if (!other.runNeighbor(this) || !other.rightRun.contains(id)) {
leftRun.remove(i); leftRun.remove(i);
changed = true; changed = true;
} else if (other.rightRun.size() == 1 && other.onTable == Boolean.TRUE) { } else if (other.rightRun.size() == 1
&& other.onTable == Boolean.TRUE) {
changed |= leftRun.retainAll(Arrays.asList(i)); changed |= leftRun.retainAll(Arrays.asList(i));
changed |= onTable != Boolean.TRUE; changed |= onTable != Boolean.TRUE;
onTable = true; onTable = true;
@ -303,7 +368,8 @@ public class TurnLogic {
if (!this.runNeighbor(other) || !other.leftRun.contains(id)) { if (!this.runNeighbor(other) || !other.leftRun.contains(id)) {
rightRun.remove(i); rightRun.remove(i);
changed = true; changed = true;
} else if (other.leftRun.size() == 1 && other.onTable == Boolean.TRUE) { } else if (other.leftRun.size() == 1
&& other.onTable == Boolean.TRUE) {
changed |= rightRun.retainAll(Arrays.asList(i)); changed |= rightRun.retainAll(Arrays.asList(i));
changed |= onTable != Boolean.TRUE; changed |= onTable != Boolean.TRUE;
onTable = true; onTable = true;
@ -320,7 +386,8 @@ public class TurnLogic {
relevantChanges.retainAll(changes); relevantChanges.retainAll(changes);
for (int i : relevantChanges) { for (int i : relevantChanges) {
StoneState other = top.stones.get(i); StoneState other = top.stones.get(i);
if (!other.groupNeighbor(this) || !other.rightGroup.contains(id)) { if (!other.groupNeighbor(this)
|| !other.rightGroup.contains(id)) {
leftGroup.remove(i); leftGroup.remove(i);
changed = true; changed = true;
} else if (other.rightGroup.size() == 1 } else if (other.rightGroup.size() == 1
@ -345,7 +412,8 @@ public class TurnLogic {
if (!this.groupNeighbor(other) || !other.leftGroup.contains(id)) { if (!this.groupNeighbor(other) || !other.leftGroup.contains(id)) {
rightGroup.remove(i); rightGroup.remove(i);
changed = true; changed = true;
} else if (other.leftGroup.size() == 1 && other.onTable == Boolean.TRUE) { } else if (other.leftGroup.size() == 1
&& other.onTable == Boolean.TRUE) {
changed |= rightGroup.retainAll(Arrays.asList(i)); changed |= rightGroup.retainAll(Arrays.asList(i));
changed |= onTable != Boolean.TRUE; changed |= onTable != Boolean.TRUE;
onTable = true; onTable = true;
@ -401,8 +469,9 @@ public class TurnLogic {
private boolean checkLonelyStone() { private boolean checkLonelyStone() {
boolean changed = false; boolean changed = false;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<HashSet<Integer>> sets = Arrays.<HashSet<Integer>> asList(leftGroup, List<HashSet<Integer>> sets = Arrays.<HashSet<Integer>> asList(
rightGroup, leftRun, rightRun, leftGroup, rightGroup, leftRun); leftGroup, rightGroup, leftRun, rightRun, leftGroup,
rightGroup, leftRun);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (isNullSet(sets.get(i)) && isNullSet(sets.get(i + 1)) if (isNullSet(sets.get(i)) && isNullSet(sets.get(i + 1))
&& isNullSet(sets.get(i + 2))) { && isNullSet(sets.get(i + 2))) {
@ -436,8 +505,8 @@ public class TurnLogic {
private boolean checkStoneCanBeOnTable() throws Contradiction { private boolean checkStoneCanBeOnTable() throws Contradiction {
boolean changed = false; boolean changed = false;
if (leftGroup.isEmpty() || rightGroup.isEmpty() || leftRun.isEmpty() if (leftGroup.isEmpty() || rightGroup.isEmpty()
|| rightRun.isEmpty()) { || leftRun.isEmpty() || rightRun.isEmpty()) {
if (onTable == Boolean.TRUE) { if (onTable == Boolean.TRUE) {
throw new Contradiction(); throw new Contradiction();
} }
@ -461,7 +530,36 @@ public class TurnLogic {
} }
private boolean checkJoker() { private boolean checkJoker() {
return false; boolean changed = false;
if (this.value == null) {
if (isSingleNonNullSet(leftGroup)) {
this.value = top.stones.get(leftGroup.iterator().next()).value;
} else if (isSingleNonNullSet(rightGroup)) {
this.value = top.stones.get(rightGroup.iterator().next()).value;
} else if (isSingleNonNullSet(leftRun)) {
Integer value = top.stones.get(leftRun.iterator().next()).value;
if (value != null) {
this.value = value % settings.getHighestValue() + 1;
}
} else if (isSingleNonNullSet(rightRun)) {
Integer value = top.stones.get(rightRun.iterator().next()).value;
if (value != null) {
this.value = (value - 2) % settings.getHighestValue()
+ 1;
}
}
changed |= this.value != null;
}
if (this.color == null) {
if (isSingleNonNullSet(leftRun)) {
this.color = top.stones.get(leftRun.iterator().next()).color;
} else if (isSingleNonNullSet(rightRun)) {
this.color = top.stones.get(rightRun.iterator().next()).color;
}
changed |= this.color != null;
}
return changed;
} }
public boolean isSolved() { public boolean isSolved() {
@ -471,8 +569,8 @@ public class TurnLogic {
if (onTable == null || color == null || value == null) { if (onTable == null || color == null || value == null) {
return false; return false;
} }
if (leftRun.size() > 1 || rightRun.size() > 1 || leftGroup.size() > 1 if (leftRun.size() > 1 || rightRun.size() > 1
|| rightGroup.size() > 1) { || leftGroup.size() > 1 || rightGroup.size() > 1) {
return false; return false;
} }
return true; return true;
@ -494,6 +592,32 @@ public class TurnLogic {
else else
return (double) value; return (double) value;
} }
public int getSize() {
return leftGroup.size() + rightGroup.size() + leftRun.size()
+ rightRun.size();
}
@SuppressWarnings("unchecked")
public boolean needsJoker(boolean side) {
if (onTable != Boolean.TRUE) {
return false;
}
for (HashSet<Integer> set : side ? Arrays
.asList(leftGroup, leftRun) : Arrays.asList(rightGroup,
rightRun)) {
cancle: if (!set.contains(null)) {
for (int idx : set) {
if (!top.stones.get(idx).joker) {
break cancle;
}
}
return true;
}
}
return false;
}
} }
private static boolean isNullSet(HashSet<Integer> i) { private static boolean isNullSet(HashSet<Integer> i) {
@ -541,27 +665,34 @@ public class TurnLogic {
@Override @Override
public int compare(Pair<Stone, Boolean> o1, Pair<Stone, Boolean> o2) { public int compare(Pair<Stone, Boolean> o1, Pair<Stone, Boolean> o2) {
int cmp; int cmp;
cmp = ((Boolean) o1.getFirst().isJoker()).compareTo(o2.getFirst() cmp = ((Boolean) o1.getFirst().isJoker()).compareTo(o2
.isJoker()); .getFirst().isJoker());
if (cmp != 0) { if (cmp != 0) {
return -cmp; return -cmp;
} }
cmp = (o1.getFirst().getColor()).compareTo(o2.getFirst().getColor()); cmp = (o1.getFirst().getColor()).compareTo(o2.getFirst()
.getColor());
if (cmp != 0) { if (cmp != 0) {
return cmp; return cmp;
} }
cmp = ((Integer) o1.getFirst().getValue()).compareTo(o2.getFirst() cmp = ((Integer) o1.getFirst().getValue()).compareTo(o2
.getValue()); .getFirst().getValue());
return cmp; return cmp;
} }
}); });
System.out.println("---");
ArrayList<Stone> handStones2 = new ArrayList<Stone>();
int i = 0; int i = 0;
for (Pair<Stone, Boolean> pair : sortedStones) { for (Pair<Stone, Boolean> pair : sortedStones) {
top.add(new StoneState(i++, pair.getFirst(), pair.getSecond())); top.add(new StoneState(i++, pair.getFirst(), pair.getSecond()));
inputStones.add(pair.getFirst()); inputStones.add(pair.getFirst());
if (!pair.getSecond()) {
handStones2.add(pair.getFirst());
} }
} }
System.out.println(handStones2);
}
/** /**
* Include initial meld threshold into turn logic * Include initial meld threshold into turn logic
@ -618,7 +749,8 @@ public class TurnLogic {
ArrayList<Stone> setStones = new ArrayList<Stone>(); ArrayList<Stone> setStones = new ArrayList<Stone>();
while (true) { while (true) {
setStones.add(inputStones.get(stone.id)); setStones.add(inputStones.get(stone.id));
if (isNullSet(stone.rightRun) && isNullSet(stone.rightGroup)) { if (isNullSet(stone.rightRun)
&& isNullSet(stone.rightGroup)) {
break; break;
} }
Integer rightRunID = stone.rightRun.iterator().next(); Integer rightRunID = stone.rightRun.iterator().next();
@ -669,18 +801,28 @@ public class TurnLogic {
public double optimize() { public double optimize() {
while (!autoAbort && solve()) { while (!autoAbort && solve()) {
neededScore = top.getScore(); neededScore = top.getScore();
System.out.println(neededScore);
} }
return neededScore; return neededScore;
} }
private void branch() throws Contradiction { private void branch() throws Contradiction {
ArrayList<Integer> indices = new ArrayList<Integer>();
for (int i = 0; i < stoneCount; i++) {
indices.add(i);
}
Collections.sort(indices, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
Integer size1 = top.stones.get(o1).getSize();
Integer size2 = top.stones.get(o2).getSize();
return size1.compareTo(size2);
}
});
for (int i = 0; i < stoneCount; i++) { for (int i = 0; i < stoneCount; i++) {
StoneState stone = top.stones.get(i); StoneState stone = top.stones.get(i);
if (stone.onTable == null) {
replace();
branchOnHand(i);
return;
}
if (stone.leftRun.size() > 1) { if (stone.leftRun.size() > 1) {
replace(); replace();
branchLeftRun(i, stone); branchLeftRun(i, stone);
@ -701,6 +843,14 @@ public class TurnLogic {
branchRightGroup(i, stone); branchRightGroup(i, stone);
return; return;
} }
if (stone.onTable == null) {
replace();
branchOnHand(i);
return;
}
}
for (int i = 0; i < stoneCount; i++) {
StoneState stone = top.stones.get(i);
if (stone.color == null) { if (stone.color == null) {
replace(); replace();
branchColor(i); branchColor(i);
@ -776,10 +926,10 @@ public class TurnLogic {
private void branchOnHand(int i) { private void branchOnHand(int i) {
State newState = new State(top); State newState = new State(top);
newState.stones.get(i).onTable = false; newState.stones.get(i).onTable = true;
newState.changedStones.add(i); newState.changedStones.add(i);
State altState = new State(top); State altState = new State(top);
altState.stones.get(i).onTable = true; altState.stones.get(i).onTable = false;
altState.changedStones.add(i); altState.changedStones.add(i);
pushes(altState, newState); pushes(altState, newState);
} }

View file

@ -232,6 +232,18 @@ public class HandTest {
stones.add(new Stone(i, BLACK)); stones.add(new Stone(i, BLACK));
} }
testInitialMeld(true, stones); testInitialMeld(true, stones);
testInitialMeld(true, Arrays.asList(new Stone(1, BLACK), new Stone(1,
BLACK), new Stone(2, BLACK), new Stone(8, BLACK), new Stone(9,
BLACK), new Stone(10, BLACK), new Stone(11, BLACK), new Stone(
12, BLACK), new Stone(12, BLACK), new Stone(3, ORANGE),
new Stone(3, ORANGE), new Stone(5, ORANGE),
new Stone(7, ORANGE), new Stone(12, ORANGE), new Stone(13,
ORANGE), new Stone(13, ORANGE), new Stone(2, BLUE),
new Stone(2, BLUE), new Stone(3, BLUE), new Stone(3, BLUE),
new Stone(8, BLUE), new Stone(9, BLUE), new Stone(1, RED),
new Stone(3, RED), new Stone(4, RED), new Stone(4, RED),
new Stone(5, RED), new Stone(8, RED)));
} }
/** */ /** */