fixed srs rotations

This commit is contained in:
2026-02-03 15:51:41 +01:00
parent 76df6643db
commit 0b3c757ce0
2 changed files with 70 additions and 41 deletions

View File

@@ -1,38 +1,52 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;
import java.util.Map;
import java.util.stream.IntStream;
public final class RotationChecker {
private record RotationKey(Orientation from, Orientation to) {}
private static final Map<RotationKey, int[][]> STANDARD_WALL_KICKS = Map.of(
new RotationKey(Orientation.NONE, Orientation.RIGHT), new int[][] {{0,0}, {-1,0}, {-1,1}, {0,-2}, {-1,-2}},
new RotationKey(Orientation.RIGHT, Orientation.NONE), new int[][] {{0,0}, {1,0}, {1,-1}, {0,2}, {1,2}},
new RotationKey(Orientation.RIGHT, Orientation.UPSIDE_DOWN), new int[][] {{0,0}, {1,0}, {1,-1}, {0,2}, {1,2}},
new RotationKey(Orientation.UPSIDE_DOWN, Orientation.RIGHT), new int[][] {{0,0}, {-1,0}, {-1,1}, {0,-2}, {-1,-2}},
new RotationKey(Orientation.UPSIDE_DOWN, Orientation.LEFT), new int[][] {{0,0}, {1,0}, {1,1}, {0,-2}, {1,-2}},
new RotationKey(Orientation.LEFT, Orientation.UPSIDE_DOWN), new int[][] {{0,0}, {-1,0}, {-1,-1}, {0,2}, {-1,2}},
new RotationKey(Orientation.LEFT, Orientation.NONE), new int[][] {{0,0}, {-1,0}, {-1,-1}, {0,2}, {-1,2}},
new RotationKey(Orientation.NONE, Orientation.LEFT), new int[][] {{0,0}, {1,0}, {1,1}, {0,-2}, {1,-2}}
public final class RotationChecker {private static final Map<Orientation, int[][]> STANDARD_WALL_KICKS = Map.of(
Orientation.NONE, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
Orientation.RIGHT, new int[][] {{0,0}, {1,0}, {1,-1}, {0,2}, {1,2}},
Orientation.UPSIDE_DOWN, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
Orientation.LEFT, new int[][] {{0,0}, {-1,0}, {-1,-1}, {0,2}, {-1,2}}
);
private static final Map<RotationKey, int[][]> I_WALL_KICKS = Map.of(
new RotationKey(Orientation.NONE, Orientation.RIGHT), new int[][] {{0,0}, {-2,0}, {1,0}, {-2,-1}, {1,2}},
new RotationKey(Orientation.RIGHT, Orientation.NONE), new int[][] {{0,0}, {2,0}, {-1,0}, {2,1}, {-1,-2}},
new RotationKey(Orientation.RIGHT, Orientation.UPSIDE_DOWN), new int[][] {{0,0}, {-1,0}, {2,0}, {-1,2}, {2,-1}},
new RotationKey(Orientation.UPSIDE_DOWN, Orientation.RIGHT), new int[][] {{0,0}, {1,0}, {-2,0}, {1,-2}, {-2,1}},
new RotationKey(Orientation.UPSIDE_DOWN, Orientation.LEFT), new int[][] {{0,0}, {2,0}, {-1,0}, {2,1}, {-1,-2}},
new RotationKey(Orientation.LEFT, Orientation.UPSIDE_DOWN), new int[][] {{0,0}, {-2,0}, {1,0}, {-2,-1}, {1,2}},
new RotationKey(Orientation.LEFT, Orientation.NONE), new int[][] {{0,0}, {1,0}, {-2,0}, {1,-2}, {-2,1}},
new RotationKey(Orientation.NONE, Orientation.LEFT), new int[][] {{0,0}, {-1,0}, {2,0}, {-1,2}, {2,-1}}
private static final Map<Orientation, int[][]> I_WALL_KICKS = Map.of(
Orientation.NONE, new int[][] {{0,0}, {-1,0}, {2,0}, {-1,0}, {2,0}},
Orientation.RIGHT, new int[][] {{-1,0}, {0,0}, {0,0}, {0,1}, {0,-2}},
Orientation.UPSIDE_DOWN, new int[][] {{-1,1}, {1,1}, {-2,1}, {1,0}, {-2,0}},
Orientation.LEFT, new int[][] {{0,1}, {0,1}, {0,1}, {0,-1}, {0,2}}
);
private static final Map<Orientation, int[][]> O_WALL_KICKS = Map.of(
Orientation.NONE, new int[][] {{0,0}},
Orientation.RIGHT, new int[][] {{0,-1}},
Orientation.UPSIDE_DOWN, new int[][] {{-1,-1}},
Orientation.LEFT, new int[][] {{-1,0}}
);
public static int[][] getKicksArray(Orientation from, Orientation to, Tetromino.Shape shape) {
RotationKey rotationKey = new RotationKey(from, to);
int[][] firstOffsets = getOffsetData(from, shape);
int[][] secondOffsets = getOffsetData(to, shape);
int[][] result = new int[firstOffsets.length][2];
for(int i = 0; i < firstOffsets.length; i++) {
result[i] = subtractInt(firstOffsets[i], secondOffsets[i]);
}
return result;
}
private static int[] subtractInt(int[] first, int[] second) {
return IntStream.range(0, first.length)
.map(i -> first[i] - second[i])
.toArray();
}
private static int[][] getOffsetData(Orientation orientation, Tetromino.Shape shape) {
return switch(shape) {
case J, L, S, T, Z -> STANDARD_WALL_KICKS.get(rotationKey);
case I -> I_WALL_KICKS.get(rotationKey);
default -> new int[][]{{0, 0}};
case J, L, S, T, Z -> STANDARD_WALL_KICKS.get(orientation);
case I -> I_WALL_KICKS.get(orientation);
case O -> O_WALL_KICKS.get(orientation);
};
}
}

View File

@@ -29,10 +29,10 @@ public class Tetromino {
this.uuid = UUID.randomUUID();
switch(this.shape) {
case I -> this.shapeArray = new int[][]{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}};
case I -> this.shapeArray = new int[][]{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 1, 1, 1, 1}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}};
case J -> this.shapeArray = new int[][]{{1, 0, 0}, {1, 1, 1}, {0, 0, 0}};
case L -> this.shapeArray = new int[][]{{0, 0, 1}, {1, 1, 1}, {0, 0, 0}};
case O -> this.shapeArray = new int[][]{{1, 1}, {1, 1}};
case O -> this.shapeArray = new int[][]{{0, 1, 1}, {0, 1, 1}, {0, 0, 0}};
case S -> this.shapeArray = new int[][]{{0, 1, 1}, {1, 1, 0}, {0, 0, 0}};
case T -> this.shapeArray = new int[][]{{0, 1, 0}, {1, 1, 1}, {0, 0, 0}};
case Z -> this.shapeArray = new int[][]{{1, 1, 0}, {0, 1, 1}, {0, 0, 0}};
@@ -53,9 +53,12 @@ public class Tetromino {
// TODO: doesn't work for I-Tetromino
int[][] kicksArray = RotationChecker.getKicksArray(this.orientation, newOrientation, this.shape);
System.out.println(this.shape.toString() + ": " + Arrays.deepToString(kicksArray));
for(int[] k : kicksArray) {
Pos candidate = new Pos(this.position.x() + k[0], this.position.y() + k[1], this.position.z());
if(this.checkCollisionAndMove(candidate, newShapeArray)) {
boolean moved = this.checkCollisionAndMove(candidate, newShapeArray);
System.out.println("Candidate: " + Arrays.toString(k) + " " + moved);
if(moved) {
this.orientation = newOrientation;
return true;
}
@@ -86,7 +89,7 @@ public class Tetromino {
public void draw(boolean withGhost) {
if(withGhost) {
Pos ghostPos = this.position;
while(!this.checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) {
while(!this.hasCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) {
ghostPos = ghostPos.sub(0, 1, 0);
}
Pos positionChange = this.position.sub(ghostPos);
@@ -212,9 +215,8 @@ public class Tetromino {
for(int y = 0; y < arrayLength; y++) {
if(shapeArray[arrayLength - 1 - y][x] == 1) {
switch(this.shape) {
case I -> returnList.add(position.add(x - 1, y - 2, 0));
case O -> returnList.add(position.add(x, y, 0));
default -> returnList.add(position.add(x - 1, y - 1, 0));
case I -> returnList.add(new Pos(position).add(x - 2, y - 2, 0));
default -> returnList.add(new Pos(position).add(x - 1, y - 1, 0));
}
}
}
@@ -223,7 +225,22 @@ public class Tetromino {
return returnList;
}
private boolean checkCollision(Pos newPosition, int[][] newShapeArray) {
// private List<Pos> getBlockPositions(Pos position, int[][] shapeArray) {
// List<Pos> returnList = new ArrayList<>();
// if(position == null) return returnList;
//
// for(int x = 0; x < shapeArray.length; x++) {
// for(int y = 0; y < shapeArray.length; y++) {
// if(shapeArray[x][y] == 1) {
// returnList.add(new Pos(position).sub(1, 1, 0).add(x, y, 0));
// }
// }
// }
//
// return returnList;
// }
private boolean hasCollision(Pos newPosition, int[][] newShapeArray) {
List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray);
for(Pos pos : newBlockPositions) {
@@ -236,15 +253,13 @@ public class Tetromino {
}
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
if(!this.checkCollision(newPosition, newShapeArray)) {
if(this.hasCollision(newPosition, newShapeArray)) return false;
this.remove();
this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new);
this.setPosition(newPosition);
this.draw();
return true;
}
return false;
}
public enum Shape {
I,