Creating a Maze Escape Game with MakeCode Arcade + microbit Motion Control & Strategic AI

Project Highlights

• Dynamic Maze Generation: Random maze layout for each playthrough

• Dual Control Modes: Switch between button input and accelerometer-based motion control

• Smart Enemy AI: A* pathfinding algorithm for adaptive chasing

• Scalable Design: Adjustable difficulty levels and customizable themes



Complete Code & Demo

Preview

• Objective: Escape maze while evading AI enemies

• Core Mechanics:

• Green sprite: Player

• Red sprite: Chasing enemy

• Gold sprite: Exit

• Blue tiles: Impassable walls

Full Implementation

// ===== Global Config =====
enum SpriteKind {
    Player,
    Enemy,
    Exit
}

// Maze parameters
let mazeWidth = 10;
let mazeHeight = 8;
let cellSize = 16;

// ===== Maze Generation =====
function generateMaze(): number[][] {
    let maze: number[][] = [];
    // Prim's algorithm implementation (abbreviated)
    return maze;
}

// ===== Game Elements =====
let maze = generateMaze();
let player = sprites.create(img`
    . . . . . 
    . 7 7 7 . 
    . 7 9 7 . 
    . 7 7 7 . 
    . . . . . 
`, SpriteKind.Player);

let enemy = sprites.create(img`
    . . 2 . . 
    . 2 2 2 . 
    2 2 4 2 2 
    . 2 2 2 . 
    . . 2 . . 
`, SpriteKind.Enemy);

let exit = sprites.create(img`
    . . 5 . . 
    . 5 5 5 . 
    5 5 d 5 5 
    . 5 5 5 . 
    . . 5 . . 
`, SpriteKind.Exit);

// ===== Control Logic =====
// Toggle control mode with A button
let useAccelerometer = false;
controller.A.onEvent(ControllerButtonEvent.Pressed, () => {
    useAccelerometer = !useAccelerometer;
    info.showScore("Mode: " + (useAccelerometer ? "Motion" : "Buttons"));
});

// Player movement
game.onUpdate(() => {
    if (useAccelerometer) {
        player.x += input.acceleration(Dimension.X) / 20;
        player.y += input.acceleration(Dimension.Y) / 20;
    } else {
        player.x += controller.dx() * 2;
        player.y += controller.dy() * 2;
    }
    
    // Wall collision
    if (tiles.tileAtLocationIsWall(tiles.getTileLocation(player.x, player.y))) {
        player.x -= controller.dx() * 2;
        player.y -= controller.dy() * 2;
        music.playTone(131, 200);
    }
});

// Enemy AI pathfinding
game.onUpdateInterval(1000, () => {
    let path = scene.aStarPath(enemy, player);
    if (path.length > 0) {
        enemy.follow(path[0]);
    }
});

// Win condition
sprites.onOverlap(SpriteKind.Player, SpriteKind.Exit, () => {
    game.over(true);
});


Technical Breakdown

1. Maze Generation (Prim's Algorithm)

function generateMaze(): number[][] {
    // Initialize full-wall maze
    let maze = [];
    for (let y = 0; y < mazeHeight; y++) {
        maze.push([]);
        for (let x = 0; x < mazeWidth; x++) {
            maze[y].push(1); // 1=wall, 0=path
        }
    }

    // Random starting point
    let startX = Math.randomRange(1, mazeWidth-2);
    let startY = Math.randomRange(1, mazeHeight-2);
    maze[startY][startX] = 0;

    // Path carving logic (abbreviated)
    return maze;
}

2. A* Pathfinding Implementation

• Heuristic: Manhattan distance h(n) = |x1-x2| + |y1-y2|

• Cost calculation: f(n) = g(n) + h(n)

• Optimization: Path step limitation prevents lag

3. Dual Control System

MODE CODE IMPLEMENTATION Sensitivity Parameter
Button Control controller.dx()/dy() Multiplier (2)
Motion Control input.acceleration() Divisor (20)


Educational Applications

Classroom Activities

1. Algorithm Workshop

  • Task: Modify maze generation rules to create symmetrical layouts 

  • Concepts: Recursive algorithms, 2D array manipulation 

2. Physics Integration

  • Task: Calculate movement speed using accelerometer data

  • Formula: v = Δposition / Δtime

3. Pixel Art Design

  • Task: Create theme-based sprites using Piskel 

  • Requirements: 16x16 pixels, thematic consistency (space/forest/castle) 


Advanced Challenges

Difficulty Level System

info.onStart(() => {
    let difficulty = game.askForNumber("Choose difficulty (1-5)", 1);
    mazeWidth = 6 + difficulty * 2;
    mazeHeight = 4 + difficulty * 2;
});

Dynamic Environment

// Regenerate maze every 30 seconds
game.onUpdateInterval(30000, () => {
    tiles.setTilemap(generateMaze());
    info.changeScoreBy(10); // Bonus points
});


Troubleshooting

Issue 1: Enemy Stuck in Corners

Solution: Add pathfinding tolerance

let path = scene.aStarPath(enemy, player, {
    stepLimit: 50 // Limit search steps
});

Issue 2: Low Motion Sensitivity

Adjustment: Modify acceleration divisor

player.x += input.acceleration(Dimension.X) / 15; // Increased sensitivity


Recommended Resources