{"id":46569507,"url":"https://github.com/rom-melnyk/lines98","last_synced_at":"2026-03-07T08:09:15.056Z","repository":{"id":44021300,"uuid":"230507989","full_name":"rom-melnyk/lines98","owner":"rom-melnyk","description":"Lines98: the classic game implemented in browser","archived":false,"fork":false,"pushed_at":"2024-03-26T16:53:34.000Z","size":336,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-03-26T17:56:36.978Z","etag":null,"topics":["fsm","game","lines98","parceljs","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rom-melnyk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2019-12-27T19:46:17.000Z","updated_at":"2024-03-10T12:01:24.000Z","dependencies_parsed_at":"2024-03-26T18:00:32.628Z","dependency_job_id":null,"html_url":"https://github.com/rom-melnyk/lines98","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rom-melnyk/lines98","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rom-melnyk%2Flines98","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rom-melnyk%2Flines98/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rom-melnyk%2Flines98/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rom-melnyk%2Flines98/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rom-melnyk","download_url":"https://codeload.github.com/rom-melnyk/lines98/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rom-melnyk%2Flines98/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30209820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T05:23:27.321Z","status":"ssl_error","status_checked_at":"2026-03-07T05:00:17.256Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["fsm","game","lines98","parceljs","typescript"],"created_at":"2026-03-07T08:09:14.413Z","updated_at":"2026-03-07T08:09:15.043Z","avatar_url":"https://github.com/rom-melnyk.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lines98: the classic game implemented in browser\n\n## Run\n\n- `npm install \u0026\u0026 npm run demo`\n- [Live demo.](https://melnyk.site/prj/20)\n\n## Architecture overview\n\n### Front end compiling\n\n[ParcelJS](https://parceljs.org/) was considered because of zero setup. All \"out of the box\" build options work perfectly fine for this project.\n\nParcelJS compiles the code into `dist/` which is .gitignore-d.\n\n### Styling\n\n- A cell is implemented as simple `\u003cdiv\u003e` with `::before` pseudo-element responsible for the ball (if any).\n- Cell styling is foreseen for following states:\n  - `\u003cdiv class=\"cell\" ball=\"...\"\u003e` for a ball cell; it might have extra `selected` class as well.\n  - `\u003cdiv class=\"cell\" intention=\"...\"\u003e` for an intended ball cell (the ball will be spawn at next move).\n  - `\u003cdiv class=\"cell\" trace=\"...\"\u003e` for a cell that resides on the trace from selected one to hovered one.\n  - The value for `ball`, `intention` and `trace` attributes defines the color.\n    - All the colors are combined into _a palette;_ see `styles/playground/_palette.scss`.\n\n### Logic\n\n- Each cell is represented as an  instance of `Cell` class; see `scripts/cell.ts`.\n  - The instance of `Cell` class is tied to its HTML `\u003cdiv\u003e`.\n  - It contains the **state data** (ball color, intention color, trace color) as well.\n  - Updating state parameters updates the HTML. Think \"MVVM\" pattern.\n- All the cells are stored in the (instance of) `Playground` class.\n- The **runtime data** (like score, history etc.) are stored in the instance of the `Runtime` class.\n\nSo in order to understand what's currently on we need _all cells_ and the _runtime instance._ This pair of parameters is often passed to multiple functions.\n\nBoth (instances of) `Playground` and `Runtime` classes are used by the `Gameplay` class.\n- It uses the [FSM](https://en.wikipedia.org/wiki/Finite-state_machine) for game states and transitions between them. See `scripts/gameplay/fsm/`. It contains text schema and the FSM implementation.\n- There are following concepts used:\n  - **Opearations** mutate the state/runtime in \"atomic\" (often undoable) manner.\n  - **Actions** are sequences of operations wrapped into some logic. They are FSM entities (see below).\n  - **UI handlers** invoke some FSM \"entry points\". They are the way to run the flow again after automaton hit the stable (final) state.  \n     Mouse click handlers are perfect examples here.\n- The gameplay data (score + cell setup) is stored on each move in the local storage. Reloading the page automatically loads last game.\n\n#### FSM\n\nThe FSM is realized as map of states which have unique name and are functions per se.\n\nOnce runned they might return\n- Another \"state\" or its name. This means the state is transitional and automaton continues the flow.\n- Nothing / `void`. This means that the sate is stable (final) and automaton stops here.\n\nThe FSM states might be invoked from the outer world (e.g.,  by `gameplay.init()` or by mouse click). FSM runs from invoked state to the stable (final) one (unless looped).\n\nFor current purposes there was no need to implement _asynchronous FSM flow._ However it's good practice in general because it's pretty easy to hit `RangeError: \"Maximum call size exceeded\"` with cyclic transitions.\n\n#### Finding shortest path\n\nIn order to find shortest path from selected cell to hovered one the modified [Dijkstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) is used. See `scripts/gameplay/trace-utils.ts`.\n- Instead of assigning the minimal _tentative distance_ (sic) to each node the _shortest path_ is assigned. The principle remains (path length is semantically equal to the \"distance\" as each edge has weight of 1).\n- The \"neighbors\" are pre-sorted by distance to destination cell (it might look expensive but we perform that operation only when necessary and for maximum four cells).\n- After _any path_ to destination cell has been found the altorithm won't continue with paths which are _already_ of same length or longer (because they cannot deliver shorter solution).\n\nThosee optimizations made finding the shortest path pretty fast. Better observed on non-mobile devices (mouse needed).\n\n## Deployment\n\n`npm run prod` generates production-ready code in the `dist/`. It contains the `index.html`, the JS and CSS files (both minified). Being served from local folder, the `index.html` should work in browser.\n\n---\n \nCredits: Roman Melnyk, \u003chttps://melnyk.site\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2From-melnyk%2Flines98","html_url":"https://awesome.ecosyste.ms/projects/github.com%2From-melnyk%2Flines98","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2From-melnyk%2Flines98/lists"}