Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Colin Kiama
Colin Kiama

Posted on • Edited on • Originally published atcolinkiama.com

Making Four-In-A-Row Using JavaScript - Part 3: Making Moves

Intro

In the previous blog post, you set up yourFourInARowGame class's fields.

Now you're ready to start implementing player moves and updating the state of the game accordingly.

Creating The Method

You'll start by creating a new method in theFourInARowGame class calledplayMove():

exportdefaultclassFourInARowGame{// ..staticcreateBoard(){// ..}playMove(columnIndex){return{board:this.currentBoard,winner:Constants.PlayerColor.NONE,status:{value:Constants.MoveStatus.SUCCESS,},winLine:[],};}}
Enter fullscreen modeExit fullscreen mode

Note: You're just returning mock data for now. You'll properly implement this method later on.

TheplayMove() method takes in acolumnIndex argument. From this input, a move will be attempted and the result of the move will be evaluated and returned. The code example above shows aMoveResult object being returned in theplayMove() method. AMoveResult can contain:

  • The current state of the board after a move has been played
  • The winner
  • The status of the move
  • The win line - an array of positions of 4 consecutive tokens were located if found.

Interacting With Your State Machine

Exposing Your State Machine Object

Now, to try out your code, you'll interact with your Four-In-A-Row state machine in your browser.

First, you'll need to expose the object so that the browser's console can access it. One way you can do this is by adding an instance of theFourInARowGame class to thewindow object. Let's do that right now. Replace the contents ofsrc/index.js with the following:

importFourInARowGamefrom"./FourInARowGame.js";window.fourInARowGame=newFourInARowGame();
Enter fullscreen modeExit fullscreen mode

Now, ensure that an HTTP server is running from the root of your project and navigate to the server's address in your web browser.

Open up the console in your browser's developer tools (The keyboard shortcut to open in it Google Chrome isCTRL +Shift +J) in your web browser then enter in:window.fourInARowGame. You should see an output of theFourInARowGame instance object. In Google Chrome it looks like this:

FourInARowGame {startingColor:'yellow',currentTurn:'yellow',status:'start',board:Array(6)}
Enter fullscreen modeExit fullscreen mode

There is also the option to expand the object so you can view more details about the object.

If you don't see some sort of representation of aFourInARow instance object then please go over your code and check that you've followed the instructions correctly!

Play A (Fake) Move

Now, call theplayMove() method from theFourInARow instance, in the browser console window.

First, store the state machine in an easier-to-reference variable calledgame:

letgame=window.fourInARowGame;
Enter fullscreen modeExit fullscreen mode

Then, call theplayMove() method with thecolumnIndex parameter set to0:

game.playMove(0);
Enter fullscreen modeExit fullscreen mode

Your browser's console window should output something resembling theMoveResult object you discussed earlier:

{board:Array(6),winner:'none',status:{},winLine:Array(0)}
Enter fullscreen modeExit fullscreen mode

When expanded, you get more details:

{board:Array(6),winner:'none',status:{},winLine:Array(0)}board:(6)[Uint8Array(7),Uint8Array(7),Uint8Array(7),Uint8Array(7),Uint8Array(7),Uint8Array(7)]status:{value:'success'}winLine:[]winner:"none"[[Prototype]]:Object
Enter fullscreen modeExit fullscreen mode

Now that you know the type of result that you're expecting after callingplayMove(), let's properly implement the method so that it returns real data based on the state of the board.

Implementing The Method (For Real This Time!)

Firstly, you'll update theplayMove() method in yourFourInARowGame class so that it checks the current status of the game before allowing the player to make a move.

It wouldn't make sense for a player to be able to perform a move when the game has already ended in a win or a draw.

You'll also update the current status of the game fromGameStatus.START to beingGameStatus.IN_PROGRESS.

playMove(columnIndex){switch(this.status){caseConstants.GameStatus.START:this.status=GameStatus.IN_PROGRESS;break;caseConstants.GameStatus.DRAW:caseConstants.GameStatus.WIN:// The game is over at this point so// re-evaluate the latest board, returning the same game status// and board details.// TODO: Implement this properlyconsole.log("Game already ended in win or draw. re-evaluating latest game state);        default:            break;    }}
Enter fullscreen modeExit fullscreen mode

You'll re-evaluate the latest game state when a win or draw happens later.

For now, let's continue focusing on allowing a player to make a move that changes the game's state.

You will first create a method calledperformMove():

performMove(columnIndex){// ...}
Enter fullscreen modeExit fullscreen mode

Now you need to create a copy of the current board that you can modify without changing the value ofthis.currentBoard.

To do this, you'll create a static method calleddeepBoardCopy():

staticdeepBoardCopy(oldBoard){letnewBoard=newArray(Constants.BoardDimensions.ROWS);for(letrowIndex=0;rowIndex<Constants.BoardDimensions.ROWS;rowIndex++){newBoard[rowIndex]=newUint8Array(Constants.BoardDimensions.COLUMNS);for(letcolumnIndex=0;columnIndex<Constants.BoardDimensions.COLUMNS;columnIndex++){newBoard[rowIndex][columnIndex]=oldBoard[rowIndex][columnIndex];}}returnnewBoard;}
Enter fullscreen modeExit fullscreen mode

Now store the board copy in a variable callednextBoard:

performMove(columnIndex){letnextBoard=FourInARowGame.deepBoardCopy(this.currentBoard);}
Enter fullscreen modeExit fullscreen mode

The next thing you need to do is to perform the move on the board, to do this you'll create a method calledtryPerformMove():

tryPerformMove(columnIndex,nextBoard){letisMoveValid=false;for(leti=nextBoard.length-1;i>-1;i--){letboardRow=nextBoard[i];letboardPosition=boardRow[columnIndex];if(boardPosition!==Constants.BoardToken.NONE){continue;}boardRow[columnIndex]=FourInARowGame.playerColorToBoardToken(this.currentTurn);isMoveValid=true;break;}if(!isMoveValid){return{status:Constants.MoveStatus.INVALID,};}return{status:Constants.MoveStatus.SUCCESS,board:nextBoard};}
Enter fullscreen modeExit fullscreen mode

The method above checks the column in the board the player specified from the bottom to the top for an empty position and then attempts to add the current player's token to the board position. Later it returns the result.

There is a static method here calledplayerColorToBoardToken that was used, this sets the value in the board position to a numeric value that is associated with the current player's colour.

Add it to theFourInARowGame class too:

staticcreateBoard(){// ...}staticplayerColorToBoardToken(playerColor){switch(playerColor){caseConstants.PlayerColor.YELLOW:returnConstants.BoardToken.YELLOW;caseConstants.PlayerColor.RED:returnConstants.BoardToken.RED;default:returnConstants.BoardToken.NONE;}}
Enter fullscreen modeExit fullscreen mode

Now you'll go back to theperformMove() method and set the current game board to the board in the object returned from thetryPerformMove() method:

performMove(columnIndex){letnextBoard=FourInARowGame.deepBoardCopy(this.currentBoard);letmoveAttemptResult=this.tryPerformMove(columnIndex,nextBoard);if(moveAttemptResult.status===Constants.MoveStatus.INVALID){return{board:nextBoard,winner:Constants.PlayerColor.NONE,status:{message:"Returned column is filled",value:Constants.MoveStatus.INVALID},winLine:[]}}// From this point, the board move was successful.this.currentBoard=moveAttemptResult.board;}
Enter fullscreen modeExit fullscreen mode

If the value of themoveAttemptResult.status isMoveStatus.Invalid then you return aMoveResult which has the same structure as the move data you returned in your fakeplayMove() method implementation.

Lastly, you need to create a method calledevaluateGame() which will be used to check if the status of the game has changed and then returns aMoveResult.

For now, you'll just return theMoveResult of a successful move, which also indicates that the game is still in progress.

evaluateGame(board){// From this point, you 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:[]};}
Enter fullscreen modeExit fullscreen mode

Make sure that you return the result of this method inperformMove():

performMove(columnIndex){letnextBoard=FourInARowGame.deepBoardCopy(this.currentBoard);letmoveAttemptResult=this.tryPerformMove(columnIndex,nextBoard);if(moveAttemptResult.status===Constants.MoveStatus.INVALID){return{board:nextBoard,winner:Constants.PlayerColor.NONE,status:{message:"Returned column is filled",value:Constants.MoveStatus.INVALID},winLine:[]}}// From this point, the board move was successful.this.currentBoard=moveAttemptResult.board;returnthis.evaluateGame(moveAttemptResult.board);}
Enter fullscreen modeExit fullscreen mode

Now theMoveResult will show an updated board value based on the column the player placed their token in.

To complete your updates to theplayMove() method, you'll add a few more lines to it:

playMove(columnIndex){switch(this.status){caseConstants.GameStatus.START:this.status=Constants.GameStatus.IN_PROGRESS;break;caseConstants.GameStatus.DRAW:caseConstants.GameStatus.WIN:// The game is over at this point so// re-evaluate the latest board, returning the same game status// and board details.// TODO: Implement this properlyconsole.log("Game already ended in win or draw. re-evaluating latest game state");default:break;}letmoveResult=this.performMove(columnIndex);// Only change current turn while the game is still in progress.if(moveResult.status.value===Constants.MoveStatus.SUCCESS){this.currentTurn=this.currentTurn===Constants.PlayerColor.YELLOW?Constants.PlayerColor.RED:Constants.PlayerColor.YELLOW;}returnmoveResult;}
Enter fullscreen modeExit fullscreen mode

You perform a move, update the current turn to the opposing player's turn then return the results of the move.

Testing Out Your Changes

You added quite a few changes, let's test them out.
Assuming you have a local HTTP server running at the root of the project, navigate to the server's address in your web browser and open the browser console window again.

First, enter the following line in your browser's console:

letgame=window.fourInARowGame;
Enter fullscreen modeExit fullscreen mode

Now call theplayMove() method with thecolumnIndex parameter set to1 instead of0 to add the token to the second column on the game board:

game.playMove(1);
Enter fullscreen modeExit fullscreen mode

You should see aMoveResult object returned. If you expand the object and then expand theboard field, you'll see that the value1 has been added to the bottom of the second column on the board.

If you do, congratulations!

If not, please go over this post carefully to check for mistakes you may have made.

Thanks for reading!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Game Developer
  • Location
    London
  • Work
    Game Developer
  • Joined

More fromColin Kiama

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp