/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Card class: represents a single card (suit, rank, value, face up/down) var Card = Container.expand(function () { var self = Container.call(this); // Card properties self.suit = null; // '♠', '♥', '♦', '♣' self.rank = null; // 'A', '2', ..., 'K' self.value = 0; // 1-11 self.faceUp = true; // Card background (white for face up, gray for face down) var cardBg = self.attachAsset('cardBg', { width: 220, height: 320, color: 0xffffff, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); // Card text (rank and suit) var cardText = new Text2('', { size: 90, fill: 0x222222 }); cardText.anchor.set(0.5, 0.5); cardText.x = 0; cardText.y = 0; self.addChild(cardText); // Face down overlay var faceDownBg = self.attachAsset('cardBack', { width: 220, height: 320, color: 0x888888, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); faceDownBg.visible = false; self.addChild(faceDownBg); // Set card data self.setCard = function (suit, rank, value, faceUp) { self.suit = suit; self.rank = rank; self.value = value; self.faceUp = faceUp !== false; updateVisual(); }; // Flip card (show/hide face) self.setFaceUp = function (faceUp) { self.faceUp = faceUp; updateVisual(); }; // Update card visuals function updateVisual() { if (self.faceUp) { cardBg.visible = true; cardText.visible = true; faceDownBg.visible = false; cardText.setText(self.rank + self.suit); // Red color for hearts/diamonds, black for spades/clubs if (self.suit === '♥' || self.suit === '♦') { cardText.setStyle({ fill: "#d22" }); } else { cardText.setStyle({ fill: "#222" }); } } else { cardBg.visible = false; cardText.visible = false; faceDownBg.visible = true; } } return self; }); // Hand class: represents a hand of cards (player or dealer) var Hand = Container.expand(function () { var self = Container.call(this); self.cards = []; // Add a card to the hand self.addCard = function (card) { self.cards.push(card); self.addChild(card); layoutCards(); }; // Remove all cards self.clear = function () { for (var i = 0; i < self.cards.length; i++) { self.cards[i].destroy(); } self.cards = []; }; // Layout cards horizontally with overlap function layoutCards() { var n = self.cards.length; var spacing = 120; var startX = -((n - 1) * spacing) / 2; for (var i = 0; i < n; i++) { var card = self.cards[i]; card.x = startX + i * spacing; card.y = 0; } } // Calculate hand value (Blackjack rules: Aces can be 1 or 11) self.getValue = function () { var total = 0; var aces = 0; for (var i = 0; i < self.cards.length; i++) { var v = self.cards[i].value; total += v; if (self.cards[i].rank === 'A') aces++; } // Adjust for aces while (total > 21 && aces > 0) { total -= 10; aces--; } return total; }; // Reveal all cards self.revealAll = function () { for (var i = 0; i < self.cards.length; i++) { self.cards[i].setFaceUp(true); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x006622 }); /**** * Game Code ****/ // Card deck data var suits = ['♠', '♥', '♦', '♣']; var ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']; var values = { 'A': 11, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, 'J': 10, 'Q': 10, 'K': 10 }; // Game state var deck = []; var playerHand = null; var dealerHand = null; var playerScore = 0; var winStreak = 0; var gamePhase = 'playerTurn'; // 'playerTurn', 'dealerTurn', 'roundOver' var messageTxt = null; var playerValueTxt = null; var dealerValueTxt = null; var hitBtn = null; var standBtn = null; var newGameBtn = null; // GUI: Score and streak var scoreTxt = new Text2('Score: 0', { size: 90, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var streakTxt = new Text2('Streak: 0', { size: 70, fill: "#fff" }); streakTxt.anchor.set(0.5, 0); LK.gui.top.addChild(streakTxt); streakTxt.y = 110; // Message text (centered) messageTxt = new Text2('', { size: 120, fill: "#fff" }); messageTxt.anchor.set(0.5, 0.5); messageTxt.y = -350; // Move handwriting/message higher LK.gui.center.addChild(messageTxt); // Add background image behind all elements var tableBg = LK.getAsset('tableBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, width: 2048, height: 2732 }); game.addChild(tableBg); // Player hand playerHand = new Hand(); game.addChild(playerHand); playerHand.x = 2048 / 2; playerHand.y = 2732 - 500; // Dealer hand dealerHand = new Hand(); game.addChild(dealerHand); dealerHand.x = 2048 / 2; dealerHand.y = 500; // Player value text playerValueTxt = new Text2('', { size: 70, fill: "#fff" }); playerValueTxt.anchor.set(0.5, 0); LK.gui.bottom.addChild(playerValueTxt); playerValueTxt.y = -200; // Dealer value text dealerValueTxt = new Text2('', { size: 70, fill: "#fff" }); dealerValueTxt.anchor.set(0.5, 0); LK.gui.top.addChild(dealerValueTxt); dealerValueTxt.y = 220; // Buttons hitBtn = createButton('Hit', 0x229922, function () { if (gamePhase !== 'playerTurn') return; dealCardToHand(playerHand, true); updateHandValues(); if (playerHand.getValue() > 21) { endRound('bust'); } }); standBtn = createButton('Stand', 0x2266cc, function () { if (gamePhase !== 'playerTurn') return; gamePhase = 'dealerTurn'; updateButtons(); dealerTurn(); }); newGameBtn = createButton('New Game', 0x993333, function () { startNewRound(); }); newGameBtn.visible = false; // Button layout hitBtn.x = 2048 / 2 - 220; hitBtn.y = 2732 - 120; // Move lower standBtn.x = 2048 / 2 + 220; standBtn.y = 2732 - 120; // Move lower newGameBtn.x = 2048 / 2; newGameBtn.y = 2732 / 2 + 600; // Move lower game.addChild(hitBtn); game.addChild(standBtn); game.addChild(newGameBtn); // Prevent elements in top left 100x100 scoreTxt.x = 2048 / 2; scoreTxt.y = 20; streakTxt.x = 2048 / 2; streakTxt.y = 110; dealerValueTxt.x = 2048 / 2; dealerValueTxt.y = 220; // Start first round startNewRound(); // --- Functions --- // Create a button (Container with shape and text) function createButton(label, color, onTap) { var btn = new Container(); var bg = btn.attachAsset('btnBg', { width: 320, height: 120, color: color, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); var txt = new Text2(label, { size: 70, fill: "#fff" }); txt.anchor.set(0.5, 0.5); txt.x = 0; txt.y = 0; btn.addChild(txt); // Touch event btn.down = function (x, y, obj) { onTap(); }; // Visual feedback btn.interactive = true; btn.buttonMode = true; btn.alpha = 1; btn.up = function () { btn.alpha = 1; }; btn.move = function () {}; return btn; } // Shuffle deck function shuffleDeck() { var d = []; for (var s = 0; s < suits.length; s++) { for (var r = 0; r < ranks.length; r++) { d.push({ suit: suits[s], rank: ranks[r], value: values[ranks[r]] }); } } // Fisher-Yates shuffle for (var i = d.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var tmp = d[i]; d[i] = d[j]; d[j] = tmp; } return d; } // Deal a card to a hand function dealCardToHand(hand, faceUp) { if (deck.length === 0) deck = shuffleDeck(); var cardData = deck.pop(); var card = new Card(); card.setCard(cardData.suit, cardData.rank, cardData.value, faceUp); hand.addCard(card); return card; } // Start a new round function startNewRound() { gamePhase = 'playerTurn'; messageTxt.setText(''); playerHand.clear(); dealerHand.clear(); deck = shuffleDeck(); // Deal initial cards dealCardToHand(playerHand, true); dealCardToHand(dealerHand, true); dealCardToHand(playerHand, true); var dealerHole = dealCardToHand(dealerHand, false); // Dealer's hole card updateHandValues(); updateButtons(); newGameBtn.visible = false; } // Update hand value displays function updateHandValues() { var pv = playerHand.getValue(); var dv = dealerHand.cards[0].faceUp ? dealerHand.cards[0].value : '?'; playerValueTxt.setText('Your Hand: ' + pv); dealerValueTxt.setText('Dealer: ' + dv + (dealerHand.cards.length > 1 ? ' + ?' : '')); } // Update score and streak displays function updateScoreDisplays() { scoreTxt.setText('Score: ' + playerScore); streakTxt.setText('Streak: ' + winStreak); } // Update button visibility function updateButtons() { if (gamePhase === 'playerTurn') { hitBtn.visible = true; standBtn.visible = true; newGameBtn.visible = false; } else if (gamePhase === 'roundOver') { hitBtn.visible = false; standBtn.visible = false; newGameBtn.visible = true; } else { hitBtn.visible = false; standBtn.visible = false; newGameBtn.visible = false; } } // End round: outcome = 'win', 'lose', 'push', 'bust', 'dealerBust' function endRound(outcome) { gamePhase = 'roundOver'; dealerHand.revealAll(); updateHandValues(); if (outcome === 'win') { messageTxt.setText('You Win!'); playerScore += 1; winStreak += 1; LK.effects.flashObject(playerHand, 0x33ff33, 800); } else if (outcome === 'lose') { messageTxt.setText('You Lose!'); playerScore = Math.max(0, playerScore - 1); winStreak = 0; LK.effects.flashObject(playerHand, 0xff3333, 800); } else if (outcome === 'push') { messageTxt.setText('Push!'); LK.effects.flashObject(playerHand, 0xcccccc, 800); } else if (outcome === 'bust') { messageTxt.setText('Bust!'); playerScore = Math.max(0, playerScore - 1); winStreak = 0; LK.effects.flashObject(playerHand, 0xff3333, 800); } else if (outcome === 'dealerBust') { messageTxt.setText('Dealer Bust! You Win!'); playerScore += 1; winStreak += 1; LK.effects.flashObject(playerHand, 0x33ff33, 800); } updateScoreDisplays(); updateButtons(); } // Dealer's turn logic function dealerTurn() { // Reveal dealer's hole card dealerHand.cards[1].setFaceUp(true); updateHandValues(); // Dealer draws until 17 or more var _dealerDraw = function dealerDraw() { var dv = dealerHand.getValue(); if (dv < 17) { // Draw card after short delay LK.setTimeout(function () { dealCardToHand(dealerHand, true); updateHandValues(); _dealerDraw(); }, 600); } else { // Compare hands var pv = playerHand.getValue(); var dv2 = dealerHand.getValue(); if (dv2 > 21) { endRound('dealerBust'); } else if (dv2 > pv) { endRound('lose'); } else if (dv2 < pv) { endRound('win'); } else { endRound('push'); } } }; _dealerDraw(); } // --- Game update loop (not used for this turn-based game) --- game.update = function () { // No per-frame logic needed };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Card class: represents a single card (suit, rank, value, face up/down)
var Card = Container.expand(function () {
var self = Container.call(this);
// Card properties
self.suit = null; // '♠', '♥', '♦', '♣'
self.rank = null; // 'A', '2', ..., 'K'
self.value = 0; // 1-11
self.faceUp = true;
// Card background (white for face up, gray for face down)
var cardBg = self.attachAsset('cardBg', {
width: 220,
height: 320,
color: 0xffffff,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
// Card text (rank and suit)
var cardText = new Text2('', {
size: 90,
fill: 0x222222
});
cardText.anchor.set(0.5, 0.5);
cardText.x = 0;
cardText.y = 0;
self.addChild(cardText);
// Face down overlay
var faceDownBg = self.attachAsset('cardBack', {
width: 220,
height: 320,
color: 0x888888,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
faceDownBg.visible = false;
self.addChild(faceDownBg);
// Set card data
self.setCard = function (suit, rank, value, faceUp) {
self.suit = suit;
self.rank = rank;
self.value = value;
self.faceUp = faceUp !== false;
updateVisual();
};
// Flip card (show/hide face)
self.setFaceUp = function (faceUp) {
self.faceUp = faceUp;
updateVisual();
};
// Update card visuals
function updateVisual() {
if (self.faceUp) {
cardBg.visible = true;
cardText.visible = true;
faceDownBg.visible = false;
cardText.setText(self.rank + self.suit);
// Red color for hearts/diamonds, black for spades/clubs
if (self.suit === '♥' || self.suit === '♦') {
cardText.setStyle({
fill: "#d22"
});
} else {
cardText.setStyle({
fill: "#222"
});
}
} else {
cardBg.visible = false;
cardText.visible = false;
faceDownBg.visible = true;
}
}
return self;
});
// Hand class: represents a hand of cards (player or dealer)
var Hand = Container.expand(function () {
var self = Container.call(this);
self.cards = [];
// Add a card to the hand
self.addCard = function (card) {
self.cards.push(card);
self.addChild(card);
layoutCards();
};
// Remove all cards
self.clear = function () {
for (var i = 0; i < self.cards.length; i++) {
self.cards[i].destroy();
}
self.cards = [];
};
// Layout cards horizontally with overlap
function layoutCards() {
var n = self.cards.length;
var spacing = 120;
var startX = -((n - 1) * spacing) / 2;
for (var i = 0; i < n; i++) {
var card = self.cards[i];
card.x = startX + i * spacing;
card.y = 0;
}
}
// Calculate hand value (Blackjack rules: Aces can be 1 or 11)
self.getValue = function () {
var total = 0;
var aces = 0;
for (var i = 0; i < self.cards.length; i++) {
var v = self.cards[i].value;
total += v;
if (self.cards[i].rank === 'A') aces++;
}
// Adjust for aces
while (total > 21 && aces > 0) {
total -= 10;
aces--;
}
return total;
};
// Reveal all cards
self.revealAll = function () {
for (var i = 0; i < self.cards.length; i++) {
self.cards[i].setFaceUp(true);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x006622
});
/****
* Game Code
****/
// Card deck data
var suits = ['♠', '♥', '♦', '♣'];
var ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
var values = {
'A': 11,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'10': 10,
'J': 10,
'Q': 10,
'K': 10
};
// Game state
var deck = [];
var playerHand = null;
var dealerHand = null;
var playerScore = 0;
var winStreak = 0;
var gamePhase = 'playerTurn'; // 'playerTurn', 'dealerTurn', 'roundOver'
var messageTxt = null;
var playerValueTxt = null;
var dealerValueTxt = null;
var hitBtn = null;
var standBtn = null;
var newGameBtn = null;
// GUI: Score and streak
var scoreTxt = new Text2('Score: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var streakTxt = new Text2('Streak: 0', {
size: 70,
fill: "#fff"
});
streakTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(streakTxt);
streakTxt.y = 110;
// Message text (centered)
messageTxt = new Text2('', {
size: 120,
fill: "#fff"
});
messageTxt.anchor.set(0.5, 0.5);
messageTxt.y = -350; // Move handwriting/message higher
LK.gui.center.addChild(messageTxt);
// Add background image behind all elements
var tableBg = LK.getAsset('tableBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
width: 2048,
height: 2732
});
game.addChild(tableBg);
// Player hand
playerHand = new Hand();
game.addChild(playerHand);
playerHand.x = 2048 / 2;
playerHand.y = 2732 - 500;
// Dealer hand
dealerHand = new Hand();
game.addChild(dealerHand);
dealerHand.x = 2048 / 2;
dealerHand.y = 500;
// Player value text
playerValueTxt = new Text2('', {
size: 70,
fill: "#fff"
});
playerValueTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(playerValueTxt);
playerValueTxt.y = -200;
// Dealer value text
dealerValueTxt = new Text2('', {
size: 70,
fill: "#fff"
});
dealerValueTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(dealerValueTxt);
dealerValueTxt.y = 220;
// Buttons
hitBtn = createButton('Hit', 0x229922, function () {
if (gamePhase !== 'playerTurn') return;
dealCardToHand(playerHand, true);
updateHandValues();
if (playerHand.getValue() > 21) {
endRound('bust');
}
});
standBtn = createButton('Stand', 0x2266cc, function () {
if (gamePhase !== 'playerTurn') return;
gamePhase = 'dealerTurn';
updateButtons();
dealerTurn();
});
newGameBtn = createButton('New Game', 0x993333, function () {
startNewRound();
});
newGameBtn.visible = false;
// Button layout
hitBtn.x = 2048 / 2 - 220;
hitBtn.y = 2732 - 120; // Move lower
standBtn.x = 2048 / 2 + 220;
standBtn.y = 2732 - 120; // Move lower
newGameBtn.x = 2048 / 2;
newGameBtn.y = 2732 / 2 + 600; // Move lower
game.addChild(hitBtn);
game.addChild(standBtn);
game.addChild(newGameBtn);
// Prevent elements in top left 100x100
scoreTxt.x = 2048 / 2;
scoreTxt.y = 20;
streakTxt.x = 2048 / 2;
streakTxt.y = 110;
dealerValueTxt.x = 2048 / 2;
dealerValueTxt.y = 220;
// Start first round
startNewRound();
// --- Functions ---
// Create a button (Container with shape and text)
function createButton(label, color, onTap) {
var btn = new Container();
var bg = btn.attachAsset('btnBg', {
width: 320,
height: 120,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
var txt = new Text2(label, {
size: 70,
fill: "#fff"
});
txt.anchor.set(0.5, 0.5);
txt.x = 0;
txt.y = 0;
btn.addChild(txt);
// Touch event
btn.down = function (x, y, obj) {
onTap();
};
// Visual feedback
btn.interactive = true;
btn.buttonMode = true;
btn.alpha = 1;
btn.up = function () {
btn.alpha = 1;
};
btn.move = function () {};
return btn;
}
// Shuffle deck
function shuffleDeck() {
var d = [];
for (var s = 0; s < suits.length; s++) {
for (var r = 0; r < ranks.length; r++) {
d.push({
suit: suits[s],
rank: ranks[r],
value: values[ranks[r]]
});
}
}
// Fisher-Yates shuffle
for (var i = d.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = d[i];
d[i] = d[j];
d[j] = tmp;
}
return d;
}
// Deal a card to a hand
function dealCardToHand(hand, faceUp) {
if (deck.length === 0) deck = shuffleDeck();
var cardData = deck.pop();
var card = new Card();
card.setCard(cardData.suit, cardData.rank, cardData.value, faceUp);
hand.addCard(card);
return card;
}
// Start a new round
function startNewRound() {
gamePhase = 'playerTurn';
messageTxt.setText('');
playerHand.clear();
dealerHand.clear();
deck = shuffleDeck();
// Deal initial cards
dealCardToHand(playerHand, true);
dealCardToHand(dealerHand, true);
dealCardToHand(playerHand, true);
var dealerHole = dealCardToHand(dealerHand, false); // Dealer's hole card
updateHandValues();
updateButtons();
newGameBtn.visible = false;
}
// Update hand value displays
function updateHandValues() {
var pv = playerHand.getValue();
var dv = dealerHand.cards[0].faceUp ? dealerHand.cards[0].value : '?';
playerValueTxt.setText('Your Hand: ' + pv);
dealerValueTxt.setText('Dealer: ' + dv + (dealerHand.cards.length > 1 ? ' + ?' : ''));
}
// Update score and streak displays
function updateScoreDisplays() {
scoreTxt.setText('Score: ' + playerScore);
streakTxt.setText('Streak: ' + winStreak);
}
// Update button visibility
function updateButtons() {
if (gamePhase === 'playerTurn') {
hitBtn.visible = true;
standBtn.visible = true;
newGameBtn.visible = false;
} else if (gamePhase === 'roundOver') {
hitBtn.visible = false;
standBtn.visible = false;
newGameBtn.visible = true;
} else {
hitBtn.visible = false;
standBtn.visible = false;
newGameBtn.visible = false;
}
}
// End round: outcome = 'win', 'lose', 'push', 'bust', 'dealerBust'
function endRound(outcome) {
gamePhase = 'roundOver';
dealerHand.revealAll();
updateHandValues();
if (outcome === 'win') {
messageTxt.setText('You Win!');
playerScore += 1;
winStreak += 1;
LK.effects.flashObject(playerHand, 0x33ff33, 800);
} else if (outcome === 'lose') {
messageTxt.setText('You Lose!');
playerScore = Math.max(0, playerScore - 1);
winStreak = 0;
LK.effects.flashObject(playerHand, 0xff3333, 800);
} else if (outcome === 'push') {
messageTxt.setText('Push!');
LK.effects.flashObject(playerHand, 0xcccccc, 800);
} else if (outcome === 'bust') {
messageTxt.setText('Bust!');
playerScore = Math.max(0, playerScore - 1);
winStreak = 0;
LK.effects.flashObject(playerHand, 0xff3333, 800);
} else if (outcome === 'dealerBust') {
messageTxt.setText('Dealer Bust! You Win!');
playerScore += 1;
winStreak += 1;
LK.effects.flashObject(playerHand, 0x33ff33, 800);
}
updateScoreDisplays();
updateButtons();
}
// Dealer's turn logic
function dealerTurn() {
// Reveal dealer's hole card
dealerHand.cards[1].setFaceUp(true);
updateHandValues();
// Dealer draws until 17 or more
var _dealerDraw = function dealerDraw() {
var dv = dealerHand.getValue();
if (dv < 17) {
// Draw card after short delay
LK.setTimeout(function () {
dealCardToHand(dealerHand, true);
updateHandValues();
_dealerDraw();
}, 600);
} else {
// Compare hands
var pv = playerHand.getValue();
var dv2 = dealerHand.getValue();
if (dv2 > 21) {
endRound('dealerBust');
} else if (dv2 > pv) {
endRound('lose');
} else if (dv2 < pv) {
endRound('win');
} else {
endRound('push');
}
}
};
_dealerDraw();
}
// --- Game update loop (not used for this turn-based game) ---
game.update = function () {
// No per-frame logic needed
};