package jrummikub.model; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import jrummikub.util.Pair; /** * A StoneTray is a collection of positioned objects (for example {@link Stone}s * or {@link StoneSet}s. * * @param * Type of positioned objects (must implement Sizeable) */ public class StoneTray implements Iterable> { protected ArrayList> objects = new ArrayList>(); /** Possible move directions in case of overlapping Stones/Sets */ private static enum Direction { LEFT, RIGHT, TOP, BOTTOM; } /** * Removes object from tray and returns it * * @param position * position of the object that will be removed * @return the picked up stone */ public E pickUp(Position position) { for (Pair i : objects) { Position currentPosition = i.getSecond(); E currentObject = i.getFirst(); // Tests if position is left of, above ... the current object if (position.getX() < currentPosition.getX()) { continue; } if (position.getY() < currentPosition.getY()) { continue; } if (position.getX() > currentPosition.getX() + currentObject.getWidth()) { continue; } if (position.getY() > currentPosition.getY() + currentObject.getHeight()) { continue; } // Position is inside the current object objects.remove(i); return currentObject; } return null; } /** * Adds object to the tray * * @param object * object to add to Hand * @param position * {@link Position} to put the object */ public void drop(E object, Position position) { drop(object, position, null); } @SuppressWarnings("unchecked") private void drop(E object, Position position, Direction direction) { for (Pair i : (List>)objects.clone()) { Position currentPosition = i.getSecond(); E currentObject = i.getFirst(); if (!objectsOverlap(object, position, currentObject, currentPosition)) { continue; } // Object would be placed inside the current object if (direction == null) { direction = getMoveDirection(object, position, i); } Position newPosition = null; // TODO dinge bewegen switch (direction) { case TOP: newPosition = new Position(i.getSecond().getX(), position.getY() - currentObject.getHeight()); break; case BOTTOM: newPosition = new Position(currentPosition.getX(), position.getY() + object.getHeight()); break; case LEFT: newPosition = new Position(position.getX() - currentObject.getWidth(), currentPosition.getY()); break; case RIGHT: newPosition = new Position(position.getX() + object.getWidth(), currentPosition.getY()); break; } objects.remove(i); drop(currentObject, newPosition, direction); } objects.add(new Pair(object, position)); } /** Tests whether two objects overlap **/ private boolean objectsOverlap(E object1, Position position1, E object2, Position position2) { // Tests if position is left of, above ... the current object if (position1.getX() + object1.getWidth() <= position2.getX()) { return false; } if (position1.getY() + object1.getHeight() <= position2.getY()) { return false; } if (position1.getX() >= position2.getX() + object2.getWidth()) { return false; } if (position1.getY() >= position2.getY() + object2.getHeight()) { return false; } return true; } private Direction getMoveDirection(E object, Position position, Pair blocking) { boolean isVertical = getMoveOrientationn(object, position, blocking); float objectMidpointX = position.getX() + object.getWidth() / 2; float objectMidpointY = position.getY() + object.getHeight() / 2; float blockingMidpointX = blocking.getSecond().getX() + blocking.getFirst().getWidth() / 2; float blockingMidpointY = blocking.getSecond().getY() + blocking.getFirst().getHeight() / 2; if (isVertical) { if (objectMidpointY < blockingMidpointY) { return Direction.BOTTOM; } else { return Direction.TOP; } } else { if (objectMidpointX < blockingMidpointX) { return Direction.RIGHT; } else { return Direction.LEFT; } } } private boolean getMoveOrientationn(E object, Position position, Pair blocking) { float objectRight = position.getX() + object.getWidth(); float blockingRight = blocking.getSecond().getX() + blocking.getFirst().getWidth(); float overlapRight = Math.min(objectRight, blockingRight); float overlapLeft = Math.max(position.getX(), blocking.getSecond() .getX()); float overlapX = overlapRight - overlapLeft; float objectBottom = position.getY() + object.getHeight(); float blockingBottom = blocking.getSecond().getY() + blocking.getFirst().getHeight(); float overlapBottom = Math.min(objectBottom, blockingBottom); float overlapTop = Math.max(position.getY(), blocking.getSecond() .getY()); float overlapY = overlapBottom - overlapTop; // vertical or horizontal Shift // TODO magic factor return overlapX > overlapY; } /** * Returns the position of an object that is already on the tray * * @param object * object whose position is requested * @return position of the object or null when the object is not on the tray */ public Position getPosition(E object) { for (Pair i : objects) { if (object.equals(i.getFirst())) { return i.getSecond(); } } return null; } @Override public Iterator> iterator() { return objects.iterator(); } }