package jrummikub.model; import static jrummikub.model.StoneTray.Direction.LEFT; import static jrummikub.model.StoneTray.Direction.RIGHT; import java.util.Arrays; import java.util.Comparator; import java.util.TreeMap; import jrummikub.util.Pair; /** Class managing a {@link Player}'s {@link Stone}s */ public class Hand extends StoneTray implements IHand { /** * The width of the hand */ public final static int WIDTH = 14; private GameSettings settings; /** * Create a new empty hand with given game settings * * @param settings * the game settings */ public Hand(GameSettings settings) { this.settings = settings; } @Override public int getFreeRowSpace(int row) { int count = 0; for (Pair entry : this) { if (entry.getSecond().getY() == row) { count++; } } return WIDTH - count; } @Override public int getRowCount() { int rows = 0; for (Pair entry : this) { if (entry.getSecond().getY() > rows) { rows = (int) entry.getSecond().getY(); } } return rows + 1; } @Override protected Pair fixInvalidDrop(Stone stone, Position pos, Direction dir) { float x = pos.getX(); float y = pos.getY(); if (x >= 0 && x <= WIDTH - 1) { return null; } if (x < 0) { return new Pair(new Position(0, y), RIGHT); } else { if (getFreeRowSpace((int) y) == 0) { return new Pair(new Position(0, y + 1), RIGHT); } else { return new Pair( new Position(WIDTH - 1, y), LEFT); } } } public int getStonePoints() { int points = 0; for (Pair entry : this) { if (entry.getFirst().isJoker()) { points += settings.getJokerPoints(); } else { points += entry.getFirst().getValue(); } } return points; } 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()); } } }; @Override public boolean isInitialMeldPossible() { Pair, Integer>, Integer> stoneCounts = countStones(); return findSetsWithTotalPoints(settings.getInitialMeldThreshold(), stoneCounts.getFirst(), stoneCounts.getSecond()); } private Pair, Integer>, Integer> countStones() { int jokerCount = 0; TreeMap, Integer> stoneCounts = new TreeMap, Integer>( comparator); for (Pair entry : this) { if (entry.getFirst().isJoker()) { jokerCount++; } else { Pair key = new Pair( entry.getFirst().getValue(), entry.getFirst() .getColor()); incrementStoneCount(stoneCounts, key); } } return new Pair, Integer>, Integer>(stoneCounts, jokerCount); } private void incrementStoneCount( TreeMap, Integer> stones, Pair stone) { if (stones.containsKey(stone)) { stones.put(stone, stones.get(stone) + 1); } else { stones.put(stone, 1); } } private void decrementStoneCount( TreeMap, Integer> stones, Pair stone) { int count = stones.get(stone); count--; if (count == 0) { stones.remove(stone); } else { stones.put(stone, count); } } @SuppressWarnings("unchecked") private 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; } private StoneColor getNextColor(StoneColor color) { int index = Arrays.binarySearch(StoneColor.values(), color) + 1; if (index >= StoneColor.values().length) { return null; } return StoneColor.values()[index]; } private 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; } private 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; } @Override public int getIdenticalStoneCount() { Pair, Integer>, Integer> stoneCounts = countStones(); int pairCount = 0; for(int count : stoneCounts.getFirst().values()) { pairCount += count / 2; } return pairCount; } }