Kofi Robo

Robo Kofi – Kɔding Wɔ Twi Mu
https://cdn.tailwindcss.com

body {
font-family: ‘Inter’, sans-serif;
touch-action: manipulation; /* Prevents double-tap zoom on mobile */
}
/* Custom styles for better visual feedback on tap */
.command-btn:active, .control-btn:active {
transform: scale(0.95);
filter: brightness(0.9);
}
.command-queue-item {
transition: all 0.2s ease-in-out;
}
.modal {
transition: opacity 0.3s ease;
}

Robo Kofi

Boa Kofi ma ɔnnyina beaeɛ a ɛyɛ ahabammono no so!

Ma ɛnkɔ (Run)

San Yɛ (Reset)

Nnwuma (Commands)

Kɔ Anim

Dane Benkum

Dane Nifa

Yɛ No Bio x2

Deɛ Wɔahyehyɛ (Program)

Popa (Clear)

window.onload = function() {
// — DOM Elements —
const canvas = document.getElementById(‘gameCanvas’);
const ctx = canvas.getContext(‘2d’);
const commandButtons = document.querySelectorAll(‘.command-btn’);
const commandQueueContainer = document.getElementById(‘command-queue’);
const runBtn = document.getElementById(‘run-btn’);
const resetBtn = document.getElementById(‘reset-btn’);
const clearBtn = document.getElementById(‘clear-btn’);
const modal = document.getElementById(‘message-modal’);
const modalContent = document.getElementById(‘modal-content’);
const modalTitle = document.getElementById(‘modal-title’);
const modalMessage = document.getElementById(‘modal-message’);
const modalCloseBtn = document.getElementById(‘modal-close-btn’);

// — Game Configuration —
const GRID_SIZE = 10;
let TILE_SIZE;

const initialRobotState = { x: 1, y: 1, dir: 1 }; // 0:up, 1:right, 2:down, 3:left

let robot = { …initialRobotState };

let level = {
goal: { x: 8, y: 8 },
obstacles: [
{ x: 3, y: 1 }, { x: 3, y: 2 }, { x: 3, y: 3 }, { x: 3, y: 4 },
{ x: 6, y: 5 }, { x: 6, y: 6 }, { x: 6, y: 7 }, { x: 6, y: 8 },
]
};

let commandQueue = [];
let isExecuting = false;

// — Canvas & Drawing —
function resizeCanvas() {
const container = canvas.parentElement;
const size = Math.min(container.clientWidth, container.clientHeight);
canvas.width = size;
canvas.height = size;
TILE_SIZE = canvas.width / GRID_SIZE;
drawGame();
}

function drawGrid() {
ctx.strokeStyle = ‘#d1d5db’; // gray-300
for (let i = 0; i {
ctx.fillRect(obs.x * TILE_SIZE, obs.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
});
}

function drawGame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawGrid();
drawObstacles();
drawGoal();
drawRobot();
}

// — Command Logic —
function addCommand(command) {
if (isExecuting) return;

if (command === ‘loop’) {
const currentCommands = […commandQueue];
commandQueue.push(…currentCommands);
} else {
commandQueue.push(command);
}
updateCommandQueueUI();
}

function updateCommandQueueUI() {
commandQueueContainer.innerHTML = ”;
commandQueue.forEach((cmd, index) => {
const commandEl = document.createElement(‘div’);
commandEl.className = ‘command-queue-item bg-gray-200 text-gray-700 p-2 my-1 rounded-md text-center font-semibold’;

let text = ”;
if (cmd === ‘forward’) text = ‘Kɔ Anim’;
else if (cmd === ‘left’) text = ‘Dane Benkum’;
else if (cmd === ‘right’) text = ‘Dane Nifa’;

commandEl.textContent = `${index + 1}. ${text}`;
commandQueueContainer.appendChild(commandEl);
});
}

function clearCommands() {
if(isExecuting) return;
commandQueue = [];
updateCommandQueueUI();
}

// — Execution Logic —
function executeCommands() {
if (isExecuting || commandQueue.length === 0) return;
isExecuting = true;
disableButtons();
resetRobot();

let commandIndex = 0;
const interval = setInterval(() => {
if (commandIndex >= commandQueue.length) {
clearInterval(interval);
checkWinCondition();
return;
}

const command = commandQueue[commandIndex];
performAction(command);
drawGame();

// Check for collision after each move
if(isColliding()) {
clearInterval(interval);
showModal(“Awi! (Oops!)”, “Woabɔ biribi. San yɛ na fa kwan foforɔ so. (You hit something. Reset and try a different path.)”, false);
return;
}

commandIndex++;
}, 600);
}

function performAction(command) {
switch (command) {
case ‘forward’:
const dx = [0, 1, 0, -1];
const dy = [-1, 0, 1, 0];
robot.x += dx[robot.dir];
robot.y += dy[robot.dir];
break;
case ‘left’:
robot.dir = (robot.dir + 3) % 4;
break;
case ‘right’:
robot.dir = (robot.dir + 1) % 4;
break;
}
}

function isColliding() {
if (robot.x = GRID_SIZE || robot.y = GRID_SIZE) {
return true; // Wall collision
}
return level.obstacles.some(obs => obs.x === robot.x && obs.y === robot.y);
}

function checkWinCondition() {
if (robot.x === level.goal.x && robot.y === level.goal.y) {
showModal(“Ayekoo! (Congratulations!)”, “Woawie adwuma no yie! (You have completed the task successfully!)”, true);
} else {
showModal(“San Bɔ Mmɔden (Try Again)”, “Woanndu beaeɛ a ɛsɛ sɛ wodu. Hwehwɛ w’ahyehyɛdeɛ no mu. (You didn’t reach the goal. Check your program.)”, false);
}
}

function resetRobot() {
robot = { …initialRobotState };
drawGame();
}

// — UI & Controls —
function showModal(title, message, isSuccess) {
modalTitle.textContent = title;
modalMessage.textContent = message;
modalContent.className = modalContent.className.replace(/bg-\w+-\d+/, isSuccess ? ‘bg-green-100’ : ‘bg-red-100’);
modalTitle.className = modalTitle.className.replace(/text-\w+-\d+/, isSuccess ? ‘text-green-600’ : ‘text-red-600’);
modalCloseBtn.className = modalCloseBtn.className.replace(/bg-\w+-\d+/, isSuccess ? ‘bg-green-500’ : ‘bg-red-500’).replace(/hover:bg-\w+-\d+/, isSuccess ? ‘hover:bg-green-600’ : ‘hover:bg-red-600’);

modal.classList.remove(‘opacity-0’, ‘pointer-events-none’);
modalContent.classList.remove(‘scale-95’);
}

function hideModal() {
modal.classList.add(‘opacity-0’, ‘pointer-events-none’);
modalContent.classList.add(‘scale-95’);
isExecuting = false;
enableButtons();
resetRobot();
}

function disableButtons() {
runBtn.disabled = true;
resetBtn.disabled = true;
clearBtn.disabled = true;
commandButtons.forEach(b => b.disabled = true);
runBtn.classList.add(‘opacity-50’, ‘cursor-not-allowed’);
}

function enableButtons() {
runBtn.disabled = false;
resetBtn.disabled = false;
clearBtn.disabled = false;
commandButtons.forEach(b => b.disabled = false);
runBtn.classList.remove(‘opacity-50’, ‘cursor-not-allowed’);
}

// — Event Listeners —
commandButtons.forEach(button => {
button.addEventListener(‘click’, () => {
addCommand(button.dataset.command);
});
});

runBtn.addEventListener(‘click’, executeCommands);
resetBtn.addEventListener(‘click’, () => {
if (isExecuting) return;
resetRobot();
});
clearBtn.addEventListener(‘click’, clearCommands);
modalCloseBtn.addEventListener(‘click’, hideModal);
modal.addEventListener(‘click’, (e) => {
if (e.target === modal) {
hideModal();
}
});

window.addEventListener(‘resize’, resizeCanvas);

// — Initialisation —
resizeCanvas();
enableButtons();
};