diff --git a/README.md b/README.md index 4603ddb..0f0322b 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,6 @@ The ordered priorities for testing are: ### TODO - Add more tests - - Finish Super Rotation System testing in [tetrimino_test.go](./pkg/tetris/tetrimino_test.go). - Add tests for scoring endOnMaxLevel. - Revisit scoring tests. - Add the remaining Lock Down options. diff --git a/pkg/tetris/tetrimino.go b/pkg/tetris/tetrimino.go index 65936ed..adb2670 100644 --- a/pkg/tetris/tetrimino.go +++ b/pkg/tetris/tetrimino.go @@ -6,6 +6,8 @@ import ( "math" ) +const invalidRotationPoint = -1 + // A Tetrimino is a geometric Tetris shape formed by four Minos connected along their sides. type Tetrimino struct { // The value of the Tetrimino. This is the character that will be used to represent the Tetrimino in the matrix. @@ -252,26 +254,28 @@ func (t *Tetrimino) Rotate(matrix Matrix, clockwise bool) error { rotated := t.DeepCopy() var err error - var foundValid bool + var rotationPoint int if clockwise { - foundValid, err = rotated.rotateClockwise(matrix) + rotationPoint, err = rotated.rotateClockwise(matrix) } else { - foundValid, err = rotated.rotateCounterClockwise(matrix) + rotationPoint, err = rotated.rotateCounterClockwise(matrix) } if err != nil { return fmt.Errorf("failed to rotate tetrimino: %w", err) } - if foundValid { - t.Pos = rotated.Pos - t.Minos = rotated.Minos - t.CompassDirection = rotated.CompassDirection + foundValid := rotationPoint != invalidRotationPoint + if !foundValid { + return nil } + t.Pos = rotated.Pos + t.Minos = rotated.Minos + t.CompassDirection = rotated.CompassDirection return nil } -func (t *Tetrimino) rotateClockwise(matrix Matrix) (bool, error) { +func (t *Tetrimino) rotateClockwise(matrix Matrix) (int, error) { // Reverse the order of the rows for i, j := 0, len(t.Minos)-1; i < j; i, j = i+1, j-1 { t.Minos[i], t.Minos[j] = t.Minos[j], t.Minos[i] @@ -282,23 +286,23 @@ func (t *Tetrimino) rotateClockwise(matrix Matrix) (bool, error) { var err error t.CompassDirection, err = positiveMod(t.CompassDirection+1, len(t.RotationCompass)) if err != nil { - return false, fmt.Errorf("failed to get positive mod: %w", err) + return invalidRotationPoint, fmt.Errorf("failed to get positive mod: %w", err) } originalX, originalY := t.Pos.X, t.Pos.Y - for _, coord := range t.RotationCompass[t.CompassDirection] { + for i, coord := range t.RotationCompass[t.CompassDirection] { t.Pos.X = originalX + coord.X t.Pos.Y = originalY + coord.Y if t.isValid(matrix) { - return true, nil + return i + 1, nil } } - return false, nil + return invalidRotationPoint, nil } -func (t *Tetrimino) rotateCounterClockwise(matrix Matrix) (bool, error) { +func (t *Tetrimino) rotateCounterClockwise(matrix Matrix) (int, error) { // Reverse the order of the columns for _, row := range t.Minos { for i, j := 0, len(row)-1; i < j; i, j = i+1, j-1 { @@ -308,28 +312,28 @@ func (t *Tetrimino) rotateCounterClockwise(matrix Matrix) (bool, error) { t.transpose() - foundValid := false + rotationPoint := invalidRotationPoint originalX, originalY := t.Pos.X, t.Pos.Y - for _, coord := range t.RotationCompass[t.CompassDirection] { + for i, coord := range t.RotationCompass[t.CompassDirection] { t.Pos.X = originalX - coord.X t.Pos.Y = originalY - coord.Y if t.isValid(matrix) { - foundValid = true + rotationPoint = i + 1 break } } - if !foundValid { - return false, nil + if rotationPoint == invalidRotationPoint { + return rotationPoint, nil } var err error t.CompassDirection, err = positiveMod(t.CompassDirection-1, len(t.RotationCompass)) if err != nil { - return false, fmt.Errorf("failed to get positive mod: %w", err) + return invalidRotationPoint, fmt.Errorf("failed to get positive mod: %w", err) } - return true, nil + return rotationPoint, nil } func (t *Tetrimino) transpose() { diff --git a/pkg/tetris/tetrimino_test.go b/pkg/tetris/tetrimino_test.go index 2e4633f..79fba15 100644 --- a/pkg/tetris/tetrimino_test.go +++ b/pkg/tetris/tetrimino_test.go @@ -402,9 +402,10 @@ func TestTetrimino_Rotate(t *testing.T) { func TestTetrimino_rotateClockwise(t *testing.T) { tt := map[string]struct { - matrix Matrix - tet *Tetrimino - wantTet *Tetrimino + matrix Matrix + tet *Tetrimino + wantTet *Tetrimino + wantRotationPoint int }{ "I; starting rotation 0 (north); rotation point 1": { matrix: Matrix{ @@ -434,6 +435,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 1, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 1, }, "I; starting rotation 0 (north); rotation point 2": { matrix: Matrix{ @@ -463,6 +465,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 1, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 2, }, "I; starting rotation 0 (north); rotation point 3": { matrix: Matrix{ @@ -492,6 +495,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 1, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 3, }, "I; starting rotation 0 (north); rotation point 4": { matrix: Matrix{ @@ -522,6 +526,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 1, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 4, }, "I; starting rotation 0 (north); rotation point 5": { matrix: Matrix{ @@ -554,6 +559,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 1, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 5, }, "I; starting rotation 0 (north); no valid rotation": { matrix: Matrix{ @@ -599,6 +605,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 2, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 1, }, "I; starting rotation 1 (east); rotation point 2": { matrix: Matrix{ @@ -628,6 +635,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 2, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 2, }, "I; starting rotation 1 (east); rotation point 3": { matrix: Matrix{ @@ -657,6 +665,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 2, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 3, }, "I; starting rotation 1 (east); rotation point 4": { matrix: Matrix{ @@ -686,6 +695,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 2, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 4, }, "I; starting rotation 1 (east); rotation point 5": { matrix: Matrix{ @@ -715,6 +725,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 2, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 5, }, "I; starting rotation 1 (east); no valid rotation": { matrix: Matrix{ @@ -763,6 +774,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 3, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 1, }, "I; starting rotation 2 (south); rotation point 2": { matrix: Matrix{ @@ -792,6 +804,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 3, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 2, }, "I; starting rotation 2 (south); rotation point 3": { matrix: Matrix{ @@ -821,6 +834,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 3, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 3, }, "I; starting rotation 2 (south); rotation point 4": { matrix: Matrix{ @@ -851,6 +865,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 3, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 4, }, "I; starting rotation 2 (south); rotation point 5": { matrix: Matrix{ @@ -882,6 +897,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 3, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 5, }, "I; starting rotation 2 (south); no valid rotation": { matrix: Matrix{ @@ -927,6 +943,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 1, }, "I; starting rotation 3 (west); rotation point 2": { matrix: Matrix{ @@ -956,6 +973,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 2, }, "I; starting rotation 3 (west); rotation point 3": { matrix: Matrix{ @@ -985,6 +1003,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 3, }, "I; starting rotation 3 (west); rotation point 4": { matrix: Matrix{ @@ -1014,6 +1033,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 4, }, "I; starting rotation 3 (west); rotation point 5": { matrix: Matrix{ @@ -1043,6 +1063,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['I'], }, + wantRotationPoint: 5, }, "I; starting rotation 3 (west); no valid rotation": { matrix: Matrix{ @@ -1088,6 +1109,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 1, RotationCompass: RotationCompasses['O'], }, + wantRotationPoint: 1, }, "O; starting rotation 0 (north); no valid rotation": { matrix: Matrix{ @@ -1131,6 +1153,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 2, RotationCompass: RotationCompasses['O'], }, + wantRotationPoint: 1, }, "O; starting rotation 1 (east); no valid rotation": { matrix: Matrix{ @@ -1174,6 +1197,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 3, RotationCompass: RotationCompasses['O'], }, + wantRotationPoint: 1, }, "O; starting rotation 2 (south); no valid rotation": { matrix: Matrix{ @@ -1217,6 +1241,7 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['O'], }, + wantRotationPoint: 1, }, "O; starting rotation 3 (west); no valid rotation": { matrix: Matrix{ @@ -1262,8 +1287,9 @@ func TestTetrimino_rotateClockwise(t *testing.T) { CompassDirection: 1, RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 1, }, - "6; starting rotation 1 (east); rotation point 1": { + "6; starting rotation 0 (north); rotation point 2": { matrix: Matrix{ {0, 0, 0}, {0, 0, 0}, @@ -1272,26 +1298,27 @@ func TestTetrimino_rotateClockwise(t *testing.T) { tet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {true, false}, - {true, true}, - {true, false}, + {false, true, false}, + {true, true, true}, }, Pos: Coordinate{X: 1, Y: 0}, - CompassDirection: 1, + CompassDirection: 0, RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {true, true, true}, - {false, true, false}, + {true, false}, + {true, true}, + {true, false}, }, - Pos: Coordinate{X: 0, Y: 1}, - CompassDirection: 2, + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 1, RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 2, }, - "6; starting rotation 2 (south); rotation point 1": { + "6; starting rotation 0 (north); rotation point 3": { matrix: Matrix{ {0, 0, 0}, {0, 0, 0}, @@ -1300,26 +1327,27 @@ func TestTetrimino_rotateClockwise(t *testing.T) { tet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {true, true, true}, {false, true, false}, + {true, true, true}, }, Pos: Coordinate{X: 0, Y: 1}, - CompassDirection: 2, + CompassDirection: 0, RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {false, true}, + {true, false}, {true, true}, - {false, true}, + {true, false}, }, Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 3, + CompassDirection: 1, RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 3, }, - "6; starting rotation 3 (west); rotation point 1": { + "6; starting rotation 0 (north); rotation point 4": { matrix: Matrix{ {0, 0, 0}, {0, 0, 0}, @@ -1328,277 +1356,415 @@ func TestTetrimino_rotateClockwise(t *testing.T) { tet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {false, true}, - {true, true}, - {false, true}, + {false, true, false}, + {true, true, true}, }, - Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 3, + Pos: Coordinate{X: -1, Y: -2}, + CompassDirection: 0, RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {false, true, false}, - {true, true, true}, + {true, false}, + {true, true}, + {true, false}, }, Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 0, + CompassDirection: 1, RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 4, }, - } - - for name, tc := range tt { - t.Run(name, func(t *testing.T) { - result, err := tc.tet.rotateClockwise(tc.matrix) - - require.NoError(t, err) - if tc.wantTet != nil { - assert.EqualValues(t, tc.wantTet, tc.tet) - assert.True(t, result) - } else { - assert.False(t, result) - } - }) - } -} - -func TestTetrimino_rotateCounterClockwise(t *testing.T) { - tt := map[string]struct { - matrix Matrix - tet *Tetrimino - wantTet *Tetrimino - }{ - "I; starting rotation 0 (north); rotation point 1": { + "6; starting rotation 0 (north); rotation point 5": { matrix: Matrix{ - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, }, tet: &Tetrimino{ - Value: 'I', + Value: 'T', Minos: [][]bool{ - {true, true, true, true}, + {false, true, false}, + {true, true, true}, }, - Pos: Coordinate{X: 0, Y: 1}, + Pos: Coordinate{X: 0, Y: -2}, CompassDirection: 0, - RotationCompass: RotationCompasses['I'], + RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ - Value: 'I', + Value: 'T', Minos: [][]bool{ - {true}, - {true}, - {true}, - {true}, + {true, false}, + {true, true}, + {true, false}, }, - Pos: Coordinate{X: 1, Y: 0}, - CompassDirection: 3, - RotationCompass: RotationCompasses['I'], + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 5, }, - "I; starting rotation 1 (east); rotation point 1": { + "6; starting rotation 0 (north); no valid rotation": { matrix: Matrix{ - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, + {0}, }, tet: &Tetrimino{ - Value: 'I', - Minos: [][]bool{ - {true}, - {true}, - {true}, - {true}, - }, - Pos: Coordinate{X: 2, Y: 0}, - CompassDirection: 1, - RotationCompass: RotationCompasses['I'], - }, - wantTet: &Tetrimino{ - Value: 'I', + Value: 'T', Minos: [][]bool{ - {true, true, true, true}, + {false, true, false}, + {true, true, true}, }, - Pos: Coordinate{X: 0, Y: 1}, + Pos: Coordinate{X: 0, Y: 0}, CompassDirection: 0, - RotationCompass: RotationCompasses['I'], + RotationCompass: RotationCompasses['6'], }, + wantTet: nil, }, - "I; starting rotation 2 (south); rotation point 1": { + + "6; starting rotation 1 (east); rotation point 1": { matrix: Matrix{ - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, }, tet: &Tetrimino{ - Value: 'I', + Value: 'T', Minos: [][]bool{ - {true, true, true, true}, + {true, false}, + {true, true}, + {true, false}, }, - Pos: Coordinate{X: 0, Y: 2}, - CompassDirection: 2, - RotationCompass: RotationCompasses['I'], + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ - Value: 'I', + Value: 'T', Minos: [][]bool{ - {true}, - {true}, - {true}, - {true}, + {true, true, true}, + {false, true, false}, }, - Pos: Coordinate{X: 2, Y: 0}, - CompassDirection: 1, - RotationCompass: RotationCompasses['I'], + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 1, }, - "I; starting rotation 3 (west); rotation point 1": { + "6; starting rotation 1 (east); rotation point 2": { matrix: Matrix{ - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, }, tet: &Tetrimino{ - Value: 'I', + Value: 'T', Minos: [][]bool{ - {true}, - {true}, - {true}, - {true}, + {true, false}, + {true, true}, + {true, false}, }, - Pos: Coordinate{X: 1, Y: 0}, - CompassDirection: 3, - RotationCompass: RotationCompasses['I'], + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ - Value: 'I', + Value: 'T', Minos: [][]bool{ - {true, true, true, true}, + {true, true, true}, + {false, true, false}, }, - Pos: Coordinate{X: 0, Y: 2}, + Pos: Coordinate{X: 0, Y: 1}, CompassDirection: 2, - RotationCompass: RotationCompasses['I'], + RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 2, }, - - "O; starting rotation 0 (north); rotation point 1": { + "6; starting rotation 1 (east); rotation point 3": { matrix: Matrix{ - {0, 0}, - {0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, }, tet: &Tetrimino{ - Value: 'O', + Value: 'T', Minos: [][]bool{ + {true, false}, {true, true}, - {true, true}, + {true, false}, }, - Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 0, - RotationCompass: RotationCompasses['O'], + Pos: Coordinate{X: 0, Y: -2}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ - Value: 'O', + Value: 'T', Minos: [][]bool{ - {true, true}, - {true, true}, + {true, true, true}, + {false, true, false}, }, Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 3, - RotationCompass: RotationCompasses['O'], + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 3, }, - "O; starting rotation 1 (east); rotation point 1": { + "6; starting rotation 1 (east); rotation point 4": { matrix: Matrix{ - {0, 0}, - {0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, }, tet: &Tetrimino{ - Value: 'O', + Value: 'T', Minos: [][]bool{ + {true, false}, {true, true}, - {true, true}, + {true, false}, }, - Pos: Coordinate{X: 0, Y: 0}, + Pos: Coordinate{X: 1, Y: 1}, CompassDirection: 1, - RotationCompass: RotationCompasses['O'], + RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ - Value: 'O', + Value: 'T', Minos: [][]bool{ - {true, true}, - {true, true}, + {true, true, true}, + {false, true, false}, }, Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 0, - RotationCompass: RotationCompasses['O'], + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 4, }, - "O; starting rotation 2 (south); rotation point 1": { + "6; starting rotation 1 (east); rotation point 5": { matrix: Matrix{ - {0, 0}, - {0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, }, tet: &Tetrimino{ - Value: 'O', + Value: 'T', Minos: [][]bool{ + {true, false}, {true, true}, - {true, true}, + {true, false}, }, - Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 2, - RotationCompass: RotationCompasses['O'], - }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, wantTet: &Tetrimino{ - Value: 'O', + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 5, + }, + "6; starting rotation 1 (east); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'T', Minos: [][]bool{ + {true, false}, {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantTet: nil, + }, + + "6; starting rotation 2 (south); rotation point 1": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, {true, true}, + {false, true}, }, Pos: Coordinate{X: 0, Y: 0}, - CompassDirection: 1, - RotationCompass: RotationCompasses['O'], + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 1, }, - "O; starting rotation 3 (west); rotation point 1": { + "6; starting rotation 2 (south); rotation point 2": { matrix: Matrix{ - {0, 0}, - {0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, }, tet: &Tetrimino{ - Value: 'O', + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: -1, Y: 1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', Minos: [][]bool{ + {false, true}, {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 2, + }, + "6; starting rotation 2 (south); rotation point 3": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: -1, Y: 2}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, {true, true}, + {false, true}, }, Pos: Coordinate{X: 0, Y: 0}, CompassDirection: 3, - RotationCompass: RotationCompasses['O'], + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 3, + }, + "6; starting rotation 2 (south); rotation point 4": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: -1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ - Value: 'O', + Value: 'T', Minos: [][]bool{ + {false, true}, {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 4, + }, + "6; starting rotation 2 (south); rotation point 5": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: -1, Y: -1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 5, + }, + "6; starting rotation 2 (south); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, }, Pos: Coordinate{X: 0, Y: 0}, CompassDirection: 2, - RotationCompass: RotationCompasses['O'], + RotationCompass: RotationCompasses['6'], }, + wantTet: nil, }, - "6; starting rotation 0 (north); rotation point 1": { + "6; starting rotation 3 (west); rotation point 1": { matrix: Matrix{ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, }, tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ Value: 'T', Minos: [][]bool{ {false, true, false}, @@ -1608,19 +1774,38 @@ func TestTetrimino_rotateCounterClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['6'], }, - wantTet: &Tetrimino{ + wantRotationPoint: 1, + }, + "6; starting rotation 3 (west); rotation point 2": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ Value: 'T', Minos: [][]bool{ {false, true}, {true, true}, {false, true}, }, - Pos: Coordinate{X: 0, Y: 0}, + Pos: Coordinate{X: 1, Y: 0}, CompassDirection: 3, RotationCompass: RotationCompasses['6'], }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 2, }, - "6; starting rotation 1 (east); rotation point 1": { + "6; starting rotation 3 (west); rotation point 3": { matrix: Matrix{ {0, 0, 0}, {0, 0, 0}, @@ -1629,12 +1814,12 @@ func TestTetrimino_rotateCounterClockwise(t *testing.T) { tet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {true, false}, + {false, true}, {true, true}, - {true, false}, + {false, true}, }, - Pos: Coordinate{X: 1, Y: 0}, - CompassDirection: 1, + Pos: Coordinate{X: 1, Y: -1}, + CompassDirection: 3, RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ @@ -1647,8 +1832,9 @@ func TestTetrimino_rotateCounterClockwise(t *testing.T) { CompassDirection: 0, RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 3, }, - "6; starting rotation 2 (south); rotation point 1": { + "6; starting rotation 3 (west); rotation point 4": { matrix: Matrix{ {0, 0, 0}, {0, 0, 0}, @@ -1657,26 +1843,27 @@ func TestTetrimino_rotateCounterClockwise(t *testing.T) { tet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {true, true, true}, - {false, true, false}, + {false, true}, + {true, true}, + {false, true}, }, - Pos: Coordinate{X: 0, Y: 1}, - CompassDirection: 2, + Pos: Coordinate{X: 0, Y: 2}, + CompassDirection: 3, RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {true, false}, - {true, true}, - {true, false}, + {false, true, false}, + {true, true, true}, }, - Pos: Coordinate{X: 1, Y: 0}, - CompassDirection: 1, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, RotationCompass: RotationCompasses['6'], }, + wantRotationPoint: 4, }, - "6; starting rotation 3 (west); rotation point 1": { + "6; starting rotation 3 (west); rotation point 5": { matrix: Matrix{ {0, 0, 0}, {0, 0, 0}, @@ -1689,30 +1876,1539 @@ func TestTetrimino_rotateCounterClockwise(t *testing.T) { {true, true}, {false, true}, }, - Pos: Coordinate{X: 0, Y: 0}, + Pos: Coordinate{X: 1, Y: 2}, CompassDirection: 3, RotationCompass: RotationCompasses['6'], }, wantTet: &Tetrimino{ Value: 'T', Minos: [][]bool{ - {true, true, true}, {false, true, false}, + {true, true, true}, }, - Pos: Coordinate{X: 0, Y: 1}, - CompassDirection: 2, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 5, + }, + "6; starting rotation 3 (west); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, RotationCompass: RotationCompasses['6'], }, + wantTet: nil, }, } for name, tc := range tt { t.Run(name, func(t *testing.T) { - result, err := tc.tet.rotateCounterClockwise(tc.matrix) + rotationPoint, err := tc.tet.rotateClockwise(tc.matrix) require.NoError(t, err) - assert.True(t, result) - assert.EqualValues(t, tc.wantTet, tc.tet) + if tc.wantTet != nil { + assert.EqualValues(t, tc.wantTet, tc.tet) + assert.Equal(t, tc.wantRotationPoint, rotationPoint) + } else { + assert.Equal(t, invalidRotationPoint, rotationPoint) + } + }) + } +} + +func TestTetrimino_rotateCounterClockwise(t *testing.T) { + tt := map[string]struct { + matrix Matrix + tet *Tetrimino + wantTet *Tetrimino + wantRotationPoint int + }{ + "I; starting rotation 0 (north); rotation point 1": { + matrix: Matrix{ + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 1, + }, + "I; starting rotation 0 (north); rotation point 2": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 2, + }, + "I; starting rotation 0 (north); rotation point 3": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: -3, Y: 1}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 3, + }, + "I; starting rotation 0 (north); rotation point 4": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 3}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 4, + }, + "I; starting rotation 0 (north); rotation point 5": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: -3, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 5, + }, + "I; starting rotation 0 (north); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantTet: nil, + }, + + "I; starting rotation 1 (east); rotation point 1": { + matrix: Matrix{ + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 2, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 1, + }, + "I; starting rotation 1 (east); rotation point 2": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: -1}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 2, + }, + "I; starting rotation 1 (east); rotation point 3": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 3, Y: -1}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 3, + }, + "I; starting rotation 1 (east); rotation point 4": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 4, + }, + "I; starting rotation 1 (east); rotation point 5": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 3, Y: -3}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 5, + }, + "I; starting rotation 1 (east); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 2, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantTet: nil, + }, + + "I; starting rotation 2 (south); rotation point 1": { + matrix: Matrix{ + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 2}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 2, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 1, + }, + "I; starting rotation 2 (south); rotation point 2": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: -3, Y: 2}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 2, + }, + "I; starting rotation 2 (south); rotation point 3": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 2}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 3, + }, + "I; starting rotation 2 (south); rotation point 4": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: -3, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 4, + }, + "I; starting rotation 2 (south); rotation point 5": { + matrix: Matrix{ + {0}, + {0}, + {0}, + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 3}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 5, + }, + "I; starting rotation 2 (south); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 2}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantTet: nil, + }, + + "I; starting rotation 3 (west); rotation point 1": { + matrix: Matrix{ + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 2}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 1, + }, + "I; starting rotation 3 (west); rotation point 2": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 3, Y: -2}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 2, + }, + "I; starting rotation 3 (west); rotation point 3": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: -2}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 3, + }, + "I; starting rotation 3 (west); rotation point 4": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 3, Y: -3}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 4, + }, + "I; starting rotation 3 (west); rotation point 5": { + matrix: Matrix{ + {0, 0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantTet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true, true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['I'], + }, + wantRotationPoint: 5, + }, + "I; starting rotation 3 (west); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'I', + Minos: [][]bool{ + {true}, + {true}, + {true}, + {true}, + }, + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['I'], + }, + wantTet: nil, + }, + + "O; starting rotation 0 (north); rotation point 1": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['O'], + }, + wantTet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['O'], + }, + wantRotationPoint: 1, + }, + "O; starting rotation 0 (north); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['O'], + }, + wantTet: nil, + }, + + "O; starting rotation 1 (east); rotation point 1": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['O'], + }, + wantTet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['O'], + }, + wantRotationPoint: 1, + }, + "O; starting rotation 1 (east); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['O'], + }, + wantTet: nil, + }, + + "O; starting rotation 2 (south); rotation point 1": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['O'], + }, + wantTet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['O'], + }, + wantRotationPoint: 1, + }, + "O; starting rotation 2 (south); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['O'], + }, + wantTet: nil, + }, + + "O; starting rotation 3 (west); rotation point 1": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['O'], + }, + wantTet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['O'], + }, + wantRotationPoint: 1, + }, + "O; starting rotation 3 (west); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'O', + Minos: [][]bool{ + {true, true}, + {true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['O'], + }, + wantTet: nil, + }, + + "6; starting rotation 0 (north); rotation point 1": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 1, + }, + "6; starting rotation 0 (north); rotation point 2": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: -1, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 2, + }, + "6; starting rotation 0 (north); rotation point 3": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: -1, Y: 1}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 3, + }, + "6; starting rotation 0 (north); rotation point 4": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: -2}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 4, + }, + "6; starting rotation 0 (north); rotation point 5": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: -1, Y: -2}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 5, + }, + "6; starting rotation 0 (north); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantTet: nil, + }, + + "6; starting rotation 1 (east); rotation point 1": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 1, + }, + "6; starting rotation 1 (east); rotation point 2": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 2, + }, + "6; starting rotation 1 (east); rotation point 3": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: -1}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 3, + }, + "6; starting rotation 1 (east); rotation point 4": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 1, Y: 2}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 4, + }, + "6; starting rotation 1 (east); rotation point 5": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: 2}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true, false}, + {true, true, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 0, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 5, + }, + "6; starting rotation 1 (east); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantTet: nil, + }, + + "6; starting rotation 2 (south); rotation point 1": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 1, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 1, + }, + "6; starting rotation 2 (south); rotation point 2": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 2, + }, + "6; starting rotation 2 (south); rotation point 3": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 2}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 3, + }, + "6; starting rotation 2 (south); rotation point 4": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: -1, Y: -1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 4, + }, + "6; starting rotation 2 (south); rotation point 5": { + matrix: Matrix{ + {0, 0}, + {0, 0}, + {0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: -1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, false}, + {true, true}, + {true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 1, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 5, + }, + "6; starting rotation 2 (south); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantTet: nil, + }, + + "6; starting rotation 3 (west); rotation point 1": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 1, + }, + "6; starting rotation 3 (west); rotation point 2": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 1, Y: -1}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 2, + }, + "6; starting rotation 3 (west); rotation point 3": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 1, Y: -2}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 3, + }, + "6; starting rotation 3 (west); rotation point 4": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 1}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 4, + }, + "6; starting rotation 3 (west); rotation point 5": { + matrix: Matrix{ + {0, 0, 0}, + {0, 0, 0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 1, Y: 1}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantTet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {true, true, true}, + {false, true, false}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 2, + RotationCompass: RotationCompasses['6'], + }, + wantRotationPoint: 5, + }, + "6; starting rotation 3 (west); no valid rotation": { + matrix: Matrix{ + {0}, + }, + tet: &Tetrimino{ + Value: 'T', + Minos: [][]bool{ + {false, true}, + {true, true}, + {false, true}, + }, + Pos: Coordinate{X: 0, Y: 0}, + CompassDirection: 3, + RotationCompass: RotationCompasses['6'], + }, + wantTet: nil, + }, + } + + for name, tc := range tt { + t.Run(name, func(t *testing.T) { + rotationPoint, err := tc.tet.rotateCounterClockwise(tc.matrix) + + require.NoError(t, err) + if tc.wantTet != nil { + assert.EqualValues(t, tc.wantTet, tc.tet) + assert.Equal(t, tc.wantRotationPoint, rotationPoint) + } else { + assert.Equal(t, invalidRotationPoint, rotationPoint) + } }) } }