From bcf44bb8e2bc42f31974c33c391bb863d973d7e9 Mon Sep 17 00:00:00 2001 From: extio-de Date: Sun, 21 Dec 2025 23:47:14 +0100 Subject: [PATCH] Add unit tests for spatial indexing and random number generation utilities --- .../spatial2/WorldUtils2Tests.java | 116 ++++++ .../spatial2/index/QuadTreeTests.java | 340 ++++++++++++++++++ .../spatial2/index/SpatialIndex2DTests.java | 198 ++++++++++ .../util/rng/JavaReferenceRandomTests.java | 54 +++ .../util/rng/JavaThreadLocalRandomTests.java | 62 ++++ .../util/rng/XorShift128RandomTests.java | 101 ++++++ .../game_engine/util/rng/ZeroRandomTests.java | 52 +++ 7 files changed, 923 insertions(+) create mode 100644 src/test/java/de/extio/game_engine/spatial2/WorldUtils2Tests.java create mode 100644 src/test/java/de/extio/game_engine/spatial2/index/QuadTreeTests.java create mode 100644 src/test/java/de/extio/game_engine/spatial2/index/SpatialIndex2DTests.java create mode 100644 src/test/java/de/extio/game_engine/util/rng/JavaReferenceRandomTests.java create mode 100644 src/test/java/de/extio/game_engine/util/rng/JavaThreadLocalRandomTests.java create mode 100644 src/test/java/de/extio/game_engine/util/rng/XorShift128RandomTests.java create mode 100644 src/test/java/de/extio/game_engine/util/rng/ZeroRandomTests.java diff --git a/src/test/java/de/extio/game_engine/spatial2/WorldUtils2Tests.java b/src/test/java/de/extio/game_engine/spatial2/WorldUtils2Tests.java new file mode 100644 index 0000000..c7c18f6 --- /dev/null +++ b/src/test/java/de/extio/game_engine/spatial2/WorldUtils2Tests.java @@ -0,0 +1,116 @@ +/* Copyright (C) 2023 Stephan Birkl - All Rights Reserved. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.extio.game_engine.spatial2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +import de.extio.game_engine.spatial2.model.Area2; +import de.extio.game_engine.spatial2.model.CoordD2; +import de.extio.game_engine.spatial2.model.CoordI2; +import de.extio.game_engine.spatial2.model.Edge2; +import de.extio.game_engine.spatial2.model.ImmutableCoordI2; +import de.extio.game_engine.spatial2.model.MutableCoordD2; + +public class WorldUtils2Tests { + + @Test + public void testVectorAngle() { + CoordD2 v0 = MutableCoordD2.create(0.0, 0.0); + CoordD2 v1 = MutableCoordD2.create(0.0, 0.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(1.0, 0.0); + v1 = MutableCoordD2.create(0.0, 0.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(0.0, 0.0); + v1 = MutableCoordD2.create(-1.0, 0.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(1.0, 0.0); + v1 = MutableCoordD2.create(0.5, 0.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(1.0, 0.0); + v1 = MutableCoordD2.create(-1.0, 0.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(1.0, 0.0); + v1 = MutableCoordD2.create(-1.0, 1.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(1.0, 0.0); + v1 = MutableCoordD2.create(-0.5, 1.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(1.0, 0.0); + v1 = MutableCoordD2.create(-1.0, 5.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(1.0, 0.0); + v1 = MutableCoordD2.create(0.0, -1.0); + this.calcAngle(v0, v1); + + v0 = MutableCoordD2.create(-0.31617965644035745, 0.21078643762690494); + v1 = MutableCoordD2.create(-0.29595508588991065, 0.19730339059327376); + this.calcAngle(v0, v1); + } + + @Test + public void testVectorAngle2() { + for (double i = -1.0; i < 1.0; i += 0.2) { + for (double ii = -1.0; ii < 1.0; ii += 0.2) { + final CoordD2 v0 = MutableCoordD2.create(i, ii); + final CoordD2 v1 = MutableCoordD2.create(ii, i); + this.calcAngle(v0, v1); + } + } + } + + @Test + public void testWorldUtils2PointsToArea() { + final CoordI2 point0 = ImmutableCoordI2.create(6, 8); + final CoordI2 point1 = ImmutableCoordI2.create(6, 9); + assertEquals(ImmutableCoordI2.create(6, 8), WorldUtils2.pointsToArea(point0, point1).getPosition()); + assertEquals(ImmutableCoordI2.create(1, 2), WorldUtils2.pointsToArea(point0, point1).getDimension()); + assertEquals(ImmutableCoordI2.create(6, 8), WorldUtils2.pointsToArea(point1, point0).getPosition()); + assertEquals(ImmutableCoordI2.create(1, 2), WorldUtils2.pointsToArea(point1, point0).getDimension()); + } + + @Test + public void testPointInCircle() { + for (double n = -Math.PI; n < Math.PI; n += 0.1) { + System.out.println(n + " " + Math.cos(n) + " " + Math.sin(n)); + } + } + + @Test + public void testSector() { + final Area2 area = new Area2(ImmutableCoordI2.create(100, 100), ImmutableCoordI2.create(20, 20)); + + assertEquals(Edge2.RIGHT, WorldUtils2.getSector(ImmutableCoordI2.create(120, 110), area)); + assertEquals(Edge2.LEFT, WorldUtils2.getSector(ImmutableCoordI2.create(105, 110), area)); + assertEquals(Edge2.TOP, WorldUtils2.getSector(ImmutableCoordI2.create(110, 100), area)); + assertEquals(Edge2.BOTTOM, WorldUtils2.getSector(ImmutableCoordI2.create(110, 120), area)); + } + + @Test + public void testPedalNaN() { + final CoordD2 result = WorldUtils2.getPedalOnLineSegmentAB(ImmutableCoordI2.create(-802, -905), ImmutableCoordI2.create(-805, -887), ImmutableCoordI2.create(-804, -893)); + assertNull(result); + } + + private void calcAngle(final CoordD2 v0, final CoordD2 v1) { + System.out.println(WorldUtils2.getVectorAngle(v0, v1) + " " + v0 + " " + v1); + } + +} diff --git a/src/test/java/de/extio/game_engine/spatial2/index/QuadTreeTests.java b/src/test/java/de/extio/game_engine/spatial2/index/QuadTreeTests.java new file mode 100644 index 0000000..380123a --- /dev/null +++ b/src/test/java/de/extio/game_engine/spatial2/index/QuadTreeTests.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2023 Stephan Birkl - All Rights Reserved. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.extio.game_engine.spatial2.index; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import de.extio.game_engine.spatial2.model.Area2; +import de.extio.game_engine.spatial2.model.CoordI2; +import de.extio.game_engine.spatial2.model.ImmutableCoordI2; +import de.extio.game_engine.spatial2.model.SpatialIndex2Capable; +import de.extio.game_engine.util.rng.XorShift128Random; + +@SuppressWarnings("resource") +public class QuadTreeTests { + + @Test + public void testQuadTreeDim1Inserts() { + final SpatialIndex2D index = new QuadTree<>(); + + for (int x = 0; x < 100; x++) { + for (int y = 0; y < 100; y++) { + index.add(new TestEntity(ImmutableCoordI2.create(x, y), ImmutableCoordI2.one())); + } + } + + for (int x = 0; x < 100; x++) { + for (int y = 0; y < 100; y++) { + final TestEntity entity = index.find(ImmutableCoordI2.create(x, y), ImmutableCoordI2.one()).get(0); + assertEquals(x, entity.getPosition().getX()); + assertEquals(y, entity.getPosition().getY()); + } + } + } + + @Test + public void testQuadTreeNDimInserts() { + final SpatialIndex2D index = new QuadTree<>(); + + for (int i = 10; i < 300; i += 20) { + index.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.create(i, i))); + } + for (int i = 290; i > 0; i -= 20) { + index.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.create(i, i))); + } + for (int i = 15; i < 300; i += 10) { + index.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.create(i, i))); + } + + for (int i = 10; i < 300; i += 20) { + final List objs = index.find(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.create(i, i)); + final int iF = i; + assertTrue(objs.stream().anyMatch(e -> e.getPosition().getX() == iF), String.valueOf(i)); + } + for (int i = 290; i > 0; i -= 20) { + final List objs = index.find(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.create(i, i)); + final int iF = i; + assertTrue(objs.stream().anyMatch(e -> e.getPosition().getX() == iF), String.valueOf(i)); + } + for (int i = 15; i < 300; i += 10) { + final List objs = index.find(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.create(i, i)); + final int iF = i; + assertTrue(objs.stream().anyMatch(e -> e.getPosition().getX() == iF), String.valueOf(i)); + } + } + + @Test + public void testRandomEntities() { + final SpatialIndex2D index = new QuadTree<>(); + final Random random = new XorShift128Random(42); + + final List testEntities = new ArrayList<>(100); + for (int i = 0; i < 100; i++) { + testEntities.add(new TestEntity(ImmutableCoordI2.create(random.nextInt(1000), random.nextInt(1000)), ImmutableCoordI2.create(random.nextInt(29) + 1, random.nextInt(29) + 1))); + index.add(testEntities.get(i)); + } + + for (int i = 0; i < 100; i++) { + final TestEntity subject = testEntities.get(i); + final List objs = index.find(subject); + this.checkForDuplicates(objs); + assertTrue(!objs.isEmpty(), String.valueOf(i)); + assertTrue(objs.stream().anyMatch(e -> e == subject)); + } + + for (int i = 0; i < 100; i++) { + final TestEntity subject = testEntities.get(i); + for (int x = subject.getPosition().getX(); x < subject.getPosition().getX() + subject.getDimension().getX(); x++) { + for (int y = subject.getPosition().getY(); y < subject.getPosition().getY() + subject.getDimension().getY(); y++) { + final List objs = index.find(ImmutableCoordI2.create(x, y), ImmutableCoordI2.one()); + assertTrue(!objs.isEmpty(), String.valueOf(i)); + assertTrue(objs.stream().anyMatch(e -> e == subject), String.valueOf(i)); + } + } + } + + final List testEntitiesForEach = new ArrayList<>(100); + index.forEach(testEntitiesForEach::add); + this.checkForDuplicates(testEntitiesForEach); + assertEquals(testEntities.size(), testEntitiesForEach.size()); + for (int i = 0; i < 100; i++) { + final TestEntity entity = testEntities.get(i); + assertTrue(testEntitiesForEach.contains(entity)); + } + + final List testEntitiesIterator = new ArrayList<>(100); + final Iterator it = index.iterator(); + while (it.hasNext()) { + testEntitiesIterator.add(it.next()); + } + this.checkForDuplicates(testEntitiesIterator); + assertEquals(testEntities.size(), testEntitiesIterator.size()); + for (int i = 0; i < 100; i++) { + final TestEntity entity = testEntities.get(i); + assertTrue(testEntitiesIterator.contains(entity)); + } + } + + private void checkForDuplicates(final List in) { + final List entities = new ArrayList<>(in); + entities.sort((e0, e1) -> Integer.compare(e0.hashCode(), e1.hashCode())); + for (int i = 1; i < entities.size(); i++) { + if (entities.get(i - 1).equals(entities.get(i))) { + fail("Duplicate " + i); + } + } + } + + @Test + public void testQuadTreeDim1LinearInserts() { + final SpatialIndex2D index = new QuadTree<>(); + + for (int i = 1; i < 50; i++) { + index.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.one())); + } + for (int i = 0; i >= -50; i--) { + index.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.one())); + } + + for (int i = -50; i < 50; i++) { + final TestEntity entity = index.find(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.one()).get(0); + assertEquals(i, entity.getPosition().getX()); + } + } + + @Test + public void testQuadTreeAddRemoveAdd() { + final SpatialIndex2D index = new QuadTree<>(); + + final List testEntities = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + testEntities.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.one())); + index.add(testEntities.get(i)); + } + + index.remove(testEntities.get(0)); + index.add(testEntities.get(0)); + + for (int i = 0; i < 5; i++) { + final List result = index.find(testEntities.get(0).getPosition(), testEntities.get(0).getDimension()); + assertEquals(1, result.size()); + assertEquals(testEntities.get(0).getPosition().getX(), result.get(0).getPosition().getX()); + assertEquals(testEntities.get(0).getPosition().getY(), result.get(0).getPosition().getY()); + } + } + + @Test + public void testQuadTreeRemove() { + final SpatialIndex2D index = new QuadTree<>(); + + final List testEntities = new ArrayList<>(); + for (int i = 0; i < 32; i++) { + testEntities.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.one())); + index.add(testEntities.get(i)); + } + + for (int i = 0; i < 32; i++) { + index.remove(testEntities.get(i)); + } + } + + @Test + public void testQueryOutsideRange() { + final SpatialIndex2D index = new QuadTree<>(); + + for (int i = 0; i < 50; i++) { + index.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.one())); + } + + assertEquals(50, index.find(new Area2(ImmutableCoordI2.create(0, 0), ImmutableCoordI2.create(50, 1))).size()); + assertEquals(50, index.find(new Area2(ImmutableCoordI2.create(-10, 0), ImmutableCoordI2.create(100, 1))).size()); + assertEquals(25, index.find(new Area2(ImmutableCoordI2.create(25, 0), ImmutableCoordI2.create(50, 1))).size()); + assertEquals(0, index.find(new Area2(ImmutableCoordI2.create(-10, 0), ImmutableCoordI2.create(5, 1))).size()); + assertEquals(0, index.find(new Area2(ImmutableCoordI2.create(100, 0), ImmutableCoordI2.create(5, 1))).size()); + } + + public void testQueryOutsideRange2() { + final SpatialIndex2D index = new QuadTree<>(); + + for (int i = 0; i < 50; i++) { + index.add(new TestEntity(ImmutableCoordI2.create(i, 0), ImmutableCoordI2.one())); + } + + assertEquals(50, index.find(new Area2(ImmutableCoordI2.create(-100000, -100000), ImmutableCoordI2.create(200000, 200000))).size()); + assertEquals(0, index.find(new Area2(ImmutableCoordI2.create(-100000, 0), ImmutableCoordI2.create(5, 1))).size()); + assertEquals(0, index.find(new Area2(ImmutableCoordI2.create(100000, 0), ImmutableCoordI2.create(5, 1))).size()); + assertEquals(0, index.find(new Area2(ImmutableCoordI2.create(0, -100000), ImmutableCoordI2.create(5, 1))).size()); + assertEquals(0, index.find(new Area2(ImmutableCoordI2.create(0, 100000), ImmutableCoordI2.create(5, 1))).size()); + } + + @Test + public void testQuadTreeExpansion() { + final SpatialIndex2D index = new QuadTree<>(); + + for (int x = 0; x < 100000; x += 1000) { + index.add(new TestEntity(ImmutableCoordI2.create(x, x), ImmutableCoordI2.one())); + } + + for (int x = 0; x < 100000; x += 1000) { + final TestEntity entity = index.find(ImmutableCoordI2.create(x, x), ImmutableCoordI2.one()).get(0); + assertEquals(x, entity.getPosition().getX()); + } + } + + @Test + public void testQuadTreeExpansion2() { + final SpatialIndex2D index = new QuadTree<>(); + + index.add(new TestEntity(ImmutableCoordI2.create(1000, 0), ImmutableCoordI2.one())); + index.add(new TestEntity(ImmutableCoordI2.create(900, -5000), ImmutableCoordI2.create(1, 10000))); + index.add(new TestEntity(ImmutableCoordI2.create(-50000, -50000), ImmutableCoordI2.create(100000, 100000))); + } + + @Disabled + @Test + public void testCompareToSearchIndexes() { + final int SIZE = 100000; + final Random random = new XorShift128Random(42); + final SpatialIndex2D quadTree = new QuadTree<>(); + final SpatialIndex2D linear = new LinearSearchIndex2D<>(); + + final List testEntities = new ArrayList<>(SIZE); + for (int i = 0; i < SIZE; i++) { + testEntities.add(new TestEntity(ImmutableCoordI2.create(random.nextInt(10000) + 1, random.nextInt(10000) + 1), ImmutableCoordI2.create(random.nextInt(29) + 1, random.nextInt(29) + 1))); + } + final List queries = new ArrayList<>(SIZE); + for (int i = 0; i < SIZE; i++) { + queries.add(new Area2(ImmutableCoordI2.create(random.nextInt(10000), random.nextInt(10000)), ImmutableCoordI2.create(random.nextInt(499) + 1, random.nextInt(499) + 1))); + } + + long start = System.currentTimeMillis(); + for (int i = 0; i < SIZE; i++) { + quadTree.add(testEntities.get(i)); + } + System.out.println("QT add took " + (System.currentTimeMillis() - start) + "ms"); + start = System.currentTimeMillis(); + for (int i = 0; i < SIZE; i++) { + linear.add(testEntities.get(i)); + } + System.out.println("LINEAR add took " + (System.currentTimeMillis() - start) + "ms"); + + long totalQT = 0; + start = System.currentTimeMillis(); + for (int i = 0; i < SIZE; i++) { + final Area2 area = queries.get(i); + final List qtResult = quadTree.find(area); + totalQT += qtResult.size(); + } + System.out.println("Queried QT " + totalQT + " entities in " + (System.currentTimeMillis() - start) + "ms"); + + long totalLinear = 0; + start = System.currentTimeMillis(); + for (int i = 0; i < SIZE; i++) { + final Area2 area = queries.get(i); + final List bsResult = linear.find(area); + totalLinear += bsResult.size(); + } + System.out.println("Queried LINEAR " + totalLinear + " entities in " + (System.currentTimeMillis() - start) + "ms"); + + if (totalQT != totalLinear) { + System.out.println("Error!"); + } + } + + private class TestEntity implements SpatialIndex2Capable { + + CoordI2 position; + + CoordI2 dimension; + + transient long bsi2Session; + + public TestEntity(final CoordI2 position, final CoordI2 dimension) { + this.position = position; + this.dimension = dimension; + } + + @Override + public CoordI2 getPosition() { + return this.position; + } + + @Override + public CoordI2 getDimension() { + return this.dimension; + } + + @Override + public boolean isInSpatialIndex2ResultSet(final long session) { + return this.bsi2Session == session; + } + + @Override + public void includeInSpatialIndex2ResultSet(final long session) { + this.bsi2Session = session; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("TestEntity [position="); + builder.append(this.position); + builder.append(", dimension="); + builder.append(this.dimension); + builder.append("]"); + return builder.toString(); + } + + } +} diff --git a/src/test/java/de/extio/game_engine/spatial2/index/SpatialIndex2DTests.java b/src/test/java/de/extio/game_engine/spatial2/index/SpatialIndex2DTests.java new file mode 100644 index 0000000..b26f7ce --- /dev/null +++ b/src/test/java/de/extio/game_engine/spatial2/index/SpatialIndex2DTests.java @@ -0,0 +1,198 @@ +/* Copyright (C) 2023 Stephan Birkl - All Rights Reserved. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.extio.game_engine.spatial2.index; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import de.extio.game_engine.spatial2.model.Area2; +import de.extio.game_engine.spatial2.model.CoordI2; +import de.extio.game_engine.spatial2.model.ImmutableCoordI2; +import de.extio.game_engine.spatial2.model.MutableCoordI2; +import de.extio.game_engine.spatial2.model.SpatialIndex2Capable; +import de.extio.game_engine.util.rng.XorShift128Random; + +public class SpatialIndex2DTests { + + @Disabled + @Test + public void testQuadTreePerformance() { + System.out.println("---"); + System.out.println("testQuadTreePerformance"); + + final SpatialIndex2D index = new QuadTree<>(); + this.doTestPerformance(index); + } + + @Disabled + @Test + public void testLinearPerformance() { + System.out.println("---"); + System.out.println("testLinearPerformance"); + + final SpatialIndex2D index = new LinearSearchIndex2D<>(); + this.doTestPerformance(index); + } + + private void doTestPerformance(final SpatialIndex2D index) { + final int SIZE = 100000; + + final Random random = new XorShift128Random(42); + + System.gc(); + try { + Thread.sleep(5000l); + } + catch (final InterruptedException e) { + e.printStackTrace(); + } + + // Add + + final List testEntities = new ArrayList<>(SIZE); + for (int i = 0; i < SIZE; i++) { + testEntities.add(new TestEntity(MutableCoordI2.create(random.nextInt(10000), random.nextInt(10000)), ImmutableCoordI2.create(random.nextInt(29) + 1, random.nextInt(29) + 1))); + } + + long start = System.currentTimeMillis(); + for (int i = 0; i < SIZE; i++) { + index.add(testEntities.get(i)); + } + System.out.println("Add " + SIZE + " entities: " + (System.currentTimeMillis() - start)); + + // Query + + final List queries = new ArrayList<>(SIZE); + for (int i = 0; i < SIZE; i++) { + queries.add(new Area2(ImmutableCoordI2.create(random.nextInt(10000), random.nextInt(10000)), ImmutableCoordI2.create(random.nextInt(499) + 1, random.nextInt(499) + 1))); + } + + start = System.currentTimeMillis(); + long total = 0; + for (int i = 0; i < SIZE; i++) { + final Area2 area = queries.get(i); + total += index.find(area).size(); + } + System.out.println("Queried " + total + " entities: " + (System.currentTimeMillis() - start)); + + // Move + + start = System.currentTimeMillis(); + final CoordI2 oldPosition = MutableCoordI2.create(); + for (int i = 0; i < SIZE; i++) { + final TestEntity entity = testEntities.get(i); + oldPosition.setXY(entity.getPosition()); + entity.getPosition().add(1); + + index.move(testEntities.get(i), oldPosition, entity.getDimension()); + } + System.out.println("Moved all entities: " + (System.currentTimeMillis() - start)); + + // Query + + start = System.currentTimeMillis(); + total = 0; + for (int i = 0; i < SIZE; i++) { + final Area2 area = queries.get(i); + total += index.find(area).size(); + } + System.out.println("Queried " + total + " entities: " + (System.currentTimeMillis() - start)); + + // Remove + + start = System.currentTimeMillis(); + final int cntDelete = SIZE / 2; + for (int i = 0; i < cntDelete; i++) { + index.remove(testEntities.get(i)); + } + System.out.println("Removed " + cntDelete + " entities: " + (System.currentTimeMillis() - start)); + + // Query + + start = System.currentTimeMillis(); + total = 0; + for (int i = 0; i < SIZE; i++) { + final Area2 area = queries.get(i); + total += index.find(area).size(); + } + System.out.println("Queried " + total + " entities: " + (System.currentTimeMillis() - start)); + + // Clear + + start = System.currentTimeMillis(); + index.clear(); + System.out.println("Clear all entities: " + (System.currentTimeMillis() - start)); + + // Add + + start = System.currentTimeMillis(); + for (int i = 0; i < SIZE; i++) { + index.add(testEntities.get(i)); + } + System.out.println("Add " + SIZE + " entities: " + (System.currentTimeMillis() - start)); + + // Query + + start = System.currentTimeMillis(); + total = 0; + for (int i = 0; i < SIZE; i++) { + final Area2 area = queries.get(i); + total += index.find(area).size(); + } + System.out.println("Queried " + total + " entities: " + (System.currentTimeMillis() - start)); + + System.gc(); + try { + Thread.sleep(5000l); + } + catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + private class TestEntity implements SpatialIndex2Capable { + + CoordI2 position; + + CoordI2 dimension; + + transient long bsi2Session; + + public TestEntity(final CoordI2 position, final CoordI2 dimension) { + this.position = position; + this.dimension = dimension; + } + + @Override + public CoordI2 getPosition() { + return this.position; + } + + @Override + public CoordI2 getDimension() { + return this.dimension; + } + + @Override + public boolean isInSpatialIndex2ResultSet(final long session) { + return this.bsi2Session == session; + } + + @Override + public void includeInSpatialIndex2ResultSet(final long session) { + this.bsi2Session = session; + } + + } + +} diff --git a/src/test/java/de/extio/game_engine/util/rng/JavaReferenceRandomTests.java b/src/test/java/de/extio/game_engine/util/rng/JavaReferenceRandomTests.java new file mode 100644 index 0000000..f9aefd4 --- /dev/null +++ b/src/test/java/de/extio/game_engine/util/rng/JavaReferenceRandomTests.java @@ -0,0 +1,54 @@ +/* Copyright (C) 2023 Stephan Birkl - All Rights Reserved. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.extio.game_engine.util.rng; + +import java.util.Random; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class JavaReferenceRandomTests { + + @Disabled + @Test + public void testReferenceRandom() { + final Random random = new Random(42); + + System.out.println("nextDouble()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextDouble()); + } + System.out.println("nextInt()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextInt()); + } + System.out.println("nextInt(5)"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextInt(5)); + } + System.out.println("nextLong()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextLong()); + } + System.out.println("nextBoolean()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextBoolean()); + } + + System.out.println(); + System.out.println("Performance:"); + final long start = System.currentTimeMillis(); + long a = 5; + for (int i = 0; i < 1000000000; i++) { + a = a + random.nextLong(); + } + System.out.println(a + " " + (System.currentTimeMillis() - start)); + } + +} diff --git a/src/test/java/de/extio/game_engine/util/rng/JavaThreadLocalRandomTests.java b/src/test/java/de/extio/game_engine/util/rng/JavaThreadLocalRandomTests.java new file mode 100644 index 0000000..fb01a1e --- /dev/null +++ b/src/test/java/de/extio/game_engine/util/rng/JavaThreadLocalRandomTests.java @@ -0,0 +1,62 @@ +/* Copyright (C) 2023 Stephan Birkl - All Rights Reserved. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.extio.game_engine.util.rng; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +import org.junit.jupiter.api.Test; + +public class JavaThreadLocalRandomTests { + + @Test + public void testReferenceRandom() { + final Random random = ThreadLocalRandom.current(); + + System.out.println("nextDouble()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextDouble()); + } + System.out.println("nextInt()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextInt()); + } + System.out.println("nextInt(5)"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextInt(5)); + } + System.out.println("nextLong()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextLong()); + } + System.out.println("nextBoolean()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextBoolean()); + } + + System.out.println(); + System.out.println("Performance longs:"); + long start = System.currentTimeMillis(); + long a = 5; + for (int i = 0; i < 1000000000; i++) { + a = a + random.nextLong(); + } + System.out.println(a + " " + (System.currentTimeMillis() - start)); + + System.out.println(); + System.out.println("Performance ints:"); + start = System.currentTimeMillis(); + int b = 5; + for (int i = 0; i < 1000000000; i++) { + b = b + random.nextInt(); + } + System.out.println(b + " " + (System.currentTimeMillis() - start)); + } + +} diff --git a/src/test/java/de/extio/game_engine/util/rng/XorShift128RandomTests.java b/src/test/java/de/extio/game_engine/util/rng/XorShift128RandomTests.java new file mode 100644 index 0000000..3c0bd83 --- /dev/null +++ b/src/test/java/de/extio/game_engine/util/rng/XorShift128RandomTests.java @@ -0,0 +1,101 @@ +/* Copyright (C) 2023 Stephan Birkl - All Rights Reserved. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.extio.game_engine.util.rng; + +import java.util.Random; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class XorShift128RandomTests { + + @Test + public void testXorShift128Random() { + final Random random = new XorShift128Random(42); + + System.out.println("nextDouble()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextDouble()); + } + System.out.println("nextInt()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextInt()); + } + System.out.println("nextInt(5)"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextInt(5)); + } + System.out.println("nextLong()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextLong()); + } + System.out.println("nextBoolean()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextBoolean()); + } + + System.out.println(); + System.out.println("Performance longs:"); + long start = System.currentTimeMillis(); + long a = 5; + for (int i = 0; i < 1000000000; i++) { + a = a + random.nextLong(); + } + System.out.println(a + " " + (System.currentTimeMillis() - start)); + + System.out.println(); + System.out.println("Performance ints:"); + start = System.currentTimeMillis(); + int b = 5; + for (int i = 0; i < 1000000000; i++) { + b = b + random.nextInt(); + } + System.out.println(b + " " + (System.currentTimeMillis() - start)); + } + + @Test + public void testXorShift128RandomSeed0() { + final Random random = new XorShift128Random(0); + + System.out.println("nextDouble()"); + for (int i = 0; i < 10; i++) { + System.out.println(random.nextDouble()); + } + } + + @Test + public void testXorShift128RandomIntRange() { + final Random random = new XorShift128Random(777); + + for (int i = 1; i < 10; i++) { + for (int ii = 0; ii < 10; ii++) { + System.out.println(i + " " + ii + " random.nextInt(i): " + random.nextInt(i)); + } + } + } + + @Test + @Disabled + public void testXorShift128RandomDoubleRange() { + final Random random = new XorShift128Random(); + + final int[] buckets = new int[1000]; + + for (int i = 0; i < 1000000; i++) { + final double n = random.nextDouble(); + final int bucket = (int) Math.floor(n / 0.001); + buckets[bucket]++; + } + + for (int i = 0; i < 1000; i++) { + System.out.println(String.format("%03d", i) + " " + buckets[i]); + } + } + +} diff --git a/src/test/java/de/extio/game_engine/util/rng/ZeroRandomTests.java b/src/test/java/de/extio/game_engine/util/rng/ZeroRandomTests.java new file mode 100644 index 0000000..19479da --- /dev/null +++ b/src/test/java/de/extio/game_engine/util/rng/ZeroRandomTests.java @@ -0,0 +1,52 @@ +/* Copyright (C) 2023 Stephan Birkl - All Rights Reserved. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package de.extio.game_engine.util.rng; + +import java.util.Random; + +import org.junit.jupiter.api.Test; + +public class ZeroRandomTests { + + @Test + public void testSpeedRunRandom() { + final Random speedRunRandom = new ZeroRandom(); + + System.out.println("nextDouble()"); + for (int i = 0; i < 10; i++) { + System.out.println(speedRunRandom.nextDouble()); + } + System.out.println("nextInt()"); + for (int i = 0; i < 10; i++) { + System.out.println(speedRunRandom.nextInt()); + } + System.out.println("nextInt(5)"); + for (int i = 0; i < 10; i++) { + System.out.println(speedRunRandom.nextInt(5)); + } + System.out.println("nextLong()"); + for (int i = 0; i < 10; i++) { + System.out.println(speedRunRandom.nextLong()); + } + System.out.println("nextBoolean()"); + for (int i = 0; i < 10; i++) { + System.out.println(speedRunRandom.nextBoolean()); + } + + System.out.println(); + System.out.println("Performance:"); + final long start = System.currentTimeMillis(); + long a = 5; + for (int i = 0; i < 1000000000; i++) { + a = a + speedRunRandom.nextLong(); + } + System.out.println(a + " " + (System.currentTimeMillis() - start)); + } + +}