Normally it's somewhat tedious to generate the squares of a chess board that can be attacked by bishops or knights, but we make short work of it using comprehensions to generate and filter the board positions down to the solution to solve the challenge from last time.
The high level strategy (0:23)
It's easier and faster to solve algorithmic problems logically first and then code them, as opposed to doing both at once. Let's start by looking at the ways chess pieces can move and figure out which squares they can attack. The visuals at the beginning of this video are very helpful with this task.
- Rooks: Moves are any distance in a straight horizontal or vertical line. This they can attack pieces that are in the same column or row that they are.
- Bishops: Moves are any distance diagonally. Squares where the difference between their row and column number is the same as a bishop can be attacked by it on the upward sloping diagonal. Squares where the sum of their row and column values are the same as the bishop can be attacked by it on the downward sloping diagonal.
- Knights: Moves in an L-shaped jump. Squares with a column number off by one from the knight's and a row number off by two can be attacked by it. So can those with a column off by two and a row number off by one.
Coding the solution (3:55)
First off, we'll pull in the Square module from the Game Board lesson. Then we'll make a helper to generate a new board and one function for generating the squares each type of piece attacks.
The most straight-forward way to generate the squares a piece attacks is by using a
for comprehension that pattern matches the columns and rows of each square of the board and then filters them based on the logic above. For each type of piece, there are two criteria to check.
For knights, the two criteria are never both matched. A square can't have a column that's both one off of the knights and two off. For rooks and bishops, meeting both criteria is possible. A square could be have the same column and the same row as the rook. That's the square the rook is on and it's an invalid move. The same is true for the bishop—matching both criteria means its own square.
The missing xor operator (6:23)
So for bishops and rooks, we need an exclusive or and surprisingly Elixir doesn't have one! Fortunately, Erlang does, so we can simply call
:erlang.xor(condition1, condition2). It will return true only when one but not both of the conditions is true. For knights, we can just use a regular
There are a lot of ways this problem can be solved without pattern matching, or comprehensions. If you did come up with such a solution, you may be surprised how terse the code in this video is... and that's okay! sometimes doing things the hard way is a great way to thoroughly explore a problem space and great motivation for learning new techniques when you do encounter them!
By all means feel free to share your solutions below!