Helper Function: canTileBeSelected()
This function, in its entirety, is
canTileBeSelected : function(inLayer: number, inRow: number, inColumn:
number): boolean {
return
(inLayer == 4 ||
this.state.layout[inLayer + 1][inRow][inColumn] <= 0
) &&
(inColumn === 0 || inColumn === 14 ||
this.state.layout[inLayer][inRow][inColumn - 1] <= 0 ||
this.state.layout[inLayer][inRow][inColumn + 1] <= 0
);
}.bind(inParentComponent),
Chapter 11 time for fun: BattleJong, the Client
337
The test here is simple:
• If the tile is in the topmost layer (4) OR there is no tile above it
• AND if the tile is in the first (0) or last (14) column OR there is no tile
to either the left or right of it
• THEN it’s free, ELSE it’s not
At this point, we know that the tile can be selected, so we can start the real work,
beginning with cloning the layout (since we’re going to alter it):
const layout: number[][][] = this.state.layout.slice(0);
Next, we need to see what the selected tile is, what its type is:
const currentTileValue: number = layout[inLayer][inRow][inColumn];
We need to determine that because of this next check:
if (currentTileValue <= 0) {
return;
}
This keeps the player from trying to select a blank space.
Following that, we have to grab some other values out of state:
const scores: IScores = { ...this.state.scores };
let gameState: string = this.state.gameState;
let timeSinceLastMatch: number = this.state.timeSinceLastMatch;
let selectedTiles: ISelectedTile[] = this.state.selectedTiles.slice(0);
We need to clone scores because we might be updating it. Likewise, gameState
might be changing (if they clear or dead-end the board), so that’s grabbed too. To
determine how many points they get, if they match a pair (if this is the second tile being
clicked), we will need to know how long it took, so timeSinceLastMatch is needed.
Finally, one way or another, the values in selectedTiles will be changing regardless, so
that gets cloned now too. Doing all of this now just makes the code later a bit cleaner and
removes some redundancy.
Chapter 11 time for fun: BattleJong, the Client
338
With that done, the true logic can begin. First, we need to deal with the case where
the tile they clicked was already highlighted. In that case, we want to de-highlight it.
When a tile is highlighted, its tile type value gets 1000 added to it, so that’s the basic
check:
if (currentTileValue > 1000) {
layout[inLayer][inRow][inColumn] = currentTileValue - 1000;
for (let i: number = 0; i < selectedTiles.length; i++) {
const selectedTile: ISelectedTile = selectedTiles[i];
if (selectedTile.layer == inLayer &&
selectedTile.row == inRow &&
selectedTile.column == inColumn
) {
selectedTiles.splice(i, 1);
break;
}
}
} else {
layout[inLayer][inRow][inColumn] = currentTileValue + 1000;
selectedTiles.push({ layer : inLayer, row : inRow,
column : inColumn, type : currentTileValue });
}
First, we revert the value so that it’s back to just its basic tile type value (101–142).
Next, we need to remove it from the selectedTiles array. Because this is an array and
not a keyed object, we have little choice but to examine all members of the array; find
the one that has the layer, row, and column value of the tile that was clicked; and then
splice() that element out. There are several ways this code could be written, but I chose
to just do a straight iteration over the array elements to find the match, then break out of
the loop when found (I chose this route not for any specific reason other than it seemed
simplest to me).
The else branch deals with the case where the tile wasn’t previously highlighted. In
that case, we add 1000 to the tile type value to indicate it’s highlighted and then push an
object into the selectedTiles array for this tile.
Chapter 11 time for fun: BattleJong, the Client
339
With the highlighting taken care of, we now need to see if there are two tiles selected,
in which case we have some more work to do:
if (selectedTiles.length === 2) {
if (selectedTiles[0].type === selectedTiles[1].type ||
selectedTiles[0].type == 101 ||
selectedTiles[1].type == 101
) {
If the type of both tiles matches, or if either of them is a wildcard (101), then that’s a
matched pair. When that happens, we first must clear the pair by setting their tile type to
–1 in the layout:
layout[selectedTiles[0].layer][selectedTiles[0].row]
[selectedTiles[0].column] = -1;
layout[selectedTiles[1].layer][selectedTiles[1].row]
[selectedTiles[1].column] = -1;
Recall that the PlayerBoard component won’t render anything in a given position
when it sees a tile type of –1.
Next, it’s time to calculate how many points they get:
let calculatedPoints: number = 10;
const now: number = new Date().getTime();
const timeTaken: number = now - timeSinceLastMatch;
const numHalfSeconds: number = Math.trunc(timeTaken / 500);
calculatedPoints -= numHalfSeconds;
if (calculatedPoints <= 0) {
calculatedPoints = 1;
}
scores.player += calculatedPoints;
timeSinceLastMatch = now;
The way this works is that they start with ten points for a match. From that, we
subtract a point for every half a second taken. But we want to be nice here, so they always
get a minimum of one point. Finally, the player’s score is updated (which is why the logic
is what it is in the “update” message handler as you saw earlier), and we record the new
time in timeSinceLastMatch so that we start counting from this moment toward their
next match.
Chapter 11 time for fun: BattleJong, the Client
340
Next, we have to let the server know what happened via a "match" message:
this.state.socketComm.send(
`match_${this.state.pid}_${calculatedPoints}`
);
The server needs to know the player ID and how many points they got, so that’s the
message that is constructed.
With that out of the way, the next task is to see if the board is either cleared or dead-
ended. For that, another helper function is employed:
const anyMovesLeft: string = this.state.anyMovesLeft(layout);
Do'stlaringiz bilan baham: |