cody-cat / script.js
burtenshaw's picture
burtenshaw HF Staff
Upload folder using huggingface_hub
dc24d6f verified
class KidsCodeApp {
constructor() {
this.codeBlocks = [];
this.character = document.getElementById('character');
this.dropZone = document.getElementById('drop-zone');
this.runButton = document.getElementById('run-button');
this.clearButton = document.getElementById('clear-button');
this.checkButton = document.getElementById('check-button');
this.isRunning = false;
this.currentAssignment = 0;
this.characterColors = ['🐱', '🦊', '🐰', '🐸', '🐼'];
this.currentColorIndex = 0;
this.sounds = {
dance: this.createSound(440, 0.3, 'triangle'),
jump: this.createSound(660, 0.2, 'square'),
wave: this.createSound(330, 0.4, 'sine'),
sound: this.createSound(880, 0.3, 'sawtooth'),
spin: this.createSound(392, 0.4, 'sine'),
sleep: this.createSound(220, 0.8, 'triangle'),
grow: this.createSound(523, 0.3, 'square'),
shrink: this.createSound(294, 0.3, 'square'),
color: this.createSound(659, 0.2, 'sawtooth'),
success: this.createSound(523, 0.2, 'sine'),
complete: this.createSound([523, 659, 784], 0.5, 'sine')
};
this.assignments = [
{
title: "First Steps!",
description: "Let's make Cody dance! πŸŽ‰",
goal: "Use the Dance block",
solution: ["dance"],
hint: "Drag the πŸ’ƒ Dance block to your code area and press Play!"
},
{
title: "Jump for Joy!",
description: "Now make Cody jump up high! 🦘",
goal: "Use the Jump block",
solution: ["jump"],
hint: "Find the 🦘 Jump block and drag it over!"
},
{
title: "Dance Party!",
description: "Make Cody dance AND jump! 🎊",
goal: "Use Dance then Jump blocks",
solution: ["dance", "jump"],
hint: "Put two blocks together - Dance first, then Jump!"
},
{
title: "Say Hello!",
description: "Make Cody wave hello to everyone! πŸ‘‹",
goal: "Use the Wave block",
solution: ["wave"],
hint: "The πŸ‘‹ Wave block will make Cody friendly!"
},
{
title: "Make Some Noise!",
description: "Let Cody make a fun sound! πŸ”Š",
goal: "Use the Make Sound block",
solution: ["sound"],
hint: "The πŸ”Š Make Sound block is waiting for you!"
},
{
title: "Spin Around!",
description: "Make Cody spin like a tornado! πŸŒͺ️",
goal: "Use the Spin block",
solution: ["spin"],
hint: "Find the πŸŒ€ Spin block to make Cody dizzy!"
},
{
title: "Growing Up!",
description: "Make Cody grow big and then small! πŸ“",
goal: "Use Grow Big then Get Small blocks",
solution: ["grow", "shrink"],
hint: "First πŸ” Grow Big, then πŸ”Ž Get Small!"
},
{
title: "Colorful Cat!",
description: "Change Cody's look with colors! 🎨",
goal: "Use the Change Color block",
solution: ["color"],
hint: "The 🎨 Change Color block makes Cody look different!"
},
{
title: "Repeat Magic!",
description: "Make Cody dance twice using repeat! πŸ”„",
goal: "Use Repeat 1 time then Dance",
solution: ["repeat", "dance"],
hint: "Put πŸ”„ Repeat 1 time first, then πŸ’ƒ Dance after it!"
},
{
title: "Grand Finale!",
description: "Create an amazing show! Use 4 different blocks! 🌟",
goal: "Use any 4 different action blocks",
solution: ["dance", "jump", "spin", "sound"],
hint: "Mix and match your favorite blocks to create a show!",
flexible: true
}
];
this.init();
}
init() {
this.setupDragAndDrop();
this.setupButtons();
this.setupAssignmentPanel();
this.hideDropHint();
this.loadAssignment(0);
}
createSound(frequency, duration, type = 'sine') {
return () => {
try {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
if (Array.isArray(frequency)) {
frequency.forEach((freq, index) => {
setTimeout(() => {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(freq, audioContext.currentTime);
oscillator.type = type;
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration / 3);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + duration / 3);
}, index * (duration * 1000 / 3));
});
} else {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
oscillator.type = type;
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + duration);
}
} catch (error) {
console.log('Audio not available');
}
};
}
setupDragAndDrop() {
const blocks = document.querySelectorAll('.block[draggable="true"]');
blocks.forEach(block => {
block.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', JSON.stringify({
type: block.dataset.blockType,
html: block.outerHTML
}));
});
});
this.dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
this.dropZone.classList.add('drag-over');
});
this.dropZone.addEventListener('dragleave', (e) => {
if (!this.dropZone.contains(e.relatedTarget)) {
this.dropZone.classList.remove('drag-over');
}
});
this.dropZone.addEventListener('drop', (e) => {
e.preventDefault();
this.dropZone.classList.remove('drag-over');
try {
const blockData = JSON.parse(e.dataTransfer.getData('text/plain'));
this.addBlock(blockData);
} catch (error) {
console.error('Error parsing block data:', error);
}
});
}
addBlock(blockData) {
this.hideDropHint();
const blockElement = document.createElement('div');
blockElement.innerHTML = blockData.html;
const block = blockElement.firstChild;
block.classList.add('code-block');
block.draggable = false;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
deleteBtn.innerHTML = 'βœ•';
deleteBtn.addEventListener('click', () => {
this.removeBlock(block);
});
block.appendChild(deleteBtn);
this.dropZone.appendChild(block);
this.codeBlocks.push({
element: block,
type: blockData.type
});
this.addBlockConnector();
}
addBlockConnector() {
if (this.codeBlocks.length > 1) {
const connector = document.createElement('div');
connector.className = 'block-connector';
connector.innerHTML = '⬇️';
connector.style.cssText = `
text-align: center;
font-size: 20px;
margin: 5px 0;
color: #a0aec0;
`;
const lastBlock = this.codeBlocks[this.codeBlocks.length - 2].element;
lastBlock.insertAdjacentElement('afterend', connector);
}
}
removeBlock(blockElement) {
const index = this.codeBlocks.findIndex(block => block.element === blockElement);
if (index !== -1) {
this.codeBlocks.splice(index, 1);
const nextElement = blockElement.nextElementSibling;
if (nextElement && nextElement.classList.contains('block-connector')) {
nextElement.remove();
}
const prevElement = blockElement.previousElementSibling;
if (prevElement && prevElement.classList.contains('block-connector') &&
(!blockElement.nextElementSibling || !blockElement.nextElementSibling.classList.contains('code-block'))) {
prevElement.remove();
}
blockElement.remove();
if (this.codeBlocks.length === 0) {
this.showDropHint();
}
}
}
hideDropHint() {
const dropHint = this.dropZone.querySelector('.drop-hint');
if (dropHint) {
dropHint.style.display = 'none';
}
}
showDropHint() {
const dropHint = this.dropZone.querySelector('.drop-hint');
if (dropHint) {
dropHint.style.display = 'block';
}
}
setupButtons() {
this.runButton.addEventListener('click', () => {
if (!this.isRunning) {
this.runCode();
}
});
this.checkButton.addEventListener('click', () => {
if (!this.isRunning) {
this.checkAssignment();
}
});
this.clearButton.addEventListener('click', () => {
this.clearCode();
});
}
async runCode() {
if (this.codeBlocks.length === 0) {
this.showMessage('Add some blocks first! 🧩');
return;
}
this.isRunning = true;
this.runButton.disabled = true;
this.runButton.querySelector('.button-text').textContent = 'Running...';
try {
await this.executeBlocks();
this.sounds.success();
this.showMessage('Great job! πŸŽ‰');
} catch (error) {
console.error('Error running code:', error);
this.showMessage('Oops! Something went wrong πŸ˜…');
} finally {
this.isRunning = false;
this.runButton.disabled = false;
this.runButton.querySelector('.button-text').textContent = 'Play';
}
}
async executeBlocks() {
for (let i = 0; i < this.codeBlocks.length; i++) {
const block = this.codeBlocks[i];
block.element.style.outline = '3px solid #4facfe';
block.element.style.boxShadow = '0 0 20px rgba(79, 172, 254, 0.5)';
await this.executeBlock(block);
block.element.style.outline = '';
block.element.style.boxShadow = '';
await this.wait(200);
}
}
async executeBlock(block) {
switch (block.type) {
case 'dance':
await this.makeDance();
break;
case 'jump':
await this.makeJump();
break;
case 'wave':
await this.makeWave();
break;
case 'sound':
await this.makeSound();
break;
case 'spin':
await this.makeSpin();
break;
case 'sleep':
await this.makeSleep();
break;
case 'grow':
await this.makeGrow();
break;
case 'shrink':
await this.makeShrink();
break;
case 'color':
await this.changeColor();
break;
case 'repeat':
await this.executeRepeat(block);
break;
case 'wait':
await this.wait(1000);
break;
}
}
async executeRepeat(repeatBlock) {
const repeatCount = 1;
const startIndex = this.codeBlocks.indexOf(repeatBlock);
const blocksToRepeat = [];
for (let i = startIndex + 1; i < this.codeBlocks.length; i++) {
const nextBlock = this.codeBlocks[i];
if (nextBlock.type === 'repeat') break;
blocksToRepeat.push(nextBlock);
}
for (let rep = 0; rep < repeatCount; rep++) {
for (const blockToRepeat of blocksToRepeat) {
await this.executeBlock(blockToRepeat);
await this.wait(200);
}
}
}
async makeDance() {
this.character.classList.add('dancing');
this.sounds.dance();
await this.wait(1000);
this.character.classList.remove('dancing');
}
async makeJump() {
this.character.classList.add('jumping');
this.sounds.jump();
await this.wait(600);
this.character.classList.remove('jumping');
}
async makeWave() {
this.character.classList.add('waving');
this.sounds.wave();
await this.wait(1000);
this.character.classList.remove('waving');
}
async makeSound() {
this.character.classList.add('bouncing');
this.sounds.sound();
await this.wait(500);
this.character.classList.remove('bouncing');
}
async makeSpin() {
this.character.classList.add('spinning');
this.sounds.spin();
await this.wait(800);
this.character.classList.remove('spinning');
}
async makeSleep() {
this.character.classList.add('sleeping');
this.sounds.sleep();
await this.wait(1200);
this.character.classList.remove('sleeping');
}
async makeGrow() {
this.character.classList.add('growing');
this.sounds.grow();
await this.wait(600);
this.character.classList.remove('growing');
}
async makeShrink() {
this.character.classList.add('shrinking');
this.sounds.shrink();
await this.wait(600);
this.character.classList.remove('shrinking');
}
async changeColor() {
this.currentColorIndex = (this.currentColorIndex + 1) % this.characterColors.length;
const characterBody = this.character.querySelector('.character-body');
characterBody.textContent = this.characterColors[this.currentColorIndex];
this.sounds.color();
await this.wait(300);
}
clearCode() {
this.codeBlocks.forEach(block => {
block.element.remove();
});
const connectors = this.dropZone.querySelectorAll('.block-connector');
connectors.forEach(connector => connector.remove());
this.codeBlocks = [];
this.showDropHint();
this.showMessage('All cleared! Ready for new code 🧹');
}
showMessage(text) {
const existingMessage = document.querySelector('.message-popup');
if (existingMessage) {
existingMessage.remove();
}
const message = document.createElement('div');
message.className = 'message-popup';
message.textContent = text;
message.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
padding: 20px 30px;
border-radius: 20px;
font-size: 24px;
font-weight: bold;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
z-index: 1000;
animation: messagePopup 0.3s ease-out;
`;
const style = document.createElement('style');
style.textContent = `
@keyframes messagePopup {
0% { transform: translate(-50%, -50%) scale(0.5); opacity: 0; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
`;
document.head.appendChild(style);
document.body.appendChild(message);
setTimeout(() => {
message.style.animation = 'messagePopup 0.3s ease-out reverse';
setTimeout(() => {
message.remove();
style.remove();
}, 300);
}, 2000);
}
setupAssignmentPanel() {
const showBtn = document.getElementById('show-assignment');
const closeBtn = document.getElementById('close-assignment');
const prevBtn = document.getElementById('prev-assignment');
const nextBtn = document.getElementById('next-assignment');
const panel = document.getElementById('assignment-panel');
showBtn.addEventListener('click', () => {
panel.style.display = 'block';
this.checkButton.style.display = 'inline-flex';
});
closeBtn.addEventListener('click', () => {
panel.style.display = 'none';
this.checkButton.style.display = 'none';
});
prevBtn.addEventListener('click', () => {
if (this.currentAssignment > 0) {
this.currentAssignment--;
this.loadAssignment(this.currentAssignment);
}
});
nextBtn.addEventListener('click', () => {
if (this.currentAssignment < this.assignments.length - 1) {
this.currentAssignment++;
this.loadAssignment(this.currentAssignment);
}
});
}
loadAssignment(index) {
const assignment = this.assignments[index];
if (!assignment) return;
document.querySelector('.assignment-title').textContent = assignment.title;
document.querySelector('.assignment-description').textContent = assignment.description;
document.querySelector('.assignment-goal').textContent = `🎯 Goal: ${assignment.goal}`;
document.querySelector('.assignment-counter').textContent = `${index + 1} / ${this.assignments.length}`;
const prevBtn = document.getElementById('prev-assignment');
const nextBtn = document.getElementById('next-assignment');
prevBtn.disabled = index === 0;
nextBtn.disabled = index === this.assignments.length - 1;
this.clearCode();
}
checkAssignment() {
const assignment = this.assignments[this.currentAssignment];
if (!assignment) return;
const currentBlocks = this.codeBlocks.map(block => block.type);
let isCorrect = false;
if (assignment.flexible && assignment.title.includes('Grand Finale')) {
const uniqueActionBlocks = currentBlocks.filter(block =>
['dance', 'jump', 'wave', 'sound', 'spin', 'sleep', 'grow', 'shrink', 'color'].includes(block)
);
const uniqueActions = new Set(uniqueActionBlocks);
isCorrect = uniqueActions.size >= 4;
} else {
isCorrect = JSON.stringify(currentBlocks) === JSON.stringify(assignment.solution);
}
if (isCorrect) {
this.sounds.complete();
this.showMessage(`πŸŽ‰ Amazing! You completed: ${assignment.title}!`);
setTimeout(() => {
if (this.currentAssignment < this.assignments.length - 1) {
this.showMessage('πŸš€ Ready for the next challenge?');
} else {
this.showMessage('πŸ† Congratulations! You completed all missions!');
}
}, 2500);
} else {
this.showMessage(`πŸ’‘ Hint: ${assignment.hint}`);
}
}
wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
document.addEventListener('DOMContentLoaded', () => {
new KidsCodeApp();
});