Use bigger canvas, scale appropriately
This commit is contained in:
parent
85d71faa3f
commit
6e2a5ba747
|
@ -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",
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
}
|
||||
|
||||
#canvas {
|
||||
border: 4mm ridge #bdbdbd;
|
||||
border: 0.8em ridge #bdbdbd;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue