commit 192858094cb7330a644f51b7423e1f58a0971e37 Author: Tobias Berger Date: Tue Jun 27 18:52:22 2023 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f81d56e --- /dev/null +++ b/.gitignore @@ -0,0 +1,169 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-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 + +# Optional stylelint cache + +.stylelintcache + +# 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 variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# 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 + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# 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 +.yarn/install-state.gz +.pnp.\* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ff9e935 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e73f87 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# minesweeper-nojs + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run server.ts +``` + +This project was created using `bun init` in bun v0.4.3. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..2157572 Binary files /dev/null and b/bun.lockb differ diff --git a/justfile b/justfile new file mode 100755 index 0000000..649a2ae --- /dev/null +++ b/justfile @@ -0,0 +1,14 @@ +#!/usr/bin/just --justfile + +# Lists all recipes +list: + {{ just_executable() }} --list + +# Run a dev server anad recompile on file change +dev: + bun --port=55782 --watch ./server.ts + +# Run a production server +run: + NODE_ENV="production" bun run --port=55782 ./server.ts + diff --git a/package.json b/package.json new file mode 100644 index 0000000..2ed941b --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "name": "minesweeper-nojs", + "module": "server.ts", + "type": "module", + "devDependencies": { + "bun-types": "^0.4.0" + } +} \ No newline at end of file diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..6d9410a --- /dev/null +++ b/server.ts @@ -0,0 +1,128 @@ +const PORT = 55782; +const HEADERS_BOARD = { + headers: { + "Content-Type": "text/html", + "Cache-Control": "no-store", + }, +}; +const HEADERS_STYLESHEET = { + headers: { + "Content-Type": "text/css", + }, +}; + +async function htmlDoc(body: string, head: string): Promise { + return `${head}${body}` +} + +async function createBoard(width: number, height: number, mines: number): Promise { + console.debug("width", width, "height", height, "mines", mines); + if (width <= 0 || height <= 0 || mines <= 0) { + return await htmlDoc("NO 0 VALUES ALLOWED", ""); + } + if (width > 50 || height > 50) { + return await htmlDoc("NO FIELDS LARGER THAN 50 IN EITHER DIMENSION", ""); + } + if (mines >= width * height) { + return await htmlDoc("TOO MANY MINES", ""); + } + + const board = new Array(width * height).fill(false); + let safeIdx = board.map((_, idx) => idx); + + for (let placedMines = 0; placedMines < mines; placedMines++) { + const mineIdx = safeIdx.splice(Math.floor(Math.random() * safeIdx.length), 1)[0]; + + board[mineIdx] = true; + } + + function getNeighbors(idx: number): number { + let count = 0; + const leftEdge = (idx % width) === 0; + const topEdge = idx < width; + const bottomEdge = idx > (height * (width - 1)); + const rightEdge = (idx % width) === (width - 1); + + if (!leftEdge && board[idx - 1]) { + count++; + } + if (!leftEdge && !topEdge && board[idx - 1 - width]) { + count++; + } + if (!leftEdge && !bottomEdge && board[idx - 1 + width]) { + count++; + } + + if (!rightEdge && board[idx + 1]) { + count++; + } + if (!rightEdge && !topEdge && board[idx + 1 - width]) { + count++; + } + if (!rightEdge && !bottomEdge && board[idx + 1 + width]) { + count++; + } + + if (!topEdge && board[idx - width]) { + count++; + } + + if (!bottomEdge && board[idx + width]) { + count++; + } + return count; + } + + let table = ""; + let inputs = ""; + let style = "" + + return await htmlDoc(`${inputs}
${table}
HAHA you lost >:)YAY, you WON!!!
`, style) +} + +Bun.serve({ + PORT, + async fetch(request) { + const url = new URL(request.url); + + if (url.pathname === "/favicon.ico") { + // TODO: Add favicon + return new Response("Not found", { status: 404, statusText: "Not found" }); + } + + if (url.pathname === "/style.css") { + return new Response(await Bun.file("style.css").text(), HEADERS_STYLESHEET); + } + + if (url.pathname !== "/") { + return new Response("Not found", { status: 404, statusText: "Not found" }); + } + + let height = Number.parseInt(url.searchParams.get("height") ?? "NaN"); + if (Number.isNaN(height)) { + height = 10; + } + let width = Number.parseInt(url.searchParams.get("width") ?? "NaN"); + if (Number.isNaN(width)) { + width = 10; + } + let mines = Number.parseInt(url.searchParams.get("mines") ?? "NaN"); + if (Number.isNaN(mines)) { + mines = 10; + } + + return new Response(await createBoard(width, height, mines), HEADERS_BOARD); + } +}) \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..83f8e5c --- /dev/null +++ b/style.css @@ -0,0 +1,53 @@ +html { + height: 100%; + background: black; + color: white; + font-size: 40px; +} + +label, label::before { + display: block; + width: 50px; + height: 50px; + cursor: pointer; +} + +label::before { + text-align: center; +} + +body { + height: 100%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +main { + margin: auto; + color: white; +} + +input { display:none; label: pointer-events: none; } + +label { pointer-events: none; } + +input ~ main label::before { content: "O"; } +#won { display: initial; user-select: none; } +input[data-safe]:not(:checked) ~ footer #won { display: none; } +input[data-safe]:not(:checked) ~ main label { pointer-events: initial; } + +#lost { display: none; user-select: none ; } +input[data-mine]:checked ~ footer #lost { display: initial; } +input[data-mine]:checked ~ main label { pointer-events: none; } + +footer { + display: flex; + justify-content: center; +} + +#lost, #won { + position: absolute; + top: 10px; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..30522c1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "lib": [ + "ESNext" + ], + "module": "esnext", + "target": "esnext", + "moduleResolution": "nodenext", + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "preserve", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" // add Bun global + ] + } +} \ No newline at end of file