Simple jigsaw puzzle with JavaScript

Time:2022-5-24


This article mainly explains how to use nativeJavaScript

To achieve a simple puzzle game.

Online experience address: Puzzle

1、 Basic logic of game

If you want to use a language to develop games, you must first understand how to use this language to realize some basic logic, such as image rendering, interactive processing, timer and so on.

1. Graphic drawing

Graphic drawing is the basis of everything. It is used hereJavaScriptIncanvasDraw on. That is, first inhtmlCreate incanvasElement, and then inJavaScriptIn, get the element through ID, andcanvasGet the corresponding contextcontextTo prepare for subsequent drawings.

<canvas id="background" width="450px" height="450px"></canvas>
var background = document.getElementById("background");
var context = background.getContext('2d');

PasscontextYesdrawImageMethod can draw pictures, which are encapsulated here:

Note:Here, you need to wait until the picture is loaded, that is, inonloadCall indrawImageMethod, otherwise the drawing will fail.

var drawImageItem = function(index, position) {
    var img = new Image();
    img.src = './image/dog_0' + String(index+1) + '.jpg';
    img.onload = () => {
        var rect = rectForPosition(position);
        context.drawImage(img, rect[0], rect[1], rect[2], rect[3]);
    }
}

After drawing the picture, we also need to refresh the view dynamically, otherwisecanvasIt’s just a static picture. If it is a simple graphic refresh, just redraw it in the original position and overwrite it. But sometimes we just need to erase the existing graphics without drawing new ones. For example, in a jigsaw puzzle, after moving a square to another position, you need to empty the original position.

PasscontextYesclearRectMethod can achieve the purpose of cleaning. The following is the clearcanvasCode of an area of:

var originRect = rectForPosition(origin);
context.clearRect(originRect[0], originRect[1], originRect[2], originRect[3]);

2. Event handling

With the drawing of graphics, we also need to deal with the player’s input events, and then decide when to refresh the view according to the input events. Input events can be divided into three types: touch screen events on the mobile phone; On the PC, there are mouse and keyboard events.

JavaScriptThe monitoring of touch screen and mouse click in is the same, both throughcanvasYesonclickEvent, as follows:

//Screen click
background.onclick = function(e) {
};

We can passe.offsetX 、 e.offsetYTo get the touch pointcanvasPosition in the.

Note:canvasThe coordinate origin of is in the upper left corner, that is, the coordinate of the upper left corner is(0, 0) 。

The key click of the keyboard is throughdocumentYesonkeyup 、 onkeydownAnd other events.  onkeyupIt refers to the lifting event of the keyonkeydownRefers to the press event of the key. We can passkeyCodeKnow the specific key currently, and then process different logic according to different keys, as follows:

If (event. Keycode = = '37') {// left
    // do something
}Else if (event. Keycode = = '38') {// on
    // do something
}Else if (event. Keycode = = '39') {// right
    // do something
}Else if (event. Keycode = = '40') {// next
    // do something
}

3. Timer

Sometimes, in addition to refreshing the view when players input, they also need to refresh the view at regular intervals. For example, in a greedy Snake game, you need to refresh the position of the snake every other period of time.

At this time, we need a timer to execute a section of code to refresh the view at regular intervals. We passsetIntervalMethod to realize the timer function:

setInterval("run()", 100);

The above code means to execute every 100 millisecondsrunMethod.

2、 Basic logic of jigsaw puzzle

With the basic logic of the game, let’s see how to realize the logic of the puzzle.

1. Generate random sequence

Because not any sequence can be restored by translation, we can’t simply generate a random sequence. For example1、0、2、3、4、5、6、7、8This sequence, no matter how it is translated, cannot be restored.

The method adopted here is: four reducible sequences are preset, one of the four sequences is randomly selected, and then the sequence is simulated and translated in several steps. So as to ensure the diversity of the initial sequence and the reducibility of the sequence as much as possible. The specific codes are as follows:

var setupRandomPosition = function() {
    var list1 = [4, 3, 2, 8, 0, 7, 5, 6, 1];
    var list2 = [2, 0, 5, 6, 8, 7, 3, 1, 4];
    var list3 = [3, 7, 2, 4, 1, 6, 8, 0, 5];
    var list4 = [3, 2, 4, 1, 7, 6, 5, 0, 8];
    var lists = [list1, list2, list3, list4];

    imageIndexForPosition = lists[parseInt(Math.random() * 4)];

    //Get empty position
    var emptyPosition = 0;
    for (var i = imageIndexForPosition.length - 1; i >= 0; i--) {
        if (imageIndexForPosition[i] == lastIndex()) {
            emptyPosition = i;
            break;
        }
    }
    background.emptyPosition = emptyPosition;

    //Random movement times
    var times = 10;
    while (times--) {
        //Get the random number and decide which position of the empty space to move
        var direction = parseInt(Math.random() * 4);

        var target = -1;
        if (direction == 0) {
            target = topOfPosition(emptyPosition);  //  upper
        } else if (direction == 1) {
            target = leftOfPosition(emptyPosition);  //  Left 
        } else if (direction == 2) {
            target = rightOfPosition(emptyPosition);  //  right
        } else if (direction == 3) {
            target = bottomOfPosition(emptyPosition);  //  lower
        }
        If (target < 0 | target > lastindex()) {// the position is illegal. Continue the next cycle
            continue;
        }
        var result = moveImageIfCanAtPosition(target);
        If (result > = 0) {// if the move succeeds, update the empty position
            emptyPosition = target;
        }
    }
}

2. Determine whether the box can be moved

When saving the sequence, the nine numbers 0 ~ 8 are used to save, and the blank box is the position of the number 8. Therefore, the only condition to judge whether it can be moved is whether the value of the target position is 8. The code is as follows:

var isPositionEmpty = function(position) {
    if (position < 0 || position > lastIndex()) {
        return false;
    } 
    if (imageIndexForPosition[position] == lastIndex()) {
        return true;
    } else {
        return false;
    }
}

AbovelastIndex()The value of is 8.

3. Block movement

The implementation of box movement is very simple. First clear the graphics in the old position, and then draw in the new position.

var refreshImagePositions = function(origin, target) {
    var originRect = rectForPosition(origin);
    context.clearRect(originRect[0], originRect[1], originRect[2], originRect[3]);
    drawImageItem(imageIndexForPosition[target], target);
}

4. Check whether it is completed

Check whether the pattern has been restored. You only need to traverse the array once to see whether it is orderly.

var checkIfFinish = function() {
    for (var index = 0; index < imageIndexForPosition.length; index++) {
        if (index != imageIndexForPosition[index]) {
            return false;
        }
    }
    return true;
}

5. Interactive event masking

After the pattern is restored, we don’t want the player to move the box through the keyboard or mouse. At this time, we need to shield the interactive events.

Only one flag bit is required to achieve this purpose:

//Screen click
background.onclick = function(e) {
    if (isFinish) {
        return;
    }

    // do something
};

//Keyboard button events
document.onkeyup = function(event) {
    if (isFinish) {
        return;
    }

    // do something
}

When the pattern is restored, the flag bitisFinishWill be set totrueThen, add judgment at the beginning of the response event of clicking on the screen and keyboard button. If it has ended, do not continue to follow the logic of box movement.

Original link:https://www.jianshu.com/p/e53…