Remove inefficient array usage
The solver is now 1.5 times as fast.
This commit is contained in:
parent
dd61229642
commit
5c53f07df1
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "minesweeper",
|
||||
"version": "0.86.12",
|
||||
"version": "0.86.13",
|
||||
"description": "Just Minesweeper!",
|
||||
"author": "Florine W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -309,31 +309,30 @@ export class Solver {
|
|||
|
||||
let unknowns: Square[];
|
||||
if (adjacentSquaresOnly) {
|
||||
unknowns = Array
|
||||
.from(new Set(knowns.reduce<Square[]>((acc, it) => acc.concat(it.neighbors), [])))
|
||||
.filter(it => it.isCovered && !it.hasFlag && !knowns.includes(it));
|
||||
unknowns =
|
||||
Array.from(new Set(knowns.flatMap(it => it.neighbors.filter(it => it.isCovered && !it.hasFlag))))
|
||||
.filter(it => !knowns.includes(it));
|
||||
} else {
|
||||
unknowns = field.squareList
|
||||
.filter(it => it.isCovered && !it.hasFlag && !knowns.includes(it));
|
||||
.filter(it => it.isCovered && !it.hasFlag)
|
||||
.filter(it => !knowns.includes(it));
|
||||
}
|
||||
if (unknowns.length === 0) return [];
|
||||
|
||||
const matrix: number[][] = [];
|
||||
knowns.forEach(square => {
|
||||
const row = Array<number>(unknowns.length).fill(0);
|
||||
const matrix = knowns.map(square => {
|
||||
const row = Array<number>(unknowns.length + 1).fill(0);
|
||||
square.neighbors
|
||||
.filter(it => it.isCovered && !it.hasFlag)
|
||||
.forEach(it => row[unknowns.indexOf(it)] = 1);
|
||||
|
||||
row.push(square.getNeighborCount(it => it.hasMine) - square.getNeighborCount(it => it.hasFlag));
|
||||
matrix.push(row);
|
||||
row[row.length - 1] = square.getNeighborCount(it => it.hasMine) - square.getNeighborCount(it => it.hasFlag);
|
||||
return row;
|
||||
});
|
||||
if (!adjacentSquaresOnly)
|
||||
matrix.push(Array(unknowns.length).fill(1).concat(field.mineCount - field.flagCount));
|
||||
|
||||
return (new Matrix(matrix))
|
||||
.solveBinary()
|
||||
.map((it, i) => it === undefined ? undefined : [it, unknowns[i]]);
|
||||
.map((it, idx) => it === undefined ? undefined : [it, unknowns[idx]]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -379,45 +378,6 @@ export class Matrix {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the `row`th row of numbers.
|
||||
*
|
||||
* @param row the index of the row to return
|
||||
* @returns the `row`th row of numbers
|
||||
*/
|
||||
getRow(row: number): number[] {
|
||||
req(row >= 0 && row < this.rowCount, () => `Row must be in range [0, ${this.rowCount}) but was ${row}.`);
|
||||
|
||||
return this.cells[row];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `col`th column of numbers.
|
||||
*
|
||||
* @param col the index of the column to return
|
||||
* @returns the `col`th column of numbers
|
||||
*/
|
||||
getCol(col: number): number[] {
|
||||
req(col >= 0 && col < this.colCount, () => `Col must be in range [0, ${this.colCount}) but was ${col}.`);
|
||||
|
||||
return this.cells.map(row => row[col]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `col`th number in the `row`th row.
|
||||
*
|
||||
* @param row the index of the row to find the number in
|
||||
* @param col the index of the column to find the number in
|
||||
* @returns the `col`th number in the `row`th row
|
||||
*/
|
||||
getCell(row: number, col: number): number {
|
||||
req(row >= 0 && row < this.rowCount, () => `Expected 0 <= row < ${this.rowCount}, actual ${row}.`);
|
||||
req(col >= 0 && col < this.colCount, () => `Expected 0 <= col < ${this.colCount}, actual ${col}.`);
|
||||
|
||||
return this.cells[row][col];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transforms this matrix into its row-reduced echelon form using Gauss-Jordan elimination.
|
||||
*/
|
||||
|
@ -425,19 +385,19 @@ export class Matrix {
|
|||
let pivot = 0;
|
||||
for (let row = 0; row < this.rowCount; row++) {
|
||||
// Find pivot
|
||||
while (pivot < this.colCount && this.getCol(pivot).slice(row).every(it => it === 0)) pivot++;
|
||||
while (pivot < this.colCount && this.cells.slice(row).every(it => it[pivot] === 0)) pivot++;
|
||||
if (pivot >= this.colCount) return;
|
||||
|
||||
// Set pivot to non-zero
|
||||
if (this.getCell(row, pivot) === 0)
|
||||
this.swap(row, this.getCol(pivot).slice(row + 1).findIndex(it => it !== 0) + row + 1);
|
||||
if (this.cells[row][pivot] === 0)
|
||||
this.swap(row, this.cells.slice(row + 1).findIndex(it => it[pivot] !== 0) + row + 1);
|
||||
// Set pivot to 1
|
||||
this.multiply(row, 1 / this.getCell(row, pivot));
|
||||
this.multiply(row, 1 / this.cells[row][pivot]);
|
||||
|
||||
// Set all other cells in this column to 0
|
||||
for (let row2 = 0; row2 < this.rowCount; row2++) {
|
||||
if (row2 === row) continue;
|
||||
this.add(row2, row, -this.getCell(row2, pivot));
|
||||
this.add(row2, row, -this.cells[row2][pivot]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -454,13 +414,13 @@ export class Matrix {
|
|||
this.rref();
|
||||
|
||||
return range(this.colCount - 1)
|
||||
.map(it => {
|
||||
const rowPivotIndex = this.getCol(it).findIndex(it => it === 1);
|
||||
.map(col => {
|
||||
const rowPivotIndex = this.cells.findIndex(it => it[col] === 1);
|
||||
if (rowPivotIndex < 0) return undefined;
|
||||
|
||||
const row = this.getRow(rowPivotIndex);
|
||||
if (row.slice(0, it).every(it => it === 0) && row.slice(it + 1, -1).every(it => it === 0))
|
||||
return row.slice(-1)[0];
|
||||
const row = this.cells[rowPivotIndex];
|
||||
if (row.slice(0, col).every(it => it === 0) && row.slice(col + 1, -1).every(it => it === 0))
|
||||
return row.at(-1);
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
@ -474,7 +434,7 @@ export class Matrix {
|
|||
solveBinary(): (number | undefined)[] {
|
||||
const resultsA = this.solve();
|
||||
const resultsB = this.solveBinarySub();
|
||||
return resultsA.map((it, i) => it ?? resultsB[i]);
|
||||
return resultsA.map((it, idx) => it ?? resultsB[idx]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -489,7 +449,7 @@ export class Matrix {
|
|||
this.cells.forEach(row => {
|
||||
// ax = b
|
||||
const a = row.slice(0, -1);
|
||||
const b = row.slice(-1)[0];
|
||||
const b = row.at(-1);
|
||||
|
||||
const negSum = a.filter(it => it < 0).reduce((sum, cell) => sum + cell, 0);
|
||||
const posSum = a.filter(it => it > 0).reduce((sum, cell) => sum + cell, 0);
|
||||
|
@ -517,7 +477,11 @@ export class Matrix {
|
|||
* @param rowB the index of the other row to swap
|
||||
*/
|
||||
swap(rowA: number, rowB: number) {
|
||||
[this.cells[rowA], this.cells[rowB]] = [this.cells[rowB], this.cells[rowA]];
|
||||
for (let i = 0; i < this.colCount; i++) {
|
||||
const temp = this.cells[rowA][i];
|
||||
this.cells[rowA][i] = this.cells[rowB][i];
|
||||
this.cells[rowB][i] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -527,7 +491,8 @@ export class Matrix {
|
|||
* @param factor the factory to multiply each number with
|
||||
*/
|
||||
multiply(row: number, factor: number) {
|
||||
this.cells[row] = this.cells[row].map(it => it * factor);
|
||||
for (let i = 0; i < this.colCount; i++)
|
||||
this.cells[row][i] *= factor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -540,7 +505,8 @@ export class Matrix {
|
|||
* @param factor the factor to multiply each added number with
|
||||
*/
|
||||
add(rowA: number, rowB: number, factor: number) {
|
||||
this.cells[rowA] = this.cells[rowA].map((_, i) => this.cells[rowA][i] + this.cells[rowB][i] * factor);
|
||||
for (let i = 0; i < this.colCount; i++)
|
||||
this.cells[rowA][i] += this.cells[rowB][i] * factor;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue