paint-brush
Checkers on React - Part 6 - Playerby@rzhelieznov
481 reads
481 reads

Checkers on React - Part 6 - Player

by RomanOctober 10th, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Checkers-on-react project has been working on a new version of the Checkers app. The app will use a simple function to create players and switch the current player on every next turn. The App component will send a prop to the Board component and a prop for the player. We also added a new class with a new div with a player and a state for players. We will use the App component to create the board and set the player's current player to the current board. We’ve also added some SVG images for the figures.

Company Mentioned

Mention Thumbnail
featured image - Checkers on React - Part 6 - Player
Roman HackerNoon profile picture


Previous parts: Part 1Part 2Part 3Part 4, Part 5

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:



Link to the repo