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
|
||||
|
||||
def moves(board, {file, rank}) do
|
||||
moves_northeast(board, {file, rank}) ++
|
||||
moves_southeast(board, {file, rank}) ++
|
||||
moves_northwest(board, {file, rank}) ++
|
||||
moves_southwest(board, {file, rank})
|
||||
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})
|
||||
Generator.moves(board, {file, rank}, {1, 1}) ++
|
||||
Generator.moves(board, {file, rank}, {1, -1}) ++
|
||||
Generator.moves(board, {file, rank}, {-1, 1}) ++
|
||||
Generator.moves(board, {file, rank}, {-1, -1})
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,12 +1,25 @@
|
||||
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
|
||||
def moves(_colour, _board, {0, _rank}, {-1, _}), do: []
|
||||
def moves(_colour, _board, {_file, 0}, {_, -1}), do: []
|
||||
def moves(_colour, _board, {7, _rank}, {1, _}), do: []
|
||||
def moves(_colour, _board, {_file, 7}, {_, 1}), do: []
|
||||
def moves(colour, board, {file, rank}, {fv, rv}) do
|
||||
def _moves(_colour, _board, {0, _rank}, {-1, _}), do: []
|
||||
def _moves(_colour, _board, {_file, 0}, {_, -1}), do: []
|
||||
def _moves(_colour, _board, {7, _rank}, {1, _}), do: []
|
||||
def _moves(_colour, _board, {_file, 7}, {_, 1}), do: []
|
||||
def _moves(colour, board, {file, rank}, {fv, rv}) do
|
||||
next_square = {file + fv, rank + rv}
|
||||
cond do
|
||||
can_capture_piece?(colour, board, next_square) ->
|
||||
@ -14,22 +27,22 @@ defmodule Chess.Moves.Generator do
|
||||
obstruction?(colour, board, next_square) ->
|
||||
[]
|
||||
true ->
|
||||
[next_square | moves(colour, board, next_square, {fv, rv})]
|
||||
[next_square | _moves(colour, board, next_square, {fv, rv})]
|
||||
end
|
||||
end
|
||||
|
||||
# Move generation for pieces that follow a pattern
|
||||
def moves(_colour, _board, {_file, _rank}, []), do: []
|
||||
def moves(colour, board, {file, rank}, [{fv, rv} | moves]) do
|
||||
def _moves(_colour, _board, {_file, _rank}, []), do: []
|
||||
def _moves(colour, board, {file, rank}, [{fv, rv} | moves]) do
|
||||
move_square = {file + fv, rank + rv}
|
||||
cond do
|
||||
outside_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) ->
|
||||
[move_square | moves(colour, board, {file, rank}, moves)]
|
||||
[move_square | _moves(colour, board, {file, rank}, moves)]
|
||||
true ->
|
||||
[move_square | moves(colour, board, {file, rank}, moves)]
|
||||
[move_square | _moves(colour, board, {file, rank}, moves)]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -4,9 +4,7 @@ defmodule Chess.Moves.King do
|
||||
alias Chess.Moves.Generator
|
||||
|
||||
def moves(board, {file, rank}) do
|
||||
board["#{file},#{rank}"]
|
||||
|> Map.get("colour")
|
||||
|> Generator.moves(board, {file, rank}, patterns())
|
||||
Generator.moves(board, {file, rank}, patterns())
|
||||
end
|
||||
|
||||
defp patterns do
|
||||
|
||||
@ -4,12 +4,10 @@ defmodule Chess.Moves.Knight do
|
||||
alias Chess.Moves.Generator
|
||||
|
||||
def moves(board, {file, rank}) do
|
||||
board["#{file},#{rank}"]
|
||||
|> Map.get("colour")
|
||||
|> Generator.moves(board, {file, rank}, patterns())
|
||||
Generator.moves(board, {file, rank}, pattern())
|
||||
end
|
||||
|
||||
defp patterns do
|
||||
def pattern do
|
||||
[{1, 2}, {2, 1}, {-1, 2}, {-2, 1}, {1, -2}, {2, -1}, {-1, -2}, {-2, -1}]
|
||||
end
|
||||
end
|
||||
|
||||
@ -18,14 +18,14 @@ defmodule Chess.Moves.Pawn do
|
||||
|> Map.get("colour")
|
||||
|
||||
colour
|
||||
|> _capture_moves(board, {file, rank}, patterns(colour))
|
||||
|> _capture_moves(board, {file, rank}, pattern(colour))
|
||||
end
|
||||
|
||||
defp patterns("white") do
|
||||
def pattern("white") do
|
||||
[{-1, 1}, {1, 1}]
|
||||
end
|
||||
|
||||
defp patterns("black") do
|
||||
def pattern("black") do
|
||||
[{-1, -1}, {1, -1}]
|
||||
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
|
||||
|
||||
def moves(board, {file, rank}) do
|
||||
moves_north(board, {file, rank}) ++
|
||||
moves_south(board, {file, rank}) ++
|
||||
moves_east(board, {file, rank}) ++
|
||||
moves_west(board, {file, rank})
|
||||
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})
|
||||
Generator.moves(board, {file, rank}, {0, 1}) ++
|
||||
Generator.moves(board, {file, rank}, {0, -1}) ++
|
||||
Generator.moves(board, {file, rank}, {-1, 0}) ++
|
||||
Generator.moves(board, {file, rank}, {1, 0})
|
||||
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