/**** * Classes ****/ var Card = Container.expand(function (context, uid, index) { var self = Container.call(this); self.row = 0; self.col = 0; self.uid = uid; self.id = index; if (!index) { self.isMatched = true; return self; } self.isMatched = false; nbCardsLeft++; self.shakeAnimation = function (card) { if (!card) { return; } var shakeCount = 0; var shakeTimes = 9; var shakeStrength = 70; var originalX = card.x; var originalY = card.y; function shake() { if (shakeCount < shakeTimes) { card.x = originalX + (Math.random() - 0.5) * shakeStrength; shakeCount++; LK.setTimeout(shake, 50); } else { card.x = originalX; card.y = originalY; card.scale.x = 1; card.scale.y = 1; if (card === selectedCard1) { selectedCard1 = null; } if (card === selectedCard2) { selectedCard2 = null; } isAnimating = false; } } shake(); }; self.id = index; self.on('down', function (x, y, obj) { LK.getSound('select').play(); if (!self.isMatched && !isAnimating) { if (!selectedCard1 || selectedCard1 && selectedCard2) { if (selectedCard1) { selectedCard1.scale.x = 1; selectedCard1.scale.y = 1; if (selectedCard1 === self) { selectedCard1 = null; return; } } selectedCard1 = self; selectedCard2 = null; self.scale.x = 1.2; self.scale.y = 1.2; } else if (selectedCard1 && !selectedCard2 && selectedCard1 === self) { selectedCard1.scale.x = 1; selectedCard1.scale.y = 1; selectedCard1 = null; } else if (selectedCard1 && !selectedCard2 && selectedCard1 !== self) { selectedCard2 = self; var canLink = canBeLinked(selectedCard1, selectedCard2); self.scale.x = 1.2; self.scale.y = 1.2; if (canLink) { LK.getSound('cardsOk').play(); lastMatched = true; selectedCard1.match(selectedCard2); selectedCard2.match(selectedCard1); selectedCard1 = null; selectedCard2 = null; } else { LK.getSound('cardsKo').play(); lastMatched = false; isAnimating = true; LK.setTimeout(function () { self.shakeAnimation(selectedCard1); self.shakeAnimation(selectedCard2); }, 300); } } } }); var cardGraphics = self.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); self.match = function (otherCard) { if (checkGameEndTimeout) { LK.clearTimeout(checkGameEndTimeout); } var line = new Line(self.x, self.y, otherCard.x, otherCard.y); game.addChild(line); var tadaAnimationSteps = 10; var tadaAnimationCount = 0; var scaleDelta = 0.2; var tadaDelay = 0; var tadaDelayMax = 60; self.tadaAnimation = function () { if (tadaAnimationCount < tadaAnimationSteps) { self.scale.x += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta; self.scale.y += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta; tadaAnimationCount++; } else { LK.off('tick', self.tadaAnimation); } }; self.shrinkAnimation = function () { if (self.scale.x > 0.1) { self.scale.x -= 0.1; self.scale.y -= 0.1; self.rotation += 0.1; } else { self.visible = false; LK.off('tick', self.tadaAnimation); LK.off('tick', self.shrinkAnimation); currentSolvedPairs[self.id] = true; currentNbSolved = Object.keys(currentSolvedPairs).length; if (checkGameEndTimeout) { LK.clearTimeout(checkGameEndTimeout); } checkGameEndTimeout = LK.setTimeout(function () { if (currentNbSolved >= lastNbPossible) { checkGameEnd(context, self.id); } }, 1000); } }; LK.on('tick', self.tadaAnimation); self.isMatched = true; LK.setTimeout(function () { LK.on('tick', self.shrinkAnimation); }, tadaAnimationSteps * 50); LK.setTimeout(function () { line.destroy(); }, 512); }; var picture = self.addChild(new Picture(index)); picture.x = 0; picture.y = 0; return self; }); var Line = Container.expand(function (startX, startY, endX, endY) { var self = Container.call(this); var lineGraphics = self.attachAsset('line', {}); lineGraphics.width = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)); lineGraphics.rotation = Math.atan2(endY - startY, endX - startX); lineGraphics.x = startX; lineGraphics.y = startY; lineGraphics.alpha = 0.6; self.addChild(lineGraphics); return self; }); var Picture = Container.expand(function (index) { var self = Container.call(this); self.index = index; var pictureGraphics = self.createAsset('picture' + self.index, { anchorX: 0.5, anchorY: 0.5 }); return self; }); var TimerLine = Container.expand(function (width, height) { var self = Container.call(this); var timerGraphics = self.attachAsset('timerLine', { anchorX: 0.5, anchorY: 0.5 }); timerGraphics.width = width; timerGraphics.height = height; timerGraphics.tint = 0x00FF00; self.addChild(timerGraphics); self._update_migrated = function (elapsedTime) { var totalTicks = levelDurationInSeconds * 1000; var deltaWidth = this.initialWidth / totalTicks; timerGraphics.width = Math.max(0, timerGraphics.width - deltaWidth * elapsedTime); if (timerGraphics.width / this.initialWidth < 0.25) { timerGraphics.tint = 0xFF1E1E - Math.round(0xFF * (this.initialWidth / timerGraphics.width)) << 16; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) { return _arrayLikeToArray(r, a); } var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) { n[e] = r[e]; } return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) { return; } f = !1; } else { for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) { ; } } } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) { return; } } finally { if (o) { throw n; } } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) { return r; } } var level = 0; var levels = [{ 'rows': 2, 'cols': 2, 'time': 20 }, { 'rows': 4, 'cols': 4, 'time': 60 }, { 'rows': 6, 'cols': 6, 'time': 120 }, { 'rows': 8, 'cols': 8, 'time': 180 }, { 'rows': 10, 'cols': 10, 'time': 300 }, { 'rows': 11, 'cols': 10, 'time': 420 }, { 'rows': 12, 'cols': 10, 'time': 600 }]; level = Math.min(levels.length - 1, Math.max(0, level)); var levelDurationInSeconds = levels[level].time; var MAX_INIT_TRIES = 1000; var debug = false; var lastMatched = false; var cardPairs = []; var cards = []; var selectedCard1 = null; var selectedCard2 = null; var isInitializing = false; var isAnimating = false; var isRemixing = false; var nbRows = 2 + 2; var nbCols = 1 + 2; var nbCards = nbRows * nbCols; var nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2; var nbCardsLeft = 0; var lastNbPossible = 0; var currentSolvedPairs = []; var currentNbSolved = 0; var cardWidth = 200; var cardHeight = 200; var horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2; var verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2; var shuffleDifficulty = 4; var lastCheckCaller = 0; var isPlaying = false; var isGameEndChecking = false; var isTryingInitGame = false; var checkGameEndTimeout = null; window.passConfettiAnim = false; var applauseSound = LK.getSound('applause'); var bgMusic; if (nbCards % 2) { console.error('Invalid number of cards ! Should be even.', nbCards); LK.showGameOver(); nbCols++; } function loopBgMusic() { if (bgMusic && Date.now() - bgMusic.lastPlayTime > 10000) { bgMusic.lastPlayTime = Date.now(); bgMusic.play(); } } function prepareConfig(level) { cardPairs = []; cards.forEach(function (row) { row.forEach(function (card) { if (card) { card.destroy(); } }); }); cards = []; nbRows = levels[level].rows + 2; nbCols = levels[level].cols + 2; nbCards = nbRows * nbCols; if (nbCards % 2) { console.error('Invalid number of cards ! Should be even.', nbCards); LK.showGameOver(); nbCols++; } nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2; horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2; verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2; nbCardsLeft = 0; lastNbPossible = 0; currentSolvedPairs = []; currentNbSolved = 0; lastCheckCaller = 0; checkGameEndTimeout = null; selectedCard1 = null; selectedCard2 = null; isAnimating = false; isRemixing = false; } function timeBasedRandom() { var currentTime = Date.now(); var timeSeed = currentTime % 1000; var randomValue = Math.random() * timeSeed; return randomValue - Math.floor(randomValue); } function shuffleArray(array, onlyNonEmpty) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(timeBasedRandom() * (i + 1)); if (onlyNonEmpty && (!array[i] || !array[j])) { continue; } var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } function shuffleWithDifficulty(array, difficulty, onlyNonEmpty) { difficulty = Math.max(0, Math.min(10, difficulty)); var maxSwapDistance = Math.ceil(array.length * (difficulty / 10)); var numberOfSwaps = Math.ceil(array.length * (difficulty / 10)); for (var i = 0; i < numberOfSwaps; i++) { var firstIndex = Math.floor(timeBasedRandom() * array.length); var swapDistance = Math.floor(timeBasedRandom() * maxSwapDistance); var secondIndex = (firstIndex + swapDistance) % array.length; if (onlyNonEmpty && (!array[firstIndex] || !array[secondIndex])) { continue; } var temp = array[firstIndex]; array[firstIndex] = array[secondIndex]; array[secondIndex] = temp; } ensureAdjacentIndexes(array, difficulty); return array; } function ensureAdjacentIndexes(array, difficulty) { var numberOfPairsToSwap = Math.floor(array.length * 0.5 * (1 / difficulty) * 0.5); var pairsToSwap = []; for (var i = 0; i < array.length - 1; i++) { for (var j = i + 1; j < array.length; j++) { if (array[i] && array[j] && array[i] === array[j] && !pairsToSwap.includes(array[i])) { pairsToSwap.push(array[i]); } } } shuffleArray(pairsToSwap, false); pairsToSwap = pairsToSwap.slice(0, numberOfPairsToSwap); for (var i = 0; i < array.length - 1; i++) { for (var j = i + 1; j < array.length; j++) { if (array[i] && array[j] && array[i] === array[j] && pairsToSwap.includes(array[i]) && array[i + 1]) { var temp = array[i + 1]; array[i + 1] = array[j]; array[j] = temp; pairsToSwap.splice(pairsToSwap.indexOf(array[i]), 1); break; } } } } function canBeLinked(card1, card2) { if (card1 === card2 || !card1 || !card2 || card1.id !== card2.id) { return false; } var visited = Array.from({ length: nbRows }, function () { return Array(nbCols).fill(false); }); function dfs(card, bends, direction) { if (!card || visited[card.row][card.col] || bends > 2) { return false; } if (card === card2) { return true; } visited[card.row][card.col] = true; var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; for (var _i = 0, _directions = directions; _i < _directions.length; _i++) { var _directions$_i = _slicedToArray(_directions[_i], 2), di = _directions$_i[0], dj = _directions$_i[1]; var ni = card.row + di, nj = card.col + dj; if (ni >= 0 && ni < nbRows && nj >= 0 && nj < nbCols) { var nextCard = cards[ni][nj]; if (!nextCard || !nextCard.id || nextCard.isMatched || nextCard === card2) { if (di !== 0) { if (dfs(nextCard, bends + (direction === -1), 1)) { return true; } } else if (dj !== 0) { if (dfs(nextCard, bends + (direction === 1), -1)) { return true; } } } } } visited[card.row][card.col] = false; return false; } return dfs(card1, 0); } function remixCards(currentDifficulty, callback) { if (isRemixing) { return false; } isRemixing = true; var unmatchedCards = cards.flat().filter(function (card) { return !card.isMatched; }); var oldPositions = unmatchedCards.map(function (card) { return { uid: card.uid, id: card.id, x: card.x, y: card.y }; }); var indexes = unmatchedCards.map(function (card) { return card.id; }); indexes = shuffleWithDifficulty(indexes, currentDifficulty); var reorderedPosition = []; var treatedIndexes = []; for (var i = 0; i < oldPositions.length; i++) { var originId = oldPositions[i].id; for (var j = 0; j < indexes.length; j++) { var newId = indexes[j]; if (originId === newId && !treatedIndexes[j]) { reorderedPosition[i] = { uid: oldPositions[j].uid, id: oldPositions[j].id, x: oldPositions[j].x, y: oldPositions[j].y }; treatedIndexes[j] = true; break; } } } unmatchedCards.forEach(function (card, index) { var newPos = reorderedPosition[index]; var moveCard = function moveCard() { var dx = 0; var dy = 0; if (newPos) { dx = newPos.x - card.x; dy = newPos.y - card.y; } if (index == 1) {} var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 1) { card.x = newPos.x; card.y = newPos.y; var newCol = Math.floor((card.x - horizontalOffset) / cardWidth); var newRow = Math.floor((card.y - verticalOffset) / cardHeight); if (cards[card.row] && cards[card.row][card.col] === card) { cards[card.row][card.col] = undefined; } card.col = newCol; card.row = newRow; cards[newRow][newCol] = card; LK.off('tick', moveCard); } else { card.x += dx * 0.05; card.y += dy * 0.05; } }; LK.on('tick', moveCard); }); LK.setTimeout(function () { isRemixing = false; if (callback) { callback(); } }, 4000); } function checkGameEnd(context, callerId) { if (isGameEndChecking) { return; } if (lastCheckCaller === callerId) { return; } isGameEndChecking = true; lastCheckCaller = callerId; nbCardsLeft = cards.flat().filter(function (card) { return !card.isMatched; }).length; if (!nbCardsLeft) { isPlaying = false; LK.setTimeout(function () { LK.getSound('winChime').play(); applauseSound.play(); if (level + 1 >= levels.length) { showConfettiAnimation(context, function () { LK.showGameOver(); }, true); } else { level++; level = Math.min(levels.length - 1, Math.max(0, level)); showConfettiAnimation(context, function () { isGameEndChecking = false; tryInitGame(context); }); } }, 300); return; } if (currentNbSolved >= lastNbPossible) { var isPossible = checkBoardPossible(); var canContinue = currentNbSolved < lastNbPossible; if (!canContinue) { tryRemixCards(); } } isGameEndChecking = false; } function checkBoardPossible() { var nbPossible = 0; var nbImpossible = 0; var indexesChecked = []; for (var i = 0; i < cardPairs.length; i++) { var index = cardPairs[i]; if (!index || indexesChecked[index]) { continue; } var card1 = cards.flat().find(function (card) { return card.id === index; }); var card2 = cards.flat().find(function (card) { return card.id === index && card !== card1; }); if (canBeLinked(card1, card2)) { nbPossible++; } else { nbImpossible++; } indexesChecked[index] = true; } lastNbPossible = nbPossible; return Math.max(currentNbSolved, nbPossible) >= (nbImpossible + nbPossible) * 0.1; } function initGame(self, currentDifficulty) { if (isInitializing) { console.log('already Initializing...'); return; } isInitializing = true; prepareConfig(level); var nextPairIndex = 1; for (var i = 0; i < nbCards; i++) { var row = Math.floor(i / nbCols); var col = i % nbCols; if (row === 0 || row === nbRows - 1 || col === 0 || col === nbCols - 1) { cardPairs.push(0); } else { cardPairs.push(nextPairIndex); nextPairIndex = nextPairIndex % nbPairs + 1; } } cardPairs = shuffleWithDifficulty(cardPairs, currentDifficulty, true); for (var i = 0; i < nbCards; i++) { var row = Math.floor(i / nbCols); var col = i % nbCols; var card = self.addChild(new Card(self, i, cardPairs[i])); card.col = col; card.row = row; card.x = horizontalOffset + card.col * cardWidth; card.y = verticalOffset + card.row * cardHeight; if (!cards[card.row]) { cards[card.row] = []; } cards[card.row][card.col] = card; } isInitializing = false; if (!bgMusic) { bgMusic = LK.getSound('music'); bgMusic.lastPlayTime = 0; } } game.update = function () { if (bgMusic) { loopBgMusic(); } }; function tryInitGame(context) { if (isTryingInitGame) { console.log('Already tryInitGame...'); return; } var isPossible = false; var nbTries = 0; var currentDifficulty = shuffleDifficulty; while (!isPossible && nbTries <= MAX_INIT_TRIES) { initGame(context, currentDifficulty); isPossible = checkBoardPossible(); isPlaying = isPossible; nbTries++; currentDifficulty--; } if (nbTries > MAX_INIT_TRIES) { console.warn('Unable to init game after ' + nbTries + ' tries'); } isTryingInitGame = false; } function tryRemixCards(nbTries, currentDifficulty) { nbTries = nbTries || 0; currentDifficulty = currentDifficulty || shuffleDifficulty; remixCards(currentDifficulty, function () { var isPossible = checkBoardPossible(); nbTries++; currentDifficulty--; if (nbTries > MAX_INIT_TRIES) { isPossible = true; console.warn('Unable to remix game after ' + nbTries + ' tries'); } if (!isPossible) { tryRemixCards(nbTries, currentDifficulty); } }); } function showConfettiAnimation(self, callback, isFinal) { var backgroundImage = self.createAsset('background' + (level + (isFinal ? 1 : 0)), { anchorX: 0.5, anchorY: 0.5 }); backgroundImage.x = 2048 / 2; backgroundImage.y = 2732 / 2; self.addChild(backgroundImage); var message = isFinal ? 'Congratulations!' : 'Well done!'; var nextMessage = isFinal ? 'Tank You!' : 'Continue...'; var congratulationsText = new Text2(message, { size: 150, fill: '#ffffff' }); congratulationsText.anchor.set(0.5, 0); congratulationsText.x = 0; congratulationsText.y = 20; LK.gui.top.addChild(congratulationsText); var nextText = new Text2(nextMessage, { size: 120, fill: '#ffffff' }); nextText.anchor.set(0.5, 1.25); nextText.x = 0; nextText.y = 0; LK.gui.bottom.addChild(nextText); // Automatically continue after 5 seconds var continueTimeout = LK.setTimeout(function () { if (backgroundImage && congratulationsText && nextText) { backgroundImage.destroy(); congratulationsText.destroy(); nextText.destroy(); } callback(); }, 5000); backgroundImage.on('down', function (x, y, obj) { backgroundImage.destroy(); congratulationsText.destroy(); nextText.destroy(); if (applauseSound) { applauseSound.stop(); } if (continueTimeout) { LK.clearTimeout(continueTimeout); } callback(); }); } function preload(context) { var titleText = new Text2('Holidays Links', { size: 200, fill: '#d7c797', anchor: { x: 0.5, y: 0.5 } }); titleText.x = 100; titleText.y = 2732 / 8; LK.gui.addChild(titleText); var loadingText = new Text2('Loading...', { size: 100, fill: '#d7c797', anchor: { x: 0.5, y: 0.5 } }); loadingText.x = 2048 / 4; loadingText.y = 2732 / 4; LK.gui.addChild(loadingText); var assetsToLoad = ['line', 'picture1', 'picture2', 'picture3', 'picture8', 'picture7', 'picture24', 'picture17', 'picture16', 'picture4', 'picture29', 'picture6', 'picture14', 'picture15', 'picture20', 'picture9', 'picture10', 'picture27', 'picture12', 'picture19', 'picture26', 'picture22', 'picture18', 'picture28', 'picture23', 'picture13', 'picture21', 'picture5', 'picture11', 'picture25', 'picture30', 'picture32', 'picture31', 'picture37', 'picture39', 'picture44', 'picture38', 'picture48', 'picture47', 'picture50', 'picture42', 'picture46', 'picture45', 'picture43', 'picture49', 'picture34', 'picture33', 'picture40', 'picture36', 'picture35', 'picture41', 'picture55', 'picture52', 'picture54', 'picture53', 'picture51', 'picture56', 'picture59', 'picture60', 'picture57', 'picture58', 'picture64', 'picture63', 'picture61', 'picture62', 'picture65', 'card', 'timerLine', 'background1', 'background2', 'background3', 'background4', 'background5', 'background6', 'background7']; var loadedAssets = 0; assetsToLoad.forEach(function (asset) { var assetInstance = LK.getAsset(asset, { anchorX: 0.5, anchorY: 0.5 }); loadedAssets++; if (loadedAssets === assetsToLoad.length) { LK.setTimeout(function () { loadingText.destroy(); titleText.destroy(); tryInitGame(context); }, 1200); } }); } preload(game); if (debug) { game.timerLine = new TimerLine(1400, 20); game.timerLine.initialWidth = 1400; game.timerLine.x = 710; game.timerLine.y = game.timerLine.y; LK.gui.topLeft.addChild(game.timerLine); var tickTime = Date.now(); LK.on('tick', function () { if (isPlaying && !isRemixing) { var elapsedTime = Date.now() - tickTime; game.timerLine._update_migrated(elapsedTime); tickTime = Date.now(); } }); var matchDebugText = new Text2('Match:', { size: 50, fill: '#ffffff' }); matchDebugText.anchor.set(0, 0); LK.gui.topLeft.addChild(matchDebugText); LK.on('tick', function () { matchDebugText.setText('Level: ' + (level + 1) + ' Solved: ' + currentNbSolved + '/' + lastNbPossible + ' / isPlaying=' + isPlaying + ' / time=' + levelDurationInSeconds); }); }
/****
* Classes
****/
var Card = Container.expand(function (context, uid, index) {
var self = Container.call(this);
self.row = 0;
self.col = 0;
self.uid = uid;
self.id = index;
if (!index) {
self.isMatched = true;
return self;
}
self.isMatched = false;
nbCardsLeft++;
self.shakeAnimation = function (card) {
if (!card) {
return;
}
var shakeCount = 0;
var shakeTimes = 9;
var shakeStrength = 70;
var originalX = card.x;
var originalY = card.y;
function shake() {
if (shakeCount < shakeTimes) {
card.x = originalX + (Math.random() - 0.5) * shakeStrength;
shakeCount++;
LK.setTimeout(shake, 50);
} else {
card.x = originalX;
card.y = originalY;
card.scale.x = 1;
card.scale.y = 1;
if (card === selectedCard1) {
selectedCard1 = null;
}
if (card === selectedCard2) {
selectedCard2 = null;
}
isAnimating = false;
}
}
shake();
};
self.id = index;
self.on('down', function (x, y, obj) {
LK.getSound('select').play();
if (!self.isMatched && !isAnimating) {
if (!selectedCard1 || selectedCard1 && selectedCard2) {
if (selectedCard1) {
selectedCard1.scale.x = 1;
selectedCard1.scale.y = 1;
if (selectedCard1 === self) {
selectedCard1 = null;
return;
}
}
selectedCard1 = self;
selectedCard2 = null;
self.scale.x = 1.2;
self.scale.y = 1.2;
} else if (selectedCard1 && !selectedCard2 && selectedCard1 === self) {
selectedCard1.scale.x = 1;
selectedCard1.scale.y = 1;
selectedCard1 = null;
} else if (selectedCard1 && !selectedCard2 && selectedCard1 !== self) {
selectedCard2 = self;
var canLink = canBeLinked(selectedCard1, selectedCard2);
self.scale.x = 1.2;
self.scale.y = 1.2;
if (canLink) {
LK.getSound('cardsOk').play();
lastMatched = true;
selectedCard1.match(selectedCard2);
selectedCard2.match(selectedCard1);
selectedCard1 = null;
selectedCard2 = null;
} else {
LK.getSound('cardsKo').play();
lastMatched = false;
isAnimating = true;
LK.setTimeout(function () {
self.shakeAnimation(selectedCard1);
self.shakeAnimation(selectedCard2);
}, 300);
}
}
}
});
var cardGraphics = self.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
self.match = function (otherCard) {
if (checkGameEndTimeout) {
LK.clearTimeout(checkGameEndTimeout);
}
var line = new Line(self.x, self.y, otherCard.x, otherCard.y);
game.addChild(line);
var tadaAnimationSteps = 10;
var tadaAnimationCount = 0;
var scaleDelta = 0.2;
var tadaDelay = 0;
var tadaDelayMax = 60;
self.tadaAnimation = function () {
if (tadaAnimationCount < tadaAnimationSteps) {
self.scale.x += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta;
self.scale.y += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta;
tadaAnimationCount++;
} else {
LK.off('tick', self.tadaAnimation);
}
};
self.shrinkAnimation = function () {
if (self.scale.x > 0.1) {
self.scale.x -= 0.1;
self.scale.y -= 0.1;
self.rotation += 0.1;
} else {
self.visible = false;
LK.off('tick', self.tadaAnimation);
LK.off('tick', self.shrinkAnimation);
currentSolvedPairs[self.id] = true;
currentNbSolved = Object.keys(currentSolvedPairs).length;
if (checkGameEndTimeout) {
LK.clearTimeout(checkGameEndTimeout);
}
checkGameEndTimeout = LK.setTimeout(function () {
if (currentNbSolved >= lastNbPossible) {
checkGameEnd(context, self.id);
}
}, 1000);
}
};
LK.on('tick', self.tadaAnimation);
self.isMatched = true;
LK.setTimeout(function () {
LK.on('tick', self.shrinkAnimation);
}, tadaAnimationSteps * 50);
LK.setTimeout(function () {
line.destroy();
}, 512);
};
var picture = self.addChild(new Picture(index));
picture.x = 0;
picture.y = 0;
return self;
});
var Line = Container.expand(function (startX, startY, endX, endY) {
var self = Container.call(this);
var lineGraphics = self.attachAsset('line', {});
lineGraphics.width = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
lineGraphics.rotation = Math.atan2(endY - startY, endX - startX);
lineGraphics.x = startX;
lineGraphics.y = startY;
lineGraphics.alpha = 0.6;
self.addChild(lineGraphics);
return self;
});
var Picture = Container.expand(function (index) {
var self = Container.call(this);
self.index = index;
var pictureGraphics = self.createAsset('picture' + self.index, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var TimerLine = Container.expand(function (width, height) {
var self = Container.call(this);
var timerGraphics = self.attachAsset('timerLine', {
anchorX: 0.5,
anchorY: 0.5
});
timerGraphics.width = width;
timerGraphics.height = height;
timerGraphics.tint = 0x00FF00;
self.addChild(timerGraphics);
self._update_migrated = function (elapsedTime) {
var totalTicks = levelDurationInSeconds * 1000;
var deltaWidth = this.initialWidth / totalTicks;
timerGraphics.width = Math.max(0, timerGraphics.width - deltaWidth * elapsedTime);
if (timerGraphics.width / this.initialWidth < 0.25) {
timerGraphics.tint = 0xFF1E1E - Math.round(0xFF * (this.initialWidth / timerGraphics.width)) << 16;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) {
return _arrayLikeToArray(r, a);
}
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) {
n[e] = r[e];
}
return n;
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) {
if (Object(t) !== t) {
return;
}
f = !1;
} else {
for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) {
;
}
}
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) {
return;
}
} finally {
if (o) {
throw n;
}
}
}
return a;
}
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) {
return r;
}
}
var level = 0;
var levels = [{
'rows': 2,
'cols': 2,
'time': 20
}, {
'rows': 4,
'cols': 4,
'time': 60
}, {
'rows': 6,
'cols': 6,
'time': 120
}, {
'rows': 8,
'cols': 8,
'time': 180
}, {
'rows': 10,
'cols': 10,
'time': 300
}, {
'rows': 11,
'cols': 10,
'time': 420
}, {
'rows': 12,
'cols': 10,
'time': 600
}];
level = Math.min(levels.length - 1, Math.max(0, level));
var levelDurationInSeconds = levels[level].time;
var MAX_INIT_TRIES = 1000;
var debug = false;
var lastMatched = false;
var cardPairs = [];
var cards = [];
var selectedCard1 = null;
var selectedCard2 = null;
var isInitializing = false;
var isAnimating = false;
var isRemixing = false;
var nbRows = 2 + 2;
var nbCols = 1 + 2;
var nbCards = nbRows * nbCols;
var nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2;
var nbCardsLeft = 0;
var lastNbPossible = 0;
var currentSolvedPairs = [];
var currentNbSolved = 0;
var cardWidth = 200;
var cardHeight = 200;
var horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2;
var verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2;
var shuffleDifficulty = 4;
var lastCheckCaller = 0;
var isPlaying = false;
var isGameEndChecking = false;
var isTryingInitGame = false;
var checkGameEndTimeout = null;
window.passConfettiAnim = false;
var applauseSound = LK.getSound('applause');
var bgMusic;
if (nbCards % 2) {
console.error('Invalid number of cards ! Should be even.', nbCards);
LK.showGameOver();
nbCols++;
}
function loopBgMusic() {
if (bgMusic && Date.now() - bgMusic.lastPlayTime > 10000) {
bgMusic.lastPlayTime = Date.now();
bgMusic.play();
}
}
function prepareConfig(level) {
cardPairs = [];
cards.forEach(function (row) {
row.forEach(function (card) {
if (card) {
card.destroy();
}
});
});
cards = [];
nbRows = levels[level].rows + 2;
nbCols = levels[level].cols + 2;
nbCards = nbRows * nbCols;
if (nbCards % 2) {
console.error('Invalid number of cards ! Should be even.', nbCards);
LK.showGameOver();
nbCols++;
}
nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2;
horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2;
verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2;
nbCardsLeft = 0;
lastNbPossible = 0;
currentSolvedPairs = [];
currentNbSolved = 0;
lastCheckCaller = 0;
checkGameEndTimeout = null;
selectedCard1 = null;
selectedCard2 = null;
isAnimating = false;
isRemixing = false;
}
function timeBasedRandom() {
var currentTime = Date.now();
var timeSeed = currentTime % 1000;
var randomValue = Math.random() * timeSeed;
return randomValue - Math.floor(randomValue);
}
function shuffleArray(array, onlyNonEmpty) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(timeBasedRandom() * (i + 1));
if (onlyNonEmpty && (!array[i] || !array[j])) {
continue;
}
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function shuffleWithDifficulty(array, difficulty, onlyNonEmpty) {
difficulty = Math.max(0, Math.min(10, difficulty));
var maxSwapDistance = Math.ceil(array.length * (difficulty / 10));
var numberOfSwaps = Math.ceil(array.length * (difficulty / 10));
for (var i = 0; i < numberOfSwaps; i++) {
var firstIndex = Math.floor(timeBasedRandom() * array.length);
var swapDistance = Math.floor(timeBasedRandom() * maxSwapDistance);
var secondIndex = (firstIndex + swapDistance) % array.length;
if (onlyNonEmpty && (!array[firstIndex] || !array[secondIndex])) {
continue;
}
var temp = array[firstIndex];
array[firstIndex] = array[secondIndex];
array[secondIndex] = temp;
}
ensureAdjacentIndexes(array, difficulty);
return array;
}
function ensureAdjacentIndexes(array, difficulty) {
var numberOfPairsToSwap = Math.floor(array.length * 0.5 * (1 / difficulty) * 0.5);
var pairsToSwap = [];
for (var i = 0; i < array.length - 1; i++) {
for (var j = i + 1; j < array.length; j++) {
if (array[i] && array[j] && array[i] === array[j] && !pairsToSwap.includes(array[i])) {
pairsToSwap.push(array[i]);
}
}
}
shuffleArray(pairsToSwap, false);
pairsToSwap = pairsToSwap.slice(0, numberOfPairsToSwap);
for (var i = 0; i < array.length - 1; i++) {
for (var j = i + 1; j < array.length; j++) {
if (array[i] && array[j] && array[i] === array[j] && pairsToSwap.includes(array[i]) && array[i + 1]) {
var temp = array[i + 1];
array[i + 1] = array[j];
array[j] = temp;
pairsToSwap.splice(pairsToSwap.indexOf(array[i]), 1);
break;
}
}
}
}
function canBeLinked(card1, card2) {
if (card1 === card2 || !card1 || !card2 || card1.id !== card2.id) {
return false;
}
var visited = Array.from({
length: nbRows
}, function () {
return Array(nbCols).fill(false);
});
function dfs(card, bends, direction) {
if (!card || visited[card.row][card.col] || bends > 2) {
return false;
}
if (card === card2) {
return true;
}
visited[card.row][card.col] = true;
var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (var _i = 0, _directions = directions; _i < _directions.length; _i++) {
var _directions$_i = _slicedToArray(_directions[_i], 2),
di = _directions$_i[0],
dj = _directions$_i[1];
var ni = card.row + di,
nj = card.col + dj;
if (ni >= 0 && ni < nbRows && nj >= 0 && nj < nbCols) {
var nextCard = cards[ni][nj];
if (!nextCard || !nextCard.id || nextCard.isMatched || nextCard === card2) {
if (di !== 0) {
if (dfs(nextCard, bends + (direction === -1), 1)) {
return true;
}
} else if (dj !== 0) {
if (dfs(nextCard, bends + (direction === 1), -1)) {
return true;
}
}
}
}
}
visited[card.row][card.col] = false;
return false;
}
return dfs(card1, 0);
}
function remixCards(currentDifficulty, callback) {
if (isRemixing) {
return false;
}
isRemixing = true;
var unmatchedCards = cards.flat().filter(function (card) {
return !card.isMatched;
});
var oldPositions = unmatchedCards.map(function (card) {
return {
uid: card.uid,
id: card.id,
x: card.x,
y: card.y
};
});
var indexes = unmatchedCards.map(function (card) {
return card.id;
});
indexes = shuffleWithDifficulty(indexes, currentDifficulty);
var reorderedPosition = [];
var treatedIndexes = [];
for (var i = 0; i < oldPositions.length; i++) {
var originId = oldPositions[i].id;
for (var j = 0; j < indexes.length; j++) {
var newId = indexes[j];
if (originId === newId && !treatedIndexes[j]) {
reorderedPosition[i] = {
uid: oldPositions[j].uid,
id: oldPositions[j].id,
x: oldPositions[j].x,
y: oldPositions[j].y
};
treatedIndexes[j] = true;
break;
}
}
}
unmatchedCards.forEach(function (card, index) {
var newPos = reorderedPosition[index];
var moveCard = function moveCard() {
var dx = 0;
var dy = 0;
if (newPos) {
dx = newPos.x - card.x;
dy = newPos.y - card.y;
}
if (index == 1) {}
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 1) {
card.x = newPos.x;
card.y = newPos.y;
var newCol = Math.floor((card.x - horizontalOffset) / cardWidth);
var newRow = Math.floor((card.y - verticalOffset) / cardHeight);
if (cards[card.row] && cards[card.row][card.col] === card) {
cards[card.row][card.col] = undefined;
}
card.col = newCol;
card.row = newRow;
cards[newRow][newCol] = card;
LK.off('tick', moveCard);
} else {
card.x += dx * 0.05;
card.y += dy * 0.05;
}
};
LK.on('tick', moveCard);
});
LK.setTimeout(function () {
isRemixing = false;
if (callback) {
callback();
}
}, 4000);
}
function checkGameEnd(context, callerId) {
if (isGameEndChecking) {
return;
}
if (lastCheckCaller === callerId) {
return;
}
isGameEndChecking = true;
lastCheckCaller = callerId;
nbCardsLeft = cards.flat().filter(function (card) {
return !card.isMatched;
}).length;
if (!nbCardsLeft) {
isPlaying = false;
LK.setTimeout(function () {
LK.getSound('winChime').play();
applauseSound.play();
if (level + 1 >= levels.length) {
showConfettiAnimation(context, function () {
LK.showGameOver();
}, true);
} else {
level++;
level = Math.min(levels.length - 1, Math.max(0, level));
showConfettiAnimation(context, function () {
isGameEndChecking = false;
tryInitGame(context);
});
}
}, 300);
return;
}
if (currentNbSolved >= lastNbPossible) {
var isPossible = checkBoardPossible();
var canContinue = currentNbSolved < lastNbPossible;
if (!canContinue) {
tryRemixCards();
}
}
isGameEndChecking = false;
}
function checkBoardPossible() {
var nbPossible = 0;
var nbImpossible = 0;
var indexesChecked = [];
for (var i = 0; i < cardPairs.length; i++) {
var index = cardPairs[i];
if (!index || indexesChecked[index]) {
continue;
}
var card1 = cards.flat().find(function (card) {
return card.id === index;
});
var card2 = cards.flat().find(function (card) {
return card.id === index && card !== card1;
});
if (canBeLinked(card1, card2)) {
nbPossible++;
} else {
nbImpossible++;
}
indexesChecked[index] = true;
}
lastNbPossible = nbPossible;
return Math.max(currentNbSolved, nbPossible) >= (nbImpossible + nbPossible) * 0.1;
}
function initGame(self, currentDifficulty) {
if (isInitializing) {
console.log('already Initializing...');
return;
}
isInitializing = true;
prepareConfig(level);
var nextPairIndex = 1;
for (var i = 0; i < nbCards; i++) {
var row = Math.floor(i / nbCols);
var col = i % nbCols;
if (row === 0 || row === nbRows - 1 || col === 0 || col === nbCols - 1) {
cardPairs.push(0);
} else {
cardPairs.push(nextPairIndex);
nextPairIndex = nextPairIndex % nbPairs + 1;
}
}
cardPairs = shuffleWithDifficulty(cardPairs, currentDifficulty, true);
for (var i = 0; i < nbCards; i++) {
var row = Math.floor(i / nbCols);
var col = i % nbCols;
var card = self.addChild(new Card(self, i, cardPairs[i]));
card.col = col;
card.row = row;
card.x = horizontalOffset + card.col * cardWidth;
card.y = verticalOffset + card.row * cardHeight;
if (!cards[card.row]) {
cards[card.row] = [];
}
cards[card.row][card.col] = card;
}
isInitializing = false;
if (!bgMusic) {
bgMusic = LK.getSound('music');
bgMusic.lastPlayTime = 0;
}
}
game.update = function () {
if (bgMusic) {
loopBgMusic();
}
};
function tryInitGame(context) {
if (isTryingInitGame) {
console.log('Already tryInitGame...');
return;
}
var isPossible = false;
var nbTries = 0;
var currentDifficulty = shuffleDifficulty;
while (!isPossible && nbTries <= MAX_INIT_TRIES) {
initGame(context, currentDifficulty);
isPossible = checkBoardPossible();
isPlaying = isPossible;
nbTries++;
currentDifficulty--;
}
if (nbTries > MAX_INIT_TRIES) {
console.warn('Unable to init game after ' + nbTries + ' tries');
}
isTryingInitGame = false;
}
function tryRemixCards(nbTries, currentDifficulty) {
nbTries = nbTries || 0;
currentDifficulty = currentDifficulty || shuffleDifficulty;
remixCards(currentDifficulty, function () {
var isPossible = checkBoardPossible();
nbTries++;
currentDifficulty--;
if (nbTries > MAX_INIT_TRIES) {
isPossible = true;
console.warn('Unable to remix game after ' + nbTries + ' tries');
}
if (!isPossible) {
tryRemixCards(nbTries, currentDifficulty);
}
});
}
function showConfettiAnimation(self, callback, isFinal) {
var backgroundImage = self.createAsset('background' + (level + (isFinal ? 1 : 0)), {
anchorX: 0.5,
anchorY: 0.5
});
backgroundImage.x = 2048 / 2;
backgroundImage.y = 2732 / 2;
self.addChild(backgroundImage);
var message = isFinal ? 'Congratulations!' : 'Well done!';
var nextMessage = isFinal ? 'Tank You!' : 'Continue...';
var congratulationsText = new Text2(message, {
size: 150,
fill: '#ffffff'
});
congratulationsText.anchor.set(0.5, 0);
congratulationsText.x = 0;
congratulationsText.y = 20;
LK.gui.top.addChild(congratulationsText);
var nextText = new Text2(nextMessage, {
size: 120,
fill: '#ffffff'
});
nextText.anchor.set(0.5, 1.25);
nextText.x = 0;
nextText.y = 0;
LK.gui.bottom.addChild(nextText);
// Automatically continue after 5 seconds
var continueTimeout = LK.setTimeout(function () {
if (backgroundImage && congratulationsText && nextText) {
backgroundImage.destroy();
congratulationsText.destroy();
nextText.destroy();
}
callback();
}, 5000);
backgroundImage.on('down', function (x, y, obj) {
backgroundImage.destroy();
congratulationsText.destroy();
nextText.destroy();
if (applauseSound) {
applauseSound.stop();
}
if (continueTimeout) {
LK.clearTimeout(continueTimeout);
}
callback();
});
}
function preload(context) {
var titleText = new Text2('Holidays Links', {
size: 200,
fill: '#d7c797',
anchor: {
x: 0.5,
y: 0.5
}
});
titleText.x = 100;
titleText.y = 2732 / 8;
LK.gui.addChild(titleText);
var loadingText = new Text2('Loading...', {
size: 100,
fill: '#d7c797',
anchor: {
x: 0.5,
y: 0.5
}
});
loadingText.x = 2048 / 4;
loadingText.y = 2732 / 4;
LK.gui.addChild(loadingText);
var assetsToLoad = ['line', 'picture1', 'picture2', 'picture3', 'picture8', 'picture7', 'picture24', 'picture17', 'picture16', 'picture4', 'picture29', 'picture6', 'picture14', 'picture15', 'picture20', 'picture9', 'picture10', 'picture27', 'picture12', 'picture19', 'picture26', 'picture22', 'picture18', 'picture28', 'picture23', 'picture13', 'picture21', 'picture5', 'picture11', 'picture25', 'picture30', 'picture32', 'picture31', 'picture37', 'picture39', 'picture44', 'picture38', 'picture48', 'picture47', 'picture50', 'picture42', 'picture46', 'picture45', 'picture43', 'picture49', 'picture34', 'picture33', 'picture40', 'picture36', 'picture35', 'picture41', 'picture55', 'picture52', 'picture54', 'picture53', 'picture51', 'picture56', 'picture59', 'picture60', 'picture57', 'picture58', 'picture64', 'picture63', 'picture61', 'picture62', 'picture65', 'card', 'timerLine', 'background1', 'background2', 'background3', 'background4', 'background5', 'background6', 'background7'];
var loadedAssets = 0;
assetsToLoad.forEach(function (asset) {
var assetInstance = LK.getAsset(asset, {
anchorX: 0.5,
anchorY: 0.5
});
loadedAssets++;
if (loadedAssets === assetsToLoad.length) {
LK.setTimeout(function () {
loadingText.destroy();
titleText.destroy();
tryInitGame(context);
}, 1200);
}
});
}
preload(game);
if (debug) {
game.timerLine = new TimerLine(1400, 20);
game.timerLine.initialWidth = 1400;
game.timerLine.x = 710;
game.timerLine.y = game.timerLine.y;
LK.gui.topLeft.addChild(game.timerLine);
var tickTime = Date.now();
LK.on('tick', function () {
if (isPlaying && !isRemixing) {
var elapsedTime = Date.now() - tickTime;
game.timerLine._update_migrated(elapsedTime);
tickTime = Date.now();
}
});
var matchDebugText = new Text2('Match:', {
size: 50,
fill: '#ffffff'
});
matchDebugText.anchor.set(0, 0);
LK.gui.topLeft.addChild(matchDebugText);
LK.on('tick', function () {
matchDebugText.setText('Level: ' + (level + 1) + ' Solved: ' + currentNbSolved + '/' + lastNbPossible + ' / isPlaying=' + isPlaying + ' / time=' + levelDurationInSeconds);
});
}
a photo realistic top view of empty flat beige plastic square. Single Game Texture. In-Game asset. 2d. No background. High contrast. No shadows.
Un ballon de plage. Image
Un ballon de plage. Image
Un château de sable. Image
Une plage calme. Image
Une plage calme. Image
Une plage calme. Image
Un parasol. Image
Un seau de plage. Image
Un mayot de bain. Image
Image de surf. Image
Un kayak. Image
Coquillage. Image
Crabe mignon. Image
Un serf volant pour enfant. Image
Un cerf volant. Image
Étoile de mer mignon. Image
Étoile de mer. Image
Soleil de plage. Image
Serviette de plage. Image
Jouet de plage. Image
Un chapeau et lunette pour la plage. Image
Un plage de rêve. Image
Table et parasol. Image coloré
Un château de sable pour enfant. Image coloré
Une bouée. Image coloré
Une bouée flamand rose. Image coloré
Une chaise longue de plage. Image
Sac de plage. Image
Bouée pour enfants. Image
Bouée canard. Image
Matelas bouée. Image
La glace. Image
Bateau de plage. Image
Voilier pour enfant. Photo
Glaces pour enfant. Photo
Glace pour enfant. Photo
Claquettes de plage. Photo
Crabe dessin animé. Photo
Parachute dessin animé. Photo
Bouée forme de flement rose. Image
Grand bateau. Image
Méduse. Image
Deux palmiers de plage. Image
planche de surf. photo
bouée. photo
a daulphin. photo