Skip to content

10. King, Queen, Rook

phu54321 edited this page Jan 24, 2018 · 13 revisions

Table of Contents generated with DocToc

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

In this chapter, we will do these things.

  • Implement rest of the pieces, King, Queen, and Rook.
  • Type-casting variables to objects.

King, Queen, and Rook

We will implement King, Queen, and Rook in this chapter, just like how we made Bishop and Knight at the last chapter.

king.eps looks like this. This just looks like the code of the knight.

import tile;
import loc;
import pieceRule.common;

function getPossibleDestination(player, x, y) {
    const dxTable = [-1, 0, 1, -1, 1, -1, 0, 1];
    const dyTable = [-1, -1, -1, 0, 0, 1, 1, 1];
    for(var direction = 0 ; direction < 8 ; direction++) {
        const dx, dy = dxTable[direction], dyTable[direction];
        if (common.canPieceGoTo(player, x + dx, y + dy)) {
            common.placeScourge(player, x + dx, y + dy);
        }
    }
}

King can't move to the place where it could be checked, but this will be covered when we later discuss about checks and checkmates.

rook.eps looks like this.

import tile;
import loc;
import pieceRule.common;


function getPossibleDestination(player, x, y) {
    const dxTable = [0, 1, 0, -1];
    const dyTable = [-1, 0, 1, 0];
    var dx, dy;
    for(var direction = 0 ; direction < 4 ; direction++) {
        dx, dy = dxTable[direction], dyTable[direction];
        var x1, y1 = x + dx, y + dy;

        while(common.inBounds(x1, y1)) {
            if(tile.tile(x1, y1) == 0) common.placeScourge(player, x1, y1);
            else if(tile.getTileOccupiedPlayer(x1, y1) != player) {
                common.placeScourge(player, x1, y1);
                break;
            }
            else break;
            x1 += dx;
            y1 += dy;
        }
    }
}

queen.eps looks like this.

import tile;
import loc;
import pieceRule.common;


function getPossibleDestination(player, x, y) {
    const dxTable = [-1, -1, -1, 0, 1, 1, 1, 0];
    const dyTable = [-1, 0, 1, 1, 1, 0, -1, -1];
    var dx, dy;
    for(var direction = 0 ; direction < 8 ; direction++) {
        dx, dy = dxTable[direction], dyTable[direction];
        var x1, y1 = x + dx, y + dy;

        while(common.inBounds(x1, y1)) {
            if(tile.tile(x1, y1) == 0) common.placeScourge(player, x1, y1);
            else if(tile.getTileOccupiedPlayer(x1, y1) != player) {
                common.placeScourge(player, x1, y1);
                break;
            }
            else break;
            x1 += dx;
            y1 += dy;
        }
    }
}

Note that all getPossibleDestination variants now have a same name. Now, afterTriggerExec looks like this.

function afterTriggerExec() {
    const unitType, selX, selY = sel.getSelectedUnit();
    if (unitType != -1) {
        if(unitType == $U('Zerg Scourge')) {
            piece.removePiece(selX, selY);
            piece.placePiece(selX, selY, beforeUnitType, beforeUnitPlayer);
            piece.removePiece(beforeUnitX, beforeUnitY);
            RemoveUnit("Zerg Scourge", Force3);

            beforeUnitX = -1;
            beforeUnitY = -1;
        }
        else if(beforeUnitX != selX || beforeUnitY != selY) {
            const tileVal = tile.tile(selX, selY);
            beforeUnitPlayer = tileVal / 1000;
            beforeUnitType = tileVal % 1000;
            beforeUnitX = selX;
            beforeUnitY = selY;
            RemoveUnit("Zerg Scourge", Force3);

            if (unitType == $U("Pawn")) pawn.getPossibleDestination(beforeUnitPlayer, selX, selY);
            else if(unitType == $U("Knight")) knight.getPossibleDestination(beforeUnitPlayer, selX, selY);
            else if(unitType == $U("Bishop")) bishop.getPossibleDestination(beforeUnitPlayer, selX, selY);
            else if(unitType == $U("King")) king.getPossibleDestination(beforeUnitPlayer, selX, selY);
            else if(unitType == $U("Queen")) queen.getPossibleDestination(beforeUnitPlayer, selX, selY);
            else if(unitType == $U("Rook")) rook.getPossibleDestination(beforeUnitPlayer, selX, selY);
        }
    }
    else {
        RemoveUnit("Zerg Scourge", Force3);
    }
    SetInvincibility(Enable, "(any unit)", AllPlayers, 'Anywhere');
}

