summaryrefslogtreecommitdiffstats
path: root/src/jrummikub/control/turn
diff options
context:
space:
mode:
Diffstat (limited to 'src/jrummikub/control/turn')
-rw-r--r--src/jrummikub/control/turn/AIControl.java2
-rw-r--r--src/jrummikub/control/turn/AbstractTurnControl.java169
-rw-r--r--src/jrummikub/control/turn/HumanTurnControl.java2
-rw-r--r--src/jrummikub/control/turn/ITurnControl.java68
4 files changed, 215 insertions, 26 deletions
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();
}
/**