package jrummikub.ai; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import jrummikub.ai.fdsolver.Constraint; import jrummikub.ai.fdsolver.Solver; import jrummikub.ai.fdsolver.Var; import jrummikub.ai.fdsolver.constraint.IfConstraint; import jrummikub.ai.fdsolver.constraint.IndexConstraint; import jrummikub.ai.fdsolver.constraint.LessThan; import jrummikub.model.GameSettings; import jrummikub.model.Stone; import jrummikub.model.StoneColor; import static jrummikub.ai.fdsolver.Constraints.*; /** * Provides Humanlike Advanced Logic (HAL) that models the options given to a * player in a turn to the Solver MCP. */ public class TurnLogic { private Solver solver = new Solver(); private GameSettings settings; private int stoneCount; private int maxSetSize; private Var trueVar; private List> onTable = new ArrayList>(); private List> stoneValue = new ArrayList>(); private List> leftStoneValue = new ArrayList>(); private List> rightStoneValue = new ArrayList>(); private List> stoneColor = new ArrayList>(); private List> leftStoneColor = new ArrayList>(); private List> rightStoneColor = new ArrayList>(); private List> lowCount = new ArrayList>(); private List> leftLowCount = new ArrayList>(); private List> rightLowCount = new ArrayList>(); private List> highCount = new ArrayList>(); private List> leftHighCount = new ArrayList>(); private List> rightHighCount = new ArrayList>(); private List> setSize = new ArrayList>(); private List> isRun = new ArrayList>(); private List> leftNeighbor = new ArrayList>(); private List> rightNeighbor = new ArrayList>(); private List> stoneID = new ArrayList>(); private List> hasLeftNeighbor = new ArrayList>(); private List> hasRightNeighbor = new ArrayList>(); private List isJoker = new ArrayList(); public TurnLogic(GameSettings settings, Collection tableStones, Collection handStones) { this.settings = settings; stoneCount = tableStones.size() + handStones.size(); maxSetSize = Math.max(settings.getHighestValue(), settings .getStoneColors().size()); trueVar = solver.makeVar(true); int i = 0; for (Stone stone : tableStones) { addStone(i, true, stone); i++; } for (Stone stone : handStones) { addStone(i, false, stone); i++; } for (i = 0; i < stoneCount; i++) { addConstraints(i); } } private void addStone(int i, boolean table, Stone stone) { if (table) { onTable.add(trueVar); } else { onTable.add(solver.makeBoolVar()); } isJoker.add(stone.isJoker()); if (stone.isJoker()) { stoneValue.add(solver.makeRangeVar(1, settings.getHighestValue())); stoneColor.add(solver.makeVar(settings.getStoneColors())); } else { stoneValue.add(solver.makeVar(stone.getValue())); stoneColor.add(solver.makeVar(stone.getColor())); } leftStoneValue.add(solver.makeRangeVar(1, settings.getHighestValue())); rightStoneValue.add(solver.makeRangeVar(1, settings.getHighestValue())); leftStoneColor.add(solver.makeVar(settings.getStoneColors())); rightStoneColor.add(solver.makeVar(settings.getStoneColors())); lowCount.add(solver.makeRangeVar(0, maxSetSize - 1)); leftLowCount.add(solver.makeRangeVar(0, maxSetSize - 1)); rightLowCount.add(solver.makeRangeVar(0, maxSetSize - 1)); highCount.add(solver.makeRangeVar(0, maxSetSize - 1)); leftHighCount.add(solver.makeRangeVar(0, maxSetSize - 1)); rightHighCount.add(solver.makeRangeVar(0, maxSetSize - 1)); setSize.add(solver.makeRangeVar(2, maxSetSize - 1)); isRun.add(solver.makeBoolVar()); leftNeighbor.add(solver.makeRangeVar(0, stoneCount - 1)); rightNeighbor.add(solver.makeRangeVar(0, stoneCount - 1)); stoneID.add(solver.makeVar(i)); hasLeftNeighbor.add(solver.makeBoolVar()); hasRightNeighbor.add(solver.makeBoolVar()); } private void add(Constraint c) { solver.addConstraint(c); } private void addConstraints(int i) { // Linking of neighbors add(when(hasLeftNeighbor.get(i), index(stoneID.get(i), leftNeighbor.get(i), rightNeighbor))); add(unless(hasLeftNeighbor.get(i), constant(leftNeighbor.get(i), 0))); add(when(hasLeftNeighbor.get(i), index(trueVar, leftNeighbor.get(i), hasRightNeighbor))); add(when(hasRightNeighbor.get(i), index(stoneID.get(i), rightNeighbor.get(i), leftNeighbor))); add(unless(hasRightNeighbor.get(i), constant(rightNeighbor.get(i), 0))); add(when(hasRightNeighbor.get(i), index(trueVar, rightNeighbor.get(i), hasLeftNeighbor))); // hand stones have no neighbors add(unless(onTable.get(i), constant(hasLeftNeighbor.get(i), false))); add(unless(onTable.get(i), constant(hasRightNeighbor.get(i), false))); // low/high counts and size add(unless(hasLeftNeighbor.get(i), constant(lowCount.get(i), 0))); add(unless(hasRightNeighbor.get(i), constant(highCount.get(i), 0))); add(when(hasLeftNeighbor.get(i), index(leftLowCount.get(i), leftNeighbor.get(i), lowCount))); add(unless(hasLeftNeighbor.get(i), constant(leftLowCount.get(i), 0))); add(when(hasRightNeighbor.get(i), index(rightLowCount.get(i), rightNeighbor.get(i), lowCount))); add(unless(hasRightNeighbor.get(i), constant(rightLowCount.get(i), 0))); add(when(hasLeftNeighbor.get(i), index(leftHighCount.get(i), leftNeighbor.get(i), highCount))); add(unless(hasLeftNeighbor.get(i), constant(leftHighCount.get(i), 0))); add(when(hasRightNeighbor.get(i), index(rightHighCount.get(i), rightNeighbor.get(i), highCount))); add(unless(hasRightNeighbor.get(i), constant(rightHighCount.get(i), 0))); add(when(hasLeftNeighbor.get(i), index(setSize.get(i), leftNeighbor.get(i), setSize))); add(when(hasRightNeighbor.get(i), index(setSize.get(i), rightNeighbor.get(i), setSize))); add(when(hasLeftNeighbor.get(i), offset(1, leftLowCount.get(i), lowCount.get(i)))); add(when(hasRightNeighbor.get(i), offset(1, lowCount.get(i), rightLowCount.get(i)))); add(when(hasRightNeighbor.get(i), offset(1, rightHighCount.get(i), highCount.get(i)))); add(when(hasLeftNeighbor.get(i), offset(1, highCount.get(i), leftHighCount.get(i)))); add(when(onTable.get(i), sum(lowCount.get(i), highCount.get(i), setSize.get(i)))); add(unless(onTable.get(i), constant(setSize.get(i), 2))); // set same rules add(when(hasLeftNeighbor.get(i), index(isRun.get(i), leftNeighbor.get(i), isRun))); add(when(hasRightNeighbor.get(i), index(isRun.get(i), rightNeighbor.get(i), isRun))); add(unless(onTable.get(i), constant(isRun.get(i), false))); // rule neighbors add(when(hasLeftNeighbor.get(i), index(leftStoneColor.get(i), leftNeighbor.get(i), stoneColor))); add(unless(hasLeftNeighbor.get(i), constant(leftStoneColor.get(i), StoneColor.RED))); add(when(hasRightNeighbor.get(i), index(rightStoneColor.get(i), rightNeighbor.get(i), stoneColor))); add(unless(hasRightNeighbor.get(i), constant(rightStoneColor.get(i), StoneColor.RED))); add(when(hasLeftNeighbor.get(i), index(leftStoneValue.get(i), leftNeighbor.get(i), stoneValue))); add(unless(hasLeftNeighbor.get(i), constant(leftStoneValue.get(i), 1))); add(when(hasRightNeighbor.get(i), index(rightStoneValue.get(i), rightNeighbor.get(i), stoneValue))); add(unless(hasRightNeighbor.get(i), constant(rightStoneValue.get(i), 1))); // general rules add(when( hasLeftNeighbor.get(i), when(isRun.get(i), lessThanEq(leftStoneValue.get(i), stoneValue.get(i))))); add(when( hasRightNeighbor.get(i), when(isRun.get(i), lessThanEq(stoneValue.get(i), rightStoneValue.get(i))))); add(when( hasLeftNeighbor.get(i), when(isRun.get(i), lessThanEq(leftStoneColor.get(i), stoneColor.get(i))))); add(when( hasRightNeighbor.get(i), when(isRun.get(i), lessThanEq(stoneColor.get(i), rightStoneColor.get(i))))); // run rules add(when( hasLeftNeighbor.get(i), when(isRun.get(i), offset(1, leftStoneValue.get(i), stoneValue.get(i))))); add(when( hasRightNeighbor.get(i), when(isRun.get(i), offset(1, stoneValue.get(i), rightStoneValue.get(i))))); add(when( hasLeftNeighbor.get(i), when(isRun.get(i), same(leftStoneColor.get(i), stoneColor.get(i))))); add(when( hasRightNeighbor.get(i), when(isRun.get(i), same(stoneColor.get(i), rightStoneColor.get(i))))); // group rules add(when( hasLeftNeighbor.get(i), unless(isRun.get(i), same(leftStoneValue.get(i), stoneValue.get(i))))); add(when( hasRightNeighbor.get(i), unless(isRun.get(i), same(stoneValue.get(i), rightStoneValue.get(i))))); add(when( hasLeftNeighbor.get(i), unless(isRun.get(i), lessThan(leftStoneColor.get(i), stoneColor.get(i))))); add(when( hasRightNeighbor.get(i), unless(isRun.get(i), lessThan(stoneColor.get(i), rightStoneColor.get(i))))); // joker defaulting if (isJoker.get(i)) { add(unless(onTable.get(i), constant(stoneValue.get(i), 1))); add(unless(onTable.get(i), constant(stoneColor.get(i), StoneColor.RED))); } } public boolean solve() { Set tableIDs = new HashSet(); Set leftIDs = new HashSet(); boolean res = solver.solve(); if (!res) return false; System.out.println("== Hand =="); for (int i = 0; i < stoneCount; i++) { if (onTable.get(i).getValue()) { tableIDs.add(i); if (!hasLeftNeighbor.get(i).getValue()) { leftIDs.add(i); } } else { outputStone(i); } } System.out.println(""); System.out.println("== Table =="); boolean newLine = false; boolean first = true; int nextID = -1; // leftIDs.iterator().next(); while (!tableIDs.isEmpty()) { if (tableIDs.contains(nextID)) { tableIDs.remove(nextID); leftIDs.remove(nextID); if (newLine) { if (!first) { System.out.println(""); } first = false; newLine = false; } outputStone(nextID); if (!hasRightNeighbor.get(nextID).getValue()) { newLine = true; nextID = -1; } else { nextID = rightNeighbor.get(nextID).getValue(); } } else { if (leftIDs.isEmpty()) { nextID = tableIDs.iterator().next(); } else { nextID = leftIDs.iterator().next(); } newLine = true; } } System.out.println(""); System.out.println(""); return res; } private void outputStone(int i) { System.out.print("[" + stoneColor.get(i).getValue().toString().substring(0, 1) + stoneValue.get(i).getValue() + "]"); // + "," + leftNeighbor.get(i).getValue() + // hasLeftNeighbor.get(i).getValue() + lowCount.get(i).getValue() + "," // + rightNeighbor.get(i).getValue() + // hasRightNeighbor.get(i).getValue() + highCount.get(i).getValue() + // "]"); } }