Result looks like this.

King, Queen, Rook added

Refactoring: Queen, Rook, and Bishop code

Since queen, rook, and bishop shares most of their code, I think we can refactor them. Their code looks like this.

function getPossibleDestination(player, x, y) {
    const dxTable = /*<a table>*/;
    const dyTable = /*<a table>*/;
    var dx, dy;
    for(var direction = 0 ; direction < /*length*/ ; direction++) {
        dx, dy = dxTable[direction], dyTable[direction];
        var x1, y1 = x + dx, y + dy;

        while(common.inBounds(x1, y1)) {
            if(tile.tile(x1, y1) == 0) common.placeScourge(player, x1, y1);
            else if(tile.getTileOccupiedPlayer(x1, y1) != player) {
                common.placeScourge(player, x1, y1);
                break;
            }
            else break;
            x1 += dx;
            y1 += dy;
        }
    }
}

So we again make this repeating code to function that receives dxTable, dyTable, and the number of directions. After this refactoring, bishop.eps should look like this.

function getPossibleDestination(player, x, y) {
    const dxTable = [-1, -1, 1, 1];
    const dyTable = [-1, 1, 1, -1];
    common.getQRBPossibleDestination(dxTable, dyTable, 4, player, x, y);
}

getQRBPossibleDestination accepts dxTable, dyTable, and the number of directions as parameters. Its code is like this.

/**
 * Common function for queen, rook and bishop.
 */
function getQRBPossibleDestination(dxTable: EUDArray, dyTable: EUDArray, dirn, player, x, y) {
    var dx, dy;
    for(var direction = 0 ; direction < dirn ; direction++) {
        dx, dy = dxTable[direction], dyTable[direction];
        var x1, y1 = x + dx, y + dy;

        while(inBounds(x1, y1)) {
            if(tile.tile(x1, y1) == 0) placeScourge(player, x1, y1);
            else if(tile.getTileOccupiedPlayer(x1, y1) != player) {
                placeScourge(player, x1, y1);
                break;
            }
            else break;
            x1 += dx;
            y1 += dy;
        }
    }
}

See the line function getQRBPossibleDestination(dxTable: EUDArray, dyTable: EUDArray, dirn, player, x, y)? dxTable: EUDArray means that we will assume that dxTable is EUDArray. You can pass a plain number like 1 to getQRBPossibleDestination function, but then this function won't work at all. Same goes for dyTable. Rest of the code is exactly same as what we've covered in chapter 9.

queen.eps and rook.eps now look like this.

import tile;
import loc;
import pieceRule.common;

// pieceRules/Queen.eps
function getPossibleDestination(player, x, y) {
    const dxTable = [-1, -1, -1, 0, 1, 1, 1, 0];
    const dyTable = [-1, 0, 1, 1, 1, 0, -1, -1];
    common.getQRBPossibleDestination(dxTable, dyTable, 8, player, x, y);
}

// pieceRules/Rook.eps
function getPossibleDestination(player, x, y) {
    const dxTable = [0, 1, 0, -1];
    const dyTable = [-1, 0, 1, 0];
    common.getQRBPossibleDestination(dxTable, dyTable, 4, player, x, y);
}

The code runs the same, but we've reduced a considerable amount of code here, and our code became much clearer.

So this is the end of this chapter. Pieces works as expected, but there are some rules that aren't implemented yet. Castling and en passant for examples. In the next chapter, we will discuss castling and en passant. See you next!