<!
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Make 24 - Math Puzzle Game</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
#gameContainer {
width: 100vw;
height: 100vh;
max-width: 400px;
max-height: 711px;
position: relative;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
#gameCanvas {
width: 100%;
height: 100%;
display: block;
touch-action: none;
}
.particle {
position: absolute;
pointer-events: none;
border-radius: 50%;
animation: particle-burst 1s ease-out forwards;
}
@keyframes particle-burst {
0% {
transform: scale(1) translate(0, 0);
opacity: 1;
}
100% {
transform: scale(0) translate(var(--dx), var(--dy));
opacity: 0;
}
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.shake {
animation: shake 0.5s ease-in-out;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.pulse {
animation: pulse 0.3s ease-in-out;
}
#startScreen, #gameOverScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
z-index: 1000;
}
.screen-title {
font-size: 3em;
font-weight: bold;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.screen-subtitle {
font-size: 1.2em;
margin-bottom: 30px;
text-align: center;
padding: 0 20px;
}
.btn {
background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
color: white;
border: none;
padding: 15px 30px;
font-size: 1.2em;
border-radius: 25px;
cursor: pointer;
margin: 10px;
min-width: 120px;
min-height: 50px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.btn:hover, .btn:active {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}
.hidden {
display: none !important;
}
</style>
</head>
<body>
<div id="gameContainer">
<div id="startScreen">
<div class="screen-title">Make 24</div>
<div class="screen-subtitle">Drag numbers and operators to create an
equation that equals 24!</div>
<button class="btn" onclick="startGame()">Start Game</button>
</div>
<div id="gameOverScreen" class="hidden">
<div class="screen-title">Level Complete!</div>
<div class="screen-subtitle" id="gameOverText"></div>
<button class="btn" onclick="nextLevel()">Next Level</button>
<button class="btn" onclick="restartGame()">Restart</button>
</div>
<canvas id="gameCanvas"></canvas>
</div>
<script>
class Make24Game {
constructor() {
[Link] = [Link]('gameCanvas');
[Link] = [Link]('2d');
[Link]();
[Link] = 'start'; // start, playing, gameover
[Link] = 1;
[Link] = 0;
[Link] = 0;
[Link] = 0;
[Link] = null;
[Link] = { x: 0, y: 0 };
[Link] = false;
[Link] = [];
[Link] = ['+', '-', '×', '÷'];
[Link] = [];
[Link] = [];
[Link] = null;
[Link]();
[Link]();
[Link]();
}
setupCanvas() {
const container = [Link]('gameContainer');
const rect = [Link]();
// Maintain 9:16 aspect ratio
[Link] = [Link] * [Link];
[Link] = [Link] * [Link];
[Link] = [Link] + 'px';
[Link] = [Link] + 'px';
[Link]([Link], [Link]);
[Link] = [Link];
[Link] = [Link];
}
setupLevel() {
// Generate random numbers that can make 24
const solutions = [
[1, 1, 8, 3], [1, 2, 3, 4], [1, 3, 4, 6], [1, 4, 5, 6],
[2, 2, 6, 2], [2, 3, 4, 4], [2, 4, 4, 3], [3, 3, 8, 1],
[4, 4, 2, 3], [6, 6, 1, 3], [8, 8, 1, 2], [9, 3, 2, 4]
];
const currentSolution = solutions[([Link] - 1) %
[Link]];
[Link] = [...currentSolution];
// Create draggable number objects
[Link] = [];
for (let i = 0; i < 4; i++) {
[Link]({
value: [Link][i],
x: 60 + (i * 80),
y: 150,
originalX: 60 + (i * 80),
originalY: 150,
width: 60,
height: 60,
isDragging: false,
type: 'number',
inSlot: false,
slotIndex: -1
});
}
// Create operator objects
[Link] = [];
for (let i = 0; i < 4; i++) {
[Link]({
value: [Link][i],
x: 60 + (i * 80),
y: 250,
originalX: 60 + (i * 80),
originalY: 250,
width: 60,
height: 60,
isDragging: false,
type: 'operator',
inSlot: false,
slotIndex: -1
});
}
// Create equation slots (make them smaller and more compact)
[Link] = [];
const slotSize = 40; // Reduced from 44
const startX = 30;
const spacing = 45; // Reduced spacing
for (let i = 0; i < 7; i++) {
[Link]({
x: startX + (i * spacing),
y: [Link] - 110,
width: slotSize,
height: slotSize,
occupied: false,
item: null,
type: i % 2 === 0 ? 'number' : 'operator'
});
}
[Link] = [];
[Link] = null;
[Link] = [Link]();
}
setupEventListeners() {
// Mouse events
[Link]('mousedown', (e) =>
[Link](e, false));
[Link]('mousemove', (e) => [Link](e,
false));
[Link]('mouseup', (e) => [Link](e,
false));
// Touch events
[Link]('touchstart', (e) => {
[Link]();
[Link]([Link][0], true);
});
[Link]('touchmove', (e) => {
[Link]();
[Link]([Link][0], true);
});
[Link]('touchend', (e) => {
[Link]();
[Link]([Link][0], true);
});
}
getEventPos(e) {
const rect = [Link]();
return {
x: [Link] - [Link],
y: [Link] - [Link]
};
}
handleStart(e, isTouch) {
if ([Link] !== 'playing') return;
const pos = [Link](e);
[Link] = isTouch;
// Check numbers
for (let item of [Link]) {
if ([Link](pos, item) && ![Link]) {
if ([Link]) {
[Link](item);
} else {
[Link](item, pos);
}
return;
}
}
// Check operators
for (let item of [Link]) {
if ([Link](pos, item) && ![Link]) {
if ([Link]) {
[Link](item);
} else {
[Link](item, pos);
}
return;
}
}
}
handleSelection(item) {
// Add visual selection feedback
[Link] = ![Link];
// Clear other selections of same type
const allItems = [...[Link], ...[Link]];
[Link](otherItem => {
if (otherItem !== item && [Link] === [Link]) {
[Link] = false;
}
});
// Update selected items array
[Link] = [Link](selectedItem =>
[Link] && selectedItem !== item);
if ([Link]) {
[Link](item);
}
// Check if we have number -> operator -> number pattern
[Link]();
// Haptic feedback
if ([Link]) {
[Link](10);
}
}
checkCalculationPreview() {
// Clear previous preview
[Link] = null;
// Need exactly 3 items: number, operator, number
if ([Link] !== 3) return;
const [first, second, third] = [Link];
// Check pattern: number -> operator -> number
if ([Link] === 'number' && [Link] === 'operator' &&
[Link] === 'number') {
try {
const result = [Link]([Link],
[Link], [Link]);
[Link] = {
value: result,
x: third.x + [Link] + 10,
y: third.y,
targetItem: third
};
} catch (e) {
// Invalid calculation
}
}
}
calculatePreview(num1, operator, num2) {
switch (operator) {
case '+': return num1 + num2;
case '-': return num1 - num2;
case '×': return num1 * num2;
case '÷':
if (num2 === 0) throw new Error('Division by zero');
return num1 / num2;
default: throw new Error('Invalid operator');
}
}
startDrag(item, pos) {
[Link] = item;
[Link] = true;
[Link].x = pos.x - item.x;
[Link].y = pos.y - item.y;
// Remove from slot if it was in one
if ([Link]) {
[Link][[Link]].occupied = false;
[Link][[Link]].item = null;
[Link] = false;
[Link] = -1;
}
// Haptic feedback
if ([Link]) {
[Link](10);
}
}
handleMove(e, isTouch) {
if (![Link] || [Link] !== 'playing' ||
[Link]) return;
const pos = [Link](e);
[Link].x = pos.x - [Link].x;
[Link].y = pos.y - [Link].y;
}
handleEnd(e, isTouch) {
if (![Link] || [Link] !== 'playing' ||
[Link]) return;
const pos = [Link](e);
const item = [Link];
// Check if dropped on equation slot
let droppedOnSlot = false;
for (let i = 0; i < [Link]; i++) {
const slot = [Link][i];
if ([Link](pos, slot) && ![Link] &&
(([Link] === 'number' && [Link] === 'number') ||
([Link] === 'operator' && [Link] === 'operator'))) {
// Snap to slot (adjust size to fit)
item.x = slot.x;
item.y = slot.y;
[Link] = [Link];
[Link] = [Link];
[Link] = true;
[Link] = i;
[Link] = true;
[Link] = item;
droppedOnSlot = true;
// Haptic feedback
if ([Link]) {
[Link](20);
}
break;
}
}
if (!droppedOnSlot) {
// Return to original position and size
item.x = [Link];
item.y = [Link];
[Link] = 60;
[Link] = 60;
}
[Link] = false;
[Link] = null;
// Check if equation is complete
[Link]();
}
isPointInRect(point, rect) {
return point.x >= rect.x && point.x <= rect.x + [Link] &&
point.y >= rect.y && point.y <= rect.y + [Link];
}
checkEquation() {
// Check if all slots are filled
let allFilled = true;
for (let slot of [Link]) {
if (![Link]) {
allFilled = false;
break;
}
}
if (!allFilled) return;
// Build equation string
let equation = '';
for (let slot of [Link]) {
if ([Link] === 'number') {
equation += [Link];
} else {
equation += [Link] === '×' ? '*' :
[Link] === '÷' ? '/' : [Link];
}
}
try {
const result = [Link](equation);
if ([Link](result - 24) < 0.0001) {
[Link]();
} else {
[Link]();
}
} catch (e) {
[Link]();
}
}
evaluateExpression(expr) {
// Simple expression evaluator with proper order of operations
const tokens = [Link](/\d+|\+|\-|\*|\//g);
const numbers = [];
const operators = [];
for (let token of tokens) {
if (!isNaN(token)) {
[Link](parseFloat(token));
} else {
[Link](token);
}
}
// Handle multiplication and division first
for (let i = 0; i < [Link]; i++) {
if (operators[i] === '*' || operators[i] === '/') {
const result = operators[i] === '*' ?
numbers[i] * numbers[i + 1] :
numbers[i] / numbers[i + 1];
[Link](i, 2, result);
[Link](i, 1);
i--;
}
}
// Handle addition and subtraction
let result = numbers[0];
for (let i = 0; i < [Link]; i++) {
if (operators[i] === '+') {
result += numbers[i + 1];
} else if (operators[i] === '-') {
result -= numbers[i + 1];
}
}
return result;
}
levelComplete() {
[Link] = ([Link]() - [Link]) / 1000;
[Link] += [Link](1000 - [Link]([Link] * 10), 100);
// Particle effects
[Link]([Link] / 2, [Link] / 2);
// Haptic feedback
if ([Link]) {
[Link]([100, 50, 100]);
}
setTimeout(() => {
[Link] = 'gameover';
[Link]('gameOverText').textContent =
`Time: ${[Link](1)}s | Score: ${[Link]}`;
[Link]('gameOverScreen').[Link]('hidden');
}, 1000);
}
showError() {
// Shake animation and error feedback
const canvas = [Link]('gameCanvas');
[Link]('shake');
setTimeout(() => [Link]('shake'), 500);
// Clear equation slots
for (let slot of [Link]) {
if ([Link] && [Link]) {
[Link].x = [Link];
[Link].y = [Link];
[Link] = 60; // Reset to original size
[Link] = 60;
[Link] = false;
[Link] = -1;
[Link] = false;
[Link] = null;
}
}
// Haptic feedback
if ([Link]) {
[Link]([50, 50, 50]);
}
}
createParticleExplosion(x, y) {
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4',
'#feca57'];
const container = [Link]('gameContainer');
for (let i = 0; i < 20; i++) {
const particle = [Link]('div');
[Link] = 'particle';
[Link] = x + 'px';
[Link] = y + 'px';
[Link] =
colors[[Link]([Link]() * [Link])];
[Link] = [Link]() * 8 + 4 + 'px';
[Link] = [Link];
const dx = ([Link]() - 0.5) * 200;
const dy = ([Link]() - 0.5) * 200;
[Link]('--dx', dx + 'px');
[Link]('--dy', dy + 'px');
[Link](particle);
setTimeout(() => {
[Link](particle);
}, 1000);
}
}
draw() {
// Clear canvas
[Link] = 'rgba(255, 255, 255, 0.1)';
[Link](0, 0, [Link], [Link]);
if ([Link] !== 'playing') return;
// Draw header
[Link]();
// Draw numbers
for (let number of [Link]) {
[Link](number, '#4ecdc4');
}
// Draw operators
for (let op of [Link]) {
[Link](op, '#ff6b6b');
}
// Draw equation slots
[Link]();
// Draw calculation preview
[Link]();
}
drawHeader() {
[Link] = 'white';
[Link] = 'bold 24px Arial';
[Link] = 'center';
[Link](`Level ${[Link]}`, [Link] / 2, 40);
[Link] = '18px Arial';
[Link](`Score: ${[Link]}`, [Link] / 4, 70);
const currentTime = ([Link]() - [Link]) / 1000;
[Link](`Time: ${[Link](1)}s`, ([Link] *
3) / 4, 70);
// Target display
[Link] = 'bold 32px Arial';
[Link] = '#feca57';
[Link]('Make 24!', [Link] / 2, 110);
}
drawItem(item, color) {
const x = item.x;
const y = item.y;
const size = [Link];
// Selection highlight
if ([Link]) {
[Link] = 'rgba(255, 255, 255, 0.3)';
[Link]();
if ([Link] === 'number') {
[Link](x + size/2, y + size/2, size/2 + 4, 0, [Link]
* 2);
} else {
[Link](x - 4, y - 4, size + 8, size + 8, 12);
}
[Link]();
}
// Shadow
[Link] = 'rgba(0, 0, 0, 0.2)';
[Link]();
if ([Link] === 'number') {
[Link](x + size/2 + 2, y + size/2 + 2, size/2, 0, [Link]
* 2);
} else {
[Link](x + 2, y + 2, size, size, 8);
}
[Link]();
// Main shape
[Link] = [Link] ?
[Link](color, 20) : color;
[Link]();
if ([Link] === 'number') {
[Link](x + size/2, y + size/2, size/2, 0, [Link] * 2);
} else {
[Link](x, y, size, size, 8);
}
[Link]();
// Border
[Link] = [Link] ?
'rgba(255, 255, 255, 0.8)' : 'rgba(255, 255, 255, 0.3)';
[Link] = [Link] ? 3 : 2;
[Link]();
// Text
[Link] = 'white';
[Link] = 'bold 24px Arial';
[Link] = 'center';
[Link]([Link], x + size/2, y + size/2 + 8);
}
drawEquationArea() {
// Background
[Link] = 'rgba(0, 0, 0, 0.2)';
[Link](10, [Link] - 130, [Link] - 20, 70, 10);
[Link]();
// Equation label
[Link] = 'white';
[Link] = 'bold 14px Arial';
[Link] = 'left';
[Link]('Build equation:', 20, [Link] - 135);
// Draw slots
for (let i = 0; i < [Link]; i++) {
const slot = [Link][i];
[Link] = [Link] ?
'rgba(255, 255, 255, 0.8)' : 'rgba(255, 255, 255, 0.3)';
[Link] = 2;
[Link]([Link] ? [] : [4, 4]);
if ([Link] === 'number') {
[Link]();
[Link](slot.x + [Link]/2, slot.y + [Link]/2,
[Link]/2, 0, [Link] * 2);
[Link]();
} else {
[Link](slot.x, slot.y, [Link], [Link],
6);
[Link]();
}
}
// Draw equals sign and 24 (positioned to fit better)
const lastSlot = [Link][[Link] - 1];
const equalsX = lastSlot.x + [Link] + 15;
[Link] = 'white';
[Link] = 'bold 18px Arial';
[Link] = 'center';
[Link]('=', equalsX, lastSlot.y + [Link]/2 +
6);
// Draw 24 in a circle
const twentyFourX = equalsX + 25;
[Link] = '#feca57';
[Link]();
[Link](twentyFourX, lastSlot.y + [Link]/2, 18, 0,
[Link] * 2);
[Link]();
[Link] = 'rgba(255, 255, 255, 0.8)';
[Link] = 2;
[Link]();
[Link] = 'white';
[Link] = 'bold 16px Arial';
[Link]('24', twentyFourX, lastSlot.y + [Link]/2
+ 5);
[Link]([]);
}
drawCalculationPreview() {
if (![Link]) return;
const preview = [Link];
const radius = 25;
// Preview circle background
[Link] = 'rgba(252, 202, 87, 0.9)';
[Link]();
[Link](preview.x + radius, preview.y + radius, radius, 0,
[Link] * 2);
[Link]();
// Border
[Link] = 'rgba(255, 255, 255, 0.8)';
[Link] = 2;
[Link]();
// Result text
[Link] = 'white';
[Link] = 'bold 16px Arial';
[Link] = 'center';
const displayValue = [Link]([Link]) ?
[Link]() : [Link](1);
[Link](displayValue, preview.x + radius, preview.y +
radius + 5);
// Connection line
[Link] = 'rgba(252, 202, 87, 0.6)';
[Link] = 2;
[Link]([5, 5]);
[Link]();
[Link]([Link].x + [Link],
[Link].y + [Link]/2);
[Link](preview.x, preview.y + radius);
[Link]();
[Link]([]);
}
lightenColor(color, percent) {
const num = parseInt([Link]("#",""), 16);
const amt = [Link](2.55 * percent);
const R = (num >> 16) + amt;
const G = (num >> 8 & 0x00FF) + amt;
const B = (num & 0x0000FF) + amt;
return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000
+
(G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
(B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
}
gameLoop() {
[Link]();
requestAnimationFrame(() => [Link]());
}
}
// Polyfill for roundRect
if (![Link]) {
[Link] = function(x, y, width,
height, radius) {
[Link]();
[Link](x + radius, y);
[Link](x + width - radius, y);
[Link](x + width, y, x + width, y + radius);
[Link](x + width, y + height - radius);
[Link](x + width, y + height, x + width - radius, y
+ height);
[Link](x + radius, y + height);
[Link](x, y + height, x, y + height - radius);
[Link](x, y + radius);
[Link](x, y, x + radius, y);
};
}
let game;
function startGame() {
[Link]('startScreen').[Link]('hidden');
game = new Make24Game();
[Link] = 'playing';
}
function nextLevel() {
[Link]('gameOverScreen').[Link]('hidden');
[Link]++;
[Link]();
[Link] = 'playing';
}
function restartGame() {
[Link]('gameOverScreen').[Link]('hidden');
[Link] = 1;
[Link] = 0;
[Link]();
[Link] = 'playing';
}
// Handle window resize
[Link]('resize', () => {
if (game) {
[Link]();
}
});
</script>
</body>
</html>