Browse Source

Added more tests and improved performance by not making deep copies of the grid

Arnaud Vergnet 4 years ago
parent
commit
3d45bc62b8

+ 5
- 5
screens/Tetris/GameLogic.js View File

@@ -101,7 +101,7 @@ export default class GameLogic {
101 101
         callback(
102 102
             this.scoreManager.getScore(),
103 103
             this.scoreManager.getLevel(),
104
-            this.gridManager.getFinalGrid(this.currentObject));
104
+            this.gridManager.getCurrentGrid());
105 105
         if (this.scoreManager.canLevelUp())
106 106
             this.setNewGameTick(this.scoreManager.getLevel());
107 107
     }
@@ -139,9 +139,9 @@ export default class GameLogic {
139 139
         if (moved) {
140 140
             if (y === 1) {
141 141
                 this.scoreManager.incrementScore();
142
-                callback(this.gridManager.getFinalGrid(this.currentObject), this.scoreManager.getScore());
142
+                callback(this.gridManager.getCurrentGrid(), this.scoreManager.getScore());
143 143
             } else
144
-                callback(this.gridManager.getFinalGrid(this.currentObject));
144
+                callback(this.gridManager.getCurrentGrid());
145 145
         }
146 146
         this.pressInInterval = setTimeout(() => this.movePressedRepeat(false, callback, x, y), isInitial ? this.autoRepeatActivationDelay : this.autoRepeatDelay);
147 147
     }
