Remove inefficient array usage

The solver is now 1.5 times as fast.
This commit is contained in:
Florine W. Dekker 2024-05-10 18:02:53 +02:00
parent dd61229642
commit 5c53f07df1
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
3 changed files with 32 additions and 66 deletions

BIN
package-lock.json generated

Binary file not shown.

View File

@ -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",

View File

@ -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;
}
}