package jrummikub.model; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import jrummikub.util.Pair; import static jrummikub.model.StoneSet.Type.*; /** Class managing {@link Stone}s joined together to form sets */ public class StoneSet implements Iterable, Sizeable { static final float VERTICAL_BORDER = 0.5f; static final float HORIZONTAL_BORDER = 0.125f; private List stones; public StoneSet(Stone stone) { stones = Collections.singletonList(stone); } public StoneSet(List stones) { this.stones = new ArrayList(stones); } public enum Type { GROUP, RUN, INVALID } /** * Test for rule conflict within the StoneSet * * @return true when the set is valid according to the rules */ public boolean isValid() { return classify() != INVALID; } /** * Test for rule conflict within the StoneSet and determine whether the set is * a group or a run * * @return GROUP or RUN for valid sets, INVALID otherwise */ public Type classify() { // TODO: extend this for score calculation (release 2...) if (stones.size() < 3) { return INVALID; } int nonJoker1 = -1, nonJoker2 = -1; for (int i = 0; i < stones.size(); i++) { if (stones.get(i).isJoker()) { continue; } nonJoker2 = nonJoker1; nonJoker1 = i; } if (nonJoker2 == -1) { return GROUP; } // is run if (stones.get(nonJoker1).getColor() == stones.get(nonJoker2).getColor()) { return isValidRun(nonJoker1) ? RUN : INVALID; } // is group else { return isValidGroup() ? GROUP : INVALID; } } /** * Test for rule conflict within the StoneSet, assuming we have a run * * @param referencePosition * position of stone used as reference (any non-joker stone) */ private boolean isValidRun(int referencePosition) { StoneColor runColor = stones.get(referencePosition).getColor(); int startValue = stones.get(referencePosition).getValue() - referencePosition; int endValue = startValue + stones.size() - 1; if (startValue < 1 || endValue > 13) { return false; } for (int i = 0; i < stones.size(); i++) { if (stones.get(i).isJoker()) { continue; } if (stones.get(i).getColor() != runColor) { // warum macht er das nicht? return false; } if (stones.get(i).getValue() != i + startValue) { return false; } } return true; } /** * Test for rule conflict within the StoneSet, assuming we have a group */ private boolean isValidGroup() { if (stones.size() > 4) { return false; } Set seenColors = new HashSet(); for (Stone i : stones) { if (i.isJoker()) { continue; } if (seenColors.contains(i.getColor())) { return false; } else { seenColors.add(i.getColor()); } } return true; } /** * Splits the StoneSet at a specified {@link Position} and returns two new * Stone Sets * * @param position * Splitting {@link Position} * @return A pair of StoneSets, one for each split part */ public Pair splitAt(int position) { if (position == 0) { return new Pair(null, this); } else if (position == stones.size()) { return new Pair(this, null); } StoneSet firstSet = new StoneSet(stones.subList(0, position)); StoneSet secondSet = new StoneSet(stones.subList(position, stones.size())); return new Pair(firstSet, secondSet); } /** * Joins StoneSet to another StoneSet and returns the resulting new StoneSet * * @param other * StoneSet to be joined to active StoneSet * @return the combined StoneSet */ public StoneSet join(StoneSet other) { List joinedList = new ArrayList(); joinedList.addAll(stones); joinedList.addAll(other.stones); return new StoneSet(joinedList); } /** * Returns the number of stones in the set. * * @return number of stones */ public int size() { return stones.size(); } /** * Returns the i-th stone of the set (starting with 0) * * @param i * number of the stone to return * @return the i-th stone */ public Stone get(int i) { return stones.get(i); } @Override public Iterator iterator() { final Iterator it = stones.iterator(); return new Iterator() { @Override public boolean hasNext() { return it.hasNext(); } @Override public Stone next() { return it.next(); } @Override public void remove() { // removing stones is impossible throw new NotImplementedException(); } }; } @Override public float getWidth() { return stones.size() + 2 * VERTICAL_BORDER; } @Override public float getHeight() { return 1 + 2 * HORIZONTAL_BORDER; } @Override public String toString() { String ret = "StoneSet["; boolean first = true; for (Stone stone : stones) { if (!first) ret += ","; ret += stone.toString(); first = false; } return ret + "]"; } }