“How I Made a Turn Based Game in React” Part 2: Turn-Based Logic

Andrew Rivera
4 min readMar 25, 2019

--

This blog post is part 2 in a series of blog posts detailing the creation of a turn-based game of “tag” built with react.js. I recommend reading part 1 of this series of blogs before continuing. That being said… welcome back!

“Let’s Play Tag” A turn-based game built in react.js

Brief Recap

Where we last left off we built the game board, set up our game state to keep track of player locations on the board, and built the functions that would control player movement. As of right now, the human controlled player moves with the arrow key and our computer controlled players are moving randomly around the board. However, there are no turn-based mechanics in the game which was one of the deliverables we defined. So let’s build that next!

Whose Turn Is It?

You might remember in the last blog where we used redux to update a player location on the board. In order to keep track of whose turn it is I again use redux to control the state of our game. Specifically, I wanted to keep track of whose turn it is, and how many moves remain in their turn.

const initialState = {
players: 4,
it: 4,
whoseTurn: 1, // Whose turn it is
moves: 3, // How many moves remain in this turn
gameOver: false,
}

Because each player can only move three spaces during the turn we set up a reducer case that will decrease the current moves stored in the games state by one. For the purposes of this blog lets just focus on the ‘DECREASE_MOVES’ case.

const gameReducer = (state = initialState, action) => {
switch (action.type) {
case 'NEXT_TURN':
return {...state, whoseTurn: action.whoseTurn }
case 'DECREASE_MOVES':
return {...state, moves: state.moves - 1} //decreases moves
case 'RESET_MOVES':
return {...state, moves: 3}
case 'READY_PLAYER_ONE':
return {...state, whoseTurn: 1}
case 'TAG_PLAYER':
return {...state, it: action.it}
default :
return state
}
}

In order to use this case, we need an action that will implement the case that decrements the moves currently in the store. This action will be called anytime a player moves.

export function decreaseMoves (){
return {
type: "DECREASE_MOVES"
}
}

Only once a player has depleted the moves for their turn (bringing the turn counter to 0) will their turn end, and the next players turn will begin. The simplest way to do this check is to run a function after a player moves to see if “moves” is equal to zero. A turn sequence should now operate like so:

  1. A player selects a location to move to.

2. Checks are run to make sure the location is valid (not occupied by a player or out of bounds of the game board).

3. If valid the player moves to that location.

4. The moves counter in the game state is decremented.

5. A check is run to see if any moves remain.

checkMoves = () => {
if (this.props.game.moves === 0) {
console.log('this players turn is over');
this.endPlayerTurn()
} else {
console.log(`${this.props.game.moves} remaining`)
}
}

If moves remain, the player can move again. If no moves remain, then it is the next player's turn.

Switching To The Next Player

Before we can switch to the next player’s turn we need to do two things. We need to reset the move counter back up to three, and then update state to the next player number. We can do this by creating a function that will call two functions, a “resetMoves” reducer action and a “nextTurn” function.

endPlayerTurn = () => {
console.log(" ending turn")
this.props.resetMoves() // this is a reducer action
this.nextTurn()
}
nextTurn = () => {let currentPlayer = this.getPlayerObject(this.props.game.whoseTurn)
if (currentPlayer === this.props.players[this.props.players.length -1]) {
let readyNextPlayer = this.props.players[0]
return this.props.nextTurn(readyNextPlayer.id)
} else {
let playerIndex = this.props.players.findIndex( player => {
return player === currentPlayer
})
let readyNextPlayer = this.props.players[playerIndex + 1]
return this.props.nextTurn(readyNextPlayer.id)
}
}

Reset Moves

Remember that reducer we looked at earlier? Did you notice the case for “RESET_MOVES”? We can use that case by making an action to reset the moves. When the game reducer runs this case we set the moves counter back to three.

export function resetMoves (){
return {
type: "RESET_MOVES"
}
}
// excerpt from the reducer //case 'RESET_MOVES':
return {...state, moves: 3}

Next Turn

The next turn function first has to check if the current player id is equal to the total player numbers. If this is the case, then the next player is going to be the first player. Otherwise, we simply increment the player id by one, to the next player id number. In order to update the state of the game to the new player, we then pass that player number as an argument to a reducer action called “nextTurn”. This will update “whoseTurn” in the game state.

export function nextTurn(payload){
return {
type: 'NEXT_TURN',
whoseTurn: payload,
}
}
// excerpt from the reducer //case 'NEXT_TURN':
return {...state, whoseTurn: action.whoseTurn }

Conclusion

At this point in the game’s development, we now have players that can move around the board in a turn-based sequence. In upcoming blogs, I plan to go over how I built AI for the computer players, the tagging mechanics for the game, and other aspects of this games development. If you want to know when those blogs are released please click the follow button up top.

Check out this demo video of the game in its current state:

--

--

Andrew Rivera

Living life on my own terms. The coding experiments, projects, and search for computer science clarity from a Musician turned Web Developer.