Previous parts: , , , Part 1 Part 2 Part 3 Part 4 Hi community. After a pause for a couple of weeks, while I was busy, I’ve come back to my pet project. In this section, I want to create the ability to move figures. And also it will be good to highlight available cells where we can make a movement. We will start with and add a new method . It will return or based on can this figure is moved to the target cell or not. In the first simple implementation, we will add a simple check here and will return if the target cell already has a figure. FigureModel canMove true false false // src/models/FigureModel.ts class FigureModel { label: Labels; imageSrc: string; isDame: boolean; cell: CellModel; name: FigureNames; constructor(label: Labels, cell: CellModel) { this.label = label; this.cell = cell; this.cell.figure = this; this.isDame = false; this.name = FigureNames.Piece; this.imageSrc = label === Labels.Light ? pieceImgLight : pieceImgDark; } canMove(targetCell: CellModel): boolean { return !targetCell.figure; } } The next step is to create method in . The logic is easy. It will take as an argument. Then we check if our selected cell has a figure and this figure can be moved to the target cell - then we will save the figure to a new cell and clean our current (selected) cell: moveFigure CellModel targetCell // src/models/CellModel.ts class CellModel { readonly x: number; readonly y: number; readonly label: Labels; figure: FigureModel | null; // our figure board: BoardModel; available: boolean; key: string; constructor(x: number, y: number, label: Labels, board: BoardModel) { this.x = x; // x coord this.y = y; // y coord this.label = label; this.board = board; this.available = false; // is it free for figure this.key = `${String(x)}${String(y)}`; this.figure = null; // null by default } moveFigure(targetCell: CellModel) { if (this.figure && this.figure.canMove(targetCell)) { targetCell.figure = this.figure; // set figure on target cell this.figure = null; // clean current cell } } } I also want to highlight cells where we can move the selected figures. To do this we can use property in our which is by default. So let’s add a simple div with class available in our Cell component if it doesn’t have a figure and the cell is available: available CellModel false cell.available && !cell.figure && <div className="available" /> Full Cell.tsx component: // src/components/Cell/Cell.tsx export const Cell = ({ cell, rowIndex, cellIndex, selected, onFigureClick, }: CellProps): ReactElement => { const { figure, label } = cell; const handleFigureClick = () => onFigureClick(cell); return ( <div className={mergeClasses('cell', label, selected ? 'selected' : '')} onClick={handleFigureClick} > {figure?.imageSrc && <img className="icon" src={figure.imageSrc} alt={figure.name} />} {cell.available && !cell.figure && <div className="available" />} {(rowIndex === 0 || rowIndex === 7) && ( <div className={mergeClasses('board-label', rowIndex === 0 ? 'top' : 'bottom')}> {Letters[cellIndex]} </div> )} {(cellIndex === 0 || cellIndex === 7) && ( <div className={mergeClasses('board-label', cellIndex === 0 ? 'left' : 'right')}> {8 - rowIndex} </div> )} </div> ); }; And some styles for highlighting: // src/components/Cell/Cell.css .available { background-color: #fff; width: 10px; height: 10px; border-radius: 50%; } Cool. Let’s check and set temporary for available property in and we can see that all cells without figures are highlighted: true CellModel this.available = true; Perfect. Let’s again switch available property to false by default because we want to highlight available cells for movement when we select a figure. To do this we need to create a function which we will call on every selection. Let’s do this in : this.available = false BoardModel highlightCells(selectedCell: CellModel | null) { this.cells.forEach((row) => { row.forEach((cell) => { cell.available = !!selectedCell?.figure?.canMove(cell); }); }); } It will take a target cell as an argument, then iterate on every cell in the cell array and set the available property for every cell based on if this cell has a figure and this figure can move. Then we need to create a highlight function in our component which will call the same function from the model and pass the selected cell as an argument. And we will run it when our selected cell is changed, so we can use hook here: Board useEffect // src/components/Board/Board.tsx const highlightCells = () => { board.highlightCells(selected); }; useEffect(() => { highlightCells(); }, [selected]); But when we run our project and select a figure (A3 is selected) we don’t see any highlights. Why this is happened? This is an important moment. On every selection, we run highlight functions in and it updates properties. . To fix this we need to update our component state with a new Board model. Let’s create method in . It will create a new BoardModel instance, save the existing cells array to it and return the instance: BoardModel available But these updates are inside the model, so our React component doesn’t track these changes and doesn’t re-render the board App.tsx getNewModel BoardModel Full BoardModel.tsx // src/models/BoardModel.ts class BoardModel { cells: CellModel[][] = []; cellsInRow = 8; createCells() { for (let i = 0; i < this.cellsInRow; i += 1) { const row: CellModel[] = []; for (let j = 0; j < this.cellsInRow; j += 1) { if ((i + j) % 2 !== 0) { row.push(new CellModel(i, j, Labels.Dark, this)); // black } else { row.push(new CellModel(i, j, Labels.Light, this)); // white } } this.cells.push(row); } } highlightCells(selectedCell: CellModel | null) { this.cells.forEach((row) => { row.forEach((cell) => { cell.available = !!selectedCell?.figure?.canMove(cell); }); }); } getNewBoard(): BoardModel { const newBoard = new BoardModel(); newBoard.cells = this.cells; return newBoard; } getCell(x: number, y: number): CellModel { return this.cells[y][x]; } addFigures() { this.cells.forEach((row, rowIndex) => { row.forEach((cell, cellIndex) => { if (rowIndex <= 2 && cell.label === Labels.Dark) { new FigureModel(Labels.Dark, this.getCell(cellIndex, rowIndex)); // add dark pieces to first 3 rows } else if (rowIndex >= this.cells.length - 3 && cell.label === Labels.Dark) { new FigureModel(Labels.Light, this.getCell(cellIndex, rowIndex)); // add light pieces to last 3 rows } }); }); } } In component we just need to create an update function and get new and call it in ( we take as a prop from ): Board BoardModel highlightCells onSetBoard App.tsx // src/components/Board/Board.tsx const updateBoard = () => { const updatedBoard = board.getNewBoard(); onSetBoard(updatedBoard); }; const highlightCells = () => { board.highlightCells(selected); updateBoard(); }; Now highlighting will work as components will be re-rendered on every cell selection. The last step is to move our figure with our function from . We will rename to and update its logic. It will check if we selected cell in component state and the new target cell is not the same as selected and our selected figure can move to the target cell - then call . And clear in state and update the Board: moveFigure CellModel handleFigureClick handleCellClick moveFigure selectedCell // src/components/Board/Board.tsx const handleCellClick = (cell: CellModel) => { if (selected && selected !== cell && selected.figure?.canMove(cell)) { selected.moveFigure(cell); setSelected(null); updateBoard(); } else { setSelected(cell); } }; Full Board.tsx: // src/components/Board/Board.tsx export const Board = ({ board, onSetBoard }: BoardProps): ReactElement => { const [selected, setSelected] = useState<CellModel | null>(null); const updateBoard = () => { const updatedBoard = board.getNewBoard(); onSetBoard(updatedBoard); }; const highlightCells = () => { board.highlightCells(selected); updateBoard(); }; const handleCellClick = (cell: CellModel) => { if (selected && selected !== cell && selected.figure?.canMove(cell)) { selected.moveFigure(cell); setSelected(null); updateBoard(); } else { setSelected(cell); } }; useEffect(() => { highlightCells(); }, [selected]); return ( <div className="board"> {board.cells.map((row, rowIndex) => ( <Fragment key={rowIndex}> {row.map((cell, cellIndex) => ( <Cell cell={cell} key={cell.key} rowIndex={rowIndex} cellIndex={cellIndex} selected={selected?.x === cell.x && selected.y === cell.y} // check if selected cell coords equal to rendered cell onCellClick={handleCellClick} /> ))} </Fragment> ))} </div> ); }; That’s all. Our base movement logic is ready. We can select a figure and move it on any empty cell: Link to the repo