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", "name": "minesweeper",
"version": "0.81.8", "version": "0.81.9",
"description": "Just Minesweeper!", "description": "Just Minesweeper!",
"author": "Felix W. Dekker", "author": "Felix W. Dekker",
"browser": "dist/bundle.js", "browser": "dist/bundle.js",

View File

@ -54,11 +54,7 @@ export class Field {
return this._deathCount; return this._deathCount;
} }
private _wasAutoSolved: boolean = false; isAutoSolving: 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;
}
/** /**
@ -214,14 +210,14 @@ export class Field {
if (square.getNeighborCount(it => it.hasMark) > 0) return; if (square.getNeighborCount(it => it.hasMark) > 0) return;
if (square.getNeighborCount(it => it.hasFlag) !== square.getNeighborCount(it => it.hasMine)) return; if (square.getNeighborCount(it => it.hasFlag) !== square.getNeighborCount(it => it.hasMine)) return;
this.statistics.squaresChorded++; if (!this.isAutoSolving) this.statistics.squaresChorded++;
this.runUndoably(() => { this.runUndoably(() => {
square.neighbors square.neighbors
.filter(it => it.isCovered && !it.hasFlag) .filter(it => it.isCovered && !it.hasFlag)
.forEach(it => this.uncover(it.coords)); .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( this.addAction(new Action(
() => { () => {
next.isCovered = false; next.isCovered = false;
this.statistics.squaresUncovered++; if (!this.isAutoSolving) this.statistics.squaresUncovered++;
if (next.hasMine) { if (next.hasMine) {
this.timer.stop(); this.timer.stop();
this._hasLost = true; this._hasLost = true;
this._deathCount++; this._deathCount++;
this.statistics.minesUncovered++; if (!this.isAutoSolving) this.statistics.minesUncovered++;
} else { } else {
this._coveredNonMineCount--; this._coveredNonMineCount--;
if (this.coveredNonMineCount === 0) { if (this.coveredNonMineCount === 0) {
@ -283,13 +279,15 @@ export class Field {
this._flagCount = this.mineCount; this._flagCount = this.mineCount;
this._hasWon = true; this._hasWon = true;
if (!this._hasWonBefore && !this._wasAutoSolved) { if (!this._hasWonBefore) {
this._hasWonBefore = true; this._hasWonBefore = true;
if (!this.isAutoSolving) {
this.statistics.gamesWon++; this.statistics.gamesWon++;
if (this.deathCount === 0) this.statistics.gamesWonWithoutLosing++; if (this.deathCount === 0) this.statistics.gamesWonWithoutLosing++;
} }
} }
} }
}
}, },
() => { () => {
next.isCovered = true; next.isCovered = true;
@ -330,7 +328,7 @@ export class Field {
this.addAction(new Action( this.addAction(new Action(
() => { () => {
square.hasFlag = !square.hasFlag; square.hasFlag = !square.hasFlag;
if (square.hasFlag) this.statistics.squaresFlagged++; if (!this.isAutoSolving && square.hasFlag) this.statistics.squaresFlagged++;
this._flagCount += (square.hasFlag ? 1 : -1); this._flagCount += (square.hasFlag ? 1 : -1);
}, },
() => { () => {
@ -355,11 +353,11 @@ export class Field {
this.addAction(new Action( this.addAction(new Action(
() => { () => {
square.hasMark = !square.hasMark; square.hasMark = !square.hasMark;
if (square.hasMark) this.statistics.squaresMarked++; if (!this.isAutoSolving && square.hasMark) this.statistics.squaresMarked++;
}, },
() => { () => {
square.hasMark = !square.hasMark; square.hasMark = !square.hasMark;
if (square.hasMark) this.statistics.squaresMarked++; if (!this.isAutoSolving && square.hasMark) this.statistics.squaresMarked++;
return true; return true;
}, },
() => true () => true
@ -408,7 +406,7 @@ export class Field {
*/ */
redo(amount: number | undefined = undefined): void { redo(amount: number | undefined = undefined): void {
const redone = this.history.redo(amount); 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 wasLost = this.hasLost;
const undone = this.history.undo(amount); const undone = this.history.undo(amount);
if (undone > 0) this.statistics.actionsUndone++; if (!this.isAutoSolving && undone > 0) this.statistics.actionsUndone++;
if (wasLost && !this.hasLost) this.statistics.lossesUndone++; if (!this.isAutoSolving && wasLost && !this.hasLost) this.statistics.lossesUndone++;
} }

View File

@ -15,60 +15,30 @@ export class Solver {
*/ */
solve(field: Field): void { solve(field: Field): void {
if (field.hasWon || field.hasLost) return; if (field.hasWon || field.hasLost) return;
field.wasAutoSolved = true; if (field.hasStarted && !this.step(field.copy())) return;
if (!field.hasStarted) { if (!field.hasStarted) {
field.isAutoSolving = true;
field.runUndoably(() => { field.runUndoably(() => {
field.squareList.filter(it => it.hasFlag).forEach(it => field.toggleFlag(it.coords)); const target = {x: Math.floor(field.width / 2), y: Math.floor(field.height / 2)};
field.squareList.filter(it => it.hasMark).forEach(it => field.toggleMark(it.coords)); const targetSquare = field.getSquareOrElse(target, undefined)!;
field.uncover({x: Math.floor(field.width / 2), y: Math.floor(field.height / 2)}); if (targetSquare.hasFlag) field.toggleFlag(target);
if (targetSquare.hasMark) field.toggleMark(target);
field.uncover(target);
}); });
field.isAutoSolving = false;
} }
field.isAutoSolving = true;
field.runUndoably(() => { field.runUndoably(() => {
field.squareList.filter(it => it.hasFlag).forEach(it => field.toggleFlag(it.coords)); 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.squareList.filter(it => it.hasMark).forEach(it => field.toggleMark(it.coords));
let flagCount = -1; while (this.step(field)) {
let coveredCount = -1; // Repeat until `step` returns false
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;
} }
}); });
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. * Solves the field as much as by considering just one square at a time and looking for trivial solutions.
* *