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

This commit is contained in:
Arnaud Vergnet 2020-03-24 10:43:05 +01:00
parent e6fcbdb165
commit 3d45bc62b8
5 changed files with 116 additions and 29 deletions

View file

@ -101,7 +101,7 @@ export default class GameLogic {
callback(
this.scoreManager.getScore(),
this.scoreManager.getLevel(),
this.gridManager.getFinalGrid(this.currentObject));
this.gridManager.getCurrentGrid());
if (this.scoreManager.canLevelUp())
this.setNewGameTick(this.scoreManager.getLevel());
}
@ -139,9 +139,9 @@ export default class GameLogic {
if (moved) {
if (y === 1) {
this.scoreManager.incrementScore();
callback(this.gridManager.getFinalGrid(this.currentObject), this.scoreManager.getScore());
callback(this.gridManager.getCurrentGrid(), this.scoreManager.getScore());
} else
callback(this.gridManager.getFinalGrid(this.currentObject));
callback(this.gridManager.getCurrentGrid());
}
this.pressInInterval = setTimeout(() => this.movePressedRepeat(false, callback, x, y), isInitial ? this.autoRepeatActivationDelay : this.autoRepeatDelay);
}
@ -156,7 +156,7 @@ export default class GameLogic {
return;
if (this.currentObject.tryRotate(this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
callback(this.gridManager.getFinalGrid(this.currentObject));
callback(this.gridManager.getCurrentGrid());
}
getNextPiecesPreviews() {
@ -223,7 +223,7 @@ export default class GameLogic {
tickCallback(
this.scoreManager.getScore(),
this.scoreManager.getLevel(),
this.gridManager.getFinalGrid(this.currentObject));
this.gridManager.getCurrentGrid());
clockCallback(this.gameTime);
this.onTick = this.onTick.bind(this, tickCallback);
this.onClock = this.onClock.bind(this, clockCallback);

View file

@ -3,9 +3,11 @@
import Piece from "./Piece";
import ScoreManager from "./ScoreManager";
export type grid = Array<Array<{color: string, isEmpty: boolean}>>;
export default class GridManager {
#currentGrid: Array<Array<Object>>;
#currentGrid: grid;
#colors: Object;
constructor(width: number, height: number, colors: Object) {
@ -36,16 +38,6 @@ export default class GridManager {
return grid;
}
getGridCopy() {
return JSON.parse(JSON.stringify(this.#currentGrid));
}
getFinalGrid(currentObject: Piece) {
let finalGrid = this.getGridCopy();
currentObject.toGrid(finalGrid, false);
return finalGrid;
}
clearLines(lines: Array<number>, scoreManager: ScoreManager) {
lines.sort();
for (let i = 0; i < lines.length; i++) {
@ -72,7 +64,6 @@ export default class GridManager {
}
freezeTetromino(currentObject: Piece, scoreManager: ScoreManager) {
currentObject.toGrid(this.#currentGrid, false);
this.clearLines(this.getLinesToClear(currentObject.getCoordinates()), scoreManager);
}
}

View file

@ -17,17 +17,28 @@ export default class Piece {
ShapeT,
ShapeZ,
];
#currentShape: Object;
#colors: Object;
constructor(colors: Object) {
this.#currentShape = this.getRandomShape(colors);
this.#colors = colors;
}
getRandomShape(colors: Object) {
return new this.#shapes[Math.floor(Math.random() * 7)](colors);
}
removeFromGrid(grid) {
const coord = this.#currentShape.getCellsCoordinates(true);
for (let i = 0; i < coord.length; i++) {
grid[coord[i].y][coord[i].x] = {
color: this.#colors.tetrisBackground,
isEmpty: true,
};
}
}
toGrid(grid: Array<Array<Object>>, isPreview: boolean) {
const coord = this.#currentShape.getCellsCoordinates(!isPreview);
for (let i = 0; i < coord.length; i++) {
@ -61,25 +72,30 @@ export default class Piece {
if (y < -1) y = -1;
if (x !== 0 && y !== 0) y = 0; // Prevent diagonal movement
this.removeFromGrid(grid);
this.#currentShape.move(x, y);
let isValid = this.isPositionValid(grid, width, height);
let shouldFreeze = false;
if (!isValid && x !== 0)
this.#currentShape.move(-x, 0);
else if (!isValid && y !== 0) {
this.#currentShape.move(0, -y);
if (!isValid)
this.#currentShape.move(-x, -y);
shouldFreeze = !isValid && y !== 0;
this.toGrid(grid, false);
if (shouldFreeze)
freezeCallback();
} else
return true;
return false;
return isValid;
}
tryRotate(grid, width, height) {
this.removeFromGrid(grid);
this.#currentShape.rotate(true);
if (!this.isPositionValid(grid, width, height)) {
this.#currentShape.rotate(false);
this.toGrid(grid, false);
return false;
}
this.toGrid(grid, false);
return true;
}

View file

@ -0,0 +1,34 @@
import React from 'react';
import GridManager from "../GridManager";
let colors = {
tetrisBackground: "#000002"
};
test('getEmptyLine', () => {
let g = new GridManager(2, 2, colors);
expect(g.getEmptyLine(2)).toStrictEqual([
{color: colors.tetrisBackground, isEmpty: true},
{color: colors.tetrisBackground, isEmpty: true},
]);
expect(g.getEmptyLine(-1)).toStrictEqual([]);
});
test('getEmptyGrid', () => {
let g = new GridManager(2, 2, colors);
expect(g.getEmptyGrid(2, 2)).toStrictEqual([
[
{color: colors.tetrisBackground, isEmpty: true},
{color: colors.tetrisBackground, isEmpty: true},
],
[
{color: colors.tetrisBackground, isEmpty: true},
{color: colors.tetrisBackground, isEmpty: true},
],
]);
expect(g.getEmptyGrid(-1, 2)).toStrictEqual([]);
expect(g.getEmptyGrid(2, -1)).toStrictEqual([[], []]);
});

View file

@ -53,8 +53,12 @@ test('tryMove', () => {
let p = new Piece(colors);
const callbackMock = jest.fn();
let isValid = true;
let spy = jest.spyOn(Piece.prototype, 'isPositionValid')
let spy1 = jest.spyOn(Piece.prototype, 'isPositionValid')
.mockImplementation(() => {return isValid;});
let spy2 = jest.spyOn(Piece.prototype, 'removeFromGrid')
.mockImplementation(() => {});
let spy3 = jest.spyOn(Piece.prototype, 'toGrid')
.mockImplementation(() => {});
expect(p.tryMove(-1, 0, null, null, null, callbackMock)).toBeTrue();
isValid = false;
@ -67,20 +71,34 @@ test('tryMove', () => {
expect(p.tryMove(0, 1, null, null, null, callbackMock)).toBeFalse();
expect(callbackMock).toBeCalledTimes(1);
spy.mockRestore();
expect(spy2).toBeCalledTimes(4);
expect(spy3).toBeCalledTimes(4);
spy1.mockRestore();
spy2.mockRestore();
spy3.mockRestore();
});
test('tryRotate', () => {
let p = new Piece(colors);
let isValid = true;
let spy = jest.spyOn(Piece.prototype, 'isPositionValid')
let spy1 = jest.spyOn(Piece.prototype, 'isPositionValid')
.mockImplementation(() => {return isValid;});
let spy2 = jest.spyOn(Piece.prototype, 'removeFromGrid')
.mockImplementation(() => {});
let spy3 = jest.spyOn(Piece.prototype, 'toGrid')
.mockImplementation(() => {});
expect(p.tryRotate( null, null, null)).toBeTrue();
isValid = false;
expect(p.tryRotate( null, null, null)).toBeFalse();
spy.mockRestore();
expect(spy2).toBeCalledTimes(2);
expect(spy3).toBeCalledTimes(2);
spy1.mockRestore();
spy2.mockRestore();
spy3.mockRestore();
});
@ -107,3 +125,31 @@ test('toGrid', () => {
spy1.mockRestore();
spy2.mockRestore();
});
test('removeFromGrid', () => {
let gridOld = [
[
{color: colors.tetrisI, isEmpty: false},
{color: colors.tetrisI, isEmpty: false},
{color: colors.tetrisBackground, isEmpty: true},
],
];
let gridNew = [
[
{color: colors.tetrisBackground, isEmpty: true},
{color: colors.tetrisBackground, isEmpty: true},
{color: colors.tetrisBackground, isEmpty: true},
],
];
let oldCoord = [{x: 0, y: 0}, {x: 1, y: 0}];
let spy1 = jest.spyOn(ShapeI.prototype, 'getCellsCoordinates')
.mockImplementation(() => {return oldCoord;});
let spy2 = jest.spyOn(ShapeI.prototype, 'getColor')
.mockImplementation(() => {return colors.tetrisI;});
let p = new Piece(colors);
p.removeFromGrid(gridOld);
expect(gridOld).toStrictEqual(gridNew);
spy1.mockRestore();
spy2.mockRestore();
});