Skip to content

06. Where can a pawn move

phu54321 edited this page Feb 10, 2018 · 5 revisions

Table of Contents generated with DocToc

The material used in this chapter can be downloaded at here.

In the previous chapter, we figured out the coordinate of the selected pawn. In this chapter, we will figure out the possible destination of the selected pawn, and update the map accordingly.

A pawn can either move a step forward or step diagonally to capture the opponent's piece.

Movement of pawn

We'll place a Scourge on the place where a pawn can go. We'll think about P8 later. Right now let's focus on P7-side only. This function places scourge on each cell which a selected pawn at (x, y) can move to. This function is for Player 7 only.

function getP7PossiblePawnDestination(x, y) {
    if (tile(x, y + 1) is empty) {
        move1x1Loc(x, y + 1);
        CreateUnit(1, 'Zerg Scourge', '1x1', P7);
    }

    if (tile(x - 1, y + 1) is occupied by enemy) {
        move1x1Loc(x - 1, y + 1);
        CreateUnit(1, 'Zerg Scourge', '1x1', P7);
    }

    if (tile(x + 1, y + 1) is occupied by enemy) {
        move1x1Loc(x + 1, y + 1);
        CreateUnit(1, 'Zerg Scourge', '1x1', P7);
    }
}

This function gets the position of the selected pawn as an input. Note that for x, 0 means left and 7 means right, and for y, 0 means top and 7 means bottom. So y + 1 means a line below the (x, y), and x + 1 means a line right to the (x, y) To complete this function, we have to check if specific tile is occupied by enemy, occupied by player, or empty. We'll create a helper function to help us with it.

function getTileOccupiedPlayer(x, y) {
    return tile(x, y) / 1000 % 10;
}

Remember setTile(x, y, unitType + player * 1000 + unitIndex * 10000);? tile(x, y) / 1000 % 10 extracts the player part out of this expression.

We can now complete the getP7PossiblePawnDestination function. We'll also add range checks for x and y coordinates. For example, if y + 1 >= 8, then the cell (x, y + 1) is out of bounds. X, Y coordinate should be in 0-7. The same thing also goes for x coordinates.

function getP7PossiblePawnDestination(x, y) {
    if (y + 1 < 8) {
        if (tile(x, y + 1) == 0){
            move1x1Loc(x, y + 1);
            CreateUnit(1, 'Zerg Scourge', '1x1', P7);
        }

        if (x > 0 && getTileOccupiedPlayer(x - 1, y + 1) == $P8) {
            move1x1Loc(x - 1, y + 1);
            CreateUnit(1, 'Zerg Scourge', '1x1', P7);
        }

        if (x < 7 && getTileOccupiedPlayer(x + 1, y + 1) == $P8) {
            move1x1Loc(x + 1, y + 1);
            CreateUnit(1, 'Zerg Scourge', '1x1', P7);
        }
    }
}

Here we saw a new symbol &&. A && B creates a new condition 'both conditions A and B are satisfied'. So if (x > 0 && getTileOccupiedPlayer(x - 1, y + 1) == $P8) means if x is greater than 0 (x - 1 >= 0) and the occupying player of the tile (x-1, y+1) is P8.

We're checking if the cell is empty by calling tile(x, y + 1) == 0. This is a property of EUDArray. Every EUDArray item's default value is 0, so comparing the item's value with 0 means checking if there were no assigned value to the EUDArray index. That means, that the corresponding chess cell is empty! When we later implement moving a piece we will call setTile(x, y, 0) for previous piece (x, y) coordinates, so we can always check if the cell is empty by comparing its array value with 0.

$P8 means 7. In epScript P8 means Player 8, and to compare that with numbers (which is the result of getTileOccupiedPlayer) we need to tell euddraft to interpret P8 as a number, not as a player. So we append $ in front of the P8.

function afterTriggerExec() {
    const selectedUnitPointer = dwread_epd_safe(EPD(0x6284E8));
    if (selectedUnitPointer != 0) {
        const selectedUnitIndex = (selectedUnitPointer - 0x59CCA8) / 336;
        const x, y = getUnitPlace(selectedUnitIndex);
        SetResources(AllPlayers, SetTo, x, Ore);
        SetResources(AllPlayers, SetTo, y, Gas);
        RemoveUnit("Zerg Scourge", P7);
        getP7PossiblePawnDestination(x, y);
    }
    else {
        SetResources(AllPlayers, SetTo, 10000, OreAndGas);
    }
    SetInvincibility(Enable, "(any unit)", AllPlayers, 'Anywhere');
}

With this code, when you select a pawn, then the position where the pawn can move is highlighted with a scourge.

Output

En passant won't be covered in this chapter. En passant diverges from normal movement of pawn, and we need more than just an in-memory board to implement that. En passant will be implemented as we discuss castling.

The same also goes for player 8. For simplicity, we will make getP7PossiblePawnDestination accept the player id and create player according to it. Also, we will make that each piece can go two-row front when the piece hasn't moved before. We will check this condition by comparing the piece's y coordinate with its starting coordinate. To summarize this all, the code now looks like this.

function getPossiblePawnDestination(player, x, y) {
    var opponentPlayer, yStart, dy;
    if (player == $P7) {
        opponentPlayer = $P8;
        yStart = 1;
        dy = 1;
    }
    else {
        opponentPlayer = $P7;
        yStart = 6;
        dy = -1;
    }

    if (0 <= y + dy && y + dy < 8) {
        if (tile(x, y + dy) == 0){
            move1x1Loc(x, y + dy);
            CreateUnit(1, 'Zerg Scourge', '1x1', player);
        }

        if (y == yStart && tile(x, y + dy) == 0 && tile(x, y + 2 * dy) == 0) {
            move1x1Loc(x, y + 2 * dy);
            CreateUnit(1, 'Zerg Scourge', '1x1', player);
        }

        if (x > 0 && getTileOccupiedPlayer(x - 1, y + dy) == opponentPlayer) {
            move1x1Loc(x - 1, y + dy);
            CreateUnit(1, 'Zerg Scourge', '1x1', player);
        }

        if (x < 7 && getTileOccupiedPlayer(x + 1, y + dy) == opponentPlayer) {
            move1x1Loc(x + 1, y + dy);
            CreateUnit(1, 'Zerg Scourge', '1x1', player);
        }
    }
}

opponentPlayer means an opposing player to the player, and dy means the forward direction of each player. For P7 y + 1 is in front of the y'th row, so dy is 1. For P8 dy is -1. yStart means a pawn's starting row number.

afterTriggerExec should also change accordingly, like this.

    if (selectedUnitPointer != 0) {
        const selectedUnitIndex = (selectedUnitPointer - 0x59CCA8) / 336;
        const x, y = getUnitPlace(selectedUnitIndex);
        SetResources(AllPlayers, SetTo, x, Ore);
        SetResources(AllPlayers, SetTo, y, Gas);
        RemoveUnit("Zerg Scourge", P7);  // Remove scourge for both color
        RemoveUnit("Zerg Scourge", P8);  // Remove scourge for both color
        getPossiblePawnDestination(getTileOccupiedPlayer(x, y), x, y);  // Pass piece's player to getPossiblePawnDestination.
    }

The game now looks like this.

Pawn's movement

Congratulations! We've calculated where the pawn can move without any tricky location moving tricks. You'll now see why I spent so much time implementing an in-memory board. In-memory board simplifies the code very well.

On the next chapter, we will move the pawn when the scourge got selected. See you soon!