@@ -156,7 +156,7 @@ export default class GameLogic {
156 156
             return;
157 157
 
158 158
         if (this.currentObject.tryRotate(this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
159
-            callback(this.gridManager.getFinalGrid(this.currentObject));
159
+            callback(this.gridManager.getCurrentGrid());
160 160
     }
161 161
 
162 162
     getNextPiecesPreviews() {
@@ -223,7 +223,7 @@ export default class GameLogic {
223 223
         tickCallback(
224 224
             this.scoreManager.getScore(),
225 225
             this.scoreManager.getLevel(),
226
-            this.gridManager.getFinalGrid(this.currentObject));
226
+            this.gridManager.getCurrentGrid());
227 227
         clockCallback(this.gameTime);
228 228
         this.onTick = this.onTick.bind(this, tickCallback);
229 229
         this.onClock = this.onClock.bind(this, clockCallback);

+ 3
- 12
screens/Tetris/GridManager.js View File

@@ -3,9 +3,11 @@
3 3
 import Piece from "./Piece";
4 4
 import ScoreManager from "./ScoreManager";
5 5
 
6
+export type grid = Array<Array<{color: string, isEmpty: boolean}>>;
7
+
6 8
 export default class GridManager {
7 9
 
8
-    #currentGrid: Array<Array<Object>>;
10
+    #currentGrid: grid;
9 11
     #colors: Object;
10 12
 
11 13
     constructor(width: number, height: number, colors: Object) {
@@ -36,16 +38,6 @@ export default class GridManager {
36 38
         return grid;
37 39
     }
38 40
 
39
-    getGridCopy() {
40
-        return JSON.parse(JSON.stringify(this.#currentGrid));
41
-    }
42
-
43
-    getFinalGrid(currentObject: Piece) {
44
-        let finalGrid = this.getGridCopy();
45
-        currentObject.toGrid(finalGrid, false);
46
-        return finalGrid;
47
-    }
48
-
49 41
     clearLines(lines: Array<number>, scoreManager: ScoreManager) {
50 42
         lines.sort();
51 43
         for (let i = 0; i < lines.length; i++) {
@@ -72,7 +64,6 @@ export default class GridManager {
72 64
     }
73 65
 
74 66
     freezeTetromino(currentObject: Piece, scoreManager: ScoreManager) {
75
-        currentObject.toGrid(this.#currentGrid, false);
76 67
         this.clearLines(this.getLinesToClear(currentObject.getCoordinates()), scoreManager);
77 68
     }
78 69
 }

+ 24
- 8
screens/Tetris/Piece.js View File

@@ -17,17 +17,28 @@ export default class Piece {
17 17
         ShapeT,
18 18
         ShapeZ,
19 19
     ];
20
-
21 20
     #currentShape: Object;
21
+    #colors: Object;
22 22
 
23 23
     constructor(colors: Object) {
24 24
         this.#currentShape = this.getRandomShape(colors);
25
+        this.#colors = colors;
25 26
     }
26 27
 
27 28
     getRandomShape(colors: Object) {
28 29
         return new this.#shapes[Math.floor(Math.random() * 7)](colors);
29 30
     }
30 31
 
32
+    removeFromGrid(grid) {
33
+        const coord = this.#currentShape.getCellsCoordinates(true);
34
+        for (let i = 0; i < coord.length; i++) {
35
+            grid[coord[i].y][coord[i].x] = {
36
+                color: this.#colors.tetrisBackground,
37
+                isEmpty: true,
38
+            };
39
+        }
40
+    }
41
+
31 42
     toGrid(grid: Array<Array<Object>>, isPreview: boolean) {
32 43
         const coord = this.#currentShape.getCellsCoordinates(!isPreview);
33 44
         for (let i = 0; i < coord.length; i++) {
@@ -61,25 +72,30 @@ export default class Piece {
61 72
         if (y < -1) y = -1;
62 73
         if (x !== 0 && y !== 0) y = 0; // Prevent diagonal movement
63 74
 
75
+        this.removeFromGrid(grid);
64 76
         this.#currentShape.move(x, y);
65 77
         let isValid = this.isPositionValid(grid, width, height);
78
+        let shouldFreeze = false;
66 79
 
67
-        if (!isValid && x !== 0)
68
-            this.#currentShape.move(-x, 0);
69
-        else if (!isValid && y !== 0) {
70
-            this.#currentShape.move(0, -y);
80
+        if (!isValid)
81
+            this.#currentShape.move(-x, -y);
82
+
83
+        shouldFreeze = !isValid && y !== 0;
84
+        this.toGrid(grid, false);
85
+        if (shouldFreeze)
71 86
             freezeCallback();
72
-        } else
73
-            return true;
74
-        return false;
87
+        return isValid;
75 88
     }
76 89
 
77 90
     tryRotate(grid, width, height) {
91
+        this.removeFromGrid(grid);
78 92
         this.#currentShape.rotate(true);
79 93
         if (!this.isPositionValid(grid, width, height)) {
80 94
             this.#currentShape.rotate(false);
95
+            this.toGrid(grid, false);
81 96
             return false;
82 97
         }
98
+        this.toGrid(grid, false);
83 99
         return true;
84 100
     }
85 101
 

+ 34
- 0
screens/Tetris/__tests__/GridManager.test.js View File

@@ -0,0 +1,34 @@
1
+import React from 'react';
2
+import GridManager from "../GridManager";
3
+
4
+let colors = {
5
+    tetrisBackground: "#000002"
6
+};
7
+
8
+test('getEmptyLine', () => {
9
+    let g = new GridManager(2, 2, colors);
10
+    expect(g.getEmptyLine(2)).toStrictEqual([
11
+        {color: colors.tetrisBackground, isEmpty: true},
12
+        {color: colors.tetrisBackground, isEmpty: true},
13
+    ]);
14
+
15
+    expect(g.getEmptyLine(-1)).toStrictEqual([]);
16
+});
17
+
18
+test('getEmptyGrid', () => {
19
+    let g = new GridManager(2, 2, colors);
20
+    expect(g.getEmptyGrid(2, 2)).toStrictEqual([
21
+        [
22
+            {color: colors.tetrisBackground, isEmpty: true},
23
+            {color: colors.tetrisBackground, isEmpty: true},
24
+        ],
25
+        [
26
+            {color: colors.tetrisBackground, isEmpty: true},
27
+            {color: colors.tetrisBackground, isEmpty: true},
28
+        ],
29
+    ]);
30
+
31
+    expect(g.getEmptyGrid(-1, 2)).toStrictEqual([]);
32
+    expect(g.getEmptyGrid(2, -1)).toStrictEqual([[], []]);
33
+});
34
+

+ 50
- 4
screens/Tetris/__tests__/Piece.test.js View File

@@ -53,8 +53,12 @@ test('tryMove', () => {
53 53
     let p = new Piece(colors);
54 54
     const callbackMock = jest.fn();
55 55
     let isValid = true;
56
-    let spy = jest.spyOn(Piece.prototype, 'isPositionValid')
56
+    let spy1 = jest.spyOn(Piece.prototype, 'isPositionValid')
57 57
         .mockImplementation(() => {return isValid;});
58
+    let spy2 = jest.spyOn(Piece.prototype, 'removeFromGrid')
59
+        .mockImplementation(() => {});
60
+    let spy3 = jest.spyOn(Piece.prototype, 'toGrid')
61
+        .mockImplementation(() => {});
58 62
 
59 63
     expect(p.tryMove(-1, 0, null, null, null, callbackMock)).toBeTrue();
60 64
     isValid = false;
@@ -67,20 +71,34 @@ test('tryMove', () => {
67 71
     expect(p.tryMove(0, 1, null, null, null, callbackMock)).toBeFalse();
68 72
     expect(callbackMock).toBeCalledTimes(1);
69 73
 
70
-    spy.mockRestore();
74
+    expect(spy2).toBeCalledTimes(4);
75
+    expect(spy3).toBeCalledTimes(4);
76
+
77
+    spy1.mockRestore();
78
+    spy2.mockRestore();
79
+    spy3.mockRestore();
71 80
 });
72 81
 
73 82
 test('tryRotate', () => {
74 83
     let p = new Piece(colors);
75 84
     let isValid = true;
76
-    let spy = jest.spyOn(Piece.prototype, 'isPositionValid')
85
+    let spy1 = jest.spyOn(Piece.prototype, 'isPositionValid')
77 86
         .mockImplementation(() => {return isValid;});
87
+    let spy2 = jest.spyOn(Piece.prototype, 'removeFromGrid')
88
+        .mockImplementation(() => {});
89
+    let spy3 = jest.spyOn(Piece.prototype, 'toGrid')
90
+        .mockImplementation(() => {});
78 91
 
79 92
     expect(p.tryRotate( null, null, null)).toBeTrue();
80 93
     isValid = false;
81 94
     expect(p.tryRotate( null, null, null)).toBeFalse();
82 95
 
83
-    spy.mockRestore();
96
+    expect(spy2).toBeCalledTimes(2);
97
+    expect(spy3).toBeCalledTimes(2);
98
+
99
+    spy1.mockRestore();
100
+    spy2.mockRestore();
101
+    spy3.mockRestore();
84 102
 });
85 103
 
86 104
 
@@ -107,3 +125,31 @@ test('toGrid', () => {
107 125
     spy1.mockRestore();
108 126
     spy2.mockRestore();
109 127
 });
128
+
129
+test('removeFromGrid', () => {
130
+    let gridOld = [
131
+        [
132
+            {color: colors.tetrisI, isEmpty: false},
133
+            {color: colors.tetrisI, isEmpty: false},
134
+            {color: colors.tetrisBackground, isEmpty: true},
135
+        ],
136
+    ];
137
+    let gridNew = [
138
+        [
139
+            {color: colors.tetrisBackground, isEmpty: true},
140
+            {color: colors.tetrisBackground, isEmpty: true},
141
+            {color: colors.tetrisBackground, isEmpty: true},
142
+        ],
143
+    ];
144
+    let oldCoord = [{x: 0, y: 0}, {x: 1, y: 0}];
145
+    let spy1 = jest.spyOn(ShapeI.prototype, 'getCellsCoordinates')
146
+        .mockImplementation(() => {return oldCoord;});
147
+    let spy2 = jest.spyOn(ShapeI.prototype, 'getColor')
148
+        .mockImplementation(() => {return colors.tetrisI;});
149
+    let p = new Piece(colors);
150
+    p.removeFromGrid(gridOld);
151
+    expect(gridOld).toStrictEqual(gridNew);
152
+
153
+    spy1.mockRestore();
154
+    spy2.mockRestore();
155
+});

Loading…
Cancel
Save