{"id":14990157,"url":"https://github.com/manuelhenke/2048-webcomponent","last_synced_at":"2026-01-20T10:32:46.491Z","repository":{"id":217949375,"uuid":"745202299","full_name":"manuelhenke/2048-webcomponent","owner":"manuelhenke","description":"2048 as a WebComponent","archived":false,"fork":false,"pushed_at":"2024-04-12T00:10:25.000Z","size":3931,"stargazers_count":1,"open_issues_count":11,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-13T17:36:06.254Z","etag":null,"topics":["2048","2048-clone","2048-game","css","game","lit-element","lit-html","typescript","webcomponent","webpack"],"latest_commit_sha":null,"homepage":"https://henkebyte.com/2048","language":"TypeScript","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/manuelhenke.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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},"funding":{"github":["manuelhenke"]}},"created_at":"2024-01-18T20:54:20.000Z","updated_at":"2024-09-15T07:40:15.000Z","dependencies_parsed_at":"2024-09-25T00:31:34.593Z","dependency_job_id":null,"html_url":"https://github.com/manuelhenke/2048-webcomponent","commit_stats":{"total_commits":5,"total_committers":2,"mean_commits":2.5,"dds":0.4,"last_synced_commit":"3b4f4f735e9adb759ba8a6c1e0d173405b43b9ce"},"previous_names":["manuelhenke/2048-webcomponent"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/manuelhenke/2048-webcomponent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelhenke%2F2048-webcomponent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelhenke%2F2048-webcomponent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelhenke%2F2048-webcomponent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelhenke%2F2048-webcomponent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/manuelhenke","download_url":"https://codeload.github.com/manuelhenke/2048-webcomponent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelhenke%2F2048-webcomponent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28601841,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T09:39:28.479Z","status":"ssl_error","status_checked_at":"2026-01-20T09:38:10.511Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["2048","2048-clone","2048-game","css","game","lit-element","lit-html","typescript","webcomponent","webpack"],"created_at":"2024-09-24T14:19:35.639Z","updated_at":"2026-01-20T10:32:46.475Z","avatar_url":"https://github.com/manuelhenke.png","language":"TypeScript","funding_links":["https://github.com/sponsors/manuelhenke"],"categories":[],"sub_categories":[],"readme":"# 2048-webcomponent\n\n[![CI](https://github.com/manuelhenke/2048-webcomponent/actions/workflows/ci.yml/badge.svg)](https://github.com/manuelhenke/2048-webcomponent/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/manuelhenke/2048-webcomponent/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/manuelhenke/2048-webcomponent/actions/workflows/codeql-analysis.yml)\n[![License](https://img.shields.io/github/license/manuelhenke/2048-webcomponent)](./LICENSE)\n[![NPM version](https://img.shields.io/npm/v/2048-webcomponent.svg?style=flat)](https://www.npmjs.com/package/2048-webcomponent)\n\nThis is **2048** as a **WebComponent**. Once you integrate it as described further, the game just follows the standard rules. To make a move, just use the arrow keys or swipe.\n\n## Getting Started\n\nInstall the package via `npm` or `yarn` and deliver the script to the user.\nThis can be done via `import`, `require` or just inserting a `script` tag.\n\n```shell\nnpm i 2048-webcomponent\n```\n\n```shell\nyarn add 2048-webcomponent\n```\n\n## Attributes\n\n| Param                      | Type     | Description                                                                      | Default |\n| -------------------------- | -------- | -------------------------------------------------------------------------------- | ------- |\n| `columns`                  | `number` | Amount of columns of the board.                                                  | `4`     |\n| `rows`                     | `number` | Amount of rows of the board.                                                     | `4`     |\n| `swipe-distance-threshold` | `number` | Amount of pixels required to trigger a swipe.                                    | `50`    |\n| `restart-selector`         | `string` | If present, attaches a click event listener to the element to trigger a restart. |         |\n\n## Events\n\nBy default, each event contains the current game state in `event.detail.game`. To discourage cheating, no events should be logged in the browser console.\n| Name | Type | Cancelable | Description | `detail` |\n| ---------------- | --------------- | ------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `2048:game-won` | `GameWonEvent` | :x: | User just won the game | \u003cul\u003e\u003cli\u003e`positions`: `Positions` current game state\u003c/li\u003e\u003c/ul\u003e |\n| `2048:game-lost` | `GameLostEvent` | :x: | User just lost the game | \u003cul\u003e\u003cli\u003e`positions`: `Positions` current game state\u003c/li\u003e\u003c/ul\u003e |\n| `2048:move` | `MoveEvent` | :white_check_mark: | User made a move | \u003cul\u003e\u003cli\u003e`positions`: `Positions` current game state\u003c/li\u003e\u003cli\u003e`direction`: `Direction` direction of the move\u003c/li\u003e\u003c/ul\u003e |\n| `2048:score` | `ScoreEvent` | :x: | Score has been updated | \u003cul\u003e\u003cli\u003e`positions`: `Positions` current game state\u003c/li\u003e\u003cli\u003e`oldScore`: `number \\| undefined` old score\u003c/li\u003e\u003cli\u003e`delta`: `number \\| undefined` difference between old and new score\u003c/li\u003e\u003cli\u003e`newScore`: `number` current score\u003c/li\u003e\u003c/ul\u003e |\n\n## Usage\n\n### Basic usage\n\nJust a basic 9x9 / 10 Mines 2048 game. Further examples can be **combined**.\n\n```html\n\u003cgame-2048\u003e\u003c/game-2048\u003e\n\n\u003cscript type=\"module\"\u003e\n  import '2048-webcomponent';\n  // Alternatives:\n  // import '2048-webcomponent/custom-element';\n  // import '2048-webcomponent/custom-element.min';\n\u003c/script\u003e\n```\n\n### Custom tag name\n\nDefine your own custom tag name\n\n```html\n\u003ccustom-game-2048\u003e\u003c/custom-game-2048\u003e\n\n\u003cscript type=\"module\"\u003e\n  import { Game } from '2048-webcomponent/game-2048';\n  // Alternative:\n  // import { Game } from '2048-webcomponent/game-2048.min';\n  window.customElements.define('custom-game-2048', Game);\n\u003c/script\u003e\n```\n\n### Provide a restart button\n\nProvide a selector where a \"click\"-event will be attached to, to restart the game.\n\n```html\n\u003cgame-2048 restart-selector=\"#restart-game-button\"\u003e\u003c/game-2048\u003e\n\u003cbutton id=\"restart-game-button\"\u003eRestart!\u003c/button\u003e\n\n\u003cscript type=\"module\"\u003e\n  import '2048-webcomponent';\n\u003c/script\u003e\n```\n\n### Trigger Restart via JavaScript\n\nWrite custom logic to restart the game.\n\n```html\n\u003cgame-2048 id=\"game\"\u003e\u003c/game-2048\u003e\n\u003cbutton id=\"restart-game-button-confirm\"\u003eRestart with Confirm!\u003c/button\u003e\n\n\u003cscript type=\"module\"\u003e\n  import '2048-webcomponent';\n\n  window.addEventListener('DOMContentLoaded', () =\u003e {\n    const game = document.querySelector('#game');\n\n    document.querySelector('#restart-game-button-confirm').addEventListener('click', (event) =\u003e {\n      event.preventDefault();\n      if (window.confirm('Are you sure, that you want to restart the game?')) {\n        game.restartGame();\n      }\n    });\n  });\n\u003c/script\u003e\n```\n\n### Custom win/lose event listener\n\nAttach an EventListener for the win/lose events.\n\n```html\n\u003cgame-2048 id=\"game\"\u003e\u003c/game-2048\u003e\n\n\u003cscript type=\"module\"\u003e\n  import '2048-webcomponent';\n\n  window.addEventListener('DOMContentLoaded', () =\u003e {\n    const game = document.querySelector('#game');\n\n    game.addEventListener('game-won', () =\u003e {\n      console.log('win');\n    });\n\n    game.addEventListener('game-lost', () =\u003e {\n      console.log('lose');\n    });\n  });\n\u003c/script\u003e\n```\n\n### Different initial game configurations\n\nOf course you can provide different configurations for the game.\n\n```html\n\u003cgame-2048 rows=\"5\" columns=\"5\"\u003e\u003c/game-2048\u003e\n\n\u003cscript type=\"module\"\u003e\n  import '2048-webcomponent';\n\u003c/script\u003e\n```\n\n## Example\n\nTry it out at [CodePen](https://codepen.io/manuelhenke/pen/ExoPKLZ).\n\n![Example Image](2048-example.png)\n\n## TypeScript\n\nThe whole package is written in TypeScript and therefore provides a strongly typed system via the `core` export of the package:\n\n```html\n\u003c!-- some-file.html --\u003e\n\u003cgame-2048 id=\"game\"\u003e\u003c/game-2048\u003e\n```\n\n```typescript\n// some-file.ts\nimport '2048-webcomponent/custom-element';\nimport { FieldInteractionEvent, FieldInteractionType } from '2048-webcomponent/core';\nimport type { Game } from '2048-webcomponent/core';\n\nconst game = document.querySelector('#game') as Game;\ngame.addEventListener('2048:field-interaction', (event: FieldInteractionEvent) =\u003e {\n  const { interaction } = event.detail;\n  switch (interaction.type) {\n    case FieldInteractionType.Unveiled:\n      console.log('Revealed field value:', interaction.value);\n      break;\n    case FieldInteractionType.FlagAction:\n    case FieldInteractionType.QuestionMarkAction:\n      console.log('Performed action:', interaction.action);\n      break;\n  }\n});\n```\n\n## Engine\n\nThe engine can be used as a standalone library (`commonjs` and `esm`) via the `engine` export of the package. This enables the usage of the engine in every application (server- or client-side).\n\n```javascript\nimport { Direction } from '2048-webcomponent/core';\nimport { Engine } from '2048-webcomponent/engine';\n\nconst engine = new Engine();\n\nengine.createBoard(3, 3);\n\nengine.move(Direction.Up);\n\nif (engine.isGameOver) {\n  // Restarts the game with the initial configuration\n  engine.restart();\n}\n```\n\n```javascript\nconst { Engine } = require('2048-webcomponent/engine');\n\nconst engine = new Engine();\n\n// ...\n```\n\n## License\n\n[MIT License](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanuelhenke%2F2048-webcomponent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanuelhenke%2F2048-webcomponent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanuelhenke%2F2048-webcomponent/lists"}