{"id":19379571,"url":"https://github.com/we-gold/algernon-js","last_synced_at":"2025-04-23T19:32:56.435Z","repository":{"id":179025390,"uuid":"662740842","full_name":"We-Gold/algernon-js","owner":"We-Gold","description":"Algernon is a JS library for efficiently generating, solving, and rendering mazes.","archived":false,"fork":false,"pushed_at":"2023-07-17T18:10:16.000Z","size":197,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-08T02:32:19.761Z","etag":null,"topics":["a-star","ant-colony-optimization","bfs","canvas","d-star-lite","dfs","javascript","maze","maze-algorithms","maze-generator","maze-solver","occupancy-grid-map","p5js","serialization"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/algernon-js","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/We-Gold.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-05T19:33:00.000Z","updated_at":"2024-11-03T22:55:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"d0441b65-6efe-4296-9d9a-b5037ab6e500","html_url":"https://github.com/We-Gold/algernon-js","commit_stats":null,"previous_names":["we-gold/algernon-js"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/We-Gold%2Falgernon-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/We-Gold%2Falgernon-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/We-Gold%2Falgernon-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/We-Gold%2Falgernon-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/We-Gold","download_url":"https://codeload.github.com/We-Gold/algernon-js/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223932087,"owners_count":17227277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a-star","ant-colony-optimization","bfs","canvas","d-star-lite","dfs","javascript","maze","maze-algorithms","maze-generator","maze-solver","occupancy-grid-map","p5js","serialization"],"created_at":"2024-11-10T09:10:34.707Z","updated_at":"2024-11-10T09:10:35.101Z","avatar_url":"https://github.com/We-Gold.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/We-Gold/algernon-js/main?label=npm%20version\u0026color=green\u0026link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Falgernon-js)\n![npm bundle size](https://img.shields.io/bundlephobia/min/algernon-js?color=green)\n[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/We-Gold/algernon-js/issues)\n![tests](https://github.com/We-Gold/algernon-js/actions/workflows/run-tests.yml/badge.svg)\n![ViewCount](https://views.whatilearened.today/views/github/We-Gold/algernon-js.svg)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n![NPM Downloads](https://img.shields.io/npm/dw/algernon-js)\n\n# Algernon-js\n\nAlgernon is a JS library for efficiently generating, solving, and rendering 2D mazes.\n\n[_Where does the name come from?_](https://en.wikipedia.org/wiki/Flowers_for_Algernon)\n\nFound an issue or a performance improvement? Feel free to leave an issue or make a pull request!\n\n![Algernon Generated Maze](./readme-maze.png)\n\n_Maze generated, solved, and rendered by Algernon-js_\n\n-   [Installation](#installation)\n-   [Usage](#usage)\n-   [Benchmarks](#benchmarks)\n\n## Installation\n\nVia NPM:\n\n```bash\nnpm i algernon-js\n```\n\nVia CDN:\n\n```html\n\u003cscript\n\ttype=\"module\"\n\tsrc=\"https://cdn.jsdelivr.net/npm/algernon-js/dist/algernon.js\"\n\u003e\u003c/script\u003e\n```\n\n## Usage\n\n### Maze Generation\n\n**Example**:\n\n```js\nimport { generateBacktrackingRaw } from \"algernon-js\"\n\nconst [rows, cols] = [20, 20]\n\nconst rawMaze = generateBacktrackingRaw(rows, cols)\n```\n\n**Raw Format:**\n\nIn `algernon-js`, mazes are stored as a 2D array of integers for a mix of space efficiency and convenience.\n\nEach cell may have North, South, East, or West walls (the outer edges of the maze will be bounded).\n\nInternally, bits are used to represent the presence of walls or other significant information.\n\nThat means a cell is internally used like this (`0b1001` - a cell with a North and West wall), but looks like this (`9` - the decimal representation).\n\nIf you are familiar with bit manipulation, feel free to use mazes in their \"raw\" format. If not, check out the conversions available for more familiar formats.\n\n| Name         | Description                                                                                | Method                    | Occupancy Grid Support |\n| ------------ | ------------------------------------------------------------------------------------------ | ------------------------- | ---------------------- |\n| Backtracking | A fast algorithm for mazes with some long corridors. Reasonable general purpose algorithm. | `generateBacktrackingRaw` | ✅                     |\n| Kruskal's    | On the slower side, good for simple and relatively easy mazes.                             | `generateKruskalRaw`      | ✅                     |\n| Growing Tree | Fast, performs like Prim's by default but configurable.                                    | `generateGrowingTreeRaw`  | ✅                     |\n\n_Some of these algorithms are additionally configurable. Check out the JSDoc comments for more info._\n\n**Occupancy Grid Format:**\n\n`Algernon-js` also supports the occupancy grid format. In this format, each cell is either open, or a wall/obstacle.\n\nOpen is represented as `false` and a wall/obstacle is `true`.\n\nThere are similar methods available for generating these mazes, like `generateBacktrackingGrid`.\n\nNote that with this maze format, only odd-dimensioned mazes can be generated, and otherwise the bottom row and right column will both be completely walls. So 19 by 19 would work, but 20 by 20 is not recommended.\n\n### Maze Solving\n\n**Example:**\n\n```js\n// maze already generated, stored as `rawMaze`\n\nimport { solveAStarRaw } from \"algernon-js\"\n\n// Solve the maze using A*, starting at the top left\n// and ending at the bottom right\nconst solution = solveAStarRaw(rawMaze, [0, 0], [rows - 1, cols - 1])\n```\n\n**Format:**\n\nSolutions are simply arrays, with each element as an index in the original `rawMaze`.\n\nFor example: `[[0,0],[1,0],[1,1],[1,2],...]`\n\n| Name     | Description                                                                                                                  | Method           | Occupancy Grid Support |\n| -------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------------- |\n| A\\*      | Fast Dijkstra's-based solver that uses heuristics. Configurable general purpose solver.                                      | `solveAStarRaw`  | ✅                     |\n| ACO      | Ant Colony Optimization is not recommended for real-world purposes. It is interesting for experimentation.                   | `solveACO`       | ⏳                     |\n| DFS      | Depth First Search is simple and fast, exploring deep paths and backtracking.                                                | `solveDFSRaw`    | ✅                     |\n| BFS      | Breadth First Search is fast, simply exploring the whole maze.                                                               | `solveBFSRaw`    | ✅                     |\n| D\\* Lite | Essentially A\\* backwards. Pretty fast, and ideal for mazes that change and require re-planning. _See main.js for examples._ | `solveDStarLite` | ⏳                     |\n\n_Many of these algorithms use heuristics or additional configuration. Check of the JSDoc comments for more info._\n\n### Rendering\n\nThis is intended as a simple solution for basic web apps or testing. Feel free to use the code as a starting point for a custom solution.\n\nFor the occupancy grid format, a similar method is available: `renderGridMazeToCanvas`.\n\n**Example:**\n\n```html\n\u003c!-- index.html --\u003e\n\n\u003ccanvas id=\"demo-canvas\" width=\"400\" height=\"400\"\u003e\u003c/canvas\u003e\n```\n\n```js\n// script.js\n\nimport {\n\tgenerateBacktrackingRaw,\n\tsolveAStarRaw,\n\trenderRawMazeToCanvas,\n} from \"algernon-js\"\n\nconst [rows, cols] = [20, 20]\n\n// Generate and solve the maze\nconst rawMaze = generateBacktrackingRaw(rows, cols)\nconst solution = solveAStarRaw(rawMaze, [0, 0], [rows - 1, cols - 1])\n\n// Render the maze\nconst canvas = document.getElementById(\"demo-canvas\")\nconst ctx = canvas.getContext(\"2d\")\n\n// Render each cell at 20 pixels x 20 pixels\n// `solution` is an optional argument\nrenderRawMazeToCanvas(ctx, 20, rawMaze, solution)\n```\n\n### Conversion\n\nFor the occupancy grid format, ✅ represents support for this format by typically adding `Grid` to the end of the method (though this may vary if the name implies it).\n\n**Raw \u003c-\u003e Node Matrix**\n\n```js\n// `rawMaze` already generated\n\nconst nodeMatrix = convertRawToNodeMatrix(rawMaze)\n\n// A node matrix is a 2D array of `MatrixNode`,\n// where each node contains the following booleans:\n// hasNorthWall, hasSouthWall, hasEastWall, hasWestWall\n\n// Convert node matrix back to raw\nconst updatedRaw = convertNodeMatrixToRaw(nodeMatrix)\n```\n\n**Raw \u003c-\u003e Node Graph**\n\n```js\n// `rawMaze` already generated\n\n// Generate a node graph with the given start and end points\nconst nodeGraph = convertRawToNodeGraph(rawMaze, [0, 0], [row - 1, col - 1])\n\n// A node graph is a graph of `GraphNode`,\n// where each node contains the following booleans:\n// isStart, isEnd, hasNorthWall, hasSouthWall, hasEastWall, hasWestWall\n// and the following references (or null if not present):\n// northNeighbor, southNeighbor, eastNeighbor, westNeighbor\n\nconst updatedRaw = convertNodeGraphToRaw(nodeGraph, [0, 0])\n```\n\n**Raw \u003c-\u003e ArrayBuffer (Binary)**\n\n```js\n// `rawMaze` already generated\n\n// Convert the maze to a Uint8Array,\n// with the first byte storing the rows and cols\nconst buffer = serializeRawToBinary(rawMaze)\n\nconst deserialized = deserializeBinaryToRaw(buffer)\n```\n\n**Raw \u003c-\u003e Base64 String**\n\n```js\n// `rawMaze` already generated\n\n// Convert the maze to a Base64 string\nconst base64 = serializeRawToString(rawMaze)\n\nconst deserialized = deserializeStringToRaw(base64)\n```\n\n**Raw -\u003e Supersampled** ✅\n\n```js\n// `rawMaze` already generated\n\n// Supersample the maze at a factor of 2\nconst supersampled = supersampleMaze(rawMaze, 2)\n\n// Output: sample maze structure but twice the rows and columns\n```\n\n**Raw \u003c-\u003e Grid Maze Format** ✅\n\n```js\n// `rawMaze` already generated\n\n// Replace wall properties with open/wall cells (and upscale to a factor of 2)\nconst gridMaze = convertRawToGridFormat(rawMaze, 2)\n\n// Output: same maze structure but walls are represented by true\n// and cells by false, and a different number of cells\n\n// Convert back to Raw (and tell it the upscale factor)\nconst rawMaze2 = convertGridToRawFormat(gridMaze, 2)\n\n// Output: the original `rawMaze`\n\n// Convert points from raw to grid and back\nconst rawPoint = [2, 2]\nconst gridPoint = convertRawToGridPoint(rawPoint)\nconst originalPoint = convertGridToRawPoint(gridPoint)\n```\n\n**Raw -\u003e Raw (Braided)** ✅\n\n```js\n// `rawMaze` already generated\n\n// Remove dead-ends with the given probability\nbraidMaze(rawMaze, 0.5)\n\n// Output: an in-place modification of the original maze,\n// with walls removed to prevent dead-ends.\n```\n\n**Grid -\u003e Degrade/Fill** ✅\n\n```js\n// `gridMaze` already generated\n\n// Remove walls with the given probability\ndegradeGrid(gridMaze, 0.1)\n\n// Add walls with the given probability\nfillGrid(gridMaze, 0.05)\n\n// Output: an in-place modification of the original maze,\n// with walls removed or added. This is ONLY for occupancy\n// grid mazes.\n```\n\n### Helpers\n\nInternally used helper methods, like `removeWall`, `getAvailableNeighbors`, `getDirection`, and more, are made available through the `helpers` namespace.\n\nAdditionally, many have support for occupancy grids as well by adding `Grid` to the end of the method name.\n\n**Example:**\n\n```js\nimport { helpers } from \"algernon-js\"\n\nconst c1 = [0, 0]\nconst c2 = [1, 0]\n\nconst directionBetweenCells = helpers.getDirection(c1, c2)\n// directionBetweenCells: 0b0100 or South\n```\n\n### Data Structures\n\nInternally used data structures are now available as well.\n\nThe two currently available are: `createDisjointSet` and `createMinHeap`.\n\n## Benchmarks\n\n_Benchmarks were run on a M2 MacBook Air with a minimum of 50 samples each._\n\n### Generation\n\n| Maze Size | Name         | Mean (ms)  | RME  |\n| --------- | ------------ | ---------- | ---- |\n| 20 x 20   | Backtracking | **0.1037** | 0.89 |\n| 50 x 50   | Backtracking | **0.5939** | 0.80 |\n| 80 x 80   | Backtracking | **1.4800** | 0.70 |\n| 20 x 20   | Kruskal      | 2.0065     | 1.49 |\n| 50 x 50   | Kruskal      | 44.493     | 3.59 |\n| 80 x 80   | Kruskal      | 325.52     | 2.84 |\n| 20 x 20   | Growing Tree | 0.1283     | 0.39 |\n| 50 x 50   | Growing Tree | 0.8033     | 0.70 |\n| 80 x 80   | Growing Tree | 2.1503     | 2.45 |\n\n### Solving\n\n_Backtracking mazes were selected for this benchmark. Performance can vary with different maze types._\n\n| Maze Size | Name     | Mean (ms)  | RME  |\n| --------- | -------- | ---------- | ---- |\n| 20 x 20   | A-Star   | **0.0173** | 1.13 |\n| 50 x 50   | A-Star   | **0.1551** | 0.81 |\n| 80 x 80   | A-Star   | 0.1676     | 0.78 |\n| 20 x 20   | ACO      | 1.3462     | 0.64 |\n| 50 x 50   | ACO      | 22.810     | 0.98 |\n| 80 x 80   | ACO      | 69.850     | 1.67 |\n| 20 x 20   | DFS      | 0.0617     | 0.41 |\n| 50 x 50   | DFS      | 0.2197     | 0.48 |\n| 80 x 80   | DFS      | 0.3464     | 0.60 |\n| 20 x 20   | BFS      | 0.0532     | 0.38 |\n| 50 x 50   | BFS      | 0.1643     | 0.62 |\n| 80 x 80   | BFS      | **0.1504** | 0.37 |\n| 20 x 20   | D\\* Lite | 0.1215     | 1.06 |\n| 50 x 50   | D\\* Lite | 0.5553     | 0.64 |\n| 80 x 80   | D\\* Lite | 0.8571     | 1.71 |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwe-gold%2Falgernon-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwe-gold%2Falgernon-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwe-gold%2Falgernon-js/lists"}