Do not count statistics during solver

Also fixes #72.
This commit is contained in:
Florine W. Dekker 2020-09-01 17:36:45 +02:00
parent 6d93d3394f
commit 07f028d369
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
3 changed files with 58 additions and 61 deletions

View File

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

View File

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

View File

@ -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.
*