minesweeper/src/main/js/Game.ts

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
);
}
}