Microbit Programming: Snake Game with Growing Body and Greedy St

  • 时间:2020-09-12 10:17:13
  • 分类:网络文摘
  • 阅读:131 次

Last week, we show the possibility of playing a simple Snake Game on the Microbit (Microbit Programming: The Development of a Snake Eating Apple Game and AI (Version 1 – Snake Does Not Grow)) – however, the snake was tiny with 1 pixel and never grows! This is boring and more like a boy is chasing a girl.

This time, we will modify the game to make the snake grows after eating the apple. However, considering there are only 25 pixels on the Microbit LED screen, the snake will stop growing once its body reaches 10 pixels – to make the game a bit longer/interesting as in theory you could play the game forever.

Snake Body

Instead of using a single game.LedSprite object, we will need to store the snake body pieces in an Array. We can define a initSnake function that takes an array of coordinates and create the corresponding sprite objects.

1
2
3
4
5
6
7
function initSnake(arr: Array<number>) {
    let result = [];
    for (let i = 0; i + 1 < arr.length; i += 2) {
        result.push(game.createSprite(arr[i], arr[i + 1]));
    }
    return result;
};
function initSnake(arr: Array<number>) {
    let result = [];
    for (let i = 0; i + 1 < arr.length; i += 2) {
        result.push(game.createSprite(arr[i], arr[i + 1]));
    }
    return result;
};

To initialize the snake:

1
2
3
4
5
6
let direction = 1; // initial direction is down
let dxOffset = [[1, 0], [0, 1], [-1, 0], [0, -1]];
let snake = initSnake([px, py, px + 1, py]);
// when snake grows to 10 pixels, it stops growing
// to avoid filling the LED
const maxLength = 10;
let direction = 1; // initial direction is down
let dxOffset = [[1, 0], [0, 1], [-1, 0], [0, -1]];
let snake = initSnake([px, py, px + 1, py]);
// when snake grows to 10 pixels, it stops growing
// to avoid filling the LED
const maxLength = 10;

Collision Detection

We need to make sure the next pixel (to move to) is also not a snake body.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// check if the (x, y) is one of the snake body's coordinate.
function isOnSnake(x: number, y: number): boolean {
    for (let body of snake) {
        if (body.x() == x && body.y() == y) {
            return true;
        }
    }
    return false;
}
 
function validPixelCoordinate(nx: number, ny: number): boolean {
    return (nx >= 0 && nx <= 4 && 
            ny >= 0 && ny <= 4) && (!isOnSnake(nx, ny));
}
// check if the (x, y) is one of the snake body's coordinate.
function isOnSnake(x: number, y: number): boolean {
    for (let body of snake) {
        if (body.x() == x && body.y() == y) {
            return true;
        }
    }
    return false;
}

function validPixelCoordinate(nx: number, ny: number): boolean {
    return (nx >= 0 && nx <= 4 && 
            ny >= 0 && ny <= 4) && (!isOnSnake(nx, ny));
}

Similarly, we need to adapt the apple-generating function to check the apple should not be on one of the snake’s body.

1
2
3
4
5
6
7
8
9
function placeNextApple() {
    let x, y;
    do {
        x = Math.randomRange(0, 4);
        y = Math.randomRange(0, 4);
    } while (isOnSnake(x, y));
    apple.goTo(x, y);
    apple.setBrightness(100);
}
function placeNextApple() {
    let x, y;
    do {
        x = Math.randomRange(0, 4);
        y = Math.randomRange(0, 4);
    } while (isOnSnake(x, y));
    apple.goTo(x, y);
    apple.setBrightness(100);
}

Reset the Game

One change to the resetGame function is to delete snake body before re-initialize a new one. Delete a sprite will make remove the sprite object from memory and it will neither interact with other sprite objects nor be shown on the LED scren.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function resetGame() {
    game.setScore(0);
    score = 0;
    direction = 0;
    px = 0;
    py = 0;
    // release the memory - by freeing up the snake body
    for (let s of snake) {
        s.delete();
    }
    snake = initSnake([px, py, px + 1, py]);
    placeNextApple();
    game.resume();
}
function resetGame() {
    game.setScore(0);
    score = 0;
    direction = 0;
    px = 0;
    py = 0;
    // release the memory - by freeing up the snake body
    for (let s of snake) {
        s.delete();
    }
    snake = initSnake([px, py, px + 1, py]);
    placeNextApple();
    game.resume();
}

Moving Forward the Snake

When the snake moves, we can push the new pixel coordinates to the front of the array using the Javascript’s array unshift method. Then, we need to delete the last one via the pop() method.

1
2
3
4
5
6
7
8
9
10
11
function moveForward() {
    let dx = dxOffset[direction];
    px += dx[0];
    py += dx[1];
    if (!validPixelCoordinate(px, py)) {
        gameOver();
    }
    snake.unshift(game.createSprite(px, py));
    let last = snake.pop();
    last.delete();
}
function moveForward() {
    let dx = dxOffset[direction];
    px += dx[0];
    py += dx[1];
    if (!validPixelCoordinate(px, py)) {
        gameOver();
    }
    snake.unshift(game.createSprite(px, py));
    let last = snake.pop();
    last.delete();
}

Snake Body Grows

This part can be dealt with in the main game loop. When collision is detected between snake head and the apple, we can push a new pixel to the tail of the body (duplication).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
basic.forever(function () {
    if (game.isGameOver()) {
        return;
    }
    let delay = Math.max(100, 1000 - score * 50);
    basic.pause(delay);
    //letComputerPlay();
    moveForward();
    if (snake[0].isTouching(apple)) { // snake eats apple
        // snake body does not grow after maxLength
        if (snake.length < maxLength) {
            // duplicate the tail
            snake.push(snake[snake.length - 1]);
        }
        score++;
        placeNextApple();
    }
})
basic.forever(function () {
    if (game.isGameOver()) {
        return;
    }
    let delay = Math.max(100, 1000 - score * 50);
    basic.pause(delay);
    //letComputerPlay();
    moveForward();
    if (snake[0].isTouching(apple)) { // snake eats apple
        // snake body does not grow after maxLength
        if (snake.length < maxLength) {
            // duplicate the tail
            snake.push(snake[snake.length - 1]);
        }
        score++;
        placeNextApple();
    }
})

Final Microbit Snake Game

The Full Source Code and Microbit Simulator is here: https://makecode.microbit.org/_2qyYchHfsDDC

The AI code is still the same, however, as the strategy is still greedy, which will fail the snake at sometime.

The Microbit Snake Game with Greedy Algorithm AI Engine: https://makecode.microbit.org/_9T89xWaMkRV4

Video of Microbit Playing Snake Game with Greedy Strategy:

Play Snake Game

Want to play the snake game? Here are two good options:

  • Simple Snake Game in Javascript
  • Chrome Extension: Snake Game

–EOF (The Ultimate Computing & Technology Blog) —

推荐阅读:
一个数的近似值是20万,这个数最大是多少?最小是多少?  图中有多少个长方形  一块正方形的纸板(如图),先剪下宽7厘米的长方形  数学题:9个队员进行单循环制猜丁壳比赛  数学题:有三位登山者要攀登一座荒无人烟的大山。出发时每人只能携带够6天的食物  数学题:一个多位数四舍五入后是1亿,这个数最小是多少?  新网站优化对于一个企业来说到底有多重要  大学生如何在渗透测试行业立足  网站渗透测试中的历程经验记录分析  百度推出的“鸿雁计划”到底有什么用?对站长有哪些影响? 
评论列表
添加评论