parent
6d93d3394f
commit
07f028d369
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "minesweeper",
|
||||
"version": "0.81.8",
|
||||
"version": "0.81.9",
|
||||
"description": "Just Minesweeper!",
|
||||
"author": "Felix W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -54,11 +54,7 @@ export class Field {
|
|||
return this._deathCount;
|
||||
}
|
||||
|
||||
private _wasAutoSolved: boolean = false;
|
||||
set wasAutoSolved(value: boolean) {
|
||||
if (this._wasAutoSolved && !value) throw new Error("Cannot set wasAutoSolved to false while it is true.");
|
||||
this._wasAutoSolved = value;
|
||||
}
|
||||
isAutoSolving: boolean = false;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -214,14 +210,14 @@ export class Field {
|
|||
if (square.getNeighborCount(it => it.hasMark) > 0) return;
|
||||
if (square.getNeighborCount(it => it.hasFlag) !== square.getNeighborCount(it => it.hasMine)) return;
|
||||
|
||||
this.statistics.squaresChorded++;
|
||||
if (!this.isAutoSolving) this.statistics.squaresChorded++;
|
||||
this.runUndoably(() => {
|
||||
square.neighbors
|
||||
.filter(it => it.isCovered && !it.hasFlag)
|
||||
.forEach(it => this.uncover(it.coords));
|
||||
});
|
||||
|
||||
if (this.hasLost) this.statistics.squaresChordedLeadingToLoss++;
|
||||
if (!this.isAutoSolving && this.hasLost) this.statistics.squaresChordedLeadingToLoss++;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,13 +263,13 @@ export class Field {
|
|||
this.addAction(new Action(
|
||||
() => {
|
||||
next.isCovered = false;
|
||||
this.statistics.squaresUncovered++;
|
||||
if (!this.isAutoSolving) this.statistics.squaresUncovered++;
|
||||
|
||||
if (next.hasMine) {
|
||||
this.timer.stop();
|
||||
this._hasLost = true;
|
||||
this._deathCount++;
|
||||
this.statistics.minesUncovered++;
|
||||
if (!this.isAutoSolving) this.statistics.minesUncovered++;
|
||||
} else {
|
||||
this._coveredNonMineCount--;
|
||||
if (this.coveredNonMineCount === 0) {
|
||||
|
@ -283,10 +279,12 @@ export class Field {
|
|||
this._flagCount = this.mineCount;
|
||||
this._hasWon = true;
|
||||
|
||||
if (!this._hasWonBefore && !this._wasAutoSolved) {
|
||||
if (!this._hasWonBefore) {
|
||||
this._hasWonBefore = true;
|
||||
this.statistics.gamesWon++;
|
||||
if (this.deathCount === 0) this.statistics.gamesWonWithoutLosing++;
|
||||
if (!this.isAutoSolving) {
|
||||
this.statistics.gamesWon++;
|
||||
if (this.deathCount === 0) this.statistics.gamesWonWithoutLosing++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -330,7 +328,7 @@ export class Field {
|
|||
this.addAction(new Action(
|
||||
() => {
|
||||
square.hasFlag = !square.hasFlag;
|
||||
if (square.hasFlag) this.statistics.squaresFlagged++;
|
||||
if (!this.isAutoSolving && square.hasFlag) this.statistics.squaresFlagged++;
|
||||
this._flagCount += (square.hasFlag ? 1 : -1);
|
||||
},
|
||||
() => {
|
||||
|
@ -355,11 +353,11 @@ export class Field {
|
|||
this.addAction(new Action(
|
||||
() => {
|
||||
square.hasMark = !square.hasMark;
|
||||
if (square.hasMark) this.statistics.squaresMarked++;
|
||||
if (!this.isAutoSolving && square.hasMark) this.statistics.squaresMarked++;
|
||||
},
|
||||
() => {
|
||||
square.hasMark = !square.hasMark;
|
||||
if (square.hasMark) this.statistics.squaresMarked++;
|
||||
if (!this.isAutoSolving && square.hasMark) this.statistics.squaresMarked++;
|
||||
return true;
|
||||
},
|
||||
() => true
|
||||
|
@ -408,7 +406,7 @@ export class Field {
|
|||
*/
|
||||
redo(amount: number | undefined = undefined): void {
|
||||
const redone = this.history.redo(amount);
|
||||
if (redone > 0) this.statistics.actionsRedone++;
|
||||
if (!this.isAutoSolving && redone > 0) this.statistics.actionsRedone++;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -420,8 +418,8 @@ export class Field {
|
|||
const wasLost = this.hasLost;
|
||||
|
||||
const undone = this.history.undo(amount);
|
||||
if (undone > 0) this.statistics.actionsUndone++;
|
||||
if (wasLost && !this.hasLost) this.statistics.lossesUndone++;
|
||||
if (!this.isAutoSolving && undone > 0) this.statistics.actionsUndone++;
|
||||
if (!this.isAutoSolving && wasLost && !this.hasLost) this.statistics.lossesUndone++;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,60 +15,30 @@ export class Solver {
|
|||
*/
|
||||
solve(field: Field): void {
|
||||
if (field.hasWon || field.hasLost) return;
|
||||
field.wasAutoSolved = true;
|
||||
if (field.hasStarted && !this.step(field.copy())) return;
|
||||
|
||||
if (!field.hasStarted) {
|
||||
field.isAutoSolving = true;
|
||||
field.runUndoably(() => {
|
||||
field.squareList.filter(it => it.hasFlag).forEach(it => field.toggleFlag(it.coords));
|
||||
field.squareList.filter(it => it.hasMark).forEach(it => field.toggleMark(it.coords));
|
||||
field.uncover({x: Math.floor(field.width / 2), y: Math.floor(field.height / 2)});
|
||||
const target = {x: Math.floor(field.width / 2), y: Math.floor(field.height / 2)};
|
||||
const targetSquare = field.getSquareOrElse(target, undefined)!;
|
||||
if (targetSquare.hasFlag) field.toggleFlag(target);
|
||||
if (targetSquare.hasMark) field.toggleMark(target);
|
||||
field.uncover(target);
|
||||
});
|
||||
field.isAutoSolving = false;
|
||||
}
|
||||
|
||||
field.isAutoSolving = true;
|
||||
field.runUndoably(() => {
|
||||
field.squareList.filter(it => it.hasFlag).forEach(it => field.toggleFlag(it.coords));
|
||||
field.squareList.filter(it => it.hasMark).forEach(it => field.toggleMark(it.coords));
|
||||
|
||||
let flagCount = -1;
|
||||
let coveredCount = -1;
|
||||
while (true) {
|
||||
let newFlagCount;
|
||||
let newCoveredCount;
|
||||
|
||||
this.stepSingleSquares(field);
|
||||
if (field.hasWon) break;
|
||||
newFlagCount = field.flagCount;
|
||||
newCoveredCount = field.coveredNonMineCount;
|
||||
if (newFlagCount !== flagCount || newCoveredCount !== coveredCount) {
|
||||
flagCount = newFlagCount;
|
||||
coveredCount = newCoveredCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
if (newFlagCount !== flagCount || newCoveredCount !== coveredCount) {
|
||||
flagCount = newFlagCount;
|
||||
coveredCount = newCoveredCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
// No solver method changed anything, so stop solving
|
||||
break;
|
||||
while (this.step(field)) {
|
||||
// Repeat until `step` returns false
|
||||
}
|
||||
});
|
||||
field.isAutoSolving = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,6 +95,35 @@ export class Solver {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Solves in one step through the field.
|
||||
*
|
||||
* @param field the field to solve one step in
|
||||
* @returns `true` if a step could be solved
|
||||
* @private
|
||||
*/
|
||||
private step(field: Field): boolean {
|
||||
let flagCount = field.flagCount;
|
||||
let coveredCount = field.coveredNonMineCount;
|
||||
|
||||
if (field.hasWon || field.hasLost)
|
||||
return false;
|
||||
|
||||
this.stepSingleSquares(field);
|
||||
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
||||
return true;
|
||||
|
||||
this.stepNeighboringSquares(field);
|
||||
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
||||
return true;
|
||||
|
||||
this.stepAllSquares(field);
|
||||
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves the field as much as by considering just one square at a time and looking for trivial solutions.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue