User prompt
Let's actually change the Play and leaderboard text to a button assets.
User prompt
ok now the white menu text is hard to read. make it bold and outlined with black.
User prompt
Please create 2 assets that are the size of the background. 1 will be the menu background and the board background.
User prompt
on the bonus score text lets start the fade out after .5 seconds. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
i don't see the new assets in the asset panel.
User prompt
ok we need to add more assets to accommodate the larger boards. let's increase the total number of cards to 15.
User prompt
ok let's change the grids from 5x5 and 6x6 to 4x5 and 4x6 so everything fits on the screen.
User prompt
ok now I would like to add an option for the player to select larger boards. right now we have 4x4, let's include a 5x5 and 6x6 option. so when the player presses the play option, they will be given the option to choose a board size. I also want to change the size for the cards to be square instead of a tall rectangle.
User prompt
ok i would like to add some animation to the streak. when a streak occurs, flash the bonus points in the middle of the screen for 1 second that will drift up and fade away. something like this ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ok i would like to add some animation to the streak. when a streak occurs, flash the bonus points in the middle of the screen for 1 second that will drift up and fade away. something like this BONUS!!! +10 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ok I would like a way for the player to make more points per match. if they get multiple matches in a row then the points for each match increase. first match - 10 points. second match - 20 points. third and greater match - 30 points. This will give a greater benefit to players who are able to make multiple matches in a row. I would also like to add a column to the leader board page that shows the number matches in a row that were made for that score. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
ok good. I would like to add more card images to give the board a greater pool to select from from for now i want to keep the board size the same. Please add 5 more card assets.
User prompt
ok i would like to improve the play board so that matches are not next to each other in any direction.
User prompt
ok, please implement that change.
User prompt
Please fix the bug: 'TypeError: storage.get is not a function' in or related to this line: 'var highScore = storage.get('highScore') || 0;' Line Number: 216 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
ok i would like to add a start screen where the player has the option to play the game or to see the leader board. The leader board screen will simple show the player's their rank, user id and score. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
ok i would like to add a start screen where the player has the option to play the game or to see the leader board. The leader board screen will simple show the player's their rank, user id and score. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
ok that is better. but i want the match animation match the timing of the match sound. please also add a delay to the match made animation so it happens at the same time as the sound. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
when a match is made the sound of the card flip and the sound of a match made are overlapping. please add a .75 second delay to play the match sound.
Code edit (1 edits merged)
Please save this source code
User prompt
Memory Match
Initial prompt
We are creating a Memory game. The type where there are cards with images on them that the player flips over to see the image 2 at a time. if the images match the player gets a point and those cards are removed from the board. this continues until all the cards have been matched.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Card = Container.expand(function (pairId) {
var self = Container.call(this);
self.pairId = pairId;
self.isFlipped = false;
self.isMatched = false;
self.back = self.attachAsset('card_back', {
anchorX: 0.5,
anchorY: 0.5
});
self.face = self.attachAsset('card_face_' + pairId, {
anchorX: 0.5,
anchorY: 0.5
});
self.face.visible = false;
// Public methods must be defined before they are called.
self.flipUp = function (onComplete) {
if (self.isFlipped) return;
self.isFlipped = true;
LK.getSound('flip').play();
tween(self, {
scaleX: 0
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
self.back.visible = false;
self.face.visible = true;
tween(self, {
scaleX: 1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: onComplete
});
}
});
};
self.flipDown = function (onComplete) {
if (!self.isFlipped) return;
self.isFlipped = false;
tween(self, {
scaleX: 0
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
self.face.visible = false;
self.back.visible = true;
tween(self, {
scaleX: 1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: onComplete
});
}
});
};
self.matchFound = function (onComplete) {
self.isMatched = true;
tween(self, {
alpha: 0,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
easing: tween.easeIn,
onFinish: onComplete
});
};
self.down = function () {
handleCardFlip(self);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50 // Dark slate blue background
});
/****
* Game Code
****/
//LK Engine will automatically create assets based on usage.
//Defining 8 pairs of cards + 1 card back.
// Red
// Blue
// Green
// Yellow
// Magenta
// Cyan
// Orange
// Purple
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
LK.setScore(0); // Initialize score
// --- Game Constants ---
var COLS = 4;
var ROWS = 4;
var TOTAL_PAIRS = COLS * ROWS / 2;
var TOTAL_CARD_FACES = 15;
var CARD_WIDTH = 350;
var CARD_HEIGHT = 350;
var MARGIN = 60;
var gridWidth = COLS * (CARD_WIDTH + MARGIN) - MARGIN;
var gridHeight = ROWS * (CARD_HEIGHT + MARGIN) - MARGIN;
var startX = (2048 - gridWidth) / 2 + CARD_WIDTH / 2;
var startY = (2732 - gridHeight) / 2 + CARD_HEIGHT / 2;
// --- Global UI and Game State Variables ---
var startScreenContainer, boardSizeContainer, leaderboardContainer, gameContainer, victoryScreenContainer;
var cards = [];
var flippedCards = [];
var matchedPairs = 0;
var canPlay = true;
var streakCount = 0;
var maxStreak = 0;
// --- Screen Management ---
function showStartScreen() {
if (leaderboardContainer) leaderboardContainer.visible = false;
if (gameContainer) gameContainer.visible = false;
if (victoryScreenContainer) victoryScreenContainer.visible = false;
if (!startScreenContainer) {
startScreenContainer = game.addChild(new Container());
var title = new Text2('Memory Match', {
size: 150,
fill: 0xFFFFFF
});
title.anchor.set(0.5, 0.5);
title.x = 2048 / 2;
title.y = 2732 / 2 - 400;
startScreenContainer.addChild(title);
var playButton = new Text2('Play', {
size: 100,
fill: 0xFFFFFF
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 2048 / 2;
playButton.y = 2732 / 2;
playButton.down = function () {
showBoardSizeSelection();
};
startScreenContainer.addChild(playButton);
var leaderboardButton = new Text2('Leaderboard', {
size: 100,
fill: 0xFFFFFF
});
leaderboardButton.anchor.set(0.5, 0.5);
leaderboardButton.x = 2048 / 2;
leaderboardButton.y = 2732 / 2 + 200;
leaderboardButton.down = function () {
showLeaderboard();
};
startScreenContainer.addChild(leaderboardButton);
}
startScreenContainer.visible = true;
scoreTxt.visible = false;
}
function showBoardSizeSelection() {
if (startScreenContainer) startScreenContainer.visible = false;
if (leaderboardContainer) leaderboardContainer.visible = false;
if (gameContainer) gameContainer.visible = false;
if (victoryScreenContainer) victoryScreenContainer.visible = false;
if (!boardSizeContainer) {
boardSizeContainer = game.addChild(new Container());
var title = new Text2('Select Board Size', {
size: 120,
fill: 0xFFFFFF
});
title.anchor.set(0.5, 0.5);
title.x = 2048 / 2;
title.y = 2732 / 2 - 400;
boardSizeContainer.addChild(title);
var size4x4Button = new Text2('4x4', {
size: 100,
fill: 0xFFFFFF
});
size4x4Button.anchor.set(0.5, 0.5);
size4x4Button.x = 2048 / 2;
size4x4Button.y = 2732 / 2 - 100;
size4x4Button.down = function () {
setBoardSize(4, 4);
startGame();
};
boardSizeContainer.addChild(size4x4Button);
var size4x5Button = new Text2('4x5', {
size: 100,
fill: 0xFFFFFF
});
size4x5Button.anchor.set(0.5, 0.5);
size4x5Button.x = 2048 / 2;
size4x5Button.y = 2732 / 2 + 50;
size4x5Button.down = function () {
setBoardSize(4, 5);
startGame();
};
boardSizeContainer.addChild(size4x5Button);
var size4x6Button = new Text2('4x6', {
size: 100,
fill: 0xFFFFFF
});
size4x6Button.anchor.set(0.5, 0.5);
size4x6Button.x = 2048 / 2;
size4x6Button.y = 2732 / 2 + 200;
size4x6Button.down = function () {
setBoardSize(4, 6);
startGame();
};
boardSizeContainer.addChild(size4x6Button);
var backButton = new Text2('Back', {
size: 80,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 2048 / 2;
backButton.y = 2732 / 2 + 400;
backButton.down = function () {
showStartScreen();
};
boardSizeContainer.addChild(backButton);
}
boardSizeContainer.visible = true;
scoreTxt.visible = false;
}
function setBoardSize(cols, rows) {
COLS = cols;
ROWS = rows;
TOTAL_PAIRS = COLS * ROWS / 2;
gridWidth = COLS * (CARD_WIDTH + MARGIN) - MARGIN;
gridHeight = ROWS * (CARD_HEIGHT + MARGIN) - MARGIN;
startX = (2048 - gridWidth) / 2 + CARD_WIDTH / 2;
startY = (2732 - gridHeight) / 2 + CARD_HEIGHT / 2;
}
function showLeaderboard() {
if (startScreenContainer) startScreenContainer.visible = false;
if (boardSizeContainer) boardSizeContainer.visible = false;
if (!leaderboardContainer) {
leaderboardContainer = game.addChild(new Container());
var title = new Text2('Leaderboard', {
size: 150,
fill: 0xFFFFFF
});
title.anchor.set(0.5, 0.5);
title.x = 2048 / 2;
title.y = 2732 / 2 - 600;
leaderboardContainer.addChild(title);
var header = new Text2('RANK USER SCORE STREAK', {
size: 70,
fill: 0xCCCCCC
});
header.anchor.set(0.5, 0.5);
header.x = 2048 / 2;
header.y = 2732 / 2 - 300;
leaderboardContainer.addChild(header);
var highScore = storage.highScore || 0;
var highScoreStreak = storage.highScoreStreak || 0;
var userLine = new Text2('1 You ' + highScore + ' ' + highScoreStreak, {
size: 90,
fill: 0xFFFFFF
});
userLine.anchor.set(0.5, 0.5);
userLine.x = 2048 / 2;
userLine.y = 2732 / 2 - 150;
leaderboardContainer.addChild(userLine);
leaderboardContainer.userLine = userLine; // Store reference
var backButton = new Text2('Back', {
size: 100,
fill: 0xFFFFFF
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 2048 / 2;
backButton.y = 2732 / 2 + 300;
backButton.down = function () {
showStartScreen();
};
leaderboardContainer.addChild(backButton);
} else {
var highScore = storage.highScore || 0;
var highScoreStreak = storage.highScoreStreak || 0;
leaderboardContainer.userLine.setText('1 You ' + highScore + ' ' + highScoreStreak);
}
leaderboardContainer.visible = true;
scoreTxt.visible = false;
}
function showVictoryScreen() {
if (gameContainer) gameContainer.visible = false;
if (boardSizeContainer) boardSizeContainer.visible = false;
scoreTxt.visible = false;
if (!victoryScreenContainer) {
victoryScreenContainer = game.addChild(new Container());
var title = new Text2('You Win!', {
size: 150,
fill: 0xFFD700
});
title.anchor.set(0.5, 0.5);
title.x = 2048 / 2;
title.y = 2732 / 2 - 400;
victoryScreenContainer.addChild(title);
var finalScoreText = new Text2('Final Score: ' + LK.getScore(), {
size: 100,
fill: 0xFFFFFF
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 2048 / 2;
finalScoreText.y = 2732 / 2 - 200;
victoryScreenContainer.addChild(finalScoreText);
victoryScreenContainer.finalScoreText = finalScoreText; // To update it later
var playAgainButton = new Text2('Play Again', {
size: 100,
fill: 0xFFFFFF
});
playAgainButton.anchor.set(0.5, 0.5);
playAgainButton.x = 2048 / 2;
playAgainButton.y = 2732 / 2;
playAgainButton.down = function () {
startGame();
};
victoryScreenContainer.addChild(playAgainButton);
var mainMenuButton = new Text2('Main Menu', {
size: 100,
fill: 0xFFFFFF
});
mainMenuButton.anchor.set(0.5, 0.5);
mainMenuButton.x = 2048 / 2;
mainMenuButton.y = 2732 / 2 + 200;
mainMenuButton.down = function () {
showStartScreen();
};
victoryScreenContainer.addChild(mainMenuButton);
} else {
victoryScreenContainer.finalScoreText.setText('Final Score: ' + LK.getScore());
}
victoryScreenContainer.visible = true;
}
function startGame() {
if (startScreenContainer) startScreenContainer.visible = false;
if (boardSizeContainer) boardSizeContainer.visible = false;
if (leaderboardContainer) leaderboardContainer.visible = false;
if (victoryScreenContainer) victoryScreenContainer.visible = false;
if (!gameContainer) {
gameContainer = game.addChild(new Container());
}
gameContainer.visible = true;
// Clear old cards
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
// Reset game state
cards = [];
flippedCards = [];
matchedPairs = 0;
canPlay = true;
streakCount = 0;
maxStreak = 0;
LK.setScore(0);
scoreTxt.setText('Score: ' + LK.getScore());
scoreTxt.visible = true;
setupBoard();
}
// --- Game Logic Functions ---
function showStreakAnimation(bonusPoints) {
var bonusContainer = new Container();
bonusContainer.x = 2048 / 2;
bonusContainer.y = 2732 / 2;
var bonusText = new Text2('BONUS!!!', {
size: 120,
fill: 0xFFD700
});
bonusText.anchor.set(0.5, 0.5);
bonusText.y = -70;
bonusContainer.addChild(bonusText);
var pointsText = new Text2('+' + bonusPoints, {
size: 100,
fill: 0xFFFFFF
});
pointsText.anchor.set(0.5, 0.5);
pointsText.y = 70;
bonusContainer.addChild(pointsText);
gameContainer.addChild(bonusContainer);
tween(bonusContainer, {
y: bonusContainer.y - 200,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Self-destruct after animation. Check parent to avoid errors
// if the game state was cleared during the animation.
if (bonusContainer.parent) {
bonusContainer.destroy();
}
}
});
}
function handleCardFlip(card) {
if (!canPlay || card.isFlipped || card.isMatched || flippedCards.length >= 2) {
return;
}
flippedCards.push(card);
card.flipUp(function () {
if (flippedCards.length === 2) {
canPlay = false;
checkMatch();
}
});
}
function checkMatch() {
var card1 = flippedCards[0];
var card2 = flippedCards[1];
if (card1.pairId === card2.pairId) {
matchedPairs++;
streakCount++;
if (streakCount > maxStreak) {
maxStreak = streakCount;
}
// Calculate points based on streak
var points = 10;
var bonusPoints = 0;
if (streakCount === 2) {
points = 20;
bonusPoints = 10;
} else if (streakCount >= 3) {
points = 30;
bonusPoints = 20;
}
if (bonusPoints > 0) {
showStreakAnimation(bonusPoints);
}
LK.setScore(LK.getScore() + points);
scoreTxt.setText('Score: ' + LK.getScore());
LK.setTimeout(function () {
LK.getSound('match').play();
card1.matchFound();
card2.matchFound(function () {
resetTurn();
if (matchedPairs === TOTAL_PAIRS) {
var highScore = storage.highScore || 0;
var highScoreStreak = storage.highScoreStreak || 0;
if (LK.getScore() > highScore) {
storage.highScore = LK.getScore();
storage.highScoreStreak = maxStreak;
}
LK.getSound('win').play();
showVictoryScreen();
}
});
}, 750);
} else {
// Reset streak on miss
streakCount = 0;
LK.setTimeout(function () {
card1.flipDown();
card2.flipDown(function () {
resetTurn();
});
}, 1000);
}
}
function resetTurn() {
flippedCards = [];
canPlay = true;
}
function shuffle(array) {
var currentIndex = array.length,
randomIndex;
while (currentIndex !== 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
var _ref = [array[randomIndex], array[currentIndex]];
array[currentIndex] = _ref[0];
array[randomIndex] = _ref[1];
}
return array;
}
function isLayoutValid(grid) {
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var currentPairId = grid[row][col];
// Check all 8 neighbors
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
if (dr === 0 && dc === 0) continue; // Don't check self
var neighborRow = row + dr;
var neighborCol = col + dc;
if (neighborRow >= 0 && neighborRow < ROWS && neighborCol >= 0 && neighborCol < COLS) {
if (grid[neighborRow][neighborCol] === currentPairId) {
return false; // Found an adjacent match
}
}
}
}
}
}
return true; // No adjacent matches found
}
// --- Board Setup ---
function setupBoard() {
var pairIds = [];
// Create a list of all available card face IDs (0 to TOTAL_CARD_FACES-1)
var allFaceIndices = [];
for (var i = 0; i < TOTAL_CARD_FACES; i++) {
allFaceIndices.push(i);
}
// Shuffle the available faces and select enough for the board
shuffle(allFaceIndices);
var gameFaceIndices = allFaceIndices.slice(0, TOTAL_PAIRS);
// Create pairs from the selected faces
for (var i = 0; i < TOTAL_PAIRS; i++) {
pairIds.push(gameFaceIndices[i], gameFaceIndices[i]);
}
var grid = [];
var attempts = 0;
var maxAttempts = 100; // Safeguard to prevent infinite loop
do {
shuffle(pairIds);
// Create a 2D grid from the shuffled IDs
for (var r = 0; r < ROWS; r++) {
grid[r] = [];
for (var c = 0; c < COLS; c++) {
grid[r][c] = pairIds[r * COLS + c];
}
}
attempts++;
} while (!isLayoutValid(grid) && attempts < maxAttempts);
// Create cards from the final grid layout
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var pairId = grid[row][col];
var card = new Card(pairId);
card.x = startX + col * (CARD_WIDTH + MARGIN);
card.y = startY + row * (CARD_HEIGHT + MARGIN);
cards.push(card);
gameContainer.addChild(card);
}
}
}
// --- Initial Call ---
showStartScreen(); ===================================================================
--- original.js
+++ change.js
@@ -113,9 +113,9 @@
// --- Game Constants ---
var COLS = 4;
var ROWS = 4;
var TOTAL_PAIRS = COLS * ROWS / 2;
-var TOTAL_CARD_FACES = 13;
+var TOTAL_CARD_FACES = 15;
var CARD_WIDTH = 350;
var CARD_HEIGHT = 350;
var MARGIN = 60;
var gridWidth = COLS * (CARD_WIDTH + MARGIN) - MARGIN;
a cartoon style 3d image of a snowman in a field with pine trees in the background. no people.. In-Game asset. 2d. High contrast. No shadows
a cartoon style 3d image of a cozy christmas living room with a cozy fire in a fireplace.. In-Game asset. 2d. High contrast. No shadows
a cartoon style 3d image of a memory match board game with some cards face up and some cards face down.. In-Game asset. 2d. High contrast. No shadows
a cartoon style 3d button that says Play.. In-Game asset. 2d. High contrast. No shadows
a cartoon style 3d button with the word Leader Board on it.. In-Game asset. 2d. High contrast. No shadows
A cartoon style 3d button with the words Match Maker all on 1 line. In-Game asset. 2d. High contrast. No shadows
a cartoon style 3d image of a green meadow with trees on the edges and a clearing in the middle.. In-Game asset. 2d. High contrast. No shadows
A tan dialog box background with a dark brown border.. In-Game asset. 2d. High contrast. No shadows. Cartoon style. 3D image
A table with the game Memory on it that got jostled so the card are flying in the air.. In-Game asset. 2d. High contrast. No shadows. Cartoon style. 3D image
Big TNT firecracker that is a dud with smoke wafting out of the fuse hole. In-Game asset. 2d. High contrast. No shadows. Cartoon style. 3D image
A small button that says Jumble. No shadow.