Use bigger canvas, scale appropriately

This commit is contained in:
Florine W. Dekker 2022-11-26 17:45:22 +01:00
parent 85d71faa3f
commit 6e2a5ba747
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
5 changed files with 28 additions and 40 deletions

View File

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

View File

@ -27,7 +27,8 @@
}
#canvas {
border: 4mm ridge #bdbdbd;
border: 0.8em ridge #bdbdbd;
max-width: 100%;
}

View File

@ -100,7 +100,7 @@
<label for="settings-mines">Mines</label>
<input type="number" id="settings-mines" min="0" value="10" />
<input type="checkbox" id="settings-solvable" />
<input role="switch" type="checkbox" id="settings-solvable" />
<label for="settings-solvable">Ensure solvability. <small>(Slow for complex settings.)</small></label>
</form>
<footer>

View File

@ -13,8 +13,7 @@ export class Display {
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 minSquareWidth: number = 6;
private readonly scale: number = 60;
private readonly canvas: HTMLCanvasElement;
private readonly preferences: Preferences;
@ -27,15 +26,16 @@ export class Display {
mouseHoldUncover: boolean = false;
mouseHoldChord: boolean = false;
private coverSymbol: HTMLCanvasElement | undefined;
private flagSymbol: HTMLCanvasElement | undefined;
private markSymbol: HTMLCanvasElement | undefined;
private uncoveredMineSymbol: HTMLCanvasElement | undefined;
private mineSymbol: HTMLCanvasElement | undefined;
private digitSymbols: HTMLCanvasElement[] | undefined;
private clockSymbol: HTMLCanvasElement | undefined;
private deathsSymbolA: HTMLCanvasElement | undefined;
private deathsSymbolB: HTMLCanvasElement | undefined;
private coverSymbol?: HTMLCanvasElement;
private flagSymbol?: HTMLCanvasElement;
private markSymbol?: HTMLCanvasElement;
private uncoveredMineSymbol?: HTMLCanvasElement;
private mineSymbol?: HTMLCanvasElement;
// noinspection JSMismatchedCollectionQueryUpdate // False positive
private digitSymbols?: HTMLCanvasElement[];
private clockSymbol?: HTMLCanvasElement;
private deathsSymbolA?: HTMLCanvasElement;
private deathsSymbolB?: HTMLCanvasElement;
/**
@ -153,18 +153,6 @@ export class Display {
}
/**
* Returns the offset of the field with respect to the borders.
*
* @returns the offset of the field with respect to the borders.
* @private
*/
private getFieldOffset(): { x: number, y: number } {
if (this.field == null) return {x: 0, y: 0};
if (this.field.width >= this.minSquareWidth) return {x: 0, y: 0};
return {x: Math.floor((this.minSquareWidth - this.field.width) * this.scale / 2), y: 0};
}
/**
* Changes the field to draw.
*
@ -175,7 +163,7 @@ export class Display {
this.field = field;
if (this.field == null) return;
this.canvas.width = Math.max(this.minSquareWidth, this.field.width) * this.scale;
this.canvas.width = this.field.width * this.scale;
this.canvas.height = this.field.height * this.scale + this.scale;
this.initSymbols();
}
@ -190,10 +178,11 @@ export class Display {
*/
posToSquare(pos: { x: number, y: number }): { x: number, y: number } {
const rect = this.canvas.getBoundingClientRect();
const offset = this.getFieldOffset();
const scaling = (rect.width - 2 * this.canvas.clientLeft) / (this.field!.width * this.scale);
return {
x: Math.floor((pos.x - rect.left - this.canvas.clientLeft - offset.x) / this.scale),
y: Math.floor((pos.y - rect.top - this.canvas.clientTop - offset.y) / this.scale)
x: Math.floor((pos.x - rect.left - this.canvas.clientLeft) / (this.scale * scaling)),
y: Math.floor((pos.y - rect.top - this.canvas.clientTop) / (this.scale * scaling))
};
}
@ -214,13 +203,11 @@ export class Display {
*/
draw(): void {
const ctx = this.canvas.getContext("2d")!;
const {x, y} = this.getFieldOffset();
this.clearCanvas(ctx);
if (this.field == null) return;
ctx.save();
ctx.translate(x, y);
this.drawGrid(ctx);
this.drawCovers(ctx);
this.drawHints(ctx);
@ -255,6 +242,7 @@ export class Display {
ctx.save();
ctx.beginPath();
ctx.lineWidth = this.scale / 30;
ctx.strokeStyle = "#7b7b7b";
for (let x = 0; x <= this.field.width; x++) {
ctx.moveTo(x * this.scale, 0);
@ -449,7 +437,7 @@ export class Display {
}
/**
* Draws confetti once once the player wins.
* Draws confetti over the field, for when the player wins, and stops the timer.
*
* @private
*/

View File

@ -14,14 +14,14 @@ export class Solver {
* @param field the field to solve
*/
static solve(field: Field): void {
if (field.hasWon || field.hasLost) return;
if (field.isOver) return;
if (field.hasStarted && !this.step(field.copy())) return;
if (!field.hasStarted) {
field.isAutoSolving = true;
field.runUndoably(() => {
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)!;
if (targetSquare.hasFlag) field.toggleFlag(target);
if (targetSquare.hasMark) field.toggleMark(target);
field.uncover(target);
@ -66,7 +66,7 @@ export class Solver {
* @returns a suggestion for a next move based on the current state of the field
*/
static getHint(field: Field): Square | null {
if (!field.hasStarted || field.hasWon || field.hasLost) return null;
if (!field.hasStarted || field.isOver) return null;
const knowns = Solver.getKnowns(field);
const candidate = knowns.find(square =>
@ -96,7 +96,7 @@ export class Solver {
/**
* Solves in one step through the field.
* Solves one step of the field.
*
* @param field the field to solve one step in
* @returns `true` if a step could be solved
@ -148,7 +148,7 @@ export class Solver {
* time.
*
* This function is slower than `#stepSingleSquares` but finds some more advanced moves by effectively considering
* two squares at time. Meanwhile, this function does not look at the bigger picture so it cannot infer some more
* two squares at time. Meanwhile, this function does not look at the bigger picture, so it cannot infer some more
* complicated moves. On the other hand, for some reason this function finds some edge cases that `#stepAllSquares`
* overlooks.
*
@ -156,8 +156,7 @@ export class Solver {
* @private
*/
private static stepNeighboringSquares(field: Field): void {
const knowns = Solver.getKnowns(field);
knowns.forEach(known => {
Solver.getKnowns(field).forEach(known => {
Solver.applySolution(
field,
this.matrixSolve(field, known.neighbors.filter(it => !it.isCovered).concat(known), true)