diff --git a/build/dp0.js b/build/dp0.js index c45e1e0..0aca64e 100644 --- a/build/dp0.js +++ b/build/dp0.js @@ -16,7 +16,7 @@ \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Deck = /** @class */ (function () {\n function Deck(allowRed) {\n this.tiles = [];\n this.initTiles(allowRed);\n }\n Deck.prototype.displayFamilies = function (ctx, x, y, size, xOffset, yOffset) {\n if (xOffset === void 0) { xOffset = 0; }\n if (yOffset === void 0) { yOffset = 0; }\n var posX = x;\n var posY = y;\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 4) { //vent\n for (var j = 1; j < 5; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 5) { //vent\n for (var j = 1; j < 4; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n }\n }\n };\n Deck.prototype.length = function () {\n return this.tiles.length;\n };\n Deck.prototype.pop = function () {\n if (this.tiles.length === 0) {\n }\n return this.tiles.pop();\n };\n Deck.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Deck.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n = i;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n return this.tiles.shift();\n }\n else {\n return undefined;\n }\n };\n Deck.prototype.count = function (family, value) {\n var n = 0;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n++;\n }\n }\n return n;\n };\n Deck.prototype.shuffle = function () {\n var _a;\n var newArray = [];\n while (this.tiles.length > 0) {\n var n = Math.floor(Math.random() * this.tiles.length);\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n newArray.push(this.tiles.shift());\n }\n this.tiles = newArray;\n };\n Deck.prototype.getRandomHand = function () {\n var hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n for (var i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n };\n Deck.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n Deck.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n var i;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n i = 0;\n _a.label = 1;\n case 1:\n if (!(i < this.tiles.length)) return [3 /*break*/, 4];\n return [4 /*yield*/, this.tiles[i].preloadImg()];\n case 2:\n _a.sent();\n _a.label = 3;\n case 3:\n i++;\n return [3 /*break*/, 1];\n case 4: return [2 /*return*/];\n }\n });\n });\n };\n Deck.prototype.initTiles = function (allowRed) {\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n if (j === 5 && allowRed) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, true));\n }\n else {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 4) { // vent\n for (var j = 1; j < 5; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 5) { // dragon\n for (var j = 1; j < 4; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n }\n };\n return Deck;\n}());\n\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \****************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n// Configuration globale\nvar CANVAS_ID = \"myCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };\nvar MOUSE = { x: 0, y: 0 };\nvar FPS = 60; // Réduit de 90 à 60 pour de meilleures performances\nvar FRAME_INTERVAL = 1000 / FPS;\nvar RAIN = new _rain__WEBPACK_IMPORTED_MODULE_0__.Rain();\n// Optimisation des références\nvar animationFrameId;\nvar lastFrameTime = 0;\nvar accumulatedTime = 0;\nvar callbacks = [];\nvar timeStep = 1 / FPS;\n// Pré-calcul des dimensions\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\", { alpha: false });\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\n// Cache statique\nvar staticCanvas = document.createElement('canvas');\nvar 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 var 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 var handleVisibilityChange = function () {\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(function () {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n });\n}\nfunction cleanup() {\n cancelAnimationFrame(animationFrameId);\n callbacks.forEach(function (fn) { return fn(); });\n}\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function () {\n var error_1;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n if (!ctx) {\n console.error(\"Context canvas indisponible\");\n return [2 /*return*/];\n }\n console.log(\"Load beginning\");\n _a.label = 1;\n case 1:\n _a.trys.push([1, 3, , 4]);\n // Préchargement des ressources\n return [4 /*yield*/, RAIN.preloadRain()];\n case 2:\n // Préchargement des ressources\n _a.sent();\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 return [3 /*break*/, 4];\n case 3:\n error_1 = _a.sent();\n console.error(\"Erreur lors de l'initialisation:\", error_1);\n return [3 /*break*/, 4];\n case 4: return [2 /*return*/];\n }\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?"); +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?"); /***/ }), @@ -36,7 +36,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\nvar G = 100;\nvar SIZE = 1;\nvar LIMIT_MAX = 1100;\nvar SPAWN_CHANCE = 0.75;\nvar INITIAL_Y = -150;\nvar INITIAL_VY = 50;\nvar MAX_X = 1000;\nvar ROTATION_FACTOR = Math.PI;\nvar MOMENTUM_RANGE = 1;\nvar FallingTile = /** @class */ (function () {\n function FallingTile(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 FallingTile.prototype.update = function (dt) {\n this.vy += dt * G;\n this.y += dt * this.vy;\n this.orientation += dt * this.momentum;\n };\n FallingTile.prototype.isOutside = function () {\n return this.y > LIMIT_MAX;\n };\n FallingTile.prototype.getTile = function () {\n return this.tile;\n };\n FallingTile.prototype.drawFallingTile = function (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 return FallingTile;\n}());\nvar Rain = /** @class */ (function () {\n function Rain() {\n this.tiles = [];\n this.tileAddTimer = 0;\n this.deck = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(true);\n }\n Rain.prototype.update = function (dt) {\n var i = this.tiles.length;\n while (i--) {\n var 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 Rain.prototype.addFallingTile = function () {\n if (this.deck.length() === 0) {\n return;\n }\n if (this.deck.length() > 1) {\n this.deck.shuffle();\n }\n var 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 Rain.prototype.drawRain = function (ctx) {\n for (var _i = 0, _a = this.tiles; _i < _a.length; _i++) {\n var tile = _a[_i];\n tile.drawFallingTile(ctx);\n }\n };\n Rain.prototype.preloadRain = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n console.log(\"preload rain\");\n return [4 /*yield*/, this.deck.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n return Rain;\n}());\n\n\n\n//# sourceURL=webpack:///./src/rain.ts?"); +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?"); /***/ }), @@ -66,7 +66,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }) diff --git a/build/dp1.js b/build/dp1.js index 46551c7..95e962c 100644 --- a/build/dp1.js +++ b/build/dp1.js @@ -16,7 +16,7 @@ \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Deck = /** @class */ (function () {\n function Deck(allowRed) {\n this.tiles = [];\n this.initTiles(allowRed);\n }\n Deck.prototype.displayFamilies = function (ctx, x, y, size, xOffset, yOffset) {\n if (xOffset === void 0) { xOffset = 0; }\n if (yOffset === void 0) { yOffset = 0; }\n var posX = x;\n var posY = y;\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 4) { //vent\n for (var j = 1; j < 5; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 5) { //vent\n for (var j = 1; j < 4; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n }\n }\n };\n Deck.prototype.length = function () {\n return this.tiles.length;\n };\n Deck.prototype.pop = function () {\n if (this.tiles.length === 0) {\n }\n return this.tiles.pop();\n };\n Deck.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Deck.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n = i;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n return this.tiles.shift();\n }\n else {\n return undefined;\n }\n };\n Deck.prototype.count = function (family, value) {\n var n = 0;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n++;\n }\n }\n return n;\n };\n Deck.prototype.shuffle = function () {\n var _a;\n var newArray = [];\n while (this.tiles.length > 0) {\n var n = Math.floor(Math.random() * this.tiles.length);\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n newArray.push(this.tiles.shift());\n }\n this.tiles = newArray;\n };\n Deck.prototype.getRandomHand = function () {\n var hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n for (var i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n };\n Deck.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n Deck.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n var i;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n i = 0;\n _a.label = 1;\n case 1:\n if (!(i < this.tiles.length)) return [3 /*break*/, 4];\n return [4 /*yield*/, this.tiles[i].preloadImg()];\n case 2:\n _a.sent();\n _a.label = 3;\n case 3:\n i++;\n return [3 /*break*/, 1];\n case 4: return [2 /*return*/];\n }\n });\n });\n };\n Deck.prototype.initTiles = function (allowRed) {\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n if (j === 5 && allowRed) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, true));\n }\n else {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 4) { // vent\n for (var j = 1; j < 5; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 5) { // dragon\n for (var j = 1; j < 4; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n }\n };\n return Deck;\n}());\n\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \****************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\nfunction preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, deck.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction display() {\n return __awaiter(this, void 0, void 0, function () {\n var canvas, ctx, x, y, size, xos, yos, deck_1, delta, eps, i, j;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n canvas = document.getElementById(\"myCanvas\");\n if (!canvas) return [3 /*break*/, 4];\n ctx = canvas.getContext(\"2d\");\n if (!ctx) return [3 /*break*/, 2];\n // tapis\n ctx.fillStyle = \"#007730\";\n ctx.fillRect(50, 50, 1000, 1000);\n x = 180;\n y = 80;\n size = 0.75;\n xos = 40;\n yos = 100;\n deck_1 = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false);\n return [4 /*yield*/, preloadDeck(deck_1)];\n case 1:\n _a.sent();\n deck_1.displayFamilies(ctx, x, y, size, xos, yos);\n // texte\n ctx.fillStyle = \"#DFDFFF\";\n ctx.font = \"30px serif\";\n delta = 50 * size;\n 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 (i = 0; i < 3; i++) {\n for (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 = function () {\n deck_1.cleanup();\n };\n return [3 /*break*/, 3];\n case 2:\n console.error(\"Impossible d'obtenir le contexte du canvas.\");\n _a.label = 3;\n case 3: return [3 /*break*/, 5];\n case 4:\n console.error(\"Canvas introuvable dans le DOM.\");\n _a.label = 5;\n case 5: return [2 /*return*/];\n }\n });\n });\n}\ndisplay();\n\n\n//# sourceURL=webpack:///./src/display/dp1.ts?"); +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?"); /***/ }), @@ -36,7 +36,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dec \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }) diff --git a/build/dp2.js b/build/dp2.js index acc9b02..53a8581 100644 --- a/build/dp2.js +++ b/build/dp2.js @@ -16,7 +16,7 @@ \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Deck = /** @class */ (function () {\n function Deck(allowRed) {\n this.tiles = [];\n this.initTiles(allowRed);\n }\n Deck.prototype.displayFamilies = function (ctx, x, y, size, xOffset, yOffset) {\n if (xOffset === void 0) { xOffset = 0; }\n if (yOffset === void 0) { yOffset = 0; }\n var posX = x;\n var posY = y;\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 4) { //vent\n for (var j = 1; j < 5; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 5) { //vent\n for (var j = 1; j < 4; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n }\n }\n };\n Deck.prototype.length = function () {\n return this.tiles.length;\n };\n Deck.prototype.pop = function () {\n if (this.tiles.length === 0) {\n }\n return this.tiles.pop();\n };\n Deck.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Deck.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n = i;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n return this.tiles.shift();\n }\n else {\n return undefined;\n }\n };\n Deck.prototype.count = function (family, value) {\n var n = 0;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n++;\n }\n }\n return n;\n };\n Deck.prototype.shuffle = function () {\n var _a;\n var newArray = [];\n while (this.tiles.length > 0) {\n var n = Math.floor(Math.random() * this.tiles.length);\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n newArray.push(this.tiles.shift());\n }\n this.tiles = newArray;\n };\n Deck.prototype.getRandomHand = function () {\n var hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n for (var i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n };\n Deck.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n Deck.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n var i;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n i = 0;\n _a.label = 1;\n case 1:\n if (!(i < this.tiles.length)) return [3 /*break*/, 4];\n return [4 /*yield*/, this.tiles[i].preloadImg()];\n case 2:\n _a.sent();\n _a.label = 3;\n case 3:\n i++;\n return [3 /*break*/, 1];\n case 4: return [2 /*return*/];\n }\n });\n });\n };\n Deck.prototype.initTiles = function (allowRed) {\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n if (j === 5 && allowRed) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, true));\n }\n else {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 4) { // vent\n for (var j = 1; j < 5; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 5) { // dragon\n for (var j = 1; j < 4; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n }\n };\n return Deck;\n}());\n\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \****************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\nvar canvas = document.getElementById(\"myCanvas\");\nfunction preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, deck.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction display() {\n return __awaiter(this, void 0, void 0, function () {\n var ctx_1, offScreenCanvas_1, offScreenCtx_1, lastTime_1, FPS, interval_1, x_1, y_1, os_1, size_1, deck_1, hands_1, i, hand, edeck_1, ehand_1, selectedTile_1, drawCanvas_1, animationLoop_1;\n var _this = this;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n if (!canvas) return [3 /*break*/, 5];\n ctx_1 = canvas.getContext(\"2d\");\n if (!ctx_1) return [3 /*break*/, 3];\n offScreenCanvas_1 = document.createElement('canvas');\n offScreenCanvas_1.width = canvas.width;\n offScreenCanvas_1.height = canvas.height;\n offScreenCtx_1 = offScreenCanvas_1.getContext('2d');\n lastTime_1 = 0;\n FPS = 30;\n interval_1 = 1000 / FPS;\n x_1 = 100;\n y_1 = 150;\n os_1 = 75;\n size_1 = 0.75;\n deck_1 = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false);\n return [4 /*yield*/, preloadDeck(deck_1)];\n case 1:\n _a.sent();\n hands_1 = [];\n for (i = 0; i < 4; i++) {\n hand = deck_1.getRandomHand();\n hand.sort();\n hands_1.push(hand);\n }\n edeck_1 = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false);\n return [4 /*yield*/, edeck_1.preload()];\n case 2:\n _a.sent();\n ehand_1 = edeck_1.getRandomHand();\n ehand_1.push(edeck_1.pop());\n ehand_1.sort();\n selectedTile_1 = undefined;\n drawCanvas_1 = function () { return __awaiter(_this, void 0, void 0, function () {\n var i;\n return __generator(this, function (_a) {\n // clean screeen\n offScreenCtx_1.clearRect(0, 0, canvas.width, canvas.height);\n // tapis\n offScreenCtx_1.fillStyle = \"#007730\";\n offScreenCtx_1.fillRect(50, 50, 1000, 1000);\n // texte\n offScreenCtx_1.fillStyle = \"#DFDFFF\";\n offScreenCtx_1.font = \"50px serif\";\n offScreenCtx_1.fillText(\"Exemples de main:\", 65, 100);\n // example hands\n for (i = 0; i < hands_1.length; i++) {\n hands_1[i].drawHand(offScreenCtx_1, x_1, y_1 + i * size_1 * (100 + os_1), 5, size_1);\n }\n // dynamic hand\n ehand_1.isolate = true;\n ehand_1.drawHand(offScreenCtx_1, x_1, 800, 5, size_1, selectedTile_1);\n ctx_1.clearRect(0, 0, canvas.width, canvas.height);\n ctx_1.drawImage(offScreenCanvas_1, 0, 0);\n return [2 /*return*/];\n });\n }); };\n animationLoop_1 = function (currentTime) {\n var deltaTime = currentTime - lastTime_1;\n if (deltaTime >= interval_1) {\n lastTime_1 = currentTime;\n drawCanvas_1();\n }\n requestAnimationFrame(animationLoop_1);\n };\n // mouse event\n canvas.addEventListener(\"mousemove\", function (event) {\n var rect = canvas.getBoundingClientRect();\n var mouseX = event.clientX - rect.left - x_1;\n var mouseY = event.clientY - rect.top;\n var q = Math.floor(mouseX / (80 * size_1));\n var r = mouseX - q * 80 * size_1;\n if (r <= 75 && q >= 0 && q < 14 && mouseY >= 800 && mouseY <= 800 + 100 * size_1) {\n selectedTile_1 = q;\n }\n else {\n selectedTile_1 = undefined;\n }\n });\n canvas.addEventListener(\"mousedown\", function (event) {\n if (selectedTile_1 !== undefined) {\n edeck_1.push(ehand_1.eject(selectedTile_1));\n edeck_1.shuffle();\n ehand_1.sort();\n ehand_1.push(edeck_1.pop());\n }\n });\n requestAnimationFrame(animationLoop_1);\n window.cleanup = function () {\n deck_1.cleanup();\n hands_1.forEach(function (hand) { return hand.cleanup(); });\n hands_1 = [];\n edeck_1.cleanup();\n ehand_1.cleanup();\n ctx_1.clearRect(0, 0, canvas.width, canvas.height);\n offScreenCtx_1.clearRect(0, 0, offScreenCanvas_1.width, offScreenCanvas_1.height);\n selectedTile_1 = undefined;\n };\n return [3 /*break*/, 4];\n case 3:\n console.error(\"Impossible d'obtenir le contexte du canvas.\");\n _a.label = 4;\n case 4: return [3 /*break*/, 6];\n case 5:\n console.error(\"Canvas introuvable dans le DOM.\");\n _a.label = 6;\n case 6: return [2 /*return*/];\n }\n });\n });\n}\ndisplay();\n\n\n//# sourceURL=webpack:///./src/display/dp2.ts?"); +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?"); /***/ }), @@ -36,7 +36,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dec \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }) diff --git a/build/dp3.js b/build/dp3.js index 0305402..72ad567 100644 --- a/build/dp3.js +++ b/build/dp3.js @@ -16,7 +16,7 @@ \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Deck = /** @class */ (function () {\n function Deck(allowRed) {\n this.tiles = [];\n this.initTiles(allowRed);\n }\n Deck.prototype.displayFamilies = function (ctx, x, y, size, xOffset, yOffset) {\n if (xOffset === void 0) { xOffset = 0; }\n if (yOffset === void 0) { yOffset = 0; }\n var posX = x;\n var posY = y;\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 4) { //vent\n for (var j = 1; j < 5; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 5) { //vent\n for (var j = 1; j < 4; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n }\n }\n };\n Deck.prototype.length = function () {\n return this.tiles.length;\n };\n Deck.prototype.pop = function () {\n if (this.tiles.length === 0) {\n }\n return this.tiles.pop();\n };\n Deck.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Deck.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n = i;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n return this.tiles.shift();\n }\n else {\n return undefined;\n }\n };\n Deck.prototype.count = function (family, value) {\n var n = 0;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n++;\n }\n }\n return n;\n };\n Deck.prototype.shuffle = function () {\n var _a;\n var newArray = [];\n while (this.tiles.length > 0) {\n var n = Math.floor(Math.random() * this.tiles.length);\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n newArray.push(this.tiles.shift());\n }\n this.tiles = newArray;\n };\n Deck.prototype.getRandomHand = function () {\n var hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n for (var i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n };\n Deck.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n Deck.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n var i;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n i = 0;\n _a.label = 1;\n case 1:\n if (!(i < this.tiles.length)) return [3 /*break*/, 4];\n return [4 /*yield*/, this.tiles[i].preloadImg()];\n case 2:\n _a.sent();\n _a.label = 3;\n case 3:\n i++;\n return [3 /*break*/, 1];\n case 4: return [2 /*return*/];\n }\n });\n });\n };\n Deck.prototype.initTiles = function (allowRed) {\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n if (j === 5 && allowRed) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, true));\n }\n else {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 4) { // vent\n for (var j = 1; j < 5; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 5) { // dragon\n for (var j = 1; j < 4; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n }\n };\n return Deck;\n}());\n\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \****************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\n// Configuration globale\nvar CANVAS_ID = \"myCanvas\";\nvar BG_COLOR = \"#007730\";\nvar BG_RECT = { x: 50, y: 50, w: 1000, h: 1000 };\nvar FPS = 30;\nvar FRAME_INTERVAL = 1000 / FPS;\n// variables globales\nvar DECKS = [];\nvar HANDS = [];\nvar selectedTile = undefined;\n// Optimisation des références\nvar animationFrameId;\nvar lastFrameTime = 0;\nvar callbacks = [];\n// Pré-calcul des dimensions\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\n// Cache statique\nvar staticCanvas = document.createElement('canvas');\nvar staticCtx = staticCanvas.getContext(\"2d\");\nstaticCanvas.width = canvas.width;\nstaticCanvas.height = canvas.height;\n// Pré-rendu du fond\nfunction prerenderBackground() {\n staticCtx.clearRect(0, 0, canvas.width, canvas.height);\n staticCtx.fillStyle = BG_COLOR;\n staticCtx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);\n}\n;\nfunction drawFrame() {\n if (!ctx)\n return;\n // Effacement intelligent (uniquement la zone nécessaire)\n prerenderBackground();\n // Ici viendrait le dessin des éléments dynamiques\n // Par exemple:\n // drawDeck();\n // drawHands();\n var x = 300;\n var y = 150;\n var xos = 250;\n var yos = 100;\n var size = 0.75;\n staticCtx.fillStyle = \"#DFDFFF\";\n staticCtx.font = \"50px serif\";\n staticCtx.fillText(\"Chii:\", 75, y + 100 * size - 5);\n HANDS[0].drawHand(staticCtx, x, y, 5, 0.75); // chii\n HANDS[1].drawHand(staticCtx, x + (75 + xos) * size, y, 5, 0.75);\n staticCtx.fillText(\"Pon:\", 75, y + (100 + yos) * size + 100 * size - 5);\n HANDS[2].drawHand(staticCtx, x, y + (100 + yos) * size, 5, 0.75); // pon\n HANDS[3].drawHand(staticCtx, x + (75 + xos) * size, y + (100 + yos) * size, 5, 0.75);\n HANDS[4].drawHand(staticCtx, x + 2 * (75 + xos) * size, y + 2 * (100 + yos) * size, 5, 0.75);\n staticCtx.fillText(\"Invalide:\", 75, y + 2 * (100 + yos) * size + 100 * size - 5);\n HANDS[5].drawHand(staticCtx, x, y + 2 * (100 + yos) * size, 5, 0.75); // wrong\n HANDS[6].drawHand(staticCtx, x + (75 + xos) * size, y + 2 * (100 + yos) * size, 5, 0.75);\n HANDS[7].drawHand(staticCtx, x + 2 * (75 + xos) * size, y + 2 * (100 + yos) * size, 5, 0.75);\n HANDS[8].isolate = true;\n HANDS[8].drawHand(staticCtx, 100, 800, 5, size, selectedTile);\n var groups = HANDS[8].toGroup();\n if (groups !== undefined) {\n staticCtx.fillStyle = \"#FF0000\";\n staticCtx.font = \"50px serif\";\n staticCtx.fillText(\"Tous les groupes sont formés !\", 100, 750);\n }\n groups = [];\n // Dessin du cache statique\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(staticCanvas, 0, 0);\n}\nfunction animationLoop(currentTime) {\n animationFrameId = requestAnimationFrame(animationLoop);\n var deltaTime = currentTime - lastFrameTime;\n if (deltaTime < FRAME_INTERVAL)\n return;\n lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL);\n drawFrame();\n}\nfunction initEventListeners() {\n var handlers = {\n mousemove: function (e) {\n var size = 0.75;\n var x = 100;\n // Logique de gestion du mouvement de la souris\n var rect = canvas.getBoundingClientRect();\n var mouseX = e.clientX - rect.left - x;\n var mouseY = e.clientY - rect.top;\n var q = Math.floor(mouseX / (80 * size));\n var r = mouseX - q * 80 * size;\n if (r <= 75 && q >= 0 && q < 14 && mouseY >= 800 && mouseY <= 800 + 100 * size) {\n selectedTile = q;\n }\n else {\n selectedTile = undefined;\n }\n },\n mousedown: function (e) {\n // Logique de gestion du clic de souris\n if (selectedTile !== undefined) {\n DECKS[0].push(HANDS[8].eject(selectedTile));\n DECKS[0].shuffle();\n HANDS[8].sort();\n HANDS[8].push(DECKS[0].pop());\n }\n }\n };\n callbacks.push(function () {\n canvas.removeEventListener('mousemove', handlers.mousemove);\n canvas.removeEventListener('mousedown', handlers.mousedown);\n });\n canvas.addEventListener('mousemove', handlers.mousemove);\n canvas.addEventListener('mousedown', handlers.mousedown);\n}\nfunction preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, deck.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction preloadHand(hand) {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, hand.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction cleanup() {\n cancelAnimationFrame(animationFrameId);\n callbacks.forEach(function (fn) { return fn(); });\n}\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n if (!ctx) {\n console.error(\"Context canvas indisponible\");\n return [2 /*return*/];\n }\n // Préchargement des ressources si nécessaire\n // const deck = new Deck();\n // await preloadDeck(deck);\n DECKS.push(new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(false));\n HANDS.push(new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"s1s2s3\"), new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"m2m3m4\"), new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"p5p5p5\"), new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"w2w2w2\"), new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"d3d3d3\"), new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"s4p5m6\"), new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"m9s9p9\"), new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand(\"d1d2d3\"), DECKS[0].getRandomHand());\n return [4 /*yield*/, Promise.all(DECKS.map(function (d) { return preloadDeck(d); }))];\n case 1:\n _a.sent();\n return [4 /*yield*/, Promise.all(HANDS.map(function (h) { return preloadHand(h); }))];\n case 2:\n _a.sent();\n HANDS[8].push(DECKS[0].pop());\n HANDS[8].sort();\n initEventListeners();\n requestAnimationFrame(animationLoop);\n window.cleanup = cleanup;\n return [2 /*return*/];\n }\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?"); +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?"); /***/ }), @@ -36,7 +36,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }) diff --git a/build/dp4.js b/build/dp4.js index 850e2a3..29f2bc4 100644 --- a/build/dp4.js +++ b/build/dp4.js @@ -16,7 +16,7 @@ \***********************/ /***/ ((__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 */ });\nfunction clickAction(x, y, chii, pon, kan, ron, tsumo) {\n var buttons = [\n [tsumo, 5],\n [ron, 4],\n [kan, 3],\n [pon, 2],\n [chii, 1]\n ];\n if (buttons.some(function (c) { return c[0]; })) {\n buttons.push([true, 0]);\n }\n else {\n return -1;\n }\n var xmin = 960 - buttons.filter(function (c) { return c[0]; }).length * 120;\n var inside = 838 < y && y < 888;\n var q = Math.floor((x - xmin) / 120);\n var r = (x - xmin) - 120 * q;\n if (q >= 0 &&\n q < buttons.filter(function (c) { return c[0]; }).length &&\n r > 10 &&\n inside) {\n return buttons.filter(function (c) { return c[0]; })[q][1];\n }\n return -1;\n}\nfunction drawButtons(ctx, chii, pon, kan, ron, tsumo) {\n var buttons = [\n [chii, buttonChii],\n [pon, buttonPon],\n [kan, buttonKan],\n [ron, buttonRon],\n [tsumo, buttonTsumo]\n ];\n if (buttons.some(function (c) { return c[0]; })) {\n buttons.unshift([true, buttonPass]);\n }\n var dx = 0;\n for (var i = 0; i < buttons.length; i++) {\n if (buttons[i][0]) {\n buttons[i][1](ctx, 850 - dx * 120, 835);\n dx++;\n }\n }\n}\nfunction clickChii(x, y, chiis) {\n var xmin = 960 - (chiis.length + 1) * 120;\n var inside = 838 < y && y < 888;\n var q = Math.floor((x - xmin) / 120);\n var r = (x - xmin) - 120 * q;\n if (q >= 0 &&\n q < (chiis.length + 1) &&\n r > 10 &&\n inside) {\n return q === chiis.length ? 0 : chiis[q][0].getValue();\n }\n return -1;\n}\nfunction drawChiis(ctx, chiis) {\n chiis.reverse();\n var r = 8;\n var w = 110;\n var h = 50;\n button(ctx, 850, 835, r, w, h, \"#FF9030\");\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n ctx.fillText(\"Retour\", 850 + w * 0.1, 835 + h / 2 * 1.3);\n var dx = 1;\n for (var i = 0; i < chiis.length; i++) {\n drawOneChii(ctx, 850 - dx * 120, 835, chiis[i]);\n dx++;\n }\n}\nfunction drawOneChii(ctx, x, y, tiles) {\n var r = 8;\n var w = 110;\n var h = 50;\n var dx = 32;\n var x0 = x + 7;\n var y0 = y + 5;\n button(ctx, x, y, r, w, h, \"#FFCC33\");\n tiles[0].drawTile(ctx, x0, y0, 0.4, false, 0, false, false);\n tiles[1].drawTile(ctx, x0 + dx, y0, 0.4, false, 0, false, false);\n tiles[2].drawTile(ctx, x0 + 2 * dx, y0, 0.4, false, 0, false, false);\n}\nfunction button(ctx, x, y, r, w, h, color) {\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.moveTo(x + r, y);\n ctx.lineTo(x + w - r, y);\n ctx.quadraticCurveTo(x + w, y, x + w, y + r);\n ctx.lineTo(x + w, y + h - r);\n ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);\n ctx.lineTo(x + r, y + h);\n ctx.quadraticCurveTo(x, y + h, x, y + h - r);\n ctx.lineTo(x, y + r);\n ctx.quadraticCurveTo(x, y, x + r, y);\n ctx.fill();\n ctx.fillStyle = \"#606060\";\n ctx.stroke();\n}\nfunction buttonPass(ctx, x, y) {\n var r = 8;\n var w = 110;\n var h = 50;\n // button(ctx, x, y, r, w, h, \"#FFAC4D\");\n button(ctx, x, y, r, w, h, \"#FF9030\");\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n ctx.fillText(\"Ignorer\", x + w * 0.1, y + h / 2 * 1.3);\n}\nfunction buttonPon(ctx, x, y) {\n var r = 8;\n var w = 110;\n var h = 50;\n button(ctx, x, y, r, w, h, \"#FFCC33\");\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n ctx.fillText(\"Pon\", x + w * 0.28, y + h / 2 * 1.3);\n}\nfunction buttonChii(ctx, x, y) {\n var r = 8;\n var w = 110;\n var h = 50;\n button(ctx, x, y, r, w, h, \"#FFCC33\");\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n ctx.fillText(\"Chii\", x + w * 0.25, y + h / 2 * 1.3);\n}\nfunction buttonKan(ctx, x, y) {\n var r = 8;\n var w = 110;\n var h = 50;\n button(ctx, x, y, r, w, h, \"#FFCC33\");\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n ctx.fillText(\"Kan\", x + w * 0.28, y + h / 2 * 1.3);\n}\nfunction buttonRon(ctx, x, y) {\n var r = 8;\n var w = 110;\n var h = 50;\n button(ctx, x, y, r, w, h, \"#FF3060\");\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n ctx.fillText(\"Ron\", x + w * 0.28, y + h / 2 * 1.3);\n}\nfunction buttonTsumo(ctx, x, y) {\n var r = 8;\n var w = 110;\n var h = 50;\n button(ctx, x, y, r, w, h, \"#FF3060\");\n ctx.fillStyle = \"black\";\n ctx.font = \"30px garamond\";\n ctx.fillText(\"Tsumo\", x + w * 0.13, y + h / 2 * 1.3);\n}\n\n\n//# sourceURL=webpack:///./src/button.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Deck = /** @class */ (function () {\n function Deck(allowRed) {\n this.tiles = [];\n this.initTiles(allowRed);\n }\n Deck.prototype.displayFamilies = function (ctx, x, y, size, xOffset, yOffset) {\n if (xOffset === void 0) { xOffset = 0; }\n if (yOffset === void 0) { yOffset = 0; }\n var posX = x;\n var posY = y;\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size, false, 0, false);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 4) { //vent\n for (var j = 1; j < 5; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n posX = x;\n posY += (100 + yOffset) * size;\n }\n else if (i === 5) { //vent\n for (var j = 1; j < 4; j++) {\n var tile = this.find(i, j);\n tile.drawTile(ctx, posX, posY, size);\n posX += (75 + xOffset) * size;\n }\n }\n }\n };\n Deck.prototype.length = function () {\n return this.tiles.length;\n };\n Deck.prototype.pop = function () {\n if (this.tiles.length === 0) {\n }\n return this.tiles.pop();\n };\n Deck.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Deck.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n = i;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n return this.tiles.shift();\n }\n else {\n return undefined;\n }\n };\n Deck.prototype.count = function (family, value) {\n var n = 0;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family &&\n this.tiles[i].getValue() === value) {\n n++;\n }\n }\n return n;\n };\n Deck.prototype.shuffle = function () {\n var _a;\n var newArray = [];\n while (this.tiles.length > 0) {\n var n = Math.floor(Math.random() * this.tiles.length);\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n newArray.push(this.tiles.shift());\n }\n this.tiles = newArray;\n };\n Deck.prototype.getRandomHand = function () {\n var hand = new _hand__WEBPACK_IMPORTED_MODULE_1__.Hand();\n this.shuffle();\n for (var i = 0; i < 13; i++) {\n hand.push(this.pop());\n }\n return hand;\n };\n Deck.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n Deck.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n var i;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n i = 0;\n _a.label = 1;\n case 1:\n if (!(i < this.tiles.length)) return [3 /*break*/, 4];\n return [4 /*yield*/, this.tiles[i].preloadImg()];\n case 2:\n _a.sent();\n _a.label = 3;\n case 3:\n i++;\n return [3 /*break*/, 1];\n case 4: return [2 /*return*/];\n }\n });\n });\n };\n Deck.prototype.initTiles = function (allowRed) {\n for (var i = 1; i < 6; i++) {\n if (i < 4) { // famille\n for (var j = 1; j < 10; j++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n if (j === 5 && allowRed) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, true));\n }\n else {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 4) { // vent\n for (var j = 1; j < 5; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n else if (i === 5) { // dragon\n for (var j = 1; j < 4; j++) {\n for (var k = 0; k < 4; k++) {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(i, j, false));\n }\n }\n }\n }\n };\n return Deck;\n}());\n\n\n\n//# sourceURL=webpack:///./src/deck.ts?"); +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?"); /***/ }), @@ -36,7 +36,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \****************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n// Configuration globale\nvar CANVAS_ID = \"myCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };\nvar MOUSE = { x: 0, y: 0 };\nvar FPS = 60;\nvar FRAME_INTERVAL = 1000 / FPS;\n// variables globales\nvar DECKS = [];\nvar HANDS = [];\nvar GAME;\n// Optimisation des références\nvar animationFrameId;\nvar lastFrameTime = 0;\nvar callbacks = [];\n// Pré-calcul des dimensions\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\n// Cache statique\nvar staticCanvas = document.createElement('canvas');\nvar 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 var deltaTime = currentTime - lastFrameTime;\n if (deltaTime < FRAME_INTERVAL)\n return;\n lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL);\n drawFrame();\n}\nfunction initEventListeners() {\n var handlers = {\n mousedown: function (e) {\n GAME === null || GAME === void 0 ? void 0 : GAME.click(e);\n },\n mousemove: function (e) {\n MOUSE.x = e.x;\n MOUSE.y = e.y;\n }\n };\n callbacks.push(function () {\n canvas.removeEventListener('mousemove', handlers.mousemove);\n canvas.removeEventListener('mousedown', handlers.mousedown);\n });\n canvas.addEventListener('mousemove', handlers.mousemove);\n canvas.addEventListener('mousedown', handlers.mousedown);\n}\nfunction preloadDeck(deck) {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, deck.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction preloadHand(hand) {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, hand.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction cleanup() {\n cancelAnimationFrame(animationFrameId);\n callbacks.forEach(function (fn) { return fn(); });\n}\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n if (!ctx) {\n console.error(\"Context canvas indisponible\");\n return [2 /*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 DECKS.push();\n HANDS.push();\n GAME = new _game__WEBPACK_IMPORTED_MODULE_0__.Game(ctx, canvas, staticCtx, staticCanvas);\n return [4 /*yield*/, Promise.all(DECKS.map(function (d) { return preloadDeck(d); }))];\n case 1:\n _a.sent();\n return [4 /*yield*/, Promise.all(HANDS.map(function (h) { return preloadHand(h); }))];\n case 2:\n _a.sent();\n return [4 /*yield*/, (GAME === null || GAME === void 0 ? void 0 : GAME.preload())];\n case 3:\n _a.sent();\n console.log(\"Loaded completed\\n\");\n initEventListeners();\n requestAnimationFrame(animationLoop);\n window.cleanup = cleanup;\n return [2 /*return*/];\n }\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/dp4.ts?"); +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?"); /***/ }), @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\n\n\nvar Game = /** @class */ (function () {\n function Game(ctx, cv, staticCtx, staticCv, red, level) {\n if (red === void 0) { red = false; }\n if (level === void 0) { level = 0; }\n this.deadWall = [];\n this.hands = [];\n this.turn = 0;\n this.minWaitingTime = 500;\n this.maxWaitingTime = 2000;\n this.waitingTime = 700;\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 // display parameter\n this.BG_RECT = { color: \"#007730\", x: 0, y: 0, w: 1050, h: 1050 };\n this.sizeHand = 0.7;\n this.sizeHiddenHand = 0.6;\n this.sizeDiscard = 0.6;\n this.ctx = ctx;\n this.cv = cv;\n this.staticCtx = staticCtx;\n this.staticCv = staticCv;\n this.level = level;\n this.deck = new _deck__WEBPACK_IMPORTED_MODULE_0__.Deck(red);\n this.deck.shuffle();\n for (var i = 0; i < 14; i++) {\n this.deadWall.push(this.deck.pop());\n }\n for (var i = 0; i < 4; i++) {\n this.hands.push(this.deck.getRandomHand());\n }\n this.discards = [[], [], [], []];\n this.lastDiscard = undefined;\n this.groups = [[], [], [], []];\n this.hands[0].sort();\n this.pick(0);\n }\n Game.prototype.draw = function (mp) {\n // background\n this.staticCtx.clearRect(0, 0, this.cv.width, this.cv.height);\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 this.ctx.clearRect(0, 0, this.cv.width, this.cv.height);\n this.ctx.drawImage(this.staticCv, 0, 0);\n };\n Game.prototype.getDeck = function () {\n return this.deck;\n };\n Game.prototype.getHands = function () {\n return this.hands;\n };\n Game.prototype.isFinished = function () {\n return this.end;\n };\n Game.prototype.click = function (// player is playing\n mp) {\n var rect = this.cv.getBoundingClientRect();\n if (this.hasWin(0)) {\n this.end = true;\n this.result = 1;\n }\n else if (this.chooseChii) { // is choosing\n var allChii = this.getChii(0);\n var c = (0,_button__WEBPACK_IMPORTED_MODULE_2__.clickChii)(mp.x - rect.x, mp.y - rect.y, allChii);\n if (c === 0) {\n this.chooseChii = false;\n }\n else {\n this.chooseChii = false;\n this.chii(c, 0);\n }\n }\n else {\n var action = (0,_button__WEBPACK_IMPORTED_MODULE_2__.clickAction)(mp.x - rect.left, mp.y - rect.top, this.canDoAChii().length > 0, this.canDoAPon(), false && 0, false && 0, false && 0);\n if (this.canCall && action !== -1) { // can call\n if (action === 0) { // pass\n this.canCall = false;\n if (this.turn === 3) {\n this.turn = 0;\n this.pick(0);\n }\n else {\n this.turn++;\n }\n this.updateWaitingTime();\n this.hasPicked = false;\n this.hasPlayed = false;\n }\n else if (action === 1) { // chii\n var chiis = this.canDoAChii();\n if (chiis.length === 1) { // only one possible\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 else { // nothing unusual\n if (this.turn === 0 && this.selectedTile !== undefined) {\n this.discard(0, this.selectedTile);\n if (!this.checkPon()) {\n var chiis = this.canDoAChii(1);\n if (chiis.length > 0) {\n var i = Math.floor(Math.random() * chiis.length);\n this.chii(chiis[i], 1);\n }\n else {\n this.updateWaitingTime();\n this.turn = (this.turn + 1) % 4;\n }\n }\n }\n }\n }\n };\n Game.prototype.getSelected = function (mp) {\n var rect = this.cv.getBoundingClientRect();\n var x = 2.5 * 75 * 0.75;\n var y = 1050 - 250 * 0.6;\n var mouseX = mp.x - rect.left - x;\n var mouseY = mp.y - rect.top;\n var s = 83.9;\n var q = Math.floor(mouseX / (s * this.sizeHand));\n var r = mouseX - q * s * this.sizeHand;\n if (r <= (s - 3) * this.sizeHand &&\n q >= 0 &&\n q < this.hands[0].length() &&\n mouseY >= y &&\n mouseY <= y + 100 * this.sizeHand) {\n this.selectedTile = q;\n }\n else {\n this.selectedTile = undefined;\n }\n };\n ;\n Game.prototype.updateWaitingTime = function () {\n this.waitingTime =\n Math.floor(Math.random() *\n (this.maxWaitingTime - this.minWaitingTime) +\n this.minWaitingTime);\n };\n Game.prototype.play = function () {\n if (this.turn !== 0 && !this.end) { // bot playing\n if (!this.hasPicked) { // begin of his turn\n this.lastPlayed = Date.now();\n this.pick(this.turn);\n this.hasPicked = true;\n }\n else if (!this.hasPlayed) { // middle of his turn\n if (this.hasWin(this.turn)) {\n this.end = true;\n this.result = 2;\n }\n else if (Date.now() - this.lastPlayed > this.waitingTime) {\n this.lastPlayed = Date.now();\n var 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 }\n if (!this.end) {\n this.checkPon();\n if (this.turn !== 3 && this.canDoAChii(this.turn + 1).length > 0) {\n var chiis = this.canDoAChii(this.turn + 1);\n var i = Math.floor(Math.random() * chiis.length);\n this.chii(chiis[i], this.turn + 1);\n }\n this.canCall = this.canDoAChii().length > 0 || this.canDoAPon();\n }\n }\n }\n else if (!this.canCall) { // end of his turn\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 }\n };\n Game.prototype.pick = function (player) {\n this.hands[player].push(this.deck.pop());\n this.hands[player].isolate = true;\n };\n Game.prototype.discard = function (player, n) {\n var 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 Game.prototype.canDoAChii = function (p) {\n if (p === void 0) { p = 0; }\n var chii = [];\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 var t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1];\n var h = this.hands[p];\n if (h.count(t.getFamily(), t.getValue() - 2) > 0 &&\n h.count(t.getFamily(), t.getValue() - 1) > 0) {\n chii.push(t.getValue() - 2);\n }\n if (h.count(t.getFamily(), t.getValue() - 1) > 0 &&\n h.count(t.getFamily(), t.getValue() + 1) > 0) {\n chii.push(t.getValue() - 1);\n }\n if (h.count(t.getFamily(), t.getValue() + 1) > 0 &&\n h.count(t.getFamily(), t.getValue() + 2) > 0) {\n chii.push(t.getValue());\n }\n }\n return chii;\n };\n Game.prototype.chii = function (minValue, p) {\n var t = this.discards[p === 0 ? 3 : p - 1].pop();\n this.lastDiscard = undefined;\n var tn = 0;\n var v = minValue;\n var tt = [];\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 [t, tt[0], tt[1]].forEach(function (t) { return t.setTilt(); });\n this.groups[p].push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t, tt[0], tt[1]], p === 0 ? 3 : p - 1, 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 Game.prototype.getChii = function (p) {\n var chiis = [];\n var numChii = this.canDoAChii();\n var d = this.discards[p === 0 ? 3 : p - 1];\n var t = d[d.length - 1];\n for (var i = 0; i < numChii.length; i++) {\n var v = numChii[i];\n var chii = [];\n for (var dv = 0; dv < 3; dv++) {\n if (v + dv === t.getValue()) {\n chii.push(t);\n }\n else {\n var 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 Game.prototype.checkPon = function () {\n for (var p = 1; p < 4; p++) {\n if (this.canDoAPon(p)) {\n this.pon(this.lastDiscard, p);\n return true;\n }\n }\n return false;\n };\n Game.prototype.canDoAPon = function (player) {\n if (player === void 0) { player = 0; }\n if (this.lastDiscard !== undefined && // il y a une défausse\n this.lastDiscard !== player && // pas sa propre défausse\n this.turn !== player && // pas son propre tour\n !(this.hasPicked && !this.hasPlayed) // pas un joueur en train de jouer\n ) {\n var t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1];\n return this.hands[player].count(t.getFamily(), t.getValue()) >= 2;\n }\n else {\n return false;\n }\n };\n Game.prototype.pon = function (p, thief) {\n if (thief === void 0) { thief = 0; }\n var t = this.discards[p].pop();\n this.lastDiscard = undefined;\n var t2 = this.hands[thief].find(t.getFamily(), t.getValue());\n var t3 = this.hands[thief].find(t.getFamily(), t.getValue());\n [t, t2, t3].forEach(function (t) { return t.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 Game.prototype.hasWin = function (p) {\n return this.hands[p].toGroup() !== undefined;\n };\n Game.prototype.drawGame = function () {\n // update game\n this.play();\n // draw winds, discard, riichi etc...\n (0,_state__WEBPACK_IMPORTED_MODULE_3__.drawState)(this.staticCtx, this.turn);\n this.drawDiscardSize();\n // draw result\n this.drawResult();\n // hands\n this.drawHands();\n // groups\n this.drawGroups(0.6);\n // discards\n for (var i = 0; i < 4; i++) {\n this.drawDiscard(i, this.hands[0].get(this.selectedTile));\n }\n // called\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 Game.prototype.drawHands = function () {\n var pi = 3.141592;\n var showHands = false;\n this.hands[0].drawHand(this.staticCtx, 2.5 * 75 * 0.75, 1000 - 150 * this.sizeHand, 5 * this.sizeHand, 0.75, this.selectedTile, false, 0);\n this.hands[1].drawHand(this.staticCtx, 1000 - 150 * this.sizeHiddenHand, 1000 - 75 * 5 * this.sizeHiddenHand, 5 * this.sizeHiddenHand, this.sizeHiddenHand, undefined, !showHands, -pi / 2);\n this.hands[2].drawHand(this.staticCtx, 1000 - 75 * 5 * this.sizeHiddenHand, 150 * this.sizeHiddenHand, 5 * this.sizeHiddenHand, this.sizeHiddenHand, undefined, !showHands, -pi);\n this.hands[3].drawHand(this.staticCtx, 150 * this.sizeHiddenHand, 75 * 5 * this.sizeHiddenHand, 5 * this.sizeHiddenHand, this.sizeHiddenHand, undefined, !showHands, pi / 2);\n };\n Game.prototype.drawGroups = function (size) {\n var os = 25;\n var pi = 3.141592;\n for (var p = 0; p < 4; p++) {\n var rotation = [0, -pi / 2, -pi, pi / 2][p];\n if (this.groups[p].length > 0) {\n for (var i = this.groups[p].length - 1; i >= 0; i--) {\n this.groups[p][i].drawGroup(this.staticCtx, 1050 - 240 - (260 + os) * size * i, 1050 - 62, 5, 0.6, rotation, this.hands[0].get(this.selectedTile));\n }\n }\n }\n };\n Game.prototype.drawDiscard = function (p, highlitedTile) {\n var pi = 3.141592;\n this.staticCtx.save();\n this.staticCtx.translate(525, 525);\n this.staticCtx.rotate([0, -pi / 2, -pi, pi / 2][p]);\n var x = -this.sizeDiscard * 475 / 2;\n var y = this.sizeDiscard * (475 / 2 + 5);\n for (var i = 0; i < this.discards[p].length; i++) {\n var tile = this.discards[p][i];\n if (i < 12) {\n tile.drawTile(this.staticCtx, x + (i % 6) * 80 * this.sizeDiscard, y + Math.floor(i / 6) * 105 * this.sizeDiscard, this.sizeDiscard, false, 0, highlitedTile === null || highlitedTile === void 0 ? void 0 : highlitedTile.isEqual(tile.getFamily(), tile.getValue()));\n }\n else {\n tile.drawTile(this.staticCtx, x + (i - 12) * 80 * this.sizeDiscard, y + 2 * 105 * this.sizeDiscard, this.sizeDiscard, false, 0, highlitedTile === null || highlitedTile === void 0 ? void 0 : highlitedTile.isEqual(tile.getFamily(), tile.getValue()));\n }\n }\n if (this.lastDiscard === p) {\n var j = this.discards[p].length - 1;\n var tx = j < 12 ?\n x + (j % 6) * 80 * this.sizeDiscard :\n x + (j - 12) * 80 * this.sizeDiscard;\n var ty = j < 12 ?\n y + Math.floor(j / 6) * 105 * this.sizeDiscard :\n y + 2 * 105 * this.sizeDiscard;\n tx += 75 / 2 * this.sizeDiscard;\n ty += 115 * this.sizeDiscard;\n var ts = 10;\n this.staticCtx.fillStyle = \"#ff0000\";\n this.staticCtx.beginPath();\n this.staticCtx.moveTo(tx, ty);\n this.staticCtx.lineTo(tx + ts / 2, ty + 0.866 * ts);\n this.staticCtx.lineTo(tx - ts / 2, ty + 0.866 * ts);\n this.staticCtx.lineTo(tx, ty);\n this.staticCtx.fill();\n this.staticCtx.stroke();\n }\n this.staticCtx.restore();\n };\n Game.prototype.drawDiscardSize = function () {\n this.staticCtx.fillStyle = \"#f070f0\";\n this.staticCtx.font = \"40px garamond\";\n var l = this.deck.length();\n if (l < 10) {\n this.staticCtx.fillText(l.toString(), 517, 537);\n }\n else {\n this.staticCtx.fillText(l.toString(), 507, 537);\n }\n };\n Game.prototype.drawResult = function () {\n if (this.result !== -1) {\n this.staticCtx.fillStyle = \"#e0e0f0\";\n this.staticCtx.fillRect(450, 430, 150, 190);\n this.staticCtx.fillRect(430, 450, 190, 150);\n this.staticCtx.fillStyle = \"#ff0000\";\n this.staticCtx.font = \"45px garamond\";\n }\n if (this.result === 0) { // Égalité\n this.staticCtx.fillText(\"Égalité\", 450, 535);\n }\n else if (this.result === 1) { // victoire\n this.staticCtx.fillText(\"Victoire !\", 440, 535);\n }\n else if (this.result === 2) { // Défaite\n this.staticCtx.fillText(\"Défaite...\", 440, 535);\n }\n };\n Game.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, this.deck.preload()];\n case 1:\n _a.sent();\n return [4 /*yield*/, Promise.all(this.hands.map(function (h) { return h.preload(); }))];\n case 2:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n return Game;\n}());\n\n\n\n//# sourceURL=webpack:///./src/game.ts?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -66,7 +66,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -76,7 +76,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**********************/ /***/ ((__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, rotation) {\n if (turn === void 0) { turn = 0; }\n if (rotation === void 0) { rotation = 0; }\n var color = \"#e0e0f0\";\n var r = 30;\n var s = 100;\n var c = 525;\n var pi = Math.PI;\n // turn\n var w = 150;\n var h = 4;\n var rd = 2;\n var 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?"); +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?"); /***/ }), @@ -86,7 +86,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }) diff --git a/build/dp5.js b/build/dp5.js index 8f5051e..484d7d7 100644 --- a/build/dp5.js +++ b/build/dp5.js @@ -16,7 +16,7 @@ \****************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n// Configuration globale\nvar CANVAS_ID = \"myCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };\nvar MOUSE = { x: 0, y: 0 };\nvar FPS = 30;\nvar FRAME_INTERVAL = 1000 / FPS;\n// variables globales\nvar DECKS = [];\nvar HANDS = [];\nvar GAME;\n// Optimisation des références\nvar animationFrameId;\nvar lastFrameTime = 0;\nvar callbacks = [];\n// Pré-calcul des dimensions\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\n// Cache statique\nvar staticCanvas = document.createElement('canvas');\nvar 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 var deltaTime = currentTime - lastFrameTime;\n if (deltaTime < FRAME_INTERVAL)\n return;\n lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL);\n drawFrame();\n}\nfunction initEventListeners() {\n var handlers = {\n mousedown: function (e) {\n GAME === null || GAME === void 0 ? void 0 : GAME.click(e);\n },\n mousemove: function (e) {\n MOUSE.x = e.x;\n MOUSE.y = e.y;\n }\n };\n callbacks.push(function () {\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 return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, deck.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction preloadHand(hand) {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, hand.preload()];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n}\nfunction cleanup() {\n cancelAnimationFrame(animationFrameId);\n callbacks.forEach(function (fn) { return fn(); });\n}\nfunction initDisplay() {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n if (!ctx) {\n console.error(\"Context canvas indisponible\");\n return [2 /*return*/];\n }\n console.log(\"Load begining\\n\");\n return [2 /*return*/];\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/dp5.ts?"); +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?"); /***/ }) diff --git a/build/test.js b/build/test.js index 80e187b..c611906 100644 --- a/build/test.js +++ b/build/test.js @@ -16,7 +16,7 @@ \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _tes \********************************/ /***/ ((__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\nvar h0 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"s1s1\");\nvar h1 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3\");\nvar h2 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1\");\nvar h3 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m9m9m9 p9p9\");\nvar h4 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1p1\");\nvar count = 0;\nvar 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?"); +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?"); /***/ }), @@ -66,7 +66,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _han \*********************************/ /***/ ((__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\nvar count = 0;\nvar total = 0;\nvar h1 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m5m6 m7m8m9 m1m2m3 m5m5\");\nvar h2 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nvar h3 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 p9p9p9 s1s1s1 w2w2w2 d1d1\");\nvar h4 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m2m3m4 p3p3p3 p4p5p6 s4s4 s6s7s8\");\nvar h5 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p7p8p9 p7p8p9 w3w3\");\nvar h6 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p7p8 p7p8p9 w3w3\");\nvar h7 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m7m8m9 p9p9\");\nvar h8 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p6 w0w0w0 w3w3w3\");\nvar h9 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 d1d1d1 d2d2d2 d3d3\");\nvar h10 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p6p6 d1d1d1 d2d2d2 d3d3d3\");\nvar h11 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 w1w1w1 w2w2w2 w3w3w3 w4w4\");\nvar h12 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nvar h13 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"d1d1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nvar h14 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m9m9m9 p1p1p1 p9p9p9 s1s1s1\");\nvar h15 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3\");\nvar h16 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m7m7\");\nvar h17 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m6m6\");\nvar h18 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nvar 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?"); +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?"); /***/ }), @@ -76,7 +76,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ass \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }), @@ -86,7 +86,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \***************************/ /***/ ((__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(function (t) { return t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9; });\n}\nfunction term(g) {\n return g.getTiles().every(function (t) {\n t.getFamily() < 4 &&\n (t.getValue() === 1 ||\n t.getValue() === 9);\n });\n}\nfunction honn(g) {\n return g.getTiles().every(function (t) {\n return t.getFamily() >= 4;\n });\n}\nfunction chii(g) {\n var t = g.getTiles();\n return t[0].getValue() !== t[1].getValue();\n}\nfunction pon(g) {\n var t = g.getTiles();\n return t[0].getValue() === t[1].getValue();\n}\nvar 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 var gr = hand.toGroup();\n gr.sort(function (g1, g2) { return g1.compare(g2); });\n for (var i = 0; i < 3; i++) {\n var g1 = gr[i].getTiles();\n var 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 var gr = hand.toGroup();\n gr.filter(function (g) { return chii(g); });\n if (gr.length < 4) { // pas assez de suite\n return 0;\n }\n gr.sort(function (g1, g2) { return g1.compare(g2); });\n var t1 = gr[0].getTiles()[0];\n var t2 = gr[1].getTiles()[0];\n var t3 = gr[2].getTiles()[0];\n var 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 var h = hand.toGroup();\n if (h !== undefined &&\n h.every(function (g) {\n var 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 var h = hand.toGroup();\n var gr = [];\n if (h !== undefined) {\n gr = groups.concat(h);\n }\n else {\n gr = groups;\n }\n gr = gr.filter(function (g) { return chii(g); });\n gr.sort(function (g1, g2) { return g1.compare(g2); });\n if (gr.length < 3) { // pas assez de chii\n return 0;\n }\n else if (gr.length === 3) {\n var t0 = gr[0].getTiles();\n var t1 = gr[1].getTiles();\n var 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 (var i = 0; i < 4; i++) {\n var index = [];\n for (var j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n var t0 = gr[index[0]].getTiles();\n var t1 = gr[index[1]].getTiles();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return chii(g); });\n gr.sort(function (g1, g2) { return 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 var g1 = gr[0].getTiles();\n var g2 = gr[1].getTiles();\n var 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 (var i = 0; i < 4; i++) {\n var index = [];\n for (var j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n var t0 = gr[index[0]].getTiles();\n var t1 = gr[index[1]].getTiles();\n var 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 var h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(function (g) { return ord(g); }) &&\n h.every(function (g) { return 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g) && g.getTiles().length === 3; });\n var han = 0;\n gr.forEach(function (g) {\n var t = g.getTiles();\n var f = t[0].getFamily();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g); });\n var nbPon = 0;\n var nbPair = 0;\n gr.forEach(function (g) {\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g); });\n var nbPon = 0;\n var nbPair = 0;\n gr.forEach(function (g) {\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g); });\n var nbPon = 0;\n var nbPair = 0;\n gr.forEach(function (g) {\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) {\n var tiles = g.getTiles();\n var f = tiles[0].getFamily();\n var v = tiles[0].getValue();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) {\n var tiles = g.getTiles();\n var f = tiles[0].getFamily();\n var v = tiles[0].getValue();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) {\n var tiles = g.getTiles();\n var f = tiles[0].getFamily();\n var 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 var h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(function (g) { return term(g); }) &&\n h.every(function (g) { return 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 var h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(function (g) { return honn(g); }) &&\n h.every(function (g) { return 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 var h = hand.getTiles();\n if (h.some(function (t) {\n return t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9;\n })) {\n return 0;\n }\n var count = 0;\n for (var 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 var h = hand.getTiles();\n if (h.length !== 14) {\n return 0;\n }\n for (var 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 var h = hand.toGroup();\n if (groups.every(function (g) { return pon(g); }) &&\n (h === null || h === void 0 ? void 0 : h.every(function (g) { return 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 var h = hand.toGroup();\n var count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(function (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 var h = hand.toGroup();\n var count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(function (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 var h = hand.getTiles();\n var t0 = h[0];\n if (h.every(function (t) { return t.getFamily() === t0.getFamily(); }) &&\n groups.every(function (g) { return g.getTiles().every(function (t) { return 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?"); +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?"); /***/ }) diff --git a/build/test_hand.js b/build/test_hand.js index 6accf21..7da8238 100644 --- a/build/test_hand.js +++ b/build/test_hand.js @@ -16,7 +16,7 @@ \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \********************************/ /***/ ((__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\nvar h0 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"s1s1\");\nvar h1 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3\");\nvar h2 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1\");\nvar h3 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m9m9m9 p9p9\");\nvar h4 = new _hand__WEBPACK_IMPORTED_MODULE_0__.Hand(\"m2m2m2 p1p1p1\");\nvar count = 0;\nvar 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?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _han \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }) diff --git a/build/test_yakus.js b/build/test_yakus.js index dae4d9c..d2ffd92 100644 --- a/build/test_yakus.js +++ b/build/test_yakus.js @@ -16,7 +16,7 @@ \**********************/ /***/ ((__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 */ });\nvar Group = /** @class */ (function () {\n function Group(tiles, stolenFrom, belongsTo) {\n this.tiles = tiles;\n this.stolenFrom = stolenFrom;\n this.belongsTo = belongsTo;\n }\n Group.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Group.prototype.pop = function () {\n return this.tiles.pop();\n };\n Group.prototype.getTiles = function () {\n return this.tiles;\n };\n Group.prototype.compare = function (g) {\n var c = this.tiles[0].compare(g.tiles[0]);\n if (c !== 0) {\n return c;\n }\n return this.tiles[1].compare(g.tiles[1]);\n };\n Group.prototype.drawGroup = function (ctx, x, y, os, size, rotation, selectedTile) {\n ctx.save();\n ctx.translate(525, 525);\n ctx.rotate(rotation);\n ctx.translate(-525, -525);\n rotation = 0;\n var v = 75 * size;\n var w = 90 * size;\n var osy = 25 * size / 2;\n var p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4;\n var sf = selectedTile === undefined ? -1 : selectedTile.getFamily();\n var sv = selectedTile === undefined ? 0 : selectedTile.getValue();\n if (p === 0) {\n this.tiles[0].drawTile(ctx, x, y + osy, size, false, 3.141592 / 2, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 1) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + w, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + 3 * os * size, y, size, false, 0, this.tiles[2].isEqual(sf, sv));\n }\n else if (p === 2) {\n this.tiles[0].drawTile(ctx, x, y, size, false, 0, this.tiles[0].isEqual(sf, sv));\n this.tiles[1].drawTile(ctx, x + v + os * size, y, size, false, 0, this.tiles[1].isEqual(sf, sv));\n this.tiles[2].drawTile(ctx, x + w + v + os * size, y + osy, size, false, 0 - 3.141592 / 2, this.tiles[2].isEqual(sf, sv));\n }\n else {\n //TODO error\n }\n ctx.restore();\n };\n return Group;\n}());\n\n\n\n//# sourceURL=webpack:///./src/group.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n\n\nvar Hand = /** @class */ (function () {\n function Hand(stiles) {\n if (stiles === void 0) { stiles = \"\"; }\n this.isolate = false;\n this.tiles = [];\n for (var i = 0; i < stiles.length - 1; i++) {\n var ss = stiles.substring(i, i + 2);\n if (ss[0] === \"m\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(1, Number(ss[1]), false));\n }\n else if (ss[0] === \"p\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(2, Number(ss[1]), false));\n }\n else if (ss[0] === \"s\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(3, Number(ss[1]), false));\n }\n else if (ss[0] === \"w\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(4, Number(ss[1]), false));\n }\n else if (ss[0] === \"d\") {\n this.tiles.push(new _tile__WEBPACK_IMPORTED_MODULE_0__.Tile(5, Number(ss[1]), false));\n }\n else { }\n }\n }\n Hand.prototype.getTiles = function () {\n return this.tiles;\n };\n Hand.prototype.length = function () {\n return this.tiles.length;\n };\n Hand.prototype.push = function (tile) {\n this.tiles.push(tile);\n };\n Hand.prototype.pop = function () {\n return this.tiles.pop();\n };\n Hand.prototype.find = function (family, value) {\n var _a;\n var n = undefined;\n for (var i = 0; i < this.tiles.length; i++) {\n if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) {\n n = i;\n break;\n }\n }\n if (n !== undefined) {\n _a = [this.tiles[0], this.tiles[n]], this.tiles[n] = _a[0], this.tiles[0] = _a[1];\n var t = this.tiles.shift();\n this.sort();\n return t;\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.eject = function (idTile) {\n var _a;\n _a = [this.tiles[idTile], this.tiles[0]], this.tiles[0] = _a[0], this.tiles[idTile] = _a[1];\n var tile = this.tiles.shift();\n this.sort();\n return tile;\n };\n Hand.prototype.get = function (idTile) {\n if (idTile !== undefined) {\n return this.tiles[idTile];\n }\n else {\n return undefined;\n }\n };\n Hand.prototype.sort = function () {\n this.tiles.sort(function (a, b) { return a.isLessThan(b) ? -1 : 1; });\n };\n Hand.prototype.count = function (family, value) {\n var c = 0;\n this.tiles.forEach(function (t) {\n if (t.getFamily() === family && t.getValue() === value) {\n c++;\n }\n });\n return c;\n };\n Hand.prototype.toGroup = function (pair) {\n if (pair === void 0) { pair = false; }\n if (this.tiles.length > 0) {\n var t1 = this.tiles.pop();\n var c = this.count(t1.getFamily(), t1.getValue());\n if (c >= 1 && !pair) { //can do a pair\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(true);\n this.tiles.push(t2);\n this.sort();\n if (groups !== undefined) {\n this.tiles.push(t1);\n this.sort();\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2], 0, 0));\n return groups;\n }\n }\n if (c >= 2) { //can do a pon\n var t2 = this.find(t1.getFamily(), t1.getValue());\n var t3 = this.find(t1.getFamily(), t1.getValue());\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t1, t2, t3], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n var c2 = this.count(t1.getFamily(), t1.getValue() - 1);\n var c3 = this.count(t1.getFamily(), t1.getValue() - 2);\n if (c2 * c3 > 0) { //can do a chii\n var t2 = this.find(t1.getFamily(), t1.getValue() - 1);\n var t3 = this.find(t1.getFamily(), t1.getValue() - 2);\n var groups = this.toGroup(pair);\n this.tiles.push(t2);\n this.tiles.push(t3);\n this.sort();\n if (groups !== undefined) {\n groups.push(new _group__WEBPACK_IMPORTED_MODULE_1__.Group([t3, t2, t1], 0, 0));\n this.tiles.push(t1);\n this.sort();\n return groups;\n }\n }\n this.tiles.push(t1);\n this.tiles.sort();\n return undefined;\n }\n else {\n return [];\n }\n };\n Hand.prototype.drawHand = function (ctx, x, y, offset, size, focusedTiled, hidden, rotation) {\n if (focusedTiled === void 0) { focusedTiled = undefined; }\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n var v = (75 + offset) * size;\n var vx = Math.cos(rotation) * v;\n var vy = Math.sin(rotation) * v;\n for (var i = 0; i < this.tiles.length; i++) {\n var e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;\n if (i === focusedTiled) {\n this.tiles[i].drawTile(ctx, x +\n i * vx +\n 25 * size * Math.sin(rotation) +\n e * size * Math.cos(rotation), y +\n i * vy -\n 25 * size * Math.cos(rotation) +\n e * size * Math.sin(rotation), size, hidden, rotation);\n }\n else {\n this.tiles[i].drawTile(ctx, x + i * vx + e * size * Math.cos(rotation), y + i * vy + e * size * Math.sin(rotation), size, hidden, rotation);\n }\n }\n };\n Hand.prototype.preload = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all(this.tiles.map(function (t) { return t.preloadImg(); }))];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Hand.prototype.cleanup = function () {\n this.tiles.forEach(function (tile) { return tile.cleanup(); });\n this.tiles = [];\n };\n return Hand;\n}());\n\n\n\n//# sourceURL=webpack:///./src/hand.ts?"); +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?"); /***/ }), @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*********************************/ /***/ ((__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\nvar count = 0;\nvar total = 0;\nvar h1 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m5m6 m7m8m9 m1m2m3 m5m5\");\nvar h2 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nvar h3 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1m1 p9p9p9 s1s1s1 w2w2w2 d1d1\");\nvar h4 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m2m3m4 p3p3p3 p4p5p6 s4s4 s6s7s8\");\nvar h5 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p7p8p9 p7p8p9 w3w3\");\nvar h6 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p7p8 p7p8p9 w3w3\");\nvar h7 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p1p2p3 s1s2s3 m7m8m9 p9p9\");\nvar h8 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 p6p6 w0w0w0 w3w3w3\");\nvar h9 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m1m2m3 d1d1d1 d2d2d2 d3d3\");\nvar h10 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 p6p6 d1d1d1 d2d2d2 d3d3d3\");\nvar h11 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 w1w1w1 w2w2w2 w3w3w3 w4w4\");\nvar h12 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nvar h13 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"d1d1 w1w1w1 w2w2w2 w3w3w3 w4w4w4\");\nvar h14 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m9m9m9 p1p1p1 p9p9p9 s1s1s1\");\nvar h15 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m9m9 p1p9 s1s9 w1w2w3w4 d1d2d3\");\nvar h16 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m7m7\");\nvar h17 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m1 m2m2 m3m3 m4m4 m5m5 m6m6 m6m6\");\nvar h18 = new _hand__WEBPACK_IMPORTED_MODULE_2__.Hand(\"m1m2m3 m4m4m4 m7m7m7 p1p1p1 p5p5\");\nvar 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?"); +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?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ass \*********************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar Tile = /** @class */ (function () {\n function Tile(family, value, red) {\n this.family = family;\n this.value = value;\n this.red = red;\n this.imgSrc = \"\";\n this.imgFront = new Image();\n this.imgBack = new Image();\n this.imgGray = new Image();\n this.img = new Image();\n this.tilt = 0;\n this.setImgSrc();\n }\n Tile.prototype.getFamily = function () {\n return this.family;\n };\n Tile.prototype.getValue = function () {\n return this.value;\n };\n Tile.prototype.isEqual = function (family, value) {\n return this.family === family && this.value === value;\n };\n Tile.prototype.isRed = function () {\n return this.red;\n };\n Tile.prototype.compare = function (t) {\n if (this.family < t.family) {\n return -1;\n }\n else if (this.family > t.family) {\n return 1;\n }\n if (this.value < t.value) {\n return -1;\n }\n else if (this.value > t.value) {\n return 1;\n }\n return 0;\n };\n Tile.prototype.setTilt = function () {\n this.tilt = (1 - 2 * Math.random()) * 0.04;\n };\n Tile.prototype.drawTile = function (ctx, x, y, size, hidden, rotation, gray, tilted) {\n if (hidden === void 0) { hidden = false; }\n if (rotation === void 0) { rotation = 0; }\n if (gray === void 0) { gray = false; }\n if (tilted === void 0) { tilted = true; }\n ctx.save();\n ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);\n if (tilted) {\n ctx.rotate(rotation + this.tilt);\n }\n else {\n ctx.rotate(rotation);\n }\n if (hidden) {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dos des tuiles\n this.imgBack, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n else {\n ctx.drawImage(// ombre\n this.imgGray, -(75 * size * 0.92) / 2, -(100 * size * 0.91) / 2, 75 * size, 100 * size);\n ctx.drawImage(// tuile à vide\n this.imgFront, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n ctx.drawImage(// le dessin sur la tuile\n this.img, -((75 - 7) * size) / 2, -((100 - 10) * size) / 2, 75 * size * 0.9, 100 * size * 0.9);\n if (gray) { // grisé\n ctx.drawImage(this.imgGray, -(75 * size) / 2, -(100 * size) / 2, 75 * size, 100 * size);\n }\n }\n ctx.restore();\n };\n Tile.prototype.isLessThan = function (t) {\n if (this.family < t.family) {\n return true;\n }\n else if (this.family === t.family && this.value <= t.value) {\n return true;\n }\n else {\n return false;\n }\n };\n Tile.prototype.cleanup = function () {\n this.imgFront.onload = null;\n this.imgFront.onerror = null;\n this.imgBack.onload = null;\n this.imgBack.onerror = null;\n this.imgGray.onload = null;\n this.imgGray.onerror = null;\n this.img.onload = null;\n this.img.onerror = null;\n };\n Tile.prototype.setImgSrc = function () {\n this.imgSrc = \"img/Regular/\";\n if (this.family <= 3) {\n this.imgSrc += [\"\", \"Man\", \"Pin\", \"Sou\"][this.family];\n this.imgSrc += String(this.value);\n if (this.red) {\n this.imgSrc += \"-Dora\";\n }\n this.imgSrc += \".svg\";\n }\n else if (this.family === 4) {\n this.imgSrc += [\"\", \"Ton\", \"Nan\", \"Shaa\", \"Pei\"][this.value] + \".svg\";\n }\n else if (this.family === 5) {\n this.imgSrc += [\"\", \"Chun\", \"Hatsu\", \"Haku\"][this.value] + \".svg\";\n }\n };\n Tile.prototype.preloadImg = function () {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0: return [4 /*yield*/, Promise.all([\n this.loadImg(this.imgFront, \"img/Regular/Front.svg\"),\n // this.loadImg(this.imgFront, \"/~img/Export/Regular/Front.png\"),\n this.loadImg(this.imgBack, \"img/Regular/Back.svg\"),\n this.loadImg(this.imgGray, \"img/Regular/Gray.svg\"),\n this.loadImg(this.img, this.imgSrc)\n ])];\n case 1:\n _a.sent();\n return [2 /*return*/];\n }\n });\n });\n };\n Tile.prototype.loadImg = function (img, src) {\n return new Promise(function (resolve, reject) {\n img.onload = function () { return resolve(); };\n img.onerror = function () { return reject(); };\n img.src = src;\n });\n };\n return Tile;\n}());\n\n\n\n//# sourceURL=webpack:///./src/tile.ts?"); +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?"); /***/ }), @@ -66,7 +66,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \***************************/ /***/ ((__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(function (t) { return t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9; });\n}\nfunction term(g) {\n return g.getTiles().every(function (t) {\n t.getFamily() < 4 &&\n (t.getValue() === 1 ||\n t.getValue() === 9);\n });\n}\nfunction honn(g) {\n return g.getTiles().every(function (t) {\n return t.getFamily() >= 4;\n });\n}\nfunction chii(g) {\n var t = g.getTiles();\n return t[0].getValue() !== t[1].getValue();\n}\nfunction pon(g) {\n var t = g.getTiles();\n return t[0].getValue() === t[1].getValue();\n}\nvar 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 var gr = hand.toGroup();\n gr.sort(function (g1, g2) { return g1.compare(g2); });\n for (var i = 0; i < 3; i++) {\n var g1 = gr[i].getTiles();\n var 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 var gr = hand.toGroup();\n gr.filter(function (g) { return chii(g); });\n if (gr.length < 4) { // pas assez de suite\n return 0;\n }\n gr.sort(function (g1, g2) { return g1.compare(g2); });\n var t1 = gr[0].getTiles()[0];\n var t2 = gr[1].getTiles()[0];\n var t3 = gr[2].getTiles()[0];\n var 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 var h = hand.toGroup();\n if (h !== undefined &&\n h.every(function (g) {\n var 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 var h = hand.toGroup();\n var gr = [];\n if (h !== undefined) {\n gr = groups.concat(h);\n }\n else {\n gr = groups;\n }\n gr = gr.filter(function (g) { return chii(g); });\n gr.sort(function (g1, g2) { return g1.compare(g2); });\n if (gr.length < 3) { // pas assez de chii\n return 0;\n }\n else if (gr.length === 3) {\n var t0 = gr[0].getTiles();\n var t1 = gr[1].getTiles();\n var 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 (var i = 0; i < 4; i++) {\n var index = [];\n for (var j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n var t0 = gr[index[0]].getTiles();\n var t1 = gr[index[1]].getTiles();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return chii(g); });\n gr.sort(function (g1, g2) { return 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 var g1 = gr[0].getTiles();\n var g2 = gr[1].getTiles();\n var 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 (var i = 0; i < 4; i++) {\n var index = [];\n for (var j = 0; j < 4; j++) {\n if (j !== i) {\n index.push(j);\n }\n }\n var t0 = gr[index[0]].getTiles();\n var t1 = gr[index[1]].getTiles();\n var 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 var h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(function (g) { return ord(g); }) &&\n h.every(function (g) { return 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g) && g.getTiles().length === 3; });\n var han = 0;\n gr.forEach(function (g) {\n var t = g.getTiles();\n var f = t[0].getFamily();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g); });\n var nbPon = 0;\n var nbPair = 0;\n gr.forEach(function (g) {\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g); });\n var nbPon = 0;\n var nbPair = 0;\n gr.forEach(function (g) {\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) { return pon(g); });\n var nbPon = 0;\n var nbPair = 0;\n gr.forEach(function (g) {\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) {\n var tiles = g.getTiles();\n var f = tiles[0].getFamily();\n var v = tiles[0].getValue();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) {\n var tiles = g.getTiles();\n var f = tiles[0].getFamily();\n var v = tiles[0].getValue();\n var 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 var gr = groups.concat(hand.toGroup());\n gr = gr.filter(function (g) {\n var tiles = g.getTiles();\n var f = tiles[0].getFamily();\n var 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 var h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(function (g) { return term(g); }) &&\n h.every(function (g) { return 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 var h = hand.toGroup();\n if (h === undefined) {\n return 0;\n }\n if (groups.every(function (g) { return honn(g); }) &&\n h.every(function (g) { return 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 var h = hand.getTiles();\n if (h.some(function (t) {\n return t.getFamily() < 4 &&\n t.getValue() > 1 &&\n t.getValue() < 9;\n })) {\n return 0;\n }\n var count = 0;\n for (var 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 var h = hand.getTiles();\n if (h.length !== 14) {\n return 0;\n }\n for (var 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 var h = hand.toGroup();\n if (groups.every(function (g) { return pon(g); }) &&\n (h === null || h === void 0 ? void 0 : h.every(function (g) { return 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 var h = hand.toGroup();\n var count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(function (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 var h = hand.toGroup();\n var count = 0;\n h === null || h === void 0 ? void 0 : h.forEach(function (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 var h = hand.getTiles();\n var t0 = h[0];\n if (h.every(function (t) { return t.getFamily() === t0.getFamily(); }) &&\n groups.every(function (g) { return g.getTiles().every(function (t) { return 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?"); +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?"); /***/ }) diff --git a/build/txt0.js b/build/txt0.js index 6daa295..56125f8 100644 --- a/build/txt0.js +++ b/build/txt0.js @@ -16,7 +16,7 @@ \***************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function () {\n var fileContent, size, dl, ll, xx, defaultColor, readingColor, color, gras, italic, line, x, _i, fileContent_1, c;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n console.log(filePath, \"\\n\");\n return [4 /*yield*/, fetch(filePath)\n .then(function (response) { return response.text(); })];\n case 1:\n fileContent = _a.sent();\n size = 30;\n dl = 5;\n ll = 50;\n xx = 10;\n defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n readingColor = false;\n color = \"\";\n gras = \"\";\n italic = \"\";\n line = 1;\n x = 0;\n for (_i = 0, fileContent_1 = fileContent; _i < fileContent_1.length; _i++) {\n c = fileContent_1[_i];\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 return [2 /*return*/];\n }\n });\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**************************/ /***/ ((__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\nvar CANVAS_ID = \"myTextCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nvar 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(function (error) { return console.error(error); });\n\n\n//# sourceURL=webpack:///./src/text/txt0.ts?"); +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?"); /***/ }) diff --git a/build/txt1.js b/build/txt1.js index 728e4bc..30a8019 100644 --- a/build/txt1.js +++ b/build/txt1.js @@ -16,7 +16,7 @@ \***************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function () {\n var fileContent, size, dl, ll, xx, defaultColor, readingColor, color, gras, italic, line, x, _i, fileContent_1, c;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n console.log(filePath, \"\\n\");\n return [4 /*yield*/, fetch(filePath)\n .then(function (response) { return response.text(); })];\n case 1:\n fileContent = _a.sent();\n size = 30;\n dl = 5;\n ll = 50;\n xx = 10;\n defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n readingColor = false;\n color = \"\";\n gras = \"\";\n italic = \"\";\n line = 1;\n x = 0;\n for (_i = 0, fileContent_1 = fileContent; _i < fileContent_1.length; _i++) {\n c = fileContent_1[_i];\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 return [2 /*return*/];\n }\n });\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**************************/ /***/ ((__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\nvar CANVAS_ID = \"myTextCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nvar 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(function (error) { return console.error(error); });\n\n\n//# sourceURL=webpack:///./src/text/txt1.ts?"); +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?"); /***/ }) diff --git a/build/txt2.js b/build/txt2.js index 3d50e97..a2f2794 100644 --- a/build/txt2.js +++ b/build/txt2.js @@ -16,7 +16,7 @@ \***************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function () {\n var fileContent, size, dl, ll, xx, defaultColor, readingColor, color, gras, italic, line, x, _i, fileContent_1, c;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n console.log(filePath, \"\\n\");\n return [4 /*yield*/, fetch(filePath)\n .then(function (response) { return response.text(); })];\n case 1:\n fileContent = _a.sent();\n size = 30;\n dl = 5;\n ll = 50;\n xx = 10;\n defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n readingColor = false;\n color = \"\";\n gras = \"\";\n italic = \"\";\n line = 1;\n x = 0;\n for (_i = 0, fileContent_1 = fileContent; _i < fileContent_1.length; _i++) {\n c = fileContent_1[_i];\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 return [2 /*return*/];\n }\n });\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**************************/ /***/ ((__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\nvar CANVAS_ID = \"myTextCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nvar 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(function (error) { return console.error(error); });\n\n\n//# sourceURL=webpack:///./src/text/txt2.ts?"); +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?"); /***/ }) diff --git a/build/txt3.js b/build/txt3.js index 28d1961..49acbd2 100644 --- a/build/txt3.js +++ b/build/txt3.js @@ -16,7 +16,7 @@ \***************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function () {\n var fileContent, size, dl, ll, xx, defaultColor, readingColor, color, gras, italic, line, x, _i, fileContent_1, c;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n console.log(filePath, \"\\n\");\n return [4 /*yield*/, fetch(filePath)\n .then(function (response) { return response.text(); })];\n case 1:\n fileContent = _a.sent();\n size = 30;\n dl = 5;\n ll = 50;\n xx = 10;\n defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n readingColor = false;\n color = \"\";\n gras = \"\";\n italic = \"\";\n line = 1;\n x = 0;\n for (_i = 0, fileContent_1 = fileContent; _i < fileContent_1.length; _i++) {\n c = fileContent_1[_i];\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 return [2 /*return*/];\n }\n });\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**************************/ /***/ ((__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\nvar CANVAS_ID = \"myTextCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nvar 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(function (error) { return console.error(error); });\n\n\n//# sourceURL=webpack:///./src/text/txt3.ts?"); +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?"); /***/ }) diff --git a/build/txt4.js b/build/txt4.js index 37cd303..7c34ce6 100644 --- a/build/txt4.js +++ b/build/txt4.js @@ -16,7 +16,7 @@ \***************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function () {\n var fileContent, size, dl, ll, xx, defaultColor, readingColor, color, gras, italic, line, x, _i, fileContent_1, c;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n console.log(filePath, \"\\n\");\n return [4 /*yield*/, fetch(filePath)\n .then(function (response) { return response.text(); })];\n case 1:\n fileContent = _a.sent();\n size = 30;\n dl = 5;\n ll = 50;\n xx = 10;\n defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n readingColor = false;\n color = \"\";\n gras = \"\";\n italic = \"\";\n line = 1;\n x = 0;\n for (_i = 0, fileContent_1 = fileContent; _i < fileContent_1.length; _i++) {\n c = fileContent_1[_i];\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 return [2 /*return*/];\n }\n });\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**************************/ /***/ ((__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\nvar CANVAS_ID = \"myTextCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nvar 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(function (error) { return console.error(error); });\n\n\n//# sourceURL=webpack:///./src/text/txt4.ts?"); +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?"); /***/ }) diff --git a/build/txt5.js b/build/txt5.js index 5e40bcd..7419a55 100644 --- a/build/txt5.js +++ b/build/txt5.js @@ -16,7 +16,7 @@ \***************************/ /***/ ((__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};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nfunction drawText(filePath, ctx) {\n return __awaiter(this, void 0, void 0, function () {\n var fileContent, size, dl, ll, xx, defaultColor, readingColor, color, gras, italic, line, x, _i, fileContent_1, c;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n console.log(filePath, \"\\n\");\n return [4 /*yield*/, fetch(filePath)\n .then(function (response) { return response.text(); })];\n case 1:\n fileContent = _a.sent();\n size = 30;\n dl = 5;\n ll = 50;\n xx = 10;\n defaultColor = \"#ffffff\";\n ctx.fillStyle = defaultColor;\n ctx.font = size + \"px Garamond\";\n readingColor = false;\n color = \"\";\n gras = \"\";\n italic = \"\";\n line = 1;\n x = 0;\n for (_i = 0, fileContent_1 = fileContent; _i < fileContent_1.length; _i++) {\n c = fileContent_1[_i];\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 return [2 /*return*/];\n }\n });\n });\n}\n\n\n//# sourceURL=webpack:///./src/text/parse.ts?"); +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?"); /***/ }), @@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**************************/ /***/ ((__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\nvar CANVAS_ID = \"myTextCanvas\";\nvar BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: \"#007733\" };\nvar canvas = document.getElementById(CANVAS_ID);\nvar ctx = canvas.getContext(\"2d\");\ncanvas.width = BG_RECT.w;\ncanvas.height = BG_RECT.h;\nvar 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(function (error) { return console.error(error); });\n\n\n//# sourceURL=webpack:///./src/text/txt5.ts?"); +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?"); /***/ }) diff --git a/src/button.ts b/src/button.ts index 0911733..a897c64 100644 --- a/src/button.ts +++ b/src/button.ts @@ -1,277 +1,311 @@ -import { Tile } from "./tile" +import { Tile } from "./tile"; -type button_t = ( - arg0: CanvasRenderingContext2D, - arg1: number, - arg2: number +/** + * Type definition for button rendering functions + */ +type ButtonRenderer = ( + ctx: CanvasRenderingContext2D, + x: number, + y: number ) => void; +/** + * Button configuration interface + */ +interface ButtonConfig { + text: string; + color: string; + action: number; +} + +// Button constants +const BUTTON_RADIUS = 8; +const BUTTON_WIDTH = 110; +const BUTTON_HEIGHT = 50; +const BUTTON_SPACING = 120; +const BUTTON_AREA_Y_MIN = 838; +const BUTTON_AREA_Y_MAX = 888; +const BUTTON_MARGIN = 10; +const BASE_X_POSITION = 850; + +// Button style configurations +const BUTTON_STYLES: Record = { + pass: { text: "Ignorer", color: "#FF9030", action: 0 }, + chii: { text: "Chii", color: "#FFCC33", action: 1 }, + pon: { text: "Pon", color: "#FFCC33", action: 2 }, + kan: { text: "Kan", color: "#FFCC33", action: 3 }, + ron: { text: "Ron", color: "#FF3060", action: 4 }, + tsumo: { text: "Tsumo", color: "#FF3060", action: 5 }, + back: { text: "Retour", color: "#FF9030", action: 0 } +}; + +/** + * Determines which button was clicked based on coordinates + * @returns The action value of the clicked button or -1 if no button was clicked + */ export function clickAction( - x: number, - y: number, - chii: boolean, - pon: boolean, - kan: boolean, - ron: boolean, - tsumo: boolean + x: number, + y: number, + chii: boolean, + pon: boolean, + kan: boolean, + ron: boolean, + tsumo: boolean ): number { - let buttons = [ - [tsumo, 5], - [ron, 4], - [kan, 3], - [pon, 2], - [chii, 1] - ] - if (buttons.some(c => c[0])) { - buttons.push([true, 0]); - } else { - return -1; - } - let xmin = 960 - buttons.filter(c => c[0]).length * 120; - let inside = 838 < y && y < 888; - let q = Math.floor((x - xmin) / 120); - let r = (x - xmin) - 120 * q; - if ( - q >= 0 && - q < buttons.filter(c => c[0]).length && - r > 10 && - inside - ) { - return buttons.filter(c => c[0])[q][1] as number; - } - return -1; + const activeButtons = getActiveButtons(chii, pon, kan, ron, tsumo); + + if (activeButtons.length === 0) { + return -1; + } + + // Calculate starting X position based on number of buttons + const xmin = 960 - activeButtons.length * BUTTON_SPACING; + + // Check if Y coordinate is within button area + const isYInButtonArea = BUTTON_AREA_Y_MIN < y && y < BUTTON_AREA_Y_MAX; + if (!isYInButtonArea) { + return -1; + } + + // Calculate which button was clicked + const buttonIndex = Math.floor((x - xmin) / BUTTON_SPACING); + const xOffset = (x - xmin) - BUTTON_SPACING * buttonIndex; + + if ( + buttonIndex >= 0 && + buttonIndex < activeButtons.length && + xOffset > BUTTON_MARGIN + ) { + return activeButtons[buttonIndex]; + } + + return -1; } +/** + * Draw all active buttons on the canvas + */ export function drawButtons( - ctx: CanvasRenderingContext2D, - chii: boolean, - pon: boolean, - kan: boolean, - ron: boolean, - tsumo: boolean + ctx: CanvasRenderingContext2D, + chii: boolean, + pon: boolean, + kan: boolean, + ron: boolean, + tsumo: boolean ): void { - let buttons = [ - [chii, buttonChii], - [pon, buttonPon], - [kan, buttonKan], - [ron, buttonRon], - [tsumo, buttonTsumo] - ] - if (buttons.some(c => c[0])) { - buttons.unshift([true, buttonPass]); - } - let dx = 0; - for (let i = 0; i < buttons.length; i++) { - if (buttons[i][0]) { - (buttons[i][1] as button_t)( - ctx, - 850 - dx * 120, - 835 - ); - dx++; - } - } + const buttonFunctions: [boolean, ButtonRenderer][] = [ + [chii, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.chii)], + [pon, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.pon)], + [kan, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.kan)], + [ron, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.ron)], + [tsumo, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.tsumo)] + ]; + + // Only show the pass button if at least one other button is active + const hasActiveButtons = buttonFunctions.some(([isActive]) => isActive); + + if (hasActiveButtons) { + buttonFunctions.unshift([true, (ctx, x, y) => renderButton(ctx, x, y, BUTTON_STYLES.pass)]); + } else { + return; // No buttons to draw + } + + // Draw active buttons + let positionOffset = 0; + for (const [isActive, renderFunc] of buttonFunctions) { + if (isActive) { + renderFunc( + ctx, + BASE_X_POSITION - positionOffset * BUTTON_SPACING, + 835 + ); + positionOffset++; + } + } } +/** + * Determines which Chi option was clicked + * @returns The value of the clicked Chi option or -1 if no option was clicked + */ export function clickChii( - x: number, - y: number, - chiis: Array> + x: number, + y: number, + chiis: Array> ): number { - let xmin = 960 - (chiis.length + 1) * 120; - let inside = 838 < y && y < 888; - let q = Math.floor((x - xmin) / 120); - let r = (x - xmin) - 120 * q; - if ( - q >= 0 && - q < (chiis.length + 1) && - r > 10 && - inside - ) { - return q === chiis.length ? 0 : chiis[q][0].getValue(); - } - return -1; + if (chiis.length === 0) { + return -1; + } + + // Calculate starting X position based on number of options + const xmin = 960 - (chiis.length + 1) * BUTTON_SPACING; + + // Check if Y coordinate is within button area + const isYInButtonArea = BUTTON_AREA_Y_MIN < y && y < BUTTON_AREA_Y_MAX; + if (!isYInButtonArea) { + return -1; + } + + // Calculate which option was clicked + const optionIndex = Math.floor((x - xmin) / BUTTON_SPACING); + const xOffset = (x - xmin) - BUTTON_SPACING * optionIndex; + + if ( + optionIndex >= 0 && + optionIndex < (chiis.length + 1) && + xOffset > BUTTON_MARGIN + ) { + // Return 0 for "back" button or the value of the selected Chi option + return optionIndex === chiis.length ? 0 : chiis[optionIndex][0].getValue(); + } + + return -1; } +/** + * Draw Chi options on the canvas + */ export function drawChiis( - ctx: CanvasRenderingContext2D, - chiis: Array> + ctx: CanvasRenderingContext2D, + chiis: Array> ): void { - chiis.reverse(); - const r = 8; - const w = 110; - const h = 50; - button(ctx, 850, 835, r, w, h, "#FF9030"); - ctx.fillStyle = "black"; - ctx.font = "30px garamond"; - ctx.fillText("Retour", 850 + w * 0.1, 835 + h/2 * 1.3); - - let dx = 1; - for (let i = 0; i < chiis.length; i++) { - drawOneChii( - ctx, - 850 - dx * 120, - 835, - chiis[i] - ); - dx++; - } + // Create a copy to avoid modifying the original array + const chiiOptions = [...chiis].reverse(); + + // Draw "back" button + renderButton(ctx, BASE_X_POSITION, 835, BUTTON_STYLES.back); + + // Draw Chi options + let positionOffset = 1; + for (const tiles of chiiOptions) { + drawOneChii( + ctx, + BASE_X_POSITION - positionOffset * BUTTON_SPACING, + 835, + tiles + ); + positionOffset++; + } } +/** + * Draw a single Chi option + */ function drawOneChii( - ctx: CanvasRenderingContext2D, - x: number, - y: number, - tiles: Array + ctx: CanvasRenderingContext2D, + x: number, + y: number, + tiles: Array ): void { - const r = 8; - const w = 110; - const h = 50; - const dx = 32; - const x0 = x + 7; - const y0 = y + 5; - button(ctx, x, y, r, w, h, "#FFCC33"); - tiles[0].drawTile( - ctx, - x0, - y0, - 0.4, - false, - 0, - false, - false - ); - tiles[1].drawTile( - ctx, - x0 + dx, - y0, - 0.4, - false, - 0, - false, - false - ); - tiles[2].drawTile( - ctx, - x0 + 2 * dx, - y0, - 0.4, - false, - 0, - false, - false - ); + const tileOffset = 32; + const tileStartX = x + 7; + const tileStartY = y + 5; + + // Draw the button background + drawButtonShape(ctx, x, y, BUTTON_RADIUS, BUTTON_WIDTH, BUTTON_HEIGHT, BUTTON_STYLES.chii.color); + + // Draw the tiles + for (let i = 0; i < tiles.length; i++) { + tiles[i].drawTile( + ctx, + tileStartX + tileOffset * i, + tileStartY, + 0.4, + false, + 0, + false, + false + ); + } } -function button( - ctx: CanvasRenderingContext2D, - x: number, - y: number, - r: number, - w: number, - h: number, - color: string -): void { - ctx.fillStyle = color; - ctx.beginPath(); - - ctx.moveTo(x + r, y); - ctx.lineTo(x + w - r, y); - ctx.quadraticCurveTo(x + w, y, x + w, y + r); - ctx.lineTo(x + w, y + h - r); - ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); - ctx.lineTo(x + r, y + h); - ctx.quadraticCurveTo(x, y + h, x, y + h - r); - ctx.lineTo(x, y + r); - ctx.quadraticCurveTo(x, y, x + r, y); - - ctx.fill(); - - ctx.fillStyle = "#606060"; - ctx.stroke(); +/** + * Get the list of active button action values + */ +function getActiveButtons( + chii: boolean, + pon: boolean, + kan: boolean, + ron: boolean, + tsumo: boolean +): number[] { + const buttonConfigs: [boolean, number][] = [ + [tsumo, BUTTON_STYLES.tsumo.action], + [ron, BUTTON_STYLES.ron.action], + [kan, BUTTON_STYLES.kan.action], + [pon, BUTTON_STYLES.pon.action], + [chii, BUTTON_STYLES.chii.action] + ]; + + const activeButtons = buttonConfigs + .filter(([isActive]) => isActive) + .map(([, action]) => action); + + // Add pass button if any other buttons are active + if (activeButtons.length > 0) { + activeButtons.push(BUTTON_STYLES.pass.action); + } + + return activeButtons; } -function buttonPass( - ctx: CanvasRenderingContext2D, - x: number, - y: number +/** + * Render a button with text + */ +function renderButton( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + config: ButtonConfig ): void { - const r = 8; - const w = 110; - const h = 50; - // button(ctx, x, y, r, w, h, "#FFAC4D"); - button(ctx, x, y, r, w, h, "#FF9030"); - ctx.fillStyle = "black"; - ctx.font = "30px garamond"; - ctx.fillText("Ignorer", x + w * 0.1, y + h/2 * 1.3); + drawButtonShape(ctx, x, y, BUTTON_RADIUS, BUTTON_WIDTH, BUTTON_HEIGHT, config.color); + + // Add text to the button + ctx.fillStyle = "black"; + ctx.font = "30px garamond"; + + // Center text based on its length + const textXPosition = x + BUTTON_WIDTH * (0.5 - config.text.length * 0.025); + const textYPosition = y + BUTTON_HEIGHT/2 * 1.3; + + ctx.fillText(config.text, textXPosition, textYPosition); } -function buttonPon( - ctx: CanvasRenderingContext2D, - x: number, - y: number +/** + * Draw a rounded rectangle button shape + */ +function drawButtonShape( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + radius: number, + width: number, + height: number, + color: string ): void { - const r = 8; - const w = 110; - const h = 50; - button(ctx, x, y, r, w, h, "#FFCC33"); - ctx.fillStyle = "black"; - ctx.font = "30px garamond"; - ctx.fillText("Pon",x + w * 0.28, y + h/2 * 1.3); -} + ctx.fillStyle = color; + ctx.beginPath(); -function buttonChii( - ctx: CanvasRenderingContext2D, - x: number, - y: number -): void { - const r = 8; - const w = 110; - const h = 50; - button(ctx, x, y, r, w, h, "#FFCC33"); - ctx.fillStyle = "black"; - ctx.font = "30px garamond"; - ctx.fillText("Chii",x + w * 0.25, y + h/2 * 1.3); -} + // Top right corner + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + + // Bottom right corner + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + + // Bottom left corner + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + + // Top left corner + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); -function buttonKan( - ctx: CanvasRenderingContext2D, - x: number, - y: number -): void { - const r = 8; - const w = 110; - const h = 50; - button(ctx, x, y, r, w, h, "#FFCC33"); - ctx.fillStyle = "black"; - ctx.font = "30px garamond"; - ctx.fillText("Kan",x + w * 0.28, y + h/2 * 1.3); -} + ctx.fill(); -function buttonRon( - ctx: CanvasRenderingContext2D, - x: number, - y: number -): void { - const r = 8; - const w = 110; - const h = 50; - button(ctx, x, y, r, w, h, "#FF3060"); - ctx.fillStyle = "black"; - ctx.font = "30px garamond"; - ctx.fillText("Ron",x + w * 0.28, y + h/2 * 1.3); -} - -function buttonTsumo( - ctx: CanvasRenderingContext2D, - x: number, - y: number -): void { - const r = 8; - const w = 110; - const h = 50; - button(ctx, x, y, r, w, h, "#FF3060"); - ctx.fillStyle = "black"; - ctx.font = "30px garamond"; - ctx.fillText("Tsumo",x + w * 0.13, y + h/2 * 1.3); + // Add border + ctx.fillStyle = "#606060"; + ctx.stroke(); } diff --git a/src/deck.ts b/src/deck.ts index 6471aab..b9abaff 100644 --- a/src/deck.ts +++ b/src/deck.ts @@ -1,152 +1,293 @@ -import { Tile } from "./tile" -import { Hand } from "./hand" +import { Tile } from "./tile"; +import { Hand } from "./hand"; + +type TileKey = `${number}-${number}`; export class Deck { - private tiles: Array; + private tiles: Tile[]; + private tileIndexMap: Map; // Fast lookup for find() and count() - public constructor(allowRed: boolean) { - this.tiles = []; - this.initTiles(allowRed); - } + public constructor(allowRed: boolean = false) { + this.tiles = []; + this.tileIndexMap = new Map(); + this.initTiles(allowRed); + } - public displayFamilies( - ctx: CanvasRenderingContext2D, - x: number, - y: number, - size: number, - xOffset: number = 0, - yOffset: number = 0 - ): void { - let posX = x; - let posY = y; - for (let i = 1; i < 6; i++) { - if (i < 4) { // famille - for (let j = 1; j < 10; j++) { - const tile = this.find(i, j) as NonNullable; - tile.drawTile(ctx, posX, posY, size, false, 0, false); - posX += (75 + xOffset) * size; - } - posX = x; - posY += (100 + yOffset) * size; - } else if (i === 4) { //vent - for (let j = 1; j < 5; j++) { - const tile = this.find(i, j) as NonNullable; - tile.drawTile(ctx, posX, posY, size); - posX += (75 + xOffset) * size; - } - posX = x; - posY += (100 + yOffset) * size; - } else if (i === 5) { //vent - for (let j = 1; j < 4; j++) { - const tile = this.find(i, j) as NonNullable; - tile.drawTile(ctx, posX, posY, size); - posX += (75 + xOffset) * size; - } - } - } - } + /** + * Displays all tile families on the canvas + */ + public displayFamilies( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + size: number, + xOffset: number = 0, + yOffset: number = 0 + ): void { + let posX = x; + let posY = y; + + // Define tile layouts for each family + const familyLayouts = [ + { family: 1, start: 1, end: 9 }, // First suit + { family: 2, start: 1, end: 9 }, // Second suit + { family: 3, start: 1, end: 9 }, // Third suit + { family: 4, start: 1, end: 4 }, // Winds + { family: 5, start: 1, end: 3 } // Dragons + ]; - public length(): number { - return this.tiles.length; - } + for (const layout of familyLayouts) { + for (let j = layout.start; j <= layout.end; j++) { + const tile = this.find(layout.family, j); + if (tile) { + tile.drawTile(ctx, posX, posY, size, false, 0, false); + posX += (75 + xOffset) * size; + } + } + posX = x; + posY += (100 + yOffset) * size; + } + } - public pop(): Tile { - if (this.tiles.length === 0) { - } - return this.tiles.pop() as NonNullable; - } + /** + * Returns the number of tiles in the deck + */ + public length(): number { + return this.tiles.length; + } - public push(tile: Tile) { - this.tiles.push(tile); - } + /** + * Removes and returns the last tile from the deck + * @throws Error if the deck is empty + */ + public pop(): Tile { + if (this.tiles.length === 0) { + throw new Error("Cannot pop from an empty deck"); + } + + const tile = this.tiles.pop()!; + // Update the index map + const key = this.getTileKey(tile.getFamily(), tile.getValue()); + const indices = this.tileIndexMap.get(key); + + if (indices && indices.length > 0) { + indices.pop(); // Remove the last index + if (indices.length === 0) { + this.tileIndexMap.delete(key); + } else { + this.tileIndexMap.set(key, indices); + } + } + + return tile; + } - public find(family: number, value: number): Tile | undefined { - let n = undefined; - for (let i = 0; i < this.tiles.length; i++) { - if ( - this.tiles[i].getFamily() === family && - this.tiles[i].getValue() === value - ) { - n = i; - } - } - if (n !== undefined) { - [this.tiles[n as NonNullable], this.tiles[0]] = [this.tiles[0], this.tiles[n as NonNullable]]; - return this.tiles.shift(); - } else { - return undefined; - } - } + /** + * Adds a tile to the deck + */ + public push(tile: Tile): void { + const index = this.tiles.length; + this.tiles.push(tile); + + // Update the index map + const key = this.getTileKey(tile.getFamily(), tile.getValue()); + const indices = this.tileIndexMap.get(key) || []; + indices.push(index); + this.tileIndexMap.set(key, indices); + } - public count(family: number, value: number): number { - let n = 0; - for (let i = 0; i < this.tiles.length; i++) { - if ( - this.tiles[i].getFamily() === family && - this.tiles[i].getValue() === value - ) { - n++; - } - } - return n; - } + /** + * Finds and removes a specific tile from the deck + */ + public find(family: number, value: number): Tile | undefined { + const key = this.getTileKey(family, value); + const indices = this.tileIndexMap.get(key); + + if (!indices || indices.length === 0) { + return undefined; + } + + // Get the first occurrence + const index = indices[0]; + if (index >= this.tiles.length) { + // Handle potential out-of-sync errors + this.rebuildIndexMap(); + return this.find(family, value); + } + + // Swap with the first element for efficient removal + [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]]; + + // Update indices in the map + this.updateIndicesAfterSwap(0, index); + + // Remove and return the tile + const tile = this.tiles.shift(); + + // Update all indices after shift + this.decrementIndicesAfterShift(); + + return tile; + } - public shuffle(): undefined { - let newArray: Array = []; - while (this.tiles.length > 0) { - let n = Math.floor(Math.random() * this.tiles.length); - [this.tiles[n], this.tiles[0]] = [this.tiles[0], this.tiles[n]]; - newArray.push(this.tiles.shift() as NonNullable); - } - this.tiles = newArray; - } + /** + * Counts the number of tiles with specific family and value + */ + public count(family: number, value: number): number { + const key = this.getTileKey(family, value); + const indices = this.tileIndexMap.get(key); + return indices ? indices.length : 0; + } - public getRandomHand(): Hand { - let hand = new Hand(); - this.shuffle(); - for (let i = 0; i < 13; i++) { - hand.push(this.pop()); - } - return hand; - } + /** + * Shuffles the deck using Fisher-Yates algorithm + */ + public shuffle(): void { + // Fisher-Yates shuffle algorithm + for (let i = this.tiles.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]]; + } + + // Rebuild the index map after shuffling + this.rebuildIndexMap(); + } - public cleanup(): void { - this.tiles.forEach(tile => tile.cleanup()); - this.tiles = []; - } + /** + * Creates a random hand from the deck + */ + public getRandomHand(): Hand { + const hand = new Hand(); + this.shuffle(); + + // Handle case where deck doesn't have enough tiles + if (this.tiles.length < 13) { + throw new Error("Not enough tiles in deck to create a hand"); + } + + for (let i = 0; i < 13; i++) { + hand.push(this.pop()); + } + + return hand; + } - public async preload(): Promise { - for (let i = 0; i < this.tiles.length; i++) { - await this.tiles[i].preloadImg(); - } - } + /** + * Cleans up resources used by tiles and empties the deck + */ + public cleanup(): void { + this.tiles.forEach(tile => tile.cleanup()); + this.tiles = []; + this.tileIndexMap.clear(); + } - private initTiles(allowRed: boolean): undefined { - for (let i = 1; i < 6; i++) { - if (i < 4) { // famille - for (let j = 1; j < 10; j++) { - this.tiles.push(new Tile(i, j, false)); - this.tiles.push(new Tile(i, j, false)); - this.tiles.push(new Tile(i, j, false)); - if (j === 5 && allowRed) { - this.tiles.push(new Tile(i, j, true)); - } else { - this.tiles.push(new Tile(i, j, false)); - } - } - } else if (i === 4) { // vent - for (let j = 1; j < 5; j++) { - for (let k = 0; k < 4; k++) { - this.tiles.push(new Tile(i, j, false)); - } - } - } else if (i === 5) { // dragon - for (let j = 1; j < 4; j++) { - for (let k = 0; k < 4; k++) { - this.tiles.push(new Tile(i, j, false)); - } - } - } - } - } + /** + * Preloads all tile images + */ + public async preload(): Promise { + const preloadPromises = this.tiles.map(tile => tile.preloadImg()); + await Promise.all(preloadPromises); + } + + /** + * Creates a unique key for each tile type + */ + private getTileKey(family: number, value: number): TileKey { + return `${family}-${value}`; + } + + /** + * Updates the index map after swapping two tiles + */ + private updateIndicesAfterSwap(index1: number, index2: number): void { + if (index1 === index2) return; + + const tile1 = this.tiles[index1]; + const tile2 = this.tiles[index2]; + + const key1 = this.getTileKey(tile1.getFamily(), tile1.getValue()); + const key2 = this.getTileKey(tile2.getFamily(), tile2.getValue()); + + const indices1 = this.tileIndexMap.get(key1) || []; + const indices2 = this.tileIndexMap.get(key2) || []; + + // Update indices + const idx1 = indices1.indexOf(index2); + const idx2 = indices2.indexOf(index1); + + if (idx1 !== -1) indices1[idx1] = index1; + if (idx2 !== -1) indices2[idx2] = index2; + + this.tileIndexMap.set(key1, indices1); + this.tileIndexMap.set(key2, indices2); + } + + /** + * Decrements all indices after a shift operation + */ + private decrementIndicesAfterShift(): void { + for (const [key, indices] of this.tileIndexMap.entries()) { + this.tileIndexMap.set( + key, + indices + .filter(idx => idx !== 0) // Remove the index 0 that was shifted + .map(idx => (idx > 0 ? idx - 1 : idx)) // Decrement all indices + ); + + // Clean up empty entries + if (this.tileIndexMap.get(key)?.length === 0) { + this.tileIndexMap.delete(key); + } + } + } + + /** + * Rebuilds the entire index map from scratch + */ + private rebuildIndexMap(): void { + this.tileIndexMap.clear(); + + for (let i = 0; i < this.tiles.length; i++) { + const tile = this.tiles[i]; + const key = this.getTileKey(tile.getFamily(), tile.getValue()); + const indices = this.tileIndexMap.get(key) || []; + indices.push(i); + this.tileIndexMap.set(key, indices); + } + } + + /** + * Initializes the deck with all tiles + */ + private initTiles(allowRed: boolean): void { + // Create suits (families 1-3) + for (let family = 1; family <= 3; family++) { + for (let value = 1; value <= 9; value++) { + // Each value appears 4 times in a suit + const isRedFive = value === 5 && allowRed; + + // Add 3 regular tiles + for (let i = 0; i < 3; i++) { + this.push(new Tile(family, value, false)); + } + + // Add the 4th tile (potentially red five) + this.push(new Tile(family, value, isRedFive)); + } + } + + // Create winds (family 4) + for (let value = 1; value <= 4; value++) { + for (let i = 0; i < 4; i++) { + this.push(new Tile(4, value, false)); + } + } + + // Create dragons (family 5) + for (let value = 1; value <= 3; value++) { + for (let i = 0; i < 4; i++) { + this.push(new Tile(5, value, false)); + } + } + } } diff --git a/src/display/dp2.ts b/src/display/dp2.ts index 97709e7..6f452a8 100644 --- a/src/display/dp2.ts +++ b/src/display/dp2.ts @@ -1,144 +1,256 @@ import { Deck } from "../deck"; -import { Hand } from "../hand" +import { Hand } from "../hand"; declare global { - interface Window { - cleanup: () => void; - } -} -export {} - -const canvas = document.getElementById("myCanvas") as HTMLCanvasElement; - -async function preloadDeck(deck: Deck) { - await deck.preload(); + interface Window { + cleanup: () => void; + } } -async function display () { - if (canvas) { - const ctx = canvas.getContext("2d") as NonNullable; +export {}; - if (ctx) { - // double buffering - const offScreenCanvas = document.createElement('canvas') as HTMLCanvasElement; - offScreenCanvas.width = canvas.width; - offScreenCanvas.height = canvas.height; - const offScreenCtx = offScreenCanvas.getContext('2d') as NonNullable; - - //animation parameter - let lastTime = 0; - const FPS = 30; - const interval = 1000 / FPS; - - // tuiles - let x = 100; - let y = 150; - let os = 75; - let size = 0.75; - const deck = new Deck(false); - await preloadDeck(deck); - - let hands: Array = []; - for (let i = 0; i < 4; i++) { - const hand = deck.getRandomHand(); - hand.sort(); - hands.push(hand); - } - - // interactive hand - const edeck = new Deck(false); - await edeck.preload(); - const ehand = edeck.getRandomHand(); - ehand.push(edeck.pop()); - ehand.sort(); - - let selectedTile: number|undefined = undefined; - - // function to draw - const drawCanvas = async () => { - // clean screeen - offScreenCtx.clearRect(0, 0, canvas.width, canvas.height); - - // tapis - offScreenCtx.fillStyle = "#007730"; - offScreenCtx.fillRect(50, 50, 1000, 1000); - - // texte - offScreenCtx.fillStyle = "#DFDFFF"; - offScreenCtx.font = "50px serif"; - offScreenCtx.fillText("Exemples de main:", 65, 100); - - // example hands - for (let i = 0; i < hands.length; i++) { - hands[i].drawHand(offScreenCtx, x, y + i * size * (100 + os), 5, size); - } - - // dynamic hand - ehand.isolate = true; - ehand.drawHand(offScreenCtx, x, 800, 5, size, selectedTile); - - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(offScreenCanvas, 0, 0); - } - - const animationLoop = (currentTime: number) => { - const deltaTime = currentTime - lastTime; - - if (deltaTime >= interval) { - lastTime = currentTime; - drawCanvas(); - } - requestAnimationFrame(animationLoop); - } - - // mouse event - canvas.addEventListener( - "mousemove", - (event) => { - const rect = canvas.getBoundingClientRect(); - const mouseX = event.clientX - rect.left - x; - const mouseY = event.clientY - rect.top; - - let q = Math.floor(mouseX / (80 * size)); - let r = mouseX - q * 80 * size; - if (r <= 75 && q >= 0 && q < 14 && mouseY >= 800 && mouseY <= 800 + 100*size) { - selectedTile = q; - } else { - selectedTile = undefined; - } - } - ); - canvas.addEventListener( - "mousedown", - (event) => { - if (selectedTile !== undefined) { - edeck.push(ehand.eject(selectedTile)); - edeck.shuffle(); - ehand.sort(); - ehand.push(edeck.pop()); - } - } - ); - - requestAnimationFrame(animationLoop); - - window.cleanup = () => { - deck.cleanup(); - hands.forEach(hand => hand.cleanup()); - hands = []; - edeck.cleanup(); - ehand.cleanup(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - offScreenCtx.clearRect(0, 0, offScreenCanvas.width, offScreenCanvas.height); - selectedTile = undefined; - } - - } else { - console.error("Impossible d'obtenir le contexte du canvas."); - } - } else { - console.error("Canvas introuvable dans le DOM."); - } +class RiichiDisplay { + private canvas: HTMLCanvasElement; + private ctx: CanvasRenderingContext2D; + private offScreenCanvas: HTMLCanvasElement; + private offScreenCtx: CanvasRenderingContext2D; + + private deck: Deck; + private hands: Hand[] = []; + private edeck: Deck; + private ehand: Hand; + + private selectedTile: number | undefined = undefined; + private animationFrameId: number | null = null; + private isDirty: boolean = true; + + // Constants + private readonly FPS: number = 30; + private readonly INTERVAL: number = 1000 / this.FPS; + private readonly X: number = 100; + private readonly Y: number = 150; + private readonly OS: number = 75; + private readonly SIZE: number = 0.75; + private readonly TILE_WIDTH: number = 80 * this.SIZE; + private readonly MAX_TILES: number = 14; + + // Cache for mouse hit detection + private tileRects: Array<{x: number, y: number, width: number, height: number}> = []; + + constructor() { + const canvas = document.getElementById("myCanvas") as HTMLCanvasElement; + if (!canvas) { + throw new Error("Canvas introuvable dans le DOM."); + } + + this.canvas = canvas; + const ctx = canvas.getContext("2d"); + if (!ctx) { + throw new Error("Impossible d'obtenir le contexte du canvas."); + } + this.ctx = ctx; + + // Create off-screen canvas for double buffering + this.offScreenCanvas = document.createElement('canvas'); + this.offScreenCanvas.width = canvas.width; + this.offScreenCanvas.height = canvas.height; + const offCtx = this.offScreenCanvas.getContext('2d'); + if (!offCtx) { + throw new Error("Impossible d'obtenir le contexte du canvas hors écran."); + } + this.offScreenCtx = offCtx; + + // Initialize decks + this.deck = new Deck(false); + this.edeck = new Deck(false); + + // Initialize with empty hand (will be populated after preload) + this.ehand = new Hand(); + + // Set up event listeners + this.setupEventListeners(); + + // Calculate tile hit areas once + this.calculateTileHitAreas(); + } + + private calculateTileHitAreas(): void { + this.tileRects = []; + for (let i = 0; i < this.MAX_TILES; i++) { + this.tileRects.push({ + x: this.X + i * this.TILE_WIDTH, + y: 800, + width: 75, + height: 100 * this.SIZE + }); + } + } + + private setupEventListeners(): void { + this.canvas.addEventListener("mousemove", this.handleMouseMove.bind(this)); + this.canvas.addEventListener("mousedown", this.handleMouseDown.bind(this)); + } + + private handleMouseMove(event: MouseEvent): void { + const rect = this.canvas.getBoundingClientRect(); + const mouseX = event.clientX - rect.left; + const mouseY = event.clientY - rect.top; + + // Check if cursor is over any tile using pre-calculated hit areas + const oldSelectedTile = this.selectedTile; + this.selectedTile = undefined; + + for (let i = 0; i < this.tileRects.length; i++) { + const tileRect = this.tileRects[i]; + if ( + mouseX >= tileRect.x && + mouseX <= tileRect.x + tileRect.width && + mouseY >= tileRect.y && + mouseY <= tileRect.y + tileRect.height + ) { + this.selectedTile = i; + break; + } + } + + // Only mark as dirty if selection changed + if (oldSelectedTile !== this.selectedTile) { + this.isDirty = true; + } + } + + private handleMouseDown(): void { + if (this.selectedTile !== undefined) { + this.edeck.push(this.ehand.eject(this.selectedTile)); + this.edeck.shuffle(); + this.ehand.sort(); + this.ehand.push(this.edeck.pop()); + this.isDirty = true; + } + } + + public async initialize(): Promise { + // Preload all assets in parallel + await Promise.all([ + this.deck.preload(), + this.edeck.preload() + ]); + + // Generate sample hands + for (let i = 0; i < 4; i++) { + const hand = this.deck.getRandomHand(); + hand.sort(); + this.hands.push(hand); + } + + // Initialize interactive hand + this.ehand = this.edeck.getRandomHand(); + this.ehand.push(this.edeck.pop()); + this.ehand.sort(); + + // Initial draw + this.drawCanvas(); + + // Start animation loop + this.startAnimationLoop(); + } + + private drawCanvas(): void { + // Only redraw if something changed (dirty flag) + if (!this.isDirty) return; + + const ctx = this.offScreenCtx; + + // Clear canvas + ctx.clearRect(0, 0, this.offScreenCanvas.width, this.offScreenCanvas.height); + + // Draw background + ctx.fillStyle = "#007730"; + ctx.fillRect(50, 50, 1000, 1000); + + // Draw title + ctx.fillStyle = "#DFDFFF"; + ctx.font = "50px serif"; + ctx.fillText("Exemples de main:", 65, 100); + + // Draw example hands + for (let i = 0; i < this.hands.length; i++) { + this.hands[i].drawHand( + ctx, + this.X, + this.Y + i * this.SIZE * (100 + this.OS), + 5, + this.SIZE + ); + } + + // Draw interactive hand + this.ehand.isolate = true; + this.ehand.drawHand( + ctx, + this.X, + 800, + 5, + this.SIZE, + this.selectedTile + ); + + // Flip double buffer + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.ctx.drawImage(this.offScreenCanvas, 0, 0); + + // Reset dirty flag + this.isDirty = false; + } + + private startAnimationLoop(): void { + let lastTime = 0; + + const animationLoop = (currentTime: number) => { + const deltaTime = currentTime - lastTime; + + if (deltaTime >= this.INTERVAL) { + lastTime = currentTime - (deltaTime % this.INTERVAL); + this.drawCanvas(); + } + + this.animationFrameId = requestAnimationFrame(animationLoop); + }; + + this.animationFrameId = requestAnimationFrame(animationLoop); + } + + public cleanup(): void { + // Cancel animation loop + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + + // Clean up resources + this.deck.cleanup(); + this.hands.forEach(hand => hand.cleanup()); + this.hands = []; + this.edeck.cleanup(); + this.ehand.cleanup(); + + // Clear canvases + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.offScreenCtx.clearRect(0, 0, this.offScreenCanvas.width, this.offScreenCanvas.height); + + // Reset state + this.selectedTile = undefined; + this.isDirty = true; + } } -display(); +// Initialize and start +const riichiDisplay = new RiichiDisplay(); +riichiDisplay.initialize().catch(error => { + console.error("Erreur lors de l'initialisation:", error); +}); + +// Expose cleanup function for window +window.cleanup = () => { + riichiDisplay.cleanup(); +}; diff --git a/src/display/dp3.ts b/src/display/dp3.ts index 7d25b9b..72af497 100644 --- a/src/display/dp3.ts +++ b/src/display/dp3.ts @@ -1,6 +1,6 @@ import { Deck } from "../deck"; import { Hand } from "../hand"; -import { Group } from "../group" +import { Group } from "../group"; // Configuration globale const CANVAS_ID = "myCanvas"; @@ -9,185 +9,373 @@ const BG_RECT = { x: 50, y: 50, w: 1000, h: 1000 }; const FPS = 30; const FRAME_INTERVAL = 1000 / FPS; -// variables globales -const DECKS: Array = []; -const HANDS: Array = []; -let selectedTile: number|undefined = undefined; - -// Optimisation des références -let animationFrameId: number; -let lastFrameTime = 0; -const callbacks: Array<() => void> = []; - -// Pré-calcul des dimensions -const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement; -const ctx = canvas.getContext("2d") as NonNullable; - -// Cache statique -const staticCanvas = document.createElement('canvas') as HTMLCanvasElement; -const staticCtx = staticCanvas.getContext("2d") as NonNullable; -staticCanvas.width = canvas.width; -staticCanvas.height = canvas.height; - -// Pré-rendu du fond -function prerenderBackground() { - staticCtx.clearRect(0, 0, canvas.width, canvas.height); - staticCtx.fillStyle = BG_COLOR; - staticCtx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h); -}; - -function drawFrame() { - if (!ctx) return; - - // Effacement intelligent (uniquement la zone nécessaire) - prerenderBackground(); - - // Ici viendrait le dessin des éléments dynamiques - // Par exemple: - // drawDeck(); - // drawHands(); - let x = 300; - let y = 150; - let xos = 250; - let yos = 100; - let size = 0.75; - - staticCtx.fillStyle = "#DFDFFF"; - staticCtx.font = "50px serif"; - - staticCtx.fillText("Chii:", 75, y + 100 * size - 5); - HANDS[0].drawHand(staticCtx, x, y, 5, 0.75); // chii - HANDS[1].drawHand(staticCtx, x + (75+xos)*size, y, 5, 0.75); - - staticCtx.fillText("Pon:", 75, y + (100+yos)*size + 100 * size - 5); - HANDS[2].drawHand(staticCtx, x, y + (100+yos)*size, 5, 0.75); // pon - HANDS[3].drawHand(staticCtx, x + (75+xos)*size, y + (100+yos)*size, 5, 0.75); - HANDS[4].drawHand(staticCtx, x + 2*(75+xos)*size, y + 2*(100+yos)*size, 5, 0.75); - - staticCtx.fillText("Invalide:", 75, y + 2*(100+yos)*size + 100 * size - 5); - HANDS[5].drawHand(staticCtx, x, y + 2*(100+yos)*size, 5, 0.75); // wrong - HANDS[6].drawHand(staticCtx, x + (75+xos)*size, y + 2*(100+yos)*size, 5, 0.75); - HANDS[7].drawHand(staticCtx, x + 2*(75+xos)*size, y + 2*(100+yos)*size, 5, 0.75); - - HANDS[8].isolate = true; - HANDS[8].drawHand(staticCtx, 100, 800, 5, size, selectedTile); - - let groups = HANDS[8].toGroup(); - if (groups !== undefined) { - staticCtx.fillStyle = "#FF0000"; - staticCtx.font = "50px serif"; - staticCtx.fillText("Tous les groupes sont formés !", 100, 750); - } - groups = []; - - // Dessin du cache statique - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(staticCanvas, 0, 0); -} - -function animationLoop(currentTime: number) { - animationFrameId = requestAnimationFrame(animationLoop); - - const deltaTime = currentTime - lastFrameTime; - if (deltaTime < FRAME_INTERVAL) return; - - lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL); - drawFrame(); -} - -function initEventListeners() { - const handlers = { - mousemove: (e: MouseEvent) => { - let size = 0.75; - let x = 100; - // Logique de gestion du mouvement de la souris - const rect = canvas.getBoundingClientRect(); - const mouseX = e.clientX - rect.left - x; - const mouseY = e.clientY - rect.top; - - let q = Math.floor(mouseX / (80 * size)); - let r = mouseX - q * 80 * size; - if (r <= 75 && q >= 0 && q < 14 && mouseY >= 800 && mouseY <= 800 + 100*size) { - selectedTile = q; - } else { - selectedTile = undefined; - } - }, - mousedown: (e: MouseEvent) => { - // Logique de gestion du clic de souris - if (selectedTile !== undefined) { - DECKS[0].push(HANDS[8].eject(selectedTile)); - DECKS[0].shuffle(); - HANDS[8].sort(); - HANDS[8].push(DECKS[0].pop()); - } - } - }; - - callbacks.push(() => { - canvas.removeEventListener('mousemove', handlers.mousemove); - canvas.removeEventListener('mousedown', handlers.mousedown); - }); - - canvas.addEventListener('mousemove', handlers.mousemove); - canvas.addEventListener('mousedown', handlers.mousedown); -} - -async function preloadDeck(deck: Deck) { - await deck.preload(); -} -async function preloadHand(hand: Hand) { - await hand.preload(); -} - -export function cleanup() { - cancelAnimationFrame(animationFrameId); - callbacks.forEach(fn => fn()); -} - -export async function initDisplay() { - if (!ctx) { - console.error("Context canvas indisponible"); - return; +/** + * Classe RiichiDisplay responsable de la gestion de l'affichage du jeu Riichi Mahjong + */ +class RiichiDisplay { + // Canvas principal et contexte + private canvas: HTMLCanvasElement; + private ctx: CanvasRenderingContext2D; + + // Canvas pour le double-buffering + private staticCanvas: HTMLCanvasElement; + private staticCtx: CanvasRenderingContext2D; + + // Ressources du jeu + private decks: Array = []; + private hands: Array = []; + + // État de l'interface + private selectedTile: number | undefined = undefined; + private isDirty: boolean = true; + + // Animation et événements + private animationFrameId: number | null = null; + private lastFrameTime: number = 0; + private cleanupCallbacks: Array<() => void> = []; + + // Constantes pour le rendu + private readonly BASE_X: number = 300; + private readonly BASE_Y: number = 150; + private readonly X_OFFSET: number = 250; + private readonly Y_OFFSET: number = 100; + private readonly INTERACTIVE_X: number = 100; + private readonly INTERACTIVE_Y: number = 800; + private readonly SIZE: number = 0.75; + private readonly TILE_WIDTH: number = 80 * this.SIZE; + private readonly MAX_TILES: number = 14; + + // Zones de détection pour l'interaction souris + private tileRects: Array<{ x: number, y: number, width: number, height: number }> = []; + + constructor(canvasId: string = CANVAS_ID) { + // Initialisation du canvas + const canvas = document.getElementById(canvasId) as HTMLCanvasElement; + if (!canvas) { + throw new Error(`Canvas avec ID '${canvasId}' introuvable`); } - - // Préchargement des ressources si nécessaire - // const deck = new Deck(); - // await preloadDeck(deck); - DECKS.push( - new Deck(false) - ); - HANDS.push( - new Hand("s1s2s3"), - new Hand("m2m3m4"), - new Hand("p5p5p5"), - new Hand("w2w2w2"), - new Hand("d3d3d3"), - new Hand("s4p5m6"), - new Hand("m9s9p9"), - new Hand("d1d2d3"), - DECKS[0].getRandomHand() - ); - await Promise.all(DECKS.map(d => preloadDeck(d))); - await Promise.all(HANDS.map(h => preloadHand(h))); - - HANDS[8].push(DECKS[0].pop()); - HANDS[8].sort(); + this.canvas = canvas; - initEventListeners(); - requestAnimationFrame(animationLoop); + // Récupération du contexte 2D + const ctx = canvas.getContext("2d"); + if (!ctx) { + throw new Error("Impossible d'obtenir le contexte 2D du canvas"); + } + this.ctx = ctx; + + // Initialisation du canvas pour le double-buffering + this.staticCanvas = document.createElement('canvas'); + this.staticCanvas.width = canvas.width; + this.staticCanvas.height = canvas.height; + + const staticCtx = this.staticCanvas.getContext("2d"); + if (!staticCtx) { + throw new Error("Impossible d'obtenir le contexte du canvas statique"); + } + this.staticCtx = staticCtx; + + // Pré-calcul des zones de détection + this.calculateTileHitAreas(); + } + + /** + * Pré-calcule les zones de détection pour l'interaction souris + */ + private calculateTileHitAreas(): void { + this.tileRects = []; + for (let i = 0; i < this.MAX_TILES; i++) { + this.tileRects.push({ + x: this.INTERACTIVE_X + i * this.TILE_WIDTH, + y: this.INTERACTIVE_Y, + width: 75, + height: 100 * this.SIZE + }); + } + } + + /** + * Pré-rendu du fond statique + */ + private prerenderBackground(): void { + this.staticCtx.clearRect(0, 0, this.staticCanvas.width, this.staticCanvas.height); + this.staticCtx.fillStyle = BG_COLOR; + this.staticCtx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h); + } + + /** + * Dessine une frame complète + */ + private drawFrame(): void { + // Vérifier si un redessinage est nécessaire + if (!this.isDirty) return; + + // Pré-rendu du fond statique + this.prerenderBackground(); + + // Dessin des éléments statiques et dynamiques + this.drawHandsAndLabels(); + + // Affichage des informations sur les groupes + this.checkAndDisplayGroups(); + + // Copie du canvas statique vers le canvas principal + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.ctx.drawImage(this.staticCanvas, 0, 0); + + // Réinitialisation du flag de modification + this.isDirty = false; + } + + /** + * Dessine les mains et leurs étiquettes + */ + private drawHandsAndLabels(): void { + const ctx = this.staticCtx; + + // Configurer le style de texte + ctx.fillStyle = "#DFDFFF"; + ctx.font = "50px serif"; + + // Dessiner les mains "Chii" + ctx.fillText("Chii:", 75, this.BASE_Y + 100 * this.SIZE - 5); + this.hands[0].drawHand(ctx, this.BASE_X, this.BASE_Y, 5, this.SIZE); + this.hands[1].drawHand(ctx, this.BASE_X + (75 + this.X_OFFSET) * this.SIZE, this.BASE_Y, 5, this.SIZE); + + // Dessiner les mains "Pon" + ctx.fillText("Pon:", 75, this.BASE_Y + (100 + this.Y_OFFSET) * this.SIZE + 100 * this.SIZE - 5); + this.hands[2].drawHand(ctx, this.BASE_X, this.BASE_Y + (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE); + 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); + + // Dessiner les mains "Invalide" + ctx.fillText("Invalide:", 75, this.BASE_Y + 2 * (100 + this.Y_OFFSET) * this.SIZE + 100 * this.SIZE - 5); + this.hands[5].drawHand(ctx, this.BASE_X, this.BASE_Y + 2 * (100 + this.Y_OFFSET) * this.SIZE, 5, this.SIZE); + 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); + 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); + + // Main supplémentaire (position spéciale) + 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); + + // Main interactive + this.hands[8].isolate = true; + this.hands[8].drawHand(ctx, this.INTERACTIVE_X, this.INTERACTIVE_Y, 5, this.SIZE, this.selectedTile); + } + + /** + * Vérifie et affiche les informations sur les groupes formés + */ + private checkAndDisplayGroups(): void { + const groups = this.hands[8].toGroup(); + if (groups !== undefined) { + this.staticCtx.fillStyle = "#FF0000"; + this.staticCtx.font = "50px serif"; + this.staticCtx.fillText("Tous les groupes sont formés !", 100, 750); + } + } + + /** + * Démarre la boucle d'animation + */ + private startAnimationLoop(): void { + const animationLoop = (currentTime: number) => { + this.animationFrameId = requestAnimationFrame(animationLoop); + + const deltaTime = currentTime - this.lastFrameTime; + if (deltaTime < FRAME_INTERVAL) return; + + this.lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL); + this.drawFrame(); + }; + + this.animationFrameId = requestAnimationFrame(animationLoop); + } + + /** + * Initialise les écouteurs d'événements + */ + private initEventListeners(): void { + const mouseMoveHandler = this.handleMouseMove.bind(this); + const mouseDownHandler = this.handleMouseDown.bind(this); + + this.canvas.addEventListener('mousemove', mouseMoveHandler); + this.canvas.addEventListener('mousedown', mouseDownHandler); + + // Enregistrer les callbacks de nettoyage + this.cleanupCallbacks.push(() => { + this.canvas.removeEventListener('mousemove', mouseMoveHandler); + this.canvas.removeEventListener('mousedown', mouseDownHandler); + }); + } + + /** + * Gère le mouvement de la souris + */ + private handleMouseMove(e: MouseEvent): void { + const rect = this.canvas.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + + // Sauvegarde de l'état précédent pour détecter les changements + const previousSelectedTile = this.selectedTile; + this.selectedTile = undefined; + + // Détection optimisée avec zones pré-calculées + for (let i = 0; i < this.tileRects.length; i++) { + const tileRect = this.tileRects[i]; + if ( + mouseX >= tileRect.x && + mouseX <= tileRect.x + tileRect.width && + mouseY >= tileRect.y && + mouseY <= tileRect.y + tileRect.height + ) { + this.selectedTile = i; + break; + } + } + + // Marquer comme "dirty" uniquement si la sélection a changé + if (previousSelectedTile !== this.selectedTile) { + this.isDirty = true; + } + } + + /** + * Gère le clic de souris + */ + private handleMouseDown(): void { + if (this.selectedTile !== undefined) { + // Exécuter l'action pour la tuile sélectionnée + 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()); + + // Marquer comme "dirty" pour forcer le redessinage + this.isDirty = true; + } + } + + /** + * Précharge un deck + */ + private async preloadDeck(deck: Deck): Promise { + await deck.preload(); + } + + /** + * Précharge une main + */ + private async preloadHand(hand: Hand): Promise { + await hand.preload(); + } + + /** + * Initialise l'affichage et démarre l'application + */ + public async initialize(): Promise { + try { + // Création du deck principal + this.decks.push(new Deck(false)); + + // Création des mains prédéfinies + this.hands.push( + new Hand("s1s2s3"), // Chii 1 + new Hand("m2m3m4"), // Chii 2 + new Hand("p5p5p5"), // Pon 1 + new Hand("w2w2w2"), // Pon 2 + new Hand("d3d3d3"), // Pon supplémentaire + new Hand("s4p5m6"), // Invalide 1 + new Hand("m9s9p9"), // Invalide 2 + new Hand("d1d2d3"), // Invalide 3 + this.decks[0].getRandomHand() // Main interactive + ); + + // Préchargement parallèle des ressources + await Promise.all([ + ...this.decks.map(deck => this.preloadDeck(deck)), + ...this.hands.map(hand => this.preloadHand(hand)) + ]); + + // Configuration de la main interactive + this.hands[8].push(this.decks[0].pop()); + this.hands[8].sort(); + + // Initialisation des écouteurs d'événements + this.initEventListeners(); + + // Premier rendu + this.isDirty = true; + this.drawFrame(); + + // Démarrage de la boucle d'animation + this.startAnimationLoop(); + + } catch (error) { + console.error("Erreur d'initialisation:", error); + } + } + + /** + * Nettoie les ressources + */ + public cleanup(): void { + // Arrêter la boucle d'animation + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + + // Exécuter tous les callbacks de nettoyage + this.cleanupCallbacks.forEach(callback => callback()); + this.cleanupCallbacks = []; + + // Nettoyer les ressources des decks et des mains + this.decks.forEach(deck => deck.cleanup()); + this.hands.forEach(hand => hand.cleanup()); + + // Vider les collections + this.decks = []; + this.hands = []; + + // Effacer les canvas + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.staticCtx.clearRect(0, 0, this.staticCanvas.width, this.staticCanvas.height); + } +} + +// Instance globale pour le nettoyage +let displayInstance: RiichiDisplay | null = null; + +/** + * Fonction de nettoyage exportée + */ +export function cleanup(): void { + if (displayInstance) { + displayInstance.cleanup(); + displayInstance = null; + } +} + +/** + * Fonction d'initialisation exportée + */ +export async function initDisplay(): Promise { + try { + displayInstance = new RiichiDisplay(CANVAS_ID); + await displayInstance.initialize(); window.cleanup = cleanup; + } catch (error) { + console.error("Erreur lors de l'initialisation:", error); + } } // Déclaration globale pour TypeScript declare global { - interface Window { - cleanup: () => void; - } + interface Window { + cleanup: () => void; + } } // Initialisation automatique si le script est chargé directement if (typeof window !== 'undefined') { - initDisplay().catch(console.error); + initDisplay().catch(console.error); } - diff --git a/src/display/dp4.ts b/src/display/dp4.ts index 49f6b4c..ef889a7 100644 --- a/src/display/dp4.ts +++ b/src/display/dp4.ts @@ -1,122 +1,253 @@ import { Deck } from "../deck"; import { Hand } from "../hand"; -import { Game } from "../game" +import { Game } from "../game"; -// Configuration globale -const CANVAS_ID = "myCanvas"; -const BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 }; -var MOUSE = { x: 0, y: 0 }; -const FPS = 60; -const FRAME_INTERVAL = 1000 / FPS; - -// variables globales -const DECKS: Array = []; -const HANDS: Array = []; -var GAME: Game|undefined; - -// Optimisation des références -let animationFrameId: number; -let lastFrameTime = 0; -const callbacks: Array<() => void> = []; - -// Pré-calcul des dimensions -const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement; -const ctx = canvas.getContext("2d") as NonNullable; -canvas.width = BG_RECT.w; -canvas.height = BG_RECT.h; - -// Cache statique -const staticCanvas = document.createElement('canvas') as HTMLCanvasElement; -const staticCtx = staticCanvas.getContext("2d") as NonNullable; -staticCanvas.width = canvas.width; -staticCanvas.height = canvas.height; - -function drawFrame() { - if (!ctx) return; - GAME?.draw(MOUSE); -} - -function animationLoop(currentTime: number) { - animationFrameId = requestAnimationFrame(animationLoop); - - const deltaTime = currentTime - lastFrameTime; - if (deltaTime < FRAME_INTERVAL) return; - - lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL); - drawFrame(); -} - -function initEventListeners() { - const handlers = { - mousedown: (e: MouseEvent) => { - GAME?.click(e); - }, - mousemove: (e: MouseEvent) => { - MOUSE.x = e.x; - MOUSE.y = e.y; +class RiichiGameManager { + // Configuration globale + private readonly CANVAS_ID: string = "myCanvas"; + private readonly BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 }; + private readonly FPS: number = 60; + private readonly FRAME_INTERVAL: number = 1000 / this.FPS; + + // Singleton instance + private static instance: RiichiGameManager; + + // Canvas et contextes + private canvas: HTMLCanvasElement; + private ctx: CanvasRenderingContext2D; + private staticCanvas: HTMLCanvasElement; + private staticCtx: CanvasRenderingContext2D; + + // État du jeu + private mouse = { x: 0, y: 0 }; + private game: Game | null = null; + private decks: Array = []; + private hands: Array = []; + + // Animation et boucle de jeu + private animationFrameId: number | null = null; + private lastFrameTime: number = 0; + private isInitialized: boolean = false; + + // Nettoyage + private cleanupCallbacks: Array<() => void> = []; + + /** + * Constructeur privé pour le Singleton + */ + private constructor() { + // Initialisation du canvas principal + const canvas = document.getElementById(this.CANVAS_ID) as HTMLCanvasElement; + if (!canvas) { + throw new Error(`Canvas avec ID '${this.CANVAS_ID}' introuvable`); + } + this.canvas = canvas; + this.canvas.width = this.BG_RECT.w; + this.canvas.height = this.BG_RECT.h; + + // Récupération du contexte 2D + const ctx = canvas.getContext("2d", { alpha: false }); + if (!ctx) { + throw new Error("Impossible d'obtenir le contexte 2D du canvas"); + } + this.ctx = ctx; + + // Initialisation du canvas pour le double-buffering + this.staticCanvas = document.createElement('canvas'); + this.staticCanvas.width = canvas.width; + this.staticCanvas.height = canvas.height; + + const staticCtx = this.staticCanvas.getContext("2d", { alpha: false }); + if (!staticCtx) { + throw new Error("Impossible d'obtenir le contexte du canvas statique"); + } + this.staticCtx = staticCtx; + } + + /** + * Accès à l'instance Singleton + */ + public static getInstance(): RiichiGameManager { + if (!RiichiGameManager.instance) { + RiichiGameManager.instance = new RiichiGameManager(); + } + return RiichiGameManager.instance; + } + + /** + * Dessine une frame du jeu + */ + private drawFrame(): void { + if (this.game) { + this.game.draw(this.mouse); + } + } + + /** + * Démarre la boucle d'animation du jeu + */ + private startAnimationLoop(): void { + const animationLoop = (currentTime: number) => { + this.animationFrameId = requestAnimationFrame(animationLoop); + + const deltaTime = currentTime - this.lastFrameTime; + if (deltaTime < this.FRAME_INTERVAL) return; + + // Correction du time drift + this.lastFrameTime = currentTime - (deltaTime % this.FRAME_INTERVAL); + + // Rendu d'une frame + this.drawFrame(); + }; + + this.animationFrameId = requestAnimationFrame(animationLoop); + } + + /** + * Initialise les écouteurs d'événements + */ + private initEventListeners(): void { + // Handler pour le déplacement de la souris + const mouseMoveHandler = (e: MouseEvent) => { + // Calcul des coordonnées relatives au canvas + const rect = this.canvas.getBoundingClientRect(); + this.mouse.x = e.clientX - rect.left; + this.mouse.y = e.clientY - rect.top; + }; + + // Handler pour le clic de souris + const mouseDownHandler = (e: MouseEvent) => { + if (this.game) { + this.game.click(e); } }; - - callbacks.push(() => { - canvas.removeEventListener('mousemove', handlers.mousemove); - canvas.removeEventListener('mousedown', handlers.mousedown); + + // Enregistrement des écouteurs + this.canvas.addEventListener('mousemove', mouseMoveHandler); + this.canvas.addEventListener('mousedown', mouseDownHandler); + + // Enregistrement des callbacks de nettoyage + this.cleanupCallbacks.push(() => { + this.canvas.removeEventListener('mousemove', mouseMoveHandler); + this.canvas.removeEventListener('mousedown', mouseDownHandler); }); - - canvas.addEventListener('mousemove', handlers.mousemove); - canvas.addEventListener('mousedown', handlers.mousedown); -} - -async function preloadDeck(deck: Deck) { + } + + /** + * Précharge un deck + */ + private async preloadDeck(deck: Deck): Promise { await deck.preload(); -} -async function preloadHand(hand: Hand) { - await hand.preload(); -} - -export function cleanup() { - cancelAnimationFrame(animationFrameId); - callbacks.forEach(fn => fn()); -} - -export async function initDisplay() { - if (!ctx) { - console.error("Context canvas indisponible"); - return; + } + + /** + * Précharge une main + */ + private async preloadHand(hand: Hand): Promise { + await hand.preload(); + } + + /** + * Initialise le jeu + */ + public async initialize(): Promise { + if (this.isInitialized) return; + + try { + console.log("Chargement en cours..."); + + // Création du jeu + this.game = new Game( + this.ctx, + this.canvas, + this.staticCtx, + this.staticCanvas + ); + + // Préchargement parallèle des ressources + await Promise.all([ + ...this.decks.map(deck => this.preloadDeck(deck)), + ...this.hands.map(hand => this.preloadHand(hand)), + this.game.preload() + ]); + + console.log("Chargement terminé"); + + // Initialisation des écouteurs d'événements + this.initEventListeners(); + + // Démarrage de la boucle d'animation + this.startAnimationLoop(); + + // Marquer comme initialisé + this.isInitialized = true; + + // Définir la fonction de nettoyage global + window.cleanup = this.cleanup.bind(this); + + } catch (error) { + console.error("Erreur lors de l'initialisation:", error); } + } + + /** + * Nettoie les ressources + */ + public cleanup(): void { + // Arrêter la boucle d'animation + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + + // Exécuter tous les callbacks de nettoyage + this.cleanupCallbacks.forEach(callback => callback()); + this.cleanupCallbacks = []; + + // Nettoyer les ressources du jeu + if (this.game) { + // Supposons que Game a une méthode cleanup + (this.game as any).cleanup?.(); + this.game = null; + } + + // Nettoyer les ressources des decks et des mains + this.decks.forEach(deck => deck.cleanup()); + this.hands.forEach(hand => hand.cleanup()); + + // Vider les collections + this.decks = []; + this.hands = []; + + // Réinitialiser l'état + this.isInitialized = false; + this.lastFrameTime = 0; + this.mouse = { x: 0, y: 0 }; + + // Effacer les canvas + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.staticCtx.clearRect(0, 0, this.staticCanvas.width, this.staticCanvas.height); + } +} - console.log("Load begining\n"); - // Préchargement des ressources si nécessaire - // const deck = new Deck(); - // await preloadDeck(deck); - DECKS.push( - ); - HANDS.push( - ); - GAME = new Game( - ctx, - canvas, - staticCtx, - staticCanvas - ); - await Promise.all(DECKS.map(d => preloadDeck(d))); - await Promise.all(HANDS.map(h => preloadHand(h))); - await GAME?.preload(); +// Fonction de nettoyage globale exportée +export function cleanup(): void { + RiichiGameManager.getInstance().cleanup(); +} - console.log("Loaded completed\n"); - initEventListeners(); - requestAnimationFrame(animationLoop); - window.cleanup = cleanup; +// Fonction d'initialisation globale exportée +export async function initDisplay(): Promise { + await RiichiGameManager.getInstance().initialize(); } // Déclaration globale pour TypeScript declare global { - interface Window { - cleanup: () => void; - } + interface Window { + cleanup: () => void; + } } // Initialisation automatique si le script est chargé directement if (typeof window !== 'undefined') { initDisplay().catch(console.error); } - diff --git a/src/game.ts b/src/game.ts index 53cfadb..e678b4d 100644 --- a/src/game.ts +++ b/src/game.ts @@ -5,614 +5,703 @@ import { Group } from "./group"; import { drawButtons, clickAction, drawChiis, clickChii } from "./button"; import { drawState } from "./state"; -export type mousePos = { x: number, y: number}; +export type MousePos = { x: number, y: number }; + +// Game constants to avoid magic numbers and improve readability +const GAME_CONSTANTS = { + PLAYERS: 4, + BACKGROUND: { color: "#007730", x: 0, y: 0, w: 1050, h: 1050 }, + DEAD_WALL_SIZE: 14, + WAITING_TIME: { + MIN: 500, + MAX: 2000, + DEFAULT: 700 + }, + DISPLAY: { + HAND_SIZE: 0.7, + HIDDEN_HAND_SIZE: 0.6, + DISCARD_SIZE: 0.6, + GROUP_SIZE: 0.6 + }, + PI: Math.PI +}; export class Game { - private deck: Deck; - private deadWall: Array = []; - private hands: Array = []; - private discards: Array>; - private lastDiscard: number|undefined; - private groups: Array>; + private deck: Deck; + private deadWall: Array = []; + private hands: Array = []; + private discards: Array> = []; + private lastDiscard: number | undefined; + private groups: Array> = []; + + // Game state + private level: number; + private turn = 0; + private waitingTime = GAME_CONSTANTS.WAITING_TIME.DEFAULT; + private selectedTile: number | undefined = undefined; + private canCall: boolean = false; + private hasPicked: boolean = false; + private hasPlayed: boolean = false; + private lastPlayed: number = Date.now(); + private chooseChii: boolean = false; + private end: boolean = false; + private result: number = -1; + + // Canvas elements + private ctx: CanvasRenderingContext2D; + private cv: HTMLCanvasElement; + private staticCtx: CanvasRenderingContext2D; + private staticCv: HTMLCanvasElement; + + // Cached values to avoid recalculations + private readonly BG_RECT = GAME_CONSTANTS.BACKGROUND; + private readonly rotations = [0, -GAME_CONSTANTS.PI / 2, -GAME_CONSTANTS.PI, GAME_CONSTANTS.PI / 2]; - // game values - private level: number; - private turn = 0; - private minWaitingTime = 500; - private maxWaitingTime = 2000; - private waitingTime = 700; - private selectedTile: number|undefined = undefined; - private canCall: boolean = false; - private hasPicked: boolean = false; - private hasPlayed: boolean = false; - private lastPlayed: number = Date.now(); - private chooseChii: boolean = false; - private end: boolean = false; - private result: number = -1; + constructor( + ctx: CanvasRenderingContext2D, + cv: HTMLCanvasElement, + staticCtx: CanvasRenderingContext2D, + staticCv: HTMLCanvasElement, + red: boolean = false, + level: number = 0 + ) { + this.ctx = ctx; + this.cv = cv; + this.staticCtx = staticCtx; + this.staticCv = staticCv; + this.level = level; + + // Initialize game elements + this.deck = new Deck(red); + this.initializeGame(); + } - // display parameter - private BG_RECT = {color: "#007730", x: 0, y: 0, w: 1050, h: 1050}; - private sizeHand = 0.7; - private sizeHiddenHand = 0.6; - private sizeDiscard = 0.6; + private initializeGame(): void { + this.deck.shuffle(); + + // Set up dead wall + for (let i = 0; i < GAME_CONSTANTS.DEAD_WALL_SIZE; i++) { + this.deadWall.push(this.deck.pop()); + } + + // Create player hands + for (let i = 0; i < GAME_CONSTANTS.PLAYERS; i++) { + this.hands.push(this.deck.getRandomHand()); + this.discards.push([]); + this.groups.push([]); + } + + this.lastDiscard = undefined; + this.hands[0].sort(); + this.pick(0); + } - // canvas - private ctx: CanvasRenderingContext2D; - private cv: HTMLCanvasElement; - private staticCtx: CanvasRenderingContext2D; - private staticCv: HTMLCanvasElement; + public draw(mp: MousePos): void { + // Clear static canvas once per frame + this.staticCtx.clearRect(0, 0, this.cv.width, this.cv.height); + + // Draw background + this.staticCtx.fillStyle = this.BG_RECT.color; + this.staticCtx.fillRect( + this.BG_RECT.x, + this.BG_RECT.y, + this.BG_RECT.w, + this.BG_RECT.h + ); + + this.getSelected(mp); + this.drawGame(); + + // Use a single drawImage operation instead of clearing and redrawing + this.ctx.clearRect(0, 0, this.cv.width, this.cv.height); + this.ctx.drawImage(this.staticCv, 0, 0); + } - public constructor( - ctx: CanvasRenderingContext2D, - cv: HTMLCanvasElement, - staticCtx: CanvasRenderingContext2D, - staticCv: HTMLCanvasElement, - red: boolean = false, - level: number = 0 - ) { - this.ctx = ctx; - this.cv = cv; - this.staticCtx = staticCtx; - this.staticCv = staticCv; - this.level = level; - this.deck = new Deck(red); - this.deck.shuffle(); - for (let i = 0; i < 14; i++) { - this.deadWall.push(this.deck.pop()); - } - for (let i = 0; i < 4; i++) { - this.hands.push(this.deck.getRandomHand()); - } - this.discards = [[], [], [], []]; - this.lastDiscard = undefined; - this.groups = [[], [], [], []]; + public getDeck(): Deck { + return this.deck; + } - this.hands[0].sort(); - this.pick(0); - } + public getHands(): Array { + return this.hands; + } - public draw(mp: mousePos) { - // background - this.staticCtx.clearRect(0, 0, this.cv.width, this.cv.height); - this.staticCtx.fillStyle = this.BG_RECT.color; - this.staticCtx.fillRect( - this.BG_RECT.x, - this.BG_RECT.y, - this.BG_RECT.w, - this.BG_RECT.h - ); + public isFinished(): boolean { + return this.end; + } - this.getSelected(mp); + public click(mp: MousePos): void { + const rect = this.cv.getBoundingClientRect(); + + if (this.hasWin(0)) { + this.end = true; + this.result = 1; + return; + } + + if (this.chooseChii) { + this.handleChiiSelection(mp, rect); + return; + } + + const action = clickAction( + mp.x - rect.x, + mp.y - rect.y, + this.canDoAChii().length > 0, + this.canDoAPon(), + false && this.level > 1, + false && this.level > 0, + false && this.level > 0 + ); + + if (this.canCall && action !== -1) { + this.handleCallAction(action); + } else if (this.turn === 0 && this.selectedTile !== undefined) { + this.handlePlayerDiscard(); + } + } - this.drawGame(); - this.ctx.clearRect(0, 0, this.cv.width, this.cv.height); - this.ctx.drawImage(this.staticCv, 0, 0); - } + private handleChiiSelection(mp: MousePos, rect: DOMRect): void { + const allChii = this.getChii(0); + const selection = clickChii( + mp.x - rect.x, + mp.y - rect.y, + allChii + ); + + if (selection === 0) { + this.chooseChii = false; + } else { + this.chooseChii = false; + this.chii(selection, 0); + } + } - public getDeck(): Deck { - return this.deck; - } + private handleCallAction(action: number): void { + if (action === 0) { // Pass + this.canCall = false; + this.advanceTurn(); + } else if (action === 1) { // Chii + const chiis = this.canDoAChii(); + if (chiis.length === 1) { + this.chii(chiis[0], 0); + } else { + this.chooseChii = true; + this.drawGame(); + } + } else if (action === 2) { // Pon + this.pon(this.turn); + } + } - public getHands(): Array { - return this.hands; - } + private handlePlayerDiscard(): void { + this.discard(0, this.selectedTile as number); + + if (!this.checkPon()) { + const chiis = this.canDoAChii(1); + if (chiis.length > 0) { + const i = Math.floor(Math.random() * chiis.length); + this.chii(chiis[i], 1); + } else { + this.advanceTurn(); + } + } + } - public isFinished(): boolean { - return this.end; - } + private advanceTurn(): void { + this.updateWaitingTime(); + this.turn = (this.turn + 1) % GAME_CONSTANTS.PLAYERS; + this.hasPicked = false; + this.hasPlayed = false; + } - public click( // player is playing - mp: mousePos, - ): void { - const rect = this.cv.getBoundingClientRect(); + private getSelected(mp: MousePos): void { + const rect = this.cv.getBoundingClientRect(); + const x = 2.5 * 75 * 0.75; + const y = 1050 - 250 * 0.6; + const sizeHand = GAME_CONSTANTS.DISPLAY.HAND_SIZE; + + const mouseX = mp.x - x; + const mouseY = mp.y; + const tileWidth = 83.9; + + const tileIndex = Math.floor(mouseX / (tileWidth * sizeHand)); + const relativeX = mouseX - tileIndex * tileWidth * sizeHand; + + if ( + relativeX <= (tileWidth - 3) * sizeHand && + tileIndex >= 0 && + tileIndex < this.hands[0].length() && + mouseY >= y && + mouseY <= y + 100 * sizeHand + ) { + this.selectedTile = tileIndex; + } else { + this.selectedTile = undefined; + } + } - if (this.hasWin(0)) { - this.end = true; - this.result = 1; - } else if (this.chooseChii) { // is choosing - let allChii = this.getChii(0); - let c = clickChii( - mp.x - rect.x, - mp.y - rect.y, - allChii - ); - if (c === 0) { - this.chooseChii = false; - } else { - this.chooseChii = false; - this.chii(c, 0); - } - } else { - let action = clickAction( - mp.x - rect.left, - mp.y - rect.top, - this.canDoAChii().length > 0, - this.canDoAPon(), - false && this.level > 1, - false && this.level > 0, - false && this.level > 0 - ); + private updateWaitingTime(): void { + this.waitingTime = Math.floor( + Math.random() * + (GAME_CONSTANTS.WAITING_TIME.MAX - GAME_CONSTANTS.WAITING_TIME.MIN) + + GAME_CONSTANTS.WAITING_TIME.MIN + ); + } - if (this.canCall && action !== -1) { // can call - if (action === 0) { // pass - this.canCall = false; - if (this.turn === 3) { - this.turn = 0; - this.pick(0); - } else { - this.turn++; - } - this.updateWaitingTime(); - this.hasPicked = false; - this.hasPlayed = false; - } else if (action === 1) { // chii - let chiis = this.canDoAChii(); - if (chiis.length === 1) { // only one possible - this.chii(chiis[0], 0); - } else { - this.chooseChii = true; - this.drawGame(); - } - } else if (action == 2) { // pon - this.pon(this.turn); - } + private play(): void { + if (this.turn !== 0 && !this.end) { + if (!this.hasPicked) { + // Begin of turn + this.lastPlayed = Date.now(); + this.pick(this.turn); + this.hasPicked = true; + } else if (!this.hasPlayed) { + // Middle of turn + this.handleBotTurn(); + } else if (!this.canCall) { + // End of turn + this.advanceBotTurn(); + } + } + } - } else { // nothing unusual - if (this.turn === 0 && this.selectedTile !== undefined) { - this.discard(0, this.selectedTile as NonNullable); - if (!this.checkPon()) { - let chiis = this.canDoAChii(1); - if (chiis.length > 0) { - let i = Math.floor(Math.random() * chiis.length); - this.chii(chiis[i], 1); - } else { - this.updateWaitingTime(); - this.turn = (this.turn + 1) % 4; - } - } - } - } - } - } + private handleBotTurn(): void { + if (this.hasWin(this.turn)) { + this.end = true; + this.result = 2; + return; + } + + if (Date.now() - this.lastPlayed > this.waitingTime) { + this.lastPlayed = Date.now(); + + // Choose random tile to discard + const n = Math.floor(this.hands[this.turn].length() * Math.random()); + this.discard(this.turn, n); + this.hasPlayed = true; + + if (this.deck.length() <= 0) { + this.result = 0; + this.end = true; + return; + } + + if (!this.end) { + this.checkPon(); + this.checkForChii(); + this.canCall = this.canDoAChii().length > 0 || this.canDoAPon(); + } + } + } - private getSelected( - mp: mousePos, - ): void { - const rect = this.cv.getBoundingClientRect(); - let x = 2.5 * 75 * 0.75; - let y = 1050 - 250 * 0.6; - - const mouseX = mp.x - rect.left - x; - const mouseY = mp.y - rect.top; - const s = 83.9; + private checkForChii(): void { + if (this.turn === 3) return; + + const nextPlayer = this.turn + 1; + const chiis = this.canDoAChii(nextPlayer); + + if (chiis.length > 0) { + const i = Math.floor(Math.random() * chiis.length); + this.chii(chiis[i], nextPlayer); + } + } - let q = Math.floor(mouseX / (s * this.sizeHand)); - let r = mouseX - q * s * this.sizeHand; - if ( - r <= (s - 3) * this.sizeHand && - q >= 0 && - q < this.hands[0].length() && - mouseY >= y && - mouseY <= y + 100 * this.sizeHand - ) { - this.selectedTile = q; - } else { - this.selectedTile = undefined; - } - }; + private advanceBotTurn(): void { + if (this.turn === 3) { + this.turn = 0; + this.pick(0); + if (this.hasWin(0)) { + this.end = true; + this.result = 1; + } + } else { + this.turn++; + } + + this.updateWaitingTime(); + this.hasPicked = false; + this.hasPlayed = false; + } - private updateWaitingTime(): void { - this.waitingTime = - Math.floor( - Math.random() * - (this.maxWaitingTime - this.minWaitingTime) + - this.minWaitingTime - ); - } + private pick(player: number): void { + this.hands[player].push(this.deck.pop()); + this.hands[player].isolate = true; + } - private play(): void { - if ( - this.turn !== 0 && !this.end - ) { // bot playing - if (!this.hasPicked) { // begin of his turn - this.lastPlayed = Date.now(); - this.pick(this.turn); - this.hasPicked = true; - } else if (!this.hasPlayed) { // middle of his turn - if (this.hasWin(this.turn)) { - this.end = true; - this.result = 2; - } else if (Date.now() - this.lastPlayed > this.waitingTime) { - this.lastPlayed = Date.now(); - let n = Math.floor(this.hands[this.turn].length() * Math.random()); - this.discard(this.turn, n); - this.hasPlayed = true; - if (this.deck.length() <= 0) { - this.result = 0; - this.end = true; - } - if (!this.end) { - this.checkPon(); - if (this.turn !== 3 && this.canDoAChii(this.turn + 1).length > 0) { - let chiis = this.canDoAChii(this.turn + 1); - let i = Math.floor(Math.random() * chiis.length); - this.chii(chiis[i], this.turn + 1); - } - this.canCall = this.canDoAChii().length > 0 || this.canDoAPon(); - } - } - } else if (!this.canCall) { // end of his turn - if (this.turn === 3) { - this.turn = 0; - this.pick(0); - if (this.hasWin(0)) { - this.end = true; - this.result = 1; - } - } else { - this.turn++; - } - this.updateWaitingTime(); - this.hasPicked = false; - this.hasPlayed = false; - } - } - } + private discard(player: number, n: number): void { + const tile = this.hands[player].eject(n); + this.hands[player].sort(); + + tile.setTilt(); + this.discards[player].push(tile); + + this.hands[player].isolate = false; + this.hands[player].sort(); + + this.lastDiscard = player; + this.lastPlayed = Date.now(); + } - private pick(player: number): void { - this.hands[player].push(this.deck.pop()); - this.hands[player].isolate = true; - } + private canDoAChii(p: number = 0): Array { + const chii: number[] = []; + + // Check if chii is possible + if ( + this.lastDiscard === undefined || + (this.lastDiscard + 1) % 4 !== p || + (this.turn + 1) % 4 !== p || + this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1].getFamily() >= 4 + ) { + return chii; + } + + const t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1]; + const h = this.hands[p]; + const family = t.getFamily(); + const value = t.getValue(); + + // Check for three possible chii patterns + if (h.count(family, value - 2) > 0 && h.count(family, value - 1) > 0) { + chii.push(value - 2); + } + + if (h.count(family, value - 1) > 0 && h.count(family, value + 1) > 0) { + chii.push(value - 1); + } + + if (h.count(family, value + 1) > 0 && h.count(family, value + 2) > 0) { + chii.push(value); + } + + return chii; + } - private discard(player: number, n: number): void { - let tile = this.hands[player].eject(n); - this.hands[player].sort(); - tile.setTilt(); - this.discards[player].push(tile); - this.hands[player].isolate = false; - this.hands[player].sort(); - this.lastDiscard = player; - this.lastPlayed = Date.now(); - } + private chii(minValue: number, p: number): void { + const discardPlayer = p === 0 ? 3 : p - 1; + const t = this.discards[discardPlayer].pop() as Tile; + this.lastDiscard = undefined; + + const tt: Tile[] = []; + let tn = 0; + let v = minValue; + + // Find the right tiles for the chii + while (tn < 2) { + if (v === t.getValue()) { + v++; + } else { + tt[tn] = this.hands[p].find(t.getFamily(), v) as Tile; + tn++; + v++; + } + } + + // Set tilt for all tiles in the group + [t, tt[0], tt[1]].forEach(tile => tile.setTilt()); + + // Create new group + this.groups[p].push(new Group([t, tt[0], tt[1]], discardPlayer, p)); + + if (this.hasWin(p)) { + this.end = true; + this.result = p === 0 ? 1 : 2; + } + + this.updateWaitingTime(); + this.turn = p; + this.hasPicked = true; + this.hasPlayed = false; + } - private canDoAChii(p: number = 0): Array { - let chii = [] as Array; - if ( - this.lastDiscard !== undefined && - (this.lastDiscard + 1) % 4 === p && - (this.turn + 1) % 4 === p && - this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1].getFamily() < 4 - ) { - let t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1]; - let h = this.hands[p]; - if ( - h.count(t.getFamily(), t.getValue()-2) > 0 && - h.count(t.getFamily(), t.getValue()-1) > 0 - ) { - chii.push(t.getValue()-2); - } - if ( - h.count(t.getFamily(), t.getValue()-1) > 0 && - h.count(t.getFamily(), t.getValue()+1) > 0 - ) { - chii.push(t.getValue()-1); - } - if ( - h.count(t.getFamily(), t.getValue()+1) > 0 && - h.count(t.getFamily(), t.getValue()+2) > 0 - ) { - chii.push(t.getValue()); - } - } - return chii; - } + private getChii(p: number): Array> { + const chiis: Array> = []; + const numChii = this.canDoAChii(); + const discardPlayer = p === 0 ? 3 : p - 1; + const d = this.discards[discardPlayer]; + const t = d[d.length - 1]; + + for (let i = 0; i < numChii.length; i++) { + const v = numChii[i]; + const chii: Tile[] = []; + + for (let dv = 0; dv < 3; dv++) { + if (v + dv === t.getValue()) { + chii.push(t); + } else { + const tt = this.hands[p].find(t.getFamily(), v + dv) as Tile; + chii.push(tt); + this.hands[p].push(tt); + this.hands[p].sort(); + } + } + + chiis.push(chii); + } + + return chiis; + } - private chii(minValue: number, p: number): void { - let t = this.discards[p === 0 ? 3 : p - 1].pop() as NonNullable; - this.lastDiscard = undefined; - let tn = 0; - let v = minValue; - let tt: Array = []; - while (tn < 2) { - if (v === t.getValue()) { - v++; - } else { - tt[tn] = this.hands[p].find(t.getFamily(), v) as NonNullable; - tn++; - v++; - } - } - [t, tt[0], tt[1]].forEach(t => t.setTilt()); - this.groups[p].push(new Group([t, tt[0], tt[1]], p === 0 ? 3 : p - 1, p)); + private checkPon(): boolean { + for (let p = 1; p < GAME_CONSTANTS.PLAYERS; p++) { + if (this.canDoAPon(p)) { + this.pon(this.lastDiscard as number, p); + return true; + } + } + return false; + } - if (this.hasWin(p)) { - this.end = true; - this.result = p === 0 ? 1 : 2; - } - this.updateWaitingTime(); - this.turn = p; - this.hasPicked = true; - this.hasPlayed = false; - } + private canDoAPon(player: number = 0): boolean { + if ( + this.lastDiscard === undefined || + this.lastDiscard === player || + this.turn === player || + (this.hasPicked && !this.hasPlayed) + ) { + return false; + } + + const t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length - 1]; + return this.hands[player].count(t.getFamily(), t.getValue()) >= 2; + } - private getChii(p: number): Array> { - let chiis = [] as Array>; - let numChii = this.canDoAChii(); + private pon(p: number, thief: number = 0): void { + const t = this.discards[p].pop() as Tile; + this.lastDiscard = undefined; + + const t2 = this.hands[thief].find(t.getFamily(), t.getValue()) as Tile; + const t3 = this.hands[thief].find(t.getFamily(), t.getValue()) as Tile; + + [t, t2, t3].forEach(tile => tile.setTilt()); + + this.groups[thief].push(new Group([t, t2, t3], p, thief)); + + if (this.hasWin(thief)) { + this.end = true; + this.result = thief === 0 ? 1 : 2; + } + + this.updateWaitingTime(); + this.turn = thief; + this.hasPicked = true; + this.hasPlayed = false; + } - let d = this.discards[p === 0 ? 3 : p - 1]; - let t = d[d.length - 1] + private hasWin(p: number): boolean { + return this.hands[p].toGroup() !== undefined; + } - for (let i = 0; i < numChii.length; i++) { - let v = numChii[i]; - let chii = []; - for (let dv = 0; dv < 3; dv++) { - if (v + dv === t.getValue()) { - chii.push(t); - } else { - let tt = this.hands[p].find(t.getFamily(), v + dv) as NonNullable; - chii.push(tt); - this.hands[p].push(tt); - this.hands[p].sort(); - } - } - chiis.push(chii); - } - return chiis; - } + private drawGame(): void { + // Update game state + this.play(); + + // Draw game elements + drawState(this.staticCtx, this.turn); + this.drawDiscardSize(); + this.drawResult(); + this.drawHands(); + this.drawGroups(GAME_CONSTANTS.DISPLAY.GROUP_SIZE); + + // Draw discards for all players + for (let i = 0; i < GAME_CONSTANTS.PLAYERS; i++) { + this.drawDiscard( + i, + this.selectedTile !== undefined ? this.hands[0].get(this.selectedTile) : undefined + ); + } + + // Draw UI elements + if (this.chooseChii) { + drawChiis(this.staticCtx, this.getChii(0)); + } else { + drawButtons( + this.staticCtx, + this.canDoAChii().length > 0, + this.canDoAPon(), + false && this.level > 1, + false && this.level > 0, + false && this.level > 0 + ); + } + } - private checkPon(): boolean { - for (var p = 1; p < 4; p++) { - if (this.canDoAPon(p)) { - this.pon(this.lastDiscard as NonNullable, p); - return true; - } - } - return false; - } + private drawHands(): void { + const showHands = false; + const { HAND_SIZE, HIDDEN_HAND_SIZE } = GAME_CONSTANTS.DISPLAY; + + // Draw player's hand + this.hands[0].drawHand( + this.staticCtx, + 2.5 * 75 * 0.75, + 1000 - 150 * HAND_SIZE, + 5 * HAND_SIZE, + 0.75, + this.selectedTile, + false, + 0 + ); + + // Draw opponents' hands + 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 + ); + + 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 + ); + + 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 + ); + } - private canDoAPon(player: number = 0): boolean { - if ( - this.lastDiscard !== undefined && // il y a une défausse - this.lastDiscard !== player && // pas sa propre défausse - this.turn !== player && // pas son propre tour - !(this.hasPicked && !this.hasPlayed) // pas un joueur en train de jouer - ) { - let t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1]; - return this.hands[player].count(t.getFamily(), t.getValue()) >= 2; - } else { - return false; - } - } + private drawGroups(size: number): void { + const offset = 25; + + for (let p = 0; p < GAME_CONSTANTS.PLAYERS; p++) { + const rotation = this.rotations[p]; + const groups = this.groups[p]; + + if (groups.length > 0) { + for (let i = groups.length - 1; i >= 0; i--) { + 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 + ); + } + } + } + } - private pon(p: number, thief: number = 0): void { - let t = this.discards[p].pop() as NonNullable; - this.lastDiscard = undefined; - let t2 = this.hands[thief].find(t.getFamily(), t.getValue()) as NonNullable; - let t3 = this.hands[thief].find(t.getFamily(), t.getValue()) as NonNullable; - [t, t2, t3].forEach(t => t.setTilt()); - this.groups[thief].push(new Group([t, t2, t3], p, thief)); + private drawDiscard(p: number, highlightedTile: Tile | undefined): void { + const sizeDiscard = GAME_CONSTANTS.DISPLAY.DISCARD_SIZE; + + this.staticCtx.save(); + this.staticCtx.translate(525, 525); + this.staticCtx.rotate(this.rotations[p]); + + const x = -sizeDiscard * 475 / 2; + const y = sizeDiscard * (475 / 2 + 5); + + // Draw all discarded tiles + for (let i = 0; i < this.discards[p].length; i++) { + const tile = this.discards[p][i]; + let tx, ty; + + if (i < 12) { + tx = x + (i % 6) * 80 * sizeDiscard; + ty = y + Math.floor(i / 6) * 105 * sizeDiscard; + } else { + tx = x + (i - 12) * 80 * sizeDiscard; + ty = y + 2 * 105 * sizeDiscard; + } + + tile.drawTile( + this.staticCtx, + tx, + ty, + sizeDiscard, + false, + 0, + highlightedTile?.isEqual(tile.getFamily(), tile.getValue()) + ); + } + + // Draw indicator for last discard + if (this.lastDiscard === p) { + this.drawLastDiscardIndicator(p); + } + + this.staticCtx.restore(); + } - if (this.hasWin(thief)) { - this.end = true; - this.result = thief === 0 ? 1 : 2; - } - this.updateWaitingTime(); - this.turn = thief; - this.hasPicked = true; - this.hasPlayed = false; - } + private drawLastDiscardIndicator(p: number): void { + const j = this.discards[p].length - 1; + const sizeDiscard = GAME_CONSTANTS.DISPLAY.DISCARD_SIZE; + const x = -sizeDiscard * 475 / 2; + const y = sizeDiscard * (475 / 2 + 5); + + let tx = j < 12 + ? x + (j % 6) * 80 * sizeDiscard + : x + (j - 12) * 80 * sizeDiscard; + + let ty = j < 12 + ? y + Math.floor(j / 6) * 105 * sizeDiscard + : y + 2 * 105 * sizeDiscard; + + tx += 75 / 2 * sizeDiscard; + ty += 115 * sizeDiscard; + + const triangleSize = 10; + this.staticCtx.fillStyle = "#ff0000"; + this.staticCtx.beginPath(); + this.staticCtx.moveTo(tx, ty); + this.staticCtx.lineTo(tx + triangleSize / 2, ty + 0.866 * triangleSize); + this.staticCtx.lineTo(tx - triangleSize / 2, ty + 0.866 * triangleSize); + this.staticCtx.lineTo(tx, ty); + this.staticCtx.fill(); + this.staticCtx.stroke(); + } - private hasWin(p: number): boolean { - return this.hands[p].toGroup() !== undefined - } + private drawDiscardSize(): void { + this.staticCtx.fillStyle = "#f070f0"; + this.staticCtx.font = "40px garamond"; + + const remainingTiles = this.deck.length(); + const x = remainingTiles < 10 ? 517 : 507; + + this.staticCtx.fillText(remainingTiles.toString(), x, 537); + } - private drawGame(): void { - // update game - this.play(); + private drawResult(): void { + if (this.result === -1) return; + + // Draw result background + this.staticCtx.fillStyle = "#e0e0f0"; + this.staticCtx.fillRect(450, 430, 150, 190); + this.staticCtx.fillRect(430, 450, 190, 150); + + // Draw result text + this.staticCtx.fillStyle = "#ff0000"; + this.staticCtx.font = "45px garamond"; + + if (this.result === 0) { + this.staticCtx.fillText("Égalité", 450, 535); + } else if (this.result === 1) { + this.staticCtx.fillText("Victoire !", 440, 535); + } else if (this.result === 2) { + this.staticCtx.fillText("Défaite...", 440, 535); + } + } - // draw winds, discard, riichi etc... - drawState(this.staticCtx, this.turn); - this.drawDiscardSize(); - - // draw result - this.drawResult(); - - // hands - this.drawHands(); - - // groups - this.drawGroups(0.6); - - // discards - for (let i = 0; i < 4; i++) { - this.drawDiscard( - i, - this.hands[0].get(this.selectedTile) as NonNullable - ); - } - - // called - if (this.chooseChii) { - drawChiis(this.staticCtx, this.getChii(0)); - } else { - drawButtons( - this.staticCtx, - this.canDoAChii().length > 0, - this.canDoAPon(), - false && this.level > 1, - false && this.level > 0, - false && this.level > 0 - ); - } - } - - private drawHands() { - const pi = 3.141592; - const showHands = false; - - this.hands[0].drawHand( - this.staticCtx, - 2.5 * 75 * 0.75, - 1000 - 150 * this.sizeHand, - 5 * this.sizeHand, - 0.75, - this.selectedTile, - false, - 0 - ); - this.hands[1].drawHand( - this.staticCtx, - 1000 - 150 * this.sizeHiddenHand, - 1000 - 75 * 5 * this.sizeHiddenHand, - 5 * this.sizeHiddenHand, - this.sizeHiddenHand, - undefined, - !showHands, - - pi / 2 - ); - this.hands[2].drawHand( - this.staticCtx, - 1000 - 75 * 5 * this.sizeHiddenHand, - 150 * this.sizeHiddenHand, - 5 * this.sizeHiddenHand, - this.sizeHiddenHand, - undefined, - !showHands, - - pi - ); - this.hands[3].drawHand( - this.staticCtx, - 150 * this.sizeHiddenHand, - 75 * 5 * this.sizeHiddenHand, - 5 * this.sizeHiddenHand, - this.sizeHiddenHand, - undefined, - !showHands, - pi / 2 - ); - - } - - private drawGroups( - size: number, - ): void { - let os = 25; - const pi = 3.141592; - for ( let p = 0; p < 4; p++) { - let rotation = [0, -pi / 2, -pi, pi / 2][p]; - if (this.groups[p].length > 0) { - for (let i = this.groups[p].length-1; i >= 0; i--) { - this.groups[p][i].drawGroup( - this.staticCtx, - 1050 - 240 - (260 + os) * size * i, - 1050 - 62, - 5, - 0.6, - rotation, - this.hands[0].get(this.selectedTile) - ); - } - } - } - } - - private drawDiscard( - p: number, - highlitedTile: Tile|undefined - ): void { - const pi = 3.141592; - - this.staticCtx.save(); - this.staticCtx.translate(525, 525); - this.staticCtx.rotate([0, -pi/2, -pi, pi/2][p]) - - let x = - this.sizeDiscard * 475 / 2; - let y = this.sizeDiscard * (475 / 2 + 5); - for (let i = 0; i < this.discards[p].length; i++) { - let tile = this.discards[p][i]; - if (i < 12) { - tile.drawTile( - this.staticCtx, - x + (i % 6) * 80 * this.sizeDiscard, - y + Math.floor(i / 6) * 105 * this.sizeDiscard, - this.sizeDiscard, - false, - 0, - highlitedTile?.isEqual(tile.getFamily(), tile.getValue()) - ); - } else { - tile.drawTile( - this.staticCtx, - x + (i - 12) * 80 * this.sizeDiscard, - y + 2 * 105 * this.sizeDiscard, - this.sizeDiscard, - false, - 0, - highlitedTile?.isEqual(tile.getFamily(), tile.getValue()) - ); - } - } - if (this.lastDiscard === p) { - const j = this.discards[p].length - 1; - let tx = - j < 12 ? - x + (j % 6) * 80 * this.sizeDiscard : - x + (j - 12) * 80 * this.sizeDiscard; - let ty = - j < 12 ? - y + Math.floor(j / 6) * 105 * this.sizeDiscard : - y + 2 * 105 * this.sizeDiscard; - tx += 75 / 2 * this.sizeDiscard; - ty += 115 * this.sizeDiscard; - const ts = 10; - this.staticCtx.fillStyle = "#ff0000"; - this.staticCtx.beginPath(); - this.staticCtx.moveTo(tx, ty); - - this.staticCtx.lineTo(tx + ts/2, ty + 0.866 * ts); - this.staticCtx.lineTo(tx - ts/2, ty + 0.866 * ts); - this.staticCtx.lineTo(tx, ty); - - this.staticCtx.fill(); - this.staticCtx.stroke(); - } - - this.staticCtx.restore(); - } - - private drawDiscardSize() { - this.staticCtx.fillStyle = "#f070f0"; - - this.staticCtx.font = "40px garamond"; - let l = this.deck.length(); - if (l < 10) { - this.staticCtx.fillText(l.toString(), 517, 537); - } else { - this.staticCtx.fillText(l.toString(), 507, 537); - } - } - - private drawResult(): void { - if (this.result !== -1) { - this.staticCtx.fillStyle = "#e0e0f0"; - this.staticCtx.fillRect(450, 430, 150, 190); - this.staticCtx.fillRect(430, 450, 190, 150); - this.staticCtx.fillStyle = "#ff0000"; - this.staticCtx.font = "45px garamond"; - - } - if (this.result === 0) { // Égalité - this.staticCtx.fillText("Égalité", 450, 535); - } else if (this.result === 1) { // victoire - this.staticCtx.fillText("Victoire !", 440, 535); - } else if (this.result === 2) { // Défaite - this.staticCtx.fillText("Défaite...", 440, 535); - } - } - - public async preload(): Promise { - await this.deck.preload(); - await Promise.all(this.hands.map(h => h.preload())); - } - + public async preload(): Promise { + await this.deck.preload(); + await Promise.all(this.hands.map(h => h.preload())); + } } diff --git a/src/group.ts b/src/group.ts index 812e85a..54f08a9 100644 --- a/src/group.ts +++ b/src/group.ts @@ -1,154 +1,91 @@ -import { Tile } from "./tile" +import { Tile } from "./tile"; export class Group { - private tiles: Array; - private stolenFrom: number; - private belongsTo: number + constructor( + private tiles: Array, + private stolenFrom: number, + private belongsTo: number + ) {} - public constructor( - tiles: Array, - stolenFrom: number, - belongsTo: number - ) { - this.tiles = tiles; - this.stolenFrom = stolenFrom; - this.belongsTo = belongsTo; - } + public push(tile: Tile): void { + this.tiles.push(tile); + } - public push(tile: Tile): void { - this.tiles.push(tile); - } + public pop(): Tile | undefined { + return this.tiles.pop(); + } - public pop(): Tile|undefined { - return this.tiles.pop(); - } + public getTiles(): Array { + return this.tiles; + } - public getTiles(): Array { - return this.tiles; - } + public compare(g: Group): number { + // Compare les premiers tiles, puis les seconds si égalité + const firstComparison = this.tiles[0].compare(g.tiles[0]); + return firstComparison !== 0 ? firstComparison : this.tiles[1].compare(g.tiles[1]); + } - public compare(g: Group): number { - let c = this.tiles[0].compare(g.tiles[0]); - if (c !== 0) { - return c; - } - return this.tiles[1].compare(g.tiles[1]); - } + public drawGroup( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + os: number, + size: number, + rotation: number, + selectedTile?: Tile + ): void { + // Sauvegarde et rotation du contexte + ctx.save(); + ctx.translate(525, 525); + ctx.rotate(rotation); + ctx.translate(-525, -525); - public drawGroup( - ctx: CanvasRenderingContext2D, - x: number, - y: number, - os: number, - size: number, - rotation: number, - selectedTile: Tile|undefined - ): void { - ctx.save(); - ctx.translate(525, 525); - ctx.rotate(rotation); - ctx.translate(-525, -525); - - rotation = 0; - let v = 75 * size; - let w = 90 * size; - let osy = 25 * size / 2; - let p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4; + // Calcul des paramètres de dessin + const v = 75 * size; + const w = 90 * size; + const osy = 25 * size / 2; + const p = (this.belongsTo - this.stolenFrom - 1 + 4) % 4; + + // Détermination du tile sélectionné + const sf = selectedTile === undefined ? -1 : selectedTile.getFamily(); + const sv = selectedTile === undefined ? 0 : selectedTile.getValue(); + + // Fonction helper pour éviter la répétition de code + const drawTile = (tile: Tile, tx: number, ty: number, angle: number) => { + tile.drawTile( + ctx, + tx, + ty, + size, + false, + angle, + tile.isEqual(sf, sv) + ); + }; + + const HALF_PI = Math.PI / 2; - const sf = selectedTile === undefined ? -1 : selectedTile.getFamily(); - const sv = selectedTile === undefined ? 0 : selectedTile.getValue(); - - if (p === 0) { - this.tiles[0].drawTile( - ctx, - x, - y + osy, - size, - false, - 3.141592 / 2, - this.tiles[0].isEqual(sf, sv), - ); - this.tiles[1].drawTile( - ctx, - x + w, - y, - size, - false, - 0, - this.tiles[1].isEqual(sf, sv), - ); - this.tiles[2].drawTile( - ctx, - x + w + v + os * size, - y, - size, - false, - 0, - this.tiles[2].isEqual(sf, sv), - ); - - } else if (p === 1) { - this.tiles[0].drawTile( - ctx, - x, - y, - size, - false, - 0, - this.tiles[0].isEqual(sf, sv), - ); - this.tiles[1].drawTile( - ctx, - x + w, - y + osy, - size, - false, - 0 - 3.141592 / 2, - this.tiles[1].isEqual(sf, sv), - ); - this.tiles[2].drawTile( - ctx, - x + w + v + 3 *os * size, - y, - size, - false, - 0, - this.tiles[2].isEqual(sf, sv), - ); - - } else if (p === 2) { - this.tiles[0].drawTile( - ctx, - x, - y, - size, - false, - 0, - this.tiles[0].isEqual(sf, sv), - ); - this.tiles[1].drawTile( - ctx, - x + v + os * size, - y, - size, - false, - 0, - this.tiles[1].isEqual(sf, sv), - ); - this.tiles[2].drawTile( - ctx, - x + w + v + os * size, - y + osy, - size, - false, - 0 - 3.141592 / 2, - this.tiles[2].isEqual(sf, sv), - ); - - } else { - //TODO error - } - - ctx.restore(); - } + // Dessin selon la position + switch (p) { + case 0: + drawTile(this.tiles[0], x, y + osy, HALF_PI); + drawTile(this.tiles[1], x + w, y, 0); + drawTile(this.tiles[2], x + w + v + os * size, y, 0); + break; + case 1: + drawTile(this.tiles[0], x, y, 0); + drawTile(this.tiles[1], x + w, y + osy, -HALF_PI); + drawTile(this.tiles[2], x + w + v + 3 * os * size, y, 0); + break; + case 2: + drawTile(this.tiles[0], x, y, 0); + drawTile(this.tiles[1], x + v + os * size, y, 0); + drawTile(this.tiles[2], x + w + v + os * size, y + osy, -HALF_PI); + break; + default: + console.error(`Position non prise en charge: ${p}`); + } + + ctx.restore(); + } } diff --git a/src/hand.ts b/src/hand.ts index 8906b79..8fcbb11 100644 --- a/src/hand.ts +++ b/src/hand.ts @@ -1,200 +1,334 @@ -import { Tile } from "./tile" -import { Group } from "./group" +import { Tile } from "./tile"; +import { Group } from "./group"; + +// Constants to avoid magic numbers and improve readability +const TILE_TYPES = { + MANZU: { code: "m", family: 1 }, + PINZU: { code: "p", family: 2 }, + SOUZU: { code: "s", family: 3 }, + WINDS: { code: "w", family: 4 }, + DRAGONS: { code: "d", family: 5 } +}; export class Hand { - private tiles: Array; - public isolate: boolean = false; - - public constructor(stiles: string = "") { - this.tiles = []; - for (let i = 0; i < stiles.length - 1; i++) { - let ss = stiles.substring(i, i+2); - if (ss[0] === "m") { - this.tiles.push(new Tile(1, Number(ss[1]), false)); - } else if (ss[0] === "p") { - this.tiles.push(new Tile(2, Number(ss[1]), false)); - } else if (ss[0] === "s") { - this.tiles.push(new Tile(3, Number(ss[1]), false)); - } else if (ss[0] === "w") { - this.tiles.push(new Tile(4, Number(ss[1]), false)); - } else if (ss[0] === "d") { - this.tiles.push(new Tile(5, Number(ss[1]), false)); - } else {} - } - } + private tiles: Array; + public isolate: boolean = false; + + /** + * Create a hand from string representation + * @param stiles String representation of tiles (e.g., "m1p2s3w1d1") + */ + public constructor(stiles: string = "") { + this.tiles = []; + this.initializeFromString(stiles); + } - public getTiles(): Array { - return this.tiles; - } + /** + * Parse string representation into tiles + */ + private initializeFromString(stiles: string): void { + for (let i = 0; i < stiles.length - 1; i++) { + const tileCode = stiles.substring(i, i + 2); + const type = tileCode[0]; + const value = Number(tileCode[1]); + + if (this.isValidTileCode(type, value)) { + this.addTileFromCode(type, value); + } + } + } - public length(): number { - return this.tiles.length; - } + /** + * Check if tile code is valid + */ + private isValidTileCode(type: string, value: number): boolean { + return ( + (type === TILE_TYPES.MANZU.code) || + (type === TILE_TYPES.PINZU.code) || + (type === TILE_TYPES.SOUZU.code) || + (type === TILE_TYPES.WINDS.code) || + (type === TILE_TYPES.DRAGONS.code) + ); + } - public push(tile: Tile): void { - this.tiles.push(tile); - } + /** + * Create a tile from type code and value + */ + private addTileFromCode(type: string, value: number): void { + const familyMap: { [key: string]: number } = { + [TILE_TYPES.MANZU.code]: TILE_TYPES.MANZU.family, + [TILE_TYPES.PINZU.code]: TILE_TYPES.PINZU.family, + [TILE_TYPES.SOUZU.code]: TILE_TYPES.SOUZU.family, + [TILE_TYPES.WINDS.code]: TILE_TYPES.WINDS.family, + [TILE_TYPES.DRAGONS.code]: TILE_TYPES.DRAGONS.family + }; - public pop(): Tile|undefined { - return this.tiles.pop(); - } + const family = familyMap[type]; + if (family !== undefined) { + this.tiles.push(new Tile(family, value, false)); + } + } - public find(family: number, value: number) :Tile|undefined { - let n = undefined; - for (let i = 0; i < this.tiles.length; i++) { - if (this.tiles[i].getFamily() === family && this.tiles[i].getValue() === value) { - n = i; - break; - } - } - if (n !== undefined) { - [this.tiles[n], this.tiles[0]] = [this.tiles[0], this.tiles[n]]; - let t = this.tiles.shift(); - this.sort(); - return t; - } else { - return undefined; - } - } + /** + * Get all tiles in hand + */ + public getTiles(): Array { + return this.tiles; + } - public eject(idTile: number): Tile { - [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]]; - let tile = this.tiles.shift(); - this.sort(); - return tile as NonNullable; - } + /** + * Get number of tiles in hand + */ + public length(): number { + return this.tiles.length; + } - public get(idTile: number|undefined): Tile|undefined { - if (idTile !== undefined) { - return this.tiles[idTile]; - } else { - return undefined; - } - } + /** + * Add a tile to hand + */ + public push(tile: Tile): void { + this.tiles.push(tile); + } - public sort(): undefined { - this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1); - } + /** + * Remove and return the last tile + */ + public pop(): Tile | undefined { + return this.tiles.pop(); + } - public count(family: number, value: number): number { - let c = 0; - this.tiles.forEach( - t => { - if (t.getFamily() === family && t.getValue() === value) { - c++; - } - } - ); - return c; - } + /** + * Find and remove a specific tile by family and value + */ + public find(family: number, value: number): Tile | undefined { + const index = this.findTileIndex(family, value); + + if (index !== -1) { + // Swap with first tile and remove + [this.tiles[index], this.tiles[0]] = [this.tiles[0], this.tiles[index]]; + const tile = this.tiles.shift(); + this.sort(); + return tile; + } + + return undefined; + } - public toGroup(pair: boolean = false): Array|undefined { - if (this.tiles.length > 0) { - let t1 = this.tiles.pop() as NonNullable; - - let c = this.count(t1.getFamily(), t1.getValue()); - if (c >= 1 && !pair) { //can do a pair - let t2 = this.find(t1.getFamily(), t1.getValue()) as NonNullable; - let groups = this.toGroup(true); - this.tiles.push(t2); - this.sort(); - if (groups !== undefined) { - this.tiles.push(t1); - this.sort(); - groups.push(new Group([t1, t2], 0, 0)); - return groups; - } - } - if (c >= 2) { //can do a pon - let t2 = this.find(t1.getFamily(), t1.getValue()) as NonNullable; - let t3 = this.find(t1.getFamily(), t1.getValue()) as NonNullable; - let groups = this.toGroup(pair); - this.tiles.push(t2); - this.tiles.push(t3); - this.sort(); - if (groups !== undefined) { - groups.push(new Group([t1, t2, t3], 0, 0)); - this.tiles.push(t1); - this.sort(); - return groups; - } - } - - let c2 = this.count(t1.getFamily(), t1.getValue()-1); - let c3 = this.count(t1.getFamily(), t1.getValue()-2); - if (c2 * c3 > 0) { //can do a chii - let t2 = this.find(t1.getFamily(), t1.getValue()-1) as NonNullable; - let t3 = this.find(t1.getFamily(), t1.getValue()-2) as NonNullable; - let groups = this.toGroup(pair); - this.tiles.push(t2); - this.tiles.push(t3); - this.sort(); - if (groups !== undefined) { - groups.push(new Group([t3, t2, t1], 0, 0)); - this.tiles.push(t1); - this.sort(); - return groups; - } - } + /** + * Find index of tile with specific family and value + */ + private findTileIndex(family: number, value: number): number { + return this.tiles.findIndex( + tile => tile.getFamily() === family && tile.getValue() === value + ); + } - this.tiles.push(t1); - this.tiles.sort(); - return undefined; + /** + * Remove tile at specific index + */ + public eject(idTile: number): Tile { + if (idTile < 0 || idTile >= this.tiles.length) { + throw new Error("Invalid tile index"); + } + + // Swap with first tile and remove + [this.tiles[0], this.tiles[idTile]] = [this.tiles[idTile], this.tiles[0]]; + const tile = this.tiles.shift(); + this.sort(); + + return tile as Tile; + } - } else { - return []; - } - } - - public drawHand ( - ctx: CanvasRenderingContext2D, - x: number, - y: number, - offset: number, - size: number, - focusedTiled: number|undefined = undefined, - hidden: boolean = false, - rotation: number = 0 - ): void { - let v = (75 + offset) * size; - let vx = Math.cos(rotation) * v; - let vy = Math.sin(rotation) * v; - for (let i = 0; i < this.tiles.length; i++) { - let e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0; - if (i === focusedTiled) { - this.tiles[i].drawTile( - ctx, - x + - i * vx + - 25 * size * Math.sin(rotation) + - e * size * Math.cos(rotation), - y + - i * vy - - 25 * size * Math.cos(rotation) + - e * size * Math.sin(rotation), - size, - hidden, - rotation - ); - } else { - this.tiles[i].drawTile( - ctx, - x + i * vx + e * size * Math.cos(rotation), - y + i * vy + e * size * Math.sin(rotation), - size, - hidden, - rotation - ); - } - } - } + /** + * Get tile at specific index without removing + */ + public get(idTile: number | undefined): Tile | undefined { + if (idTile === undefined || idTile < 0 || idTile >= this.tiles.length) { + return undefined; + } + + return this.tiles[idTile]; + } - public async preload(): Promise { - await Promise.all(this.tiles.map(t => t.preloadImg())); - } + /** + * Sort tiles in ascending order + */ + public sort(): void { + this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1); + } - public cleanup(): void { - this.tiles.forEach(tile => tile.cleanup()); - this.tiles = []; - } + /** + * Count tiles with specific family and value + */ + public count(family: number, value: number): number { + return this.tiles.filter( + tile => tile.getFamily() === family && tile.getValue() === value + ).length; + } + + /** + * Try to form hand into groups (for winning detection) + */ + public toGroup(pair: boolean = false): Array | undefined { + if (this.tiles.length === 0) { + return []; + } + + // Take last tile to try forming a group + const lastTile = this.tiles.pop() as Tile; + const family = lastTile.getFamily(); + const value = lastTile.getValue(); + + // Try to form a pair + if (this.count(family, value) >= 1 && !pair) { + const result = this.tryFormPair(lastTile); + if (result) return result; + } + + // Try to form a triplet (pon) + if (this.count(family, value) >= 2) { + const result = this.tryFormTriplet(lastTile, pair); + if (result) return result; + } + + // Try to form a sequence (chii) + const hasMinusOne = this.count(family, value - 1) > 0; + const hasMinusTwo = this.count(family, value - 2) > 0; + + if (hasMinusOne && hasMinusTwo) { + const result = this.tryFormSequence(lastTile, pair); + if (result) return result; + } + + // If no valid group could be formed, put tile back and return undefined + this.tiles.push(lastTile); + this.sort(); + return undefined; + } + + /** + * Try to form a pair with the given tile + */ + private tryFormPair(tile: Tile): Array | undefined { + const pairTile = this.find(tile.getFamily(), tile.getValue()) as Tile; + const groups = this.toGroup(true); + + // Put the tile back + this.tiles.push(pairTile); + this.sort(); + + if (groups !== undefined) { + this.tiles.push(tile); + this.sort(); + groups.push(new Group([tile, pairTile], 0, 0)); + return groups; + } + + return undefined; + } + + /** + * Try to form a triplet (pon) with the given tile + */ + private tryFormTriplet(tile: Tile, pair: boolean): Array | undefined { + const secondTile = this.find(tile.getFamily(), tile.getValue()) as Tile; + const thirdTile = this.find(tile.getFamily(), tile.getValue()) as Tile; + + const groups = this.toGroup(pair); + + // Put tiles back + this.tiles.push(secondTile); + this.tiles.push(thirdTile); + this.sort(); + + if (groups !== undefined) { + groups.push(new Group([tile, secondTile, thirdTile], 0, 0)); + this.tiles.push(tile); + this.sort(); + return groups; + } + + return undefined; + } + + /** + * Try to form a sequence (chii) with the given tile + */ + private tryFormSequence(tile: Tile, pair: boolean): Array | undefined { + const secondTile = this.find(tile.getFamily(), tile.getValue() - 1) as Tile; + const thirdTile = this.find(tile.getFamily(), tile.getValue() - 2) as Tile; + + const groups = this.toGroup(pair); + + // Put tiles back + this.tiles.push(secondTile); + this.tiles.push(thirdTile); + this.sort(); + + if (groups !== undefined) { + groups.push(new Group([thirdTile, secondTile, tile], 0, 0)); + this.tiles.push(tile); + this.sort(); + return groups; + } + + return undefined; + } + + /** + * Draw hand tiles on canvas + */ + public drawHand( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + offset: number, + size: number, + focusedTile: number | undefined = undefined, + hidden: boolean = false, + rotation: number = 0 + ): void { + const tileOffset = (75 + offset) * size; + const offsetX = Math.cos(rotation) * tileOffset; + const offsetY = Math.sin(rotation) * tileOffset; + + for (let i = 0; i < this.tiles.length; i++) { + const isLastAndIsolated = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0; + + // Calculate position + let tileX = x + i * offsetX + isLastAndIsolated * size * Math.cos(rotation); + let tileY = y + i * offsetY + isLastAndIsolated * size * Math.sin(rotation); + + // Add additional offset for focused tile + if (i === focusedTile) { + tileX += 25 * size * Math.sin(rotation); + tileY -= 25 * size * Math.cos(rotation); + } + + // Draw tile + this.tiles[i].drawTile( + ctx, + tileX, + tileY, + size, + hidden, + rotation + ); + } + } + + /** + * Preload tile images + */ + public async preload(): Promise { + await Promise.all(this.tiles.map(tile => tile.preloadImg())); + } + + /** + * Clean up resources + */ + public cleanup(): void { + this.tiles.forEach(tile => tile.cleanup()); + this.tiles = []; + } } diff --git a/src/tile.ts b/src/tile.ts index bec0b1a..c3ccda7 100644 --- a/src/tile.ts +++ b/src/tile.ts @@ -1,181 +1,156 @@ export class Tile { - private family: number; - private value: number; - private red: boolean; - private imgSrc: string; - private imgFront: HTMLImageElement; - private imgBack: HTMLImageElement; - private imgGray: HTMLImageElement; - private img: HTMLImageElement; - private tilt: number; - - public constructor(family: number, value: number , red: boolean) { - this.family = family; - this.value = value; - this.red = red; - this.imgSrc = ""; - this.imgFront = new Image(); - this.imgBack = new Image(); - this.imgGray = new Image(); - this.img = new Image(); - this.tilt = 0; - this.setImgSrc(); - } + private imgFront: HTMLImageElement = new Image(); + private imgBack: HTMLImageElement = new Image(); + private imgGray: HTMLImageElement = new Image(); + private img: HTMLImageElement = new Image(); + private imgSrc: string = ""; + private tilt: number = 0; + + constructor( + private family: number, + private value: number, + private red: boolean + ) { + this.setImgSrc(); + } - public getFamily(): number { - return this.family; - } + public getFamily(): number { + return this.family; + } - public getValue(): number { - return this.value; - } + public getValue(): number { + return this.value; + } - public isEqual(family: number, value: number): boolean { - return this.family === family && this.value === value; - } + public isEqual(family: number, value: number): boolean { + return this.family === family && this.value === value; + } - public isRed(): boolean { - return this.red; - } + public isRed(): boolean { + return this.red; + } - public compare(t: Tile): number { - if (this.family < t.family) { - return -1; - } else if (this.family > t.family) { - return 1; - } - if (this.value < t.value) { - return -1; - } else if (this.value > t.value) { - return 1; - } - return 0; - } + public compare(t: Tile): number { + // Compare d'abord par famille, puis par valeur + if (this.family !== t.family) { + return this.family < t.family ? -1 : 1; + } + if (this.value !== t.value) { + return this.value < t.value ? -1 : 1; + } + return 0; + } - public setTilt(): void { - this.tilt = (1 - 2 * Math.random()) * 0.04; - } + public isLessThan(t: Tile): boolean { + return this.family < t.family || + (this.family === t.family && this.value <= t.value); + } - public drawTile( - ctx: CanvasRenderingContext2D, - x: number, - y: number, - size: number, - hidden: boolean = false, - rotation: number = 0, - gray: boolean = false, - tilted: boolean = true - ): void { - ctx.save(); - ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2); - if (tilted) { - ctx.rotate(rotation + this.tilt); - } else { - ctx.rotate(rotation); - } + public setTilt(): void { + this.tilt = (1 - 2 * Math.random()) * 0.04; + } - if (hidden) { - ctx.drawImage( // ombre - this.imgGray, - -(75 * size * 0.92) / 2, - -(100 * size * 0.91) / 2, - 75 * size, - 100 * size - ); - ctx.drawImage( // le dos des tuiles - this.imgBack, - -(75 * size) / 2, - -(100 * size) / 2, - 75 * size, - 100 * size - ); - } else { - ctx.drawImage( // ombre - this.imgGray, - -(75 * size * 0.92) / 2, - -(100 * size * 0.91) / 2, - 75 * size, - 100 * size - ); - ctx.drawImage( // tuile à vide - this.imgFront, - -(75 * size) / 2, - -(100 * size) / 2, - 75 * size, 100 * size - ); - ctx.drawImage( // le dessin sur la tuile - this.img, - -((75 - 7) * size) / 2, - -((100 - 10) * size) / 2, - 75 * size * 0.9, - 100 * size * 0.9 - ); - if (gray) { // grisé - ctx.drawImage( - this.imgGray, - -(75 * size) / 2, - -(100 * size) / 2, - 75 * size, - 100 * size - ); - } - } + public drawTile( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + size: number, + hidden: boolean = false, + rotation: number = 0, + gray: boolean = false, + tilted: boolean = true + ): void { + const tileWidth = 75 * size; + const tileHeight = 100 * size; + const halfWidth = tileWidth / 2; + const halfHeight = tileHeight / 2; + const shadowScale = 0.92; + + // Sauvegarde du contexte et positionnement + ctx.save(); + ctx.translate(x + halfWidth, y + halfHeight); + ctx.rotate(rotation + (tilted ? this.tilt : 0)); + + // Position de l'ombre (légèrement décalée) + const shadowX = -(tileWidth * shadowScale) / 2; + const shadowY = -(tileHeight * shadowScale) / 2; + + // Dessin de l'ombre (commun aux deux cas) + ctx.drawImage(this.imgGray, shadowX, shadowY, tileWidth, tileHeight); + + if (hidden) { + // Dessin du dos de la tuile + ctx.drawImage(this.imgBack, -halfWidth, -halfHeight, tileWidth, tileHeight); + } else { + // Dessin de la tuile face visible + ctx.drawImage(this.imgFront, -halfWidth, -halfHeight, tileWidth, tileHeight); + + // Dessin du motif sur la tuile (légèrement plus petit) + const patternScale = 0.9; + const patternWidth = tileWidth * patternScale; + const patternHeight = tileHeight * patternScale; + const patternX = -((75 - 7) * size) / 2; + const patternY = -((100 - 10) * size) / 2; + + ctx.drawImage(this.img, patternX, patternY, patternWidth, patternHeight); + + // Appliquer un filtre gris si demandé + if (gray) { + ctx.drawImage(this.imgGray, -halfWidth, -halfHeight, tileWidth, tileHeight); + } + } + + ctx.restore(); + } - ctx.restore(); - } + public cleanup(): void { + // Supprimer tous les gestionnaires d'événements + const images = [this.imgFront, this.imgBack, this.imgGray, this.img]; + images.forEach(img => { + img.onload = null; + img.onerror = null; + }); + } - public isLessThan(t: Tile): boolean { - if (this.family < t.family) { - return true; - } else if (this.family === t.family && this.value <= t.value) { - return true; - } else { - return false; - } - } + public async preloadImg(): Promise { + const imagesToLoad = [ + { 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 } + ]; + + await Promise.all( + imagesToLoad.map(({ img, src }) => this.loadImg(img, src)) + ); + } - public cleanup(): void { - this.imgFront.onload = null; - this.imgFront.onerror = null; - this.imgBack.onload = null; - this.imgBack.onerror = null; - this.imgGray.onload = null; - this.imgGray.onerror = null; - this.img.onload = null; - this.img.onerror = null; - } + private loadImg(img: HTMLImageElement, src: string): Promise { + return new Promise((resolve, reject) => { + img.onload = () => resolve(); + img.onerror = () => reject(); + img.src = src; + }); + } - private setImgSrc(): void { - this.imgSrc = "img/Regular/" - if (this.family <= 3) { - this.imgSrc += ["", "Man", "Pin", "Sou"][this.family]; - this.imgSrc += String(this.value); - if (this.red) { - this.imgSrc += "-Dora"; - } - this.imgSrc += ".svg"; - } else if (this.family === 4) { - this.imgSrc += ["", "Ton", "Nan", "Shaa", "Pei"][this.value] + ".svg"; - } else if (this.family === 5) { - this.imgSrc += ["", "Chun", "Hatsu", "Haku"][this.value] + ".svg"; - } - } - - public async preloadImg(): Promise { - await Promise.all([ - this.loadImg(this.imgFront, "img/Regular/Front.svg"), - // this.loadImg(this.imgFront, "/~img/Export/Regular/Front.png"), - this.loadImg(this.imgBack, "img/Regular/Back.svg"), - this.loadImg(this.imgGray, "img/Regular/Gray.svg"), - this.loadImg(this.img, this.imgSrc) - ]); - } - - private loadImg(img: HTMLImageElement, src: string): Promise { - return new Promise((resolve, reject) => { - img.onload = () => resolve(); - img.onerror = () => reject(); - img.src = src; - }); - } + private setImgSrc(): void { + this.imgSrc = "img/Regular/"; + + if (this.family <= 3) { + const families = ["", "Man", "Pin", "Sou"]; + this.imgSrc += families[this.family] + String(this.value); + + if (this.red) { + this.imgSrc += "-Dora"; + } + } else if (this.family === 4) { + const winds = ["", "Ton", "Nan", "Shaa", "Pei"]; + this.imgSrc += winds[this.value]; + } else if (this.family === 5) { + const dragons = ["", "Chun", "Hatsu", "Haku"]; + this.imgSrc += dragons[this.value]; + } + + this.imgSrc += ".svg"; + } } diff --git a/tsconfig.json b/tsconfig.json index cde939d..ac3c012 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2015", "module": "esnext", "strict": true, }