制作四排 - 第4部分:赢得方式和乏味的联系
#javascript #html #教程 #game

介绍

在上一篇博客文章中,您实现了播放器移动。

现在您准备检查胜利并吸引游戏。

检查获胜

在四排,有三种方法可以让玩家赢得比赛:

  1. 位置4个连续令牌水平
  2. 位置4个连续令牌垂直
  3. 位置4个连续的令牌。

由于游戏中的板是使用一系列数组创建的,因此您可以通过迭代游戏板来创建连续令牌的支票,检查每个方向的胜利线。

寻找胜利线

虽然赢得胜利的方向是不同的,但我们仍然在每个方向上检查相同数字的连续令牌,因此,我们可以创建一种为您检查胜利线的方法。

在称为tryFindWinLineFourInARowGame类中添加静态方法:

/**
 *
 * options: {
 *     startRowIndex,
 *     startColumnIndex,
 *     rowCountStep,
 *     columnCountStep
 * }
 *
 * Any missing options will be 0 by default.
 */
static tryFindWinLine(board, options) {
    // If `options` is null/undefined, set it's value to an empty object.
    options = options || {};

    let config = {
        startRowIndex: options.startRowIndex || 0,
        startColumnIndex: options.startColumnIndex || 0,
        rowCountStep: options.rowCountStep || 0,
        columnCountStep: options.columnCountStep || 0
    };

    let count = 0;
    let tokenToCheck = Constants.BoardToken.NONE;
    let winLine = [];

    for (let i = 0; i < Constants.BoardDimensions.WIN_LINE_LENGTH; i++) {
        let row = config.startRowIndex + config.rowCountStep * i;
        let column = config.startColumnIndex + config.columnCountStep * i;

        if (FourInARowGame.checkIfOutOfBounds(row, column)) {
            break;
        }

        let currentToken = board[row][column];
        if (currentToken === Constants.BoardToken.NONE) {
            break;
        }

        if (tokenToCheck === Constants.BoardToken.NONE) {
            tokenToCheck = currentToken;
        }

        if (currentToken === tokenToCheck) {
            count++;
        }

        winLine.push({ row: row, column: column });
    }

    if (count === Constants.BoardDimensions.WIN_LINE_LENGTH) {
        return {
            winLine: winLine,
            winner: FourInARowGame.boardTokenToPlayerColor(tokenToCheck),
        };
    }

    return {
        winLine: []
    };
}

上面的方法计算从起始位置检查连续令牌的检查。该方法的行为由传递的options对象控制。

startRowIndexstartColumnIndex设置了起始位置。

rowCountStepcolumnCountStep控制方向胜利线。

还有另外两种在checkForWinLine()中调用的方法。

FourInARowGame.checkIfOutBounds()检查指定位置是否超出边界。这使我们能够早日停止检查胜利线。

将静态方法添加到FourInARowGame类:

static checkIfOutOfBounds(row, column) {
    return row < 0
        || row > Constants.BoardDimensions.ROWS
        || column < 0
        || column > Constants.BoardDimensions.COLUMNS;
}

另一种方法是另一种称为FourInARow.boardTokenToPlayerColor()的静态方法,它将板令牌值转换为播放器颜色值。

将其添加到FourInARowGame类:

static boardTokenToPlayerColor(boardToken) {
    switch (boardToken) {
        case Constants.BoardToken.YELLOW:
            return Constants.PlayerColor.YELLOW;
        case Constants.BoardToken.RED:
            return Constants.PlayerColor.RED;
        default:
            return Constants.PlayerColor.NONE;
    }
}

检查各个方向的胜利

现在您已经在板上创建了Win Wines,现在您准备开始检查玩家是否赢得了游戏。

为称为checkForWin()FourInARowGame类创建一个新的静态方法:

// Each win line is an array of board position coordinates:
// e.g: winLine = [{row: 0, column: 0}, {row: 0, column: 1}, {row: 0, column : 2}, {row: 0, column: 3}]
static checkForWin(board) {
    // Starts from bottom left of the board and ends on top right of board
    for (let columnIndex = 0; columnIndex < Constants.BoardDimensions.COLUMNS; columnIndex++) {
        for (let rowIndex = Constants.BoardDimensions.ROWS - 1; rowIndex > -1; rowIndex--) {
            // Check for vertical win
            let verticalWinCheckResult = FourInARowGame.tryFindWinLine(board, {
                startRowIndex: rowIndex,
                startColumnIndex: columnIndex,
                rowCountStep: -1,
            });

            if (verticalWinCheckResult.winner) {
                return verticalWinCheckResult;
            }

            let horizontalWinCheckResult = FourInARowGame.tryFindWinLine(board, {
                startRowIndex: rowIndex,
                startColumnIndex: columnIndex,
                columnCountStep: -1,
            });

            if (horizontalWinCheckResult.winner) {
                return horizontalWinCheckResult;
            }

            let leftDiagonalWinCheck = FourInARowGame.tryFindWinLine(board, {
                startRowIndex: rowIndex,
                startColumnIndex: columnIndex,
                rowCountStep: -1,
                columnCountStep: -1
            });

            if (leftDiagonalWinCheck.winner) {
                return leftDiagonalWinCheck;
            }

            let rightDiagonalWinCheck = FourInARowGame.tryFindWinLine(board, {
                startRowIndex: rowIndex,
                startColumnIndex: columnIndex,
                rowCountStep: -1,
                columnCountStep: 1
            });

            if (rightDiagonalWinCheck.winner) {
                return rightDiagonalWinCheck;
            }
        }
    }

    return {
        winLine: [],
        winner: Constants.PlayerColor.NONE
    };
}

