From 18be98aa2a74640dd2bd35c4988bc4226d89ffc8 Mon Sep 17 00:00:00 2001 From: Didictateur Date: Sat, 12 Apr 2025 14:40:37 +0200 Subject: [PATCH] move tests and replace by mode production --- build/dp0.js | 137 +------------------------ build/dp1.js | 127 +---------------------- build/dp2.js | 127 +---------------------- build/dp3.js | 127 +---------------------- build/dp4.js | 157 +---------------------------- build/dp5.js | 67 +----------- build/test.js | 157 +---------------------------- build/test_hand.js | 127 +---------------------- build/test_yakus.js | 137 +------------------------ build/txt0.js | 97 +----------------- build/txt1.js | 97 +----------------- build/txt2.js | 97 +----------------- build/txt3.js | 97 +----------------- build/txt4.js | 97 +----------------- build/txt5.js | 97 +----------------- {src/tests => tests}/assert.ts | 0 {src/tests => tests}/test.ts | 0 {src/tests => tests}/test_hand.ts | 2 +- {src/tests => tests}/test_yakus.ts | 5 +- webpack.config.js | 2 +- 20 files changed, 19 insertions(+), 1735 deletions(-) rename {src/tests => tests}/assert.ts (100%) rename {src/tests => tests}/test.ts (100%) rename {src/tests => tests}/test_hand.ts (95%) rename {src/tests => tests}/test_yakus.ts (98%) diff --git a/build/dp0.js b/build/dp0.js index 0aca64e..7d541ae 100644 --- a/build/dp0.js +++ b/build/dp0.js @@ -1,136 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/deck.ts": -/*!*********************!*\ - !*** ./src/deck.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Deck: () => (/* binding */ Deck)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hand */ \"./src/hand.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\nclass Deck {\n constructor(allowRed = false) {\n this.tiles = [];\n this.tileIndexMap = new Map();\n this.initTiles(allowRed);\n }\n /**\n * Displays all tile families on the canvas\n */\n displayFamilies(ctx, x, y, size, xOffset = 0, yOffset = 0) {\n let posX = x;\n let posY = y;\n // Define tile layouts for each family\n const familyLayouts = [\n { family: 1, start: 1, end: 9 }, // First suit\n { family: 2, start: 1, end: 9 }, // Second suit\n { family: 3, start: 1, end: 9 }, // Third suit\n { family: 4, start: 1, end: 4 }, // Winds\n { family: 5, start: 1, end: 3 } // Dragons\n ];\n for (const layout of familyLayouts) {\n for (let j = layout.start; j <= layout.end; j++) {\n const tile = this.find(layout.family, j);\n if (tile) {\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n }\n /**\n * Returns the number of tiles in the deck\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Removes and returns the last tile from the deck\n * @throws Error if the deck is empty\n */\n pop() {\n if (this.tiles.length === 0) {\n throw new Error(\"Cannot pop from an empty deck\");\n }\n const tile = this.tiles.pop();\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key);\n if (indices && indices.length > 0) {\n indices.pop(); // Remove the last index\n if (indices.length === 0) {\n this.tileIndexMap.delete(key);\n }\n else {\n this.tileIndexMap.set(key, indices);\n }\n }\n return tile;\n }\n /**\n * Adds a tile to the deck\n */\n push(tile) {\n const index = this.tiles.length;\n this.tiles.push(tile);\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(index);\n this.tileIndexMap.set(key, indices);\n }\n /**\n * Finds and removes a specific tile from the deck\n */\n find(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n if (!indices || indices.length === 0) {\n return undefined;\n }\n // Get the first occurrence\n const index = indices[0];\n if (index >= this.tiles.length) {\n // Handle potential out-of-sync errors\n this.rebuildIndexMap();\n return this.find(family, value);\n }\n // Swap with the first element for efficient removal\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n // Update indices in the map\n this.updateIndicesAfterSwap(0, index);\n // Remove and return the tile\n const tile = this.tiles.shift();\n // Update all indices after shift\n this.decrementIndicesAfterShift();\n return tile;\n }\n /**\n * Counts the number of tiles with specific family and value\n */\n count(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n return indices ? indices.length : 0;\n }\n /**\n * Shuffles the deck using Fisher-Yates algorithm\n */\n shuffle() {\n // Fisher-Yates shuffle algorithm\n for (let i = this.tiles.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]];\n }\n // Rebuild the index map after shuffling\n this.rebuildIndexMap();\n }\n /**\n * Creates a random hand from the deck\n */\n getRandomHand() {\n const hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n // Handle case where deck doesn't have enough tiles\n if (this.tiles.length < 13) {\n throw new Error(\"Not enough tiles in deck to create a hand\");\n }\n for (let i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n }\n /**\n * Cleans up resources used by tiles and empties the deck\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n this.tileIndexMap.clear();\n }\n /**\n * Preloads all tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n const preloadPromises = this.tiles.map(tile => tile.preloadImg());\n yield Promise.all(preloadPromises);\n });\n }\n /**\n * Creates a unique key for each tile type\n */\n getTileKey(family, value) {\n return `${family}-${value}`;\n }\n /**\n * Updates the index map after swapping two tiles\n */\n updateIndicesAfterSwap(index1, index2) {\n if (index1 === index2)\n return;\n const tile1 = this.tiles[index1];\n const tile2 = this.tiles[index2];\n const key1 = this.getTileKey(tile1.getFamily(), tile1.getValue());\n const key2 = this.getTileKey(tile2.getFamily(), tile2.getValue());\n const indices1 = this.tileIndexMap.get(key1) || [];\n const indices2 = this.tileIndexMap.get(key2) || [];\n // Update indices\n const idx1 = indices1.indexOf(index2);\n const idx2 = indices2.indexOf(index1);\n if (idx1 !== -1)\n indices1[idx1] = index1;\n if (idx2 !== -1)\n indices2[idx2] = index2;\n this.tileIndexMap.set(key1, indices1);\n this.tileIndexMap.set(key2, indices2);\n }\n /**\n * Decrements all indices after a shift operation\n */\n decrementIndicesAfterShift() {\n var _a;\n for (const [key, indices] of this.tileIndexMap.entries()) {\n this.tileIndexMap.set(key, indices\n .filter(idx => idx !== 0) // Remove the index 0 that was shifted\n .map(idx => (idx > 0 ? idx - 1 : idx)) // Decrement all indices\n );\n // Clean up empty entries\n if (((_a = this.tileIndexMap.get(key)) === null || _a === void 0 ? void 0 : _a.length) === 0) {\n this.tileIndexMap.delete(key);\n }\n }\n }\n /**\n * Rebuilds the entire index map from scratch\n */\n rebuildIndexMap() {\n this.tileIndexMap.clear();\n for (let i = 0; i < this.tiles.length; i++) {\n const tile = this.tiles[i];\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(i);\n this.tileIndexMap.set(key, indices);\n }\n }\n /**\n * Initializes the deck with all tiles\n */\n initTiles(allowRed) {\n // Create suits (families 1-3)\n for (let family = 1; family <= 3; family++) {\n for (let value = 1; value <= 9; value++) {\n // Each value appears 4 times in a suit\n const isRedFive = value === 5 && allowRed;\n // Add 3 regular tiles\n for (let i = 0; i < 3; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n // Add the 4th tile (potentially red five)\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, isRedFive));\n }\n }\n // Create winds (family 4)\n for (let value = 1; value <= 4; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, value, false));\n }\n }\n // Create dragons (family 5)\n for (let value = 1; value <= 3; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, value, false));\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); - -/***/ }), - -/***/ "./src/display/dp0.ts": -/*!****************************!*\ - !*** ./src/display/dp0.ts ***! - \****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ cleanup: () => (/* binding */ cleanup),\n/* harmony export */ initDisplay: () => (/* binding */ initDisplay)\n/* harmony export */ });\n/* harmony import */ var _rain__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../rain */ \"./src/rain.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n// Configuration globale\nconst CANVAS_ID = \"myCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };\nconst MOUSE = { x: 0, y: 0 };\nconst FPS = 60; // Réduit de 90 à 60 pour de meilleures performances\nconst FRAME_INTERVAL = 1000 / FPS;\nconst RAIN = new _rain__WEBPACK_IMPORTED_MODULE_0__.Rain();\n// Optimisation des références\nlet animationFrameId;\nlet lastFrameTime = 0;\nlet accumulatedTime = 0;\nconst callbacks = [];\nconst timeStep = 1 / FPS;\n// Pré-calcul des dimensions\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\", { alpha: false });\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\n// Cache statique\nconst staticCanvas = document.createElement('canvas');\nconst staticCtx = staticCanvas.getContext(\"2d\", { alpha: false });\nstaticCanvas.width = canvas.width;\nstaticCanvas.height = canvas.height;\n// Optimisation du rendu avec requestAnimationFrame\nfunction drawFrame(deltaTime) {\n if (!ctx)\n return;\n // Mise à jour avec pas de temps fixe pour stabilité physique\n accumulatedTime += deltaTime / 1000; // Convertir en secondes\n // Mettre à jour la simulation avec un pas de temps fixe\n while (accumulatedTime >= timeStep) {\n RAIN.update(timeStep);\n accumulatedTime -= timeStep;\n }\n // Optimisation du rendu\n staticCtx.fillStyle = \"#007730\"; // Couleur de fond\n staticCtx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n // Dessin de la pluie\n RAIN.drawRain(staticCtx);\n // Copier le buffer au canvas principal\n ctx.drawImage(staticCanvas, 0, 0);\n}\nfunction animationLoop(currentTime) {\n if (!lastFrameTime) {\n lastFrameTime = currentTime;\n animationFrameId = requestAnimationFrame(animationLoop);\n return;\n }\n const deltaTime = currentTime - lastFrameTime;\n lastFrameTime = currentTime;\n // Limiter la fréquence des mises à jour si le navigateur est trop lent\n if (deltaTime < 100) { // Ignorer les deltas trop grands (changement d'onglet, etc.)\n drawFrame(deltaTime);\n }\n animationFrameId = requestAnimationFrame(animationLoop);\n}\nfunction initEventListeners() {\n // Gestion des événements de redimensionnement et de pause\n const handleVisibilityChange = () => {\n if (document.hidden) {\n cancelAnimationFrame(animationFrameId);\n lastFrameTime = 0;\n }\n else {\n lastFrameTime = performance.now();\n animationFrameId = requestAnimationFrame(animationLoop);\n }\n };\n document.addEventListener('visibilitychange', handleVisibilityChange);\n // Ajouter le nettoyage de l'event listener\n callbacks.push(() => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n });\n}\nfunction cleanup() {\n cancelAnimationFrame(animationFrameId);\n callbacks.forEach(fn => fn());\n}\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function* () {\n if (!ctx) {\n console.error(\"Context canvas indisponible\");\n return;\n }\n console.log(\"Load beginning\");\n try {\n // Préchargement des ressources\n yield RAIN.preloadRain();\n console.log(\"Loading completed\");\n // Initialiser les écouteurs d'événements\n initEventListeners();\n // Démarrer la boucle d'animation avec le temps actuel\n lastFrameTime = performance.now();\n animationFrameId = requestAnimationFrame(animationLoop);\n window.cleanup = cleanup;\n }\n catch (error) {\n console.error(\"Erreur lors de l'initialisation:\", error);\n }\n });\n}\n// Initialisation automatique si le script est chargé directement\nif (typeof window !== 'undefined') {\n initDisplay().catch(console.error);\n}\n\n\n//# sourceURL=webpack:///./src/display/dp0.ts?"); - -/***/ }), - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/rain.ts": -/*!*********************!*\ - !*** ./src/rain.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Rain: () => (/* binding */ Rain)\n/* harmony export */ });\n/* harmony import */ var _deck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./deck */ \"./src/deck.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\nconst G = 100;\nconst SIZE = 1;\nconst LIMIT_MAX = 1100;\nconst SPAWN_CHANCE = 0.75;\nconst INITIAL_Y = -150;\nconst INITIAL_VY = 50;\nconst MAX_X = 1000;\nconst ROTATION_FACTOR = Math.PI;\nconst MOMENTUM_RANGE = 1;\nclass FallingTile {\n constructor(tile, x, y, orientation, momentum) {\n this.vy = INITIAL_VY;\n this.tile = tile;\n this.x = x;\n this.y = y;\n this.orientation = orientation;\n this.momentum = momentum;\n }\n update(dt) {\n this.vy += dt * G;\n this.y += dt * this.vy;\n this.orientation += dt * this.momentum;\n }\n isOutside() {\n return this.y > LIMIT_MAX;\n }\n getTile() {\n return this.tile;\n }\n drawFallingTile(ctx) {\n var _a;\n (_a = this.tile) === null || _a === void 0 ? void 0 : _a.drawTile(ctx, this.x, this.y, SIZE, false, this.orientation, false, false);\n }\n}\nclass Rain {\n constructor() {\n this.tiles = [];\n this.tileAddTimer = 0;\n this.deck = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(true);\n }\n update(dt) {\n let i = this.tiles.length;\n while (i--) {\n const tile = this.tiles[i];\n tile.update(dt);\n if (tile.isOutside()) {\n this.deck.push(tile.getTile());\n this.tiles.splice(i, 1);\n }\n }\n this.tileAddTimer += dt;\n if (this.tileAddTimer >= (1 / SPAWN_CHANCE)) {\n this.addFallingTile();\n this.tileAddTimer = 0;\n }\n else if (Math.random() < SPAWN_CHANCE * dt) {\n this.addFallingTile();\n }\n }\n addFallingTile() {\n if (this.deck.length() === 0) {\n return;\n }\n if (this.deck.length() > 1) {\n this.deck.shuffle();\n }\n const newTile = new FallingTile(this.deck.pop(), Math.floor(Math.random() * MAX_X), INITIAL_Y, Math.random() * ROTATION_FACTOR, (Math.random() * 2 - 1) * MOMENTUM_RANGE);\n this.tiles.push(newTile);\n }\n drawRain(ctx) {\n for (const tile of this.tiles) {\n tile.drawFallingTile(ctx);\n }\n }\n preloadRain() {\n return __awaiter(this, void 0, void 0, function* () {\n console.log(\"preload rain\");\n yield this.deck.preload();\n });\n }\n}\n\n\n//# sourceURL=webpack:///./src/rain.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/display/dp0.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";class t{constructor(t,i,e){this.family=t,this.value=i,this.red=e,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,s=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((e=void 0)||(e=Promise))((function(n,l){function h(t){try{a(s.next(t))}catch(t){l(t)}}function r(t){try{a(s.throw(t))}catch(t){l(t)}}function a(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(h,r)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}loadImg(t,i){return new Promise(((e,s)=>{t.onload=()=>e(),t.onerror=()=>s(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,e){this.tiles=t,this.stolenFrom=i,this.belongsTo=e}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,e,s,n,l,h){t.save(),t.translate(525,525),t.rotate(l),t.translate(-525,-525);const r=75*n,a=90*n,o=25*n/2,c=(this.belongsTo-this.stolenFrom-1+4)%4,u=void 0===h?-1:h.getFamily(),d=void 0===h?0:h.getValue(),m=(i,e,s,l)=>{i.drawTile(t,e,s,n,!1,l,i.isEqual(u,d))},g=Math.PI/2;switch(c){case 0:m(this.tiles[0],i,e+o,g),m(this.tiles[1],i+a,e,0),m(this.tiles[2],i+a+r+s*n,e,0);break;case 1:m(this.tiles[0],i,e,0),m(this.tiles[1],i+a,e+o,-g),m(this.tiles[2],i+a+r+3*s*n,e,0);break;case 2:m(this.tiles[0],i,e,0),m(this.tiles[1],i+r+s*n,e,0),m(this.tiles[2],i+a+r+s*n,e+o,-g);break;default:console.error(`Position non prise en charge: ${c}`)}t.restore()}}const e="m",s=1,n="p",l=2,h="s",r=3,a="w",o=4,c="d",u=5;class d{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;ie.getFamily()===t&&e.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((e=>e.getFamily()===t&&e.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),e=i.getFamily(),s=i.getValue();if(this.count(e,s)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(e,s)>=2){const e=this.tryFormTriplet(i,t);if(e)return e}const n=this.count(e,s-1)>0,l=this.count(e,s-2)>0;if(n&&l){const e=this.tryFormSequence(i,t);if(e)return e}this.tiles.push(i),this.sort()}tryFormPair(t){const e=this.find(t.getFamily(),t.getValue()),s=this.toGroup(!0);if(this.tiles.push(e),this.sort(),void 0!==s)return this.tiles.push(t),this.sort(),s.push(new i([t,e],0,0)),s}tryFormTriplet(t,e){const s=this.find(t.getFamily(),t.getValue()),n=this.find(t.getFamily(),t.getValue()),l=this.toGroup(e);if(this.tiles.push(s),this.tiles.push(n),this.sort(),void 0!==l)return l.push(new i([t,s,n],0,0)),this.tiles.push(t),this.sort(),l}tryFormSequence(t,e){const s=this.find(t.getFamily(),t.getValue()-1),n=this.find(t.getFamily(),t.getValue()-2),l=this.toGroup(e);if(this.tiles.push(s),this.tiles.push(n),this.sort(),void 0!==l)return l.push(new i([n,s,t],0,0)),this.tiles.push(t),this.sort(),l}drawHand(t,i,e,s,n,l=void 0,h=!1,r=0){const a=(75+s)*n,o=Math.cos(r)*a,c=Math.sin(r)*a;for(let s=0;st.preloadImg())))},new((e=void 0)||(e=Promise))((function(n,l){function h(t){try{a(s.next(t))}catch(t){l(t)}}function r(t){try{a(s.throw(t))}catch(t){l(t)}}function a(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(h,r)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class m{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,e,s,n=0,l=0){let h=i,r=e;const a=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const e of a){for(let i=e.start;i<=e.end;i++){const l=this.find(e.family,i);l&&(l.drawTile(t,h,r,s,!1,0,!1),h+=(75+n)*s)}h=i,r+=(100+l)*s}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(i);return e&&e.length>0&&(e.pop(),0===e.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,e)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const e=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(e)||[];s.push(i),this.tileIndexMap.set(e,s)}find(t,i){const e=this.getTileKey(t,i),s=this.tileIndexMap.get(e);if(!s||0===s.length)return;const n=s[0];if(n>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[n],this.tiles[0]]=[this.tiles[0],this.tiles[n]],this.updateIndicesAfterSwap(0,n);const l=this.tiles.shift();return this.decrementIndicesAfterShift(),l}count(t,i){const e=this.getTileKey(t,i),s=this.tileIndexMap.get(e);return s?s.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new d;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,s=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((e=void 0)||(e=Promise))((function(n,l){function h(t){try{a(s.next(t))}catch(t){l(t)}}function r(t){try{a(s.throw(t))}catch(t){l(t)}}function a(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(h,r)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const e=this.tiles[t],s=this.tiles[i],n=this.getTileKey(e.getFamily(),e.getValue()),l=this.getTileKey(s.getFamily(),s.getValue()),h=this.tileIndexMap.get(n)||[],r=this.tileIndexMap.get(l)||[],a=h.indexOf(i),o=r.indexOf(t);-1!==a&&(h[a]=t),-1!==o&&(r[o]=i),this.tileIndexMap.set(n,h),this.tileIndexMap.set(l,r)}decrementIndicesAfterShift(){var t;for(const[i,e]of this.tileIndexMap.entries())this.tileIndexMap.set(i,e.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;t1100}getTile(){return this.tile}drawFallingTile(t){var i;null===(i=this.tile)||void 0===i||i.drawTile(t,this.x,this.y,1,!1,this.orientation,!1,!1)}}const p=new class{constructor(){this.tiles=[],this.tileAddTimer=0,this.deck=new m(!0)}update(t){let i=this.tiles.length;for(;i--;){const e=this.tiles[i];e.update(t),e.isOutside()&&(this.deck.push(e.getTile()),this.tiles.splice(i,1))}this.tileAddTimer+=t,this.tileAddTimer>=1/.75?(this.addFallingTile(),this.tileAddTimer=0):Math.random()<.75*t&&this.addFallingTile()}addFallingTile(){if(0===this.deck.length())return;this.deck.length()>1&&this.deck.shuffle();const t=new f(this.deck.pop(),Math.floor(1e3*Math.random()),-150,Math.random()*g,1*(2*Math.random()-1));this.tiles.push(t)}drawRain(t){for(const i of this.tiles)i.drawFallingTile(t)}preloadRain(){return t=this,i=void 0,s=function*(){console.log("preload rain"),yield this.deck.preload()},new((e=void 0)||(e=Promise))((function(n,l){function h(t){try{a(s.next(t))}catch(t){l(t)}}function r(t){try{a(s.throw(t))}catch(t){l(t)}}function a(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(h,r)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}};let y,v=0,w=0;const I=[],x=1/60,F=document.getElementById("myCanvas"),T=F.getContext("2d",{alpha:!1});F.width=1050,F.height=1050;const M=document.createElement("canvas"),S=M.getContext("2d",{alpha:!1});function k(t){if(!v)return v=t,void(y=requestAnimationFrame(k));const i=t-v;v=t,i<100&&function(t){if(T){for(w+=t/1e3;w>=x;)p.update(x),w-=x;S.fillStyle="#007730",S.fillRect(0,0,1050,1050),p.drawRain(S),T.drawImage(M,0,0)}}(i),y=requestAnimationFrame(k)}function V(){cancelAnimationFrame(y),I.forEach((t=>t()))}M.width=F.width,M.height=F.height,"undefined"!=typeof window&&function(){return t=this,i=void 0,s=function*(){if(T){console.log("Load beginning");try{yield p.preloadRain(),console.log("Loading completed"),function(){const t=()=>{document.hidden?(cancelAnimationFrame(y),v=0):(v=performance.now(),y=requestAnimationFrame(k))};document.addEventListener("visibilitychange",t),I.push((()=>{document.removeEventListener("visibilitychange",t)}))}(),v=performance.now(),y=requestAnimationFrame(k),window.cleanup=V}catch(t){console.error("Erreur lors de l'initialisation:",t)}}else console.error("Context canvas indisponible")},new((e=void 0)||(e=Promise))((function(n,l){function h(t){try{a(s.next(t))}catch(t){l(t)}}function r(t){try{a(s.throw(t))}catch(t){l(t)}}function a(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(h,r)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}().catch(console.error)})(); \ No newline at end of file diff --git a/build/dp1.js b/build/dp1.js index 95e962c..2d7910a 100644 --- a/build/dp1.js +++ b/build/dp1.js @@ -1,126 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/deck.ts": -/*!*********************!*\ - !*** ./src/deck.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Deck: () => (/* binding */ Deck)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hand */ \"./src/hand.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\nclass Deck {\n constructor(allowRed = false) {\n this.tiles = [];\n this.tileIndexMap = new Map();\n this.initTiles(allowRed);\n }\n /**\n * Displays all tile families on the canvas\n */\n displayFamilies(ctx, x, y, size, xOffset = 0, yOffset = 0) {\n let posX = x;\n let posY = y;\n // Define tile layouts for each family\n const familyLayouts = [\n { family: 1, start: 1, end: 9 }, // First suit\n { family: 2, start: 1, end: 9 }, // Second suit\n { family: 3, start: 1, end: 9 }, // Third suit\n { family: 4, start: 1, end: 4 }, // Winds\n { family: 5, start: 1, end: 3 } // Dragons\n ];\n for (const layout of familyLayouts) {\n for (let j = layout.start; j <= layout.end; j++) {\n const tile = this.find(layout.family, j);\n if (tile) {\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n }\n /**\n * Returns the number of tiles in the deck\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Removes and returns the last tile from the deck\n * @throws Error if the deck is empty\n */\n pop() {\n if (this.tiles.length === 0) {\n throw new Error(\"Cannot pop from an empty deck\");\n }\n const tile = this.tiles.pop();\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key);\n if (indices && indices.length > 0) {\n indices.pop(); // Remove the last index\n if (indices.length === 0) {\n this.tileIndexMap.delete(key);\n }\n else {\n this.tileIndexMap.set(key, indices);\n }\n }\n return tile;\n }\n /**\n * Adds a tile to the deck\n */\n push(tile) {\n const index = this.tiles.length;\n this.tiles.push(tile);\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(index);\n this.tileIndexMap.set(key, indices);\n }\n /**\n * Finds and removes a specific tile from the deck\n */\n find(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n if (!indices || indices.length === 0) {\n return undefined;\n }\n // Get the first occurrence\n const index = indices[0];\n if (index >= this.tiles.length) {\n // Handle potential out-of-sync errors\n this.rebuildIndexMap();\n return this.find(family, value);\n }\n // Swap with the first element for efficient removal\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n // Update indices in the map\n this.updateIndicesAfterSwap(0, index);\n // Remove and return the tile\n const tile = this.tiles.shift();\n // Update all indices after shift\n this.decrementIndicesAfterShift();\n return tile;\n }\n /**\n * Counts the number of tiles with specific family and value\n */\n count(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n return indices ? indices.length : 0;\n }\n /**\n * Shuffles the deck using Fisher-Yates algorithm\n */\n shuffle() {\n // Fisher-Yates shuffle algorithm\n for (let i = this.tiles.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]];\n }\n // Rebuild the index map after shuffling\n this.rebuildIndexMap();\n }\n /**\n * Creates a random hand from the deck\n */\n getRandomHand() {\n const hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n // Handle case where deck doesn't have enough tiles\n if (this.tiles.length < 13) {\n throw new Error(\"Not enough tiles in deck to create a hand\");\n }\n for (let i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n }\n /**\n * Cleans up resources used by tiles and empties the deck\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n this.tileIndexMap.clear();\n }\n /**\n * Preloads all tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n const preloadPromises = this.tiles.map(tile => tile.preloadImg());\n yield Promise.all(preloadPromises);\n });\n }\n /**\n * Creates a unique key for each tile type\n */\n getTileKey(family, value) {\n return `${family}-${value}`;\n }\n /**\n * Updates the index map after swapping two tiles\n */\n updateIndicesAfterSwap(index1, index2) {\n if (index1 === index2)\n return;\n const tile1 = this.tiles[index1];\n const tile2 = this.tiles[index2];\n const key1 = this.getTileKey(tile1.getFamily(), tile1.getValue());\n const key2 = this.getTileKey(tile2.getFamily(), tile2.getValue());\n const indices1 = this.tileIndexMap.get(key1) || [];\n const indices2 = this.tileIndexMap.get(key2) || [];\n // Update indices\n const idx1 = indices1.indexOf(index2);\n const idx2 = indices2.indexOf(index1);\n if (idx1 !== -1)\n indices1[idx1] = index1;\n if (idx2 !== -1)\n indices2[idx2] = index2;\n this.tileIndexMap.set(key1, indices1);\n this.tileIndexMap.set(key2, indices2);\n }\n /**\n * Decrements all indices after a shift operation\n */\n decrementIndicesAfterShift() {\n var _a;\n for (const [key, indices] of this.tileIndexMap.entries()) {\n this.tileIndexMap.set(key, indices\n .filter(idx => idx !== 0) // Remove the index 0 that was shifted\n .map(idx => (idx > 0 ? idx - 1 : idx)) // Decrement all indices\n );\n // Clean up empty entries\n if (((_a = this.tileIndexMap.get(key)) === null || _a === void 0 ? void 0 : _a.length) === 0) {\n this.tileIndexMap.delete(key);\n }\n }\n }\n /**\n * Rebuilds the entire index map from scratch\n */\n rebuildIndexMap() {\n this.tileIndexMap.clear();\n for (let i = 0; i < this.tiles.length; i++) {\n const tile = this.tiles[i];\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(i);\n this.tileIndexMap.set(key, indices);\n }\n }\n /**\n * Initializes the deck with all tiles\n */\n initTiles(allowRed) {\n // Create suits (families 1-3)\n for (let family = 1; family <= 3; family++) {\n for (let value = 1; value <= 9; value++) {\n // Each value appears 4 times in a suit\n const isRedFive = value === 5 && allowRed;\n // Add 3 regular tiles\n for (let i = 0; i < 3; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n // Add the 4th tile (potentially red five)\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, isRedFive));\n }\n }\n // Create winds (family 4)\n for (let value = 1; value <= 4; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, value, false));\n }\n }\n // Create dragons (family 5)\n for (let value = 1; value <= 3; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, value, false));\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); - -/***/ }), - -/***/ "./src/display/dp1.ts": -/*!****************************!*\ - !*** ./src/display/dp1.ts ***! - \****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _deck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deck */ \"./src/deck.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\nfunction preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function* () {\n yield deck.preload();\n });\n}\nfunction display() {\n return __awaiter(this, void 0, void 0, function* () {\n const canvas = document.getElementById(\"myCanvas\");\n if (canvas) {\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n // tapis\n ctx.fillStyle = \"#007730\";\n ctx.fillRect(50, 50, 1000, 1000);\n // tuiles\n let x = 180;\n let y = 80;\n let size = 0.75;\n let xos = 40;\n let yos = 100;\n const deck = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false);\n yield preloadDeck(deck);\n deck.displayFamilies(ctx, x, y, size, xos, yos);\n // texte\n ctx.fillStyle = \"#DFDFFF\";\n ctx.font = \"30px serif\";\n let delta = 50 * size;\n let eps = 30 * size;\n ctx.fillText(\"Caractère:\", 55, y + delta + eps);\n ctx.fillText(\"Rond:\", 55, y + 3 * delta + yos * size + eps);\n ctx.fillText(\"Bambou:\", 55, y + 5 * delta + 2 * yos * size + eps);\n ctx.fillText(\"Vent:\", 55, y + 7 * delta + 3 * yos * size + eps);\n ctx.fillText(\"Dragon:\", 55, y + 9 * delta + 4 * yos * size + eps);\n // familles\n for (let i = 0; i < 3; i++) {\n for (let j = 0; j < 9; j++) {\n ctx.fillText(String(j + 1), x + j * (xos + 75) * size + 30 * size, y + i * (yos + 100) * size + 150 * size);\n }\n }\n // vent\n ctx.fillText(\"Est\", x + 0 * (xos + 75) * size, y + 3 * (yos + 100) * size + 150 * size);\n ctx.fillText(\"Sud\", x + 1 * (xos + 75) * size, y + 3 * (yos + 100) * size + 150 * size);\n ctx.fillText(\"Ouest\", x + 2 * (xos + 75) * size, y + 3 * (yos + 100) * size + 150 * size);\n ctx.fillText(\"Nord\", x + 3 * (xos + 75) * size, y + 3 * (yos + 100) * size + 150 * size);\n // dragon\n ctx.fillText(\"Rouge\", x + 0 * (xos + 75) * size, y + 4 * (yos + 100) * size + 150 * size);\n ctx.fillText(\"Vert\", x + 1 * (xos + 75) * size, y + 4 * (yos + 100) * size + 150 * size);\n ctx.fillText(\"Blanc\", x + 2 * (xos + 75) * size, y + 4 * (yos + 100) * size + 150 * size);\n window.cleanup = () => {\n deck.cleanup();\n };\n }\n else {\n console.error(\"Impossible d'obtenir le contexte du canvas.\");\n }\n }\n else {\n console.error(\"Canvas introuvable dans le DOM.\");\n }\n });\n}\ndisplay();\n\n\n//# sourceURL=webpack:///./src/display/dp1.ts?"); - -/***/ }), - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/display/dp1.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";class t{constructor(t,i,e){this.family=t,this.value=i,this.red=e,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,s=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((e=void 0)||(e=Promise))((function(l,n){function r(t){try{a(s.next(t))}catch(t){n(t)}}function h(t){try{a(s.throw(t))}catch(t){n(t)}}function a(t){var i;t.done?l(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(r,h)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}loadImg(t,i){return new Promise(((e,s)=>{t.onload=()=>e(),t.onerror=()=>s(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,e){this.tiles=t,this.stolenFrom=i,this.belongsTo=e}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,e,s,l,n,r){t.save(),t.translate(525,525),t.rotate(n),t.translate(-525,-525);const h=75*l,a=90*l,o=25*l/2,u=(this.belongsTo-this.stolenFrom-1+4)%4,c=void 0===r?-1:r.getFamily(),f=void 0===r?0:r.getValue(),d=(i,e,s,n)=>{i.drawTile(t,e,s,l,!1,n,i.isEqual(c,f))},g=Math.PI/2;switch(u){case 0:d(this.tiles[0],i,e+o,g),d(this.tiles[1],i+a,e,0),d(this.tiles[2],i+a+h+s*l,e,0);break;case 1:d(this.tiles[0],i,e,0),d(this.tiles[1],i+a,e+o,-g),d(this.tiles[2],i+a+h+3*s*l,e,0);break;case 2:d(this.tiles[0],i,e,0),d(this.tiles[1],i+h+s*l,e,0),d(this.tiles[2],i+a+h+s*l,e+o,-g);break;default:console.error(`Position non prise en charge: ${u}`)}t.restore()}}const e="m",s=1,l="p",n=2,r="s",h=3,a="w",o=4,u="d",c=5;class f{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;ie.getFamily()===t&&e.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((e=>e.getFamily()===t&&e.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),e=i.getFamily(),s=i.getValue();if(this.count(e,s)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(e,s)>=2){const e=this.tryFormTriplet(i,t);if(e)return e}const l=this.count(e,s-1)>0,n=this.count(e,s-2)>0;if(l&&n){const e=this.tryFormSequence(i,t);if(e)return e}this.tiles.push(i),this.sort()}tryFormPair(t){const e=this.find(t.getFamily(),t.getValue()),s=this.toGroup(!0);if(this.tiles.push(e),this.sort(),void 0!==s)return this.tiles.push(t),this.sort(),s.push(new i([t,e],0,0)),s}tryFormTriplet(t,e){const s=this.find(t.getFamily(),t.getValue()),l=this.find(t.getFamily(),t.getValue()),n=this.toGroup(e);if(this.tiles.push(s),this.tiles.push(l),this.sort(),void 0!==n)return n.push(new i([t,s,l],0,0)),this.tiles.push(t),this.sort(),n}tryFormSequence(t,e){const s=this.find(t.getFamily(),t.getValue()-1),l=this.find(t.getFamily(),t.getValue()-2),n=this.toGroup(e);if(this.tiles.push(s),this.tiles.push(l),this.sort(),void 0!==n)return n.push(new i([l,s,t],0,0)),this.tiles.push(t),this.sort(),n}drawHand(t,i,e,s,l,n=void 0,r=!1,h=0){const a=(75+s)*l,o=Math.cos(h)*a,u=Math.sin(h)*a;for(let s=0;st.preloadImg())))},new((e=void 0)||(e=Promise))((function(l,n){function r(t){try{a(s.next(t))}catch(t){n(t)}}function h(t){try{a(s.throw(t))}catch(t){n(t)}}function a(t){var i;t.done?l(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(r,h)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class d{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,e,s,l=0,n=0){let r=i,h=e;const a=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const e of a){for(let i=e.start;i<=e.end;i++){const n=this.find(e.family,i);n&&(n.drawTile(t,r,h,s,!1,0,!1),r+=(75+l)*s)}r=i,h+=(100+n)*s}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(i);return e&&e.length>0&&(e.pop(),0===e.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,e)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const e=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(e)||[];s.push(i),this.tileIndexMap.set(e,s)}find(t,i){const e=this.getTileKey(t,i),s=this.tileIndexMap.get(e);if(!s||0===s.length)return;const l=s[0];if(l>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[l],this.tiles[0]]=[this.tiles[0],this.tiles[l]],this.updateIndicesAfterSwap(0,l);const n=this.tiles.shift();return this.decrementIndicesAfterShift(),n}count(t,i){const e=this.getTileKey(t,i),s=this.tileIndexMap.get(e);return s?s.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new f;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,s=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((e=void 0)||(e=Promise))((function(l,n){function r(t){try{a(s.next(t))}catch(t){n(t)}}function h(t){try{a(s.throw(t))}catch(t){n(t)}}function a(t){var i;t.done?l(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(r,h)}a((s=s.apply(t,i||[])).next())}));var t,i,e,s}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const e=this.tiles[t],s=this.tiles[i],l=this.getTileKey(e.getFamily(),e.getValue()),n=this.getTileKey(s.getFamily(),s.getValue()),r=this.tileIndexMap.get(l)||[],h=this.tileIndexMap.get(n)||[],a=r.indexOf(i),o=h.indexOf(t);-1!==a&&(r[a]=t),-1!==o&&(h[o]=i),this.tileIndexMap.set(l,r),this.tileIndexMap.set(n,h)}decrementIndicesAfterShift(){var t;for(const[i,e]of this.tileIndexMap.entries())this.tileIndexMap.set(i,e.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;t{r.cleanup()}}else console.error("Impossible d'obtenir le contexte du canvas.")}else console.error("Canvas introuvable dans le DOM.")}))}()})(); \ No newline at end of file diff --git a/build/dp2.js b/build/dp2.js index 53a8581..33ba4fb 100644 --- a/build/dp2.js +++ b/build/dp2.js @@ -1,126 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/deck.ts": -/*!*********************!*\ - !*** ./src/deck.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Deck: () => (/* binding */ Deck)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hand */ \"./src/hand.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\nclass Deck {\n constructor(allowRed = false) {\n this.tiles = [];\n this.tileIndexMap = new Map();\n this.initTiles(allowRed);\n }\n /**\n * Displays all tile families on the canvas\n */\n displayFamilies(ctx, x, y, size, xOffset = 0, yOffset = 0) {\n let posX = x;\n let posY = y;\n // Define tile layouts for each family\n const familyLayouts = [\n { family: 1, start: 1, end: 9 }, // First suit\n { family: 2, start: 1, end: 9 }, // Second suit\n { family: 3, start: 1, end: 9 }, // Third suit\n { family: 4, start: 1, end: 4 }, // Winds\n { family: 5, start: 1, end: 3 } // Dragons\n ];\n for (const layout of familyLayouts) {\n for (let j = layout.start; j <= layout.end; j++) {\n const tile = this.find(layout.family, j);\n if (tile) {\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n }\n /**\n * Returns the number of tiles in the deck\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Removes and returns the last tile from the deck\n * @throws Error if the deck is empty\n */\n pop() {\n if (this.tiles.length === 0) {\n throw new Error(\"Cannot pop from an empty deck\");\n }\n const tile = this.tiles.pop();\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key);\n if (indices && indices.length > 0) {\n indices.pop(); // Remove the last index\n if (indices.length === 0) {\n this.tileIndexMap.delete(key);\n }\n else {\n this.tileIndexMap.set(key, indices);\n }\n }\n return tile;\n }\n /**\n * Adds a tile to the deck\n */\n push(tile) {\n const index = this.tiles.length;\n this.tiles.push(tile);\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(index);\n this.tileIndexMap.set(key, indices);\n }\n /**\n * Finds and removes a specific tile from the deck\n */\n find(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n if (!indices || indices.length === 0) {\n return undefined;\n }\n // Get the first occurrence\n const index = indices[0];\n if (index >= this.tiles.length) {\n // Handle potential out-of-sync errors\n this.rebuildIndexMap();\n return this.find(family, value);\n }\n // Swap with the first element for efficient removal\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n // Update indices in the map\n this.updateIndicesAfterSwap(0, index);\n // Remove and return the tile\n const tile = this.tiles.shift();\n // Update all indices after shift\n this.decrementIndicesAfterShift();\n return tile;\n }\n /**\n * Counts the number of tiles with specific family and value\n */\n count(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n return indices ? indices.length : 0;\n }\n /**\n * Shuffles the deck using Fisher-Yates algorithm\n */\n shuffle() {\n // Fisher-Yates shuffle algorithm\n for (let i = this.tiles.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]];\n }\n // Rebuild the index map after shuffling\n this.rebuildIndexMap();\n }\n /**\n * Creates a random hand from the deck\n */\n getRandomHand() {\n const hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n // Handle case where deck doesn't have enough tiles\n if (this.tiles.length < 13) {\n throw new Error(\"Not enough tiles in deck to create a hand\");\n }\n for (let i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n }\n /**\n * Cleans up resources used by tiles and empties the deck\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n this.tileIndexMap.clear();\n }\n /**\n * Preloads all tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n const preloadPromises = this.tiles.map(tile => tile.preloadImg());\n yield Promise.all(preloadPromises);\n });\n }\n /**\n * Creates a unique key for each tile type\n */\n getTileKey(family, value) {\n return `${family}-${value}`;\n }\n /**\n * Updates the index map after swapping two tiles\n */\n updateIndicesAfterSwap(index1, index2) {\n if (index1 === index2)\n return;\n const tile1 = this.tiles[index1];\n const tile2 = this.tiles[index2];\n const key1 = this.getTileKey(tile1.getFamily(), tile1.getValue());\n const key2 = this.getTileKey(tile2.getFamily(), tile2.getValue());\n const indices1 = this.tileIndexMap.get(key1) || [];\n const indices2 = this.tileIndexMap.get(key2) || [];\n // Update indices\n const idx1 = indices1.indexOf(index2);\n const idx2 = indices2.indexOf(index1);\n if (idx1 !== -1)\n indices1[idx1] = index1;\n if (idx2 !== -1)\n indices2[idx2] = index2;\n this.tileIndexMap.set(key1, indices1);\n this.tileIndexMap.set(key2, indices2);\n }\n /**\n * Decrements all indices after a shift operation\n */\n decrementIndicesAfterShift() {\n var _a;\n for (const [key, indices] of this.tileIndexMap.entries()) {\n this.tileIndexMap.set(key, indices\n .filter(idx => idx !== 0) // Remove the index 0 that was shifted\n .map(idx => (idx > 0 ? idx - 1 : idx)) // Decrement all indices\n );\n // Clean up empty entries\n if (((_a = this.tileIndexMap.get(key)) === null || _a === void 0 ? void 0 : _a.length) === 0) {\n this.tileIndexMap.delete(key);\n }\n }\n }\n /**\n * Rebuilds the entire index map from scratch\n */\n rebuildIndexMap() {\n this.tileIndexMap.clear();\n for (let i = 0; i < this.tiles.length; i++) {\n const tile = this.tiles[i];\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(i);\n this.tileIndexMap.set(key, indices);\n }\n }\n /**\n * Initializes the deck with all tiles\n */\n initTiles(allowRed) {\n // Create suits (families 1-3)\n for (let family = 1; family <= 3; family++) {\n for (let value = 1; value <= 9; value++) {\n // Each value appears 4 times in a suit\n const isRedFive = value === 5 && allowRed;\n // Add 3 regular tiles\n for (let i = 0; i < 3; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n // Add the 4th tile (potentially red five)\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, isRedFive));\n }\n }\n // Create winds (family 4)\n for (let value = 1; value <= 4; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, value, false));\n }\n }\n // Create dragons (family 5)\n for (let value = 1; value <= 3; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, value, false));\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); - -/***/ }), - -/***/ "./src/display/dp2.ts": -/*!****************************!*\ - !*** ./src/display/dp2.ts ***! - \****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _deck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deck */ \"./src/deck.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../hand */ \"./src/hand.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\nclass RiichiDisplay {\n constructor() {\n this.hands = [];\n this.selectedTile = undefined;\n this.animationFrameId = null;\n this.isDirty = true;\n // Constants\n this.FPS = 30;\n this.INTERVAL = 1000 / this.FPS;\n this.X = 100;\n this.Y = 150;\n this.OS = 75;\n this.SIZE = 0.75;\n this.TILE_WIDTH = 80 * this.SIZE;\n this.MAX_TILES = 14;\n // Cache for mouse hit detection\n this.tileRects = [];\n const canvas = document.getElementById(\"myCanvas\");\n if (!canvas) {\n throw new Error(\"Canvas introuvable dans le DOM.\");\n }\n this.canvas = canvas;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Impossible d'obtenir le contexte du canvas.\");\n }\n this.ctx = ctx;\n // Create off-screen canvas for double buffering\n this.offScreenCanvas = document.createElement('canvas');\n this.offScreenCanvas.width = canvas.width;\n this.offScreenCanvas.height = canvas.height;\n const offCtx = this.offScreenCanvas.getContext('2d');\n if (!offCtx) {\n throw new Error(\"Impossible d'obtenir le contexte du canvas hors écran.\");\n }\n this.offScreenCtx = offCtx;\n // Initialize decks\n this.deck = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false);\n this.edeck = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false);\n // Initialize with empty hand (will be populated after preload)\n this.ehand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n // Set up event listeners\n this.setupEventListeners();\n // Calculate tile hit areas once\n this.calculateTileHitAreas();\n }\n calculateTileHitAreas() {\n this.tileRects = [];\n for (let i = 0; i < this.MAX_TILES; i++) {\n this.tileRects.push({\n x: this.X + i * this.TILE_WIDTH,\n y: 800,\n width: 75,\n height: 100 * this.SIZE\n });\n }\n }\n setupEventListeners() {\n this.canvas.addEventListener(\"mousemove\", this.handleMouseMove.bind(this));\n this.canvas.addEventListener(\"mousedown\", this.handleMouseDown.bind(this));\n }\n handleMouseMove(event) {\n const rect = this.canvas.getBoundingClientRect();\n const mouseX = event.clientX - rect.left;\n const mouseY = event.clientY - rect.top;\n // Check if cursor is over any tile using pre-calculated hit areas\n const oldSelectedTile = this.selectedTile;\n this.selectedTile = undefined;\n for (let i = 0; i < this.tileRects.length; i++) {\n const tileRect = this.tileRects[i];\n if (mouseX >= tileRect.x &&\n mouseX <= tileRect.x + tileRect.width &&\n mouseY >= tileRect.y &&\n mouseY <= tileRect.y + tileRect.height) {\n this.selectedTile = i;\n break;\n }\n }\n // Only mark as dirty if selection changed\n if (oldSelectedTile !== this.selectedTile) {\n this.isDirty = true;\n }\n }\n handleMouseDown() {\n if (this.selectedTile !== undefined) {\n this.edeck.push(this.ehand.eject(this.selectedTile));\n this.edeck.shuffle();\n this.ehand.sort();\n this.ehand.push(this.edeck.pop());\n this.isDirty = true;\n }\n }\n initialize() {\n return __awaiter(this, void 0, void 0, function* () {\n // Preload all assets in parallel\n yield Promise.all([\n this.deck.preload(),\n this.edeck.preload()\n ]);\n // Generate sample hands\n for (let i = 0; i < 4; i++) {\n const hand = this.deck.getRandomHand();\n hand.sort();\n this.hands.push(hand);\n }\n // Initialize interactive hand\n this.ehand = this.edeck.getRandomHand();\n this.ehand.push(this.edeck.pop());\n this.ehand.sort();\n // Initial draw\n this.drawCanvas();\n // Start animation loop\n this.startAnimationLoop();\n });\n }\n drawCanvas() {\n // Only redraw if something changed (dirty flag)\n if (!this.isDirty)\n return;\n const ctx = this.offScreenCtx;\n // Clear canvas\n ctx.clearRect(0, 0, this.offScreenCanvas.width, this.offScreenCanvas.height);\n // Draw background\n ctx.fillStyle = \"#007730\";\n ctx.fillRect(50, 50, 1000, 1000);\n // Draw title\n ctx.fillStyle = \"#DFDFFF\";\n ctx.font = \"50px serif\";\n ctx.fillText(\"Exemples de main:\", 65, 100);\n // Draw example hands\n for (let i = 0; i < this.hands.length; i++) {\n this.hands[i].drawHand(ctx, this.X, this.Y + i * this.SIZE * (100 + this.OS), 5, this.SIZE);\n }\n // Draw interactive hand\n this.ehand.isolate = true;\n this.ehand.drawHand(ctx, this.X, 800, 5, this.SIZE, this.selectedTile);\n // Flip double buffer\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.ctx.drawImage(this.offScreenCanvas, 0, 0);\n // Reset dirty flag\n this.isDirty = false;\n }\n startAnimationLoop() {\n let lastTime = 0;\n const animationLoop = (currentTime) => {\n const deltaTime = currentTime - lastTime;\n if (deltaTime >= this.INTERVAL) {\n lastTime = currentTime - (deltaTime % this.INTERVAL);\n this.drawCanvas();\n }\n this.animationFrameId = requestAnimationFrame(animationLoop);\n };\n this.animationFrameId = requestAnimationFrame(animationLoop);\n }\n cleanup() {\n // Cancel animation loop\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n // Clean up resources\n this.deck.cleanup();\n this.hands.forEach(hand => hand.cleanup());\n this.hands = [];\n this.edeck.cleanup();\n this.ehand.cleanup();\n // Clear canvases\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.offScreenCtx.clearRect(0, 0, this.offScreenCanvas.width, this.offScreenCanvas.height);\n // Reset state\n this.selectedTile = undefined;\n this.isDirty = true;\n }\n}\n// Initialize and start\nconst riichiDisplay = new RiichiDisplay();\nriichiDisplay.initialize().catch(error => {\n console.error(\"Erreur lors de l'initialisation:\", error);\n});\n// Expose cleanup function for window\nwindow.cleanup = () => {\n riichiDisplay.cleanup();\n};\n\n\n//# sourceURL=webpack:///./src/display/dp2.ts?"); - -/***/ }), - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/display/dp2.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";class t{constructor(t,i,e){this.family=t,this.value=i,this.red=e,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,s=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((e=void 0)||(e=Promise))((function(n,h){function l(t){try{r(s.next(t))}catch(t){h(t)}}function a(t){try{r(s.throw(t))}catch(t){h(t)}}function r(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(l,a)}r((s=s.apply(t,i||[])).next())}));var t,i,e,s}loadImg(t,i){return new Promise(((e,s)=>{t.onload=()=>e(),t.onerror=()=>s(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,e){this.tiles=t,this.stolenFrom=i,this.belongsTo=e}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,e,s,n,h,l){t.save(),t.translate(525,525),t.rotate(h),t.translate(-525,-525);const a=75*n,r=90*n,o=25*n/2,c=(this.belongsTo-this.stolenFrom-1+4)%4,d=void 0===l?-1:l.getFamily(),u=void 0===l?0:l.getValue(),f=(i,e,s,h)=>{i.drawTile(t,e,s,n,!1,h,i.isEqual(d,u))},m=Math.PI/2;switch(c){case 0:f(this.tiles[0],i,e+o,m),f(this.tiles[1],i+r,e,0),f(this.tiles[2],i+r+a+s*n,e,0);break;case 1:f(this.tiles[0],i,e,0),f(this.tiles[1],i+r,e+o,-m),f(this.tiles[2],i+r+a+3*s*n,e,0);break;case 2:f(this.tiles[0],i,e,0),f(this.tiles[1],i+a+s*n,e,0),f(this.tiles[2],i+r+a+s*n,e+o,-m);break;default:console.error(`Position non prise en charge: ${c}`)}t.restore()}}const e="m",s=1,n="p",h=2,l="s",a=3,r="w",o=4,c="d",d=5;class u{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;ie.getFamily()===t&&e.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((e=>e.getFamily()===t&&e.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),e=i.getFamily(),s=i.getValue();if(this.count(e,s)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(e,s)>=2){const e=this.tryFormTriplet(i,t);if(e)return e}const n=this.count(e,s-1)>0,h=this.count(e,s-2)>0;if(n&&h){const e=this.tryFormSequence(i,t);if(e)return e}this.tiles.push(i),this.sort()}tryFormPair(t){const e=this.find(t.getFamily(),t.getValue()),s=this.toGroup(!0);if(this.tiles.push(e),this.sort(),void 0!==s)return this.tiles.push(t),this.sort(),s.push(new i([t,e],0,0)),s}tryFormTriplet(t,e){const s=this.find(t.getFamily(),t.getValue()),n=this.find(t.getFamily(),t.getValue()),h=this.toGroup(e);if(this.tiles.push(s),this.tiles.push(n),this.sort(),void 0!==h)return h.push(new i([t,s,n],0,0)),this.tiles.push(t),this.sort(),h}tryFormSequence(t,e){const s=this.find(t.getFamily(),t.getValue()-1),n=this.find(t.getFamily(),t.getValue()-2),h=this.toGroup(e);if(this.tiles.push(s),this.tiles.push(n),this.sort(),void 0!==h)return h.push(new i([n,s,t],0,0)),this.tiles.push(t),this.sort(),h}drawHand(t,i,e,s,n,h=void 0,l=!1,a=0){const r=(75+s)*n,o=Math.cos(a)*r,c=Math.sin(a)*r;for(let s=0;st.preloadImg())))},new((e=void 0)||(e=Promise))((function(n,h){function l(t){try{r(s.next(t))}catch(t){h(t)}}function a(t){try{r(s.throw(t))}catch(t){h(t)}}function r(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(l,a)}r((s=s.apply(t,i||[])).next())}));var t,i,e,s}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class f{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,e,s,n=0,h=0){let l=i,a=e;const r=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const e of r){for(let i=e.start;i<=e.end;i++){const h=this.find(e.family,i);h&&(h.drawTile(t,l,a,s,!1,0,!1),l+=(75+n)*s)}l=i,a+=(100+h)*s}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(i);return e&&e.length>0&&(e.pop(),0===e.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,e)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const e=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(e)||[];s.push(i),this.tileIndexMap.set(e,s)}find(t,i){const e=this.getTileKey(t,i),s=this.tileIndexMap.get(e);if(!s||0===s.length)return;const n=s[0];if(n>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[n],this.tiles[0]]=[this.tiles[0],this.tiles[n]],this.updateIndicesAfterSwap(0,n);const h=this.tiles.shift();return this.decrementIndicesAfterShift(),h}count(t,i){const e=this.getTileKey(t,i),s=this.tileIndexMap.get(e);return s?s.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new u;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,s=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((e=void 0)||(e=Promise))((function(n,h){function l(t){try{r(s.next(t))}catch(t){h(t)}}function a(t){try{r(s.throw(t))}catch(t){h(t)}}function r(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(l,a)}r((s=s.apply(t,i||[])).next())}));var t,i,e,s}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const e=this.tiles[t],s=this.tiles[i],n=this.getTileKey(e.getFamily(),e.getValue()),h=this.getTileKey(s.getFamily(),s.getValue()),l=this.tileIndexMap.get(n)||[],a=this.tileIndexMap.get(h)||[],r=l.indexOf(i),o=a.indexOf(t);-1!==r&&(l[r]=t),-1!==o&&(a[o]=i),this.tileIndexMap.set(n,l),this.tileIndexMap.set(h,a)}decrementIndicesAfterShift(){var t;for(const[i,e]of this.tileIndexMap.entries())this.tileIndexMap.set(i,e.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;t=i.x&&e<=i.x+i.width&&s>=i.y&&s<=i.y+i.height){this.selectedTile=t;break}}n!==this.selectedTile&&(this.isDirty=!0)}handleMouseDown(){void 0!==this.selectedTile&&(this.edeck.push(this.ehand.eject(this.selectedTile)),this.edeck.shuffle(),this.ehand.sort(),this.ehand.push(this.edeck.pop()),this.isDirty=!0)}initialize(){return t=this,i=void 0,s=function*(){yield Promise.all([this.deck.preload(),this.edeck.preload()]);for(let t=0;t<4;t++){const t=this.deck.getRandomHand();t.sort(),this.hands.push(t)}this.ehand=this.edeck.getRandomHand(),this.ehand.push(this.edeck.pop()),this.ehand.sort(),this.drawCanvas(),this.startAnimationLoop()},new((e=void 0)||(e=Promise))((function(n,h){function l(t){try{r(s.next(t))}catch(t){h(t)}}function a(t){try{r(s.throw(t))}catch(t){h(t)}}function r(t){var i;t.done?n(t.value):(i=t.value,i instanceof e?i:new e((function(t){t(i)}))).then(l,a)}r((s=s.apply(t,i||[])).next())}));var t,i,e,s}drawCanvas(){if(!this.isDirty)return;const t=this.offScreenCtx;t.clearRect(0,0,this.offScreenCanvas.width,this.offScreenCanvas.height),t.fillStyle="#007730",t.fillRect(50,50,1e3,1e3),t.fillStyle="#DFDFFF",t.font="50px serif",t.fillText("Exemples de main:",65,100);for(let i=0;i{const s=e-t;s>=this.INTERVAL&&(t=e-s%this.INTERVAL,this.drawCanvas()),this.animationFrameId=requestAnimationFrame(i)};this.animationFrameId=requestAnimationFrame(i)}cleanup(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.deck.cleanup(),this.hands.forEach((t=>t.cleanup())),this.hands=[],this.edeck.cleanup(),this.ehand.cleanup(),this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.offScreenCtx.clearRect(0,0,this.offScreenCanvas.width,this.offScreenCanvas.height),this.selectedTile=void 0,this.isDirty=!0}};m.initialize().catch((t=>{console.error("Erreur lors de l'initialisation:",t)})),window.cleanup=()=>{m.cleanup()}})(); \ No newline at end of file diff --git a/build/dp3.js b/build/dp3.js index 72ad567..8989019 100644 --- a/build/dp3.js +++ b/build/dp3.js @@ -1,126 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/deck.ts": -/*!*********************!*\ - !*** ./src/deck.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Deck: () => (/* binding */ Deck)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hand */ \"./src/hand.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\nclass Deck {\n constructor(allowRed = false) {\n this.tiles = [];\n this.tileIndexMap = new Map();\n this.initTiles(allowRed);\n }\n /**\n * Displays all tile families on the canvas\n */\n displayFamilies(ctx, x, y, size, xOffset = 0, yOffset = 0) {\n let posX = x;\n let posY = y;\n // Define tile layouts for each family\n const familyLayouts = [\n { family: 1, start: 1, end: 9 }, // First suit\n { family: 2, start: 1, end: 9 }, // Second suit\n { family: 3, start: 1, end: 9 }, // Third suit\n { family: 4, start: 1, end: 4 }, // Winds\n { family: 5, start: 1, end: 3 } // Dragons\n ];\n for (const layout of familyLayouts) {\n for (let j = layout.start; j <= layout.end; j++) {\n const tile = this.find(layout.family, j);\n if (tile) {\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n }\n /**\n * Returns the number of tiles in the deck\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Removes and returns the last tile from the deck\n * @throws Error if the deck is empty\n */\n pop() {\n if (this.tiles.length === 0) {\n throw new Error(\"Cannot pop from an empty deck\");\n }\n const tile = this.tiles.pop();\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key);\n if (indices && indices.length > 0) {\n indices.pop(); // Remove the last index\n if (indices.length === 0) {\n this.tileIndexMap.delete(key);\n }\n else {\n this.tileIndexMap.set(key, indices);\n }\n }\n return tile;\n }\n /**\n * Adds a tile to the deck\n */\n push(tile) {\n const index = this.tiles.length;\n this.tiles.push(tile);\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(index);\n this.tileIndexMap.set(key, indices);\n }\n /**\n * Finds and removes a specific tile from the deck\n */\n find(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n if (!indices || indices.length === 0) {\n return undefined;\n }\n // Get the first occurrence\n const index = indices[0];\n if (index >= this.tiles.length) {\n // Handle potential out-of-sync errors\n this.rebuildIndexMap();\n return this.find(family, value);\n }\n // Swap with the first element for efficient removal\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n // Update indices in the map\n this.updateIndicesAfterSwap(0, index);\n // Remove and return the tile\n const tile = this.tiles.shift();\n // Update all indices after shift\n this.decrementIndicesAfterShift();\n return tile;\n }\n /**\n * Counts the number of tiles with specific family and value\n */\n count(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n return indices ? indices.length : 0;\n }\n /**\n * Shuffles the deck using Fisher-Yates algorithm\n */\n shuffle() {\n // Fisher-Yates shuffle algorithm\n for (let i = this.tiles.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]];\n }\n // Rebuild the index map after shuffling\n this.rebuildIndexMap();\n }\n /**\n * Creates a random hand from the deck\n */\n getRandomHand() {\n const hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n // Handle case where deck doesn't have enough tiles\n if (this.tiles.length < 13) {\n throw new Error(\"Not enough tiles in deck to create a hand\");\n }\n for (let i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n }\n /**\n * Cleans up resources used by tiles and empties the deck\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n this.tileIndexMap.clear();\n }\n /**\n * Preloads all tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n const preloadPromises = this.tiles.map(tile => tile.preloadImg());\n yield Promise.all(preloadPromises);\n });\n }\n /**\n * Creates a unique key for each tile type\n */\n getTileKey(family, value) {\n return `${family}-${value}`;\n }\n /**\n * Updates the index map after swapping two tiles\n */\n updateIndicesAfterSwap(index1, index2) {\n if (index1 === index2)\n return;\n const tile1 = this.tiles[index1];\n const tile2 = this.tiles[index2];\n const key1 = this.getTileKey(tile1.getFamily(), tile1.getValue());\n const key2 = this.getTileKey(tile2.getFamily(), tile2.getValue());\n const indices1 = this.tileIndexMap.get(key1) || [];\n const indices2 = this.tileIndexMap.get(key2) || [];\n // Update indices\n const idx1 = indices1.indexOf(index2);\n const idx2 = indices2.indexOf(index1);\n if (idx1 !== -1)\n indices1[idx1] = index1;\n if (idx2 !== -1)\n indices2[idx2] = index2;\n this.tileIndexMap.set(key1, indices1);\n this.tileIndexMap.set(key2, indices2);\n }\n /**\n * Decrements all indices after a shift operation\n */\n decrementIndicesAfterShift() {\n var _a;\n for (const [key, indices] of this.tileIndexMap.entries()) {\n this.tileIndexMap.set(key, indices\n .filter(idx => idx !== 0) // Remove the index 0 that was shifted\n .map(idx => (idx > 0 ? idx - 1 : idx)) // Decrement all indices\n );\n // Clean up empty entries\n if (((_a = this.tileIndexMap.get(key)) === null || _a === void 0 ? void 0 : _a.length) === 0) {\n this.tileIndexMap.delete(key);\n }\n }\n }\n /**\n * Rebuilds the entire index map from scratch\n */\n rebuildIndexMap() {\n this.tileIndexMap.clear();\n for (let i = 0; i < this.tiles.length; i++) {\n const tile = this.tiles[i];\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(i);\n this.tileIndexMap.set(key, indices);\n }\n }\n /**\n * Initializes the deck with all tiles\n */\n initTiles(allowRed) {\n // Create suits (families 1-3)\n for (let family = 1; family <= 3; family++) {\n for (let value = 1; value <= 9; value++) {\n // Each value appears 4 times in a suit\n const isRedFive = value === 5 && allowRed;\n // Add 3 regular tiles\n for (let i = 0; i < 3; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n // Add the 4th tile (potentially red five)\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, isRedFive));\n }\n }\n // Create winds (family 4)\n for (let value = 1; value <= 4; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, value, false));\n }\n }\n // Create dragons (family 5)\n for (let value = 1; value <= 3; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, value, false));\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); - -/***/ }), - -/***/ "./src/display/dp3.ts": -/*!****************************!*\ - !*** ./src/display/dp3.ts ***! - \****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ cleanup: () => (/* binding */ cleanup),\n/* harmony export */ initDisplay: () => (/* binding */ initDisplay)\n/* harmony export */ });\n/* harmony import */ var _deck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deck */ \"./src/deck.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../hand */ \"./src/hand.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Configuration globale\nconst CANVAS_ID = \"myCanvas\";\nconst BG_COLOR = \"#007730\";\nconst BG_RECT = { x: 50, y: 50, w: 1000, h: 1000 };\nconst FPS = 30;\nconst FRAME_INTERVAL = 1000 / FPS;\n/**\n * Classe RiichiDisplay responsable de la gestion de l'affichage du jeu Riichi Mahjong\n */\nclass RiichiDisplay {\n constructor(canvasId = CANVAS_ID) {\n // Ressources du jeu\n this.decks = [];\n this.hands = [];\n // État de l'interface\n this.selectedTile = undefined;\n this.isDirty = true;\n // Animation et événements\n this.animationFrameId = null;\n this.lastFrameTime = 0;\n this.cleanupCallbacks = [];\n // Constantes pour le rendu\n this.BASE_X = 300;\n this.BASE_Y = 150;\n this.X_OFFSET = 250;\n this.Y_OFFSET = 100;\n this.INTERACTIVE_X = 100;\n this.INTERACTIVE_Y = 800;\n this.SIZE = 0.75;\n this.TILE_WIDTH = 80 * this.SIZE;\n this.MAX_TILES = 14;\n // Zones de détection pour l'interaction souris\n this.tileRects = [];\n // Initialisation du canvas\n const canvas = document.getElementById(canvasId);\n if (!canvas) {\n throw new Error(`Canvas avec ID '${canvasId}' introuvable`);\n }\n this.canvas = canvas;\n // Récupération du contexte 2D\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Impossible d'obtenir le contexte 2D du canvas\");\n }\n this.ctx = ctx;\n // Initialisation du canvas pour le double-buffering\n this.staticCanvas = document.createElement('canvas');\n this.staticCanvas.width = canvas.width;\n this.staticCanvas.height = canvas.height;\n const staticCtx = this.staticCanvas.getContext(\"2d\");\n if (!staticCtx) {\n throw new Error(\"Impossible d'obtenir le contexte du canvas statique\");\n }\n this.staticCtx = staticCtx;\n // Pré-calcul des zones de détection\n this.calculateTileHitAreas();\n }\n /**\n * Pré-calcule les zones de détection pour l'interaction souris\n */\n calculateTileHitAreas() {\n this.tileRects = [];\n for (let i = 0; i < this.MAX_TILES; i++) {\n this.tileRects.push({\n x: this.INTERACTIVE_X + i * this.TILE_WIDTH,\n y: this.INTERACTIVE_Y,\n width: 75,\n height: 100 * this.SIZE\n });\n }\n }\n /**\n * Pré-rendu du fond statique\n */\n prerenderBackground() {\n this.staticCtx.clearRect(0, 0, this.staticCanvas.width, this.staticCanvas.height);\n this.staticCtx.fillStyle = BG_COLOR;\n this.staticCtx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n }\n /**\n * Dessine une frame complète\n */\n drawFrame() {\n // Vérifier si un redessinage est nécessaire\n if (!this.isDirty)\n return;\n // Pré-rendu du fond statique\n this.prerenderBackground();\n // Dessin des éléments statiques et dynamiques\n this.drawHandsAndLabels();\n // Affichage des informations sur les groupes\n this.checkAndDisplayGroups();\n // Copie du canvas statique vers le canvas principal\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.ctx.drawImage(this.staticCanvas, 0, 0);\n // Réinitialisation du flag de modification\n this.isDirty = false;\n }\n /**\n * Dessine les mains et leurs étiquettes\n */\n drawHandsAndLabels() {\n const ctx = this.staticCtx;\n // Configurer le style de texte\n ctx.fillStyle = \"#DFDFFF\";\n ctx.font = \"50px serif\";\n // Dessiner les mains \"Chii\"\n ctx.fillText(\"Chii:\", 75, this.BASE_Y + 100 * this.SIZE - 5);\n this.hands[0].drawHand(ctx, this.BASE_X, this.BASE_Y, 5, this.SIZE);\n this.hands[1].drawHand(ctx, this.BASE_X + (75 + this.X_OFFSET) * this.SIZE, this.BASE_Y, 5, this.SIZE);\n // Dessiner les mains \"Pon\"\n ctx.fillText(\"Pon:\", 75, this.BASE_Y + (100 + this.Y_OFFSET) * this.SIZE + 100 * this.SIZE - 5);\n this.hands[2].drawHand(ctx, this.BASE_X, this.BASE_Y + (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE);\n this.hands[3].drawHand(ctx, this.BASE_X + (75 + this.X_OFFSET) * this.SIZE, this.BASE_Y + (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE);\n // Dessiner les mains \"Invalide\"\n ctx.fillText(\"Invalide:\", 75, this.BASE_Y + 2 * (100 + this.Y_OFFSET) * this.SIZE + 100 * this.SIZE - 5);\n this.hands[5].drawHand(ctx, this.BASE_X, this.BASE_Y + 2 * (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE);\n this.hands[6].drawHand(ctx, this.BASE_X + (75 + this.X_OFFSET) * this.SIZE, this.BASE_Y + 2 * (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE);\n this.hands[7].drawHand(ctx, this.BASE_X + 2 * (75 + this.X_OFFSET) * this.SIZE, this.BASE_Y + 2 * (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE);\n // Main supplémentaire (position spéciale)\n this.hands[4].drawHand(ctx, this.BASE_X + 2 * (75 + this.X_OFFSET) * this.SIZE, this.BASE_Y + (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE);\n // Main interactive\n this.hands[8].isolate = true;\n this.hands[8].drawHand(ctx, this.INTERACTIVE_X, this.INTERACTIVE_Y, 5, this.SIZE, this.selectedTile);\n }\n /**\n * Vérifie et affiche les informations sur les groupes formés\n */\n checkAndDisplayGroups() {\n const groups = this.hands[8].toGroup();\n if (groups !== undefined) {\n this.staticCtx.fillStyle = \"#FF0000\";\n this.staticCtx.font = \"50px serif\";\n this.staticCtx.fillText(\"Tous les groupes sont formés !\", 100, 750);\n }\n }\n /**\n * Démarre la boucle d'animation\n */\n startAnimationLoop() {\n const animationLoop = (currentTime) => {\n this.animationFrameId = requestAnimationFrame(animationLoop);\n const deltaTime = currentTime - this.lastFrameTime;\n if (deltaTime < FRAME_INTERVAL)\n return;\n this.lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL);\n this.drawFrame();\n };\n this.animationFrameId = requestAnimationFrame(animationLoop);\n }\n /**\n * Initialise les écouteurs d'événements\n */\n initEventListeners() {\n const mouseMoveHandler = this.handleMouseMove.bind(this);\n const mouseDownHandler = this.handleMouseDown.bind(this);\n this.canvas.addEventListener('mousemove', mouseMoveHandler);\n this.canvas.addEventListener('mousedown', mouseDownHandler);\n // Enregistrer les callbacks de nettoyage\n this.cleanupCallbacks.push(() => {\n this.canvas.removeEventListener('mousemove', mouseMoveHandler);\n this.canvas.removeEventListener('mousedown', mouseDownHandler);\n });\n }\n /**\n * Gère le mouvement de la souris\n */\n handleMouseMove(e) {\n const rect = this.canvas.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n // Sauvegarde de l'état précédent pour détecter les changements\n const previousSelectedTile = this.selectedTile;\n this.selectedTile = undefined;\n // Détection optimisée avec zones pré-calculées\n for (let i = 0; i < this.tileRects.length; i++) {\n const tileRect = this.tileRects[i];\n if (mouseX >= tileRect.x &&\n mouseX <= tileRect.x + tileRect.width &&\n mouseY >= tileRect.y &&\n mouseY <= tileRect.y + tileRect.height) {\n this.selectedTile = i;\n break;\n }\n }\n // Marquer comme \"dirty\" uniquement si la sélection a changé\n if (previousSelectedTile !== this.selectedTile) {\n this.isDirty = true;\n }\n }\n /**\n * Gère le clic de souris\n */\n handleMouseDown() {\n if (this.selectedTile !== undefined) {\n // Exécuter l'action pour la tuile sélectionnée\n this.decks[0].push(this.hands[8].eject(this.selectedTile));\n this.decks[0].shuffle();\n this.hands[8].sort();\n this.hands[8].push(this.decks[0].pop());\n // Marquer comme \"dirty\" pour forcer le redessinage\n this.isDirty = true;\n }\n }\n /**\n * Précharge un deck\n */\n preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function* () {\n yield deck.preload();\n });\n }\n /**\n * Précharge une main\n */\n preloadHand(hand) {\n return __awaiter(this, void 0, void 0, function* () {\n yield hand.preload();\n });\n }\n /**\n * Initialise l'affichage et démarre l'application\n */\n initialize() {\n return __awaiter(this, void 0, void 0, function* () {\n try {\n // Création du deck principal\n this.decks.push(new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false));\n // Création des mains prédéfinies\n this.hands.push(new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"s1s2s3\"), // Chii 1\n new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"m2m3m4\"), // Chii 2\n new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"p5p5p5\"), // Pon 1\n new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"w2w2w2\"), // Pon 2\n new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"d3d3d3\"), // Pon supplémentaire\n new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"s4p5m6\"), // Invalide 1\n new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"m9s9p9\"), // Invalide 2\n new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"d1d2d3\"), // Invalide 3\n this.decks[0].getRandomHand() // Main interactive\n );\n // Préchargement parallèle des ressources\n yield Promise.all([\n ...this.decks.map(deck => this.preloadDeck(deck)),\n ...this.hands.map(hand => this.preloadHand(hand))\n ]);\n // Configuration de la main interactive\n this.hands[8].push(this.decks[0].pop());\n this.hands[8].sort();\n // Initialisation des écouteurs d'événements\n this.initEventListeners();\n // Premier rendu\n this.isDirty = true;\n this.drawFrame();\n // Démarrage de la boucle d'animation\n this.startAnimationLoop();\n }\n catch (error) {\n console.error(\"Erreur d'initialisation:\", error);\n }\n });\n }\n /**\n * Nettoie les ressources\n */\n cleanup() {\n // Arrêter la boucle d'animation\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n // Exécuter tous les callbacks de nettoyage\n this.cleanupCallbacks.forEach(callback => callback());\n this.cleanupCallbacks = [];\n // Nettoyer les ressources des decks et des mains\n this.decks.forEach(deck => deck.cleanup());\n this.hands.forEach(hand => hand.cleanup());\n // Vider les collections\n this.decks = [];\n this.hands = [];\n // Effacer les canvas\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.staticCtx.clearRect(0, 0, this.staticCanvas.width, this.staticCanvas.height);\n }\n}\n// Instance globale pour le nettoyage\nlet displayInstance = null;\n/**\n * Fonction de nettoyage exportée\n */\nfunction cleanup() {\n if (displayInstance) {\n displayInstance.cleanup();\n displayInstance = null;\n }\n}\n/**\n * Fonction d'initialisation exportée\n */\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function* () {\n try {\n displayInstance = new RiichiDisplay(CANVAS_ID);\n yield displayInstance.initialize();\n window.cleanup = cleanup;\n }\n catch (error) {\n console.error(\"Erreur lors de l'initialisation:\", error);\n }\n });\n}\n// Initialisation automatique si le script est chargé directement\nif (typeof window !== 'undefined') {\n initDisplay().catch(console.error);\n}\n\n\n//# sourceURL=webpack:///./src/display/dp3.ts?"); - -/***/ }), - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/display/dp3.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";class t{constructor(t,i,s){this.family=t,this.value=i,this.red=s,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,e=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((s=void 0)||(s=Promise))((function(h,n){function a(t){try{r(e.next(t))}catch(t){n(t)}}function l(t){try{r(e.throw(t))}catch(t){n(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(a,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}loadImg(t,i){return new Promise(((s,e)=>{t.onload=()=>s(),t.onerror=()=>e(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,s){this.tiles=t,this.stolenFrom=i,this.belongsTo=s}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,s,e,h,n,a){t.save(),t.translate(525,525),t.rotate(n),t.translate(-525,-525);const l=75*h,r=90*h,o=25*h/2,c=(this.belongsTo-this.stolenFrom-1+4)%4,d=void 0===a?-1:a.getFamily(),u=void 0===a?0:a.getValue(),m=(i,s,e,n)=>{i.drawTile(t,s,e,h,!1,n,i.isEqual(d,u))},p=Math.PI/2;switch(c){case 0:m(this.tiles[0],i,s+o,p),m(this.tiles[1],i+r,s,0),m(this.tiles[2],i+r+l+e*h,s,0);break;case 1:m(this.tiles[0],i,s,0),m(this.tiles[1],i+r,s+o,-p),m(this.tiles[2],i+r+l+3*e*h,s,0);break;case 2:m(this.tiles[0],i,s,0),m(this.tiles[1],i+l+e*h,s,0),m(this.tiles[2],i+r+l+e*h,s+o,-p);break;default:console.error(`Position non prise en charge: ${c}`)}t.restore()}}const s="m",e=1,h="p",n=2,a="s",l=3,r="w",o=4,c="d",d=5;class u{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;is.getFamily()===t&&s.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((s=>s.getFamily()===t&&s.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),s=i.getFamily(),e=i.getValue();if(this.count(s,e)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(s,e)>=2){const s=this.tryFormTriplet(i,t);if(s)return s}const h=this.count(s,e-1)>0,n=this.count(s,e-2)>0;if(h&&n){const s=this.tryFormSequence(i,t);if(s)return s}this.tiles.push(i),this.sort()}tryFormPair(t){const s=this.find(t.getFamily(),t.getValue()),e=this.toGroup(!0);if(this.tiles.push(s),this.sort(),void 0!==e)return this.tiles.push(t),this.sort(),e.push(new i([t,s],0,0)),e}tryFormTriplet(t,s){const e=this.find(t.getFamily(),t.getValue()),h=this.find(t.getFamily(),t.getValue()),n=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==n)return n.push(new i([t,e,h],0,0)),this.tiles.push(t),this.sort(),n}tryFormSequence(t,s){const e=this.find(t.getFamily(),t.getValue()-1),h=this.find(t.getFamily(),t.getValue()-2),n=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==n)return n.push(new i([h,e,t],0,0)),this.tiles.push(t),this.sort(),n}drawHand(t,i,s,e,h,n=void 0,a=!1,l=0){const r=(75+e)*h,o=Math.cos(l)*r,c=Math.sin(l)*r;for(let e=0;et.preloadImg())))},new((s=void 0)||(s=Promise))((function(h,n){function a(t){try{r(e.next(t))}catch(t){n(t)}}function l(t){try{r(e.throw(t))}catch(t){n(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(a,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class m{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,s,e,h=0,n=0){let a=i,l=s;const r=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const s of r){for(let i=s.start;i<=s.end;i++){const n=this.find(s.family,i);n&&(n.drawTile(t,a,l,e,!1,0,!1),a+=(75+h)*e)}a=i,l+=(100+n)*e}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(i);return s&&s.length>0&&(s.pop(),0===s.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,s)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const s=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(s)||[];e.push(i),this.tileIndexMap.set(s,e)}find(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);if(!e||0===e.length)return;const h=e[0];if(h>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[h],this.tiles[0]]=[this.tiles[0],this.tiles[h]],this.updateIndicesAfterSwap(0,h);const n=this.tiles.shift();return this.decrementIndicesAfterShift(),n}count(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);return e?e.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new u;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,e=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((s=void 0)||(s=Promise))((function(h,n){function a(t){try{r(e.next(t))}catch(t){n(t)}}function l(t){try{r(e.throw(t))}catch(t){n(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(a,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const s=this.tiles[t],e=this.tiles[i],h=this.getTileKey(s.getFamily(),s.getValue()),n=this.getTileKey(e.getFamily(),e.getValue()),a=this.tileIndexMap.get(h)||[],l=this.tileIndexMap.get(n)||[],r=a.indexOf(i),o=l.indexOf(t);-1!==r&&(a[r]=t),-1!==o&&(l[o]=i),this.tileIndexMap.set(h,a),this.tileIndexMap.set(n,l)}decrementIndicesAfterShift(){var t;for(const[i,s]of this.tileIndexMap.entries())this.tileIndexMap.set(i,s.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;t{this.animationFrameId=requestAnimationFrame(t);const s=i-this.lastFrameTime;s{this.canvas.removeEventListener("mousemove",t),this.canvas.removeEventListener("mousedown",i)}))}handleMouseMove(t){const i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,e=t.clientY-i.top,h=this.selectedTile;this.selectedTile=void 0;for(let t=0;t=i.x&&s<=i.x+i.width&&e>=i.y&&e<=i.y+i.height){this.selectedTile=t;break}}h!==this.selectedTile&&(this.isDirty=!0)}handleMouseDown(){void 0!==this.selectedTile&&(this.decks[0].push(this.hands[8].eject(this.selectedTile)),this.decks[0].shuffle(),this.hands[8].sort(),this.hands[8].push(this.decks[0].pop()),this.isDirty=!0)}preloadDeck(t){return p(this,void 0,void 0,(function*(){yield t.preload()}))}preloadHand(t){return p(this,void 0,void 0,(function*(){yield t.preload()}))}initialize(){return p(this,void 0,void 0,(function*(){try{this.decks.push(new m(!1)),this.hands.push(new u("s1s2s3"),new u("m2m3m4"),new u("p5p5p5"),new u("w2w2w2"),new u("d3d3d3"),new u("s4p5m6"),new u("m9s9p9"),new u("d1d2d3"),this.decks[0].getRandomHand()),yield Promise.all([...this.decks.map((t=>this.preloadDeck(t))),...this.hands.map((t=>this.preloadHand(t)))]),this.hands[8].push(this.decks[0].pop()),this.hands[8].sort(),this.initEventListeners(),this.isDirty=!0,this.drawFrame(),this.startAnimationLoop()}catch(t){console.error("Erreur d'initialisation:",t)}}))}cleanup(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.cleanupCallbacks.forEach((t=>t())),this.cleanupCallbacks=[],this.decks.forEach((t=>t.cleanup())),this.hands.forEach((t=>t.cleanup())),this.decks=[],this.hands=[],this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.staticCtx.clearRect(0,0,this.staticCanvas.width,this.staticCanvas.height)}}let E=null;function v(){E&&(E.cleanup(),E=null)}"undefined"!=typeof window&&function(){return p(this,void 0,void 0,(function*(){try{E=new I(g),yield E.initialize(),window.cleanup=v}catch(t){console.error("Erreur lors de l'initialisation:",t)}}))}().catch(console.error)})(); \ No newline at end of file diff --git a/build/dp4.js b/build/dp4.js index 29f2bc4..fff9bbc 100644 --- a/build/dp4.js +++ b/build/dp4.js @@ -1,156 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/button.ts": -/*!***********************!*\ - !*** ./src/button.ts ***! - \***********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ clickAction: () => (/* binding */ clickAction),\n/* harmony export */ clickChii: () => (/* binding */ clickChii),\n/* harmony export */ drawButtons: () => (/* binding */ drawButtons),\n/* harmony export */ drawChiis: () => (/* binding */ drawChiis)\n/* harmony export */ });\n// Button constants\nconst BUTTON_RADIUS = 8;\nconst BUTTON_WIDTH = 110;\nconst BUTTON_HEIGHT = 50;\nconst BUTTON_SPACING = 120;\nconst BUTTON_AREA_Y_MIN = 838;\nconst BUTTON_AREA_Y_MAX = 888;\nconst BUTTON_MARGIN = 10;\nconst BASE_X_POSITION = 850;\n// Button style configurations\nconst BUTTON_STYLES = {\n pass: { text: \"Ignorer\", color: \"#FF9030\", action: 0 },\n chii: { text: \"Chii\", color: \"#FFCC33\", action: 1 },\n pon: { text: \"Pon\", color: \"#FFCC33\", action: 2 },\n kan: { text: \"Kan\", color: \"#FFCC33\", action: 3 },\n ron: { text: \"Ron\", color: \"#FF3060\", action: 4 },\n tsumo: { text: \"Tsumo\", color: \"#FF3060\", action: 5 },\n back: { text: \"Retour\", color: \"#FF9030\", action: 0 }\n};\n/**\n * Determines which button was clicked based on coordinates\n * @returns The action value of the clicked button or -1 if no button was clicked\n */\nfunction clickAction(x, y, chii, pon, kan, ron, tsumo) {\n const activeButtons = getActiveButtons(chii, pon, kan, ron, tsumo);\n if (activeButtons.length === 0) {\n return -1;\n }\n // Calculate starting X position based on number of buttons\n const xmin = 960 - activeButtons.length * BUTTON_SPACING;\n // Check if Y coordinate is within button area\n const isYInButtonArea = BUTTON_AREA_Y_MIN < y && y < BUTTON_AREA_Y_MAX;\n if (!isYInButtonArea) {\n return -1;\n }\n // Calculate which button was clicked\n const buttonIndex = Math.floor((x - xmin) / BUTTON_SPACING);\n const xOffset = (x - xmin) - BUTTON_SPACING * buttonIndex;\n if (buttonIndex >= 0 &&\n buttonIndex < activeButtons.length &&\n xOffset > BUTTON_MARGIN) {\n return activeButtons[buttonIndex];\n }\n return -1;\n}\n/**\n * Draw all active buttons on the canvas\n */\nfunction drawButtons(ctx, chii, pon, kan, ron, tsumo) {\n const buttonFunctions = [\n [chii, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.chii)],\n [pon, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.pon)],\n [kan, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.kan)],\n [ron, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.ron)],\n [tsumo, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.tsumo)]\n ];\n // Only show the pass button if at least one other button is active\n const hasActiveButtons = buttonFunctions.some(([isActive]) => isActive);\n if (hasActiveButtons) {\n buttonFunctions.unshift([true, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.pass)]);\n }\n else {\n return; // No buttons to draw\n }\n // Draw active buttons\n let positionOffset = 0;\n for (const [isActive, renderFunc] of buttonFunctions) {\n if (isActive) {\n renderFunc(ctx, BASE_X_POSITION - positionOffset * BUTTON_SPACING, 835);\n positionOffset++;\n }\n }\n}\n/**\n * Determines which Chi option was clicked\n * @returns The value of the clicked Chi option or -1 if no option was clicked\n */\nfunction clickChii(x, y, chiis) {\n if (chiis.length === 0) {\n return -1;\n }\n // Calculate starting X position based on number of options\n const xmin = 960 - (chiis.length + 1) * BUTTON_SPACING;\n // Check if Y coordinate is within button area\n const isYInButtonArea = BUTTON_AREA_Y_MIN < y && y < BUTTON_AREA_Y_MAX;\n if (!isYInButtonArea) {\n return -1;\n }\n // Calculate which option was clicked\n const optionIndex = Math.floor((x - xmin) / BUTTON_SPACING);\n const xOffset = (x - xmin) - BUTTON_SPACING * optionIndex;\n if (optionIndex >= 0 &&\n optionIndex < (chiis.length + 1) &&\n xOffset > BUTTON_MARGIN) {\n // Return 0 for \"back\" button or the value of the selected Chi option\n return optionIndex === chiis.length ? 0 : chiis[optionIndex][0].getValue();\n }\n return -1;\n}\n/**\n * Draw Chi options on the canvas\n */\nfunction drawChiis(ctx, chiis) {\n // Create a copy to avoid modifying the original array\n const chiiOptions = [...chiis].reverse();\n // Draw \"back\" button\n renderButton(ctx, BASE_X_POSITION, 835, BUTTON_STYLES.back);\n // Draw Chi options\n let positionOffset = 1;\n for (const tiles of chiiOptions) {\n drawOneChii(ctx, BASE_X_POSITION - positionOffset * BUTTON_SPACING, 835, tiles);\n positionOffset++;\n }\n}\n/**\n * Draw a single Chi option\n */\nfunction drawOneChii(ctx, x, y, tiles) {\n const tileOffset = 32;\n const tileStartX = x + 7;\n const tileStartY = y + 5;\n // Draw the button background\n drawButtonShape(ctx, x, y, BUTTON_RADIUS, BUTTON_WIDTH, BUTTON_HEIGHT, BUTTON_STYLES.chii.color);\n // Draw the tiles\n for (let i = 0; i < tiles.length; i++) {\n tiles[i].drawTile(ctx, tileStartX + tileOffset * i, tileStartY, 0.4, false, 0, false, false);\n }\n}\n/**\n * Get the list of active button action values\n */\nfunction getActiveButtons(chii, pon, kan, ron, tsumo) {\n const buttonConfigs = [\n [tsumo, BUTTON_STYLES.tsumo.action],\n [ron, BUTTON_STYLES.ron.action],\n [kan, BUTTON_STYLES.kan.action],\n [pon, BUTTON_STYLES.pon.action],\n [chii, BUTTON_STYLES.chii.action]\n ];\n const activeButtons = buttonConfigs\n .filter(([isActive]) => isActive)\n .map(([, action]) => action);\n // Add pass button if any other buttons are active\n if (activeButtons.length > 0) {\n activeButtons.push(BUTTON_STYLES.pass.action);\n }\n return activeButtons;\n}\n/**\n * Render a button with text\n */\nfunction renderButton(ctx, x, y, config) {\n drawButtonShape(ctx, x, y, BUTTON_RADIUS, BUTTON_WIDTH, BUTTON_HEIGHT, config.color);\n // Add text to the button\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n // Center text based on its length\n const textXPosition = x + BUTTON_WIDTH * (0.5 - config.text.length * 0.025);\n const textYPosition = y + BUTTON_HEIGHT / 2 * 1.3;\n ctx.fillText(config.text, textXPosition, textYPosition);\n}\n/**\n * Draw a rounded rectangle button shape\n */\nfunction drawButtonShape(ctx, x, y, radius, width, height, color) {\n ctx.fillStyle = color;\n ctx.beginPath();\n // Top right corner\n ctx.moveTo(x + radius, y);\n ctx.lineTo(x + width - radius, y);\n ctx.quadraticCurveTo(x + width, y, x + width, y + radius);\n // Bottom right corner\n ctx.lineTo(x + width, y + height - radius);\n ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);\n // Bottom left corner\n ctx.lineTo(x + radius, y + height);\n ctx.quadraticCurveTo(x, y + height, x, y + height - radius);\n // Top left corner\n ctx.lineTo(x, y + radius);\n ctx.quadraticCurveTo(x, y, x + radius, y);\n ctx.fill();\n // Add border\n ctx.fillStyle = \"#606060\";\n ctx.stroke();\n}\n\n\n//# sourceURL=webpack:///./src/button.ts?"); - -/***/ }), - -/***/ "./src/deck.ts": -/*!*********************!*\ - !*** ./src/deck.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Deck: () => (/* binding */ Deck)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hand */ \"./src/hand.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\nclass Deck {\n constructor(allowRed = false) {\n this.tiles = [];\n this.tileIndexMap = new Map();\n this.initTiles(allowRed);\n }\n /**\n * Displays all tile families on the canvas\n */\n displayFamilies(ctx, x, y, size, xOffset = 0, yOffset = 0) {\n let posX = x;\n let posY = y;\n // Define tile layouts for each family\n const familyLayouts = [\n { family: 1, start: 1, end: 9 }, // First suit\n { family: 2, start: 1, end: 9 }, // Second suit\n { family: 3, start: 1, end: 9 }, // Third suit\n { family: 4, start: 1, end: 4 }, // Winds\n { family: 5, start: 1, end: 3 } // Dragons\n ];\n for (const layout of familyLayouts) {\n for (let j = layout.start; j <= layout.end; j++) {\n const tile = this.find(layout.family, j);\n if (tile) {\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n }\n /**\n * Returns the number of tiles in the deck\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Removes and returns the last tile from the deck\n * @throws Error if the deck is empty\n */\n pop() {\n if (this.tiles.length === 0) {\n throw new Error(\"Cannot pop from an empty deck\");\n }\n const tile = this.tiles.pop();\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key);\n if (indices && indices.length > 0) {\n indices.pop(); // Remove the last index\n if (indices.length === 0) {\n this.tileIndexMap.delete(key);\n }\n else {\n this.tileIndexMap.set(key, indices);\n }\n }\n return tile;\n }\n /**\n * Adds a tile to the deck\n */\n push(tile) {\n const index = this.tiles.length;\n this.tiles.push(tile);\n // Update the index map\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(index);\n this.tileIndexMap.set(key, indices);\n }\n /**\n * Finds and removes a specific tile from the deck\n */\n find(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n if (!indices || indices.length === 0) {\n return undefined;\n }\n // Get the first occurrence\n const index = indices[0];\n if (index >= this.tiles.length) {\n // Handle potential out-of-sync errors\n this.rebuildIndexMap();\n return this.find(family, value);\n }\n // Swap with the first element for efficient removal\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n // Update indices in the map\n this.updateIndicesAfterSwap(0, index);\n // Remove and return the tile\n const tile = this.tiles.shift();\n // Update all indices after shift\n this.decrementIndicesAfterShift();\n return tile;\n }\n /**\n * Counts the number of tiles with specific family and value\n */\n count(family, value) {\n const key = this.getTileKey(family, value);\n const indices = this.tileIndexMap.get(key);\n return indices ? indices.length : 0;\n }\n /**\n * Shuffles the deck using Fisher-Yates algorithm\n */\n shuffle() {\n // Fisher-Yates shuffle algorithm\n for (let i = this.tiles.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]];\n }\n // Rebuild the index map after shuffling\n this.rebuildIndexMap();\n }\n /**\n * Creates a random hand from the deck\n */\n getRandomHand() {\n const hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n // Handle case where deck doesn't have enough tiles\n if (this.tiles.length < 13) {\n throw new Error(\"Not enough tiles in deck to create a hand\");\n }\n for (let i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n }\n /**\n * Cleans up resources used by tiles and empties the deck\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n this.tileIndexMap.clear();\n }\n /**\n * Preloads all tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n const preloadPromises = this.tiles.map(tile => tile.preloadImg());\n yield Promise.all(preloadPromises);\n });\n }\n /**\n * Creates a unique key for each tile type\n */\n getTileKey(family, value) {\n return `${family}-${value}`;\n }\n /**\n * Updates the index map after swapping two tiles\n */\n updateIndicesAfterSwap(index1, index2) {\n if (index1 === index2)\n return;\n const tile1 = this.tiles[index1];\n const tile2 = this.tiles[index2];\n const key1 = this.getTileKey(tile1.getFamily(), tile1.getValue());\n const key2 = this.getTileKey(tile2.getFamily(), tile2.getValue());\n const indices1 = this.tileIndexMap.get(key1) || [];\n const indices2 = this.tileIndexMap.get(key2) || [];\n // Update indices\n const idx1 = indices1.indexOf(index2);\n const idx2 = indices2.indexOf(index1);\n if (idx1 !== -1)\n indices1[idx1] = index1;\n if (idx2 !== -1)\n indices2[idx2] = index2;\n this.tileIndexMap.set(key1, indices1);\n this.tileIndexMap.set(key2, indices2);\n }\n /**\n * Decrements all indices after a shift operation\n */\n decrementIndicesAfterShift() {\n var _a;\n for (const [key, indices] of this.tileIndexMap.entries()) {\n this.tileIndexMap.set(key, indices\n .filter(idx => idx !== 0) // Remove the index 0 that was shifted\n .map(idx => (idx > 0 ? idx - 1 : idx)) // Decrement all indices\n );\n // Clean up empty entries\n if (((_a = this.tileIndexMap.get(key)) === null || _a === void 0 ? void 0 : _a.length) === 0) {\n this.tileIndexMap.delete(key);\n }\n }\n }\n /**\n * Rebuilds the entire index map from scratch\n */\n rebuildIndexMap() {\n this.tileIndexMap.clear();\n for (let i = 0; i < this.tiles.length; i++) {\n const tile = this.tiles[i];\n const key = this.getTileKey(tile.getFamily(), tile.getValue());\n const indices = this.tileIndexMap.get(key) || [];\n indices.push(i);\n this.tileIndexMap.set(key, indices);\n }\n }\n /**\n * Initializes the deck with all tiles\n */\n initTiles(allowRed) {\n // Create suits (families 1-3)\n for (let family = 1; family <= 3; family++) {\n for (let value = 1; value <= 9; value++) {\n // Each value appears 4 times in a suit\n const isRedFive = value === 5 && allowRed;\n // Add 3 regular tiles\n for (let i = 0; i < 3; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n // Add the 4th tile (potentially red five)\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, isRedFive));\n }\n }\n // Create winds (family 4)\n for (let value = 1; value <= 4; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, value, false));\n }\n }\n // Create dragons (family 5)\n for (let value = 1; value <= 3; value++) {\n for (let i = 0; i < 4; i++) {\n this.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, value, false));\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); - -/***/ }), - -/***/ "./src/display/dp4.ts": -/*!****************************!*\ - !*** ./src/display/dp4.ts ***! - \****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ cleanup: () => (/* binding */ cleanup),\n/* harmony export */ initDisplay: () => (/* binding */ initDisplay)\n/* harmony export */ });\n/* harmony import */ var _game__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game */ \"./src/game.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\nclass RiichiGameManager {\n /**\n * Constructeur privé pour le Singleton\n */\n constructor() {\n // Configuration globale\n this.CANVAS_ID = \"myCanvas\";\n this.BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };\n this.FPS = 60;\n this.FRAME_INTERVAL = 1000 / this.FPS;\n // État du jeu\n this.mouse = { x: 0, y: 0 };\n this.game = null;\n this.decks = [];\n this.hands = [];\n // Animation et boucle de jeu\n this.animationFrameId = null;\n this.lastFrameTime = 0;\n this.isInitialized = false;\n // Nettoyage\n this.cleanupCallbacks = [];\n // Initialisation du canvas principal\n const canvas = document.getElementById(this.CANVAS_ID);\n if (!canvas) {\n throw new Error(`Canvas avec ID '${this.CANVAS_ID}' introuvable`);\n }\n this.canvas = canvas;\n this.canvas.width = this.BG_RECT.w;\n this.canvas.height = this.BG_RECT.h;\n // Récupération du contexte 2D\n const ctx = canvas.getContext(\"2d\", { alpha: false });\n if (!ctx) {\n throw new Error(\"Impossible d'obtenir le contexte 2D du canvas\");\n }\n this.ctx = ctx;\n // Initialisation du canvas pour le double-buffering\n this.staticCanvas = document.createElement('canvas');\n this.staticCanvas.width = canvas.width;\n this.staticCanvas.height = canvas.height;\n const staticCtx = this.staticCanvas.getContext(\"2d\", { alpha: false });\n if (!staticCtx) {\n throw new Error(\"Impossible d'obtenir le contexte du canvas statique\");\n }\n this.staticCtx = staticCtx;\n }\n /**\n * Accès à l'instance Singleton\n */\n static getInstance() {\n if (!RiichiGameManager.instance) {\n RiichiGameManager.instance = new RiichiGameManager();\n }\n return RiichiGameManager.instance;\n }\n /**\n * Dessine une frame du jeu\n */\n drawFrame() {\n if (this.game) {\n this.game.draw(this.mouse);\n }\n }\n /**\n * Démarre la boucle d'animation du jeu\n */\n startAnimationLoop() {\n const animationLoop = (currentTime) => {\n this.animationFrameId = requestAnimationFrame(animationLoop);\n const deltaTime = currentTime - this.lastFrameTime;\n if (deltaTime < this.FRAME_INTERVAL)\n return;\n // Correction du time drift\n this.lastFrameTime = currentTime - (deltaTime % this.FRAME_INTERVAL);\n // Rendu d'une frame\n this.drawFrame();\n };\n this.animationFrameId = requestAnimationFrame(animationLoop);\n }\n /**\n * Initialise les écouteurs d'événements\n */\n initEventListeners() {\n // Handler pour le déplacement de la souris\n const mouseMoveHandler = (e) => {\n // Calcul des coordonnées relatives au canvas\n const rect = this.canvas.getBoundingClientRect();\n this.mouse.x = e.clientX - rect.left;\n this.mouse.y = e.clientY - rect.top;\n };\n // Handler pour le clic de souris\n const mouseDownHandler = (e) => {\n if (this.game) {\n this.game.click(e);\n }\n };\n // Enregistrement des écouteurs\n this.canvas.addEventListener('mousemove', mouseMoveHandler);\n this.canvas.addEventListener('mousedown', mouseDownHandler);\n // Enregistrement des callbacks de nettoyage\n this.cleanupCallbacks.push(() => {\n this.canvas.removeEventListener('mousemove', mouseMoveHandler);\n this.canvas.removeEventListener('mousedown', mouseDownHandler);\n });\n }\n /**\n * Précharge un deck\n */\n preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function* () {\n yield deck.preload();\n });\n }\n /**\n * Précharge une main\n */\n preloadHand(hand) {\n return __awaiter(this, void 0, void 0, function* () {\n yield hand.preload();\n });\n }\n /**\n * Initialise le jeu\n */\n initialize() {\n return __awaiter(this, void 0, void 0, function* () {\n if (this.isInitialized)\n return;\n try {\n console.log(\"Chargement en cours...\");\n // Création du jeu\n this.game = new _game__WEBPACK_IMPORTED_MODULE_0__.Game(this.ctx, this.canvas, this.staticCtx, this.staticCanvas);\n // Préchargement parallèle des ressources\n yield Promise.all([\n ...this.decks.map(deck => this.preloadDeck(deck)),\n ...this.hands.map(hand => this.preloadHand(hand)),\n this.game.preload()\n ]);\n console.log(\"Chargement terminé\");\n // Initialisation des écouteurs d'événements\n this.initEventListeners();\n // Démarrage de la boucle d'animation\n this.startAnimationLoop();\n // Marquer comme initialisé\n this.isInitialized = true;\n // Définir la fonction de nettoyage global\n window.cleanup = this.cleanup.bind(this);\n }\n catch (error) {\n console.error(\"Erreur lors de l'initialisation:\", error);\n }\n });\n }\n /**\n * Nettoie les ressources\n */\n cleanup() {\n var _a, _b;\n // Arrêter la boucle d'animation\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n // Exécuter tous les callbacks de nettoyage\n this.cleanupCallbacks.forEach(callback => callback());\n this.cleanupCallbacks = [];\n // Nettoyer les ressources du jeu\n if (this.game) {\n // Supposons que Game a une méthode cleanup\n (_b = (_a = this.game).cleanup) === null || _b === void 0 ? void 0 : _b.call(_a);\n this.game = null;\n }\n // Nettoyer les ressources des decks et des mains\n this.decks.forEach(deck => deck.cleanup());\n this.hands.forEach(hand => hand.cleanup());\n // Vider les collections\n this.decks = [];\n this.hands = [];\n // Réinitialiser l'état\n this.isInitialized = false;\n this.lastFrameTime = 0;\n this.mouse = { x: 0, y: 0 };\n // Effacer les canvas\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.staticCtx.clearRect(0, 0, this.staticCanvas.width, this.staticCanvas.height);\n }\n}\n// Fonction de nettoyage globale exportée\nfunction cleanup() {\n RiichiGameManager.getInstance().cleanup();\n}\n// Fonction d'initialisation globale exportée\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function* () {\n yield RiichiGameManager.getInstance().initialize();\n });\n}\n// Initialisation automatique si le script est chargé directement\nif (typeof window !== 'undefined') {\n initDisplay().catch(console.error);\n}\n\n\n//# sourceURL=webpack:///./src/display/dp4.ts?"); - -/***/ }), - -/***/ "./src/game.ts": -/*!*********************!*\ - !*** ./src/game.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Game: () => (/* binding */ Game)\n/* harmony export */ });\n/* harmony import */ var _deck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./deck */ \"./src/deck.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\n/* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./button */ \"./src/button.ts\");\n/* harmony import */ var _state__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./state */ \"./src/state.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n\n\n// Game constants to avoid magic numbers and improve readability\nconst GAME_CONSTANTS = {\n PLAYERS: 4,\n BACKGROUND: { color: \"#007730\", x: 0, y: 0, w: 1050, h: 1050 },\n DEAD_WALL_SIZE: 14,\n WAITING_TIME: {\n MIN: 500,\n MAX: 2000,\n DEFAULT: 700\n },\n DISPLAY: {\n HAND_SIZE: 0.7,\n HIDDEN_HAND_SIZE: 0.6,\n DISCARD_SIZE: 0.6,\n GROUP_SIZE: 0.6\n },\n PI: Math.PI\n};\nclass Game {\n constructor(ctx, cv, staticCtx, staticCv, red = false, level = 0) {\n this.deadWall = [];\n this.hands = [];\n this.discards = [];\n this.groups = [];\n this.turn = 0;\n this.waitingTime = GAME_CONSTANTS.WAITING_TIME.DEFAULT;\n this.selectedTile = undefined;\n this.canCall = false;\n this.hasPicked = false;\n this.hasPlayed = false;\n this.lastPlayed = Date.now();\n this.chooseChii = false;\n this.end = false;\n this.result = -1;\n // Cached values to avoid recalculations\n this.BG_RECT = GAME_CONSTANTS.BACKGROUND;\n this.rotations = [0, -GAME_CONSTANTS.PI / 2, -GAME_CONSTANTS.PI, GAME_CONSTANTS.PI / 2];\n this.ctx = ctx;\n this.cv = cv;\n this.staticCtx = staticCtx;\n this.staticCv = staticCv;\n this.level = level;\n // Initialize game elements\n this.deck = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(red);\n this.initializeGame();\n }\n initializeGame() {\n this.deck.shuffle();\n // Set up dead wall\n for (let i = 0; i < GAME_CONSTANTS.DEAD_WALL_SIZE; i++) {\n this.deadWall.push(this.deck.pop());\n }\n // Create player hands\n for (let i = 0; i < GAME_CONSTANTS.PLAYERS; i++) {\n this.hands.push(this.deck.getRandomHand());\n this.discards.push([]);\n this.groups.push([]);\n }\n this.lastDiscard = undefined;\n this.hands[0].sort();\n this.pick(0);\n }\n draw(mp) {\n // Clear static canvas once per frame\n this.staticCtx.clearRect(0, 0, this.cv.width, this.cv.height);\n // Draw background\n this.staticCtx.fillStyle = this.BG_RECT.color;\n this.staticCtx.fillRect(this.BG_RECT.x, this.BG_RECT.y, this.BG_RECT.w, this.BG_RECT.h);\n this.getSelected(mp);\n this.drawGame();\n // Use a single drawImage operation instead of clearing and redrawing\n this.ctx.clearRect(0, 0, this.cv.width, this.cv.height);\n this.ctx.drawImage(this.staticCv, 0, 0);\n }\n getDeck() {\n return this.deck;\n }\n getHands() {\n return this.hands;\n }\n isFinished() {\n return this.end;\n }\n click(mp) {\n const rect = this.cv.getBoundingClientRect();\n if (this.hasWin(0)) {\n this.end = true;\n this.result = 1;\n return;\n }\n if (this.chooseChii) {\n this.handleChiiSelection(mp, rect);\n return;\n }\n const action = (0,_button__WEBPACK_IMPORTED_MODULE_2__.clickAction)(mp.x - rect.x, mp.y - rect.y, this.canDoAChii().length > 0, this.canDoAPon(), false && 0, false && 0, false && 0);\n if (this.canCall && action !== -1) {\n this.handleCallAction(action);\n }\n else if (this.turn === 0 && this.selectedTile !== undefined) {\n this.handlePlayerDiscard();\n }\n }\n handleChiiSelection(mp, rect) {\n const allChii = this.getChii(0);\n const selection = (0,_button__WEBPACK_IMPORTED_MODULE_2__.clickChii)(mp.x - rect.x, mp.y - rect.y, allChii);\n if (selection === 0) {\n this.chooseChii = false;\n }\n else {\n this.chooseChii = false;\n this.chii(selection, 0);\n }\n }\n handleCallAction(action) {\n if (action === 0) { // Pass\n this.canCall = false;\n this.advanceTurn();\n }\n else if (action === 1) { // Chii\n const chiis = this.canDoAChii();\n if (chiis.length === 1) {\n this.chii(chiis[0], 0);\n }\n else {\n this.chooseChii = true;\n this.drawGame();\n }\n }\n else if (action === 2) { // Pon\n this.pon(this.turn);\n }\n }\n handlePlayerDiscard() {\n this.discard(0, this.selectedTile);\n if (!this.checkPon()) {\n const chiis = this.canDoAChii(1);\n if (chiis.length > 0) {\n const i = Math.floor(Math.random() * chiis.length);\n this.chii(chiis[i], 1);\n }\n else {\n this.advanceTurn();\n }\n }\n }\n advanceTurn() {\n this.updateWaitingTime();\n this.turn = (this.turn + 1) % GAME_CONSTANTS.PLAYERS;\n this.hasPicked = false;\n this.hasPlayed = false;\n }\n getSelected(mp) {\n const rect = this.cv.getBoundingClientRect();\n const x = 2.5 * 75 * 0.75;\n const y = 1050 - 250 * 0.6;\n const sizeHand = GAME_CONSTANTS.DISPLAY.HAND_SIZE;\n const mouseX = mp.x - x;\n const mouseY = mp.y;\n const tileWidth = 83.9;\n const tileIndex = Math.floor(mouseX / (tileWidth * sizeHand));\n const relativeX = mouseX - tileIndex * tileWidth * sizeHand;\n if (relativeX <= (tileWidth - 3) * sizeHand &&\n tileIndex >= 0 &&\n tileIndex < this.hands[0].length() &&\n mouseY >= y &&\n mouseY <= y + 100 * sizeHand) {\n this.selectedTile = tileIndex;\n }\n else {\n this.selectedTile = undefined;\n }\n }\n updateWaitingTime() {\n this.waitingTime = Math.floor(Math.random() *\n (GAME_CONSTANTS.WAITING_TIME.MAX - GAME_CONSTANTS.WAITING_TIME.MIN) +\n GAME_CONSTANTS.WAITING_TIME.MIN);\n }\n play() {\n if (this.turn !== 0 && !this.end) {\n if (!this.hasPicked) {\n // Begin of turn\n this.lastPlayed = Date.now();\n this.pick(this.turn);\n this.hasPicked = true;\n }\n else if (!this.hasPlayed) {\n // Middle of turn\n this.handleBotTurn();\n }\n else if (!this.canCall) {\n // End of turn\n this.advanceBotTurn();\n }\n }\n }\n handleBotTurn() {\n if (this.hasWin(this.turn)) {\n this.end = true;\n this.result = 2;\n return;\n }\n if (Date.now() - this.lastPlayed > this.waitingTime) {\n this.lastPlayed = Date.now();\n // Choose random tile to discard\n const n = Math.floor(this.hands[this.turn].length() * Math.random());\n this.discard(this.turn, n);\n this.hasPlayed = true;\n if (this.deck.length() <= 0) {\n this.result = 0;\n this.end = true;\n return;\n }\n if (!this.end) {\n this.checkPon();\n this.checkForChii();\n this.canCall = this.canDoAChii().length > 0 || this.canDoAPon();\n }\n }\n }\n checkForChii() {\n if (this.turn === 3)\n return;\n const nextPlayer = this.turn + 1;\n const chiis = this.canDoAChii(nextPlayer);\n if (chiis.length > 0) {\n const i = Math.floor(Math.random() * chiis.length);\n this.chii(chiis[i], nextPlayer);\n }\n }\n advanceBotTurn() {\n if (this.turn === 3) {\n this.turn = 0;\n this.pick(0);\n if (this.hasWin(0)) {\n this.end = true;\n this.result = 1;\n }\n }\n else {\n this.turn++;\n }\n this.updateWaitingTime();\n this.hasPicked = false;\n this.hasPlayed = false;\n }\n pick(player) {\n this.hands[player].push(this.deck.pop());\n this.hands[player].isolate = true;\n }\n discard(player, n) {\n const tile = this.hands[player].eject(n);\n this.hands[player].sort();\n tile.setTilt();\n this.discards[player].push(tile);\n this.hands[player].isolate = false;\n this.hands[player].sort();\n this.lastDiscard = player;\n this.lastPlayed = Date.now();\n }\n canDoAChii(p = 0) {\n const chii = [];\n // Check if chii is possible\n if (this.lastDiscard === undefined ||\n (this.lastDiscard + 1) % 4 !== p ||\n (this.turn + 1) % 4 !== p ||\n this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1].getFamily() >= 4) {\n return chii;\n }\n const t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1];\n const h = this.hands[p];\n const family = t.getFamily();\n const value = t.getValue();\n // Check for three possible chii patterns\n if (h.count(family, value - 2) > 0 && h.count(family, value - 1) > 0) {\n chii.push(value - 2);\n }\n if (h.count(family, value - 1) > 0 && h.count(family, value + 1) > 0) {\n chii.push(value - 1);\n }\n if (h.count(family, value + 1) > 0 && h.count(family, value + 2) > 0) {\n chii.push(value);\n }\n return chii;\n }\n chii(minValue, p) {\n const discardPlayer = p === 0 ? 3 : p - 1;\n const t = this.discards[discardPlayer].pop();\n this.lastDiscard = undefined;\n const tt = [];\n let tn = 0;\n let v = minValue;\n // Find the right tiles for the chii\n while (tn < 2) {\n if (v === t.getValue()) {\n v++;\n }\n else {\n tt[tn] = this.hands[p].find(t.getFamily(), v);\n tn++;\n v++;\n }\n }\n // Set tilt for all tiles in the group\n [t, tt[0], tt[1]].forEach(tile => tile.setTilt());\n // Create new group\n this.groups[p].push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t, tt[0], tt[1]], discardPlayer, p));\n if (this.hasWin(p)) {\n this.end = true;\n this.result = p === 0 ? 1 : 2;\n }\n this.updateWaitingTime();\n this.turn = p;\n this.hasPicked = true;\n this.hasPlayed = false;\n }\n getChii(p) {\n const chiis = [];\n const numChii = this.canDoAChii();\n const discardPlayer = p === 0 ? 3 : p - 1;\n const d = this.discards[discardPlayer];\n const t = d[d.length - 1];\n for (let i = 0; i < numChii.length; i++) {\n const v = numChii[i];\n const chii = [];\n for (let dv = 0; dv < 3; dv++) {\n if (v + dv === t.getValue()) {\n chii.push(t);\n }\n else {\n const tt = this.hands[p].find(t.getFamily(), v + dv);\n chii.push(tt);\n this.hands[p].push(tt);\n this.hands[p].sort();\n }\n }\n chiis.push(chii);\n }\n return chiis;\n }\n checkPon() {\n for (let p = 1; p < GAME_CONSTANTS.PLAYERS; p++) {\n if (this.canDoAPon(p)) {\n this.pon(this.lastDiscard, p);\n return true;\n }\n }\n return false;\n }\n canDoAPon(player = 0) {\n if (this.lastDiscard === undefined ||\n this.lastDiscard === player ||\n this.turn === player ||\n (this.hasPicked && !this.hasPlayed)) {\n return false;\n }\n const t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1];\n return this.hands[player].count(t.getFamily(), t.getValue()) >= 2;\n }\n pon(p, thief = 0) {\n const t = this.discards[p].pop();\n this.lastDiscard = undefined;\n const t2 = this.hands[thief].find(t.getFamily(), t.getValue());\n const t3 = this.hands[thief].find(t.getFamily(), t.getValue());\n [t, t2, t3].forEach(tile => tile.setTilt());\n this.groups[thief].push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t, t2, t3], p, thief));\n if (this.hasWin(thief)) {\n this.end = true;\n this.result = thief === 0 ? 1 : 2;\n }\n this.updateWaitingTime();\n this.turn = thief;\n this.hasPicked = true;\n this.hasPlayed = false;\n }\n hasWin(p) {\n return this.hands[p].toGroup() !== undefined;\n }\n drawGame() {\n // Update game state\n this.play();\n // Draw game elements\n (0,_state__WEBPACK_IMPORTED_MODULE_3__.drawState)(this.staticCtx, this.turn);\n this.drawDiscardSize();\n this.drawResult();\n this.drawHands();\n this.drawGroups(GAME_CONSTANTS.DISPLAY.GROUP_SIZE);\n // Draw discards for all players\n for (let i = 0; i < GAME_CONSTANTS.PLAYERS; i++) {\n this.drawDiscard(i, this.selectedTile !== undefined ? this.hands[0].get(this.selectedTile) : undefined);\n }\n // Draw UI elements\n if (this.chooseChii) {\n (0,_button__WEBPACK_IMPORTED_MODULE_2__.drawChiis)(this.staticCtx, this.getChii(0));\n }\n else {\n (0,_button__WEBPACK_IMPORTED_MODULE_2__.drawButtons)(this.staticCtx, this.canDoAChii().length > 0, this.canDoAPon(), false && 0, false && 0, false && 0);\n }\n }\n drawHands() {\n const showHands = false;\n const { HAND_SIZE, HIDDEN_HAND_SIZE } = GAME_CONSTANTS.DISPLAY;\n // Draw player's hand\n this.hands[0].drawHand(this.staticCtx, 2.5 * 75 * 0.75, 1000 - 150 * HAND_SIZE, 5 * HAND_SIZE, 0.75, this.selectedTile, false, 0);\n // Draw opponents' hands\n this.hands[1].drawHand(this.staticCtx, 1000 - 150 * HIDDEN_HAND_SIZE, 1000 - 75 * 5 * HIDDEN_HAND_SIZE, 5 * HIDDEN_HAND_SIZE, HIDDEN_HAND_SIZE, undefined, !showHands, -GAME_CONSTANTS.PI / 2);\n this.hands[2].drawHand(this.staticCtx, 1000 - 75 * 5 * HIDDEN_HAND_SIZE, 150 * HIDDEN_HAND_SIZE, 5 * HIDDEN_HAND_SIZE, HIDDEN_HAND_SIZE, undefined, !showHands, -GAME_CONSTANTS.PI);\n this.hands[3].drawHand(this.staticCtx, 150 * HIDDEN_HAND_SIZE, 75 * 5 * HIDDEN_HAND_SIZE, 5 * HIDDEN_HAND_SIZE, HIDDEN_HAND_SIZE, undefined, !showHands, GAME_CONSTANTS.PI / 2);\n }\n drawGroups(size) {\n const offset = 25;\n for (let p = 0; p < GAME_CONSTANTS.PLAYERS; p++) {\n const rotation = this.rotations[p];\n const groups = this.groups[p];\n if (groups.length > 0) {\n for (let i = groups.length - 1; i >= 0; i--) {\n groups[i].drawGroup(this.staticCtx, 1050 - 240 - (260 + offset) * size * i, 1050 - 62, 5, 0.6, rotation, this.selectedTile !== undefined ? this.hands[0].get(this.selectedTile) : undefined);\n }\n }\n }\n }\n drawDiscard(p, highlightedTile) {\n const sizeDiscard = GAME_CONSTANTS.DISPLAY.DISCARD_SIZE;\n this.staticCtx.save();\n this.staticCtx.translate(525, 525);\n this.staticCtx.rotate(this.rotations[p]);\n const x = -sizeDiscard * 475 / 2;\n const y = sizeDiscard * (475 / 2 + 5);\n // Draw all discarded tiles\n for (let i = 0; i < this.discards[p].length; i++) {\n const tile = this.discards[p][i];\n let tx, ty;\n if (i < 12) {\n tx = x + (i % 6) * 80 * sizeDiscard;\n ty = y + Math.floor(i / 6) * 105 * sizeDiscard;\n }\n else {\n tx = x + (i - 12) * 80 * sizeDiscard;\n ty = y + 2 * 105 * sizeDiscard;\n }\n tile.drawTile(this.staticCtx, tx, ty, sizeDiscard, false, 0, highlightedTile === null || highlightedTile === void 0 ? void 0 : highlightedTile.isEqual(tile.getFamily(), tile.getValue()));\n }\n // Draw indicator for last discard\n if (this.lastDiscard === p) {\n this.drawLastDiscardIndicator(p);\n }\n this.staticCtx.restore();\n }\n drawLastDiscardIndicator(p) {\n const j = this.discards[p].length - 1;\n const sizeDiscard = GAME_CONSTANTS.DISPLAY.DISCARD_SIZE;\n const x = -sizeDiscard * 475 / 2;\n const y = sizeDiscard * (475 / 2 + 5);\n let tx = j < 12\n ? x + (j % 6) * 80 * sizeDiscard\n : x + (j - 12) * 80 * sizeDiscard;\n let ty = j < 12\n ? y + Math.floor(j / 6) * 105 * sizeDiscard\n : y + 2 * 105 * sizeDiscard;\n tx += 75 / 2 * sizeDiscard;\n ty += 115 * sizeDiscard;\n const triangleSize = 10;\n this.staticCtx.fillStyle = \"#ff0000\";\n this.staticCtx.beginPath();\n this.staticCtx.moveTo(tx, ty);\n this.staticCtx.lineTo(tx + triangleSize / 2, ty + 0.866 * triangleSize);\n this.staticCtx.lineTo(tx - triangleSize / 2, ty + 0.866 * triangleSize);\n this.staticCtx.lineTo(tx, ty);\n this.staticCtx.fill();\n this.staticCtx.stroke();\n }\n drawDiscardSize() {\n this.staticCtx.fillStyle = \"#f070f0\";\n this.staticCtx.font = \"40px garamond\";\n const remainingTiles = this.deck.length();\n const x = remainingTiles < 10 ? 517 : 507;\n this.staticCtx.fillText(remainingTiles.toString(), x, 537);\n }\n drawResult() {\n if (this.result === -1)\n return;\n // Draw result background\n this.staticCtx.fillStyle = \"#e0e0f0\";\n this.staticCtx.fillRect(450, 430, 150, 190);\n this.staticCtx.fillRect(430, 450, 190, 150);\n // Draw result text\n this.staticCtx.fillStyle = \"#ff0000\";\n this.staticCtx.font = \"45px garamond\";\n if (this.result === 0) {\n this.staticCtx.fillText(\"Égalité\", 450, 535);\n }\n else if (this.result === 1) {\n this.staticCtx.fillText(\"Victoire !\", 440, 535);\n }\n else if (this.result === 2) {\n this.staticCtx.fillText(\"Défaite...\", 440, 535);\n }\n }\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield this.deck.preload();\n yield Promise.all(this.hands.map(h => h.preload()));\n });\n }\n}\n\n\n//# sourceURL=webpack:///./src/game.ts?"); - -/***/ }), - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/state.ts": -/*!**********************!*\ - !*** ./src/state.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ drawState: () => (/* binding */ drawState)\n/* harmony export */ });\nfunction drawState(ctx, turn = 0, rotation = 0) {\n let color = \"#e0e0f0\";\n let r = 30;\n let s = 100;\n let c = 525;\n let pi = Math.PI;\n // turn\n let w = 150;\n let h = 4;\n let rd = 2;\n let y = 525 + s + 5;\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate([0, -pi / 2, pi, pi / 2][turn]);\n ctx.translate(-525, -525);\n ctx.fillStyle = \"#ffcc33\";\n ctx.beginPath();\n ctx.moveTo(c, y);\n ctx.lineTo(c - w / 2 + rd, y);\n ctx.quadraticCurveTo(c - w / 2, y, c - w / 2, y + rd);\n ctx.lineTo(c - w / 2, y + h - rd);\n ctx.quadraticCurveTo(c - w / 2, y + h, c - w / 2 + rd, y + h);\n ctx.lineTo(c + w / 2 - rd, y + h);\n ctx.quadraticCurveTo(c + w / 2, y + h, c + w / 2, y + h - rd);\n ctx.lineTo(c + w / 2, y + rd);\n ctx.quadraticCurveTo(c + w / 2, y, c + w / 2 - rd, y);\n ctx.lineTo(c, y);\n ctx.fill();\n ctx.stroke();\n ctx.restore();\n // big rectangle\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.moveTo(c - s, c);\n ctx.lineTo(c - s, c + s - r);\n ctx.quadraticCurveTo(c - s, c + s, c - s + r, c + s);\n ctx.lineTo(c + s - r, c + s);\n ctx.quadraticCurveTo(c + s, c + s, c + s, c + s - r);\n ctx.lineTo(c + s, c - s + r);\n ctx.quadraticCurveTo(c + s, c - s, c + s - r, c - s);\n ctx.lineTo(c - s + r, c - s);\n ctx.quadraticCurveTo(c - s, c - s, c - s, c - s + r);\n ctx.lineTo(c - s, c);\n ctx.fill();\n ctx.stroke();\n // winds\n ctx.fillStyle = \"#000000\";\n ctx.font = \"40px garamond\";\n ctx.fillText(\"Est\", 505, 515 + s);\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(-3.141592 / 2);\n ctx.translate(-525, -525);\n ctx.fillText(\"Sud\", 500, 515 + s);\n ctx.restore();\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(3.141592);\n ctx.translate(-525, -525);\n ctx.fillText(\"Ouest\", 480, 515 + s);\n ctx.restore();\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(3.141592 / 2);\n ctx.translate(-525, -525);\n ctx.fillText(\"Nord\", 485, 515 + s);\n ctx.restore();\n ctx.restore();\n}\n\n\n//# sourceURL=webpack:///./src/state.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/display/dp4.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";class t{constructor(t,i,s){this.family=t,this.value=i,this.red=s,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,e=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}loadImg(t,i){return new Promise(((s,e)=>{t.onload=()=>s(),t.onerror=()=>e(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,s){this.tiles=t,this.stolenFrom=i,this.belongsTo=s}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,s,e,h,a,n){t.save(),t.translate(525,525),t.rotate(a),t.translate(-525,-525);const l=75*h,r=90*h,o=25*h/2,c=(this.belongsTo-this.stolenFrom-1+4)%4,d=void 0===n?-1:n.getFamily(),u=void 0===n?0:n.getValue(),g=(i,s,e,a)=>{i.drawTile(t,s,e,h,!1,a,i.isEqual(d,u))},f=Math.PI/2;switch(c){case 0:g(this.tiles[0],i,s+o,f),g(this.tiles[1],i+r,s,0),g(this.tiles[2],i+r+l+e*h,s,0);break;case 1:g(this.tiles[0],i,s,0),g(this.tiles[1],i+r,s+o,-f),g(this.tiles[2],i+r+l+3*e*h,s,0);break;case 2:g(this.tiles[0],i,s,0),g(this.tiles[1],i+l+e*h,s,0),g(this.tiles[2],i+r+l+e*h,s+o,-f);break;default:console.error(`Position non prise en charge: ${c}`)}t.restore()}}const s="m",e=1,h="p",a=2,n="s",l=3,r="w",o=4,c="d",d=5;class u{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;is.getFamily()===t&&s.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((s=>s.getFamily()===t&&s.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),s=i.getFamily(),e=i.getValue();if(this.count(s,e)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(s,e)>=2){const s=this.tryFormTriplet(i,t);if(s)return s}const h=this.count(s,e-1)>0,a=this.count(s,e-2)>0;if(h&&a){const s=this.tryFormSequence(i,t);if(s)return s}this.tiles.push(i),this.sort()}tryFormPair(t){const s=this.find(t.getFamily(),t.getValue()),e=this.toGroup(!0);if(this.tiles.push(s),this.sort(),void 0!==e)return this.tiles.push(t),this.sort(),e.push(new i([t,s],0,0)),e}tryFormTriplet(t,s){const e=this.find(t.getFamily(),t.getValue()),h=this.find(t.getFamily(),t.getValue()),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([t,e,h],0,0)),this.tiles.push(t),this.sort(),a}tryFormSequence(t,s){const e=this.find(t.getFamily(),t.getValue()-1),h=this.find(t.getFamily(),t.getValue()-2),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([h,e,t],0,0)),this.tiles.push(t),this.sort(),a}drawHand(t,i,s,e,h,a=void 0,n=!1,l=0){const r=(75+e)*h,o=Math.cos(l)*r,c=Math.sin(l)*r;for(let e=0;et.preloadImg())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class g{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,s,e,h=0,a=0){let n=i,l=s;const r=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const s of r){for(let i=s.start;i<=s.end;i++){const a=this.find(s.family,i);a&&(a.drawTile(t,n,l,e,!1,0,!1),n+=(75+h)*e)}n=i,l+=(100+a)*e}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(i);return s&&s.length>0&&(s.pop(),0===s.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,s)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const s=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(s)||[];e.push(i),this.tileIndexMap.set(s,e)}find(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);if(!e||0===e.length)return;const h=e[0];if(h>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[h],this.tiles[0]]=[this.tiles[0],this.tiles[h]],this.updateIndicesAfterSwap(0,h);const a=this.tiles.shift();return this.decrementIndicesAfterShift(),a}count(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);return e?e.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new u;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,e=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const s=this.tiles[t],e=this.tiles[i],h=this.getTileKey(s.getFamily(),s.getValue()),a=this.getTileKey(e.getFamily(),e.getValue()),n=this.tileIndexMap.get(h)||[],l=this.tileIndexMap.get(a)||[],r=n.indexOf(i),o=l.indexOf(t);-1!==r&&(n[r]=t),-1!==o&&(l[o]=i),this.tileIndexMap.set(h,n),this.tileIndexMap.set(a,l)}decrementIndicesAfterShift(){var t;for(const[i,s]of this.tileIndexMap.entries())this.tileIndexMap.set(i,s.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;tt)).map((([,t])=>t));return a.length>0&&a.push(m.action),a}(s,e,h,a,n);if(0===l.length)return-1;const r=960-l.length*f;if(!(838=0&&o10?l[o]:-1}(t.x-i.x,t.y-i.y,this.canDoAChii().length>0,this.canDoAPon(),!1,!1,!1);this.canCall&&-1!==s?this.handleCallAction(s):0===this.turn&&void 0!==this.selectedTile&&this.handlePlayerDiscard()}handleChiiSelection(t,i){const s=this.getChii(0),e=function(t,i,s){if(0===s.length)return-1;const e=960-(s.length+1)*f;if(!(838=0&&h10?h===s.length?0:s[h][0].getValue():-1}(t.x-i.x,t.y-i.y,s);0===e?this.chooseChii=!1:(this.chooseChii=!1,this.chii(e,0))}handleCallAction(t){if(0===t)this.canCall=!1,this.advanceTurn();else if(1===t){const t=this.canDoAChii();1===t.length?this.chii(t[0],0):(this.chooseChii=!0,this.drawGame())}else 2===t&&this.pon(this.turn)}handlePlayerDiscard(){if(this.discard(0,this.selectedTile),!this.checkPon()){const t=this.canDoAChii(1);if(t.length>0){const i=Math.floor(Math.random()*t.length);this.chii(t[i],1)}else this.advanceTurn()}}advanceTurn(){this.updateWaitingTime(),this.turn=(this.turn+1)%4,this.hasPicked=!1,this.hasPlayed=!1}getSelected(t){this.cv.getBoundingClientRect();const i=E.HAND_SIZE,s=t.x-140.625,e=t.y,h=Math.floor(s/(83.9*i));s-83.9*h*i<=80.9*i&&h>=0&&h=900&&e<=900+100*i?this.selectedTile=h:this.selectedTile=void 0}updateWaitingTime(){this.waitingTime=Math.floor(Math.random()*(S-P)+P)}play(){0===this.turn||this.end||(this.hasPicked?this.hasPlayed?this.canCall||this.advanceBotTurn():this.handleBotTurn():(this.lastPlayed=Date.now(),this.pick(this.turn),this.hasPicked=!0))}handleBotTurn(){if(this.hasWin(this.turn))return this.end=!0,void(this.result=2);if(Date.now()-this.lastPlayed>this.waitingTime){this.lastPlayed=Date.now();const t=Math.floor(this.hands[this.turn].length()*Math.random());if(this.discard(this.turn,t),this.hasPlayed=!0,this.deck.length()<=0)return this.result=0,void(this.end=!0);this.end||(this.checkPon(),this.checkForChii(),this.canCall=this.canDoAChii().length>0||this.canDoAPon())}}checkForChii(){if(3===this.turn)return;const t=this.turn+1,i=this.canDoAChii(t);if(i.length>0){const s=Math.floor(Math.random()*i.length);this.chii(i[s],t)}}advanceBotTurn(){3===this.turn?(this.turn=0,this.pick(0),this.hasWin(0)&&(this.end=!0,this.result=1)):this.turn++,this.updateWaitingTime(),this.hasPicked=!1,this.hasPlayed=!1}pick(t){this.hands[t].push(this.deck.pop()),this.hands[t].isolate=!0}discard(t,i){const s=this.hands[t].eject(i);this.hands[t].sort(),s.setTilt(),this.discards[t].push(s),this.hands[t].isolate=!1,this.hands[t].sort(),this.lastDiscard=t,this.lastPlayed=Date.now()}canDoAChii(t=0){const i=[];if(void 0===this.lastDiscard||(this.lastDiscard+1)%4!==t||(this.turn+1)%4!==t||this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1].getFamily()>=4)return i;const s=this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1],e=this.hands[t],h=s.getFamily(),a=s.getValue();return e.count(h,a-2)>0&&e.count(h,a-1)>0&&i.push(a-2),e.count(h,a-1)>0&&e.count(h,a+1)>0&&i.push(a-1),e.count(h,a+1)>0&&e.count(h,a+2)>0&&i.push(a),i}chii(t,s){const e=0===s?3:s-1,h=this.discards[e].pop();this.lastDiscard=void 0;const a=[];let n=0,l=t;for(;n<2;)l===h.getValue()||(a[n]=this.hands[s].find(h.getFamily(),l),n++),l++;[h,a[0],a[1]].forEach((t=>t.setTilt())),this.groups[s].push(new i([h,a[0],a[1]],e,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}getChii(t){const i=[],s=this.canDoAChii(),e=0===t?3:t-1,h=this.discards[e],a=h[h.length-1];for(let e=0;e=2}pon(t,s=0){const e=this.discards[t].pop();this.lastDiscard=void 0;const h=this.hands[s].find(e.getFamily(),e.getValue()),a=this.hands[s].find(e.getFamily(),e.getValue());[e,h,a].forEach((t=>t.setTilt())),this.groups[s].push(new i([e,h,a],t,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}hasWin(t){return void 0!==this.hands[t].toGroup()}drawGame(){this.play(),function(t,i=0,s=0){let e=525,h=Math.PI,a=630;t.save(),t.translate(525,525),t.rotate([0,-h/2,h,h/2][i]),t.translate(-525,-525),t.fillStyle="#ffcc33",t.beginPath(),t.moveTo(e,a),t.lineTo(452,a),t.quadraticCurveTo(450,a,450,632),t.lineTo(450,632),t.quadraticCurveTo(450,634,452,634),t.lineTo(598,634),t.quadraticCurveTo(600,634,600,632),t.lineTo(600,632),t.quadraticCurveTo(600,a,598,a),t.lineTo(e,a),t.fill(),t.stroke(),t.restore(),t.save(),t.translate(525,525),t.rotate(s),t.translate(-525,-525),t.fillStyle="#e0e0f0",t.beginPath(),t.moveTo(425,e),t.lineTo(425,595),t.quadraticCurveTo(425,625,455,625),t.lineTo(595,625),t.quadraticCurveTo(625,625,625,595),t.lineTo(625,455),t.quadraticCurveTo(625,425,595,425),t.lineTo(455,425),t.quadraticCurveTo(425,425,425,455),t.lineTo(425,e),t.fill(),t.stroke(),t.fillStyle="#000000",t.font="40px garamond",t.fillText("Est",505,615),t.save(),t.translate(525,525),t.rotate(-1.570796),t.translate(-525,-525),t.fillText("Sud",500,615),t.restore(),t.save(),t.translate(525,525),t.rotate(3.141592),t.translate(-525,-525),t.fillText("Ouest",480,615),t.restore(),t.save(),t.translate(525,525),t.rotate(1.570796),t.translate(-525,-525),t.fillText("Nord",485,615),t.restore(),t.restore()}(this.staticCtx,this.turn),this.drawDiscardSize(),this.drawResult(),this.drawHands(),this.drawGroups(E.GROUP_SIZE);for(let t=0;t<4;t++)this.drawDiscard(t,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0);this.chooseChii?function(t,i){const s=[...i].reverse();I(t,850,835,T);let e=1;for(const i of s)w(t,850-e*f,835,i),e++}(this.staticCtx,this.getChii(0)):function(t,i,s){const e=[[i,(t,i,s)=>I(t,i,s,p)],[s,(t,i,s)=>I(t,i,s,v)],[!1,(t,i,s)=>I(t,i,s,y)],[!1,(t,i,s)=>I(t,i,s,C)],[!1,(t,i,s)=>I(t,i,s,x)]];if(!e.some((([t])=>t)))return;e.unshift([!0,(t,i,s)=>I(t,i,s,m)]);let h=0;for(const[i,s]of e)i&&(s(t,850-h*f,835),h++)}(this.staticCtx,this.canDoAChii().length>0,this.canDoAPon())}drawHands(){const{HAND_SIZE:t,HIDDEN_HAND_SIZE:i}=E;this.hands[0].drawHand(this.staticCtx,140.625,1e3-150*t,5*t,.75,this.selectedTile,!1,0),this.hands[1].drawHand(this.staticCtx,1e3-150*i,1e3-375*i,5*i,i,void 0,!0,-M/2),this.hands[2].drawHand(this.staticCtx,1e3-375*i,150*i,5*i,i,void 0,!0,-M),this.hands[3].drawHand(this.staticCtx,150*i,375*i,5*i,i,void 0,!0,M/2)}drawGroups(t){for(let i=0;i<4;i++){const s=this.rotations[i],e=this.groups[i];if(e.length>0)for(let i=e.length-1;i>=0;i--)e[i].drawGroup(this.staticCtx,810-285*t*i,988,5,.6,s,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0)}}drawDiscard(t,i){const s=E.DISCARD_SIZE;this.staticCtx.save(),this.staticCtx.translate(525,525),this.staticCtx.rotate(this.rotations[t]);const e=475*-s/2,h=242.5*s;for(let a=0;at.preload())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}}var R=function(t,i,s,e){return new(s||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}))};class V{constructor(){this.CANVAS_ID="myCanvas",this.BG_RECT={x:0,y:0,w:1050,h:1050},this.FPS=60,this.FRAME_INTERVAL=1e3/this.FPS,this.mouse={x:0,y:0},this.game=null,this.decks=[],this.hands=[],this.animationFrameId=null,this.lastFrameTime=0,this.isInitialized=!1,this.cleanupCallbacks=[];const t=document.getElementById(this.CANVAS_ID);if(!t)throw new Error(`Canvas avec ID '${this.CANVAS_ID}' introuvable`);this.canvas=t,this.canvas.width=this.BG_RECT.w,this.canvas.height=this.BG_RECT.h;const i=t.getContext("2d",{alpha:!1});if(!i)throw new Error("Impossible d'obtenir le contexte 2D du canvas");this.ctx=i,this.staticCanvas=document.createElement("canvas"),this.staticCanvas.width=t.width,this.staticCanvas.height=t.height;const s=this.staticCanvas.getContext("2d",{alpha:!1});if(!s)throw new Error("Impossible d'obtenir le contexte du canvas statique");this.staticCtx=s}static getInstance(){return V.instance||(V.instance=new V),V.instance}drawFrame(){this.game&&this.game.draw(this.mouse)}startAnimationLoop(){const t=i=>{this.animationFrameId=requestAnimationFrame(t);const s=i-this.lastFrameTime;s{const i=this.canvas.getBoundingClientRect();this.mouse.x=t.clientX-i.left,this.mouse.y=t.clientY-i.top},i=t=>{this.game&&this.game.click(t)};this.canvas.addEventListener("mousemove",t),this.canvas.addEventListener("mousedown",i),this.cleanupCallbacks.push((()=>{this.canvas.removeEventListener("mousemove",t),this.canvas.removeEventListener("mousedown",i)}))}preloadDeck(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}preloadHand(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}initialize(){return R(this,void 0,void 0,(function*(){if(!this.isInitialized)try{console.log("Chargement en cours..."),this.game=new A(this.ctx,this.canvas,this.staticCtx,this.staticCanvas),yield Promise.all([...this.decks.map((t=>this.preloadDeck(t))),...this.hands.map((t=>this.preloadHand(t))),this.game.preload()]),console.log("Chargement terminé"),this.initEventListeners(),this.startAnimationLoop(),this.isInitialized=!0,window.cleanup=this.cleanup.bind(this)}catch(t){console.error("Erreur lors de l'initialisation:",t)}}))}cleanup(){var t,i;null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.cleanupCallbacks.forEach((t=>t())),this.cleanupCallbacks=[],this.game&&(null===(i=(t=this.game).cleanup)||void 0===i||i.call(t),this.game=null),this.decks.forEach((t=>t.cleanup())),this.hands.forEach((t=>t.cleanup())),this.decks=[],this.hands=[],this.isInitialized=!1,this.lastFrameTime=0,this.mouse={x:0,y:0},this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.staticCtx.clearRect(0,0,this.staticCanvas.width,this.staticCanvas.height)}}"undefined"!=typeof window&&function(){return R(this,void 0,void 0,(function*(){yield V.getInstance().initialize()}))}().catch(console.error)})(); \ No newline at end of file diff --git a/build/dp5.js b/build/dp5.js index 484d7d7..00f64dd 100644 --- a/build/dp5.js +++ b/build/dp5.js @@ -1,66 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/display/dp5.ts": -/*!****************************!*\ - !*** ./src/display/dp5.ts ***! - \****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ cleanup: () => (/* binding */ cleanup),\n/* harmony export */ initDisplay: () => (/* binding */ initDisplay)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n// Configuration globale\nconst CANVAS_ID = \"myCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };\nvar MOUSE = { x: 0, y: 0 };\nconst FPS = 30;\nconst FRAME_INTERVAL = 1000 / FPS;\n// variables globales\nconst DECKS = [];\nconst HANDS = [];\nvar GAME;\n// Optimisation des références\nlet animationFrameId;\nlet lastFrameTime = 0;\nconst callbacks = [];\n// Pré-calcul des dimensions\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\n// Cache statique\nconst staticCanvas = document.createElement('canvas');\nconst staticCtx = staticCanvas.getContext(\"2d\");\nstaticCanvas.width = canvas.width;\nstaticCanvas.height = canvas.height;\nfunction drawFrame() {\n if (!ctx)\n return;\n GAME === null || GAME === void 0 ? void 0 : GAME.draw(MOUSE);\n}\nfunction animationLoop(currentTime) {\n animationFrameId = requestAnimationFrame(animationLoop);\n const deltaTime = currentTime - lastFrameTime;\n if (deltaTime < FRAME_INTERVAL)\n return;\n lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL);\n drawFrame();\n}\nfunction initEventListeners() {\n const handlers = {\n mousedown: (e) => {\n GAME === null || GAME === void 0 ? void 0 : GAME.click(e);\n },\n mousemove: (e) => {\n MOUSE.x = e.x;\n MOUSE.y = e.y;\n }\n };\n callbacks.push(() => {\n canvas.removeEventListener('mousemove', handlers.mousemove);\n });\n canvas.addEventListener('mousedown', handlers.mousedown);\n}\nfunction preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function* () {\n yield deck.preload();\n });\n}\nfunction preloadHand(hand) {\n return __awaiter(this, void 0, void 0, function* () {\n yield hand.preload();\n });\n}\nfunction cleanup() {\n cancelAnimationFrame(animationFrameId);\n callbacks.forEach(fn => fn());\n}\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function* () {\n if (!ctx) {\n console.error(\"Context canvas indisponible\");\n return;\n }\n console.log(\"Load begining\\n\");\n // Préchargement des ressources si nécessaire\n // const deck = new Deck();\n // await preloadDeck(deck);\n });\n}\n// Initialisation automatique si le script est chargé directement\nif (typeof window !== 'undefined') {\n initDisplay().catch(console.error);\n}\n\n\n//# sourceURL=webpack:///./src/display/dp5.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The require scope -/******/ var __webpack_require__ = {}; -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = {}; -/******/ __webpack_modules__["./src/display/dp5.ts"](0, __webpack_exports__, __webpack_require__); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";const n=document.getElementById("myCanvas"),t=n.getContext("2d");n.width=1050,n.height=1050;const e=document.createElement("canvas");e.getContext("2d"),e.width=n.width,e.height=n.height,"undefined"!=typeof window&&function(){return n=this,e=void 0,i=function*(){t?console.log("Load begining\n"):console.error("Context canvas indisponible")},new((o=void 0)||(o=Promise))((function(t,c){function a(n){try{r(i.next(n))}catch(n){c(n)}}function d(n){try{r(i.throw(n))}catch(n){c(n)}}function r(n){var e;n.done?t(n.value):(e=n.value,e instanceof o?e:new o((function(n){n(e)}))).then(a,d)}r((i=i.apply(n,e||[])).next())}));var n,e,o,i}().catch(console.error)})(); \ No newline at end of file diff --git a/build/test.js b/build/test.js index c611906..5c814e5 100644 --- a/build/test.js +++ b/build/test.js @@ -1,156 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/tests/assert.ts": -/*!*****************************!*\ - !*** ./src/tests/assert.ts ***! - \*****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ assert: () => (/* binding */ assert)\n/* harmony export */ });\nfunction assert(b, msg) {\n if (b) {\n console.log(\"%c[SUCCES] \" + msg, \"color: green\");\n return 1;\n }\n else {\n console.log(\"%c[ECHEC] \" + msg, \"color: red\");\n return 0;\n }\n}\n\n\n//# sourceURL=webpack:///./src/tests/assert.ts?"); - -/***/ }), - -/***/ "./src/tests/test.ts": -/*!***************************!*\ - !*** ./src/tests/test.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _test_hand__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./test_hand */ \"./src/tests/test_hand.ts\");\n/* harmony import */ var _test_yakus__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./test_yakus */ \"./src/tests/test_yakus.ts\");\n\n\n\n\n//# sourceURL=webpack:///./src/tests/test.ts?"); - -/***/ }), - -/***/ "./src/tests/test_hand.ts": -/*!********************************!*\ - !*** ./src/tests/test_hand.ts ***! - \********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../hand */ \"./src/hand.ts\");\n/* harmony import */ var _assert__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./assert */ \"./src/tests/assert.ts\");\nvar _a, _b, _c, _d, _e;\n\n\nlet h0 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"s1s1\");\nlet h1 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3\");\nlet h2 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1\");\nlet h3 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m9m9m9 p9p9\");\nlet h4 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1p1\");\nlet count = 0;\nlet total = 0;\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_a = h0.toGroup()) === null || _a === void 0 ? void 0 : _a.length) === 1, \"s11 has 1 group\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_b = h1.toGroup()) === null || _b === void 0 ? void 0 : _b.length) === 1, \"m123 has 1 group\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_c = h2.toGroup()) === null || _c === void 0 ? void 0 : _c.length) === 2, \"m222 p11 has 2 groups\");\ncount == (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_d = h3.toGroup()) === null || _d === void 0 ? void 0 : _d.length) === 5, \"m123 p123 s123 m999 p99 has 5 groups\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_e = h4.toGroup()) === null || _e === void 0 ? void 0 : _e.length) === 2, \"m222 p111 has 2 groups\");\ntotal += 4;\nconsole.log(\"Succès: \" + count.toString() + \"/\" + total.toString());\n\n\n//# sourceURL=webpack:///./src/tests/test_hand.ts?"); - -/***/ }), - -/***/ "./src/tests/test_yakus.ts": -/*!*********************************!*\ - !*** ./src/tests/test_yakus.ts ***! - \*********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./assert */ \"./src/tests/assert.ts\");\n/* harmony import */ var _yakus_yaku__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../yakus/yaku */ \"./src/yakus/yaku.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../hand */ \"./src/hand.ts\");\n\n\n\nlet count = 0;\nlet total = 0;\nlet h1 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m5m6 m7m8m9 m1m2m3 m5m5\");\nlet h2 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nlet h3 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 p9p9p9 s1s1s1 w2w2w2 d1d1\");\nlet h4 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m2m3m4 p3p3p3 p4p5p6 s4s4 s6s7s8\");\nlet h5 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p7p8p9 p7p8p9 w3w3\");\nlet h6 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p7p8 p7p8p9 w3w3\");\nlet h7 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m7m8m9 p9p9\");\nlet h8 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p6 w0w0w0 w3w3w3\");\nlet h9 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 d1d1d1 d2d2d2 d3d3\");\nlet h10 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p6p6 d1d1d1 d2d2d2 d3d3d3\");\nlet h11 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 w1w1w1 w2w2w2 w3w3w3 w4w4\");\nlet h12 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nlet h13 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"d1d1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nlet h14 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m9m9m9 p1p1p1 p9p9p9 s1s1s1\");\nlet h15 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3\");\nlet h16 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m7m7\");\nlet h17 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m6m6\");\nlet h18 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nlet h19 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m7m7m7 p1p1p1 p5p5\");\n// lipeikou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.lipekou(h5, [], 0) === 1, \"m123 m123 p789 p789 w33 is Lipeikou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.lipekou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Lipeikou\");\ntotal += 2;\n// ryanpeikou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ryanpeikou(h5, [], 0) === 3, \"m123 m123 p789 p789 w33 is Ryanpeikou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ryanpeikou(h6, [], 0) === 0, \"m1123 m123 p678 p789 w33 is not Ryanpeikou\");\ntotal += 2;\n// pinfu\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.pinfu(h1, [], 0) === 1, \"m123 m456 m789 m123 p55 is Pinfu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.pinfu(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Pinfu\");\ntotal += 2;\n// sanshoku doujun\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanshokuDoujun(h7, [], 0) === 2, \"m123 p123 s123 m789 p99 is Shanshokou Doujun\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanshokuDoujun(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Shanshokou Doujun\");\ntotal += 2;\n// ittsuu\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ittsuu(h1, [], 0) === 2, \"m123 m456 m789 m123 m55 is Ittsuu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ittsuu(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Ittsu\");\ntotal += 2;\n// tanyao\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tanyao(h4, [], 0) === 1, \"m234 p333 p456 s44 s678 is Tanyao\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tanyao(h1, [], 0) === 0, \"m123 m456 m789 m123 p55 is not Tanyao\");\ntotal += 2;\n// yakuhai\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.yakuhai(h3, [], 2) === 1, \"m111 p999 s111 w222 d11 West is Yakuhai\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.yakuhai(h8, [], 3) === 2, \"m123 m123 p66 w000 w333 North is double Yakuhai\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.yakuhai(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 Est is not Yakuhai\");\ntotal += 3;\n// shousangen\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousangen(h9, [], 0) === 2, \"m123 m123 d111 d222 d33 is Shousangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousangen(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Shousangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousangen(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Shousangen\");\ntotal += 3;\n// daisangen\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisangen(h10, [], 0) === 13, \"m123 p66 d111 d222 d333 is Daisangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisangen(h9, [], 0) === 0, \"m123 m123 d111 d222 d33 is not Daisangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisangen(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Daisangen\");\ntotal += 3;\n// shousuushi\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousuushii(h11, [], 0) === 13, \"m123 w111 w222 w333 w44 is Shousuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousuushii(h12, [], 0) === 0, \"m11 w111 w222 w333 w444 is not Shousuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousuushii(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Shousuushi\");\ntotal += 3;\n// daisuushi\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisuushi(h12, [], 0) === 13, \"m11 w111 w222 w333 w444 is Daisuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisuushi(h11, [], 0) === 0, \"m123 w111 w222 w333 w44 is not Daisuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisuushi(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Daisuushi\");\ntotal += 3;\n// chanta\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chanta(h13, [], 0) === 2, \"d11 w111 w222 w333 w444 is Chanta\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chanta(h12, [], 0) === 2, \"m11 w111 w222 w333 w444 is Chanta\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chanta(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Chanta\");\ntotal += 3;\n// junchan\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.junchan(h7, [], 0) === 3, \"m123 p123 s123 m789 p99 is Junchan\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.junchan(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Junchan\");\ntotal += 2;\n// honroutou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.honroutou(h14, [], 0) === 2, \"m11 m999 p111 p999 s111 is Honroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.honroutou(h3, [], 0) === 2, \"m111 p999 s111 w222 d11 is Honroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.honroutou(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Honroutou\");\ntotal += 3;\n// chinroutou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chinroutou(h14, [], 0) === 13, \"m11 m999 p111 p999 s111 is Chinroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chinroutou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Chinroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chinroutou(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Chinroutou\");\ntotal += 3;\n// tsuuisou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tsuuiisou(h13, [], 0) === 13, \"d11 w111 w222 w333 w444 is Tsuuisou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tsuuiisou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Tsuuisou\");\ntotal += 2;\n// kokushiMusou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.kokushiMusou(h15, [], 0) === 13, \"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3 is Kokushi Musou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.kokushiMusou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Kokushi Musou\");\ntotal += 2;\n// chiitoitsu\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chiitoitsu(h16, [], 0) === 2, \"m11 m22 m33 m44 m55 m66 m77 is Chiitoitsu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chiitoitsu(h17, [], 0) === 0, \"m11 m22 m33 m44 m55 m66 m66 is not Chiitoitsu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chiitoitsu(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Chiitoitsu\");\ntotal += 3;\n// toitoi\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.toitoi(h2, [], 0) === 2, \"m111 m444 m777 p111 p55 is Toitoi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.toitoi(h7, [], 0) === 0, \"m123 p123 s123 m789 p99 is not Toitoi\");\ntotal += 2;\n// sanankou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanankou(h18, [], 0) === 2, \"m123 m444 m777 p111 p55 is Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanankou(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanankou(h7, [], 0) === 0, \"m123 p123 s123 m789 p99 is not Sanankou\");\ntotal += 3;\n// sanankou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.suuankou(h2, [], 0) === 13, \"m111 m444 m777 p111 p55 is Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.suuankou(h18, [], 0) === 0, \"m123 m444 m777 p111 p55 is not Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.suuankou(h7, [], 0) === 0, \"m123 p123 s123 m789 p99 is not Sanankou\");\ntotal += 3;\n// total\nconsole.log(\"Succès: \" + count.toString() + \"/\" + total.toString());\n\n\n//# sourceURL=webpack:///./src/tests/test_yakus.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }), - -/***/ "./src/yakus/yaku.ts": -/*!***************************!*\ - !*** ./src/yakus/yaku.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ yakus: () => (/* binding */ yakus)\n/* harmony export */ });\nfunction ord(g) {\n return g.getTiles().every(t => t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9);\n}\nfunction term(g) {\n return g.getTiles().every(t => {\n t.getFamily() < 4 &&\n (t.getValue() === 1 ||\n t.getValue() === 9);\n });\n}\nfunction honn(g) {\n return g.getTiles().every(t => t.getFamily() >= 4);\n}\nfunction chii(g) {\n let t = g.getTiles();\n return t[0].getValue() !== t[1].getValue();\n}\nfunction pon(g) {\n let t = g.getTiles();\n return t[0].getValue() === t[1].getValue();\n}\nconst yakus = {\n /**\n * double suite pure\n * 0/1\n */\n lipekou: function (hand, groups, wind) {\n if (groups.length > 0 && !yakus.ryanpeikou(hand, groups, wind)) { // ouvert\n return 0;\n }\n let gr = hand.toGroup();\n gr.sort((g1, g2) => g1.compare(g2));\n for (let i = 0; i < 3; i++) {\n let g1 = gr[i].getTiles();\n let g2 = gr[i + 1].getTiles();\n if (g1[0].isEqual(g2[0].getFamily(), g2[0].getValue()) &&\n chii(gr[i]) && chii(gr[i + 1])) {\n return 1;\n }\n }\n return 0;\n },\n /**\n * deux doubles suites pures\n * 0/3\n */\n ryanpeikou: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let gr = hand.toGroup();\n gr.filter(g => chii(g));\n if (gr.length < 4) { // pas assez de suite\n return 0;\n }\n gr.sort((g1, g2) => g1.compare(g2));\n let t1 = gr[0].getTiles()[0];\n let t2 = gr[1].getTiles()[0];\n let t3 = gr[2].getTiles()[0];\n let t4 = gr[3].getTiles()[0];\n if (t1.compare(t2) === 0 &&\n t3.compare(t4) === 0) {\n return 3;\n }\n return 0;\n },\n /**\n * tout suite\n * 0/1\n */\n pinfu: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let h = hand.toGroup();\n if (h !== undefined &&\n h.every(g => {\n let tiles = g.getTiles();\n return (chii(g) || tiles.length === 2);\n })) {\n return 1;\n }\n return 0;\n },\n /**\n * triple suite\n * 1/2\n */\n sanshokuDoujun: function (hand, groups, wind) {\n let h = hand.toGroup();\n let gr = [];\n if (h !== undefined) {\n gr = groups.concat(h);\n }\n else {\n gr = groups;\n }\n gr = gr.filter(g => chii(g));\n gr.sort((g1, g2) => g1.compare(g2));\n if (gr.length < 3) { // pas assez de chii\n return 0;\n }\n else if (gr.length === 3) {\n let t0 = gr[0].getTiles();\n let t1 = gr[1].getTiles();\n let t2 = gr[2].getTiles();\n if (t0[0].getValue() === t1[0].getValue() &&\n t0[0].getValue() === t2[0].getValue() &&\n t0[0].getFamily() !== t1[0].getFamily() &&\n t0[0].getFamily() !== t2[0].getFamily() &&\n t1[0].getFamily() !== t2[0].getFamily()) {\n return groups.length > 0 ? 1 : 2;\n }\n return 0;\n }\n else { // il y a un intrus\n for (let i = 0; i < 4; i++) {\n let index = [];\n for (let j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n let t0 = gr[index[0]].getTiles();\n let t1 = gr[index[1]].getTiles();\n let t2 = gr[index[2]].getTiles();\n if (t0[0].getValue() === t1[0].getValue() &&\n t0[0].getValue() === t2[0].getValue() &&\n t0[0].getFamily() !== t1[0].getFamily() &&\n t0[0].getFamily() !== t2[0].getFamily() &&\n t1[0].getFamily() !== t2[0].getFamily()) {\n return groups.length > 0 ? 1 : 2;\n }\n }\n return 0;\n }\n },\n /**\n * grande suite pure\n * 1/2\n */\n ittsuu: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => chii(g));\n gr.sort((g1, g2) => g1.compare(g2));\n if (gr.length < 3) { // trop peu de suite\n return 0;\n }\n else if (gr.length === 3) { // pile le bon nombre\n let g1 = gr[0].getTiles();\n let g2 = gr[1].getTiles();\n let g3 = gr[2].getTiles();\n if (g1[0].getFamily() === g2[0].getFamily() &&\n g2[0].getFamily() === g3[0].getFamily() &&\n g1[0].getValue() === 1 &&\n g2[0].getValue() === 4 &&\n g3[0].getValue() === 7) {\n return groups.length > 0 ? 1 : 2;\n }\n }\n else { // il y a un intrus\n for (let i = 0; i < 4; i++) {\n let index = [];\n for (let j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n let t0 = gr[index[0]].getTiles();\n let t1 = gr[index[1]].getTiles();\n let t2 = gr[index[2]].getTiles();\n if (t0[0].getValue() === 1 &&\n t1[0].getValue() === 4 &&\n t2[0].getValue() === 7 &&\n t0[0].getFamily() === t1[0].getFamily() &&\n t0[0].getFamily() === t2[0].getFamily()) {\n return groups.length > 0 ? 1 : 2;\n }\n }\n return 0;\n }\n return 0;\n },\n /**\n * tout ordinaire\n * 1/1\n */\n tanyao: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(g => ord(g)) &&\n h.every(g => ord(g))) {\n return 1;\n }\n return 0;\n },\n /**\n * brelan de valeur\n * 1/1\n */\n yakuhai: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g) && g.getTiles().length === 3);\n let han = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n let f = t[0].getFamily();\n let v = t[0].getValue();\n if (f === 5) { // brelan de dragon\n han++;\n }\n if (f === 4 && v === 0) { // vent d'est\n han++;\n }\n if (f === 4 && v === wind) { // vent du joueur\n han++;\n }\n });\n return han;\n },\n /**\n * trois petits dragons\n * 2/2\n */\n shousangen: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g));\n let nbPon = 0;\n let nbPair = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n if (t[0].getFamily() === 5) {\n if (t.length === 3) {\n nbPon++;\n }\n else {\n nbPair++;\n }\n }\n });\n if (nbPon == 2 && nbPair == 1) {\n return 2;\n }\n return 0;\n },\n /**\n * trois grands dragons\n * 13/13\n */\n daisangen: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g) &&\n g.getTiles().length === 3 &&\n g.getTiles()[0].getFamily() === 5);\n if (gr.length === 3) {\n return 13;\n }\n return 0;\n },\n /**\n * quatre petits vents\n * 13/13\n */\n shousuushii: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g));\n let nbPon = 0;\n let nbPair = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n if (t[0].getFamily() === 4) {\n if (t.length === 3) {\n nbPon++;\n }\n else {\n nbPair++;\n }\n }\n });\n if (nbPon == 3 && nbPair == 1) {\n return 13;\n }\n return 0;\n },\n /**\n * quatre grands vents\n * 13/13\n */\n daisuushi: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g));\n let nbPon = 0;\n let nbPair = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n if (t[0].getFamily() === 4) {\n if (t.length === 3) {\n nbPon++;\n }\n else {\n nbPair++;\n }\n }\n });\n if (nbPon == 4 && nbPair == 0) {\n return 13;\n }\n return 0;\n },\n /**\n * terminales et honneurs partout\n * 1/2\n */\n chanta: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => {\n let tiles = g.getTiles();\n let f = tiles[0].getFamily();\n let v = tiles[0].getValue();\n let vd = tiles[tiles.length - 1].getValue();\n return f < 4 && v !== 1 && vd !== 9;\n });\n if (gr.length > 0) {\n return 0;\n }\n return groups.length > 0 ? 1 : 2;\n },\n /**\n * terminales partout\n * 2/3\n */\n junchan: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => {\n let tiles = g.getTiles();\n let f = tiles[0].getFamily();\n let v = tiles[0].getValue();\n let vd = tiles[tiles.length - 1].getValue();\n return f > 3 || (v !== 1 && vd !== 9);\n });\n if (gr.length > 0) {\n return 0;\n }\n return groups.length > 0 ? 2 : 3;\n },\n /**\n * tout terminale et honneur\n * 2/2\n */\n honroutou: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => {\n let tiles = g.getTiles();\n let f = tiles[0].getFamily();\n let v = tiles[1].getValue();\n return f < 4 && v !== 1 && v !== 9;\n });\n if (gr.length > 0) {\n return 0;\n }\n return 2;\n },\n /**\n * tout terminale\n * 13/13\n */\n chinroutou: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(g => term(g)) &&\n h.every(g => term(g))) {\n return 13;\n }\n return 0;\n },\n /**\n * tout honneur\n * 13/13\n */\n tsuuiisou: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(g => honn(g)) &&\n h.every(g => honn(g))) {\n return 13;\n }\n return 0;\n },\n /**\n * treize orphelins\n * 0/13\n */\n kokushiMusou: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let h = hand.getTiles();\n if (h.some(t => t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9)) {\n return 0;\n }\n let count = 0;\n for (let i = 0; i < h.length - 1; i++) {\n if (h[i].isEqual(h[i + 1].getFamily(), h[i + 1].getValue())) {\n count++;\n }\n if (count > 1) {\n break;\n }\n }\n if (count === 1) {\n return 13;\n }\n return 0;\n },\n /**\n * sept paires\n * 0/2\n */\n chiitoitsu: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let h = hand.getTiles();\n if (h.length !== 14) {\n return 0;\n }\n for (let i = 0; i < 14; i += 2) {\n if (h[i].compare(h[i + 1]) !== 0 ||\n (i > 0 && h[i].compare(h[i - 1]) === 0)) {\n return 0;\n }\n }\n return 2;\n },\n /**\n * tout brelans\n * 2/2\n */\n toitoi: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (groups.every(g => pon(g)) &&\n (h === null || h === void 0 ? void 0 : h.every(g => pon(g)))) {\n return 2;\n }\n return 0;\n },\n /**\n * trois brelan cachés\n * 2/2\n */\n sanankou: function (hand, groups, wind) {\n let h = hand.toGroup();\n let count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(g => {\n if (pon(g) && g.getTiles().length === 3) {\n count++;\n }\n });\n if (count === 3) {\n return 2;\n }\n return 0;\n },\n /**\n * quatre brelans cachés\n * 0/13\n */\n suuankou: function (hand, groups, wind) {\n let h = hand.toGroup();\n let count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(g => {\n if (pon(g) && g.getTiles().length === 3) {\n count++;\n }\n });\n if (count === 4) {\n return 13;\n }\n return 0;\n },\n sanshokuDoukou: function (//TODO\n /**\n * triple brelan\n * 2/2\n */\n hand, groups, wind) {\n return 0;\n },\n sankantsu: function (//TODO\n /**\n * trois carrés\n * 2/2\n */\n hand, groups, wind) {\n return 0;\n },\n suukantsu: function (//TODO\n /**\n * quatre carrés\n * 13/13\n */\n hand, groups, wind) {\n return 0;\n },\n honitsu: function (//TODO\n /**\n * semie pure\n * 2/3\n */\n hand, groups, wind) {\n return 0;\n },\n chinitsu: function (\n /**\n * main pure\n * 5/6\n */\n hand, groups, wind) {\n let h = hand.getTiles();\n let t0 = h[0];\n if (h.every(t => t.getFamily() === t0.getFamily()) &&\n groups.every(g => g.getTiles().every(t => t.getFamily() === t0.getFamily()))) {\n return groups.length > 0 ? 5 : 6;\n }\n return 0;\n },\n ryuuisou: function (//TODO\n /**\n * main verte\n * 13/13\n */\n hand, groups, wind) {\n if (yakus.chinitsu(hand, groups, wind) === 0) {\n return 0;\n }\n return 0;\n },\n chuurenPoutou: function (//TODO\n /**\n * neuf portes\n * 0/13\n */\n hand, groups, wind) {\n if (groups.length > 0 || yakus.chinitsu(hand, groups, wind) === 0) {\n return 0;\n }\n return 0;\n }\n};\n\n\n//# sourceURL=webpack:///./src/yakus/yaku.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/tests/test.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";var t={274:(t,e,i)=>{i.d(e,{F:()=>s});class s{constructor(t,e,i){this.family=t,this.value=e,this.red=i,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,e){return this.family===t&&this.value===e}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,e=void 0,s=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:e})=>this.loadImg(t,e))))},new((i=void 0)||(i=Promise))((function(n,o){function u(t){try{l(s.next(t))}catch(t){o(t)}}function r(t){try{l(s.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(u,r)}l((s=s.apply(t,e||[])).next())}));var t,e,i,s}loadImg(t,e){return new Promise(((i,s)=>{t.onload=()=>i(),t.onerror=()=>s(),t.src=e}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}},305:(t,e,i)=>{function s(t,e){return t?(console.log("%c[SUCCES] "+e,"color: green"),1):(console.log("%c[ECHEC] "+e,"color: red"),0)}i.d(e,{v:()=>s})},679:(t,e,i)=>{var s,n,o,u,r,l=i(959),a=i(305);let m=new l.U("s1s1"),h=new l.U("m1m2m3"),g=new l.U("m2m2m2 p1p1"),p=new l.U("m1m2m3 p1p2p3 s1s2s3 m9m9m9 p9p9"),c=new l.U("m2m2m2 p1p1p1"),d=0,w=0;d+=(0,a.v)(1===(null===(s=m.toGroup())||void 0===s?void 0:s.length),"s11 has 1 group"),d+=(0,a.v)(1===(null===(n=h.toGroup())||void 0===n?void 0:n.length),"m123 has 1 group"),d+=(0,a.v)(2===(null===(o=g.toGroup())||void 0===o?void 0:o.length),"m222 p11 has 2 groups"),(0,a.v)(5===(null===(u=p.toGroup())||void 0===u?void 0:u.length),"m123 p123 s123 m999 p99 has 5 groups"),d+=(0,a.v)(2===(null===(r=c.toGroup())||void 0===r?void 0:r.length),"m222 p111 has 2 groups"),w+=4,console.log("Succès: "+d.toString()+"/"+w.toString())},762:(t,e,i)=>{var s=i(305);function n(t){return t.getTiles().every((t=>t.getFamily()<4&&t.getValue()>1&&t.getValue()<9))}function o(t){return t.getTiles().every((t=>{t.getFamily()<4&&(1===t.getValue()||t.getValue())}))}function u(t){return t.getTiles().every((t=>t.getFamily()>=4))}function r(t){let e=t.getTiles();return e[0].getValue()!==e[1].getValue()}function l(t){let e=t.getTiles();return e[0].getValue()===e[1].getValue()}const a={lipekou:function(t,e,i){if(e.length>0&&!a.ryanpeikou(t,e,i))return 0;let s=t.toGroup();s.sort(((t,e)=>t.compare(e)));for(let t=0;t<3;t++){let e=s[t].getTiles(),i=s[t+1].getTiles();if(e[0].isEqual(i[0].getFamily(),i[0].getValue())&&r(s[t])&&r(s[t+1]))return 1}return 0},ryanpeikou:function(t,e,i){if(e.length>0)return 0;let s=t.toGroup();if(s.filter((t=>r(t))),s.length<4)return 0;s.sort(((t,e)=>t.compare(e)));let n=s[0].getTiles()[0],o=s[1].getTiles()[0],u=s[2].getTiles()[0],l=s[3].getTiles()[0];return 0===n.compare(o)&&0===u.compare(l)?3:0},pinfu:function(t,e,i){if(e.length>0)return 0;let s=t.toGroup();return void 0!==s&&s.every((t=>{let e=t.getTiles();return r(t)||2===e.length}))?1:0},sanshokuDoujun:function(t,e,i){let s=t.toGroup(),n=[];if(n=void 0!==s?e.concat(s):e,n=n.filter((t=>r(t))),n.sort(((t,e)=>t.compare(e))),n.length<3)return 0;if(3===n.length){let t=n[0].getTiles(),i=n[1].getTiles(),s=n[2].getTiles();return t[0].getValue()===i[0].getValue()&&t[0].getValue()===s[0].getValue()&&t[0].getFamily()!==i[0].getFamily()&&t[0].getFamily()!==s[0].getFamily()&&i[0].getFamily()!==s[0].getFamily()?e.length>0?1:2:0}for(let t=0;t<4;t++){let i=[];for(let e=0;e<4;e++)e!==t&&i.push(e);let s=n[i[0]].getTiles(),o=n[i[1]].getTiles(),u=n[i[2]].getTiles();if(s[0].getValue()===o[0].getValue()&&s[0].getValue()===u[0].getValue()&&s[0].getFamily()!==o[0].getFamily()&&s[0].getFamily()!==u[0].getFamily()&&o[0].getFamily()!==u[0].getFamily())return e.length>0?1:2}return 0},ittsuu:function(t,e,i){let s=e.concat(t.toGroup());if(s=s.filter((t=>r(t))),s.sort(((t,e)=>t.compare(e))),s.length<3)return 0;if(3!==s.length){for(let t=0;t<4;t++){let i=[];for(let e=0;e<4;e++)e!==t&&i.push(e);let n=s[i[0]].getTiles(),o=s[i[1]].getTiles(),u=s[i[2]].getTiles();if(1===n[0].getValue()&&4===o[0].getValue()&&7===u[0].getValue()&&n[0].getFamily()===o[0].getFamily()&&n[0].getFamily()===u[0].getFamily())return e.length>0?1:2}return 0}{let t=s[0].getTiles(),i=s[1].getTiles(),n=s[2].getTiles();if(t[0].getFamily()===i[0].getFamily()&&i[0].getFamily()===n[0].getFamily()&&1===t[0].getValue()&&4===i[0].getValue()&&7===n[0].getValue())return e.length>0?1:2}return 0},tanyao:function(t,e,i){let s=t.toGroup();return void 0===s?0:e.every((t=>n(t)))&&s.every((t=>n(t)))?1:0},yakuhai:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)&&3===t.getTiles().length));let n=0;return s.forEach((t=>{let e=t.getTiles(),s=e[0].getFamily(),o=e[0].getValue();5===s&&n++,4===s&&0===o&&n++,4===s&&o===i&&n++})),n},shousangen:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)));let n=0,o=0;return s.forEach((t=>{let e=t.getTiles();5===e[0].getFamily()&&(3===e.length?n++:o++)})),2==n&&1==o?2:0},daisangen:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>l(t)&&3===t.getTiles().length&&5===t.getTiles()[0].getFamily())),3===s.length?13:0},shousuushii:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)));let n=0,o=0;return s.forEach((t=>{let e=t.getTiles();4===e[0].getFamily()&&(3===e.length?n++:o++)})),3==n&&1==o?13:0},daisuushi:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)));let n=0,o=0;return s.forEach((t=>{let e=t.getTiles();4===e[0].getFamily()&&(3===e.length?n++:o++)})),4==n&&0==o?13:0},chanta:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>{let e=t.getTiles(),i=e[0].getFamily(),s=e[0].getValue(),n=e[e.length-1].getValue();return i<4&&1!==s&&9!==n})),s.length>0?0:e.length>0?1:2},junchan:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>{let e=t.getTiles(),i=e[0].getFamily(),s=e[0].getValue(),n=e[e.length-1].getValue();return i>3||1!==s&&9!==n})),s.length>0?0:e.length>0?2:3},honroutou:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>{let e=t.getTiles(),i=e[0].getFamily(),s=e[1].getValue();return i<4&&1!==s&&9!==s})),s.length>0?0:2},chinroutou:function(t,e,i){let s=t.toGroup();return void 0===s?0:e.every((t=>o(t)))&&s.every((t=>o(t)))?13:0},tsuuiisou:function(t,e,i){let s=t.toGroup();return void 0===s?0:e.every((t=>u(t)))&&s.every((t=>u(t)))?13:0},kokushiMusou:function(t,e,i){if(e.length>0)return 0;let s=t.getTiles();if(s.some((t=>t.getFamily()<4&&t.getValue()>1&&t.getValue()<9)))return 0;let n=0;for(let t=0;t1));t++);return 1===n?13:0},chiitoitsu:function(t,e,i){if(e.length>0)return 0;let s=t.getTiles();if(14!==s.length)return 0;for(let t=0;t<14;t+=2)if(0!==s[t].compare(s[t+1])||t>0&&0===s[t].compare(s[t-1]))return 0;return 2},toitoi:function(t,e,i){let s=t.toGroup();return e.every((t=>l(t)))&&(null==s?void 0:s.every((t=>l(t))))?2:0},sanankou:function(t,e,i){let s=t.toGroup(),n=0;return null==s||s.forEach((t=>{l(t)&&3===t.getTiles().length&&n++})),3===n?2:0},suuankou:function(t,e,i){let s=t.toGroup(),n=0;return null==s||s.forEach((t=>{l(t)&&3===t.getTiles().length&&n++})),4===n?13:0},sanshokuDoukou:function(t,e,i){return 0},sankantsu:function(t,e,i){return 0},suukantsu:function(t,e,i){return 0},honitsu:function(t,e,i){return 0},chinitsu:function(t,e,i){let s=t.getTiles(),n=s[0];return s.every((t=>t.getFamily()===n.getFamily()))&&e.every((t=>t.getTiles().every((t=>t.getFamily()===n.getFamily()))))?e.length>0?5:6:0},ryuuisou:function(t,e,i){return a.chinitsu(t,e,i),0},chuurenPoutou:function(t,e,i){return e.length>0||a.chinitsu(t,e,i),0}};var m=i(959);let h=0,g=0,p=new m.U("m1m2m3 m4m5m6 m7m8m9 m1m2m3 m5m5"),c=new m.U("m1m1m1 m4m4m4 m7m7m7 p1p1p1 p5p5"),d=new m.U("m1m1m1 p9p9p9 s1s1s1 w2w2w2 d1d1"),w=new m.U("m2m3m4 p3p3p3 p4p5p6 s4s4 s6s7s8"),f=new m.U("m1m2m3 m1m2m3 p7p8p9 p7p8p9 w3w3"),v=new m.U("m1m2m3 m1m2m3 p6p7p8 p7p8p9 w3w3"),y=new m.U("m1m2m3 p1p2p3 s1s2s3 m7m8m9 p9p9"),F=new m.U("m1m2m3 m1m2m3 p6p6 w0w0w0 w3w3w3"),T=new m.U("m1m2m3 m1m2m3 d1d1d1 d2d2d2 d3d3"),k=new m.U("m1m2m3 p6p6 d1d1d1 d2d2d2 d3d3d3"),V=new m.U("m1m2m3 w1w1w1 w2w2w2 w3w3w3 w4w4"),S=new m.U("m1m1 w1w1w1 w2w2w2 w3w3w3 w4w4w4"),G=new m.U("d1d1 w1w1w1 w2w2w2 w3w3w3 w4w4w4"),U=new m.U("m1m1 m9m9m9 p1p1p1 p9p9p9 s1s1s1"),I=new m.U("m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3"),C=new m.U("m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m7m7"),E=new m.U("m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m6m6"),P=new m.U("m1m2m3 m4m4m4 m7m7m7 p1p1p1 p5p5");new m.U("m1m2m3 m7m7m7 p1p1p1 p5p5"),h+=(0,s.v)(1===a.lipekou(f,[],0),"m123 m123 p789 p789 w33 is Lipeikou"),h+=(0,s.v)(0===a.lipekou(d,[],0),"m111 p999 s111 w222 d11 is not Lipeikou"),g+=2,h+=(0,s.v)(3===a.ryanpeikou(f,[],0),"m123 m123 p789 p789 w33 is Ryanpeikou"),h+=(0,s.v)(0===a.ryanpeikou(v,[],0),"m1123 m123 p678 p789 w33 is not Ryanpeikou"),g+=2,h+=(0,s.v)(1===a.pinfu(p,[],0),"m123 m456 m789 m123 p55 is Pinfu"),h+=(0,s.v)(0===a.pinfu(c,[],0),"m111 m444 m777 p111 p55 is not Pinfu"),g+=2,h+=(0,s.v)(2===a.sanshokuDoujun(y,[],0),"m123 p123 s123 m789 p99 is Shanshokou Doujun"),h+=(0,s.v)(0===a.sanshokuDoujun(c,[],0),"m111 m444 m777 p111 p55 is not Shanshokou Doujun"),g+=2,h+=(0,s.v)(2===a.ittsuu(p,[],0),"m123 m456 m789 m123 m55 is Ittsuu"),h+=(0,s.v)(0===a.ittsuu(c,[],0),"m111 m444 m777 p111 p55 is not Ittsu"),g+=2,h+=(0,s.v)(1===a.tanyao(w,[],0),"m234 p333 p456 s44 s678 is Tanyao"),h+=(0,s.v)(0===a.tanyao(p,[],0),"m123 m456 m789 m123 p55 is not Tanyao"),g+=2,h+=(0,s.v)(1===a.yakuhai(d,[],2),"m111 p999 s111 w222 d11 West is Yakuhai"),h+=(0,s.v)(2===a.yakuhai(F,[],3),"m123 m123 p66 w000 w333 North is double Yakuhai"),h+=(0,s.v)(0===a.yakuhai(d,[],0),"m111 p999 s111 w222 d11 Est is not Yakuhai"),g+=3,h+=(0,s.v)(2===a.shousangen(T,[],0),"m123 m123 d111 d222 d33 is Shousangen"),h+=(0,s.v)(0===a.shousangen(k,[],0),"m123 p66 d111 d222 d333 is not Shousangen"),h+=(0,s.v)(0===a.shousangen(d,[],0),"m111 p999 s111 w222 d11 is not Shousangen"),g+=3,h+=(0,s.v)(13===a.daisangen(k,[],0),"m123 p66 d111 d222 d333 is Daisangen"),h+=(0,s.v)(0===a.daisangen(T,[],0),"m123 m123 d111 d222 d33 is not Daisangen"),h+=(0,s.v)(0===a.daisangen(d,[],0),"m111 p999 s111 w222 d11 is not Daisangen"),g+=3,h+=(0,s.v)(13===a.shousuushii(V,[],0),"m123 w111 w222 w333 w44 is Shousuushi"),h+=(0,s.v)(0===a.shousuushii(S,[],0),"m11 w111 w222 w333 w444 is not Shousuushi"),h+=(0,s.v)(0===a.shousuushii(d,[],0),"m111 p999 s111 w222 d11 is not Shousuushi"),g+=3,h+=(0,s.v)(13===a.daisuushi(S,[],0),"m11 w111 w222 w333 w444 is Daisuushi"),h+=(0,s.v)(0===a.daisuushi(V,[],0),"m123 w111 w222 w333 w44 is not Daisuushi"),h+=(0,s.v)(0===a.daisuushi(d,[],0),"m111 p999 s111 w222 d11 is not Daisuushi"),g+=3,h+=(0,s.v)(2===a.chanta(G,[],0),"d11 w111 w222 w333 w444 is Chanta"),h+=(0,s.v)(2===a.chanta(S,[],0),"m11 w111 w222 w333 w444 is Chanta"),h+=(0,s.v)(0===a.chanta(k,[],0),"m123 p66 d111 d222 d333 is not Chanta"),g+=3,h+=(0,s.v)(3===a.junchan(y,[],0),"m123 p123 s123 m789 p99 is Junchan"),h+=(0,s.v)(0===a.junchan(k,[],0),"m123 p66 d111 d222 d333 is not Junchan"),g+=2,h+=(0,s.v)(2===a.honroutou(U,[],0),"m11 m999 p111 p999 s111 is Honroutou"),h+=(0,s.v)(2===a.honroutou(d,[],0),"m111 p999 s111 w222 d11 is Honroutou"),h+=(0,s.v)(0===a.honroutou(k,[],0),"m123 p66 d111 d222 d333 is not Honroutou"),g+=3,h+=(0,s.v)(13===a.chinroutou(U,[],0),"m11 m999 p111 p999 s111 is Chinroutou"),h+=(0,s.v)(0===a.chinroutou(d,[],0),"m111 p999 s111 w222 d11 is not Chinroutou"),h+=(0,s.v)(0===a.chinroutou(k,[],0),"m123 p66 d111 d222 d333 is not Chinroutou"),g+=3,h+=(0,s.v)(13===a.tsuuiisou(G,[],0),"d11 w111 w222 w333 w444 is Tsuuisou"),h+=(0,s.v)(0===a.tsuuiisou(d,[],0),"m111 p999 s111 w222 d11 is not Tsuuisou"),g+=2,h+=(0,s.v)(13===a.kokushiMusou(I,[],0),"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3 is Kokushi Musou"),h+=(0,s.v)(0===a.kokushiMusou(d,[],0),"m111 p999 s111 w222 d11 is not Kokushi Musou"),g+=2,h+=(0,s.v)(2===a.chiitoitsu(C,[],0),"m11 m22 m33 m44 m55 m66 m77 is Chiitoitsu"),h+=(0,s.v)(0===a.chiitoitsu(E,[],0),"m11 m22 m33 m44 m55 m66 m66 is not Chiitoitsu"),h+=(0,s.v)(0===a.chiitoitsu(d,[],0),"m111 p999 s111 w222 d11 is not Chiitoitsu"),g+=3,h+=(0,s.v)(2===a.toitoi(c,[],0),"m111 m444 m777 p111 p55 is Toitoi"),h+=(0,s.v)(0===a.toitoi(y,[],0),"m123 p123 s123 m789 p99 is not Toitoi"),g+=2,h+=(0,s.v)(2===a.sanankou(P,[],0),"m123 m444 m777 p111 p55 is Sanankou"),h+=(0,s.v)(0===a.sanankou(c,[],0),"m111 m444 m777 p111 p55 is not Sanankou"),h+=(0,s.v)(0===a.sanankou(y,[],0),"m123 p123 s123 m789 p99 is not Sanankou"),g+=3,h+=(0,s.v)(13===a.suuankou(c,[],0),"m111 m444 m777 p111 p55 is Sanankou"),h+=(0,s.v)(0===a.suuankou(P,[],0),"m123 m444 m777 p111 p55 is not Sanankou"),h+=(0,s.v)(0===a.suuankou(y,[],0),"m123 p123 s123 m789 p99 is not Sanankou"),g+=3,console.log("Succès: "+h.toString()+"/"+g.toString())},879:(t,e,i)=>{i.d(e,{Y:()=>s});class s{constructor(t,e,i){this.tiles=t,this.stolenFrom=e,this.belongsTo=i}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const e=this.tiles[0].compare(t.tiles[0]);return 0!==e?e:this.tiles[1].compare(t.tiles[1])}drawGroup(t,e,i,s,n,o,u){t.save(),t.translate(525,525),t.rotate(o),t.translate(-525,-525);const r=75*n,l=90*n,a=25*n/2,m=(this.belongsTo-this.stolenFrom-1+4)%4,h=void 0===u?-1:u.getFamily(),g=void 0===u?0:u.getValue(),p=(e,i,s,o)=>{e.drawTile(t,i,s,n,!1,o,e.isEqual(h,g))},c=Math.PI/2;switch(m){case 0:p(this.tiles[0],e,i+a,c),p(this.tiles[1],e+l,i,0),p(this.tiles[2],e+l+r+s*n,i,0);break;case 1:p(this.tiles[0],e,i,0),p(this.tiles[1],e+l,i+a,-c),p(this.tiles[2],e+l+r+3*s*n,i,0);break;case 2:p(this.tiles[0],e,i,0),p(this.tiles[1],e+r+s*n,i,0),p(this.tiles[2],e+l+r+s*n,i+a,-c);break;default:console.error(`Position non prise en charge: ${m}`)}t.restore()}}},959:(t,e,i)=>{i.d(e,{U:()=>d});var s=i(274),n=i(879);const o="m",u=1,r="p",l=2,a="s",m=3,h="w",g=4,p="d",c=5;class d{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let e=0;ei.getFamily()===t&&i.getValue()===e))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const e=this.tiles.shift();return this.sort(),e}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,e)=>t.isLessThan(e)?-1:1))}count(t,e){return this.tiles.filter((i=>i.getFamily()===t&&i.getValue()===e)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const e=this.tiles.pop(),i=e.getFamily(),s=e.getValue();if(this.count(i,s)>=1&&!t){const t=this.tryFormPair(e);if(t)return t}if(this.count(i,s)>=2){const i=this.tryFormTriplet(e,t);if(i)return i}const n=this.count(i,s-1)>0,o=this.count(i,s-2)>0;if(n&&o){const i=this.tryFormSequence(e,t);if(i)return i}this.tiles.push(e),this.sort()}tryFormPair(t){const e=this.find(t.getFamily(),t.getValue()),i=this.toGroup(!0);if(this.tiles.push(e),this.sort(),void 0!==i)return this.tiles.push(t),this.sort(),i.push(new n.Y([t,e],0,0)),i}tryFormTriplet(t,e){const i=this.find(t.getFamily(),t.getValue()),s=this.find(t.getFamily(),t.getValue()),o=this.toGroup(e);if(this.tiles.push(i),this.tiles.push(s),this.sort(),void 0!==o)return o.push(new n.Y([t,i,s],0,0)),this.tiles.push(t),this.sort(),o}tryFormSequence(t,e){const i=this.find(t.getFamily(),t.getValue()-1),s=this.find(t.getFamily(),t.getValue()-2),o=this.toGroup(e);if(this.tiles.push(i),this.tiles.push(s),this.sort(),void 0!==o)return o.push(new n.Y([s,i,t],0,0)),this.tiles.push(t),this.sort(),o}drawHand(t,e,i,s,n,o=void 0,u=!1,r=0){const l=(75+s)*n,a=Math.cos(r)*l,m=Math.sin(r)*l;for(let s=0;st.preloadImg())))},new((i=void 0)||(i=Promise))((function(n,o){function u(t){try{l(s.next(t))}catch(t){o(t)}}function r(t){try{l(s.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(u,r)}l((s=s.apply(t,e||[])).next())}));var t,e,i,s}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s](o,o.exports,i),o.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),i(679),i(762)})(); \ No newline at end of file diff --git a/build/test_hand.js b/build/test_hand.js index 7da8238..3219afc 100644 --- a/build/test_hand.js +++ b/build/test_hand.js @@ -1,126 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/tests/assert.ts": -/*!*****************************!*\ - !*** ./src/tests/assert.ts ***! - \*****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ assert: () => (/* binding */ assert)\n/* harmony export */ });\nfunction assert(b, msg) {\n if (b) {\n console.log(\"%c[SUCCES] \" + msg, \"color: green\");\n return 1;\n }\n else {\n console.log(\"%c[ECHEC] \" + msg, \"color: red\");\n return 0;\n }\n}\n\n\n//# sourceURL=webpack:///./src/tests/assert.ts?"); - -/***/ }), - -/***/ "./src/tests/test_hand.ts": -/*!********************************!*\ - !*** ./src/tests/test_hand.ts ***! - \********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../hand */ \"./src/hand.ts\");\n/* harmony import */ var _assert__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./assert */ \"./src/tests/assert.ts\");\nvar _a, _b, _c, _d, _e;\n\n\nlet h0 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"s1s1\");\nlet h1 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3\");\nlet h2 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1\");\nlet h3 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m9m9m9 p9p9\");\nlet h4 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1p1\");\nlet count = 0;\nlet total = 0;\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_a = h0.toGroup()) === null || _a === void 0 ? void 0 : _a.length) === 1, \"s11 has 1 group\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_b = h1.toGroup()) === null || _b === void 0 ? void 0 : _b.length) === 1, \"m123 has 1 group\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_c = h2.toGroup()) === null || _c === void 0 ? void 0 : _c.length) === 2, \"m222 p11 has 2 groups\");\ncount == (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_d = h3.toGroup()) === null || _d === void 0 ? void 0 : _d.length) === 5, \"m123 p123 s123 m999 p99 has 5 groups\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(((_e = h4.toGroup()) === null || _e === void 0 ? void 0 : _e.length) === 2, \"m222 p111 has 2 groups\");\ntotal += 4;\nconsole.log(\"Succès: \" + count.toString() + \"/\" + total.toString());\n\n\n//# sourceURL=webpack:///./src/tests/test_hand.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/tests/test_hand.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";var t={274:(t,i,s)=>{s.d(i,{F:()=>e});class e{constructor(t,i,s){this.family=t,this.value=i,this.red=s,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,e=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((s=void 0)||(s=Promise))((function(r,o){function l(t){try{h(e.next(t))}catch(t){o(t)}}function n(t){try{h(e.throw(t))}catch(t){o(t)}}function h(t){var i;t.done?r(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(l,n)}h((e=e.apply(t,i||[])).next())}));var t,i,s,e}loadImg(t,i){return new Promise(((s,e)=>{t.onload=()=>s(),t.onerror=()=>e(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}},305:(t,i,s)=>{function e(t,i){return t?(console.log("%c[SUCCES] "+i,"color: green"),1):(console.log("%c[ECHEC] "+i,"color: red"),0)}s.d(i,{v:()=>e})},879:(t,i,s)=>{s.d(i,{Y:()=>e});class e{constructor(t,i,s){this.tiles=t,this.stolenFrom=i,this.belongsTo=s}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,s,e,r,o,l){t.save(),t.translate(525,525),t.rotate(o),t.translate(-525,-525);const n=75*r,h=90*r,a=25*r/2,u=(this.belongsTo-this.stolenFrom-1+4)%4,m=void 0===l?-1:l.getFamily(),c=void 0===l?0:l.getValue(),g=(i,s,e,o)=>{i.drawTile(t,s,e,r,!1,o,i.isEqual(m,c))},p=Math.PI/2;switch(u){case 0:g(this.tiles[0],i,s+a,p),g(this.tiles[1],i+h,s,0),g(this.tiles[2],i+h+n+e*r,s,0);break;case 1:g(this.tiles[0],i,s,0),g(this.tiles[1],i+h,s+a,-p),g(this.tiles[2],i+h+n+3*e*r,s,0);break;case 2:g(this.tiles[0],i,s,0),g(this.tiles[1],i+n+e*r,s,0),g(this.tiles[2],i+h+n+e*r,s+a,-p);break;default:console.error(`Position non prise en charge: ${u}`)}t.restore()}}},959:(t,i,s)=>{s.d(i,{U:()=>d});var e=s(274),r=s(879);const o="m",l=1,n="p",h=2,a="s",u=3,m="w",c=4,g="d",p=5;class d{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;is.getFamily()===t&&s.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((s=>s.getFamily()===t&&s.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),s=i.getFamily(),e=i.getValue();if(this.count(s,e)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(s,e)>=2){const s=this.tryFormTriplet(i,t);if(s)return s}const r=this.count(s,e-1)>0,o=this.count(s,e-2)>0;if(r&&o){const s=this.tryFormSequence(i,t);if(s)return s}this.tiles.push(i),this.sort()}tryFormPair(t){const i=this.find(t.getFamily(),t.getValue()),s=this.toGroup(!0);if(this.tiles.push(i),this.sort(),void 0!==s)return this.tiles.push(t),this.sort(),s.push(new r.Y([t,i],0,0)),s}tryFormTriplet(t,i){const s=this.find(t.getFamily(),t.getValue()),e=this.find(t.getFamily(),t.getValue()),o=this.toGroup(i);if(this.tiles.push(s),this.tiles.push(e),this.sort(),void 0!==o)return o.push(new r.Y([t,s,e],0,0)),this.tiles.push(t),this.sort(),o}tryFormSequence(t,i){const s=this.find(t.getFamily(),t.getValue()-1),e=this.find(t.getFamily(),t.getValue()-2),o=this.toGroup(i);if(this.tiles.push(s),this.tiles.push(e),this.sort(),void 0!==o)return o.push(new r.Y([e,s,t],0,0)),this.tiles.push(t),this.sort(),o}drawHand(t,i,s,e,r,o=void 0,l=!1,n=0){const h=(75+e)*r,a=Math.cos(n)*h,u=Math.sin(n)*h;for(let e=0;et.preloadImg())))},new((s=void 0)||(s=Promise))((function(r,o){function l(t){try{h(e.next(t))}catch(t){o(t)}}function n(t){try{h(e.throw(t))}catch(t){o(t)}}function h(t){var i;t.done?r(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(l,n)}h((e=e.apply(t,i||[])).next())}));var t,i,s,e}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}}},i={};function s(e){var r=i[e];if(void 0!==r)return r.exports;var o=i[e]={exports:{}};return t[e](o,o.exports,s),o.exports}s.d=(t,i)=>{for(var e in i)s.o(i,e)&&!s.o(t,e)&&Object.defineProperty(t,e,{enumerable:!0,get:i[e]})},s.o=(t,i)=>Object.prototype.hasOwnProperty.call(t,i);var e,r,o,l,n,h=s(959),a=s(305);let u=new h.U("s1s1"),m=new h.U("m1m2m3"),c=new h.U("m2m2m2 p1p1"),g=new h.U("m1m2m3 p1p2p3 s1s2s3 m9m9m9 p9p9"),p=new h.U("m2m2m2 p1p1p1"),d=0,f=0;d+=(0,a.v)(1===(null===(e=u.toGroup())||void 0===e?void 0:e.length),"s11 has 1 group"),d+=(0,a.v)(1===(null===(r=m.toGroup())||void 0===r?void 0:r.length),"m123 has 1 group"),d+=(0,a.v)(2===(null===(o=c.toGroup())||void 0===o?void 0:o.length),"m222 p11 has 2 groups"),(0,a.v)(5===(null===(l=g.toGroup())||void 0===l?void 0:l.length),"m123 p123 s123 m999 p99 has 5 groups"),d+=(0,a.v)(2===(null===(n=p.toGroup())||void 0===n?void 0:n.length),"m222 p111 has 2 groups"),f+=4,console.log("Succès: "+d.toString()+"/"+f.toString())})(); \ No newline at end of file diff --git a/build/test_yakus.js b/build/test_yakus.js index d2ffd92..388afe6 100644 --- a/build/test_yakus.js +++ b/build/test_yakus.js @@ -1,136 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/group.ts": -/*!**********************!*\ - !*** ./src/group.ts ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Group: () => (/* binding */ Group)\n/* harmony export */ });\nclass Group {\n constructor(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n push(tile) {\n this.tiles.push(tile);\n }\n pop() {\n return this.tiles.pop();\n }\n getTiles() {\n return this.tiles;\n }\n compare(g) {\n // Compare les premiers tiles, puis les seconds si égalité\n const firstComparison = this.tiles[0].compare(g.tiles[0]);\n return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]);\n }\n drawGroup(ctx, x, y, os, size, rotation, selectedTile) {\n // Sauvegarde et rotation du contexte\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n // Calcul des paramètres de dessin\n const v = 75 * size;\n const w = 90 * size;\n const osy = 25 * size / 2;\n const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n // Détermination du tile sélectionné\n const sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n const sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n // Fonction helper pour éviter la répétition de code\n const drawTile = (tile, tx, ty, angle) => {\n tile.drawTile(ctx, tx, ty, size, false, angle, tile.isEqual(sf, sv));\n };\n const HALF_PI = Math.PI / 2;\n // Dessin selon la position\n switch (p) {\n case 0:\n drawTile(this.tiles[0], x, y + osy, HALF_PI);\n drawTile(this.tiles[1], x + w, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y, 0);\n break;\n case 1:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + w, y + osy, -HALF_PI);\n drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0);\n break;\n case 2:\n drawTile(this.tiles[0], x, y, 0);\n drawTile(this.tiles[1], x + v + os * size, y, 0);\n drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI);\n break;\n default:\n console.error(`Position non prise en charge: ${p}`);\n }\n ctx.restore();\n }\n}\n\n\n//# sourceURL=webpack:///./src/group.ts?"); - -/***/ }), - -/***/ "./src/hand.ts": -/*!*********************!*\ - !*** ./src/hand.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Hand: () => (/* binding */ Hand)\n/* harmony export */ });\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _group__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./group */ \"./src/group.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Constants to avoid magic numbers and improve readability\nconst TILE_TYPES = {\n MANZU: { code: \"m\", family: 1 },\n PINZU: { code: \"p\", family: 2 },\n SOUZU: { code: \"s\", family: 3 },\n WINDS: { code: \"w\", family: 4 },\n DRAGONS: { code: \"d\", family: 5 }\n};\nclass Hand {\n /**\n * Create a hand from string representation\n * @param stiles String representation of tiles (e.g., \"m1p2s3w1d1\")\n */\n constructor(stiles = \"\") {\n this.isolate = false;\n this.tiles = [];\n this.initializeFromString(stiles);\n }\n /**\n * Parse string representation into tiles\n */\n initializeFromString(stiles) {\n for (let i = 0; i < stiles.length - 1; i++) {\n const tileCode = stiles.substring(i, i + 2);\n const type = tileCode[0];\n const value = Number(tileCode[1]);\n if (this.isValidTileCode(type, value)) {\n this.addTileFromCode(type, value);\n }\n }\n }\n /**\n * Check if tile code is valid\n */\n isValidTileCode(type, value) {\n return ((type === TILE_TYPES.MANZU.code) ||\n (type === TILE_TYPES.PINZU.code) ||\n (type === TILE_TYPES.SOUZU.code) ||\n (type === TILE_TYPES.WINDS.code) ||\n (type === TILE_TYPES.DRAGONS.code));\n }\n /**\n * Create a tile from type code and value\n */\n addTileFromCode(type, value) {\n const familyMap = {\n [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family,\n [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family,\n [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family,\n [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family,\n [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family\n };\n const family = familyMap[type];\n if (family !== undefined) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(family, value, false));\n }\n }\n /**\n * Get all tiles in hand\n */\n getTiles() {\n return this.tiles;\n }\n /**\n * Get number of tiles in hand\n */\n length() {\n return this.tiles.length;\n }\n /**\n * Add a tile to hand\n */\n push(tile) {\n this.tiles.push(tile);\n }\n /**\n * Remove and return the last tile\n */\n pop() {\n return this.tiles.pop();\n }\n /**\n * Find and remove a specific tile by family and value\n */\n find(family, value) {\n const index = this.findTileIndex(family, value);\n if (index !== -1) {\n // Swap with first tile and remove\n [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n return undefined;\n }\n /**\n * Find index of tile with specific family and value\n */\n findTileIndex(family, value) {\n return this.tiles.findIndex(tile => tile.getFamily() === family && tile.getValue() === value);\n }\n /**\n * Remove tile at specific index\n */\n eject(idTile) {\n if (idTile < 0 || idTile >= this.tiles.length) {\n throw new Error(\"Invalid tile index\");\n }\n // Swap with first tile and remove\n [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]];\n const tile = this.tiles.shift();\n this.sort();\n return tile;\n }\n /**\n * Get tile at specific index without removing\n */\n get(idTile) {\n if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) {\n return undefined;\n }\n return this.tiles[idTile];\n }\n /**\n * Sort tiles in ascending order\n */\n sort() {\n this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);\n }\n /**\n * Count tiles with specific family and value\n */\n count(family, value) {\n return this.tiles.filter(tile => tile.getFamily() === family && tile.getValue() === value).length;\n }\n /**\n * Try to form hand into groups (for winning detection)\n */\n toGroup(pair = false) {\n if (this.tiles.length === 0) {\n return [];\n }\n // Take last tile to try forming a group\n const lastTile = this.tiles.pop();\n const family = lastTile.getFamily();\n const value = lastTile.getValue();\n // Try to form a pair\n if (this.count(family, value) >= 1 && !pair) {\n const result = this.tryFormPair(lastTile);\n if (result)\n return result;\n }\n // Try to form a triplet (pon)\n if (this.count(family, value) >= 2) {\n const result = this.tryFormTriplet(lastTile, pair);\n if (result)\n return result;\n }\n // Try to form a sequence (chii)\n const hasMinusOne = this.count(family, value - 1) > 0;\n const hasMinusTwo = this.count(family, value - 2) > 0;\n if (hasMinusOne && hasMinusTwo) {\n const result = this.tryFormSequence(lastTile, pair);\n if (result)\n return result;\n }\n // If no valid group could be formed, put tile back and return undefined\n this.tiles.push(lastTile);\n this.sort();\n return undefined;\n }\n /**\n * Try to form a pair with the given tile\n */\n tryFormPair(tile) {\n const pairTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(true);\n // Put the tile back\n this.tiles.push(pairTile);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(tile);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, pairTile], 0, 0));\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a triplet (pon) with the given tile\n */\n tryFormTriplet(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue());\n const thirdTile = this.find(tile.getFamily(), tile.getValue());\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([tile, secondTile, thirdTile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Try to form a sequence (chii) with the given tile\n */\n tryFormSequence(tile, pair) {\n const secondTile = this.find(tile.getFamily(), tile.getValue() - 1);\n const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2);\n const groups = this.toGroup(pair);\n // Put tiles back\n this.tiles.push(secondTile);\n this.tiles.push(thirdTile);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([thirdTile, secondTile, tile], 0, 0));\n this.tiles.push(tile);\n this.sort();\n return groups;\n }\n return undefined;\n }\n /**\n * Draw hand tiles on canvas\n */\n drawHand(ctx, x, y, offset, size, focusedTile = undefined, hidden = false, rotation = 0) {\n const tileOffset = (75 + offset) * size;\n const offsetX = Math.cos(rotation) * tileOffset;\n const offsetY = Math.sin(rotation) * tileOffset;\n for (let i = 0; i < this.tiles.length; i++) {\n const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n // Calculate position\n let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation);\n let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation);\n // Add additional offset for focused tile\n if (i === focusedTile) {\n tileX += 25 * size * Math.sin(rotation);\n tileY -= 25 * size * Math.cos(rotation);\n }\n // Draw tile\n this.tiles[i].drawTile(ctx, tileX, tileY, size, hidden, rotation);\n }\n }\n /**\n * Preload tile images\n */\n preload() {\n return __awaiter(this, void 0, void 0, function* () {\n yield Promise.all(this.tiles.map(tile => tile.preloadImg()));\n });\n }\n /**\n * Clean up resources\n */\n cleanup() {\n this.tiles.forEach(tile => tile.cleanup());\n this.tiles = [];\n }\n}\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); - -/***/ }), - -/***/ "./src/tests/assert.ts": -/*!*****************************!*\ - !*** ./src/tests/assert.ts ***! - \*****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ assert: () => (/* binding */ assert)\n/* harmony export */ });\nfunction assert(b, msg) {\n if (b) {\n console.log(\"%c[SUCCES] \" + msg, \"color: green\");\n return 1;\n }\n else {\n console.log(\"%c[ECHEC] \" + msg, \"color: red\");\n return 0;\n }\n}\n\n\n//# sourceURL=webpack:///./src/tests/assert.ts?"); - -/***/ }), - -/***/ "./src/tests/test_yakus.ts": -/*!*********************************!*\ - !*** ./src/tests/test_yakus.ts ***! - \*********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./assert */ \"./src/tests/assert.ts\");\n/* harmony import */ var _yakus_yaku__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../yakus/yaku */ \"./src/yakus/yaku.ts\");\n/* harmony import */ var _hand__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../hand */ \"./src/hand.ts\");\n\n\n\nlet count = 0;\nlet total = 0;\nlet h1 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m5m6 m7m8m9 m1m2m3 m5m5\");\nlet h2 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nlet h3 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 p9p9p9 s1s1s1 w2w2w2 d1d1\");\nlet h4 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m2m3m4 p3p3p3 p4p5p6 s4s4 s6s7s8\");\nlet h5 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p7p8p9 p7p8p9 w3w3\");\nlet h6 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p7p8 p7p8p9 w3w3\");\nlet h7 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m7m8m9 p9p9\");\nlet h8 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p6 w0w0w0 w3w3w3\");\nlet h9 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 d1d1d1 d2d2d2 d3d3\");\nlet h10 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p6p6 d1d1d1 d2d2d2 d3d3d3\");\nlet h11 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 w1w1w1 w2w2w2 w3w3w3 w4w4\");\nlet h12 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nlet h13 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"d1d1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nlet h14 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m9m9m9 p1p1p1 p9p9p9 s1s1s1\");\nlet h15 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3\");\nlet h16 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m7m7\");\nlet h17 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m6m6\");\nlet h18 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nlet h19 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m7m7m7 p1p1p1 p5p5\");\n// lipeikou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.lipekou(h5, [], 0) === 1, \"m123 m123 p789 p789 w33 is Lipeikou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.lipekou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Lipeikou\");\ntotal += 2;\n// ryanpeikou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ryanpeikou(h5, [], 0) === 3, \"m123 m123 p789 p789 w33 is Ryanpeikou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ryanpeikou(h6, [], 0) === 0, \"m1123 m123 p678 p789 w33 is not Ryanpeikou\");\ntotal += 2;\n// pinfu\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.pinfu(h1, [], 0) === 1, \"m123 m456 m789 m123 p55 is Pinfu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.pinfu(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Pinfu\");\ntotal += 2;\n// sanshoku doujun\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanshokuDoujun(h7, [], 0) === 2, \"m123 p123 s123 m789 p99 is Shanshokou Doujun\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanshokuDoujun(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Shanshokou Doujun\");\ntotal += 2;\n// ittsuu\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ittsuu(h1, [], 0) === 2, \"m123 m456 m789 m123 m55 is Ittsuu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.ittsuu(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Ittsu\");\ntotal += 2;\n// tanyao\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tanyao(h4, [], 0) === 1, \"m234 p333 p456 s44 s678 is Tanyao\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tanyao(h1, [], 0) === 0, \"m123 m456 m789 m123 p55 is not Tanyao\");\ntotal += 2;\n// yakuhai\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.yakuhai(h3, [], 2) === 1, \"m111 p999 s111 w222 d11 West is Yakuhai\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.yakuhai(h8, [], 3) === 2, \"m123 m123 p66 w000 w333 North is double Yakuhai\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.yakuhai(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 Est is not Yakuhai\");\ntotal += 3;\n// shousangen\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousangen(h9, [], 0) === 2, \"m123 m123 d111 d222 d33 is Shousangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousangen(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Shousangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousangen(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Shousangen\");\ntotal += 3;\n// daisangen\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisangen(h10, [], 0) === 13, \"m123 p66 d111 d222 d333 is Daisangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisangen(h9, [], 0) === 0, \"m123 m123 d111 d222 d33 is not Daisangen\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisangen(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Daisangen\");\ntotal += 3;\n// shousuushi\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousuushii(h11, [], 0) === 13, \"m123 w111 w222 w333 w44 is Shousuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousuushii(h12, [], 0) === 0, \"m11 w111 w222 w333 w444 is not Shousuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.shousuushii(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Shousuushi\");\ntotal += 3;\n// daisuushi\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisuushi(h12, [], 0) === 13, \"m11 w111 w222 w333 w444 is Daisuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisuushi(h11, [], 0) === 0, \"m123 w111 w222 w333 w44 is not Daisuushi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.daisuushi(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Daisuushi\");\ntotal += 3;\n// chanta\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chanta(h13, [], 0) === 2, \"d11 w111 w222 w333 w444 is Chanta\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chanta(h12, [], 0) === 2, \"m11 w111 w222 w333 w444 is Chanta\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chanta(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Chanta\");\ntotal += 3;\n// junchan\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.junchan(h7, [], 0) === 3, \"m123 p123 s123 m789 p99 is Junchan\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.junchan(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Junchan\");\ntotal += 2;\n// honroutou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.honroutou(h14, [], 0) === 2, \"m11 m999 p111 p999 s111 is Honroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.honroutou(h3, [], 0) === 2, \"m111 p999 s111 w222 d11 is Honroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.honroutou(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Honroutou\");\ntotal += 3;\n// chinroutou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chinroutou(h14, [], 0) === 13, \"m11 m999 p111 p999 s111 is Chinroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chinroutou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Chinroutou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chinroutou(h10, [], 0) === 0, \"m123 p66 d111 d222 d333 is not Chinroutou\");\ntotal += 3;\n// tsuuisou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tsuuiisou(h13, [], 0) === 13, \"d11 w111 w222 w333 w444 is Tsuuisou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.tsuuiisou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Tsuuisou\");\ntotal += 2;\n// kokushiMusou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.kokushiMusou(h15, [], 0) === 13, \"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3 is Kokushi Musou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.kokushiMusou(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Kokushi Musou\");\ntotal += 2;\n// chiitoitsu\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chiitoitsu(h16, [], 0) === 2, \"m11 m22 m33 m44 m55 m66 m77 is Chiitoitsu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chiitoitsu(h17, [], 0) === 0, \"m11 m22 m33 m44 m55 m66 m66 is not Chiitoitsu\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.chiitoitsu(h3, [], 0) === 0, \"m111 p999 s111 w222 d11 is not Chiitoitsu\");\ntotal += 3;\n// toitoi\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.toitoi(h2, [], 0) === 2, \"m111 m444 m777 p111 p55 is Toitoi\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.toitoi(h7, [], 0) === 0, \"m123 p123 s123 m789 p99 is not Toitoi\");\ntotal += 2;\n// sanankou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanankou(h18, [], 0) === 2, \"m123 m444 m777 p111 p55 is Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanankou(h2, [], 0) === 0, \"m111 m444 m777 p111 p55 is not Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.sanankou(h7, [], 0) === 0, \"m123 p123 s123 m789 p99 is not Sanankou\");\ntotal += 3;\n// sanankou\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.suuankou(h2, [], 0) === 13, \"m111 m444 m777 p111 p55 is Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.suuankou(h18, [], 0) === 0, \"m123 m444 m777 p111 p55 is not Sanankou\");\ncount += (0,_assert__WEBPACK_IMPORTED_MODULE_0__.assert)(_yakus_yaku__WEBPACK_IMPORTED_MODULE_1__.yakus.suuankou(h7, [], 0) === 0, \"m123 p123 s123 m789 p99 is not Sanankou\");\ntotal += 3;\n// total\nconsole.log(\"Succès: \" + count.toString() + \"/\" + total.toString());\n\n\n//# sourceURL=webpack:///./src/tests/test_yakus.ts?"); - -/***/ }), - -/***/ "./src/tile.ts": -/*!*********************!*\ - !*** ./src/tile.ts ***! - \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tile: () => (/* binding */ Tile)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Tile {\n constructor(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.imgSrc = \"\";\n this.tilt = 0;\n this.setImgSrc();\n }\n getFamily() {\n return this.family;\n }\n getValue() {\n return this.value;\n }\n isEqual(family, value) {\n return this.family === family && this.value === value;\n }\n isRed() {\n return this.red;\n }\n compare(t) {\n // Compare d'abord par famille, puis par valeur\n if (this.family !== t.family) {\n return this.family < t.family ? -1 : 1;\n }\n if (this.value !== t.value) {\n return this.value < t.value ? -1 : 1;\n }\n return 0;\n }\n isLessThan(t) {\n return this.family < t.family ||\n (this.family === t.family && this.value <= t.value);\n }\n setTilt() {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n }\n drawTile(ctx, x, y, size, hidden = false, rotation = 0, gray = false, tilted = true) {\n const tileWidth = 75 * size;\n const tileHeight = 100 * size;\n const halfWidth = tileWidth / 2;\n const halfHeight = tileHeight / 2;\n const shadowScale = 0.92;\n // Sauvegarde du contexte et positionnement\n ctx.save();\n ctx.translate(x + halfWidth, y + halfHeight);\n ctx.rotate(rotation + (tilted ? this.tilt : 0));\n // Position de l'ombre (légèrement décalée)\n const shadowX = -(tileWidth * shadowScale) / 2;\n const shadowY = -(tileHeight * shadowScale) / 2;\n // Dessin de l'ombre (commun aux deux cas)\n ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight);\n if (hidden) {\n // Dessin du dos de la tuile\n ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n else {\n // Dessin de la tuile face visible\n ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight);\n // Dessin du motif sur la tuile (légèrement plus petit)\n const patternScale = 0.9;\n const patternWidth = tileWidth * patternScale;\n const patternHeight = tileHeight * patternScale;\n const patternX = -((75 - 7) * size) / 2;\n const patternY = -((100 - 10) * size) / 2;\n ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight);\n // Appliquer un filtre gris si demandé\n if (gray) {\n ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight);\n }\n }\n ctx.restore();\n }\n cleanup() {\n // Supprimer tous les gestionnaires d'événements\n const images = [this.imgFront, this.imgBack, this.imgGray, this.img];\n images.forEach(img => {\n img.onload = null;\n img.onerror = null;\n });\n }\n preloadImg() {\n return __awaiter(this, void 0, void 0, function* () {\n const imagesToLoad = [\n { img: this.imgFront, src: \"img/Regular/Front.svg\" },\n { img: this.imgBack, src: \"img/Regular/Back.svg\" },\n { img: this.imgGray, src: \"img/Regular/Gray.svg\" },\n { img: this.img, src: this.imgSrc }\n ];\n yield Promise.all(imagesToLoad.map(({ img, src }) => this.loadImg(img, src)));\n });\n }\n loadImg(img, src) {\n return new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject();\n img.src = src;\n });\n }\n setImgSrc() {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n const families = [\"\", \"Man\", \"Pin\", \"Sou\"];\n this.imgSrc += families[this.family] + String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n }\n else if (this.family === 4) {\n const winds = [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"];\n this.imgSrc += winds[this.value];\n }\n else if (this.family === 5) {\n const dragons = [\"\", \"Chun\", \"Hatsu\", \"Haku\"];\n this.imgSrc += dragons[this.value];\n }\n this.imgSrc += \".svg\";\n }\n}\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); - -/***/ }), - -/***/ "./src/yakus/yaku.ts": -/*!***************************!*\ - !*** ./src/yakus/yaku.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ yakus: () => (/* binding */ yakus)\n/* harmony export */ });\nfunction ord(g) {\n return g.getTiles().every(t => t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9);\n}\nfunction term(g) {\n return g.getTiles().every(t => {\n t.getFamily() < 4 &&\n (t.getValue() === 1 ||\n t.getValue() === 9);\n });\n}\nfunction honn(g) {\n return g.getTiles().every(t => t.getFamily() >= 4);\n}\nfunction chii(g) {\n let t = g.getTiles();\n return t[0].getValue() !== t[1].getValue();\n}\nfunction pon(g) {\n let t = g.getTiles();\n return t[0].getValue() === t[1].getValue();\n}\nconst yakus = {\n /**\n * double suite pure\n * 0/1\n */\n lipekou: function (hand, groups, wind) {\n if (groups.length > 0 && !yakus.ryanpeikou(hand, groups, wind)) { // ouvert\n return 0;\n }\n let gr = hand.toGroup();\n gr.sort((g1, g2) => g1.compare(g2));\n for (let i = 0; i < 3; i++) {\n let g1 = gr[i].getTiles();\n let g2 = gr[i + 1].getTiles();\n if (g1[0].isEqual(g2[0].getFamily(), g2[0].getValue()) &&\n chii(gr[i]) && chii(gr[i + 1])) {\n return 1;\n }\n }\n return 0;\n },\n /**\n * deux doubles suites pures\n * 0/3\n */\n ryanpeikou: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let gr = hand.toGroup();\n gr.filter(g => chii(g));\n if (gr.length < 4) { // pas assez de suite\n return 0;\n }\n gr.sort((g1, g2) => g1.compare(g2));\n let t1 = gr[0].getTiles()[0];\n let t2 = gr[1].getTiles()[0];\n let t3 = gr[2].getTiles()[0];\n let t4 = gr[3].getTiles()[0];\n if (t1.compare(t2) === 0 &&\n t3.compare(t4) === 0) {\n return 3;\n }\n return 0;\n },\n /**\n * tout suite\n * 0/1\n */\n pinfu: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let h = hand.toGroup();\n if (h !== undefined &&\n h.every(g => {\n let tiles = g.getTiles();\n return (chii(g) || tiles.length === 2);\n })) {\n return 1;\n }\n return 0;\n },\n /**\n * triple suite\n * 1/2\n */\n sanshokuDoujun: function (hand, groups, wind) {\n let h = hand.toGroup();\n let gr = [];\n if (h !== undefined) {\n gr = groups.concat(h);\n }\n else {\n gr = groups;\n }\n gr = gr.filter(g => chii(g));\n gr.sort((g1, g2) => g1.compare(g2));\n if (gr.length < 3) { // pas assez de chii\n return 0;\n }\n else if (gr.length === 3) {\n let t0 = gr[0].getTiles();\n let t1 = gr[1].getTiles();\n let t2 = gr[2].getTiles();\n if (t0[0].getValue() === t1[0].getValue() &&\n t0[0].getValue() === t2[0].getValue() &&\n t0[0].getFamily() !== t1[0].getFamily() &&\n t0[0].getFamily() !== t2[0].getFamily() &&\n t1[0].getFamily() !== t2[0].getFamily()) {\n return groups.length > 0 ? 1 : 2;\n }\n return 0;\n }\n else { // il y a un intrus\n for (let i = 0; i < 4; i++) {\n let index = [];\n for (let j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n let t0 = gr[index[0]].getTiles();\n let t1 = gr[index[1]].getTiles();\n let t2 = gr[index[2]].getTiles();\n if (t0[0].getValue() === t1[0].getValue() &&\n t0[0].getValue() === t2[0].getValue() &&\n t0[0].getFamily() !== t1[0].getFamily() &&\n t0[0].getFamily() !== t2[0].getFamily() &&\n t1[0].getFamily() !== t2[0].getFamily()) {\n return groups.length > 0 ? 1 : 2;\n }\n }\n return 0;\n }\n },\n /**\n * grande suite pure\n * 1/2\n */\n ittsuu: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => chii(g));\n gr.sort((g1, g2) => g1.compare(g2));\n if (gr.length < 3) { // trop peu de suite\n return 0;\n }\n else if (gr.length === 3) { // pile le bon nombre\n let g1 = gr[0].getTiles();\n let g2 = gr[1].getTiles();\n let g3 = gr[2].getTiles();\n if (g1[0].getFamily() === g2[0].getFamily() &&\n g2[0].getFamily() === g3[0].getFamily() &&\n g1[0].getValue() === 1 &&\n g2[0].getValue() === 4 &&\n g3[0].getValue() === 7) {\n return groups.length > 0 ? 1 : 2;\n }\n }\n else { // il y a un intrus\n for (let i = 0; i < 4; i++) {\n let index = [];\n for (let j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n let t0 = gr[index[0]].getTiles();\n let t1 = gr[index[1]].getTiles();\n let t2 = gr[index[2]].getTiles();\n if (t0[0].getValue() === 1 &&\n t1[0].getValue() === 4 &&\n t2[0].getValue() === 7 &&\n t0[0].getFamily() === t1[0].getFamily() &&\n t0[0].getFamily() === t2[0].getFamily()) {\n return groups.length > 0 ? 1 : 2;\n }\n }\n return 0;\n }\n return 0;\n },\n /**\n * tout ordinaire\n * 1/1\n */\n tanyao: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(g => ord(g)) &&\n h.every(g => ord(g))) {\n return 1;\n }\n return 0;\n },\n /**\n * brelan de valeur\n * 1/1\n */\n yakuhai: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g) && g.getTiles().length === 3);\n let han = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n let f = t[0].getFamily();\n let v = t[0].getValue();\n if (f === 5) { // brelan de dragon\n han++;\n }\n if (f === 4 && v === 0) { // vent d'est\n han++;\n }\n if (f === 4 && v === wind) { // vent du joueur\n han++;\n }\n });\n return han;\n },\n /**\n * trois petits dragons\n * 2/2\n */\n shousangen: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g));\n let nbPon = 0;\n let nbPair = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n if (t[0].getFamily() === 5) {\n if (t.length === 3) {\n nbPon++;\n }\n else {\n nbPair++;\n }\n }\n });\n if (nbPon == 2 && nbPair == 1) {\n return 2;\n }\n return 0;\n },\n /**\n * trois grands dragons\n * 13/13\n */\n daisangen: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g) &&\n g.getTiles().length === 3 &&\n g.getTiles()[0].getFamily() === 5);\n if (gr.length === 3) {\n return 13;\n }\n return 0;\n },\n /**\n * quatre petits vents\n * 13/13\n */\n shousuushii: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g));\n let nbPon = 0;\n let nbPair = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n if (t[0].getFamily() === 4) {\n if (t.length === 3) {\n nbPon++;\n }\n else {\n nbPair++;\n }\n }\n });\n if (nbPon == 3 && nbPair == 1) {\n return 13;\n }\n return 0;\n },\n /**\n * quatre grands vents\n * 13/13\n */\n daisuushi: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => pon(g));\n let nbPon = 0;\n let nbPair = 0;\n gr.forEach(g => {\n let t = g.getTiles();\n if (t[0].getFamily() === 4) {\n if (t.length === 3) {\n nbPon++;\n }\n else {\n nbPair++;\n }\n }\n });\n if (nbPon == 4 && nbPair == 0) {\n return 13;\n }\n return 0;\n },\n /**\n * terminales et honneurs partout\n * 1/2\n */\n chanta: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => {\n let tiles = g.getTiles();\n let f = tiles[0].getFamily();\n let v = tiles[0].getValue();\n let vd = tiles[tiles.length - 1].getValue();\n return f < 4 && v !== 1 && vd !== 9;\n });\n if (gr.length > 0) {\n return 0;\n }\n return groups.length > 0 ? 1 : 2;\n },\n /**\n * terminales partout\n * 2/3\n */\n junchan: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => {\n let tiles = g.getTiles();\n let f = tiles[0].getFamily();\n let v = tiles[0].getValue();\n let vd = tiles[tiles.length - 1].getValue();\n return f > 3 || (v !== 1 && vd !== 9);\n });\n if (gr.length > 0) {\n return 0;\n }\n return groups.length > 0 ? 2 : 3;\n },\n /**\n * tout terminale et honneur\n * 2/2\n */\n honroutou: function (hand, groups, wind) {\n let gr = groups.concat(hand.toGroup());\n gr = gr.filter(g => {\n let tiles = g.getTiles();\n let f = tiles[0].getFamily();\n let v = tiles[1].getValue();\n return f < 4 && v !== 1 && v !== 9;\n });\n if (gr.length > 0) {\n return 0;\n }\n return 2;\n },\n /**\n * tout terminale\n * 13/13\n */\n chinroutou: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(g => term(g)) &&\n h.every(g => term(g))) {\n return 13;\n }\n return 0;\n },\n /**\n * tout honneur\n * 13/13\n */\n tsuuiisou: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(g => honn(g)) &&\n h.every(g => honn(g))) {\n return 13;\n }\n return 0;\n },\n /**\n * treize orphelins\n * 0/13\n */\n kokushiMusou: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let h = hand.getTiles();\n if (h.some(t => t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9)) {\n return 0;\n }\n let count = 0;\n for (let i = 0; i < h.length - 1; i++) {\n if (h[i].isEqual(h[i + 1].getFamily(), h[i + 1].getValue())) {\n count++;\n }\n if (count > 1) {\n break;\n }\n }\n if (count === 1) {\n return 13;\n }\n return 0;\n },\n /**\n * sept paires\n * 0/2\n */\n chiitoitsu: function (hand, groups, wind) {\n if (groups.length > 0) {\n return 0;\n }\n let h = hand.getTiles();\n if (h.length !== 14) {\n return 0;\n }\n for (let i = 0; i < 14; i += 2) {\n if (h[i].compare(h[i + 1]) !== 0 ||\n (i > 0 && h[i].compare(h[i - 1]) === 0)) {\n return 0;\n }\n }\n return 2;\n },\n /**\n * tout brelans\n * 2/2\n */\n toitoi: function (hand, groups, wind) {\n let h = hand.toGroup();\n if (groups.every(g => pon(g)) &&\n (h === null || h === void 0 ? void 0 : h.every(g => pon(g)))) {\n return 2;\n }\n return 0;\n },\n /**\n * trois brelan cachés\n * 2/2\n */\n sanankou: function (hand, groups, wind) {\n let h = hand.toGroup();\n let count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(g => {\n if (pon(g) && g.getTiles().length === 3) {\n count++;\n }\n });\n if (count === 3) {\n return 2;\n }\n return 0;\n },\n /**\n * quatre brelans cachés\n * 0/13\n */\n suuankou: function (hand, groups, wind) {\n let h = hand.toGroup();\n let count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(g => {\n if (pon(g) && g.getTiles().length === 3) {\n count++;\n }\n });\n if (count === 4) {\n return 13;\n }\n return 0;\n },\n sanshokuDoukou: function (//TODO\n /**\n * triple brelan\n * 2/2\n */\n hand, groups, wind) {\n return 0;\n },\n sankantsu: function (//TODO\n /**\n * trois carrés\n * 2/2\n */\n hand, groups, wind) {\n return 0;\n },\n suukantsu: function (//TODO\n /**\n * quatre carrés\n * 13/13\n */\n hand, groups, wind) {\n return 0;\n },\n honitsu: function (//TODO\n /**\n * semie pure\n * 2/3\n */\n hand, groups, wind) {\n return 0;\n },\n chinitsu: function (\n /**\n * main pure\n * 5/6\n */\n hand, groups, wind) {\n let h = hand.getTiles();\n let t0 = h[0];\n if (h.every(t => t.getFamily() === t0.getFamily()) &&\n groups.every(g => g.getTiles().every(t => t.getFamily() === t0.getFamily()))) {\n return groups.length > 0 ? 5 : 6;\n }\n return 0;\n },\n ryuuisou: function (//TODO\n /**\n * main verte\n * 13/13\n */\n hand, groups, wind) {\n if (yakus.chinitsu(hand, groups, wind) === 0) {\n return 0;\n }\n return 0;\n },\n chuurenPoutou: function (//TODO\n /**\n * neuf portes\n * 0/13\n */\n hand, groups, wind) {\n if (groups.length > 0 || yakus.chinitsu(hand, groups, wind) === 0) {\n return 0;\n }\n return 0;\n }\n};\n\n\n//# sourceURL=webpack:///./src/yakus/yaku.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/tests/test_yakus.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";var t={274:(t,e,i)=>{i.d(e,{F:()=>s});class s{constructor(t,e,i){this.family=t,this.value=e,this.red=i,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,e){return this.family===t&&this.value===e}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,e=void 0,s=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:e})=>this.loadImg(t,e))))},new((i=void 0)||(i=Promise))((function(n,o){function u(t){try{l(s.next(t))}catch(t){o(t)}}function r(t){try{l(s.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(u,r)}l((s=s.apply(t,e||[])).next())}));var t,e,i,s}loadImg(t,e){return new Promise(((i,s)=>{t.onload=()=>i(),t.onerror=()=>s(),t.src=e}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}},305:(t,e,i)=>{function s(t,e){return t?(console.log("%c[SUCCES] "+e,"color: green"),1):(console.log("%c[ECHEC] "+e,"color: red"),0)}i.d(e,{v:()=>s})},879:(t,e,i)=>{i.d(e,{Y:()=>s});class s{constructor(t,e,i){this.tiles=t,this.stolenFrom=e,this.belongsTo=i}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const e=this.tiles[0].compare(t.tiles[0]);return 0!==e?e:this.tiles[1].compare(t.tiles[1])}drawGroup(t,e,i,s,n,o,u){t.save(),t.translate(525,525),t.rotate(o),t.translate(-525,-525);const r=75*n,l=90*n,a=25*n/2,m=(this.belongsTo-this.stolenFrom-1+4)%4,h=void 0===u?-1:u.getFamily(),g=void 0===u?0:u.getValue(),p=(e,i,s,o)=>{e.drawTile(t,i,s,n,!1,o,e.isEqual(h,g))},c=Math.PI/2;switch(m){case 0:p(this.tiles[0],e,i+a,c),p(this.tiles[1],e+l,i,0),p(this.tiles[2],e+l+r+s*n,i,0);break;case 1:p(this.tiles[0],e,i,0),p(this.tiles[1],e+l,i+a,-c),p(this.tiles[2],e+l+r+3*s*n,i,0);break;case 2:p(this.tiles[0],e,i,0),p(this.tiles[1],e+r+s*n,i,0),p(this.tiles[2],e+l+r+s*n,i+a,-c);break;default:console.error(`Position non prise en charge: ${m}`)}t.restore()}}},959:(t,e,i)=>{i.d(e,{U:()=>d});var s=i(274),n=i(879);const o="m",u=1,r="p",l=2,a="s",m=3,h="w",g=4,p="d",c=5;class d{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let e=0;ei.getFamily()===t&&i.getValue()===e))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const e=this.tiles.shift();return this.sort(),e}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,e)=>t.isLessThan(e)?-1:1))}count(t,e){return this.tiles.filter((i=>i.getFamily()===t&&i.getValue()===e)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const e=this.tiles.pop(),i=e.getFamily(),s=e.getValue();if(this.count(i,s)>=1&&!t){const t=this.tryFormPair(e);if(t)return t}if(this.count(i,s)>=2){const i=this.tryFormTriplet(e,t);if(i)return i}const n=this.count(i,s-1)>0,o=this.count(i,s-2)>0;if(n&&o){const i=this.tryFormSequence(e,t);if(i)return i}this.tiles.push(e),this.sort()}tryFormPair(t){const e=this.find(t.getFamily(),t.getValue()),i=this.toGroup(!0);if(this.tiles.push(e),this.sort(),void 0!==i)return this.tiles.push(t),this.sort(),i.push(new n.Y([t,e],0,0)),i}tryFormTriplet(t,e){const i=this.find(t.getFamily(),t.getValue()),s=this.find(t.getFamily(),t.getValue()),o=this.toGroup(e);if(this.tiles.push(i),this.tiles.push(s),this.sort(),void 0!==o)return o.push(new n.Y([t,i,s],0,0)),this.tiles.push(t),this.sort(),o}tryFormSequence(t,e){const i=this.find(t.getFamily(),t.getValue()-1),s=this.find(t.getFamily(),t.getValue()-2),o=this.toGroup(e);if(this.tiles.push(i),this.tiles.push(s),this.sort(),void 0!==o)return o.push(new n.Y([s,i,t],0,0)),this.tiles.push(t),this.sort(),o}drawHand(t,e,i,s,n,o=void 0,u=!1,r=0){const l=(75+s)*n,a=Math.cos(r)*l,m=Math.sin(r)*l;for(let s=0;st.preloadImg())))},new((i=void 0)||(i=Promise))((function(n,o){function u(t){try{l(s.next(t))}catch(t){o(t)}}function r(t){try{l(s.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(u,r)}l((s=s.apply(t,e||[])).next())}));var t,e,i,s}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s](o,o.exports,i),o.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var s=i(305);function n(t){return t.getTiles().every((t=>t.getFamily()<4&&t.getValue()>1&&t.getValue()<9))}function o(t){return t.getTiles().every((t=>{t.getFamily()<4&&(1===t.getValue()||t.getValue())}))}function u(t){return t.getTiles().every((t=>t.getFamily()>=4))}function r(t){let e=t.getTiles();return e[0].getValue()!==e[1].getValue()}function l(t){let e=t.getTiles();return e[0].getValue()===e[1].getValue()}const a={lipekou:function(t,e,i){if(e.length>0&&!a.ryanpeikou(t,e,i))return 0;let s=t.toGroup();s.sort(((t,e)=>t.compare(e)));for(let t=0;t<3;t++){let e=s[t].getTiles(),i=s[t+1].getTiles();if(e[0].isEqual(i[0].getFamily(),i[0].getValue())&&r(s[t])&&r(s[t+1]))return 1}return 0},ryanpeikou:function(t,e,i){if(e.length>0)return 0;let s=t.toGroup();if(s.filter((t=>r(t))),s.length<4)return 0;s.sort(((t,e)=>t.compare(e)));let n=s[0].getTiles()[0],o=s[1].getTiles()[0],u=s[2].getTiles()[0],l=s[3].getTiles()[0];return 0===n.compare(o)&&0===u.compare(l)?3:0},pinfu:function(t,e,i){if(e.length>0)return 0;let s=t.toGroup();return void 0!==s&&s.every((t=>{let e=t.getTiles();return r(t)||2===e.length}))?1:0},sanshokuDoujun:function(t,e,i){let s=t.toGroup(),n=[];if(n=void 0!==s?e.concat(s):e,n=n.filter((t=>r(t))),n.sort(((t,e)=>t.compare(e))),n.length<3)return 0;if(3===n.length){let t=n[0].getTiles(),i=n[1].getTiles(),s=n[2].getTiles();return t[0].getValue()===i[0].getValue()&&t[0].getValue()===s[0].getValue()&&t[0].getFamily()!==i[0].getFamily()&&t[0].getFamily()!==s[0].getFamily()&&i[0].getFamily()!==s[0].getFamily()?e.length>0?1:2:0}for(let t=0;t<4;t++){let i=[];for(let e=0;e<4;e++)e!==t&&i.push(e);let s=n[i[0]].getTiles(),o=n[i[1]].getTiles(),u=n[i[2]].getTiles();if(s[0].getValue()===o[0].getValue()&&s[0].getValue()===u[0].getValue()&&s[0].getFamily()!==o[0].getFamily()&&s[0].getFamily()!==u[0].getFamily()&&o[0].getFamily()!==u[0].getFamily())return e.length>0?1:2}return 0},ittsuu:function(t,e,i){let s=e.concat(t.toGroup());if(s=s.filter((t=>r(t))),s.sort(((t,e)=>t.compare(e))),s.length<3)return 0;if(3!==s.length){for(let t=0;t<4;t++){let i=[];for(let e=0;e<4;e++)e!==t&&i.push(e);let n=s[i[0]].getTiles(),o=s[i[1]].getTiles(),u=s[i[2]].getTiles();if(1===n[0].getValue()&&4===o[0].getValue()&&7===u[0].getValue()&&n[0].getFamily()===o[0].getFamily()&&n[0].getFamily()===u[0].getFamily())return e.length>0?1:2}return 0}{let t=s[0].getTiles(),i=s[1].getTiles(),n=s[2].getTiles();if(t[0].getFamily()===i[0].getFamily()&&i[0].getFamily()===n[0].getFamily()&&1===t[0].getValue()&&4===i[0].getValue()&&7===n[0].getValue())return e.length>0?1:2}return 0},tanyao:function(t,e,i){let s=t.toGroup();return void 0===s?0:e.every((t=>n(t)))&&s.every((t=>n(t)))?1:0},yakuhai:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)&&3===t.getTiles().length));let n=0;return s.forEach((t=>{let e=t.getTiles(),s=e[0].getFamily(),o=e[0].getValue();5===s&&n++,4===s&&0===o&&n++,4===s&&o===i&&n++})),n},shousangen:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)));let n=0,o=0;return s.forEach((t=>{let e=t.getTiles();5===e[0].getFamily()&&(3===e.length?n++:o++)})),2==n&&1==o?2:0},daisangen:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>l(t)&&3===t.getTiles().length&&5===t.getTiles()[0].getFamily())),3===s.length?13:0},shousuushii:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)));let n=0,o=0;return s.forEach((t=>{let e=t.getTiles();4===e[0].getFamily()&&(3===e.length?n++:o++)})),3==n&&1==o?13:0},daisuushi:function(t,e,i){let s=e.concat(t.toGroup());s=s.filter((t=>l(t)));let n=0,o=0;return s.forEach((t=>{let e=t.getTiles();4===e[0].getFamily()&&(3===e.length?n++:o++)})),4==n&&0==o?13:0},chanta:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>{let e=t.getTiles(),i=e[0].getFamily(),s=e[0].getValue(),n=e[e.length-1].getValue();return i<4&&1!==s&&9!==n})),s.length>0?0:e.length>0?1:2},junchan:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>{let e=t.getTiles(),i=e[0].getFamily(),s=e[0].getValue(),n=e[e.length-1].getValue();return i>3||1!==s&&9!==n})),s.length>0?0:e.length>0?2:3},honroutou:function(t,e,i){let s=e.concat(t.toGroup());return s=s.filter((t=>{let e=t.getTiles(),i=e[0].getFamily(),s=e[1].getValue();return i<4&&1!==s&&9!==s})),s.length>0?0:2},chinroutou:function(t,e,i){let s=t.toGroup();return void 0===s?0:e.every((t=>o(t)))&&s.every((t=>o(t)))?13:0},tsuuiisou:function(t,e,i){let s=t.toGroup();return void 0===s?0:e.every((t=>u(t)))&&s.every((t=>u(t)))?13:0},kokushiMusou:function(t,e,i){if(e.length>0)return 0;let s=t.getTiles();if(s.some((t=>t.getFamily()<4&&t.getValue()>1&&t.getValue()<9)))return 0;let n=0;for(let t=0;t1));t++);return 1===n?13:0},chiitoitsu:function(t,e,i){if(e.length>0)return 0;let s=t.getTiles();if(14!==s.length)return 0;for(let t=0;t<14;t+=2)if(0!==s[t].compare(s[t+1])||t>0&&0===s[t].compare(s[t-1]))return 0;return 2},toitoi:function(t,e,i){let s=t.toGroup();return e.every((t=>l(t)))&&(null==s?void 0:s.every((t=>l(t))))?2:0},sanankou:function(t,e,i){let s=t.toGroup(),n=0;return null==s||s.forEach((t=>{l(t)&&3===t.getTiles().length&&n++})),3===n?2:0},suuankou:function(t,e,i){let s=t.toGroup(),n=0;return null==s||s.forEach((t=>{l(t)&&3===t.getTiles().length&&n++})),4===n?13:0},sanshokuDoukou:function(t,e,i){return 0},sankantsu:function(t,e,i){return 0},suukantsu:function(t,e,i){return 0},honitsu:function(t,e,i){return 0},chinitsu:function(t,e,i){let s=t.getTiles(),n=s[0];return s.every((t=>t.getFamily()===n.getFamily()))&&e.every((t=>t.getTiles().every((t=>t.getFamily()===n.getFamily()))))?e.length>0?5:6:0},ryuuisou:function(t,e,i){return a.chinitsu(t,e,i),0},chuurenPoutou:function(t,e,i){return e.length>0||a.chinitsu(t,e,i),0}};var m=i(959);let h=0,g=0,p=new m.U("m1m2m3 m4m5m6 m7m8m9 m1m2m3 m5m5"),c=new m.U("m1m1m1 m4m4m4 m7m7m7 p1p1p1 p5p5"),d=new m.U("m1m1m1 p9p9p9 s1s1s1 w2w2w2 d1d1"),w=new m.U("m2m3m4 p3p3p3 p4p5p6 s4s4 s6s7s8"),f=new m.U("m1m2m3 m1m2m3 p7p8p9 p7p8p9 w3w3"),v=new m.U("m1m2m3 m1m2m3 p6p7p8 p7p8p9 w3w3"),y=new m.U("m1m2m3 p1p2p3 s1s2s3 m7m8m9 p9p9"),F=new m.U("m1m2m3 m1m2m3 p6p6 w0w0w0 w3w3w3"),T=new m.U("m1m2m3 m1m2m3 d1d1d1 d2d2d2 d3d3"),k=new m.U("m1m2m3 p6p6 d1d1d1 d2d2d2 d3d3d3"),V=new m.U("m1m2m3 w1w1w1 w2w2w2 w3w3w3 w4w4"),S=new m.U("m1m1 w1w1w1 w2w2w2 w3w3w3 w4w4w4"),G=new m.U("d1d1 w1w1w1 w2w2w2 w3w3w3 w4w4w4"),I=new m.U("m1m1 m9m9m9 p1p1p1 p9p9p9 s1s1s1"),U=new m.U("m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3"),C=new m.U("m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m7m7"),E=new m.U("m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m6m6"),P=new m.U("m1m2m3 m4m4m4 m7m7m7 p1p1p1 p5p5");new m.U("m1m2m3 m7m7m7 p1p1p1 p5p5"),h+=(0,s.v)(1===a.lipekou(f,[],0),"m123 m123 p789 p789 w33 is Lipeikou"),h+=(0,s.v)(0===a.lipekou(d,[],0),"m111 p999 s111 w222 d11 is not Lipeikou"),g+=2,h+=(0,s.v)(3===a.ryanpeikou(f,[],0),"m123 m123 p789 p789 w33 is Ryanpeikou"),h+=(0,s.v)(0===a.ryanpeikou(v,[],0),"m1123 m123 p678 p789 w33 is not Ryanpeikou"),g+=2,h+=(0,s.v)(1===a.pinfu(p,[],0),"m123 m456 m789 m123 p55 is Pinfu"),h+=(0,s.v)(0===a.pinfu(c,[],0),"m111 m444 m777 p111 p55 is not Pinfu"),g+=2,h+=(0,s.v)(2===a.sanshokuDoujun(y,[],0),"m123 p123 s123 m789 p99 is Shanshokou Doujun"),h+=(0,s.v)(0===a.sanshokuDoujun(c,[],0),"m111 m444 m777 p111 p55 is not Shanshokou Doujun"),g+=2,h+=(0,s.v)(2===a.ittsuu(p,[],0),"m123 m456 m789 m123 m55 is Ittsuu"),h+=(0,s.v)(0===a.ittsuu(c,[],0),"m111 m444 m777 p111 p55 is not Ittsu"),g+=2,h+=(0,s.v)(1===a.tanyao(w,[],0),"m234 p333 p456 s44 s678 is Tanyao"),h+=(0,s.v)(0===a.tanyao(p,[],0),"m123 m456 m789 m123 p55 is not Tanyao"),g+=2,h+=(0,s.v)(1===a.yakuhai(d,[],2),"m111 p999 s111 w222 d11 West is Yakuhai"),h+=(0,s.v)(2===a.yakuhai(F,[],3),"m123 m123 p66 w000 w333 North is double Yakuhai"),h+=(0,s.v)(0===a.yakuhai(d,[],0),"m111 p999 s111 w222 d11 Est is not Yakuhai"),g+=3,h+=(0,s.v)(2===a.shousangen(T,[],0),"m123 m123 d111 d222 d33 is Shousangen"),h+=(0,s.v)(0===a.shousangen(k,[],0),"m123 p66 d111 d222 d333 is not Shousangen"),h+=(0,s.v)(0===a.shousangen(d,[],0),"m111 p999 s111 w222 d11 is not Shousangen"),g+=3,h+=(0,s.v)(13===a.daisangen(k,[],0),"m123 p66 d111 d222 d333 is Daisangen"),h+=(0,s.v)(0===a.daisangen(T,[],0),"m123 m123 d111 d222 d33 is not Daisangen"),h+=(0,s.v)(0===a.daisangen(d,[],0),"m111 p999 s111 w222 d11 is not Daisangen"),g+=3,h+=(0,s.v)(13===a.shousuushii(V,[],0),"m123 w111 w222 w333 w44 is Shousuushi"),h+=(0,s.v)(0===a.shousuushii(S,[],0),"m11 w111 w222 w333 w444 is not Shousuushi"),h+=(0,s.v)(0===a.shousuushii(d,[],0),"m111 p999 s111 w222 d11 is not Shousuushi"),g+=3,h+=(0,s.v)(13===a.daisuushi(S,[],0),"m11 w111 w222 w333 w444 is Daisuushi"),h+=(0,s.v)(0===a.daisuushi(V,[],0),"m123 w111 w222 w333 w44 is not Daisuushi"),h+=(0,s.v)(0===a.daisuushi(d,[],0),"m111 p999 s111 w222 d11 is not Daisuushi"),g+=3,h+=(0,s.v)(2===a.chanta(G,[],0),"d11 w111 w222 w333 w444 is Chanta"),h+=(0,s.v)(2===a.chanta(S,[],0),"m11 w111 w222 w333 w444 is Chanta"),h+=(0,s.v)(0===a.chanta(k,[],0),"m123 p66 d111 d222 d333 is not Chanta"),g+=3,h+=(0,s.v)(3===a.junchan(y,[],0),"m123 p123 s123 m789 p99 is Junchan"),h+=(0,s.v)(0===a.junchan(k,[],0),"m123 p66 d111 d222 d333 is not Junchan"),g+=2,h+=(0,s.v)(2===a.honroutou(I,[],0),"m11 m999 p111 p999 s111 is Honroutou"),h+=(0,s.v)(2===a.honroutou(d,[],0),"m111 p999 s111 w222 d11 is Honroutou"),h+=(0,s.v)(0===a.honroutou(k,[],0),"m123 p66 d111 d222 d333 is not Honroutou"),g+=3,h+=(0,s.v)(13===a.chinroutou(I,[],0),"m11 m999 p111 p999 s111 is Chinroutou"),h+=(0,s.v)(0===a.chinroutou(d,[],0),"m111 p999 s111 w222 d11 is not Chinroutou"),h+=(0,s.v)(0===a.chinroutou(k,[],0),"m123 p66 d111 d222 d333 is not Chinroutou"),g+=3,h+=(0,s.v)(13===a.tsuuiisou(G,[],0),"d11 w111 w222 w333 w444 is Tsuuisou"),h+=(0,s.v)(0===a.tsuuiisou(d,[],0),"m111 p999 s111 w222 d11 is not Tsuuisou"),g+=2,h+=(0,s.v)(13===a.kokushiMusou(U,[],0),"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3 is Kokushi Musou"),h+=(0,s.v)(0===a.kokushiMusou(d,[],0),"m111 p999 s111 w222 d11 is not Kokushi Musou"),g+=2,h+=(0,s.v)(2===a.chiitoitsu(C,[],0),"m11 m22 m33 m44 m55 m66 m77 is Chiitoitsu"),h+=(0,s.v)(0===a.chiitoitsu(E,[],0),"m11 m22 m33 m44 m55 m66 m66 is not Chiitoitsu"),h+=(0,s.v)(0===a.chiitoitsu(d,[],0),"m111 p999 s111 w222 d11 is not Chiitoitsu"),g+=3,h+=(0,s.v)(2===a.toitoi(c,[],0),"m111 m444 m777 p111 p55 is Toitoi"),h+=(0,s.v)(0===a.toitoi(y,[],0),"m123 p123 s123 m789 p99 is not Toitoi"),g+=2,h+=(0,s.v)(2===a.sanankou(P,[],0),"m123 m444 m777 p111 p55 is Sanankou"),h+=(0,s.v)(0===a.sanankou(c,[],0),"m111 m444 m777 p111 p55 is not Sanankou"),h+=(0,s.v)(0===a.sanankou(y,[],0),"m123 p123 s123 m789 p99 is not Sanankou"),g+=3,h+=(0,s.v)(13===a.suuankou(c,[],0),"m111 m444 m777 p111 p55 is Sanankou"),h+=(0,s.v)(0===a.suuankou(P,[],0),"m123 m444 m777 p111 p55 is not Sanankou"),h+=(0,s.v)(0===a.suuankou(y,[],0),"m123 p123 s123 m789 p99 is not Sanankou"),g+=3,console.log("Succès: "+h.toString()+"/"+g.toString())})(); \ No newline at end of file diff --git a/build/txt0.js b/build/txt0.js index 56125f8..0c191b7 100644 --- a/build/txt0.js +++ b/build/txt0.js @@ -1,96 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/text/parse.ts": -/*!***************************!*\ - !*** ./src/text/parse.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ drawText: () => (/* binding */ drawText)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function* () {\n console.log(filePath, \"\\n\");\n const fileContent = yield fetch(filePath)\n .then(response => response.text());\n const size = 30;\n const dl = 5;\n const ll = 50;\n const xx = 10;\n const defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n let readingColor = false;\n let color = \"\";\n let gras = \"\";\n let italic = \"\";\n let line = 1;\n let x = 0;\n for (var c of fileContent) {\n if (c === '*') {\n if (gras === \"\") {\n gras = \"bold \";\n }\n else {\n gras = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '~') {\n if (italic === \"\") {\n italic = \"italic \";\n }\n else {\n italic = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '#') {\n color = \"#\";\n readingColor = true;\n }\n else if (c === '{') {\n readingColor = false;\n ctx.fillStyle = color;\n }\n else if (c === '}') {\n color = \"\";\n ctx.fillStyle = defaultColor;\n }\n else if (readingColor) {\n color += c;\n }\n else if (c === '\\n') {\n line++;\n x = 0;\n }\n else {\n ctx.fillText(c, xx + x, ll + line * (size + dl));\n x += ctx.measureText(c).width;\n }\n }\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); - -/***/ }), - -/***/ "./src/text/txt0.ts": -/*!**************************!*\ - !*** ./src/text/txt0.ts ***! - \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parse */ \"./src/text/parse.ts\");\n\nconst CANVAS_ID = \"myTextCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nconst path = \"src/text/\";\nctx.fillStyle = BG_RECT.color;\nctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n(0,_parse__WEBPACK_IMPORTED_MODULE_0__.drawText)(path + \"txt0.txt\", ctx).catch(error => console.error(error));\n\n\n//# sourceURL=webpack:///./src/text/txt0.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/text/txt0.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,50+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt0.txt",n).catch((t=>console.error(t)))})(); \ No newline at end of file diff --git a/build/txt1.js b/build/txt1.js index 30a8019..bff53f3 100644 --- a/build/txt1.js +++ b/build/txt1.js @@ -1,96 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/text/parse.ts": -/*!***************************!*\ - !*** ./src/text/parse.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ drawText: () => (/* binding */ drawText)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function* () {\n console.log(filePath, \"\\n\");\n const fileContent = yield fetch(filePath)\n .then(response => response.text());\n const size = 30;\n const dl = 5;\n const ll = 50;\n const xx = 10;\n const defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n let readingColor = false;\n let color = \"\";\n let gras = \"\";\n let italic = \"\";\n let line = 1;\n let x = 0;\n for (var c of fileContent) {\n if (c === '*') {\n if (gras === \"\") {\n gras = \"bold \";\n }\n else {\n gras = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '~') {\n if (italic === \"\") {\n italic = \"italic \";\n }\n else {\n italic = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '#') {\n color = \"#\";\n readingColor = true;\n }\n else if (c === '{') {\n readingColor = false;\n ctx.fillStyle = color;\n }\n else if (c === '}') {\n color = \"\";\n ctx.fillStyle = defaultColor;\n }\n else if (readingColor) {\n color += c;\n }\n else if (c === '\\n') {\n line++;\n x = 0;\n }\n else {\n ctx.fillText(c, xx + x, ll + line * (size + dl));\n x += ctx.measureText(c).width;\n }\n }\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); - -/***/ }), - -/***/ "./src/text/txt1.ts": -/*!**************************!*\ - !*** ./src/text/txt1.ts ***! - \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parse */ \"./src/text/parse.ts\");\n\nconst CANVAS_ID = \"myTextCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nconst path = \"src/text/\";\nctx.fillStyle = BG_RECT.color;\nctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n(0,_parse__WEBPACK_IMPORTED_MODULE_0__.drawText)(path + \"txt1.txt\", ctx).catch(error => console.error(error));\n\n\n//# sourceURL=webpack:///./src/text/txt1.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/text/txt1.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,50+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt1.txt",n).catch((t=>console.error(t)))})(); \ No newline at end of file diff --git a/build/txt2.js b/build/txt2.js index a2f2794..d111de8 100644 --- a/build/txt2.js +++ b/build/txt2.js @@ -1,96 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/text/parse.ts": -/*!***************************!*\ - !*** ./src/text/parse.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ drawText: () => (/* binding */ drawText)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function* () {\n console.log(filePath, \"\\n\");\n const fileContent = yield fetch(filePath)\n .then(response => response.text());\n const size = 30;\n const dl = 5;\n const ll = 50;\n const xx = 10;\n const defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n let readingColor = false;\n let color = \"\";\n let gras = \"\";\n let italic = \"\";\n let line = 1;\n let x = 0;\n for (var c of fileContent) {\n if (c === '*') {\n if (gras === \"\") {\n gras = \"bold \";\n }\n else {\n gras = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '~') {\n if (italic === \"\") {\n italic = \"italic \";\n }\n else {\n italic = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '#') {\n color = \"#\";\n readingColor = true;\n }\n else if (c === '{') {\n readingColor = false;\n ctx.fillStyle = color;\n }\n else if (c === '}') {\n color = \"\";\n ctx.fillStyle = defaultColor;\n }\n else if (readingColor) {\n color += c;\n }\n else if (c === '\\n') {\n line++;\n x = 0;\n }\n else {\n ctx.fillText(c, xx + x, ll + line * (size + dl));\n x += ctx.measureText(c).width;\n }\n }\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); - -/***/ }), - -/***/ "./src/text/txt2.ts": -/*!**************************!*\ - !*** ./src/text/txt2.ts ***! - \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parse */ \"./src/text/parse.ts\");\n\nconst CANVAS_ID = \"myTextCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nconst path = \"src/text/\";\nctx.fillStyle = BG_RECT.color;\nctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n(0,_parse__WEBPACK_IMPORTED_MODULE_0__.drawText)(path + \"txt2.txt\", ctx).catch(error => console.error(error));\n\n\n//# sourceURL=webpack:///./src/text/txt2.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/text/txt2.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,50+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt2.txt",n).catch((t=>console.error(t)))})(); \ No newline at end of file diff --git a/build/txt3.js b/build/txt3.js index 49acbd2..5004924 100644 --- a/build/txt3.js +++ b/build/txt3.js @@ -1,96 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/text/parse.ts": -/*!***************************!*\ - !*** ./src/text/parse.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ drawText: () => (/* binding */ drawText)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function* () {\n console.log(filePath, \"\\n\");\n const fileContent = yield fetch(filePath)\n .then(response => response.text());\n const size = 30;\n const dl = 5;\n const ll = 50;\n const xx = 10;\n const defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n let readingColor = false;\n let color = \"\";\n let gras = \"\";\n let italic = \"\";\n let line = 1;\n let x = 0;\n for (var c of fileContent) {\n if (c === '*') {\n if (gras === \"\") {\n gras = \"bold \";\n }\n else {\n gras = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '~') {\n if (italic === \"\") {\n italic = \"italic \";\n }\n else {\n italic = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '#') {\n color = \"#\";\n readingColor = true;\n }\n else if (c === '{') {\n readingColor = false;\n ctx.fillStyle = color;\n }\n else if (c === '}') {\n color = \"\";\n ctx.fillStyle = defaultColor;\n }\n else if (readingColor) {\n color += c;\n }\n else if (c === '\\n') {\n line++;\n x = 0;\n }\n else {\n ctx.fillText(c, xx + x, ll + line * (size + dl));\n x += ctx.measureText(c).width;\n }\n }\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); - -/***/ }), - -/***/ "./src/text/txt3.ts": -/*!**************************!*\ - !*** ./src/text/txt3.ts ***! - \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parse */ \"./src/text/parse.ts\");\n\nconst CANVAS_ID = \"myTextCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nconst path = \"src/text/\";\nctx.fillStyle = BG_RECT.color;\nctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n(0,_parse__WEBPACK_IMPORTED_MODULE_0__.drawText)(path + \"txt3.txt\", ctx).catch(error => console.error(error));\n\n\n//# sourceURL=webpack:///./src/text/txt3.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/text/txt3.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,50+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt3.txt",n).catch((t=>console.error(t)))})(); \ No newline at end of file diff --git a/build/txt4.js b/build/txt4.js index 7c34ce6..d233a09 100644 --- a/build/txt4.js +++ b/build/txt4.js @@ -1,96 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/text/parse.ts": -/*!***************************!*\ - !*** ./src/text/parse.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ drawText: () => (/* binding */ drawText)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function* () {\n console.log(filePath, \"\\n\");\n const fileContent = yield fetch(filePath)\n .then(response => response.text());\n const size = 30;\n const dl = 5;\n const ll = 50;\n const xx = 10;\n const defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n let readingColor = false;\n let color = \"\";\n let gras = \"\";\n let italic = \"\";\n let line = 1;\n let x = 0;\n for (var c of fileContent) {\n if (c === '*') {\n if (gras === \"\") {\n gras = \"bold \";\n }\n else {\n gras = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '~') {\n if (italic === \"\") {\n italic = \"italic \";\n }\n else {\n italic = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '#') {\n color = \"#\";\n readingColor = true;\n }\n else if (c === '{') {\n readingColor = false;\n ctx.fillStyle = color;\n }\n else if (c === '}') {\n color = \"\";\n ctx.fillStyle = defaultColor;\n }\n else if (readingColor) {\n color += c;\n }\n else if (c === '\\n') {\n line++;\n x = 0;\n }\n else {\n ctx.fillText(c, xx + x, ll + line * (size + dl));\n x += ctx.measureText(c).width;\n }\n }\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); - -/***/ }), - -/***/ "./src/text/txt4.ts": -/*!**************************!*\ - !*** ./src/text/txt4.ts ***! - \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parse */ \"./src/text/parse.ts\");\n\nconst CANVAS_ID = \"myTextCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nconst path = \"src/text/\";\nctx.fillStyle = BG_RECT.color;\nctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n(0,_parse__WEBPACK_IMPORTED_MODULE_0__.drawText)(path + \"txt4.txt\", ctx).catch(error => console.error(error));\n\n\n//# sourceURL=webpack:///./src/text/txt4.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/text/txt4.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,50+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt4.txt",n).catch((t=>console.error(t)))})(); \ No newline at end of file diff --git a/build/txt5.js b/build/txt5.js index 7419a55..d01456f 100644 --- a/build/txt5.js +++ b/build/txt5.js @@ -1,96 +1 @@ -/* - * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). - * This devtool is neither made for production nor for readable output files. - * It uses "eval()" calls to create a separate source file in the browser devtools. - * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) - * or disable the default devtool with "devtool: false". - * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). - */ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/text/parse.ts": -/*!***************************!*\ - !*** ./src/text/parse.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ drawText: () => (/* binding */ drawText)\n/* harmony export */ });\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function* () {\n console.log(filePath, \"\\n\");\n const fileContent = yield fetch(filePath)\n .then(response => response.text());\n const size = 30;\n const dl = 5;\n const ll = 50;\n const xx = 10;\n const defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n let readingColor = false;\n let color = \"\";\n let gras = \"\";\n let italic = \"\";\n let line = 1;\n let x = 0;\n for (var c of fileContent) {\n if (c === '*') {\n if (gras === \"\") {\n gras = \"bold \";\n }\n else {\n gras = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '~') {\n if (italic === \"\") {\n italic = \"italic \";\n }\n else {\n italic = \"\";\n }\n ctx.font = italic + gras + size + \"px Garamond\";\n }\n else if (c === '#') {\n color = \"#\";\n readingColor = true;\n }\n else if (c === '{') {\n readingColor = false;\n ctx.fillStyle = color;\n }\n else if (c === '}') {\n color = \"\";\n ctx.fillStyle = defaultColor;\n }\n else if (readingColor) {\n color += c;\n }\n else if (c === '\\n') {\n line++;\n x = 0;\n }\n else {\n ctx.fillText(c, xx + x, ll + line * (size + dl));\n x += ctx.measureText(c).width;\n }\n }\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); - -/***/ }), - -/***/ "./src/text/txt5.ts": -/*!**************************!*\ - !*** ./src/text/txt5.ts ***! - \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parse */ \"./src/text/parse.ts\");\n\nconst CANVAS_ID = \"myTextCanvas\";\nconst BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nconst canvas = document.getElementById(CANVAS_ID);\nconst ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nconst path = \"src/text/\";\nctx.fillStyle = BG_RECT.color;\nctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n(0,_parse__WEBPACK_IMPORTED_MODULE_0__.drawText)(path + \"txt5.txt\", ctx).catch(error => console.error(error));\n\n\n//# sourceURL=webpack:///./src/text/txt5.ts?"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module can't be inlined because the eval devtool is used. -/******/ var __webpack_exports__ = __webpack_require__("./src/text/txt5.ts"); -/******/ -/******/ })() -; \ No newline at end of file +(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,50+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt5.txt",n).catch((t=>console.error(t)))})(); \ No newline at end of file diff --git a/src/tests/assert.ts b/tests/assert.ts similarity index 100% rename from src/tests/assert.ts rename to tests/assert.ts diff --git a/src/tests/test.ts b/tests/test.ts similarity index 100% rename from src/tests/test.ts rename to tests/test.ts diff --git a/src/tests/test_hand.ts b/tests/test_hand.ts similarity index 95% rename from src/tests/test_hand.ts rename to tests/test_hand.ts index 260b2ea..667c55b 100644 --- a/src/tests/test_hand.ts +++ b/tests/test_hand.ts @@ -1,4 +1,4 @@ -import { Hand } from "../hand" +import { Hand } from "../src/hand" import { assert } from "./assert"; let h0 = new Hand("s1s1"); diff --git a/src/tests/test_yakus.ts b/tests/test_yakus.ts similarity index 98% rename from src/tests/test_yakus.ts rename to tests/test_yakus.ts index 932e472..df34fbf 100644 --- a/src/tests/test_yakus.ts +++ b/tests/test_yakus.ts @@ -1,7 +1,6 @@ import { assert } from "./assert" -import { yakus } from "../yakus/yaku" -import { Hand } from "../hand" -import { Group } from "../group"; +import { yakus } from "../src/yakus/yaku" +import { Hand } from "../src/hand" let count = 0; let total = 0; diff --git a/webpack.config.js b/webpack.config.js index d8d6ee1..2337164 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,7 +5,7 @@ const fs = require('fs'); function getEntryPoints() { const displayDir = path.resolve(__dirname, 'src', 'display'); const textDir = path.resolve(__dirname, 'src', 'text'); - const testDir = path.resolve(__dirname, 'src', 'tests'); + const testDir = path.resolve(__dirname, '.', 'tests'); const files = fs.readdirSync(displayDir); // Lire les fichiers du répertoire const texts = fs.readdirSync(textDir); const tests = fs.readdirSync(testDir);