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.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import jrummikub.model.GameSettings;
@ -64,14 +65,19 @@ public class TurnLogic {
}
public void updateStones() throws Contradiction {
checkJokers();
checkJokerCount();
for (int i : changedStones) {
stones.get(i).checkState();
}
checkScoreAndPoints();
while (!changedStones.isEmpty()) {
updateStonesStep();
while (!changedStones.isEmpty()) {
updateStonesStep();
checkScoreAndPoints();
}
checkJokerCount();
checkScoreAndPoints();
}
}
@ -97,33 +103,20 @@ public class TurnLogic {
return true;
}
public void checkJokers() throws Contradiction {
for (int i = 0; i < jokerIDs.size(); i++) {
StoneState left = stones.get(jokerIDs.get(i));
HashSet<Integer> leftLeftGroup = new HashSet<Integer>(left.leftGroup);
HashSet<Integer> leftLeftRun = new HashSet<Integer>(left.leftRun);
leftLeftGroup.remove(null);
leftLeftRun.remove(null);
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++) {
StoneState right = stones.get(jokerIDs.get(j));
if (right.leftGroup.remove(groupID)) {
changedStones.add(jokerIDs.get(j));
private void checkJokerCount() throws Contradiction {
int jokersLeft = jokerIDs.size();
int jokersRight = jokerIDs.size();
for (StoneState stone : stones) {
if (stone.needsJoker(true)) {
jokersLeft--;
if (jokersLeft < 0) {
throw new Contradiction();
}
if (right.leftRun.remove(runID)) {
changedStones.add(jokerIDs.get(j));
}
if (stone.needsJoker(false)) {
jokersRight--;
if (jokersRight < 0) {
throw new Contradiction();
}
}
}
@ -214,8 +207,8 @@ public class TurnLogic {
public boolean isInterested(HashSet<Integer> changes) {
return !(Collections.disjoint(changes, leftRun)
&& Collections.disjoint(changes, rightRun)
&& Collections.disjoint(changes, leftGroup) && Collections.disjoint(
changes, rightGroup));
&& Collections.disjoint(changes, leftGroup) && Collections
.disjoint(changes, rightGroup));
}
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 {
boolean changed = false;
changed |= updateRightRuns(changes);
changed |= updateLeftRuns(changes);
changed |= updateRightGroups(changes);
changed |= updateLeftGroups(changes);
changed |= checkState();
breakSymmetries(changes);
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)
throws Contradiction {
boolean changed = false;
@ -283,7 +347,8 @@ public class TurnLogic {
if (!other.runNeighbor(this) || !other.rightRun.contains(id)) {
leftRun.remove(i);
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 |= onTable != Boolean.TRUE;
onTable = true;
@ -303,7 +368,8 @@ public class TurnLogic {
if (!this.runNeighbor(other) || !other.leftRun.contains(id)) {
rightRun.remove(i);
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 |= onTable != Boolean.TRUE;
onTable = true;
@ -320,7 +386,8 @@ public class TurnLogic {
relevantChanges.retainAll(changes);
for (int i : relevantChanges) {
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);
changed = true;
} else if (other.rightGroup.size() == 1
@ -345,7 +412,8 @@ public class TurnLogic {
if (!this.groupNeighbor(other) || !other.leftGroup.contains(id)) {
rightGroup.remove(i);
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 |= onTable != Boolean.TRUE;
onTable = true;
@ -401,8 +469,9 @@ public class TurnLogic {
private boolean checkLonelyStone() {
boolean changed = false;
@SuppressWarnings("unchecked")
List<HashSet<Integer>> sets = Arrays.<HashSet<Integer>> asList(leftGroup,
rightGroup, leftRun, rightRun, leftGroup, rightGroup, leftRun);
List<HashSet<Integer>> sets = Arrays.<HashSet<Integer>> asList(
leftGroup, rightGroup, leftRun, rightRun, leftGroup,
rightGroup, leftRun);
for (int i = 0; i < 4; i++) {
if (isNullSet(sets.get(i)) && isNullSet(sets.get(i + 1))
&& isNullSet(sets.get(i + 2))) {
@ -436,8 +505,8 @@ public class TurnLogic {
private boolean checkStoneCanBeOnTable() throws Contradiction {
boolean changed = false;
if (leftGroup.isEmpty() || rightGroup.isEmpty() || leftRun.isEmpty()
|| rightRun.isEmpty()) {
if (leftGroup.isEmpty() || rightGroup.isEmpty()
|| leftRun.isEmpty() || rightRun.isEmpty()) {
if (onTable == Boolean.TRUE) {
throw new Contradiction();
}
@ -461,7 +530,36 @@ public class TurnLogic {
}
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() {
@ -471,8 +569,8 @@ public class TurnLogic {
if (onTable == null || color == null || value == null) {
return false;
}
if (leftRun.size() > 1 || rightRun.size() > 1 || leftGroup.size() > 1
|| rightGroup.size() > 1) {
if (leftRun.size() > 1 || rightRun.size() > 1
|| leftGroup.size() > 1 || rightGroup.size() > 1) {
return false;
}
return true;
@ -494,6 +592,32 @@ public class TurnLogic {
else
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) {
@ -508,11 +632,11 @@ public class TurnLogic {
* Creates new turn logic
*
* @param settings
* the game settings
* the game settings
* @param tableStones
* all stones on the table
* all stones on the table
* @param handStones
* all stones on the hand
* all stones on the hand
*/
public TurnLogic(GameSettings settings, Collection<Stone> tableStones,
Collection<Stone> handStones) {
@ -541,26 +665,33 @@ public class TurnLogic {
@Override
public int compare(Pair<Stone, Boolean> o1, Pair<Stone, Boolean> o2) {
int cmp;
cmp = ((Boolean) o1.getFirst().isJoker()).compareTo(o2.getFirst()
.isJoker());
cmp = ((Boolean) o1.getFirst().isJoker()).compareTo(o2
.getFirst().isJoker());
if (cmp != 0) {
return -cmp;
}
cmp = (o1.getFirst().getColor()).compareTo(o2.getFirst().getColor());
cmp = (o1.getFirst().getColor()).compareTo(o2.getFirst()
.getColor());
if (cmp != 0) {
return cmp;
}
cmp = ((Integer) o1.getFirst().getValue()).compareTo(o2.getFirst()
.getValue());
cmp = ((Integer) o1.getFirst().getValue()).compareTo(o2
.getFirst().getValue());
return cmp;
}
});
System.out.println("---");
ArrayList<Stone> handStones2 = new ArrayList<Stone>();
int i = 0;
for (Pair<Stone, Boolean> pair : sortedStones) {
top.add(new StoneState(i++, pair.getFirst(), pair.getSecond()));
inputStones.add(pair.getFirst());
if (!pair.getSecond()) {
handStones2.add(pair.getFirst());
}
}
System.out.println(handStones2);
}
/**
@ -618,7 +749,8 @@ public class TurnLogic {
ArrayList<Stone> setStones = new ArrayList<Stone>();
while (true) {
setStones.add(inputStones.get(stone.id));
if (isNullSet(stone.rightRun) && isNullSet(stone.rightGroup)) {
if (isNullSet(stone.rightRun)
&& isNullSet(stone.rightGroup)) {
break;
}
Integer rightRunID = stone.rightRun.iterator().next();
@ -669,18 +801,28 @@ public class TurnLogic {
public double optimize() {
while (!autoAbort && solve()) {
neededScore = top.getScore();
System.out.println(neededScore);
}
return neededScore;
}
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++) {
StoneState stone = top.stones.get(i);
if (stone.onTable == null) {
replace();
branchOnHand(i);
return;
}
if (stone.leftRun.size() > 1) {
replace();
branchLeftRun(i, stone);
@ -701,6 +843,14 @@ public class TurnLogic {
branchRightGroup(i, stone);
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) {
replace();
branchColor(i);
@ -776,10 +926,10 @@ public class TurnLogic {
private void branchOnHand(int i) {
State newState = new State(top);
newState.stones.get(i).onTable = false;
newState.stones.get(i).onTable = true;
newState.changedStones.add(i);
State altState = new State(top);
altState.stones.get(i).onTable = true;
altState.stones.get(i).onTable = false;
altState.changedStones.add(i);
pushes(altState, newState);
}

View file

@ -232,6 +232,18 @@ public class HandTest {
stones.add(new Stone(i, BLACK));
}
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)));
}
/** */