diff --git a/mavenmcserver/src/main/java/mavenmcserver/game/Game.java b/mavenmcserver/src/main/java/mavenmcserver/game/Game.java index d96af9c..dc6cb95 100644 --- a/mavenmcserver/src/main/java/mavenmcserver/game/Game.java +++ b/mavenmcserver/src/main/java/mavenmcserver/game/Game.java @@ -60,8 +60,6 @@ public class Game { public GameListener listener; public GameState state; - /// The FieldPoint of the last marked field, starting as null - public FieldPoint lastPlacePosition = null; private boolean didCompletePlace = true; public boolean opponentPlayersTurn = true; @@ -95,7 +93,7 @@ public Game(GameConfig config, Plugin plugin, boolean isReturnMatch) { @Override public void run() { - boolean didApplyAnyChange = state.applyGravityTick(lastPlacePosition); + boolean didApplyAnyChange = state.applyGravityTick(); if(didApplyAnyChange) { state.applyVisually(location); @@ -318,8 +316,8 @@ private void registerEnded() { * @param position */ public void placeAt(FieldPoint position) { - // Store the position for use in checkForWin(); - this.lastPlacePosition = position; + // Store the position for use in getWinnerIfAny(); and similar + this.state.lastPlacePosition = position; this.didCompletePlace = false; if(this.state.getStateAt(position) != FieldState.NEUTRAL) return; @@ -339,14 +337,14 @@ public void placeAt(FieldPoint position) { public void checkForWin() { - if(this.state.getWinnerIfAny(this.config.winRequiredAmount, this.lastPlacePosition) != FieldState.NEUTRAL) { + if(this.state.getWinnerIfAny(this.config.winRequiredAmount) != FieldState.NEUTRAL) { this.listener.allowMarkingFields = false; new BukkitRunnable() { int i = -1; - ArrayList blockLocations = state.getWinRowBlockLocations(config.winRequiredAmount, location, lastPlacePosition); + ArrayList blockLocations = state.getWinRowBlockLocations(config.winRequiredAmount, location); @Override public void run() { diff --git a/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java b/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java index d6c53d0..7d4841c 100644 --- a/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java +++ b/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java @@ -47,6 +47,10 @@ public int hashCode() { return result; } + + public FieldPoint copy() { + return new FieldPoint(this.x, this.y, this.z); + } } /** @@ -98,6 +102,9 @@ public enum FieldState { FieldState blockStates[]; + /// The FieldPoint of the last marked field, starting as null + public FieldPoint lastPlacePosition = null; + public GameState(Vector3i gameSize) { this.gameSize = gameSize; @@ -162,6 +169,7 @@ public void setStateAt(FieldPoint position, FieldState newState) { if(!this.fieldPointIsValid(position)) throw new IllegalArgumentException("point " + position + " is invalid for size " + this.gameSize); this.blockStates[position.x * this.gameSize.y * this.gameSize.z + position.y * this.gameSize.z + position.z] = newState; + this.lastPlacePosition = position.copy(); } public void setStateAt(int x, int y, int z, FieldState newState) { @@ -170,10 +178,9 @@ public void setStateAt(int x, int y, int z, FieldState newState) { /** * Advances the physics of this state by one tick. Every marking in air will fall down by a block. - * @param lastPlacePosition The FieldPoint of the last-changed block. * @return Whether any changes were made. */ - public boolean applyGravityTick(FieldPoint lastPlacePosition) { + public boolean applyGravityTick() { boolean didApplyAnyChange = false; for(int y = 1; y < this.gameSize.y; y++) { @@ -181,12 +188,16 @@ public boolean applyGravityTick(FieldPoint lastPlacePosition) { for(int z = 0; z < this.gameSize.z; z++) { if(this.getStateAt(x, y, z) != FieldState.NEUTRAL) { if(this.getStateAt(x, y - 1, z) == FieldState.NEUTRAL) { + FieldPoint lastPlacePosition = this.lastPlacePosition; + this.setStateAt(x, y - 1, z, this.getStateAt(x, y, z)); this.setStateAt(x, y, z, FieldState.NEUTRAL); - boolean didModifyBlockAtLastPlacePosition = lastPlacePosition.equals(new FieldPoint(x, y, z)); + this.lastPlacePosition = lastPlacePosition; + + boolean didModifyBlockAtLastPlacePosition = this.lastPlacePosition.equals(new FieldPoint(x, y, z)); if(didModifyBlockAtLastPlacePosition) { - lastPlacePosition.y -= 1; + this.lastPlacePosition.y -= 1; } didApplyAnyChange = true; @@ -231,24 +242,23 @@ public void applyVisually(Location gameStartBlock) { /** * Checks if a player won the game - * @param lastPlacePosition the field point that was last changed. Checks are done from this point into all possible directions. * @return NEUTRAL if there is no winner yet. MAIN if the mainPlayer has won, OPPONENT if the opponentPlayer has won! */ - public FieldState getWinnerIfAny(int winRequiredAmount, FieldPoint lastPlacePosition) { + public FieldState getWinnerIfAny(int winRequiredAmount) { for(Vector3i direction: GameState.DIRECTIONS_TO_CHECK) { - int amountOfCorrectFields = this.getFieldsInARowCount(lastPlacePosition, direction); + int amountOfCorrectFields = this.getFieldsInARowCount(this.lastPlacePosition, direction); - if(amountOfCorrectFields >= winRequiredAmount) return this.getStateAt(lastPlacePosition); + if(amountOfCorrectFields >= winRequiredAmount) return this.getStateAt(this.lastPlacePosition); else if(amountOfCorrectFields > 1) { // Check in opposite direction Vector3i oppositeDirection = new Vector3i(-direction.x, -direction.y, -direction.z); - amountOfCorrectFields += this.getFieldsInARowCount(lastPlacePosition, oppositeDirection) - 1; // subtract 1 because the lastChanged Block is counted twice. + amountOfCorrectFields += this.getFieldsInARowCount(this.lastPlacePosition, oppositeDirection) - 1; // subtract 1 because the lastChanged Block is counted twice. - if(amountOfCorrectFields >= winRequiredAmount) return this.getStateAt(lastPlacePosition); + if(amountOfCorrectFields >= winRequiredAmount) return this.getStateAt(this.lastPlacePosition); } } @@ -286,17 +296,17 @@ public boolean winIsPossible() { return false; // TODO: finish } - public ArrayList getWinRowBlockLocations(int winRequiredAmount, Location gameStartBlock, FieldPoint lastPlacePosition) { + public ArrayList getWinRowBlockLocations(int winRequiredAmount, Location gameStartBlock) { for(Vector3i direction : GameState.DIRECTIONS_TO_CHECK) { - int amountOfCorrectFields = this.getFieldsInARowCount(lastPlacePosition, direction) - 1; + int amountOfCorrectFields = this.getFieldsInARowCount(this.lastPlacePosition, direction) - 1; int amountOfCorrectFieldsInOppositeDirection = 0; if(amountOfCorrectFields > 0 && amountOfCorrectFields < winRequiredAmount - 1) { // Check in opposite direction Vector3i oppositeDirection = new Vector3i(-direction.x, -direction.y, -direction.z); - amountOfCorrectFieldsInOppositeDirection = this.getFieldsInARowCount(lastPlacePosition, oppositeDirection) - 1; + amountOfCorrectFieldsInOppositeDirection = this.getFieldsInARowCount(this.lastPlacePosition, oppositeDirection) - 1; } if(amountOfCorrectFields + amountOfCorrectFieldsInOppositeDirection < winRequiredAmount - 1) continue; @@ -304,7 +314,7 @@ public ArrayList getWinRowBlockLocations(int winRequiredAmount, Locati ArrayList blockLocations = new ArrayList(); for(int i = -amountOfCorrectFieldsInOppositeDirection; i <= amountOfCorrectFields; i++) { - FieldPoint currentPoint = lastPlacePosition.offsetBy(i * direction.x, i * direction.y, i * direction.z); + FieldPoint currentPoint = this.lastPlacePosition.offsetBy(i * direction.x, i * direction.y, i * direction.z); Location fieldPointAsBlockLocation = this.fieldPointToBlockLocation(gameStartBlock, currentPoint); blockLocations.add(fieldPointAsBlockLocation); } diff --git a/mavenmcserver/src/main/resources/classes.uml b/mavenmcserver/src/main/resources/classes.uml index 48fba21..abe609e 100644 --- a/mavenmcserver/src/main/resources/classes.uml +++ b/mavenmcserver/src/main/resources/classes.uml @@ -42,7 +42,6 @@ class FieldPoint { +GameConfig config +GameListener listener +GameState state /' Stores the marked fields in a separate positioning system '/ - +FieldPoint lastPlacePosition boolean didCompletePlace +boolean opponentPlayersTurn /' Whose turn it is! '/ +Location location /' Start block location; stores world '/ @@ -101,6 +100,7 @@ class GameState { .. Fields .. +Vector3i gameSize FieldState blockStates[] + +FieldPoint lastPlacePosition .. Methods .. +GameState(Vector3i gameSize) /' Sizes the array and fills it with FieldState.NEUTRAL '/ @@ -113,14 +113,14 @@ class GameState { +void setStateAt(FieldPoint position, FieldState newState) +void setStateAt(int x, int y, int z, FieldState newState) - +boolean applyGravityTick(FieldPoint lastPlacePosition) /' Makes all blocks in air fall by one '/ + +boolean applyGravityTick() /' Makes all blocks in air fall by one '/ +void applyVisually(Location gameStartBlock) /' Updates in-world blocks to match the state '/ // Returns NEUTRAL if there is no winner yet - +FieldState getWinnerIfAny(int winRequiredAmount, FieldPoint lastPlacePosition) /' Returns NEUTRAL for no winner yet '/ + +FieldState getWinnerIfAny(int winRequiredAmount) /' Returns NEUTRAL for no winner yet '/ -int getFieldsInARowCount(FieldPoint startPoint, Vector3i direction) /' Counts how many fields, starting from *startPoint* and going into *direction*, have the same state. Immediate return if the state at *startPoint* == *FieldState.NEUTRAL* '/ +boolean winIsPossible() /' Whether (false) or not (true) to cancel the game '/ - +ArrayList getWinRowBlockLocations(int winRequiredAmount, Location gameStartBlock, FieldPoint lastPlacePosition) /' Returns the Locations of the Blocks used to win the game '/ + +ArrayList getWinRowBlockLocations(int winRequiredAmount, Location gameStartBlock) /' Returns the Locations of the Blocks used to win the game '/ } +class GameListener { diff --git a/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java b/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java index 7f85806..64654a7 100644 --- a/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java +++ b/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java @@ -14,6 +14,8 @@ import mavenmcserver.game.GameState.FieldState; public class GameStateTest { + + public static final int TEST_WIN_REQUIRED_AMOUNT = 3; @Test public void testStateAt() { @@ -69,55 +71,55 @@ public void testGetWinnerIfAny() { GameState state = new GameState(new Vector3i(3, 3, 3)); state.setStateAt(0, 0, 0, FieldState.OPPONENT); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(0, 0, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(0, 1, 0, FieldState.MAIN); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(0, 1, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(1, 0, 0, FieldState.OPPONENT); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(1, 0, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(0, 2, 0, FieldState.MAIN); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(0, 2, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(2, 0, 0, FieldState.OPPONENT); - assertEquals(FieldState.OPPONENT, state.getWinnerIfAny(3, new FieldPoint(2, 0, 0))); + assertEquals(FieldState.OPPONENT, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state = new GameState(new Vector3i(3, 3, 3)); state.setStateAt(0, 0, 0, FieldState.OPPONENT); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(0, 0, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(0, 0, 1, FieldState.OPPONENT); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(0, 0, 1))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(0, 0, 2, FieldState.OPPONENT); - assertEquals(FieldState.OPPONENT, state.getWinnerIfAny(3, new FieldPoint(0, 0, 2))); + assertEquals(FieldState.OPPONENT, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state = new GameState(new Vector3i(3, 3, 3)); state.setStateAt(0, 0, 0, FieldState.MAIN); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(0, 0, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(1, 1, 1, FieldState.MAIN); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(1, 1, 1))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(2, 2, 2, FieldState.MAIN); - assertEquals(FieldState.MAIN, state.getWinnerIfAny(3, new FieldPoint(2, 2, 2))); + assertEquals(FieldState.MAIN, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state = new GameState(new Vector3i(3, 1, 3)); state.setStateAt(0, 0, 0, FieldState.MAIN); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(0, 0, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(2, 0, 0, FieldState.MAIN); - assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(3, new FieldPoint(2, 0, 0))); + assertEquals(FieldState.NEUTRAL, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); state.setStateAt(1, 0, 0, FieldState.MAIN); - assertEquals(FieldState.MAIN, state.getWinnerIfAny(3, new FieldPoint(1, 0, 0))); + assertEquals(FieldState.MAIN, state.getWinnerIfAny(GameStateTest.TEST_WIN_REQUIRED_AMOUNT)); } @@ -176,92 +178,88 @@ public void testApplyGravityTick() { // 3 x 3 x 3 GameState state = new GameState(new Vector3i(3, 3, 3)); - FieldPoint lastPlacePosition = new FieldPoint(0, 2, 0); - state.setStateAt(lastPlacePosition, FieldState.OPPONENT); + state.setStateAt(new FieldPoint(0, 2, 0), FieldState.OPPONENT); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 2, 0), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(0, 1, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(0, 1, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 1, 0), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(0, 0, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(0, 0, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 0, 0), FieldState.OPPONENT)))); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); // 4 x 4 x 4 state = new GameState(new Vector3i(4, 4, 4)); - lastPlacePosition = new FieldPoint(0, 3, 0); - state.setStateAt(lastPlacePosition, FieldState.MAIN); + state.setStateAt(new FieldPoint(0, 3, 0), FieldState.MAIN); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 3, 0), FieldState.MAIN)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(0, 2, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(0, 2, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 2, 0), FieldState.MAIN)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(0, 1, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(0, 1, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 1, 0), FieldState.MAIN)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(0, 0, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(0, 0, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 0, 0), FieldState.MAIN)))); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); - lastPlacePosition = new FieldPoint(1, 3, 3); - state.setStateAt(lastPlacePosition, FieldState.OPPONENT); + state.setStateAt(new FieldPoint(1, 3, 3), FieldState.OPPONENT); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 0, 0), FieldState.MAIN), entry(new FieldPoint(1, 3, 3), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(1, 2, 3), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(1, 2, 3), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 0, 0), FieldState.MAIN), entry(new FieldPoint(1, 2, 3), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(1, 1, 3), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(1, 1, 3), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 0, 0), FieldState.MAIN), entry(new FieldPoint(1, 1, 3), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(1, 0, 3), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(1, 0, 3), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(0, 0, 0), FieldState.MAIN), entry(new FieldPoint(1, 0, 3), FieldState.OPPONENT)))); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); // 5 x 4 x 5 state = new GameState(new Vector3i(5, 4, 5)); - lastPlacePosition = new FieldPoint(4, 3, 0); - state.setStateAt(lastPlacePosition, FieldState.OPPONENT); + state.setStateAt(new FieldPoint(4, 3, 0), FieldState.OPPONENT); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(4, 3, 0), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(4, 2, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(4, 2, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(4, 2, 0), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(4, 1, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(4, 1, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(4, 1, 0), FieldState.OPPONENT)))); - assertTrue(state.applyGravityTick(lastPlacePosition)); - assertEquals(new FieldPoint(4, 0, 0), lastPlacePosition); + assertTrue(state.applyGravityTick()); + assertEquals(new FieldPoint(4, 0, 0), state.lastPlacePosition); assertTrue(this.stateIsNeutralExceptFor(state, Map.ofEntries(entry(new FieldPoint(4, 0, 0), FieldState.OPPONENT)))); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); - assertFalse(state.applyGravityTick(lastPlacePosition)); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); + assertFalse(state.applyGravityTick()); }