summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/jrummikub/ai/TurnLogic.java412
1 files changed, 350 insertions, 62 deletions
diff --git a/src/jrummikub/ai/TurnLogic.java b/src/jrummikub/ai/TurnLogic.java
index 214288a..26318cf 100644
--- a/src/jrummikub/ai/TurnLogic.java
+++ b/src/jrummikub/ai/TurnLogic.java
@@ -71,7 +71,7 @@ public class TurnLogic {
* Create a copy of a state
*
* @param state
- * the state to copy
+ * the state to copy
*/
public State(State state) {
for (StoneState stone : state.stones) {
@@ -84,7 +84,7 @@ public class TurnLogic {
* Adds stones to be considered in the state
*
* @param stone
- * the stone to add
+ * the stone to add
*/
public void add(StoneState stone) {
stones.add(stone);
@@ -98,7 +98,7 @@ public class TurnLogic {
* Update the stones to consider the changes of the changed stones
*
* @throws Contradiction
- * Is thrown if the changes contradict each other
+ * Is thrown if the changes contradict each other
*/
public void updateStones() throws Contradiction {
checkJokerCount();
@@ -121,9 +121,9 @@ public class TurnLogic {
* One step in the update process
*
* @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>();
for (int i = 0; i < stoneCount; 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
- * solved
+ * Returns whether the stones have a definite value and the state is
+ * thereby 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
- * Is thrown if too many jokers are trying to be used
+ * Is thrown if too many jokers are needed
*/
private void checkJokerCount() throws Contradiction {
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
- * 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 {
if (getPoints() < neededPoints) {
@@ -224,8 +224,8 @@ public class TurnLogic {
}
/**
- * Contains the remaining possible values, colors, positions, etc. a stone can
- * take and its possible neighbors in a group or run
+ * Contains the remaining possible values, colors, positions, etc. a stone
+ * can take and its possible neighbors in a group or run
*/
private class StoneState {
int id;
@@ -243,11 +243,11 @@ public class TurnLogic {
* Creates a new
*
* @param id
- * the stone's identifier
+ * the stone's identifier
* @param stone
- * the stone
+ * the stone
* @param table
- * whether is on the table
+ * whether is on the table
*/
public StoneState(int id, Stone stone, boolean table) {
this.id = id;
@@ -268,7 +268,7 @@ public class TurnLogic {
* Creates a copy
*
* @param stone
- * the state to copy
+ * the state to copy
*/
public StoneState(StoneState stone) {
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
- * the changes
- * @return whether the stone is affected
+ * the changes
+ * @return whether the stone could be affected
*/
- public boolean isInterested(HashSet<Integer> changes) {
+ private 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) {
+ /**
+ * 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;
}
- 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);
}
- 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;
}
- 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
|| 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)) {
return false;
}
@@ -340,7 +388,14 @@ public class TurnLogic {
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)) {
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;
changed |= updateRightRuns(changes);
@@ -372,11 +436,29 @@ public class TurnLogic {
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) {
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();
if (mySym == null) {
return;
@@ -387,7 +469,8 @@ public class TurnLogic {
}
StoneState otherStone = top.stones.get(other);
if (!(joker && otherStone.joker || nonNullEquals(value,
- otherStone.value) && nonNullEquals(color, otherStone.color))) {
+ otherStone.value)
+ && nonNullEquals(color, otherStone.color))) {
continue;
}
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() {
if (onTable != Boolean.TRUE) {
return -1;
}
- if (leftRun.size() > 1 || leftGroup.size() > 1 || rightRun.size() > 1
- || rightGroup.size() > 1) {
+ if (leftRun.size() > 1 || leftGroup.size() > 1
+ || rightRun.size() > 1 || rightGroup.size() > 1) {
return null;
}
int mode;
@@ -437,7 +526,17 @@ public class TurnLogic {
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 {
boolean changed = false;
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftRun);
@@ -447,7 +546,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;
@@ -457,7 +557,17 @@ public class TurnLogic {
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 {
boolean changed = false;
HashSet<Integer> relevantChanges = new HashSet<Integer>(rightRun);
@@ -467,7 +577,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;
@@ -477,14 +588,25 @@ public class TurnLogic {
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 {
boolean changed = false;
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftGroup);
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
@@ -498,7 +620,17 @@ public class TurnLogic {
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 {
boolean changed = false;
HashSet<Integer> relevantChanges = new HashSet<Integer>(leftGroup);
@@ -509,7 +641,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;
@@ -519,7 +652,14 @@ public class TurnLogic {
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;
if (onTable == Boolean.FALSE) {
@@ -550,6 +690,12 @@ public class TurnLogic {
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() {
boolean changed = false;
if (!(leftGroup.contains(null) || rightGroup.contains(null))) {
@@ -563,11 +709,18 @@ public class TurnLogic {
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() {
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))) {
@@ -577,6 +730,12 @@ public class TurnLogic {
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() {
boolean changed = false;
if (isSingleNonNullSet(leftRun)
@@ -599,10 +758,19 @@ public class TurnLogic {
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 {
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();
}
@@ -618,6 +786,13 @@ public class TurnLogic {
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 {
if ((isSingleNonNullSet(leftGroup) || isSingleNonNullSet(rightGroup))
&& (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() {
boolean changed = false;
if (this.value == null) {
@@ -640,7 +820,8 @@ public class TurnLogic {
} else if (isSingleNonNullSet(rightRun)) {
Integer value = top.stones.get(rightRun.iterator().next()).value;
if (value != null) {
- this.value = (value - 2) % settings.getHighestValue() + 1;
+ this.value = (value - 2) % settings.getHighestValue()
+ + 1;
}
}
changed |= this.value != null;
@@ -648,6 +829,11 @@ public class TurnLogic {
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() {
boolean changed = false;
if (this.color == null) {
@@ -661,6 +847,11 @@ public class TurnLogic {
return changed;
}
+ /**
+ * Check whether this stone's position, value and color are known
+ *
+ * @return true when this stone is solved
+ */
public boolean isSolved() {
if (onTable == Boolean.FALSE) {
return true;
@@ -668,13 +859,18 @@ 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;
}
+ /**
+ * How badly we want to get rid of this stone
+ *
+ * @return this stone's badness score
+ */
public double getScore() {
if (fromTable) {
return 0;
@@ -682,6 +878,11 @@ public class TurnLogic {
return 1;
}
+ /**
+ * How many points we get for melding this stone
+ *
+ * @return amount of points
+ */
public double getPoints() {
if (fromTable) {
return 0;
@@ -692,13 +893,22 @@ public class TurnLogic {
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")
public boolean needsJoker(boolean side) {
if (onTable != Boolean.TRUE) {
return false;
}
- for (HashSet<Integer> set : side ? Arrays.asList(leftGroup, leftRun)
- : Arrays.asList(rightGroup, rightRun)) {
+ 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) {
@@ -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) {
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) {
return i.size() == 1 && !i.contains(null);
}
@@ -725,11 +949,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) {
@@ -758,17 +982,18 @@ 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;
}
});
@@ -787,6 +1012,9 @@ public class TurnLogic {
neededPoints = settings.getInitialMeldThreshold();
}
+ /**
+ * Remove a contradicted state from the try stack, reset top
+ */
private void pop() {
stack.remove(stack.size() - 1);
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() {
stack.remove(stack.size() - 1);
}
+ /**
+ * Push multiple new state onto the try stack
+ *
+ * @param states
+ * states to push
+ */
private void pushes(State... states) {
for (State state : states) {
stack.add(state);
}
}
+ /**
+ * Done with modifying the try stack, reset top
+ */
private void done() {
top = stack.get(stack.size() - 1);
}
@@ -835,7 +1076,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();
@@ -890,7 +1132,10 @@ public class TurnLogic {
return neededScore;
}
- private void branch() throws Contradiction {
+ /**
+ * Refine the currently unsolved top stack state
+ */
+ private void branch() {
for (int i = 0; i < stoneCount; i++) {
StoneState stone = top.stones.get(i);
if (stone.leftRun.size() > 1) {
@@ -929,6 +1174,12 @@ public class TurnLogic {
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) {
replace();
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) {
replace();
for (StoneColor color : stoneColors) {
@@ -947,8 +1204,15 @@ public class TurnLogic {
newState.changedStones.add(i);
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) {
replace();
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) {
replace();
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) {
replace();
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) {
replace();
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) {
replace();
State newState = new State(top);