User prompt
The game is not detecting pairs well
User prompt
Please fix the bug: 'TypeError: self.getCardLabel is not a function' in or related to this line: 'self.rankText = new Text2(self.getCardLabel(), {' Line Number: 90
Code edit (1 edits merged)
Please save this source code
User prompt
Ultimate Video Poker Challenge
Initial prompt
We are making a video-poker game. In this kind of game the player is initially drawn 5 cards and he can select any of the cards dealt to keep, the other cards being rerolled. His score is based on the final hand he obtains after the reroll based on the classic poker hand ranking (with exact values of each hand I will come back later). This is the hand ranking in order from best to worst: Royal Flush: Ace, King, Queen, Jack, Ten, all of the same suit; Straight Flush: Five cards in sequence, all of the same suit; Four of a Kind: Four cards of the same rank; Full House: Three of a kind combined with a pair; Flush: Five cards of the same suit, not in sequence; Straight: Five cards in sequence, not all of the same suit; Three of a Kind: Three cards of the same rank; Two Pair: Two different pairs; One Pair: Two cards of the same rank; High Card: When no other hand is made, the highest card plays
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highestScore: 0 }); /**** * Classes ****/ var Button = Container.expand(function (text, width, height, color) { var self = Container.call(this); self.buttonShape = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5, width: width || 300, height: height || 100, tint: color || 0x2ecc71 }); self.buttonText = new Text2(text, { size: 40, fill: 0xFFFFFF }); self.buttonText.anchor.set(0.5); self.addChild(self.buttonText); self.disable = function () { self.buttonShape.tint = 0x95a5a6; self.interactive = false; }; self.enable = function () { self.buttonShape.tint = color || 0x2ecc71; self.interactive = true; }; // Event handlers self.down = function (x, y, obj) { tween(self, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); }; self.up = function (x, y, obj) { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100 }); if (self.onPress) { self.onPress(); } }; return self; }); var Card = Container.expand(function (suit, rank) { var self = Container.call(this); self.suit = suit; self.rank = rank; self.isHeld = false; // Get suit symbol self.getSuitSymbol = function () { if (self.suit === 'hearts') { return '♥'; } if (self.suit === 'diamonds') { return '♦'; } if (self.suit === 'clubs') { return '♣'; } if (self.suit === 'spades') { return '♠'; } return ''; }; // Get card color self.getCardColor = function () { if (self.suit === 'hearts' || self.suit === 'diamonds') { return "#ff0000"; } return "#000000"; }; // Get card label self.getCardLabel = function () { return self.rank + self.getSuitSymbol(); }; // Front and back of card self.cardFront = self.attachAsset('cardFront', { anchorX: 0.5, anchorY: 0.5 }); self.cardBack = self.attachAsset('cardBack', { anchorX: 0.5, anchorY: 0.5 }); self.cardBack.visible = false; // Hold indicator self.holdIndicator = self.attachAsset('holdIndicator', { anchorX: 0.5, anchorY: 0.5, y: 130 }); self.holdIndicator.visible = false; // Card text (suit and rank) self.rankText = new Text2(self.getCardLabel(), { size: 58, fill: self.getCardColor() }); self.rankText.anchor.set(0.5); self.rankText.y = -100; self.addChild(self.rankText); // Suit symbol in the center self.suitText = new Text2(self.getSuitSymbol(), { size: 116, fill: self.getCardColor() }); self.suitText.anchor.set(0.5); self.addChild(self.suitText); // Hold text self.holdText = new Text2("HOLD", { size: 32, fill: 0x000000 }); self.holdText.anchor.set(0.5); self.holdText.y = 130; self.addChild(self.holdText); self.holdText.visible = false; // Toggle hold state self.toggleHold = function () { self.isHeld = !self.isHeld; self.holdIndicator.visible = self.isHeld; self.holdText.visible = self.isHeld; }; // Get card value for hand evaluations (1-13) self.getValue = function () { if (self.rank === 'A') { return 14; } if (self.rank === 'K') { return 13; } if (self.rank === 'Q') { return 12; } if (self.rank === 'J') { return 11; } return parseInt(self.rank); }; // Event handlers self.down = function (x, y, obj) { // Scale down slightly on press tween(self, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); }; self.up = function (x, y, obj) { // Scale back to normal tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100 }); self.toggleHold(); // Play sound when toggling hold LK.getSound('holdCard').play(); // Add small bounce animation when card is held if (self.isHeld) { tween(self, { y: self.y - 15 }, { duration: 150, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(self, { y: CARD_START_Y }, { duration: 200, easing: tween.elasticOut }); } }); } }; return self; }); var CashOutButton = Container.expand(function () { var self = Container.call(this); self.buttonShape = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5, tint: 0xe74c3c // Red color }); self.buttonText = new Text2("CASH OUT", { size: 40, fill: 0xFFFFFF }); self.buttonText.anchor.set(0.5); self.addChild(self.buttonText); // Event handlers self.down = function (x, y, obj) { tween(self, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); }; self.up = function (x, y, obj) { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100 }); if (self.onPress) { self.onPress(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c6e31 }); /**** * Game Code ****/ // Constants var CARD_SPACING = 260; var CARDS_PER_HAND = 5; var CARD_START_Y = 700; var DECK_POSITION_X = 300; var DECK_POSITION_Y = 300; // Game states var STATE_DEAL = "deal"; var STATE_PLAYER_TURN = "playerTurn"; var STATE_FINAL = "final"; // Game variables var deck = []; var playerHand = []; var gameState = STATE_DEAL; var handScore = 0; var handRank = ""; var highestScore = storage.highestScore || 0; var currentBet = 1; // Each bet costs 1 score // Game themes var themes = [{ name: "Classic", tableTint: 0x2c6e31, // Green buttonTint: 0x2ecc71, cardBackTint: 0x0e4d92, // Blue holdTint: 0xf39c12 // Orange }, { name: "Dark", tableTint: 0x2c3e50, // Dark blue buttonTint: 0x3498db, cardBackTint: 0x34495e, holdTint: 0xe74c3c }, { name: "Royal", tableTint: 0x8e44ad, // Purple buttonTint: 0x9b59b6, cardBackTint: 0x2980b9, holdTint: 0xf1c40f }]; var currentTheme = 0; // Start with classic theme // Hand rankings and their scores var handRankings = { "Royal Flush": 250, "Straight Flush": 50, "Four of a Kind": 25, "Full House": 15, "Flush": 12, // Doubled from 6 to 12 "Straight": 8, // Doubled from 4 to 8 "Three of a Kind": 3, "Two Pair": 2, "Jacks or Better": 1, "High Card": 0 }; // Create the table var table = game.addChild(LK.getAsset('table', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 })); // Create scoreboard text var scoreText = new Text2("Score: 0", { size: 50, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.y = 50; // Create high score text var highScoreText = new Text2("High Score: " + highestScore, { size: 40, fill: 0xFFFFFF }); highScoreText.anchor.set(1, 0); LK.gui.topRight.addChild(highScoreText); highScoreText.x = -30; highScoreText.y = 30; // Create hand rank text var handRankText = new Text2("", { size: 60, fill: 0xFFFFFF }); handRankText.anchor.set(0.5, 0); handRankText.y = 150; LK.gui.top.addChild(handRankText); // Create paytable display var payTableContainer = new Container(); payTableContainer.x = 2048 / 2; payTableContainer.y = 1800; game.addChild(payTableContainer); // Create title for pay table var payTableTitle = new Text2("PAYOUTS", { size: 40, fill: 0xFFFFFF }); payTableTitle.anchor.set(0.5, 0); payTableTitle.y = -280; payTableContainer.addChild(payTableTitle); // Create pay table entries var payTableEntries = [{ hand: "Royal Flush", payout: "250×" }, { hand: "Straight Flush", payout: "50×" }, { hand: "Four of a Kind", payout: "25×" }, { hand: "Full House", payout: "15×" }, { hand: "Flush", payout: "12×" }, { hand: "Straight", payout: "8×" }, { hand: "Three of a Kind", payout: "3×" }, { hand: "Two Pair", payout: "2×" }, { hand: "Pairs of Jacks or Better", payout: "1×" }, { hand: "High Card", payout: "0×" }]; // Initialize references to pay table text objects var payTableTexts = {}; // Create each entry in the pay table for (var i = 0; i < payTableEntries.length; i++) { var entry = payTableEntries[i]; // Hand name var handText = new Text2(entry.hand, { size: 30, fill: 0xFFFFFF, align: 'right' }); handText.anchor.set(1, 0.5); handText.x = -20; handText.y = i * 40 - 220; payTableContainer.addChild(handText); // Payout value var payoutText = new Text2(entry.payout, { size: 30, fill: 0xFFFFFF, align: 'left' }); payoutText.anchor.set(0, 0.5); payoutText.x = 20; payoutText.y = i * 40 - 220; payTableContainer.addChild(payoutText); // Store references to text objects payTableTexts[entry.hand] = { hand: handText, payout: payoutText }; // Ensure text objects have style property initialized handText.style = handText.style || {}; payoutText.style = payoutText.style || {}; } // Function to highlight the current hand in the pay table function highlightPayTableEntry(handName) { // Reset all entries to default color for (var key in payTableTexts) { payTableTexts[key].hand.style.fill = 0xFFFFFF; payTableTexts[key].payout.style.fill = 0xFFFFFF; } // Highlight the matching entry if it exists if (payTableTexts[handName]) { payTableTexts[handName].hand.style.fill = 0xF1C40F; // Yellow highlight payTableTexts[handName].payout.style.fill = 0xF1C40F; // Add a scale animation to the highlighted entry tween.stop(payTableTexts[handName].hand); tween.stop(payTableTexts[handName].payout); tween(payTableTexts[handName].hand, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.elasticOut }); tween(payTableTexts[handName].payout, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.elasticOut }); } } // Create draw button var drawButton = new Button("DEAL", 400, 120); drawButton.x = 2048 / 2; drawButton.y = 1300; game.addChild(drawButton); // Create cost indicator text var costIndicator = new Text2("Each draw costs 1 score", { size: 30, fill: 0xFFFFFF }); costIndicator.anchor.set(0.5, 0); costIndicator.x = drawButton.x; costIndicator.y = drawButton.y + 70; game.addChild(costIndicator); // Setup button behavior drawButton.onPress = function () { if (gameState === STATE_DEAL) { startNewGame(); gameState = STATE_PLAYER_TURN; drawButton.buttonText.setText("DRAW"); } else if (gameState === STATE_PLAYER_TURN) { drawRemainingCards(); gameState = STATE_FINAL; drawButton.buttonText.setText("DEAL AGAIN"); } else if (gameState === STATE_FINAL) { resetGame(); gameState = STATE_DEAL; } }; // Initialize deck function createDeck() { var suits = ['hearts', 'diamonds', 'clubs', 'spades']; var ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']; var newDeck = []; for (var s = 0; s < suits.length; s++) { for (var r = 0; r < ranks.length; r++) { newDeck.push({ suit: suits[s], rank: ranks[r] }); } } return newDeck; } // Shuffle deck function shuffleDeck(deck) { // Play shuffle sound LK.getSound('shuffle').play(); // Regular shuffle first for (var i = deck.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = deck[i]; deck[i] = deck[j]; deck[j] = temp; } // Slightly increase chances for flushes and straights var rng = Math.random(); if (rng < 0.25) { // 25% chance to modify the deck for better hands (increased from 15%) if (Math.random() < 0.5) { // Try to create a flush by grouping cards of same suit deck.sort(function (a, b) { // Sort partially by suit, but keep some randomness if (a.suit === b.suit) { return Math.random() - 0.5; } return a.suit.localeCompare(b.suit); }); } else { // Try to create a straight by grouping sequential cards var ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']; deck.sort(function (a, b) { // Sort partially by rank, but keep some randomness var rankA = ranks.indexOf(a.rank); var rankB = ranks.indexOf(b.rank); if (Math.abs(rankA - rankB) <= 1) { return Math.random() - 0.5; } return rankA - rankB; }); } // Shuffle again but less thoroughly to maintain more of the patterns for (var i = deck.length - 1; i > 15; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = deck[i]; deck[i] = deck[j]; deck[j] = temp; } } return deck; } // Deal cards to player function dealCards() { // Remove old cards from display for (var i = 0; i < playerHand.length; i++) { if (playerHand[i] && playerHand[i].parent) { playerHand[i].parent.removeChild(playerHand[i]); } } // Clear hand playerHand = []; // Create new hand for (var i = 0; i < CARDS_PER_HAND; i++) { if (deck.length > 0) { var cardData = deck.pop(); var card = new Card(cardData.suit, cardData.rank); // Position card card.x = 2048 / 2 + (i - Math.floor(CARDS_PER_HAND / 2)) * CARD_SPACING; card.y = CARD_START_Y; // Add animation card.alpha = 0; card.y += 50; tween(card, { alpha: 1, y: CARD_START_Y }, { duration: 300, easing: tween.easeOutQuad, delay: i * 100 }); playerHand.push(card); game.addChild(card); // Play sound LK.setTimeout(function () { LK.getSound('cardDeal').play(); }, i * 100); } } } // Start a new game function startNewGame() { // Check if player has enough score to place a bet if (LK.getScore() < currentBet) { // Game over - not enough score to bet LK.showGameOver(); return; } // Deduct bet amount LK.setScore(LK.getScore() - currentBet); updateScore(); deck = createDeck(); shuffleDeck(deck); dealCards(); handRankText.setText(""); handScore = 0; // Reset pay table highlighting highlightPayTableEntry(""); } // Draw new cards to replace non-held cards function drawRemainingCards() { for (var i = 0; i < playerHand.length; i++) { if (!playerHand[i].isHeld && deck.length > 0) { var cardData = deck.pop(); // Store old card for animation var oldCard = playerHand[i]; // Create new card var newCard = new Card(cardData.suit, cardData.rank); newCard.x = oldCard.x; newCard.y = oldCard.y; // Disable card interaction - we're in final state now newCard.interactive = false; // Replace old card game.removeChild(oldCard); game.addChild(newCard); playerHand[i] = newCard; // Animation and sound newCard.alpha = 0; newCard.y += 20; tween(newCard, { alpha: 1, y: CARD_START_Y }, { duration: 200, easing: tween.easeOutQuad, delay: i * 50 }); LK.setTimeout(function () { LK.getSound('cardDeal').play(); }, i * 50); } else { // Disable interaction for held cards too playerHand[i].interactive = false; } } // Evaluate hand after a short delay LK.setTimeout(function () { evaluateHand(); }, 500); } // Reset the game for a new round function resetGame() { // Check if player has enough score to continue if (LK.getScore() <= 0) { // Game over - out of score LK.showGameOver(); return; } // Clear the board for (var i = 0; i < playerHand.length; i++) { if (playerHand[i] && playerHand[i].parent) { tween(playerHand[i], { alpha: 0, y: playerHand[i].y + 50 }, { duration: 300, delay: i * 50, onFinish: function () { if (this && this.parent) { this.parent.removeChild(this); } }.bind(playerHand[i]) }); } } playerHand = []; handRankText.setText(""); // Reset pay table highlighting highlightPayTableEntry(""); } // Evaluate the player's hand function evaluateHand() { var cardValues = playerHand.map(function (card) { return { value: card.getValue(), suit: card.suit }; }); // Sort cards by value (descending) cardValues.sort(function (a, b) { return b.value - a.value; }); // Count occurrences of each value var valueCounts = {}; var suitCounts = {}; for (var i = 0; i < cardValues.length; i++) { var value = cardValues[i].value; var suit = cardValues[i].suit; valueCounts[value] = (valueCounts[value] || 0) + 1; suitCounts[suit] = (suitCounts[suit] || 0) + 1; } // Check for pairs, three of a kind, etc. var pairs = 0; var threeOfAKind = false; var fourOfAKind = false; var hasPairOfJacksOrBetter = false; for (var value in valueCounts) { // Need to convert value from string to number for proper comparison var numValue = parseInt(value); if (valueCounts[value] === 2) { pairs++; if (numValue >= 11) { // Jack or better hasPairOfJacksOrBetter = true; } } else if (valueCounts[value] === 3) { threeOfAKind = true; } else if (valueCounts[value] === 4) { fourOfAKind = true; } } // Check for flush var isFlush = false; for (var suit in suitCounts) { if (suitCounts[suit] === 5) { isFlush = true; break; } } // Check for straight var isStraight = false; if (Object.keys(valueCounts).length === 5) { // Create an array with just the values for easier checking var values = cardValues.map(function (c) { return c.value; }); // Sort values in ascending order values.sort(function (a, b) { return a - b; }); // Check if all cards are in sequence var isSequential = true; for (var i = 0; i < values.length - 1; i++) { if (values[i] + 1 !== values[i + 1]) { isSequential = false; break; } } if (isSequential) { isStraight = true; } // Special case for A-5 straight (where Ace is low) if (values[0] === 2 && values[1] === 3 && values[2] === 4 && values[3] === 5 && values[4] === 14) { isStraight = true; } } // Determine hand rank if (isStraight && isFlush) { if (cardValues[0].value === 14 && cardValues[1].value === 13 && cardValues[2].value === 12 && cardValues[3].value === 11 && cardValues[4].value === 10) { handRank = "Royal Flush"; } else { handRank = "Straight Flush"; } } else if (fourOfAKind) { handRank = "Four of a Kind"; } else if (threeOfAKind && pairs === 1) { handRank = "Full House"; } else if (isFlush) { handRank = "Flush"; } else if (isStraight) { handRank = "Straight"; } else if (threeOfAKind) { handRank = "Three of a Kind"; } else if (pairs === 2) { handRank = "Two Pair"; } else if (hasPairOfJacksOrBetter) { handRank = "Jacks or Better"; } else { handRank = "High Card"; } // Set score handScore = handRankings[handRank]; LK.setScore(LK.getScore() + handScore); // Update high score if needed if (LK.getScore() > highestScore) { highestScore = LK.getScore(); storage.highestScore = highestScore; highScoreText.setText("High Score: " + highestScore); } // Display hand rank handRankText.setText(handRank + " - " + handScore + " points!"); // Update score updateScore(); // Highlight the current hand in the pay table highlightPayTableEntry(handRank); // Play win sound if score is positive if (handScore > 0) { // Animate winning cards for (var i = 0; i < playerHand.length; i++) { // Animate each card tween(playerHand[i], { rotation: Math.PI * 2, // Full rotation y: playerHand[i].y - 30 // Bounce up }, { duration: 800, easing: tween.elasticOut, delay: i * 100, onFinish: function () { // Back to normal position after animation tween(this, { y: CARD_START_Y }, { duration: 300, easing: tween.easeOutQuad }); }.bind(playerHand[i]) }); } // Choose appropriate win sound based on hand value if (handScore >= 25) { // Big wins (Royal Flush, Straight Flush, Four of a Kind) LK.getSound('bigWin').play(); // Add screen flash for big wins LK.effects.flashScreen(0xf1c40f, 500); // Gold/yellow flash } else { // Normal wins var volume = Math.min(0.5 + handScore / 100, 1.0); var winSound = LK.getSound('win'); winSound.volume = volume; winSound.play(); } } } // Update the score display function updateScore() { scoreText.setText("Score: " + LK.getScore()); } // Create theme button var themeButton = new Button("THEME", 200, 80, 0x3498db); themeButton.x = highScoreText.x - 140; themeButton.y = highScoreText.y + 100; game.addChild(themeButton); // Setup theme button behavior themeButton.onPress = function () { // Switch to next theme currentTheme = (currentTheme + 1) % themes.length; applyTheme(themes[currentTheme]); // Create notification text var notification = new Text2("Theme: " + themes[currentTheme].name, { size: 40, fill: 0xFFFFFF }); notification.anchor.set(0.5); notification.x = 2048 / 2; notification.y = 400; game.addChild(notification); // Set initial animation properties notification.alpha = 0; // Animate notification tween(notification, { alpha: 1, y: 350 }, { duration: 300, easing: tween.easeOutQuad, onFinish: function onFinish() { // Fade out after delay LK.setTimeout(function () { tween(notification, { alpha: 0, y: 300 }, { duration: 500, onFinish: function onFinish() { if (notification.parent) { notification.parent.removeChild(notification); } } }); }, 1500); } }); }; // Function to apply theme to game elements function applyTheme(theme) { // Apply to table table.tint = theme.tableTint; // Apply to buttons drawButton.buttonShape.tint = theme.buttonTint; themeButton.buttonShape.tint = theme.buttonTint; // Apply to cards for (var i = 0; i < playerHand.length; i++) { if (playerHand[i]) { playerHand[i].cardBack.tint = theme.cardBackTint; playerHand[i].holdIndicator.tint = theme.holdTint; } } } // Create cash out button var cashOutButton = new CashOutButton(); cashOutButton.x = 2048 / 2; cashOutButton.y = payTableContainer.y + 350; // Position lower below the pay table game.addChild(cashOutButton); // Setup cash out button behavior cashOutButton.onPress = function () { if (LK.getScore() > 0) { // Check if this is a new high score if (LK.getScore() > highestScore) { highestScore = LK.getScore(); storage.highestScore = highestScore; highScoreText.setText("High Score: " + highestScore); } // Show game over with current score LK.showYouWin(); } }; // Initialize the game with 10 score LK.setScore(10); updateScore(); LK.playMusic('bgMusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highestScore: 0
});
/****
* Classes
****/
var Button = Container.expand(function (text, width, height, color) {
var self = Container.call(this);
self.buttonShape = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
width: width || 300,
height: height || 100,
tint: color || 0x2ecc71
});
self.buttonText = new Text2(text, {
size: 40,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5);
self.addChild(self.buttonText);
self.disable = function () {
self.buttonShape.tint = 0x95a5a6;
self.interactive = false;
};
self.enable = function () {
self.buttonShape.tint = color || 0x2ecc71;
self.interactive = true;
};
// Event handlers
self.down = function (x, y, obj) {
tween(self, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
self.up = function (x, y, obj) {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
if (self.onPress) {
self.onPress();
}
};
return self;
});
var Card = Container.expand(function (suit, rank) {
var self = Container.call(this);
self.suit = suit;
self.rank = rank;
self.isHeld = false;
// Get suit symbol
self.getSuitSymbol = function () {
if (self.suit === 'hearts') {
return '♥';
}
if (self.suit === 'diamonds') {
return '♦';
}
if (self.suit === 'clubs') {
return '♣';
}
if (self.suit === 'spades') {
return '♠';
}
return '';
};
// Get card color
self.getCardColor = function () {
if (self.suit === 'hearts' || self.suit === 'diamonds') {
return "#ff0000";
}
return "#000000";
};
// Get card label
self.getCardLabel = function () {
return self.rank + self.getSuitSymbol();
};
// Front and back of card
self.cardFront = self.attachAsset('cardFront', {
anchorX: 0.5,
anchorY: 0.5
});
self.cardBack = self.attachAsset('cardBack', {
anchorX: 0.5,
anchorY: 0.5
});
self.cardBack.visible = false;
// Hold indicator
self.holdIndicator = self.attachAsset('holdIndicator', {
anchorX: 0.5,
anchorY: 0.5,
y: 130
});
self.holdIndicator.visible = false;
// Card text (suit and rank)
self.rankText = new Text2(self.getCardLabel(), {
size: 58,
fill: self.getCardColor()
});
self.rankText.anchor.set(0.5);
self.rankText.y = -100;
self.addChild(self.rankText);
// Suit symbol in the center
self.suitText = new Text2(self.getSuitSymbol(), {
size: 116,
fill: self.getCardColor()
});
self.suitText.anchor.set(0.5);
self.addChild(self.suitText);
// Hold text
self.holdText = new Text2("HOLD", {
size: 32,
fill: 0x000000
});
self.holdText.anchor.set(0.5);
self.holdText.y = 130;
self.addChild(self.holdText);
self.holdText.visible = false;
// Toggle hold state
self.toggleHold = function () {
self.isHeld = !self.isHeld;
self.holdIndicator.visible = self.isHeld;
self.holdText.visible = self.isHeld;
};
// Get card value for hand evaluations (1-13)
self.getValue = function () {
if (self.rank === 'A') {
return 14;
}
if (self.rank === 'K') {
return 13;
}
if (self.rank === 'Q') {
return 12;
}
if (self.rank === 'J') {
return 11;
}
return parseInt(self.rank);
};
// Event handlers
self.down = function (x, y, obj) {
// Scale down slightly on press
tween(self, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
self.up = function (x, y, obj) {
// Scale back to normal
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
self.toggleHold();
// Play sound when toggling hold
LK.getSound('holdCard').play();
// Add small bounce animation when card is held
if (self.isHeld) {
tween(self, {
y: self.y - 15
}, {
duration: 150,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
tween(self, {
y: CARD_START_Y
}, {
duration: 200,
easing: tween.elasticOut
});
}
});
}
};
return self;
});
var CashOutButton = Container.expand(function () {
var self = Container.call(this);
self.buttonShape = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xe74c3c // Red color
});
self.buttonText = new Text2("CASH OUT", {
size: 40,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5);
self.addChild(self.buttonText);
// Event handlers
self.down = function (x, y, obj) {
tween(self, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
self.up = function (x, y, obj) {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
if (self.onPress) {
self.onPress();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c6e31
});
/****
* Game Code
****/
// Constants
var CARD_SPACING = 260;
var CARDS_PER_HAND = 5;
var CARD_START_Y = 700;
var DECK_POSITION_X = 300;
var DECK_POSITION_Y = 300;
// Game states
var STATE_DEAL = "deal";
var STATE_PLAYER_TURN = "playerTurn";
var STATE_FINAL = "final";
// Game variables
var deck = [];
var playerHand = [];
var gameState = STATE_DEAL;
var handScore = 0;
var handRank = "";
var highestScore = storage.highestScore || 0;
var currentBet = 1; // Each bet costs 1 score
// Game themes
var themes = [{
name: "Classic",
tableTint: 0x2c6e31,
// Green
buttonTint: 0x2ecc71,
cardBackTint: 0x0e4d92,
// Blue
holdTint: 0xf39c12 // Orange
}, {
name: "Dark",
tableTint: 0x2c3e50,
// Dark blue
buttonTint: 0x3498db,
cardBackTint: 0x34495e,
holdTint: 0xe74c3c
}, {
name: "Royal",
tableTint: 0x8e44ad,
// Purple
buttonTint: 0x9b59b6,
cardBackTint: 0x2980b9,
holdTint: 0xf1c40f
}];
var currentTheme = 0; // Start with classic theme
// Hand rankings and their scores
var handRankings = {
"Royal Flush": 250,
"Straight Flush": 50,
"Four of a Kind": 25,
"Full House": 15,
"Flush": 12,
// Doubled from 6 to 12
"Straight": 8,
// Doubled from 4 to 8
"Three of a Kind": 3,
"Two Pair": 2,
"Jacks or Better": 1,
"High Card": 0
};
// Create the table
var table = game.addChild(LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
}));
// Create scoreboard text
var scoreText = new Text2("Score: 0", {
size: 50,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 50;
// Create high score text
var highScoreText = new Text2("High Score: " + highestScore, {
size: 40,
fill: 0xFFFFFF
});
highScoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(highScoreText);
highScoreText.x = -30;
highScoreText.y = 30;
// Create hand rank text
var handRankText = new Text2("", {
size: 60,
fill: 0xFFFFFF
});
handRankText.anchor.set(0.5, 0);
handRankText.y = 150;
LK.gui.top.addChild(handRankText);
// Create paytable display
var payTableContainer = new Container();
payTableContainer.x = 2048 / 2;
payTableContainer.y = 1800;
game.addChild(payTableContainer);
// Create title for pay table
var payTableTitle = new Text2("PAYOUTS", {
size: 40,
fill: 0xFFFFFF
});
payTableTitle.anchor.set(0.5, 0);
payTableTitle.y = -280;
payTableContainer.addChild(payTableTitle);
// Create pay table entries
var payTableEntries = [{
hand: "Royal Flush",
payout: "250×"
}, {
hand: "Straight Flush",
payout: "50×"
}, {
hand: "Four of a Kind",
payout: "25×"
}, {
hand: "Full House",
payout: "15×"
}, {
hand: "Flush",
payout: "12×"
}, {
hand: "Straight",
payout: "8×"
}, {
hand: "Three of a Kind",
payout: "3×"
}, {
hand: "Two Pair",
payout: "2×"
}, {
hand: "Pairs of Jacks or Better",
payout: "1×"
}, {
hand: "High Card",
payout: "0×"
}];
// Initialize references to pay table text objects
var payTableTexts = {};
// Create each entry in the pay table
for (var i = 0; i < payTableEntries.length; i++) {
var entry = payTableEntries[i];
// Hand name
var handText = new Text2(entry.hand, {
size: 30,
fill: 0xFFFFFF,
align: 'right'
});
handText.anchor.set(1, 0.5);
handText.x = -20;
handText.y = i * 40 - 220;
payTableContainer.addChild(handText);
// Payout value
var payoutText = new Text2(entry.payout, {
size: 30,
fill: 0xFFFFFF,
align: 'left'
});
payoutText.anchor.set(0, 0.5);
payoutText.x = 20;
payoutText.y = i * 40 - 220;
payTableContainer.addChild(payoutText);
// Store references to text objects
payTableTexts[entry.hand] = {
hand: handText,
payout: payoutText
};
// Ensure text objects have style property initialized
handText.style = handText.style || {};
payoutText.style = payoutText.style || {};
}
// Function to highlight the current hand in the pay table
function highlightPayTableEntry(handName) {
// Reset all entries to default color
for (var key in payTableTexts) {
payTableTexts[key].hand.style.fill = 0xFFFFFF;
payTableTexts[key].payout.style.fill = 0xFFFFFF;
}
// Highlight the matching entry if it exists
if (payTableTexts[handName]) {
payTableTexts[handName].hand.style.fill = 0xF1C40F; // Yellow highlight
payTableTexts[handName].payout.style.fill = 0xF1C40F;
// Add a scale animation to the highlighted entry
tween.stop(payTableTexts[handName].hand);
tween.stop(payTableTexts[handName].payout);
tween(payTableTexts[handName].hand, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.elasticOut
});
tween(payTableTexts[handName].payout, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.elasticOut
});
}
}
// Create draw button
var drawButton = new Button("DEAL", 400, 120);
drawButton.x = 2048 / 2;
drawButton.y = 1300;
game.addChild(drawButton);
// Create cost indicator text
var costIndicator = new Text2("Each draw costs 1 score", {
size: 30,
fill: 0xFFFFFF
});
costIndicator.anchor.set(0.5, 0);
costIndicator.x = drawButton.x;
costIndicator.y = drawButton.y + 70;
game.addChild(costIndicator);
// Setup button behavior
drawButton.onPress = function () {
if (gameState === STATE_DEAL) {
startNewGame();
gameState = STATE_PLAYER_TURN;
drawButton.buttonText.setText("DRAW");
} else if (gameState === STATE_PLAYER_TURN) {
drawRemainingCards();
gameState = STATE_FINAL;
drawButton.buttonText.setText("DEAL AGAIN");
} else if (gameState === STATE_FINAL) {
resetGame();
gameState = STATE_DEAL;
}
};
// Initialize deck
function createDeck() {
var suits = ['hearts', 'diamonds', 'clubs', 'spades'];
var ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
var newDeck = [];
for (var s = 0; s < suits.length; s++) {
for (var r = 0; r < ranks.length; r++) {
newDeck.push({
suit: suits[s],
rank: ranks[r]
});
}
}
return newDeck;
}
// Shuffle deck
function shuffleDeck(deck) {
// Play shuffle sound
LK.getSound('shuffle').play();
// Regular shuffle first
for (var i = deck.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
// Slightly increase chances for flushes and straights
var rng = Math.random();
if (rng < 0.25) {
// 25% chance to modify the deck for better hands (increased from 15%)
if (Math.random() < 0.5) {
// Try to create a flush by grouping cards of same suit
deck.sort(function (a, b) {
// Sort partially by suit, but keep some randomness
if (a.suit === b.suit) {
return Math.random() - 0.5;
}
return a.suit.localeCompare(b.suit);
});
} else {
// Try to create a straight by grouping sequential cards
var ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
deck.sort(function (a, b) {
// Sort partially by rank, but keep some randomness
var rankA = ranks.indexOf(a.rank);
var rankB = ranks.indexOf(b.rank);
if (Math.abs(rankA - rankB) <= 1) {
return Math.random() - 0.5;
}
return rankA - rankB;
});
}
// Shuffle again but less thoroughly to maintain more of the patterns
for (var i = deck.length - 1; i > 15; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
}
return deck;
}
// Deal cards to player
function dealCards() {
// Remove old cards from display
for (var i = 0; i < playerHand.length; i++) {
if (playerHand[i] && playerHand[i].parent) {
playerHand[i].parent.removeChild(playerHand[i]);
}
}
// Clear hand
playerHand = [];
// Create new hand
for (var i = 0; i < CARDS_PER_HAND; i++) {
if (deck.length > 0) {
var cardData = deck.pop();
var card = new Card(cardData.suit, cardData.rank);
// Position card
card.x = 2048 / 2 + (i - Math.floor(CARDS_PER_HAND / 2)) * CARD_SPACING;
card.y = CARD_START_Y;
// Add animation
card.alpha = 0;
card.y += 50;
tween(card, {
alpha: 1,
y: CARD_START_Y
}, {
duration: 300,
easing: tween.easeOutQuad,
delay: i * 100
});
playerHand.push(card);
game.addChild(card);
// Play sound
LK.setTimeout(function () {
LK.getSound('cardDeal').play();
}, i * 100);
}
}
}
// Start a new game
function startNewGame() {
// Check if player has enough score to place a bet
if (LK.getScore() < currentBet) {
// Game over - not enough score to bet
LK.showGameOver();
return;
}
// Deduct bet amount
LK.setScore(LK.getScore() - currentBet);
updateScore();
deck = createDeck();
shuffleDeck(deck);
dealCards();
handRankText.setText("");
handScore = 0;
// Reset pay table highlighting
highlightPayTableEntry("");
}
// Draw new cards to replace non-held cards
function drawRemainingCards() {
for (var i = 0; i < playerHand.length; i++) {
if (!playerHand[i].isHeld && deck.length > 0) {
var cardData = deck.pop();
// Store old card for animation
var oldCard = playerHand[i];
// Create new card
var newCard = new Card(cardData.suit, cardData.rank);
newCard.x = oldCard.x;
newCard.y = oldCard.y;
// Disable card interaction - we're in final state now
newCard.interactive = false;
// Replace old card
game.removeChild(oldCard);
game.addChild(newCard);
playerHand[i] = newCard;
// Animation and sound
newCard.alpha = 0;
newCard.y += 20;
tween(newCard, {
alpha: 1,
y: CARD_START_Y
}, {
duration: 200,
easing: tween.easeOutQuad,
delay: i * 50
});
LK.setTimeout(function () {
LK.getSound('cardDeal').play();
}, i * 50);
} else {
// Disable interaction for held cards too
playerHand[i].interactive = false;
}
}
// Evaluate hand after a short delay
LK.setTimeout(function () {
evaluateHand();
}, 500);
}
// Reset the game for a new round
function resetGame() {
// Check if player has enough score to continue
if (LK.getScore() <= 0) {
// Game over - out of score
LK.showGameOver();
return;
}
// Clear the board
for (var i = 0; i < playerHand.length; i++) {
if (playerHand[i] && playerHand[i].parent) {
tween(playerHand[i], {
alpha: 0,
y: playerHand[i].y + 50
}, {
duration: 300,
delay: i * 50,
onFinish: function () {
if (this && this.parent) {
this.parent.removeChild(this);
}
}.bind(playerHand[i])
});
}
}
playerHand = [];
handRankText.setText("");
// Reset pay table highlighting
highlightPayTableEntry("");
}
// Evaluate the player's hand
function evaluateHand() {
var cardValues = playerHand.map(function (card) {
return {
value: card.getValue(),
suit: card.suit
};
});
// Sort cards by value (descending)
cardValues.sort(function (a, b) {
return b.value - a.value;
});
// Count occurrences of each value
var valueCounts = {};
var suitCounts = {};
for (var i = 0; i < cardValues.length; i++) {
var value = cardValues[i].value;
var suit = cardValues[i].suit;
valueCounts[value] = (valueCounts[value] || 0) + 1;
suitCounts[suit] = (suitCounts[suit] || 0) + 1;
}
// Check for pairs, three of a kind, etc.
var pairs = 0;
var threeOfAKind = false;
var fourOfAKind = false;
var hasPairOfJacksOrBetter = false;
for (var value in valueCounts) {
// Need to convert value from string to number for proper comparison
var numValue = parseInt(value);
if (valueCounts[value] === 2) {
pairs++;
if (numValue >= 11) {
// Jack or better
hasPairOfJacksOrBetter = true;
}
} else if (valueCounts[value] === 3) {
threeOfAKind = true;
} else if (valueCounts[value] === 4) {
fourOfAKind = true;
}
}
// Check for flush
var isFlush = false;
for (var suit in suitCounts) {
if (suitCounts[suit] === 5) {
isFlush = true;
break;
}
}
// Check for straight
var isStraight = false;
if (Object.keys(valueCounts).length === 5) {
// Create an array with just the values for easier checking
var values = cardValues.map(function (c) {
return c.value;
});
// Sort values in ascending order
values.sort(function (a, b) {
return a - b;
});
// Check if all cards are in sequence
var isSequential = true;
for (var i = 0; i < values.length - 1; i++) {
if (values[i] + 1 !== values[i + 1]) {
isSequential = false;
break;
}
}
if (isSequential) {
isStraight = true;
}
// Special case for A-5 straight (where Ace is low)
if (values[0] === 2 && values[1] === 3 && values[2] === 4 && values[3] === 5 && values[4] === 14) {
isStraight = true;
}
}
// Determine hand rank
if (isStraight && isFlush) {
if (cardValues[0].value === 14 && cardValues[1].value === 13 && cardValues[2].value === 12 && cardValues[3].value === 11 && cardValues[4].value === 10) {
handRank = "Royal Flush";
} else {
handRank = "Straight Flush";
}
} else if (fourOfAKind) {
handRank = "Four of a Kind";
} else if (threeOfAKind && pairs === 1) {
handRank = "Full House";
} else if (isFlush) {
handRank = "Flush";
} else if (isStraight) {
handRank = "Straight";
} else if (threeOfAKind) {
handRank = "Three of a Kind";
} else if (pairs === 2) {
handRank = "Two Pair";
} else if (hasPairOfJacksOrBetter) {
handRank = "Jacks or Better";
} else {
handRank = "High Card";
}
// Set score
handScore = handRankings[handRank];
LK.setScore(LK.getScore() + handScore);
// Update high score if needed
if (LK.getScore() > highestScore) {
highestScore = LK.getScore();
storage.highestScore = highestScore;
highScoreText.setText("High Score: " + highestScore);
}
// Display hand rank
handRankText.setText(handRank + " - " + handScore + " points!");
// Update score
updateScore();
// Highlight the current hand in the pay table
highlightPayTableEntry(handRank);
// Play win sound if score is positive
if (handScore > 0) {
// Animate winning cards
for (var i = 0; i < playerHand.length; i++) {
// Animate each card
tween(playerHand[i], {
rotation: Math.PI * 2,
// Full rotation
y: playerHand[i].y - 30 // Bounce up
}, {
duration: 800,
easing: tween.elasticOut,
delay: i * 100,
onFinish: function () {
// Back to normal position after animation
tween(this, {
y: CARD_START_Y
}, {
duration: 300,
easing: tween.easeOutQuad
});
}.bind(playerHand[i])
});
}
// Choose appropriate win sound based on hand value
if (handScore >= 25) {
// Big wins (Royal Flush, Straight Flush, Four of a Kind)
LK.getSound('bigWin').play();
// Add screen flash for big wins
LK.effects.flashScreen(0xf1c40f, 500); // Gold/yellow flash
} else {
// Normal wins
var volume = Math.min(0.5 + handScore / 100, 1.0);
var winSound = LK.getSound('win');
winSound.volume = volume;
winSound.play();
}
}
}
// Update the score display
function updateScore() {
scoreText.setText("Score: " + LK.getScore());
}
// Create theme button
var themeButton = new Button("THEME", 200, 80, 0x3498db);
themeButton.x = highScoreText.x - 140;
themeButton.y = highScoreText.y + 100;
game.addChild(themeButton);
// Setup theme button behavior
themeButton.onPress = function () {
// Switch to next theme
currentTheme = (currentTheme + 1) % themes.length;
applyTheme(themes[currentTheme]);
// Create notification text
var notification = new Text2("Theme: " + themes[currentTheme].name, {
size: 40,
fill: 0xFFFFFF
});
notification.anchor.set(0.5);
notification.x = 2048 / 2;
notification.y = 400;
game.addChild(notification);
// Set initial animation properties
notification.alpha = 0;
// Animate notification
tween(notification, {
alpha: 1,
y: 350
}, {
duration: 300,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Fade out after delay
LK.setTimeout(function () {
tween(notification, {
alpha: 0,
y: 300
}, {
duration: 500,
onFinish: function onFinish() {
if (notification.parent) {
notification.parent.removeChild(notification);
}
}
});
}, 1500);
}
});
};
// Function to apply theme to game elements
function applyTheme(theme) {
// Apply to table
table.tint = theme.tableTint;
// Apply to buttons
drawButton.buttonShape.tint = theme.buttonTint;
themeButton.buttonShape.tint = theme.buttonTint;
// Apply to cards
for (var i = 0; i < playerHand.length; i++) {
if (playerHand[i]) {
playerHand[i].cardBack.tint = theme.cardBackTint;
playerHand[i].holdIndicator.tint = theme.holdTint;
}
}
}
// Create cash out button
var cashOutButton = new CashOutButton();
cashOutButton.x = 2048 / 2;
cashOutButton.y = payTableContainer.y + 350; // Position lower below the pay table
game.addChild(cashOutButton);
// Setup cash out button behavior
cashOutButton.onPress = function () {
if (LK.getScore() > 0) {
// Check if this is a new high score
if (LK.getScore() > highestScore) {
highestScore = LK.getScore();
storage.highestScore = highestScore;
highScoreText.setText("High Score: " + highestScore);
}
// Show game over with current score
LK.showYouWin();
}
};
// Initialize the game with 10 score
LK.setScore(10);
updateScore();
LK.playMusic('bgMusic');