namespace ActionBlocks.Game.Core; public interface IRotationSystem { /// /// Tries to rotate by (+1 = clockwise, -1 = counter-clockwise). /// Returns the rotated piece and the index of the kick that was used, or null if no kick fits. /// (ActivePiece Piece, int KickIndex)? TryRotate(Board board, ActivePiece piece, int direction); } /// Standard rotation system with wall and floor kicks. public sealed class SuperRotationSystem : IRotationSystem { // Kick tables use the conventional y-up notation and are flipped when applied. private static readonly Dictionary<(int From, int To), Point[]> JlstzKicks = new() { [(0, 1)] = [new(0, 0), new(-1, 0), new(-1, 1), new(0, -2), new(-1, -2)], [(1, 0)] = [new(0, 0), new(1, 0), new(1, -1), new(0, 2), new(1, 2)], [(1, 2)] = [new(0, 0), new(1, 0), new(1, -1), new(0, 2), new(1, 2)], [(2, 1)] = [new(0, 0), new(-1, 0), new(-1, 1), new(0, -2), new(-1, -2)], [(2, 3)] = [new(0, 0), new(1, 0), new(1, 1), new(0, -2), new(1, -2)], [(3, 2)] = [new(0, 0), new(-1, 0), new(-1, -1), new(0, 2), new(-1, 2)], [(3, 0)] = [new(0, 0), new(-1, 0), new(-1, -1), new(0, 2), new(-1, 2)], [(0, 3)] = [new(0, 0), new(1, 0), new(1, 1), new(0, -2), new(1, -2)], }; private static readonly Dictionary<(int From, int To), Point[]> IKicks = new() { [(0, 1)] = [new(0, 0), new(-2, 0), new(1, 0), new(-2, -1), new(1, 2)], [(1, 0)] = [new(0, 0), new(2, 0), new(-1, 0), new(2, 1), new(-1, -2)], [(1, 2)] = [new(0, 0), new(-1, 0), new(2, 0), new(-1, 2), new(2, -1)], [(2, 1)] = [new(0, 0), new(1, 0), new(-2, 0), new(1, -2), new(-2, 1)], [(2, 3)] = [new(0, 0), new(2, 0), new(-1, 0), new(2, 1), new(-1, -2)], [(3, 2)] = [new(0, 0), new(-2, 0), new(1, 0), new(-2, -1), new(1, 2)], [(3, 0)] = [new(0, 0), new(1, 0), new(-2, 0), new(1, -2), new(-2, 1)], [(0, 3)] = [new(0, 0), new(-1, 0), new(2, 0), new(-1, 2), new(2, -1)], }; private static readonly Point[] NoKicks = [new(0, 0)]; public (ActivePiece Piece, int KickIndex)? TryRotate(Board board, ActivePiece piece, int direction) { var from = PieceDefinitions.Mod(piece.Rotation); var to = PieceDefinitions.Mod(piece.Rotation + direction); var kicks = piece.Type switch { PieceType.O => NoKicks, PieceType.I => IKicks[(from, to)], _ => JlstzKicks[(from, to)], }; for (var i = 0; i < kicks.Length; i++) { var candidate = piece with { Rotation = to, X = piece.X + kicks[i].X, Y = piece.Y - kicks[i].Y }; if (board.Fits(candidate)) { return (candidate, i); } } return null; } }