173 lines
5.5 KiB
TypeScript
173 lines
5.5 KiB
TypeScript
// @ts-ignore
|
|
import {$} from "@fwdekker/template";
|
|
import {Display} from "./Display";
|
|
import {Field} from "./Field";
|
|
import {Solver} from "./Solver";
|
|
|
|
|
|
/**
|
|
* Controls the interaction with a game of Minesweeper.
|
|
*/
|
|
export class Game {
|
|
private readonly canvas: HTMLCanvasElement;
|
|
private readonly solveForm: HTMLFormElement;
|
|
private readonly controlForm: HTMLFormElement;
|
|
private readonly displayScale: HTMLInputElement;
|
|
private readonly settingsForm: HTMLFormElement;
|
|
private readonly widthInput: HTMLInputElement;
|
|
private readonly heightInput: HTMLInputElement;
|
|
private readonly minesInput: HTMLInputElement;
|
|
private readonly seedInput: HTMLInputElement;
|
|
private field: Field;
|
|
private display: Display;
|
|
private leftDown: boolean;
|
|
private rightDown: boolean;
|
|
private holdsAfterChord: boolean;
|
|
|
|
|
|
/**
|
|
* Constructs and starts a new game of Minesweeper.
|
|
*/
|
|
constructor() {
|
|
this.canvas = $("#canvas");
|
|
|
|
this.solveForm = $("#solveForm");
|
|
this.controlForm = $("#controlForm");
|
|
this.displayScale = $("#displayScale");
|
|
|
|
this.settingsForm = $("#settingsForm");
|
|
this.widthInput = $("#settingsWidth");
|
|
this.heightInput = $("#settingsHeight");
|
|
this.minesInput = $("#settingsMines");
|
|
this.seedInput = $("#settingsSeed");
|
|
|
|
this.field = this.createNewField();
|
|
this.display = new Display(this.canvas, this.field);
|
|
this.display.setScale(+this.displayScale.value);
|
|
this.display.startDrawLoop();
|
|
|
|
this.leftDown = false;
|
|
this.rightDown = false;
|
|
this.holdsAfterChord = false;
|
|
|
|
|
|
this.solveForm.addEventListener(
|
|
"submit",
|
|
event => {
|
|
event.preventDefault();
|
|
new Solver().solve(this.field);
|
|
}
|
|
);
|
|
this.controlForm.addEventListener(
|
|
"submit",
|
|
event => event.preventDefault()
|
|
);
|
|
this.displayScale.addEventListener(
|
|
"change",
|
|
event => {
|
|
event.preventDefault();
|
|
this.display.setScale(+this.displayScale.value);
|
|
}
|
|
);
|
|
|
|
this.settingsForm.addEventListener(
|
|
"submit",
|
|
event => {
|
|
event.preventDefault();
|
|
if (+this.widthInput.value * +this.heightInput.value < +this.minesInput.value + 9) {
|
|
window.alert("Field must contain at least 9 empty squares.")
|
|
return;
|
|
}
|
|
|
|
this.field = this.createNewField();
|
|
this.display.field = this.field;
|
|
this.display.setScale(+this.displayScale.value);
|
|
}
|
|
);
|
|
this.canvas.addEventListener(
|
|
"mousemove",
|
|
event => this.display.mouseSquare = this.display.posToSquare({x: event.clientX, y: event.clientY})
|
|
);
|
|
this.canvas.addEventListener(
|
|
"mouseleave",
|
|
_ => {
|
|
this.display.mouseSquare = null;
|
|
this.leftDown = false;
|
|
this.rightDown = false;
|
|
this.holdsAfterChord = false;
|
|
this.display.mouseHoldChord = false;
|
|
}
|
|
);
|
|
this.canvas.addEventListener(
|
|
"contextmenu",
|
|
event => event.preventDefault()
|
|
);
|
|
this.canvas.addEventListener(
|
|
"mousedown",
|
|
event => {
|
|
event.preventDefault()
|
|
|
|
const square = this.display.posToSquare({x: event.clientX, y: event.clientY});
|
|
switch (event.button) {
|
|
case 0:
|
|
this.leftDown = true;
|
|
break;
|
|
case 2:
|
|
if (!this.leftDown && square !== null) square.flag();
|
|
|
|
this.rightDown = true;
|
|
break;
|
|
}
|
|
|
|
this.display.mouseHoldChord = this.leftDown && this.rightDown;
|
|
}
|
|
);
|
|
this.canvas.addEventListener(
|
|
"mouseup",
|
|
event => {
|
|
event.preventDefault();
|
|
|
|
const square = this.display.posToSquare({x: event.clientX, y: event.clientY});
|
|
switch (event.button) {
|
|
case 0:
|
|
if (square !== null && this.leftDown && this.rightDown)
|
|
square.chord();
|
|
else if (square !== null && !this.holdsAfterChord && this.leftDown)
|
|
square.uncover();
|
|
|
|
this.leftDown = false;
|
|
this.holdsAfterChord = this.rightDown;
|
|
break;
|
|
case 1:
|
|
if (square !== null) square.chord();
|
|
break;
|
|
case 2:
|
|
if (square !== null && this.leftDown && this.rightDown)
|
|
square.chord();
|
|
|
|
this.rightDown = false;
|
|
this.holdsAfterChord = this.leftDown;
|
|
break;
|
|
}
|
|
|
|
this.display.mouseHoldChord = this.leftDown && this.rightDown;
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new field according to the current settings.
|
|
*
|
|
* @return the newly created field
|
|
*/
|
|
createNewField(): Field {
|
|
return new Field(
|
|
+this.widthInput.value,
|
|
+this.heightInput.value,
|
|
+this.minesInput.value,
|
|
+this.seedInput.value
|
|
);
|
|
}
|
|
}
|