Add more preferences for automatic hints

This commit is contained in:
Florine W. Dekker 2022-11-26 16:47:52 +01:00
parent b60c7609cf
commit 85d71faa3f
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
5 changed files with 63 additions and 26 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "minesweeper", "name": "minesweeper",
"version": "0.84.0", "version": "0.85.0",
"description": "Just Minesweeper!", "description": "Just Minesweeper!",
"author": "Florine W. Dekker", "author": "Florine W. Dekker",
"browser": "dist/bundle.js", "browser": "dist/bundle.js",

View File

@ -138,11 +138,12 @@
<header> <header>
<hgroup> <hgroup>
<h1>Preferences</h1> <h1>Preferences</h1>
<h2> <h2>Configure the gameplay to your liking.</h2>
Configure the gameplay to your liking.
</h2>
</hgroup> </hgroup>
</header> </header>
<article>
Options marked with * make the game considerably easier, and are not recommended except for learning.
</article>
<form id="preferences-form"> <form id="preferences-form">
<input role="switch" type="checkbox" id="preferences-enable-marks" autofocus /> <input role="switch" type="checkbox" id="preferences-enable-marks" autofocus />
<label for="preferences-enable-marks">Right-clicking a square twice places a question mark.</label> <label for="preferences-enable-marks">Right-clicking a square twice places a question mark.</label>
@ -150,12 +151,21 @@
<input role="switch" type="checkbox" id="preferences-show-too-many-flags-hints" /> <input role="switch" type="checkbox" id="preferences-show-too-many-flags-hints" />
<label for="preferences-show-too-many-flags-hints"> <label for="preferences-show-too-many-flags-hints">
Highlight squares with too many flags around them in red. Highlight squares in red if there are too many adjacent flags.
</label> </label>
<br /><br /> <br /><br />
<input role="switch" type="checkbox" id="preferences-show-chordable-hints" /> <input role="switch" type="checkbox" id="preferences-show-chordable-hints" />
<label for="preferences-show-chordable-hints">Highlight squares that can be chorded in blue.</label> <label for="preferences-show-chordable-hints">
*Highlight squares in green if they can be
<a href="https://www.minesweeper.info/wiki/Chord">chorded</a>.
</label>
<br /><br />
<input role="switch" type="checkbox" id="preferences-show-all-neighbors-are-mines-hints" />
<label for="preferences-show-all-neighbors-are-mines-hints">
*Highlight squares in green if all neighbors have mines.
</label>
</form> </form>
<footer> <footer>
<a role="button" href="#" id="preferences-cancel" class="secondary">Cancel</a> <a role="button" href="#" id="preferences-cancel" class="secondary">Cancel</a>

View File

@ -9,6 +9,10 @@ import {Preferences} from "./Preferences";
* Displays a Minesweeper field. * Displays a Minesweeper field.
*/ */
export class Display { export class Display {
private readonly errorColor: string = "rgba(255, 0, 0, 0.3)";
private readonly hintColor: string = "rgba(0, 0, 255, 0.3)";
private readonly safeColor: string = "rgba(0, 255, 0, 0.5)";
private readonly scale: number = 30; private readonly scale: number = 30;
private readonly minSquareWidth: number = 6; private readonly minSquareWidth: number = 6;
@ -301,19 +305,35 @@ export class Display {
private drawHints(ctx: CanvasRenderingContext2D): void { private drawHints(ctx: CanvasRenderingContext2D): void {
if (this.field == null) return; if (this.field == null) return;
if (this.preferences.showTooManyFlagsHints) { let madeMistakes = false;
let showsHint = false;
if (this.hintSquare != null) {
ctx.save(); ctx.save();
ctx.fillStyle = "rgba(255, 0, 0, 0.3)"; ctx.fillStyle = this.hintColor;
this.field.squareList ctx.fillRect(this.hintSquare.x * this.scale, this.hintSquare.y * this.scale, this.scale, this.scale);
ctx.restore();
showsHint = true;
}
if (!showsHint && this.preferences.showTooManyFlagsHints) {
ctx.save();
ctx.fillStyle = this.errorColor;
madeMistakes = madeMistakes || this.field.squareList
.filter(it => !it.isCovered) .filter(it => !it.isCovered)
.filter(it => it.getNeighborCount(it => it.hasMine) < it.getNeighborCount(it => it.hasFlag)) .filter(it => it.getNeighborCount(it => it.hasMine) < it.getNeighborCount(it => it.hasFlag))
.forEach(square => ctx.fillRect(square.x * this.scale, square.y * this.scale, this.scale, this.scale)); .map(square => ctx.fillRect(square.x * this.scale, square.y * this.scale, this.scale, this.scale))
.length > 0;
ctx.restore(); ctx.restore();
} }
if (this.preferences.showChordableHints) { if (
!showsHint && !madeMistakes &&
(this.preferences.showChordableHints || this.preferences.showAllNeighborsAreMinesHints)
) {
ctx.save(); ctx.save();
ctx.fillStyle = "rgba(0, 0, 255, 0.3)"; ctx.fillStyle = this.safeColor;
this.field.squareList this.field.squareList
.filter(it => !it.isCovered) .filter(it => !it.isCovered)
.filter(it => { .filter(it => {
@ -321,18 +341,14 @@ export class Display {
const flags = it.getNeighborCount(it => it.hasFlag); const flags = it.getNeighborCount(it => it.hasFlag);
const covered = it.getNeighborCount(it => it.isCovered); const covered = it.getNeighborCount(it => it.isCovered);
return mines === flags && covered !== flags; return (
(this.preferences.showChordableHints && mines === flags && covered !== flags) ||
(this.preferences.showAllNeighborsAreMinesHints && mines === covered && mines !== flags)
);
}) })
.forEach(square => ctx.fillRect(square.x * this.scale, square.y * this.scale, this.scale, this.scale)); .forEach(square => ctx.fillRect(square.x * this.scale, square.y * this.scale, this.scale, this.scale));
ctx.restore(); ctx.restore();
} }
if (this.hintSquare != null) {
ctx.save();
ctx.fillStyle = "rgba(0, 255, 0, 0.3)";
ctx.fillRect(this.hintSquare.x * this.scale, this.hintSquare.y * this.scale, this.scale, this.scale);
ctx.restore();
}
} }
/** /**

View File

@ -181,24 +181,27 @@ export class Game {
); );
// Preferences // Preferences
const enableMarksInput = $("#preferences-enable-marks"); const enableMarks = $("#preferences-enable-marks");
const showTooManyFlagsHintsInput = $("#preferences-show-too-many-flags-hints"); const showTooManyFlagsHints = $("#preferences-show-too-many-flags-hints");
const showAllNeighborsAreMinesHints = $("#preferences-show-all-neighbors-are-mines-hints");
const showChordableHints = $("#preferences-show-chordable-hints"); const showChordableHints = $("#preferences-show-chordable-hints");
const preferencesDialog = new ModalDialog({ const preferencesDialog = new ModalDialog({
dialog: $("#preferences-dialog"), dialog: $("#preferences-dialog"),
openButton: $("#preferences-open"), openButton: $("#preferences-open"),
onOpen: () => { onOpen: () => {
enableMarksInput.checked = preferences.marksEnabled; enableMarks.checked = preferences.marksEnabled;
showTooManyFlagsHintsInput.checked = preferences.showTooManyFlagsHints; showTooManyFlagsHints.checked = preferences.showTooManyFlagsHints;
showChordableHints.checked = preferences.showChordableHints; showChordableHints.checked = preferences.showChordableHints;
showAllNeighborsAreMinesHints.checked = preferences.showAllNeighborsAreMinesHints;
}, },
form: $("#preferences-form"), form: $("#preferences-form"),
closeButton: $("#preferences-cancel"), closeButton: $("#preferences-cancel"),
submitButton: $("#preferences-submit"), submitButton: $("#preferences-submit"),
onSubmit: () => { onSubmit: () => {
preferences.marksEnabled = enableMarksInput.checked; preferences.marksEnabled = enableMarks.checked;
preferences.showTooManyFlagsHints = showTooManyFlagsHintsInput.checked; preferences.showTooManyFlagsHints = showTooManyFlagsHints.checked;
preferences.showChordableHints = showChordableHints.checked; preferences.showChordableHints = showChordableHints.checked;
preferences.showAllNeighborsAreMinesHints = showAllNeighborsAreMinesHints.checked;
preferencesDialog.close(); preferencesDialog.close();
} }

View File

@ -43,6 +43,14 @@ export class Preferences {
this.storage.setBoolean("showChordableHints", value); this.storage.setBoolean("showChordableHints", value);
} }
get showAllNeighborsAreMinesHints(): boolean {
return this.storage.getBoolean("showAllNeighborsAreMinesHints", false);
}
set showAllNeighborsAreMinesHints(value: boolean) {
this.storage.setBoolean("showAllNeighborsAreMinesHints", value);
}
get showTooManyFlagsHints(): boolean { get showTooManyFlagsHints(): boolean {
return this.storage.getBoolean("showTooManyFlagsHints", true); return this.storage.getBoolean("showTooManyFlagsHints", true);
} }