/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Bot class: represents a singing bot to be tuned
var Bot = Container.expand(function () {
	var self = Container.call(this);
	// Bot state: 'untuned', 'tuning', 'tuned', 'failed'
	self.state = 'untuned';
	// Randomly pick a sequence of notes and a scale for this bot
	self.notes = Bot.randomNoteSequence();
	self.correctScale = Bot.notesToScale(self.notes);
	// Timer for tuning (15s)
	self.tuneTimer = null;
	self.tuneTimeLeft = 15;
	self.tuneTimerText = null;
	// Create bot body (red box for untuned, green for tuned)
	self.botAsset = self.attachAsset('botBody', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.botAsset.tint = 0xd83318; // red
	// Display the notes above the bot
	self.noteText = new Text2(self.notes.join(" "), {
		size: 90,
		fill: 0xFFFFFF
	});
	self.noteText.anchor.set(0.5, 1);
	self.noteText.x = 0;
	self.noteText.y = -self.botAsset.height / 2 - 20;
	self.addChild(self.noteText);
	// "Accorder" button under the bot
	self.tuneBtn = self.attachAsset('tuneBtn', {
		anchorX: 0.5,
		anchorY: 0
	});
	self.tuneBtn.y = self.botAsset.height / 2 + 30;
	self.tuneBtn.x = 0;
	self.addChild(self.tuneBtn);
	// Button label
	self.tuneBtnLabel = new Text2("Accorder", {
		size: 60,
		fill: 0x222222
	});
	self.tuneBtnLabel.anchor.set(0.5, 0.5);
	self.tuneBtnLabel.x = self.tuneBtn.x;
	self.tuneBtnLabel.y = self.tuneBtn.y + self.tuneBtn.height / 2;
	self.addChild(self.tuneBtnLabel);
	// When tuning, show scale choices
	self.scaleBtns = [];
	self.scaleBtnLabels = [];
	// Event: press on "Accorder" button
	self.tuneBtn.down = function (x, y, obj) {
		if (self.state !== 'untuned') return;
		LK.getSound('tune_press').play();
		self.showScaleChoices();
	};
	// Show scale choices under the bot
	self.showScaleChoices = function () {
		if (self.state !== 'untuned') return;
		self.state = 'tuning';
		// Remove previous scale buttons if any
		for (var i = 0; i < self.scaleBtns.length; i++) {
			self.removeChild(self.scaleBtns[i]);
			self.removeChild(self.scaleBtnLabels[i]);
		}
		self.scaleBtns = [];
		self.scaleBtnLabels = [];
		// Get 3 scales: correct + 2 random wrong
		var scales = Bot.getScaleChoices(self.correctScale);
		for (var i = 0; i < scales.length; i++) {
			var btn = self.attachAsset('scaleBtn', {
				anchorX: 0.5,
				anchorY: 0
			});
			btn.x = 0 + (i - 1) * (btn.width + 40);
			btn.y = self.tuneBtn.y + self.tuneBtn.height + 30;
			self.addChild(btn);
			var label = new Text2(scales[i], {
				size: 55,
				fill: 0x222222
			});
			label.anchor.set(0.5, 0.5);
			label.x = btn.x;
			label.y = btn.y + btn.height / 2;
			self.addChild(label);
			// Closure for scale selection
			(function (scaleName, btnObj) {
				btn.down = function (x, y, obj) {
					self.handleScaleChoice(scaleName);
				};
			})(scales[i], btn);
			self.scaleBtns.push(btn);
			self.scaleBtnLabels.push(label);
		}
		// Show and start 15s timer
		if (self.tuneTimerText) {
			self.removeChild(self.tuneTimerText);
			self.tuneTimerText = null;
		}
		self.tuneTimeLeft = 15;
		self.tuneTimerText = new Text2("15", {
			size: 70,
			fill: 0xFFD700
		});
		self.tuneTimerText.anchor.set(0.5, 0);
		self.tuneTimerText.x = 0;
		self.tuneTimerText.y = self.tuneBtn.y + self.tuneBtn.height + 140;
		self.addChild(self.tuneTimerText);
		if (self.tuneTimer) {
			LK.clearInterval(self.tuneTimer);
			self.tuneTimer = null;
		}
		self.tuneTimer = LK.setInterval(function () {
			if (self.state !== 'tuning') {
				LK.clearInterval(self.tuneTimer);
				self.tuneTimer = null;
				return;
			}
			self.tuneTimeLeft--;
			self.tuneTimerText.setText(self.tuneTimeLeft + "");
			if (self.tuneTimeLeft <= 0) {
				LK.clearInterval(self.tuneTimer);
				self.tuneTimer = null;
				// Remove scale buttons
				for (var i = 0; i < self.scaleBtns.length; i++) {
					self.removeChild(self.scaleBtns[i]);
					self.removeChild(self.scaleBtnLabels[i]);
				}
				self.scaleBtns = [];
				self.scaleBtnLabels = [];
				self.state = 'failed';
				self.botAsset.tint = 0xd83318;
				self.noteText.setText(self.notes.join(" ") + " ✗");
				if (self.tuneTimerText) {
					self.removeChild(self.tuneTimerText);
					self.tuneTimerText = null;
				}
				// Animate and remove after a short delay
				tween(self, {
					alpha: 0
				}, {
					duration: 600,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						self.destroy();
						onBotTuned(false);
					}
				});
			}
		}, 1000);
	};
	// Handle scale choice
	self.handleScaleChoice = function (chosenScale) {
		if (self.state !== 'tuning') return;
		// Stop timer and remove timer text
		if (self.tuneTimer) {
			LK.clearInterval(self.tuneTimer);
			self.tuneTimer = null;
		}
		if (self.tuneTimerText) {
			self.removeChild(self.tuneTimerText);
			self.tuneTimerText = null;
		}
		// Remove scale buttons
		for (var i = 0; i < self.scaleBtns.length; i++) {
			self.removeChild(self.scaleBtns[i]);
			self.removeChild(self.scaleBtnLabels[i]);
		}
		self.scaleBtns = [];
		self.scaleBtnLabels = [];
		if (chosenScale === self.correctScale) {
			LK.getSound('tune_success').play();
			self.state = 'tuned';
			self.botAsset.tint = 0x83de44; // green
			self.noteText.setText(self.notes.join(" ") + " ✓");
			// Show "Le bot chante juste !" message in the center for 1s
			showSuccess("Le bot chante juste !");
			// Animate and remove after a short delay
			tween(self, {
				alpha: 0
			}, {
				duration: 600,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					self.destroy();
					onBotTuned(true);
				}
			});
		} else {
			LK.getSound('tune_fail').play();
			self.state = 'failed';
			self.botAsset.tint = 0xd83318; // red
			self.noteText.setText(self.notes.join(" ") + " ✗");
			// Animate and remove after a short delay
			tween(self, {
				alpha: 0
			}, {
				duration: 600,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					self.destroy();
					onBotTuned(false);
				}
			});
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
// Static: pick a random note
var game = new LK.Game({
	backgroundColor: 0x000000 // black fallback
});
/**** 
* Game Code
****/ 
// Add custom background image
var backgroundImage = LK.getAsset('customBackground', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0
});
game.addChild(backgroundImage);
// Play background music in a loop
LK.playMusic('bgmusic', {
	loop: true
});
// End of file;
Bot.randomNoteSequence = function () {
	// Expanded notes: include all 12 chromatic notes
	var notes = ["C", "C#", "Db", "D", "D#", "Eb", "E", "F", "F#", "Gb", "G", "G#", "Ab", "A", "A#", "Bb", "B"];
	var len = 2 + Math.floor(Math.random() * 3); // 2-4 notes
	var seq = [];
	for (var i = 0; i < len; i++) {
		var n = notes[Math.floor(Math.random() * notes.length)];
		seq.push(n);
	}
	return seq;
};
// Map a sequence of notes to a scale (use first note for simplicity, but support more scales)
Bot.notesToScale = function (notes) {
	// Map note to scale name (major/minor, sharps/flats)
	var note = notes[0];
	// Normalize note for mapping
	var map = {
		"C": "Do majeur",
		"C#": "Do# majeur",
		"Db": "Réb majeur",
		"D": "Ré majeur",
		"D#": "Ré# majeur",
		"Eb": "Mib majeur",
		"E": "Mi majeur",
		"F": "Fa majeur",
		"F#": "Fa# majeur",
		"Gb": "Solb majeur",
		"G": "Sol majeur",
		"G#": "Sol# majeur",
		"Ab": "Lab majeur",
		"A": "La majeur",
		"A#": "La# majeur",
		"Bb": "Sib majeur",
		"B": "Si majeur"
	};
	// Add some minor scales for more variety
	var minorMap = {
		"C": "Do mineur",
		"C#": "Do# mineur",
		"Db": "Réb mineur",
		"D": "Ré mineur",
		"D#": "Ré# mineur",
		"Eb": "Mib mineur",
		"E": "Mi mineur",
		"F": "Fa mineur",
		"F#": "Fa# mineur",
		"Gb": "Solb mineur",
		"G": "Sol mineur",
		"G#": "Sol# mineur",
		"Ab": "Lab mineur",
		"A": "La mineur",
		"A#": "La# mineur",
		"Bb": "Sib mineur",
		"B": "Si mineur"
	};
	// 50% chance to use minor scale
	var useMinor = Math.random() < 0.5;
	if (useMinor && minorMap[note]) return minorMap[note];
	return map[note] || "Do majeur";
};
// Static: get 3 scale choices (1 correct, 2 random wrong) from expanded list
Bot.getScaleChoices = function (correct) {
	var all = ["Do majeur", "Do# majeur", "Réb majeur", "Ré majeur", "Ré# majeur", "Mib majeur", "Mi majeur", "Fa majeur", "Fa# majeur", "Solb majeur", "Sol majeur", "Sol# majeur", "Lab majeur", "La majeur", "La# majeur", "Sib majeur", "Si majeur", "Do mineur", "Do# mineur", "Réb mineur", "Ré mineur", "Ré# mineur", "Mib mineur", "Mi mineur", "Fa mineur", "Fa# mineur", "Solb mineur", "Sol mineur", "Sol# mineur", "Lab mineur", "La mineur", "La# mineur", "Sib mineur", "Si mineur"];
	var choices = [correct];
	// Pick 2 random wrong scales
	var wrong = [];
	for (var i = 0; i < all.length; i++) {
		if (all[i] !== correct) wrong.push(all[i]);
	}
	for (var i = 0; i < 2; i++) {
		var idx = Math.floor(Math.random() * wrong.length);
		choices.push(wrong[idx]);
		wrong.splice(idx, 1);
	}
	// Shuffle
	for (var i = choices.length - 1; i > 0; i--) {
		var j = Math.floor(Math.random() * (i + 1));
		var tmp = choices[i];
		choices[i] = choices[j];
		choices[j] = tmp;
	}
	return choices;
};
// Game state
var bots = [];
var level = 1;
var botsToTune = 5;
var botsTuned = 0;
var botsFailed = 0;
var maxBotsOnScreen = 3;
var isSpawning = true;
// Score/level display
var scoreTxt = new Text2("Niveau 1\n0/5 bots accordés", {
	size: 90,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Error message
var errorMsg = new Text2("", {
	size: 80,
	fill: 0xFF3333
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.visible = false;
LK.gui.center.addChild(errorMsg);
// Success message
var successMsg = new Text2("", {
	size: 80,
	fill: 0x44FF44
});
successMsg.anchor.set(0.5, 0.5);
successMsg.visible = false;
LK.gui.center.addChild(successMsg);
// Show success message for 1s
function showSuccess(msg) {
	successMsg.setText(msg);
	successMsg.visible = true;
	tween(successMsg, {
		alpha: 1
	}, {
		duration: 0
	});
	LK.setTimeout(function () {
		tween(successMsg, {
			alpha: 0
		}, {
			duration: 400,
			onFinish: function onFinish() {
				successMsg.visible = false;
			}
		});
	}, 1000);
}
// Called when a bot is tuned or failed
function onBotTuned(success) {
	if (success) {
		botsTuned++;
	} else {
		botsFailed++;
		showError("Mauvaise gamme !");
	}
	updateScoreText();
	// Remove from bots array
	for (var i = bots.length - 1; i >= 0; i--) {
		if (bots[i].state === 'tuned' || bots[i].state === 'failed') {
			bots.splice(i, 1);
		}
	}
	// Check level up
	if (botsTuned >= botsToTune) {
		nextLevel();
	}
}
// Show error message for 1s
function showError(msg) {
	errorMsg.setText(msg);
	errorMsg.visible = true;
	tween(errorMsg, {
		alpha: 1
	}, {
		duration: 0
	});
	LK.setTimeout(function () {
		tween(errorMsg, {
			alpha: 0
		}, {
			duration: 400,
			onFinish: function onFinish() {
				errorMsg.visible = false;
			}
		});
	}, 1000);
}
// Update score/level text
function updateScoreText() {
	scoreTxt.setText("Niveau " + level + "\n" + botsTuned + "/" + botsToTune + " bots accordés");
}
// Level up
function nextLevel() {
	level++;
	botsTuned = 0;
	botsFailed = 0;
	botsToTune = botsToTune * 2;
	updateScoreText();
	// Flash green
	LK.effects.flashScreen(0x83de44, 600);
}
// Spawn a bot at a random position
function spawnBot() {
	if (!isSpawning) return;
	if (bots.length >= maxBotsOnScreen) return;
	if (botsTuned >= botsToTune) return;
	var bot = new Bot();
	// Place randomly, avoid top 100px and bottom 200px
	var margin = 120;
	var minY = 200;
	var maxY = 2732 - 350;
	var minX = margin;
	var maxX = 2048 - margin;
	bot.x = minX + Math.random() * (maxX - minX);
	bot.y = minY + Math.random() * (maxY - minY);
	bots.push(bot);
	game.addChild(bot);
}
// Main game update
game.update = function () {
	// Spawn bots as needed
	if (isSpawning && bots.length < maxBotsOnScreen && botsTuned < botsToTune) {
		spawnBot();
	}
	// Win condition: after last level, e.g. level 50 (arbitrary cap)
	if (level > 50) {
		LK.showYouWin();
		isSpawning = false;
	}
};
// Reset game state on game over
game.onGameOver = function () {
	bots = [];
	level = 1;
	botsToTune = 5;
	botsTuned = 0;
	botsFailed = 0;
	isSpawning = true;
	updateScoreText();
	errorMsg.visible = false;
};
// (Optional) handle resizing, but LK does this automatically
// No dragging or movement needed; all interaction is via buttons
// End of file /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Bot class: represents a singing bot to be tuned
var Bot = Container.expand(function () {
	var self = Container.call(this);
	// Bot state: 'untuned', 'tuning', 'tuned', 'failed'
	self.state = 'untuned';
	// Randomly pick a sequence of notes and a scale for this bot
	self.notes = Bot.randomNoteSequence();
	self.correctScale = Bot.notesToScale(self.notes);
	// Timer for tuning (15s)
	self.tuneTimer = null;
	self.tuneTimeLeft = 15;
	self.tuneTimerText = null;
	// Create bot body (red box for untuned, green for tuned)
	self.botAsset = self.attachAsset('botBody', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.botAsset.tint = 0xd83318; // red
	// Display the notes above the bot
	self.noteText = new Text2(self.notes.join(" "), {
		size: 90,
		fill: 0xFFFFFF
	});
	self.noteText.anchor.set(0.5, 1);
	self.noteText.x = 0;
	self.noteText.y = -self.botAsset.height / 2 - 20;
	self.addChild(self.noteText);
	// "Accorder" button under the bot
	self.tuneBtn = self.attachAsset('tuneBtn', {
		anchorX: 0.5,
		anchorY: 0
	});
	self.tuneBtn.y = self.botAsset.height / 2 + 30;
	self.tuneBtn.x = 0;
	self.addChild(self.tuneBtn);
	// Button label
	self.tuneBtnLabel = new Text2("Accorder", {
		size: 60,
		fill: 0x222222
	});
	self.tuneBtnLabel.anchor.set(0.5, 0.5);
	self.tuneBtnLabel.x = self.tuneBtn.x;
	self.tuneBtnLabel.y = self.tuneBtn.y + self.tuneBtn.height / 2;
	self.addChild(self.tuneBtnLabel);
	// When tuning, show scale choices
	self.scaleBtns = [];
	self.scaleBtnLabels = [];
	// Event: press on "Accorder" button
	self.tuneBtn.down = function (x, y, obj) {
		if (self.state !== 'untuned') return;
		LK.getSound('tune_press').play();
		self.showScaleChoices();
	};
	// Show scale choices under the bot
	self.showScaleChoices = function () {
		if (self.state !== 'untuned') return;
		self.state = 'tuning';
		// Remove previous scale buttons if any
		for (var i = 0; i < self.scaleBtns.length; i++) {
			self.removeChild(self.scaleBtns[i]);
			self.removeChild(self.scaleBtnLabels[i]);
		}
		self.scaleBtns = [];
		self.scaleBtnLabels = [];
		// Get 3 scales: correct + 2 random wrong
		var scales = Bot.getScaleChoices(self.correctScale);
		for (var i = 0; i < scales.length; i++) {
			var btn = self.attachAsset('scaleBtn', {
				anchorX: 0.5,
				anchorY: 0
			});
			btn.x = 0 + (i - 1) * (btn.width + 40);
			btn.y = self.tuneBtn.y + self.tuneBtn.height + 30;
			self.addChild(btn);
			var label = new Text2(scales[i], {
				size: 55,
				fill: 0x222222
			});
			label.anchor.set(0.5, 0.5);
			label.x = btn.x;
			label.y = btn.y + btn.height / 2;
			self.addChild(label);
			// Closure for scale selection
			(function (scaleName, btnObj) {
				btn.down = function (x, y, obj) {
					self.handleScaleChoice(scaleName);
				};
			})(scales[i], btn);
			self.scaleBtns.push(btn);
			self.scaleBtnLabels.push(label);
		}
		// Show and start 15s timer
		if (self.tuneTimerText) {
			self.removeChild(self.tuneTimerText);
			self.tuneTimerText = null;
		}
		self.tuneTimeLeft = 15;
		self.tuneTimerText = new Text2("15", {
			size: 70,
			fill: 0xFFD700
		});
		self.tuneTimerText.anchor.set(0.5, 0);
		self.tuneTimerText.x = 0;
		self.tuneTimerText.y = self.tuneBtn.y + self.tuneBtn.height + 140;
		self.addChild(self.tuneTimerText);
		if (self.tuneTimer) {
			LK.clearInterval(self.tuneTimer);
			self.tuneTimer = null;
		}
		self.tuneTimer = LK.setInterval(function () {
			if (self.state !== 'tuning') {
				LK.clearInterval(self.tuneTimer);
				self.tuneTimer = null;
				return;
			}
			self.tuneTimeLeft--;
			self.tuneTimerText.setText(self.tuneTimeLeft + "");
			if (self.tuneTimeLeft <= 0) {
				LK.clearInterval(self.tuneTimer);
				self.tuneTimer = null;
				// Remove scale buttons
				for (var i = 0; i < self.scaleBtns.length; i++) {
					self.removeChild(self.scaleBtns[i]);
					self.removeChild(self.scaleBtnLabels[i]);
				}
				self.scaleBtns = [];
				self.scaleBtnLabels = [];
				self.state = 'failed';
				self.botAsset.tint = 0xd83318;
				self.noteText.setText(self.notes.join(" ") + " ✗");
				if (self.tuneTimerText) {
					self.removeChild(self.tuneTimerText);
					self.tuneTimerText = null;
				}
				// Animate and remove after a short delay
				tween(self, {
					alpha: 0
				}, {
					duration: 600,
					easing: tween.easeIn,
					onFinish: function onFinish() {
						self.destroy();
						onBotTuned(false);
					}
				});
			}
		}, 1000);
	};
	// Handle scale choice
	self.handleScaleChoice = function (chosenScale) {
		if (self.state !== 'tuning') return;
		// Stop timer and remove timer text
		if (self.tuneTimer) {
			LK.clearInterval(self.tuneTimer);
			self.tuneTimer = null;
		}
		if (self.tuneTimerText) {
			self.removeChild(self.tuneTimerText);
			self.tuneTimerText = null;
		}
		// Remove scale buttons
		for (var i = 0; i < self.scaleBtns.length; i++) {
			self.removeChild(self.scaleBtns[i]);
			self.removeChild(self.scaleBtnLabels[i]);
		}
		self.scaleBtns = [];
		self.scaleBtnLabels = [];
		if (chosenScale === self.correctScale) {
			LK.getSound('tune_success').play();
			self.state = 'tuned';
			self.botAsset.tint = 0x83de44; // green
			self.noteText.setText(self.notes.join(" ") + " ✓");
			// Show "Le bot chante juste !" message in the center for 1s
			showSuccess("Le bot chante juste !");
			// Animate and remove after a short delay
			tween(self, {
				alpha: 0
			}, {
				duration: 600,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					self.destroy();
					onBotTuned(true);
				}
			});
		} else {
			LK.getSound('tune_fail').play();
			self.state = 'failed';
			self.botAsset.tint = 0xd83318; // red
			self.noteText.setText(self.notes.join(" ") + " ✗");
			// Animate and remove after a short delay
			tween(self, {
				alpha: 0
			}, {
				duration: 600,
				easing: tween.easeIn,
				onFinish: function onFinish() {
					self.destroy();
					onBotTuned(false);
				}
			});
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
// Static: pick a random note
var game = new LK.Game({
	backgroundColor: 0x000000 // black fallback
});
/**** 
* Game Code
****/ 
// Add custom background image
var backgroundImage = LK.getAsset('customBackground', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0
});
game.addChild(backgroundImage);
// Play background music in a loop
LK.playMusic('bgmusic', {
	loop: true
});
// End of file;
Bot.randomNoteSequence = function () {
	// Expanded notes: include all 12 chromatic notes
	var notes = ["C", "C#", "Db", "D", "D#", "Eb", "E", "F", "F#", "Gb", "G", "G#", "Ab", "A", "A#", "Bb", "B"];
	var len = 2 + Math.floor(Math.random() * 3); // 2-4 notes
	var seq = [];
	for (var i = 0; i < len; i++) {
		var n = notes[Math.floor(Math.random() * notes.length)];
		seq.push(n);
	}
	return seq;
};
// Map a sequence of notes to a scale (use first note for simplicity, but support more scales)
Bot.notesToScale = function (notes) {
	// Map note to scale name (major/minor, sharps/flats)
	var note = notes[0];
	// Normalize note for mapping
	var map = {
		"C": "Do majeur",
		"C#": "Do# majeur",
		"Db": "Réb majeur",
		"D": "Ré majeur",
		"D#": "Ré# majeur",
		"Eb": "Mib majeur",
		"E": "Mi majeur",
		"F": "Fa majeur",
		"F#": "Fa# majeur",
		"Gb": "Solb majeur",
		"G": "Sol majeur",
		"G#": "Sol# majeur",
		"Ab": "Lab majeur",
		"A": "La majeur",
		"A#": "La# majeur",
		"Bb": "Sib majeur",
		"B": "Si majeur"
	};
	// Add some minor scales for more variety
	var minorMap = {
		"C": "Do mineur",
		"C#": "Do# mineur",
		"Db": "Réb mineur",
		"D": "Ré mineur",
		"D#": "Ré# mineur",
		"Eb": "Mib mineur",
		"E": "Mi mineur",
		"F": "Fa mineur",
		"F#": "Fa# mineur",
		"Gb": "Solb mineur",
		"G": "Sol mineur",
		"G#": "Sol# mineur",
		"Ab": "Lab mineur",
		"A": "La mineur",
		"A#": "La# mineur",
		"Bb": "Sib mineur",
		"B": "Si mineur"
	};
	// 50% chance to use minor scale
	var useMinor = Math.random() < 0.5;
	if (useMinor && minorMap[note]) return minorMap[note];
	return map[note] || "Do majeur";
};
// Static: get 3 scale choices (1 correct, 2 random wrong) from expanded list
Bot.getScaleChoices = function (correct) {
	var all = ["Do majeur", "Do# majeur", "Réb majeur", "Ré majeur", "Ré# majeur", "Mib majeur", "Mi majeur", "Fa majeur", "Fa# majeur", "Solb majeur", "Sol majeur", "Sol# majeur", "Lab majeur", "La majeur", "La# majeur", "Sib majeur", "Si majeur", "Do mineur", "Do# mineur", "Réb mineur", "Ré mineur", "Ré# mineur", "Mib mineur", "Mi mineur", "Fa mineur", "Fa# mineur", "Solb mineur", "Sol mineur", "Sol# mineur", "Lab mineur", "La mineur", "La# mineur", "Sib mineur", "Si mineur"];
	var choices = [correct];
	// Pick 2 random wrong scales
	var wrong = [];
	for (var i = 0; i < all.length; i++) {
		if (all[i] !== correct) wrong.push(all[i]);
	}
	for (var i = 0; i < 2; i++) {
		var idx = Math.floor(Math.random() * wrong.length);
		choices.push(wrong[idx]);
		wrong.splice(idx, 1);
	}
	// Shuffle
	for (var i = choices.length - 1; i > 0; i--) {
		var j = Math.floor(Math.random() * (i + 1));
		var tmp = choices[i];
		choices[i] = choices[j];
		choices[j] = tmp;
	}
	return choices;
};
// Game state
var bots = [];
var level = 1;
var botsToTune = 5;
var botsTuned = 0;
var botsFailed = 0;
var maxBotsOnScreen = 3;
var isSpawning = true;
// Score/level display
var scoreTxt = new Text2("Niveau 1\n0/5 bots accordés", {
	size: 90,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Error message
var errorMsg = new Text2("", {
	size: 80,
	fill: 0xFF3333
});
errorMsg.anchor.set(0.5, 0.5);
errorMsg.visible = false;
LK.gui.center.addChild(errorMsg);
// Success message
var successMsg = new Text2("", {
	size: 80,
	fill: 0x44FF44
});
successMsg.anchor.set(0.5, 0.5);
successMsg.visible = false;
LK.gui.center.addChild(successMsg);
// Show success message for 1s
function showSuccess(msg) {
	successMsg.setText(msg);
	successMsg.visible = true;
	tween(successMsg, {
		alpha: 1
	}, {
		duration: 0
	});
	LK.setTimeout(function () {
		tween(successMsg, {
			alpha: 0
		}, {
			duration: 400,
			onFinish: function onFinish() {
				successMsg.visible = false;
			}
		});
	}, 1000);
}
// Called when a bot is tuned or failed
function onBotTuned(success) {
	if (success) {
		botsTuned++;
	} else {
		botsFailed++;
		showError("Mauvaise gamme !");
	}
	updateScoreText();
	// Remove from bots array
	for (var i = bots.length - 1; i >= 0; i--) {
		if (bots[i].state === 'tuned' || bots[i].state === 'failed') {
			bots.splice(i, 1);
		}
	}
	// Check level up
	if (botsTuned >= botsToTune) {
		nextLevel();
	}
}
// Show error message for 1s
function showError(msg) {
	errorMsg.setText(msg);
	errorMsg.visible = true;
	tween(errorMsg, {
		alpha: 1
	}, {
		duration: 0
	});
	LK.setTimeout(function () {
		tween(errorMsg, {
			alpha: 0
		}, {
			duration: 400,
			onFinish: function onFinish() {
				errorMsg.visible = false;
			}
		});
	}, 1000);
}
// Update score/level text
function updateScoreText() {
	scoreTxt.setText("Niveau " + level + "\n" + botsTuned + "/" + botsToTune + " bots accordés");
}
// Level up
function nextLevel() {
	level++;
	botsTuned = 0;
	botsFailed = 0;
	botsToTune = botsToTune * 2;
	updateScoreText();
	// Flash green
	LK.effects.flashScreen(0x83de44, 600);
}
// Spawn a bot at a random position
function spawnBot() {
	if (!isSpawning) return;
	if (bots.length >= maxBotsOnScreen) return;
	if (botsTuned >= botsToTune) return;
	var bot = new Bot();
	// Place randomly, avoid top 100px and bottom 200px
	var margin = 120;
	var minY = 200;
	var maxY = 2732 - 350;
	var minX = margin;
	var maxX = 2048 - margin;
	bot.x = minX + Math.random() * (maxX - minX);
	bot.y = minY + Math.random() * (maxY - minY);
	bots.push(bot);
	game.addChild(bot);
}
// Main game update
game.update = function () {
	// Spawn bots as needed
	if (isSpawning && bots.length < maxBotsOnScreen && botsTuned < botsToTune) {
		spawnBot();
	}
	// Win condition: after last level, e.g. level 50 (arbitrary cap)
	if (level > 50) {
		LK.showYouWin();
		isSpawning = false;
	}
};
// Reset game state on game over
game.onGameOver = function () {
	bots = [];
	level = 1;
	botsToTune = 5;
	botsTuned = 0;
	botsFailed = 0;
	isSpawning = true;
	updateScoreText();
	errorMsg.visible = false;
};
// (Optional) handle resizing, but LK does this automatically
// No dragging or movement needed; all interaction is via buttons
// End of file
 Modern App Store icon, high definition, square with rounded corners, for a game titled "Accorder les bots" and with the description "Des bots rouges chantent faux à l’écran. Appuie sur "Accorder", choisis la bonne gamme selon les notes affichées pour accorder chaque bot. Monte de niveau en accordant de plus en plus de bots.". No text on icon!
 Fond d'écran salle de programmation. In-Game asset. 2d. High contrast. No shadows