User prompt
show number of stacks left as card decks on stack
User prompt
remove stack icon when finished
User prompt
remove eclipse in cards
User prompt
put little spider in cards
User prompt
widen vertical space
User prompt
put numbers on two corners
User prompt
use all card face for placing numbres
User prompt
put card numbers on top left and bottom right corners
User prompt
make numbers visible
User prompt
make numbers visible on the card behind
User prompt
give larger spaces between cards in each column
User prompt
show numbers only on top left and bottom rşght
User prompt
show only corner numbers
User prompt
show numbers on corners
User prompt
center numbers
User prompt
show larger numbers within card boundaries
User prompt
fit numbers to cards
User prompt
show numbers on corners
User prompt
dont show center numbers
User prompt
show larger numbers on cards
User prompt
allign cards on screen
User prompt
narrow spaces
User prompt
fit screen
User prompt
add sets automatic,ally
User prompt
no space between columns
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Card class var Card = Container.expand(function () { var self = Container.call(this); // Card properties self.rank = 1; // 1=Ace, 13=King self.suit = 0; // 0=spades (for MVP, only one suit) self.faceUp = false; self.selected = false; // Card graphics // Add a drop shadow and rounded rectangle background for realism // Scale card assets to fit calculated CARD_WIDTH and CARD_HEIGHT var scaleX = typeof CARD_WIDTH !== "undefined" ? CARD_WIDTH / BASE_CARD_WIDTH : 1; var scaleY = typeof CARD_HEIGHT !== "undefined" ? CARD_HEIGHT / BASE_CARD_HEIGHT : 1; var cardShadow = LK.getAsset('cardFace', { anchorX: 0.5, anchorY: 0.5, tint: 0x222222, alpha: 0.18, scaleX: 1.04 * scaleX, scaleY: 1.04 * scaleY }); self.addChild(cardShadow); var cardFace = self.attachAsset('cardFace', { anchorX: 0.5, anchorY: 0.5, scaleX: scaleX, scaleY: scaleY }); var cardBack = self.attachAsset('cardBack', { anchorX: 0.5, anchorY: 0.5, scaleX: scaleX, scaleY: scaleY }); cardBack.visible = true; cardFace.visible = false; // Corner labels (rank + suit) for top-left and bottom-right var labelTL = new Text2('', { size: Math.floor(cardFace.height * 0.22), fill: 0xffffff, align: "left" }); labelTL.anchor.set(0, 0); labelTL.x = -cardFace.width / 2 + Math.floor(cardFace.width * 0.08); labelTL.y = -cardFace.height / 2 + Math.floor(cardFace.height * 0.08); self.addChild(labelTL); var labelBR = new Text2('', { size: Math.floor(cardFace.height * 0.22), fill: 0xffffff, align: "right" }); labelBR.anchor.set(1, 1); labelBR.x = cardFace.width / 2 - Math.floor(cardFace.width * 0.08); labelBR.y = cardFace.height / 2 - Math.floor(cardFace.height * 0.08); self.addChild(labelBR); // Set card data self.setCard = function (rank, suit, faceUp) { self.rank = rank; self.suit = suit; self.faceUp = faceUp; self.updateFace(); }; // Update card face/back and label self.updateFace = function () { cardFace.visible = self.faceUp; cardBack.visible = !self.faceUp; // Only spades for MVP var suitChar = '♠'; var rankStr = ''; if (self.rank === 1) rankStr = 'A';else if (self.rank === 11) rankStr = 'J';else if (self.rank === 12) rankStr = 'Q';else if (self.rank === 13) rankStr = 'K';else rankStr = '' + self.rank; // Show only in corners if (typeof labelTL !== "undefined" && typeof labelBR !== "undefined") { var labelText = rankStr + suitChar; labelTL.setText(labelText); labelBR.setText(labelText); labelTL.visible = true; labelBR.visible = true; if (self.faceUp) { labelTL.setStyle({ fill: 0xffffff, alpha: 1 }); labelBR.setStyle({ fill: 0xffffff, alpha: 1 }); } else { labelTL.setStyle({ fill: 0xcccccc, alpha: 0.45 }); labelBR.setStyle({ fill: 0xcccccc, alpha: 0.45 }); } } // Optionally, show a faint spider on the back for realism self.updateSelected(); }; // Visual feedback for selection self.updateSelected = function () { if (self.selected) { cardFace.tint = 0x99ccff; cardBack.tint = 0x99ccff; } else { cardFace.tint = 0xffffff; cardBack.tint = 0xffffff; } }; // Flip card self.flip = function (faceUp) { self.faceUp = faceUp; self.updateFace(); }; // Touch events self.down = function (x, y, obj) { if (self.faceUp) { // Let game handle selection if (game) game.onCardDown(self, x, y, obj); } }; return self; }); // Stock pile class (for dealing new rows) var StockPile = Container.expand(function () { var self = Container.call(this); self.cards = []; // Add card to stock self.addCard = function (card) { self.cards.push(card); self.addChild(card); card.x = 0; card.y = 0; }; // Deal one card per tableau pile self.dealToTableau = function (tableauPiles) { if (self.cards.length < tableauPiles.length) return false; for (var i = 0; i < tableauPiles.length; i++) { var card = self.cards.shift(); card.flip(true); tableauPiles[i].addCard(card); } return true; }; // Is empty? self.isEmpty = function () { return self.cards.length === 0; }; return self; }); // Tableau pile class var TableauPile = Container.expand(function () { var self = Container.call(this); self.cards = []; // Add card to pile self.addCard = function (card) { self.cards.push(card); self.addChild(card); self.layout(); }; // Remove card(s) from pile starting at index self.removeCardsFrom = function (idx) { var removed = []; while (self.cards.length > idx) { var c = self.cards.pop(); c.parent.removeChild(c); removed.unshift(c); } self.layout(); return removed; }; // Get top card self.topCard = function () { if (self.cards.length === 0) return null; return self.cards[self.cards.length - 1]; }; // Layout cards in pile self.layout = function () { // Increase vertical spacing between cards for more space in each column var CARD_VERTICAL_SPACING = 110; for (var i = 0; i < self.cards.length; i++) { var c = self.cards[i]; // Animate to new position using tween tween.stop(c, { x: true, y: true }); // Stop any previous tweens on x/y tween(c, { x: 0, y: i * CARD_VERTICAL_SPACING }, { duration: 400, easing: tween.cubicOut }); c.zIndex = i; } }; // Reveal top card if needed self.revealTop = function () { var top = self.topCard(); if (top && !top.faceUp) { top.flip(true); } }; // Get index of card in pile self.indexOf = function (card) { for (var i = 0; i < self.cards.length; i++) { if (self.cards[i] === card) return i; } return -1; }; // Can move sequence starting at idx? self.canMoveSequence = function (idx) { // All cards from idx to end must be face up and in descending order for (var i = idx; i < self.cards.length - 1; i++) { var c1 = self.cards[i]; var c2 = self.cards[i + 1]; if (!c1.faceUp || !c2.faceUp) return false; if (c1.rank !== c2.rank + 1) return false; } return self.cards[idx].faceUp; }; // Can accept a sequence of cards (cardsArr)? self.canAccept = function (cardsArr) { if (self.cards.length === 0) { // Only King can be placed on empty pile return cardsArr[0].rank === 13; } else { var top = self.topCard(); var moving = cardsArr[0]; return top.faceUp && top.rank === moving.rank + 1; } }; // Remove completed set (K-A) self.removeCompleteSet = function () { // Check if last 13 cards are a complete set if (self.cards.length < 13) return false; for (var i = 0; i < 13; i++) { var c = self.cards[self.cards.length - 1 - i]; if (!c.faceUp || c.rank !== 13 - i) return false; } // Remove them var removed = []; for (var i = 0; i < 13; i++) { var c = self.cards.pop(); c.parent.removeChild(c); removed.unshift(c); } self.layout(); return true; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xcccccc }); /**** * Game Code ****/ // (Removed stock label) // Show number of stacks left as card decks on stock pile var stockDecks = []; function updateStockDecks() { // Remove old for (var i = 0; i < stockDecks.length; i++) { if (stockDecks[i].parent) stockDecks[i].parent.removeChild(stockDecks[i]); } stockDecks = []; // Each "stack" is 10 cards (8 stacks of 10 = 80, but Spider uses 50 in stock: 5 stacks of 10) var stacksLeft = Math.floor(stockPile.cards.length / TABLEAU_COUNT); for (var i = 0; i < stacksLeft; i++) { var deckImg = LK.getAsset('cardBack', { anchorX: 0.5, anchorY: 0.5, scaleX: CARD_WIDTH / BASE_CARD_WIDTH * 0.9, scaleY: CARD_HEIGHT / BASE_CARD_HEIGHT * 0.9 }); // Stack them with a slight offset deckImg.x = stockPile.x + CARD_WIDTH / 2 + i * 10; deckImg.y = stockPile.y + CARD_HEIGHT / 2 - i * 6; deckImg.alpha = 0.85 - i * 0.08; game.addChild(deckImg); stockDecks.push(deckImg); } } // Add a large faded spider icon to the background, centered and scaled for portrait var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; // Spider background: scale to fit width, center var backgroundSpider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5, scaleX: 10, scaleY: 10 }); backgroundSpider.x = GAME_WIDTH / 2; backgroundSpider.y = GAME_HEIGHT / 2; backgroundSpider.alpha = 0.08; game.addChildAt(backgroundSpider, 0); // Ensure it's behind all other elements // Card and layout constants for portrait var TABLEAU_COUNT = 8; var GAME_MARGIN_X = 40; var GAME_MARGIN_Y = 120; // Get original card asset size var tempCard = LK.getAsset('cardFace', { anchorX: 0.5, anchorY: 0.5 }); var BASE_CARD_WIDTH = tempCard.width; var BASE_CARD_HEIGHT = tempCard.height; // Calculate max card width to fit 8 columns with margin var availableWidth = GAME_WIDTH - 2 * GAME_MARGIN_X; var CARD_WIDTH = Math.floor(availableWidth / TABLEAU_COUNT); var CARD_HEIGHT = Math.floor(BASE_CARD_HEIGHT * (CARD_WIDTH / BASE_CARD_WIDTH)); // No spacing between columns var TABLEAU_SPACING = 0; // Top offset for vertical layout (fit 2 rows of cards + margin) var TABLEAU_TOP = GAME_MARGIN_Y + CARD_HEIGHT + 40; // Center columns horizontally var totalTableauWidth = TABLEAU_COUNT * CARD_WIDTH + (TABLEAU_COUNT - 1) * TABLEAU_SPACING; var TABLEAU_LEFT = Math.round((GAME_WIDTH - totalTableauWidth) / 2); // Stock and completed set positions (fit to portrait) var STOCK_X = GAME_WIDTH - CARD_WIDTH - GAME_MARGIN_X; var STOCK_Y = GAME_MARGIN_Y; var COMPLETED_X = GAME_MARGIN_X + CARD_WIDTH / 2; var COMPLETED_Y = GAME_MARGIN_Y; // Clean up tempCard if (tempCard.parent) tempCard.parent.removeChild(tempCard); // Game state var tableauPiles = []; var stockPile = null; var completedSets = 0; var movesCount = 0; // Moves counter var draggingCards = null; var draggingFromPile = null; var dragOffsetX = 0; var dragOffsetY = 0; var dragStartX = 0; var dragStartY = 0; var dragValidTarget = null; var dragTargetPile = null; var canDeal = true; // Undo stack var undoStack = []; // Helper: clone game state for undo function cloneGameState() { // Deep copy of tableau piles, stock, completedSets, movesCount var tableau = []; for (var i = 0; i < tableauPiles.length; i++) { var pile = tableauPiles[i]; var pileArr = []; for (var j = 0; j < pile.cards.length; j++) { var c = pile.cards[j]; pileArr.push({ rank: c.rank, suit: c.suit, faceUp: c.faceUp }); } tableau.push(pileArr); } var stock = []; for (var i = 0; i < stockPile.cards.length; i++) { var c = stockPile.cards[i]; stock.push({ rank: c.rank, suit: c.suit, faceUp: c.faceUp }); } return { tableau: tableau, stock: stock, completedSets: completedSets, movesCount: movesCount }; } // Helper: restore game state from undo function restoreGameState(state) { // Remove all cards for (var i = 0; i < tableauPiles.length; i++) { var pile = tableauPiles[i]; while (pile.cards.length > 0) { var c = pile.cards.pop(); if (c.parent) c.parent.removeChild(c); } } while (stockPile.cards.length > 0) { var c = stockPile.cards.pop(); if (c.parent) c.parent.removeChild(c); } // Restore tableau for (var i = 0; i < tableauPiles.length; i++) { var pileArr = state.tableau[i]; for (var j = 0; j < pileArr.length; j++) { var cdata = pileArr[j]; var card = new Card(); card.setCard(cdata.rank, cdata.suit, cdata.faceUp); tableauPiles[i].addCard(card); } } // Restore stock for (var i = 0; i < state.stock.length; i++) { var cdata = state.stock[i]; var card = new Card(); card.setCard(cdata.rank, cdata.suit, cdata.faceUp); stockPile.addCard(card); } completedSets = state.completedSets; movesCount = state.movesCount; updateScore(); updateCompletedSpiders(); canDeal = true; updateStockDecks(); draggingCards = null; draggingFromPile = null; dragValidTarget = null; dragTargetPile = null; } // Push undo state function pushUndoState() { // Limit undo stack to 50 if (undoStack.length > 50) undoStack.shift(); undoStack.push(cloneGameState()); } // GUI var scoreTxt = new Text2('0', { size: 90, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var movesTxt = new Text2('Moves: 0', { size: 70, fill: 0xffffff }); movesTxt.anchor.set(0.5, 0); // Place moves counter under the sets counter (scoreTxt) movesTxt.x = GAME_WIDTH / 2; movesTxt.y = scoreTxt.y + scoreTxt.height + 10; LK.gui.top.addChild(movesTxt); // Undo button (now shown as an arrow icon) var undoBtn = new Text2('↶', { size: 180, fill: 0xffffff }); undoBtn.anchor.set(1, 1); // Place at the bottom right corner of the screen (using LK.gui.bottomRight) undoBtn.x = -40; undoBtn.y = -40; LK.gui.bottomRight.addChild(undoBtn); // Moves counter next to undo button var movesTxtBR = new Text2('0', { size: 70, fill: 0xffffff }); movesTxtBR.anchor.set(1, 1); // Place to the left of the undo button, aligned at bottom right movesTxtBR.x = undoBtn.x - 120; movesTxtBR.y = undoBtn.y - 10; LK.gui.bottomRight.addChild(movesTxtBR); // Undo button handler (moved here after undoBtn is defined) undoBtn.down = function (x, y, obj) { if (undoStack.length === 0) return; var prev = undoStack.pop(); // When redoing, do not decrease movesCount; keep current movesCount var currentMoves = movesCount; restoreGameState(prev); // Restore everything except movesCount (keep current) movesCount = currentMoves; updateScore(); }; // Restart button (shown as a circular arrow) var restartBtn = new Text2('⟳', { size: 180, fill: 0xffffff }); restartBtn.anchor.set(0, 1); // Place at the bottom left corner of the screen (using LK.gui.bottomLeft) restartBtn.x = 40; restartBtn.y = -40; LK.gui.bottomLeft.addChild(restartBtn); restartBtn.down = function (x, y, obj) { resetGame(); }; var dealBtn = new Text2('Deal', { size: 70, fill: 0xFFFFFF }); dealBtn.anchor.set(0.5, 0.5); dealBtn.x = STOCK_X + CARD_WIDTH / 2; dealBtn.y = STOCK_Y + CARD_HEIGHT + 60; LK.gui.top.addChild(dealBtn); // Completed sets display var completedSpiders = []; // Initialize tableau piles for (var i = 0; i < TABLEAU_COUNT; i++) { var pile = new TableauPile(); // Align piles evenly across the screen, centered pile.x = TABLEAU_LEFT + i * (CARD_WIDTH + TABLEAU_SPACING) + CARD_WIDTH / 2; pile.y = TABLEAU_TOP; game.addChild(pile); tableauPiles.push(pile); } // Initialize stock pile stockPile = new StockPile(); stockPile.x = STOCK_X; stockPile.y = STOCK_Y; game.addChild(stockPile); // (Removed stock label) // Shuffle and deal cards function shuffleDeck() { // 8 decks of spades (104 cards) var deck = []; for (var d = 0; d < 8; d++) { for (var r = 1; r <= 13; r++) { var card = new Card(); card.setCard(r, 0, false); deck.push(card); } } // Fisher-Yates shuffle for (var i = deck.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var tmp = deck[i]; deck[i] = deck[j]; deck[j] = tmp; } return deck; } function dealInitial() { var deck = shuffleDeck(); // 52 cards to tableau (first 4 piles get 7, rest get 6) for (var i = 0; i < TABLEAU_COUNT; i++) { var count = i < 4 ? 7 : 6; for (var j = 0; j < count; j++) { var card = deck.shift(); tableauPiles[i].addCard(card); } } // Flip top card of each pile for (var i = 0; i < TABLEAU_COUNT; i++) { tableauPiles[i].topCard().flip(true); } // Remaining 50 cards to stock while (deck.length > 0) { var card = deck.shift(); stockPile.addCard(card); } completedSets = 0; updateScore(); updateCompletedSpiders(); updateStockDecks(); canDeal = true; } function updateScore() { scoreTxt.setText('Sets: ' + completedSets); if (typeof movesTxt !== "undefined") { movesTxt.setText('Moves: ' + movesCount); } if (typeof movesTxtBR !== "undefined") { movesTxtBR.setText(movesCount + ''); } } function updateCompletedSpiders() { // Remove old for (var i = 0; i < completedSpiders.length; i++) { if (completedSpiders[i].parent) completedSpiders[i].parent.removeChild(completedSpiders[i]); } completedSpiders = []; // Add new for (var i = 0; i < completedSets; i++) { var spider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5 }); spider.x = COMPLETED_X + i * (CARD_WIDTH * 0.7); spider.y = COMPLETED_Y; game.addChild(spider); completedSpiders.push(spider); } } // Handle card selection and drag game.onCardDown = function (card, x, y, obj) { // Find which pile this card is in var pile = null; var idx = -1; for (var i = 0; i < tableauPiles.length; i++) { var p = tableauPiles[i]; var j = p.indexOf(card); if (j !== -1) { pile = p; idx = j; break; } } if (!pile) return; // Can move sequence? if (!pile.canMoveSequence(idx)) return; // Prepare drag pushUndoState(); draggingCards = []; for (var k = idx; k < pile.cards.length; k++) { var c = pile.cards[k]; c.selected = true; c.updateSelected(); draggingCards.push(c); } draggingFromPile = pile; // Try to auto-move to a valid pile var autoMoved = false; for (var t = 0; t < tableauPiles.length; t++) { var targetPile = tableauPiles[t]; if (targetPile === pile) continue; if (targetPile.canAccept(draggingCards)) { // Remove from old pile pile.removeCardsFrom(idx); // Add to new pile for (var m = 0; m < draggingCards.length; m++) { targetPile.addCard(draggingCards[m]); } // Play card move sound LK.getSound('card_move').play(); // Reveal top card in old pile pile.revealTop(); // Check for completed set var completedAny = false; while (targetPile.removeCompleteSet()) { // Play card set sound LK.getSound('card_set').play(); completedSets += 1; updateScore(); updateCompletedSpiders(); // Animate spider var spider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5 }); spider.x = targetPile.x + CARD_WIDTH / 2; spider.y = targetPile.y + targetPile.cards.length * 40 + CARD_HEIGHT / 2; game.addChild(spider); (function (spider, setIndex) { tween(spider, { y: COMPLETED_Y, x: COMPLETED_X + setIndex * (CARD_WIDTH * 0.7) }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (spider.parent) spider.parent.removeChild(spider); updateCompletedSpiders(); } }); })(spider, completedSets - 1); completedAny = true; } // After moving, check all piles for additional completed sets (auto-collect) for (var auto_i = 0; auto_i < tableauPiles.length; auto_i++) { var pile = tableauPiles[auto_i]; while (pile.removeCompleteSet()) { LK.getSound('card_set').play(); completedSets += 1; updateScore(); updateCompletedSpiders(); var spider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5 }); spider.x = pile.x + CARD_WIDTH / 2; spider.y = pile.y + pile.cards.length * 40 + CARD_HEIGHT / 2; game.addChild(spider); (function (spider, setIndex) { tween(spider, { y: COMPLETED_Y, x: COMPLETED_X + setIndex * (CARD_WIDTH * 0.7) }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (spider.parent) spider.parent.removeChild(spider); updateCompletedSpiders(); } }); })(spider, completedSets - 1); completedAny = true; } } // Win condition: 8 sets if (completedSets >= 8) { LK.showYouWin(); return; } // Deselect for (var m = 0; m < draggingCards.length; m++) { draggingCards[m].selected = false; draggingCards[m].updateSelected(); } movesCount += 1; // Increment moves on auto-move updateScore(); draggingCards = null; draggingFromPile = null; dragValidTarget = null; dragTargetPile = null; autoMoved = true; break; } } if (autoMoved) return; // Bring to front for manual drag for (var k = 0; k < draggingCards.length; k++) { game.addChild(draggingCards[k]); } // Offset dragOffsetX = card.x; dragOffsetY = card.y; dragStartX = card.parent.x + card.x; dragStartY = card.parent.y + card.y; }; // Handle drag move game.move = function (x, y, obj) { if (!draggingCards) return; // Move cards for (var i = 0; i < draggingCards.length; i++) { draggingCards[i].x = x - dragOffsetX; draggingCards[i].y = y - dragOffsetY + i * 60; } // Check for valid drop target dragValidTarget = null; dragTargetPile = null; for (var i = 0; i < tableauPiles.length; i++) { var pile = tableauPiles[i]; // Don't allow drop on self if (pile === draggingFromPile) continue; // Get pile bounds var px = pile.x; var py = pile.y + pile.cards.length * 40; var pw = CARD_WIDTH; var ph = CARD_HEIGHT; // If pile is empty, allow drop anywhere in the pile area if (pile.cards.length === 0) { px = pile.x; py = pile.y; pw = CARD_WIDTH; ph = CARD_HEIGHT; if (x > px && x < px + pw && y > py && y < py + ph) { if (pile.canAccept(draggingCards)) { dragValidTarget = pile; dragTargetPile = pile; break; } } } else { if (x > px && x < px + pw && y > py - 40 && y < py + ph) { if (pile.canAccept(draggingCards)) { dragValidTarget = pile; dragTargetPile = pile; break; } } } } }; // Handle drag end game.up = function (x, y, obj) { if (!draggingCards) return; // Drop if (dragValidTarget && dragTargetPile) { pushUndoState(); // Remove from old pile var idx = draggingFromPile.indexOf(draggingCards[0]); draggingFromPile.removeCardsFrom(idx); // Add to new pile for (var i = 0; i < draggingCards.length; i++) { dragTargetPile.addCard(draggingCards[i]); } // Play card move sound LK.getSound('card_move').play(); movesCount += 1; // Increment moves on manual drag updateScore(); // Reveal top card in old pile draggingFromPile.revealTop(); // Check for completed set var completedAny = false; while (dragTargetPile.removeCompleteSet()) { // Play card set sound LK.getSound('card_set').play(); completedSets += 1; updateScore(); updateCompletedSpiders(); // Animate spider var spider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5 }); spider.x = dragTargetPile.x + CARD_WIDTH / 2; spider.y = dragTargetPile.y + dragTargetPile.cards.length * 40 + CARD_HEIGHT / 2; game.addChild(spider); (function (spider, setIndex) { tween(spider, { y: COMPLETED_Y, x: COMPLETED_X + setIndex * (CARD_WIDTH * 0.7) }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (spider.parent) spider.parent.removeChild(spider); updateCompletedSpiders(); } }); })(spider, completedSets - 1); completedAny = true; } // After moving, check all piles for additional completed sets (auto-collect) for (var auto_i = 0; auto_i < tableauPiles.length; auto_i++) { var pile = tableauPiles[auto_i]; while (pile.removeCompleteSet()) { LK.getSound('card_set').play(); completedSets += 1; updateScore(); updateCompletedSpiders(); var spider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5 }); spider.x = pile.x + CARD_WIDTH / 2; spider.y = pile.y + pile.cards.length * 40 + CARD_HEIGHT / 2; game.addChild(spider); (function (spider, setIndex) { tween(spider, { y: COMPLETED_Y, x: COMPLETED_X + setIndex * (CARD_WIDTH * 0.7) }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (spider.parent) spider.parent.removeChild(spider); updateCompletedSpiders(); } }); })(spider, completedSets - 1); completedAny = true; } } // Win condition: 8 sets if (completedSets >= 8) { LK.showYouWin(); return; } } else { // Return to original pile for (var i = 0; i < draggingCards.length; i++) { draggingCards[i].x = dragStartX - draggingFromPile.x; draggingCards[i].y = dragStartY - draggingFromPile.y + i * 60; draggingFromPile.addChild(draggingCards[i]); } draggingFromPile.layout(); } // Deselect for (var i = 0; i < draggingCards.length; i++) { draggingCards[i].selected = false; draggingCards[i].updateSelected(); } draggingCards = null; draggingFromPile = null; dragValidTarget = null; dragTargetPile = null; }; // Deal new row dealBtn.down = function (x, y, obj) { if (!canDeal) return; // Only allow if all tableau piles have at least one card for (var i = 0; i < tableauPiles.length; i++) { if (tableauPiles[i].cards.length === 0) return; } if (stockPile.isEmpty()) return; canDeal = false; pushUndoState(); // Animate deal var dealt = stockPile.dealToTableau(tableauPiles); if (dealt) { // Play shuffle sound when dealing from stock LK.getSound('shuffle').play(); // Flip top card of each pile for (var i = 0; i < tableauPiles.length; i++) { tableauPiles[i].topCard().flip(true); } updateStockDecks(); // After dealing, auto-collect any completed sets for (var auto_i = 0; auto_i < tableauPiles.length; auto_i++) { var pile = tableauPiles[auto_i]; while (pile.removeCompleteSet()) { LK.getSound('card_set').play(); completedSets += 1; updateScore(); updateCompletedSpiders(); var spider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5 }); spider.x = pile.x + CARD_WIDTH / 2; spider.y = pile.y + pile.cards.length * 40 + CARD_HEIGHT / 2; game.addChild(spider); (function (spider, setIndex) { tween(spider, { y: COMPLETED_Y, x: COMPLETED_X + setIndex * (CARD_WIDTH * 0.7) }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (spider.parent) spider.parent.removeChild(spider); updateCompletedSpiders(); } }); })(spider, completedSets - 1); } } } // Allow next deal after short delay LK.setTimeout(function () { canDeal = true; }, 400); }; // Touch on stock pile (deal) stockPile.down = function (x, y, obj) { dealBtn.down(x, y, obj); }; // Reset game function resetGame() { // Remove all cards for (var i = 0; i < tableauPiles.length; i++) { var pile = tableauPiles[i]; while (pile.cards.length > 0) { var c = pile.cards.pop(); if (c.parent) c.parent.removeChild(c); } } while (stockPile.cards.length > 0) { var c = stockPile.cards.pop(); if (c.parent) c.parent.removeChild(c); } completedSets = 0; movesCount = 0; // Reset moves counter updateScore(); updateCompletedSpiders(); updateStockDecks(); canDeal = true; draggingCards = null; draggingFromPile = null; dragValidTarget = null; dragTargetPile = null; dealInitial(); } // Game over: no more moves function checkGameOver() { // If no moves and stock is empty var canMove = false; for (var i = 0; i < tableauPiles.length; i++) { var pile = tableauPiles[i]; for (var j = 0; j < pile.cards.length; j++) { if (pile.canMoveSequence(j)) { // Try to move to another pile var seq = []; for (var k = j; k < pile.cards.length; k++) seq.push(pile.cards[k]); for (var t = 0; t < tableauPiles.length; t++) { if (t === i) continue; if (tableauPiles[t].canAccept(seq)) { canMove = true; break; } } } if (canMove) break; } if (canMove) break; } if (!canMove && stockPile.isEmpty()) { LK.showGameOver(); } } // Game update game.update = function () { // Auto-collect completed sets after any move, deal, or flip for (var auto_i = 0; auto_i < tableauPiles.length; auto_i++) { var pile = tableauPiles[auto_i]; while (pile.removeCompleteSet()) { LK.getSound('card_set').play(); completedSets += 1; updateScore(); updateCompletedSpiders(); var spider = LK.getAsset('spider', { anchorX: 0.5, anchorY: 0.5 }); spider.x = pile.x + CARD_WIDTH / 2; spider.y = pile.y + pile.cards.length * 40 + CARD_HEIGHT / 2; game.addChild(spider); (function (spider, setIndex) { tween(spider, { y: COMPLETED_Y, x: COMPLETED_X + setIndex * (CARD_WIDTH * 0.7) }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (spider.parent) spider.parent.removeChild(spider); updateCompletedSpiders(); } }); })(spider, completedSets - 1); } } // Check for game over checkGameOver(); }; // Start game dealInitial(); LK.playMusic('relaxing_bg');
===================================================================
--- original.js
+++ change.js
@@ -270,9 +270,34 @@
/****
* Game Code
****/
-// Game constants for 2048x2732 portrait
+// (Removed stock label)
+// Show number of stacks left as card decks on stock pile
+var stockDecks = [];
+function updateStockDecks() {
+ // Remove old
+ for (var i = 0; i < stockDecks.length; i++) {
+ if (stockDecks[i].parent) stockDecks[i].parent.removeChild(stockDecks[i]);
+ }
+ stockDecks = [];
+ // Each "stack" is 10 cards (8 stacks of 10 = 80, but Spider uses 50 in stock: 5 stacks of 10)
+ var stacksLeft = Math.floor(stockPile.cards.length / TABLEAU_COUNT);
+ for (var i = 0; i < stacksLeft; i++) {
+ var deckImg = LK.getAsset('cardBack', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: CARD_WIDTH / BASE_CARD_WIDTH * 0.9,
+ scaleY: CARD_HEIGHT / BASE_CARD_HEIGHT * 0.9
+ });
+ // Stack them with a slight offset
+ deckImg.x = stockPile.x + CARD_WIDTH / 2 + i * 10;
+ deckImg.y = stockPile.y + CARD_HEIGHT / 2 - i * 6;
+ deckImg.alpha = 0.85 - i * 0.08;
+ game.addChild(deckImg);
+ stockDecks.push(deckImg);
+ }
+}
// Add a large faded spider icon to the background, centered and scaled for portrait
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
// Spider background: scale to fit width, center
@@ -399,8 +424,9 @@
movesCount = state.movesCount;
updateScore();
updateCompletedSpiders();
canDeal = true;
+ updateStockDecks();
draggingCards = null;
draggingFromPile = null;
dragValidTarget = null;
dragTargetPile = null;
@@ -537,8 +563,9 @@
}
completedSets = 0;
updateScore();
updateCompletedSpiders();
+ updateStockDecks();
canDeal = true;
}
function updateScore() {
scoreTxt.setText('Sets: ' + completedSets);
@@ -634,13 +661,8 @@
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (spider.parent) spider.parent.removeChild(spider);
- // Remove the completed stack icon for this set
- if (completedSpiders[setIndex] && completedSpiders[setIndex].parent) {
- completedSpiders[setIndex].parent.removeChild(completedSpiders[setIndex]);
- completedSpiders[setIndex] = null;
- }
updateCompletedSpiders();
}
});
})(spider, completedSets - 1);
@@ -795,13 +817,8 @@
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (spider.parent) spider.parent.removeChild(spider);
- // Remove the completed stack icon for this set
- if (completedSpiders[setIndex] && completedSpiders[setIndex].parent) {
- completedSpiders[setIndex].parent.removeChild(completedSpiders[setIndex]);
- completedSpiders[setIndex] = null;
- }
updateCompletedSpiders();
}
});
})(spider, completedSets - 1);
@@ -880,8 +897,9 @@
// Flip top card of each pile
for (var i = 0; i < tableauPiles.length; i++) {
tableauPiles[i].topCard().flip(true);
}
+ updateStockDecks();
// After dealing, auto-collect any completed sets
for (var auto_i = 0; auto_i < tableauPiles.length; auto_i++) {
var pile = tableauPiles[auto_i];
while (pile.removeCompleteSet()) {
@@ -904,13 +922,8 @@
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (spider.parent) spider.parent.removeChild(spider);
- // Remove the completed stack icon for this set
- if (completedSpiders[setIndex] && completedSpiders[setIndex].parent) {
- completedSpiders[setIndex].parent.removeChild(completedSpiders[setIndex]);
- completedSpiders[setIndex] = null;
- }
updateCompletedSpiders();
}
});
})(spider, completedSets - 1);
@@ -943,8 +956,9 @@
completedSets = 0;
movesCount = 0; // Reset moves counter
updateScore();
updateCompletedSpiders();
+ updateStockDecks();
canDeal = true;
draggingCards = null;
draggingFromPile = null;
dragValidTarget = null;
@@ -1003,13 +1017,8 @@
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (spider.parent) spider.parent.removeChild(spider);
- // Remove the completed stack icon for this set
- if (completedSpiders[setIndex] && completedSpiders[setIndex].parent) {
- completedSpiders[setIndex].parent.removeChild(completedSpiders[setIndex]);
- completedSpiders[setIndex] = null;
- }
updateCompletedSpiders();
}
});
})(spider, completedSets - 1);