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(
|
callback(
|
||||||
this.scoreManager.getScore(),
|
this.scoreManager.getScore(),
|
||||||
this.scoreManager.getLevel(),
|
this.scoreManager.getLevel(),
|
||||||
this.gridManager.getFinalGrid(this.currentObject));
|
this.gridManager.getCurrentGrid());
|
||||||
if (this.scoreManager.canLevelUp())
|
if (this.scoreManager.canLevelUp())
|
||||||
this.setNewGameTick(this.scoreManager.getLevel());
|
this.setNewGameTick(this.scoreManager.getLevel());
|
||||||
}
|
}
|
||||||
|
@ -139,9 +139,9 @@ export default class GameLogic {
|
||||||
if (moved) {
|
if (moved) {
|
||||||
if (y === 1) {
|
if (y === 1) {
|
||||||
this.scoreManager.incrementScore();
|
this.scoreManager.incrementScore();
|
||||||
callback(this.gridManager.getFinalGrid(this.currentObject), this.scoreManager.getScore());
|
callback(this.gridManager.getCurrentGrid(), this.scoreManager.getScore());
|
||||||
} else
|
} 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);
|
this.pressInInterval = setTimeout(() => this.movePressedRepeat(false, callback, x, y), isInitial ? this.autoRepeatActivationDelay : this.autoRepeatDelay);
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ export default class GameLogic {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.currentObject.tryRotate(this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
|
if (this.currentObject.tryRotate(this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
|
||||||
callback(this.gridManager.getFinalGrid(this.currentObject));
|
callback(this.gridManager.getCurrentGrid());
|
||||||
}
|
}
|
||||||
|
|
||||||
getNextPiecesPreviews() {
|
getNextPiecesPreviews() {
|
||||||
|
@ -223,7 +223,7 @@ export default class GameLogic {
|
||||||
tickCallback(
|
tickCallback(
|
||||||
this.scoreManager.getScore(),
|
this.scoreManager.getScore(),
|
||||||
this.scoreManager.getLevel(),
|
this.scoreManager.getLevel(),
|
||||||
this.gridManager.getFinalGrid(this.currentObject));
|
this.gridManager.getCurrentGrid());
|
||||||
clockCallback(this.gameTime);
|
clockCallback(this.gameTime);
|
||||||
this.onTick = this.onTick.bind(this, tickCallback);
|
this.onTick = this.onTick.bind(this, tickCallback);
|
||||||
this.onClock = this.onClock.bind(this, clockCallback);
|
this.onClock = this.onClock.bind(this, clockCallback);
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
import Piece from "./Piece";
|
import Piece from "./Piece";
|
||||||
import ScoreManager from "./ScoreManager";
|
import ScoreManager from "./ScoreManager";
|
||||||
|
|
||||||
|
export type grid = Array<Array<{color: string, isEmpty: boolean}>>;
|
||||||
|
|
||||||
export default class GridManager {
|
export default class GridManager {
|
||||||
|
|
||||||
#currentGrid: Array<Array<Object>>;
|
#currentGrid: grid;
|
||||||
#colors: Object;
|
#colors: Object;
|
||||||
|
|
||||||
constructor(width: number, height: number, colors: Object) {
|
constructor(width: number, height: number, colors: Object) {
|
||||||
|
@ -36,16 +38,6 @@ export default class GridManager {
|
||||||
return grid;
|
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) {
|
clearLines(lines: Array<number>, scoreManager: ScoreManager) {
|
||||||
lines.sort();
|
lines.sort();
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
@ -72,7 +64,6 @@ export default class GridManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
freezeTetromino(currentObject: Piece, scoreManager: ScoreManager) {
|
freezeTetromino(currentObject: Piece, scoreManager: ScoreManager) {
|
||||||
currentObject.toGrid(this.#currentGrid, false);
|
|
||||||
this.clearLines(this.getLinesToClear(currentObject.getCoordinates()), scoreManager);
|
this.clearLines(this.getLinesToClear(currentObject.getCoordinates()), scoreManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,28 @@ export default class Piece {
|
||||||
ShapeT,
|
ShapeT,
|
||||||
ShapeZ,
|
ShapeZ,
|
||||||
];
|
];
|
||||||
|
|
||||||
#currentShape: Object;
|
#currentShape: Object;
|
||||||
|
#colors: Object;
|
||||||
|
|
||||||
constructor(colors: Object) {
|
constructor(colors: Object) {
|
||||||
this.#currentShape = this.getRandomShape(colors);
|
this.#currentShape = this.getRandomShape(colors);
|
||||||
|
this.#colors = colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRandomShape(colors: Object) {
|
getRandomShape(colors: Object) {
|
||||||
return new this.#shapes[Math.floor(Math.random() * 7)](colors);
|
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) {
|
toGrid(grid: Array<Array<Object>>, isPreview: boolean) {
|
||||||
const coord = this.#currentShape.getCellsCoordinates(!isPreview);
|
const coord = this.#currentShape.getCellsCoordinates(!isPreview);
|
||||||
for (let i = 0; i < coord.length; i++) {
|
for (let i = 0; i < coord.length; i++) {
|
||||||
|
@ -61,25 +72,30 @@ export default class Piece {
|
||||||
if (y < -1) y = -1;
|
if (y < -1) y = -1;
|
||||||
if (x !== 0 && y !== 0) y = 0; // Prevent diagonal movement
|
if (x !== 0 && y !== 0) y = 0; // Prevent diagonal movement
|
||||||
|
|
||||||
|
this.removeFromGrid(grid);
|
||||||
this.#currentShape.move(x, y);
|
this.#currentShape.move(x, y);
|
||||||
let isValid = this.isPositionValid(grid, width, height);
|
let isValid = this.isPositionValid(grid, width, height);
|
||||||
|
let shouldFreeze = false;
|
||||||
|
|
||||||
if (!isValid && x !== 0)
|
if (!isValid)
|
||||||
this.#currentShape.move(-x, 0);
|
this.#currentShape.move(-x, -y);
|
||||||
else if (!isValid && y !== 0) {
|
|
||||||
this.#currentShape.move(0, -y);
|
shouldFreeze = !isValid && y !== 0;
|
||||||
|
this.toGrid(grid, false);
|
||||||
|
if (shouldFreeze)
|
||||||
freezeCallback();
|
freezeCallback();
|
||||||
} else
|
return isValid;
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tryRotate(grid, width, height) {
|
tryRotate(grid, width, height) {
|
||||||
|
this.removeFromGrid(grid);
|
||||||
this.#currentShape.rotate(true);
|
this.#currentShape.rotate(true);
|
||||||
if (!this.isPositionValid(grid, width, height)) {
|
if (!this.isPositionValid(grid, width, height)) {
|
||||||
this.#currentShape.rotate(false);
|
this.#currentShape.rotate(false);
|
||||||
|
this.toGrid(grid, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
this.toGrid(grid, false);
|
||||||
return true;
|
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);
|
let p = new Piece(colors);
|
||||||
const callbackMock = jest.fn();
|
const callbackMock = jest.fn();
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
let spy = jest.spyOn(Piece.prototype, 'isPositionValid')
|
let spy1 = jest.spyOn(Piece.prototype, 'isPositionValid')
|
||||||
.mockImplementation(() => {return isValid;});
|
.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();
|
expect(p.tryMove(-1, 0, null, null, null, callbackMock)).toBeTrue();
|
||||||
isValid = false;
|
isValid = false;
|
||||||
|
@ -67,20 +71,34 @@ test('tryMove', () => {
|
||||||
expect(p.tryMove(0, 1, null, null, null, callbackMock)).toBeFalse();
|
expect(p.tryMove(0, 1, null, null, null, callbackMock)).toBeFalse();
|
||||||
expect(callbackMock).toBeCalledTimes(1);
|
expect(callbackMock).toBeCalledTimes(1);
|
||||||
|
|
||||||
spy.mockRestore();
|
expect(spy2).toBeCalledTimes(4);
|
||||||
|
expect(spy3).toBeCalledTimes(4);
|
||||||
|
|
||||||
|
spy1.mockRestore();
|
||||||
|
spy2.mockRestore();
|
||||||
|
spy3.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('tryRotate', () => {
|
test('tryRotate', () => {
|
||||||
let p = new Piece(colors);
|
let p = new Piece(colors);
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
let spy = jest.spyOn(Piece.prototype, 'isPositionValid')
|
let spy1 = jest.spyOn(Piece.prototype, 'isPositionValid')
|
||||||
.mockImplementation(() => {return isValid;});
|
.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();
|
expect(p.tryRotate( null, null, null)).toBeTrue();
|
||||||
isValid = false;
|
isValid = false;
|
||||||
expect(p.tryRotate( null, null, null)).toBeFalse();
|
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();
|
spy1.mockRestore();
|
||||||
spy2.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