介绍
在上一篇博客文章中,您实现了播放器移动。
现在您准备检查胜利并吸引游戏。
检查获胜
在四排,有三种方法可以让玩家赢得比赛:
- 位置4个连续令牌水平
- 位置4个连续令牌垂直
- 位置4个连续的令牌。
由于游戏中的板是使用一系列数组创建的,因此您可以通过迭代游戏板来创建连续令牌的支票,检查每个方向的胜利线。
寻找胜利线
虽然赢得胜利的方向是不同的,但我们仍然在每个方向上检查相同数字的连续令牌,因此,我们可以创建一种为您检查胜利线的方法。
在称为tryFindWinLine
的FourInARowGame
类中添加静态方法:
/**
*
* 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
对象控制。
startRowIndex
和startColumnIndex
设置了起始位置。
rowCountStep
和columnCountStep
控制方向胜利线。
还有另外两种在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
的列表,其中包含谁赢得了游戏。
请注意如何为每个方向设置rowCountStep
和columnCountStep
值。
允许球员获胜
现在您已经创建了一种检查获胜的方法,现在可以检测到胜利并相应地更新游戏的状态。
更新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
字段,值为win
,winner
将设置为Red
或Yellow
,而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帆布的工作,并将您创建的状态机对象集成到其中!
现在全部ð!