I am learning Angular, and as a first project I'm trying to make a web page to play chess.
For this I've made two components, with their respective classes: SquareComponent and BoardComponent.
The class SquareComponent has these properties:
export class SquareComponent {
@Input() board: BoardComponent;
@Input() color: 'dark' | 'light';
@Input() piece: '' | 'Pawn' | 'Rook' | 'Knight' | 'Bishop' | 'King' | 'Queen';
@Input() player: '' | 'Black' | 'White';
@Input() rank: string; // 1, 2, 3, ..., 8
@Input() file: string; // a, b, c, ..., h
@Input() selected: boolean;
@Input() index: number;
A board, which is an object of the class BoardComponent, because I'll need to gain access to some of its properties later. It has a color, to know if I should paint it as a light or dark square. It has the info of the piece on it and who it belongs to, so if it's a Pawn or Rook or whatever, and whether it belongs to the white player or black, or if there is no piece at all in the square.
Finally, and the bit I'm most interested in, it has a file and a rank, which, if you don't know how that works in chess, are basically the coordinates of the square. (I'll leave the explanation for the last two properties for later).
This would be my BoardComponent class:
export class BoardComponent implements OnInit {
squares: Array<SquareComponent>;
whiteNext: boolean;
winner: 'Black' | 'White';
selectedSquareIndex: number;
It has an array of objects of class SquareComponent (the 64 squares in the chess board), a boolean value which tells me which player's turn it is, and a winner. For the game's logic, it would be useful if each square knows the index where it's placed in the squares
array in the BoardComponent class, and the last property's explanation will be left for later.
With the templates I've set up and the properties I've described, I have been successful in making Angular paint a chess board with all the initial pieces (this is the continuation of the BoardComponent class):
constructor() { }
ngOnInit(): void {
this.newGame();
}
newGame() {
this.winner = null;
this.whiteNext = true;
this.squares = Array(0);
this.selectedSquareIndex = -1;
let lightSquare = true;
let squareIndex = 0;
for (let i = 8; i >= 1; i--) {
for (let j = 1; j <= 8; j++) {
let square = new SquareComponent(this);
square.rank = i + "";
square.file = String.fromCharCode(j + 96);
square.index = squareIndex;
squareIndex ++;
if (lightSquare) {
square.color = 'light';
} else {
square.color = 'dark';
}
square.player = '';
square.piece = '';
this.squares.push(square);
lightSquare = !lightSquare;
}
lightSquare = !lightSquare;
}
this.placeInitialPieces();
}
And here's function placeInitialPieces()
which uses the square's rank to identify some of the squares to place pawns and assign that side of the board to the players:
placeInitialPieces() {
// Players and Pawns:
for (let i = 0; i < this.squares.length; i++) {
this.squares[i].setSelected(false);
if (this.squares[i].rank == '8') {
this.squares[i].player = 'Black';
}
if (this.squares[i].rank == '7') {
this.squares[i].piece = 'Pawn';
this.squares[i].player = 'Black';
}
if (this.squares[i].rank == '2') {
this.squares[i].piece = 'Pawn';
this.squares[i].player = 'White';
}
if (this.squares[i].rank == '1') {
this.squares[i].player = 'White';
}
}
// Rooks:
this.squares[0].piece = 'Rook';
this.squares[7].piece = 'Rook';
this.squares[56].piece = 'Rook';
this.squares[63].piece = 'Rook';
// Knights:
this.squares[1].piece = 'Knight';
this.squares[6].piece = 'Knight';
this.squares[57].piece = 'Knight';
this.squares[62].piece = 'Knight';
// Bishops:
this.squares[2].piece = 'Bishop';
this.squares[5].piece = 'Bishop';
this.squares[58].piece = 'Bishop';
this.squares[61].piece = 'Bishop';
// Kings and Queens:
this.squares[3].piece = 'Queen';
this.squares[4].piece = 'King';
this.squares[59].piece = 'Queen';
this.squares[60].piece = 'King';
}
Now, for the game's logic, I want to make it so that you click on one of the pieces, which selects it, and then it's highlighted with a light blue. Then, the squares where it can move will be highlighted with say yellow, and then you click on a yellow square to move the piece.
Alternatively, if you missclick a piece you should be able to click it again to unselect it, or to click a different piece to select that one instead.
For that, I created the properties selected: boolean
in the class SquareComponent, and I also need the BoardComponent class to know which square is currently selected, which is why I defined property selectedSquareIndex
in the BoardComponent class, which is initially set to -1
. And when you click on a square, it goes to the select()
function below:
select() {
console.log(this); // I use this line to see what's going on
let condition1 = this.piece.length > 0;
let condition2 = this.board.playerTurn === this.player;
if (condition1 && condition2) {
if (this.board.getSelectedSquareIndex() == -1) {
this.selected = true;
this.board.setSelectedSquareIndex(this.getIndex());
} else if (this.getIndex() == this.board.getSelectedSquareIndex()) { // <-- undefined == undefined
this.selected = false;
this.board.setSelectedSquareIndex(-1);
} else {
console.log("We're in else");
this.board.deleteSelection();
this.selected = true;
this.board.setSelectedSquareIndex(this.getIndex());
}
}
}
Here's where my problem arises:
When I print the object in the select()
function, the properties rank
, file
and index
simply disappear, even though I used rank to place pieces earlier, and if I print the array of squares after being created, all the properties are there. I can select and unselect a square, but I can't swap them (in the else
statement) because the square's index isn't a defined property when I get to this method.
P.D: There's a few methods like getters, setters and deleteSelection()
that aren't here, but they're unimportant. However if you wish to see them, you can ask.
I can also publish my code to GitHub or something if you want to see the entire thing and test or review it.