Completed comments of TurnLogic
git-svn-id: svn://sunsvr01.isp.uni-luebeck.de/swproj13/trunk@535 72836036-5685-4462-b002-a69064685172
This commit is contained in:
parent
0b1f9383ce
commit
9b5f3648ed
1 changed files with 350 additions and 62 deletions
|
@ -71,7 +71,7 @@ public class TurnLogic {
|
||||||
* Create a copy of a state
|
* Create a copy of a state
|
||||||
*
|
*
|
||||||
* @param state
|
* @param state
|
||||||
* the state to copy
|
* the state to copy
|
||||||
*/
|
*/
|
||||||
public State(State state) {
|
public State(State state) {
|
||||||
for (StoneState stone : state.stones) {
|
for (StoneState stone : state.stones) {
|
||||||
|
@ -84,7 +84,7 @@ public class TurnLogic {
|
||||||
* Adds stones to be considered in the state
|
* Adds stones to be considered in the state
|
||||||
*
|
*
|
||||||
* @param stone
|
* @param stone
|
||||||
* the stone to add
|
* the stone to add
|
||||||
*/
|
*/
|
||||||
public void add(StoneState stone) {
|
public void add(StoneState stone) {
|
||||||
stones.add(stone);
|
stones.add(stone);
|
||||||
|
@ -98,7 +98,7 @@ public class TurnLogic {
|
||||||
* Update the stones to consider the changes of the changed stones
|
* Update the stones to consider the changes of the changed stones
|
||||||
*
|
*
|
||||||
* @throws Contradiction
|
* @throws Contradiction
|
||||||
* Is thrown if the changes contradict each other
|
* Is thrown if the changes contradict each other
|
||||||
*/
|
*/
|
||||||
public void updateStones() throws Contradiction {
|
public void updateStones() throws Contradiction {
|
||||||
checkJokerCount();
|
checkJokerCount();
|
||||||
|
@ -121,9 +121,9 @@ public class TurnLogic {
|
||||||
* One step in the update process
|
* One step in the update process
|
||||||
*
|
*
|
||||||
* @throws Contradiction
|
* @throws Contradiction
|
||||||
* Is thrown if changes contradict each other
|
* Is thrown if changes contradict each other
|
||||||
*/
|
*/
|
||||||
public void updateStonesStep() throws Contradiction {
|
private void updateStonesStep() throws Contradiction {
|
||||||
HashSet<Integer> newChangedStones = new HashSet<Integer>();
|
HashSet<Integer> newChangedStones = new HashSet<Integer>();
|
||||||
for (int i = 0; i < stoneCount; i++) {
|
for (int i = 0; i < stoneCount; i++) {
|
||||||
StoneState stone = stones.get(i);
|
StoneState stone = stones.get(i);
|
||||||
|
@ -137,8 +137,8 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the stones have a definite value and the state is thereby
|
* Returns whether the stones have a definite value and the state is
|
||||||
* solved
|
* thereby solved
|
||||||
*
|
*
|
||||||
* @return whether the state is solved
|
* @return whether the state is solved
|
||||||
*/
|
*/
|
||||||
|
@ -152,10 +152,10 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that not too many jokers are trying to be used
|
* Checks that not more jokers than available are needed
|
||||||
*
|
*
|
||||||
* @throws Contradiction
|
* @throws Contradiction
|
||||||
* Is thrown if too many jokers are trying to be used
|
* Is thrown if too many jokers are needed
|
||||||
*/
|
*/
|
||||||
private void checkJokerCount() throws Contradiction {
|
private void checkJokerCount() throws Contradiction {
|
||||||
int jokersLeft = jokerIDs.size();
|
int jokersLeft = jokerIDs.size();
|
||||||
|
@ -177,10 +177,10 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that enough points and a high enough score are reached
|
* Checks that enough points and a high enough score can be reached
|
||||||
*
|
*
|
||||||
* @throws Contradiction
|
* @throws Contradiction
|
||||||
* Is thrown if points are too few or score is too low
|
* Is thrown if points are too few or score is too low
|
||||||
*/
|
*/
|
||||||
public void checkScoreAndPoints() throws Contradiction {
|
public void checkScoreAndPoints() throws Contradiction {
|
||||||
if (getPoints() < neededPoints) {
|
if (getPoints() < neededPoints) {
|
||||||
|
@ -224,8 +224,8 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the remaining possible values, colors, positions, etc. a stone can
|
* Contains the remaining possible values, colors, positions, etc. a stone
|
||||||
* take and its possible neighbors in a group or run
|
* can take and its possible neighbors in a group or run
|
||||||
*/
|
*/
|
||||||
private class StoneState {
|
private class StoneState {
|
||||||
int id;
|
int id;
|
||||||
|
@ -243,11 +243,11 @@ public class TurnLogic {
|
||||||
* Creates a new
|
* Creates a new
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id
|
||||||
* the stone's identifier
|
* the stone's identifier
|
||||||
* @param stone
|
* @param stone
|
||||||
* the stone
|
* the stone
|
||||||
* @param table
|
* @param table
|
||||||
* whether is on the table
|
* whether is on the table
|
||||||
*/
|
*/
|
||||||
public StoneState(int id, Stone stone, boolean table) {
|
public StoneState(int id, Stone stone, boolean table) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -268,7 +268,7 @@ public class TurnLogic {
|
||||||
* Creates a copy
|
* Creates a copy
|
||||||
*
|
*
|
||||||
* @param stone
|
* @param stone
|
||||||
* the state to copy
|
* the state to copy
|
||||||
*/
|
*/
|
||||||
public StoneState(StoneState stone) {
|
public StoneState(StoneState stone) {
|
||||||
this.id = stone.id;
|
this.id = stone.id;
|
||||||
|
@ -300,37 +300,85 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the recent changes affect the stone
|
* Returns whether the recent changes could affect the stone
|
||||||
*
|
*
|
||||||
* @param changes
|
* @param changes
|
||||||
* the changes
|
* the changes
|
||||||
* @return whether the stone is affected
|
* @return whether the stone could be affected
|
||||||
*/
|
*/
|
||||||
public boolean isInterested(HashSet<Integer> changes) {
|
private 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) {
|
/**
|
||||||
|
* Compare two objects, returning true if either is null
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* object's type
|
||||||
|
* @param a
|
||||||
|
* first object
|
||||||
|
* @param b
|
||||||
|
* second object
|
||||||
|
* @return whether a is less than b or either is null
|
||||||
|
*/
|
||||||
|
private <T extends Comparable<T>> boolean lessThan(T a, T b) {
|
||||||
return a == null || b == null || a.compareTo(b) < 0;
|
return a == null || b == null || a.compareTo(b) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> boolean same(T a, T b) {
|
/**
|
||||||
|
* Compare two objects, returning true if either is null
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* object;s type
|
||||||
|
* @param a
|
||||||
|
* first object
|
||||||
|
* @param b
|
||||||
|
* second object
|
||||||
|
* @return whether a is less than b or either is null
|
||||||
|
*/
|
||||||
|
private <T> boolean same(T a, T b) {
|
||||||
return a == null || b == null || a.equals(b);
|
return a == null || b == null || a.equals(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean step(Integer a, Integer b) {
|
/**
|
||||||
|
* Checks whether a and b are consecutive integers or either is null
|
||||||
|
*
|
||||||
|
* @param a
|
||||||
|
* first integer
|
||||||
|
* @param b
|
||||||
|
* next integer
|
||||||
|
* @return whether a + 1 is b or either is null
|
||||||
|
*/
|
||||||
|
private boolean step(Integer a, Integer b) {
|
||||||
return a == null || b == null || (int) a == b - 1;
|
return a == null || b == null || (int) a == b - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean cycleStep(Integer a, Integer b) {
|
/**
|
||||||
|
* Checks whether a and b are consecutive integers modulo highest value
|
||||||
|
* or either i snull
|
||||||
|
*
|
||||||
|
* @param a
|
||||||
|
* first integer
|
||||||
|
* @param b
|
||||||
|
* second integer
|
||||||
|
* @return whether a + 1 is b (modulo highest value) or either is null
|
||||||
|
*/
|
||||||
|
private boolean cycleStep(Integer a, Integer b) {
|
||||||
return a == null || b == null || (int) a == b - 1
|
return a == null || b == null || (int) a == b - 1
|
||||||
|| a == settings.getHighestValue() && b == 1;
|
|| a == settings.getHighestValue() && b == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean groupNeighbor(StoneState other) {
|
/**
|
||||||
|
* Checks whether this stone could be left to other in a group
|
||||||
|
*
|
||||||
|
* @param other
|
||||||
|
* possible right neighbor
|
||||||
|
* @return whether they can be neighbors
|
||||||
|
*/
|
||||||
|
private boolean groupNeighbor(StoneState other) {
|
||||||
if (isNullSet(leftGroup) && isNullSet(other.rightGroup)) {
|
if (isNullSet(leftGroup) && isNullSet(other.rightGroup)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -340,7 +388,14 @@ public class TurnLogic {
|
||||||
return lessThan(color, other.color) && same(value, other.value);
|
return lessThan(color, other.color) && same(value, other.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean runNeighbor(StoneState other) {
|
/**
|
||||||
|
* Checks whether this stone could be left to other in a run
|
||||||
|
*
|
||||||
|
* @param other
|
||||||
|
* possible right neighbor
|
||||||
|
* @return whether they can be neighbors
|
||||||
|
*/
|
||||||
|
private boolean runNeighbor(StoneState other) {
|
||||||
if (isNullSet(leftRun) && isNullSet(other.rightRun)) {
|
if (isNullSet(leftRun) && isNullSet(other.rightRun)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -358,7 +413,16 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean update(HashSet<Integer> changes) throws Contradiction {
|
/**
|
||||||
|
* Updates this stone considering the changes of all changed stones
|
||||||
|
*
|
||||||
|
* @param changes
|
||||||
|
* stones that have changed
|
||||||
|
* @return whether we could be updated
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if the changes contradict each other
|
||||||
|
*/
|
||||||
|
private boolean update(HashSet<Integer> changes) throws Contradiction {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|
||||||
changed |= updateRightRuns(changes);
|
changed |= updateRightRuns(changes);
|
||||||
|
@ -372,11 +436,29 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that two objects are equal to each other and not null
|
||||||
|
*
|
||||||
|
* @param a
|
||||||
|
* @param b
|
||||||
|
* @return whether a equals b and both are not null
|
||||||
|
*/
|
||||||
private boolean nonNullEquals(Object a, Object b) {
|
private boolean nonNullEquals(Object a, Object b) {
|
||||||
return a != null && b != null && a.equals(b);
|
return a != null && b != null && a.equals(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void breakSymmetries(HashSet<Integer> changes) throws Contradiction {
|
/**
|
||||||
|
* Try to break symmetries between exchangeable stones to reduce the
|
||||||
|
* state space
|
||||||
|
*
|
||||||
|
* @param changes
|
||||||
|
* stones that changed
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown when there exists another trivial solution by
|
||||||
|
* symmetry
|
||||||
|
*/
|
||||||
|
private void breakSymmetries(HashSet<Integer> changes)
|
||||||
|
throws Contradiction {
|
||||||
Integer mySym = symmetryBreakerValue();
|
Integer mySym = symmetryBreakerValue();
|
||||||
if (mySym == null) {
|
if (mySym == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -387,7 +469,8 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
StoneState otherStone = top.stones.get(other);
|
StoneState otherStone = top.stones.get(other);
|
||||||
if (!(joker && otherStone.joker || nonNullEquals(value,
|
if (!(joker && otherStone.joker || nonNullEquals(value,
|
||||||
otherStone.value) && nonNullEquals(color, otherStone.color))) {
|
otherStone.value)
|
||||||
|
&& nonNullEquals(color, otherStone.color))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Integer otherSym = top.stones.get(other).symmetryBreakerValue();
|
Integer otherSym = top.stones.get(other).symmetryBreakerValue();
|
||||||
|
@ -402,12 +485,18 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order stones by their neighbors to decide on a solution of a set of
|
||||||
|
* symmetric solutions
|
||||||
|
*
|
||||||
|
* @return the order value or null if it can't be determined yer
|
||||||
|
*/
|
||||||
private Integer symmetryBreakerValue() {
|
private Integer symmetryBreakerValue() {
|
||||||
if (onTable != Boolean.TRUE) {
|
if (onTable != Boolean.TRUE) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (leftRun.size() > 1 || leftGroup.size() > 1 || rightRun.size() > 1
|
if (leftRun.size() > 1 || leftGroup.size() > 1
|
||||||
|| rightGroup.size() > 1) {
|
|| rightRun.size() > 1 || rightGroup.size() > 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int mode;
|
int mode;
|
||||||
|
@ -437,7 +526,17 @@ public class TurnLogic {
|
||||||
return neighbor + stoneCount * mode;
|
return neighbor + stoneCount * mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateRightRuns(HashSet<Integer> changes)
|
/**
|
||||||
|
* Updates this stone's right run neighbors considering the changes of
|
||||||
|
* all changed stones
|
||||||
|
*
|
||||||
|
* @param changes
|
||||||
|
* stones that have changed
|
||||||
|
* @return whether we could be updated
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if the changes contradict each other
|
||||||
|
*/
|
||||||
|
private boolean updateRightRuns(HashSet<Integer> changes)
|
||||||
throws Contradiction {
|
throws Contradiction {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftRun);
|
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftRun);
|
||||||
|
@ -447,7 +546,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;
|
||||||
|
@ -457,7 +557,17 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateLeftRuns(HashSet<Integer> changes)
|
/**
|
||||||
|
* Updates this stone's left run neighbors considering the changes of
|
||||||
|
* all changed stones
|
||||||
|
*
|
||||||
|
* @param changes
|
||||||
|
* stones that have changed
|
||||||
|
* @return whether we could be updated
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if the changes contradict each other
|
||||||
|
*/
|
||||||
|
private boolean updateLeftRuns(HashSet<Integer> changes)
|
||||||
throws Contradiction {
|
throws Contradiction {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
HashSet<Integer> relevantChanges = new HashSet<Integer>(rightRun);
|
HashSet<Integer> relevantChanges = new HashSet<Integer>(rightRun);
|
||||||
|
@ -467,7 +577,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;
|
||||||
|
@ -477,14 +588,25 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateRightGroups(HashSet<Integer> changes)
|
/**
|
||||||
|
* Updates this stone's right group neighbors considering the changes of
|
||||||
|
* all changed stones
|
||||||
|
*
|
||||||
|
* @param changes
|
||||||
|
* stones that have changed
|
||||||
|
* @return whether we could be updated
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if the changes contradict each other
|
||||||
|
*/
|
||||||
|
private boolean updateRightGroups(HashSet<Integer> changes)
|
||||||
throws Contradiction {
|
throws Contradiction {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftGroup);
|
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftGroup);
|
||||||
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
|
||||||
|
@ -498,7 +620,17 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateLeftGroups(HashSet<Integer> changes)
|
/**
|
||||||
|
* Updates this stone's left group neighbors considering the changes of
|
||||||
|
* all changed stones
|
||||||
|
*
|
||||||
|
* @param changes
|
||||||
|
* stones that have changed
|
||||||
|
* @return whether we could be updated
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if the changes contradict each other
|
||||||
|
*/
|
||||||
|
private boolean updateLeftGroups(HashSet<Integer> changes)
|
||||||
throws Contradiction {
|
throws Contradiction {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftGroup);
|
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftGroup);
|
||||||
|
@ -509,7 +641,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;
|
||||||
|
@ -519,7 +652,14 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkState() throws Contradiction {
|
/**
|
||||||
|
* Enforce local rule consistency
|
||||||
|
*
|
||||||
|
* @return whether this stone changed
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if this stone can't be placed anywhere
|
||||||
|
*/
|
||||||
|
private boolean checkState() throws Contradiction {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|
||||||
if (onTable == Boolean.FALSE) {
|
if (onTable == Boolean.FALSE) {
|
||||||
|
@ -550,6 +690,12 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this stone has to be in a group it can't be part of a run and the
|
||||||
|
* other way around
|
||||||
|
*
|
||||||
|
* @return whether this stone changed
|
||||||
|
*/
|
||||||
private boolean checkSetTypeKnown() {
|
private boolean checkSetTypeKnown() {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (!(leftGroup.contains(null) || rightGroup.contains(null))) {
|
if (!(leftGroup.contains(null) || rightGroup.contains(null))) {
|
||||||
|
@ -563,11 +709,18 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we have no group/run neighbors and no left/right neighbors but are
|
||||||
|
* on the table we need to have a neighbor for the remaining case
|
||||||
|
*
|
||||||
|
* @return whether this stone changed
|
||||||
|
*/
|
||||||
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))) {
|
||||||
|
@ -577,6 +730,12 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we have a neighbor that is on the end of a set we can't be on the
|
||||||
|
* end either
|
||||||
|
*
|
||||||
|
* @return whether this stone changed
|
||||||
|
*/
|
||||||
private boolean checkNeighborStoneNeeded() {
|
private boolean checkNeighborStoneNeeded() {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (isSingleNonNullSet(leftRun)
|
if (isSingleNonNullSet(leftRun)
|
||||||
|
@ -599,10 +758,19 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we need to have some kind of neighbor but there isn't any
|
||||||
|
* possible one we're either not on the table or if we have to have a
|
||||||
|
* contradiction
|
||||||
|
*
|
||||||
|
* @return whether this stone changed
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if this stone can't be placed anywhere
|
||||||
|
*/
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
@ -618,6 +786,13 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we need to be in a group and in a run at the same time we have a
|
||||||
|
* contradiction
|
||||||
|
*
|
||||||
|
* @throws Contradiction
|
||||||
|
* Is thrown if this stone can't be placed anywhere
|
||||||
|
*/
|
||||||
private void checkGroupRunExclusive() throws Contradiction {
|
private void checkGroupRunExclusive() throws Contradiction {
|
||||||
if ((isSingleNonNullSet(leftGroup) || isSingleNonNullSet(rightGroup))
|
if ((isSingleNonNullSet(leftGroup) || isSingleNonNullSet(rightGroup))
|
||||||
&& (isSingleNonNullSet(leftRun) || isSingleNonNullSet(rightRun))) {
|
&& (isSingleNonNullSet(leftRun) || isSingleNonNullSet(rightRun))) {
|
||||||
|
@ -625,6 +800,11 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to derive the value of a joker stone from it's neighbor(s)
|
||||||
|
*
|
||||||
|
* @return whether we could derive the value
|
||||||
|
*/
|
||||||
private boolean checkJokerValue() {
|
private boolean checkJokerValue() {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (this.value == null) {
|
if (this.value == null) {
|
||||||
|
@ -640,7 +820,8 @@ public class TurnLogic {
|
||||||
} else if (isSingleNonNullSet(rightRun)) {
|
} else if (isSingleNonNullSet(rightRun)) {
|
||||||
Integer value = top.stones.get(rightRun.iterator().next()).value;
|
Integer value = top.stones.get(rightRun.iterator().next()).value;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
this.value = (value - 2) % settings.getHighestValue() + 1;
|
this.value = (value - 2) % settings.getHighestValue()
|
||||||
|
+ 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
changed |= this.value != null;
|
changed |= this.value != null;
|
||||||
|
@ -648,6 +829,11 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to derive the color of a joker stone from it's neighbor(s)
|
||||||
|
*
|
||||||
|
* @return whether we could derive the color
|
||||||
|
*/
|
||||||
private boolean checkJokerColor() {
|
private boolean checkJokerColor() {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (this.color == null) {
|
if (this.color == null) {
|
||||||
|
@ -661,6 +847,11 @@ public class TurnLogic {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this stone's position, value and color are known
|
||||||
|
*
|
||||||
|
* @return true when this stone is solved
|
||||||
|
*/
|
||||||
public boolean isSolved() {
|
public boolean isSolved() {
|
||||||
if (onTable == Boolean.FALSE) {
|
if (onTable == Boolean.FALSE) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -668,13 +859,18 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How badly we want to get rid of this stone
|
||||||
|
*
|
||||||
|
* @return this stone's badness score
|
||||||
|
*/
|
||||||
public double getScore() {
|
public double getScore() {
|
||||||
if (fromTable) {
|
if (fromTable) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -682,6 +878,11 @@ public class TurnLogic {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many points we get for melding this stone
|
||||||
|
*
|
||||||
|
* @return amount of points
|
||||||
|
*/
|
||||||
public double getPoints() {
|
public double getPoints() {
|
||||||
if (fromTable) {
|
if (fromTable) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -692,13 +893,22 @@ public class TurnLogic {
|
||||||
return (double) value;
|
return (double) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether we need a joker on the left or right side to place
|
||||||
|
* this stone
|
||||||
|
*
|
||||||
|
* @param side
|
||||||
|
* which side to check
|
||||||
|
* @return true when a joker is the only possible neighbor
|
||||||
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public boolean needsJoker(boolean side) {
|
public boolean needsJoker(boolean side) {
|
||||||
if (onTable != Boolean.TRUE) {
|
if (onTable != Boolean.TRUE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (HashSet<Integer> set : side ? Arrays.asList(leftGroup, leftRun)
|
for (HashSet<Integer> set : side ? Arrays
|
||||||
: Arrays.asList(rightGroup, rightRun)) {
|
.asList(leftGroup, leftRun) : Arrays.asList(rightGroup,
|
||||||
|
rightRun)) {
|
||||||
cancle: if (!set.contains(null)) {
|
cancle: if (!set.contains(null)) {
|
||||||
for (int idx : set) {
|
for (int idx : set) {
|
||||||
if (!top.stones.get(idx).joker) {
|
if (!top.stones.get(idx).joker) {
|
||||||
|
@ -713,10 +923,24 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a set of Integers only contains null
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* set to check
|
||||||
|
* @return true when it only contains null
|
||||||
|
*/
|
||||||
private static boolean isNullSet(HashSet<Integer> i) {
|
private static boolean isNullSet(HashSet<Integer> i) {
|
||||||
return i.size() == 1 && i.contains(null);
|
return i.size() == 1 && i.contains(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a set of Integers only contains a single non null value
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* set to check
|
||||||
|
* @return true when it only contains a single non null value
|
||||||
|
*/
|
||||||
private static boolean isSingleNonNullSet(HashSet<Integer> i) {
|
private static boolean isSingleNonNullSet(HashSet<Integer> i) {
|
||||||
return i.size() == 1 && !i.contains(null);
|
return i.size() == 1 && !i.contains(null);
|
||||||
}
|
}
|
||||||
|
@ -725,11 +949,11 @@ public class TurnLogic {
|
||||||
* Creates new turn logic
|
* Creates new turn logic
|
||||||
*
|
*
|
||||||
* @param settings
|
* @param settings
|
||||||
* the game settings
|
* the game settings
|
||||||
* @param tableStones
|
* @param tableStones
|
||||||
* all stones on the table
|
* all stones on the table
|
||||||
* @param handStones
|
* @param handStones
|
||||||
* all stones on the hand
|
* all stones on the hand
|
||||||
*/
|
*/
|
||||||
public TurnLogic(GameSettings settings, Collection<Stone> tableStones,
|
public TurnLogic(GameSettings settings, Collection<Stone> tableStones,
|
||||||
Collection<Stone> handStones) {
|
Collection<Stone> handStones) {
|
||||||
|
@ -758,17 +982,18 @@ 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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -787,6 +1012,9 @@ public class TurnLogic {
|
||||||
neededPoints = settings.getInitialMeldThreshold();
|
neededPoints = settings.getInitialMeldThreshold();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a contradicted state from the try stack, reset top
|
||||||
|
*/
|
||||||
private void pop() {
|
private void pop() {
|
||||||
stack.remove(stack.size() - 1);
|
stack.remove(stack.size() - 1);
|
||||||
if (!stack.isEmpty()) {
|
if (!stack.isEmpty()) {
|
||||||
|
@ -794,16 +1022,29 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an unsolved state, to be replaced with refined state, from the try
|
||||||
|
* stack
|
||||||
|
*/
|
||||||
private void replace() {
|
private void replace() {
|
||||||
stack.remove(stack.size() - 1);
|
stack.remove(stack.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push multiple new state onto the try stack
|
||||||
|
*
|
||||||
|
* @param states
|
||||||
|
* states to push
|
||||||
|
*/
|
||||||
private void pushes(State... states) {
|
private void pushes(State... states) {
|
||||||
for (State state : states) {
|
for (State state : states) {
|
||||||
stack.add(state);
|
stack.add(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Done with modifying the try stack, reset top
|
||||||
|
*/
|
||||||
private void done() {
|
private void done() {
|
||||||
top = stack.get(stack.size() - 1);
|
top = stack.get(stack.size() - 1);
|
||||||
}
|
}
|
||||||
|
@ -835,7 +1076,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();
|
||||||
|
@ -890,7 +1132,10 @@ public class TurnLogic {
|
||||||
return neededScore;
|
return neededScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void branch() throws Contradiction {
|
/**
|
||||||
|
* Refine the currently unsolved top stack state
|
||||||
|
*/
|
||||||
|
private void branch() {
|
||||||
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.leftRun.size() > 1) {
|
if (stone.leftRun.size() > 1) {
|
||||||
|
@ -929,6 +1174,12 @@ public class TurnLogic {
|
||||||
throw new Error("Internal AI error");
|
throw new Error("Internal AI error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create states for all possible values of stone i
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* stone id to branch on
|
||||||
|
*/
|
||||||
private void branchValue(int i) {
|
private void branchValue(int i) {
|
||||||
replace();
|
replace();
|
||||||
for (int value = 1; value <= settings.getHighestValue(); value++) {
|
for (int value = 1; value <= settings.getHighestValue(); value++) {
|
||||||
|
@ -939,6 +1190,12 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create states for all possible colors of stone i
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* stone id to branch on
|
||||||
|
*/
|
||||||
private void branchColor(int i) {
|
private void branchColor(int i) {
|
||||||
replace();
|
replace();
|
||||||
for (StoneColor color : stoneColors) {
|
for (StoneColor color : stoneColors) {
|
||||||
|
@ -947,8 +1204,15 @@ public class TurnLogic {
|
||||||
newState.changedStones.add(i);
|
newState.changedStones.add(i);
|
||||||
pushes(newState);
|
pushes(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create states for all possible right group neighbors of stone i
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* stone id to branch on
|
||||||
|
*/
|
||||||
private void branchRightGroup(int i, StoneState stone) {
|
private void branchRightGroup(int i, StoneState stone) {
|
||||||
replace();
|
replace();
|
||||||
for (Integer id : stone.rightGroup) {
|
for (Integer id : stone.rightGroup) {
|
||||||
|
@ -960,6 +1224,12 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create states for all possible left group neighbors of stone i
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* stone id to branch on
|
||||||
|
*/
|
||||||
private void branchLeftGroup(int i, StoneState stone) {
|
private void branchLeftGroup(int i, StoneState stone) {
|
||||||
replace();
|
replace();
|
||||||
for (Integer id : stone.leftGroup) {
|
for (Integer id : stone.leftGroup) {
|
||||||
|
@ -971,6 +1241,12 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create states for all possible right run neighbors of stone i
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* stone id to branch on
|
||||||
|
*/
|
||||||
private void branchRightRun(int i, StoneState stone) {
|
private void branchRightRun(int i, StoneState stone) {
|
||||||
replace();
|
replace();
|
||||||
for (Integer id : stone.rightRun) {
|
for (Integer id : stone.rightRun) {
|
||||||
|
@ -982,6 +1258,12 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create states for all possible left run neighbors of stone i
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* stone id to branch on
|
||||||
|
*/
|
||||||
private void branchLeftRun(int i, StoneState stone) {
|
private void branchLeftRun(int i, StoneState stone) {
|
||||||
replace();
|
replace();
|
||||||
for (Integer id : stone.leftRun) {
|
for (Integer id : stone.leftRun) {
|
||||||
|
@ -993,6 +1275,12 @@ public class TurnLogic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create states with stone i on the table and stone i on the hand
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* stone id to branch on
|
||||||
|
*/
|
||||||
private void branchOnHand(int i) {
|
private void branchOnHand(int i) {
|
||||||
replace();
|
replace();
|
||||||
State newState = new State(top);
|
State newState = new State(top);
|
||||||
|
|
Reference in a new issue