forked from vergnet/application-amicale
Added more tests and improved performance by not making deep copies of the grid
This commit is contained in:
parent
e6fcbdb165
commit
3d45bc62b8
5 changed files with 116 additions and 29 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
34
screens/Tetris/__tests__/GridManager.test.js
Normal file
34
screens/Tetris/__tests__/GridManager.test.js
Normal 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([[], []]);
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue