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