/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var DrawingPoint = Container.expand(function () {
var self = Container.call(this);
var dot = self.attachAsset('drawingPath', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var ReferenceCircle = Container.expand(function () {
var self = Container.call(this);
var circle = self.attachAsset('referenceCircle', {
anchorX: 0.5,
anchorY: 0.5
});
circle.alpha = 0;
self.show = function () {
circle.alpha = 0;
tween(circle, {
alpha: 0.3
}, {
duration: 500
});
};
self.hide = function () {
tween(circle, {
alpha: 0
}, {
duration: 300
});
};
self.setRadius = function (radius) {
circle.width = radius * 2;
circle.height = radius * 2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xF8F9FA
});
/****
* Game Code
****/
// Game state variables
var isDrawing = false;
var drawingPoints = [];
var drawnPath = [];
var centerX = 1024;
var centerY = 1366;
var currentScore = 0;
var bestScore = storage.bestScore || 0;
// UI Elements
var instructionText = new Text2('Tap and drag to draw a perfect circle', {
size: 60,
fill: 0x666666
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = centerX;
instructionText.y = 400;
game.addChild(instructionText);
var scoreText = new Text2('', {
size: 120,
fill: 0x333333
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = centerX;
scoreText.y = 600;
game.addChild(scoreText);
var bestScoreText = new Text2('Best: ' + bestScore, {
size: 50,
fill: 0x888888
});
bestScoreText.anchor.set(0.5, 0.5);
bestScoreText.x = centerX;
bestScoreText.y = 700;
game.addChild(bestScoreText);
var tryAgainText = new Text2('Try Again', {
size: 60,
fill: 0x4A90E2
});
tryAgainText.anchor.set(0.5, 0.5);
tryAgainText.x = centerX;
tryAgainText.y = 2400;
tryAgainText.alpha = 0;
game.addChild(tryAgainText);
// Center reference dot
var centerDot = game.addChild(LK.getAsset('centerDot', {
anchorX: 0.5,
anchorY: 0.5
}));
centerDot.x = centerX;
centerDot.y = centerY;
centerDot.alpha = 0.3;
// Reference circle for comparison
var referenceCircle = game.addChild(new ReferenceCircle());
referenceCircle.x = centerX;
referenceCircle.y = centerY;
function clearDrawing() {
// Remove all drawing points
for (var i = drawingPoints.length - 1; i >= 0; i--) {
drawingPoints[i].destroy();
drawingPoints.splice(i, 1);
}
drawnPath = [];
referenceCircle.hide();
scoreText.setText('');
// Hide try again button
tween(tryAgainText, {
y: 2400,
alpha: 0
}, {
duration: 300
});
// Show instruction text
tween(instructionText, {
alpha: 1
}, {
duration: 300
});
}
function calculateCircleScore(points) {
if (points.length < 10) return 0;
// Calculate center point
var avgX = 0,
avgY = 0;
for (var i = 0; i < points.length; i++) {
avgX += points[i].x;
avgY += points[i].y;
}
avgX /= points.length;
avgY /= points.length;
// Calculate average radius
var totalRadius = 0;
for (var i = 0; i < points.length; i++) {
var dx = points[i].x - avgX;
var dy = points[i].y - avgY;
totalRadius += Math.sqrt(dx * dx + dy * dy);
}
var avgRadius = totalRadius / points.length;
// Calculate deviation from perfect circle
var totalDeviation = 0;
for (var i = 0; i < points.length; i++) {
var dx = points[i].x - avgX;
var dy = points[i].y - avgY;
var distance = Math.sqrt(dx * dx + dy * dy);
totalDeviation += Math.abs(distance - avgRadius);
}
var avgDeviation = totalDeviation / points.length;
var maxAllowedDeviation = avgRadius * 0.3; // Allow 30% deviation for 0 score
var score = Math.max(0, Math.min(100, 100 - avgDeviation / maxAllowedDeviation * 100));
// Set reference circle to match the ideal circle
referenceCircle.x = avgX;
referenceCircle.y = avgY;
referenceCircle.setRadius(avgRadius);
return Math.round(score);
}
function showScore(score) {
currentScore = score;
// Update best score
if (score > bestScore) {
bestScore = score;
storage.bestScore = bestScore;
bestScoreText.setText('Best: ' + bestScore);
if (score >= 90) {
LK.getSound('perfectScore').play();
LK.effects.flashScreen(0x00FF00, 500);
}
}
// Color code the score
var color = "#FF4444"; // Red for low scores
if (score >= 70) color = "#FFAA00"; // Orange for medium scores
if (score >= 85) color = "#44AA44"; // Green for high scores
if (score >= 95) color = "#00AA00"; // Bright green for excellent scores
scoreText.fill = color;
scoreText.setText('Score: ' + score);
// Show reference circle
referenceCircle.show();
// Show try again button
tween(tryAgainText, {
y: 2200,
alpha: 1
}, {
duration: 500
});
// Hide instruction text
tween(instructionText, {
alpha: 0
}, {
duration: 300
});
// Play completion sound
LK.getSound('drawComplete').play();
}
// Event handlers
game.down = function (x, y, obj) {
if (tryAgainText.alpha > 0.5) {
// Check if clicking try again button
var dx = x - tryAgainText.x;
var dy = y - tryAgainText.y;
if (Math.abs(dx) < 100 && Math.abs(dy) < 50) {
clearDrawing();
return;
}
}
if (!isDrawing && drawnPath.length === 0) {
isDrawing = true;
drawnPath.push({
x: x,
y: y
});
// Create first drawing point
var point = game.addChild(new DrawingPoint());
point.x = x;
point.y = y;
drawingPoints.push(point);
}
};
game.move = function (x, y, obj) {
if (isDrawing) {
// Add point to path
drawnPath.push({
x: x,
y: y
});
// Create visual point every few pixels to avoid too many objects
if (drawnPath.length % 3 === 0) {
var point = game.addChild(new DrawingPoint());
point.x = x;
point.y = y;
drawingPoints.push(point);
}
}
};
game.up = function (x, y, obj) {
if (isDrawing) {
isDrawing = false;
// Calculate and show score
var score = calculateCircleScore(drawnPath);
showScore(score);
}
};
// Initialize display
clearDrawing(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var DrawingPoint = Container.expand(function () {
var self = Container.call(this);
var dot = self.attachAsset('drawingPath', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var ReferenceCircle = Container.expand(function () {
var self = Container.call(this);
var circle = self.attachAsset('referenceCircle', {
anchorX: 0.5,
anchorY: 0.5
});
circle.alpha = 0;
self.show = function () {
circle.alpha = 0;
tween(circle, {
alpha: 0.3
}, {
duration: 500
});
};
self.hide = function () {
tween(circle, {
alpha: 0
}, {
duration: 300
});
};
self.setRadius = function (radius) {
circle.width = radius * 2;
circle.height = radius * 2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xF8F9FA
});
/****
* Game Code
****/
// Game state variables
var isDrawing = false;
var drawingPoints = [];
var drawnPath = [];
var centerX = 1024;
var centerY = 1366;
var currentScore = 0;
var bestScore = storage.bestScore || 0;
// UI Elements
var instructionText = new Text2('Tap and drag to draw a perfect circle', {
size: 60,
fill: 0x666666
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = centerX;
instructionText.y = 400;
game.addChild(instructionText);
var scoreText = new Text2('', {
size: 120,
fill: 0x333333
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = centerX;
scoreText.y = 600;
game.addChild(scoreText);
var bestScoreText = new Text2('Best: ' + bestScore, {
size: 50,
fill: 0x888888
});
bestScoreText.anchor.set(0.5, 0.5);
bestScoreText.x = centerX;
bestScoreText.y = 700;
game.addChild(bestScoreText);
var tryAgainText = new Text2('Try Again', {
size: 60,
fill: 0x4A90E2
});
tryAgainText.anchor.set(0.5, 0.5);
tryAgainText.x = centerX;
tryAgainText.y = 2400;
tryAgainText.alpha = 0;
game.addChild(tryAgainText);
// Center reference dot
var centerDot = game.addChild(LK.getAsset('centerDot', {
anchorX: 0.5,
anchorY: 0.5
}));
centerDot.x = centerX;
centerDot.y = centerY;
centerDot.alpha = 0.3;
// Reference circle for comparison
var referenceCircle = game.addChild(new ReferenceCircle());
referenceCircle.x = centerX;
referenceCircle.y = centerY;
function clearDrawing() {
// Remove all drawing points
for (var i = drawingPoints.length - 1; i >= 0; i--) {
drawingPoints[i].destroy();
drawingPoints.splice(i, 1);
}
drawnPath = [];
referenceCircle.hide();
scoreText.setText('');
// Hide try again button
tween(tryAgainText, {
y: 2400,
alpha: 0
}, {
duration: 300
});
// Show instruction text
tween(instructionText, {
alpha: 1
}, {
duration: 300
});
}
function calculateCircleScore(points) {
if (points.length < 10) return 0;
// Calculate center point
var avgX = 0,
avgY = 0;
for (var i = 0; i < points.length; i++) {
avgX += points[i].x;
avgY += points[i].y;
}
avgX /= points.length;
avgY /= points.length;
// Calculate average radius
var totalRadius = 0;
for (var i = 0; i < points.length; i++) {
var dx = points[i].x - avgX;
var dy = points[i].y - avgY;
totalRadius += Math.sqrt(dx * dx + dy * dy);
}
var avgRadius = totalRadius / points.length;
// Calculate deviation from perfect circle
var totalDeviation = 0;
for (var i = 0; i < points.length; i++) {
var dx = points[i].x - avgX;
var dy = points[i].y - avgY;
var distance = Math.sqrt(dx * dx + dy * dy);
totalDeviation += Math.abs(distance - avgRadius);
}
var avgDeviation = totalDeviation / points.length;
var maxAllowedDeviation = avgRadius * 0.3; // Allow 30% deviation for 0 score
var score = Math.max(0, Math.min(100, 100 - avgDeviation / maxAllowedDeviation * 100));
// Set reference circle to match the ideal circle
referenceCircle.x = avgX;
referenceCircle.y = avgY;
referenceCircle.setRadius(avgRadius);
return Math.round(score);
}
function showScore(score) {
currentScore = score;
// Update best score
if (score > bestScore) {
bestScore = score;
storage.bestScore = bestScore;
bestScoreText.setText('Best: ' + bestScore);
if (score >= 90) {
LK.getSound('perfectScore').play();
LK.effects.flashScreen(0x00FF00, 500);
}
}
// Color code the score
var color = "#FF4444"; // Red for low scores
if (score >= 70) color = "#FFAA00"; // Orange for medium scores
if (score >= 85) color = "#44AA44"; // Green for high scores
if (score >= 95) color = "#00AA00"; // Bright green for excellent scores
scoreText.fill = color;
scoreText.setText('Score: ' + score);
// Show reference circle
referenceCircle.show();
// Show try again button
tween(tryAgainText, {
y: 2200,
alpha: 1
}, {
duration: 500
});
// Hide instruction text
tween(instructionText, {
alpha: 0
}, {
duration: 300
});
// Play completion sound
LK.getSound('drawComplete').play();
}
// Event handlers
game.down = function (x, y, obj) {
if (tryAgainText.alpha > 0.5) {
// Check if clicking try again button
var dx = x - tryAgainText.x;
var dy = y - tryAgainText.y;
if (Math.abs(dx) < 100 && Math.abs(dy) < 50) {
clearDrawing();
return;
}
}
if (!isDrawing && drawnPath.length === 0) {
isDrawing = true;
drawnPath.push({
x: x,
y: y
});
// Create first drawing point
var point = game.addChild(new DrawingPoint());
point.x = x;
point.y = y;
drawingPoints.push(point);
}
};
game.move = function (x, y, obj) {
if (isDrawing) {
// Add point to path
drawnPath.push({
x: x,
y: y
});
// Create visual point every few pixels to avoid too many objects
if (drawnPath.length % 3 === 0) {
var point = game.addChild(new DrawingPoint());
point.x = x;
point.y = y;
drawingPoints.push(point);
}
}
};
game.up = function (x, y, obj) {
if (isDrawing) {
isDrawing = false;
// Calculate and show score
var score = calculateCircleScore(drawnPath);
showScore(score);
}
};
// Initialize display
clearDrawing();