diff options
Diffstat (limited to 'src/jrummikub/control')
-rw-r--r-- | src/jrummikub/control/RoundControl.java | 254 | ||||
-rw-r--r-- | src/jrummikub/control/network/ConnectionControl.java | 53 | ||||
-rw-r--r-- | src/jrummikub/control/network/IConnectionControl.java | 12 | ||||
-rw-r--r-- | src/jrummikub/control/network/NetworkRoundControl.java | 27 | ||||
-rw-r--r-- | src/jrummikub/control/network/NetworkTurnControl.java | 18 | ||||
-rw-r--r-- | src/jrummikub/control/turn/AIControl.java | 2 | ||||
-rw-r--r-- | src/jrummikub/control/turn/AbstractTurnControl.java | 169 | ||||
-rw-r--r-- | src/jrummikub/control/turn/HumanTurnControl.java | 2 | ||||
-rw-r--r-- | src/jrummikub/control/turn/ITurnControl.java | 68 |
9 files changed, 332 insertions, 273 deletions
diff --git a/src/jrummikub/control/RoundControl.java b/src/jrummikub/control/RoundControl.java index 778c3d0..aeff095 100644 --- a/src/jrummikub/control/RoundControl.java +++ b/src/jrummikub/control/RoundControl.java @@ -2,18 +2,17 @@ package jrummikub.control; import static jrummikub.model.PlayerSettings.Type.*; +import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import jrummikub.control.turn.AIControl; import jrummikub.control.turn.HumanTurnControl; import jrummikub.control.turn.ITurnControl; import jrummikub.control.turn.TurnControlFactory; import jrummikub.control.turn.TurnMode; -import jrummikub.model.Hand; import jrummikub.model.IHand; import jrummikub.model.IPlayer; import jrummikub.model.IRoundState; @@ -29,7 +28,7 @@ import jrummikub.util.Event1; import jrummikub.util.IEvent; import jrummikub.util.IEvent1; import jrummikub.util.IListener; -import jrummikub.util.IListener3; +import jrummikub.util.IListener2; import jrummikub.util.Pair; import jrummikub.view.IView; import jrummikub.view.IView.BottomPanelType; @@ -38,18 +37,45 @@ import jrummikub.view.IView.BottomPanelType; * Controller that manages a single round of rummikub */ public class RoundControl { + public enum InvalidTurnType { + INVALID_SETS, INITIAL_MELD_ERROR, NOT_ENOUGH_POINTS + } + + public static class InvalidTurnInfo implements Serializable { + private static final long serialVersionUID = -3591000741414366776L; + + private ITable table; + private InvalidTurnType type; + private ArrayList<StoneSet> invalidSets; + + public InvalidTurnInfo(ITable table, InvalidTurnType type, + Collection<StoneSet> invalidSets) { + this.table = (ITable) table.clone(); + this.type = type; + this.invalidSets = new ArrayList<StoneSet>(invalidSets); + } + + public ITable getTable() { + return table; + } + + public InvalidTurnType getType() { + return type; + } + + public List<StoneSet> getInvalidSets() { + return invalidSets; + } + } + private ITurnControl turnControl; protected IRoundState roundState; private IView view; - private ITable clonedTable; - IHand clonedHand; private Event restartRoundEvent = new Event(); private Event1<IRoundState> roundStateUpdateEvent = new Event1<IRoundState>(); private Event1<Score> endOfRoundEvent = new Event1<Score>(); protected List<Connection> connections = new ArrayList<Connection>(); private boolean roundFinished; - private boolean lastTurnNotEnoughPoints; - private boolean lastTurnMeldError; /** * Create a new RoundControl using the given gameState and view @@ -183,13 +209,10 @@ public class RoundControl { view.getTablePanel().setStoneSets(roundState.getTable().clone()); - clonedTable = (ITable) roundState.getTable().clone(); - clonedHand = (IHand) roundState.getActivePlayer().getHand().clone(); - if (roundState.getTurnNumber() < 1) { view.setStoneCollectionHidden(true); turnMode = TurnMode.INSPECT_ONLY; - if (clonedHand.getIdenticalStoneCount() >= 3) { + if (roundState.getActivePlayer().getHand().getIdenticalStoneCount() >= 3) { turnMode = TurnMode.MAY_REDEAL; } } @@ -198,21 +221,15 @@ public class RoundControl { view.getPlayerPanel().setEndTurnMode(turnMode); } - turnControl.setup(new ITurnControl.TurnInfo(clonedTable, clonedHand, - roundState.getActivePlayer().getLaidOut(), turnMode), roundState - .getGameSettings(), view); + turnControl.setup(new ITurnControl.TurnInfo(roundState, turnMode), + roundState.getGameSettings(), view); turnControl.getEndOfTurnEvent().add( - new IListener3<IHand, ITable, ITable>() { + new IListener2<IRoundState, InvalidTurnInfo>() { @Override - public void handle(IHand oldHand, ITable oldTable, ITable newTable) { - if (oldHand == null) { - oldHand = roundState.getActivePlayer().getHand(); - } - if (oldTable == null) { - oldTable = roundState.getTable(); - } - - endOfTurn(oldHand, oldTable, newTable); + public void handle(IRoundState newState, + InvalidTurnInfo invalidTurnInfo) { + setRoundState(newState); + endOfTurn(invalidTurnInfo); } }); turnControl.getRedealEvent().add(new IListener() { @@ -258,87 +275,46 @@ public class RoundControl { view.getSidePanel().setHeapSize(roundState.getGameHeap().getSize()); } - private boolean laidOutValidPoints(ITable oldTable, ITable newTable) { - List<StoneSet> newSets = tableSetDifference(oldTable, newTable); - - int totalValue = 0; - for (StoneSet set : newSets) { - totalValue += set.classify(roundState.getGameSettings()).getSecond(); - } - - return totalValue == 0 - || totalValue >= roundState.getGameSettings().getInitialMeldThreshold(); - } - - protected void endOfTurn(IHand oldHand, ITable oldTable, ITable newTable) { + protected void endOfTurn(InvalidTurnInfo invalidTurnInfo) { boolean wasHuman = (turnControl instanceof HumanTurnControl); boolean wasAI = (turnControl instanceof AIControl); - - view.setBottomPanel(BottomPanelType.NONHUMAN_HAND_PANEL); - turnControl = null; - roundState.getActivePlayer().setLastTurnInvalid(false); - roundState.getActivePlayer() - .setLastTurnStoneCount( - roundState.getActivePlayer().getHand().getSize() - - clonedHand.getSize()); - - roundState.getActivePlayer().setHand(clonedHand); - boolean goToNextPlayer = true; - lastTurnNotEnoughPoints = false; - lastTurnMeldError = false; - if (roundState.getTurnNumber() >= 1) { - goToNextPlayer = checkTurn(oldTable, newTable); - } - if (goToNextPlayer || wasAI) { - nextPlayer(); - } else { + view.getTablePanel().setStoneSets(invalidTurnInfo.getTable()); + + if (invalidTurnInfo.getType() != null) { if (wasHuman) { view.setBottomPanel(BottomPanelType.INVALID_TURN_PANEL); } - if (lastTurnNotEnoughPoints) { - view.setInitialMeldError(roundState.getGameSettings() - .getInitialMeldThreshold()); - view.getTablePanel().setStoneSets(newTable); - view.setInvalidStoneSets(tableSetDifference(oldTable, newTable)); - } else if (lastTurnMeldError) { - view.setInitialMeldFirstError(); - view.getTablePanel().setStoneSets(newTable); - view.setInvalidStoneSets(touchedStoneSets(oldHand, oldTable, newTable)); - } else { - view.setStoneCollectionHidden(true); - view.getTablePanel().setStoneSets(newTable); - view.setInvalidStoneSets(invalidStoneSets(newTable)); + view.setInvalidStoneSets(invalidTurnInfo.getInvalidSets()); + + switch (invalidTurnInfo.getType()) { + case INITIAL_MELD_ERROR: + view.setInitialMeldFirstError(); + break; + case INVALID_SETS: + view.setStoneCollectionHidden(true); + break; + case NOT_ENOUGH_POINTS: + view.setInitialMeldError(roundState.getGameSettings() + .getInitialMeldThreshold()); + break; } - } - } - private List<StoneSet> invalidStoneSets(ITable newTable) { - List<StoneSet> invalidSets = new ArrayList<StoneSet>(); - for (Pair<StoneSet, Position> set : newTable) { - if (set.getFirst().isValid(roundState.getGameSettings())) { - continue; + if (wasAI) { + nextPlayer(); } - invalidSets.add(set.getFirst()); + return; } - return invalidSets; - } - private List<StoneSet> touchedStoneSets(IHand oldHand, ITable oldTable, - ITable newTable) { - List<StoneSet> touchedSets = new ArrayList<StoneSet>(); - for (StoneSet set : tableSetDifference(oldTable, newTable)) { - for (Stone stone : set) { - if (!oldHand.contains(stone)) { - touchedSets.add(set); - break; - } - } + if (roundState.getActivePlayer().getHand().getSize() == 0) { + endOfRound(); + return; } - return touchedSets; + view.setBottomPanel(BottomPanelType.NONHUMAN_HAND_PANEL); + nextPlayer(); } private void nextPlayer() { @@ -347,7 +323,7 @@ public class RoundControl { view.setStoneCollectionHidden(false); if (roundState.getLastPlayer() == null) { if (roundState.getGameHeap().getSize() == 0) { - roundState.setLastPlayer(roundState.getNthNextPlayer(0)); + roundState.setLastPlayer(roundState.getActivePlayer()); roundState.nextPlayer(); roundState.nextTurn(); } else { @@ -367,100 +343,6 @@ public class RoundControl { } } - private boolean checkTurn(ITable oldTable, ITable newTable) { - if (!newTable.isValid()) { - rejectMove(oldTable, newTable); - return false; - } - if (!roundState.getActivePlayer().getLaidOut()) { - // Player touched forbidden stones - if (!tableSetDifference(newTable, oldTable).isEmpty()) { - rejectMove(oldTable, newTable); - lastTurnMeldError = true; - return false; - } - if (!laidOutValidPoints(oldTable, newTable)) { - rejectMove(oldTable, newTable); - lastTurnNotEnoughPoints = true; - return false; - } - } - Set<Stone> tableDiff = tableDifference(oldTable, newTable); - - roundState.setTable(newTable); - - if (tableDiff.isEmpty()) { - // Player hasn't made a move - dealStone(); - } else { - roundState.getActivePlayer().setLaidOut(true); - if (roundState.getActivePlayer().getHand().getSize() == 0) { - endOfRound(); - } - } - return true; - } - - private void rejectMove(ITable oldTable, ITable newTable) { - Set<Stone> tableDiff = tableDifference(oldTable, newTable); - // deal penalty, reset - roundState.getGameHeap().putBack(tableDiff); - roundState.getActivePlayer().setLastTurnInvalid(true); - dealPenalty(tableDiff.size()); - - } - - static Set<Stone> tableDifference(ITable oldTable, ITable newTable) { - Set<Stone> ret = new HashSet<Stone>(); - - for (Pair<StoneSet, Position> entry : newTable) { - for (Stone stone : entry.getFirst()) { - ret.add(stone); - } - } - for (Pair<StoneSet, Position> entry : oldTable) { - for (Stone stone : entry.getFirst()) { - ret.remove(stone); - } - } - return ret; - } - - static List<StoneSet> tableSetDifference(ITable oldTable, ITable newTable) { - List<StoneSet> ret = new ArrayList<StoneSet>(); - - for (Pair<StoneSet, Position> entry : newTable) { - ret.add(entry.getFirst()); - } - for (Pair<StoneSet, Position> entry : oldTable) { - ret.remove(entry.getFirst()); - } - return ret; - } - - void dealStones(int count) { - IHand hand = roundState.getActivePlayer().getHand(); - int rowCount = hand.getRowCount(); - - for (int i = 0; i < count; ++i) { - if (hand.getFreeRowSpace(rowCount - 1) == 0) { - rowCount++; - } - - hand.drop(roundState.getGameHeap().drawStone(), new Position( - Hand.WIDTH - 1, rowCount - 1)); - } - - } - - private void dealStone() { - dealStones(1); - } - - private void dealPenalty(int count) { - dealStones(count + 3); - } - void endOfRound() { removeListeners(); Score roundScore = score(); diff --git a/src/jrummikub/control/network/ConnectionControl.java b/src/jrummikub/control/network/ConnectionControl.java index 76b430a..4ed160a 100644 --- a/src/jrummikub/control/network/ConnectionControl.java +++ b/src/jrummikub/control/network/ConnectionControl.java @@ -6,19 +6,17 @@ import java.util.UUID; import javax.swing.SwingUtilities; +import jrummikub.control.RoundControl.InvalidTurnInfo; import jrummikub.model.GameSettings; -import jrummikub.model.IHand; import jrummikub.model.IRoundState; import jrummikub.model.ITable; import jrummikub.util.Event; import jrummikub.util.Event1; import jrummikub.util.Event2; -import jrummikub.util.Event3; import jrummikub.util.GameData; import jrummikub.util.IEvent; import jrummikub.util.IEvent1; import jrummikub.util.IEvent2; -import jrummikub.util.IEvent3; import jrummikub.util.LoginData; import jrummikub.view.LoginError; @@ -48,26 +46,20 @@ public class ConnectionControl implements IConnectionControl { private static class TurnEndData implements Serializable { private static final long serialVersionUID = -3800572117130220737L; - private IHand oldHand; - private ITable oldTable; - private ITable newTable; + private IRoundState roundState; + private InvalidTurnInfo invalidTurnInfo; - TurnEndData(IHand oldHand, ITable oldTable, ITable newTable) { - this.oldHand = oldHand; - this.oldTable = oldTable; - this.newTable = newTable; + TurnEndData(IRoundState roundState, InvalidTurnInfo invalidTurnInfo) { + this.roundState = roundState; + this.invalidTurnInfo = invalidTurnInfo; } - IHand getOldHand() { - return oldHand; + IRoundState getRoundState() { + return roundState; } - ITable getOldTable() { - return oldTable; - } - - ITable getNewTable() { - return newTable; + InvalidTurnInfo getInvalidTurnInfo() { + return invalidTurnInfo; } } @@ -95,8 +87,8 @@ public class ConnectionControl implements IConnectionControl { private Event roundStartEvent = new Event(); private Event1<ITable> tableUpdateEvent = new Event1<ITable>(); - private Event3<IHand, ITable, ITable> turnEndEvent = new Event3<IHand, ITable, ITable>(); - private Event1<IRoundState> turnStartEvent = new Event1<IRoundState>(); + private Event2<IRoundState, InvalidTurnInfo> turnEndEvent = new Event2<IRoundState, InvalidTurnInfo>(); + private Event turnStartEvent = new Event(); private GameData currentGame; @@ -195,12 +187,12 @@ public class ConnectionControl implements IConnectionControl { } @Override - public IEvent3<IHand, ITable, ITable> getTurnEndEvent() { + public IEvent2<IRoundState, InvalidTurnInfo> getTurnEndEvent() { return turnEndEvent; } @Override - public IEvent1<IRoundState> getTurnStartEvent() { + public IEvent getTurnStartEvent() { return turnStartEvent; } @@ -334,29 +326,28 @@ public class ConnectionControl implements IConnectionControl { } @Override - public void endTurn(final IHand oldHand, final ITable oldTable, - final ITable newTable) { + public void endTurn(final IRoundState state, + final InvalidTurnInfo invalidTurnInfo) { final UUID uuid = currentGame.getGameID(); run(new SendRunner() { @Override protected void addData(DefaultPacketExtension extension) { extension.setValue("messageType", "turn_end"); extension.setValue("uuid", uuid.toString()); - extension.setValue("data", Base64.encodeObject(new TurnEndData(oldHand, - oldTable, newTable), Base64.GZIP)); + extension.setValue("data", Base64.encodeObject(new TurnEndData(state, + invalidTurnInfo), Base64.GZIP)); } }); } @Override - public void startTurn(final IRoundState state) { + public void startTurn() { final UUID uuid = currentGame.getGameID(); run(new SendRunner() { @Override protected void addData(DefaultPacketExtension extension) { extension.setValue("messageType", "turn_start"); extension.setValue("uuid", uuid.toString()); - extension.setValue("state", Base64.encodeObject(state, Base64.GZIP)); } }); } @@ -474,11 +465,9 @@ public class ConnectionControl implements IConnectionControl { } else if (messageType.equals("turn_end")) { TurnEndData data = (TurnEndData) Base64.decodeToObject(extension .getValue("data")); - turnEndEvent.emit(data.getOldHand(), data.getOldTable(), - data.getNewTable()); + turnEndEvent.emit(data.getRoundState(), data.getInvalidTurnInfo()); } else if (messageType.equals("turn_start")) { - turnStartEvent.emit((IRoundState) Base64.decodeToObject(extension - .getValue("state"))); + turnStartEvent.emit(); } else { System.err.println("Received unrecognized message of type '" + messageType + "'"); diff --git a/src/jrummikub/control/network/IConnectionControl.java b/src/jrummikub/control/network/IConnectionControl.java index 38bc05c..b93748c 100644 --- a/src/jrummikub/control/network/IConnectionControl.java +++ b/src/jrummikub/control/network/IConnectionControl.java @@ -3,14 +3,13 @@ package jrummikub.control.network; import java.awt.Color; import java.util.UUID; -import jrummikub.model.IHand; +import jrummikub.control.RoundControl.InvalidTurnInfo; import jrummikub.model.IRoundState; import jrummikub.model.ITable; import jrummikub.util.GameData; import jrummikub.util.IEvent; import jrummikub.util.IEvent1; import jrummikub.util.IEvent2; -import jrummikub.util.IEvent3; import jrummikub.view.LoginError; interface IConnectionControl { @@ -43,9 +42,9 @@ interface IConnectionControl { public IEvent1<ITable> getTableUpdateEvent(); - public IEvent3<IHand, ITable, ITable> getTurnEndEvent(); + public IEvent2<IRoundState, InvalidTurnInfo> getTurnEndEvent(); - public IEvent1<IRoundState> getTurnStartEvent(); + public IEvent getTurnStartEvent(); public void offerGame(GameData data); @@ -69,7 +68,8 @@ interface IConnectionControl { public void updateTable(ITable table); - public void endTurn(IHand oldHand, ITable oldTable, ITable newTable); + public void endTurn(IRoundState state, InvalidTurnInfo invalidTurnInfo); + + public void startTurn(); - public void startTurn(IRoundState state); }
\ No newline at end of file diff --git a/src/jrummikub/control/network/NetworkRoundControl.java b/src/jrummikub/control/network/NetworkRoundControl.java index 81820e7..64faebb 100644 --- a/src/jrummikub/control/network/NetworkRoundControl.java +++ b/src/jrummikub/control/network/NetworkRoundControl.java @@ -2,10 +2,10 @@ package jrummikub.control.network; import jrummikub.control.RoundControl; import jrummikub.control.turn.ITurnControl; -import jrummikub.model.IHand; import jrummikub.model.IRoundState; import jrummikub.model.ITable; import jrummikub.model.PlayerSettings.Type; +import jrummikub.util.IListener; import jrummikub.util.IListener1; import jrummikub.view.IView; @@ -20,17 +20,12 @@ public class NetworkRoundControl extends RoundControl { this.connectionControl = connectionControl; currentlyActive = startActive; - connections.add(connectionControl.getTurnStartEvent().add( - new IListener1<IRoundState>() { - @Override - public void handle(IRoundState state) { - NetworkControl.fixGameSettings(state.getGameSettings(), - connectionControl.getNickname()); - setRoundState(state); - - startTurn(); - } - })); + connections.add(connectionControl.getTurnStartEvent().add(new IListener() { + @Override + public void handle() { + startTurn(); + } + })); } @Override @@ -68,16 +63,16 @@ public class NetworkRoundControl extends RoundControl { doPrepareTurn(); if (wasActive) { - connectionControl.startTurn(roundState); + connectionControl.startTurn(); } } @Override - protected void endOfTurn(IHand oldHand, ITable oldTable, ITable newTable) { + protected void endOfTurn(InvalidTurnInfo invalidTurnInfo) { if (currentlyActive) { - connectionControl.endTurn(oldHand, oldTable, newTable); + connectionControl.endTurn(roundState, invalidTurnInfo); } - super.endOfTurn(oldHand, oldTable, newTable); + super.endOfTurn(invalidTurnInfo); } } diff --git a/src/jrummikub/control/network/NetworkTurnControl.java b/src/jrummikub/control/network/NetworkTurnControl.java index 57851a2..7b0bcbb 100644 --- a/src/jrummikub/control/network/NetworkTurnControl.java +++ b/src/jrummikub/control/network/NetworkTurnControl.java @@ -1,13 +1,13 @@ package jrummikub.control.network; +import jrummikub.control.RoundControl.InvalidTurnInfo; import jrummikub.control.turn.AbstractTurnControl; -import jrummikub.model.IHand; import jrummikub.model.IRoundState; import jrummikub.model.ITable; import jrummikub.util.Event1; import jrummikub.util.IEvent1; import jrummikub.util.IListener1; -import jrummikub.util.IListener3; +import jrummikub.util.IListener2; public class NetworkTurnControl extends AbstractTurnControl { private IConnectionControl connectionControl; @@ -31,20 +31,24 @@ public class NetworkTurnControl extends AbstractTurnControl { } })); connections.add(connectionControl.getTurnEndEvent().add( - new IListener3<IHand, ITable, ITable>() { + new IListener2<IRoundState, InvalidTurnInfo>() { @Override - public void handle(IHand oldHand, ITable oldTable, ITable newTable) { - endOfTurn(oldHand, oldTable, newTable); + public void handle(IRoundState state, + InvalidTurnInfo invalidTurnInfo) { + NetworkControl.fixGameSettings(state.getGameSettings(), + connectionControl.getNickname()); + + endOfTurn(state, invalidTurnInfo); } })); timer.startTimer(); } - private void endOfTurn(IHand oldHand, ITable oldTable, ITable newTable) { + private void endOfTurn(IRoundState roundState, InvalidTurnInfo invalidTurnInfo) { cleanUp(); - endOfTurnEvent.emit(oldHand, oldTable, newTable); + endOfTurnEvent.emit(roundState, invalidTurnInfo); } @Override diff --git a/src/jrummikub/control/turn/AIControl.java b/src/jrummikub/control/turn/AIControl.java index b633f96..f480143 100644 --- a/src/jrummikub/control/turn/AIControl.java +++ b/src/jrummikub/control/turn/AIControl.java @@ -225,7 +225,7 @@ public class AIControl extends AbstractTurnControl { return; } cleanUp(); - endOfTurnEvent.emit(null, null, turnInfo.getTable()); + endOfTurnEvent.emit(turnInfo.getRoundState(), checkTurn()); } /** diff --git a/src/jrummikub/control/turn/AbstractTurnControl.java b/src/jrummikub/control/turn/AbstractTurnControl.java index 8b481bc..d64ee62 100644 --- a/src/jrummikub/control/turn/AbstractTurnControl.java +++ b/src/jrummikub/control/turn/AbstractTurnControl.java @@ -1,28 +1,39 @@ 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.Event3; +import jrummikub.util.Event2; import jrummikub.util.IEvent; import jrummikub.util.IEvent1; -import jrummikub.util.IEvent3; +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 Event3<IHand, ITable, ITable> endOfTurnEvent = new Event3<IHand, ITable, ITable>(); + protected Event2<IRoundState, InvalidTurnInfo> endOfTurnEvent = new Event2<IRoundState, InvalidTurnInfo>(); protected Event redealEvent = new Event(); protected Event1<ITable> tableUpdateEvent = new Event1<ITable>(); protected TurnInfo turnInfo; @@ -33,7 +44,7 @@ public abstract class AbstractTurnControl implements ITurnControl { private boolean started = false; @Override - public IEvent3<IHand, ITable, ITable> getEndOfTurnEvent() { + public IEvent2<IRoundState, InvalidTurnInfo> getEndOfTurnEvent() { return endOfTurnEvent; } @@ -114,4 +125,154 @@ public abstract class AbstractTurnControl implements ITurnControl { 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.<StoneSet> 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<Stone> 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.<StoneSet> emptyList()); + } + + private List<StoneSet> invalidStoneSets() { + List<StoneSet> invalidSets = new ArrayList<StoneSet>(); + for (Pair<StoneSet, Position> set : turnInfo.getTable()) { + if (set.getFirst().isValid(turnInfo.getRoundState().getGameSettings())) { + continue; + } + invalidSets.add(set.getFirst()); + } + return invalidSets; + } + + private List<StoneSet> touchedStoneSets() { + List<StoneSet> touchedSets = new ArrayList<StoneSet>(); + 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<StoneSet> 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<Stone> tableDiff = tableDifference(turnInfo.getOldTable(), + turnInfo.getTable()); + // deal penalty, reset + turnInfo.getRoundState().getGameHeap().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().getGameHeap().drawStone(), + new Position(Hand.WIDTH - 1, rowCount - 1)); + } + + } + + private void dealStone() { + dealStones(1); + } + + private void dealPenalty(int count) { + dealStones(count + 3); + } + + static Set<Stone> tableDifference(ITable oldTable, ITable newTable) { + Set<Stone> ret = new HashSet<Stone>(); + + for (Pair<StoneSet, Position> entry : newTable) { + for (Stone stone : entry.getFirst()) { + ret.add(stone); + } + } + for (Pair<StoneSet, Position> entry : oldTable) { + for (Stone stone : entry.getFirst()) { + ret.remove(stone); + } + } + return ret; + } + + static List<StoneSet> tableSetDifference(ITable oldTable, ITable newTable) { + List<StoneSet> ret = new ArrayList<StoneSet>(); + + for (Pair<StoneSet, Position> entry : newTable) { + ret.add(entry.getFirst()); + } + for (Pair<StoneSet, Position> entry : oldTable) { + ret.remove(entry.getFirst()); + } + return ret; + } }
\ No newline at end of file diff --git a/src/jrummikub/control/turn/HumanTurnControl.java b/src/jrummikub/control/turn/HumanTurnControl.java index e43ec5b..43c2317 100644 --- a/src/jrummikub/control/turn/HumanTurnControl.java +++ b/src/jrummikub/control/turn/HumanTurnControl.java @@ -448,7 +448,7 @@ public class HumanTurnControl extends AbstractTurnControl { if (redeal) { redealEvent.emit(); } else { - endOfTurnEvent.emit(null, null, turnInfo.getTable()); + endOfTurnEvent.emit(turnInfo.getRoundState(), checkTurn()); } } diff --git a/src/jrummikub/control/turn/ITurnControl.java b/src/jrummikub/control/turn/ITurnControl.java index 5c91c0c..6994466 100644 --- a/src/jrummikub/control/turn/ITurnControl.java +++ b/src/jrummikub/control/turn/ITurnControl.java @@ -1,11 +1,13 @@ package jrummikub.control.turn; +import jrummikub.control.RoundControl.InvalidTurnInfo; import jrummikub.model.GameSettings; import jrummikub.model.IHand; +import jrummikub.model.IRoundState; import jrummikub.model.ITable; import jrummikub.util.IEvent; import jrummikub.util.IEvent1; -import jrummikub.util.IEvent3; +import jrummikub.util.IEvent2; import jrummikub.view.IView; /** @@ -17,12 +19,12 @@ public interface ITurnControl { * Start the turn * * @param info - * the current turn state + * the current turn state * * @param settings - * the game settings + * the game settings * @param view - * view for user interaction. + * view for user interaction. */ public void setup(TurnInfo info, GameSettings settings, IView view); @@ -31,7 +33,7 @@ public interface ITurnControl { * * @return end of turn event */ - public IEvent3<IHand, ITable, ITable> getEndOfTurnEvent(); + public IEvent2<IRoundState, InvalidTurnInfo> getEndOfTurnEvent(); /** * Emitted when the round is aborted and needs to be restarted @@ -58,35 +60,61 @@ public interface ITurnControl { public IEvent1<ITable> getTableUpdateEvent(); /** - * The TurnInfo class encapsulates all information concerning the current - * turn + * The TurnInfo class encapsulates all information concerning the current turn */ public class TurnInfo { + private IRoundState roundState; + + private ITable oldTable; + private IHand oldHand; + private ITable table; private IHand hand; - private boolean hasLaidOut; + private TurnMode turnMode; /** * Creates a new TurnInfo instance * - * @param table - * the current table - * @param hand - * the current player's hand * @param hasLaidOut - * has the player laid out yet? + * has the player laid out yet? * @param turnMode - * the turn mode + * the turn mode */ - public TurnInfo(ITable table, IHand hand, boolean hasLaidOut, - TurnMode turnMode) { - this.table = table; - this.hand = hand; - this.hasLaidOut = hasLaidOut; + public TurnInfo(IRoundState roundState, TurnMode turnMode) { + this.roundState = roundState; + + oldTable = roundState.getTable(); + oldHand = roundState.getActivePlayer().getHand(); + + this.table = (ITable) oldTable.clone(); + this.hand = (IHand) oldHand.clone(); + this.turnMode = turnMode; } + public IRoundState getRoundState() { + return roundState; + } + + /** + * Gets the table at the beginning of the turn + * + * @return the table + */ + public ITable getOldTable() { + return oldTable; + } + + /** + * Gets the current player's hand at the beginning of the turn + * + * @return the hand + */ + public IHand getOldHand() { + return oldHand; + } + /** * Gets the current table * @@ -111,7 +139,7 @@ public interface ITurnControl { * @return if the player has laid out */ public boolean getLaidOut() { - return hasLaidOut; + return roundState.getActivePlayer().getLaidOut(); } /** |