package jrummikub.control; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.TreeMap; import jrummikub.model.Stone; import jrummikub.model.StoneColor; import jrummikub.util.Pair; /** * A collection of several AI utility methods * */ public class AIUtil { private AIUtil() { } /** * Finds sets with a certain total of points * * @param pointsMissing * the desired number of points * @param stoneCounts * the number of each stone * @param jokerCount * the total number of jokers in the game * @return whether such sets exist */ @SuppressWarnings("unchecked") public static boolean findSetsWithTotalPoints(int pointsMissing, TreeMap, Integer> stoneCounts, int jokerCount) { if (pointsMissing <= 0) { return true; } stoneCounts = (TreeMap, Integer>) stoneCounts .clone(); for (int value = 13; value >= 1; value--) { for (StoneColor color : StoneColor.values()) { Pair stone = new Pair(value, color); if (stoneCounts.containsKey(stone)) { decrementStoneCount(stoneCounts, stone); if (findRunsWithTotalPoints(pointsMissing - value, stoneCounts, jokerCount, stone, 1)) return true; if (findGroupsWithTotalPoints(pointsMissing - value, stoneCounts, jokerCount, stone, 1)) return true; } if (jokerCount > 0) { if (findRunsWithTotalPoints(pointsMissing - value, stoneCounts, jokerCount - 1, stone, 1)) return true; if (findGroupsWithTotalPoints(pointsMissing - value, stoneCounts, jokerCount - 1, stone, 1)) return true; } } } return false; } static boolean findGroupsWithTotalPoints(int pointsMissing, TreeMap, Integer> stoneCounts, int jokerCount, Pair stone, int groupSize) { StoneColor nextColor = getNextColor(stone.getSecond()); Pair nextStone = new Pair( stone.getFirst(), nextColor); if (nextColor != null) { if (stoneCounts.containsKey(nextStone)) { decrementStoneCount(stoneCounts, nextStone); if (findGroupsWithTotalPoints(pointsMissing - stone.getFirst(), stoneCounts, jokerCount, nextStone, groupSize + 1)) return true; incrementStoneCount(stoneCounts, nextStone); } if (jokerCount > 0) { if (findGroupsWithTotalPoints(pointsMissing - stone.getFirst(), stoneCounts, jokerCount - 1, nextStone, groupSize + 1)) return true; } if (findGroupsWithTotalPoints(pointsMissing, stoneCounts, jokerCount, nextStone, groupSize)) return true; } if (groupSize >= 3) { if (findSetsWithTotalPoints(pointsMissing, stoneCounts, jokerCount)) return true; } return false; } static boolean findRunsWithTotalPoints(int pointsMissing, TreeMap, Integer> stoneCounts, int jokerCount, Pair stone, int runLength) { Pair nextStone = null; if (stone.getFirst() > 1) { int nextValue = stone.getFirst() - 1; nextStone = new Pair(nextValue, stone.getSecond()); if (stoneCounts.containsKey(nextStone)) { decrementStoneCount(stoneCounts, nextStone); if (findRunsWithTotalPoints(pointsMissing - nextValue, stoneCounts, jokerCount, nextStone, runLength + 1)) return true; incrementStoneCount(stoneCounts, nextStone); } if (jokerCount > 0) { if (findRunsWithTotalPoints(pointsMissing - nextValue, stoneCounts, jokerCount - 1, nextStone, runLength + 1)) return true; } } if (runLength >= 3) { if (findSetsWithTotalPoints(pointsMissing, stoneCounts, jokerCount)) return true; } return false; } static void incrementStoneCount( TreeMap, Integer> stones, Pair stone) { if (stones.containsKey(stone)) { stones.put(stone, stones.get(stone) + 1); } else { stones.put(stone, 1); } } static void decrementStoneCount( TreeMap, Integer> stones, Pair stone) { int count = stones.get(stone); count--; if (count == 0) { stones.remove(stone); } else { stones.put(stone, count); } } static StoneColor getNextColor(StoneColor color) { int index = Arrays.binarySearch(StoneColor.values(), color) + 1; if (index >= StoneColor.values().length) { return null; } return StoneColor.values()[index]; } private final static Comparator> comparator = new Comparator>() { @Override public int compare(Pair o1, Pair o2) { int firstComparison = o1.getFirst().compareTo(o2.getFirst()); if (firstComparison != 0) { return -firstComparison; } else { return o1.getSecond().compareTo(o2.getSecond()); } } }; /** * Counts the numbers of stones * * @param stones * the stones to count * @return the numbers for all stones */ public static Pair, Integer>, Integer> countStones( List stones) { int jokerCount = 0; TreeMap, Integer> stoneCounts = new TreeMap, Integer>( comparator); for (Stone stone : stones) { if (stone.isJoker()) { jokerCount++; } else { Pair key = new Pair( stone.getValue(), stone.getColor()); incrementStoneCount(stoneCounts, key); } } return new Pair, Integer>, Integer>( stoneCounts, jokerCount); } }