mirror of
https://github.com/danbee/chess
synced 2025-03-04 08:39:06 +00:00
Determine whether a piece is being attacked
This also refactors a bunch of the movement generation code to simplify things and make it more flexible. This allows us to use the same code to check pieces under attack. * Still need to add pawns
This commit is contained in:
parent
474860a004
commit
18aa9faab4
@ -4,33 +4,9 @@ defmodule Chess.Moves.Bishop do
|
|||||||
alias Chess.Moves.Generator
|
alias Chess.Moves.Generator
|
||||||
|
|
||||||
def moves(board, {file, rank}) do
|
def moves(board, {file, rank}) do
|
||||||
moves_northeast(board, {file, rank}) ++
|
Generator.moves(board, {file, rank}, {1, 1}) ++
|
||||||
moves_southeast(board, {file, rank}) ++
|
Generator.moves(board, {file, rank}, {1, -1}) ++
|
||||||
moves_northwest(board, {file, rank}) ++
|
Generator.moves(board, {file, rank}, {-1, 1}) ++
|
||||||
moves_southwest(board, {file, rank})
|
Generator.moves(board, {file, rank}, {-1, -1})
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_northeast(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {1, 1})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_southeast(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {1, -1})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_northwest(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {-1, 1})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_southwest(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {-1, -1})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,12 +1,25 @@
|
|||||||
defmodule Chess.Moves.Generator do
|
defmodule Chess.Moves.Generator do
|
||||||
@moduledoc false
|
@moduledoc """
|
||||||
|
Generates moves for a piece. We can generate moves in a straight line,
|
||||||
|
and these can be combined to make an entire pieces allowed moves. Or we
|
||||||
|
can generate moves based on a pattern of vectors. The knight works
|
||||||
|
like this.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# `movement` is either a vector (bishop, rook, queen)
|
||||||
|
# or a pattern (king, knight)
|
||||||
|
def moves(board, {file, rank}, movement) do
|
||||||
|
board["#{file},#{rank}"]
|
||||||
|
|> Map.get("colour")
|
||||||
|
|> _moves(board, {file, rank}, movement)
|
||||||
|
end
|
||||||
|
|
||||||
# Move generation for pieces that move in straight lines
|
# Move generation for pieces that move in straight lines
|
||||||
def moves(_colour, _board, {0, _rank}, {-1, _}), do: []
|
def _moves(_colour, _board, {0, _rank}, {-1, _}), do: []
|
||||||
def moves(_colour, _board, {_file, 0}, {_, -1}), do: []
|
def _moves(_colour, _board, {_file, 0}, {_, -1}), do: []
|
||||||
def moves(_colour, _board, {7, _rank}, {1, _}), do: []
|
def _moves(_colour, _board, {7, _rank}, {1, _}), do: []
|
||||||
def moves(_colour, _board, {_file, 7}, {_, 1}), do: []
|
def _moves(_colour, _board, {_file, 7}, {_, 1}), do: []
|
||||||
def moves(colour, board, {file, rank}, {fv, rv}) do
|
def _moves(colour, board, {file, rank}, {fv, rv}) do
|
||||||
next_square = {file + fv, rank + rv}
|
next_square = {file + fv, rank + rv}
|
||||||
cond do
|
cond do
|
||||||
can_capture_piece?(colour, board, next_square) ->
|
can_capture_piece?(colour, board, next_square) ->
|
||||||
@ -14,22 +27,22 @@ defmodule Chess.Moves.Generator do
|
|||||||
obstruction?(colour, board, next_square) ->
|
obstruction?(colour, board, next_square) ->
|
||||||
[]
|
[]
|
||||||
true ->
|
true ->
|
||||||
[next_square | moves(colour, board, next_square, {fv, rv})]
|
[next_square | _moves(colour, board, next_square, {fv, rv})]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Move generation for pieces that follow a pattern
|
# Move generation for pieces that follow a pattern
|
||||||
def moves(_colour, _board, {_file, _rank}, []), do: []
|
def _moves(_colour, _board, {_file, _rank}, []), do: []
|
||||||
def moves(colour, board, {file, rank}, [{fv, rv} | moves]) do
|
def _moves(colour, board, {file, rank}, [{fv, rv} | moves]) do
|
||||||
move_square = {file + fv, rank + rv}
|
move_square = {file + fv, rank + rv}
|
||||||
cond do
|
cond do
|
||||||
outside_board?(move_square) ||
|
outside_board?(move_square) ||
|
||||||
obstruction?(colour, board, move_square) ->
|
obstruction?(colour, board, move_square) ->
|
||||||
moves(colour, board, {file, rank}, moves)
|
_moves(colour, board, {file, rank}, moves)
|
||||||
can_capture_piece?(colour, board, move_square) ->
|
can_capture_piece?(colour, board, move_square) ->
|
||||||
[move_square | moves(colour, board, {file, rank}, moves)]
|
[move_square | _moves(colour, board, {file, rank}, moves)]
|
||||||
true ->
|
true ->
|
||||||
[move_square | moves(colour, board, {file, rank}, moves)]
|
[move_square | _moves(colour, board, {file, rank}, moves)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,7 @@ defmodule Chess.Moves.King do
|
|||||||
alias Chess.Moves.Generator
|
alias Chess.Moves.Generator
|
||||||
|
|
||||||
def moves(board, {file, rank}) do
|
def moves(board, {file, rank}) do
|
||||||
board["#{file},#{rank}"]
|
Generator.moves(board, {file, rank}, patterns())
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, patterns())
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp patterns do
|
defp patterns do
|
||||||
|
|||||||
@ -4,12 +4,10 @@ defmodule Chess.Moves.Knight do
|
|||||||
alias Chess.Moves.Generator
|
alias Chess.Moves.Generator
|
||||||
|
|
||||||
def moves(board, {file, rank}) do
|
def moves(board, {file, rank}) do
|
||||||
board["#{file},#{rank}"]
|
Generator.moves(board, {file, rank}, pattern())
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, patterns())
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp patterns do
|
def pattern do
|
||||||
[{1, 2}, {2, 1}, {-1, 2}, {-2, 1}, {1, -2}, {2, -1}, {-1, -2}, {-2, -1}]
|
[{1, 2}, {2, 1}, {-1, 2}, {-2, 1}, {1, -2}, {2, -1}, {-1, -2}, {-2, -1}]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -18,14 +18,14 @@ defmodule Chess.Moves.Pawn do
|
|||||||
|> Map.get("colour")
|
|> Map.get("colour")
|
||||||
|
|
||||||
colour
|
colour
|
||||||
|> _capture_moves(board, {file, rank}, patterns(colour))
|
|> _capture_moves(board, {file, rank}, pattern(colour))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp patterns("white") do
|
def pattern("white") do
|
||||||
[{-1, 1}, {1, 1}]
|
[{-1, 1}, {1, 1}]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp patterns("black") do
|
def pattern("black") do
|
||||||
[{-1, -1}, {1, -1}]
|
[{-1, -1}, {1, -1}]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
55
lib/chess/moves/piece.ex
Normal file
55
lib/chess/moves/piece.ex
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
defmodule Chess.Moves.Piece do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
alias Chess.Moves.Generator
|
||||||
|
alias Chess.Moves.Knight
|
||||||
|
|
||||||
|
def attacked?(board, {file, rank}) do
|
||||||
|
attacked_by_rook_or_queen?(board, {file, rank}) ||
|
||||||
|
attacked_by_bishop_or_queen?(board, {file, rank}) ||
|
||||||
|
attacked_by_knight?(board, {file, rank})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp attacked_by_rook_or_queen?(board, {file, rank}) do
|
||||||
|
_attacked?(board, {file, rank}, {0, 1}, ["rook", "queen"]) ||
|
||||||
|
_attacked?(board, {file, rank}, {0, -1}, ["rook", "queen"]) ||
|
||||||
|
_attacked?(board, {file, rank}, {1, 0}, ["rook", "queen"]) ||
|
||||||
|
_attacked?(board, {file, rank}, {-1, 0}, ["rook", "queen"])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp attacked_by_bishop_or_queen?(board, {file, rank}) do
|
||||||
|
_attacked?(board, {file, rank}, {1, 1}, ["bishop", "queen"]) ||
|
||||||
|
_attacked?(board, {file, rank}, {1, -1}, ["bishop", "queen"]) ||
|
||||||
|
_attacked?(board, {file, rank}, {-1, 1}, ["bishop", "queen"]) ||
|
||||||
|
_attacked?(board, {file, rank}, {-1, -1}, ["bishop", "queen"])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp attacked_by_knight?(board, {file, rank}) do
|
||||||
|
_attacked?(board, {file, rank}, Knight.pattern, "knight")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp _attacked?(board, {file, rank}, {fv, rv}, pieces) do
|
||||||
|
{file, rank} =
|
||||||
|
board
|
||||||
|
|> Generator.moves({file, rank}, {fv, rv})
|
||||||
|
|> List.last
|
||||||
|
|
||||||
|
piece = board["#{file},#{rank}"]
|
||||||
|
|
||||||
|
Enum.any?(pieces, &(match?(%{"type" => &1}, piece)))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp _attacked?(board, {file, rank}, pattern, piece_type) do
|
||||||
|
moves =
|
||||||
|
board
|
||||||
|
|> Generator.moves({file, rank}, pattern)
|
||||||
|
|
||||||
|
Enum.any?(moves, &(match_piece(board, &1, piece_type)))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp match_piece(board, {file, rank}, piece_type) do
|
||||||
|
piece = board["#{file},#{rank}"]
|
||||||
|
|
||||||
|
piece["type"] == piece_type
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -4,33 +4,9 @@ defmodule Chess.Moves.Rook do
|
|||||||
alias Chess.Moves.Generator
|
alias Chess.Moves.Generator
|
||||||
|
|
||||||
def moves(board, {file, rank}) do
|
def moves(board, {file, rank}) do
|
||||||
moves_north(board, {file, rank}) ++
|
Generator.moves(board, {file, rank}, {0, 1}) ++
|
||||||
moves_south(board, {file, rank}) ++
|
Generator.moves(board, {file, rank}, {0, -1}) ++
|
||||||
moves_east(board, {file, rank}) ++
|
Generator.moves(board, {file, rank}, {-1, 0}) ++
|
||||||
moves_west(board, {file, rank})
|
Generator.moves(board, {file, rank}, {1, 0})
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_north(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {0, 1})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_south(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {0, -1})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_east(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {1, 0})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp moves_west(board, {file, rank}) do
|
|
||||||
board["#{file},#{rank}"]
|
|
||||||
|> Map.get("colour")
|
|
||||||
|> Generator.moves(board, {file, rank}, {-1, 0})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
59
test/chess/moves/piece_test.exs
Normal file
59
test/chess/moves/piece_test.exs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
defmodule Chess.Moves.PieceTest do
|
||||||
|
use Chess.DataCase
|
||||||
|
|
||||||
|
alias Chess.Moves.Piece
|
||||||
|
|
||||||
|
test "piece is not being attacked" do
|
||||||
|
board = %{
|
||||||
|
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||||
|
"2,1" => %{"type" => "rook", "colour" => "black"},
|
||||||
|
}
|
||||||
|
|
||||||
|
refute Piece.attacked?(board, {4, 5})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "piece can be attacked by a rook" do
|
||||||
|
board = %{
|
||||||
|
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||||
|
"2,5" => %{"type" => "rook", "colour" => "black"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Piece.attacked?(board, {4, 5})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "piece can be attacked by a bishop" do
|
||||||
|
board = %{
|
||||||
|
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||||
|
"6,7" => %{"type" => "bishop", "colour" => "black"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Piece.attacked?(board, {4, 5})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "piece can be attacked by a queen" do
|
||||||
|
board = %{
|
||||||
|
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||||
|
"6,7" => %{"type" => "queen", "colour" => "black"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Piece.attacked?(board, {4, 5})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "piece is not attacked by a knight" do
|
||||||
|
board = %{
|
||||||
|
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||||
|
"7,7" => %{"type" => "knight", "colour" => "black"},
|
||||||
|
}
|
||||||
|
|
||||||
|
refute Piece.attacked?(board, {4, 5})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "piece can be attacked by a knight" do
|
||||||
|
board = %{
|
||||||
|
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||||
|
"5,7" => %{"type" => "knight", "colour" => "black"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Piece.attacked?(board, {4, 5})
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue
Block a user