From 65da82257b15f77d16d15e09c73630c9f56bc9e6 Mon Sep 17 00:00:00 2001 From: "Felix W. Dekker" Date: Sat, 25 Jul 2020 20:35:59 +0200 Subject: [PATCH] Set up deployment with npm --- .editorconfig | 11 + .gitattributes | 1 + .gitignore | 117 ++++++++++ Gruntfile.js | 121 ++++++++++ LICENSE | 21 ++ README.md | 22 ++ index.html | 523 ------------------------------------------- package-lock.json | Bin 0 -> 206594 bytes package.json | 33 +++ src/main/index.html | 66 ++++++ src/main/js/index.js | 503 +++++++++++++++++++++++++++++++++++++++++ 11 files changed, 895 insertions(+), 523 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Gruntfile.js create mode 100644 LICENSE create mode 100644 README.md delete mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/main/index.html create mode 100644 src/main/js/index.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fd1294f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +charset = utf-8 +trim_trailing_whitespace = true + +end_of_line = lf +insert_final_newline = true + +indent_style = space +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1a6bd45 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +package-lock.json binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7ea1e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,117 @@ +## Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.pnp.* diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..e404c27 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,121 @@ +const path = require("path"); + +module.exports = grunt => { + grunt.initConfig({ + pkg: grunt.file.readJSON("package.json"), + clean: { + default: ["dist/"], + }, + copy: { + css: { + files: [{expand: true, cwd: "src/main/", src: "**/*.css", dest: "dist/"}] + }, + html: { + files: [{expand: true, cwd: "src/main/", src: "**/*.html", dest: "dist/"}] + }, + }, + focus: { + dev: { + include: ["css", "html", "js", "link"], + }, + }, + replace: { + dev: { + src: ["./dist/*.html", "./dist/*.js"], + replacements: [ + { + from: "%%VERSION_NUMBER%%", + to: "<%= pkg.version %>+" + new Date().toISOString().slice(0, 19).replace(/[-:T]/g, "") + } + ], + overwrite: true + }, + deploy: { + src: ["./dist/*.html", "./dist/*.js"], + replacements: [ + { + from: "%%VERSION_NUMBER%%", + to: "<%= pkg.version %>" + } + ], + overwrite: true + }, + }, + watch: { + css: { + files: ["src/main/**/*.css"], + tasks: ["copy:css"], + }, + html: { + files: ["src/main/**/*.html"], + tasks: ["copy:html"], + }, + js: { + files: ["src/main/**/*.js"], + tasks: ["webpack:dev", "replace:dev"], + }, + link: { + files: ["node_modules/@fwdekker/*/dist/**"], + tasks: ["webpack:dev", "replace:dev"], + }, + }, + webpack: { + options: { + entry: "./src/main/js/index.js", + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: [".js"], + }, + output: { + filename: "bundle.js", + path: path.resolve(__dirname, "dist/"), + }, + }, + dev: { + mode: "development", + devtool: "inline-source-map", + }, + deploy: { + mode: "production", + }, + }, + }); + + grunt.loadNpmTasks("grunt-contrib-clean"); + grunt.loadNpmTasks("grunt-contrib-copy"); + grunt.loadNpmTasks("grunt-contrib-watch"); + grunt.loadNpmTasks("grunt-focus"); + grunt.loadNpmTasks("grunt-text-replace"); + grunt.loadNpmTasks("grunt-webpack"); + + grunt.registerTask("dev", [ + // Pre + "clean", + // Copy files + "copy:css", + "copy:html", + // Compile JS + "webpack:dev", + "replace:dev", + ]); + grunt.registerTask("dev:server", ["dev", "focus:dev"]); + grunt.registerTask("deploy", [ + // Pre + "clean", + // Copy files + "copy:css", + "copy:html", + // Compile JS + "webpack:deploy", + "replace:deploy", + ]); + + grunt.registerTask("default", ["dev"]); +}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e172412 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Felix W. Dekker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c6577c2 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Minesweeper +An implementation of Minesweeper. + +## Development +### Requirements +* [npm](https://www.npmjs.com/) + +### Setting up +```shell script +# Install dependencies (only needed once) +$> npm ci +``` + +### Building +```shell script +# Build the tool in `dist/` for development +$> npm run dev +# Same as above, but automatically rerun it whenever files are changed +$> npm run dev:server +# Build the tool in `dist/` for deployment +$> npm run deploy +``` diff --git a/index.html b/index.html deleted file mode 100644 index 8616b45..0000000 --- a/index.html +++ /dev/null @@ -1,523 +0,0 @@ - - - - - - - - - - - Swinemeeper - - - -0 -
- -
- - - - - - - - - - - - - -
- - - - - - - - diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..a8ba45a897dc3191595c7cf843c84d628d5ded49 GIT binary patch literal 206594 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 kJ2fWpL+D<4Qu{CxQR{e<>)$y8r+H literal 0 HcmV?d00001 diff --git a/package.json b/package.json new file mode 100644 index 0000000..9a6927f --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "minesweeper", + "version": "0.0.1", + "description": "Just Minesweeper!", + "author": "Felix W. Dekker", + "browser": "dist/bundle.js", + "repository": { + "type": "git", + "url": "git@git.fwdekker.com:FWDekker/minesweeper.git" + }, + "private": true, + "scripts": { + "clean": "grunt clean", + "dev": "grunt dev", + "dev:server": "grunt dev:server", + "deploy": "grunt deploy" + }, + "dependencies": { + "@fwdekker/template": "^0.0.18" + }, + "devDependencies": { + "grunt": "^1.2.1", + "grunt-cli": "^1.3.2", + "grunt-contrib-clean": "^2.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-watch": "^1.1.0", + "grunt-focus": "^1.0.0", + "grunt-text-replace": "^0.4.0", + "grunt-webpack": "^4.0.2", + "webpack": "^4.44.0", + "webpack-cli": "^3.3.12" + } +} diff --git a/src/main/index.html b/src/main/index.html new file mode 100644 index 0000000..f9a70e8 --- /dev/null +++ b/src/main/index.html @@ -0,0 +1,66 @@ + + + + + + + + + + + Swinemeeper + + + + +
+ +
+ + +
+ + + 0 +
+ + +
+ + + + + + + + + + + + + +
+ + + + +
+
+ +
+ + + + + + + diff --git a/src/main/js/index.js b/src/main/js/index.js new file mode 100644 index 0000000..f307782 --- /dev/null +++ b/src/main/js/index.js @@ -0,0 +1,503 @@ +import {$, doAfterLoad, footer, header, nav} from "@fwdekker/template"; + + +const logArea = document.getElementById("logArea"); +const log = (message) => { + logArea.value += `${message}\n`; + logArea.scrollTop = logArea.scrollHeight; +} + + +/** + * Controls the interaction with a game of Minesweeper. + */ +class Game { + /** + * Constructs and starts a new game of Minesweeper. + */ + constructor() { + this.canvas = document.getElementById("canvas"); + this.settingsForm = document.getElementById("settingsForm"); + this.widthInput = document.getElementById("settingsWidth"); + this.heightInput = document.getElementById("settingsHeight"); + this.minesInput = document.getElementById("settingsMines"); + this.seedInput = document.getElementById("settingsSeed"); + + this.reset(); + this.display = new Display(this.canvas, this.field); + this.display.startDrawLoop(); + + + this.settingsForm.addEventListener( + "submit", + event => { + event.preventDefault(); + this.reset(); + this.display.field = this.field; + } + ); + this.canvas.addEventListener( + "mousemove", + event => this.display.mouseCell = this.display.posToCell({x: event.clientX, y: event.clientY}) + ); + this.canvas.addEventListener( + "contextmenu", + event => event.preventDefault() + ); + this.canvas.addEventListener( + "mouseup", + event => { + event.preventDefault(); + if (!this.isAlive) return; + + const cell = this.display.posToCell({x: event.clientX, y: event.clientY}); + switch (event.button) { + case 0: + if (!cell.hasFlag) { + if (!this.hasClicked) { + cell.firstUncover(); + log("First uncover complete."); + } else + cell.uncover(); + + this.hasClicked = true; + if (cell.hasMine) { + this.isAlive = false; + log("You died!"); + } + } + break; + case 1: + cell.chord(); + break; + case 2: + cell.flag(); + break; + } + + if (this.field.isCleared()) log("Level complete!"); + } + ) + } + + + /** + * Resets the game, re-generating the field according to the current settings. + */ + reset() { + this.field = new Field( + +this.widthInput.value, + +this.heightInput.value, + +this.minesInput.value, + +this.seedInput.value + ); + this.isAlive = true; + this.hasClicked = false; + + log("Let's go!"); + } +} + +/** + * Displays a Minesweeper field. + */ +class Display { + /** + * Constructs a new display. + * + * @param canvas {HTMLCanvasElement} the canvas to draw the field in + * @param field {Field} the field to draw + */ + constructor(canvas, field) { + // TODO Remove this \/ + this.frameNumber = 0; + this.counter = document.getElementById("counter"); + window.setInterval(() => { + this.counter.innerText = "" + (this.frameNumber * 4); + this.frameNumber = 0; + }, 250); + // TODO Remove this /\ + + this.canvas = canvas; + this.field = field; + + this.mouseCell = undefined; + } + + + /** + * Calculates the scale, which is defined as the width and height of each (square) cell in pixels. + * + * @return the scale of the display + */ + calcScale() { + const rect = this.canvas.getBoundingClientRect(); + return Math.min(rect.width / this.field.width, rect.height / this.field.height); + } + + /** + * Returns the cell at the given coordinates, or `undefined` if there is no cell there. + * + * @param pos {{x: number, y: number}} the client-relative pixel coordinates to find the cell at + * @return {Cell} the cell at the given coordinates + */ + posToCell(pos) { + const rect = this.canvas.getBoundingClientRect(); + const scale = this.calcScale(); + + return this.field.getCellOrElse( + Math.floor((pos.x - rect.left) / scale), + Math.floor((pos.y - rect.top) / scale) + ); + } + + + /** + * Invokes `#draw` in every animation frame of this window. + */ + startDrawLoop() { + const cb = () => { + this.draw(); + this.frameNumber++; // TODO Remove this + window.requestAnimationFrame(cb); + }; + window.requestAnimationFrame(cb); + } + + /** + * Draws the field. + */ + draw() { + const ctx = this.canvas.getContext("2d", {alpha: false}); + const rect = this.canvas.getBoundingClientRect(); + const scale = this.calcScale(); + + // Clear + ctx.save(); + ctx.fillStyle = "#FFF"; + ctx.fillRect(0, 0, rect.width, rect.height); + ctx.restore(); + + // Cover cells + ctx.save(); + ctx.fillStyle = "#555"; + for (let x = 0; x < this.field.width; x++) { + for (let y = 0; y < this.field.height; y++) { + const cell = this.field.getCell(x, y); + if (cell.isCovered) { + ctx.fillRect(x * scale, y * scale, scale, scale); + } + } + } + ctx.restore(); + + // Fill cells + ctx.save(); + ctx.fillStyle = "#000"; + ctx.font = "30px serif"; + ctx.textBaseline = "middle"; + ctx.textAlign = "center"; + for (let x = 0; x < this.field.width; x++) { + for (let y = 0; y < this.field.height; y++) { + const cell = this.field.getCell(x, y); + + const neighborMineCount = cell.getNeighborMineCount(); + let contents; + if (cell.isCovered) { + if (cell.hasFlag) + contents = "⚑"; + else + contents = ""; + } else { + if (cell.hasMine) + contents = "💣"; + else if (neighborMineCount === 0) + contents = ""; + else + contents = "" + neighborMineCount; + } + ctx.fillText(contents, (x + 0.5) * scale, (y + 0.5) * scale, scale); + } + } + ctx.restore(); + + // Create grid + ctx.save(); + ctx.strokeStyle = "#000"; + ctx.beginPath(); + for (let x = 0; x <= this.field.width; x++) { + ctx.moveTo(x * scale, 0); + ctx.lineTo(x * scale, this.field.height * scale); + } + for (let y = 0; y <= this.field.height; y++) { + ctx.moveTo(0, y * scale); + ctx.lineTo(this.field.width * scale, y * scale); + } + ctx.stroke(); + ctx.restore(); + + // Highlight mouse cell + if (this.mouseCell !== undefined) { + ctx.save(); + ctx.strokeStyle = "#F00"; + ctx.strokeRect(this.mouseCell.x * scale, this.mouseCell.y * scale, scale, scale); + ctx.restore(); + } + + // Done + } +} + +/** + * A playing field for a game of Minesweeper. + */ +class Field { + /** + * Constructs a new playing field for a game of Minesweeper. + * + * @param width {number} the number of cells per row in the field + * @param height {number} the number of rows in the field + * @param mineCount {number} the initial number of mines to place in the field + * @param seed {number|undefined} the seed to generate the field with + */ + constructor(width, height, mineCount, seed = undefined) { + this.width = width; + this.height = height; + + const mines = Array(width * height).fill(true, 0, mineCount).fill(false, mineCount); + shuffleArrayInPlace(mines, seed); + + this.cells = chunkifyArray( + mines.map((hasMine, i) => new Cell(this, Math.floor(i / this.width), i % this.width, hasMine)), + this.width + ); + } + + + /** + * Returns the cell at the given coordinates, or throws an error if there is no cell there. + * + * @param x {number} the horizontal coordinate of the cell to look up + * @param y {number} the vertical coordinate of the cell to look up + * @return {Cell} the cell at the given coordinates + */ + getCell(x, y) { + if (x < 0 || x >= this.width) throw new Error(`x must be in range [0, ${this.width}), but was ${x}.`); + if (y < 0 || y >= this.height) throw new Error(`y must be in range [0, ${this.height}), but was ${y}.`); + + return this.cells[x][y]; + } + + /** + * Returns the cell at the given coordinates, or `orElse` if there is no cell there. + * + * @param x {number} the horizontal coordinate of the cell to look up + * @param y {number} the vertical coordinate of the cell to look up + * @param orElse {*} the value to return if there is no cell at the given coordinates + * @return {Cell|*} the cell at the given coordinates, or `orElse` if there is no cell there + */ + getCellOrElse(x, y, orElse = undefined) { + const row = this.cells[x]; + return row === undefined ? orElse : row[y]; + } + + /** + * Returns `true` if and only if all mineless cells have been uncovered. + * + * @return `true` if and only if all mineless cells have been uncovered + */ + isCleared() { + for (let x = 0; x < this.width; x++) { + for (let y = 0; y < this.height; y++) { + const cell = this.getCell(x, y); + if (cell.isCovered && !cell.hasMine) + return false; + } + } + + return true; + } +} + +/** + * A cell in a Minesweeper `Field`. + */ +class Cell { + /** + * Constructs a new cell. + * + * @param field {Field} the field in which this cell is located + * @param x {number} the horizontal coordinate of this cell in the field + * @param y {number} the vertical coordinate of this cell in the field + * @param hasMine {boolean} `true` if and only if this cell contains a mine + */ + constructor(field, x, y, hasMine) { + this.field = field; + this.x = x; + this.y = y; + + this.isCovered = true; + this.hasMine = hasMine; + this.hasFlag = false; + } + + + /** + * Returns the `Cell`s that are adjacent to this cell. + * + * @return {Cell[]} the `Cell`s that are adjacent to this cell + */ + getNeighbors() { + return [ + this.field.getCellOrElse(this.x - 1, this.y - 1), + this.field.getCellOrElse(this.x, this.y - 1), + this.field.getCellOrElse(this.x + 1, this.y - 1), + this.field.getCellOrElse(this.x - 1, this.y), + this.field.getCellOrElse(this.x + 1, this.y), + this.field.getCellOrElse(this.x - 1, this.y + 1), + this.field.getCellOrElse(this.x, this.y + 1), + this.field.getCellOrElse(this.x + 1, this.y + 1), + ].filter(it => it !== undefined); + } + + /** + * Returns the number of neighbors that have a flag. + * + * @returns {number} the number of neighbors that have a flag + */ + getNeighborFlagCount() { + return this.getNeighbors().filter(it => it.hasFlag).length; + } + + /** + * Returns the number of neighbors that have a mine. + * + * @returns {number} the number of neighbors that have a mine + */ + getNeighborMineCount() { + return this.getNeighbors().filter(it => it.hasMine).length; + } + + + /** + * Chords this cell, i.e. if this cell is covered and the number of neighboring flags equals the number in this + * cell, then all unflagged neighbors are uncovered. + */ + chord() { + if (this.isCovered) return; + if (this.getNeighborMineCount() !== this.getNeighborFlagCount()) return; + + this.getNeighbors() + .filter(it => it.isCovered && !it.hasFlag) + .forEach(it => it.uncover()); + } + + /** + * Uncovers this cell as in `#uncover`, but adjacent 0-mine cells are also uncovered and if this cell contains a + * mine the mine is moved to the first cell without a mine, starting from the top-left moving in a horizontal + * scanning fashion. + */ + firstUncover() { + if (this.hasMine) { + this.hasMine = false; + + for (let y = 0; y < this.field.height; y++) { + for (let x = 0; x < this.field.width; x++) { + if (x === this.x && y === this.y) continue; + + const cell = this.field.getCell(x, y); + if (!cell.hasMine) { + cell.hasMine = true; + break; + } + } + } + } + + this.getNeighbors() + .filter(it => it.getNeighborMineCount() === 0 && !it.hasMine && !it.hasFlag) + .forEach(it => it.uncover()); + } + + /** + * Adds or removes a flag at this cell. + */ + flag() { + if (!this.isCovered) return; + + this.hasFlag = !this.hasFlag; + } + + /** + * Uncovers this cell, revealing the contents beneath. + */ + uncover() { + if (!this.isCovered) return; + + this.isCovered = false; + this.hasFlag = false; + if (!this.hasMine && this.getNeighborMineCount() === 0) + this.chord(); + } +} + + +/** + * Shuffles the given array in-place. + * + * @param array {*[]} the array to shuffle + * @param seed {number|undefined} the seed for the random number generator + * @returns {*[]} the array that was given to this function to shuffle + */ +function shuffleArrayInPlace(array, seed = undefined) { + const engine = Random.engines.mt19937(); + engine.autoSeed(); + if (seed !== undefined) engine.seed(seed); + return new Random(engine).shuffle(array); +} + +/** + * Slices `array` into chunks of `chunkSize` elements each. + * + * If `array` does not contain a multiple of `chunkSize` elements, the last chunk will contain fewer elements. + * + * @param array {*[]} the array to chunkify + * @param chunkSize {number} the size of each chunk + * @returns {*[]} an array of the extracted chunks + */ +function chunkifyArray(array, chunkSize) { + const chunks = []; + for (let i = 0; i < array.length; i += chunkSize) + chunks.push(array.slice(i, i + chunkSize)); + return chunks; +} + + +doAfterLoad(() => { + // Initialize template + $("#nav").appendChild(nav("/Tools/Minesweeper/")); + $("#header").appendChild(header({ + title: "Minesweeper", + description: "Just Minesweeper!" + })); + $("#footer").appendChild(footer({ + author: "Felix W. Dekker", + authorURL: "https://fwdekker.com/", + license: "MIT License", + licenseURL: "https://git.fwdekker.com/FWDekker/minesweeper/src/branch/master/LICENSE", + vcs: "git", + vcsURL: "https://git.fwdekker.com/FWDekker/minesweeper/", + version: "v%%VERSION_NUMBER%%" + })); + $("main").style.display = null; + + + // Initialize game + const urlParams = new URLSearchParams(window.location.search); + document.getElementById("settingsSeed").value = + urlParams.get("seed") === null + ? "" + Math.floor(Math.random() * 1000000000000) + : urlParams.get("seed"); + + new Game(); +});