package jrummikub.control.turn; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import jrummikub.control.ITurnTimer; import jrummikub.control.RoundControl.InvalidTurnInfo; import jrummikub.control.RoundControl.InvalidTurnType; import jrummikub.control.TurnTimer; import jrummikub.model.GameSettings; import jrummikub.model.Hand; import jrummikub.model.IHand; import jrummikub.model.IRoundState; import jrummikub.model.ITable; import jrummikub.model.Position; import jrummikub.model.Stone; import jrummikub.model.StoneSet; import jrummikub.util.Connection; import jrummikub.util.Event; import jrummikub.util.Event1; import jrummikub.util.Event2; import jrummikub.util.IEvent; import jrummikub.util.IEvent1; import jrummikub.util.IEvent2; import jrummikub.util.IListener; import jrummikub.util.Pair; import jrummikub.view.IView; /** * Abstract base class for TurnControls */ public abstract class AbstractTurnControl implements ITurnControl { protected Event2 endOfTurnEvent = new Event2(); protected Event redealEvent = new Event(); protected Event1 tableUpdateEvent = new Event1(); protected TurnInfo turnInfo; protected GameSettings settings; protected IView view; protected ITurnTimer timer; protected List connections = new ArrayList(); private boolean started = false; @Override public IEvent2 getEndOfTurnEvent() { return endOfTurnEvent; } @Override public IEvent getRedealEvent() { return redealEvent; } @Override public IEvent1 getTableUpdateEvent() { return tableUpdateEvent; } protected void pauseTurn() { if (!turnInfo.isMayPause()) { return; } timer.stopTimer(); view.enablePauseMode(true); } protected void resumeTurn() { timer.startTimer(); view.enablePauseMode(false); } @Override public void startTurn() { if (started) { return; } started = true; doStartTurn(); } protected abstract void doStartTurn(); protected abstract void timeOut(); @Override public void setup(TurnInfo info, GameSettings settings, IView view) { turnInfo = info; this.settings = settings; this.view = view; if (timer == null) { timer = new TurnTimer(view, settings.getTotalTime()); } connections.add(timer.getTimeRunOutEvent().add(new IListener() { @Override public void handle() { timeOut(); } })); connections.add(view.getPauseEvent().add(new IListener() { @Override public void handle() { pauseTurn(); } })); connections.add(view.getEndPauseEvent().add(new IListener() { @Override public void handle() { resumeTurn(); } })); view.setMayPause(info.isMayPause()); } protected void cleanUp() { if (timer != null) { timer.stopTimer(); } started = true; for (Connection c : connections) { c.remove(); } } public void abortTurn() { cleanUp(); } protected InvalidTurnInfo checkTurn() { turnInfo.getRoundState().getActivePlayer().setLastTurnInvalid(false); turnInfo .getRoundState() .getActivePlayer() .setLastTurnStoneCount( turnInfo.getOldHand().getSize() - turnInfo.getHand().getSize()); turnInfo.getRoundState().getActivePlayer().setHand(turnInfo.getHand()); if (turnInfo.getRoundState().getTurnNumber() < 1) { return new InvalidTurnInfo(turnInfo.getTable(), null, Collections. emptyList()); } if (!turnInfo.getTable().isValid()) { rejectMove(); return new InvalidTurnInfo(turnInfo.getTable(), InvalidTurnType.INVALID_SETS, invalidStoneSets()); } if (!turnInfo.getLaidOut()) { // Player touched forbidden stones if (!tableSetDifference(turnInfo.getTable(), turnInfo.getOldTable()) .isEmpty()) { rejectMove(); return new InvalidTurnInfo(turnInfo.getTable(), InvalidTurnType.INITIAL_MELD_ERROR, touchedStoneSets()); } if (!laidOutValidPoints()) { rejectMove(); return new InvalidTurnInfo(turnInfo.getTable(), InvalidTurnType.NOT_ENOUGH_POINTS, tableSetDifference( turnInfo.getOldTable(), turnInfo.getTable())); } } Set tableDiff = tableDifference(turnInfo.getOldTable(), turnInfo.getTable()); turnInfo.getRoundState().setTable(turnInfo.getTable()); if (tableDiff.isEmpty()) { // Player hasn't made a move dealStone(); } else { turnInfo.getRoundState().getActivePlayer().setLaidOut(true); } return new InvalidTurnInfo(turnInfo.getTable(), null, Collections. emptyList()); } private List invalidStoneSets() { List invalidSets = new ArrayList(); for (Pair set : turnInfo.getTable()) { if (set.getFirst().isValid(turnInfo.getRoundState().getGameSettings())) { continue; } invalidSets.add(set.getFirst()); } return invalidSets; } private List touchedStoneSets() { List touchedSets = new ArrayList(); for (StoneSet set : tableSetDifference(turnInfo.getOldTable(), turnInfo.getTable())) { for (Stone stone : set) { if (!turnInfo.getOldHand().contains(stone)) { touchedSets.add(set); break; } } } return touchedSets; } private boolean laidOutValidPoints() { List newSets = tableSetDifference(turnInfo.getOldTable(), turnInfo.getTable()); int totalValue = 0; for (StoneSet set : newSets) { totalValue += set.classify(turnInfo.getRoundState().getGameSettings()) .getSecond(); } return totalValue == 0 || totalValue >= turnInfo.getRoundState().getGameSettings() .getInitialMeldThreshold(); } private void rejectMove() { Set tableDiff = tableDifference(turnInfo.getOldTable(), turnInfo.getTable()); // deal penalty, reset turnInfo.getRoundState().getStoneHeap().putBack(tableDiff); turnInfo.getRoundState().getActivePlayer().setLastTurnInvalid(true); dealPenalty(tableDiff.size()); } private void dealStones(int count) { IHand hand = turnInfo.getRoundState().getActivePlayer().getHand(); int rowCount = hand.getRowCount(); for (int i = 0; i < count; ++i) { if (hand.getFreeRowSpace(rowCount - 1) == 0) { rowCount++; } hand.drop(turnInfo.getRoundState().getStoneHeap().drawStone(), new Position(Hand.WIDTH - 1, rowCount - 1)); } } private void dealStone() { dealStones(1); } private void dealPenalty(int count) { dealStones(count + 3); } static Set tableDifference(ITable oldTable, ITable newTable) { Set ret = new HashSet(); for (Pair entry : newTable) { for (Stone stone : entry.getFirst()) { ret.add(stone); } } for (Pair entry : oldTable) { for (Stone stone : entry.getFirst()) { ret.remove(stone); } } return ret; } static List tableSetDifference(ITable oldTable, ITable newTable) { List ret = new ArrayList(); for (Pair entry : newTable) { ret.add(entry.getFirst()); } for (Pair entry : oldTable) { ret.remove(entry.getFirst()); } return ret; } }