Hi all. I’m continuing with my experiments in the Checkers game. And firstly, I want to say thank you maratabdulin for his help with SVG images for figures. They look good now:
It’s time to add Players and the ability to change the current player on every next turn.
For Player, I’ll create a new class in our models - PlayerModel
. Initial logic is very easy, it’s just contained information about the player label: light player or dark player (Ha, ha, the dark side of the checkers:))
// src/models/PlayerModel.ts
import { Labels } from 'models/Labels';
class PlayerModel {
label: Labels;
constructor(label: Labels) {
this.label = label;
}
}
export { PlayerModel };
Now it has only 1 field which we will set during instance creation in the constructor.
We will initialize player instances in App.tsx
component:
// src/App.tsx
function App() {
const [board, setBoard] = useState<BoardModel>(new BoardModel());
const lightPlayer = new PlayerModel(Labels.Light); // user
const darkPlayer = new PlayerModel(Labels.Dark);
const [currentPlayer, setCurrentPlayer] = useState<PlayerModel>(lightPlayer);
const restart = () => {
const newBoard = new BoardModel();
newBoard.createCells();
newBoard.addFigures();
setBoard(newBoard);
setCurrentPlayer(lightPlayer);
};
const changePlayer = () => {
setCurrentPlayer(currentPlayer?.label === Labels.Light ? darkPlayer : lightPlayer);
};
useEffect(() => {
restart();
}, []);
return (
<div className="app">
<div className="player">Current Player: {currentPlayer.label}</div>
<Board
board={board}
currentPlayer={currentPlayer}
onChangePlayer={changePlayer}
onSetBoard={setBoard}
/>
</div>
);
}
We’ve created 2 instances for players and a state for the current player. changePlayer
is a simple function that will switch the player on every next turn. currentPlayer
and changePlayer
we will send it as a prop to Board
component.
I also added a new div with class player
to show which turn is right now, and some styles:
// src/App.css
.player {
height: 60px;
text-transform: capitalize;
font-weight: 600;
}
Now let’s add these 2 new props to Board types:
// src/components/Board.tsx
type BoardProps = {
board: BoardModel;
currentPlayer: PlayerModel;
onChangePlayer: () => void;
onSetBoard: (board: BoardModel) => void;
};
And finally, in our handleCellClick
function, we will call onChangePlayer
after every figure movement and in else conditions we will check that our selected figure label is the same as a current player’s label. This will allow us to move only current players’ figures:
// src/components/Board/Board.tsx
const handleCellClick = (cell: CellModel) => {
if (selected && selected !== cell && selected.figure?.canMove(cell)) {
selected.moveFigure(cell);
setSelected(null);
onChangePlayer(); // change player after we move the figure
updateBoard();
} else {
if (cell.figure?.label === currentPlayer.label) {
// we can only select our figures
setSelected(cell);
}
}
};
I also want to add logic that will calculate the correct available cell where we can move our figure. (Full game rules I’ll implement later) According to the rules, every figure can be moved only forward (except Dama) for the neighbor's dark cell. For example, the figure from C3 can be moved only to D4 or B4 cells, and figure A3 only to B4 cells:
To do this let’s add a function isForwardCell
to our CellModel
:
//src/models/CellModel.ts
isForwardCell(targetCell: CellModel, selectedFigure: FigureModel): boolean {
const { cell, label } = selectedFigure;
const dx = Math.abs(cell.x - targetCell.x);
const dy = cell.y - targetCell.y;
return label === Labels.Light ? dx === 1 && dy === 1 : dx === 1 && dy === -1;
}
The idea is next: we take targetCell
and selectedFigure
. Then we calculate diff between the selected cell x position and the target cell x position. As we can move our figure only to 1 pos to left or right then we check that dx
is equal to 1, for the Y axis it depends on the figure labels, as figures can be moved to the top or to the bottom parts of the board. so dy
will be 1 or -1.
And this function will be called in our FigureModel canMove method:
//src/models/FigureModel.ts
canMove(targetCell: CellModel): boolean {
return this.cell.isForwardCell(targetCell, this);
}
After all of these changes we can select only figures based on which turn is now and move the figure only to the correct cell: