From 4f930088153648691c824d9f8b64ace260a9de42 Mon Sep 17 00:00:00 2001 From: levischnorr Date: Wed, 7 Feb 2024 10:07:41 +0100 Subject: [PATCH] Add applyGravityTick()-test --- .../command/CommandTicTacToe.java | 7 -- .../command/CommandTicTacToeAccept.java | 4 +- .../main/java/mavenmcserver/game/Game.java | 9 +- .../java/mavenmcserver/game/GameState.java | 59 +++++++++--- mavenmcserver/src/main/resources/classes.uml | 3 +- .../mavenmcserver/game/GameStateTest.java | 89 +++++++++++++++++++ 6 files changed, 147 insertions(+), 24 deletions(-) diff --git a/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToe.java b/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToe.java index e57cffd..20658b7 100644 --- a/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToe.java +++ b/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToe.java @@ -144,11 +144,6 @@ public List onTabComplete(CommandSender sender, Command command, String if(!(sender instanceof Player)) return new ArrayList(); - ArrayList a = new ArrayList(); - for(String arg: args) a.add(arg); - - sender.sendMessage("Your original list " + a + " (count=" + a.size() + ")"); - ArrayList argList = new ArrayList(); int i = 0; for(String arg: args) { @@ -159,8 +154,6 @@ public List onTabComplete(CommandSender sender, Command command, String i++; } - sender.sendMessage("Your list " + argList + " (count=" + argList.size() + ")"); - ArrayList completions = new ArrayList(); boolean playerIsCurrentlyInAGame = Game.runningGames.containsKey((Player)sender); diff --git a/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToeAccept.java b/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToeAccept.java index f477a81..98be842 100644 --- a/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToeAccept.java +++ b/mavenmcserver/src/main/java/mavenmcserver/command/CommandTicTacToeAccept.java @@ -79,7 +79,7 @@ public List onTabComplete(CommandSender sender, Command command, String ArrayList argList = new ArrayList(); for(String arg: args) argList.add(arg); - argList.removeIf((arg) -> arg.isEmpty() && !CommandTicTacToeAccept.containsNonEmptyString(argList.subList(0, Math.max(0, argList.indexOf(arg) - 1)))); + argList.removeIf((arg) -> arg.isEmpty() && !CommandTicTacToeAccept.listContainsNonEmptyString(argList.subList(0, Math.max(0, argList.indexOf(arg) - 1)))); if(argList.size() >= CommandTicTacToeAccept.ARG_COUNT) return new ArrayList(); @@ -97,7 +97,7 @@ public List onTabComplete(CommandSender sender, Command command, String } - public static boolean containsNonEmptyString(List list) { + public static boolean listContainsNonEmptyString(List list) { for(String string: list) { if(!string.isEmpty()) return true; } diff --git a/mavenmcserver/src/main/java/mavenmcserver/game/Game.java b/mavenmcserver/src/main/java/mavenmcserver/game/Game.java index 2bf06f9..c38b9cd 100644 --- a/mavenmcserver/src/main/java/mavenmcserver/game/Game.java +++ b/mavenmcserver/src/main/java/mavenmcserver/game/Game.java @@ -86,8 +86,13 @@ public Game(GameConfig config, Plugin plugin, boolean isReturnMatch) { @Override public void run() { - boolean didApplyAnyChangeInCurrentTick = state.applyGravityTick(location, lastPlacePosition); - if(!didApplyAnyChangeInCurrentTick && !didCompletePlace) { + boolean didApplyAnyChange = state.applyGravityTick(lastPlacePosition); + + if(didApplyAnyChange) { + state.applyVisually(location); + } + + if(!didApplyAnyChange && !didCompletePlace) { // Falling is now done checkForWin(); didCompletePlace = true; diff --git a/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java b/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java index 73f2b5f..ba8d884 100644 --- a/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java +++ b/mavenmcserver/src/main/java/mavenmcserver/game/GameState.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.World; import org.joml.Vector3i; @@ -36,6 +37,16 @@ public boolean equals(Object obj) { FieldPoint point = (FieldPoint)obj; return point.x == this.x && point.y == this.y && point.z == this.z; } + + @Override + public int hashCode() { + int result = (this.x ^ (this.x >>> 32)); + + result = 31 * result + (this.y ^ (this.y >>> 32)); + result = 31 * result + (this.z ^ (this.z >>> 32)); + + return result; + } } /** @@ -157,8 +168,12 @@ public void setStateAt(int x, int y, int z, FieldState newState) { this.setStateAt(new FieldPoint(x, y, z), newState); } - - public boolean applyGravityTick(Location gameStartBlock, FieldPoint lastPlacePosition) { + /** + * 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) { boolean didApplyAnyChange = false; for(int y = 1; y < this.gameSize.y; y++) { @@ -169,16 +184,6 @@ public boolean applyGravityTick(Location gameStartBlock, FieldPoint lastPlacePos this.setStateAt(x, y - 1, z, this.getStateAt(x, y, z)); this.setStateAt(x, y, z, FieldState.NEUTRAL); - // Update changes visually - World gameWorld = gameStartBlock.getWorld(); - - Location inWorldLocationOfUnderneathBlock = this.fieldPointToBlockLocation(gameStartBlock, new FieldPoint(x, y - 1, z)); - if(this.getStateAt(x, y - 1, z) == FieldState.MAIN) gameWorld.getBlockAt(inWorldLocationOfUnderneathBlock).setType(Game.MAIN_PLAYER_MATERIAL); - else if(this.getStateAt(x, y - 1, z) == FieldState.OPPONENT) gameWorld.getBlockAt(inWorldLocationOfUnderneathBlock).setType(Game.OPPONENT_PLAYER_MATERIAL); - - Location inWorldLocationOfCurrentBlock = this.fieldPointToBlockLocation(gameStartBlock, new FieldPoint(x, y, z)); - gameWorld.getBlockAt(inWorldLocationOfCurrentBlock).setType(Game.NEUTRAL_MATERIAL); - boolean didModifyBlockAtLastPlacePosition = lastPlacePosition.equals(new FieldPoint(x, y, z)); if(didModifyBlockAtLastPlacePosition) { lastPlacePosition.y -= 1; @@ -194,6 +199,36 @@ public boolean applyGravityTick(Location gameStartBlock, FieldPoint lastPlacePos return didApplyAnyChange; } + /** + * Updates any changes made to this state visually by placing the blocks (replaces ALL blocks). + * @param gameStartBlock The location of the target game. + */ + public void applyVisually(Location gameStartBlock) { + + World gameWorld = gameStartBlock.getWorld(); + + for(int y = 0; y < this.gameSize.y; y++) { + for(int x = 0; x < this.gameSize.x; x++) { + for(int z = 0; z < this.gameSize.z; z++) { + + FieldState stateOfCurrentField = this.getStateAt(x, y, z); + + Material newTypeOfBlock = Game.NEUTRAL_MATERIAL; + if(stateOfCurrentField == FieldState.MAIN) { + newTypeOfBlock = Game.MAIN_PLAYER_MATERIAL; + } else if(stateOfCurrentField == FieldState.OPPONENT) { + newTypeOfBlock = Game.OPPONENT_PLAYER_MATERIAL; + } + + Location inWorldLocationOfCurrentBlock = this.fieldPointToBlockLocation(gameStartBlock, new FieldPoint(x, y, z)); + gameWorld.getBlockAt(inWorldLocationOfCurrentBlock).setType(newTypeOfBlock); + + } + } + } + + } + /** * 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. diff --git a/mavenmcserver/src/main/resources/classes.uml b/mavenmcserver/src/main/resources/classes.uml index 27e224a..f30eda9 100644 --- a/mavenmcserver/src/main/resources/classes.uml +++ b/mavenmcserver/src/main/resources/classes.uml @@ -108,7 +108,8 @@ class GameState { +void setStateAt(FieldPoint position, FieldState newState) +void setStateAt(int x, int y, int z, FieldState newState) - +boolean applyGravityTick(Location gameStartBlock, FieldPoint lastPlacePosition) /' Makes all blocks in air fall by one '/ + +boolean applyGravityTick(FieldPoint lastPlacePosition) /' 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 '/ diff --git a/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java b/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java index b549e58..048f96f 100644 --- a/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java +++ b/mavenmcserver/src/test/java/mavenmcserver/game/GameStateTest.java @@ -5,6 +5,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import static java.util.Map.entry; + import org.joml.Vector3i; import org.junit.jupiter.api.Test; @@ -154,5 +159,89 @@ public void testWinIsPossible() { } + public boolean stateIsNeutralExceptFor(GameState state, Map exceptions) { + for(int x = 0; x < state.gameSize.x; x++) { + for(int y = 0; y < state.gameSize.y; y++) { + for(int z = 0; z < state.gameSize.z; z++) { + FieldPoint currentPoint = new FieldPoint(x, y, z); + FieldState expectedState = exceptions.containsKey(currentPoint) ? exceptions.get(currentPoint) : FieldState.NEUTRAL; + if(state.getStateAt(currentPoint) != expectedState) return false; + } + } + } + + return true; + } + + @Test + 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); + 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(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(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)); + + + // 4 x 4 x 4 + state = new GameState(new Vector3i(4, 4, 4)); + lastPlacePosition = new FieldPoint(0, 3, 0); + + + state.setStateAt(lastPlacePosition, 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(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(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(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)); + + lastPlacePosition = new FieldPoint(1, 3, 3); + state.setStateAt(lastPlacePosition, 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(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(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(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)); + + } + }