You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
7.0 KiB
252 lines
7.0 KiB
export default class Match3 {
|
|
// constructor, simply turns obj information into class properties
|
|
constructor(obj) {
|
|
this.rows = obj.rows;
|
|
this.columns = obj.columns;
|
|
this.items = obj.items;
|
|
}
|
|
|
|
// generates the game field
|
|
generateField() {
|
|
this.gameArray = [];
|
|
this.selectedItem = false;
|
|
for (let i = 0; i < this.rows; i++) {
|
|
this.gameArray[i] = [];
|
|
for (let j = 0; j < this.columns; j++) {
|
|
do {
|
|
let randomValue = Math.floor(Math.random() * this.items);
|
|
this.gameArray[i][j] = {
|
|
value: randomValue,
|
|
isEmpty: false,
|
|
row: i,
|
|
column: j,
|
|
};
|
|
} while (this.isPartOfMatch(i, j));
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns true if there is a match in the board
|
|
matchInBoard() {
|
|
for (let i = 0; i < this.rows; i++) {
|
|
for (let j = 0; j < this.columns; j++) {
|
|
if (this.isPartOfMatch(i, j)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// returns true if the item at (row, column) is part of a match
|
|
isPartOfMatch(row, column) {
|
|
return (
|
|
this.isPartOfHorizontalMatch(row, column) ||
|
|
this.isPartOfVerticalMatch(row, column)
|
|
);
|
|
}
|
|
|
|
// returns true if the item at (row, column) is part of an horizontal match
|
|
isPartOfHorizontalMatch(row, column) {
|
|
return (
|
|
(this.valueAt(row, column) === this.valueAt(row, column - 1) &&
|
|
this.valueAt(row, column) === this.valueAt(row, column - 2)) ||
|
|
(this.valueAt(row, column) === this.valueAt(row, column + 1) &&
|
|
this.valueAt(row, column) === this.valueAt(row, column + 2)) ||
|
|
(this.valueAt(row, column) === this.valueAt(row, column - 1) &&
|
|
this.valueAt(row, column) === this.valueAt(row, column + 1))
|
|
);
|
|
}
|
|
|
|
// returns true if the item at (row, column) is part of an horizontal match
|
|
isPartOfVerticalMatch(row, column) {
|
|
return (
|
|
(this.valueAt(row, column) === this.valueAt(row - 1, column) &&
|
|
this.valueAt(row, column) === this.valueAt(row - 2, column)) ||
|
|
(this.valueAt(row, column) === this.valueAt(row + 1, column) &&
|
|
this.valueAt(row, column) === this.valueAt(row + 2, column)) ||
|
|
(this.valueAt(row, column) === this.valueAt(row - 1, column) &&
|
|
this.valueAt(row, column) === this.valueAt(row + 1, column))
|
|
);
|
|
}
|
|
|
|
// returns the value of the item at (row, column), or false if it's not a valid pick
|
|
valueAt(row, column) {
|
|
if (!this.validPick(row, column)) {
|
|
return false;
|
|
}
|
|
return this.gameArray[row][column].value;
|
|
}
|
|
|
|
// returns true if the item at (row, column) is a valid pick
|
|
validPick(row, column) {
|
|
return (
|
|
row >= 0 &&
|
|
row < this.rows &&
|
|
column >= 0 &&
|
|
column < this.columns &&
|
|
this.gameArray[row] != undefined &&
|
|
this.gameArray[row][column] != undefined
|
|
);
|
|
}
|
|
|
|
// returns the number of board rows
|
|
getRows() {
|
|
return this.rows;
|
|
}
|
|
|
|
// returns the number of board columns
|
|
getColumns() {
|
|
return this.columns;
|
|
}
|
|
|
|
// sets a custom data on the item at (row, column)
|
|
setCustomData(row, column, customData) {
|
|
this.gameArray[row][column].customData = customData;
|
|
}
|
|
|
|
// returns the custom data of the item at (row, column)
|
|
customDataOf(row, column) {
|
|
return this.gameArray[row][column].customData;
|
|
}
|
|
|
|
// returns the selected item
|
|
getSelectedItem() {
|
|
return this.selectedItem;
|
|
}
|
|
|
|
// set the selected item as a {row, column} object
|
|
setSelectedItem(row, column) {
|
|
this.selectedItem = {
|
|
row: row,
|
|
column: column,
|
|
};
|
|
}
|
|
|
|
// deleselects any item
|
|
deleselectItem() {
|
|
this.selectedItem = false;
|
|
}
|
|
|
|
// checks if the item at (row, column) is the same as the item at (row2, column2)
|
|
areTheSame(row, column, row2, column2) {
|
|
return row == row2 && column == column2;
|
|
}
|
|
|
|
// returns true if two items at (row, column) and (row2, column2) are next to each other horizontally or vertically
|
|
areNext(row, column, row2, column2) {
|
|
return Math.abs(row - row2) + Math.abs(column - column2) == 1;
|
|
}
|
|
|
|
// swap the items at (row, column) and (row2, column2) and returns an object with movement information
|
|
swapItems(row, column, row2, column2) {
|
|
let tempObject = Object.assign(this.gameArray[row][column]);
|
|
this.gameArray[row][column] = Object.assign(this.gameArray[row2][column2]);
|
|
this.gameArray[row2][column2] = Object.assign(tempObject);
|
|
return [
|
|
{
|
|
row: row,
|
|
column: column,
|
|
deltaRow: row - row2,
|
|
deltaColumn: column - column2,
|
|
},
|
|
{
|
|
row: row2,
|
|
column: column2,
|
|
deltaRow: row2 - row,
|
|
deltaColumn: column2 - column,
|
|
},
|
|
];
|
|
}
|
|
|
|
// return the items part of a match in the board as an array of {row, column} object
|
|
getMatchList() {
|
|
let matches = [];
|
|
for (let i = 0; i < this.rows; i++) {
|
|
for (let j = 0; j < this.columns; j++) {
|
|
if (this.isPartOfMatch(i, j)) {
|
|
matches.push({
|
|
row: i,
|
|
column: j,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return matches;
|
|
}
|
|
|
|
// removes all items forming a match
|
|
removeMatches() {
|
|
let matches = this.getMatchList();
|
|
matches.forEach(
|
|
function (item) {
|
|
this.setEmpty(item.row, item.column);
|
|
}.bind(this)
|
|
);
|
|
}
|
|
|
|
// set the item at (row, column) as empty
|
|
setEmpty(row, column) {
|
|
this.gameArray[row][column].isEmpty = true;
|
|
}
|
|
|
|
// returns true if the item at (row, column) is empty
|
|
isEmpty(row, column) {
|
|
return this.gameArray[row][column].isEmpty;
|
|
}
|
|
|
|
// returns the amount of empty spaces below the item at (row, column)
|
|
emptySpacesBelow(row, column) {
|
|
let result = 0;
|
|
if (row != this.getRows()) {
|
|
for (let i = row + 1; i < this.getRows(); i++) {
|
|
if (this.isEmpty(i, column)) {
|
|
result++;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// arranges the board after a match, making items fall down. Returns an object with movement information
|
|
arrangeBoardAfterMatch() {
|
|
let result = [];
|
|
for (let i = this.getRows() - 2; i >= 0; i--) {
|
|
for (let j = 0; j < this.getColumns(); j++) {
|
|
let emptySpaces = this.emptySpacesBelow(i, j);
|
|
if (!this.isEmpty(i, j) && emptySpaces > 0) {
|
|
this.swapItems(i, j, i + emptySpaces, j);
|
|
result.push({
|
|
row: i + emptySpaces,
|
|
column: j,
|
|
deltaRow: emptySpaces,
|
|
deltaColumn: 0,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// replenished the board and returns an object with movement information
|
|
replenishBoard() {
|
|
let result = [];
|
|
for (let i = 0; i < this.getColumns(); i++) {
|
|
if (this.isEmpty(0, i)) {
|
|
let emptySpaces = this.emptySpacesBelow(0, i) + 1;
|
|
for (let j = 0; j < emptySpaces; j++) {
|
|
let randomValue = Math.floor(Math.random() * this.items);
|
|
result.push({
|
|
row: j,
|
|
column: i,
|
|
deltaRow: emptySpaces,
|
|
deltaColumn: 0,
|
|
});
|
|
this.gameArray[j][i].value = randomValue;
|
|
this.gameArray[j][i].isEmpty = false;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|