以上方法从每个董事会位置检查所有方向上的胜利线。然后,它返回由a winLine组成的WinCheckResult,它是连续董事会位置和winner的列表,其中包含谁赢得了游戏。

请注意如何为每个方向设置rowCountStepcolumnCountStep值。

允许球员获胜

现在您已经创建了一种检查获胜的方法,现在可以检测到胜利并相应地更新游戏的状态。

更新evaluateGame()方法,以便它处理检测到的胜利:

evaluateGame(board) {
    let winCheckResult = FourInARowGame.checkForWin(board);

    if (winCheckResult.winner !== Constants.PlayerColor.NONE) {
        this.status = Constants.GameStatus.WIN;
        return {
            board: board,
            winner: winCheckResult.winner,
            status: {
                value: Constants.MoveStatus.WIN
            },
            winLine: winCheckResult.winLine
        };
    }

    // From this point, we can assume that a successful move was made and the game will
    // continue on.
    return {
        board: board,
        winner: Constants.PlayerColor.NONE,
        status: {
            value: Constants.MoveStatus.SUCCESS
        },
        winLine: []
    };
}

您还需要做的一件事是更新playMove(),以便在游戏结束时重新评估董事会:

playMove(columnIndex) {
    switch (this.status) {
        case Constants.GameStatus.START:
            this.status = Constants.GameStatus.IN_PROGRESS;
            break;
        case Constants.GameStatus.DRAW:
        case Constants.GameStatus.WIN:
            // The game is over at this point so
            // re-evaluate the latest board, returning the same game status
            // and board details.
            return this.evaluateGame(this.currentBoard);
        default:
            break;
    }

    let moveResults = this.performMove(columnIndex);

    // Do not change player turn if move is invalid
    if (moveResults.status !== Constants.MoveStatus.INVALID && moveResults.status.value !== Constants.MoveStatus.INVALID) {
        this.currentTurn = this.currentTurn === Constants.PlayerColor.YELLOW
            ? Constants.PlayerColor.RED
            : Constants.PlayerColor.YELLOW;
    }
    return moveResults;
}

随时在浏览器的控制台中测试它。

当动作导致胜利时,输出中的移动结果对象将具有status字段,值为winwinner将设置为RedYellow,而winLine将充满位置。

>

随后对playMove()的呼叫每次都会产生相同的输出。游戏毕竟已经结束。

检查抽奖

并非每个四轮比赛都以胜利结束。在某些情况下,董事会没有赢家。在这些情况下,游戏以平局结束。

您现在将添加检测抽奖的功能。

首先,在FourInARowGame类中添加称为checkForFilledBoard()的静态方法:

static checkForFilledBoard(board) {
    for (let j = 0; j < board.length; j++) {
        let boardColumn = board[j];
        for (let i = 0; i < boardColumn.length; i++) {
            let boardPosition = boardColumn[i];
            if (boardPosition === Constants.BoardToken.NONE) {
                return false;
            }
        }
    }

    return true;
}

现在,对evaluateGame()方法进行另一个更新

evaluateGame(board) {
    let winCheckResult = FourInARowGame.checkForWin(board);

    if (winCheckResult.winner !== Constants.PlayerColor.NONE) {
        this.status = Constants.GameStatus.WIN;
        return {
            board: board,
            winner: winCheckResult.winner,
            status: {
                value: Constants.MoveStatus.WIN,
            },
            winLine: winCheckResult.winLine,
        };
    }

    // If board is full right now, we can assume the game to be a draw
    // since there weren't any winning lines detected.
    if (FourInARowGame.checkForFilledBoard(board)) {
        this.status = Constants.GameStatus.DRAW;

        return {
            board: board,
            winner: Constants.PlayerColor.NONE,
            status: {
            value: Constants.MoveStatus.DRAW,
            },
            winLine: [],
        };
    }

    // From this point, we can assume that a successful move was made and the game will
    // continue on.
    return {
        board: board,
        winner: Constants.PlayerColor.NONE,
        status: {
            value: Constants.MoveStatus.SUCCESS,
        },
        winLine: [],
    };
}

可以随意在浏览器的控制台中测试自己的自我。输出中返回的MoveResult对象将具有一个status字段,值为draw

如果您遇到任何错误或意外行为,请仔细阅读您到目前为止编写的代码,然后再次阅读此帖子。

否则,恭喜!现在,您已经在JavaScript中实现了四英寸游戏的核心逻辑。

在此博客系列的即将发布的帖子中,您将在游戏中为您的游戏前端进行HTML5帆布的工作,并将您创建的状态机对象集成到其中!

现在全部ð!