diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | index.html | 6 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 122 | ||||
| -rw-r--r-- | src/Ball.ts | 75 | ||||
| -rw-r--r-- | src/consts.ts | 11 | ||||
| -rw-r--r-- | src/main.ts | 28 | ||||
| -rw-r--r-- | src/map.ts | 43 | ||||
| -rw-r--r-- | src/style.css | 13 | ||||
| -rw-r--r-- | src/ui.ts | 170 | ||||
| -rw-r--r-- | src/utils.ts | 14 | ||||
| -rw-r--r-- | tsconfig.json | 1 | ||||
| -rw-r--r-- | typedoc.json | 3 |
13 files changed, 454 insertions, 34 deletions
@@ -22,3 +22,4 @@ dist-ssr *.njsproj *.sln *.sw? +docs/ @@ -15,11 +15,13 @@ <div></div> <div></div> </div> - <h3>Points: <span>0</span></h3> + <div> + <h3>Points: <span>0</span></h3> + </div> + <div id="funny"></div> </header> <table> </table> - <div id="app"></div> <script type="module" src="/src/main.ts"></script> </body> diff --git a/package.json b/package.json index 3fad234..45c1e4e 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "preview": "vite preview" }, "devDependencies": { + "typedoc": "^0.22.13", "typescript": "^4.5.4", "vite": "^2.8.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db6a5ec..2f2b2f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,15 +1,38 @@ lockfileVersion: 5.3 specifiers: + typedoc: ^0.22.13 typescript: ^4.5.4 vite: ^2.8.0 devDependencies: + typedoc: 0.22.13_typescript@4.6.2 typescript: 4.6.2 vite: 2.8.6 packages: + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion/2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + dev: true + /esbuild-android-64/0.14.27: resolution: {integrity: sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ==} engines: {node: '>=12'} @@ -218,6 +241,10 @@ packages: esbuild-windows-arm64: 0.14.27 dev: true + /fs.realpath/1.0.0: + resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} + dev: true + /fsevents/2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -230,6 +257,17 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true + /glob/7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + /has/1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -237,18 +275,67 @@ packages: function-bind: 1.1.1 dev: true + /inflight/1.0.6: + resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + /is-core-module/2.8.1: resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==} dependencies: has: 1.0.3 dev: true + /jsonc-parser/3.0.0: + resolution: {integrity: sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==} + dev: true + + /lunr/2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + dev: true + + /marked/4.0.12: + resolution: {integrity: sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==} + engines: {node: '>= 12'} + hasBin: true + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch/5.0.1: + resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /nanoid/3.3.1: resolution: {integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true + /once/1.4.0: + resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} + dependencies: + wrappy: 1.0.2 + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} + engines: {node: '>=0.10.0'} + dev: true + /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true @@ -283,6 +370,14 @@ packages: fsevents: 2.3.2 dev: true + /shiki/0.10.1: + resolution: {integrity: sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==} + dependencies: + jsonc-parser: 3.0.0 + vscode-oniguruma: 1.6.2 + vscode-textmate: 5.2.0 + dev: true + /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -293,6 +388,21 @@ packages: engines: {node: '>= 0.4'} dev: true + /typedoc/0.22.13_typescript@4.6.2: + resolution: {integrity: sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==} + engines: {node: '>= 12.10.0'} + hasBin: true + peerDependencies: + typescript: 4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x + dependencies: + glob: 7.2.0 + lunr: 2.3.9 + marked: 4.0.12 + minimatch: 5.0.1 + shiki: 0.10.1 + typescript: 4.6.2 + dev: true + /typescript/4.6.2: resolution: {integrity: sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==} engines: {node: '>=4.2.0'} @@ -322,3 +432,15 @@ packages: optionalDependencies: fsevents: 2.3.2 dev: true + + /vscode-oniguruma/1.6.2: + resolution: {integrity: sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==} + dev: true + + /vscode-textmate/5.2.0: + resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==} + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + dev: true diff --git a/src/Ball.ts b/src/Ball.ts index e2c04f1..d67cb6e 100644 --- a/src/Ball.ts +++ b/src/Ball.ts @@ -1,45 +1,114 @@ +/** + * @module Ball +*/ import { START_NO_OF_BALLS, MAP_WIDTH, MAP_HEIGHT, COLORS } from "./consts"; import { getRandomInt, getRandomEl } from "./utils"; -import { getTd } from "./ui"; +import { finishGame, getTd, setFunnyText } from "./ui"; -export default class Ball { +/** Ball is the class for holding ball data and ball logic */ +export class Ball { + /// x coordinate x: number; + /// y coordinate y: number; + /// color of ball color: string; + /** + * Basic constructor for ball + * @param x x coord + * @param y y coord + * @param color color + */ constructor(x: number, y: number, color: string) { this.x = x; this.y = y; this.color = color; } + /** + * Getter for this ball <td> + * @returns Td of this ball + */ get td() { return getTd(this.x, this.y); } + + /** + * Helper + * @param b Another abll to check for equality with this + * @returns True if balls have same coordinates else false + */ isEqual(b: Ball) { if (!b) throw Error("b is undeifneds") if (!this) throw Error("this is undeifneds") return this.x === b.x && this.y === b.y; } + /** Makes generating balls more funny */ + @funny + /** + * Creates table of new balls + * @param currentBalls Current balls + * @returns New balls + */ static generateNewBalls(currentBalls: Ball[]): Array<Ball> { const out = <Ball[]>[]; for (let i = 0; i < START_NO_OF_BALLS; i++) { let x: number, y: number, color: string; do { [x, y, color] = [getRandomInt(0, MAP_HEIGHT), getRandomInt(0, MAP_WIDTH), Nexts.pop()]; + if (currentBalls.length + out.length >= MAP_WIDTH * MAP_HEIGHT) { + finishGame(false); + return out; + } } while ( out.find(b => b.x === x && b.y === y) || currentBalls.find(b => b.x === x && b.y === y) ) out.push(new Ball(x, y, color)); } + // out.push(new Ball(1, 1, COLORS[1])) return out; } } +/** + * Function used only as a decorator, makes `generateNewBalls` more funny + * @param _target - not used + * @param _propertyKey - name of decorated function + * @param descriptor - object with decorated function +*/ +function funny(_target: any, _propertyKey: "generateNewBalls", descriptor: PropertyDescriptor) { + const orig = descriptor.value; + descriptor.value = function(currentBalls: Ball[]) { + let bolzNumber = currentBalls.length + START_NO_OF_BALLS; + if (bolzNumber > 81) bolzNumber = 81 + const txts = [ + "Mmmmmm masz " + bolzNumber + " kuleczek", + bolzNumber + " pysznych kuleczek", + "Masz jakas rozowa kuleczke?", + "Show me your bolz!", + "Deez nuts", + `Vous avez ${bolzNumber} boules`, + "Yummy yummu yummy, must be funny", + "Ou, I thought you died of ligma", + "Kuleczek moc!!", + "Keep it goin' bro", + "You know deez?", + "Luuubie kuleczki ^^", + "BoulzNumber: " + bolzNumber + ". It's under 9000!", + ] + setFunnyText(getRandomEl(txts)) + return orig(currentBalls); + }; +}; +/** Class wraper for showing next balls */ export class Nexts { + /** HTMLDivElements that shows next balls */ static els = Array.from(document.querySelectorAll("#next div")) as HTMLDivElement[] + + /** Setter to properly set new next balls */ static setEls() { for (const el of this.els) { if (el.style.backgroundColor === "") { @@ -47,6 +116,8 @@ export class Nexts { } } } + + /** Function to get first new color and rotate rest of them */ static pop() { const out = this.els[0].style.backgroundColor; this.els[0].style.backgroundColor = this.els[1].style.backgroundColor; diff --git a/src/consts.ts b/src/consts.ts index f97cbd1..81d8f87 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -1,7 +1,14 @@ -export const COLORS = ["red", "green", "blue", "orange", "pink", "white", "cyan"]; +/** Colors of balls */ +export const COLORS = ["red", "green", "blue", "orange", "#ff009d", "white", "cyan"]; +/** Number of {@linkcode Ball} generated each turn */ export const START_NO_OF_BALLS = 3; +/** Height of map */ export const MAP_HEIGHT = 9; +/** Width of map */ export const MAP_WIDTH = 9; +/** Size of html td element */ export const TD_SIZE = "50px"; +/** Size of div that renders like a ball */ export const CIRCLE_SIZE = "35px"; -export const DELETE_FROM_BALLS = 3; +/** How many {@linkcode Ball} there have to be in row to delete it */ +export const DELETE_FROM_BALLS = 5; diff --git a/src/main.ts b/src/main.ts index 50d41e6..7b2b445 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,27 @@ import './style.css' -import Ball, { Nexts } from "./Ball" -import { genGameTable, render } from "./ui"; +/* For documentation only */ +import * as UI from "./ui"; +// import * as Consts from "./consts"; +// import * as Map from "./map"; +// import * as Utils from "./utils"; +import * as Ball from "./Ball"; +// import * as Pathfinder from "./pathfinder"; +// import * as Graph from "./pathfinder/Graph"; +// export { UI, Consts, Map, Utils, Ball, Pathfinder, Graph }; +/** Global array of {@linkcode Ball} */ declare global { - interface Window { balls: Ball[]; } + interface Window { balls: Ball.Ball[]; startTime: number; } } -Nexts.setEls(); -window.balls = Ball.generateNewBalls([]); -genGameTable(); -render(); +/** Sets preview of next balls */ +Ball.Nexts.setEls(); +/** Generate stat balls */ +window.balls = Ball.Ball.generateNewBalls([]); + +/** Generate html table for balls */ +UI.genGameTable(); +/** renders balls */ +UI.render(); +window.startTime = Date.now() @@ -1,8 +1,22 @@ -import Ball from "./Ball"; +/** + * @module Map +*/ + +import { Ball } from "./Ball"; import { DELETE_FROM_BALLS, MAP_HEIGHT, MAP_WIDTH } from "./consts"; import { Graph } from "./pathfinder/Graph"; +import { clearTd } from "./ui"; -export function findToRemove(balls: Ball[]): Ball[] { +/** @internal */ +export interface BallsArray<T> extends Array<T> { + clear: () => void; +} +/** + * Finds ball that should be deleted + * @param balls - array of {@linkcode Ball} + * @return Balls to remove +*/ +export function findToRemove(balls: Ball[]): BallsArray<Ball> { const out = new Set<Ball>() for (const ball of balls) { const xNeighbours = new Set<Ball>(); @@ -60,10 +74,20 @@ export function findToRemove(balls: Ball[]): Ball[] { if (s.size >= DELETE_FROM_BALLS) s.forEach(b => out.add(b)) } } - return [...out]; + + const rout = [...out] as BallsArray<Ball>; + rout.clear = function() { + this.forEach(b => clearTd(b.td)) + } + return rout; } +/** Array of array of balls */ export type Map = (Ball | null)[][]; +/** + * Function that generates map (Array of Arrays) of balls from arrays of balls. Empty spots are null + * @param balls - array of {@linkcode Ball} +*/ export function genMap(balls: Array<Ball>): Map { const map = <Map>[]; for (let i = 0; i < MAP_HEIGHT; i++) { @@ -78,7 +102,17 @@ export function genMap(balls: Array<Ball>): Map { return map; } -type Coord = { x: number; y: number }; +/** + * Object of coordinates +*/ +export type Coord = { x: number; y: number }; + +/** + * Function that converts map to {@link Graph} + * @param map - map to be converted + * @param start - start of search + * @param goal - finsish of search +*/ export function genGraph(map: Map, start: Coord, goal: Coord): Graph { const graph: Graph = []; map.forEach((row, i) => { @@ -117,6 +151,7 @@ export function genGraph(map: Map, start: Coord, goal: Coord): Graph { return graph; } +/** Generates UUID4 */ function uuidv4() { // @ts-ignore return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => diff --git a/src/style.css b/src/style.css index 8803e96..53fc5f8 100644 --- a/src/style.css +++ b/src/style.css @@ -4,8 +4,8 @@ -moz-osx-font-smoothing: grayscale; color: white; background-color: black; + font-size: 2em; } - table { border-collapse: collapse; margin: 20px auto; @@ -21,3 +21,14 @@ td div, #next div { margin: auto auto; border-radius: 50%; } +header { + margin-top: 5vh; + margin-left: 5vh; + text-align: center; +} +header div { + margin: 20px; +} +#next div { + display: inline-block; +} @@ -1,42 +1,129 @@ -import Ball from "./Ball"; +/** + * @module UI + * The only place with side effects +*/ + +import { Ball } from "./Ball"; import { MAP_WIDTH, MAP_HEIGHT, TD_SIZE } from "./consts"; import { sleep } from "./utils"; import { findToRemove, genGraph, genMap } from "./map"; import findPath, { Path } from "./pathfinder"; +/** @internal */ let selectedBall: Ball | null = null; +/** @internal */ +let newBalls = <Ball[]>[]; +/** @internal */ let currentPath: Path = []; +/** @internal */ let turnIsChanging = false; -const setCurrentPath = (p: any) => { + +/** + * Setter for path, to automatically remove old one + * @param p - new path +*/ +const setCurrentPath = (p: Path) => { for (const v of currentPath) { getTd(v.x!, v.y!).style.backgroundColor = ""; } currentPath = p; } +/** Function that handles making new turn */ export async function nextTurn() { turnIsChanging = true; for (const v of currentPath) { getTd(v.x!, v.y!).style.backgroundColor = "rgba(50, 50, 50, 0.8)"; } - await sleep(1000); + // await sleep(1000); - const toRemove = findToRemove(window.balls); + await clearAndGenerateBalls(); + setCurrentPath([]); + turnIsChanging = false; +} +/** Function to clear balls that should be deleted and generate new ones, also check for some to delete after that */ +export async function clearAndGenerateBalls() { + let toRemove = findToRemove(window.balls); + const remFromGlobal = () => window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))); + const colorNewBalls = () => newBalls.forEach(nb => nb.td.style.backgroundColor = "rgba(123, 45, 99, 0.8)") + const clearNewBalls = () => newBalls.forEach(nb => nb.td.style.backgroundColor = "") - window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))) - for (const b of toRemove) { - clearTd(b.td) - } - pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() - setCurrentPath([]); - window.balls.push(...Ball.generateNewBalls(window.balls)); - render(); - turnIsChanging = false; + do { + await sleep(1000); + remFromGlobal() + pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() + clearNewBalls() + newBalls = Ball.generateNewBalls(window.balls) + colorNewBalls() + window.balls.push(...newBalls); + render() + if (window.balls.length >= MAP_WIDTH * MAP_HEIGHT) { + finishGame(); + return; + } + toRemove.clear() + toRemove = findToRemove(window.balls); + } while (toRemove.length) } +// export async function clearAndGenerateBalls2() { +// let toRemove = findToRemove(window.balls); + +// window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))) +// toRemove.clear(); +// pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() + +// for (const ball of newBalls) { +// ball.td.style.backgroundColor = "" +// } +// newBalls = Ball.generateNewBalls(window.balls) +// for (const ball of newBalls) { +// ball.td.style.backgroundColor = "rgba(123, 45, 99, 0.8)" +// } +// window.balls.push(...newBalls); +// render(); +// if (window.balls.length >= MAP_WIDTH * MAP_HEIGHT) { +// console.log("too many balls") +// finishGame(); +// return; +// } +// toRemove = findToRemove(window.balls); + +// if (toRemove.length) { +// render(); +// await sleep(1000); +// window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))) +// toRemove.clear(); +// pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() +// } +// if (window.balls.length >= MAP_WIDTH * MAP_HEIGHT) { +// finishGame(); +// return; +// } +// render(); +// } + +/** + * Function that handles finishing the game + * @param finish - should it finish the game or only do everything except it +*/ +export function finishGame(finish = true) { + turnIsChanging = true; + const gameTime = ((Date.now() - window.startTime) / 1000).toFixed(2) + if (finish) { + setTimeout(() => { + alert("You lost, because you are too bad even for simple Boules game, you have made " + pointsEl.innerText + " points. It took you " + `${gameTime} seconds`) + throw new Error("Game has been finished") + }, 1) + } +} +/** + * Function that is set as OnClickHandler for td elements + * @param _e - not used +*/ export function tdOnClick(_e: PointerEvent) { if (!currentPath.length || turnIsChanging) return; const std = getTd(currentPath[0].x!, currentPath[0].y!); @@ -48,6 +135,13 @@ export function tdOnClick(_e: PointerEvent) { render(window.balls); nextTurn(); } + +/** + * Function that is set as MouseEnter for td elements + * @param _e - not used + * @param x - x cordinate of td + * @param y - y cordinate of td +*/ export function tdOnMouseEnter(_e: PointerEvent, x: number, y: number) { if (!selectedBall || turnIsChanging) return; const graph = genGraph(genMap(window.balls), { x: selectedBall.x, y: selectedBall.y }, { x, y }); @@ -58,6 +152,11 @@ export function tdOnMouseEnter(_e: PointerEvent, x: number, y: number) { getTd(v.x!, v.y!).style.backgroundColor = "rgba(45, 123, 78, 0.6)"; } } + +/** + * Function that renders the balls + * @param balls - array of current {@link Ball} if null then use global balls +*/ export function render(balls?: Ball[]) { balls = balls ? balls : window.balls; for (const ball of balls) { @@ -66,11 +165,11 @@ export function render(balls?: Ball[]) { td.firstChild.style.backgroundColor = color; td.onmousedown = () => { - if (turnIsChanging) return; + if (turnIsChanging || ballCantMove(ball)) return; const circle = td.firstChild as HTMLDivElement; setCurrentPath([]); const smallerCircle = () => { - if (!selectedBall) return; + if (!selectedBall || turnIsChanging) return; selectedBall.td.firstChild.style.width = "" selectedBall.td.firstChild.style.height = "" } @@ -94,15 +193,37 @@ export function render(balls?: Ball[]) { }; } } + +/** + * Checks if selcted ball can mpve anywhere + * @param ball ball to check +*/ +export function ballCantMove(ball: Ball): boolean { + const sb = ball; + const top = !!window.balls.find(b => b.x === sb.x && b.y === sb.y + 1) || sb.y + 1 >= MAP_HEIGHT + const bottom = !!window.balls.find(b => b.x === sb.x && b.y === sb.y - 1) || sb.y - 1 < 0 + const right = !!window.balls.find(b => b.x === sb.x + 1 && b.y === sb.y) || sb.x + 1 >= MAP_WIDTH + const left = !!window.balls.find(b => b.x === sb.x - 1 && b.y === sb.y) || sb.x - 1 < 0 + + const out = (top && bottom && left && right) + return out; +} + +/** + * Function that clear visual look of given td + * @param td - td (HTML td element) +*/ export function clearTd(td: Td) { if (td.localName !== "td") { - console.log(td) throw new Error("Not td passed to clearTd()"); } td.innerHTML = "" td.onmousedown = null; + td.style.backgroundColor = "" td.appendChild(document.createElement("div")) } + +/** Funtion that generates game table */ export function genGameTable() { const table = getGameMapEl(); for (let i = 0; i < MAP_HEIGHT; i++) { @@ -119,13 +240,30 @@ export function genGameTable() { table.appendChild(tr) } } + +/** Setter for funny text on funny element */ +export function setFunnyText(t: string) { + document.querySelector("#funny")!.innerHTML = t; +} + +/** Getter for game map HTML element */ export function getGameMapEl(): HTMLTableElement { return document.querySelector('table')! } + +/** Fixes HTMLTableCellElement interface */ export interface Td extends HTMLTableCellElement { firstChild: HTMLDivElement; } + +/** + * Getter for td element at given cordinates + * @param x - x coordinate + * @param y - y coordinate + * @return wanted td +*/ export function getTd(x: number, y: number): Td { return getGameMapEl().children[x].children[y] as Td; } +/** HTML element when points are visible */ export const pointsEl = document.querySelector("h3 span") as HTMLSpanElement diff --git a/src/utils.ts b/src/utils.ts index a767896..48771e9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,12 +1,26 @@ /** * Returns a random number between min (inclusive) and max (exclusive) + * @param min - minimum + * @param max - maximum + * @returns Random int */ export function getRandomInt(min: number, max: number): number { return Math.floor(Math.random() * (max - min) + min); } +/** + * Gets random element from array + * @typeParam T - type of elements inside given array + * @param arr - array + * @returns Random element from given array +*/ export function getRandomEl<T>(arr: Array<T>): T { return arr[getRandomInt(0, arr.length)]; } +/** + * Function that sleeps + * @param time - sleep for this time [ms] + * @returns Promise that resolves after `time`s +*/ export async function sleep(time: number) { return new Promise(resolve => setTimeout(resolve, time)); } diff --git a/tsconfig.json b/tsconfig.json index 4eb0b8f..a7dba88 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,7 @@ "esModuleInterop": true, "noEmit": true, "noUnusedLocals": true, + "experimentalDecorators": true, "noUnusedParameters": true, "noImplicitReturns": true }, diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..19d8c43 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,3 @@ +{ + "name": "Boules" +} |
