forked from vergnet/application-amicale
Moved grid and score logic in separate files
This commit is contained in:
parent
fcec2a3c8a
commit
8dc620b987
4 changed files with 182 additions and 127 deletions
|
@ -1,6 +1,8 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import Piece from "./Piece";
|
import Piece from "./Piece";
|
||||||
|
import ScoreManager from "./ScoreManager";
|
||||||
|
import GridManager from "./GridManager";
|
||||||
|
|
||||||
export default class GameLogic {
|
export default class GameLogic {
|
||||||
|
|
||||||
|
@ -15,9 +17,8 @@ export default class GameLogic {
|
||||||
100,
|
100,
|
||||||
];
|
];
|
||||||
|
|
||||||
static scoreLinesModifier = [40, 100, 300, 1200];
|
scoreManager: ScoreManager;
|
||||||
|
gridManager: GridManager;
|
||||||
currentGrid: Array<Array<Object>>;
|
|
||||||
|
|
||||||
height: number;
|
height: number;
|
||||||
width: number;
|
width: number;
|
||||||
|
@ -25,8 +26,6 @@ export default class GameLogic {
|
||||||
gameRunning: boolean;
|
gameRunning: boolean;
|
||||||
gamePaused: boolean;
|
gamePaused: boolean;
|
||||||
gameTime: number;
|
gameTime: number;
|
||||||
score: number;
|
|
||||||
level: number;
|
|
||||||
|
|
||||||
currentObject: Piece;
|
currentObject: Piece;
|
||||||
|
|
||||||
|
@ -48,8 +47,6 @@ export default class GameLogic {
|
||||||
|
|
||||||
colors: Object;
|
colors: Object;
|
||||||
|
|
||||||
levelProgression: number;
|
|
||||||
|
|
||||||
constructor(height: number, width: number, colors: Object) {
|
constructor(height: number, width: number, colors: Object) {
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
|
@ -60,16 +57,8 @@ export default class GameLogic {
|
||||||
this.autoRepeatDelay = 50;
|
this.autoRepeatDelay = 50;
|
||||||
this.nextPieces = [];
|
this.nextPieces = [];
|
||||||
this.nextPiecesCount = 3;
|
this.nextPiecesCount = 3;
|
||||||
}
|
this.scoreManager = new ScoreManager();
|
||||||
|
this.gridManager = new GridManager(this.getWidth(), this.getHeight(), this.colors);
|
||||||
getNextPiecesPreviews() {
|
|
||||||
let finalArray = [];
|
|
||||||
for (let i = 0; i < this.nextPieces.length; i++) {
|
|
||||||
finalArray.push(this.getEmptyGrid(4, 4));
|
|
||||||
this.nextPieces[i].toGrid(finalArray[i], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeight(): number {
|
getHeight(): number {
|
||||||
|
@ -80,6 +69,10 @@ export default class GameLogic {
|
||||||
return this.width;
|
return this.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCurrentGrid() {
|
||||||
|
return this.gridManager.getCurrentGrid();
|
||||||
|
}
|
||||||
|
|
||||||
isGameRunning(): boolean {
|
isGameRunning(): boolean {
|
||||||
return this.gameRunning;
|
return this.gameRunning;
|
||||||
}
|
}
|
||||||
|
@ -88,90 +81,9 @@ export default class GameLogic {
|
||||||
return this.gamePaused;
|
return this.gamePaused;
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmptyLine(width: number) {
|
onFreeze() {
|
||||||
let line = [];
|
this.gridManager.freezeTetromino(this.currentObject, this.scoreManager);
|
||||||
for (let col = 0; col < width; col++) {
|
this.createTetromino();
|
||||||
line.push({
|
|
||||||
color: this.colors.tetrisBackground,
|
|
||||||
isEmpty: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
getEmptyGrid(height: number, width: number) {
|
|
||||||
let grid = [];
|
|
||||||
for (let row = 0; row < height; row++) {
|
|
||||||
grid.push(this.getEmptyLine(width));
|
|
||||||
}
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
getGridCopy() {
|
|
||||||
return JSON.parse(JSON.stringify(this.currentGrid));
|
|
||||||
}
|
|
||||||
|
|
||||||
getFinalGrid() {
|
|
||||||
let finalGrid = this.getGridCopy();
|
|
||||||
this.currentObject.toGrid(finalGrid, false);
|
|
||||||
return finalGrid;
|
|
||||||
}
|
|
||||||
|
|
||||||
getLinesRemovedPoints(numberRemoved: number) {
|
|
||||||
if (numberRemoved < 1 || numberRemoved > 4)
|
|
||||||
return 0;
|
|
||||||
return GameLogic.scoreLinesModifier[numberRemoved-1] * (this.level + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
canLevelUp() {
|
|
||||||
let canLevel = this.levelProgression > this.level * 5;
|
|
||||||
if (canLevel)
|
|
||||||
this.levelProgression -= this.level * 5;
|
|
||||||
return canLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
freezeTetromino() {
|
|
||||||
this.currentObject.toGrid(this.currentGrid, false);
|
|
||||||
this.clearLines(this.getLinesToClear(this.currentObject.getCoordinates()));
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLines(lines: Array<number>) {
|
|
||||||
lines.sort();
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
|
||||||
this.currentGrid.splice(lines[i], 1);
|
|
||||||
this.currentGrid.unshift(this.getEmptyLine(this.getWidth()));
|
|
||||||
}
|
|
||||||
switch (lines.length) {
|
|
||||||
case 1:
|
|
||||||
this.levelProgression += 1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
this.levelProgression += 3;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
this.levelProgression += 5;
|
|
||||||
break;
|
|
||||||
case 4: // Did a tetris !
|
|
||||||
this.levelProgression += 8;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.score += this.getLinesRemovedPoints(lines.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLinesToClear(coord: Object) {
|
|
||||||
let rows = [];
|
|
||||||
for (let i = 0; i < coord.length; i++) {
|
|
||||||
let isLineFull = true;
|
|
||||||
for (let col = 0; col < this.getWidth(); col++) {
|
|
||||||
if (this.currentGrid[coord[i].y][col].isEmpty) {
|
|
||||||
isLineFull = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isLineFull && rows.indexOf(coord[i].y) === -1)
|
|
||||||
rows.push(coord[i].y);
|
|
||||||
}
|
|
||||||
return rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setNewGameTick(level: number) {
|
setNewGameTick(level: number) {
|
||||||
|
@ -182,20 +94,16 @@ export default class GameLogic {
|
||||||
this.gameTickInterval = setInterval(this.onTick, this.gameTick);
|
this.gameTickInterval = setInterval(this.onTick, this.gameTick);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFreeze() {
|
|
||||||
this.freezeTetromino();
|
|
||||||
this.createTetromino();
|
|
||||||
}
|
|
||||||
|
|
||||||
onTick(callback: Function) {
|
onTick(callback: Function) {
|
||||||
this.currentObject.tryMove(0, 1,
|
this.currentObject.tryMove(0, 1,
|
||||||
this.currentGrid, this.getWidth(), this.getHeight(),
|
this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight(),
|
||||||
() => this.onFreeze());
|
() => this.onFreeze());
|
||||||
callback(this.score, this.level, this.getFinalGrid());
|
callback(
|
||||||
if (this.canLevelUp()) {
|
this.scoreManager.getScore(),
|
||||||
this.level++;
|
this.scoreManager.getLevel(),
|
||||||
this.setNewGameTick(this.level);
|
this.gridManager.getFinalGrid(this.currentObject));
|
||||||
}
|
if (this.scoreManager.canLevelUp())
|
||||||
|
this.setNewGameTick(this.scoreManager.getLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
onClock(callback: Function) {
|
onClock(callback: Function) {
|
||||||
|
@ -226,14 +134,14 @@ export default class GameLogic {
|
||||||
if (!this.canUseInput() || !this.isPressedIn)
|
if (!this.canUseInput() || !this.isPressedIn)
|
||||||
return;
|
return;
|
||||||
const moved = this.currentObject.tryMove(x, y,
|
const moved = this.currentObject.tryMove(x, y,
|
||||||
this.currentGrid, this.getWidth(), this.getHeight(),
|
this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight(),
|
||||||
() => this.onFreeze());
|
() => this.onFreeze());
|
||||||
if (moved) {
|
if (moved) {
|
||||||
if (y === 1) {
|
if (y === 1) {
|
||||||
this.score++;
|
this.scoreManager.incrementScore();
|
||||||
callback(this.getFinalGrid(), this.score);
|
callback(this.gridManager.getFinalGrid(this.currentObject), this.scoreManager.getScore());
|
||||||
} else
|
} else
|
||||||
callback(this.getFinalGrid());
|
callback(this.gridManager.getFinalGrid(this.currentObject));
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -247,8 +155,18 @@ export default class GameLogic {
|
||||||
if (!this.canUseInput())
|
if (!this.canUseInput())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.currentObject.tryRotate(this.currentGrid, this.getWidth(), this.getHeight()))
|
if (this.currentObject.tryRotate(this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
|
||||||
callback(this.getFinalGrid());
|
callback(this.gridManager.getFinalGrid(this.currentObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextPiecesPreviews() {
|
||||||
|
let finalArray = [];
|
||||||
|
for (let i = 0; i < this.nextPieces.length; i++) {
|
||||||
|
finalArray.push(this.gridManager.getEmptyGrid(4, 4));
|
||||||
|
this.nextPieces[i].toGrid(finalArray[i], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
recoverNextPiece() {
|
recoverNextPiece() {
|
||||||
|
@ -265,7 +183,7 @@ export default class GameLogic {
|
||||||
createTetromino() {
|
createTetromino() {
|
||||||
this.pressedOut();
|
this.pressedOut();
|
||||||
this.recoverNextPiece();
|
this.recoverNextPiece();
|
||||||
if (!this.currentObject.isPositionValid(this.currentGrid, this.getWidth(), this.getHeight()))
|
if (!this.currentObject.isPositionValid(this.gridManager.getCurrentGrid(), this.getWidth(), this.getHeight()))
|
||||||
this.endGame(false);
|
this.endGame(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +205,7 @@ export default class GameLogic {
|
||||||
this.gamePaused = false;
|
this.gamePaused = false;
|
||||||
clearInterval(this.gameTickInterval);
|
clearInterval(this.gameTickInterval);
|
||||||
clearInterval(this.gameTimeInterval);
|
clearInterval(this.gameTimeInterval);
|
||||||
this.endCallback(this.gameTime, this.score, isRestart);
|
this.endCallback(this.gameTime, this.scoreManager.getScore(), isRestart);
|
||||||
}
|
}
|
||||||
|
|
||||||
startGame(tickCallback: Function, clockCallback: Function, endCallback: Function) {
|
startGame(tickCallback: Function, clockCallback: Function, endCallback: Function) {
|
||||||
|
@ -296,15 +214,16 @@ export default class GameLogic {
|
||||||
this.gameRunning = true;
|
this.gameRunning = true;
|
||||||
this.gamePaused = false;
|
this.gamePaused = false;
|
||||||
this.gameTime = 0;
|
this.gameTime = 0;
|
||||||
this.score = 0;
|
this.scoreManager = new ScoreManager();
|
||||||
this.level = 0;
|
this.gameTick = GameLogic.levelTicks[this.scoreManager.getLevel()];
|
||||||
this.levelProgression = 0;
|
this.gridManager = new GridManager(this.getWidth(), this.getHeight(), this.colors);
|
||||||
this.gameTick = GameLogic.levelTicks[this.level];
|
|
||||||
this.currentGrid = this.getEmptyGrid(this.getHeight(), this.getWidth());
|
|
||||||
this.nextPieces = [];
|
this.nextPieces = [];
|
||||||
this.generateNextPieces();
|
this.generateNextPieces();
|
||||||
this.createTetromino();
|
this.createTetromino();
|
||||||
tickCallback(this.score, this.level, this.getFinalGrid());
|
tickCallback(
|
||||||
|
this.scoreManager.getScore(),
|
||||||
|
this.scoreManager.getLevel(),
|
||||||
|
this.gridManager.getFinalGrid(this.currentObject));
|
||||||
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);
|
||||||
|
|
78
screens/Tetris/GridManager.js
Normal file
78
screens/Tetris/GridManager.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import Piece from "./Piece";
|
||||||
|
import ScoreManager from "./ScoreManager";
|
||||||
|
|
||||||
|
export default class GridManager {
|
||||||
|
|
||||||
|
#currentGrid: Array<Array<Object>>;
|
||||||
|
#colors: Object;
|
||||||
|
|
||||||
|
constructor(width: number, height: number, colors: Object) {
|
||||||
|
this.#colors = colors;
|
||||||
|
this.#currentGrid = this.getEmptyGrid(height, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentGrid() {
|
||||||
|
return this.#currentGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmptyLine(width: number) {
|
||||||
|
let line = [];
|
||||||
|
for (let col = 0; col < width; col++) {
|
||||||
|
line.push({
|
||||||
|
color: this.#colors.tetrisBackground,
|
||||||
|
isEmpty: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmptyGrid(height: number, width: number) {
|
||||||
|
let grid = [];
|
||||||
|
for (let row = 0; row < height; row++) {
|
||||||
|
grid.push(this.getEmptyLine(width));
|
||||||
|
}
|
||||||
|
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++) {
|
||||||
|
this.#currentGrid.splice(lines[i], 1);
|
||||||
|
this.#currentGrid.unshift(this.getEmptyLine(this.#currentGrid[0].length));
|
||||||
|
}
|
||||||
|
scoreManager.addLinesRemovedPoints(lines.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLinesToClear(coord: Object) {
|
||||||
|
let rows = [];
|
||||||
|
for (let i = 0; i < coord.length; i++) {
|
||||||
|
let isLineFull = true;
|
||||||
|
for (let col = 0; col < this.#currentGrid[coord[i].y].length; col++) {
|
||||||
|
if (this.#currentGrid[coord[i].y][col].isEmpty) {
|
||||||
|
isLineFull = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isLineFull && rows.indexOf(coord[i].y) === -1)
|
||||||
|
rows.push(coord[i].y);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
freezeTetromino(currentObject: Piece, scoreManager: ScoreManager) {
|
||||||
|
currentObject.toGrid(this.#currentGrid, false);
|
||||||
|
this.clearLines(this.getLinesToClear(currentObject.getCoordinates()), scoreManager);
|
||||||
|
}
|
||||||
|
}
|
58
screens/Tetris/ScoreManager.js
Normal file
58
screens/Tetris/ScoreManager.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
export default class ScoreManager {
|
||||||
|
|
||||||
|
#scoreLinesModifier = [40, 100, 300, 1200];
|
||||||
|
|
||||||
|
#score: number;
|
||||||
|
#level: number;
|
||||||
|
#levelProgression: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#score = 0;
|
||||||
|
this.#level = 0;
|
||||||
|
this.#levelProgression = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getScore(): number {
|
||||||
|
return this.#score;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLevel(): number {
|
||||||
|
return this.#level;
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementScore() {
|
||||||
|
this.#score++;
|
||||||
|
}
|
||||||
|
|
||||||
|
addLinesRemovedPoints(numberRemoved: number) {
|
||||||
|
if (numberRemoved < 1 || numberRemoved > 4)
|
||||||
|
return 0;
|
||||||
|
this.#score += this.#scoreLinesModifier[numberRemoved-1] * (this.#level + 1);
|
||||||
|
switch (numberRemoved) {
|
||||||
|
case 1:
|
||||||
|
this.#levelProgression += 1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this.#levelProgression += 3;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this.#levelProgression += 5;
|
||||||
|
break;
|
||||||
|
case 4: // Did a tetris !
|
||||||
|
this.#levelProgression += 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canLevelUp() {
|
||||||
|
let canLevel = this.#levelProgression > this.#level * 5;
|
||||||
|
if (canLevel){
|
||||||
|
this.#levelProgression -= this.#level * 5;
|
||||||
|
this.#level++;
|
||||||
|
}
|
||||||
|
return canLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ class TetrisScreen extends React.Component<Props, State> {
|
||||||
this.colors = props.theme.colors;
|
this.colors = props.theme.colors;
|
||||||
this.logic = new GameLogic(20, 10, this.colors);
|
this.logic = new GameLogic(20, 10, this.colors);
|
||||||
this.state = {
|
this.state = {
|
||||||
grid: this.logic.getEmptyGrid(this.logic.getHeight(), this.logic.getWidth()),
|
grid: this.logic.getCurrentGrid(),
|
||||||
gameRunning: false,
|
gameRunning: false,
|
||||||
gameTime: 0,
|
gameTime: 0,
|
||||||
gameScore: 0,
|
gameScore: 0,
|
||||||
|
|
Loading…
Reference in a new issue