player physics with bidirectional movement with tests, intended for use in navigable surface (because euler tour forests require an undirected graph)

This commit is contained in:
Leijurv
2023-03-27 09:14:30 +09:00
parent b3afa98958
commit 5ad90997ff
7 changed files with 271 additions and 20 deletions

View File

@@ -825,6 +825,8 @@ public class NavigableSurfaceTest {
"XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|X XXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXX X\n" +
"XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXX \n" +
"XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX\n";
// basically the story is that i saw THIS ^^^ weird ass shape, then i spent the next few hours figuring out exactly what happened and convincing myself that it actually is accurate and I don't have any bugs causing this incorrectly
// because i mean what are the odds lol, it looks crazy
assertEquals(shouldBeWeird, reportAllFourWalls(surface3));
}

View File

@@ -0,0 +1,146 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone.builder;
import org.junit.Test;
import static org.junit.Assert.*;
public class PlayerPhysicsTest {
@Test
public void testBasic() {
assertEquals(16, PlayerPhysics.highestCollision(BlockStateCachedData.SCAFFOLDING, BlockStateCachedData.SCAFFOLDING));
Column normal = new Column();
normal.underneath = BlockStateCachedData.SCAFFOLDING;
normal.feet = BlockStateCachedData.AIR;
normal.head = BlockStateCachedData.AIR;
normal.above = BlockStateCachedData.AIR;
normal.aboveAbove = BlockStateCachedData.AIR;
normal.init();
Column up = new Column();
up.underneath = BlockStateCachedData.SCAFFOLDING;
up.feet = BlockStateCachedData.SCAFFOLDING;
up.head = BlockStateCachedData.AIR;
up.above = BlockStateCachedData.AIR;
up.aboveAbove = BlockStateCachedData.AIR;
up.init();
assertEquals(PlayerPhysics.Collision.VOXEL_LEVEL, PlayerPhysics.playerTravelCollides(normal, normal));
assertEquals(PlayerPhysics.Collision.JUMP_TO_VOXEL_UP, PlayerPhysics.playerTravelCollides(normal, up));
}
private static final BlockStateCachedData[] BY_HEIGHT;
static {
BY_HEIGHT = new BlockStateCachedData[Blip.FULL_BLOCK + 1];
for (int height = 1; height <= Blip.FULL_BLOCK; height++) {
BY_HEIGHT[height] = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(height * Blip.RATIO));
}
BY_HEIGHT[0] = BlockStateCachedData.AIR;
}
private static BlockStateCachedData[] makeColToHeight(int height) {
height += Blip.FULL_BLOCK * 3; // i don't truck with negative division / modulo
if (height < 0) {
throw new IllegalStateException();
}
int fullBlocks = height / Blip.FULL_BLOCK;
BlockStateCachedData[] ret = new BlockStateCachedData[7];
for (int i = 0; i < fullBlocks; i++) {
ret[i] = BlockStateCachedData.SCAFFOLDING;
}
ret[fullBlocks] = BY_HEIGHT[height % Blip.FULL_BLOCK];
for (int i = fullBlocks + 1; i < ret.length; i++) {
ret[i] = BlockStateCachedData.AIR;
}
return ret;
}
private static Column toCol(BlockStateCachedData[] fromCol) {
Column col = new Column();
col.underneath = fromCol[2];
col.feet = fromCol[3];
col.head = fromCol[4];
col.above = fromCol[5];
col.aboveAbove = fromCol[6];
col.init();
return col;
}
@Test
public void testMakeCol() {
for (int i = 0; i < Blip.FULL_BLOCK; i++) {
Column col = toCol(makeColToHeight(i));
assertTrue(col.standing());
assertEquals(i, (int) col.feetBlips);
}
assertFalse(toCol(makeColToHeight(-1)).standing());
assertFalse(toCol(makeColToHeight(Blip.FULL_BLOCK)).standing());
}
@Test
public void testAscDesc() {
for (int startHeight = 0; startHeight < Blip.FULL_BLOCK; startHeight++) {
BlockStateCachedData[] fromCol = makeColToHeight(startHeight);
Column from = toCol(fromCol);
assertEquals(startHeight, (int) from.feetBlips);
for (int endHeight = startHeight - Blip.JUMP - 10; endHeight <= startHeight + Blip.JUMP + 10; endHeight++) {
BlockStateCachedData[] toCol = makeColToHeight(endHeight);
Column to = toCol(toCol);
int endVoxel = (endHeight + Blip.FULL_BLOCK * 10) / Blip.FULL_BLOCK - 10; // hate negative division rounding to zero, punch negative division rounding to zero
PlayerPhysics.Collision col = PlayerPhysics.playerTravelCollides(from, to);
Integer dy = PlayerPhysics.bidirectionalPlayerTravel(from, to, toCol[1], toCol[0]);
assertEquals(col == PlayerPhysics.Collision.BLOCKED, endHeight > startHeight + Blip.JUMP);
assertEquals(col == PlayerPhysics.Collision.FALL, endVoxel < 0);
assertEquals(dy == null, endHeight > startHeight + Blip.JUMP || endHeight < startHeight - Blip.JUMP);
if (dy != null) {
Integer reverse = -dy;
BlockStateCachedData[] shiftedTo = shift(toCol, reverse);
BlockStateCachedData[] shiftedFrom = shift(fromCol, reverse);
// make sure it's actually bidirectional
assertEquals(reverse, PlayerPhysics.bidirectionalPlayerTravel(toCol(shiftedTo), toCol(shiftedFrom), shiftedFrom[1], shiftedFrom[0]));
}
if (col != PlayerPhysics.Collision.BLOCKED && col != PlayerPhysics.Collision.FALL) {
assertEquals(col.voxelVerticalOffset(), endVoxel);
assertEquals(endVoxel, (int) dy);
assertEquals(col.requiresJump(), endHeight > startHeight + Blip.HALF_BLOCK);
}
System.out.println(startHeight + " " + (endHeight - startHeight) + " " + col + " " + endVoxel + " " + dy);
}
}
}
private static BlockStateCachedData[] shift(BlockStateCachedData[] col, int dy) {
BlockStateCachedData[] ret = new BlockStateCachedData[7];
for (int i = 0; i < ret.length; i++) {
int j = i - dy;
if (j >= 0 && j < col.length) {
ret[i] = col[j];
} else {
ret[i] = BlockStateCachedData.OUT_OF_BOUNDS;
}
}
return ret;
}
}