Compare commits
1 Commits
main
...
solve-with
Author | SHA1 | Date |
---|---|---|
Florine W. Dekker | 30e3467598 |
Binary file not shown.
|
@ -160,7 +160,7 @@ export class Field {
|
||||||
* @param square the square from to move mines away from
|
* @param square the square from to move mines away from
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private clearMines(square: Square): void {
|
private async moveMinesAwayFrom(square: Square): Promise<void> {
|
||||||
const swapAction = (source: Square, target: Square) => new Action(
|
const swapAction = (source: Square, target: Square) => new Action(
|
||||||
() => {
|
() => {
|
||||||
source.hasMine = false;
|
source.hasMine = false;
|
||||||
|
@ -174,21 +174,20 @@ export class Field {
|
||||||
() => true
|
() => true
|
||||||
);
|
);
|
||||||
|
|
||||||
this.runUndoably(() => {
|
await this.runUndoably(async () => {
|
||||||
if (square.hasMine) {
|
if (square.hasMine) {
|
||||||
const target = this.squareList.find(it => !it.hasMine && it !== square)!;
|
const target = this.squareList.find(it => !it.hasMine && it !== square)!;
|
||||||
this.addAction(swapAction(square, target));
|
await this.addAction(swapAction(square, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
square.neighbors
|
const eligibleNeighbors = square.neighbors.filter(it => it.hasMine);
|
||||||
.filter(it => it.hasMine)
|
for (const neighbors of eligibleNeighbors) {
|
||||||
.forEach(it => {
|
const target = this.squareList
|
||||||
const target = this.squareList
|
.find(it => !it.hasMine && it !== square && square.neighbors.indexOf(it) < 0);
|
||||||
.find(it => !it.hasMine && it !== square && square.neighbors.indexOf(it) < 0);
|
|
||||||
|
|
||||||
if (target !== undefined)
|
if (target !== undefined)
|
||||||
this.addAction(swapAction(it, target));
|
await this.addAction(swapAction(neighbors, target));
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +213,7 @@ export class Field {
|
||||||
*
|
*
|
||||||
* @param coords the coordinates of the square to chord
|
* @param coords the coordinates of the square to chord
|
||||||
*/
|
*/
|
||||||
chord(coords: { x: number, y: number }): void {
|
async chord(coords: { x: number, y: number }): Promise<void> {
|
||||||
const square = this.squares[coords.y][coords.x];
|
const square = this.squares[coords.y][coords.x];
|
||||||
|
|
||||||
if (square === undefined) throw new Error(`Cannot chord undefined square at (${coords}).`);
|
if (square === undefined) throw new Error(`Cannot chord undefined square at (${coords}).`);
|
||||||
|
@ -223,10 +222,11 @@ export class Field {
|
||||||
if (square.getNeighborCount(it => it.hasFlag) !== square.getNeighborCount(it => it.hasMine)) return;
|
if (square.getNeighborCount(it => it.hasFlag) !== square.getNeighborCount(it => it.hasMine)) return;
|
||||||
|
|
||||||
if (!this.isAutoSolving) this.statistics.squaresChorded++;
|
if (!this.isAutoSolving) this.statistics.squaresChorded++;
|
||||||
this.runUndoably(() => {
|
await this.runUndoably(async () => {
|
||||||
square.neighbors
|
const eligibleNeighbors = square.neighbors.filter(it => it.isCovered && !it.hasFlag);
|
||||||
.filter(it => it.isCovered && !it.hasFlag)
|
for (const neighbor of eligibleNeighbors) {
|
||||||
.forEach(it => this.uncover(it.coords));
|
await this.uncover(neighbor.coords);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.isAutoSolving && this.hasLost) this.statistics.squaresChordedLeadingToLoss++;
|
if (!this.isAutoSolving && this.hasLost) this.statistics.squaresChordedLeadingToLoss++;
|
||||||
|
@ -237,31 +237,50 @@ export class Field {
|
||||||
*
|
*
|
||||||
* @param coords the coordinates of the square to uncover
|
* @param coords the coordinates of the square to uncover
|
||||||
*/
|
*/
|
||||||
uncover(coords: { x: number, y: number }): void {
|
async uncover(coords: { x: number, y: number }): Promise<void> {
|
||||||
const square = this.squares[coords.y][coords.x];
|
const square = this.squares[coords.y][coords.x];
|
||||||
if (square === undefined) throw new Error(`Cannot uncover undefined square at (${coords}).`);
|
if (square === undefined) throw new Error(`Cannot uncover undefined square at (${coords}).`);
|
||||||
if (this.hasWon || this.hasLost) return;
|
if (this.hasWon || this.hasLost) return;
|
||||||
|
|
||||||
this.runUndoably(() => {
|
await this.runUndoably(async () => {
|
||||||
if (!this.hasStarted) {
|
if (!this.hasStarted) {
|
||||||
this.statistics.gamesStarted++;
|
this.statistics.gamesStarted++;
|
||||||
|
|
||||||
if (this.isSolvable) {
|
if (this.isSolvable) {
|
||||||
let i = 1;
|
let i = 0;
|
||||||
const time = Timer.time(() => {
|
const time = await Timer.time(async () => {
|
||||||
while (!Solver.canSolve(this, coords)) {
|
// while (!(await Solver.canSolve(this, coords))) {
|
||||||
this.shuffle(this.rng.uint32());
|
// this.shuffle(this.rng.uint32());
|
||||||
i++;
|
// i++;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
let seed = undefined;
|
||||||
|
|
||||||
|
while (seed === undefined) {
|
||||||
|
const seeds = Array.from({length: 10}, () => this.rng.uint32());
|
||||||
|
i += seeds.length;
|
||||||
|
|
||||||
|
const tasks = seeds.map(async seed => {
|
||||||
|
const copy = this.copy();
|
||||||
|
copy.shuffle(seed);
|
||||||
|
return await Solver.canSolve(copy, coords);
|
||||||
|
});
|
||||||
|
const solvable = await Promise.all(tasks);
|
||||||
|
|
||||||
|
seed = seeds[solvable.indexOf(true)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.shuffle(seed);
|
||||||
});
|
});
|
||||||
console.log(`Found solvable field in ${time}ms in ${i} attempts.`);
|
console.log(`Found solvable field in ${time}ms in ${i} attempts.`);
|
||||||
}
|
}
|
||||||
this.clearMines(square);
|
await this.moveMinesAwayFrom(square);
|
||||||
|
|
||||||
this._hasStarted = true;
|
this._hasStarted = true;
|
||||||
this.timer.start();
|
this.timer.start();
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
this.addAction(new Action(() => {}, () => false, () => false));
|
await this.addAction(new Action(() => {}, () => false, () => false));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +290,7 @@ export class Field {
|
||||||
if (!next.isCovered || next.hasFlag || next.hasMark) continue;
|
if (!next.isCovered || next.hasFlag || next.hasMark) continue;
|
||||||
|
|
||||||
let remainingFlags: Square[] | undefined;
|
let remainingFlags: Square[] | undefined;
|
||||||
this.addAction(new Action(
|
await this.addAction(new Action(
|
||||||
() => {
|
() => {
|
||||||
next.isCovered = false;
|
next.isCovered = false;
|
||||||
if (!this.isAutoSolving) this.statistics.squaresUncovered++;
|
if (!this.isAutoSolving) this.statistics.squaresUncovered++;
|
||||||
|
@ -335,12 +354,12 @@ export class Field {
|
||||||
*
|
*
|
||||||
* @param coords the coordinates of the square to toggle the flag at
|
* @param coords the coordinates of the square to toggle the flag at
|
||||||
*/
|
*/
|
||||||
toggleFlag(coords: { x: number, y: number }): void {
|
async toggleFlag(coords: { x: number, y: number }): Promise<void> {
|
||||||
const square = this.squares[coords.y][coords.x];
|
const square = this.squares[coords.y][coords.x];
|
||||||
if (square === undefined) throw new Error(`Cannot toggle flag of undefined square at (${coords}).`);
|
if (square === undefined) throw new Error(`Cannot toggle flag of undefined square at (${coords}).`);
|
||||||
if (!square.isCovered || square.hasMark || this.hasWon || this.hasLost) return;
|
if (!square.isCovered || square.hasMark || this.hasWon || this.hasLost) return;
|
||||||
|
|
||||||
this.addAction(new Action(
|
await this.addAction(new Action(
|
||||||
() => {
|
() => {
|
||||||
square.hasFlag = !square.hasFlag;
|
square.hasFlag = !square.hasFlag;
|
||||||
if (!this.isAutoSolving && square.hasFlag) this.statistics.squaresFlagged++;
|
if (!this.isAutoSolving && square.hasFlag) this.statistics.squaresFlagged++;
|
||||||
|
@ -360,12 +379,12 @@ export class Field {
|
||||||
*
|
*
|
||||||
* @param coords the coordinates of the square to toggle the question mark at
|
* @param coords the coordinates of the square to toggle the question mark at
|
||||||
*/
|
*/
|
||||||
toggleMark(coords: { x: number, y: number }): void {
|
async toggleMark(coords: { x: number, y: number }): Promise<void> {
|
||||||
const square = this.squares[coords.y][coords.x];
|
const square = this.squares[coords.y][coords.x];
|
||||||
if (square === undefined) throw new Error(`Cannot toggle flag of undefined square at (${coords}).`);
|
if (square === undefined) throw new Error(`Cannot toggle flag of undefined square at (${coords}).`);
|
||||||
if (!square.isCovered || square.hasFlag || this.hasWon || this.hasLost) return;
|
if (!square.isCovered || square.hasFlag || this.hasWon || this.hasLost) return;
|
||||||
|
|
||||||
this.addAction(new Action(
|
await this.addAction(new Action(
|
||||||
() => {
|
() => {
|
||||||
square.hasMark = !square.hasMark;
|
square.hasMark = !square.hasMark;
|
||||||
if (!this.isAutoSolving && square.hasMark) this.statistics.squaresMarked++;
|
if (!this.isAutoSolving && square.hasMark) this.statistics.squaresMarked++;
|
||||||
|
@ -389,9 +408,9 @@ export class Field {
|
||||||
* @param callback a function such that all its calls to `#addAction` should be undoable with a single invocation of
|
* @param callback a function such that all its calls to `#addAction` should be undoable with a single invocation of
|
||||||
* `#undo`
|
* `#undo`
|
||||||
*/
|
*/
|
||||||
runUndoably(callback: () => void): void {
|
async runUndoably(callback: () => Promise<void>): Promise<void> {
|
||||||
this.history.startSequence();
|
this.history.startSequence();
|
||||||
callback();
|
await callback();
|
||||||
this.history.commitSequence();
|
this.history.commitSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,11 +422,11 @@ export class Field {
|
||||||
* @param action the action that can be undone
|
* @param action the action that can be undone
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private addAction(action: Action): void {
|
private async addAction(action: Action): Promise<void> {
|
||||||
if (this.history.hasUncommittedSequence)
|
if (this.history.hasUncommittedSequence)
|
||||||
this.history.addAction(action);
|
this.history.addAction(action);
|
||||||
else
|
else
|
||||||
this.runUndoably(() => this.history.addAction(action));
|
await this.runUndoably(async () => this.history.addAction(action));
|
||||||
|
|
||||||
action.run();
|
action.run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,12 +218,12 @@ export class Game {
|
||||||
this.hintForm = $("#hintForm");
|
this.hintForm = $("#hintForm");
|
||||||
this.hintForm.addEventListener(
|
this.hintForm.addEventListener(
|
||||||
"submit",
|
"submit",
|
||||||
event => {
|
async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (this.field !== null) {
|
if (this.field !== null) {
|
||||||
this.statistics.hintsRequested++;
|
this.statistics.hintsRequested++;
|
||||||
this.display.hintSquare = Solver.getHint(this.field);
|
this.display.hintSquare = await Solver.getHint(this.field);
|
||||||
}
|
}
|
||||||
blurActiveElement();
|
blurActiveElement();
|
||||||
}
|
}
|
||||||
|
@ -233,12 +233,12 @@ export class Game {
|
||||||
this.solveForm = $("#solveForm");
|
this.solveForm = $("#solveForm");
|
||||||
this.solveForm.addEventListener(
|
this.solveForm.addEventListener(
|
||||||
"submit",
|
"submit",
|
||||||
event => {
|
async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (this.field !== null) {
|
if (this.field !== null) {
|
||||||
this.statistics.solverUsages++;
|
this.statistics.solverUsages++;
|
||||||
Solver.solve(this.field);
|
await Solver.solve(this.field);
|
||||||
}
|
}
|
||||||
blurActiveElement();
|
blurActiveElement();
|
||||||
}
|
}
|
||||||
|
@ -355,11 +355,11 @@ export class Game {
|
||||||
this.canvas.addEventListener("contextmenu", event => event.preventDefault());
|
this.canvas.addEventListener("contextmenu", event => event.preventDefault());
|
||||||
this.canvas.addEventListener(
|
this.canvas.addEventListener(
|
||||||
"mousedown",
|
"mousedown",
|
||||||
event => {
|
async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.field === null) return;
|
if (this.field === null) return;
|
||||||
|
|
||||||
this.field.runUndoably(() => {
|
await this.field.runUndoably(async () => {
|
||||||
const coords = this.display.posToSquare({x: event.clientX, y: event.clientY});
|
const coords = this.display.posToSquare({x: event.clientX, y: event.clientY});
|
||||||
if (this.field === null || !this.field.hasSquareAt(coords)) return;
|
if (this.field === null || !this.field.hasSquareAt(coords)) return;
|
||||||
|
|
||||||
|
@ -372,13 +372,13 @@ export class Game {
|
||||||
const square = this.field.getSquareOrElse(coords);
|
const square = this.field.getSquareOrElse(coords);
|
||||||
if (square !== null) {
|
if (square !== null) {
|
||||||
if (square.hasFlag) {
|
if (square.hasFlag) {
|
||||||
this.field.toggleFlag(coords);
|
await this.field.toggleFlag(coords);
|
||||||
if (preferences.marksEnabled)
|
if (preferences.marksEnabled)
|
||||||
this.field.toggleMark(coords);
|
await this.field.toggleMark(coords);
|
||||||
} else if (square.hasMark) {
|
} else if (square.hasMark) {
|
||||||
this.field.toggleMark(coords);
|
await this.field.toggleMark(coords);
|
||||||
} else {
|
} else {
|
||||||
this.field.toggleFlag(coords);
|
await this.field.toggleFlag(coords);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,30 +395,30 @@ export class Game {
|
||||||
);
|
);
|
||||||
this.canvas.addEventListener(
|
this.canvas.addEventListener(
|
||||||
"mouseup",
|
"mouseup",
|
||||||
event => {
|
async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.field === null) return;
|
if (this.field === null) return;
|
||||||
|
|
||||||
this.field.runUndoably(() => {
|
await this.field.runUndoably(async () => {
|
||||||
const coords = this.display.posToSquare({x: event.clientX, y: event.clientY});
|
const coords = this.display.posToSquare({x: event.clientX, y: event.clientY});
|
||||||
if (this.field === null || !this.field.hasSquareAt(coords)) return;
|
if (this.field === null || !this.field.hasSquareAt(coords)) return;
|
||||||
|
|
||||||
switch (event.button) {
|
switch (event.button) {
|
||||||
case 0:
|
case 0:
|
||||||
if (this.leftDown && this.rightDown)
|
if (this.leftDown && this.rightDown)
|
||||||
this.field.chord(coords);
|
await this.field.chord(coords);
|
||||||
else if (!this.holdsAfterChord && this.leftDown)
|
else if (!this.holdsAfterChord && this.leftDown)
|
||||||
this.field.uncover(coords);
|
await this.field.uncover(coords);
|
||||||
|
|
||||||
this.leftDown = false;
|
this.leftDown = false;
|
||||||
this.holdsAfterChord = this.rightDown;
|
this.holdsAfterChord = this.rightDown;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
this.field.chord(coords);
|
await this.field.chord(coords);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (this.leftDown && this.rightDown)
|
if (this.leftDown && this.rightDown)
|
||||||
this.field.chord(coords);
|
await this.field.chord(coords);
|
||||||
|
|
||||||
this.rightDown = false;
|
this.rightDown = false;
|
||||||
this.holdsAfterChord = this.leftDown;
|
this.holdsAfterChord = this.leftDown;
|
||||||
|
|
|
@ -13,28 +13,30 @@ export class Solver {
|
||||||
*
|
*
|
||||||
* @param field the field to solve
|
* @param field the field to solve
|
||||||
*/
|
*/
|
||||||
static solve(field: Field): void {
|
static async solve(field: Field): Promise<void> {
|
||||||
if (field.hasWon || field.hasLost) return;
|
if (field.hasWon || field.hasLost) return;
|
||||||
if (field.hasStarted && !this.step(field.copy())) return;
|
if (field.hasStarted && !(await this.step(field.copy()))) return;
|
||||||
|
|
||||||
if (!field.hasStarted) {
|
if (!field.hasStarted) {
|
||||||
field.isAutoSolving = true;
|
field.isAutoSolving = true;
|
||||||
field.runUndoably(() => {
|
await field.runUndoably(async () => {
|
||||||
const target = {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)!;
|
const targetSquare = field.getSquareOrElse(target, undefined)!;
|
||||||
if (targetSquare.hasFlag) field.toggleFlag(target);
|
if (targetSquare.hasFlag) await field.toggleFlag(target);
|
||||||
if (targetSquare.hasMark) field.toggleMark(target);
|
if (targetSquare.hasMark) await field.toggleMark(target);
|
||||||
field.uncover(target);
|
await field.uncover(target);
|
||||||
});
|
});
|
||||||
field.isAutoSolving = false;
|
field.isAutoSolving = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
field.isAutoSolving = true;
|
field.isAutoSolving = true;
|
||||||
field.runUndoably(() => {
|
await field.runUndoably(async () => {
|
||||||
field.squareList.filter(it => it.hasFlag).forEach(it => field.toggleFlag(it.coords));
|
for (const squares of field.squareList.filter(it => it.hasFlag))
|
||||||
field.squareList.filter(it => it.hasMark).forEach(it => field.toggleMark(it.coords));
|
await field.toggleFlag(squares.coords);
|
||||||
|
for (const squares of field.squareList.filter(it => it.hasMark))
|
||||||
|
await field.toggleMark(squares.coords);
|
||||||
|
|
||||||
while (this.step(field)) {
|
while (await this.step(field)) {
|
||||||
// Repeat until `step` returns false
|
// Repeat until `step` returns false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -52,10 +54,10 @@ export class Solver {
|
||||||
* @param field the field to check for solvability
|
* @param field the field to check for solvability
|
||||||
* @param initialSquare the initial coordinates to click at
|
* @param initialSquare the initial coordinates to click at
|
||||||
*/
|
*/
|
||||||
static canSolve(field: Field, initialSquare: { x: number, y: number } | undefined = undefined): boolean {
|
static async canSolve(field: Field, initialSquare: { x: number, y: number } | undefined = undefined): Promise<boolean> {
|
||||||
const copy = field.copy();
|
const copy = field.copy();
|
||||||
if (initialSquare !== undefined) copy.runUndoably(() => copy.uncover(initialSquare));
|
if (initialSquare !== undefined) await copy.runUndoably(async () => await copy.uncover(initialSquare));
|
||||||
this.solve(copy);
|
await this.solve(copy);
|
||||||
return copy.hasWon;
|
return copy.hasWon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ export class Solver {
|
||||||
* @param field the field to suggest a move for
|
* @param field the field to suggest a move for
|
||||||
* @returns a suggestion for a next move based on the current state of the field
|
* @returns a suggestion for a next move based on the current state of the field
|
||||||
*/
|
*/
|
||||||
static getHint(field: Field): Square | null {
|
static async getHint(field: Field): Promise<Square | null> {
|
||||||
if (!field.hasStarted || field.hasWon || field.hasLost) return null;
|
if (!field.hasStarted || field.hasWon || field.hasLost) return null;
|
||||||
const knowns = Solver.getKnowns(field);
|
const knowns = Solver.getKnowns(field);
|
||||||
|
|
||||||
|
@ -102,22 +104,22 @@ export class Solver {
|
||||||
* @returns `true` if a step could be solved
|
* @returns `true` if a step could be solved
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private static step(field: Field): boolean {
|
private static async step(field: Field): Promise<boolean> {
|
||||||
let flagCount = field.flagCount;
|
let flagCount = field.flagCount;
|
||||||
let coveredCount = field.coveredNonMineCount;
|
let coveredCount = field.coveredNonMineCount;
|
||||||
|
|
||||||
if (field.hasWon || field.hasLost)
|
if (field.hasWon || field.hasLost)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this.stepSingleSquares(field);
|
await this.stepSingleSquares(field);
|
||||||
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
this.stepNeighboringSquares(field);
|
await this.stepNeighboringSquares(field);
|
||||||
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
this.stepAllSquares(field);
|
await this.stepAllSquares(field);
|
||||||
// noinspection RedundantIfStatementJS // Makes it easier to add more steps
|
// noinspection RedundantIfStatementJS // Makes it easier to add more steps
|
||||||
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
if (field.hasWon || field.flagCount !== flagCount || field.coveredNonMineCount !== coveredCount)
|
||||||
return true;
|
return true;
|
||||||
|
@ -134,13 +136,16 @@ export class Solver {
|
||||||
* @param field the field to solve
|
* @param field the field to solve
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private static stepSingleSquares(field: Field): void {
|
private static async stepSingleSquares(field: Field): Promise<void> {
|
||||||
Solver.getKnowns(field)
|
for (const square of Solver.getKnowns(field)) {
|
||||||
.forEach(square => {
|
await field.chord(square);
|
||||||
field.chord(square);
|
|
||||||
if (square.getNeighborCount(it => it.isCovered) === square.getNeighborCount(it => it.hasMine))
|
if (square.getNeighborCount(it => it.isCovered) !== square.getNeighborCount(it => it.hasMine))
|
||||||
square.neighbors.filter(it => !it.hasFlag).forEach(it => field.toggleFlag(it));
|
continue;
|
||||||
});
|
|
||||||
|
for (const neighbor of square.neighbors.filter(it => !it.hasFlag))
|
||||||
|
await field.toggleFlag(neighbor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,14 +160,14 @@ export class Solver {
|
||||||
* @param field the field to solve
|
* @param field the field to solve
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private static stepNeighboringSquares(field: Field): void {
|
private static async stepNeighboringSquares(field: Field): Promise<void> {
|
||||||
const knowns = Solver.getKnowns(field);
|
const knowns = Solver.getKnowns(field);
|
||||||
knowns.forEach(known => {
|
for (const known of knowns) {
|
||||||
Solver.applySolution(
|
await Solver.applySolution(
|
||||||
field,
|
field,
|
||||||
this.matrixSolve(field, known.neighbors.filter(it => !it.isCovered).concat(known), true)
|
this.matrixSolve(field, known.neighbors.filter(it => !it.isCovered).concat(known), true)
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,11 +179,11 @@ export class Solver {
|
||||||
* @param field the field to solve
|
* @param field the field to solve
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private static stepAllSquares(field: Field): void {
|
private static async stepAllSquares(field: Field): Promise<void> {
|
||||||
if (!field.hasStarted || field.hasWon || field.hasLost) return;
|
if (!field.hasStarted || field.hasWon || field.hasLost) return;
|
||||||
|
|
||||||
const knowns = Solver.getKnowns(field);
|
const knowns = Solver.getKnowns(field);
|
||||||
Solver.applySolution(
|
await Solver.applySolution(
|
||||||
field,
|
field,
|
||||||
this.matrixSolve(field, knowns, false)
|
this.matrixSolve(field, knowns, false)
|
||||||
);
|
);
|
||||||
|
@ -245,14 +250,14 @@ export class Solver {
|
||||||
* @param solution the solution to apply
|
* @param solution the solution to apply
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private static applySolution(field: Field, solution: Solution): void {
|
private static async applySolution(field: Field, solution: Solution): Promise<void> {
|
||||||
solution.forEach(target => {
|
for (const target of solution) {
|
||||||
if (target === undefined) return;
|
if (target === undefined) continue;
|
||||||
|
|
||||||
const [solution, square] = target;
|
const [solution, square] = target;
|
||||||
if (solution === 0) field.uncover(square.coords);
|
if (solution === 0) await field.uncover(square.coords);
|
||||||
else if (solution === 1) field.toggleFlag(square.coords);
|
else if (solution === 1) await field.toggleFlag(square.coords);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,9 +69,9 @@ export class Timer {
|
||||||
*
|
*
|
||||||
* @param callback the callback to time the execution of
|
* @param callback the callback to time the execution of
|
||||||
*/
|
*/
|
||||||
time(callback: () => void): void {
|
async time(callback: () => Promise<void>): Promise<void> {
|
||||||
this.start();
|
this.start();
|
||||||
callback();
|
await callback();
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,9 +82,9 @@ export class Timer {
|
||||||
* @param callback the function to time the execution of
|
* @param callback the function to time the execution of
|
||||||
* @returns the number of milliseconds the callback took to execute
|
* @returns the number of milliseconds the callback took to execute
|
||||||
*/
|
*/
|
||||||
static time(callback: () => void): number {
|
static async time(callback: () => Promise<void>): Promise<number> {
|
||||||
const timer = new Timer();
|
const timer = new Timer();
|
||||||
timer.time(callback);
|
await timer.time(callback);
|
||||||
return timer.elapsedTime;
|
return timer.elapsedTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue