diff --git a/lib/chess/game.ex b/lib/chess/game.ex deleted file mode 100644 index 56075d0..0000000 --- a/lib/chess/game.ex +++ /dev/null @@ -1,21 +0,0 @@ -defmodule Chess.Game do - @moduledoc false - - alias Chess.Board - alias Chess.Moves.Piece - - def player_checkmated?(board, colour) do - board - |> Board.search(%{"colour" => colour}) - end - - def king_in_check?(board, colour) do - king = - board - |> Board.search(%{"type" => "king", "colour" => colour}) - |> List.first - - board - |> Piece.attacked?(king) - end -end diff --git a/lib/chess/game_state.ex b/lib/chess/game_state.ex new file mode 100644 index 0000000..7b22fd7 --- /dev/null +++ b/lib/chess/game_state.ex @@ -0,0 +1,40 @@ +defmodule Chess.GameState do + @moduledoc false + + alias Chess.Board + alias Chess.Moves + alias Chess.Moves.Piece + + def player_checkmated?(board, colour) do + board + |> Board.search(%{"colour" => colour}) + |> Enum.all?(fn({file, rank}) -> + board + |> cannot_escape_check?({file, rank}) + end) + end + + def cannot_escape_check?(board, {file, rank}) do + piece = + board + |> Board.piece({file, rank}) + + board + |> Moves.available({file, rank}) + |> Enum.all?(fn({to_file, to_rank}) -> + board + |> Board.move_piece(%{"from" => [file, rank], "to" => [to_file, to_rank]}) + |> king_in_check?(piece["colour"]) + end) + end + + def king_in_check?(board, colour) do + king = + board + |> Board.search(%{"type" => "king", "colour" => colour}) + |> List.first + + board + |> Piece.attacked?(king) + end +end diff --git a/lib/chess/store/game.ex b/lib/chess/store/game.ex index b977e8f..48c4dc6 100644 --- a/lib/chess/store/game.ex +++ b/lib/chess/store/game.ex @@ -10,6 +10,7 @@ defmodule Chess.Store.Game do alias Chess.Board alias Chess.Store.Game + alias Chess.GameState schema "games" do field :board, :map, default: Board.default() @@ -49,7 +50,7 @@ defmodule Chess.Store.Game do end def validate_king_in_check(changeset, %Game{turn: turn}, %{board: board}) do - if Board.king_in_check?(board, turn) do + if GameState.king_in_check?(board, turn) do changeset |> add_error( :board, diff --git a/test/chess/game_state_test.exs b/test/chess/game_state_test.exs new file mode 100644 index 0000000..d04b251 --- /dev/null +++ b/test/chess/game_state_test.exs @@ -0,0 +1,94 @@ +defmodule Chess.GameStateTest do + @moduledoc false + + use Chess.DataCase + + alias Chess.GameState + + test "king is in check" do + board = %{ + "4,0" => %{"type" => "king", "colour" => "white"}, + "4,7" => %{"type" => "queen", "colour" => "black"}, + } + + assert GameState.king_in_check?(board, "white") + end + + test "king is not in check" do + board = %{ + "5,0" => %{"type" => "king", "colour" => "white"}, + "4,7" => %{"type" => "queen", "colour" => "black"}, + } + + refute GameState.king_in_check?(board, "white") + end + + test "king is in check by a knight" do + board = %{ + "4,0" => %{"type" => "king", "colour" => "white"}, + "3,2" => %{"type" => "knight", "colour" => "black"}, + } + + assert GameState.king_in_check?(board, "white") + end + + test "king is in check by a pawn" do + board = %{ + "4,0" => %{"type" => "king", "colour" => "white"}, + "3,1" => %{"type" => "pawn", "colour" => "black"}, + } + + assert GameState.king_in_check?(board, "white") + end + + test "king is in checkmate by queen and rook" do + board = %{ + "0,0" => %{"type" => "king", "colour" => "white"}, + "0,4" => %{"type" => "queen", "colour" => "black"}, + "1,4" => %{"type" => "rook", "colour" => "black"}, + } + + assert GameState.player_checkmated?(board, "white") + end + + test "king is not in checkmate by a queen" do + board = %{ + "0,0" => %{"type" => "king", "colour" => "white"}, + "0,4" => %{"type" => "queen", "colour" => "black"}, + } + + refute GameState.player_checkmated?(board, "white") + end + + test "king is checkmate by a queen and a knight" do + board = %{ + "0,0" => %{"type" => "king", "colour" => "white"}, + "1,1" => %{"type" => "queen", "colour" => "black"}, + "2,3" => %{"type" => "knight", "colour" => "black"}, + } + + assert GameState.player_checkmated?(board, "white") + end + + test "knight can block checkmate by queen and rook" do + board = %{ + "0,0" => %{"type" => "king", "colour" => "white"}, + "2,0" => %{"type" => "knight", "colour" => "white"}, + "0,5" => %{"type" => "queen", "colour" => "black"}, + "1,5" => %{"type" => "rook", "colour" => "black"}, + } + + refute GameState.player_checkmated?(board, "white") + end + + test "bishop can take checking queen" do + board = %{ + "0,0" => %{"type" => "king", "colour" => "white"}, + "2,3" => %{"type" => "bishop", "colour" => "white"}, + "0,5" => %{"type" => "queen", "colour" => "black"}, + "1,5" => %{"type" => "rook", "colour" => "black"}, + } + + refute GameState.player_checkmated?(board, "white") + end +end diff --git a/test/chess/game_test.ex b/test/chess/game_test.ex deleted file mode 100644 index 4a634ea..0000000 --- a/test/chess/game_test.ex +++ /dev/null @@ -1,43 +0,0 @@ -defmodule Chess.GameTest do - @moduledoc false - - use Chess.DataCase - - alias Chess.Game - - test "recognise when the king is in check" do - board = %{ - "4,0" => %{"type" => "king", "colour" => "white"}, - "4,7" => %{"type" => "queen", "colour" => "black"}, - } - - assert Game.king_in_check?(board, "white") - end - - test "recognise when the king is not in check" do - board = %{ - "5,0" => %{"type" => "king", "colour" => "white"}, - "4,7" => %{"type" => "queen", "colour" => "black"}, - } - - refute Game.king_in_check?(board, "white") - end - - test "recognize when the king is in check by a knight" do - board = %{ - "4,0" => %{"type" => "king", "colour" => "white"}, - "3,2" => %{"type" => "knight", "colour" => "black"}, - } - - assert Game.king_in_check?(board, "white") - end - - test "recognize when the king is in check by a pawn" do - board = %{ - "4,0" => %{"type" => "king", "colour" => "white"}, - "3,1" => %{"type" => "pawn", "colour" => "black"}, - } - - assert Game.king_in_check?(board, "white") - end -end