parent
fb1825b262
commit
336337eedb
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "minesweeper",
|
||||
"version": "0.78.3",
|
||||
"version": "0.78.4",
|
||||
"description": "Just Minesweeper!",
|
||||
"author": "Felix W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -14,6 +14,8 @@ export class Solver {
|
|||
* @param field the field to solve
|
||||
*/
|
||||
solve(field: Field): void {
|
||||
if (field.hasWon || field.hasLost) return;
|
||||
|
||||
if (!field.hasStarted) {
|
||||
field.runUndoably(() => {
|
||||
field.squareList.filter(it => it.hasFlag).forEach(it => field.flag(it.coords));
|
||||
|
@ -30,7 +32,7 @@ export class Solver {
|
|||
let newFlagCount;
|
||||
let newCoveredCount;
|
||||
|
||||
this.chordAndFlagTrivial(field);
|
||||
this.stepSingleSquares(field);
|
||||
if (field.hasWon) break;
|
||||
newFlagCount = field.flagCount;
|
||||
newCoveredCount = field.coveredNonMineCount;
|
||||
|
@ -40,7 +42,17 @@ export class Solver {
|
|||
continue;
|
||||
}
|
||||
|
||||
this.step(field);
|
||||
this.stepNeighboringSquares(field);
|
||||
if (field.hasWon) break;
|
||||
newFlagCount = field.flagCount;
|
||||
newCoveredCount = field.coveredNonMineCount;
|
||||
if (newFlagCount !== flagCount || newCoveredCount !== coveredCount) {
|
||||
flagCount = newFlagCount;
|
||||
coveredCount = newCoveredCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
this.stepAllSquares(field);
|
||||
if (field.hasWon) break;
|
||||
newFlagCount = field.flagCount;
|
||||
newCoveredCount = field.coveredNonMineCount;
|
||||
|
@ -76,12 +88,15 @@ export class Solver {
|
|||
|
||||
|
||||
/**
|
||||
* Automatically chords and flags all trivial squares.
|
||||
* Solves the field as much as by considering just one square at a time and looking for trivial solutions.
|
||||
*
|
||||
* @param field the field to chord and flag in
|
||||
* This function is very fast but only finds trivial moves such as a square that can be chorded or a square of which
|
||||
* all neighbors can be flagged.
|
||||
*
|
||||
* @param field the field to solve
|
||||
* @private
|
||||
*/
|
||||
private chordAndFlagTrivial(field: Field): void {
|
||||
private stepSingleSquares(field: Field): void {
|
||||
field.squareList
|
||||
.filter(it => !it.isCovered)
|
||||
.forEach(square => {
|
||||
|
@ -92,18 +107,50 @@ export class Solver {
|
|||
}
|
||||
|
||||
/**
|
||||
* Solves the given field given only the information currently available, without considering the information that
|
||||
* is gained from the actions performed by this function.
|
||||
* Solves the field as much as possible by considering only one uncovered square and its uncovered neighbors at a
|
||||
* time.
|
||||
*
|
||||
* This function is slower than `#stepSingleSquares` but finds some more advanced moves by effectively considering
|
||||
* two squares at time. Meanwhile, this function does not look at the bigger picture so it cannot infer some more
|
||||
* complicated moves. On the other hand, for some reason this function finds some edge cases that `#stepAllSquares`
|
||||
* overlooks.
|
||||
*
|
||||
* @param field the field to solve
|
||||
* @private
|
||||
*/
|
||||
private step(field: Field): void {
|
||||
private stepNeighboringSquares(field: Field): void {
|
||||
const knowns = field.squareList
|
||||
.filter(it => !it.isCovered)
|
||||
.filter(it => it.getNeighborCount(it => it.isCovered && !it.hasFlag) > 0);
|
||||
knowns.forEach(known => {
|
||||
const system = this.matrixSolve(field, known.neighbors.filter(it => !it.isCovered).concat(known), true);
|
||||
|
||||
system.forEach(target => {
|
||||
if (target === undefined) return;
|
||||
|
||||
const [solution, square] = target;
|
||||
if (solution === 0) field.uncover(square.coords);
|
||||
else if (solution === 1) field.flag(square.coords);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves the field as much as possible by looking at all uncovered squares and the remaining number of mines.
|
||||
*
|
||||
* Because this function considers all squares in the field, it is very slow. Then again, it finds a lot of steps
|
||||
* as well.
|
||||
*
|
||||
* @param field the field to solve
|
||||
* @private
|
||||
*/
|
||||
private stepAllSquares(field: Field): void {
|
||||
if (!field.hasStarted || field.hasWon || field.hasLost) return;
|
||||
|
||||
const knowns = field.squareList
|
||||
.filter(it => !it.isCovered)
|
||||
.filter(it => it.getNeighborCount(it => it.isCovered && !it.hasFlag) > 0);
|
||||
const system = this.solveFrom(field, knowns, false);
|
||||
const system = this.matrixSolve(field, knowns, false);
|
||||
|
||||
system.forEach(target => {
|
||||
if (target === undefined) return;
|
||||
|
@ -123,7 +170,7 @@ export class Solver {
|
|||
* all squares in the field. Enabling this option increases complexity, but may uncover some edge cases
|
||||
* @private
|
||||
*/
|
||||
private solveFrom(field: Field, knowns: Square[], adjacentSquaresOnly: boolean): ([number, Square] | undefined)[] {
|
||||
private matrixSolve(field: Field, knowns: Square[], adjacentSquaresOnly: boolean): ([number, Square] | undefined)[] {
|
||||
if (knowns.length === 0) return [];
|
||||
|
||||
let unknowns: Square[];
|
||||
|
|
Loading…
Reference in New Issue